跳到主要内容

小程序虚拟支付集成指南

说明

小程序虚拟支付集成即将开放,敬请期待。

在 CloudBase 控制台「集成中心」创建小程序虚拟支付集成后,平台会自动生成一个 HTTP 云函数,封装好签名、查单与回调处理能力。本指南介绍如何完成从创建集成、配置微信消息推送到小程序端调起收银台的完整链路。

关于函数名的约定:集成中心生成的云函数名形如 <集成名>-<随机串>(例如 xpay-demo-rwmx67sc)。为方便讲解,本文统一称其为 xpay-common——你在自己的代码中替换为实际函数名即可。

虚拟支付面向虚拟商品(道具直购、代币支付、会员订阅)。它解决了 iOS 端的支付合规问题:底层把微信支付与 Apple IAP 打通,同一套 wx.requestVirtualPayment 在安卓与 iOS 上都能拉起收银台。

与微信支付集成的区别

实物商品请使用微信小程序支付集成指南;虚拟商品必须使用虚拟支付,否则 iOS 端会因违反应用商店规则而无法上架。

整体调用链路:

  • 正向请求:小程序 → wx.cloud.callHTTPFunction(平台自动鉴权 + 注入 openid)→ xpay-common HTTP 云函数 → 返回 signData + 双签名
  • 支付拉起:小程序拿到签名参数后,由客户端调用 wx.requestVirtualPayment 自行完成下单(服务端不参与下单)
  • 异步回调:微信消息推送 → 集成中心网关(验签 + 解密)→ xpay-common → 业务侧发货

架构总览​

职责分工:

角色职责
小程序通过 callHTTPFunction 获取签名参数,再调 wx.requestVirtualPayment 拉起收银台
xpay-common 云函数生成 signData 与双签名;提供查单接口;接收回调后完成幂等与发货
微信虚拟支付支付业务实际执行方(安卓走微信支付,iOS 走 Apple IAP)
集成中心托管凭证与环境变量;代收回调并完成验签、解密,以明文转发至云函数
业务侧维护道具清单(productId + 价格)、订单状态机、幂等发货与风控
服务端不参与下单

这是虚拟支付与普通微信支付最大的差异。普通支付由服务端调 V3 接口拿 prepay_id;虚拟支付由服务端只产出签名参数,真正的下单动作发生在客户端调用 wx.requestVirtualPayment 时。


与微信支付集成的差异​

维度微信支付(实物)虚拟支付
适用商品实物商品、线下服务道具直购、代币、会员订阅等虚拟商品
客户端 APIwx.requestPaymentwx.requestVirtualPayment
下单动作服务端调 V3 接口拿 prepay_id服务端本地构造 signData + 双签名,客户端自行拉起
服务端接口/v3/pay/transactions/*/xpay/*(米大师)
回调通道微信支付回调 + AES-GCM 解密小程序消息推送 + AES-256-CBC 解密
核心凭证商户号、APIv3 密钥、商户证书、支付公钥OfferID、AppKey、AppID / AppSecret
费率与结算约 0.6%,T+1安卓 1%,T+3;iOS 12%,约 45–60 天

前置条件​

项要求
小程序已通过微信认证,并已开通虚拟支付能力(类目与主体要求以微信公众平台为准)
虚拟支付商户信息已在 MP 后台「虚拟支付 → 基本配置」获取 OfferID 与 AppKey
道具已在 MP 后台创建并发布道具,取得 productId 与价格;发布后同步需数分钟至半小时
小程序基础库≥ 3.15.2(wx.cloud.callHTTPFunction 的前提条件)
微信开发者工具最新稳定版;支付需在真机验证
关于 iOS

iOS 端通过 Apple IAP 收款,最低金额为 1 元(100 分)。低于 1 元的道具档位在 iPhone 上必然失败;且 Apple 支付不支持沙箱环境,开发调试请在安卓端完成或使用现网小额道具。


第一步 · 准备虚拟支付凭证​

登录 微信公众平台,按以下各处取值。所有值将在第二步填入集成中心表单。

1.1 虚拟支付基本配置​

路径:MP 后台 → 虚拟支付 → 基本配置。

#凭证说明
1offerId虚拟支付商户号(米大师应用 ID)
2appKey支付签名密钥,用于计算 paySig,只放服务端

1.2 小程序基本信息​

路径:MP 后台 → 设置 → 基本设置。

#凭证说明
3appId小程序 AppID
4secret小程序 AppSecret,用于换取 sessionKey,只放服务端

1.3 消息推送配置(回调)​

路径:MP 后台 → 虚拟支付 → 基本配置 → 基础配置 → 发货推送配置。

#凭证说明
5token消息推送 Token,用于验签
6msgSecretEncodingAESKey(43 位),用于安全模式解密
说明

虚拟支付的发货推送复用了消息推送的验签 / 解密能力,但配置项挂在虚拟支付下,不是「开发管理 → 消息推送」,两处指向同一套 Token 与 EncodingAESKey。

采用集成中心托管回调(system 模式)时,这两项由平台注入并由网关使用,业务代码无需实现任何密码学逻辑。

1.4 凭证清单​

#集成中心字段 / 环境变量取值出处
1offerId1.1
2appKey1.1
3appId1.2
4secret1.2
5token1.3
6msgSecret1.3

第二步 · 在 CloudBase 创建集成​

2.1 进入集成中心​

路径:云开发平台 → 模板与集成 → 集成中心 → 小程序虚拟支付 → 创建集成。

2.2 填写集成信息​

控制台向导需填写以下内容:

字段说明
集成名称自定义,例如 xpay-demo。决定云函数名前缀与回调域名子域
环境集成资源所属的云开发环境
凭证1.4 中的 offerId / appKey / appId / secret
回调承接资源指定回调转发到哪个云函数;不绑定则推送无处可转
Token / EncodingAESKey1.3 中的 token / msgSecret,须与微信后台逐字符一致
加解密方式 / 数据格式明文 / 兼容 / 安全;XML / JSON,须与微信后台一致
事件订阅勾选需要接收的 xpay_* 事件

2.3 自动生成云函数资源​

提交后平台异步生成资源,约需 1–2 分钟:

资源说明
HTTP 云函数名称形如 <集成名>-<随机串>,例如 xpay-demo-rwmx67sc
环境变量凭证、回调地址等由平台注入,代码中不出现明文
回调基础域名形如 https://<集成标识>.integration-callback.tcloudbase.com
回调路由平台预置,不可修改路径
上线顺序

必须先创建集成、等资源就绪,再去微信后台提交推送配置。 微信在保存推送配置时会立即发起 GET 探测,若集成尚未就绪,探测必然失败。


第三步 · 配置微信后台消息推送​

  1. 进入 MP 后台 → 虚拟支付 → 基本配置 → 基础配置 → 发货推送配置。
  2. 填写集成中心提供的回调 URL。
  3. 填写 Token 与 EncodingAESKey(与集成中心表单完全一致)。
  4. 选择与集成中心一致的数据格式(JSON / XML)与加密方式(明文 / 兼容 / 安全模式)。
  5. 保存并确认 GET 探测通过(网关会原样返回 echostr)。

配置完成后可在集成中心查看回调状态,或通过云函数日志确认推送已到达 /xpay/callback。


第四步 · 了解 xpay-common 云函数​

创建集成时,平台已把表单中的凭证与回调配置作为环境变量注入云函数,运行时通过 process.env 读取。如需修改配置,进入「集成中心 → 对应集成 → 编辑」,保存后平台将自动重新部署(约 1–2 分钟)。

4.1 源码结构​

xpay-common/
├── index.js 云函数入口
├── app.js Express 装配(同时支持 JSON 与 XML)
├── scf_bootstrap SCF 自定义运行时启动脚本
├── cloudbaserc.json CloudBase 部署配置(含环境变量占位符)
├── config/config.js 读取 process.env
├── routes/xpay.js 路由表
├── controllers/xpayController.js 下单 / 查单 / 回调应答
├── services/
│ ├── xpayService.js 下单 + 事件分发 + 幂等
│ ├── signService.js 双签名(paySig + signature)
│ └── strategies/xpayStrategy.js /xpay/* 服务端接口
├── utils/
│ ├── wechatApi.js code2Session / access_token
│ ├── xml.js XML 推送解析
│ └── cloudbaseAuth.js 读取平台注入的 x-wx-openid / x-wx-appid
└── tests/ 单元测试

4.2 对外路由​

路由鉴权说明
POST /xpay/create_order上游网关(必须带 code)返回 payData 给 wx.requestVirtualPayment
POST /xpay/query_order上游网关(必须带 code + x-wx-openid)查单,推送丢失时兜底补发货
POST /xpay/callback网关代验签;自建回调时由函数自验签解密所有 xpay_* 事件统一入口
鉴权边界

云函数本身不挂鉴权中间件,信任前提是「只经 callHTTPFunction 或集成中心网关调用」。如直接暴露公网,请在上游(API 网关 / 云托管)自行做身份校验。

4.3 响应结构​

下单与查单接口返回统一结构:

{
"code": 0,
"msg": "success",
"data": {
"signData": "...",
"mode": "short_series_goods",
"paySig": "...",
"signature": "..."
}
}

code 为 0 表示成功;data 即调起收银台所需的参数。


第五步 · 小程序端接入​

5.1 初始化云开发​

// app.js
const ENV_ID = 'your-env-id' // ⚠️ 替换为你的云开发环境 ID
const FN_NAME = 'xpay-demo-xxxxxx' // ⚠️ 替换为集成中心生成的云函数名

App({
globalData: { envId: ENV_ID, fnName: FN_NAME },

onLaunch() {
wx.cloud.init({ env: ENV_ID, traceUser: true })
},
})

callHTTPFunction 无需开启 CloudBase Auth 身份源、无需安装 @cloudbase/js-sdk、无需构建 npm。

5.2 封装调用工具​

// utils/xpay.js
function callXpay(path, data) {
const { envId, fnName } = getApp().globalData

return new Promise((resolve, reject) => {
wx.cloud.callHTTPFunction({
name: fnName,
config: { env: envId },
method: 'POST',
header: { 'Content-Type': 'application/json' },
path, // 例如 /xpay/create_order
data,
success: (res) => {
// 响应体可能是字符串,也可能是已解析对象,统一处理
const body = typeof res.data === 'string' ? JSON.parse(res.data) : res.data
resolve(body)
},
fail: (err) => reject(new Error(err.errMsg || '请求失败')),
})
})
}

module.exports = { callXpay }

5.3 发起支付​

// pages/index/index.js
const { callXpay } = require('../../utils/xpay')

Page({
data: { paying: false },

async onTapPay() {
if (this.data.paying) return
this.setData({ paying: true })

try {
// 1. 取登录凭证 code
const { code } = await wx.login()

// 2. 服务端生成 signData + 双签名(不会真正下单)
const res = await callXpay('/xpay/create_order', {
code,
productId: 'product_001', // 道具 ID,需与 MP 后台一致
goodsPrice: 100, // 单位:分(100 = 1 元)
buyQuantity: 1,
})

if (res.code !== 0) {
wx.showToast({ title: res.msg || '下单失败', icon: 'error' })
return
}

// 3. 客户端拉起收银台(安卓走微信支付,iOS 走 Apple IAP)
const { signData, mode, paySig, signature } = res.data
await wx.requestVirtualPayment({ signData, mode, paySig, signature })

wx.showToast({ title: '支付成功', icon: 'success' })

// 4. 兜底查单(应对推送延迟,最终以服务端回调为准)
const q = await callXpay('/xpay/query_order', {
code,
outTradeNo: signData.outTradeNo,
})
if (q.code === 0 && q.data?.paid) {
// 已支付:若业务尚未收到回调,可在此触发补发货
}
} catch (err) {
if (err?.errMsg?.includes('cancel')) return // 用户取消不算失败
console.error('[xpay] 支付失败:', err)
wx.showToast({ title: '支付失败', icon: 'error' })
} finally {
this.setData({ paying: false })
}
},
})

实现要点:

  • signData 必须原样透传:任何 JSON.parse → stringify 导致键顺序变化,都会让签名失效。
  • 前端成功回调不能作为发货依据:用户异常退出、网络中断都会导致前端回调丢失,发货以服务端推送为准,查单仅作兜底。
  • goodsPrice 单位为分,且需与 MP 后台发布的道具价格一致。
  • 基础库版本:callHTTPFunction 要求 ≥ 3.15.2,模拟器无法完成真实支付,请用真机验证。

第六步 · 实现发货与订单逻辑​

模板中的 services/xpayService.js 已标注 TODO(业务侧),以下三处需要你自行实现:

位置模板现状生产环境必须替换
幂等校验进程内 Set(有容量上限)数据库唯一索引(冷启动会丢内存状态)
发货逻辑仅更新内存订单状态对接订单表、道具 / 代币余额
iOS 退款问询未发货即放过查持久化订单表 + 真实风控规则,否则已发货订单有资损风险

6.1 回调事件​

Event处理
xpay_goods_deliver_notify核心:幂等发货
xpay_coin_pay_notify更新代币余额
xpay_refund_notify更新订单 + 回收道具
xpay_complaint_notify记录,人工跟进
xpay_wxpay_callback_notify记录风控,按需申诉
xpay_subscribe_ios_refund_query_notify3 秒内应答退款决策

6.2 发货所需字段​

回调返回值中已带 goods,业务侧无需再解析推送结构:

{
"ok": true,
"eventType": "xpay_goods_deliver_notify",
"biz": "deliver",
"outTradeNo": "ORDER20261020001",
"wxOrderId": "WXORDER...",
"openid": "o_xxx",
"goods": {
"productId": "goods_001",
"quantity": 2,
"attach": "uid=123"
},
"goodsValid": true,
"missingFields": []
}

只拿到 outTradeNo 是发不了货的——必须知道「发哪件、发几个」,即 goods.productId 与 goods.quantity。

6.3 幂等与应答​

  • 幂等键使用平台单号 wxOrderId(推送中的 WeChatPayInfo.MchOrderNo);缺失时回退商家单号 outTradeNo。用商家单号去重,会在「用户取消后重新下单复用同一单号」时误判为重复而漏发货。
  • 执行顺序必须是「查重 → 处理 → 落标记」。先落标记再处理,一旦处理失败,重推会被当作重复事件忽略,导致永久丢单。
  • 错误分级应答:结构性错误(缺 Event)返回 {"ErrCode":0,"ErrMsg":"success"} 避免无效重推;业务异常返回非 0 触发微信重推。全返回 0 等于静默丢单。
  • 商品信息不全时不要一律返回成功:缺 productId 应标记异常订单(重推也补不上);quantity 缺失可按 1 兜底并标记 goodsValid: false;quantity 非法(0 / 小数 / 非数字)应抛错触发重推,绝不能静默改成 1,否则「买了 5 件只发 1 件」属于无感知资损。
  • 建议配置定时触发器每 5 分钟调用一次 /xpay/query_order,对已支付但未发货的订单补发货。

常见问题 FAQ​

Q1:报 41004 appsecret missing​

secret 为空或未注入。

排查:查看函数启动日志中的环境变量清单(只打印长度不打印值),确认 secret 有长度;走集成中心时不要单独执行 tcb fn deploy 覆盖平台注入的变量。

Q2:报 41002 appid missing​

appId 为空或未注入,排查方式同上。

Q3:回调返回 404 或收不到推送​

按顺序排查:

  1. 微信后台的推送 URL 是否指向集成中心回调域名。
  2. 集成中心的「回调承接资源」是否已绑定到目标云函数。
  3. 数据格式(JSON / XML)与加密方式是否与微信后台一致——格式不一致会导致解析出空对象,进而无路由可匹配而返回 404。
  4. 上线顺序是否正确:先创建集成并等待就绪,再提交微信后台配置。

Q4:回调验签失败​

  • Token 与 EncodingAESKey 必须与微信后台逐字符一致。
  • 安全模式下校验的是 msg_signature(签名串包含 Encrypt),不是 signature,这是最高频的错误。
  • 采用集成中心托管(system 模式)时,验签解密由网关完成,业务侧不要重复验签。

Q5:signData 签名无效​

签名必须基于原样字符串。任何解析后重新序列化、或对字段做 trim / 补空格,都会改变待签内容。模板的 signService.js 已强制只接收字符串,自行改造时请保持这一约束。

Q6:iOS 支付失败 / 金额受限​

  • iOS 走 Apple IAP,最低 1 元,低于该金额的档位无法支付。
  • Apple 支付不支持沙箱,沙箱仅在开发版 / 体验版可用,且真机预览可能被 PAYMENT_ILLEGAL_IN_SANDBOX 拦截。
  • 模板固定使用现网(env=0),不提供沙箱切换。

Q7:下单报 COIN_OR_PRODUCT_ID_CREATED_IN_RECENTLY​

道具刚创建或刚发布,微信侧尚未完成同步。等待几分钟至半小时后重试。

Q8:query_order 查不到订单​

query_order 只能查现金单,查不了代币单;代币单请通过用户余额接口与幂等重试兜底。此外必须校验订单归属(openid 与订单的对应关系),否则他人可用自己的 code + 别人的单号越权查单。

Q9:能否修改函数名或回调路径​

不能。云函数名中的随机串与回调域名子域、注入的环境变量相互绑定,改名即断回调;回调路由由平台预置,不要修改路径。

Q10:删除集成后云函数还在吗​

删除集成不等于删除云函数。删除集成会释放回调域名与回调路由(不可恢复),但云函数与环境变量会保留,需要手动清理。


附录 · 环境变量速查​

集成中心创建集成时会自动注入下列变量,用户无需手动维护;此处列出仅供排查与扩展开发参考。

变量来源说明
appId集成中心注入小程序 AppID
secret集成中心注入小程序 AppSecret
offerId集成中心注入虚拟支付商户号
appKey集成中心注入支付签名密钥
token集成中心注入消息推送 Token
msgSecret集成中心注入EncodingAESKey(43 位)
notifyUrlCallbackType集成中心注入system = 网关代验签;未声明 = 本函数自行验签
msgEncryptType集成中心注入plaintext / aes / compatible
sandboxAppKey集成中心注入沙箱密钥,模板固定走现网,留空即可
notifyFormat需自行配置json(默认)/ xml,必须与微信后台数据格式一致
trustBearerIdentity需自行配置是否采信 Authorization: Bearer JWT,默认关闭
单独部署的风险

cloudbaserc.json 的 functions[].envVariables 是整体覆盖语义。若凭证为空就单独执行 tcb fn deploy,会把平台上已有的值覆盖为空串(表现为 41004 appsecret missing)。走集成中心托管时,请通过「编辑集成」触发部署,或先补全配置再部署。


延伸阅读​

主题链接
示例工程(云函数模板 + 小程序 Demo)https://github.com/1225zhangqian/awesome-cloudbase-examples/tree/master/integration/cloudbase-virtualpay
wx.requestVirtualPayment APIhttps://developers.weixin.qq.com/miniprogram/dev/api/payment/wx.requestVirtualPayment.html
wx.cloud.callHTTPFunctionhttps://developers.weixin.qq.com/miniprogram/dev/wxcloudservice/wxcloud/HTTPCloudFunction.html
微信小程序支付集成(实物商品)微信小程序支付集成指南