编程 微信虚拟支付踩坑 8 天,这 6 个坑帮你全绕过去

2026-07-06 20:18:21 +0800 CST views 12

微信虚拟支付踩坑 8 天,这 6 个坑帮你全绕过去

接一个微信虚拟支付,踩了 6 个坑,折腾了 8 天。不是文档没写——是文档分散在微信开放平台、米大师独立文档站、开放社区、英文版四个地方,关键信息藏在不同页面的角落里。这篇把碎片拼成完整地图。

一句话结论

微信虚拟支付有两条并行的技术栈(xpay 2.0 和米大师 1.0),签名有两套公式、回调不走 HTTP、退款需要双签名、pay_sig 不存就退不了款。

一、两套支付体系,别混了

微信开放文档里有两个虚拟支付入口:虚拟支付 2.0(xpay)米大师支付(midas)。不是新旧版本迭代,是不同的产品。

对比项xpay 2.0米大师 1.0
负责团队小程序团队游戏团队
域名api.weixin.qq.comapi.midas.qq.com
适用场景会员、课程、虚拟工具游戏、金币系统
客户端签名"requestVirtualPayment&" + JSON不同拼接规则
服务端签名uri + "&" + JSON额外包含 url_path
回调方式消息推送(免配公网)HTTP 回调(需配公网地址)

最常见的翻车:在 xpay 后台配密钥,却用米大师的 API 文档写服务端——签名永久报错,且看不到任何额外错误信息。

二、同一个 HMAC-SHA256,两套输入

客户端签名和服务端签名用同一个 HMAC-SHA256、同一个 appKey,但输入结构不同:

客户端:sign = HMAC-SHA256(appKey, "requestVirtualPayment" + "&" + JSON.stringify(signDataObj))
服务端:sign = HMAC-SHA256(appKey, uri + "&" + JSON.stringify(body))

客户端签名第一个参数是固定字符串 "requestVirtualPayment",服务端是请求路径 uri。同一笔订单算出来的结果完全不同。

签名错误 40066 不是 bug,是认知盲区。 建议:每写一个 HMAC-SHA256,代码上方加三行注释标注用途和公式。

三、沙箱和生产:同一个域名,两套密钥

xpay 2.0 的沙箱和生产用同一个域名 api.weixin.qq.com,区别在请求体的 env 字段:沙箱 env=1,生产 env=0

// sandbox
app_key: process.env.WX_SANDBOX_APP_KEY
env: 1

// production
app_key: process.env.WX_PROD_APP_KEY
env: 0

切 env 时必须切 key,否则微信用沙箱密钥去验签你拿生产密钥算出的值——40066。

四、支付成功不代表回调到了

xpay 的回调不走 HTTP 回调地址,走的是消息推送——微信服务器内部通道,直接推送到小程序消息队列。不需要配公网回调地址,不需要验签。

注意:回调事件名和文档可能不一致——文档写的 xPayGoodsDelivery,实际推送可能是 xiaochengxu_pay_callback。以实际收到的推送事件名为准。消息推送触发器漏了开启,回调就永远收不到。

五、退款连着报错:一个操作,三层同时错

下单走 xpay,退款走米大师通用 API——两套路径、两套字段名、两套签名规则:

路径     → /api/common/refund(不是 /xpay/refund_order)
金额字段  → amt(不是 refund_fee),还要传 ts 和 action
签名     → Header 有额外校验字段,少传一个就过不了

三个维度同时出错时,报错信息看不出是哪一层没对齐。只能逐层试——修一层,发一次,看返回,再修下一层。

六、忘了存 pay_sig?退款全线报废

这是代价最大的坑。 测试环境退款全通,上线后全部失败。

支付下单时微信返回的响应里有一个 pay_sig 字段。当时以为它是下单阶段的中间变量,用完就扔了——数据库表里根本没存。

退款需要两个签名:

  • Sign:自己用 appKey 算出来的
  • pay_sig:支付时微信返回的,必须原样传回

两个签名缺一不可。已支付订单的 pay_sig 永久丢失,无法补救。

结论:支付回调里必须把 pay_sig 存入数据库(TEXT/VARCHAR(1024))。

上线前检查清单

云函数配置

  • timeout 设 15-20 秒(米大师 API 偶有延迟)
  • 消息推送触发器手动开启(漏了 = 回调永远收不到)
  • 上传选「云端安装依赖」

密钥管理

  • app_key 走 process.env,沙箱和生产各一套
  • pay_sig 存入数据库
  • 切生产时:改 body.env 的同时必须切换 app_key

定时兜底

  • cron 每 5 分钟扫已支付未发货超 2 分钟的订单,主动查单补发
  • 退款中超 30 秒的主动查结果

沙箱验证:跑通下单 → 支付 → 回调 → 发货 → 退款全闭环。

审核准备:msgSecCheck 覆盖所有 UGC 入口 + 未成年人退款全流程 + 隐私政策 + 企业主体 + 商品已发布。

六坑汇总

后果耗时
pay_sig 未存储退款全废,已有订单无法补救约3天
退款路径/字段/签名全错退款 API 连续报错约2天
回调事件名与文档不一致回调不触发半天
沙箱/生产 app_key 未分开签名错误 40066半天
退款双签名(Sign + pay_sig)退款失败约1天
xpay vs 米大师 API 混淆签名报错约1天

核心观点:坑不在文档内容上,在文档结构上。四个独立的文档站各自写了同一个支付系统的一部分,没有人把它们拼在一起。

接入路线(按顺序做,别跳步)

确认类目 → 开通虚拟支付获取 app_key → 沙箱跑通全链路 → 开启消息推送触发器 → 接入 msgSecCheck → 加 cron 兜底 → 切生产 → 提交审核。

推荐文章

利用Python构建语音助手
2024-11-19 04:24:50 +0800 CST
php获取当前域名
2024-11-18 00:12:48 +0800 CST
Vue3中如何扩展VNode?
2024-11-17 19:33:18 +0800 CST
程序员茄子在线接单