跳到主要内容

公众号 JSAPI 网页支付集成指南

在 CloudBase 控制台创建「微信支付」集成后,系统会自动生成一个 HTTP 云函数(函数名形如 <集成名>-<随机串>,本文统称 pay-common)。本指南介绍如何基于该云函数在**微信公众号网页(H5 within WeChat)**端,使用 JSAPI 支付完成从下单到回调的完整支付闭环。

适用场景:用户在微信浏览器内打开的公众号 H5 页面发起支付(最常见的服务号支付)。 不适用:微信外浏览器(Chrome/Safari/系统浏览器)→ 应使用 H5 支付下单接口;PC 网页 → 应使用 Native(扫码)支付。

整体调用链路:

  • 正向请求:网页 → 业务后端获取微信网页授权 → CloudBase 云 API 网关(accessToken 鉴权)→ pay-common HTTP 云函数 → 微信支付 API
  • 异步回调:微信支付 → 集成中心(验签 + 解密)→ pay-common → 业务侧

架构总览

┌──────────────┐ ┌──────────────┐ ┌──────────────────┐
│ 公众号网页 │ │ 网页授权后端 │ │ CloudBase 云 API │
│ (微信浏览器)│───►│ code → openid│ │ 网关 │
│ │ └──────────────┘ │ accessToken 鉴权 │
│ fetch │ │ │ │
│ ──────────►│ Bearer │ │ HTTP invoke │
│ │ Token │ │ ────────────►│
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────┴───┐
│ │ │ pay-common (HTTP 云函数) │
│ │ │ SDK 自签 → 调用 /v3/pay/transactions/jsapi
│ │ └────────────┬─────────────────────────────┘
│ │ │ 统一下单
│ │ ▼
│ │ ┌──────────────────┐
│ │ │ 微信支付 API │
│ │ │ (JSAPI/小程序) │
│ │ └────────┬─────────┘
│ WeixinJSBridge.invoke │ 异步支付回调
│ ('getBrandWCPayRequest') ▼
│ │ ┌──────────────────┐
│ │ │ 集成中心 │
└──────────────┘ │ (验签 + 解密) │
└────────┬─────────┘
│ 明文转发

┌──────────────────┐
│ pay-common │
│ /wechatpay/order │
└──────────────────┘

JSAPI 与小程序支付的关键区别:

维度小程序支付公众号 JSAPI 网页支付
下单接口/v3/pay/transactions/jsapi/v3/pay/transactions/jsapi(同一个)
appid小程序 AppID公众号 AppID
openid 来源signInWithOpenId() 直接拿微信网页授权(OAuth2.0)code → openid
调起支付 APIwx.requestPaymentWeixinJSBridge.invoke('getBrandWCPayRequest', ...)
运行环境微信小程序容器微信内置浏览器(必须)

因此 pay-common 函数本身无差异,差异在「openid 获取」与「前端调起 API」两处。

职责分工:

角色职责
公众号网页通过微信网页授权拿 code → 业务后端换 openid;携带 Bearer Token 调用 pay-common;用 WeixinJSBridge.invoke 调起支付
业务后端(自备)持有公众号 AppSecret,调用 sns/oauth2/access_tokencodeopenid不能让 AppSecret 出现在前端
云 API 网关校验 Bearer {accessToken},将请求转发到 HTTP 云函数
pay-common 云函数处理下单、查单、退款;接收并处理回调
微信支付 API支付业务实际执行方
集成中心统一托管凭证;接收回调后完成验签与解密,并以明文转发至云函数

前置条件

要求
公众号服务号(订阅号不支持 JSAPI 支付)已通过微信认证
公众号 JSAPI 支付权限在公众平台「微信支付」中开通并通过审核
微信支付商户号已申请并在商户平台完成与上述公众号的绑定
公众号支付授权目录商户平台 → 产品中心 → JSAPI 支付 → 添加授权目录(必须与下单页面 URL 前缀一致,含末尾 /
公众号网页授权域名公众平台 → 设置与开发 → 公众号设置 → 功能设置 → 网页授权域名
商户超管权限用于下载 API 证书、设置 APIv3 密钥
业务后端(任选)用于换 openid 的轻量服务(自有 Node/Java/Go 服务,或一个独立的云函数)

第一步 · 准备商户凭证

需要获得 7 项凭证:

#字段说明
1appId公众号 AppID(不是小程序 AppID)
2merchantId10 位数字商户号
3apiV3Key32 字符 APIv3 密钥
4merchantSerialNumber40 位十六进制证书序列号
5privateKeyapiclient_key.pem 完整内容
6wxPayPublicKey微信支付公钥 PEM
7wxPayPublicKeyId公钥 ID(PUB_KEY_ID_...

⚠️ appId 必须是公众号 AppID,且该 AppID 已在商户平台「JSAPI 支付」产品下与商户号关联。详见小程序指南附录的 AppID 区别说明。


第二步 · 在 CloudBase 创建集成

与小程序支付指南完全相同。集成创建后,系统自动生成:

  • HTTP 云函数 pay-common
  • 回调基础域名 https://<集成标识>.integration-callback.tcloudbase.com
  • 支付回调路径 /wechatpay/order
  • 退款回调路径 /wechatpay/refund

完整支付回调 URL 需填入商户平台「产品中心 → 开发配置 → 支付通知 URL」。


第三步 · 了解云函数和环境变量

完全复用小程序指南所述源码结构与环境变量注入逻辑。pay-common 中/v3/pay/transactions/jsapi 下单的实现对小程序与公众号网页是同一份代码(路由 _action: wxpay_order)。

唯一区别:调用方传入的 payer.openid 必须是该公众号 AppID 下用户的 openid


第四步 · 接入公众号网页

pay-common 部署完成后,公众号网页的接入分四步:网页授权拿 openid → 调用 pay-common 下单 → JSBridge 调起支付 → 服务端处理回调。

4.1 配置公众号支付授权目录与网页授权域名

支付授权目录(必须):

路径:商户平台 → 产品中心 → JSAPI 支付 → 添加授权目录

填写调起支付的页面所在 URL 前缀,必须以 / 结尾。例如页面是 https://shop.example.com/pay/cashier.html,则授权目录填 https://shop.example.com/pay/

网页授权域名(必须):

路径:公众平台 → 设置与开发 → 公众号设置 → 功能设置 → 网页授权域名

4.2 微信网页授权拿 openid

公众号 JSAPI 支付要求 payer.openid,必须通过微信网页授权(OAuth2.0)获取。换 openid 这一步不能在前端做——会泄漏公众号 AppSecret。下面给两种实现方案:

推荐方案:使用微信公众号集成获取 openid

CloudBase 平台提供的「微信公众号」集成(offiaccount-common 云函数)已经把网页授权链路封装好,业务方无需自建后端、无需保管 AppSecret

详细接入步骤参见 公众号集成接入指南 · 网页授权篇

优势:AppSecret 留在 offiaccount-common 云函数环境变量中,不出集成中心;网页授权、用户资料拉取、token 续期等能力开箱即用。

备选方案:自建业务后端换 openid

如果业务侧已有独立后端服务,且不想引入公众号集成,可自行调用微信网页授权接口。重点:换 openid 必须在后端做,前端只做跳转和回调处理

1. 网页跳转:https://open.weixin.qq.com/connect/oauth2/authorize
?appid=<公众号AppID>
&redirect_uri=<URL编码后的业务回跳地址>
&response_type=code
&scope=snsapi_base ← 静默授权,仅拿 openid,无需用户确认
&state=<自定义>
#wechat_redirect

2. 微信回跳到 redirect_uri 时附带 ?code=xxx&state=xxx

3. **业务后端**用 code 调微信接口换 openid(不要在前端调,会泄漏 AppSecret):
GET https://api.weixin.qq.com/sns/oauth2/access_token
?appid=<公众号AppID>
&secret=<公众号AppSecret>
&code=<上一步的 code>
&grant_type=authorization_code

返回:{ access_token, openid, refresh_token, expires_in, scope, ... }

4. 业务后端把 openid 写入会话/cookie 或返回给前端

scope=snsapi_base 不需要用户确认弹窗,只要用户在公众号会话中打开,跳转一瞬间就拿到了 openid,体验最好。如果业务还需要昵称/头像等用户信息,用 scope=snsapi_userinfo,会多一个授权弹窗。

4.3 支付相关路由

JSAPI 网页支付与小程序支付走同一套路由,差异仅在客户端调起方式。常用路由:

_action类别说明
wxpay_order下单JSAPI / 小程序下单(本指南使用)
wxpay_query_order_by_out_trade_no查询按商户订单号查单
wxpay_query_order_by_transaction_id查询按微信订单号查单
wxpay_close_order关单关闭订单(10 分钟未支付建议主动关闭)
wxpay_refund退款申请退款
wxpay_refund_query退款查询退款

所有路由通过 body 中的 _action 字段区分,除回调路由外均需携带 Authorization: Bearer {access_token}

4.4 回调处理

完全复用小程序指南所述 services/orderService.js 中的钩子方法(handlerUnified / handlerUnifiedTrigger / handlerRefund / handlerRefundTrigger),实现幂等、金额校验、状态原子更新等原则。

JSAPI 支付与小程序支付回调结构 100% 一致——都是 event_type: TRANSACTION.SUCCESS,明文中拿到 out_trade_no / transaction_id / amount 等字段。业务侧无需为支付方式区分回调代码。

4.5 完整调用示例

适合原生 H5 自研工程。下面是从拿到 openid 到调起支付的端到端示例。

关键约束:

  • ENV_ID:云开发环境 ID
  • FN_NAME:集成创建后自动生成的 HTTP 云函数名(形如 <集成标识>-<随机串>
  • 必须在微信内置浏览器中打开页面,否则 WeixinJSBridge 不存在
  • 必须在 payer.openid 中传入公众号 AppID 下的 openid
  • accessToken 必须来自「认证登录」——匿名登录(signInAnonymously)拿到的 token 无权调用云函数,网关会直接返回 401。具体登录方式(邮箱、手机号、自定义登录等)请参考 CloudBase Web SDK · 认证登录
<!-- pay.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>公众号支付</title>
</head>
<body>
<button id="payBtn">微信支付 0.2 元</button>
<script src="https://cdn.jsdelivr.net/npm/@cloudbase/js-sdk/dist/cloudbase.full.js"></script>
<script>
const ENV_ID = 'your-env-id'
const FN_NAME = 'pay-common'

// 通过业务后端拿到的当前用户 openid(公众号 AppID 维度)
const OPENID = window.__USER_OPENID__ // 由业务后端模板渲染或 cookie 写入

// 1. 初始化 CloudBase Web SDK,拿 accessToken
const app = cloudbase.init({ env: ENV_ID })
const auth = app.auth({ persistence: 'local' })

async function getAccessToken() {
// ⚠️ 匿名登录(signInAnonymously)拿到的 accessToken 无法调用云函数(401)
// 必须使用「认证登录」。可选登录方式(邮箱、手机号、自定义登录等)参见:
// https://docs.cloudbase.net/api-reference/webv2/authentication#%E8%AE%A4%E8%AF%81%E7%99%BB%E5%BD%95
throw new Error('请按 CloudBase Web SDK 文档实现认证登录后返回 accessToken')
}

document.getElementById('payBtn').onclick = async () => {
const accessToken = await getAccessToken()
const out_trade_no = 'ORDER_' + Date.now()

// 2. 调用 pay-common 下单
const res = await fetch(
`https://${ENV_ID}.api.tcloudbasegateway.com/v1/functions/${FN_NAME}?webfn=true`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({
_action: 'wxpay_order',
description: '测试商品',
out_trade_no,
amount: { total: 20, currency: 'CNY' }, // 0.2 元
payer: { openid: OPENID },
}),
}
)
const body = await res.json()
if (body.code !== 0) {
alert(body.msg || '下单失败')
return
}
const p = body.data && body.data.data ? body.data.data : body.data
// p = { timeStamp, nonceStr, package, signType, paySign }

// 3. 调起 JSBridge 支付
callJsBridgePay(p, out_trade_no)
}

function callJsBridgePay(p, outTradeNo) {
function onBridgeReady() {
WeixinJSBridge.invoke(
'getBrandWCPayRequest',
{
appId: '<公众号AppID>', // 必须与下单时一致
timeStamp: p.timeStamp,
nonceStr: p.nonceStr,
package: p.package, // 'prepay_id=wx20...'
signType: p.signType || 'RSA',
paySign: p.paySign,
},
function (resp) {
if (resp.err_msg === 'get_brand_wcpay_request:ok') {
// 支付成功(前端仅用于 UI;最终以服务端回调为准)
// 建议 1-2 秒后调用 wxpay_query_order_by_out_trade_no 主动查单兜底
location.href = `/order/result?out_trade_no=${outTradeNo}`
} else if (resp.err_msg === 'get_brand_wcpay_request:cancel') {
alert('用户取消支付')
} else {
alert('支付失败:' + resp.err_msg)
}
}
)
}
if (typeof WeixinJSBridge === 'undefined') {
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false)
} else {
onBridgeReady()
}
}
</script>
</body>
</html>

实现要点:

  • WeixinJSBridge 仅在微信内置浏览器中存在,外部浏览器需切到 H5 支付方案(不在本指南范围)。
  • appId 字段在 getBrandWCPayRequest 入参中必须与下单时使用的公众号 AppID 完全一致,否则会报"appid 和 openid 不匹配"。
  • package 字段直接用接口返回值,请勿自行拼接 prepay_id=...
  • 最终支付状态以服务端回调为准,前端 ok 仅用于跳转结果页;服务端收到回调后再执行发货等副作用。

4.6 真机测试

JSAPI 支付的调试链路相比小程序更繁琐:

  • 必须在微信内置浏览器中访问页面,桌面浏览器无法调起 WeixinJSBridge
  • 微信开发者工具有「公众号网页调试」模式(菜单 → 公众号网页项目 → 微信网页调试),可模拟微信 UA,但仍无法发起真实支付——必须真机。
  • 真机访问页面前,确认:
    1. 域名已配在公众号「网页授权域名」白名单
    2. 页面 URL 前缀已在商户平台「JSAPI 支付授权目录」中(含末尾 /
    3. 用户公众号关注关系不影响(snsapi_base 不强制关注)

测试金额建议同小程序:使用 0.1–1 元,避免 0.01 元触发风控。


常见问题 FAQ

Q1:调起支付报 appid 和 openid 不匹配

最常见错误。getBrandWCPayRequest 入参中的 appId、下单时填入的 appId、以及换 openid 时使用的 AppID 必须三处一致且都是公众号 AppID

排查:

  1. 集成中心填写的是公众号 AppID,不是小程序 AppID
  2. 业务后端 OAuth2 用的也是同一个公众号 AppID + AppSecret
  3. 前端 getBrandWCPayRequest 入参中没硬编码错误的 appId

Q2:报 APPID_MCHID_NOT_MATCH 或商户不存在

公众号 AppID 未在商户平台与商户号完成关联。商户平台 → 产品中心 → JSAPI 支付 → 关联 AppID,提交申请,由公众号管理员审批后生效。

Q3:报当前 URL 不在支付授权目录

调起支付的页面 URL 不在「JSAPI 支付授权目录」白名单。商户平台 → 产品中心 → JSAPI 支付 → 添加授权目录,填该页面 URL 的前缀(含末尾 /)。生效需 5 分钟左右。

Q4:网页授权回跳后没拿到 code

redirect_uri 域名不在公众号「网页授权域名」白名单,或域名校验文件未上线。补完网页授权域名验证后重试。

Q5:WeixinJSBridge is not defined

页面在非微信内置浏览器中打开。JSAPI 支付只能在微信浏览器中跑,外部浏览器需走 H5 支付(/v3/pay/transactions/h5)。

Q6:模拟器/桌面 Chrome 修改 UA 伪装微信,能调起支付吗

不能。WeixinJSBridge 是微信原生注入的全局对象,UA 伪装无法注入它。开发期可用微信开发者工具「公众号网页项目」模式联调页面渲染与请求,但支付调起必须真机

Q7:是否需要 wx.config 或 JSSDK 鉴权

JSAPI 支付(getBrandWCPayRequest不需要 wx.config。只有需要调用拍照、定位、分享等微信 JSSDK 能力时才需要。

Q8:能否复用小程序支付的同一个集成

能,但要看你的 appId 配置。pay-common 集成里只有一个 appId 字段,填的是哪个就只能服务对应应用。如果要同时支持小程序与公众号 JSAPI,两种做法:

  1. 创建两个独立集成(一个填小程序 AppID、一个填公众号 AppID),分别服务两端
  2. 改 pay-common 代码,按调用方动态切换 appId,环境变量增加 jsapiAppId

附录 · 与小程序支付的差异速查

维度小程序公众号 JSAPI 网页
集成中心 appId小程序 AppID公众号 AppID
openid 来源signInWithOpenId()OAuth2.0 网页授权 + 业务后端换取
拉起支付 APIwx.requestPaymentWeixinJSBridge.invoke('getBrandWCPayRequest')
运行环境小程序容器微信内置浏览器
域名白名单小程序 request 合法域名公众号网页授权域名 + 支付授权目录
pay-common 路由_action: wxpay_order_action: wxpay_order(同一个)
回调结构TRANSACTION.SUCCESSTRANSACTION.SUCCESS(完全一致)

延伸阅读

主题链接
JSAPI 下单(小程序/公众号支付)https://pay.weixin.qq.com/doc/v3/merchant/4012791897
公众号网页授权https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
getBrandWCPayRequest APIhttps://pay.weixin.qq.com/doc/v3/merchant/4013080345
支付回调协议https://pay.weixin.qq.com/docs/merchant/apis/mini-program-payment/payment-notice.html
CloudBase Auth · Web SDKhttps://docs.cloudbase.net/api-reference/webv2/authentication