- 后端
- 金融科技
【免费下载链接】pay
可能是我用过的最优雅的 Alipay/WeChat/Unipay/江苏银行 的支付 SDK 扩展包了
本文基于仓库根目录的 AGENTS.md 展开。这份文档是 yansongda/pay 项目面向开发者(及 AI Agent)的工程约定总纲,规定了项目的目录结构、核心组件、安全原则、开发命令与验签体系。读懂它,你不仅能掌握这套 PHP 多支付网关 SDK 的插件管道架构与回调验签全貌,还能按仓库既定规范独立完成一个支付网关 Provider 的完整扩展。
一、项目概览:以插件管道为核心的多网关支付 SDK
AGENTS.md 对项目的一句话定义是:PHP 支付 SDK,支持支付宝、微信、银联、抖音、江苏银行、PayPal、Stripe,基于插件管道架构,关注支付流程、回调验签和 Provider 扩展一致性。
结合仓库现状可以看到:
- 从 composer.json 可知,项目要求 PHP >= 8.2,依赖
yansongda/artful(管道/容器/事件底层)与yansongda/supports(Collection、Config 等支持类); - src/ 下内置了 11 个 Provider:Alipay、Wechat、Unipay、Jsb、Douyin、Allinpay、Bestpay、Apple、Airwallex、Paypal、Stripe,其入口类均在 src/Provider/ 下;
- 每个 Provider 的完整能力由「配置类 + Provider 类 + 插件集 + Shortcut + Trait」五部分协同提供,这一结构正是 AGENTS.md 中「代码一致性」原则要求新增网关时必须遵循的骨架。
二、目录结构:各层职责一目了然
AGENTS.md 给出的src/结构总览如下:
src/ ├── Config/ # Typed Config(WechatConfig、AlipayConfig 等) ├── Event/ # PSR-14 事件 ├── Plugin/{Provider}/V{n}/ ├── Provider/ # Alipay、Wechat 等 ├── Shortcut/{Provider}/ ├── Traits/ # {Provider}Trait、ProviderConfigTrait ├── CertManager.php # 证书缓存 └── Pay.php对照实际源码,各目录的职责可以进一步说明:
| 目录/文件 | 职责 | 典型证据 |
|---|---|---|
| src/Config/ | 每个 Provider 一个类型化配置类(如AlipayConfig、WechatConfig),并各自实现validate() | AlipayConfig.php、WechatConfig.php |
| src/Provider/ | Provider 门面类,统一实现ProviderInterface,暴露pay/query/cancel/close/refund/callback/success等能力 | Alipay.php、Wechat.php |
| src/Plugin/ | 按{Provider}/V{n}/组织版本化插件,是管道中执行具体动作的最小单元 | Alipay/V2/Pay/、Wechat/V3/Pay/ |
| src/Shortcut/ | {Action}Shortcut将「一次调用」编排为一串插件列表 | Alipay/WebShortcut.php |
| src/Traits/ | 静态工具方法:网关 URL 构建、回调验签、配置获取 | AlipayTrait.php、WechatTrait.php |
| src/Event/ | PSR-14 事件定义:CallbackReceived、MethodCalled、PayStart/PayEnd、HttpStart/HttpEnd | CallbackReceived.php |
| src/CertManager.php | 证书读取、解析与静态缓存 | 见第七节详解 |
| src/Pay.php | 全局静态入口,负责配置注入与各 Provider 服务注册 | 见第六节详解 |
三、核心组件速查
AGENTS.md 列出了一张核心组件表,这里逐项结合源码位置加以印证:
| 符号 | 位置 | 用途 |
|---|---|---|
Pay::alipay() | src/Pay.php | 支付入口 |
Pay::wechat() | src/Pay.php | 微信入口 |
ProviderInterface | src/Contract/ | 核心接口(pay/query/cancel/close/refund/callback/success) |
Rocket | Yansongda\Artful\Rocket | 承载 params/payload/radar/destination |
CertManager | src/CertManager.php | 证书读取与缓存 |
Event | src/Event.php | PSR-14 事件分发 |
{Provider}Config | src/Config/ | 类型化配置对象 |
关于Pay::alipay()这类入口方法的真实形态:Pay.php 中通过__callStatic实现,任意Pay::xxx()静态调用都会先从容器取出对应服务;而 11 个 Provider 的服务实例,是在Pay::config()执行时通过$providers数组逐个Artful::load()注入的(见 Pay.php 与 Pay.php),每个 ServiceProvider 继承自 src/Service/AbstractServiceProvider.php,在register()中把 Provider 实例同时注册为「类名」和「Provider 名」(如alipay)两个键。
ProviderInterface则是所有 Provider 的统一契约,见 src/Contract/ProviderInterface.php:pay(array $plugins, array $params)接收插件列表与参数,其余query/cancel/close/refund均以订单数组为入参,callback()同时兼容array与ServerRequestInterface两种回调内容。
四、两条核心开发原则
AGENTS.md 将开发原则浓缩为两条:
- 安全第一:回调/Webhook 必须验签,禁止直接信任传入数据。这一点在整个代码库中是强制执行的。以支付宝为例,src/Plugin/Alipay/CallbackPlugin.php 的
assembly()在解析业务数据前先完成 RSA2 验签;以抖音为例,src/Plugin/Douyin/V1/CallbackPlugin.php 中如果回调参数缺少_request或其不是ServerRequestInterface实例,会直接抛出InvalidParamsException,根本走不到业务解析环节。 - 代码一致性:新增 Provider 遵循现有结构。具体的六阶段流程在
.agents/skills/dev-guide/SKILL.md中展开(见下节)。
五、开发命令与代码规范
AGENTS.md 将「COMMANDS & 开发规范」指向.agents/skills/dev-guide/SKILL.md。该指南规定,对src/PHP 代码的任何修改,需运行以下三项检查:
composer cs-fix && composer analyse && composer test各命令的具体含义(与 composer.json 中scripts一一对应):
| 命令 | 作用 | 说明 |
|---|---|---|
composer test | 运行 PHPUnit | PHPUnit 11.x + Mockery 1.6;测试前需安装hyperf/pimple;用 Mock HTTP 客户端避免真实请求 |
composer cs-fix | 代码格式化检查 | php-cs-fixer 的 dry-run 模式,带 diff 输出 |
composer analyse | 静态分析 | PHPStan,-l 6级别,300M 内存上限,作用于./src |
文档站开发命令(VitePress):
cd web && pnpm web:dev # 文档开发 cd web && pnpm web:build # 文档构建 cd web && pnpm web:serve # 本地预览代码规范要点:
- 每个 PHP 文件必须
declare(strict_types=1);(可在任意 src 文件开头确认); - 必须使用
use导入,禁止在代码中直接书写\Yansongda\Pay\...完整命名空间; - 日志与异常消息统一使用中文(例如 CertManager.php 中的
'配置异常: 解析证书失败'); - 命名约定:
{Action}Plugin.php、{Action}Shortcut.php、{Provider}Trait.php。
新增 Provider 的六阶段流程(源自 SKILL.md,可结合仓库现状逐一对照):
- 配置:
src/Config/{Provider}Config.php; - 核心类:
src/Provider/{Provider}.php+src/Service/{Provider}ServiceProvider.php(继承AbstractServiceProvider); - 插件:
src/Plugin/{Provider}/V{n}/(必须含 CallbackPlugin); - 快捷方式:
src/Shortcut/{Provider}/{Action}Shortcut.php; - Trait:
src/Traits/{Provider}Trait.php(提供get{Provider}Url、verify{Provider}Sign); - 注册:
Pay.php的$providers数组 +Config.php的PROVIDERS映射 +Exception.php常量 + 测试 + 文档。
其中第 6 步可以在 src/Pay.php(Provider 常量与服务提供者清单)和 src/Config.php(PROVIDERS名单)中直接看到对应的注册点。
六、架构要点深度解析
6.1 插件管道:一次调用的完整流转
AGENTS.md 给出的管道模型是:
StartPlugin → [前置] → 业务插件 → [后置] → ParserPlugin
以最典型的支付宝电脑支付为例,src/Shortcut/Alipay/WebShortcut.php 中getPlugins()返回的插件链完整印证了这一结构:
return [ StartPlugin::class, // 装载配置、初始化 Rocket PayPlugin::class, // 业务插件:构造下单报文 FormatPayloadBizContentPlugin::class, // 前置:格式化 biz_content AddPayloadSignaturePlugin::class, // 后置:签名 AddRadarPlugin::class, // 后置:挂载调试雷达信息 ResponseHtmlPlugin::class, // 组装 HTML 表单响应 ParserPlugin::class, // 终态:解析/收尾 ];管道中数据流动的载体是 AGENTS.md 表中提到的Rocket(来自Yansongda\Artful\Rocket),它贯穿始终,承载params(入参)、payload(请求体)、radar(调试信息)与destination(最终响应)。每个插件实现统一的assembly(Rocket $rocket, Closure $next)签名,通过修改 Rocket 后调用$next($rocket)将控制权交给下一个插件。
6.2 动态调用:__call→ Shortcut → 管道
AGENTS.md 指出「Provider 通过__call调用 Shortcut,再进入管道」。以 src/Provider/Alipay.php 为例:
public function __call(string $shortcut, array $params): Collection|MessageInterface|Rocket|null { $shortcut = strtolower($shortcut); $plugin = '\Yansongda\Pay\Shortcut\Alipay\\'.Str::studly($shortcut).'Shortcut'; return Artful::shortcut($plugin, ...$params); }即Pay::alipay()->web($order)会自动映射到Shortcut\Alipay\WebShortcut,再由 Artful 框架按该 Shortcut 声明的插件列表执行管道。显式方法(query、cancel、close、refund)则先派发MethodCalled事件再委托给__call,见 Alipay.php。而pay(array $plugins, array $params)是手动指定插件列表的底层入口,直接调用Artful::artful(),见 Alipay.php。
6.3 配置系统:数组自动转为类型化 Config 对象
AGENTS.md 提到「Config类自动将数组配置转为{Provider}Config对象」。实现位于 src/Config.php:构造函数遍历PROVIDERS名单,对items中每个 Provider 下的每个租户(default或多租户名)执行match,实例化对应的WechatConfig/AlipayConfig等类型化对象并替换原始数组。此后任意插件通过getProviderConfig(Pay::PROVIDER_ALIPAY, $params)获取配置时(见 src/Traits/ProviderConfigTrait.php),都能拿到已具备validate()能力的强类型对象。多租户能力由此而来——getTenant($params)从_config参数读取租户名,缺省为default。
6.4 事件系统:PayListener 桥接 Artful 事件 → Pay 事件
AGENTS.md 概括为「PayListener桥接 Artful 事件 → Pay 事件」。注册时机在 Pay.php:Pay::config()成功配置 Artful 后,立即监听四个底层事件:
Event::addListener(ArtfulStart::class, [PayListener::class, 'artfulStart']); Event::addListener(ArtfulEnd::class, [PayListener::class, 'artfulEnd']); Event::addListener(HttpStart::class, [PayListener::class, 'httpStart']); Event::addListener(HttpEnd::class, [PayListener::class, 'httpEnd']);src/PayListener.php 中四个监听方法分别将其转发为 Pay 层事件:ArtfulStart → PayStart、ArtfulEnd → PayEnd、HttpStart/HttpEnd → Pay\{HttpStart,HttpEnd}。此外,Provider 在关键动作处还会主动派发业务事件:每次query/cancel/close/refund派发MethodCalled,每次callback()进入时派发CallbackReceived(携带 provider 名、原始回调内容与参数,见 src/Event/CallbackReceived.php)。开发者可以基于这些 PSR-14 事件挂载审计、日志或统计逻辑,无需侵入插件代码。
七、签名验证体系(必须掌握)
回调验签是支付 SDK 的安全底线。AGENTS.md 给出的各 Provider 验签方式对照表如下:
| Provider | 方式 | Trait 方法 |
|---|---|---|
| 支付宝 | RSA 本地验证 | AlipayTrait::verifyAlipaySign() |
| 微信 | 证书签名本地验证 | WechatTrait::verifyWechatSign() |
| PayPal | API 验证 | PaypalTrait::verifyPaypalWebhookSign() |
| Stripe | HMAC-SHA256 本地 | StripeTrait::verifyStripeWebhookSign() |
| 银联 | 证书签名本地验证 | UnipayTrait::verifyUnipaySign() |
| 江苏银行 | RSA 本地验证 | JsbTrait::verifyJsbSign() |
| 抖音 | SHA1 本地验证 | CallbackPlugin::verifySign() |
| 通联支付 | RSA-SHA1 本地验证 | AllinpayTrait::verifyAllinpaySign() |
各方法在源码中的实际位置可以进一步确认:
- 支付宝:src/Traits/AlipayTrait.php
verifyAlipaySign(AlipayConfig, string $contents, string $sign),回调侧由 CallbackPlugin.php 先把参数「除去sign/sign_type后按字典序组串」再验签; - 微信:src/Traits/WechatTrait.php
verifyWechatSign(),另有 V2 版本verifyWechatSignV2()(同文件 #L185),并内置时间戳校验verifyWechatTimestamp()(#L479); - Stripe:src/Traits/StripeTrait.php 从
Stripe-Signature请求头解析t(时间戳)与v1签名再本地 HMAC 验证,缺少webhook_secret配置会直接抛出配置异常; - PayPal:src/Traits/PaypalTrait.php
verifyPaypalWebhookSign(),采用 API 验证方式; - 银联:src/Traits/UnipayTrait.php
verifyUnipaySign(),另有针对二维码(QRA)通道的verifyUnipaySignQra()(#L110)。
关于抖音一栏,从源码结构看可以更精确地描述:抖音的验签实际发生在 src/Plugin/Douyin/V1/CallbackPlugin.php 的assembly()中,委托给DouyinTrait::verifyDouyinTradeSign($request, $config)(见 src/Traits/DouyinTrait.php),即在插件内部完成,而非独立的 Trait 静态入口——使用上无差别,均遵循「先验签、后解析」的顺序。
八、回调处理差异:_request与Collection两种形态
AGENTS.md 特别强调了回调处理的一个重要差异:
- Wechat/Stripe/Paypal:把
_request(ServerRequestInterface)传入 CallbackPlugin; - Alipay/Douyin/Unipay/Jsb/Allinpay:通过
getCallbackParams()获取Collection。
源码印证如下:
- 微信:src/Provider/Wechat.php 的
callback()中,getCallbackParams()的返回类型是ServerRequestInterface(见 #L193-L199),它把body/headers形态的数组重新包装成ServerRequest,最终以['_request' => $request, '_params' => $params]送入管道。原因是微信 V3 回调是 JSON 报文 + 头部签名(Wechatpay-Signature等),验签必须基于原始请求体与请求头,Collection形态会丢失这些信息; - 支付宝:src/Provider/Alipay.php 的
getCallbackParams()返回Collection,按GET/POST方法、body/headers转发形态或全局请求三种来源解析参数。支付宝 form 回调只需键值对即可完成 RSA 组串验签,因此Collection已足够。
进一步从源码结构看,这个二分法在仓库中还有延伸:Douyin、Allinpay、Airwallex、Bestpay、Apple的callback()同样以['_request' => $request, ...]形式传入完整请求(例如 src/Provider/Douyin.php),这与它们「需要原始 body 或头部才能验签」的特性一致。对回调接口的实现者而言,可以归纳出两条使用规则:
- 若网关验签依赖原始报文/请求头(微信、Stripe、PayPal、抖音、Apple 等),框架内部会自动构造并传递
ServerRequestInterface,业务侧只需保证把框架要求的请求对象交给callback(); - 若网关验签基于表单参数组串(支付宝、银联、江苏银行等),参数会被归一化为
Collection参与验签。
两条路径最终都收敛到同一个success()响应约定:验签与业务处理后,按 Provider 各自的应答规范返回(如支付宝返回字符串success,微信 V3 返回{"code":"SUCCESS","message":"成功"},见 src/Provider/Wechat.php)。
九、Trait 参考与证书管理
AGENTS.md 的 Trait 参考表按用途归纳了各 Provider Trait 中的方法族:
| 用途 | 方法(示例) |
|---|---|
| 配置获取 | ProviderConfigTrait::getProviderConfig()、getTenant()、getRadarUrl() |
| URL 构建 | get{Provider}Url()(各 Trait) |
| 签名验证 | verify{Provider}Sign()、verify{Provider}WebhookSign() |
| 证书管理 | CertManager::getPublicCert()、getPrivateCert() |
| 微信专用 | decryptWechatResource()、reloadWechatPublicCerts() |
其中:
getProviderConfig()与getTenant()的完整实现见 src/Traits/ProviderConfigTrait.php,它同时是「Provider 名 + 租户名 → 类型化配置」的唯一入口,配置缺失时抛出带 Provider 与租户名的InvalidConfigException;- URL 构建方法签名统一为
get{Provider}Url(ProviderConfig, ?Collection $payload): string,如 AlipayTrait.php(支付宝还额外提供 V3 专用的getAlipayV3Url(),#L141); - 微信专用方法
decryptWechatResource()(AES-256-GCM 解密回调 resource)与reloadWechatPublicCerts()(拉取并缓存微信平台证书,支持按序列号指定)均位于 src/Traits/WechatTrait.php 与 #L297。
AGENTS.md 提示「完整方法列表见src/Traits/{Provider}Trait.php」,11 个 Trait 文件均位于 src/Traits/。
CertManager:证书读取与缓存的实现细节
src/CertManager.php 是各 Trait 验签、加签共用的证书底座,几个值得注意的实现点:
- 静态缓存:所有读取都经过
getCachedContent()(#L212-L223),缓存键为{type}_{sha1($key)},避免同一请求内重复读文件、重复解析证书; - 私钥多形态兼容:
getPrivateCert()(#L44-L59)接受三种输入——文件路径、完整 PEM 字符串(以-----BEGIN PRIVATE KEY-----开头则原样使用)、或裸 base64 私钥(自动包裹成 RSA PEM 并折行); - 支付宝证书 SN 提取:
alipayGetAppCertSn()/alipayGetRootCertSn()解析证书 issuer + serialNumber 生成 SN,根证书还支持多证书拼接文件; - PKCS12 支持:
getPkcs12Certs()/unipayGetPkcs12Certs()(#L135-L159)用密码读取.pfx/.p12证书包,供银联、翼支付等 PKCS12 体系的 Provider 共用,读取失败抛出「配置异常: 读取证书失败,确认参数是否正确」; - 微信按租户隔离的证书缓存:
$wechatCerts[$tenant][$serialNo] = $cert(#L189-L210),回调验签时根据Wechatpay-Serial头部快速定位平台证书,多租户互不干扰; - 缓存随
Pay::clear()一并清理(见 Pay.php 中的CertManager::clearCache()调用)。
十、上手路径:从入口到管道的一次完整走查
把 AGENTS.md 的约定串起来,一次典型的支付调用链路如下,每一环都可在仓库中定位:
Pay::config($config)—— 数组配置被 src/Config.php 转为类型化 Config 对象,11 个 ServiceProvider 完成注册(src/Pay.php);Pay::alipay()->web($order)——__callStatic取出AlipayProvider,__call('web')映射到WebShortcut(src/Provider/Alipay.php);- Shortcut 声明插件链,Artful 逐个执行
StartPlugin → PayPlugin → 签名 → Radar → Response → ParserPlugin(src/Shortcut/Alipay/WebShortcut.php),全程以 Rocket 传递数据,AddRadarPlugin把调试信息挂在结果上; - 回调侧
Pay::alipay()->callback()先派发CallbackReceived事件,再由CallbackPlugin完成验签(src/Provider/Alipay.php、src/Plugin/Alipay/CallbackPlugin.php); - 开发完成后按第五节的
composer cs-fix && composer analyse && composer test三项检查收尾,测试用例分布在 tests/ 下与 src 同构的目录中(如 tests/Provider/AlipayTest.php、tests/Plugin/Alipay/CallbackPluginTest.php)。
最后补充一点适用前提:本文基于当前仓库(yansongda/artful ~1.2.0、yansongda/supports ~4.1.0、PHP >= 8.2)的实际代码结构撰写;Rocket、Artful 容器等底层实现位于外部依赖包中,仓库内只体现其调用面。若需要逐行核对,建议从 AGENTS.md 列出的符号出发,按本文给出的文件路径在仓库内直接查看。
- 后端
- 金融科技
【免费下载链接】pay
可能是我用过的最优雅的 Alipay/WeChat/Unipay/江苏银行 的支付 SDK 扩展包了
相关推荐
yansongda/pay 支付 SDK 架构与扩展开发指南:插件管道、多 Provider 统一契约与回调验签全解析
yansongda/pay 支付 SDK 架构与扩展开发指南:插件管道、多 Provider 统一契约与回调验签全解析 本篇技术指南以仓库根目录下的 AGENT
金融科技后端yansongda/pay 支付宝回调接收指南:异步通知验签与 ISV 应用网关验证
yansongda/pay 支付宝回调接收指南:异步通知验签与 ISV 应用网关验证 本指南聚焦 yansongda/pay 中支付宝(Alipay)回调的完整
金融科技后端Yansongda Pay 微信确认回调指南:success() 方法与响应插件体系全解析
Yansongda Pay 微信确认回调指南:success 方法与响应插件体系全解析 本指南以 Yansongda Pay 微信支付模块的"确认回调"能力为核
金融科技后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考