uni-app 平台接入 · 微信支付配置说明

面向 uni-app 多端(微信小程序 / 微信内 H5 / 普通 H5 / App / PC 扫码)的微信支付配置保姆级说明:讲清为什么会有这么多 AppID 与 openid、五端配置矩阵、微信与商户后台操作步骤、常见报错排查。

版本:V1.0
更新日期:2026-09-14
对应基础库:reglow 0.4.5(Python / Node.js / Java / Rust 四语言一致)
适用场景:用 uni-app(含 uni-app x / uvue)开发的应用接入微信支付,一个后端要同时服务小程序、微信内 H5、普通 H5、App、PC 扫码多端。
读者假设:没接触过微信支付,或接过一次但被 AppID / openid 绕晕过。
确定性说明:文中微信后台的菜单名、报错提示语与错误码会随微信改版而变化,实际以微信官方后台与官方文档为准;代码、接口与配置项部分对本仓库(reglow 0.4.5)是确定的。


写在前面:一个大白话故事

┌──────────────────────────────────────────────────────────────┐
│                                                              │
│  微信支付就像一家大公司,进去办事要出示"工牌":                   │
│                                                              │
│   ① 公众号工牌   → 在【微信里的网页】办事用它                    │
│   ② 小程序工牌   → 在【小程序】里办事用它                        │
│   ③ 移动应用工牌 → 在【自家 App】里办事用它                      │
│                                                              │
│  而"钱"这件事,公司另有规矩:                                   │
│                                                              │
│   商户号          = 你的收款账户(钱进哪儿)                     │
│   三张工牌都要先"挂靠"到这个收款账户,才允许收钱                   │
│                                                              │
│  最坑的一点:同一个人在三个窗口的"客户编号"(openid)不一样!      │
│  所以你不能拿公众号的编号去小程序窗口办事 → 会被驳回。             │
│                                                              │
└──────────────────────────────────────────────────────────────┘

一句话总结:

复杂的是微信的规则,不是我们的代码。 好消息是:Reglow 基础库已经把这些规则吸收掉了 —— 业务侧只需要说清楚「我现在跑在哪个端」,剩下的 AppID 选择、接口选择、openid 换取,全部由库里自动处理。


目录

  1. 先搞懂:为什么会有这么多 AppID 和 openid
  2. 五端配置矩阵与对照表
  3. 微信 + 商户后台配置步骤(保姆级)
  4. 常见报错排查清单
  5. 联调验证:按这个顺序自测
  6. 附录 A:本仓库代码与接口速查
  7. 附录 B:十条铁律

一、先搞懂:为什么会有这么多 AppID 和 openid

1.1 三张工牌:三个互相独立的应用实体

微信生态里,公众号、小程序、移动应用是三个独立的应用,各自申请、各自审核、各自有一个 AppID。它们不是"同一个东西的三种形态"。

配置项对应实体申请入口我们用它做什么
wechat_app_id公众号(服务号)mp.weixin.qq.com微信内置浏览器里打开你的 H5 时,用公众号 JSAPI 拉起支付;以及网页授权换 openid
wechat_mini_app_id小程序mp.weixin.qq.com(小程序后台)小程序内拉起支付;wx.login → jscode2session 换 openid
wechat_app_app_id开放平台·移动应用open.weixin.qq.com自研 App 里拉起微信 App 支付(移动应用没有 AppSecret,App 支付也不需要 openid)
                    你的服务器(Reglow 后端)
                              │
              ┌───────────────┼───────────────┐
              │               │               │
         ① 小程序工牌     ② 公众号工牌     ③ 移动应用工牌
       wechat_mini_app_id  wechat_app_id   wechat_app_app_id
              │               │               │
              ▼               ▼               ▼
        小程序里支付     微信里打开H5支付    自家App里支付
      (JSAPI 接口)      (JSAPI 接口)      (APP 接口)

                    ↑ 三张工牌都必须先"挂靠"到 ↓
                          商户号 wechat_mch_id
                    (在【微信支付商户平台】完成关联)

不要试图只配一个 AppID 打通所有端。微信支付 V3 在下单时会校验 appid 与 payer.openid 的同源性,也会校验 AppID 是否已关联到该商户号;混用会被微信拒绝。

1.2 openid 是「用户 × 应用」的身份证

openid 不是用户的微信号,而是「某个微信用户,在某一个应用里的编号」。

同一个人的 openid:

场景值
在你的公众号里oXa...A1
在你的小程序里oYb...B2(和上面不一样)
在你的 App 里(走开放平台)App 支付不需要 openid

所以两种 openid 的获取方式、以及能用在哪种下单接口上,是绑定死的:

openid 来源获取方式用于哪种下单
小程序 openid小程序 wx.login() 拿 code → 后端调 sns/jscode2session(用小程序 AppID + 小程序 AppSecret)只能用小程序 AppID 下 JSAPI 单
公众号 openid用户在微信里打开 H5 → 静默网页授权(snsapi_base)→ 后端用 code 调 sns/oauth2/access_token(用公众号 AppID + 公众号 AppSecret)只能用公众号 AppID 下 JSAPI 单
App 支付——不需要 openid,用移动应用 AppID 下 APP 单

为什么小程序不需要网页授权,而微信内 H5 需要? 因为小程序内有 wx.login() 这个原生能力,可以直接拿到 code 交给后端换 openid;而浏览器页面没有这个能力,只能借道「网页授权」——先整页跳到微信、用户授权后带着 code 跳回来。这就是微信内 H5 比小程序「多一次跳转」的根本原因。

这正是「appid 和 openid 不匹配」这类报错的根本原因。 库内部已经按「端」自动挑对了 AppID(见第二章矩阵表),业务代码不需要手动传 AppID。

想要打通同一个用户? 只有把公众号、小程序、移动应用都绑定到同一个微信开放平台账号下,才会多出一个跨应用共享的 unionid。但注意:

unionid 只解决「这是同一个人」,不解决「用哪个 AppID 下单」。下单仍然必须用对应端自己的 AppID。

1.3 商户号与证书:收钱的那一端

AppID 解决「谁在调用」,商户号解决「钱进哪里」。

配置项是什么从哪里拿
wechat_mch_id微信支付商户号(收款主体)微信支付商户平台 → 账户中心 → 商户信息
wechat_api_v3_keyAPIv3 密钥,用于解密回调数据商户平台 → 账户中心 → API 安全 → APIv3 密钥(自己设置,务必留存)
wechat_serial_no商户 API 证书序列号商户平台 → 账户中心 → API 安全 → API 证书(下载证书后可见)
wechat_private_key商户私钥(apiclient_key.pem 的内容)同上,随证书一起下载
wechat_notify_url支付回调地址(微信支付成功后通知你的服务器)你自己的公网 HTTPS 地址,如 https://api.example.com/api/v1/payment/wechat/notify

1.4 那 11 个配置项,到底谁在用

后端的支付配置分组是 payment,在后台「运营 → 系统设置 → 支付设置」里维护。逐项的"用途 / 什么时候必须填"如下:

配置项(key)用途谁来消费
wechat_enabled是否启用微信支付渠道库:选择当前生效渠道
wechat_app_id公众号 AppID微信内 H5(JSAPI)、网页授权
wechat_app_secret公众号 AppSecret网页授权 code 换 openid
wechat_mini_app_id小程序 AppID小程序支付;也用于生成小程序码
wechat_mini_app_secret小程序 AppSecretjscode2session 换 openid、取 access_token 生成小程序码
wechat_app_app_id开放平台移动应用 AppIDApp 端微信支付
wechat_mch_id商户号各端通用(下单必填)
wechat_serial_no商户证书序列号各端通用(请求签名)
wechat_private_key商户私钥各端通用(请求签名)
wechat_api_v3_keyAPIv3 密钥各端通用(回调验签解密)
wechat_notify_url回调地址各端通用(可选,但强烈建议填:接收支付结果)

只做某几端时,未用到的项可以不填。 下表里的「基础四项」指 wechat_mch_id、wechat_serial_no、wechat_private_key(下单签名必需)、wechat_api_v3_key(回调验签解密必需):

只做这个端必须填可以不填
PC 扫码(web)基础四项 + 公众号 AppID两个 AppSecret、wechat_app_app_id
微信小程序(miniapp)基础四项 + wechat_mini_app_id + wechat_mini_app_secretwechat_app_id / wechat_app_secret、wechat_app_app_id
微信内 H5(mpweb)基础四项 + wechat_app_id + wechat_app_secretwechat_mini_app_id / wechat_mini_app_secret、wechat_app_app_id
普通浏览器 H5(h5)基础四项 + wechat_app_id两个 AppSecret、wechat_app_app_id
App(app)基础四项 + wechat_app_app_idwechat_app_id、两个 AppSecret
  • wechat_notify_url 强烈建议填:不填微信无法回调,支付结果只能靠前端轮询兜底,线上会延迟入账甚至漏入账。
  • wechat_enabled 要在后台打开(渠道开关)。
  • 多个端都要做时,取各端并集即可。

关于兜底:库对 miniapp 有「小程序 AppID 未配则回退公众号 AppID」的逻辑,对 web 扫码也有「公众号 AppID 未配则回退小程序 AppID」的逻辑。两处兜底互相独立,都只是防止初始化直接报错;一旦回退后 openid 与 AppID 不同源,微信会拒绝下单。所以生产环境请把用到的 AppID 全部配齐 —— 小程序端一定要填小程序 AppID 与 AppSecret。

1.5 名词速查(看不懂就回来查)

名词一句话解释
AppID一个微信应用的唯一标识(公众号 / 小程序 / 移动应用各有一个)
AppSecret该应用的密钥,只在服务端使用,用于换 openid、换 access_token
openid「某个用户 × 某个应用」的唯一编号;同一人在不同应用下 openid 不同
unionid同一用户在同一开放平台账号下多个应用间的共享编号(只解决「是同一人」,不解决「用哪个 AppID 下单」)
JSAPI微信支付的一种下单方式,用于小程序和微信内置浏览器里拉起支付面板
prepay_id微信下单成功后返回的「预支付交易会话标识」,前端拉起支付必用
pay_params后端返回的「拉起支付所需参数集合」:小程序 / 公众号给 timeStamp nonceStr package signType paySign;App 给一组 orderInfo 字段
qr_codeNative 下单返回的二维码链接,PC 端渲染成二维码供扫码
h5_urlH5 下单返回的收银台地址,普通手机浏览器直接跳转过去
WeixinJSBridge微信客户端注入到内置浏览器里的桥,公众号 JSAPI 靠它拉起支付
access_token微信应用的「接口调用凭证」(微信侧有效期约 2 小时),库已做单点缓存
scene(场景值)生成小程序码时携带的短参数,扫码进入小程序后可读到(上限 32 字符)
ICP 备案中国大陆网站必须完成的域名备案;微信的网页授权域名、H5 支付域名都要求已备案
内网穿透把本地服务临时映射成公网地址,用于让微信服务器能回调到你的开发机
X-Forwarded-For反向代理透传「真实客户端 IP」的请求头;H5 支付下单必须带真实 IP
幂等同一操作执行多次与执行一次结果相同(支付回调重复触发时只入账一次)

二、五端配置矩阵与对照表

2.1 一张表看懂五端差异

业务侧只需要告诉后端「我现在跑在哪个端」——这个值就是 platform。AppID 由库按 platform 自动选,前端不许也不需要传 AppID。

端platform微信下单接口用哪个 AppID 下单需要 openid?前端调起方式后端返回
PC / 桌面浏览器web/v3/pay/transactions/native公众号 AppID(未配则回退小程序)否页面渲染二维码,轮询支付状态qr_code
微信小程序miniapp/v3/pay/transactions/jsapi小程序 AppID(必须配,见下方说明)需要(小程序 openid)uni.requestPayment({provider:'wxpay', ...})pay_params
微信内置浏览器(H5)mpweb/v3/pay/transactions/jsapi公众号 AppID需要(公众号 openid)WeixinJSBridge.invoke('getBrandWCPayRequest')pay_params
手机普通浏览器(H5)h5/v3/pay/transactions/h5公众号 AppID否(但要客户端 IP)跳转 h5_url,回跳后查单pay_params.h5_url
原生 App(iOS/Android)app/v3/pay/transactions/app开放平台移动应用 AppID不需要uni.requestPayment({provider:'wxpay', orderInfo})pay_params

三个必须知道的"反常点":

  1. 小程序和公众号用的是同一个微信接口(/v3/pay/transactions/jsapi),区别只在 appid 与 openid 的来源必须配对。
  2. 微信内置浏览器里不能用 H5 支付(platform=h5)。微信会拦截,提示「请到微信外打开」。微信内必须走 mpweb。
  3. App 支付不需要 openid,这是五端里最简单的一环。

2.2 前端怎么判定当前端

项目已封装好,不要在页面里自己写判断。见 common/utils/pay.uts:

import { resolvePayPlatform } from "../../common/utils/pay.uts"

const platform = resolvePayPlatform()
// 小程序 → "miniapp"
// App    → "app"
// H5(微信内)  → "mpweb"
// H5(普通浏览器)→ "h5"
// PC / 桌面浏览器 → "web"

判定规则:编译期按平台裁剪(MP-WEIXIN / APP-ANDROID / APP-IOS / WEB),H5 端再用 UA 判断是否微信内置浏览器(UA 含 MicroMessenger)。

2.3 前端标准调用流程

五端流程统一为三步,差异全部被工具函数吃掉了:

import {
  resolvePayPlatform, preparePayOpenid, needPayOpenid,
  invokeWechatPay, startPayPolling, stopPayPolling,
  savePendingPay, takePendingPay, PAY_WEB, PAY_H5
} from "../../common/utils/pay.uts"

const platform = resolvePayPlatform()

// ① 准备 openid(小程序走 wx.login;微信内 H5 走公众号网页授权;其它端返回空串)
preparePayOpenid(platform).then((openid : string) => {
  if (needPayOpenid(platform) && openid == "") {
    // 微信内 H5:首次会整页跳转公众号授权,回跳后再发起即可
    return
  }
  // ② 下单(后端接口:把 platform + openid 交给服务端,AppID 由服务端决定)
  //    payOrder(订单id, method, provider, platform, openid)
  //    method:支付方式,"wechat"(人民币)/ "points"(积分抵扣)
  //    provider:支付渠道,这里固定 "wechat"
  return payOrder(orderId, "wechat", "wechat", platform, openid)
}).then((data) => {
  // ③ 调起支付(五端差异在此收敛)
  if (platform == PAY_WEB) {
    // 渲染 data.qr_code 二维码 + startPayPolling(...) 轮询
    startPayPolling(data.payment_order_id, onPaid, onClosed)
  } else if (platform == PAY_H5) {
    savePendingPay(data.payment_order_id, orderId, orderNo) // 落盘,回跳后恢复
    invokeWechatPay(platform, data.pay_params)               // 跳转 h5_url
  } else {
    invokeWechatPay(platform, data.pay_params).then(onPaid)  // 小程序 / mpweb / App
  }
})

invokeWechatPay 内部对各端的处理:

platform内部动作
miniappuni.requestPayment({provider:'wxpay', timeStamp, nonceStr, package, signType, paySign})
mpwebWeixinJSBridge.invoke('getBrandWCPayRequest', {appId, timeStamp, nonceStr, package, signType, paySign})(桥未注入时会重试)
appuni.requestPayment({provider:'wxpay', orderInfo:{appid, partnerid, prepayid, package, noncestr, timestamp, sign}})
h5window.location.href = pay_params.h5_url
web不适用(请用二维码 + 轮询)

微信内 H5 首次支付需要点两次:第一次点击会跳转到微信授权页换取 openid,回跳后 openid 已缓存,再点一次即正常拉起支付。 授权在同一浏览器、且本地缓存未被清除的前提下只会发生一次(openid 存在本地 Key K_WX_OPENID);换设备、换浏览器或清缓存后会重新授权一次。

2.4 后端接口清单

接口方法用途公开
/api/v1/payment/ordersPOST创建支付订单(通用,含 platform / openid)是
/api/v1/payment/orders/{id}/statusGET查询支付单状态(前端轮询用)是
/api/v1/payment/wechat/notifyPOST微信支付结果回调(微信服务器调用)是
/api/v1/payment/wechat/oauth-urlGET取公众号网页授权跳转 URL是
/api/v1/payment/wechat/openidGET网页授权 code 换 openid是
/api/v1/wechat/miniapp/openidPOST小程序 code 换 openid(jscode2session)是
/api/v1/wechat/miniapp/qrcodePOST生成小程序码(邀请海报等用)是
/api/v1/orders/{id}/payPOST业务侧:为打印订单发起支付需登录
/api/v1/credits/rechargePOST业务侧:积分充值下单需登录

三、微信 + 商户后台配置步骤(保姆级)

所有后台菜单名以微信官方后台实际显示为准(微信会改版)。下面给的是大致路径 + 你要得到什么。 建议按 3.6 的推荐顺序操作,能显著减少来回返工。

3.1 商户平台(微信支付)——先把"收钱的地方"建好

打开 pay.weixin.qq.com,用商户号登录。

步骤菜单路径(大致)你要拿到 / 填什么
1账户中心 → 商户信息复制 商户号 → 后台 wechat_mch_id
2账户中心 → API 安全 → APIv3 密钥自己设置一个 32 位密钥并妥善保存 → 后台 wechat_api_v3_key
3账户中心 → API 安全 → API 证书申请/下载证书 → 得到 证书序列号 与 apiclient_key.pem 内容 → 后台 wechat_serial_no / wechat_private_key
4产品中心 → 开通产品按需开通:JSAPI 支付(小程序/微信内 H5)、H5 支付(普通浏览器)、Native 支付(PC 扫码)、APP 支付
5产品中心 → AppID 账号管理把 公众号 AppID / 小程序 AppID / 移动应用 AppID 逐个「关联」到本商户号
6产品中心 → 开发配置配置 JSAPI 支付授权目录(微信内 H5 页面所在目录)、H5 支付域名(需 ICP 备案)
7(自定义)回调地址无需在商户平台预先登记:V3 接口的回调地址随下单请求传递(即后端填的 wechat_notify_url)

第 5 步是「appid 和 mch_id 不匹配」最直接的解法:AppID 没关联商户号,下单会失败。 第 3 步的私钥与序列号必须成对:序列号对不上私钥,会报签名错误。

3.2 公众号(负责"微信内 H5")

打开 mp.weixin.qq.com,用公众号管理员登录。

步骤菜单路径(大致)你要拿到 / 填什么
1设置与开发 → 基本配置复制 AppID → 后台 wechat_app_id;生成/重置 AppSecret → 后台 wechat_app_secret
2设置与开发 → 公众号设置 → 功能设置 → 网页授权域名填你的 H5 站点域名(如 h5.example.com),并按要求把校验文件放到域名根目录
3设置与开发 → 公众号设置 → 功能设置 → JS 接口安全域名(可选)只有使用微信 JS-SDK 时才需要填;本项目用 WeixinJSBridge 拉起支付,不依赖此项
4设置与开发 → 公众号设置 → 功能设置 → 确认「接口权限」网页授权能力通常要求已认证的服务号(订阅号一般没有);以该页实际显示的权限为准

第 2 步是「redirect_uri 域名与后台配置不一致(10003)」最直接的解法。 注意:网页授权域名只填域名,不带路径、不带端口,且必须是已备案的 HTTPS 域名。

3.3 小程序(负责"小程序内支付")

打开小程序后台(mp.weixin.qq.com,切换到小程序)。

步骤菜单路径(大致)你要拿到 / 填什么
1开发 → 开发管理 → 开发设置复制 AppID → 后台 wechat_mini_app_id;生成 AppSecret → 后台 wechat_mini_app_secret
2开发 → 开发管理 → 开发设置 → 服务器域名request 合法域名填后端 API 域名;downloadFile 合法域名填图片 CDN 域名(生成/展示小程序码、海报时要用)
3设置 → 基本设置确认小程序类目允许支付;小程序需已完成认证
4——小程序支付不需要配置网页授权域名(那是公众号侧的事)

小程序 AppSecret 只在服务端使用(换 openid、换 access_token),绝不能写进前端代码。 manifest.json 里的 mp-weixin.appid 只需填 AppID(不是 AppSecret)。

3.4 开放平台·移动应用(负责"App 内支付")

打开 open.weixin.qq.com,用开放平台账号登录。

步骤菜单路径(大致)你要拿到 / 填什么
1管理中心 → 移动应用 → 创建/详情复制 AppID → 后台 wechat_app_app_id
2应用详情 → 开发信息Android:填包名 + 应用签名(用微信官方「签名生成工具」取,必须是正式签名包)
3应用详情 → 开发信息iOS:填 Bundle ID,并配置 Universal Links
4确认已开通在应用详情确认微信支付能力已开通(未开通需先申请)

包名/签名填错是 App 支付的常见失败原因,且报错信息往往很含糊。务必用正式打包的 APK 取签名。 iOS 还需在 manifest.json 里配置微信支付的 appid 与 Universal Links。

3.5 配置核对清单(可勾选)

商户平台

  • 商户号已复制到 wechat_mch_id
  • APIv3 密钥已设置并填入 wechat_api_v3_key
  • 证书已下载,序列号填入 wechat_serial_no,私钥内容填入 wechat_private_key
  • 已开通所需支付产品(JSAPI / H5 / Native / APP)
  • 公众号 / 小程序 / 移动应用 AppID 已逐一关联到本商户号
  • JSAPI 支付授权目录已配置(微信内 H5 用)
  • H5 支付域名已配置(普通浏览器 H5 用,域名需备案)

公众号

  • wechat_app_id / wechat_app_secret 已填
  • 网页授权域名已配置(H5 站点域名)
  • 公众号为服务号且已认证

小程序

  • wechat_mini_app_id / wechat_mini_app_secret 已填
  • request / downloadFile 合法域名已配置
  • manifest.json 中小程序 appid 已填

移动应用

  • wechat_app_app_id 已填
  • Android 包名 + 签名 / iOS Bundle ID + Universal Links 已填

服务器

  • wechat_notify_url 为公网可访问的 HTTPS 地址
  • H5 站点为 HTTPS + 已备案域名
  • 反向代理已正确透传 X-Forwarded-For(H5 支付要用真实客户端 IP)
  • 后端已重启,加载最新库版本

3.6 推荐的配置顺序

① 商户平台:拿商户号 + 设置 APIv3 密钥 + 下载证书
        ↓
② 公众号 / 小程序 / 开放平台:拿到三个 AppID 与两个 AppSecret
        ↓
③ 商户平台:把三个 AppID 关联到商户号 + 开通支付产品
        ↓
④ 商户平台:配置 JSAPI 支付授权目录 + H5 支付域名
        ↓
⑤ 公众号后台:配置网页授权域名(放校验文件)
        ↓
⑥ 小程序后台:配置 request / downloadFile 合法域名
        ↓
⑦ 后台「支付设置」:把**本端用得到的项**一次填完并保存(对照 1.4 的按端最小配置表)
        ↓
⑧ 重启后端 → 按第五章顺序联调

顺序反了会怎样:先配前端、后关联 AppID,你会一直以为是代码问题,其实是商户号没关联。


四、常见报错排查清单

4.1 下单阶段

报错关键词(微信返回)根因处置
appid和openid不匹配下单用的 AppID 与 openid 来源不是同一个应用(如拿公众号 openid 用小程序 AppID 下单)确认前端 platform 传对了:小程序 miniapp、微信内 H5 mpweb。库 0.4.5 已按 platform 自动选 AppID
appid和mch_id不匹配 / APPID_MCHID_NOT_MATCH该 AppID 未关联到商户号商户平台 → 产品中心 → AppID 账号管理,关联对应 AppID
商户号该产品权限未开通 / NOAUTH未开通对应支付产品商户平台 → 产品中心,开通 JSAPI / H5 / Native / APP 支付
签名错误 / SIGN_ERROR私钥与证书序列号不配对,或 APIv3 密钥错重新核对 wechat_serial_no + wechat_private_key;确认没有多余换行/空格
微信小程序未配置缺 wechat_mini_app_id 或 wechat_mini_app_secret后台支付设置补齐这两项(小程序端必需)
金额相关报错微信金额单位是分,不是元后端传 amount_cents(¥15 → 1500)
小程序支付需要 openidplatform=miniapp 但没带 openid前端先调 preparePayOpenid("miniapp") 再下单

4.2 调起阶段

报错关键词根因处置
当前页面的URL未注册微信内 H5 的页面目录不在「JSAPI 支付授权目录」内商户平台 → 产品中心 → 开发配置,把 H5 页面所在目录加入授权目录(填目录,不是完整 URL;注意层级与末尾斜杠)
redirect_uri 域名与后台配置不一致(10003)网页授权域名未配置或不一致公众号后台配置「网页授权域名」= H5 站点域名;前端会自动带授权域名校验
请在微信客户端打开链接在非微信浏览器里尝试公众号 JSAPI 支付普通浏览器应走 h5(H5 支付),不该走 mpweb
请到微信外打开在微信内浏览器里用了 H5 支付(h5)微信内必须用 mpweb(JSAPI)。库已按 UA 自动区分
微信支付未就绪,请在微信中重新打开本页WeixinJSBridge 未注入(页面刚加载就被调用,或不在微信内)正常会自动重试 10 次;若持续出现,确认确实在微信内打开
支付时弹出空白页 / 白屏网页授权整页跳转被拦截,或 redirect_uri 域名未备案使用 HTTPS + 已备案域名;确认授权域名与访问域名一致
total_fee / 金额校验失败前后端金额不一致(尤其积分抵扣场景)金额一律由后端计算,前端只展示

4.3 回调与到账阶段

现象根因处置
微信支付成功,但订单仍是「待支付」回调地址不可达,或验签/解密失败确认 wechat_notify_url 公网 HTTPS 可访问;确认 wechat_api_v3_key 正确;查看后端日志
回调重复触发微信在未收到成功响应时会重试库已做幂等(同一支付单只入账一次),无需额外处理
微信返回「商户回调地址不是 HTTPS」回调地址用了 http换成 HTTPS
支付成功但页面没跳成功态前端未做状态确认小程序/App 在 requestPayment 成功后要调业务「确认支付」接口;H5 回跳后由 resumePendingPay 自动确认

4.4 小程序码 / access_token

现象根因处置
获取微信 access_token 失败 40001/40014token 被其它程序刷新顶掉了库已做单点缓存(按 appid 加锁),不要在任何业务代码里自行获取 token
生成小程序码失败 invalid ip小程序后台开启了 IP 白名单小程序后台 → 开发管理 → 开发设置 → IP 白名单,加入服务器公网出口 IP
小程序码图片为空场景值超长scene 上限 32 字符,库会自动截断

五、联调验证:按这个顺序自测

按「从简单到复杂」的顺序,每步都能独立验证,避免多端问题混在一起排查。

顺序验证端怎么测通过标准
1PC 扫码(web)后台填好支付设置 → 后端重启 → PC 打开下单页 → 选微信支付页面出现二维码;用手机微信扫码支付成功;PC 页面状态变为已支付
2小程序(miniapp)微信开发者工具打开小程序 → 下单 → 选微信支付直接弹出微信支付面板(不跳授权页);支付后订单变已支付
3App(app)真机装上正式签名包 → 下单 → 选微信支付拉起微信 App 完成支付,回到 App 显示成功
4微信内 H5(mpweb)微信里打开 H5 站点 → 下单 → 选微信支付(第 1 次)跳转微信授权页并自动回跳;提示「微信授权成功,请重新发起支付」
5同上(第 2 次)再点一次支付直接弹出微信支付面板,不再跳授权页
6普通浏览器 H5(h5)手机自带浏览器(非微信)打开 H5 → 下单跳转到微信 H5 收银台完成支付;返回 H5 后自动确认订单状态

第 1 步不通,先别测其它端。扫码链路最短,建议先用它暴露商户号、证书、回调地址这类配置问题。

本地开发时,微信服务器无法回调 localhost:请用内网穿透把本地端口暴露成公网 HTTPS,再把 wechat_notify_url 指向它。


附录 A:本仓库代码与接口速查

A.1 前端(uni-app / uvue)

文件作用
common/utils/pay.uts微信支付多端统一封装:端判定、openid 准备、调起支付、支付单轮询、H5 回跳恢复
common/api/payment.uts支付相关 API:fetchWechatMiniappOpenid、fetchWechatOauthUrl、fetchWechatOpenidByCode、fetchPaymentOrderStatus、fetchMiniappQrcode
common/config.uts存储 Key:K_WX_OPENID(公众号 openid 缓存)、K_PENDING_PAY(H5 回跳恢复用)
env.d.tsWeixinJSBridge 全局声明(微信客户端注入的公众号 JSAPI 桥)

common/utils/pay.uts 对外导出:

导出说明
PAY_MINIAPP / PAY_MPWEB / PAY_H5 / PAY_APP / PAY_WEBplatform 常量,避免手写字符串
resolvePayPlatform()当前端 → platform
isWechatBrowser()是否微信内置浏览器(仅 WEB 有意义)
needPayOpenid(platform)该端是否需要先准备 openid
preparePayOpenid(platform)准备 openid(小程序 wx.login / 微信内 H5 网页授权)
invokeWechatPay(platform, params)调起支付(五端差异在此收敛)
startPayPolling(id, onPaid, onClosed) / stopPayPolling()支付单轮询(扫码、H5 用)
savePendingPay() / takePendingPay()H5 跳转支付回跳后的恢复
handleWechatOauthCallback(query)在页面 onLoad 调用,处理公众号授权回跳

A.2 后端(Reglow 基础库)

位置作用
reglow/core/payment.py(Python)微信支付 Provider:Native / JSAPI / H5 / APP 下单、签名、回调验签
reglow/core/wechat.py(Python)微信基础能力:jscode2session、get_miniapp_access_token(单点缓存)、get_miniapp_qrcode
reglow/modules/wechat/公开接口:POST /wechat/miniapp/openid、POST /wechat/miniapp/qrcode
reglow/modules/payment/支付单:创建订单、查状态、回调

Node.js / Java / Rust 版库提供了同名同语义的能力,接口路径与 platform 取值完全一致,四语言可互换。


附录 B:十条铁律

  1. 三张工牌各管一摊,不要试图用一个 AppID 打通所有端。
  2. openid 与 AppID 必须同源,且三张工牌都要先在商户平台关联。
  3. AppID 与 AppSecret 只在服务端使用,AppSecret 绝不进前端代码。
  4. AppID 不需要前端传,前端只传 platform,选哪个 AppID 是后端的事。
  5. 微信内浏览器只能用 JSAPI(mpweb),H5 支付(h5)只在微信外的浏览器可用。
  6. 金额单位是分,且金额一律由后端计算。
  7. 回调地址必须公网 HTTPS,本地开发用内网穿透。
  8. access_token 全库单点缓存,业务代码不要自己获取。
  9. 先配商务后台,再配代码;很多"代码问题"其实是 AppID 没关联。
  10. 按 PC 扫码 → 小程序 → App → 微信内 H5 → 普通 H5 的顺序联调,一次只排查一个变量。