Laravel 8集成PayPal支付的服务端SDK使用及交易校验问题咨询
Laravel 8 集成 PayPal 服务端实现方案
整体逻辑
将原来客户端侧的订单创建、金额配置、订单捕获逻辑全部迁移到服务端处理,前端仅负责触发流程和接收最终结果,从根源避免客户端篡改价格、伪造支付成功状态的问题。
第一步:服务端依赖安装与配置
- 安装PayPal官方服务端SDK
composer require paypal/paypal-checkout-sdk 1.*
- 在
.env文件中添加PayPal配置参数
PAYPAL_CLIENT_ID=你的PayPal客户端ID PAYPAL_CLIENT_SECRET=你的PayPal客户端密钥 PAYPAL_MODE=sandbox # 生产环境改为live
- 新建PayPal服务类
app/Services/PayPalService.php统一处理请求初始化
<?php namespace App\Services; use PayPalCheckoutSdk\Core\PayPalHttpClient; use PayPalCheckoutSdk\Core\SandboxEnvironment; use PayPalCheckoutSdk\Core\ProductionEnvironment; class PayPalService { public $client; public function __construct() { $environment = config('app.paypal_mode') === 'sandbox' ? new SandboxEnvironment(config('app.paypal_client_id'), config('app.paypal_client_secret')) : new ProductionEnvironment(config('app.paypal_client_id'), config('app.paypal_client_secret')); $this->client = new PayPalHttpClient($environment); } }
- 在
config/app.php中添加配置读取项
'paypal_client_id' => env('PAYPAL_CLIENT_ID'), 'paypal_client_secret' => env('PAYPAL_CLIENT_SECRET'), 'paypal_mode' => env('PAYPAL_MODE'),
第二步:新增服务端接口
2.1 创建订单接口
- 新增路由
routes/api.php
Route::post('/paypal/create-order', [PayPalController::class, 'createOrder']);
- 新建控制器
app/Http/Controllers/PayPalController.php实现创建逻辑
注意:套餐ID可以从当前登录用户的订阅信息或者请求参数中读取,价格、商品名绝对不能从前端传递,必须从数据库查询
<?php namespace App\Http\Controllers; use App\Services\PayPalService; use PayPalCheckoutSdk\Orders\OrdersCreateRequest; use Illuminate\Support\Facades\Auth; class PayPalController extends Controller { protected $paypal; public function __construct(PayPalService $paypal) { $this->paypal = $paypal; // 可选:做登录校验 $this->middleware('auth'); } public function createOrder() { // 从数据库查询当前用户要购买的套餐信息,示例代码根据实际业务修改 $plan = Auth::user()->setup->plan; $request = new OrdersCreateRequest(); $request->prefer('return=representation'); $request->body = [ 'intent' => 'CAPTURE', 'purchase_units' => [ [ 'description' => $plan->name, 'amount' => [ 'currency_code' => 'USD', // 改成你的实际币种 'value' => $plan->price ] ] ], 'application_context' => [ 'shipping_preference' => 'NO_SHIPPING', 'return_url' => url('/payment/success'), // 可选,不需要可以不用 'cancel_url' => url('/payment/cancel') // 可选 ] ]; try { $response = $this->paypal->client->execute($request); return response()->json([ 'id' => $response->result->id ]); } catch (\Exception $e) { return response()->json(['error' => '订单创建失败'], 500); } } }
2.2 订单捕获与校验接口
- 新增路由
routes/api.php
Route::post('/paypal/capture-order', [PayPalController::class, 'captureOrder']);
- 在
PayPalController中添加捕获逻辑,此处必须做三重校验:订单状态、交易金额、币种
use PayPalCheckoutSdk\Orders\OrdersCaptureRequest; use App\Models\Transaction; public function captureOrder(\Illuminate\Http\Request $request) { $request->validate([ 'order_id' => 'required|string' ]); $orderId = $request->order_id; // 先查询当前用户要购买的套餐价格,用于后续校验 $plan = Auth::user()->setup->plan; $expectAmount = (string)$plan->price; $expectCurrency = 'USD'; // 和创建订单时的币种保持一致 $request = new OrdersCaptureRequest($orderId); $request->prefer('return=representation'); try { $response = $this->paypal->client->execute($request); $result = $response->result; // 校验1:订单状态必须是已完成 if ($result->status !== 'COMPLETED') { return response()->json(['error' => '支付未完成'], 400); } // 校验2:交易金额和币种必须和预期一致 $capturedAmount = $result->purchase_units[0]->payments->captures[0]->amount->value; $capturedCurrency = $result->purchase_units[0]->payments->captures[0]->amount->currency_code; if ($capturedAmount !== $expectAmount || $capturedCurrency !== $expectCurrency) { return response()->json(['error' => '交易金额不匹配'], 400); } // 校验通过,处理后续业务逻辑:给用户开通权益、保存交易记录等 Transaction::create([ 'user_id' => Auth::id(), 'plan_id' => $plan->id, 'transaction_id' => $result->purchase_units[0]->payments->captures[0]->id, 'amount' => $capturedAmount, 'currency' => $capturedCurrency, 'status' => 'success' ]); // 此处添加开通会员、更新订阅状态等业务代码 return response()->json(['status' => 'success', 'message' => '支付成功']); } catch (\Exception $e) { return response()->json(['error' => '支付捕获失败:' . $e->getMessage()], 500); } }
第三步:前端代码改造
替换原来的客户端创建、捕获订单逻辑,改为调用自己的服务端接口:
paypal.Buttons({ style: { layout: 'vertical', color: 'blue', shape: 'rect', label: 'pay', }, createOrder: function(data, actions) { // 调用自己的服务端接口创建订单,返回订单ID return fetch('/api/paypal/create-order', { method: 'POST', headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content, 'Content-Type': 'application/json' } }).then(function(res) { return res.json(); }).then(function(orderData) { return orderData.id; }); }, onApprove: function(data, actions) { // 调用自己的服务端接口捕获、校验订单 return fetch('/api/paypal/capture-order', { method: 'POST', headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content, 'Content-Type': 'application/json' }, body: JSON.stringify({ order_id: data.orderID }) }).then(function(res) { return res.json(); }).then(function(result) { if (result.status === 'success') { alert('支付成功'); // 跳转成功页或者刷新页面更新状态 } else { alert('支付失败:' + result.error); } }); } }).render('#paypal-button-container');
可选增强
可以额外配置PayPal Webhook监听支付相关事件,避免前端回调中断导致的业务逻辑未执行问题,进一步提升可靠性。
内容的提问来源于stack exchange,提问作者user17417786
相关产品推荐
相关产品推荐

