Webhooks və hadisələrin idempotentliyi
TL; DR
Yaxşı vebhuk (HMAC/mTLS) imzalanmış, ümumiləşdirilə bilən və idempotent hadisədir, alıcıdan eksponensial backoff və duplikasiya ilə at-least-once modelinə uyğun olaraq çatdırılır. Zərf ('event _ id', 'type', 'ts', 'version', 'attempt', 'signature'), vaxt pəncərəsi (≤ 5 dəq), cavab kodları, retralar, DLQ və status-end-point haqqında razılaşın.
1) Rolları və çatdırılma modeli
Göndərici (siz/provayder): hadisəni formalaşdırır, imzalayır, 2xx-ə çatdırmağa çalışır, 3xx/4xx/5xx-də retrait (açıq «qəbul etməyin» istisna olmaqla), DLQ aparır, API replay verir.
Alıcı (partnyor/xidmətiniz): imza/vaxt pəncərəsini yoxlayır, dedup və idempotent emal edir, düzgün kodla cavab verir ,/status və/ack replay 'event _ id' ilə təmin edir.
Zəmanət: at-least-once. Alıcı dublikatları emal edə və nizam dəyişdirə bilməlidir.
2) Hadisə zərfi (envelope)
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"
}
}
Məcburi sahələr: 'event _ id', 'type', 'version', 'ts', 'attempt'.
Təkamül qaydaları: sahələr əlavə edin; Tiplərin silinməsi/dəyişdirilməsi - yalnız yeni 'version' ilə.
3) Təhlükəsizlik: imzalar və bağlama
3. 1 HMAC imzası (default tövsiyə olunur)
Başlıqlar:
X-Signature: v1=base64(hmac_sha256(<secret>, <canonical>))
X-Timestamp: 2025-11-03T12:34:56Z
X-Event-Id: 01HF7...
Kanonik sətir:
<timestamp>\n<method>\n<path>\n<sha256(body)>
Alıcıda yoxlama:
- abs(now − `X-Timestamp`) ≤ 300s
- 'X-Event-Id' əvvəllər işlənməyib (dedup)
- 'X-Signature' üst-üstə düşür (time-təhlükəsiz müqayisə ilə)
3. 2 əlavə. tədbirlər
mTLS yüksək həssas webhook üçün.
IP/ASN allow-list.
DPoP (opsiyonel) üçün sender-constrained, əgər vebhuk geri zəngləri başlatır.
4) İdempotentlik və duplikasiya
4. 1 Hadisənin idempotantlığı
Eyni 'event _ id' hadisəsi vəziyyəti yenidən dəyişməməlidir. Alıcı:- 'event _ id' -ni TTL ≥ 24-72 saat ərzində idempotent keşdə (KV/Redis/DB) saxlayır;
- təkrar geri qaytarılması üçün emal nəticəsini (uğur/səhv, artefaktlar) saxlayır.
4. 2 Komandaların idempotentliyi (əks çağırışlar)
Əgər vebhuk müştərini API-ni çəkməyə məcbur edirsə (məsələn, «payout təsdiq et»), həmin REST çağırışında 'Idempotency-Key' istifadə edin, nəticəni xidmət tərəfində saxlayın (exactly-once outcome).
KV modeli (minimum):
key: idempotency:event:01HF7...
val: { status: "ok", processed_at: "...", handler_version: "..." }
TTL: 3d
5) Retrai və backoff
Tövsiyə olunan qrafik (jitter ilə eksponensial):- '5s, 15s, 30s, 1m, 2m, 5m, 10m, 30m, 1h, 3h, 6h, 12h, 24h' (N günə qədər gündəlik)
- 2xx - uğur, retrajları dayandırın.
- '400/ 401/403/404/422' - imza/formatı tamam olarsa (müştəri səhvi) geri qaytarılmır.
- '429' - 'Retry-After' və ya backoff retraimi.
- 5xx/şəbəkə - retraim.
Göndəricinin başlıqları: «User-Agent», «X-Webhook-Producer», «X-Attempt».
6) Alıcı tərəfində emal
Psevdopaypline: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
Tranzaksiyalılıq: «seen» etiketi uğursuzluq zamanı ikiqat emal edilməməsi üçün əməliyyat effekti ilə (və ya nəticəni təyin etdikdən sonra) atomik olaraq qoyulmalıdır.
7) Sifariş və snapshot zəmanətləri
Sifariş təmin edilmir. 'ts' və domen 'seq '/' version' in 'data' istifadə edin.
Uzun lag/itkilər üçün - göndəricidən/replay və alıcıdan/resync əlavə edin (vaxt/ID pəncərəsindən snapshot və delta əldə edin).
8) Status, replay və DLQ
8. 1 Göndərən nöqtələri
'POST/webhooks/replay' - 'event _ id' və ya vaxt pəncərəsi ilə.
'GET/webhooks/events/: id' - ilkin paketi və cəhd tarixçəsini göstərmək.
DLQ: «ölü» hadisələr (retraj limiti tükənib) → ayrı saxlama, alertlər.
8. 2 Alıcının son nöqtələri
`GET /webhooks/status/:event_id` — `seen=true/false`, `processed_at`, `handler_version`.
'POST/webhooks/ack' - (isteğe bağlı) DLQ əl emalı təsdiq.
9) Səhv müqavilələri (alıcının cavabı)
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..."
}
Tövsiyələr: həmişə aydın kodu və mümkünsə, 'Retry-After'. Ətraflı təhlükəsizlik detallarını geri qaytarmayın.
10) Monitorinq və SLO
Metriklər (göndərici):- delivery p50/p95, success rate, retray/hadisə, drop-rate DLQ, share 2xx/4xx/5xx, 2xx qədər gecikmə pəncərəsi.
- verify fail rate (imza/vaxt), dup-rate, latency handler p95, 5xx.
- Çatdırılma: ≥ 99. Hadisələrin 9% -i 2xx <3 c p95 alır (ilk uğurlu cəhddən sonra).
- Kriptoproverləmə: imzanın təsdiqlənməsi ≤ 2-5 ms p95.
- Dedup: 0 təkrarlanan effektlər (domen səviyyəsində exactly-once outcome).
11) Məlumatların təhlükəsizliyi və məxfiliyi
Vebhuk bədənində PAN/PII ötürməyin; səlahiyyətli API detalları üçün identifikatorları və sonrakı pull istifadə edin.
Yuvalarda həssas sahələri maskalayın; TTL ilə hadisələrin cəsədlərini yalnız minimum saxlayın.
DLQ və replay anbarlarını şifrələyin.
12) Version və uyğunluq
'version' (zərf) və yolda versiyası: '/webhooks/v1/payments '.
Yeni sahələr - isteğe bağlıdır; silinir - yalnız 'Sunset' dövründən sonra.
machine-readable changelog-da dəyişiklikləri sənədləşdirin (avtomatik yoxlama üçün).
13) Test-cases (UAT çek siyahısı)
- Eyni 'event _ id' → bir effekt və '200' təkrarlanan çatdırılması.
- İmza: düzgün açar, səhv açar, köhnə açar (rotasiya), pəncərə xaricində 'X-Timestamp'.
- Backoff: Alıcı '429' s 'Retry-After' → düzgün fasilə verir.
- Sifariş: hadisələr '... processed' əvvəl gəlir '... created' → düzgün emal/gözləmə.
- və 'mark _ seen' → atom/təkrarlama effekti arasında alıcının DB uğursuzluğu.
- DLQ və əl replay → uğurlu çatdırılma.
- Kütləvi «fırtına» (provayder paket göndərir) → itkisiz, limitlər kritik boğmaq deyil.
14) Mini snippet
Göndərənin imzası (psevdo):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)
Alıcının yoxlanılması və dedupu (psevdo):
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) Tez-tez səhvlər
No deadup → təkrar effektlər (ikiqat refands/payauts).
Vaxt tuşu/pəncərə olmadan imza → replay üçün zəiflik.
Bütün tərəfdaşlarda bir HMAC sirri saxlamaq.
Cavablar '200' nəticəni düzəltmədən əvvəl → crash zamanı hadisələrin itkisi.
Cavab/log təhlükəsizlik detallarının «yuyulması».
DLQ/replay olmaması - insidentlərin həlli mümkün deyil.
16) Tətbiq şparqalı
Təhlükəsizlik: HMAC v1 + 'X-Timestamp' + 'X-Event-Id', pəncərə ≤ 5 dəq; mTLS/IP allow-list lazım.
Конверт: `event_id`, `type`, `version`, `ts`, `attempt`, `data`.
Çatdırılma: at-least-once, jitter ilə backoff, 'Retry-After', DLQ + replay API.
İdempotentlik: KV-keş 24-72 saat, + 'mark _ seen' effektinin atomik fiksasiyası.
Müşahidə: çatdırılma metrləri, imzalar, dublikatlar; track 'trace _ id'.
Sənədləşmə: versiya, cavab kodları, nümunələr, UAT çek siyahısı.
Xülasə
Davamlı webhucks üç balina üzərində qurulur: imzalanmış zərf, at-least-once çatdırılması və idempotent emalı. Müqaviləni rəsmiləşdirin, HMAC/mTLS və vaxt pəncərəsini açın, retraları + DLQ və replikaları həyata keçirin, idempotent etiketlərini saxlayın və effektləri atomik olaraq düzəldin. Sonra hadisələr şəbəkə uğursuzluqları, yükün zirvələri və nadir «taleyin dublikatları» zamanı da etibarlı olaraq qalır.