catch-of-the-day/stripe-webhook-bundle
Installation
composer require mrp/stripe-webhook-bundle
Enable the bundle in config/app.php (Laravel 5.5+) or AppKernel.php (Symfony):
MRP\StripeWebhookBundle\MRPStripeWebhookBundle::class,
Route Configuration
Add the webhook route in routes/web.php (Laravel):
Route::prefix('stripe-webhooks')->group(function () {
Route::post('/', [\MRP\StripeWebhookBundle\Controller\WebhookController::class, 'handle']);
});
First Use Case
Listen to a Stripe event (e.g., charge.succeeded) in an event listener:
// app/Listeners/HandleStripeChargeSucceeded.php
namespace App\Listeners;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class HandleStripeChargeSucceeded
{
public function handle($event)
{
// $event->payload contains the raw Stripe event data
\Log::info('Charge succeeded:', $event->payload);
}
}
Register the listener in EventServiceProvider:
protected $listen = [
'mrp_stripe_webhook.charge.succeeded' => [
\App\Listeners\HandleStripeChargeSucceeded::class,
],
];
Route Webhook Endpoint
Ensure the /stripe-webhooks endpoint is publicly accessible (no auth middleware) and uses POST.
Event Listeners
'mrp_stripe_webhook.<event_name>' => [Listener::class, 'handle']
charge.succeededpayment_intent.succeededinvoice.payment_succeededPayload Access
The event payload is available in the listener as $event->payload (Stripe’s raw JSON data). Parse it with:
$stripeData = json_decode($event->payload, true);
Idempotency
Use Stripe’s idempotency_key to handle duplicate events:
if ($stripeData['idempotency_key'] === 'existing_key') {
return; // Skip duplicate processing
}
Async Processing Queue listeners for heavy operations:
class HandleStripeEvent implements ShouldQueue
{
// ...
}
Stripe_Signature (if the bundle doesn’t handle it natively).\Log::debug('Stripe Webhook Received', ['event' => $event->payload]);
$this->post('/stripe-webhooks', $stripeEventJson, ['headers' => ['HTTP_STRIPE_SIGNATURE' => 'sig']]);
Missing Signature Verification The bundle may not verify Stripe’s webhook signatures by default. Add middleware:
// app/Http/Middleware/VerifyStripeSignature.php
public function handle($request, Closure $next)
{
$sigHeader = $request->header('Stripe-Signature');
$payload = $request->getContent();
if (!$sigHeader || !\Stripe\Webhook::constructEvent($payload, $sigHeader, 'your_webhook_secret')) {
abort(403, 'Invalid signature');
}
return $next($request);
}
Apply it to the webhook route.
Event Naming Conflicts
Ensure event names (e.g., mrp_stripe_webhook.charge.succeeded) don’t clash with existing listeners.
WIP Status The bundle is labeled "WIP" (Work in Progress). Check for updates or fork if critical features are missing.
'mrp_stripe_webhook.*' => [\App\Listeners\LogAllStripeEvents::class],
stripe listen --forward-to localhost:8000/stripe-webhooks
Custom Events
Extend the bundle to dispatch additional events by modifying the WebhookController or creating a decorator.
Middleware Hooks Add middleware to the webhook route for pre/post-processing:
Route::post('/stripe-webhooks', [WebhookController::class, 'handle'])
->middleware(VerifyStripeSignature::class);
Database Storage Store processed events in a table for reconciliation:
// In listener
\App\Models\StripeWebhook::create([
'event' => $event->name,
'payload' => $event->payload,
]);
How can I help you explore Laravel packages today?