微信支付 JSAPI 前端调起排查:验证签名失败、URL 未注册、permission value is offline verifying
这篇只讲一件事:prepay_id 已经拿到之后,前端把收银台调起来这一步出的问题。服务端下单、请求签名、回调验签不在这里展开。
适用范围:公众号 JSAPI(WeixinJSBridge)为主,小程序 wx.requestPayment 参数结构类似但报错文案不同。文中的命令行验证是本地做的字符串比对,真机全链路没有全部复现,涉及白名单配置的部分以官方文档为准。
官方文档
先分层:调起支付到底分几步
- 后端调 JSAPI 下单接口,拿到
prepay_id。该值有效期 2 小时,超期要重新下单。 - 前端用微信浏览器内置对象
WeixinJSBridge的getBrandWCPayRequest调起收银台。
第二步没有后端接口交互,但对参数要重新做一次签名(paySign),不能复用下单返回的 sign。绝大多数「验证签名失败」都出在这里:拿下单的签名直接塞给前端。
APIv3 调起支付的 6 个参数
全部是 string:
appId:填下单时传入的 appid,必须与当前实际调起支付的公众号 appid 一致,否则调不起来。注意这个参数名里是大写I。timeStamp:Unix 时间戳,秒级(10 位)。不少语言默认给毫秒(13 位),必须转成秒。nonceStr:随机字符串,不长于 32 位。package:格式prepay_id=***,取下单接口返回的prepay_id。signType:固定RSA。这个参数不参与签名,但必须传。paySign:用appId、timeStamp、nonceStr、package算出来的签名值。
签名串构造(4 行)
每一行一个参数,行尾以 \n(换行符,ASCII 0x0A)结束,最后一行也要。
appId\n
时间戳\n
随机字符串\n
prepay_id=xxx\n
待签名串示例:
wx8888888888888888
1414561699
5K8264ILTKCH16CQ2502SI8ZNMTM67VS
prepay_id=wx201410272009395522657a690389285100
用商户 API 证书私钥对待签名串做 SHA256 with RSA 签名,再 Base64 编码得到 paySign。命令行验证:
echo -n -e "wx8888888888888888\n1414561699\n5K8264ILTKCH16CQ2502SI8ZNMTM67VS\nprepay_id=xxx\n" | openssl dgst -sha256 -sign apiclient_key.pem | openssl base64 -A
签名用的是商户 API 证书私钥(apiclient_key.pem),不是 APIv3 密钥,也不是平台证书。这三样东西搞混,签名串再怎么拼都对不上。
前端调用:
WeixinJSBridge.invoke('getBrandWCPayRequest', {
"appId": "wx2421b1c4370ec43b",
"timeStamp": "1395712654",
"nonceStr": "e61463f8efa94090b1f366cccfbbb444",
"package": "prepay_id=wx21201855730335ac86f8c43d1889123400",
"signType": "RSA",
"paySign": "oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ..."
}, function(res){ if(res.err_msg=="get_brand_wcpay_request:ok"){ /* 仍需后端查单 */ } });
get_brand_wcpay_request:ok 只在用户完成支付时返回,但官方明确说它「并不保证绝对可靠」,必须以服务端查单结果为准。get_brand_wcpay_request:cancel 和 :fail 可以统一当「用户取消或出错」处理,不用细分。
常见报错与归因
支付验证签名失败
官方给的排查方向:
- 签名类型与下单时不一致(下单 HMAC-SHA256,调起却用了 MD5)。
- 生成签名原串的参数和实际发出去的参数不一致,比如时间戳、随机串两处取的不是同一个值。
- 调起时大小写写错(
appId的大写I写成小写i)。 - API 版本混用:下单走 V2,调起参考了 V3。
- 商户号不一致:下单用 A 商户号,调起签名用 B。
- key 用错:V2 用 APIv2 密钥,V3 用商户 API 证书私钥。
- 没有重新生成签名,直接复用了统一下单返回的
sign。
本地签名校验工具通过、真机仍报失败,常见原因就是「下单和调起不是同一套参数 / 商户号 / appid」。
chooseWXPay:fail, the permission value is offline verifying
- 在模拟器里发起,没在真机。
- 支付授权目录(V2)与真实发起支付的 URL 不匹配。例如页面是
http://www.xxx.com/order/pay/id-115,授权目录应配http://www.xxx.com/order/pay/。 - 授权目录规则:区分大小写、需 http/https 开头、以
/结尾、不能配 IP、最多 5 个。 - 网页授权域名 / JS 安全域名未配置或配错。
当前页面的URL未注册:http://xxx/pay.do(V2)
先看下单接口用的商户号有没有在商户平台配对应支付目录;服务商或子商户任一侧配了能匹配上的目录即可调起。
调用支付JSAPI缺少参数: appId / total_fee
- 检查调起参数里
appId有没有传。用wx.chooseWXPay时,appId依赖wx.config注入是否成功。 - 检查
prepay_id是否已失效(2 小时)。 - 检查请求的 appid 与下单接口的 appid 是否一致。
V2 和 V3 别混着参考
- V2:签名 MD5 / HMAC-SHA256,需要支付授权目录、网页授权域名、JS 接口安全域名。
- V3:
signType固定RSA,签名用商户 API 证书私钥做 SHA256withRSA,调起参数的重点是那四行签名串和 appid 一致性。
V3 下支付目录、域名白名单的具体要求,不同接入方式(服务商 / 直连)不一样,以官方文档为准。