Webhook’lar — Genel bakış
Webhook’lar yetkili sinyaldir. Tarayıcı callback’leri (onSuccess)
ve panel görünümleri kolaylıktır; webhook’lar gerçek kaynaktır.
Teslim garantileri
- En az bir kez. Sunucunuz zaman aşımı içinde 2xx döndürmezse, tek
bir event en fazla 6 kez teslim edilebilir. Handler’ınızı
idempotent yapın —
X-Deliveryüzerinde dedup yapın (payload’da birevent_idalanı yoktur; stabil teslim UUID’si idempotency anahtarıdır). - HTTP isteği başına bir event. Batch yapma yok.
- Uç nokta başına izolasyon. Birden fazla kayıtlı uç noktanız varsa, her biri kendi teslim + yeniden deneme izini alır. Yavaş bir satıcı URL’si diğerlerini açlığa terk edemez — her host’un kendi devre kesicisi vardır.
- İmzalı. Her payload bir
X-Signaturebaşlığı taşır (ve bir rotation’dan sonraki 24 saatlik pencerede, ayrıca birX-Signature-Prev). Gövde ile bir şey yapmadan önce doğrulayın. Bkz. İmza doğrulama.
Abone olunabilir event tipleri
| Event | Şu durumda tetiklenir… |
|---|---|
payment.settled | Zincir üstü transfer, zincirin onay sayısını temizledi. Siparişleri ödendi olarak işaretlemek için bunu kullanın. |
payment.failed | Bir fiat ödeme provider tarafından açıkça reddedildi (şu anda: başarısızlığı sinyalleyen Stripe webhook’u). Kripto zaman aşımları için tetiklenmez — onlar checkout.expired olarak yüzeye çıkar, kısa kripto ödemeleri ise payment.underpaid olarak yüzeye çıkar. |
payment.underpaid | Fonlar geldi ama sipariş toplamından eksik (tipik: stablecoin transfer ücreti tutardan alındı). |
payment.overpaid | Fonlar sipariş toplamının fazlasıyla geldi. Fazlalık kaydedilir ama otomatik olarak iade edilmez. |
order.created | Yeni bir sipariş açıldı — ya B2B API çağrınız ile ya da bir checkout-oturumu dönüşümüyle. |
order.canceled | Bir sipariş iptal edildi durumuna geçti. Payload’un data.reason’ı manuel iptali payment_timeout (worker tarafından süpürülen eski ödenmemiş sipariş) ile ayırır. |
order.resolved | Bir PARTIAL_PAID sipariş PAID’e çözümlendi — satıcı eksikliği kabul etti. |
order.reopened | Önceden otomatik-iptal edilmiş bir sipariş (canceled_reason=payment_timeout) satıcı tarafından yeniden açıldı. |
checkout.created | Bir alıcı bir sipariş için checkout’u açtı. |
checkout.completed | Alıcı tarafı akış bitti (zincir üstü tahsilatı ima etmez — bunun için payment.settled kullanın). |
checkout.expired | Alıcı vazgeçti ve oturum TTL’i doldu. |
payment.refund.requested | Bir iade kaydı oluşturuldu — satıcı tarafından başlatılan bir API çağrısı veya müşteri-gönderimli bir iade-talep formundan. |
payment.refund.approved | Bekleyen bir iade onay iş akışınızı geçti. |
payment.refund.rejected | Bekleyen bir iade reddedildi. |
payment.refund.executed | İadenin zincir üstü transferi temizlendi ve kayıt nihai executed durumuna geçti. |
refund_request.created | Bir iade-talep token’ı üretildi. data.source b2b / dashboard / renewal. Abone olmak opsiyoneldir — hangi token’ın sipariş başına şu anda aktif olduğunu takip eden denetim pipeline’ları için kullanışlı. |
refund_request.renewal_requested | Bir alıcı token süresi dolduktan sonra “Yeni bağlantı talep et” butonuna tıkladı. Abone olmak şiddetle önerilir — bu, satıcının yenileme widget’ında harekete geçmesi gereken yeni bir öğenin olduğuna dair işarettir. |
refund_request.renewed | Bir yenileme onaylandı ve yeni bir token eskisini değiştirdi. data.old_token / data.new_token denetim zincirini oluşturur. |
refund_request.canceled | Bir satıcı panelden bir token’ı CANCELED’a geçirdi (örn. bir yenileme talebini reddetti, aktif bir bağlantıyı sonlandırdı). Idempotent — yalnızca ilk geçiş bir event yayar. data.reason opsiyonel satıcı notudur. |
Panel bu listeyi GET /v1/webhooks/event-types üzerinden alır, böylece
uç nokta oluşturma / düzenleme formu her zaman platformun fiilen yaydığı
ile eşleşir. Göndermediğimiz bir event’e abone olmak, oluşturma zamanında
açık bir hata ile reddedilir.
Test event’leri abone olunabilir değildir. Panelin uç nokta başına
Send Test butonu, senkron olarak o tek uç noktaya bir
webhook.test.ping zarfı POST eder (yeniden deneme pipeline’ını
atlayarak) ve eski satıcı-seviyesi “Send test event” yolu, filtresinden
bağımsız olarak her aktif uç noktaya bir webhook.test zarfı yayar.
Hiçbiri yukarıdaki katalogda görünmez — kayıtlı bir uç noktanız olduğu
için alırsınız, abone olduğunuz için değil.
Yalnızca işlediğiniz event’lere abone olun. Her uç noktanın kendi event
filtresi vardır; joker "*" “her event, gelecekte eklenecekler dahil”
anlamına gelir. Daha az event’e abone olmak handler’ınızı daha temiz
tutar ve hata durumunda yeniden denememiz gereken yüzey alanını
azaltır.
Payload + başlıklar
HTTP gövdesi doğrudan event’e özgü veri nesnesidir. Stripe stili
dış zarf yok — event tipi, teslim ID’si ve emisyon timestamp’i gibi
alanlar başlıklarda yaşar. payment.settled için gövde şöyle görünür:
{
"receipt_id": "rcp_…",
"order_id": "ord_…",
"payment_intent_id": "pin_…",
"checkout_session_id": "cst_…",
"merchant_id": "mer_…",
"customer_id": "cus_…",
"total": "49.00",
"currency": "USD",
"payment_method": "crypto",
"token": "USDC",
"network": "polygon",
"tx_hash": "0x…",
"deposit_address": "0x…",
"treasury_address": "0x…",
"amount_received": "49.00",
"confirmations": 5,
"metadata": { /* event başına */ }
}Diğer event’ler kendi alan setlerini taşır — event başına dokümanlar
gelene kadar kanonik şekil için payment-service/internal/domain/events.go
içindeki publisher struct’larına bakın. Alan adları stabildir (lower
snake_case); zincir üstü tx hash her zaman tx_hash’tir
(transaction_hash değil).
Gelen istekteki başlıklar
Content-Type: application/json
X-Event: payment.settled
X-Delivery: 7c9e6679-7425-40de-944b-e07fc1f90ae7
Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7
X-Timestamp: 1729536000
X-Signature: sha256=9a8b7c…
X-Signature-Prev: sha256=fa31b2… (yalnızca bir rotation kayma penceresinde)| Başlık | Ne olduğu |
|---|---|
X-Event | Event tipi (örn. payment.settled). JSON ayrıştırmasını atlamak istiyorsanız proxy katmanında bunun üzerinde yönlendirin. |
X-Delivery | Teslim satırını tanımlayan UUID. Aynı (event, endpoint) çiftinin tüm yeniden denemelerinde stabil — idempotency anahtarınız olarak kullanın. |
Idempotency-Key | X-Delivery’i yansıtır (aynı değer). Her teslimde ayarlanır — Stripe / GitHub’dan gelen kuralı alır. |
X-Timestamp | Denemenin gönderildiği Unix-saniye. Payload’a imzalanmıştır, böylece yakalanan bir (body, X-Signature) çifti süresiz olarak yeniden oynatılamaz — timestamp’i tolerans pencerenizin dışında olan teslimleri reddedin. |
X-Signature | HMAC-SHA256(secret, X-Timestamp + "." + raw_body)’nin sha256=<hex>’i. Bkz. İmza doğrulama. |
X-Signature-Prev | Önceki secret ile aynı algoritma. Yalnızca siz rotate ettikten sonraki 24 saatlik pencerede mevcuttur — geçiş sırasında her iki anahtarı da çalıştıran doğrulayıcıların teslimleri kabul etmeye devam etmesini sağlar. Pencere kapandıktan sonra başlık gönderilmeyi durdurur. |
Yeniden deneme programı
Uç noktanız zaman aşımı içinde 2xx döndürmezse, bu programda yeniden
deneriz (timestamp’ler ilk denemeye göre):
| Deneme | Gecikme | Birikimli |
|---|---|---|
| 1 | 0s | 0s |
| 2 | +1 dk | 1dk |
| 3 | +5 dk | 6dk |
| 4 | +15 dk | 21dk |
| 5 | +1 saat | 1s 21dk |
| 6 | +6 saat | 7s 21dk |
- deneme başarısız olduktan sonra, teslim dead letter’a taşınır ve
satıcı hesabı e-postası bilgilendirilir. Dead-letter’lanmış event’ler
panelin Geliştiriciler → Webhook’lar → Teslim geçmişi panelinden
veya doğrudan
POST /v1/webhooks/deliveries/:id/replayüzerinden yeniden oynatılabilir. Her replay kendiX-Delivery’si ile yeni bir teslim satırı oluşturur — denetim zinciriparent_delivery_idüzerinden orijinale geri bağlanır, böylece replay’lerin yeniden denemeleri kaynak event’i gölgelemez.
Bir uç nokta kaydedin
- Geliştiriciler → Webhook’lar → + Uç nokta ekle
- URL’nizi yapıştırın — yalnızca
https://…(düz HTTP reddedilir; oluşturma formu ayrıcalocalhost’u, özel IP aralıklarını ve userinfo taşıyan URL’leri engeller) - Abone olunacak event’leri seçin (veya hepsi için
*) - Ortam seçin — test veya canlı (her biri kendi secret’ını alır; asla çapraz geçmezler)
- Kaydet → panel imzalama secret’ını (
whsec_…) bir kez gösterir. Sunucu tarafında saklayın; sonraki iki özellik için ihtiyacınız olacak.
Satıcı başına ortam başına 10 uç noktaya kadar kaydedebilirsiniz (örn. biri production sipariş karşılama için, biri staging yansıtması için, biri bir Slack bildirici için). Her biri kendi yeniden deneme durumunu, secret’ını ve host başına devre kesicisini korur.
Her uç noktada yaşam döngüsü eylemleri
Her uç nokta kartındaki ⋮ menüsü şunları sunar:
- Düzenle — URL’yi, açıklamayı veya abonelik listesini değiştir.
Yeni URL, oluşturma ile aynı
https:///SSRF kurallarıyla yeniden doğrulanır. - Send Test — mevcut secret’ınızla imzalanmış bir
webhook.test.pingzarfını senkron olarak POST eder. Panel HTTP durumunu, gecikmeyi ve yanıtınızın 512 baytlık bir parçacığını gösterir. RMQ pipeline’ını atlar, böylece cevap anlıktır. - Rotate Secret — yeni bir secret oluşturur. Önceki olan 24
saat geçerli kalır (teslimler pencere boyunca hem
X-Signaturehem deX-Signature-Prevtaşır, böylece her iki anahtarı da çalıştıran doğrulayıcılar siz yeniden dağıtırken event’leri kabul etmeye devam eder). - Reveal Secret — mevcut secret’ı yeniden gösterir. Yeni 2FA doğrulaması ile korunur ve denetim günlüğüne kaydedilir; yalnızca kopyanızı kaybettiğinizde ve Rotate kabul edilemediğinde kullanın.
- Etkinleştir / Devre Dışı Bırak — teslim geçmişini kaybetmeden
is_active’i değiştirin. Devre dışı bırakılmış uç noktalar panelde kalır ama yeni teslim almaz. - Sil — kalıcıdır. Daha sonra yeniden etkinleştirebilirseniz Devre Dışı Bırak’ı kullanın.
Handler’lar için ipuçları
- Hızlıca 2xx döndürün. Ağır iş yapmadan önce
200 OKile onaylayın — sipariş karşılamayı bir arka plan kuyruğuna atın. Deneme başına zaman aşımı 10 saniyedir; yanıtı bundan daha uzun tutmak bir yeniden denemeyi tetikler. Zaman aşımı platform tarafıdır ve satıcı tarafından yapılandırılamaz — handler’ınız gerçekten daha fazla zamana ihtiyaç duyuyorsa destekle iletişime geçin. X-Deliveryüzerinde dedup yapın (veyaIdempotency-Key— aynı değer). 2xx döndürseniz bile, yukarı yönlü bir proxy bağlantıyı düşürebilir ve bir yeniden denemeyi tetikleyebilir; teslim ID’si aynı teslim satırının her yeniden denemesinde stabildir, bu yüzden doğru anahtardır.- Bilinmeyen event tiplerine tolerans gösterin. Yeni event’ler görünebilir; 4xx yerine 200 döndürün ve hiçbir şey yapmayın, yoksa yeniden deneme kuyruğunu doldurursunuz.
X-Delivery’yi iş mantığınızın yanında loglayın. Bir şey ters gittiğinde, bu, bizim tarafımız ile sizin tarafınız arasındaki join anahtarıdır.
Sırada ne var
- İmza doğrulama — tam algoritma
- replay-koruma desenleri.
- Kavramlar → Oturumlar — her event tetiklendiğinde bir oturumun hangi durumda olduğu.