Sari la conținut

Publicat 4 min de citit

Webhookuri de plată: cum eviți comenzile procesate de două ori

Verificarea semnăturii, salvarea evenimentelor și procesarea reluabilă au roluri diferite. Cum proiectezi integrarea astfel încât o întrerupere să nu piardă sau să dubleze munca.

NT Fondator, Pitstop Technologies

O recepționeră păstrează un plic în tăviță și oprește alte două plicuri identice care vin în fugă.

Clientul achită comanda, dar magazinul pregătește două colete. Plata nu a fost neapărat încasată de două ori. Este posibil ca aplicația să fi primit din nou aceeași notificare și să fi repetat operațiunea de expediere.

Stripe precizează că evenimentele pot fi retrimise, pot apărea duplicate și nu ajung obligatoriu în ordinea în care au fost generate. Aceste situații trebuie prevăzute în integrare. Comportamentul livrării evenimentelor Stripe.

Mai jos este o abordare de proiectare pe care aș lua-o în calcul. Nu este o implementare completă: alegerea bazei de date, fluxul comercial și serviciile externe stabilesc detaliile care trebuie verificate.

Exemplu de integrare a plăților

Primește în siguranță. Recuperează ce eșuează.

Prima livrareevt_42
Același eveniment, reluatevt_42
Verifică semnătura + contul + mediul
Inbox persistentUNIQUE(provider, account, event_id)

Un singur eveniment salvat. Confirmă după commit; un duplicat autentificat folosește aceeași confirmare.

Procesul de lucru preia evenimentul
O singură tranzacție în baza de date
Tranziție permisă a comenziiAcțiune unică în outbox

Se confirmă împreună sau nu se schimbă nimic. Evenimentele diferite au nevoie și de o identitate stabilă a operațiunii.

Livrează folosind aceeași identitate la reîncercări
Serviciu externAcceptat, dar conexiunea a expirat?

Verifică rezultatul extern sau reconciliază. Un marcaj local „trimis” nu dovedește o singură executare.

Un eveniment duplicat și o operațiune repetată sunt probleme diferite. Outbox-ul nu garantează o singură executare în sistemul extern.

Verifici sursa înainte să faci ceva cu plata

Semnătura se verifică folosind corpul original al cererii, antetul semnăturii și secretul corect al endpointului. Dacă transformi mai întâi mesajul în JSON și apoi îl reconstruiești, poți modifica datele semnate. Folosește biblioteca oficială de verificare. Documentația Stripe despre semnături.

Păstrează verificarea timestampului, limitează dimensiunea cererii și respinge semnăturile nevalide. Verifică apoi structura evenimentului, contul și mediul așteptat. O notificare autentică nu justifică asocierea plății cu orice comandă: legătura trebuie confirmată din datele de încredere ale aplicației. În jurnale nu au ce căuta secretele sau detalii de plată inutile.

Confirmi primirea după ce ai salvat evenimentul

Aș separa primirea de procesare. Endpointul verifică mesajul și îl salvează într-un tabel de intrare, numit adesea inbox. O constrângere UNIQUE(provider, account, event_id) împiedică două cereri simultane să creeze două înregistrări pentru același eveniment. Contul trebuie normalizat, fără valori nule, iar datele de test separate de cele reale.

Răspunsul HTTP de succes pleacă după confirmarea tranzacției în baza de date sau după verificarea că evenimentul autentic există deja salvat. Dacă baza de date nu răspunde, nici endpointul nu pretinde că a primit mesajul în siguranță.

Un proces separat preia evenimentele salvate. Rezervarea unei sarcini trebuie să poată expira și să fie recuperată după oprirea neașteptată a procesului. Altfel, un eveniment rămâne blocat la „în lucru”. Confirmarea primirii nu înseamnă că expedierea sau activarea serviciului s-a încheiat.

Protejezi și operațiunea comercială, nu doar mesajul

Identificatorul evenimentului rezolvă repetarea aceleiași notificări. Nu dovedește însă că o altă notificare trebuie să creeze încă o expediere. Operațiunea are nevoie de o identitate proprie: de exemplu, comanda și acțiunea de pregătire pentru livrare.

Într-o singură tranzacție, procesul blochează sau actualizează condiționat înregistrarea comercială, verifică tranziția permisă, salvează acțiunea externă într-un outbox și marchează evenimentul ca procesat. Modificările se confirmă împreună. Dacă tranzacția eșuează, niciuna nu rămâne aplicată parțial.

Outboxul este lista persistentă a acțiunilor care mai trebuie trimise: o solicitare către curier, o notificare sau o actualizare în contabilitate. Astfel, modificarea comenzii nu poate fi confirmată fără să rămână înregistrată și munca externă necesară.

Ce se întâmplă dacă serviciul extern nu confirmă primirea?

Serviciul destinatar poate accepta cererea chiar înainte ca legătura să se întrerupă. Aplicația ta nu știe încă rezultatul, iar o nouă încercare poate repeta efectul.

La cererile trimise către API-ul Stripe, cheile de idempotență permit reluarea operațiunilor acceptate de acest mecanism. Sunt diferite de identificatorii webhookurilor primite. Păstrezi aceeași cheie pentru aceeași cerere logică și respecți regulile privind parametrii și retenția. Contractul de idempotență Stripe.

Pentru curier sau alt furnizor verifici separat posibilitățile de deduplicare și consultare a rezultatului. Păstrezi identificatorii operațiunii. Dacă rezultatul nu poate fi stabilit sigur, ai nevoie de reconciliere sau verificare manuală. Un câmp local „trimis” nu garantează executarea externă exact o dată.

Evenimentele întârziate nu trebuie să strice starea comenzii

Unde este necesar, citește situația actuală din sistemul de plăți și aplică reguli explicite, cu protecție împotriva procesării concurente. Citirea externă nu blochează automat niciunul dintre sisteme.

O notificare veche de eșec nu trebuie să transforme mecanic o plată confirmată în comandă neachitată. Dar o rambursare sau o contestație ulterioară este o schimbare reală. Modelează aceste cazuri separat; regula „odată plătită, nu se mai schimbă” este prea simplă.

Verifici și ce se întâmplă când ceva cade

Aș testa duplicate simultane, ordinea inversată, indisponibilitatea bazei de date, oprirea procesului înainte și după confirmarea tranzacției, plus acceptarea externă urmată de timeout. Testele trebuie să verifice rezultatul persistent, inclusiv pentru semnături nevalide și conturi greșite.

Limitează reîncercările, mărește intervalele dintre ele și alertează un responsabil când se epuizează. Oferă reluare controlată și compară periodic înregistrările locale cu cele ale furnizorului. Urmărește vechimea sarcinilor restante. Fără această recuperare, integrarea poate afișa răspunsuri corecte și totuși să piardă muncă.

Hai să discutăm ce trebuie să se întâmple după plata clientului. Integrarea merită evaluată până la finalizarea comenzii, inclusiv atunci când un serviciu nu răspunde.

De la idee la un pas concret.

Spune-ne ce trebuie să rezolve site-ul tău. Stabilim împreună cerințele, responsabilitățile și următorul pas.

Apelurile se desfășoară în engleză, cu asistență în română la cerere.