简介
本指南面向在 DouPHP 框架中开发支付插件的开发者,围绕“支付插件接口、支付流程控制、回调处理机制”展开,结合支付宝、微信支付、PayPal 等现有插件的实现方式,说明如何创建自定义支付网关。内容覆盖支付参数配置、签名验证、退款处理、订单状态同步、主动对账与轮询查询、幂等性与异常处理等关键能力,并提供调试建议与最佳实践。
项目结构
DouPHP 将支付能力抽象为“核心服务 + 插件 Provider/Service + SDK”的分层结构:
- 核心服务:PaymentService 负责支付单台账、状态机推进、订单联动、退款分账、钱包支付腿等。
- 插件 Provider:对外暴露统一接口(start/notify/finish/query/status),屏蔽具体网关差异。
- 插件 Service:封装各支付渠道的具体业务逻辑(签名、请求构建、回调解析、对账/轮询)。
- 插件 manifest:声明插件分组与 Provider 类路径。
graph TB
A["前端/后台下单"] --> B["PaymentService<br/>创建支付单/推进状态"]
B --> C["插件 Provider<br/>AlipayProvider / WxpayProvider / PaypalProvider"]
C --> D["插件 Service<br/>AlipayService / WxpayService / PaypalService"]
D --> E["第三方SDK/网关"]
E --> |异步通知| C
C --> B
B --> F["OrderStatusTransition<br/>订单状态推进"]
核心组件
- PaymentService:单笔支付尝试台账、状态迁移、订单联动、退款分账、钱包支付腿、回调记录与凭证附件。
- PaymentStatus:支付单状态枚举与迁移表驱动校验。
- 插件 Provider:统一入口,承载 meta 配置、start/notify/finish/query/status 等能力。
- 插件 Service:具体网关对接(签名、请求、回调解析、对账/轮询)。
架构总览
支付主流程(以支付宝为例):
- 下单后调用 PaymentService.createForOrder 创建 pending 支付单。
- 通过 AlipayProvider.start -> AlipayService.start 生成跳转链接或表单。
- 用户完成支付后,支付宝异步通知到 AlipayProvider.notify -> AlipayService.notify。
- AlipayService 验签通过后,调用 PaymentService.markSucceeded 推进支付单成功并联动订单状态。
- 可选:主动对账 query 或扫码轮询 status,确保最终一致性。
sequenceDiagram
participant U as "用户"
participant OS as "订单系统"
participant PS as "PaymentService"
participant AP as "AlipayProvider"
participant AS as "AlipayService"
participant AZ as "支付宝网关"
U->>OS : 提交订单
OS->>PS : createForOrder(订单号, 金额, 网关)
PS-->>OS : 返回 payment_sn
OS->>AP : start(PaymentRequest)
AP->>AS : start()
AS->>AZ : 发起支付(含out_trade_no=payment_sn)
AZ-->>U : 跳转支付页
U->>AZ : 完成支付
AZ-->>AS : 异步通知(notify)
AS->>AS : 验签
AS->>PS : markSucceeded(payment_sn, trade_no)
PS-->>OS : 联动订单状态为已付款
AS-->>AP : 返回success
AP-->>AZ : 响应成功
详细组件分析
支付单与状态机(PaymentService + PaymentStatus)
- 支付单台账:每次 createForOrder 落库一行 order_payment,作为第三方 out_trade_no。
- 状态迁移:由 PaymentStatus 表驱动校验,避免非法迁移。
- 成功推进:markSucceeded 先更新支付单,累计网关已付金额,再尝试推进订单至 PAID;若未付满则保持部分支付。
- 钱包支付腿:markSucceededByWallet 扣余额、落成功支付单、累计 wallet_paid,并尝试收尾订单。
- 退款分账:refundOrder 按“网关优先、钱包在后”的顺序拆分退款,网关腿登记 pending 待人工/回调收尾,钱包腿直接退回余额。
- 退款收尾:markRefundSucceeded 标记退款成功,并回写支付腿状态(全额/部分退款)。
flowchart TD
Start(["进入 markSucceeded"]) --> Find["查找支付单"]
Find --> Found{"找到?"}
Found -- 否 --> LogErr["记录错误并返回false"]
Found -- 是 --> CheckState{"状态可迁移到成功?"}
CheckState -- 否 --> LogIllegal["记录非法迁移并返回false"]
CheckState -- 是 --> OrderCheck["检查订单是否可收款"]
OrderCheck --> UpdatePay["更新支付单为成功/记录交易号/时间"]
UpdatePay --> AccAmt["累计网关已付金额"]
AccAmt --> TryFinalize["tryFinalizeOrder 尝试推进订单"]
TryFinalize --> Done(["结束"])
支付宝插件(AlipayProvider + AlipayService)
- Provider 职责:声明插件元信息(名称、描述、客户端限制、配置项),转发 start/notify/finish/query。
- Service 职责:
- start:构建支付请求,设置 out_trade_no = payment_sn,返回跳转页面。
- notify:验签通过后,调用 PaymentService.markSucceeded 推进成功。
- finish:验签通过后,再次确认成功并发送邮件通知。
- query:主动对账,根据 trade_status 返回 succeeded/pending/closed。
- 配置项:APPID、应用私钥、支付宝公钥、回调地址等。
classDiagram
class AlipayProvider {
+pluginId() string
+meta() array
+start(request) string
+notify(payload) string
+finish(payload) string
+query(request) PaymentQueryResult
}
class AlipayService {
+start(request) string
+notify(payload) string
+finish(payload) string
+query(request) PaymentQueryResult
-buildConfig() array
}
AlipayProvider --> AlipayService : "委托业务逻辑"
微信支付插件(WxpayProvider)
- Provider 职责:声明插件元信息与配置项(AppID、AppSecret、商户号、API密钥),支持 start/notify/finish/query/status。
- 特点:支持 Native 扫码轮询(status)与主动对账(query),增强支付结果确定性。
PayPal 插件(PaypalProvider)
- Provider 职责:声明插件元信息与配置项(收款邮箱、货币),提供 start/notify/finish。
- 适用场景:跨境支付,货币选择灵活。
依赖关系分析
- PaymentService 依赖:
- 订单状态迁移:OrderStatusTransition(用于将订单推进到 PAID)。
- 钱包服务:WalletService(余额扣减与退款)。
- 支付流水号生成:PaymentSnGenerator。
- 插件 Provider 依赖:
- 各自 Service(AlipayService/WxpayService/PaypalService)。
- 插件 Service 依赖:
- 对应第三方支付 SDK。
- PaymentService(统一推进支付单与订单状态)。
graph LR
PS["PaymentService"] --> OST["OrderStatusTransition"]
PS --> WS["WalletService"]
PS --> PSG["PaymentSnGenerator"]
AP["AlipayProvider"] --> AS["AlipayService"]
WP["WxpayProvider"] --> WXS["WxpayService"]
PP["PaypalProvider"] --> PPS["PaypalService"]
AS --> PS
WXS --> PS
PPS --> PS
性能与可靠性
- 幂等性保障:
- 支付单唯一键(gateway + transaction_id)防止重复入账。
- markSucceeded 对已成功的支付单直接返回 true,避免重复联动订单。
- 并发安全:
- 钱包支付使用事务与行锁(FOR UPDATE)防止超扣。
- 容错与自愈:
- 金额比较使用容差常量避免浮点误差。
- 订单推进失败时保持支付单 pending,交由对账兜底。
- 可观测性:
- 记录 raw_callback 与 pay_evidence,便于问题定位与审计。
故障排查指南
- 常见回调问题:
- 验签失败:检查插件配置(私钥/公钥/密钥)与回调地址是否正确。
- 重复回调:利用幂等机制与 raw_callback 记录进行比对。
- 订单状态不同步:
- 检查 PaymentService.tryFinalizeOrder 是否因未付满而返回 false。
- 核对订单状态机是否允许从当前状态迁移到 PAID。
- 退款异常:
- 网关腿退款需管理员在网关后台完成后调用 markRefundSucceeded 收尾。
- 钱包腿退款失败需检查余额与事务日志。
结论
DouPHP 的支付体系通过 PaymentService 统一治理支付单状态与订单联动,插件 Provider/Service 解耦具体网关实现,具备幂等、可对账、可扩展的优势。基于此架构,开发者可以高效地接入支付宝、微信支付、PayPal 等渠道,并按需扩展自定义支付网关。
附录:自定义支付网关实现清单
- 实现 Provider:
- 实现 PaymentPluginProviderInterface 或 ReconcilablePaymentProviderInterface(如需对账/轮询)。
- 提供 pluginId、meta(配置项)、start、notify、finish、query(可选)、status(可选)。
- 实现 Service:
- start:构造支付请求,设置 out_trade_no = payment_sn,返回跳转或表单。
- notify:验签通过后调用 PaymentService.markSucceeded。
- finish:二次确认成功后可触发通知或页面跳转。
- query/status:实现主动对账或轮询,返回 PaymentQueryResult。
- 配置管理:
- 在 meta.config 中定义字段(如 app_id、key、证书等),并在 Service 中读取。
- 退款与售后:
- 网关退款登记为 pending,完成后调用 PaymentService.markRefundSucceeded。
- 钱包退款可直接退回余额并更新支付腿状态。
- 调试技巧:
- 启用 raw_callback 记录与 pay_evidence 附件。
- 使用 query/status 进行主动对账,确保最终一致。
- 关注 PaymentStatus 迁移是否合法,避免非法状态变更。