Ga direct naar de inhoud
DedicatedPHP Contact

Webhooks buiten volgorde in PHP zonder statuscorruptie

Ontwerp een PHP-integratie die bestand is tegen dubbele, late en gelijktijdige events met validatie, audit en idempotentie.

Redactioneel diagram van dubbele en late webhookevents die door een PHP-applicatie met statuscontrole worden verwerkt

Webhooks buiten volgorde in PHP vormen een consistentieprobleem, niet alleen een connectiviteitsprobleem. Een provider kan een aflevering opnieuw verzenden omdat deze geen geldige reactie heeft ontvangen, een queue kan een bericht vertragen of twee events van dezelfde entiteit kunnen via verschillende routes reizen. Als de applicatie ervan uitgaat dat elk event één keer en in volgorde aankomt, kan een oude bevestiging een latere annulering overschrijven, of kan een herhaling een onomkeerbare operatie twee keer uitvoeren.

De uitgangsregel is eenvoudig: een webhook is een melding dat er mogelijk iets is gewijzigd in een ander systeem. Op zichzelf is het geen betrouwbare instructie om de lokale status zonder controles te muteren. Het ontwerp moet het ontvangen bewijs bewaren, bepalen welke events toelaatbaar zijn en wijzigingen idempotent en geordend toepassen volgens de domeinregels.

Ontvangst, validatie en toepassing op het domein scheiden

Ontvangst, validatie en toepassing op het domein scheiden — guía visual de DedicatedPHP

Het HTTP-endpoint moet weinig doen en dat voorspelbaar doen. De verantwoordelijkheid ervan is het verzoek ontvangen, verifiëren, een onveranderlijk record persisteren en reageren binnen de door de verzender verwachte termijn. Het werk dat bestellingen, abonnementen, voorraad of andere bedrijfsentiteiten wijzigt, moet later plaatsvinden, doorgaans via een asynchroon proces.

Het scheiden van fasen voorkomt dat een tijdelijke storing van een interne API een geldige aflevering verandert in een dubbelzinnige retry. Het maakt het ook mogelijk de verwerking te hervatten zonder de provider te vragen oude events opnieuw te verzenden.

  1. Ontvangst: headers, ongewijzigde body, ontvangsttijdstip en geïdentificeerde herkomst vastleggen.
  2. Invoervalidatie: handtekening, formaat, grootte, contenttype en minimale velden controleren.
  3. Persistentie: het event en de initiële status ervan opslaan in een korte transactie.
  4. In de queue plaatsen: aangeven dat er werk wacht, zonder afhankelijk te zijn van verwerking binnen de HTTP-respons.
  5. Toepassing: een worker interpreteert het event, haalt de benodigde status op en voert een gecontroleerde bedrijfstransitie uit.

Het is belangrijk om een aflevering van een event te onderscheiden. Dezelfde aflevering kan worden herhaald, en sommige providers kennen aan elke afleveringspoging een andere identificatie toe. Als er een stabiele event-ID bestaat, is die doorgaans de beste basis voor deduplicatie. Als die niet bestaat, moet een sleutel worden gedefinieerd met herkomst, externe entiteit, type en een versie of tijdstempel met bekende betekenis.

Wat registreren om te kunnen auditen en herverwerken

Een eventtabel mag niet alleen de geïnterpreteerde JSON opslaan. Bewaar de oorspronkelijke body, omdat normalisatie vóór opslag informatie kan verwijderen die nodig is om een handtekening te verifiëren, een incident te onderzoeken of een latere parser aan te passen.

Minimaal moet het record bevatten:

  • Herkomst of provider en integratieomgeving.
  • Externe event-ID en, indien aanwezig, afleverings-ID.
  • Eventtype, externe entiteits-ID en versie, volgnummer of ingangsdatum.
  • Relevante headers en de oorspronkelijke payload, beschermd tegen wijzigingen.
  • Lokaal ontvangsttijdstip en, afzonderlijk, de door de verzender opgegeven tijdstempel.
  • Cryptografische hash van de payload voor diagnose en aanvullende deduplicatie.
  • Verwerkingsstatus: ontvangen, gevalideerd, in behandeling, toegepast, genegeerd, mislukt of in beoordeling.
  • Aantal pogingen, samengevatte fout, tijdstip van de laatste poging en referentie naar de betrokken lokale entiteit.

Een unieke constraint op (herkomst, external_event_id) lost herhaling op wanneer de provider een stabiele ID biedt. Voeg eerst in en behandel het conflict als een al bekende aflevering, niet als een bedrijfsfout. De respons kan nog steeds succesvol zijn om retries te stoppen.

Maar het dedupliceren van het bericht volstaat niet om idempotentie te garanderen. Twee verschillende events kunnen bijvoorbeeld dezelfde bevestiging uitdrukken en beide proberen een boeking te creëren. De bedrijfsoperatie moet een eigen bescherming hebben: een idempotentiesleutel, een unieke constraint op het effect of een transitie die controleert of het resultaat al bestaat.

Authenticiteit valideren en het invoeroppervlak beperken

Accepteer een webhook niet omdat deze van een verwacht IP-adres komt of omdat deze een veld bevat dat geheim lijkt. Wanneer de provider dit toestaat, valideer dan een handtekening die is berekend over de ruwe body en een tijdstempel. De vergelijking moet constant-time zijn en het tijdvenster moet replays beperken, rekening houdend met een redelijke klokafwijking.

Leg vóór het persisteren operationele limieten op: maximale bodygrootte, leestijd, geaccepteerde formaten en minimaal schema. Geldige JSON is niet noodzakelijk een geldig event. Wijs onbekende typen af als er geen expliciet beleid bestaat om ze te archiveren zonder effecten toe te passen.

Handtekeninggeheimen vereisen rotatie. Tijdens een wijziging kan het nodig zijn om gedurende een afgebakende periode zowel een oude als een nieuwe sleutel te accepteren en vast te leggen welke de aflevering valideerde. Neem geen volledige bodies, tokens of onnodige persoonsgegevens op in applicatielogs. Het auditlog moet toegangscontroles en een bewaarbeleid hebben dat past bij de gevoeligheid van de gegevens.

De logische volgorde bepalen, niet vertrouwen op netwerkvolgorde

Het ontvangsttijdstip bepaalt niet wat er eerst is gebeurd. Ook een datum in de payload is niet altijd voldoende: deze kan bij benadering zijn, betrekking hebben op het aanmaken van het event en niet op de transitie, of worden beïnvloed door niet-gesynchroniseerde klokken. Het beste signaal is een monotone versie of een volgnummer per entiteit dat door het bronsysteem wordt geleverd.

Wanneer er een versie bestaat, sla dan de laatst toegepaste versie op in de lokale entiteit. Een worker mag een event alleen toepassen als de versie hoger is dan de opgeslagen versie; een gelijke versie duidt op herhaling en een lagere op een laat event. Als er gaten in de volgorde zijn, verzin dan geen tussentijdse status: markeer de entiteit voor reconciliatie of raadpleeg de bron-API, als die API het referentieregister is.

Als er geen volgnummer of versie is, moeten de regels uit het domein komen. Een expliciete statemachine is veiliger dan rechtstreeks een ontvangen tekst toe te wijzen. Een geannuleerde entiteit kan bijvoorbeeld verhinderen dat deze weer bevestigd wordt, tenzij er een gedocumenteerde en geautoriseerde transitie bestaat. Het model moet bepalen wat te doen met elke combinatie van huidige status en binnenkomend event.

if ($eventVersion <= $entity->lastExternalVersion) {
    markIgnored($event, 'versie_niet_nieuwer');
    return;
}

applyAllowedTransition($entity, $event);
$entity->lastExternalVersion = $eventVersion;

De code illustreert het criterium, maar vervangt de transactie of de transitregels niet. Voor events zonder versie is een datumvergelijking alleen aanvaardbaar als het contract van de verzender de semantiek en nauwkeurigheid ervan garandeert.

Late events behandelen volgens de kosten van een fout

Niet alle vertraagde events verdienen dezelfde reactie. De keuze tussen negeren, registreren, herberekenen of compenseren hangt af van de vraag of het event een reële verplichting kan wijzigen en wat de bron van waarheid is.

  • Negeren: passend voor een oude versie waarvan het effect al is opgenomen in een verifieerbare latere status.
  • Registreren en waarschuwen: nuttig als de volgorde inconsistent is of informatie ontbreekt om zonder tussenkomst te beslissen.
  • Herberekenen: de huidige status in het externe systeem raadplegen en de lokale spiegel bijwerken wanneer de externe bron prevaleert.
  • Compenseren: een traceerbare corrigerende actie aanmaken wanneer een eerder effect al gevolgen heeft gehad en niet veilig kan worden verwijderd.

Beschouw een hypothetisch geval van een externe operatie. Er komt een bevestiging met versie 12 binnen, vervolgens een annulering met versie 13 en later wordt de bevestiging 12 opnieuw geprobeerd. Met versiecontrole brengt de herhaling de operatie niet opnieuw tot leven. Als de annulering eerst aankomt en het systeem weet dat versie 12 ontbreekt, kan het de annulering toepassen als de statemachine dit toestaat, of om reconciliatie vragen voordat een gevoelig effect wordt geproduceerd.

Interne concurrency, queues en vergrendelingen per entiteit

Asynchrone verwerking verbetert het reactievermogen, maar introduceert interne race conditions: twee workers kunnen dezelfde status lezen voordat één van hen schrijft. Eventdeduplicatie voorkomt deze toestand niet.

Voor gevoelige entiteiten moet verwerking worden geserialiseerd op sleutel van de externe of lokale entiteit. Dit kan worden bereikt met queuepartities op basis van die sleutel, een gedistribueerde lock met zorgvuldig ontworpen expiry of een row lock binnen een korte transactie. Een andere optie is optimistische controle: alleen bijwerken als de opgeslagen versie nog steeds de verwachte versie is, en opnieuw proberen bij een conflict.

Vermijd een transactie open te houden terwijl u remote services aanroept. Reserveer of lees eerst de status op een consistente manier; voer vervolgens, waar mogelijk, de aanroep uit met een idempotente sleutel; registreer ten slotte het resultaat. Als het proces tussen stappen faalt, moet een retry een in behandeling zijnde operatie kunnen onderscheiden van een reeds voltooide operatie.

Operatie, observeerbaarheid en tests vóór publicatie

Operatie, observeerbaarheid en tests vóór publicatie — guía visual de DedicatedPHP

Een operationeel dashboard moet tonen hoeveel events in behandeling blijven, herhaaldelijk mislukken, wegens ouderdom worden genegeerd, wegens handtekening worden afgewezen en gaten in de volgorde vertonen. Meet ook de leeftijd van de queue en de tijd tussen ontvangst en toepassing. Deze signalen maken het mogelijk een verslechterde integratie te detecteren voordat de afwijking een bedrijfsprobleem wordt.

Behoud mechanismen voor herverwerking die uitgaan van het oorspronkelijke event en een expliciete versie van de parser of handler. Herverwerken betekent niet blind uitvoeren: beperk de reikwijdte, registreer wie dit heeft aangevraagd en houd dezelfde idempotentiegaranties actief.

Checklist

  • Hetzelfde event meerdere keren verzenden, ook gelijktijdig.
  • Een annulering vóór de gerelateerde bevestiging afleveren.
  • Een oud event uitstellen tot na een event met een hogere versie.
  • Gaten, onbekende typen, afgekorte payloads en ongeldige handtekeningen introduceren.
  • Een workerstoring simuleren na het creëren van een extern effect en vóór het markeren van het event als toegepast.
  • Verifiëren dat twee workers op dezelfde entiteit geen onmogelijke transitie produceren.
  • Controleren dat herverwerking de audit behoudt en geen effecten dupliceert.

Een robuuste integratie probeert niet het netwerk te dwingen in volgorde af te leveren. Zij ontwerpt een betrouwbare grens: bewaart elke verifieerbare invoer, past idempotente bedrijfsregels toe, gebruikt een logische volgorde wanneer die bestaat en voert reconciliatie uit wanneer zij de status niet met zekerheid kan kennen.

Wil je deze ideeën toepassen op je project?Laten we uw PHP-platform bespreken.
Bekijk gerelateerde service