Laravel 10集成Cashier Stripe Checkout需监听哪些事件?
在Laravel 10 Cashier中使用Stripe Checkout的事件监听方案(欧盟3DS场景)
针对你的培训课程预约需求,结合欧盟3DS支付要求,以下是必须监听的Stripe事件及落地建议:
核心必监听事件
checkout.session.completed:这是触发预约创建的核心事件。无论用户是否经过3DS验证,只有当支付完全成功、Checkout会话完成时才会触发该事件。事件中会返回payment_status: succeeded的状态,此时可以安全地为用户创建培训预约。
辅助监听事件(处理异常/边缘场景)
payment_intent.succeeded:作为checkout.session.completed的双重验证,确保支付意图确实完成,避免极端场景下会话状态与支付状态不一致的问题。payment_intent.payment_failed:当支付失败时触发(比如3DS验证不通过、卡余额不足、卡被拒),此时需要清理用户的临时预约占位,并通知用户重新尝试支付。checkout.session.async_payment_succeeded:如果支持欧盟本地异步支付方式(如Sofort),这个事件会在异步支付确认成功时触发,避免遗漏这类场景下的预约创建。checkout.session.async_payment_failed:对应异步支付失败的场景,同样需要处理预约占位释放和用户通知。
Laravel Cashier中的实现步骤
- 配置Webhook密钥:在
.env文件中设置STRIPE_WEBHOOK_SECRET,值从Stripe后台的Webhook设置中获取。 - 注册Webhook路由:在
routes/web.php中添加:Route::stripeWebhooks('/stripe/webhook'); - 创建事件监听类:以处理
checkout.session.completed为例,创建App\Listeners\HandleCheckoutSessionCompleted类:<?php namespace App\Listeners; use Laravel\Cashier\Events\WebhookReceived; use App\Models\User; use App\Models\Course; class HandleCheckoutSessionCompleted { public function handle(WebhookReceived $event) { if ($event->payload['type'] !== 'checkout.session.completed') { return; } $session = $event->payload['data']['object']; // 仅处理支付成功的会话 if ($session['payment_status'] !== 'succeeded') { return; } // 从metadata中获取预存的用户ID和课程ID(创建Checkout会话时需存入) $userId = $session['metadata']['user_id'] ?? null; $courseId = $session['metadata']['course_id'] ?? null; if (!$userId || !$courseId) { // 记录日志,缺少必要数据 logger()->error('Checkout session missing metadata', ['session_id' => $session['id']]); return; } // 幂等性处理:检查该会话是否已处理过 if (\App\Models\CourseBooking::where('stripe_session_id', $session['id'])->exists()) { return; } // 创建培训预约 $user = User::find($userId); $course = Course::find($courseId); $user->courseBookings()->create([ 'course_id' => $courseId, 'stripe_session_id' => $session['id'], 'status' => 'confirmed' ]); // 发送预约确认通知给用户 $user->notify(new \App\Notifications\CourseBookingConfirmed($course)); } } - 注册监听器:在
App\Providers\EventServiceProvider的$listen数组中添加:protected $listen = [ \Laravel\Cashier\Events\WebhookReceived::class => [ \App\Listeners\HandleCheckoutSessionCompleted::class, \App\Listeners\HandlePaymentIntentSucceeded::class, \App\Listeners\HandlePaymentIntentFailed::class, ], ];
关键注意事项
- Metadata必传:创建Checkout会话时,一定要通过
metadata参数传入user_id和course_id,否则Webhook事件中无法关联到具体用户和课程。 - 幂等性保障:必须对Webhook事件做幂等处理(比如通过
stripe_session_id记录已处理的会话),避免因Stripe重复发送事件导致重复创建预约。 - 不要依赖前端跳转:
success_url/cancel_url仅作为用户端的跳转提示,绝对不能用来触发预约创建逻辑——用户可能因网络问题、手动关闭页面等原因不跳转,Webhook才是可靠的服务器端通知方式。 - 3DS自动处理:Stripe会自动处理3DS验证流程,用户完成验证后才会触发
checkout.session.completed,无需额外处理3DS相关的单独事件。
内容的提问来源于stack exchange,提问作者RomanPierce
相关产品推荐
相关产品推荐

