Carti web si idempotenta evenimentului
TL; DR
Un webhook bun este un eveniment semnat (HMAC/mTLS), rezumat și idempotent livrat pe un model cel puțin o dată, cu backoff exponențial și duplicare la destinatar. Conveniți asupra unui plic ('event _ id',' type ',' ts ',' version ',' încercare ',' semnătură '), fereastra de timp (≤5 minute), codurile de răspuns, retraiele, DLQ și punctul final de stare.
1) Roluri și modelul de livrare
Expeditor (tu/furnizor): generează un eveniment, semne, încearcă să livreze până la 2xx, retrait la 3xx/4xx/5xx (cu excepția explicit „nu accepta”), conduce DLQ, dă API reluare.
Destinatar (partener/serviciul dvs.): verifică fereastra de semnătură/timp, efectuează procesarea dedup și idempotent, răspunde cu codul corect, oferă/stare și/ack reluare prin 'event _ id'.
Garanții: cel puțin o dată. Destinatarul trebuie să fie capabil să se ocupe de duplicate și reordonare.
2) Plicul evenimentului
json
{
"event_id": "01HF7H9J9Q3E7DYT5Y6K3ZFD6M",
"type": "payout.processed",
"version": "2025-01-01",
"ts": "2025-11-03T12:34:56.789Z",
"attempt": 1,
"producer": "payments",
"tenant": "acme",
"data": {
"payout_id": "p_123",
"status": "processed",
"amount_minor": 10000,
"currency": "EUR"
}
}
Câmpurile obligatorii sunt 'event _ id',' type ',' version ',' ts ',' încercare '.
Reguli de evoluție: adăugați câmpuri; ștergeți/schimbați tipurile - numai cu noua „versiune”.
3) Securitate: semnături și obligatorii
3. 1 Semnătură HMAC (implicit recomandată)
Titluri:
X-Signature: v1=base64(hmac_sha256(<secret>, <canonical>))
X-Timestamp: 2025-11-03T12:34:56Z
X-Event-Id: 01HF7...
Șir canonic:
<timestamp>\n<method>\n<path>\n<sha256(body)>
Verificați cu destinatarul:
- abs (acum − „X-Timestamp”) ≤ 300s
- "X-Event-Id 'not procesate înainte (dedup)
- "X-Signature 'matches (comparație timp-siguranță)
3. 2 Add. măsuri
mTLS pentru cârlige web foarte sensibile.
Lista permisiunilor IP/ASN.
DPoP (opțional) pentru expeditor-constrâns în cazul în care webhook-ul inițiază callback-uri.
4) Idempotență și deduplicare
4. 1 Idempotenţa evenimentului
Un eveniment cu același 'event _ id' nu ar trebui să schimbe din nou starea. Destinatar:- stochează event _ id' în memoria cache idempotentă (KV/Redis/DB) pe TTL ≥ 24-72 ore;
- salvează rezultatul procesării (succes/eroare, artefacte) pentru re-returnare.
4. 2 Idempotența comenzii (callback)
Dacă webhook-ul obligă clientul să tragă API-ul (de exemplu, „confirmați plata”), utilizați 'Idempotency-Key' pe apelul REST, stocați rezultatul pe partea de serviciu (exact odată rezultat).
Modelul KV (minim):
key: idempotency:event:01HF7...
val: { status: "ok", processed_at: "...", handler_version: "..." }
TTL: 3d
5) Retrai și backoff
Teren recomandat (exponențial cu jitter):- '5s, 15s, 30, 1m, 2m, 5m, 10m, 30m, 1h, 3h, 6h, 12h, 24h' (apoi zilnic până la N zile)
- 2xx - succes, opriți retraiele.
- '400/ 401/403/404/422' - nu se poate retrăi dacă semnătura/formatul este ok (eroare client).
- '429' - retraim prin 'Retry-After' sau backoff.
- 5xx/rețea - retraim.
Anteturile expeditorului: 'User-Agent', 'X-Webhook-Producator', 'X-Incercare'.
6) Prelucrarea laterală a receptorului
Pseudo-conducte:pseudo verify_signature()
if abs(now - X-Timestamp) > 300s: return 401
if seen(event_id):
return 200 // идемпотентный ответ
begin transaction if seen(event_id): commit; return 200 handle(data) // доменная логика mark_seen(event_id) // запись в KV/DB commit return 200
Tranzacționalitate: Eticheta „văzut” trebuie să fie stabilită atomic cu efectul operațiunii (sau după fixarea rezultatului) pentru a evita prelucrarea dublă pe eșec.
7) Garanții de ordine și instantanee
Comanda nu este garantată. Utilizați "ts' și" seq "/" versiune "în" date "pentru a verifica relevanța.
Pentru lag-uri/pierderi lungi - adăugați/reluarea la expeditor și/resync la receptor (obțineți instantaneu și delta la fereastra de timp/ID).
8) Stare, reluare și DLQ
8. 1 Criterii finale ale expeditorului
'POST/webhooks/reluare' - prin 'event _ id' list sau prin fereastra de timp.
'GET/webhooks/events/: id' - arată pachetul sursă și istoricul încercărilor.
DLQ: evenimente „moarte” (limita retraiului a fost epuizată) → stocare separată, alerte.
8. 2 Criteriile finale ale destinatarului
'GET/ webhooks/status/:event_id' -' seen = true/false ',' processed _ at ',' handler _ version '.
„POST/webhooks/ack” - (opțional) confirmarea procesării manuale de la DLQ.
9) Contracte de eroare (răspuns receptor)
http
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
Retry-After: 120
X-Trace-Id: 4e3f...
{
"error": "invalid_state",
"error_description": "payout not found",
"trace_id": "4e3f..."
}
Recomandări: returnați întotdeauna un cod clar și, dacă este posibil, 'Retry-After'. Nu returnați detalii detaliate de securitate.
10) Monitorizare și SLO
Valori (expeditor):- livrare p50/p95, rata de succes, retray/eveniment, drop-rate DLQ, share 2xx/4xx/5xx, fereastră de întârziere până la 2xx.
- verificați rata de eșec (semnătură/timp), rata duplicatului, manipulatorul de latență p95, 5xx.
- Livrare: ≥ 99. 9% din evenimente primesc 2xx <3 c p95 (după prima încercare reușită).
- Verificare criptografică: validarea semnăturii ≤ 2-5 ms p95.
- Dedup: 0 efecte repetate (rezultat exact o dată la nivel de domeniu).
11) Securitatea datelor și confidențialitatea
Nu transmiteți PAN/PII în corpul cârligului web; utilizați ID-uri și apoi trageți pentru detalii împotriva unui API autorizat.
Masca câmpuri sensibile în jurnalele; depozitează corpurile de evenimente doar la minimum, cu TTL.
Criptați magazinele DLQ și reluarea.
12) Versioning și compatibilitate
Versiune în 'versiune' (plic) și în tranzit: '/webhooks/v1/payments'.
Câmpurile noi sunt opționale; îndepărtarea - numai după perioada „Apus de soare”.
Documentați modificările din changelog-ul care poate fi citit automat (pentru verificări automate).
13) Cazuri de testare (lista de verificare UAT)
- Re-livrarea aceluiași 'event _ id' → un efect și' 200 'la duplicate.
- Semnătură: cheie corectă, cheie incorectă, cheie veche (rotație), „X-Timestamp” din fereastră.
- Backoff: Destinatarul dă '429' cu 'Retry-After' → pauză corectă.
- Ordine: Evenimente '... procesate „vin înainte”... creat "→ prelucrare corectă/așteptare.
- Eșecul bazei de date la receptor între efect și 'mark _ seen' → atomicitate/repetare.
- DLQ și reluarea manuală → livrarea cu succes.
- Masa „furtună” (furnizorul trimite pachete) → fără pierderi, limitele nu înăbușă critice.
14) Mini fragmente
Semnătura expeditorului (pseudo):pseudo body = json(event)
canonical = ts + "\n" + "POST" + "\n" + path + "\n" + sha256(body)
sig = base64(hmac_sha256(secret, canonical))
headers = {"X-Timestamp": ts, "X-Event-Id": event.event_id, "X-Signature": "v1="+sig}
POST(url, body, headers)
Verificare și destinație (pseudo):
pseudo assert abs(now - X-Timestamp) <= 300 assert timingSafeEqual(hmac(secret, canonical), sig)
if kv.exists("idemp:"+event_id): return 200
begin tx if kv.exists("idemp:"+event_id): commit; return 200 handle(event.data) // доменная логика kv.set("idemp:"+event_id, "ok", ttl=259200)
commit return 200
15) Erori frecvente
Fără deduplicare → efecte repetate (refanduri duble/plăți).
Semnătură fără timestamp/fereastră → reluare vulnerabilitate.
Stocarea unui secret HMAC pe toți partenerii.
Răspunsuri '200' înainte de fixarea rezultatului → pierderea evenimentelor de accident.
„Spălarea” detaliilor de securitate în răspunsuri/jurnale.
Lipsa DLQ/reluare - incidentele sunt de nerezolvat.
16) Punerea în aplicare ieftin foaie
Securitate: HMAC v1 + 'X-Timestamp' + 'X-Event-Id', fereastră ≤ 5 minute; mTLS/IP permite-lista după cum este necesar.
Конверт: 'event _ id',' type ',' version ',' ts ',' încercare ',' data '.
Livrare: cel puțin o dată, backoff cu jitter, 'Retry-After', DLQ + reluare API.
Idempotența: KV-cache 24-72 h, fixarea atomică a efectului + 'mark _ seen'.
Observabilitate: livrare, semnătură, măsurători duplicate; trace _ id.
Documentație: versiune, coduri de răspuns, exemple, lista de verificare UAT.
Rezumat reluare
Cârligele persistente sunt construite pe trei balene: un plic semnat, cel puțin o dată livrare și procesare idempotentă. Formalizați contractul, activați HMAC/mTLS și fereastra de timp, implementați retrai + DLQ și reluarea, stocați etichetele idempotente și capturați efectele atomic. Apoi, evenimentele rămân fiabile chiar și cu defecțiuni de rețea, vârfuri de încărcare și rare „duplicate ale sorții”.