PHPackages                             rasuvaeff/yii3-webhooks - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. rasuvaeff/yii3-webhooks

ActiveLibrary[API Development](/categories/api)

rasuvaeff/yii3-webhooks
=======================

HMAC-signed webhook infrastructure for Yii3 — outbound signing, inbound verification, replay protection, retry policy

v1.0.0(today)001BSD-3-ClausePHPPHP 8.3 - 8.5CI passing

Since Jun 19Pushed todayCompare

[ Source](https://github.com/rasuvaeff/yii3-webhooks)[ Packagist](https://packagist.org/packages/rasuvaeff/yii3-webhooks)[ Docs](https://github.com/rasuvaeff/yii3-webhooks)[ RSS](/packages/rasuvaeff-yii3-webhooks/feed)WikiDiscussions master Synced today

READMEChangelogDependencies (10)Versions (1)Used By (1)

rasuvaeff/yii3-webhooks
=======================

[](#rasuvaeffyii3-webhooks)

[![Stable Version](https://camo.githubusercontent.com/16db8ca19c7bdb89c90d31d18325f98af140e9e7004e987c3661843f946e57ec/68747470733a2f2f706f7365722e707567782e6f72672f7261737576616566662f796969332d776562686f6f6b732f762f737461626c65)](https://packagist.org/packages/rasuvaeff/yii3-webhooks)[![Total Downloads](https://camo.githubusercontent.com/7c951e3c9bcbe7e25f899993de7589c10deafcb438238efbfae4ffffe65a41ca/68747470733a2f2f706f7365722e707567782e6f72672f7261737576616566662f796969332d776562686f6f6b732f646f776e6c6f616473)](https://packagist.org/packages/rasuvaeff/yii3-webhooks)[![Build](https://github.com/rasuvaeff/yii3-webhooks/actions/workflows/build.yml/badge.svg)](https://github.com/rasuvaeff/yii3-webhooks/actions)[![Static analysis](https://github.com/rasuvaeff/yii3-webhooks/actions/workflows/static-analysis.yml/badge.svg)](https://github.com/rasuvaeff/yii3-webhooks/actions)[![Psalm Level](https://camo.githubusercontent.com/72b2894b5a3ab66d081bc658674d698410395811eafcd0dfe9c4e9aac42b0f7e/68747470733a2f2f73686570686572642e6465762f6769746875622f7261737576616566662f796969332d776562686f6f6b732f6c6576656c2e737667)](https://shepherd.dev/github/rasuvaeff/yii3-webhooks)[![PHP](https://camo.githubusercontent.com/a6c4c6e3bca032fd6abf26ad9927d434835fe7ba5d73a1f298ea04b7f6032a81/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f646570656e64656e63792d762f7261737576616566662f796969332d776562686f6f6b732f706870)](https://packagist.org/packages/rasuvaeff/yii3-webhooks)[![License](https://camo.githubusercontent.com/cd766433c01b324b8f604d92d1f29678e41446c35def076a72d57a192b4dbcd6/68747470733a2f2f706f7365722e707567782e6f72672f7261737576616566662f796969332d776562686f6f6b732f6c6963656e7365)](https://packagist.org/packages/rasuvaeff/yii3-webhooks)

HMAC-signed webhook infrastructure for Yii3: outbound signing, inbound verification, replay protection, and delivery retry policy. It signs the exact payload bytes you send or receive; no hard HTTP client dependency — bring your own dispatcher.

> Using an AI coding assistant? [llms.txt](llms.txt) has a compact API reference you can use.

Requirements
------------

[](#requirements)

- PHP 8.3+
- `psr/clock` ^1.0

Installation
------------

[](#installation)

```
composer require rasuvaeff/yii3-webhooks
```

Usage
-----

[](#usage)

### Signing an outbound webhook

[](#signing-an-outbound-webhook)

```
use Rasuvaeff\Yii3Webhooks\HmacSha256Signer;
use Rasuvaeff\Yii3Webhooks\WebhookEndpoint;
use Rasuvaeff\Yii3Webhooks\WebhookEvent;

$signer = new HmacSha256Signer();
$endpoint = new WebhookEndpoint(
    url: 'https://partner.example.com/webhook',
    secret: 'whsec_...',
);

$event = WebhookEvent::create(
    type: 'order.created',
    payload: json_encode(['orderId' => 42]),
);

$timestamp = $clock->now()->getTimestamp();
$signature = $signer->sign(
    payload: $event->getPayload(),
    secret: $endpoint->getSecret(),
    timestamp: $timestamp,
    eventId: $event->getId(),
);

// Add to outgoing request:
// X-Webhook-Id:
// X-Webhook-Signature: t=1717228800,v1=
$header = $signature->toHeaderValue();
```

### Verifying an inbound webhook

[](#verifying-an-inbound-webhook)

```
use Rasuvaeff\Yii3Webhooks\HmacSha256Signer;
use Rasuvaeff\Yii3Webhooks\WebhookSignature;
use Rasuvaeff\Yii3Webhooks\WebhookVerifier;

$verifier = new WebhookVerifier(
    signer: new HmacSha256Signer(),
    clock: $clock,
    toleranceSeconds: 300,
);

$signature = WebhookSignature::fromHeaderValue(
    $request->getHeaderLine('X-Webhook-Signature'),
);

$eventId = $request->getHeaderLine('X-Webhook-Id');

$valid = $verifier->verify(
    payload: (string) $request->getBody(),
    secret: 'whsec_...',
    signature: $signature,
    eventId: $eventId,
);
```

### Replay protection

[](#replay-protection)

Use the event ID (from the `X-Webhook-Id` header) as the nonce — it uniquely identifies the delivery and allows replay detection independently of signature verification.

```
use Rasuvaeff\Yii3Webhooks\InMemoryNonceStorage;
use Rasuvaeff\Yii3Webhooks\ReplayGuard;

$guard = new ReplayGuard(new InMemoryNonceStorage());

// $eventId = $request->getHeaderLine('X-Webhook-Id');
if ($valid) {
    $guard->accept($eventId); // throws RuntimeException if already seen
    // process the webhook...
}
```

### Tracking deliveries

[](#tracking-deliveries)

```
use Rasuvaeff\Yii3Webhooks\InMemoryDeliveryStorage;
use Rasuvaeff\Yii3Webhooks\WebhookDelivery;
use Rasuvaeff\Yii3Webhooks\WebhookRetryPolicy;

$storage = new InMemoryDeliveryStorage();
$policy = WebhookRetryPolicy::fixed(maxAttempts: 3, delaySeconds: 60);
// or: WebhookRetryPolicy::exponential(maxAttempts: 5, baseSeconds: 10, cap: 3600)

$delivery = WebhookDelivery::create(event: $event, endpoint: $endpoint);
$storage->save($delivery);

// After attempt:
$delivery = $delivery->withAttempt($clock->now(), error: 'Connection refused');
$storage->save($delivery);

if ($policy->isReadyForRetry($delivery, $clock->now())) {
    // retry...
}
```

API reference
-------------

[](#api-reference)

### WebhookEvent

[](#webhookevent)

MethodDescription`create(type, payload, occurredAt?)`Factory with auto-generated ID`getId()`32-char hex ID`getType()`Event type string`getPayload()`Raw payload bytes to sign and deliver`getOccurredAt()``DateTimeImmutable`### WebhookEndpoint

[](#webhookendpoint)

MethodDescription`__construct(url, secret, headers?)`URL must use http/https; secret non-empty`getUrl()`Endpoint URL`getSecret()`Shared secret (not stored in delivery)`getHeaders()`Additional request headers### WebhookSignature

[](#webhooksignature)

MethodDescription`__construct(timestamp, value)`Positive timestamp, non-empty value`fromHeaderValue(header)`Parse `t=...,v1=...` format`toHeaderValue()`Serialize to `t=...,v1=...` format`getTimestamp()`Unix timestamp`getValue()`HMAC hex string### WebhookSigner

[](#webhooksigner)

Interface for outbound signature implementations. Custom signers must sign the exact payload bytes and return a `WebhookSignature`.

MethodDescription`sign(payload, secret, timestamp, eventId)`Returns `WebhookSignature`### HmacSha256Signer

[](#hmacsha256signer)

Signs `"{eventId}.{timestamp}.{payload}"` with the secret using HMAC-SHA256. `payload` is the exact HTTP body string, not a re-encoded JSON value.

MethodDescription`sign(payload, secret, timestamp, eventId)`Returns `WebhookSignature`### WebhookVerifier

[](#webhookverifier)

MethodDescription`__construct(signer, clock, toleranceSeconds?)`Default tolerance: 300s`verify(payload, secret, signature, eventId)`Returns `bool`; uses `hash_equals`### WebhookRetryPolicy

[](#webhookretrypolicy)

MethodDescription`fixed(maxAttempts?, delaySeconds?)`Constant delay; default: 3 attempts, 60s`exponential(maxAttempts?, baseSeconds?, cap?, multiplier?)`Doubling delay; default: 5 attempts, 10s base, 3600s cap`getMaxAttempts()`Max retry attempts`nextDelaySeconds(attempts)`Delay before next attempt; `attempts` = current attempt count`shouldRetry(delivery)`Returns `true` when status is Pending and attempts &lt; maxAttempts`isReadyForRetry(delivery, now)`Returns `true` when delay has elapsed### WebhookDelivery

[](#webhookdelivery)

MethodDescription`create(event, endpoint, createdAt?)`Factory; stores URL only (no secret)`getId()`32-char hex ID`getEventId()`Source event ID`getEventType()`Source event type`getEndpointUrl()`Endpoint URL`getStatus()``WebhookDeliveryStatus` enum`getCreatedAt()``DateTimeImmutable` creation time`getAttempts()`Attempt count`getLastAttemptAt()``?DateTimeImmutable``getLastError()``?string``withAttempt(at, error?)`Returns new instance with incremented attempts`withStatus(status)`Returns new instance with updated status### WebhookDeliveryStorage

[](#webhookdeliverystorage)

Interface for persistence backends. Core ships `InMemoryDeliveryStorage` for tests; use a persistent backend in production.

MethodDescription`save(delivery)`Stores a delivery attempt record`findPending(limit)`Returns pending deliveries`markDelivered(delivery)`Marks a delivery as delivered`markFailed(delivery)`Marks a delivery as failed`getById(id)`Loads a delivery by ID### ReplayGuard

[](#replayguard)

MethodDescription`__construct(NonceStorage)`Storage must atomically reject duplicate nonces`isReplayed(nonce)`Returns `bool``accept(nonce)`Marks as seen; throws `RuntimeException` if duplicate### WebhookDeliveryStatus

[](#webhookdeliverystatus)

Backed string enum with three cases:

CaseValue`Pending``'pending'``Delivered``'delivered'``Failed``'failed'`### WebhookDispatcher

[](#webhookdispatcher)

Interface for HTTP transport implementations. The package ships no concrete dispatcher — bring your own (Guzzle, PSR-18, etc.).

MethodDescription`dispatch(event, endpoint)`Sends signed webhook; returns `WebhookDelivery`### NonceStorage

[](#noncestorage)

Interface for replay-protection storage backends. Implementations must reject duplicate nonces atomically.

MethodDescription`has(nonce)`Returns `true` if nonce was already seen`add(nonce)`Stores nonce; returns `false` if duplicate### InMemoryNonceStorage

[](#inmemorynoncestorage)

Test-only `NonceStorage` implementation. Not safe for production use.

MethodDescription`has(nonce)`Returns `bool``add(nonce)`Returns `false` on duplicate`clear()`Removes all stored nonces### InMemoryDeliveryStorage

[](#inmemorydeliverystorage)

Test-only `WebhookDeliveryStorage` implementation. Implements `IteratorAggregate` and `Countable` for easy inspection.

MethodDescription`save(delivery)`Stores a delivery record`findPending(limit)`Returns pending deliveries`markDelivered(delivery)`Sets status to `Delivered``markFailed(delivery)`Sets status to `Failed``getById(id)`Loads a delivery by ID`clear()`Removes all recordsSecurity
--------

[](#security)

- Signature comparison uses `hash_equals()` — safe against timing attacks.
- `WebhookDelivery` stores only the endpoint URL, never the secret.
- All secret parameters are marked `#[\SensitiveParameter]` — they do not appear in stack traces.
- Always validate timestamps (tolerance) to prevent replay of old signatures.
- Use `ReplayGuard` with a persistent `NonceStorage` in production; storage implementations must reject duplicates atomically.

Examples
--------

[](#examples)

See [examples/](examples/) for complete usage examples.

Development
-----------

[](#development)

```
make install
make build
make cs-fix
make test
make test-coverage
make mutation
make release-check
```

`make test-coverage` and `make mutation` bootstrap `pcov` inside the `composer:2` container because the base image has no coverage driver.

License
-------

[](#license)

BSD-3-Clause. See [LICENSE.md](LICENSE.md).

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance100

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

0d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/b0812d5572a7041dfe36e222d295b2e6dc55833a605350fcde58a51a5965ed30?d=identicon)[rasuvaeff](/maintainers/rasuvaeff)

---

Top Contributors

[![rasuvaeff](https://avatars.githubusercontent.com/u/1352718?v=4)](https://github.com/rasuvaeff "rasuvaeff (2 commits)")

---

Tags

hmacphppsr-15replay-protectionwebhooksyii3signaturewebhookhmacyii3replay-protection

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm, Rector

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/rasuvaeff-yii3-webhooks/health.svg)

```
[![Health](https://phpackages.com/badges/rasuvaeff-yii3-webhooks/health.svg)](https://phpackages.com/packages/rasuvaeff-yii3-webhooks)
```

###  Alternatives

[standard-webhooks/standard-webhooks

Standard Webhooks - Open source tools and guidelines for sending webhooks easily, securely, and reliably

1.7k109.5k20](/packages/standard-webhooks-standard-webhooks)[tempest/framework

The PHP framework that gets out of your way.

2.2k31.1k12](/packages/tempest-framework)[kyon147/laravel-shopify

Shopify package for Laravel to aide in app development

482287.3k](/packages/kyon147-laravel-shopify)[hitrov/oci-api-php-request-sign

This package generates proper HTTP headers to sign Oracle Cloud Infrastructure API requests

251.5M4](/packages/hitrov-oci-api-php-request-sign)[stymiee/authnetjson

Library that abstracts Authorize.Net's JSON APIs. This includes the Advanced Integration Method (AIM), Automated Recurring Billing (ARB), Customer Information Manager (CIM), Transaction Reporting, Simple Integration Method (SIM), and Webhooks.

19585.5k](/packages/stymiee-authnetjson)[kayedspace/laravel-n8n

A complete, expressive, and fluent Laravel client for the n8n public REST API and Webhooks Triggering.

1409.0k1](/packages/kayedspace-laravel-n8n)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
