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

Laravel Webhook Client Laravel Package

spatie/laravel-webhook-client

Receive and process incoming webhooks in Laravel. Verify signatures, store webhook payloads, and handle them in queued jobs. Flexible configuration for multiple webhook endpoints and secure validation.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require spatie/laravel-webhook-client
    php artisan vendor:publish --provider="Spatie\WebhookClient\WebhookClientServiceProvider" --tag="webhook-client-config"
    php artisan vendor:publish --provider="Spatie\WebhookClient\WebhookClientServiceProvider" --tag="webhook-client-migrations"
    php artisan migrate
    
  2. Configure .env:

    WEBHOOK_CLIENT_SECRET=your_webhook_secret_here
    
  3. Set Up Routing:

    // routes/web.php
    Route::webhooks('webhook-receiving-url');
    
  4. Create a Job:

    php artisan make:job ProcessWebhook
    

    Extend Spatie\WebhookClient\Jobs\ProcessWebhookJob and implement handle():

    // app/Jobs/ProcessWebhook.php
    public function handle()
    {
        $payload = $this->webhookCall->payload;
        // Process payload (e.g., update a model, trigger an event)
    }
    
  5. Update Config:

    // config/webhook-client.php
    'process_webhook_job' => App\Jobs\ProcessWebhook::class,
    
  6. Test with a Webhook Sender: Use tools like webhook.site or Postman to send a signed request to your endpoint.


First Use Case

Receiving and Processing a Stripe Webhook:

  1. Configure Stripe’s webhook endpoint in your Laravel app.
  2. Use the ProcessWebhook job to handle events like payment_intent.succeeded:
    public function handle()
    {
        $payload = json_decode($this->webhookCall->payload, true);
        if ($payload['type'] === 'payment_intent.succeeded') {
            // Update your order model or trigger a notification
        }
    }
    

Implementation Patterns

Core Workflow

  1. Incoming Request:

    • Laravel routes the request to the WebhookController.
    • The package validates the signature using the configured SignatureValidator.
  2. Filtering:

    • Use WebhookProfile to filter requests (e.g., only process POST requests or specific payloads):
      // app/WebhookProfiles/CustomProfile.php
      public function shouldProcess(Request $request): bool
      {
          return $request->isMethod('post') && $request->has('event_type');
      }
      
      Update config:
      'webhook_profile' => App\WebhookProfiles\CustomProfile::class,
      
  3. Storage:

    • Valid requests are stored in the webhook_calls table (customizable via webhook_model).
    • Store additional headers:
      // config/webhook-client.php
      'store_headers' => ['Authorization', 'X-Custom-Header'],
      
  4. Processing:

    • Dispatch a queued job (ProcessWebhookJob) to handle the payload asynchronously.
    • Example job for GitHub webhooks:
      public function handle()
      {
          $payload = json_decode($this->webhookCall->payload, true);
          if ($payload['action'] === 'opened') {
              // Create a GitHub issue in your app
          }
      }
      
  5. Response:

    • Customize the response using WebhookResponse:
      // app/WebhookResponses/CustomResponse.php
      public function respondToValidWebhook(Request $request, WebhookConfig $config)
      {
          return response()->json(['status' => 'success', 'message' => 'Webhook processed'], 200);
      }
      
      Update config:
      'webhook_response' => App\WebhookResponses\CustomResponse::class,
      

Integration Tips

  1. Multiple Webhook Endpoints: Configure multiple entries in webhook-client.php under configs:

    'configs' => [
        [
            'name' => 'stripe',
            'signing_secret' => env('STRIPE_WEBHOOK_SECRET'),
            'process_webhook_job' => App\Jobs\ProcessStripeWebhook::class,
        ],
        [
            'name' => 'github',
            'signing_secret' => env('GITHUB_WEBHOOK_SECRET'),
            'process_webhook_job' => App\Jobs\ProcessGitHubWebhook::class,
        ],
    ],
    

    Route them separately:

    Route::webhooks('stripe-webhook', 'stripe');
    Route::webhooks('github-webhook', 'github');
    
  2. Testing: Use Laravel’s HTTP tests to simulate webhooks:

    public function test_webhook_received()
    {
        $response = $this->postJson('/webhook-receiving-url', ['key' => 'value'], [
            'Signature' => hash_hmac('sha256', '{"key":"value"}', env('WEBHOOK_CLIENT_SECRET')),
        ]);
        $response->assertStatus(200);
    }
    
  3. Retry Logic: Implement retries for failed jobs using Laravel’s retryAfter:

    public function handle()
    {
        try {
            // Process logic
        } catch (\Exception $e) {
            throw $e->retryAfter(5); // Retry after 5 seconds
        }
    }
    
  4. Logging: Log webhook payloads and errors for debugging:

    public function handle()
    {
        \Log::info('Webhook payload', ['payload' => $this->webhookCall->payload]);
        // Processing logic
    }
    

Gotchas and Tips

Pitfalls

  1. Signature Mismatches:

    • Ensure the signing_secret in .env matches the secret used by the webhook sender.
    • Debug with:
      // In a custom SignatureValidator
      \Log::debug('Computed signature:', [$computedSignature, $request->header('Signature')]);
      
  2. Queue Failures:

    • If the queue driver fails (e.g., Redis connection issues), webhooks may appear as "pending" in the database.
    • Monitor failed jobs with:
      php artisan queue:failed
      
  3. CSRF Exceptions:

    • Forgetting to exclude the webhook route from CSRF protection will cause 419 errors.
    • Verify in app/Http/Middleware/VerifyCsrfToken.php:
      protected $except = ['webhook-receiving-url'];
      
  4. Payload Size Limits:

    • Large payloads may exceed Laravel’s default input size limit (e.g., 1MB).
    • Increase in bootstrap/app.php:
      $app->useInputBinding(function ($request) {
          $request->enableHttpMethodParameterOverride();
          $request->merge([
              'payload' => $request->getContent(),
          ]);
      });
      
  5. Time Synchronization:

    • Webhook senders may use timestamps. Ensure your server’s time is synchronized to avoid signature validation failures.

Debugging Tips

  1. Inspect Webhook Calls: Query the webhook_calls table to debug:

    php artisan tinker
    >>> \Spatie\WebhookClient\Models\WebhookCall::latest()->first();
    
  2. Event Listeners: Listen to InvalidWebhookSignatureEvent for debugging:

    // app/Providers/EventServiceProvider.php
    protected $listen = [
        \Spatie\WebhookClient\Events\InvalidWebhookSignatureEvent::class => [
            \App\Listeners\LogInvalidWebhook::class,
        ],
    ];
    
  3. Custom Middleware: Add middleware to log all incoming webhooks:

    // app/Http/Middleware/LogWebhooks.php
    public function handle($request, Closure $next)
    {
        \Log::info('Incoming webhook', [
            'url' => $request->url(),
            'headers' => $request->header(),
            'payload' => $request->getContent(),
        ]);
        return $next($request);
    }
    

    Register in app/Http/Kernel.php:

    protected $middleware = [
        \App\Http\Middleware\LogWebhooks::class,
    ];
    

Extension Points

  1. Custom Models: Extend WebhookCall to add custom fields:

    // app/Models/CustomWebhookCall.php
    class CustomWebhookCall extends \Spatie\WebhookClient\Models\WebhookCall
    {
        protected $casts = [
            'metadata' => 'array',
        ];
    }
    

    Update config:

    'webhook_model' => App\Models\CustomWebhookCall::class,
    
  2. Dynamic Signing Secrets: Use a SignatureValidator to fetch secrets dynamically (e.g., from a database):

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.
codraw/framework-extra-bundle
codraw/messenger
codraw/security
codraw/mailer
codraw/contracts
codraw/profiling
codraw/dependency-injection
codraw/tester
codraw/core
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony