Appearance
JSAPI 支付接口
接口说明
JSAPI 支付接口用于在 H5 页面 或 小程序 内发起支付请求,支持微信支付和支付宝两种支付渠道。商户通过统一接口传入支付参数,平台返回对应渠道的支付凭证,前端调用相应 SDK 完成支付。
接口信息
- 接口地址:
POST /v1/pay/jsapi - Content-Type:
application/json - 字符编码: UTF-8
- 签名方式: RSA-SHA256
请求参数
公共参数
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| pay_mch_id | String | 是 | 商户号,由平台分配 |
| timestamp | String | 是 | 时间戳(秒级),有效期 5 分钟 |
| nonce_str | String | 是 | 随机字符串,32 位以内 |
| sign_type | String | 是 | 签名类型,固定值:RSA |
| sign | String | 是 | 签名值 |
业务参数
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| out_trade_no | String | 是 | 商户订单号,商户系统内部唯一,建议 32 位以内 |
| total_amount | Integer | 是 | 订单总金额,单位:分 |
| subject | String | 是 | 商品名称,显示在支付页面,128 字符以内 |
| method | String | 是 | 支付方式:wechat(微信)或 alipay(支付宝) |
| sub_openid | String | 条件 | 用户 sub_openid,微信支付必填 |
| notify_url | String | 否 | 异步通知地址,必须为公网可访问的 HTTPS 地址 |
| return_url | String | 否 | 同步回调地址,支付完成后跳转地址 |
| time_expire | String | 否 | 订单失效时间,格式: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": "签名值"
}响应参数
成功响应
| 参数名 | 类型 | 说明 |
|---|---|---|
| code | String | 响应码,0000 表示成功 |
| msg | String | 响应消息 |
| data | Object | 业务数据 |
| data.trade_no | String | 支付交易号,对应用户支付凭证里的交易号 |
| data.out_trade_no | String | 商户订单号 |
| data.transaction_id | String | 支付宝平台交易号(仅支付宝渠道返回) |
| data.channel | String | 支付渠道: wechat 或 alipay |
| data.pay_params | Object | 支付参数(用于前端调起支付) |
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": "签名值"
}处理流程
- 验证签名: 使用平台公钥验证通知签名
- 验证数据: 检查
out_trade_no、total_amount等关键信息 - 处理业务: 更新订单状态、发货等
- 返回响应: 返回
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 参数是否正确 |
| 1009 | sub_openid 无效 | 确认 sub_openid 是否正确 |
| 1010 | 系统繁忙 | 稍后重试或联系客服 |
注意事项
- 订单号唯一性:
out_trade_no在商户系统内必须唯一,重复订单号将返回错误 - 金额单位: 金额单位统一为分,避免浮点数精度问题
- 时间戳: 请求时间戳与服务器时间相差超过 5 分钟将被拒绝
- 签名安全: 私钥必须严格保密,不可在前端暴露
- 异步通知: 必须正确处理异步通知,不能仅依赖前端回调
- 幂等性: 同一订单号多次请求将返回相同结果,确保接口幂等
- HTTPS: 所有接口必须使用 HTTPS 协议
- 通知地址:
notify_url必须为公网可访问的 HTTPS 地址
常见问题
Q: 微信支付提示"签名错误"怎么办?
- 检查商户 API 密钥是否正确
- 确认签名算法是否为 RSA-SHA256
- 检查参数排序和拼接是否正确
Q: 支付宝支付无法调起?
- 检查是否正确提交表单
- 确认买家 ID(buyer_id)是否正确
- 检查支付宝公钥是否配置正确
Q: 异步通知收不到怎么办?
- 确认
notify_url是否为公网可访问地址 - 检查服务器防火墙是否拦截了请求
- 查看服务器日志,确认是否正常接收通知
- 可在商户平台查看通知发送记录
Q: 如何测试支付功能?
- 使用测试环境进行开发调试
- 微信支付可使用沙箱环境
- 支付宝可使用沙箱账号测试
- 测试完成后切换到生产环境
