Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
11 KiB
11 KiB
微信参数配置管理功能
功能概述
在管理后台支持多套微信支付配置的 CRUD 管理,每套配置代表一套完整的"微信身份"(公众号 OAuth + 小程序 OAuth + 支付凭证),支持全局唯一激活约束和秒级切换。同时集成富友支付 SDK,作为微信直连的备选渠道。
背景与动机
原有微信相关参数(公众号 OAuth、小程序、支付凭证)硬编码在环境变量中,只有一套配置,无法动态切换。业务上微信公众号/小程序随时可能被封禁,需要在管理后台秒级切换到备用配置恢复 OAuth 登录和支付能力。同时需要接入富友支付作为备选通道,降低对微信直连的单一依赖。
核心设计
配置切换流程
管理员激活新配置 POST /api/admin/wechat-configs/:id/activate
│
├─ ① BEGIN 事务
│ ├─ UPDATE tb_wechat_config SET is_active=false WHERE is_active=true
│ └─ UPDATE tb_wechat_config SET is_active=true WHERE id=:id
├─ ② COMMIT
├─ ③ DEL Redis "wechat:config:active"(即时生效)
└─ ④ 记录审计日志
│
├─ 新订单 → 使用新配置(记录新的 payment_config_id)
└─ 旧订单(待支付)→ 回调时按 payment_config_id 加载旧配置验签
└─ 30 分钟超时自动取消
生效配置缓存策略
- Redis Key:
wechat:config:active(见pkg/constants/redis.go) - TTL:5 分钟(兜底,防 Redis 缓存与 DB 长期不一致)
- 主动失效:激活、停用、更新生效配置、删除配置时主动 DEL 缓存
- 空标记:无生效配置时缓存
"none",TTL 1 分钟,防止缓存穿透 - 读取流程:Redis GET → 命中返回 → MISS → 查 DB → SET 缓存
配置切换时在途订单处理
tb_order、tb_asset_recharge_record、tb_agent_recharge_record均新增payment_config_id字段(nullable)- 下单时记录当前使用的配置 ID,配置切换后旧订单仍按
payment_config_id加载旧配置验签 - 旧待支付订单由现有 30 分钟超时自动取消机制清理
- 有待支付订单引用的配置不允许删除(软删除后仍可用于验签)
支付回调统一分发
回调到达
│
├─ 微信回调 POST /api/callback/wechat-pay
│ └─ PowerWeChat SDK 解析 → 取 out_trade_no
│
└─ 富友回调 POST /api/callback/fuiou-pay
└─ GBK→UTF-8 → XML 解析 → 取 mchnt_order_no
│
└─ 按订单号前缀分发
├─ "ORD" → 套餐订单 → orderService.HandlePaymentCallback()
├─ "CRCH" → 资产充值 → rechargeService.HandlePaymentCallback()
└─ "ARCH" → 代理充值 → agentRechargeService.HandlePaymentCallback()
接口说明
基础路径
/api/admin/wechat-configs
权限要求:仅超级管理员(user_type=1)和平台用户(user_type=2)可访问,其他类型返回 1005。
接口列表
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/admin/wechat-configs |
创建配置 |
| GET | /api/admin/wechat-configs |
查询配置列表(分页+筛选) |
| GET | /api/admin/wechat-configs/active |
查询当前生效配置 |
| GET | /api/admin/wechat-configs/:id |
查询配置详情 |
| PUT | /api/admin/wechat-configs/:id |
更新配置 |
| DELETE | /api/admin/wechat-configs/:id |
软删除配置 |
| POST | /api/admin/wechat-configs/:id/activate |
激活配置 |
| POST | /api/admin/wechat-configs/:id/deactivate |
停用配置 |
| POST | /api/callback/fuiou-pay |
富友支付回调(无需认证) |
渠道类型(provider_type)
| 值 | 说明 | 必填支付字段 |
|---|---|---|
wechat |
微信直连 | wx_mch_id、wx_api_v3_key、wx_cert_content、wx_key_content、wx_serial_no、wx_notify_url |
fuiou |
富友聚合支付 | fy_ins_cd、fy_mchnt_cd、fy_term_id、fy_private_key、fy_public_key、fy_api_url、fy_notify_url |
敏感字段脱敏规则
接口响应中所有敏感字段均脱敏,数据库明文存储:
| 字段类型 | 脱敏规则 | 示例 |
|---|---|---|
| Secret/Key(短) | 前4位 + *** + 后4位 |
abcd***7890 |
| 证书/私钥(长) | 仅显示状态 | [已配置] / [未配置] |
更新脱敏字段:不传或传空字符串 = 保留原值;传新明文值 = 替换。
删除保护规则
| 条件 | 错误码 | 错误消息 |
|---|---|---|
配置 is_active=true |
1171 |
不能删除当前生效的支付配置,请先停用 |
| 存在待支付订单引用 | 1172 |
该配置存在未完成的支付订单,暂时无法删除 |
富友支付 SDK
位置:pkg/fuiou/
| 文件 | 说明 |
|---|---|
types.go |
WxPreCreateRequest/Response、NotifyRequest 等 XML 结构体 |
client.go |
Client 结构体、NewClient、RSA 签名/验签、HTTP 请求(XML+GBK) |
wxprecreate.go |
WxPreCreate 方法(公众号 JSAPI + 小程序支付下单) |
notify.go |
VerifyNotify(GBK→UTF-8 + XML 解析 + RSA 验签)、BuildNotifyResponse |
签名算法:字典序排列参数 → GBK 编码 → MD5 哈希 → RSA 签名 → Base64
新增依赖:golang.org/x/text(GBK 编解码)
数据库变更
新建表 tb_wechat_config(迁移 000078)
| 字段组 | 字段 | 说明 |
|---|---|---|
| 基础信息 | id, name, description, provider_type, is_active |
配置基础字段 |
| 公众号 OAuth | oa_app_id, oa_app_secret, oa_token, oa_aes_key, oa_oauth_redirect_url |
公众号相关 |
| 小程序 OAuth | miniapp_app_id, miniapp_app_secret |
小程序相关 |
| 微信直连 | wx_mch_id, wx_api_v3_key, wx_api_v2_key, wx_cert_content, wx_key_content, wx_serial_no, wx_notify_url |
provider_type=wechat 时使用 |
| 富友 | fy_ins_cd, fy_mchnt_cd, fy_term_id, fy_private_key, fy_public_key, fy_api_url, fy_notify_url |
provider_type=fuiou 时使用 |
| 审计 | creator, updater, created_at, updated_at, deleted_at |
标准审计字段 |
新增字段
| 表 | 字段 | 类型 | 迁移文件 |
|---|---|---|---|
tb_order |
payment_config_id |
bigint, nullable | 000079 |
tb_asset_recharge_record |
payment_config_id |
bigint, nullable | 000080 |
tb_agent_recharge_record |
payment_config_id |
bigint, nullable | 000081 |
新增错误码
| 错误码 | 常量 | 说明 |
|---|---|---|
| 1170 | CodeWechatConfigNotFound |
微信支付配置不存在 |
| 1171 | CodeWechatConfigActive |
不能删除/操作当前生效的支付配置 |
| 1172 | CodeWechatConfigHasPendingOrders |
该配置存在未完成的支付订单 |
| 1173 | CodeFuiouPayFailed |
富友支付失败 |
| 1174 | CodeFuiouCallbackInvalid |
富友回调验签失败 |
| 1175 | CodeNoPaymentConfig |
当前无可用的支付配置 |
审计日志
以下操作均记录审计日志(异步写入,失败不影响业务):
| 操作 | operation_type | 说明 |
|---|---|---|
| 创建配置 | create |
after_data 存脱敏后配置 |
| 更新配置 | update |
before/after_data 均脱敏 |
| 删除配置 | delete |
before_data 存脱敏后配置 |
| 激活配置 | activate |
before_data=旧配置,after_data=新配置 |
| 停用配置 | deactivate |
before/after_data 存状态变更 |
涉及文件
新增文件
| 层级 | 文件 | 说明 |
|---|---|---|
| 模型 | internal/model/wechat_config.go |
WechatConfig 模型、渠道类型常量 |
| DTO | internal/model/dto/wechat_config_dto.go |
CRUD 请求/响应 DTO、脱敏方法 |
| Store | internal/store/postgres/wechat_config_store.go |
CRUD + 激活/停用 + 统计 |
| Service | internal/service/wechat_config/service.go |
业务逻辑、缓存管理、删除保护 |
| Handler | internal/handler/admin/wechat_config.go |
8 个 Handler 方法 |
| 路由 | internal/routes/wechat_config.go |
路由注册(含平台权限中间件) |
| SDK | pkg/fuiou/types.go |
富友 XML 结构体 |
| SDK | pkg/fuiou/client.go |
富友 HTTP 客户端、签名/验签 |
| SDK | pkg/fuiou/wxprecreate.go |
富友支付下单 |
| SDK | pkg/fuiou/notify.go |
富友回调验签 |
| 迁移 | migrations/000078_create_wechat_config_table.up.sql |
创建 tb_wechat_config 表 |
| 迁移 | migrations/000079_add_payment_config_id_to_order.up.sql |
tb_order 新增字段 |
| 迁移 | migrations/000080_add_payment_config_id_to_asset_recharge.up.sql |
tb_asset_recharge_record 新增字段 |
| 迁移 | migrations/000081_add_payment_config_id_to_agent_recharge.up.sql |
tb_agent_recharge_record 新增字段 |
修改文件
| 文件 | 变更说明 |
|---|---|
internal/model/order.go |
新增 PaymentConfigID *uint 字段 |
internal/model/asset_wallet.go |
新增 PaymentConfigID *uint 字段 |
internal/handler/callback/payment.go |
支持富友回调 + 按订单前缀分发 + 按 payment_config_id 验签 |
internal/routes/order.go |
新增 /api/callback/fuiou-pay 路由 |
internal/service/order/service.go |
注入 wechatConfigService、下单时记录 payment_config_id |
internal/bootstrap/ 系列 |
注册 WechatConfigStore/Service/Handler |
cmd/api/docs.go / cmd/gendocs/main.go |
注册 WechatConfigHandler |
删除/精简文件(YAML 支付方案遗留清理)
| 文件 | 变更说明 |
|---|---|
pkg/config/config.go |
删除 PaymentConfig 结构体 + WechatConfig.Payment 字段 |
pkg/config/defaults/config.yaml |
删除 wechat.payment: 整个配置节 |
pkg/wechat/config.go |
删除 NewPaymentApp() 函数(YAML/CertPath 方式已被 DB Base64 方案替代) |
cmd/api/main.go |
删除 validateWechatConfig 中所有 wechatCfg.Payment.* 相关校验代码 |
常量定义
// pkg/constants/wallet.go(Card* 重命名为 Asset*,旧名保留为废弃别名)
AssetWalletResourceTypeIotCard // 原 CardWalletResourceTypeIotCard
AssetWalletResourceTypeDevice // 原 CardWalletResourceTypeDevice
AssetRechargeOrderPrefix // "CRCH"(原 CardRechargeOrderPrefix)
AssetRechargeMinAmount // 最小充值金额(分)
AssetRechargeMaxAmount // 最大充值金额(分)
// pkg/constants/redis.go
RedisWechatConfigActiveKey() // "wechat:config:active"
// internal/model/wechat_config.go
ProviderTypeWechat = "wechat" // 微信直连
ProviderTypeFuiou = "fuiou" // 富友
已知限制(留桩)
以下功能本次未实现,待后续会话补全:
- 客户端支付发起:
WechatPayJSAPI、WechatPayH5、FuiouPayJSAPI、FuiouPayMiniApp均为留桩(返回"暂未实现"错误或 TODO 注释),当前仍保留wechatPayment单例注入 - OAuth 配置动态加载:
OfficialAccountService仍从环境变量读取,tb_wechat_config中的oa_*字段仅存储,待 H5/小程序重构时切换
部署注意事项
- 执行数据库迁移(000078~000081)后,现有数据不受影响(新字段均为 nullable)
- 原环境变量
JUNHONG_WECHAT_PAYMENT_*系列已不再读取,可清理 - 首次上线后,需要在管理后台手动创建并激活一个微信配置,否则第三方支付功能处于禁用状态(系统自动降级为仅支持钱包/线下支付)