Nisan'da platformumuzun miras kalan eski ödeme akışında tuhaf bir şikâyet deseni yakaladık: ayda bir iki kez, ödemesi bankadan onaylanmış siparişler sistemde "ödeme bekliyor" durumunda kalıyordu. Kayıtları eşeledik ve tabloyu gördük: bir pazar gecesi 40 dakikalık deploy kesintisi sırasında ödeme sağlayıcısının gönderdiği 312 webhook boşluğa düşmüş. Karşı taraf tekrar denememiş, bu taraf da fark etmemiş. 312 müşteri parasını ödediği halde siparişini alamamış ve çağrı merkezi bir hafta bunun faturasını ödemiş.
Webhook'lar entegrasyonların görünmez kahramanıdır: çalışırken kimse varlıklarını hatırlamaz, düştüklerinde herkes öğrenir. On altı yılda yüzlerce entegrasyon kurdum; bu yazı, o entegrasyonlarda canımı yakan derslerin damıtılmış hali.
Webhook Size "En Az Bir Kez" Der, "Tam Bir Kez" Demez
İlk ve en önemli zihniyet değişikliği bu. İyi tasarlanmış bir webhook sağlayıcısı olayı en az bir kez ulaştırmayı vaat eder. Bu vaadin doğal sonucu: aynı olay bazen iki kez, bazen beş kez gelir. Ağ hıçkırıkları, timeout'lar, karşı tarafın retry mantığı — hepsi mükerrer teslimat üretir ve bunların hiçbiri hata değildir, sistemin tasarımı gereğidir.
Kabul eden taraf bunu bilerek yazılmalı. "Ödeme onaylandı" webhook'u iki kez geldiğinde iki kez fatura kesen, iki kez kargo etiketi oluşturan sistemler gördük. (Bir keresinde aynı müşteriye üç kez hoş geldin indirimi tanımlayan bir sistem de gördük; müşteri nedense şikâyet etmemişti.) Çözümün adı idempotency: her olayın taşıdığı benzersiz kimliği işlenmiş olaylar tablosunda tutarsınız; aynı kimlik ikinci kez geldiğinde 200 dönüp hiçbir şey yapmazsınız. Bir satırlık ek sorgu, sizi mükerrer faturadan kurtarır.
Hızlı Cevap Verin, İşi Sonra Yapın
Klasik hata şöyle işliyor: webhook geldi, aynı HTTP isteğinin içinde stok düşülüyor, mail atılıyor, ERP'ye kayıt açılıyor... İşlem 25 saniye sürüyor, gönderen taraf 10 saniyede timeout'a düşüyor ve olayı başarısız sayıp tekrar gönderiyor. Artık elinizde hem yarım kalmış bir işlem hem de yolda olan bir mükerrer var.
Doğru desen tam tersi: webhook geldiğinde imzayı doğrula, payload'u olduğu gibi bir kuyruğa yaz, hemen 200 dön. Toplam süre milisaniyeler. Asıl iş kuyruğun arkasındaki worker'da, kendi hızında ve kendi retry mantığıyla yapılır. Bu ayrım webhook alan tarafın altın kuralı; uygulanmayan her sistemde er ya da geç o 312 siparişlik pazar gecesi yaşanıyor.
Kuyruk mimarisinin bir bonusu daha var: gözlemlenebilirlik. Kuyruk derinliği tek başına muazzam bir sağlık göstergesi — birikiyor mu, eriyor mu, en eski olay kaç dakikadır bekliyor? Bu üç sayıyı panoya koyup alarma bağladığınızda, webhook hattınızın nezle olduğunu zatürreye dönmeden öğrenirsiniz.
Gelen Her İsteğe İnanmayın
Webhook endpoint'iniz internete açık bir kapı ve URL'i tahmin edilebilir. İmza doğrulaması yapmayan bir endpoint'e kim ne gönderirse, sisteminiz onu gerçek sanır. "Ödeme onaylandı" olayını taklit eden bir istekle bedava alışveriş yapılan vakalar sektörde hiç nadir değil.
Sağlayıcıların çoğu HMAC imzası kullanır: payload paylaşılan bir secret ile imzalanır ve header'da gelir. Doğrulamayı yaparken gövdenin ham halini kullanın — JSON'u parse edip yeniden serialize ederseniz imza tutmaz ve bu hatayı ayıklamak insana saç baş yolduran cinstendir. Zaman damgası kontrolü de ekleyin ki ele geçirilen eski bir istek tekrar oynatılamasın.
Ortam ayrımını da ciddiye alın. Test sisteminin webhook'unu üretim endpoint'ine yönlendirilmiş bulmak, yıllar içinde girdiğim denetimlerde utandırıcı sıklıkta karşıma çıktı; sahte siparişlerin gerçek stoktan düşüldüğü bir vakayı bizzat temizlemiştim. Her ortamın kendi secret'ı, kendi endpoint'i, kendi log akışı olsun ve secret'lar da tıpkı şifreler gibi rotasyon görsün.
Payload konusunda da bir tasarım tercihi paylaşayım: hassas entegrasyonlarda "ince" webhook gönderiyoruz — olayın kimliği, tipi ve zamanı; verinin gövdesi yok. Tüketen taraf detayı API'den kendisi çeker. Böylece webhook yolda yakalansa bile sızacak veri yoktur ve veri her zaman en güncel halinden okunduğu için sıra problemi de kendiliğinden yumuşar.
Sıra meselesini de buraya iliştireyim: webhook'lar gönderildikleri sırayla gelmek zorunda değil. "Sipariş iptal edildi" olayı, "sipariş oluşturuldu"dan önce kapınıza dayanabilir. Kararlarınızı olayın üzerindeki zaman damgasına ya da sürüm numarasına göre verin; geliş sırasına güvenen kod, er ya da geç yanlış duruma yazar.
Bir de kimsenin konuşmadığı sigorta: mutabakat. Webhook ne kadar sağlam kurulursa kurulsun, tek haber alma kanalınız olmamalı. Biz kritik entegrasyonlarda güne bir kez çalışan bir mutabakat işi kuruyoruz: karşı tarafın API'sinden son 24 saatin kayıtları çekilir, bizim tarafla karşılaştırılır, açık varsa kapatılır ve raporlanır. O 312 siparişlik vaka bir daha yaşansa bile artık en geç ertesi sabah, otomatik olarak yakalanır. Webhook hız içindir; mutabakat doğruluk için. İkisi rakip değil, ortak.
Gönderen Taraftaysanız Yük Sizde
Madalyonun öbür yüzü: kendi platformunuzdan müşterilerinize webhook gönderiyorsanız, güvenilirlik sözünü artık siz veriyorsunuz. Bizim platformdan restoran ve lojistik entegrasyonlarına günde iki milyonun üzerinde olay dağıtıyoruz; bu ölçekte "ben gönderdim, gerisi karşı tarafın sorunu" diyemezsiniz. Yıllar içinde oturttuğumuz asgari çerçeve şu:
- Başarısız teslimatlarda üstel artan aralıklarla (1 dk, 5 dk, 30 dk, 2 sa...) en az 24 saat boyunca retry.
- Kalıcı olarak teslim edilemeyen olaylar için ölü mektup kutusu ve müşterinin panelden görüp yeniden oynatabileceği bir arayüz.
- Her olayda benzersiz ID, olay tipi, zaman damgası ve imza.
- Müşteri endpoint'i art arda düşüyorsa devre kesici ve otomatik bilgilendirme.
Bir de dokümantasyon: hangi olaylar var, payload şeması ne, retry politikanız ne? Son dönemde bunlara bir de MCP yüzü ekliyoruz; entegrasyonu yapan taraftaki geliştirici — ya da açıkçası çoğu zaman onun kod ajanı — olay şemalarını sorgulayıp panelden test olayı tetikleyebiliyor. Entegrasyon süreleri günlerden saatlere indi, çünkü karşı tarafın "acaba payload tam olarak nasıl geliyor" diye bize mail atıp cevap beklemesi gerekmiyor.
Şema sürümlemesini de bu sözleşmenin parçası yapın. Payload'a yeni alan eklemek serbesttir; var olan alanı silmek ya da tipini değiştirmek kırıcı değişikliktir ve ayrı bir sürümle duyurulur. Tüketen tarafa da aynı disiplinin aynası düşer: tanımadığınız alanları hata saymadan yok sayın ki karşı tarafın masum bir eklemesi sizin sisteminizi durdurmasın.
O 312 siparişlik vakanın sonu iyi bitti: araya kuyruk girdi, idempotency tablosu kuruldu, sağlayıcının yeniden oynatma API'siyle kayıp olaylar telafi edildi. Şimdi aynı sistem kesinti yediğinde olaylar birikiyor ve sistem ayağa kalktığında sırayla işleniyor; çağrı merkezinin haberi bile olmuyor. Kendi entegrasyonlarınızda böyle sessiz kayıplardan şüpheleniyorsanız, işe kuyruk derinliği ve günlük mutabakat raporuyla başlayın; zayıf halka çoğu zaman daha ilk haftada kendini gösteriyor. Webhook dertleşmesine her zaman varım; iletişim sayfası açık.