更新openspec
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 46s

This commit is contained in:
2026-03-17 14:22:01 +08:00
parent b44363b335
commit 817d0d6e04
16 changed files with 2271 additions and 10 deletions

View File

@@ -227,3 +227,185 @@
- **WHEN** 微信支付和公众号使用相同的 AppID
- **THEN** 系统复用同一个 Redis Cache 实例
- **THEN** Token 缓存 Key 相同,避免重复获取
---
## MODIFIED Requirements (from: add-payment-config-management)
### 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 日志,返回失败响应