编程 支付调起失败别先查签名:微信 JSAPI / 小程序 / 支付宝手机网站支付的五层排查

2026-10-24 00:03:48

支付调起失败别先查签名:微信 JSAPI / 小程序 / 支付宝手机网站支付的五层排查

调起支付失败先别查签名:签名只解释一类错误,下面按环境层→配置层→参数层→场景层→前端回调语义拆报错;微信 paySign 的签名串构造见站内另一篇《微信 paySign 两种构造与 8 条排查顺序》。

资料:

高频报错与所属层对照:

报错/现象层先查什么
该商户暂不支持通过外部拉起微信完成支付 / 为保障支付安全,暂不支持从外部进入微信网页并完成支付环境是否在微信内置浏览器;外部浏览器改 H5(trade_type=MWEB)
chooseWXPay:fail, the permission value is offline verifying环境换真机;支付授权目录是否为实际发起页 URL
system:access_denied环境是否小程序 web-view;调起页 URL 是否过长
WeixinJSBridge.invoke is not a function环境小游戏需用小游戏虚拟支付
当前页面的URL未注册:http://xxx/pay.do配置下单商户号的支付授权目录
调用支付JSAPI缺少参数: appId配置wx.config 是否成功
调用支付JSAPI缺少参数:total_fee参数package 是否为 "prepay_id=xxx"、timeStamp 是否为字符串等
商户传入的APPID参数不正确参数下单/调起 appid 是否一致、prepay_id 是否过期
下单账号与支付账号不一致,请核实后再支付参数openid 是否实时获取、是否跨用户复用
支付场景非法场景下单接口与调起方式是否成对
当前商户该支付方式存在异常,暂时无法支付场景是否拿别的下单接口的 prepay_id 去小程序调起

第一层:环境层(浏览器/容器不对,改代码没用)

微信 JSAPI 只能在微信内置浏览器里调起。外部浏览器会报:

  • 「该商户暂不支持通过外部拉起微信完成支付」
  • 「为保障支付安全,暂不支持从外部进入微信网页并完成支付」

外部浏览器要用 H5 支付(trade_type=MWEB,订单默认有效期 5 分钟),JSAPI 的 prepay_id 在外部浏览器无解。

判断是否在微信内:

const isWeChat = typeof navigator !== 'undefined' && navigator.userAgent.includes('MicroMessenger')

其它环境类报错:

  • chooseWXPay:fail, the permission value is offline verifying:多在模拟器发起,或支付授权目录填的成分不是实际发起页面的 URL。用真机测。
  • system:access_denied:小程序内嵌 web-view 不支持发 JSAPI 支付;也可能是调起页 URL 过长导致 webview 鉴权异常。
  • WeixinJSBridge.invoke is not a function:小游戏不支持 JSAPI 支付,要用小游戏虚拟支付。
  • 小程序 webview 内嵌网页不支持 JSAPI 支付,官方明确不支持。

第二层:配置层(商户平台/公众号后台的开关)

「当前页面的URL未注册:http://xxx/pay.do」= 下单用的商户号没配置匹配的支付目录。规则:

  • 最多 5 个授权目录;需要覆盖多级目录时直接配父级
  • 严格区分大小写,必须带 http/https 头、以 / 结尾
  • 不能配 IP(如 https://8.8.8.8/test)
  • 授权目录指「付款页面所在目录」,不是后端接口地址
  • 设置后一般 5 分钟内生效
  • 服务商模式下,服务商或子商户任一配置了匹配目录即可调起(这也解释了「子商户没配却能成功」)

公众号侧还有几条:

  • 错误码 10003「redirect_url 域名与后台配置不一致」:获取 openid 的接口报的,检查下单 appid 与取 openid 的 appid 是否同一个、授权域名是否一致(公众平台→设置→公众号设置→功能设置→网页授权域名)
  • 错误码 10005「此公众号并没有这些scope的权限」:订阅号/未认证公众号调认证服务号的能力,或 AppID 填错、认证过期;用 snsapi_userinfo 授权可绕开一部分
  • wx.chooseWXPay 的「调用支付JSAPI缺少参数: appId」:该方法 appId 依赖 wx.config 注入,先查 config 是否成功

「商家暂时没有此类交易权限」:商户号没有 jsapi 支付权限(商户平台→产品中心),或下单没指定支付用户身份。

第三层:参数层(形态错,肉眼像对的)

「调用支付JSAPI缺少参数:total_fee」是最容易误判的一条——total_fee 是 v2 的参数名,v3 报这个错通常意味着调起参数形态不对。逐项查:

  1. package 必须是字符串 "prepay_id=xxx",不是裸 prepay_id,也不能为空
  2. prepay_id 是否失效(默认 2 小时)
  3. 下单金额是否为空、订单号是否重复或为空(会导致 prepay_id 为空)
  4. 下单 appid 与调起 appid 是否一致
  5. 金额单位是分,不能有小数点
  6. timeStamp 必须是字符串。社区里有案例:后端已返回 string,前端又 toString() 一次,结果报 total_fee 缺失——排查时先打印实际传给 requestPayment 的对象
  7. 「商户传入的APPID参数不正确」:统一下单的 appid 与调起时的 appid 不一致,或 prepay_id 已过期;也可能是参数带空格、大小写问题

「下单账号与支付账号不一致,请核实后再支付」:下单传的 openid 与调起支付的人不是同一个,openid 必须实时获取,不要缓存跨用户复用。

第四层:场景层(下单接口与调起方式必须成对)

「支付场景非法」= 下单与调起场景不匹配。对应关系:

调起方式下单接口/参数
公众号 JSAPIJSAPI 下单,trade_type=JSAPI
小程序JSAPI/小程序下单,且 prepay_id 只能用于小程序调起
APPtrade_type=APP
扫码 Nativetrade_type=NATIVE
H5trade_type=MWEB

用别的下单接口拿到的 prepay_id 去小程序调起,会报「当前商户该支付方式存在异常,暂时无法支付」。

第五层:前端回调语义——不等于订单状态

各端「用户取消」的返回值不一样,别写成统一判断:

  • 小程序:requestPayment:fail cancel
  • JSAPI:get_brand_wcpay_request:cancel
  • App:errCode=-2

密码输错不是终态,用户还能继续输,前端无法判断。前端 success/fail 都不可靠(网络中断、用户切后台),订单状态只能以后端查单 + 异步通知为准。

支付宝侧:唤端是另一个问题

alipay.trade.wap.pay(手机网站支付 2.0)默认就会唤起支付宝客户端。老接口 alipay.wap.create.direct.pay.by.user 需要显式传 app_pay=Y。

常见「装了支付宝却唤不起来」:

  • 在商家 App 内嵌 webview 里走纯手机网站支付:主要是 alipays 协议不在该 webview 白名单,官方建议 App 内改用 App 支付,或用「手机网站支付转 Native 支付」;需自行让 webview 处理 alipays 协议
  • 浏览器不支持或用户禁止唤起:换浏览器或改设置
  • 沙箱环境要装沙箱版客户端(仅安卓),用生产客户端扫码会失败

电脑网站支付(pageExecute)没有同步报错:先用 GET 方式拿到 URL 手工打开验证,GET 通而 POST 表单失败就是表单构造问题;再检查 biz_content 是否合法 JSON(末尾多逗号是高频问题)、out_trade_no/total_amount/subject 是否齐全。

唤端失败率本身是产品层面的取舍:H5 唤起受网络和浏览器版本影响,成功率低于原生 SDK 唤端;如果业务强依赖 App 内支付,别把手机网站支付当作 App 支付的替代。

推荐文章

程序员茄子在线接单