Skip to content

统一支付中心 ​

设计目标 ​

支付能力属于核心基础设施。业务插件只维护完整业务订单,通过稳定插件契约向核心提供可信金额和支付成功状态变更,禁止插件复制微信或支付宝签名、回调和密钥管理代码。

系统将三类数据分开:

数据数据表职责
业务订单plg_{plugin_id}_*商品、配送、库存、售后和业务状态
核心统一订单sys_finance_order财务列表、模块汇总和报表查询
支付交易sys_payment_order / sys_payment_attempt渠道请求、回调、幂等和支付状态

插件接入 ​

包含可支付订单的插件实现 PaymentOrderProvider:

go
type PaymentOrderProvider interface {
    GetPayableOrder(ctx context.Context, tx *gorm.DB, orderUID, memberSN string) (*PayableOrder, error)
    MarkOrderPaid(ctx context.Context, tx *gorm.DB, orderUID string, result PaidResult) error
}
  • GetPayableOrder 必须从插件数据库读取订单会员、真实金额和支付状态,禁止信任用户端金额。
  • MarkOrderPaid 必须使用原状态条件更新,例如 WHERE pay_status=1 AND status=1。
  • 核心支付中心禁止通过插件ID拼接表名或直接修改插件业务表。
  • 插件创建订单时实时调用 financeorder.Upsert;增量同步只承担历史数据修复。

支付状态和锁顺序 ​

第三方支付回调可能重复或并发到达,统一使用以下顺序:

  1. 验证签名、应用、商户和金额。
  2. 按 out_trade_no 查询支付尝试。
  3. 开启数据库事务。
  4. SELECT ... FOR UPDATE 锁定支付尝试。
  5. SELECT ... FOR UPDATE 锁定统一支付单。
  6. 已成功的同一支付直接幂等返回。
  7. 不同渠道重复支付记录第三方流水并标记待人工退款,不重复更新业务订单,同时向渠道确认已接收以停止重试。
  8. 条件更新支付尝试和统一支付单。
  9. 调用插件 MarkOrderPaid。
  10. 条件更新 sys_finance_order,提交后向第三方返回成功。

request_uid、out_trade_no、支付单号和业务来源关系均有数据库唯一索引。金额使用 decimal 和字符串处理,不使用浮点数。

支付场景 ​

场景值场景可用用户端
1微信小程序微信小程序
2微信JSAPI微信公众号、微信内H5
3微信H5微信外普通手机浏览器
4微信APPiOS、Android
5支付宝小程序支付宝小程序
6支付宝H5微信外普通手机浏览器
7支付宝APPiOS、Android

微信小程序不返回支付宝渠道。微信内H5不直接拉起支付宝,只返回微信JSAPI;支付宝支付需要引导用户在系统浏览器打开,不通过URL Scheme绕过平台限制。

用户端流程 ​

  1. 业务页面请求可用支付渠道。
  2. 使用核心 AppPaymentSheet 展示后端返回的渠道。
  3. 用户确认后创建支付尝试。
  4. 根据 launch_type 调用 uni.requestPayment 或H5跳转。
  5. 客户端支付调用结束后查询服务端支付状态。
  6. 只有服务端返回支付成功才展示支付成功结果。

安全约束 ​

  • 支付创建与状态查询接口必须登录,并验证订单属于当前会员。
  • 回调接口不使用会员令牌,必须完成渠道签名校验。
  • 商户私钥、APIv3 Key和平台公钥不通过接口返回。
  • 支付回调原文只保存脱敏摘要,限制请求体大小。
  • 支付网络请求设置超时,数据库事务中不执行第三方网络请求。
  • 支付配置按渠道、支付场景和用户端渠道三重过滤。