bindbox-game/.trae/documents/修复微信支付回调验签失败(支持平台公钥_PUB_KEY_ID).md
邹方成 6ee627139c
Some checks failed
Build docker and publish / linux (1.24.5) (push) Failing after 40s
feat: 新增支付测试小程序与微信支付集成
feat(pay): 添加支付API基础结构
feat(miniapp): 创建支付测试小程序页面与配置
feat(wechatpay): 配置微信支付参数与证书
fix(guild): 修复成员列表查询条件
docs: 更新代码规范文档与需求文档
style: 统一前后端枚举显示与注释格式
refactor(admin): 重构用户奖励发放接口参数处理
test(title): 添加称号效果参数验证测试
2025-11-17 00:42:08 +08:00

3.4 KiB
Raw Blame History

问题定位

  • 现象POST /api/pay/wechat/notify 返回 400日志提示“certificate [PUB_KEY_ID_0116104396352025041000211519001600] not found in verifier”。
  • 原因:微信侧请求头 Wechatpay-SerialPUB_KEY_ID_* 格式,说明该商户已启用“微信支付平台公钥”模式;当前代码仅使用“平台证书”验签(verifiers.NewSHA256WithRSAVerifier + downloader),导致找不到匹配的证书序列号而验签失败。
  • 代码位置:
    • 路由注册:internal/router/router.go:252
    • 回调处理:internal/api/pay/wechat_notify.go:40-46(注册证书下载器+证书验签器)
    • 客户端初始化:internal/pkg/pay/client.go:32-39WithWechatPayAutoAuthCipher 使用平台证书模式)

修复方案

  • 增加“平台公钥”模式配置并切换验签/签名实现:
    • 在配置新增 wechatpay.public_key_idwechatpay.public_key_path(商户后台下载的 pub_key.pem 与对应 PUB_KEY_ID_*)。
    • 回调验签:当检测到已配置公钥,改用 verifiers.NewSHA256WithRSAPubkeyVerifier(publicKeyID, publicKey) 构造 notify.Handler;否则保持现有证书模式。
    • 请求侧签名与自动加密:当检测到已配置公钥,改用 option.WithWechatPayPublicKeyAuthCipher(mchid, serialNo, privateKey, publicKeyID, publicKey) 初始化 core.Client;否则保持 WithWechatPayAutoAuthCipher
  • 初始化时机优化:
    • 将证书下载器注册/公钥加载前置至应用启动阶段,避免首次回调时的“尚未拉取证书”竞态问题。
  • 兼容策略:
    • 双模式自动选择:优先检测公钥配置;缺省走证书模式,确保对旧商户不破坏。

实施步骤

  • 配置:
    • configs/*.toml[wechatpay] 增加 public_key_idpublic_key_path 键;同时支持环境变量覆盖(如 WECHAT_PUBLIC_KEY_IDWECHAT_PUBLIC_KEY_PATH)。
  • 代码改动:
    • internal/api/pay/wechat_notify.go:根据配置分支选择 verifiers.NewSHA256WithRSAPubkeyVerifier 或现有证书验签器;加载公钥使用 utils.LoadPublicKeyWithPath
    • internal/pkg/pay/client.go:根据配置分支选择 option.WithWechatPayPublicKeyAuthCipher 或现有证书选项。
    • 启动流程:在应用初始化位置集中完成下载器注册/公钥加载与复用,不在每次回调时重复注册。

验证步骤

  • 本地联调:
    • 使用真实微信回调数据(包含 Wechatpay-Serial: PUB_KEY_ID_*)验证回调成功返回 {"code":"SUCCESS","message":"OK"}
    • 下单→支付→回调链路验证:检查订单状态从 1→2paidAt 按回调 success_time 生效(internal/api/pay/wechat_notify.go:67-71)。
  • 日志与安全:
    • 打印初始化模式(证书/公钥)与关键配置是否完整,避免静默失败。
    • 不输出密钥/私钥内容,符合安全规范。

额外检查关于“req 没有写”)

  • 当前回调处理直接使用 ctx.Request() 原始请求体(internal/api/pay/wechat_notify.go:48),未提前读取/篡改;若上游中间件读取了 Body,需保证使用 io.NopCloser 复位请求体后再交给 notify.Handler。本次改造同时确认路由链路未破坏请求体传递。

请确认是否按照“平台公钥”接入(已具备 pub_key.pemPUB_KEY_ID_*)。确认后我将按上述方案实现代码与配置改动,并完成联调与验证。