微信支付 JSAPI 拉不起收银台:授权目录、10003、openid 不匹配怎么逐条查
JSAPI 支付用在「用户在微信内置浏览器打开的网页里发起支付」这个场景。它跟 Native(PC/线下扫码,不需要 openid)的关键差别是:JSAPI 下单必须带 openid,所以链路得从网页授权开始,报错也往往先出在授权环节,而不是支付环节。
链路长什么样
公众号网页授权拿 code:redirect 到
https://open.weixin.qq.com/connect/oauth2/authorize?appid=xxx&redirect_uri=xxx&response_type=code&scope=snsapi_userinfo&state=STATE&connect_redirect=1#wechat_redirect服务端拿 code 换 openid。
服务端调 JSAPI 下单接口,传
appid、mchid、description、out_trade_no、notify_url、amount.total(单位分,int)、payer.openid,拿prepay_id。普通商户:
POST https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi
服务商走/v3/pay/partner/transactions/jsapi,参数用sp_appid/sp_mchid/sp_openid或sub_前缀。服务端按签名规则算
paySign(RSA-SHA256,用商户私钥),把timeStamp、nonceStr、package=prepay_id=xxx、signType=RSA、paySign返回前端。前端调起:老方式是
WeixinJSBridge.invoke('getBrandWCPayRequest', {...}),新方式是 JSSDK 的wx.chooseWXPay(这个方法的appId依赖wx.config注入)。prepay_id有效期 2 小时,过期要用原下单参数重新下单拿新的。
两个容易混的签名:wx.config 的 signature(JS-SDK 用,ticket 算)和 getBrandWCPayRequest 的 paySign(支付用,商户私钥算)是两套东西,互不通用。另外生成签名时参数名是 timeStamp,前端 JS 里写 timestamp,大小写不同,别复制串了。
报「当前页面的URL未注册」:查支付授权目录
报错会带上实际页面地址,比如 当前页面的URL未注册:http://xxx/pay.do。这个 URL 所在目录,必须在下单所用商户号的支付授权目录里。
授权目录的定义是「商户最后拉起微信支付收银台的那个页面」所在目录,不是后台的支付接口地址。支付页是 https://www.weixin.com/123/456/abc.html,就配 https://www.weixin.com/123/456/。也见过服务商「统一下单用 A 商户号、授权目录却配到 B 商户号」的坑。
配置路径:商户平台 → 产品中心 → 开发配置。服务商可以给全体子商户配,也可以给单个子商户单独配。约 5 分钟生效。
规则记牢:一个商户号最多 5 个;区分大小写;必须以 http:// 或 https:// 开头;接已 ICP 备案域名;以 / 结尾(多级目录最后一级也要 / 结尾);不支持 IP,https://192.168.1.1/ 不行。
两种配法,取舍看页面分布:
- 只配到域名
https://www.weixin.com/:只校验协议 + 域名,不校验多级目录。多个页面同域名不同目录时用这种,能省配置个数。 - 配到最后一级目录
https://www.weixin.com/123/:协议 + 域名 + 多级目录全匹配,更严格。
常见错配,实际支付页是 https://www.weixin.com/123/456/abc.html:
- 配
https://www.Weixin.com/—— 错,域名大小写不一致 - 配
http://www.weixin.com/—— 错,协议不一致 - 配
https://www.weixin.com/abc/—— 错,目录不一致 - 配
https://www.weixin.com/123/—— 错,没配到最后一级 - 配
https://www.weixin.com/456/—— 错,丢中间目录 - 配
https://www.weixin.com/123或https://www.weixin.com—— 错,没以/结尾
服务商/子商户场景:子商户没配,但服务商自己配了匹配的目录,也能调起支付,只要其中一个匹配即可。
报 10003 redirect_uri 不一致:查网页授权域名
JSAPI 下单必传 openid,而 openid 只能用被「网页授权域名」加白过的域名去请求获取,否则取不到。
配置路径:公众平台 → 设置 → 公众号设置 → 功能设置 → 网页授权域名。
按现象逐条排查
JSAPI 调起支付报
当前页面的URL未注册:http://xxx/pay.do
→ 下单接口用的商户号没在商户平台配置对应的支付授权目录。报
调用支付JSAPI缺少参数: appId
→ 检查调起支付参数是否传了appId;如果用wx.chooseWXPay,它的appId依赖wx.config注入,回头确认wx.config是否注入成功。报
redirect_url域名与后台配置不一致,错误码:10003
→ 这是公众号获取 openid 接口报的错,查两点:① 下单接口传的 appid 与获取 openid 接口用的 appid 是否同一个(必须一致);② 该 appid 对应公众号后台配的「网页授权域名」是否和获取 openid 的域名一致。报
此公众号并没有这些scope的权限,错误码:10005
→ 常见于订阅号/未认证公众号调用认证服务号才有的功能;确认 AppID 是否认证过期或填错;可尝试snsapi_userinfo授权方式。下单返回
appid和mch_id不匹配,请检查后再试(APPID_MCHID_NOT_MATCH)
→ 下单传的 appid 需要和商户号绑定。服务商场景是sp_appid与服务商商户号绑定;子商户场景sub_appid需在服务商平台绑定。下单返回
appid与openid不匹配(OPENID_MISMATCH)或无效的openid
→ openid 不是在该 appid 下获取的,或 openid 不存在。sp_openid必须来自sp_appid,sub_openid必须来自sub_appid,不能混用。报
{errMsg: "chooseWXPay:fail, the permission value is offline verifying"}
→ 别在模拟器里发起支付,用真机;并把支付授权目录填成实际发起支付的页面 url。页面是http://www.newfms.com/order/pay/id-115,就填http://www.newfms.com/order/pay/。JS-SDK 报
config:fail 40048 invalid url domain
→ 公众号设置 → 功能设置 → JS接口安全域名 没配。JS-SDK 报
config:fail 63002 invalid signature
→ JS-SDK 签名问题,与支付授权目录无关,但要一起查。redirect_uri 参数错误
→ 网页授权域名没配或配错;授权回调页所在域名要加到网页授权域名里(带不带 http、带不带 www 以平台要求为准)。
排查顺序上,先看报错出自哪一环:下单接口返回的错,去查商户号/appid/openid 绑定;redirect 类错误和 10003/10005,去查公众号后台的网页授权域名和 appid 一致性;前端调起阶段的错,去查支付授权目录和 JS-SDK 域名。这三块配置在三个不同的后台位置,混着改只会浪费时间。
调起成功不等于到账
前端 getBrandWCPayRequest 的 success 回调只是客户端动作。真正确认以后端回调 + 主动查单为准,判断 trade_state == SUCCESS 且验签通过。
(未实测:上述为官方文档口径整理,具体以你所用商户类型/服务商模式的实际控制台为准。)
参考:
- 配置 JSAPI 支付授权目录:https://pay.weixin.qq.com/doc/v3/merchant/4013287088
- JSAPI 支付常见问题:https://pay.weixin.qq.com/doc/v3/partner/4013334850
- 网页授权(公众号):https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
- JSAPI 开发指引:https://pay.weixin.qq.com/docs/merchant/products/jsapi-payment/development.html