Files
Genarrative/docs/【技术方案】微信虚拟支付接入-2026-05-26.md
T
2026-05-31 22:44:22 +08:00

5.8 KiB
Raw Blame History

微信虚拟支付接入

更新时间:2026-05-26

接入口径

  • 泥点充值在微信小程序 WebView 内走 wechat_mp_virtual,由小程序页调用 wx.requestVirtualPaymentshort_series_coin 模式。
  • 会员商品在微信小程序 WebView 内同样走 wechat_mp_virtual,由小程序页调用 wx.requestVirtualPaymentshort_series_goods 模式,并在 signData 内带 productIdgoodsPrice
  • H5 与桌面微信环境仍分别走 wechat_h5 / wechat_native,不进入虚拟支付链路。
  • session_key 只保存在后端认证仓储内,用于计算虚拟支付用户态签名,不下发给前端。
  • 客户端支付成功回调只代表已拉起支付并返回成功;最终到账仍以后端虚拟支付消息推送写入订单为准,普通微信支付订单则继续走微信支付 V3 notify / query。
  • 小程序 WebView 默认进入时会静默调用 wx.login 刷新后端微信登录态,避免历史登录用户只有前端 JWT、后端缺少 session_key 时无法生成虚拟支付签名。

关键文件

  • 前端渠道选择:src/services/payment/paymentPlatform.ts
  • 充值入口:src/components/rpg-entry/RpgEntryHomeView.tsx
  • 小程序支付承接页:miniprogram/pages/wechat-pay/index.shared.js
  • API 契约:packages/shared/src/contracts/runtime.tsserver-rs/crates/shared-contracts/src/runtime.rs
  • 后端下单与签名:server-rs/crates/api-server/src/runtime_profile.rs
  • 微信登录态保存:server-rs/crates/platform-auth/src/lib.rsserver-rs/crates/module-auth/src/lib.rs

后端配置

生产接入虚拟支付至少需要:

WECHAT_PAY_ENABLED=true
WECHAT_PAY_PROVIDER=real
WECHAT_MINI_PROGRAM_VIRTUAL_PAYMENT_OFFER_ID=<微信虚拟支付 offerId>
WECHAT_MINI_PROGRAM_VIRTUAL_PAYMENT_APP_KEY=<现网 AppKey>
WECHAT_MINI_PROGRAM_VIRTUAL_PAYMENT_SANDBOX_APP_KEY=<沙箱 AppKey,可选>
WECHAT_MINIPROGRAM_MESSAGE_TOKEN=<微信消息推送 Token>
WECHAT_MINIPROGRAM_MESSAGE_ENCODING_AES_KEY=<微信消息推送 EncodingAESKey>
WECHAT_MINI_PROGRAM_VIRTUAL_PAYMENT_ENV=0

WECHAT_MINI_PROGRAM_VIRTUAL_PAYMENT_ENV=0 表示现网,1 表示沙箱。后端会按 env 选择 AppKey,并生成:

  • signData:传给 wx.requestVirtualPayment 的订单数据。
  • paySigHMAC-SHA256(appKey, "requestVirtualPayment&" + signData) 的小写 hex。
  • signatureHMAC-SHA256(session_key, signData) 的小写 hex。
  • 泥点属于微信虚拟支付代币(coin),short_series_coinbuyQuantity 必须使用当前泥点商品的 points_amount;例如 60 泥点商品应传 buyQuantity: 60
  • 会员直购 signData 额外包含 productIdgoodsPricegoodsPrice 使用后端商品配置价,和微信后台道具价格校验保持一致。
  • 微信小程序“开发者服务器接收消息推送”必须配置为安全模式,数据格式选 JSON,URL 统一指向 /api/profile/recharge/wechat/virtual-notify
  • WECHAT_MINIPROGRAM_MESSAGE_TOKENWECHAT_MINIPROGRAM_MESSAGE_ENCODING_AES_KEY 由环境变量注入;后端会先校验 signature/msg_signature,再用 EncodingAESKey 解密 Encrypt,然后按虚拟支付事件入账。
  • 安全模式下,GET 验证会直接返回解密后的 echostrPOST 推送会先解密再解析 xpay_goods_deliver_notify / xpay_coin_pay_notify

验收命令

npm exec vitest run miniprogram/pages/wechat-pay/index.test.js src/services/payment/paymentPlatform.test.ts src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx
cargo check -p api-server --manifest-path server-rs/Cargo.toml
cargo test -p shared-contracts --manifest-path server-rs/Cargo.toml create_profile_recharge_order_response_serializes_virtual_wechat_payloads
npm run typecheck
npm run check:encoding

注意事项

  • 旧微信登录快照可能没有 session_key;小程序 WebView 会在普通进入时静默刷新一次微信登录态,刷新失败时仍允许匿名打开 WebView,但虚拟支付会继续由后端拦截并提示重新登录。
  • 小程序充值商品全部映射到虚拟支付;泥点使用 short_series_coin,会员使用 short_series_goods
  • short_series_coin 只用于代币购买,后端从本次下单返回的充值中心商品快照读取 points_amount 并写入 buyQuantity;不要把 coin 商品当成道具,也不要把 buyQuantity 固定为 1。
  • 后台新增的会员类充值商品会直接把商品 productId 作为微信 short_series_goods 的道具 ID;例如微信后台道具 ID 为 item01 时,后台会员商品 productId 也应配置为 item01,且商品价格需要与微信后台道具价格一致。
  • 小程序页必须保留普通支付与虚拟支付双分支,按 pay params 字段判断调用 wx.requestPaymentwx.requestVirtualPayment
  • 小程序支付承接页回传 wx_pay_result 时必须携带 requestId:status:orderId[:error],并同时写入上一页 hash 与本地 storageWebView onShow 会立即检查一次、延迟二次检查一次,且同名 hash 参数必须替换,避免支付状态停留在处理中或重复处理。
  • 微信虚拟支付消息推送使用独立后端入口 /api/profile/recharge/wechat/virtual-notify,按 xpay_goods_deliver_notifyxpay_coin_pay_notify 推进充值订单入账;回包需按入站格式返回 ErrCode=0 / ErrMsg=successJSON 入站回 JSON,XML 入站回 XML),错误时带具体 ErrMsg 便于微信侧重试与排障。
  • 沙箱或基础库失败会把微信返回的 errCode / errMsg 透传到前端失败弹窗,便于区分微信后台道具、沙箱 AppKey、签名和基础库能力问题。
  • Web 侧在“正在支付”状态下会短时轮询 wx_pay_result,即使小程序 web-view 回写 hash 没触发浏览器 hashchange,也必须展示回写的微信错误内容。