Files
huang 817d0d6e04
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 46s
更新openspec
2026-03-17 14:22:01 +08:00

5.4 KiB
Raw Permalink Blame History

ADDED Requirements

Requirement: 富友支付公众号 JSAPI 下单

系统 SHALL 支持通过富友支付 wxPreCreate 接口发起微信公众号 JSAPI 支付。

本次留桩FuiouPayJSAPI 方法在 Service 层定义,但实际调用第三方获取支付参数的逻辑暂不实现,返回"富友支付发起暂未实现"错误。pkg/fuiou/ SDK 包完整实现。

Scenario: 公众号 JSAPI 下单成功

  • WHEN 系统调用富友 wxPreCreate 接口
    • trade_type=JSAPI
    • sub_appid=公众号AppID(从 tb_wechat_config.oa_app_id 读取)
    • sub_openid=用户公众号OpenID
    • 传入订单号、金额(分)、商品描述、终端 IP、回调地址
  • THEN 富友返回 result_code=000000,包含支付参数

富友返回支付参数结构

{
  "sdk_appid": "wx1234567890abcdef",
  "sdk_timestamp": "1711411341",
  "sdk_noncestr": "abc123def456",
  "sdk_prepayid": "wx26112221580621e9b071c00d9e093b0000",
  "sdk_package": "Sign=WXPay",
  "sdk_signtype": "RSA",
  "sdk_paysign": "..."
}
  • THEN 系统将支付参数返回给前端,前端调用 WeixinJSBridge.invoke('getBrandWCPayRequest', ...) 拉起支付

Scenario: 公众号 JSAPI 下单失败

  • WHEN 富友返回 result_code000000
  • THEN 系统记录 ERROR 日志(订单号、错误码、错误消息)
  • THEN 系统返回错误
{
  "code": 1173,
  "data": null,
  "msg": "支付发起失败,请重试",
  "timestamp": "2026-03-16T10:00:00+08:00"
}

Requirement: 富友支付小程序下单

系统 SHALL 支持通过富友支付 wxPreCreate 接口发起微信小程序支付。

Scenario: 小程序下单成功

  • WHEN 系统调用富友 wxPreCreate 接口
    • trade_type=LETPAY
    • sub_appid=小程序AppID(从 tb_wechat_config.miniapp_app_id 读取)
    • sub_openid=用户小程序OpenID
  • THEN 富友返回 result_code=000000,包含支付参数
  • THEN 系统将支付参数返回给前端,前端调用 wx.requestPayment(...) 拉起支付

Scenario: 小程序下单缺少 OpenID

  • WHEN 系统发起小程序支付但未传入 sub_openid
  • THEN 系统返回错误
{
  "code": 1001,
  "data": null,
  "msg": "小程序支付必须提供用户 OpenID",
  "timestamp": "2026-03-16T10:00:00+08:00"
}

Requirement: 富友支付回调处理

系统 SHALL 接收并处理富友支付成功回调通知,验证签名后更新订单/充值状态。

Scenario: 接收到合法的支付成功回调

POST /api/callback/fuiou-pay
Content-Type: application/x-www-form-urlencoded
无需认证

请求体格式req=<双重URL编码的GBK XML>

  • THEN 系统将请求体从 GBK 转换为 UTF-8
  • THEN 系统解析 XML 格式的回调数据
  • THEN 系统根据 mchnt_order_no 判断订单类型:
    • ORD 开头 → 套餐订单 → 查询 tb_order
    • CRCH 开头 → 资产充值 → 查询 tb_asset_recharge_record
    • ARCH 开头 → 代理充值 → 查询 tb_agent_recharge_record
  • THEN 通过记录的 payment_config_id 加载对应的富友配置
  • THEN 使用该配置的富友公钥验证 RSA 签名
  • THEN 验证 result_code=000000 且金额匹配
  • THEN 调用对应 Service 的 HandlePaymentCallback
  • THEN 返回成功 XML 响应GBK 编码)

成功响应

<?xml version="1.0" encoding="GBK"?>
<xml>
  <result_code>000000</result_code>
  <result_msg>success</result_msg>
</xml>

Scenario: 回调签名验证失败

  • WHEN 富友回调的 RSA 签名与本地计算不匹配
  • THEN 系统记录 ERROR 日志
  • THEN 返回失败 XML 响应
<?xml version="1.0" encoding="GBK"?>
<xml>
  <result_code>999999</result_code>
  <result_msg>signature verification failed</result_msg>
</xml>

Scenario: 回调订单号不存在

  • WHEN mchnt_order_no 在系统中不存在
  • THEN 系统记录 ERROR 日志,返回失败 XML 响应

Scenario: 重复回调幂等处理

  • WHEN 富友对同一订单多次发送支付成功回调
  • THEN 系统识别已支付,直接返回成功 XML 响应

Requirement: 富友 XML 通信协议

系统 SHALL 正确处理富友支付的 XML + GBK 编码通信协议。

Scenario: 请求编码

  • WHEN 系统向富友发送请求
  • THEN 请求体为 XML 格式GBK 编码声明
  • THEN XML 内容经 GBK 编码后进行两次 URL 编码
  • THENreq=<encoded_xml> 的 form 格式发送

Scenario: 响应解码

  • WHEN 系统接收富友响应
  • THEN 先进行 URL 解码
  • THEN 将 GBK 内容转换为 UTF-8
  • THEN 替换 XML 声明中的 encoding="GBK"encoding="UTF-8"
  • THEN 解析 XML 到结构体

Requirement: 富友 RSA 签名算法

系统 SHALL 实现富友支付的 RSA + MD5 签名验签算法。

Scenario: 生成请求签名

  • WHEN 系统需要对富友请求签名
  • THEN 提取所有非空字段(排除 signreserved_ 开头字段)
  • THEN 按字典序排列为 key=value&key=value 格式
  • THEN 将签名原文转换为 GBK 编码
  • THEN 计算 MD5 哈希
  • THEN 使用商户私钥对 MD5 哈希进行 RSA PKCS1v15 签名
  • THEN 对签名结果进行 Base64 编码

Scenario: 验证回调签名

  • WHEN 系统需要验证富友回调签名
  • THEN 使用相同算法计算签名原文的 MD5 哈希
  • THEN 使用富友公钥对回调中的 sign 字段进行 RSA PKCS1v15 验签