Appearance
二维码支付接口
接口说明
二维码支付接口用于生成支付二维码,支持微信支付和支付宝。商户通过接口传入订单信息,平台返回 qrpay_url,用户扫描二维码完成支付。
接口信息
- 接口地址:
POST /v1/pay/qrpay - 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(支付宝) |
| notify_url | String | 否 | 异步通知地址,必须为公网可访问的 HTTPS 地址 |
| time_expire | Integer | 否 | 订单过期时间(秒),从创建时开始计算,如 300 表示5分钟 |
请求示例
http
POST /v1/pay/qrpay
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",
"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.qrpay_url | String | 支付链接,商户用此生成二维码供用户扫码 |
响应示例
微信支付响应
json
{
"code": "00000",
"msg": "success",
"data": {
"out_trade_no": "ORDER20240101120000",
"pay_trade_no": "QR20240101120000001",
"pay_method": "wechat",
"qrpay_url": "https://pay.meepay.org/q/QR20240101120000001"
}
}支付宝响应
json
{
"code": "00000",
"msg": "success",
"data": {
"out_trade_no": "ORDER20240101120000",
"pay_trade_no": "ALI20240101120000001",
"pay_method": "alipay",
"qrpay_url": "https://pay.meepay.org/q/ALI20240101120000001"
}
}前端调用示例
生成二维码
javascript
// 1. 后端请求二维码支付接口
const response = await fetch('https://your-backend.meepay.org/api/pay/qrpay', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
out_trade_no: 'ORDER20240101120000',
total_amount: 100,
subject: '测试商品',
pay_method: 'wechat', // 或 'alipay'
notify_url: 'https://your-domain.meepay.org/api/notify'
})
});
const result = await response.json();
// 2. 使用 qrpay_url 生成二维码
if (result.code === '00000') {
const qrpayUrl = result.data.qrpay_url;
// 使用二维码库生成二维码
// 例如使用 qrcode.js
QRCode.toCanvas(document.getElementById('qrcode'), qrpayUrl, function (error) {
if (error) console.error(error);
console.log('二维码已生成!');
});
// 开始轮询支付状态
startPolling(result.data.pay_trade_no);
}
// 轮询支付状态
function startPolling(payTradeNo) {
const timer = setInterval(async () => {
const response = await fetch('https://your-backend.meepay.org/api/pay/tradeQuery', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
pay_trade_no: payTradeNo
})
});
const result = await response.json();
if (result.code === '00000' && result.data.trade_state === 'SUCCESS') {
clearInterval(timer);
alert('支付成功!');
window.location.href = 'https://your-domain.meepay.org/pay/success';
}
}, 2000); // 每2秒查询一次
}异步通知
通知地址
商户需在请求参数中提供 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 参数是否正确 |
| 10010 | 系统繁忙 | 稍后重试或联系客服 |
注意事项
- 订单号唯一性:
out_trade_no在商户系统内必须唯一,重复订单号将返回错误 - 金额单位: 金额单位统一为分,避免浮点数精度问题
- 时间戳: 请求时间戳与服务器时间相差超过 5 分钟将被拒绝
- 签名安全: 私钥必须严格保密,不可在前端暴露
- 异步通知: 必须正确处理异步通知,不能仅依赖前端轮询
- 幂等性: 同一订单号多次请求将返回相同结果,确保接口幂等
- HTTPS: 所有接口必须使用 HTTPS 协议
- 通知地址:
notify_url必须为公网可访问的 HTTPS 地址 - 二维码生成: 商户需在前端使用二维码库将
qrpay_url转换为二维码图片 - 状态轮询: 建议每2-3秒轮询一次支付状态,支付成功后停止轮询
常见问题
Q: 二维码支付和扫码支付有什么区别?
二维码支付是用户扫描商户生成的二维码完成支付,无需用户提供 sub_openid 或 sub_appid,适用于线下门店、PC网站等场景。
Q: 签名验证失败怎么办?
- 检查商户 API 密钥是否正确
- 确认签名算法是否为 RSA-SHA256
- 检查参数排序和拼接是否正确
- 确认时间戳是否与服务器时间同步
Q: 异步通知收不到怎么办?
- 确认
notify_url是否为公网可访问地址 - 检查服务器防火墙是否拦截了请求
- 查看服务器日志,确认是否正常接收通知
- 可在商户平台查看通知发送记录
Q: 如何测试支付功能?
- 使用测试环境进行开发调试
- 微信支付可使用沙箱环境
- 支付宝可使用沙箱账号测试
- 测试完成后切换到生产环境
Q: 二维码生成失败怎么办?
- 确认
qrpay_url是否正确返回 - 检查二维码库是否正确引入
- 确认 DOM 元素是否存在
- 查看浏览器控制台错误信息
Q: 轮询支付状态的最佳实践?
- 建议轮询间隔 2-3 秒
- 设置最大轮询次数(如 150 次,对应 5 分钟)
- 支付成功后立即停止轮询
- 订单超时后停止轮询并提示用户