Laravel中用srmklive/laravel-paypal实现带学生ID的PayPal支付管理
使用srmklive/laravel-paypal实现学生关联支付的解决方案
针对你提出的三个技术需求,结合srmklive/laravel-paypal包的特性,解决方案如下:
1. 将学生ID加入支付请求,取消时获取并入库
PayPal支持通过自定义字段传递业务关联数据,你可以在创建订阅时将学生ID存入custom字段或结构化的metadata中,后续从回调或API中提取。
修改创建订阅的代码:
$studentId = 123; // 替换为实际学生ID $provider = \PayPal::setProvider(); $provider->setApiCredentials(config('paypal')); $token = $provider->getAccessToken(); $provider->setAccessToken($token); $response = $provider->addProduct('Demo Product', 'Demo Product', 'SERVICE', 'SOFTWARE') ->addDailyPlan('Demo Plan', 'Demo Plan', 10.50) ->setReturnAndCancelUrl('https://example.com/paypal-success', 'https://example.com/paypal-cancel') ->setCustom($studentId) // 存入学生ID到自定义字段 // 可选:用metadata存储结构化数据(推荐复杂场景) // ->addMetadata('student_id', $studentId) ->setupSubscription('John Doe', 'john@example.com', '2023-02-21T15:47:00+00:00'); // 保存订阅ID与学生ID的关联到数据库 DB::table('student_subscriptions')->insert([ 'student_id' => $studentId, 'subscription_id' => $response['id'], 'status' => 'pending' ]);
处理取消回调:
在paypal-cancel对应的控制器方法中,提取学生ID并更新数据库:
public function paypalCancel(Request $request) { // 直接从请求参数获取custom字段 $studentId = $request->get('custom'); // 若使用metadata,需通过订阅ID查询详情 // $subscriptionId = $request->get('subscription_id'); // $subscription = $provider->showSubscriptionDetails($subscriptionId); // $studentId = $subscription['metadata']['student_id']; DB::table('student_subscriptions') ->where('student_id', $studentId) ->update(['status' => 'cancelled']); return redirect()->route('student.dashboard')->with('message', '支付已取消'); }
2. 通过Webhook接收预付款通知时识别学生
配置PayPal Webhook接收PAYMENT.SALE.COMPLETED或BILLING.SUBSCRIPTION.CREATED等事件,在处理时从订阅资源中提取之前存入的学生ID。
Webhook处理控制器示例:
public function handleWebhook(Request $request) { $provider = \PayPal::setProvider(); $provider->setApiCredentials(config('paypal')); $provider->getAccessToken(); // 必须验证Webhook签名,防止伪造请求 $verification = $provider->verifyWebhookSignature($request->all()); if ($verification['status'] !== 'SUCCESS') { abort(403, '无效的Webhook签名'); } $eventType = $request->input('event_type'); $resource = $request->input('resource'); // 处理预付款完成事件 if ($eventType === 'PAYMENT.SALE.COMPLETED') { $subscriptionId = $resource['billing_agreement_id']; $subscription = $provider->showSubscriptionDetails($subscriptionId); // 提取学生ID(根据存储方式选择) $studentId = $subscription['custom']; // $studentId = $subscription['metadata']['student_id']; // 更新学生支付状态 DB::table('student_subscriptions') ->where('subscription_id', $subscriptionId) ->update([ 'status' => 'paid', 'last_paid_at' => now() ]); } return response()->json(['status' => 'success']); }
3. 主动取消学生的支付订阅
从数据库中获取学生对应的订阅ID,调用包的cancelSubscription方法完成取消,并同步更新本地状态。
主动取消的控制器方法:
public function cancelStudentSubscription($studentId) { $subscription = DB::table('student_subscriptions') ->where('student_id', $studentId) ->where('status', '!=', 'cancelled') ->first(); if (!$subscription) { return redirect()->back()->with('error', '无有效订阅可取消'); } $provider = \PayPal::setProvider(); $provider->setApiCredentials(config('paypal')); $provider->getAccessToken(); // 调用PayPal API取消订阅 $response = $provider->cancelSubscription($subscription->subscription_id); if ($response['status'] === 'CANCELLED') { DB::table('student_subscriptions') ->where('id', $subscription->id) ->update([ 'status' => 'cancelled', 'cancelled_at' => now() ]); return redirect()->back()->with('message', '订阅已成功取消'); } return redirect()->back()->with('error', '取消订阅失败'); }
内容的提问来源于stack exchange,提问作者majid
相关产品推荐
相关产品推荐

