Laravel Cashier迁移Stripe Checkout后事件监听问题咨询
我正在将Laravel Cashier配置迁移至Stripe Checkout功能,平台同时支持订阅和一次性收费,其中一项一次性收费为用户账户充值短信额度。
原本使用invoicePrice方法并传递元数据:
$addonItemPrice = Cashier::stripe()->prices->retrieve($addonItem->stripe_id); Auth::user()->invoicePrice( $addonItem->stripe_id, 1, [ 'metadata' => [ 'is_addon' => true, 'is_sms_addon' => true, 'created_at' => Carbon::now(), 'user_id' => Auth::id(), 'addon_id' => $addonItem->id, 'addon_quantity' => $addonItem->quantity, 'apply_credit_balance' => true, ], ] );
之后通过监听invoice.payment_succeeded Webhook事件为用户添加短信额度:
/** * Handle received Stripe webhooks. */ public function handle(WebhookReceived $event) { match ($event->payload['type']) { 'customer.subscription.created' => $this->handleSubscriptionCreated($event->payload), 'invoice.payment_succeeded' => $this->handleInvoicePaymentSucceeded($event->payload), default => null }; }
迁移至Stripe Checkout后,代码改为:
$charge = $user->checkout([$addonItemPrice->id => 1], [ 'success_url' => "$frontendURL"."account/billing/checkout/success/", 'cancel_url' => "$frontendURL"."account/billing/add-ons/", 'metadata' => [ 'is_addon' => true, 'is_sms_addon' => true, 'created_at' => Carbon::now(), 'user_id' => Auth::id(), 'addon_id' => $addonItem->id, 'addon_quantity' => $addonItem->quantity, 'apply_credit_balance' => true, ], ]);
但现在完全收不到invoice.payment_succeeded事件,元数据关联到了checkout.session.completed事件。我想咨询是否应该迁移至该事件,是否存在不可靠的情况?我仅希望在发票支付成功时触发相关逻辑。
为什么收不到invoice.payment_succeeded?
Stripe Checkout处理一次性收费时,默认不会自动生成发票,因此不会触发invoice.payment_succeeded事件。此时支付完成后只会触发checkout.session.completed,你设置的元数据也会绑定在这个会话事件上。
两种可行方案
方案1:迁移至checkout.session.completed事件
完全可以迁移到这个事件,但必须注意:checkout.session.completed仅表示用户完成了Checkout流程,不代表支付一定成功(比如用户用了无效卡片后关闭页面,会话也会标记为完成,但支付状态是失败的)。
你需要在事件处理逻辑中先校验支付状态,再执行业务代码:
public function handle(WebhookReceived $event) { match ($event->payload['type']) { 'customer.subscription.created' => $this->handleSubscriptionCreated($event->payload), 'invoice.payment_succeeded' => $this->handleInvoicePaymentSucceeded($event->payload), 'checkout.session.completed' => $this->handleCheckoutSessionCompleted($event->payload), default => null }; } private function handleCheckoutSessionCompleted(array $payload) { $session = $payload['data']['object']; // 核心校验:只有支付成功才执行逻辑 if ($session['payment_status'] !== 'paid') { return; } $metadata = $session['metadata']; // 判断是否是短信充值的一次性收费 if (isset($metadata['is_sms_addon']) && $metadata['is_sms_addon'] === 'true') { // 执行添加短信额度的业务逻辑 $userId = $metadata['user_id']; $addonQuantity = $metadata['addon_quantity']; // ...你的具体业务代码 } }
这个方案的优势是贴合Stripe Checkout的原生流程,无需额外配置,适合仅需处理支付成功触发逻辑的场景。
方案2:启用发票生成,继续监听invoice.payment_succeeded
如果你必须依赖发票实体(比如需要记录发票号、后续对账),可以在Checkout配置中强制生成发票,这样支付成功后就会触发invoice.payment_succeeded事件。
修改Checkout代码,添加invoice_creation参数,并将元数据同步到发票中:
$charge = $user->checkout([$addonItemPrice->id => 1], [ 'success_url' => "$frontendURL"."account/billing/checkout/success/", 'cancel_url' => "$frontendURL"."account/billing/add-ons/", 'metadata' => [ 'is_addon' => true, 'is_sms_addon' => true, 'created_at' => Carbon::now(), 'user_id' => Auth::id(), 'addon_id' => $addonItem->id, 'addon_quantity' => $addonItem->quantity, 'apply_credit_balance' => true, ], // 启用发票生成 'invoice_creation' => [ 'enabled' => true, 'invoice_data' => [ 'metadata' => [ // 将核心元数据同步到发票,确保事件中能获取到 'is_addon' => true, 'is_sms_addon' => true, 'addon_id' => $addonItem->id, 'addon_quantity' => $addonItem->quantity, ] ] ] ]);
这样配置后,Stripe会为这次一次性收费生成发票,支付成功后就会触发invoice.payment_succeeded,你可以复用之前写好的handleInvoicePaymentSucceeded逻辑,无需大幅修改。
总结
- 若无需依赖发票实体,方案1更简洁高效,只要做好支付状态校验就不存在可靠性问题;
- 若业务必须关联发票,选择方案2,配置发票生成即可继续使用原有事件逻辑。
内容的提问来源于stack exchange,提问作者Ryan H

