💰 支付基础能力
SunAdmin 支付能力分为两层:
System提供支付基础能力:统一支付单、退款单、回调日志、支付结果分发、业务处理器注册。- 支付通道由独立模块提供:例如微信支付模块注册
WechatPay驱动,后续支付宝等通道也按同一方式扩展。
框架本身不直接依赖某个支付渠道。没有安装对应支付模块时,业务仍可创建自己的订单,但发起该渠道支付会得到“支付驱动未安装或未启用”的明确提示。
本页讲支付基础能力的组成、驱动契约、默认驱动选择和回调流程。业务模块如何调用支付、注册处理器、走退款流程详见 支付业务对接。
🎯 本页目标
读完本章节后,应能掌握:
- 支付基础能力由哪些服务、注册表与数据表组成。
- 支付通道模块需要实现哪些契约才能被识别和调用。
- 默认支付驱动如何在后台维护、如何被业务读取。
- 渠道回调如何从支付模块流向 System 基础能力再分发给业务模块。
🧱 核心组成
| 能力 | 位置 | 说明 |
|---|---|---|
| 支付单服务 | modules/System/Application/Services/Pay/PayOrderService.php | 创建支付单、发起支付、主动查单、写统一状态 |
| 退款服务 | modules/System/Application/Services/Pay/PayRefundService.php | 创建退款单、提交退款、处理退款中心操作 |
| 支付驱动接口 | PaymentDriverInterface.php | 约束支付模块必须实现的 pay、refund、syncPaid 和后台配置能力 |
| 支付驱动注册表 | PaymentDriverRegistry.php | 从 config('sunadmin.pay.drivers') 读取已启用模块注册的驱动 |
| 默认驱动解析 | PaymentDriverResolver.php | 读取“应用接入 / 支付接入”中的默认支付驱动,业务未指定时自动补齐 |
| 渠道回调分发 | PaymentCallbackRegistry.php | 分发支付、退款等渠道回调 |
| 业务结果分发 | PayBusinessRegistry.php | 把已支付、已退款事件交给业务模块处理 |
| 退款中心 | modules/System/.../Finance/Refund* | 后台“财务管理 / 退款中心”统一处理退款单 |
🗃️ 数据表
| 表 | 说明 |
|---|---|
pay_orders | 统一支付单,记录业务归属、支付驱动、渠道流水和支付状态 |
pay_refunds | 统一退款单,支持部分退款和多次退款 |
pay_callback_logs | 渠道回调日志,保存验签结果、原始载荷和处理状态 |
🔌 支付驱动
支付驱动是支付通道模块暴露给框架的统一入口。业务模块不直接调用微信、支付宝或其他 SDK,而是调用 PayOrderService。业务没有显式传入 driver 时,系统会读取“应用接入 / 支付接入”中选择的默认支付驱动,并写入 pay_orders.driver。
use Modules\System\Application\Services\Pay\Contracts\PaymentDriverInterface;
use Modules\System\Domain\Models\Pay\PayOrder;
class ExamplePaymentDriver implements PaymentDriverInterface
{
public function driver(): string
{
return 'example';
}
public function name(): string
{
return '示例支付';
}
public function channels(): array
{
return ['mini', 'mp', 'h5', 'app'];
}
public function configured(): bool
{
return true;
}
public function configFields(): array
{
return [];
}
public function configValues(): array
{
return [];
}
public function saveConfig(array $data): void
{
// 保存支付模块自己的商户号、密钥、证书等参数。
}
public function pay(PayOrder $order, array $extraPayload = []): array
{
// 调用第三方预支付接口,返回前端调起支付需要的数据。
}
public function refund(PayOrder $order, array $data = []): array
{
// 调用第三方退款接口,返回渠道退款受理结果。
}
public function syncPaid(PayOrder $order): array
{
// 主动查单,返回 paid、payload、query 等标准字段。
}
}支付模块在自己的服务提供者中注册驱动:
namespace Modules\ExamplePay\Providers;
use Illuminate\Support\ServiceProvider;
use Modules\ExamplePay\Application\Services\Pay\ExamplePaymentDriver;
class ExamplePayServiceProvider extends ServiceProvider
{
public function register(): void
{
$drivers = config('sunadmin.pay.drivers', []);
$drivers[] = ExamplePaymentDriver::class;
config()->set('sunadmin.pay.drivers', array_values(array_unique($drivers)));
}
}只有模块启用后,模块服务提供者才会加载,对应支付驱动才会进入 PaymentDriverRegistry。
⚙️ 默认支付驱动
默认支付驱动在后台“支付接入”中维护:
应用接入 / 支付接入 / 默认支付驱动配置项保存到:
configs.group = pay
configs.key = default_driver支付模块安装后会把自己的驱动注册到 PaymentDriverRegistry,支付接入页会动态读取已安装驱动作为下拉选项。页面上方选择默认支付驱动,下方按支付模块生成配置标签页。业务模块只需要调用统一支付服务,不需要在每个业务里写死微信支付、支付宝支付或其他支付通道。
🧩 微信支付模块
微信支付由独立模块提供,是支付驱动的参考实现:
# 安装微信支付模块:注册 wechat 驱动、提供微信支付配置项、写入模块状态
php artisan module:install WechatPay安装后提供:
| 能力 | 说明 |
|---|---|
| 支付驱动 | wechat |
| 后台配置入口 | 应用接入 / 支付接入 / 微信支付 |
| 配置分组 | wechat_pay |
| 支付回调 | POST /api/wechat-pay/notify/pay |
| 退款回调 | POST /api/wechat-pay/notify/refund |
微信支付配置读取 .env 中的 APP_URL 生成回调地址,当 APP_URL 为空则自动获取当前服务器部署域名地址。测试环境或内网穿透环境都应填写微信可访问的 HTTP 域名:
APP_URL=https://example.com修改 APP_URL 后,如果项目启用了配置缓存,需要清理缓存:
# 清理 Laravel 配置、路由、视图等优化缓存,让新的 APP_URL 生效
php artisan optimize:clear🔁 回调流程
支付模块负责渠道验签、解密和标准化,System 支付基础能力负责写统一支付单、统一退款单和分发业务处理器。
业务模块不需要自己写渠道回调入口,也不要在业务模块里手写微信验签和解密。业务模块对接已支付、已退款事件的方式详见 支付业务对接 / 业务处理器。
✨ 接入规范
- 所有业务支付先写
pay_orders。 out_trade_no使用pay_orders.order_no。- 业务单号写入
biz_no。 - 业务类型写入
biz_type。 - 业务场景写入
scene。 - 业务模块通常不需要传
driver,支付基础能力会按系统配置写入默认支付驱动。 - 特殊业务需要覆盖默认驱动时,可以显式传入
driver,例如wechat。 - 支付通道写入
channel,例如mini、mp、h5、app、scan。 - 支付模块负责第三方 SDK、配置页、验签解密和渠道回调入口。
- System 支付基础能力负责统一支付单、统一退款单、回调日志和业务分发。
- 业务模块只注册自己的业务处理器,不把业务逻辑写入 System。
- 回调中的业务处理必须幂等。
- 支付回调域名来自
.env的APP_URL,不要依赖前端请求域名。
