Skip to content

Commit 364182e

Browse files
authored
feat: process webhook once (#107)
1 parent fa8fe56 commit 364182e

File tree

4 files changed

+42
-4
lines changed

4 files changed

+42
-4
lines changed

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ return [
6969
/**
7070
* This class determines if the webhook call should be stored and processed.
7171
*/
72-
'profile' => \Spatie\WebhookClient\WebhookProfile\ProcessEverythingWebhookProfile::class,
72+
'profile' => \Spatie\StripeWebhooks\StripeWebhookProfile::class,
7373

7474
/*
7575
* When disabled, the package will not verify if the signature is valid.
@@ -112,7 +112,9 @@ Stripe will send out webhooks for several event types. You can find the [full li
112112

113113
Stripe will sign all requests hitting the webhook url of your app. This package will automatically verify if the signature is valid. If it is not, the request was probably not sent by Stripe.
114114

115-
Unless something goes terribly wrong, this package will always respond with a `200` to webhook requests. Sending a `200` will prevent Stripe from resending the same event over and over again. All webhook requests with a valid signature will be logged in the `webhook_calls` table. The table has a `payload` column where the entire payload of the incoming webhook is saved.
115+
Unless something goes terribly wrong, this package will always respond with a `200` to webhook requests. Sending a `200` will prevent Stripe from resending the same event over and over again.
116+
Stripe might occasionally send a duplicate webhook request [more than once](https://stripe.com/docs/webhooks/best-practices#duplicate-events). This package makes sure that each request will only be processed once.
117+
All webhook requests with a valid signature will be logged in the `webhook_calls` table. The table has a `payload` column where the entire payload of the incoming webhook is saved.
116118

117119
If the signature is not valid, the request will not be logged in the `webhook_calls` table but a `Spatie\StripeWebhooks\WebhookFailed` exception will be thrown.
118120
If something goes wrong during the webhook request the thrown exception will be saved in the `exception` column. In that case the controller will send a `500` instead of `200`.
@@ -251,7 +253,7 @@ class MyCustomStripeWebhookJob extends ProcessStripeWebhookJob
251253

252254
You may use your own logic to determine if a request should be processed or not. You can do this by specifying your own profile in the `profile` key of the `stripe-webhooks` config file. The class should implement `Spatie\WebhookClient\WebhookProfile\WebhookProfile`.
253255

254-
Stripe might occasionally send a webhook request [more than once](https://stripe.com/docs/webhooks/best-practices#duplicate-events). In this example we will make sure to only process a request if it wasn't processed before.
256+
In this example we will make sure to only process a request if it wasn't processed before.
255257

256258
```php
257259
use Illuminate\Http\Request;

config/stripe-webhooks.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
/**
2929
* This class determines if the webhook call should be stored and processed.
3030
*/
31-
'profile' => \Spatie\WebhookClient\WebhookProfile\ProcessEverythingWebhookProfile::class,
31+
'profile' => \Spatie\StripeWebhooks\StripeWebhookProfile::class,
3232

3333
/*
3434
* When disabled, the package will not verify if the signature is valid.

src/StripeWebhookProfile.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Spatie\StripeWebhooks;
4+
5+
use Illuminate\Http\Request;
6+
use Spatie\WebhookClient\Models\WebhookCall;
7+
use Spatie\WebhookClient\WebhookProfile\WebhookProfile;
8+
9+
class StripeWebhookProfile implements WebhookProfile
10+
{
11+
public function shouldProcess(Request $request): bool
12+
{
13+
return ! WebhookCall::where('payload->id', $request->get('id'))->exists();
14+
}
15+
}

tests/IntegrationTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,4 +193,25 @@ public function a_request_with_a_config_key_will_use_the_correct_signing_secret(
193193
->postJson('stripe-webhooks/somekey', $payload, $headers)
194194
->assertSuccessful();
195195
}
196+
197+
/** @test */
198+
public function a_request_will_only_be_processed_once()
199+
{
200+
$payload = [
201+
'type' => 'my.type',
202+
'id' => 'evt_123',
203+
];
204+
205+
$headers = ['Stripe-Signature' => $this->determineStripeSignature($payload)];
206+
207+
$this
208+
->postJson('stripe-webhooks', $payload, $headers)
209+
->assertSuccessful();
210+
211+
$this
212+
->postJson('stripe-webhooks', $payload, $headers)
213+
->assertSuccessful();
214+
215+
$this->assertCount(1, WebhookCall::where('payload->id', $payload['id'])->get());
216+
}
196217
}

0 commit comments

Comments
 (0)