5.8 KiB
5.8 KiB
微信虚拟支付接入
更新时间:2026-05-26
接入口径
- 泥点充值在微信小程序 WebView 内走
wechat_mp_virtual,由小程序页调用wx.requestVirtualPayment的short_series_coin模式。 - 会员商品在微信小程序 WebView 内同样走
wechat_mp_virtual,由小程序页调用wx.requestVirtualPayment的short_series_goods模式,并在signData内带productId与goodsPrice。 - 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.ts、server-rs/crates/shared-contracts/src/runtime.rs - 后端下单与签名:
server-rs/crates/api-server/src/runtime_profile.rs - 微信登录态保存:
server-rs/crates/platform-auth/src/lib.rs、server-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的订单数据。paySig:HMAC-SHA256(appKey, "requestVirtualPayment&" + signData)的小写 hex。signature:HMAC-SHA256(session_key, signData)的小写 hex。- 泥点属于微信虚拟支付代币(coin),
short_series_coin的buyQuantity必须使用当前泥点商品的points_amount;例如 60 泥点商品应传buyQuantity: 60。 - 会员直购
signData额外包含productId和goodsPrice;goodsPrice使用后端商品配置价,和微信后台道具价格校验保持一致。 - 微信小程序“开发者服务器接收消息推送”必须配置为安全模式,数据格式选 JSON,URL 统一指向
/api/profile/recharge/wechat/virtual-notify。 WECHAT_MINIPROGRAM_MESSAGE_TOKEN和WECHAT_MINIPROGRAM_MESSAGE_ENCODING_AES_KEY由环境变量注入;后端会先校验signature/msg_signature,再用EncodingAESKey解密Encrypt,然后按虚拟支付事件入账。- 安全模式下,GET 验证会直接返回解密后的
echostr;POST 推送会先解密再解析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.requestPayment或wx.requestVirtualPayment。 - 小程序支付承接页回传
wx_pay_result时必须携带requestId:status:orderId[:error],并同时写入上一页 hash 与本地 storage;WebViewonShow会立即检查一次、延迟二次检查一次,且同名 hash 参数必须替换,避免支付状态停留在处理中或重复处理。 - 微信虚拟支付消息推送使用独立后端入口
/api/profile/recharge/wechat/virtual-notify,按xpay_goods_deliver_notify和xpay_coin_pay_notify推进充值订单入账;回包需按入站格式返回ErrCode=0/ErrMsg=success(JSON 入站回 JSON,XML 入站回 XML),错误时带具体ErrMsg便于微信侧重试与排障。 - 沙箱或基础库失败会把微信返回的
errCode/errMsg透传到前端失败弹窗,便于区分微信后台道具、沙箱 AppKey、签名和基础库能力问题。 - Web 侧在“正在支付”状态下会短时轮询
wx_pay_result,即使小程序web-view回写 hash 没触发浏览器hashchange,也必须展示回写的微信错误内容。