Skip to content

JSAPI 支付接口

接口说明

JSAPI 支付接口用于在 H5 页面小程序 内发起支付请求,支持微信支付和支付宝两种支付渠道。商户通过统一接口传入支付参数,平台返回对应渠道的支付凭证,前端调用相应 SDK 完成支付。

接口信息

  • 接口地址: POST /v1/pay/jsapi
  • Content-Type: application/json
  • 字符编码: UTF-8
  • 签名方式: RSA-SHA256

请求参数

公共参数

参数名类型必填说明
pay_mch_idString商户号,由平台分配
timestampString时间戳(秒级),有效期 5 分钟
nonce_strString随机字符串,32 位以内
sign_typeString签名类型,固定值:RSA
signString签名值

业务参数

参数名类型必填说明
out_trade_noString商户订单号,商户系统内部唯一,建议 32 位以内
total_amountInteger订单总金额,单位:分
subjectString商品名称,显示在支付页面,128 字符以内
methodString支付方式:wechat(微信)或 alipay(支付宝)
sub_openidString条件用户 sub_openid,微信支付必填
notify_urlString异步通知地址,必须为公网可访问的 HTTPS 地址
return_urlString同步回调地址,支付完成后跳转地址
time_expireString订单失效时间,格式:yyyy-MM-dd HH:mm:ss

请求示例

json
{
  "pay_mch_id": "1234567890",
  "out_trade_no": "ORDER20240101120000",
  "total_amount": 100,
  "subject": "测试商品",
  "method": "wechat",
  "sub_openid": "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o",
  "notify_url": "https://your-domain.com/api/notify",
  "return_url": "https://your-domain.com/pay/result",
  "timestamp": "1704067200",
  "nonce_str": "5K8264ILTKCH16CQ2502SI8ZNMTM67VS",
  "sign_type": "RSA",
  "sign": "签名值"
}

响应参数

成功响应

参数名类型说明
codeString响应码,0000 表示成功
msgString响应消息
dataObject业务数据
data.trade_noString支付交易号,对应用户支付凭证里的交易号
data.out_trade_noString商户订单号
data.transaction_idString支付宝平台交易号(仅支付宝渠道返回)
data.channelString支付渠道: wechat 或 alipay
data.pay_paramsObject支付参数(用于前端调起支付)

pay_params 说明

微信支付

参数名说明
appId微信公众号/小程序 AppID
timeStamp时间戳
nonceStr随机字符串
package预支付交易会话标识,格式:prepay_id=xxx
signType签名类型,固定值:RSA
paySign支付签名

支付宝

参数名说明
form支付表单 HTML(H5 支付)
query_param查询参数(小程序支付)

响应示例

微信支付响应

json
{
  "code": "0000",
  "msg": "success",
  "data": {
    "out_trade_no": "ORDER20240101120000",
    "trade_no": "WX20240101120000001",
    "channel": "wechat",
    "pay_params": {
      "appId": "wx1234567890",
      "timeStamp": "1704067200",
      "nonceStr": "5K8264ILTKCH16CQ2502SI8ZNMTM67VS",
      "package": "prepay_id=wx20240101120000",
      "signType": "RSA",
      "paySign": "微信支付签名"
    }
  }
}

支付宝响应

json
{
  "code": "0000",
  "msg": "success",
  "data": {
    "out_trade_no": "ORDER20240101120000",
    "trade_no": "ALI20240101120000001",
    "transaction_id": "2024010122001234567890",
    "channel": "alipay",
    "pay_params": {
      "form": "<form>...</form>"
    }
  }
}

前端调用示例

微信支付(H5/小程序)

javascript
// 1. 后端请求 JSAPI 接口,获取 pay_params
const response = await fetch('/your-backend/api/pay/jsapi', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    out_trade_no: 'ORDER20240101120000',
    total_amount: 100,
    subject: '测试商品',
    method: 'wechat',
    sub_openid: 'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o'
  })
});

const result = await response.json();

// 2. 调用微信支付
if (result.code === '0000') {
  const payParams = result.data.pay_params;
  
  // H5 环境
  if (typeof WeixinJSBridge !== 'undefined') {
    WeixinJSBridge.invoke('getBrandWCPayRequest', {
      appId: payParams.appId,
      timeStamp: payParams.timeStamp,
      nonceStr: payParams.nonceStr,
      package: payParams.package,
      signType: payParams.signType,
      paySign: payParams.paySign
    }, function(res) {
      if (res.err_msg === 'get_brand_wcpay_request:ok') {
        // 支付成功
        alert('支付成功');
        window.location.href = '/pay/success';
      } else {
        // 支付失败或取消
        alert('支付失败');
      }
    });
  }
  
  // 小程序环境
  # wx.requestPayment({
    timeStamp: payParams.timeStamp,
    nonceStr: payParams.nonceStr,
    package: payParams.package,
    signType: payParams.signType,
    paySign: payParams.paySign,
    success: function(res) {
      // 支付成功
      wx.showToast({ title: '支付成功' });
    },
    fail: function(err) {
      // 支付失败
      wx.showToast({ title: '支付失败', icon: 'error' });
    }
  });
}

支付宝支付(H5)

javascript
// 1. 后端请求 JSAPI 接口
const response = await fetch('/your-backend/api/pay/jsapi', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    out_trade_no: 'ORDER20240101120000',
    total_amount: 100,
    subject: '测试商品',
    method: 'alipay',
    sub_openid: '20881234567890'
  })
});

const result = await response.json();

// 2. 提交支付宝表单
if (result.code === '0000') {
  const form = result.data.pay_params.form;
  
  // 创建临时 div 并提交表单
  const div = document.createElement('div');
  div.style.display = 'none';
  div.innerHTML = form;
  document.body.appendChild(div);
  div.querySelector('form').submit();
}

异步通知

通知地址

商户需在请求参数中提供 notify_url,支付完成后平台将向该地址发送异步通知。

通知参数

json
{
  "pay_mch_id": "1234567890",
  "out_trade_no": "ORDER20240101120000",
  "trade_no": "MF20240101120000001",
  "total_amount": 100,
  "pay_amount": 100,
  "trade_state": "SUCCESS",
  "method": "wechat",
  "pay_time": "2024-01-01 12:00:00",
  "notify_time": "2024-01-01 12:00:01",
  "sign_type": "RSA",
  "sign": "签名值"
}

处理流程

  1. 验证签名: 使用平台公钥验证通知签名
  2. 验证数据: 检查 out_trade_nototal_amount 等关键信息
  3. 处理业务: 更新订单状态、发货等
  4. 返回响应: 返回 success 字符串(不含引号)

通知重试机制

  • 通知频率:0s、15s、15s、30s、3min、10min、20min、30min、30min
  • 最多重试 10 次
  • 只有在接收到 success 响应后才停止重试

通知处理示例(Node.js)

javascript
const express = require('express');
const router = express.Router();

router.post('/api/notify', async (req, res) => {
  try {
    const notifyData = req.body;
    
    // 1. 验证签名
    const isValid = verifySign(notifyData, platformPublicKey);
    if (!isValid) {
      console.error('签名验证失败');
      res.send('failure');
      return;
    }
    
    // 2. 验证订单金额
    const order = await getOrder(notifyData.out_trade_no);
    if (order.total_amount !== notifyData.total_amount) {
      console.error('订单金额不匹配');
      res.send('failure');
      return;
    }
    
    // 3. 更新订单状态
    if (notifyData.trade_state === 'SUCCESS' || notifyData.trade_state === 'TRADE_FINISHED') {
      await updateOrderStatus(notifyData.out_trade_no, 'PAID');
      // 执行业务逻辑:发货、发送通知等
    }
    
    // 4. 返回成功
    res.send('success');
  } catch (error) {
    console.error('处理通知失败:', error);
    res.send('failure');
  }
});

module.exports = router;

支付流程

mermaid
sequenceDiagram
    participant User as 用户
    participant Frontend as 前端页面
    participant Backend as 商户后端
    participant Platform as 米付平台
    participant PayChannel as 支付渠道
    
    User->>Frontend: 点击支付
    Frontend->>Backend: 请求支付
    Backend->>Backend: 生成签名
    Backend->>Platform: 请求 JSAPI 接口
    Platform->>PayChannel: 创建预支付订单
    PayChannel-->>Platform: 返回预支付凭证
    Platform-->>Backend: 返回支付参数
    Backend-->>Frontend: 返回支付参数
    Frontend->>PayChannel: 调起支付
    User->>PayChannel: 完成支付
    PayChannel-->>Frontend: 支付结果
    PayChannel->>Platform: 支付结果通知
    Platform->>Backend: 异步通知
    Backend-->>Platform: 返回 success
    Frontend->>Backend: 查询支付结果
    Backend->>Platform: 调用查询接口
    Platform-->>Backend: 返回订单状态
    Backend-->>Frontend: 显示支付结果

错误码

错误码说明处理建议
0000成功-
1001参数错误检查请求参数是否完整、格式是否正确
1002签名验证失败检查签名算法、密钥是否正确
1003商户号不存在确认商户号是否正确
1004商户未开通该支付方式联系平台开通相应方式
1005订单号重复更换唯一的订单号
1006订单金额错误确认金额为正整数,单位:分
1007时间戳过期检查服务器时间是否同步
1008支付方式错误检查 method 参数是否正确
1009sub_openid 无效确认 sub_openid 是否正确
1010系统繁忙稍后重试或联系客服

注意事项

  1. 订单号唯一性: out_trade_no 在商户系统内必须唯一,重复订单号将返回错误
  2. 金额单位: 金额单位统一为,避免浮点数精度问题
  3. 时间戳: 请求时间戳与服务器时间相差超过 5 分钟将被拒绝
  4. 签名安全: 私钥必须严格保密,不可在前端暴露
  5. 异步通知: 必须正确处理异步通知,不能仅依赖前端回调
  6. 幂等性: 同一订单号多次请求将返回相同结果,确保接口幂等
  7. HTTPS: 所有接口必须使用 HTTPS 协议
  8. 通知地址: notify_url 必须为公网可访问的 HTTPS 地址

常见问题

Q: 微信支付提示"签名错误"怎么办?

  1. 检查商户 API 密钥是否正确
  2. 确认签名算法是否为 RSA-SHA256
  3. 检查参数排序和拼接是否正确

Q: 支付宝支付无法调起?

  1. 检查是否正确提交表单
  2. 确认买家 ID(buyer_id)是否正确
  3. 检查支付宝公钥是否配置正确

Q: 异步通知收不到怎么办?

  1. 确认 notify_url 是否为公网可访问地址
  2. 检查服务器防火墙是否拦截了请求
  3. 查看服务器日志,确认是否正常接收通知
  4. 可在商户平台查看通知发送记录

Q: 如何测试支付功能?

  1. 使用测试环境进行开发调试
  2. 微信支付可使用沙箱环境
  3. 支付宝可使用沙箱账号测试
  4. 测试完成后切换到生产环境

Copyright © 2024 米付科技