Laravel 最优雅的支付系统
摘要
本文全面解析 Laravel 支付系统的构建方法,包括架构设计、多支付网关集成、最佳实践和扩展功能。通过本文,您将学习:
- Laravel 支付系统的核心架构和设计模式
- 如何使用 Omnipay、Laravel Pay 和 Cashier 等流行支付包
- 构建统一支付网关抽象层的具体实现
- 支付系统的安全考虑、性能优化和可维护性
- 如何集成多种支付方式(支付宝、微信支付、PayPal 等)
- 订阅支付、支付分账和多币种支持等高级功能
本文适合希望在 Laravel 项目中构建或优化支付系统的开发者,提供了详细的技术指南和实战示例。
1. 引言
在现代 Web 应用中,支付系统是电子商务和订阅服务的核心组件。一个优雅的支付系统不仅要功能完善,还要易于集成、扩展和维护。Laravel 作为 PHP 生态中最流行的框架之一,提供了丰富的工具和包来构建高质量的支付系统。
本文将介绍如何在 Laravel 中构建一个最优雅的支付系统,包括架构设计、常用包推荐、具体实现和最佳实践。如果您还不熟悉 Laravel 的基本概念,可以参考我们的 Laravel 12 教程 - Web 开发实战入门 文章,了解 Laravel 的核心功能和使用方法。
2. Laravel 支付系统架构设计
2.1 Laravel 支付系统核心组件
一个优雅的 Laravel 支付系统应该包含以下核心组件:
- 支付网关抽象层:统一不同支付渠道的接口
- 订单管理:处理订单创建、状态更新等
- 支付记录:跟踪每笔交易的详细信息
- 退款处理:处理退款请求和状态更新
- 通知处理:处理支付渠道的异步通知
- 支付验证:验证支付的有效性和安全性
2.2 Laravel 支付系统架构模式
推荐使用以下架构模式:
- 策略模式:用于不同支付方式的实现,通过统一接口隔离不同支付网关的具体实现细节
- 工厂模式:用于创建不同支付网关实例,根据配置或用户选择动态创建相应的支付网关
- 观察者模式:用于处理支付状态变化的事件,如支付成功后发送通知、更新订单状态等
- 仓库模式:用于数据访问层的抽象,简化数据操作并提高可测试性
设计模式应用示例
策略模式 + 工厂模式组合使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| interface PaymentGatewayInterface { public function pay(array $params); public function refund(array $params); }
class AlipayGateway implements PaymentGatewayInterface { public function pay(array $params) { } public function refund(array $params) { } }
class WechatGateway implements PaymentGatewayInterface { public function pay(array $params) { } public function refund(array $params) { } }
class PaymentGatewayFactory { public static function create($gateway) { switch ($gateway) { case 'alipay': return new AlipayGateway(); case 'wechat': return new WechatGateway(); default: throw new \InvalidArgumentException("Unsupported gateway: {$gateway}"); } } }
|
观察者模式应用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class PaymentStatusChanged { public $payment; public $oldStatus; public $newStatus; public function __construct($payment, $oldStatus, $newStatus) { $this->payment = $payment; $this->oldStatus = $oldStatus; $this->newStatus = $newStatus; } }
class PaymentNotificationListener { public function handle(PaymentStatusChanged $event) { if ($event->newStatus === 'succeeded') { $this->sendNotification($event->payment); } } protected function sendNotification($payment) { } }
Event::listen(PaymentStatusChanged::class, PaymentNotificationListener::class);
|
3. Laravel 推荐的支付包
3.1 Laravel Omnipay 集成
Omnipay 是一个功能强大的 PHP 支付处理库,提供了统一的支付网关接口。
特点
- 支持 100+ 种支付网关
- 统一的 API 接口
- 易于扩展和定制
- 活跃的社区支持
安装
1
| composer require league/omnipay
|
3.2 Laravel Pay 集成
Laravel Pay 是基于 Omnipay 开发的 Laravel 支付扩展包,提供了更符合 Laravel 风格的 API。
特点
- 符合 Laravel 优雅风格
- 支持多种支付方式
- 内置支付记录管理
- 提供简洁的配置方式
安装
1
| composer require yansongda/laravel-pay
|
3.3 Laravel Cashier 集成
Cashier 是 Laravel 官方提供的订阅计费管理包,专为处理订阅服务设计。
特点
- 官方维护,与 Laravel 深度集成
- 支持 Stripe 和 Paddle
- 内置订阅管理功能
- 自动处理发票和付款
安装
1 2 3 4 5
| composer require laravel/cashier
composer require laravel/cashier-paddle
|
4. 构建优雅的 Laravel 支付系统
4.1 Laravel 支付系统目录结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| app/ ├── Http/ │ ├── Controllers/ │ │ └── PaymentController.php │ ├── Middleware/ │ └── Requests/ │ └── PaymentRequest.php ├── Models/ │ ├── Order.php │ ├── Payment.php │ └── Refund.php ├── Services/ │ └── Payment/ │ ├── Gateways/ │ │ ├── AlipayGateway.php │ │ ├── WechatGateway.php │ │ └── PayPalGateway.php │ ├── Contracts/ │ │ └── GatewayInterface.php │ ├── Factory.php │ ├── PaymentService.php │ └── Events/ │ ├── PaymentCreated.php │ ├── PaymentSucceeded.php │ └── PaymentFailed.php └── Providers/ └── PaymentServiceProvider.php
|
4.2 Laravel 支付网关抽象层
首先,定义支付网关接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <?php
namespace App\Services\Payment\Contracts;
interface GatewayInterface {
public function create(array $params);
public function handleCallback($data);
public function refund(array $params);
public function query(array $params); }
|
然后,实现具体的支付网关:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| <?php
namespace App\Services\Payment\Gateways;
use App\Services\Payment\Contracts\GatewayInterface; use Omnipay\Omnipay;
class AlipayGateway implements GatewayInterface { protected $gateway; public function __construct() { $this->gateway = Omnipay::create('Alipay_AopApp'); $this->gateway->setAppId(config('payment.alipay.app_id')); $this->gateway->setPrivateKey(config('payment.alipay.private_key')); $this->gateway->setAppId(config('payment.alipay.app_id')); $this->gateway->setAlipayPublicKey(config('payment.alipay.public_key')); $this->gateway->setSignType('RSA2'); $this->gateway->setNotifyUrl(route('payment.alipay.notify')); $this->gateway->setReturnUrl(route('payment.alipay.return')); } public function create(array $params) { $order = [ 'out_trade_no' => $params['order_no'], 'total_amount' => $params['amount'], 'subject' => $params['subject'], 'body' => $params['body'] ?? $params['subject'], ]; $response = $this->gateway->purchase($order)->send(); return $response->getData(); } public function handleCallback($data) { $response = $this->gateway->completePurchase([ 'request_params' => $data ])->send(); if ($response->isSuccessful() && $response->isTradeStatusOk()) { return [ 'success' => true, 'order_no' => $data['out_trade_no'], 'transaction_id' => $data['trade_no'], 'amount' => $data['total_amount'], ]; } return ['success' => false]; } public function refund(array $params) { $response = $this->gateway->refund([ 'out_trade_no' => $params['order_no'], 'refund_amount' => $params['amount'], 'out_request_no' => $params['refund_no'], ])->send(); return $response->getData(); } public function query(array $params) { $response = $this->gateway->query([ 'out_trade_no' => $params['order_no'], ])->send(); return $response->getData(); } }
|
4.3 Laravel 支付工厂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <?php
namespace App\Services\Payment;
use App\Services\Payment\Gateways\AlipayGateway; use App\Services\Payment\Gateways\WechatGateway; use App\Services\Payment\Gateways\PayPalGateway;
class Factory { public static function make($gateway) { switch (strtolower($gateway)) { case 'alipay': return new AlipayGateway(); case 'wechat': return new WechatGateway(); case 'paypal': return new PayPalGateway(); default: throw new \InvalidArgumentException("Unsupported gateway: {$gateway}"); } } }
|
4.4 Laravel 支付服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
| <?php
namespace App\Services\Payment;
use App\Services\Payment\Events\PaymentCreated; use App\Services\Payment\Events\PaymentSucceeded; use App\Services\Payment\Events\PaymentFailed; use App\Models\Order; use App\Models\Payment;
class PaymentService {
public function createPayment($orderId, $gateway, $amount, $extra = []) { $order = Order::findOrFail($orderId); $payment = Payment::create([ 'order_id' => $order->id, 'gateway' => $gateway, 'amount' => $amount, 'order_no' => $this->generateOrderNo(), 'status' => 'pending', 'extra' => $extra, ]); event(new PaymentCreated($payment)); $gatewayInstance = Factory::make($gateway); $result = $gatewayInstance->create([ 'order_no' => $payment->order_no, 'amount' => $amount, 'subject' => $order->subject, 'body' => $order->body, ...$extra, ]); $payment->update(['payload' => $result]); return [ 'payment' => $payment, 'gateway_data' => $result, ]; }
public function handleCallback($gateway, $data) { $gatewayInstance = Factory::make($gateway); $result = $gatewayInstance->handleCallback($data); if ($result['success']) { $payment = Payment::where('order_no', $result['order_no'])->first(); if ($payment && $payment->status === 'pending') { $payment->update([ 'status' => 'succeeded', 'transaction_id' => $result['transaction_id'], 'completed_at' => now(), ]); $payment->order->update(['status' => 'paid']); event(new PaymentSucceeded($payment)); } } else { event(new PaymentFailed($data)); } return $result; }
public function refund($paymentId, $amount, $reason = '') { $payment = Payment::findOrFail($paymentId); if ($payment->status !== 'succeeded') { throw new \Exception('Only succeeded payments can be refunded'); } $refundNo = $this->generateRefundNo(); $gatewayInstance = Factory::make($payment->gateway); $result = $gatewayInstance->refund([ 'order_no' => $payment->order_no, 'amount' => $amount, 'refund_no' => $refundNo, 'reason' => $reason, ]); $refund = $payment->refunds()->create([ 'refund_no' => $refundNo, 'amount' => $amount, 'reason' => $reason, 'status' => $result['success'] ? 'succeeded' : 'failed', 'payload' => $result, ]); if ($result['success']) { $payment->update(['refunded_amount' => $payment->refunded_amount + $amount]); } return $refund; }
protected function generateOrderNo() { return date('YmdHis') . str_pad(mt_rand(1, 9999), 4, '0', STR_PAD_LEFT); }
protected function generateRefundNo() { return 'REFUND_' . date('YmdHis') . str_pad(mt_rand(1, 9999), 4, '0', STR_PAD_LEFT); } }
|
4.5 Laravel 支付模型定义
Order 模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Order extends Model { protected $fillable = [ 'user_id', 'subject', 'body', 'amount', 'status', 'extra', ]; protected $casts = [ 'extra' => 'array', ]; public function payments() { return $this->hasMany(Payment::class); } }
|
Payment 模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| <?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Payment extends Model { const STATUS_PENDING = 'pending'; const STATUS_SUCCEEDED = 'succeeded'; const STATUS_FAILED = 'failed'; const STATUS_REFUNDED = 'refunded'; protected $fillable = [ 'order_id', 'gateway', 'order_no', 'transaction_id', 'amount', 'refunded_amount', 'status', 'payload', 'extra', 'completed_at', ]; protected $casts = [ 'payload' => 'array', 'extra' => 'array', 'completed_at' => 'datetime', ]; public function order() { return $this->belongsTo(Order::class); } public function refunds() { return $this->hasMany(Refund::class); } }
|
Refund 模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| <?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Refund extends Model { const STATUS_PENDING = 'pending'; const STATUS_SUCCEEDED = 'succeeded'; const STATUS_FAILED = 'failed'; protected $fillable = [ 'payment_id', 'refund_no', 'amount', 'reason', 'status', 'payload', 'completed_at', ]; protected $casts = [ 'payload' => 'array', 'completed_at' => 'datetime', ]; public function payment() { return $this->belongsTo(Payment::class); } }
|
4.6 Laravel 支付控制器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| <?php
namespace App\Http\Controllers;
use App\Http\Requests\PaymentRequest; use App\Services\Payment\PaymentService; use Illuminate\Http\Request;
class PaymentController extends Controller { protected $paymentService; public function __construct(PaymentService $paymentService) { $this->paymentService = $paymentService; } public function create(PaymentRequest $request) { $data = $request->validated(); $result = $this->paymentService->createPayment( $data['order_id'], $data['gateway'], $data['amount'], $data['extra'] ?? [] ); return response()->json($result); } public function alipayNotify(Request $request) { $result = $this->paymentService->handleCallback('alipay', $request->all()); return $result['success'] ? 'success' : 'fail'; } public function wechatNotify(Request $request) { $data = $request->all(); $result = $this->paymentService->handleCallback('wechat', $data); return $result['success'] ? '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>' : '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[FAIL]]></return_msg></xml>'; } public function refund(Request $request) { $refund = $this->paymentService->refund( $request->input('payment_id'), $request->input('amount'), $request->input('reason') ); return response()->json($refund); } }
|
4.7 Laravel 支付事件定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| <?php
namespace App\Services\Payment\Events;
use App\Models\Payment; use Illuminate\Queue\SerializesModels;
class PaymentCreated { use SerializesModels; public $payment; public function __construct(Payment $payment) { $this->payment = $payment; } }
class PaymentSucceeded { use SerializesModels; public $payment; public function __construct(Payment $payment) { $this->payment = $payment; } }
class PaymentFailed { use SerializesModels; public $data; public function __construct($data) { $this->data = $data; } }
|
4.8 Laravel 支付服务提供者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider; use App\Services\Payment\PaymentService;
class PaymentServiceProvider extends ServiceProvider {
public function register() { $this->app->singleton(PaymentService::class, function ($app) { return new PaymentService(); }); }
public function boot() { $this->publishes([ __DIR__.'/../Config/payment.php' => config_path('payment.php'), ]); } }
|
4.9 Laravel 支付配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <?php
return [ 'alipay' => [ 'app_id' => env('ALIPAY_APP_ID'), 'private_key' => env('ALIPAY_PRIVATE_KEY'), 'public_key' => env('ALIPAY_PUBLIC_KEY'), 'sandbox' => env('ALIPAY_SANDBOX', false), ], 'wechat' => [ 'app_id' => env('WECHAT_APP_ID'), 'mch_id' => env('WECHAT_MCH_ID'), 'key' => env('WECHAT_KEY'), 'cert_path' => env('WECHAT_CERT_PATH'), 'key_path' => env('WECHAT_KEY_PATH'), 'sandbox' => env('WECHAT_SANDBOX', false), ], 'paypal' => [ 'client_id' => env('PAYPAL_CLIENT_ID'), 'secret' => env('PAYPAL_SECRET'), 'sandbox' => env('PAYPAL_SANDBOX', false), ], ];
|
5. Laravel 支付系统最佳实践
5.1 Laravel 支付系统安全考虑
- 使用 HTTPS:确保所有支付相关的请求都通过 HTTPS 传输,防止中间人攻击
- 验证签名:严格验证支付网关的回调签名,使用官方提供的 SDK 或验证工具
- 防重放攻击:使用唯一的订单号和时间戳防止重复支付,设置合理的订单过期时间
- 数据加密:敏感信息如支付凭证应加密存储,使用 Laravel 的加密功能或环境变量
- 权限控制:限制支付相关操作的访问权限,使用 Laravel 的中间件和策略进行授权
- 输入验证:对所有支付相关的输入进行严格验证,使用 Laravel 的表单验证功能
- 日志记录:记录所有支付操作和异常,便于审计和问题排查
- 定期安全审计:定期检查支付系统的安全配置和依赖包的安全更新
安全实现示例
签名验证实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public function verifySignature($data, $signature) { $key = config('payment.' . $this->gateway . '.key'); switch ($this->gateway) { case 'alipay': return $this->verifyAlipaySignature($data, $signature, $key); case 'wechat': return $this->verifyWechatSignature($data, $signature, $key); default: return false; } }
protected function verifyAlipaySignature($data, $signature, $key) { unset($data['sign']); unset($data['sign_type']); ksort($data); $stringToSign = http_build_query($data, '', '&'); $publicKey = openssl_pkey_get_public($key); $result = openssl_verify($stringToSign, base64_decode($signature), $publicKey, OPENSSL_ALGO_SHA256); openssl_free_key($publicKey); return $result === 1; }
|
5.2 Laravel 支付系统性能优化
- 使用队列:将支付处理和通知处理放入队列,使用 Laravel 的队列系统异步处理
- 缓存配置:缓存支付网关配置,减少重复加载,使用 Laravel 的缓存系统
- 数据库索引:为支付表的关键字段添加索引,如订单号、交易号、状态等
- 批量操作:对于批量退款等操作,使用批量处理,减少数据库查询次数
- 连接池:对于高并发场景,使用数据库连接池和 Redis 连接池
- 代码优化:优化支付处理逻辑,减少不必要的计算和查询
- 资源限制:合理设置 PHP 和服务器的资源限制,避免性能瓶颈
性能优化实现示例
使用队列处理支付通知:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class PaymentSucceededListener implements ShouldQueue { use InteractsWithQueue; public function handle(PaymentSucceeded $event) { $payment = $event->payment; Mail::to($payment->order->user->email)->send( new PaymentSuccessMail($payment) ); $this->sendSmsNotification($payment); $payment->order->update(['status' => 'paid']); } protected function sendSmsNotification($payment) { } }
Event::listen(PaymentSucceeded::class, PaymentSucceededListener::class);
|
缓存支付网关配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class PaymentGatewayFactory { public static function create($gateway) { $configKey = "payment.gateway.{$gateway}"; $config = Cache::remember($configKey, 3600, function () use ($gateway) { return config("payment.{$gateway}"); }); switch ($gateway) { case 'alipay': return new AlipayGateway($config); case 'wechat': return new WechatGateway($config); default: throw new \InvalidArgumentException("Unsupported gateway: {$gateway}"); } } }
|
5.3 Laravel 支付系统可维护性
- 代码规范:遵循 Laravel 代码风格和 PSR 规范
- 文档完善:为支付系统编写详细的文档
- 日志记录:记录所有支付相关的操作和错误
- 监控告警:监控支付系统的运行状态和异常
- 测试覆盖:编写单元测试和集成测试
6. Laravel 集成多种支付方式
6.1 Laravel 支付前端集成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| <template> <div class="payment-methods"> <div v-for="method in paymentMethods" :key="method.id" class="payment-method" @click="selectPaymentMethod(method)" > <img :src="method.icon" :alt="method.name"> <span>{{ method.name }}</span> </div> <button v-if="selectedMethod" @click="submitPayment" class="submit-btn"> 确认支付 </button> </div> </template>
<script> export default { data() { return { paymentMethods: [ { id: 'alipay', name: '支付宝', icon: '/icons/alipay.png' }, { id: 'wechat', name: '微信支付', icon: '/icons/wechat.png' }, { id: 'paypal', name: 'PayPal', icon: '/icons/paypal.png' }, ], selectedMethod: null, orderId: 1, amount: 99.99, }; }, methods: { selectPaymentMethod(method) { this.selectedMethod = method; }, async submitPayment() { try { const response = await axios.post('/api/payments', { order_id: this.orderId, gateway: this.selectedMethod.id, amount: this.amount, }); const { payment, gateway_data } = response.data; // 根据支付方式处理跳转或唤起支付 if (this.selectedMethod.id === 'alipay') { // 支付宝 H5 支付 window.location.href = gateway_data.url; } else if (this.selectedMethod.id === 'wechat') { // 微信支付,显示二维码 this.showWechatQrCode(gateway_data.code_url); } else if (this.selectedMethod.id === 'paypal') { // PayPal 支付 window.location.href = gateway_data.redirect_url; } } catch (error) { console.error('支付失败:', error); } }, showWechatQrCode(codeUrl) { // 显示微信支付二维码 this.$alert( `<img src="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(codeUrl)}" />`, '微信支付', { html: true, confirmButtonText: '已支付', callback: (action) => { if (action === 'confirm') { this.checkPaymentStatus(); } } } ); }, async checkPaymentStatus() { // 轮询检查支付状态 // ... }, }, }; </script>
|
6.2 Laravel 支付后端路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php
use Illuminate\Support\Facades\Route; use App\Http\Controllers\PaymentController;
Route::prefix('api')->group(function () { Route::post('payments', [PaymentController::class, 'create']); Route::post('payments/refund', [PaymentController::class, 'refund']); });
Route::prefix('payment')->group(function () { Route::post('alipay/notify', [PaymentController::class, 'alipayNotify']); Route::get('alipay/return', [PaymentController::class, 'alipayReturn']); Route::post('wechat/notify', [PaymentController::class, 'wechatNotify']); Route::post('paypal/notify', [PaymentController::class, 'paypalNotify']); });
|
7. Laravel 支付扩展功能
7.1 Laravel 订阅支付
对于需要定期扣费的订阅服务,可以使用 Laravel Cashier:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php
use Laravel\Cashier\Billable;
class User extends Authenticatable { use Billable; }
$user->newSubscription('default', 'price_monthly')->create($paymentMethod);
$user->subscription('default')->cancel();
$user->subscription('default')->resume();
|
7.2 Laravel 支付分账
对于需要分账的场景,可以扩展支付网关:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?php
class AlipayGateway extends Gateway { public function transfer(array $params) { $response = $this->gateway->transfer([ 'out_biz_no' => $params['order_no'], 'trans_amount' => $params['amount'], 'payee_type' => $params['type'], 'payee_account' => $params['account'], 'payer_show_name' => $params['payer_name'], 'payee_real_name' => $params['payee_name'], ])->send(); return $response->getData(); } }
|
7.3 Laravel 支付多币种支持
对于需要支持多币种的场景,可以在支付服务中添加币种转换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <?php
class PaymentService { public function createPayment($orderId, $gateway, $amount, $currency = 'CNY', $extra = []) { if ($currency !== 'CNY' && in_array($gateway, ['alipay', 'wechat'])) { $amount = $this->convertCurrency($amount, $currency, 'CNY'); } } protected function convertCurrency($amount, $from, $to) { return $convertedAmount; } }
|
8. Laravel 支付系统总结
构建一个优雅的 Laravel 支付系统需要考虑以下几个方面:
- 架构设计:使用合理的设计模式和目录结构,确保系统的可扩展性和可维护性
- 网关抽象:通过抽象层统一不同支付渠道的接口,简化集成和切换
- 安全性:严格验证支付回调,防止欺诈和攻击
- 用户体验:提供流畅的支付流程和清晰的错误提示
- 监控和日志:记录所有支付相关的操作和错误,便于排查问题
- 测试覆盖:编写充分的测试用例,确保系统的稳定性
通过本文介绍的方案,你可以构建一个功能完善、易于扩展、安全可靠的 Laravel 支付系统,为你的应用提供优雅的支付体验。
9. Laravel 支付参考资料
官方文档
第三方支付库
支付网关文档
相关教程
技术博客和资源
安全资源