简介
本文件面向DouPHP框架的支付插件体系,系统性阐述“Provider与Service分离”的设计模式、统一的支付接口抽象、支付流程状态管理、生命周期与事件机制、错误处理策略,以及插件注册、配置管理与依赖注入等关键技术点。文档以微信支付与支付宝两个现有插件为例,说明如何遵循框架规范实现自定义支付网关。
项目结构
DouPHP将支付能力以“插件”形式组织在 plugin 目录下,每个支付插件通常包含:
- Provider:对外暴露统一接口(start/notify/finish/query/status),负责路由到具体Service。
- Service:承载具体业务逻辑,调用第三方SDK,并与框架PaymentService协作推进订单与支付单状态。
- manifest.php:插件元数据与配置项定义(由框架加载)。
- sdk/:各支付渠道SDK及辅助类。
graph TB
subgraph "插件层"
WXProv["微信支付 Provider"]
WXSvc["微信支付 Service"]
ALProv["支付宝 Provider"]
ALSvc["支付宝 Service"]
end
subgraph "框架核心"
PaySvc["PaymentService<br/>状态机/对账/回调"]
DTO["DTO: PaymentRequest/PaymentCallbackPayload/PaymentQueryRequest/PaymentQueryResult"]
IFace["接口: ReconcilablePaymentProviderInterface / PollablePaymentProviderInterface"]
end
WXProv --> IFace
ALProv --> IFace
WXProv --> WXSvc
ALProv --> ALSvc
WXSvc --> PaySvc
ALSvc --> PaySvc
WXProv --> DTO
ALProv --> DTO
核心组件
- Provider(提供者)
- 职责:声明插件ID、元信息(名称、描述、版本、适用客户端、配置字段)、实现统一接口方法(start/notify/finish/query/status)。
- 特点:薄封装,仅做参数转换与委托给Service;便于框架通过接口多态调度不同支付渠道。
- Service(服务)
- 职责:加载并初始化SDK、组装请求、发起支付、处理回调与查询、调用框架PaymentService推进状态。
- 特点:集中业务逻辑,屏蔽第三方差异;通过依赖注入获取PaymentService与配置。
- DTO(数据传输对象)
- PaymentRequest:发起支付所需上下文(如订单号、金额、支付单号等)。
- PaymentCallbackPayload:回调载荷(含原始请求数据)。
- PaymentQueryRequest/PaymentQueryResult:主动对账的请求与结果封装。
- 接口契约
- ReconcilablePaymentProviderInterface:支持主动对账(query)。
- PollablePaymentProviderInterface:支持轮询查询(status,如Native扫码)。
架构总览
支付插件采用“接口+Provider+Service”的分层架构:
- 框架通过统一接口识别并调用Provider,Provider再委派给Service执行具体逻辑。
- Service与框架PaymentService交互,完成支付单与订单的状态推进,保证一致性。
- 通过DTO传递结构化数据,避免散落的数组/字符串耦合。
- 插件通过manifest.php声明配置项,运行时由框架注入或读取。
sequenceDiagram
participant Client as "客户端/前端"
participant Prov as "Provider"
participant Svc as "Service"
participant Pay as "PaymentService"
participant SDK as "第三方支付SDK"
Client->>Prov : start(PaymentRequest)
Prov->>Svc : start(request)
Svc->>SDK : 创建支付订单/生成跳转URL
SDK-->>Svc : 返回支付参数/URL
Svc-->>Prov : 返回渲染内容/跳转地址
Prov-->>Client : 展示二维码/按钮/跳转
Note over Client,Pay : 异步回调/轮询/主动对账
SDK-->>Svc : notify(payload)
Svc->>Pay : markSucceeded(paymentSn, transactionId, raw)
Pay-->>Svc : 状态推进结果
Svc-->>SDK : 响应成功/失败
Client->>Prov : query(PaymentQueryRequest)
Prov->>Svc : query(request)
Svc->>SDK : 查询交易状态
SDK-->>Svc : 返回状态
Svc-->>Prov : PaymentQueryResult
Prov-->>Client : 返回查询结果
详细组件分析
微信支付插件(wxpay)
- 角色与职责
- WxpayProvider:实现ReconcilablePaymentProviderInterface与PollablePaymentProviderInterface,提供pluginId/meta/start/notify/finish/status/query。
- WxpayService:根据UA自动选择JSAPI/H5/NATIVE三种支付方式;统一notify验签后调用PaymentService推进状态;status用于Native扫码轮询;query用于主动对账。
- 关键流程
- start:按UA分发至JSAPI/H5/Native;构造统一订单参数;返回页面/按钮/二维码与轮询脚本。
- notify:使用SDK验签,提取paymentSn与transactionId,调用PaymentService标记成功。
- status:按paymentSn查询微信订单,若SUCCESS则标记成功并返回状态。
- query:封装为PaymentQueryResult,区分成功/关闭/待支付/未找到等状态。
- 配置与日志
- buildConfig从插件配置读取APPID/MCHID/KEY/APPSECRET等,并设置证书路径与代理。
- initLog写入存储目录下的日志文件,便于排障。
classDiagram
class WxpayProvider {
+pluginId() string
+meta() array
+start(PaymentRequest) string
+notify(PaymentCallbackPayload) string
+finish(PaymentCallbackPayload) string
+status(PaymentCallbackPayload) string
+query(PaymentQueryRequest) PaymentQueryResult
}
class WxpayService {
+start(PaymentRequest) string
+notify(PaymentCallbackPayload) string
+finish(PaymentCallbackPayload) string
+status(PaymentCallbackPayload) string
+query(PaymentQueryRequest) PaymentQueryResult
-buildConfig() array
-bootSdk(files) void
-initLog() void
}
WxpayProvider --> WxpayService : "委托"
支付宝插件(alipay)
- 角色与职责
- AlipayProvider:实现ReconcilablePaymentProviderInterface,提供pluginId/meta/start/notify/finish/query。
- AlipayService:构建支付页面请求;notify/finish验签后调用PaymentService标记成功;query进行主动对账,封装PaymentQueryResult。
- 关键流程
- start:加载SDK并生成PC端支付页面URL。
- notify:验签通过后,依据out_trade_no(即paymentSn)与trade_no调用markSucceeded。
- finish:二次验签与幂等处理,成功后发送邮件通知并返回订单页。
- query:根据trade_status映射为成功/关闭/待支付/未找到等结果。
sequenceDiagram
participant C as "客户端"
participant P as "AlipayProvider"
participant S as "AlipayService"
participant A as "支付宝SDK"
participant PS as "PaymentService"
C->>P : start(PaymentRequest)
P->>S : start(request)
S->>A : pagePay(生成支付页面)
A-->>S : 返回HTML/URL
S-->>P : 返回渲染内容
P-->>C : 展示支付页面
A-->>S : notify(payload)
S->>PS : markSucceeded(paymentSn, tradeNo, raw)
PS-->>S : 状态推进完成
S-->>A : success/fail
支付流程状态管理
- 统一入口:所有支付渠道通过Provider.start进入,Service内部决定具体渠道行为。
- 回调处理:Service在notify中完成验签与幂等校验,随后调用PaymentService.markSucceeded推进支付单与订单状态。
- 轮询与对账:
- 轮询:如微信Native扫码,通过status周期性查询并推进状态。
- 主动对账:通过query拉取第三方状态,封装为PaymentQueryResult供上层消费。
- 结果归一化:Service将不同渠道的差异结果映射为标准状态(成功/关闭/待支付/未找到/失败)。
flowchart TD
Start(["开始"]) --> Choose["根据渠道/UA选择支付方式"]
Choose --> CreateOrder["创建支付订单/生成支付参数"]
CreateOrder --> Render["渲染支付页面/二维码/跳转"]
Render --> Wait{"等待支付结果"}
Wait --> |异步回调| Notify["notify 验签与处理"]
Wait --> |轮询| Status["status 查询并推进"]
Wait --> |主动对账| Query["query 查询并封装结果"]
Notify --> Mark["调用 PaymentService.markSucceeded"]
Status --> Check{"是否成功?"}
Check --> |是| Mark
Check --> |否| EndFail["返回失败/继续轮询"]
Query --> ReturnRes["返回 PaymentQueryResult"]
Mark --> End(["结束"])
EndFail --> End
ReturnRes --> End
依赖关系分析
- 插件与框架
- Provider依赖框架定义的接口(ReconcilablePaymentProviderInterface、PollablePaymentProviderInterface)与DTO类型。
- Service依赖框架PaymentService进行状态推进,依赖BaseService获取通用能力(如配置、日志)。
- 插件内部
- Provider仅持有Service实例,通过构造函数注入,保持低耦合。
- Service内通过buildConfig读取插件配置,通过bootSdk按需加载第三方SDK,降低启动开销。
- 外部依赖
- 第三方支付SDK(微信/支付宝)仅在需要时加载,减少内存占用。
- 日志写入存储目录,便于运维监控。
graph LR
Prov["Provider"] --> IFace["框架接口"]
Prov --> Svc["Service"]
Svc --> PaySvc["PaymentService"]
Svc --> SDK["第三方SDK"]
Svc --> Log["日志系统"]
性能考量
- 按需加载SDK:Service在必要时才require SDK文件,避免全局加载带来的内存与启动开销。
- 轮询节流:微信Native扫码在前端控制轮询频率与次数,降低服务器压力。
- 日志分级:日志级别与路径可控,避免过大日志影响IO。
- 幂等与去重:notify与finish均进行验签与重复检查,防止重复入账。
- 配置缓存:buildConfig从插件配置读取,建议结合框架缓存机制提升读取性能(可在Service层扩展)。
故障排查指南
- 配置不完整
- 现象:start或query时报错提示配置缺失。
- 排查:检查manifest中配置的字段是否已正确填写(如APPID、密钥等)。
- 回调验签失败
- 现象:notify返回fail或无状态推进。
- 排查:确认回调URL、签名算法、密钥一致;查看日志文件定位问题。
- 轮询无结果
- 现象:status一直返回FAIL或未推进。
- 排查:核对paymentSn是否正确;检查网络与第三方接口返回;查看日志。
- 主动对账异常
- 现象:query返回failed或notFound。
- 排查:根据返回码判断是否为未找到或错误码;记录raw数据以便复现。
- 日志位置
- 微信支付日志:存储于storage/log/payment/wxpay/日期.log。
- 其他渠道日志:可参考各自Service中的日志路径与级别。
结论
DouPHP支付插件通过“接口+Provider+Service”的分层设计,实现了支付能力的解耦与可扩展性。Provider负责统一入口与元信息,Service专注业务逻辑与第三方集成,并通过PaymentService保障状态一致性与事务安全。基于该架构,开发者可以便捷地接入新的支付渠道,同时复用框架提供的配置管理、依赖注入、日志与对账能力。
附录
- 插件开发要点清单
- 实现对应接口(ReconcilablePaymentProviderInterface、可选PollablePaymentProviderInterface)。
- 在Provider中实现pluginId、meta、start、notify、finish、query(及status)。
- 在Service中实现业务逻辑,调用PaymentService推进状态。
- 在manifest中声明配置项,确保运行时可用。
- 做好日志记录与异常处理,便于排障。
- 注意幂等与安全性(验签、防重放)。
- 常见最佳实践
- 使用DTO传递数据,避免散落参数。
- 对第三方响应进行归一化处理。
- 合理设置超时与重试策略。
- 对敏感配置进行加密或隔离存储。