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

38 lines
3.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 问题定位
- 现象POST `/api/pay/wechat/notify` 返回 400日志提示“certificate [PUB_KEY_ID_0116104396352025041000211519001600] not found in verifier”。
- 原因:微信侧请求头 `Wechatpay-Serial``PUB_KEY_ID_*` 格式,说明该商户已启用“微信支付平台公钥”模式;当前代码仅使用“平台证书”验签(`verifiers.NewSHA256WithRSAVerifier` + `downloader`),导致找不到匹配的证书序列号而验签失败。
- 代码位置:
- 路由注册:`internal/router/router.go:252`
- 回调处理:`internal/api/pay/wechat_notify.go:40-46`(注册证书下载器+证书验签器)
- 客户端初始化:`internal/pkg/pay/client.go:32-39``WithWechatPayAutoAuthCipher` 使用平台证书模式)
## 修复方案
- 增加“平台公钥”模式配置并切换验签/签名实现:
- 在配置新增 `wechatpay.public_key_id``wechatpay.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_id``public_key_path` 键;同时支持环境变量覆盖(如 `WECHAT_PUBLIC_KEY_ID``WECHAT_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→2`paidAt` 按回调 `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.pem``PUB_KEY_ID_*`)。确认后我将按上述方案实现代码与配置改动,并完成联调与验证。