Files
zt-closet-backend/.codebuddy/plans/虚拟支付沙箱环境自动选择_bb981350.md
T
2026-09-11 01:41:52 +08:00

124 lines
7.0 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.
---
name: 虚拟支付沙箱环境自动选择
overview: 为虚拟支付增加环境自动选择:保持 EnvAppKey/SandboxAppKey 双槽位配置,按「只填沙箱 key → env=1 沙箱;只填现网 key 或两把都填 → env=0 现网」的规则在客户端初始化时决定激活环境,并把 env 贯穿到道具同步、下单签名、订单记录与回调校验全链路。
todos:
- id: wechat-env-select
content: 改造 pkg/wechat/virtualpay.go:新增 resolveEnvKey 与 client.env,调整初始化校验,道具 API 带 env,QueryOrder/RefundOrder/BuildPaymentSign 去 env 入参并新增 GetVirtualPayEnv
status: completed
- id: service-env-wire
content: 接入 internal/service/virtualpay.go:VirtualOrderCreate 改用 GetVirtualPayEnv 记录订单环境并适配签名调用
status: completed
dependencies:
- wechat-env-select
- id: test-adapt
content: 适配 virtualpay_test.go 现有测试并新增 TestResolveEnvKey 四分支单测
status: completed
dependencies:
- wechat-env-select
- id: sync-config-docs
content: 更新 config.yaml、config.example.yaml 与 README 中 WechatVirtualPay 的环境选择规则说明
status: completed
dependencies:
- wechat-env-select
- id: verify-build
content: 执行 go build、go vet、go test 与 read_lints 全量验证
status: completed
dependencies:
- service-env-wire
- test-adapt
- sync-config-docs
---
## 产品概述
为微信小程序虚拟支付(XPay)增加环境自动选择能力:配置保留双槽位(EnvAppKey 现网 / SandboxAppKey 沙箱),服务启动初始化时按规则自动判定激活环境,并把 env 贯穿到道具同步、下单签名、查单退款全链路。测试与上线切换只需修改配置文件,无需改代码。
## 核心功能
- 环境自动选择:只填沙箱 AppKey 时激活沙箱环境(env=1,测试不真实扣款);只填现网 AppKey 或两把都填时激活现网环境(env=0);两把都空时初始化报错、虚拟支付禁用
- 全链路 env 贯穿:道具上传/发布、订单查询/退款、前端调起签名(signData 的 env 字段与 paySig)均使用激活环境的 key 与 env 值
- 订单记录环境:虚拟支付订单的 xpay_env 字段记录下单时的激活环境,发货推送按此校验(沙箱推送不会误发现网订单)
- 环境切换零代码:切换测试/现网只需改配置后重启,启动时的全量道具同步会自动把商品发布到新激活的环境
## 技术栈
- 语言与框架:Go 1.27(复用现有项目栈,不改架构)
- 涉及模块:pkg/wechat/virtualpay.go(XPay 客户端)、internal/service/virtualpay.go(虚拟支付业务)、pkg/wechat/virtualpay_test.go(单元测试)
- 配置:configs/config.yaml、configs/config.example.yaml(仅更新注释)
## 实现方案
### 环境选择规则(初始化时一次决定)
抽成纯函数便于单测:
```
// resolveEnvKey 按配置决定激活环境:
// EnvAppKey 非空 -> 现网(env=0,两把都填时现网优先);
// EnvAppKey 空且 SandboxAppKey 非空 -> 沙箱(env=1);
// 两把都空 -> 返回错误。
func resolveEnvKey(envAppKey, sandboxAppKey string) (appKey string, env int, err error)
```
`InitVirtualPayClient` 调用该函数得到激活 key 与 env:校验 OfferID 非空 + resolveEnvKey 成功;激活 key 存入 client 的 envAppKey 字段(保持 callXPay 签名逻辑不动),新增 `env int` 字段记录激活环境。日志输出当前激活环境,便于运维确认。
### env 贯穿链路
```mermaid
flowchart LR
A[config.yaml<br/>EnvAppKey / SandboxAppKey] --> B[InitVirtualPayClient<br/>resolveEnvKey]
B --> C[client.envAppKey = 激活key<br/>client.env = 0/1]
C --> D[道具同步 API<br/>body 带 env + 沙箱key签名]
C --> E[BuildPaymentSign<br/>signData.env = client.env<br/>paySig 用激活key]
C --> F[QueryOrder / RefundOrder<br/>内部用 client.env]
E --> G[VirtualOrderCreate<br/>订单 xpay_env = GetVirtualPayEnv]
G --> H[发货推送校验<br/>推送 Env == 订单 xpay_env]
```
### 关键改动点
**pkg/wechat/virtualpay.go**
- 新增 `resolveEnvKey` 纯函数(规则见上)
- `virtualPayClient` 增加 `env int` 字段;`InitVirtualPayClient` 改用 resolveEnvKey(原校验改为 OfferID 非空 + 至少一把 key)
- 道具 API(StartUploadGoods/QueryUploadGoods/StartPublishGoods/QueryPublishGoods)body 增加 `"env": c.env`(微信按 env+key 双校验,注释标注字段联调以官方文档为准)
- `QueryOrder`/`RefundOrder` 删除 env 入参(当前无 service 调用点,改动安全),内部使用 `c.env`
- `BuildPaymentSign` 删除 env 入参,signData 的 env 字段与 paySig 使用 `c.env` 与 `c.envAppKey`(即激活环境)
- 新增导出函数 `GetVirtualPayEnv() (int, error)` 供 service 层记录订单环境
**internal/service/virtualpay.go**
- `VirtualOrderCreate`:`XPayEnv` 由写死的 `wechat.XPayEnvProd` 改为 `wechat.GetVirtualPayEnv()` 返回值;`BuildPaymentSign` 调用去掉 env 实参
- `SyncVirtualGoods` 道具同步调用不变(env 已在 wechat 层内部带入)
- `handleGoodsDeliver` 的 Env 校验逻辑保持不动(订单 xpay_env 与推送 Env 匹配)
**pkg/wechat/virtualpay_test.go**
- 适配 `TestBuildPaymentSign`:client 构造改为含 env 字段、调用去掉 env 实参
- 新增 `TestResolveEnvKey`:覆盖四个分支(只填现网、只填沙箱、两把都填、两把都空)
**配置与文档**
- `configs/config.yaml` 与 `configs/config.example.yaml`:WechatVirtualPay 段注释更新为环境选择规则(只填沙箱 key 走沙箱测试;只填现网 key 或两把都填走现网)
- `README.md` 配置表 WechatVirtualPay 行同步该规则
## 目录结构
```
pkg/wechat/
├── virtualpay.go # [MODIFY] resolveEnvKey 纯函数、client.env 字段、初始化调整、道具 API 带 env、QueryOrder/RefundOrder 去 env 入参、BuildPaymentSign 去 env 入参、GetVirtualPayEnv
└── virtualpay_test.go # [MODIFY] 适配现有签名测试 + 新增 TestResolveEnvKey 四分支单测
internal/service/
└── virtualpay.go # [MODIFY] VirtualOrderCreate 改用 GetVirtualPayEnv 记录订单环境
configs/
├── config.yaml # [MODIFY] WechatVirtualPay 段注释更新(不改已填值)
└── config.example.yaml # [MODIFY] WechatVirtualPay 段注释更新环境选择规则
README.md # [MODIFY] 配置表 WechatVirtualPay 行说明同步
```
## 实现注意事项
- 只改虚拟支付环境选择与 env 贯穿,不动支付架构、权益发放/回退、订单状态机、普通 JSAPI 支付
- 环境切换后重启即可:P0-2 修复后的全量道具同步会自动把商品发布到新激活环境,无需额外迁移
- 沙箱与现网的道具/订单/推送隔离由微信侧保证(按签名 key 与 env 双校验)
- 验证:go build ./...、go vet ./...、go test ./pkg/wechat/ 通过;read_lints 无新增错误