Lightpack Webhook Receiver

A robust, flexible, and production-ready webhook processing framework for PHP/Lightpack projects. Easily receive, verify, store, and process webhooks from any provider (Stripe, GitHub, Slack, etc.)


Features


Event Storage Schema

Column Type Description
id bigint Primary key
provider varchar Provider name (e.g., 'stripe')
event_id varchar Unique event ID (nullable)
payload text Full event payload (array, JSON-cast)
headers text All request headers (array, JSON-cast)
status varchar Event status (pending, processed, failed)
received_at datetime Timestamp of event receipt

Migration

Create schema migration file:

php console create:migration --support=webhooks

Run migration:

php console migrate:up

Configuration

Please run following command to create config/webhooks.php configuration file.

php console create:config --support=webhooks

In your config (e.g., config/webhook.php) you need to configure webhook provider details. Below are sample examples for stripe and github webhooks.

return [
    // example: stripe
    'stripe' => [
        'secret' => 'your-stripe-webhook-secret',
        'algo' => 'hmac', // or 'static' for static secrets
        'id' => 'id', // field in payload for event ID
        'handler' => App\Webhooks\StripeWebhookHandler::class,
    ],
    // example: github
    'github' => [
        'secret' => 'your-github-secret',
        'algo' => 'hmac',
        'id' => 'delivery', // GitHub's event ID is in a header
        'handler' => App\Webhooks\GitHubWebhookHandler::class,
    ],
];

Usage

  1. Define your webhook route:

Add following route definition in routes/web.php file. The corresponding WebhookController is already implemented by the framework.

route()->post('/webhook/:provider', WebhookController::class, 'handle');
  1. Implement custom handlers as needed:

app/Webhooks/StripeWebhookHandler.php

use Lightpack\Webhook\BaseWebhookHandler;

class StripeWebhookHandler extends BaseWebhookHandler
{
    public function verifySignature(): static
    {
        // Stripe-specific signature logic
        return $this;
    }

    public function handle(): Response
    {
        // Custom event processing
        return response()->json(['status' => 'ok'], 200);
    }
}

Example: Custom Handler for Header-Based Event ID

In cases where the webhook receiver gets the event id in request header, you need to override storeEvent() method.

class GitHubWebhookHandler extends BaseWebhookHandler
{
    public function storeEvent(?string $eventId): WebhookEvent
    {
        // GitHub's delivery ID is in a header
        $eventId = $this->request->header('X-GitHub-Delivery');

        return parent::storeEvent($eventId);
    }
}

Security & Idempotency


Best Practices & Tips


Notes

What if my provider doesn't send a unique event ID?

The event will be stored with event_id = null, and idempotency will not be enforced. You may want to generate your own unique key if needed.

Can I process multiple providers with different logic?

Yes! Simply specify a different handler class per provider in your config.

How do I debug webhook failures?

All events (including failed ones) are stored with full payload and headers. Check the webhook_events table for details.