2、统一下单接口:接口地址与参数、签名算法详解、请求与响应示例、常见错误码
好,咱们进入正题。
统一下单接口,可以说是整个支付流程的「起手式」。你想想看,用户点击「立即支付」之后,第一步要干什么?就是告诉微信服务器:「嘿,我这有一笔订单,帮我生成一个预支付订单号。」这个接口就是干这个的。
2.1 接口地址与参数
先看接口地址。微信支付分两个版本,我习惯用 V3 版本,因为它更简洁,签名方式也更统一。
POST https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi
嗯,这里要注意,jsapi 是给小程序用的。如果是 APP 支付,换成 app;如果是 H5,换成 h5。别搞混了,我在项目中就见过有人把 JSAPI 的地址拿去调 APP 支付,结果返回一堆看不懂的错误码。
接下来是请求参数。我列个表,你对照着看,心里有个数。
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
appid |
string | 是 | 小程序 AppID |
mchid |
string | 是 | 商户号 |
description |
string | 是 | 商品描述,比如「测试商品-001」 |
out_trade_no |
string | 是 | 商户订单号,你自己生成的,要保证唯一 |
notify_url |
string | 是 | 支付结果回调地址,必须是 HTTPS |
amount |
object | 是 | 订单金额,包含 total 和 currency |
payer |
object | 是 | 支付者信息,小程序里传 openid |
重点提醒:out_trade_no 必须唯一。我曾经因为订单号生成逻辑有 bug,导致两个用户同时下单时生成了相同的订单号,结果第二个用户支付成功,第一个用户的订单却显示「已支付」—— 那叫一个乱。后来我加上了时间戳 + 随机数 + 用户ID 的组合,再也没出过问题。
2.2 签名算法详解
签名,说白了就是给请求加个「防伪标签」。微信收到你的请求后,会用同样的算法算一遍签名,如果对不上,直接拒绝。
V3 版本的签名算法是这样的:
- 构造签名串,格式是
HTTP请求方法\nURL\n时间戳\n随机串\n请求体\n - 用商户私钥对签名串进行 SHA256 签名
- 把签名结果放到请求头的
Authorization字段里
举个例子,假设你要 POST 一个请求:
POST /v3/pay/transactions/jsapi
Host: api.mch.weixin.qq.com
Authorization: WECHATPAY2-SHA256-RSA2048 mchid="1230000109",nonce_str="dontbeafraid",timestamp="1712345678",signature="xxxxxx"
签名串长这样:
POST
/v3/pay/transactions/jsapi
1712345678
dontbeafraid
{"appid":"wx123456","mchid":"1230000109","description":"测试商品","out_trade_no":"20250301120001","notify_url":"https://yourdomain.com/notify","amount":{"total":1,"currency":"CNY"},"payer":{"openid":"oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"}}
我的小技巧:签名串里的换行符是 \n,不是 \r\n。我刚开始写的时候,用 Postman 调试一直报签名错误,查了半天发现是换行符的问题。你写代码时最好用 StringBuilder 或者模板字符串,手动加 \n,别依赖框架的自动换行。
2.3 请求与响应示例
好,咱们直接看一个完整的请求示例。假设用户要支付 1 分钱(测试用):
{
"appid": "wx1234567890abcdef",
"mchid": "1230000109",
"description": "测试商品-1分钱",
"out_trade_no": "20250301120001",
"notify_url": "https://yourdomain.com/pay/notify",
"amount": {
"total": 1,
"currency": "CNY"
},
"payer": {
"openid": "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"
}
}
如果一切正常,微信会返回这样的响应:
{
"prepay_id": "wx201410272009395522657a690389285100"
}
这个 prepay_id 就是预支付订单号。你拿到它之后,才能去调小程序的 wx.requestPayment 接口。说白了,统一下单就是「先拿号,再付款」的过程。
注意:prepay_id 的有效期只有 2 小时。如果用户 2 小时内没支付,这个订单就过期了,需要重新下单。我见过有些同学把 prepay_id 存到数据库里,第二天还想用,结果当然是不行。
2.4 常见错误码
接口调不通怎么办?别慌,先看错误码。我整理了几个最常见的:
| 错误码 | 说明 | 解决方案 |
|---|---|---|
PARAM_ERROR |
参数格式错误 | 检查请求体 JSON 格式,特别是金额字段必须是整数 |
NO_AUTH |
商户无权限 | 检查商户号是否已开通支付权限,或者 API 证书是否配置正确 |
OUT_TRADE_NO_USED |
商户订单号重复 | 换个订单号,或者检查生成逻辑 |
APPID_MCHID_NOT_MATCH |
AppID 与商户号不匹配 | 确认小程序 AppID 和商户号是否在微信支付后台关联过 |
INVALID_REQUEST |
请求无效 | 最常见的原因是签名错误,重新检查签名算法 |
避坑指南:我曾经遇到一个 INVALID_REQUEST,查了整整一下午。最后发现是 notify_url 写成了 HTTP,而微信要求必须是 HTTPS。你想想看,就少了一个 's',折腾了四个小时。所以,遇到这个错误,先检查 URL 协议、签名、还有参数类型。
嗯,统一下单接口的核心内容就这些。说白了,就是三步:构造参数、签名、发请求。拿到 prepay_id 之后,下一步就是调起小程序支付了。咱们下一章接着聊。