rasuvaeff / yii3-outbox-webhooks-bridge
Bridge between yii3-outbox and yii3-webhooks for durable at-least-once webhook delivery
Package info
github.com/rasuvaeff/yii3-outbox-webhooks-bridge
pkg:composer/rasuvaeff/yii3-outbox-webhooks-bridge
Requires
- php: 8.3 - 8.5
- rasuvaeff/yii3-outbox: ^1.0
- rasuvaeff/yii3-webhooks: ^1.0
Requires (Dev)
- ergebnis/composer-normalize: ^2.51
- friendsofphp/php-cs-fixer: ^3.95
- infection/infection: ^0.29
- maglnet/composer-require-checker: ^4.17
- phpunit/phpunit: ^11.5
- rector/rector: ^2.4
- roave/backward-compatibility-check: ^8.0
- vimeo/psalm: ^6.16
This package is auto-updated.
Last update: 2026-06-19 20:49:21 UTC
README
Bridges yii3-outbox and yii3-webhooks for durable at-least-once webhook delivery. Each outbox message is converted to a WebhookEvent and dispatched to configured endpoints via an injected WebhookDispatcher.
Using an AI coding assistant? llms.txt has a compact API reference designed for LLMs.
Requirements
- PHP 8.3–8.5
rasuvaeff/yii3-outbox^1.0rasuvaeff/yii3-webhooks^1.0- A
WebhookDispatcherimplementation (e.g. a PSR-18-based adapter in your app) - A
WebhookDeliveryStorageimplementation (e.g.yii3-webhooks-db)
Installation
composer require rasuvaeff/yii3-outbox-webhooks-bridge
Usage
1. Configure endpoints
use Rasuvaeff\Yii3OutboxWebhooksBridge\ConfigWebhookEndpointProvider; use Rasuvaeff\Yii3Webhooks\WebhookEndpoint; $endpointProvider = new ConfigWebhookEndpointProvider(map: [ 'order.created' => [ new WebhookEndpoint(url: 'https://partner-a.example.com/hooks', secret: 'secret-a'), new WebhookEndpoint(url: 'https://partner-b.example.com/hooks', secret: 'secret-b'), ], 'order.paid' => [ new WebhookEndpoint(url: 'https://partner-a.example.com/hooks', secret: 'secret-a'), ], ]);
2. Wire the publisher
use Rasuvaeff\Yii3OutboxWebhooksBridge\OutboxWebhookPublisher; $publisher = new OutboxWebhookPublisher( dispatcher: $dispatcher, // your WebhookDispatcher impl endpointProvider: $endpointProvider, deliveryStorage: $deliveryStorage, // e.g. DbWebhookDeliveryStorage );
3. Run the outbox processor
use Rasuvaeff\Yii3Outbox\Processor; $processor = new Processor( storage: $outboxStorage, publisher: $publisher, clock: $clock, ); // In a background worker or console command: $result = $processor->process(types: ['order.created', 'order.paid']);
Behaviour
| Situation | Result |
|---|---|
Endpoint returns Delivered |
Delivery saved; message marked published |
Endpoint returns Failed |
Delivery saved; PublishException thrown → outbox retries |
| Dispatcher throws | PublishException thrown → outbox retries |
| No endpoints for type | Silent success (zero deliveries, message published) |
| Multiple endpoints, one fails | All dispatched; PublishException thrown → all retried |
Event id dedup
The outbox message id is reused as the WebhookEvent id. On retry, the same id
is sent again. Receivers should use the X-Webhook-Id header (set by
HmacSha256Signer) for idempotency.
Custom endpoint provider
Implement WebhookEndpointProvider to load endpoints from a database, cache, or
any runtime source:
use Rasuvaeff\Yii3OutboxWebhooksBridge\WebhookEndpointProvider; use Rasuvaeff\Yii3Webhooks\WebhookEndpoint; final readonly class DbWebhookEndpointProvider implements WebhookEndpointProvider { public function __construct(private \PDO $db) {} public function getEndpointsForType(string $type): array { // load from DB... } }
Security
- Secrets are never stored in
WebhookDelivery(comes fromyii3-webhooks). - Use
HmacSha256Signer(fromyii3-webhooks) as yourWebhookDispatcher's signer to authenticate outbound requests. - Receivers should validate the signature via
WebhookVerifierand useReplayGuardagainst nonce replay.
Examples
See examples/ for runnable scripts.
Development
docker run --rm -v "$PWD":/app -w /app composer:2 composer build docker run --rm -v "$PWD":/app -w /app composer:2 composer cs:fix docker run --rm -v "$PWD":/app -w /app composer:2 composer test
License
BSD-3-Clause. See LICENSE.md.