Some checks failed
Build docker and publish / linux (1.24.5) (push) Failing after 40s
feat(pay): 添加支付API基础结构 feat(miniapp): 创建支付测试小程序页面与配置 feat(wechatpay): 配置微信支付参数与证书 fix(guild): 修复成员列表查询条件 docs: 更新代码规范文档与需求文档 style: 统一前后端枚举显示与注释格式 refactor(admin): 重构用户奖励发放接口参数处理 test(title): 添加称号效果参数验证测试
3.4 KiB
3.4 KiB
问题定位
- 现象: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_*)。确认后我将按上述方案实现代码与配置改动,并完成联调与验证。