NovinkaZverejnili sme 50+ Laravel balíkov ako open source
Custom AI apps, agents and automation — Roundly ConsultingRoundly
Všetky balíky
Webhooks for Laravel

Prijímanie webhookov

Jeden endpoint na odosielateľa. Adresou je kľúč v URL — 32 náhodných alfanumerických znakov, približne 190 bitov; tajný kľúč sa zobrazí len raz. Vygenerujte ho alebo doslovne importujte ten, ktorý vám dal odosielateľ:

use RoundlyConsulting\Webhooks\DataTransferObjects\InboundEndpointOptions;
use RoundlyConsulting\Webhooks\Facades\Webhooks;

$created = Webhooks::for($team)->inbound()->create(handler: 'billing');   // owned by $team
$created = Webhooks::inbound()->create(handler: 'billing');               // owner-less
$created = Webhooks::inbound()->create(
    handler: 'github', scheme: 'github', name: 'GitHub',
    secret: $theSecretGitHubShowedYou,                                   // imported verbatim
    options: new InboundEndpointOptions(rateLimitPerMinute: 120, maxPayloadBytes: 1_048_576,
        contentTypes: ['application/json'], toleranceSeconds: 60),
);

$created->endpoint;   // InboundEndpoint
$created->url;        // route('webhooks.inbound', key) — or inbound.route.url_base + '/' + key
$created->secret;     // whsec_… (generated) — never readable again

Neznámy alias handlera vyhodí UnknownHandlerException, neznáma schéma UnknownSchemeException a vlastník, ktorého kľúč nezodpovedá webhooks.key_type, vyhodí InvalidWebhooksConfigurationException ešte predtým, ako sa čokoľvek zapíše. Voľby sa kontrolujú voči limitom v časti Správa endpointov.

Spracovanie — pevné poradie, rozhoduje prvé zlyhanie

POST {prefix}/{key} (predvolene /webhooks/{key}, názov trasy webhooks.inbound) prejde tieto kroky. Žiadne odmietnutie neuloží doručenie ani nič nezaradí do frontu:

#KontrolaOdpoveďUdalosť
1Kľúč má 32 alfanumerických znakov404 not_found—
2Vyhľadanie endpointu — neznámy, mäkko zmazaný alebo vypnutýRovnaké telo 404Len vypnutý: WebhookRejected(endpoint_disabled)
3Strážca odmietnutí na endpoint a IP (60 za minútu, počítajú sa len odmietnutia v krokoch 4–7)429 rate_limited + Retry-AfterRaz za okno
4Veľkosť — Content-Length nad limitom alebo telo čítané do limitu + 1 bajt413 payload_too_largeWebhookRejected
5Typ obsahu (parametre sa ignorujú)415 unsupported_content_typeWebhookRejected
5bLimit overených doručení na endpoint (600 za minútu) — kontroluje sa tu, započíta sa až po overení podpisu429 + Retry-AfterRaz za okno
6Podpis a časová pečiatka — schéma endpointu401 missing_signature · invalid_signature · missing_timestamp · invalid_timestamp · stale_timestamp · missing_id · key_not_bound · unknown_scheme · replayedWebhookRejected
7Dekódovanie JSON — len polia, hĺbka ≤ 64, veľké čísla ako reťazce; skalár na najvyššej úrovni sa odmietne400 invalid_payloadWebhookRejected
8Opakovanie a idempotenciaDuplicita → 200 (alebo 409 conflict); rovnaké id, iné telo → 409 idempotency_key_reused; práve sa spracúva → 409 in_progress + Retry-After: 1; bez id → 400 missing_idOpätovne použité alebo chýbajúce id: WebhookRejected
9Sanitizácia → uloženie (+ zaznamenanie nonce RFC 9421 v jednej transakcii) → zaradenie ProcessInboundDelivery202 accepted; zlyhaný sanitizér → 500 sanitizer_failedWebhookReceived sa vyvolá na workeri
  • Veľkosť a typ obsahu sa kontrolujú pred podpisom: neoverený volajúci nedokáže prinútiť aplikáciu počítať MAC nad megabajtmi. Limit doručení sa kontroluje tiež pred ním, započíta sa však len overené doručenie.
  • Strážca odmietnutí a limit doručení sú samostatné počítadlá — chybné podpisy cudzieho z inej IP nikdy neminú rozpočet skutočného odosielateľa, pokiaľ $request->ip() vracia adresu klienta (nastavte dôveryhodné proxy).
  • Neznáme kľúče nevyvolajú udalosť, takže cudzí nemôže zahlcovať vaše listenery; odmietnutie pre limit sa vyvolá raz za okno limitéra; duplicita nevyvolá nič.
  • Odmietnutia sú application/problem+json (RFC 9457) s preloženým nadpisom — bez interných detailov a bez názvov tried.
// 202 — stored and queued
{"status": "accepted", "id": "01JCDEF0000000000000000000"}

// 200 — a duplicate (inbound.on_duplicate = accept)
{"status": "duplicate"}

// any refusal — application/problem+json (RFC 9457), no internals, no class names
{"title": "…", "status": 401, "code": "invalid_signature"}

Ochrana pred opakovaním a idempotencia

  • Kľúčom je id doručenia zo schémy (pri predvolenej schéme podpísané webhook-id), inak hlavička Idempotency-Key (v úvodzovkách alebo samostatný token, kým je zapnuté inbound.accept_idempotency_header), inak 400. Kľúč dlhší ako 255 bajtov alebo s netlačiteľnými znakmi sa zmení na sha256:<hex>.
  • Deduplikácia beží cez úložisko idempotencie Sentinelu, oddelene pre každý endpoint, s odtlačkom SHA-256 tela a pamäťou inbound.dedupe_ttl (7 dní): prvá požiadavka sa uloží a zaradí, opakovanie je duplicita, rovnaký kľúč s iným telom je idempotency_key_reused.
  • Podpísané id má prednosť: podvrhnutý Idempotency-Key nedokáže rozdeliť jednu podpísanú správu na dve.
  • Nepodpísané id (hlavička doručenia v štýle GitHubu, samostatný Idempotency-Key) majú druhého strážcu podľa odtlačku tela, takže rovnaké telo pod novým id je duplicita. Pri formáte bez podpísanej časovej pečiatky tento strážca kontroluje aj uložené doručenia — rovnaké telo zostáva duplicitou, kým existuje riadok prvého doručenia (payloads.delivery_retention_days), nielen počas okna deduplikácie.
  • Poistkou je unikátny index nad (endpoint_id, idempotency_key): doručenie, ktorého id úložisko zabudlo, je stále jedno doručenie.
  • Ak úložisko zlyhá až po uložení a zaradení doručenia, úspech odosielateľa sa nezmení na chybu — požiadavka aj tak odpovie 202.
  • Doručenie uložené počas výpadku frontu zaradí jeho najbližšia duplicita a pravidelný beh po inbound.requeue_after (60 s).

Nonce v RFC 9421 používajú odložené API Sentinelu: krok s podpisom overí, že nonce ešte nebol použitý, a krok s uložením ho zaznamená v tej istej databázovej transakcii, ktorá zapisuje doručenie — pred odpoveďou 202 a pred zaradením jobu handlera. Požiadavka odmietnutá cestou — 400, 500 sanitizer_failed, chyba databázy — nechá nonce voľný, takže nezmenené opätovné odoslanie sa posúdi nanovo; z dvoch súbežných kópií sa uloží presne jedna. Úložisko nonce v Sentineli musí implementovať InspectsNonces (jeho úložiská database a cache to robia). Pozrite odložené nonce v Sentineli a idempotenciu.

Overenie bez spracovania

Pre vlastný postup spustí verify() len schému endpointu — bez kontroly veľkosti, bez deduplikácie, nič sa neuloží (nonce RFC 9421 sa zaznamená hneď):

use RoundlyConsulting\Webhooks\Exceptions\WebhookSignatureException;

try {
    $verified = Webhooks::inbound()->verify($endpoint, $request);   // the scheme only — nothing stored
    $verified->deliveryId;   // also: timestamp, eventType, idIsSigned, slot
} catch (WebhookSignatureException $e) {
    $e->reason();            // RejectionReason, e.g. RejectionReason::StaleTimestamp
}

Vlastná trasa

Trasu balíka vypnite cez WEBHOOKS_INBOUND_ROUTE=off a pripojte ju, kam potrebujete. Route::webhooks() pripojí POST {prefix}/{key} s názvom webhooks.inbound (URL adresy endpointov sa podľa nej riadia) a pre daný prefix vypne TrimStrings a ConvertEmptyStringsToNull — spracovanie si surové telo overí a dekóduje samo. Nikdy ju nedávajte do skupiny middleware web: odosielateľ nemá session ani CSRF token.

// WEBHOOKS_INBOUND_ROUTE=off, then in routes/web.php or routes/api.php:
Route::webhooks('hooks', middleware: ['throttle:120,1']);

Trasa vlastného návrhu volá spracovanie priamo — potom zaregistrujte obe výnimky pre middleware sami:

Route::post('/hooks/{key}', fn (Request $request, string $key) => Webhooks::inbound()->receive($key, $request));
TrimStrings::skipWhen(fn (Request $r) => $r->is('hooks/*'));
ConvertEmptyStringsToNull::skipWhen(fn (Request $r) => $r->is('hooks/*'));

Nastavte webhooks.inbound.route.url_base (WEBHOOKS_INBOUND_URL), aby ->url ukazovalo adresu, ktorú odosielatelia naozaj používajú, napríklad za proxy.

Prejavte lásku k open source

Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.

Ďalšie spôsoby podpory vrátane kryptomien

Odoslaním daru súhlasíte s našimi podmienkami prijímania darov.

Chcete to zabudovať do svojho produktu?

Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.