docs: 新增 OpenSpec 提案 add-payment-config-management
包含 proposal.md、design.md、tasks.md 及各模块 spec 文件(微信配置管理、富友支付、代理充值、订单支付、资产充值适配、微信支付留桩) Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: 微信支付配置动态加载
|
||||
|
||||
微信支付配置 MUST 从数据库动态加载(通过 `tb_wechat_config` 表),替代原有的环境变量静态配置。Payment 实例按需创建,支持请求级 AppID 覆盖(区分公众号和小程序)。
|
||||
|
||||
#### Scenario: 从数据库加载配置创建 Payment 实例
|
||||
|
||||
- **WHEN** 支付流程需要使用微信支付
|
||||
- **THEN** 系统从 Redis 缓存或数据库加载当前生效的微信参数配置(`is_active=true` 且 `provider_type=wechat`)
|
||||
- **THEN** 系统使用配置中的 `wx_mch_id`、`wx_api_v3_key`、`wx_cert_content`、`wx_key_content`、`wx_serial_no` 创建 `payment.Payment` 实例
|
||||
- **THEN** 证书内容从 Base64 解码后写入临时文件供 PowerWeChat SDK 使用
|
||||
|
||||
> **本次留桩**:WechatPayJSAPI 和 WechatPayH5 方法保留现有 wechatPayment 单例调用,添加 TODO 注释标记后续替换点。
|
||||
|
||||
#### Scenario: 无生效微信支付配置时拒绝支付
|
||||
|
||||
- **WHEN** 系统查询不到 `is_active=true` 的微信参数配置,或生效配置的 `provider_type` 非 `wechat`
|
||||
- **THEN** 微信支付相关接口返回错误
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 1175,
|
||||
"data": null,
|
||||
"msg": "当前无可用的支付渠道",
|
||||
"timestamp": "2026-03-16T10:00:00+08:00"
|
||||
}
|
||||
```
|
||||
|
||||
#### Scenario: 公众号 JSAPI 支付使用公众号 AppID
|
||||
|
||||
```
|
||||
POST /api/h5/orders/:id/wechat-pay/jsapi
|
||||
Authorization: Bearer {token}
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
**请求体**
|
||||
|
||||
```json
|
||||
{
|
||||
"openid": "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `openid` | string | ✅ | 用户在公众号下的 OpenID |
|
||||
|
||||
- **THEN** 系统使用配置中的 `oa_app_id`(公众号 AppID)创建支付订单
|
||||
- **THEN** Payer OpenID 为用户在该公众号下的 OpenID
|
||||
|
||||
**成功响应 `200 OK`**(本次留桩,返回结构不变)
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": {
|
||||
"prepay_id": "wx26112221580621e9b071c00d9e093b0000",
|
||||
"pay_config": {
|
||||
"appId": "wx1234567890abcdef",
|
||||
"timeStamp": "1711411341",
|
||||
"nonceStr": "abc123",
|
||||
"package": "prepay_id=wx26112221580621e9b071c00d9e093b0000",
|
||||
"signType": "RSA",
|
||||
"paySign": "..."
|
||||
}
|
||||
},
|
||||
"msg": "success",
|
||||
"timestamp": "2026-03-16T10:00:00+08:00"
|
||||
}
|
||||
```
|
||||
|
||||
#### Scenario: 小程序支付使用小程序 AppID
|
||||
|
||||
- **WHEN** 用户在小程序中发起支付
|
||||
- **THEN** 系统在调用 `JSAPITransaction` 时将 AppID 覆盖为配置中的 `miniapp_app_id`
|
||||
- **THEN** Payer OpenID 为用户在该小程序下的 OpenID
|
||||
|
||||
#### Scenario: 微信 H5 支付
|
||||
|
||||
```
|
||||
POST /api/h5/orders/:id/wechat-pay/h5
|
||||
Authorization: Bearer {token}
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
**请求体**
|
||||
|
||||
```json
|
||||
{
|
||||
"scene_info": {
|
||||
"payer_client_ip": "14.23.150.211",
|
||||
"h5_info": {
|
||||
"type": "Wap"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `scene_info.payer_client_ip` | string | ✅ | 用户终端 IP |
|
||||
| `scene_info.h5_info.type` | string | ❌ | 场景类型:`iOS` / `Android` / `Wap` |
|
||||
|
||||
**成功响应 `200 OK`**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": {
|
||||
"h5_url": "https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx..."
|
||||
},
|
||||
"msg": "success",
|
||||
"timestamp": "2026-03-16T10:00:00+08:00"
|
||||
}
|
||||
```
|
||||
|
||||
#### Scenario: 配置缺失时系统正常启动
|
||||
|
||||
- **WHEN** 系统启动时数据库中无微信参数配置或配置不完整
|
||||
- **THEN** 系统正常启动,支付功能降级为仅支持钱包/线下
|
||||
- **THEN** 系统记录 WARN 日志"无可用微信参数配置,第三方支付功能不可用"
|
||||
|
||||
---
|
||||
|
||||
### Requirement: 微信支付回调按配置验签
|
||||
|
||||
系统 SHALL 接收并处理微信支付成功通知。回调验签 MUST 使用订单关联的支付配置(而非当前生效配置)。
|
||||
|
||||
#### Scenario: 接收到合法的支付成功通知
|
||||
|
||||
```
|
||||
POST /api/callback/wechat-pay
|
||||
Content-Type: 由微信服务器决定
|
||||
无需认证
|
||||
```
|
||||
|
||||
- **WHEN** 微信回调端点收到支付成功通知
|
||||
- **THEN** 系统解析通知中的商户订单号(`out_trade_no`)
|
||||
- **THEN** 按订单号前缀分发(`ORD` → 套餐订单,`CRCH` → 资产充值,`ARCH` → 代理充值)
|
||||
- **THEN** 查询对应表记录,通过 `payment_config_id` 加载对应的微信参数配置
|
||||
- **THEN** 使用该配置的凭证通过 PowerWeChat SDK 验证回调签名
|
||||
- **THEN** 调用对应 Service 的 HandlePaymentCallback
|
||||
- **THEN** 返回成功响应
|
||||
|
||||
**成功响应**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": {
|
||||
"return_code": "SUCCESS"
|
||||
},
|
||||
"msg": "success",
|
||||
"timestamp": "2026-03-16T10:00:00+08:00"
|
||||
}
|
||||
```
|
||||
|
||||
#### Scenario: 订单关联的配置已被软删除
|
||||
|
||||
- **WHEN** 回调到达,但 `payment_config_id` 对应的配置已被软删除
|
||||
- **THEN** 系统使用 `GetByIDUnscoped` 加载该配置(软删除不影响回调处理)
|
||||
- **THEN** 正常完成验签和订单处理
|
||||
|
||||
#### Scenario: 重复回调幂等处理
|
||||
|
||||
- **WHEN** 微信多次发送同一订单的支付成功通知
|
||||
- **THEN** 系统通过幂等检查识别已支付,直接返回成功响应
|
||||
|
||||
#### Scenario: 回调签名验证失败
|
||||
|
||||
- **WHEN** 签名无效或被篡改
|
||||
- **THEN** PowerWeChat SDK 自动拒绝,系统记录 ERROR 日志,返回 HTTP 400
|
||||
|
||||
#### Scenario: 订单号不存在
|
||||
|
||||
- **WHEN** 回调中的商户订单号在系统中不存在
|
||||
- **THEN** 系统记录 ERROR 日志,返回失败响应
|
||||
Reference in New Issue
Block a user