## 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 日志,返回失败响应