Ga naar de inhoud
agovena.
agovena.
Aan de slag
Community

Voor ontwikkelaars

Events en webhooks

Het verschil tussen interne domeinevents, ondertekende uitgaande berichten en betaalcallbacks van providers.

Op deze pagina

Drie verschillende berichtgrenzen

Mechanisme Richting Doel
PHP-domeinevent Binnen de applicatie Gedrag tussen Core en Modules coördineren
Uitgaande webhook Van Agovena naar je HTTPS-endpoint Een externe integratie informeren
Betaalwebhook Van provider naar Agovena Providerstatus verifiëren en verwerken

Deze mechanismen verschillen in payload, authenticatie en herhalingsgedrag. Gebruik het ondertekeningsalgoritme voor uitgaande Agovena-webhooks niet voor een inkomende providercallback, tenzij die provider dat algoritme daadwerkelijk voorschrijft.

Interne domeinevents

Modules registreren listeners via ModuleContext::listen(). Inventory luistert bijvoorbeeld naar OrderPlacing, OrderCreated en OrderCancelled voor voorraadcontrole, reservering en terugboeking.

Het uitvoermoment hoort bij het contract:

  • OrderPreflight bevat geprijsde regels en een wijzigbare array checks. PlaceOrder verstuurt dit event vóór de besteltransactie.
  • OrderPlacing bevat regels, een optionele bestelling en optionele preflightcontext. Het wordt tijdens de opslag verstuurd, zodat een listener de bestelling vóór commit kan weigeren.
  • OrderCreated implementeert ShouldDispatchAfterCommit. De aanroep staat binnen PlaceOrder, maar het eventcontract stelt de dispatch uit tot na commit. Reken er niet op dat een after-commitlistener een al vastgelegde bestelling kan terugdraaien.

Leg gevolgen van terugbetalingen en annuleringen expliciet vast. Een refundbericht betekent niet automatisch dat een abonnement, service, download of toegangsbewijs moet worden ingetrokken. De Module die het toegangsrecht beheert bepaalt dat beleid.

Houd transactiegevoelige validatie lokaal en voorspelbaar. Plan externe neveneffecten pas nadat de gegevens zijn vastgelegd. Test het gedrag van listeners en niet alleen of een event is verstuurd. Bronnen: PlaceOrder, ModuleContext en AvailabilityCapability.

Catalogus van uitgaande events

De publieke webhookcatalogus bevat momenteel:

Eventtype Velden onder data
order.created order_id, order_number, status, payment_status, total, currency
order.paid Dezelfde bestelvelden
order.cancelled Dezelfde bestelvelden
payment.recorded payment_id, order_id, status, amount, currency
refund.recorded refund_id, order_id, amount, currency, status
credit_note.issued credit_note_id, invoice_id, order_id, amount, currency

ID's verwijzen naar records, bedragen gebruiken de kleinste munteenheid en statussen zijn domeinenumwaarden. Bij bestellingen kan payment_status null zijn. Let op: bestelevents gebruiken total, terwijl de andere payloads amount gebruiken. Geen van beide is de volledige API-resource.

Niet ieder PHP-event wordt automatisch een webhook. De namen staan expliciet in WebhookEventCatalog. WebhookEventSubscriber selecteert de payloadvelden.

HTTP-omhulling en headers

Een gewone HTTP-bestemming ontvangt onderstaande structuur. Alle waarden zijn voorbeelden:

json
{
  "id": "00000000-0000-4000-8000-000000000001",
  "type": "order.created",
  "created_at": "2026-01-01T12:00:00+00:00",
  "data": {
    "order_id": 42,
    "order_number": "EXAMPLE-42",
    "status": "pending",
    "payment_status": "pending",
    "total": 1200,
    "currency": "EUR"
  }
}
Header Betekenis
Content-Type application/json
X-Agovena-Event Eventtype
X-Agovena-Delivery UUID van de levering
X-Agovena-Timestamp Unix-tijdstip in seconden voor deze poging
X-Agovena-Signature v1= gevolgd door een hexadecimale HMAC-SHA256

Payload-ID en leverings-UUID zijn afzonderlijke waarden. De publisher maakt beide identifiers per overeenkomend endpoint, niet één globaal event-ID voor alle bestemmingen. Actieve endpoints abonneren zich op expliciete eventnamen of *. De publisher filtert gevoelige gegevens en zet de levering na commit in de wachtrij.

Discord-bestemmingen krijgen een andere body: een embeds-array op basis van eventtype en data. De handtekening dekt altijd de daadwerkelijk verzonden body, inclusief de bestemmingsopmaak. Bronnen: WebhookEventPublisher en WebhookPayloadFormatter.

Eerst verifiëren, dan verwerken

Het ondertekende bericht bestaat uit het decimale tijdstip, een letterlijke punt en de ruwe HTTP-body. De geheime sleutel is die van het ingestelde endpoint:

text
v1=HMAC-SHA256(secret, timestamp + "." + raw_body)

Parseer en herschrijf JSON niet vóór verificatie. Witruimte en escaping beïnvloeden de handtekening. Gebruik een vergelijking in constante tijd en weiger oude tijdstippen. WebhookSigner gebruikt standaard een tolerantie van 300 seconden en accepteert handtekeningen gescheiden door komma's of puntkomma's.

Een zelfstandige PHP-verificatiefunctie volgens dit contract:

php
<?php

function verifyAgovenaWebhook(
    string $secret,
    string $timestampHeader,
    string $rawBody,
    string $signatureHeader,
    int $now,
): bool {
    if ($secret === '' || ! ctype_digit($timestampHeader)) {
        return false;
    }

    $timestamp = (int) $timestampHeader;
    if (abs($now - $timestamp) > 300) {
        return false;
    }

    $expected = 'v1='.hash_hmac('sha256', $timestamp.'.'.$rawBody, $secret);
    foreach (preg_split('/[;,]/', $signatureHeader) ?: [] as $candidate) {
        if (hash_equals($expected, trim($candidate))) {
            return true;
        }
    }

    return false;
}

Geef de tijd van je eigen server mee, niet een door de client aangeleverde now. Valideer na verificatie de structuur, bewaar de leveringsidentifier met een unieke constraint en accepteer het werk duurzaam voordat je een 2xx-response geeft. Herhaalde levering mag geen dubbele fulfilment of boeking veroorzaken. Bewaar ondertekeningsgeheimen in een veilige opslag en sluit ruwe gevoelige payloads uit van logs.

Aflevering en herhaling

DeliverWebhook claimt een levering met een lease vóór verzending. De verbindingstime-out is vijf seconden, de totale time-out tien seconden. Een HTTPS-bestemming moet de controle op publieke adressen doorstaan. Redirects zijn uitgeschakeld en het gevalideerde IP wordt voor het verzoek vastgezet.

  • Iedere geslaagde 2xx-response markeert de levering als afgeleverd.
  • Transportfouten, HTTP 429 en serverfouten kunnen worden herhaald.
  • Andere mislukte responses, inclusief redirects en clientfouten anders dan 429, worden niet herhaald.
  • Er zijn maximaal vijf pogingen, met wachttijden van 60, 300, 1800 en 7200 seconden.
  • Uitgeputte of niet-herhaalbare leveringen krijgen dead_letter.
  • Uitgeschakelde bestemmingen worden overgeslagen. De job bewaart geen responsebody.

Bij een volgende poging worden tijdstip en handtekening opnieuw gemaakt. Ontdubbel daarom op leveringsidentiteit, niet op de handtekening. De scheduler draait agovena:deliver-webhooks iedere minuut om wachtend werk op te pakken. Een actieve scheduler en queueworker zijn operationele vereisten, geen optionele clientfuncties.

Inkomende betaalcallbacks

Core registreert POST /webhooks/payments/{gateway} buiten /api/v1. Deze route is uitgesloten van CSRF-controle, omdat providers geen browsersessie hebben. Authenticatie gebeurt via verifyWebhook() van de gateway, niet met een klanttoken.

HandlePaymentWebhook weigert onbekende gateways en gateways zonder webhooks, verifieert vóór parsing, normaliseert de payload, bewaart de identiteit voor herhalingen en verwerkt de betaalstatus met de eigen levenscycluscontroles. Een geslaagde response van PaymentWebhookController bevat ok, het opgeslagen event-ID id en duplicate. Verificatiefouten geven 403; onverwachte verwerkingsfouten geven 500.

Er bestaat geen providerneutrale curl-body waarmee je geldig een betaling kunt bevestigen. Iedere adapter moet het ondertekenings- en payloadprotocol van zijn provider implementeren en testen. Maak geen fictief betaald-voorbeeld dat verificatie omzeilt.

Test de foutpaden

bash
php artisan test tests/Unit/OutboundWebhookContractTest.php
php artisan test tests/Feature/OutboundWebhookDeliveryTest.php
php artisan test tests/Feature/WebhookDeadLetterTest.php
php artisan test tests/Feature/PaymentGatewayWebhookTest.php

OutboundWebhookContractTest test geldige, verlopen en ongeldige handtekeningen en geweigerde SSRF-bestemmingen. Voeg ontvangertests toe voor gewijzigde bodies, dubbele levering, duurzame acceptatie vóór bevestiging en herhaling na een tijdelijke storing.

Doorzoek de documentatie

Zoek handleidingen, commando’s en API-endpoints

Waar ben je naar op zoek?

Documentatie