strucura/webhooks

Lightweight utilities for dispatching and managing webhooks from your application. This package provides a small Connector abstraction, a WebhookProcessor contract for building outgoing requests, and job wiring that records attempts and schedules retries.

Fund package maintenance!
Strucura

Installs: 0

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/strucura/webhooks

0.1 2026-02-16 15:03 UTC

This package is auto-updated.

Last update: 2026-02-16 15:04:51 UTC


README

Caution

This is not ready for production usage, use at your own risk.

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

Lightweight utilities for dispatching and managing webhooks from your application. This package provides a small Connector abstraction, a WebhookProcessor contract for building outgoing requests, and job wiring that records attempts and schedules retries.

Installation

You can install the package via composer:

composer require strucura/webhooks

You can publish and run the migrations with:

php artisan vendor:publish --tag="webhooks-migrations"
php artisan vendor:publish --tag="webhooks-config"
php artisan migrate

Quick overview

  • Connector: encapsulates base URL, default headers and client options. Optionally implements rate limiting to protect external APIs.
  • WebhookProcessor: responsible for producing the request data and choosing the Connector to use.
  • ProcessWebhookJob: invokes the processor, logs results, saves attempts, and schedules retries when appropriate. Automatically handles rate limiting via middleware.

Configuration Example

use Strucura\Webhooks\BackoffStrategies\ExponentialBackoff;
use App\Webhooks\Events\OrderCreatedEvent;
use App\Webhooks\Processors\OrderCreatedProcessor;

return [
    'defaults' => [
        'max_attempts' => 5,
        'queue' => 'webhooks',
        'backoff_strategy' => new ExponentialBackoff(2, 600),
    ],

    'events' => [
        OrderCreatedEvent::class => [
            'processor' => OrderCreatedProcessor::class,
            'max_attempts' => 10, // Override default
            'backoff_strategy' => new JitteredBackoff(60, 30), // Override default
        ],
    ],
];

Backoff Strategies

Backoff strategies determine the delay between webhook retry attempts. Configure them in your config/webhooks.php file.

Strategy Description Parameters Example Usage
StaticIntervalBackoff Fixed delay between all retries $waitTimeInSeconds (int) new StaticIntervalBackoff(60) - waits 60 seconds between each retry
ExponentialBackoff Exponentially increasing delay (2^n) $baseWaitTime (int, default: 1)
$maxWaitTime (int, default: 60)
new ExponentialBackoff(1, 300) - starts at 1s, doubles each time, caps at 300s
LinearBackoff Linearly increasing delay $baseWaitTime (int, default: 1) new LinearBackoff(30) - waits 30s, 60s, 90s, 120s...
JitteredBackoff Linear delay with random jitter to prevent thundering herd $baseWaitTime (int, default: 1)
$maxJitter (int, default: 5)
new JitteredBackoff(30, 10) - waits 30s + 0-10s random, then 60s + 0-10s random...

Artisan Commands

The package includes several artisan commands for managing webhooks.

Command Description Options Example
webhooks:retry Retry failed webhooks that haven't reached max attempts --all - Retry all incomplete webhooks regardless of attempt count php artisan webhooks:retry
php artisan webhooks:retry --all
webhooks:increase-attempts Interactively increase max attempts for a specific webhook None (interactive prompts) php artisan webhooks:increase-attempts
make:connector Generate a new Connector class Standard make command options php artisan make:connector StripeConnector
make:webhook-processor Generate a new WebhookProcessor class Standard make command options php artisan make:webhook-processor StripeChargeProcessor

Rate Limiting

Protect external APIs from being overwhelmed by implementing rate limiting on your connectors. Rate limiting prevents your application from exceeding API rate limits and receiving 429 errors.

How It Works

  • Job Middleware: Rate limiting is implemented via job middleware that checks before webhook execution
  • Automatic Retry: When rate limited, jobs are released back to the queue with an appropriate delay
  • Doesn't Consume Attempts: Throttled jobs don't count toward max retry attempts
  • Per-Connector Configuration: Each connector can define its own rate limits

Basic Usage

Implement the HasRateLimit contract on any connector to enable rate limiting:

<?php

namespace App\Http\Integrations;

use Strucura\Webhooks\Abstracts\Connector;
use Strucura\Webhooks\Contracts\HasRateLimit;

class StripeConnector extends Connector implements HasRateLimit
{
    protected function resolveBaseUrl(): string
    {
        return 'https://api.stripe.com';
    }

    public function rateLimitKey(): string
    {
        // Unique key for this connector's rate limit
        return 'webhook:connector:stripe';
    }

    public function rateLimitMaxAttempts(): int
    {
        // Stripe allows 100 requests per second
        return 100;
    }

    public function rateLimitDecaySeconds(): int
    {
        // Time window in seconds
        return 1; // per second
    }
}

Configuration Methods

Method Return Type Description
rateLimitKey() string Unique identifier for this rate limit. Used to track requests in Laravel's RateLimiter.
rateLimitMaxAttempts() int Maximum number of webhook jobs allowed within the time window.
rateLimitDecaySeconds() int Time window in seconds. After this period, the counter resets.

Behavior When Rate Limited

When a webhook job exceeds the rate limit:

  1. The job is released back to the queue (not failed)
  2. Release delay equals the time until the rate limit window resets
  3. The job will be retried automatically when the window resets
  4. Webhook attempts counter is not incremented (throttling doesn't count as a failure)
  5. Jobs have a 24-hour retry window to allow sufficient time for throttling delays

Testing

Prefer Laravel's HTTP testing helpers for deterministic tests. Example:

use Illuminate\Support\Facades\Http;

Http::fake([
		'https://api.example.test/*' => Http::response(['ok' => true], 200),
]);

// Run code that triggers the request (e.g. ProcessWebhookJob)

Http::assertSent(function ($request) {
		return $request->url() === 'https://api.example.test/hook' &&
					 $request->method() === 'POST' &&
					 $request['id'] === 1;
});

Examples

Stripe connector & processor example

Below is an example of how you might implement a Stripe connector and a processor that uses it to create a charge. This is documentation only — the actual classes are not required in this repository. Implement them in your application under App/Http/Integrations or a similar location.

StripeConnector (example)

<?php

namespace App\Http\Integrations;

use Strucura\Webhooks\Abstracts\Connector;
use Strucura\Webhooks\Contracts\HasRateLimit;

class StripeConnector extends Connector implements HasRateLimit
{
	public function __construct(protected string $apiKey = '')
	{
		parent::__construct();
		if ($apiKey !== '') {
			$this->addHeader('Authorization', 'Bearer ' . $apiKey);
		}
	}

	protected function resolveBaseUrl(): string
	{
		return 'https://api.stripe.com';
	}

	protected function defaultHeaders(): array
	{
		return [
			'Accept' => 'application/json',
		];
	}

	protected function defaultConfig(): array
	{
		return [
			'timeout' => 30,
		];
	}

	// Rate limiting: Stripe allows 100 requests per second
	public function rateLimitKey(): string
	{
		return 'webhook:connector:stripe';
	}

	public function rateLimitMaxAttempts(): int
	{
		return 100;
	}

	public function rateLimitDecaySeconds(): int
	{
		return 1; // per second
	}
}

StripeChargeProcessor (example)

<?php

namespace App\Http\Integrations;

use Strucura\Webhooks\Abstracts\WebhookEvent;
use Strucura\Webhooks\Abstracts\WebhookProcessor;
use Strucura\Webhooks\Enums\HttpMethod;
use Strucura\Webhooks\Connectors\StripeConnector;
use Strucura\Webhooks\Models\Webhook;

class StripeChargeProcessor extends WebhookProcessor
{
	public function canDispatch(WebhookEvent $event): bool
	{
		return true;
	}

	public function canRetry(Webhook $webhook): bool
	{
		return true;
	}

	public function requestBody(): array
	{
		return [
			'amount' => 1000,
			'currency' => 'usd',
			'source' => 'tok_visa',
			'description' => 'Example charge',
		];
	}

	public function connector(): StripeConnector
	{
		$apiKey = config('services.stripe.secret', '');

		return new StripeConnector($apiKey);
	}

	protected function requestMethod(): HttpMethod
	{
		return HttpMethod::POST;
	}

	protected function requestEndpoint(): string
	{
		return '/v1/charges';
	}

	protected function requestHeaders(): array
	{
		return [
			'Content-Type' => 'application/x-www-form-urlencoded',
		];
	}
}

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.