pascalebeier / laravel-stripe-webhooks
在 Laravel 应用中处理 Stripe Webhooks
Requires
- php: ^8.0
- illuminate/support: ^8.0|^9.0
- spatie/laravel-webhook-client: ^3.0
- stripe/stripe-php: ^7.51|^8.0|^9.0
Requires (Dev)
- orchestra/testbench: ^6.0|^7.0
- phpunit/phpunit: ^9.4
This package is auto-updated.
Last update: 2024-09-04 04:50:26 UTC
README
spatie/laravel-stripe-webhooks 的分支
Stripe 可以通过 Webhooks 通知您的应用程序事件。此包可以帮助您处理这些 Webhooks。默认情况下,它会验证所有传入请求的 Stripe 签名。所有有效的调用都将记录到数据库中。您可以轻松定义在特定事件发生时应该分发的作业或事件。
此包不会处理在验证 Webhook 请求并调用正确的作业或事件之后应该做什么。您仍然需要自己编写任何工作(例如,有关付款的工作)的代码。
在使用此包之前,我们强烈建议您阅读 Stripe 上关于 Webhooks 的完整文档。
此包是 spatie/laravel-stripe-webhooks 的分支。有关所有更改,请参阅 CHANGELOG。
安装
您可以通过 composer 安装此包
composer require pascalebeier/laravel-stripe-webhooks
服务提供程序将自动注册自己。
您必须使用以下命令发布配置文件
php artisan vendor:publish --provider="PascaleBeier\StripeWebhooks\StripeWebhooksServiceProvider"
这是将在 config/stripe-webhooks.php
下发布的配置文件的内容
return [ /* * Stripe will sign each webhook using a secret. You can find the used secret at the * webhook configuration settings: https://dashboard.stripe.com/account/webhooks. */ 'signing_secret' => env('STRIPE_WEBHOOK_SECRET'), /* * You can define a default job that should be run for all other Stripe event type * without a job defined in next configuration. * You may leave it empty to store the job in database but without processing it. */ 'default_job' => '', /* * You can define the job that should be run when a certain webhook hits your application * here. The key is the name of the Stripe event type with the `.` replaced by a `_`. * * You can find a list of Stripe webhook types here: * https://stripe.com/docs/api#event_types. */ 'jobs' => [ // 'source_chargeable' => \App\Jobs\StripeWebhooks\HandleChargeableSource::class, // 'charge_failed' => \App\Jobs\StripeWebhooks\HandleFailedCharge::class, ], /* * The classname of the model to be used. The class should equal or extend * Spatie\WebhookClient\Models\WebhookCall. */ 'model' => \Spatie\WebhookClient\Models\WebhookCall::class, /** * This class determines if the webhook call should be stored and processed. */ 'profile' => \PascaleBeier\StripeWebhooks\StripeWebhookProfile::class, /* * When disabled, the package will not verify if the signature is valid. * This can be handy in local environments. */ 'verify_signature' => env('STRIPE_SIGNATURE_VERIFY', true), ];
在配置文件的 signing_secret
键中,您应该添加一个有效的 Webhook 秘密。您可以在 Stripe 控制台上的 Webhook 配置设置中找到使用的密钥。
接下来,您必须使用以下命令发布迁移
php artisan vendor:publish --provider="Spatie\WebhookClient\WebhookClientServiceProvider" --tag="webhook-client-migrations"
迁移发布后,您可以通过运行迁移来创建 webhook_calls
表
php artisan migrate
最后,注意路由:在 Stripe 控制台 中,您必须配置 Stripe Webhooks 应该击中您的应用程序的 URL。在您的应用程序的路由文件中,您必须将此路由传递给 Route::stripeWebhooks
Route::stripeWebhooks('webhook-route-configured-at-the-stripe-dashboard');
幕后,这将注册一个由此包提供的控制器提供的 POST
路由。因为 Stripe 没有获取 csrf-token 的方法,所以您必须将该路由添加到 VerifyCsrfToken
中间件的 except
数组中
protected $except = [ 'webhook-route-configured-at-the-stripe-dashboard', ];
使用方法
Stripe 将为几种事件类型发送 Webhooks。您可以在 Stripe 文档中找到 事件类型的完整列表。
Stripe 将为击中您的应用程序 Webhook URL 的所有请求签名。此包将自动验证签名是否有效。如果不有效,则请求可能不是由 Stripe 发送的。
除非发生严重错误,否则此包将始终以 200
响应 Webhook 请求。发送 200
可以防止 Stripe 反复重发相同的事件。Stripe 可能会偶尔发送重复的 Webhook 请求 多次。此包确保每个请求只处理一次。所有具有有效签名的 Webhook 请求都将记录在 webhook_calls
表中。该表有一个 payload
列,其中保存了传入 Webhook 的整个有效负载。
如果签名无效,请求将不会记录在 webhook_calls
表中,而是会抛出 PascaleBeier\StripeWebhooks\WebhookFailed
异常。如果在处理 webhook 请求过程中出现问题,抛出的异常将保存在 exception
列表中。在这种情况下,控制器将发送 500
而不是 200
。
此包提供了两种处理 webhook 请求的方法:您可以选择排队一个作业或监听该包将引发的事件。
使用作业处理 webhook 请求
如果您希望在收到特定事件类型时执行某些操作,可以定义一个执行工作的作业。以下是一个此类作业的示例
<?php namespace App\Jobs\StripeWebhooks; use Illuminate\Bus\Queueable; use Illuminate\Queue\SerializesModels; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; use Spatie\WebhookClient\Models\WebhookCall: use App\Actions\UpdatePaymentIntentAction; class HandlePaymentIntentChange implements ShouldQueue { use InteractsWithQueue, Queueable, SerializesModels; public function __construct(private readonly WebhookCall $webhookCall, private readonly UpdatePaymentIntentAction $updatePaymentIntentAction) public function handle() { // do your work here $this->updatePaymentIntentAction->update($this->webhookCall->payload); // you can access the payload of the webhook call with `$this->webhookCall->payload` } }
我们强烈建议您将此作业设置为可排队的,因为这将最小化 webhook 请求的响应时间。这允许您处理更多的 Stripe webhook 请求并避免超时。
创建您的作业后,必须在 stripe-webhooks.php
配置文件中的 jobs
数组中注册它。键应该是 Stripe 事件类型 的名称,但用 .
替换为 _
。值应该是完全限定的类名。
// config/stripe-webhooks.php 'jobs' => [ 'source_chargeable' => \App\Jobs\StripeWebhooks\HandleChargeableSource::class, ],
如果您想将一个作业配置为默认值以处理所有未定义的事件,可以在 stripe-webhooks.php
配置文件中的 default_job
设置作业。值应该是完全限定的类名。
默认情况下,配置是一个空字符串 ''
,它只会将事件存储在数据库中,但不会处理。
// config/stripe-webhooks.php 'default_job' => \App\Jobs\StripeWebhooks\HandleOtherEvent::class,
使用事件处理 webhook 请求
您可以选择监听此包将引发的事件,而不是在 webhook 请求到来时排队作业以执行一些工作。每当有效的请求击中您的应用程序时,该包将引发一个 stripe-webhooks::<name-of-the-event>
事件。
事件的负载将是为传入请求创建的 WebhookCall
实例。
让我们看看如何监听此类事件。在 EventServiceProvider
中可以注册监听器。
/** * The event listener mappings for the application. * * @var array */ protected $listen = [ 'stripe-webhooks::source.chargeable' => [ App\Listeners\ChargeSource::class, ], ];
以下是一个此类监听器的示例
<?php namespace App\Listeners; use Illuminate\Contracts\Queue\ShouldQueue; use Spatie\WebhookClient\Models\WebhookCall; class ChargeSource implements ShouldQueue { public function handle(WebhookCall $webhookCall) { // do your work here // you can access the payload of the webhook call with `$webhookCall->payload` } }
我们强烈建议您将事件监听器设置为可排队的,因为这将最小化 webhook 请求的响应时间。这允许您处理更多的 Stripe webhook 请求并避免超时。
上述示例只是 Laravel 中处理事件的一种方式。要了解其他选项,请阅读 Laravel 处理事件的文档。
高级用法
重试处理 webhook
所有传入的 webhook 请求都将写入数据库。当处理 webhook 调用时出现问题,这非常有价值。您可以在调查并修复失败原因后轻松重试处理 webhook 调用,如下所示
use Spatie\WebhookClient\Models\WebhookCall; use PascaleBeier\StripeWebhooks\ProcessStripeWebhookJob; dispatch(new ProcessStripeWebhookJob(WebhookCall::find($id)));
执行自定义逻辑
您可以通过指定自己的模型来在 stripe-webhooks
配置文件中的 model
键中添加一些自定义逻辑,该逻辑应在排队作业的调度之前和/或之后执行。该类应扩展 PascaleBeier\StripeWebhooks\ProcessStripeWebhookJob
。
以下是一个示例
use PascaleBeier\StripeWebhooks\ProcessStripeWebhookJob; class MyCustomStripeWebhookJob extends ProcessStripeWebhookJob { public function handle() { // do some custom stuff beforehand parent::handle(); // do some custom stuff afterwards } }
确定是否应处理请求
您可以使用自己的逻辑来确定是否应处理请求。您可以在 stripe-webhooks
配置文件中的 profile
键中指定自己的配置。该类应实现 Spatie\WebhookClient\WebhookProfile\WebhookProfile
。
在此示例中,我们将确保只有在请求之前未处理的情况下才处理请求。
use Illuminate\Http\Request; use Spatie\WebhookClient\Models\WebhookCall; use Spatie\WebhookClient\WebhookProfile\WebhookProfile; class StripeWebhookProfile implements WebhookProfile { public function shouldProcess(Request $request): bool { return ! WebhookCall::where('payload->id', $request->get('id'))->exists(); } }
处理多个签名密钥
当使用 Stripe Connect 时,您可能希望包处理多个端点和密钥。以下是配置该行为的步骤。
如果您正在使用 Route::stripeWebhooks
宏,可以这样添加 configKey
Route::stripeWebhooks('webhook-url/{configKey}');
或者,如果您正在手动定义路由,可以像这样添加 configKey
Route::post('webhook-url/{configKey}', '\PascaleBeier\StripeWebhooks\StripeWebhooksController');
如果这个路由参数存在,验证中间件将使用不同的配置键来查找密钥,通过将给定的参数值附加到默认配置键。例如,如果 Stripe 向 webhook-url/my-named-secret
发送,您将添加一个新的配置 signing_secret_my-named-secret
Connect 的示例配置可能如下所示
// secret for when Stripe posts to webhook-url/account 'signing_secret_account' => 'whsec_abc', // secret for when Stripe posts to webhook-url/connect 'signing_secret_connect' => 'whsec_123',
将 Webhook 负载转换为 Stripe 对象
您可以将 Webhook 负载转换为 Stripe 对象,以帮助访问其各种方法和属性。
为此,使用 Stripe\Event::constructFrom($payload)
方法与 WebhookCall
的负载
use Stripe\Event; // ... public function handle(WebhookCall $webhookCall) { /** @var \Stripe\StripeObject|null */ $stripeObject = Event::constructFrom($webhookCall->payload)->data?->object; }
例如,如果您已为 invoice.created
事件设置了 Stripe Webhook,您可以将负载转换为 StripeInvoice
对象
/** @var \Stripe\StripeInvoice|null */ $stripeInvoice = Event::constructFrom($webhookCall->payload)->data?->object; // $stripeInvoice->status // $stripeInvoice->amount_due // $stripeInvoice->amount_paid // $stripeInvoice->amount_remaining foreach ($stripeInvoice->lines as $invoiceLine) { // ... }
变更日志
有关最近更改的更多信息,请参阅 变更日志
测试
composer test
安全
如果您发现了关于安全性的问题,请发送邮件至 mail@pascalebeier.de,而不是使用问题跟踪器。
鸣谢
非常感谢 Sebastiaan Luca,他慷慨地分享了他的 Stripe Webhook 解决方案,这启发了本软件包。
许可证
MIT 许可证 (MIT)。有关更多信息,请参阅 许可证文件