简介
本文件面向电商平台开发者,系统化梳理 DouPHP 电商系统的支付结算数据模型,围绕以下目标展开:
- 明确支付记录、支付配置、钱包账户、充值/余额流水等核心表的设计与用途。
- 解释多种支付方式集成、支付状态同步、退款处理、对账结算的业务逻辑在数据层如何落地。
- 给出支付安全验证、防重复提交、异步通知处理的关键机制与数据支撑。
- 提供支付异常处理与资金安全保障的技术方案建议。
说明:仓库中未直接出现名为 dou_payment、dou_payment_config、dou_recharge 的表定义;实际实现采用 order_payment、money、user_wallet 等表承载支付流水、钱包余额与充值入账。下文以仓库内真实表为准进行设计与解读。
项目结构与范围
- 数据库定义集中在系统表结构 SQL 与各模块备份 SQL 中。
- 支付核心服务位于 core/service/payment/PaymentService.php,前台收银台在前端 service/order/CashierService.php,后台订单审核在 admin/service/order/.../OrderService.php。
- 支付插件契约在 module/plugin/core/infra/plugin/contract/PaymentPluginProviderInterface.php,具体网关如微信支付在 plugin/wxpay/WxpayService.php。
- 钱包与余额流水在 user 模块的 WalletService.php 及 user.sql 中的 user_wallet 表。
graph TB
A["前端收银台<br/>CashierService"] --> B["支付服务<br/>PaymentService"]
B --> C["订单表<br/>dou_order"]
B --> D["支付流水表<br/>dou_order_payment"]
B --> E["钱包与余额流水<br/>dou_money / dou_user_wallet"]
A --> F["支付插件接口<br/>PaymentPluginProviderInterface"]
F --> G["微信插件<br/>WxpayService"]
H["后台订单审核<br/>OrderService"] --> B
核心数据模型总览
- 订单主表:dou_order,承载订单金额、支付方式、支付时间、物流信息等。
- 支付流水表:dou_order_payment,承载每笔支付腿(含混合支付的多条记录)、状态机、第三方流水号、回调原文、过期时间等。
- 钱包余额表:dou_user_wallet,用户余额与积分的快照视图,便于快速查询。
- 余额流水表:dou_money,记录余额变动明细,作为权威流水源。
- 充值包表:dou_money_package,定义充值套餐价格与赠送金额,用于购买后入账。
erDiagram
DOU_ORDER ||--o{ DOU_ORDER_PAYMENT : "一对多"
DOU_USER_WALLET ||--|| DOU_USER : "一对一(以用户ID为主键)"
DOU_MONEY }o--|| DOU_USER : "按用户聚合"
DOU_MONEY_PACKAGE ||--o{ DOU_MONEY : "购买后产生入账流水"
架构与流程概览
- 发起支付:前台收银台创建支付流水(pending),并调用支付插件生成支付请求或页面。
- 异步通知:支付网关回调通过插件 notify() 进入 PaymentService::markSucceeded(),推进 payment 到 succeeded,并联动订单状态机推进到 paid/completed。
- 轮询兜底:部分场景(如扫码支付)支持主动查询状态,命中成功则同样走 markSucceeded。
- 线下凭证:货到付款或线下付款时,管理员审核后调用 markSucceeded 完成支付闭环。
- 钱包入账:若为充值包购买,订单支付成功后触发钱包入账,写入 dou_money 并更新 dou_user_wallet。
sequenceDiagram
participant U as "用户"
participant F as "前台收银台"
participant P as "支付服务"
participant O as "订单表"
participant W as "钱包服务"
participant G as "支付网关"
U->>F : 提交订单并选择支付方式
F->>P : 创建支付流水(pending)
F->>G : 生成支付请求/跳转
G-->>F : 同步回跳/展示结果
G-->>P : 异步回调(可能延迟/重发)
P->>O : 推进订单状态至已支付/完成
P-->>F : 返回支付结果
Note over P,G : 幂等:已成功的支付仅做收尾
alt 购买充值包
P->>W : 触发钱包入账
W->>W : 写入余额流水并更新快照
end
详细表结构设计
支付流水表:dou_order_payment
- 作用:记录每一笔支付腿(支持一单多付),承载支付状态机、第三方交易号、回调原文、过期时间等。
- 关键字段
- payment_sn:支付流水号,唯一索引,用于幂等控制与审计。
- order_id/order_sn:关联订单。
- gateway:支付网关标识(如 wxpay、alipay、balancepay、cod 等)。
- amount:支付金额。
- status:支付状态(pending/succeeded/closed 等),由状态机驱动。
- transaction_id:第三方交易号,唯一约束避免重复入账。
- pay_evidence:支付凭证(线下上传)。
- raw_request/raw_callback:请求与回调原文,用于对账与排障。
- paid_at/expired_at/add_time:时间戳字段。
- 索引策略
- payment_sn 唯一索引保证幂等。
- gateway+transaction_id 唯一索引防止重复交易号。
- status+expired_at、status+gateway+add_time 复合索引用于超时扫描与对账。
flowchart TD
Start(["创建支付流水"]) --> CheckOrder{"订单可支付?"}
CheckOrder -- 否 --> Reject["拒绝并记录原因"]
CheckOrder -- 是 --> Insert["插入 dou_order_payment(status=pending)"]
Insert --> CallGateway["调用支付插件发起支付"]
CallGateway --> WaitNotify["等待异步回调/轮询"]
WaitNotify --> Notify{"收到成功回调?"}
Notify -- 是 --> MarkSuccess["markSucceeded: 更新状态=success, 写paid_at, 保存回调"]
MarkSuccess --> UpdateOrder["推进订单状态到已支付/完成"]
Notify -- 否 --> Timeout{"是否超时?"}
Timeout -- 是 --> Close["关闭支付腿"]
Timeout -- 否 --> WaitNotify
钱包账户表:dou_user_wallet
- 作用:用户余额与积分的快照视图,提升查询性能;权威数据来自 dou_money/dou_point 流水。
- 关键字段
- money_balance/money_freeze:可用余额与冻结余额。
- point_balance/point_freeze:可用积分与冻结积分。
- total_consumption/total_promote:累计消费与推广收益。
- updated_at:最后更新时间。
- 使用方式
- 余额变动通过 WalletService::createMoney() 写入 dou_money,并 upsert 更新 user_wallet 快照。
- 升级脚本会基于历史流水初始化该表。
classDiagram
class UserWallet {
+id : 用户ID
+money_balance : 余额
+money_freeze : 冻结余额
+point_balance : 积分
+point_freeze : 冻结积分
+total_consumption : 累计消费
+total_promote : 累计推广
+updated_at : 更新时间
}
class MoneyLog {
+id : 自增ID
+user_id : 用户ID
+action : 动作
+money : 变动金额
+total : 变更后余额
+from : 来源
+source_type/source_id : 业务来源
+create_time : 时间
}
UserWallet <.. MoneyLog : "由流水聚合更新"
余额流水表:dou_money
- 作用:记录所有余额变动的权威流水,包括充值、消费、奖励、退款等。
- 关键字段
- action:动作标识(如 recharge、order_pay、order_refund、money_package 等)。
- money:变动金额(可为负)。
- total:变动后的累计余额。
- from/from_user_id:来源单号/来源用户。
- source_type/source_id:业务来源类型与主键,便于溯源。
- price/sale_price/sale_price_type:购买价格信息(如充值包)。
- item_id:关联商品/套餐 ID。
- 使用方式
- 充值包购买成功后,WalletService 根据订单项写入 dou_money,并更新 user_wallet 快照。
充值包表:dou_money_package
- 作用:定义充值套餐的价格与赠送金额,供下单购买后入账。
- 关键字段
- name/price/promote_price:套餐名称、原价、促销价。
- money:充值到账金额。
- brief/image/sort:描述、图片、排序。
订单主表:dou_order(与支付相关字段)
- 关键字段
- pay_id:支付方式标识(如 wxpay、balancepay、cod 等)。
- pay_evidence:支付凭证路径(线下上传)。
- order_amount/item_amount/shipping_fee:订单金额构成。
- pay_time:支付完成时间。
- status:订单状态(与支付状态机联动)。
依赖关系分析
- 前台收银台 CashierService 负责创建支付流水、发起支付、处理线下凭证与货到付款。
- 支付服务 PaymentService 负责状态机推进、幂等控制、订单状态联动、对账兜底。
- 支付插件 PaymentPluginProviderInterface 统一抽象 start/notify/finish/status 能力,各网关实现差异。
- 钱包服务 WalletService 在订单支付成功后,针对充值包场景写入余额流水并更新快照。
- 后台订单 OrderService 提供线下付款审核入口,调用 markSucceeded 完成支付闭环。
graph LR
CS["CashierService"] --> PS["PaymentService"]
PS --> OP["dou_order_payment"]
PS --> OR["dou_order"]
PS --> WS["WalletService"]
WS --> WM["dou_money"]
WS --> UW["dou_user_wallet"]
PI["PaymentPluginProviderInterface"] --> WX["WxpayService"]
OS["OrderService(后台)"] --> PS
性能与一致性设计
- 幂等性
- payment_sn 唯一索引保障重复回调不重复入账。
- gateway+transaction_id 唯一索引防止同一第三方交易号重复处理。
- PaymentService::markSucceeded 对已成功的 payment 直接做收尾,不重复落库。
- 事务与顺序
- 先推进订单状态(包含库存扣减、积分发放、分销奖励等高风险联动),再标记 payment succeeded,避免不可逆终态与订单状态错配。
- changeStatus 内部失败时订单不进 PAID,payment 保持 pending,交由对账任务兜底。
- 并发与锁
- 钱包余额写入使用行级锁(FOR UPDATE)确保余额计算一致。
- 索引优化
- 支付流水表针对 status+expired_at、status+gateway+add_time 建立复合索引,提高超时扫描与对账效率。
安全与异常处理
- 安全验证
- 插件接口统一接收回调载荷,校验签名与参数完整性后再进入业务处理。
- 原始请求与回调报文保存在 raw_request/raw_callback,便于审计与排障。
- 防重复提交
- 通过 payment_sn 与 transaction_id 双重唯一约束,结合幂等处理逻辑,避免重复入账。
- 异步通知处理
- 支持异步回调与主动查询(如微信 Native 扫码轮询),命中成功即推进支付状态。
- 异常处理
- 非法状态迁移会被拒绝并记录日志。
- 订单不可支付时,payment 保持 pending,留给对账或人工处理。
对账、退款与结算
- 对账
- 利用 status+expired_at 与 status+gateway+add_time 索引,定时扫描超时与未确认支付,结合第三方查询接口进行对账。
- 通过 raw_callback 与 transaction_id 比对,确保账务一致。
- 退款
- 退款通常对应余额流水 action 为 order_refund,写入 dou_money 并更新 user_wallet 快照;支付流水可记录退款关联信息(扩展字段或关联退款单)。
- 结算
- 订单支付成功后,若为充值包购买,触发钱包入账;其他商品或服务类订单按业务规则结算给商家或平台。
- 后台订单审核提供线下付款确认入口,调用 markSucceeded 完成支付闭环。
常见问题排查
- 支付成功但订单未推进
- 检查 PaymentService::markSucceeded 是否被调用,查看日志中“order not payable”或状态机拒绝原因。
- 核对订单状态是否为可支付状态(pending/awaiting_confirmation/paid/completed)。
- 重复回调导致重复入账
- 检查 payment_sn 与 transaction_id 的唯一约束是否生效,确认幂等逻辑是否执行。
- 余额不一致
- 核对 dou_money 流水与 dou_user_wallet 快照是否一致,必要时重新初始化快照。
- 线下付款审核无效
- 确认后台调用了 markSucceeded,且订单存在待审核的支付记录。
结论
DouPHP 的支付结算数据模型以 dou_order_payment 为核心,配合 dou_order、dou_money、dou_user_wallet 等表,实现了多支付方式接入、状态机驱动的状态同步、幂等与对账兜底、钱包余额与充值入账的完整闭环。通过插件化接口与严格的状态迁移控制,系统在安全性、一致性与可扩展性方面具备良好基础。开发者可在此基础上扩展更多支付方式、完善退款与结算流程,并结合对账任务保障资金安全。