Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Webhook Laravel Package

symfony/webhook

Symfony Webhook simplifies sending and consuming webhooks in Symfony apps. It provides tools to define webhook endpoints, validate and process incoming payloads, and dispatch outgoing webhooks with a consistent, reusable workflow.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup in Laravel

  1. Install the Package:

    composer require symfony/webhook
    

    Add to composer.json if using Laravel’s autoloader:

    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "Symfony\\Component\\Webhook\\": "vendor/symfony/webhook/"
        }
    }
    

    Run composer dump-autoload.

  2. First Use Case: Consuming a Webhook Create a route and middleware to validate and parse incoming webhooks:

    // routes/web.php
    Route::post('/stripe-webhook', [WebhookController::class, 'handleStripeWebhook']);
    
    // app/Http/Middleware/ValidateWebhookSignature.php
    use Symfony\Component\Webhook\Webhook;
    
    public function handle($request, Closure $next) {
        $webhook = new Webhook($request->getContent(), $request->headers->get('stripe-signature'));
        if (!$webhook->validate(config('services.stripe.webhook_secret'))) {
            abort(401, 'Invalid signature');
        }
        return $next($request);
    }
    

    Register the middleware in app/Http/Kernel.php:

    protected $routeMiddleware = [
        'validate.webhook' => \App\Http\Middleware\ValidateWebhookSignature::class,
    ];
    
  3. First Use Case: Sending a Webhook Use the Webhook class to send a payload with optional signing:

    use Symfony\Component\Webhook\Webhook;
    
    $webhook = new Webhook('https://example.com/webhook-endpoint');
    $webhook->setPayload(['event' => 'order.paid', 'data' => $order]);
    $webhook->setSecret(config('services.example.webhook_secret')); // Optional signing
    $response = $webhook->send();
    

Implementation Patterns

Core Workflows

1. Consuming Webhooks (Inbound)

  • Signature Validation: Use Symfony\Component\Webhook\Webhook to validate HMAC signatures for security:

    $webhook = new Webhook($rawPayload, $request->headers->get('x-hub-signature-256'));
    if (!$webhook->validate('your-secret-key')) {
        abort(401);
    }
    
  • Payload Parsing: Decouple parsing logic by implementing RequestParserInterface:

    use Symfony\Component\Webhook\RequestParserInterface;
    
    class StripeRequestParser implements RequestParserInterface
    {
        public function parse(Request $request): RemoteEvent
        {
            $payload = json_decode($request->getContent(), true);
            return new RemoteEvent('stripe', $payload['type'], $payload);
        }
    }
    

    Register the parser in your service container:

    $container->set('stripe.parser', StripeRequestParser::class);
    
  • Event-Driven Handling: Dispatch Laravel events after parsing:

    $event = $this->parseWebhook($request);
    event(new WebhookReceived($event));
    

2. Sending Webhooks (Outbound)

  • Basic Sending: Use the Webhook class with Laravel’s HTTP client for async support:
    $webhook = new Webhook('https://slack.com/webhook');
    $webhook->setPayload(['text' => 'Alert: New order #'.$order->id]);
    $webhook->setSecret(config('services.slack.signing_secret')); // Optional
    $response = $webhook->send();
    
  • Retry Logic: Leverage Symfony’s retry mechanism via Webhook::sendWithRetry() (if using Symfony Messenger) or wrap in Laravel’s queue job:
    class SendWebhookJob implements ShouldQueue
    {
        public function handle()
        {
            $webhook = new Webhook('https://api.example.com/webhook');
            $webhook->sendWithRetry(3); // Retry 3 times
        }
    }
    

3. Integration with Laravel Ecosystem

  • Service Provider: Bind Symfony’s Webhook to Laravel’s container for dependency injection:

    public function register()
    {
        $this->app->singleton(Webhook::class, function ($app) {
            return new Webhook();
        });
    }
    
  • Facade: Create a facade for cleaner syntax:

    // app/Facades/Webhook.php
    public static function send(string $url, array $payload): Response
    {
        return app(Webhook::class)->send($url, $payload);
    }
    

    Usage:

    Webhook::send('https://example.com', ['data' => $payload]);
    
  • Middleware for Inbound: Centralize webhook validation in middleware:

    public function handle($request, Closure $next)
    {
        $webhook = new Webhook($request->getContent(), $request->header('x-signature'));
        if (!$webhook->validate(config('webhooks.secret'))) {
            abort(401);
        }
        return $next($request);
    }
    

Advanced Patterns

1. Dynamic Webhook Endpoints

Use Laravel’s routing to dynamically handle multiple webhook providers:

Route::post('/webhooks/{provider}', [WebhookController::class, 'handle'])
    ->middleware('validate.webhook');
public function handle($provider, Request $request)
{
    $parser = app("webhook.parser.$provider"); // Resolves to StripeRequestParser, etc.
    $event = $parser->parse($request);
    // Handle event...
}

2. Idempotency

Implement idempotency keys for outbound webhooks:

$webhook = new Webhook('https://example.com/webhook');
$webhook->setIdempotencyKey($order->id); // Ensures duplicate payloads are ignored
$webhook->send();

3. Async Processing with Queues

Offload webhook processing to queues for performance:

class ProcessWebhookJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable;

    public function handle()
    {
        $event = $this->parseWebhook($this->request);
        // Process event...
    }
}

4. Testing Webhooks

Use Laravel’s HTTP tests with mock responses:

public function test_webhook_received()
{
    $response = $this->postJson('/webhooks/stripe', $payload, [
        'HTTP_X_STRIPE_SIGNATURE' => $this->generateSignature($payload),
    ]);
    $response->assertOk();
}

Gotchas and Tips

Common Pitfalls

1. Signature Mismatches

  • Issue: Webhook validation fails due to incorrect secret keys or payload hashing.
  • Debugging:
    • Verify the secret key matches exactly (including case sensitivity).
    • Ensure the payload is hashed in the same order as the webhook sender.
    • Use var_dump($webhook->getSignature(), $request->header('x-signature')) to compare.
  • Fix:
    // Debug signature generation
    $secret = 'your-secret';
    $payload = $request->getContent();
    $expectedSignature = hash_hmac('sha256', $payload, $secret);
    

2. Payload Parsing Errors

  • Issue: RequestParserInterface fails to parse JSON or malformed payloads.
  • Debugging:
    • Log the raw payload: Log::debug('Raw payload:', [$request->getContent()]).
    • Validate JSON structure: json_decode($request->getContent(), true).
  • Fix:
    class FallbackRequestParser implements RequestParserInterface
    {
        public function parse(Request $request): RemoteEvent
        {
            $payload = json_decode($request->getContent(), true) ?: [];
            return new RemoteEvent('unknown', 'fallback', $payload);
        }
    }
    

3. Retry Logic Overhead

  • Issue: Outbound webhooks with retries may cause performance bottlenecks.
  • Debugging:
    • Monitor queue jobs for stuck retries: php artisan queue:work --sleep=3 --tries=3.
  • Fix:
    • Limit retries: $webhook->sendWithRetry(3).
    • Use exponential backoff in custom retry logic.

4. Middleware Order

  • Issue: Webhook validation middleware may run after route logic, causing security gaps.
  • Fix: Ensure middleware runs before route handling:
    Route::post('/webhook', [WebhookController::class, 'handle'])
        ->middleware('validate.webhook:stripe'); // Runs before controller
    

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament