Appearance
JSAPI 支付接口
接口说明
JSAPI 支付接口用于在 H5 页面 或 小程序 内发起支付请求,支持微信支付和支付宝两种支付渠道。商户通过统一接口传入支付参数,平台返回对应渠道的支付凭证,前端调用相应 SDK 完成支付。
接口信息
- 接口地址:
POST /v1/pay/jsapi - Content-Type:
application/json - 字符编码: UTF-8
- 签名方式: RSA-SHA256
请求参数
认证信息
请在 HTTP Header 中携带 Authorization 字段,格式详见 接口概览
业务参数
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| merchant_no | String | 是 | 商户号 |
| out_trade_no | String | 是 | 商户订单号,服务商自定义,商户系统内部唯一,建议 32 位以内 |
| total_amount | Integer | 是 | 订单总金额,单位:分 |
| subject | String | 是 | 商品名称,显示在支付页面,128 字符以内 |
| pay_method | String | 是 | 支付方式:wechat(微信)或 alipay(支付宝) |
| sub_appid | String | 条件 | 微信公众号或小程序的AppID,微信支付必填 |
| sub_openid | String | 条件 | 用户标识:微信支付为用户openid,支付宝为用户userid |
| notify_url | String | 否 | 异步通知地址,必须为公网可访问的 HTTPS 地址 |
| time_expire | Integer | 否 | 订单过期时间(秒),从创建时开始计算,如 300 表示5分钟 |
请求示例
http
POST /v1/pay/jsapi
Content-Type: application/json
Authorization: RSA service_id=SVC1234567890,timestamp=1704067200,nonce_str=abc123,sign=签名值
{
"merchant_no": "1234567890",
"out_trade_no": "ORDER20240101120000",
"total_amount": 100,
"subject": "测试商品",
"pay_method": "wechat",
"sub_appid": "wx1234567890",
"sub_openid": "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o",
"notify_url": "https://your-domain.meepay.org/api/notify",
"time_expire": 300
}响应参数
成功响应
| 参数名 | 类型 | 说明 |
|---|---|---|
| code | String | 响应码,00000 表示成功 |
| msg | String | 响应消息 |
| data | Object | 业务数据 |
| data.pay_trade_no | String | 商户单号,对应用户支付凭证里的商户单号 |
| data.out_trade_no | String | 商户订单号,服务商自定义 |
| data.transaction_id | String | 交易单号,对应用户支付凭证里的交易单号(支付宝返回) |
| data.pay_method | String | 支付方式: wechat 或 alipay |
| data.jspay_params | Object | JSAPI支付参数(用于前端调起支付,仅微信支付返回) |
jspay_params 说明
微信支付
| 参数名 | 说明 |
|---|---|
| appId | 微信公众号/小程序 AppID |
| timeStamp | 时间戳 |
| nonceStr | 随机字符串 |
| package | 预支付交易会话标识,格式:prepay_id=xxx |
| signType | 签名类型,固定值:RSA |
| paySign | 支付签名 |
支付宝
支付宝支付不返回 jspay_params,商户直接通过 transaction_id 或支付渠道SDK完成支付。
通用返回参数
响应示例
微信支付响应
json
{
"code": "00000",
"msg": "success",
"data": {
"out_trade_no": "ORDER20240101120000",
"pay_trade_no": "WX20240101120000001",
"pay_method": "wechat",
"jspay_params": {
"appId": "wx1234567890",
"timeStamp": "1704067200",
"nonceStr": "5K8264ILTKCH16CQ2502SI8ZNMTM67VS",
"package": "prepay_id=wx20240101120000",
"signType": "RSA",
"paySign": "微信支付签名"
}
}
}支付宝响应
json
{
"code": "00000",
"msg": "success",
"data": {
"out_trade_no": "ORDER20240101120000",
"pay_trade_no": "ALI20240101120000001",
"transaction_id": "2024010122001234567890",
"pay_method": "alipay"
}
}前端调用示例
微信支付(H5/小程序)
javascript
// 1. 后端请求 JSAPI 接口,获取 jspay_params
const response = await fetch('https://your-backend.meepay.org/api/pay/jsapi', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
out_trade_no: 'ORDER20240101120000',
total_amount: 100,
subject: '测试商品',
pay_method: 'wechat',
sub_appid: 'wx1234567890',
sub_openid: 'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o'
})
});
const result = await response.json();
// 2. 调用微信支付
if (result.code === '00000') {
const jspayParams = result.data.jspay_params;
// H5 环境
if (typeof WeixinJSBridge !== 'undefined') {
WeixinJSBridge.invoke('getBrandWCPayRequest', {
appId: jspayParams.appId,
timeStamp: jspayParams.timeStamp,
nonceStr: jspayParams.nonceStr,
package: jspayParams.package,
signType: jspayParams.signType,
paySign: jspayParams.paySign
}, function(res) {
if (res.err_msg === 'get_brand_wcpay_request:ok') {
// 支付成功
alert('支付成功');
window.location.href = 'https://your-domain.meepay.org/pay/success';
} else {
// 支付失败或取消
alert('支付失败');
}
});
}
// 小程序环境
wx.requestPayment({
timeStamp: jspayParams.timeStamp,
nonceStr: jspayParams.nonceStr,
package: jspayParams.package,
signType: jspayParams.signType,
paySign: jspayParams.paySign,
success: function(res) {
// 支付成功
wx.showToast({ title: '支付成功' });
},
fail: function(err) {
// 支付失败
wx.showToast({ title: '支付失败', icon: 'error' });
}
});
}支付宝支付
javascript
// 1. 后端请求 JSAPI 接口
const response = await fetch('https://your-backend.meepay.org/api/pay/jsapi', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
out_trade_no: 'ORDER20240101120000',
total_amount: 100,
subject: '测试商品',
pay_method: 'alipay',
sub_openid: '20881234567890'
})
});
const result = await response.json();
// 2. 使用 transaction_id 或跳转支付
if (result.code === '00000') {
const transactionId = result.data.transaction_id;
// 根据业务需求处理
console.log('支付宝交易单号:', transactionId);
// 可使用 transaction_id 查询支付状态
}异步通知
通知地址
商户需在请求参数中提供 notify_url,支付完成后平台将向该地址发送异步通知。
通知参数
json
{
"service_id": "SVC1234567890",
"merchant_no": "1234567890",
"out_trade_no": "ORDER20240101120000",
"trade_no": "MF20240101120000001",
"total_amount": 100,
"pay_amount": 100,
"trade_state": "SUCCESS",
"pay_method": "wechat",
"pay_time": "20240101120000",
"notify_time": "20240101120001"
}处理流程
- 验证签名: 从 HTTP Header 中获取 Authorization,使用平台公钥验证通知签名
- 验证数据: 检查
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('https://api.meepay.org/notify', async (req, res) => {
try {
const notifyData = req.body;
const authorization = req.headers.authorization;
// 1. 验证签名(从 Authorization Header 中)
const isValid = verifySign(authorization, 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;支付流程
错误码
| 错误码 | 说明 | 处理建议 |
|---|---|---|
| 00000 | 成功 | - |
| 10001 | 参数错误 | 检查请求参数是否完整、格式是否正确 |
| 10002 | 签名验证失败 | 检查签名算法、密钥是否正确 |
| 10003 | 服务商号不存在 | 确认 service_id 是否正确 |
| 10004 | 商户未开通该支付方式 | 联系平台开通相应方式 |
| 10005 | 订单号重复 | 更换唯一的订单号 |
| 10006 | 订单金额错误 | 确认金额为正整数,单位:分 |
| 10007 | 时间戳过期 | 检查服务器时间是否同步 |
| 10008 | 支付方式错误 | 检查 pay_method 参数是否正确 |
| 10009 | sub_openid 无效 | 确认 sub_openid/sub_appid 是否正确 |
| 10010 | 系统繁忙 | 稍后重试或联系客服 |
注意事项
- 订单号唯一性:
out_trade_no在商户系统内必须唯一,重复订单号将返回错误 - 金额单位: 金额单位统一为分,避免浮点数精度问题
- 时间戳: 请求时间戳与服务器时间相差超过 5 分钟将被拒绝
- 签名安全: 私钥必须严格保密,不可在前端暴露
- 异步通知: 必须正确处理异步通知,不能仅依赖前端回调
- 幂等性: 同一订单号多次请求将返回相同结果,确保接口幂等
- HTTPS: 所有接口必须使用 HTTPS 协议
- 通知地址:
notify_url必须为公网可访问的 HTTPS 地址
常见问题
Q: 微信支付提示"签名错误"怎么办?
- 检查商户 API 密钥是否正确
- 确认签名算法是否为 RSA-SHA256
- 检查参数排序和拼接是否正确
Q: 支付宝支付无法调起?
- 检查是否正确提交表单
- 确认付款方 ID(payer_id)是否正确
- 检查支付宝公钥是否配置正确
Q: 异步通知收不到怎么办?
- 确认
notify_url是否为公网可访问地址 - 检查服务器防火墙是否拦截了请求
- 查看服务器日志,确认是否正常接收通知
- 可在商户平台查看通知发送记录
Q: 如何测试支付功能?
- 使用测试环境进行开发调试
- 微信支付可使用沙箱环境
- 支付宝可使用沙箱账号测试
- 测试完成后切换到生产环境