Webhooki otrzymane poza kolejnością w PHP stanowią problem spójności, a nie tylko łączności. Dostawca może ponownie wysłać dostarczenie, ponieważ nie otrzymał prawidłowej odpowiedzi, kolejka może opóźnić wiadomość albo dwa zdarzenia tej samej encji mogą podróżować różnymi ścieżkami. Jeśli aplikacja zakłada, że każde zdarzenie dociera raz i w sekwencji, stare potwierdzenie może nadpisać późniejsze anulowanie albo powtórzenie może dwukrotnie wykonać nieodwracalną operację.
Zasada wyjściowa jest prosta: webhook to powiadomienie, że coś mogło zmienić się w innym systemie. Sam w sobie nie jest wiarygodną instrukcją do zmiany stanu lokalnego bez sprawdzeń. Projekt powinien zachowywać otrzymane dowody, decydować, które zdarzenia są dopuszczalne, oraz stosować zmiany w sposób idempotentny i uporządkowany zgodnie z regułami domeny.
Oddziel odbiór, walidację i zastosowanie w domenie

Endpoint HTTP powinien robić niewiele i robić to w przewidywalny sposób. Jego odpowiedzialnością jest odebranie żądania, zweryfikowanie go, zapisanie niezmiennego rekordu i udzielenie odpowiedzi w terminie oczekiwanym przez nadawcę. Praca zmieniająca zamówienia, subskrypcje, zapasy lub jakąkolwiek inną encję biznesową powinna nastąpić później, zwykle za pośrednictwem procesu asynchronicznego.
Rozdzielenie faz zapobiega temu, by przejściowa awaria wewnętrznego API zamieniła prawidłowe dostarczenie w niejednoznaczną ponowną próbę. Pozwala także wznowić przetwarzanie bez proszenia dostawcy o ponowne wysłanie starych zdarzeń.
- Odbiór: przechwycenie nagłówków, nieprzekształconej treści, chwili odbioru i zidentyfikowanego źródła.
- Walidacja wejścia: sprawdzenie podpisu, formatu, rozmiaru, typu treści i minimalnego zestawu pól.
- Trwałe zapisanie: zapisanie zdarzenia i jego stanu początkowego w krótkiej transakcji.
- Umieszczenie w kolejce: oznaczenie, że istnieje oczekująca praca, bez zależności od jej przetworzenia w odpowiedzi HTTP.
- Zastosowanie: worker interpretuje zdarzenie, pobiera niezbędny stan i wykonuje kontrolowane przejście biznesowe.
Ważne jest rozróżnienie dostarczenia od zdarzenia. To samo dostarczenie może się powtórzyć, a niektórzy dostawcy przypisują inny identyfikator każdej próbie dostarczenia. Jeśli istnieje stabilny identyfikator zdarzenia, zwykle stanowi on najlepszą podstawę deduplikacji. Jeśli go nie ma, trzeba zdefiniować klucz zawierający źródło, zewnętrzną encję, typ oraz wersję lub znacznik czasu o znanym znaczeniu.
Co rejestrować, aby móc audytować i ponownie przetwarzać
Tabela zdarzeń nie powinna przechowywać wyłącznie zinterpretowanego JSON-a. Zachowaj oryginalną treść, ponieważ znormalizowanie jej przed zapisaniem może usunąć informacje potrzebne do weryfikacji podpisu, zbadania incydentu lub dostosowania późniejszego parsera.
Rekord powinien zawierać co najmniej:
- Źródło lub dostawcę oraz środowisko integracji.
- Zewnętrzny identyfikator zdarzenia oraz, jeśli istnieje, identyfikator dostarczenia.
- Typ zdarzenia, identyfikator zewnętrznej encji oraz wersję, sekwencję lub datę obowiązywania.
- Istotne nagłówki i oryginalny payload zabezpieczony przed modyfikacjami.
- Lokalną chwilę odbioru oraz, oddzielnie, znacznik czasu zadeklarowany przez nadawcę.
- Kryptograficzny skrót payloadu na potrzeby diagnostyki i pomocniczej deduplikacji.
- Stan przetwarzania: odebrane, zwalidowane, oczekujące, zastosowane, zignorowane, nieudane lub w trakcie weryfikacji.
- Liczbę prób, skrócony błąd, chwilę ostatniej próby i odniesienie do objętej wpływem lokalnej encji.
Ograniczenie unikalności dla kolumn (origen, external_event_id) rozwiązuje problem powtórzeń, gdy dostawca udostępnia stabilne ID. Najpierw wykonaj insert i traktuj konflikt jako już znane dostarczenie, a nie jako błąd biznesowy. Odpowiedź może nadal być pomyślna, aby zatrzymać ponowne próby.
Jednak deduplikacja wiadomości nie wystarcza do zagwarantowania idempotencji. Na przykład dwa różne zdarzenia mogą wyrażać to samo potwierdzenie i oba próbować utworzyć zapis księgowy. Operacja biznesowa musi mieć własne zabezpieczenie: klucz idempotencji, ograniczenie unikalności efektu albo przejście sprawdzające, czy rezultat już istnieje.
Weryfikuj autentyczność i ogranicz powierzchnię wejścia
Nie akceptuj webhooka tylko dlatego, że pochodzi z oczekiwanego adresu IP albo zawiera pole wyglądające na sekret. Gdy dostawca na to pozwala, weryfikuj podpis wyliczony na podstawie surowej treści i znacznika czasu. Porównanie musi odbywać się w czasie stałym, a okno czasowe powinno ograniczać ataki replay, z uwzględnieniem rozsądnego rozjazdu zegarów.
Przed zapisaniem narzuć limity operacyjne: maksymalny rozmiar treści, czas odczytu, akceptowane formaty i minimalny schemat. Prawidłowy JSON nie musi być prawidłowym zdarzeniem. Odrzucaj nieznane typy, jeśli nie istnieje wyraźna polityka ich archiwizowania bez wywoływania efektów.
Sekrety podpisu wymagają rotacji. Podczas zmiany może być konieczne akceptowanie starego i nowego klucza przez ograniczony okres, z rejestrowaniem, który z nich zwalidował dostarczenie. Nie umieszczaj w logach aplikacji pełnych treści, tokenów ani niepotrzebnych danych osobowych. Rejestr audytowy powinien mieć kontrolę dostępu i politykę retencji odpowiednią do wrażliwości danych.
Ustalaj porządek logiczny, nie ufaj kolejności sieciowej
Godzina odbioru nie określa, co wydarzyło się wcześniej. Również data zawarta w payloadzie nie zawsze jest wystarczająca: może być przybliżona, dotyczyć utworzenia zdarzenia, a nie przejścia, lub być zależna od niezsynchronizowanych zegarów. Najlepszym sygnałem jest monotoniczna wersja albo numer sekwencyjny dla każdej encji, dostarczany przez system źródłowy.
Gdy wersja istnieje, przechowuj ostatnią zastosowaną wersję w lokalnej encji. Worker może zastosować zdarzenie tylko wtedy, gdy jego wersja jest wyższa od zapisanej; taka sama wersja oznacza powtórzenie, a niższa — opóźnione zdarzenie. Jeśli występują luki w sekwencji, nie wymyślaj stanu pośredniego: oznacz encję do uzgodnienia albo odpytaj źródłowe API, jeśli to API jest systemem referencyjnym.
Jeśli nie ma sekwencji ani wersji, reguły muszą wynikać z domeny. Jawna maszyna stanów jest bezpieczniejsza niż bezpośrednie przypisywanie otrzymanego tekstu. Na przykład anulowana encja mogłaby nie pozwalać na powrót do stanu potwierdzonego, chyba że istnieje udokumentowane i autoryzowane przejście. Model musi określać, co robić z każdą kombinacją bieżącego stanu i przychodzącego zdarzenia.
if ($eventVersion <= $entity->lastExternalVersion) {
markIgnored($event, 'version_no_mas_reciente');
return;
}
applyAllowedTransition($entity, $event);
$entity->lastExternalVersion = $eventVersion;Kod ilustruje kryterium, nie zastępuje transakcji ani reguł przejść. W przypadku zdarzeń bez wersji porównanie dat jest dopuszczalne wyłącznie wtedy, gdy umowa z nadawcą gwarantuje ich semantykę i precyzję.
Obsługuj opóźnione zdarzenia zgodnie z kosztem pomyłki
Nie wszystkie opóźnione zdarzenia zasługują na taką samą odpowiedź. Wybór między zignorowaniem, zarejestrowaniem, ponownym wyliczeniem lub skompensowaniem zależy od tego, czy zdarzenie może zmienić rzeczywiste zobowiązanie, oraz od tego, jakie jest źródło prawdy.
- Ignorowanie: odpowiednie dla starej wersji, której efekt jest już zawarty w możliwym do zweryfikowania późniejszym stanie.
- Rejestrowanie i alertowanie: przydatne, jeśli sekwencja jest niespójna lub brakuje informacji, by podjąć decyzję bez interwencji.
- Ponowne wyliczenie: odpytywanie bieżącego stanu w systemie zewnętrznym i aktualizowanie lokalnego odzwierciedlenia, gdy źródło zewnętrzne ma pierwszeństwo.
- Kompensowanie: utworzenie śledzalnej akcji korygującej, gdy wcześniejszy efekt już wywołał konsekwencje i nie można go bezpiecznie usunąć.
Rozważ hipotetyczny przypadek zewnętrznej operacji. Przychodzi potwierdzenie z wersją 12, następnie anulowanie z wersją 13, a później ponawiane jest potwierdzenie 12. Przy kontroli wersji powtórzenie nie przywraca operacji. Jeśli anulowanie przyjdzie pierwsze, a system wie, że brakuje wersji 12, może zastosować anulowanie, jeżeli maszyna stanów na to pozwala, albo zażądać uzgodnienia przed wywołaniem wrażliwego efektu.
Współbieżność wewnętrzna, kolejki i blokady per encja
Przetwarzanie asynchroniczne poprawia responsywność, ale wprowadza wewnętrzne wyścigi: dwóch workerów może odczytać ten sam stan, zanim jeden z nich go zapisze. Deduplikacja zdarzenia nie eliminuje tego warunku.
W przypadku wrażliwych encji serializuj przetwarzanie według klucza zewnętrznej lub lokalnej encji. Można to osiągnąć przez partycje kolejki oparte na tym kluczu, rozproszoną blokadę z ostrożnie zaprojektowanym wygaśnięciem albo blokadę wiersza w ramach krótkiej transakcji. Inną opcją jest kontrola optymistyczna: aktualizuj tylko wtedy, gdy zapisana wersja nadal jest oczekiwana, i ponawiaj próbę po wykryciu konfliktu.
Unikaj utrzymywania otwartej transakcji podczas wywoływania zdalnych usług. Najpierw zarezerwuj lub odczytaj stan w spójny sposób; następnie wykonaj wywołanie z kluczem idempotentnym, gdy jest to możliwe; na końcu zapisz rezultat. Jeśli proces zakończy się niepowodzeniem między krokami, ponowna próba musi umieć odróżnić operację oczekującą od już ukończonej.
Eksploatacja, obserwowalność i testy przed publikacją

Panel operacyjny powinien pokazywać, ile zdarzeń pozostaje oczekujących, wielokrotnie kończy się niepowodzeniem, jest ignorowanych z powodu wieku, odrzucanych z powodu podpisu oraz ma luki w sekwencji. Mierz także wiek kolejki i czas od odbioru do zastosowania. Sygnały te pozwalają wykryć zdegradowaną integrację, zanim rozbieżność stanie się problemem biznesowym.
Utrzymuj mechanizmy ponownego przetwarzania, które wychodzą od oryginalnego zdarzenia i jawnej wersji parsera lub handlera. Ponowne przetwarzanie nie oznacza ślepego wykonania: ogranicz zakres, rejestruj, kto go zażądał, i zachowuj te same gwarancje idempotencji.
Lista kontrolna
- Wyślij to samo zdarzenie wiele razy, również współbieżnie.
- Dostarcz anulowanie przed powiązanym potwierdzeniem.
- Opóźnij stare zdarzenie do chwili po zdarzeniu o wyższej wersji.
- Wprowadź luki, nieznane typy, obcięte payloady i nieprawidłowe podpisy.
- Zasymuluj awarię workera po utworzeniu zewnętrznego efektu, a przed oznaczeniem zdarzenia jako zastosowanego.
- Zweryfikuj, że dwóch workerów obsługujących tę samą encję nie powoduje niemożliwego przejścia.
- Sprawdź, że ponowne przetwarzanie zachowuje audyt i nie duplikuje efektów.
Solidna integracja nie próbuje wymuszać, aby sieć dostarczała zdarzenia w kolejności. Projektuje wiarygodną granicę: zapisuje każde weryfikowalne wejście, stosuje idempotentne reguły biznesowe, używa porządku logicznego, gdy istnieje, i uzgadnia stan, gdy nie może go bezpiecznie poznać.



