简介
本指南面向 DouPHP 框架的插件开发者,聚焦“插件单元测试、集成测试、第三方服务模拟、调试工具、日志最佳实践、常见错误排查、性能与安全测试”等主题。文档以现有支付插件(支付宝、微信支付)和登录插件(Google、微信登录)为示例,结合框架提供的 Provider/Service/DTO/Registry 体系,给出可操作的测试策略与排障方法。
项目结构
DouPHP 插件系统采用“契约 + 提供者 + 服务 + DTO + 注册表”的分层设计:
- 契约层:定义统一的 Provider 接口(支付、连接、可轮询、可对账)。
- 提供者层:具体插件实现(如 AlipayProvider、WxpayProvider),负责编排 Service 调用。
- 服务层:封装第三方 SDK 与业务逻辑(如 AlipayService、WxpayService)。
- DTO 层:标准化请求/响应数据载体(PaymentRequest、PaymentCallbackPayload、PaymentQueryRequest、PaymentQueryResult)。
- 注册表:自动发现并加载 manifest.php 声明的 Provider,注入容器。
graph TB
subgraph "契约"
I1["PaymentPluginProviderInterface"]
I2["ReconcilablePaymentProviderInterface"]
I3["PollablePaymentProviderInterface"]
end
subgraph "提供者"
P1["AlipayProvider"]
P2["WxpayProvider"]
end
subgraph "服务"
S1["AlipayService"]
S2["WxpayService"]
end
subgraph "DTO"
D1["PaymentRequest"]
D2["PaymentCallbackPayload"]
D3["PaymentQueryRequest"]
D4["PaymentQueryResult"]
end
subgraph "注册与配置"
R1["PaymentPluginRegistry"]
R2["ConnectPluginRegistry"]
V1["ManifestValidator"]
C1["PluginServiceProvider"]
CFG["Config"]
end
I1 --> P1
I1 --> P2
I2 --> P1
I2 --> P2
I3 --> P2
P1 --> S1
P2 --> S2
P1 --> D1
P1 --> D2
P1 --> D3
P1 --> D4
P2 --> D1
P2 --> D2
P2 --> D3
P2 --> D4
R1 --> P1
R1 --> P2
R2 --> P1
R2 --> P2
V1 --> R1
V1 --> R2
C1 --> R1
C1 --> R2
CFG --> S1
CFG --> S2
核心组件
- 支付契约与扩展
- PaymentPluginProviderInterface:统一 start/notify/finish 能力。
- ReconcilablePaymentProviderInterface:增加 query 主动对账能力。
- PollablePaymentProviderInterface:支持 Native 扫码轮询查询。
- 插件提供者与服务
- AlipayProvider/WxpayProvider:薄包装,委托到 Service。
- AlipayService/WxpayService:对接第三方 SDK,处理验签、状态机推进、回调与查询。
- DTO
- PaymentRequest:发起支付的入参(订单号、金额等)。
- PaymentCallbackPayload:异步/同步回调载荷。
- PaymentQueryRequest/PaymentQueryResult:对账查询的请求与结果模型。
- 注册与校验
- PaymentPluginRegistry/ConnectPluginRegistry:扫描 manifest.php 并实例化 Provider。
- ManifestValidator:白名单校验 manifest 键与 provider 命名空间。
- PluginServiceProvider:按需绑定真实或空实现,保证调用面稳定。
架构总览
下图展示一次“发起支付 → 异步通知 → 同步回跳 → 主动对账”的完整流程,覆盖支付宝与微信两种典型路径。
sequenceDiagram
participant Client as "客户端"
participant Reg as "PaymentPluginRegistry"
participant Prov as "Provider(Alipay/Wxpay)"
participant Svc as "Service(Alipay/Wxpay)"
participant SDK as "第三方SDK"
participant PS as "PaymentService"
Client->>Reg : 获取指定插件Provider
Reg-->>Client : Provider实例
Client->>Prov : start(PaymentRequest)
Prov->>Svc : start(request)
Svc->>SDK : 创建订单/生成支付参数
SDK-->>Svc : 支付链接/二维码/JS参数
Svc-->>Prov : HTML/URL
Prov-->>Client : 返回前端渲染内容
Note over Client,SDK : 用户完成支付后,第三方回调
SDK-->>Prov : notify(payload)
Prov->>Svc : notify(payload)
Svc->>PS : markSucceeded(paymentSn, transactionId, raw)
PS-->>Svc : 成功
Svc-->>Prov : success/fail
Client->>Prov : finish(payload)
Prov->>Svc : finish(payload)
Svc-->>Prov : 跳转URL
Prov-->>Client : 重定向到订单页
Note over Client,SDK : 定时轮询或对账任务
Client->>Prov : query(status/status_url)
Prov->>Svc : query(PaymentQueryRequest)
Svc->>SDK : 查询交易状态
SDK-->>Svc : 状态
Svc-->>Prov : PaymentQueryResult
Prov-->>Client : pending/success/closed/notFound
详细组件分析
支付宝插件(AlipayProvider/AlipayService)
- 职责边界
- Provider:暴露 pluginId/meta/start/notify/finish/query,委托 Service。
- Service:组装 SDK 请求、验签、调用 PaymentService 推进状态、构建对账查询。
- 关键行为
- start:构造页面支付请求,返回 HTML。
- notify:验签通过后标记支付成功。
- finish:验签成功后发送邮件并跳转订单页。
- query:根据 paymentSn 查询交易状态,映射为 succeeded/pending/closed/notFound。
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
-encodeRaw(data) string
-responseToArray(response) array|null
}
AlipayProvider --> AlipayService : "委托"
微信支付插件(WxpayProvider/WxpayService)
- 职责边界
- Provider:同时实现可轮询与可对账能力,委托 Service。
- Service:按 UA 选择 JSAPI/H5/Native;统一 notify;status 轮询;query 对账。
- 关键行为
- start:根据环境选择支付方式,返回按钮/二维码/跳转链接。
- notify:使用 SDK 验签并通过 PaymentService 推进状态。
- status:Native 扫码轮询,命中 SUCCESS 即推进状态。
- query:OrderQuery 查询,映射为 succeeded/pending/closed/notFound。
flowchart TD
Start(["进入 WxpayService.start"]) --> UA{"是否微信内置浏览器?"}
UA --> |是| JSAPI["startJsapi"]
UA --> |否| Mobile{"是否移动端?"}
Mobile --> |是| H5["startH5"]
Mobile --> |否| Native["startNative"]
JSAPI --> ReturnA["返回JS支付参数"]
H5 --> ReturnB["返回MWEB跳转"]
Native --> ReturnC["返回二维码+轮询脚本"]
登录插件(Google/Wxlogin)
- Google 登录 Provider 基于 OAuth2/OIDC,start 跳转授权页,finish 用 code 换 token 并拉取用户信息。
- 与支付 Provider 类似,通过 ConnectPluginRegistry 自动发现与实例化。
依赖关系分析
- 契约与实现解耦:Provider 仅依赖接口,便于替换与 Mock。
- 注册表集中管理:manifest.php 声明 provider FQCN,由 Registry 扫描并实例化。
- 配置注入:Service 通过 Config 读取插件配置,避免硬编码。
- 外部依赖:各 Service 内 require_once SDK 文件,隔离第三方库。
graph LR
M["manifest.php"] --> V["ManifestValidator"]
V --> R["PaymentPluginRegistry / ConnectPluginRegistry"]
R --> P["Provider(Alipay/Wxpay/Google)"]
P --> S["Service(Alipay/Wxpay)"]
S --> CFG["Config"]
S --> SDK["第三方SDK"]
性能考虑
- 延迟加载 SDK:Service 仅在需要时 require_once SDK 文件,减少启动开销。
- 轮询节流:Native 扫码轮询有最大次数限制,避免频繁请求。
- 对账幂等:notify/finish/query 均通过 PaymentService 推进状态,天然具备幂等性。
- 日志分级:SDK 日志可按级别输出,生产建议关闭低级别日志以减少 I/O。
故障排查指南
- 常见问题定位
- 配置缺失:Service 在 start 前检查必要配置项,缺失会抛出异常或返回失败。
- 验签失败:notify 中 SDK 验签失败直接返回 fail,需核对密钥与回调参数。
- 对账异常:query 返回 failed/notFound/pending,需查看第三方返回码与消息。
- 日志与调试
- 微信支付 SDK 日志:写入 storage/log/payment/wxpay/ 日期日志文件,便于回溯。
- SMTP 调试:可通过设置 Debugoutput 与 do_debug 级别观察 SMTP 握手过程。
- 最小化复现:参考 devtools 下的 smoke 脚本模式,独立引导框架并执行断言。
- 安全与权限
- manifest 校验:仅允许 plugin_group/provider 键,provider 必须位于 Dou\Plugin\ 命名空间。
- 回调安全:所有回调均需验签后再执行业务逻辑。
结论
DouPHP 插件系统通过清晰的契约与分层,使插件的单元测试与集成测试具备良好的切入点:以 Provider 为边界进行 Mock,以 Service 为核心验证业务逻辑,以 DTO 作为输入输出断言点。配合注册表的自动发现与 manifest 的安全校验,可在保证安全的前提下快速迭代与回归。对于第三方服务,优先通过本地日志与最小化 smoke 脚本进行问题定位与回归验证。
附录
单元测试编写要点
- 目标
- 验证 Provider 正确委托 Service。
- 验证 Service 对 SDK 的调用参数与返回值映射。
- 验证 DTO 的序列化/反序列化与边界值。
- 方法
- 使用接口与 DTO 进行隔离:对 PaymentPluginProviderInterface 进行 Mock,断言传入的 PaymentRequest/PaymentCallbackPayload。
- 针对 Service:Mock 第三方 SDK 类或拦截其网络调用,断言最终状态推进。
- 针对 Registry:构造不同 manifest.php 内容,校验白名单与命名空间限制。
- 参考路径
- PaymentPluginProviderInterface.php:24-66
- ReconcilablePaymentProviderInterface.php:24-49
- PaymentRequest.php
- PaymentCallbackPayload.php
- PaymentQueryRequest.php
- PaymentQueryResult.php
集成测试策略
- 场景
- 支付全流程:start → 回调 → finish → query。
- 轮询流程:Native 扫码 → 多次 status 查询 → 成功跳转。
- 登录流程:start → finish 获取用户信息。
- 方法
- 使用 smoke 脚本模式引导框架,构造真实路由与请求上下文。
- 使用内存/临时存储替代数据库与文件系统,确保可重复执行。
- 对第三方网络调用使用本地 mock 或代理。
- 参考路径
- ai-prompt-composer-smoke.php:15-70
- WxpayService.php:98-134
- AlipayService.php:122-172
模拟第三方服务的技巧
- 支付网关
- 使用本地 mock 返回固定报文,覆盖成功、失败、未找到、关闭等分支。
- 对验签逻辑,提供合法/非法签名用例,验证 reject 路径。
- 登录平台
- 模拟 OAuth 回调,构造不同 user info 与错误码,验证登录失败处理。
- 邮件服务
- 使用 SMTP 调试输出,捕获握手与命令交互,定位连接与认证问题。
调试工具与日志最佳实践
- 日志
- 微信支付 SDK:按天落盘,生产环境降低日志级别。
- SMTP:开启 DEBUG_CONNECTION/DEBUG_SERVER 辅助定位连接问题。
- 配置:通过 Config 统一管理开关与通道,避免硬编码。
- 工具
- Smoke 脚本:最小化引导框架,快速复现场景。
- 路由与中间件:利用框架路由与中间件记录请求链路。
- 参考路径
- WxpayService.php:447-455
- Smtp.php:102-127
- Config.php:23-64
- ai-prompt-composer-smoke.php:15-70
常见错误排查清单
- 配置不完整:检查 app_id、密钥、证书路径等。
- 验签失败:核对回调参数顺序、编码、密钥版本。
- 轮询超时:检查 status 接口频率与最大次数。
- 对账不一致:对比 query 返回码与业务状态机。
- 邮件发送失败:检查 SMTP 主机、端口、认证与 TLS。
性能测试与安全测试
- 性能测试
- 对 query/status 接口进行压测,关注第三方限流与重试策略。
- 评估日志 I/O 对吞吐的影响,合理设置日志级别与采样率。
- 安全测试
- 校验 manifest 白名单与命名空间限制,防止恶意 Provider 注入。
- 对所有回调进行验签与参数白名单校验,防止篡改。
- 敏感配置(密钥、证书)不落盘或加密存储。