İçeriğe atlayın

Aynı ödeme webhooku neden iki kez geliyor?

15 Eyl 2026 8 dakikalık okuma Paymos Tech Paymos Tech
Ödeme webhooku tekrarı — dört özdeş teslimat zarfı, yalnızca ilki işlenmiş olarak işaretli

Özet

Ödeme webhooku iki kez gelebilir ve bunu zararsız kılabilecek tek yer alıcı tarafıdır: ağ üzerinden teslimat en fazla "en az bir kez" söz verir. Paymos'ta teslimat döngüsü yaklaşık 16 saate yayılan 11 denemedir, sonrasında başarısız olay elle yeniden oynatılabilir. Teslimatı X-Webhook-Id başlığındaki evt_ kimliğiyle tekilleştirin, siparişi ise data içindeki kaynak kimliğiyle eşleştirin. İlki uç noktadan uç noktaya değişir, ikincisi hiç değişmez.

Aynı ödemeyi iki kez teslim etmenin bedeli siparişin kendisidir. Webhook iki kez gelebildiği için de bu, entegrasyonun kenar durumu değil olağan hâlidir: ağın verebileceği en güçlü söz "en az bir kez".

Gönderen tarafta sebep basit. Zaman aşımı dolduğunda elde tek bilgi kalır: cevap gelmedi. O bilgi iki ihtimali birbirinden ayıramaz — istek hiç ulaşmadı, ya da cevap dönüşte kayboldu. Paymos'ta gönderen on bir kez dener; denemeler tükendiğinde olay elle yeniden oynatılabilir.

Alıcı tarafta çözüm tek bir ayrıma iniyor. Her teslimat kendi evt_… kimliğini X-Webhook-Id başlığında taşır; data içindeki fatura ise ömrü boyunca değişmeyen kendi kimliğini taşır. Teslimatı birincisiyle tekilleştirin, siparişi ikincisiyle eşleştirin.

Sözleşmenin kendisi belgede duruyor. Buradaki konu, o sözleşmenin sizdeki koddan ne istediği.

Gönderen neden "bir kez ulaştı" diyemez?

Hiçbir ağ bunu diyemez. Mesaj düşürebilen tek kanal üzerinden konuşan iki taraf, birbirinin durumu hakkında kesinliğe ulaşamaz; bu 1975'ten beri bütçe sorusu değil, kanıtlanmış sonuç. Üç yıl sonra Jim Gray ona bugün taşıdığı adı verdi: iki general paradoksu.

Bunun tek webhookta nasıl göründüğüne bakın. Gönderen isteği açar, gövdeyi yazar, bekler. 10 saniyelik deneme zaman aşımı, sokette hiçbir şey yokken dolar. Alıcı hiç görmedi mi? Yoksa imzayı doğrulayıp siparişi işledi de cevabı dönüşte mi kaybetti? Gönderen taraftan ikisi aynı sessizlik.

Geriye iki seçenek kalır ve para işin içindeyken yalnızca biri dürüst. Bir kez gönderin: ödenmiş fatura bazen sipariş sistemine hiç ulaşmaz. Yeniden gönderin: işleyici bazen tek ödeme için iki kez çalışır. İkincisi, alıcının karşı önlem alabileceği tek arızadır.

En az bir kez teslimat, sektörün ortak kararı

Burada kriptoya özgü hiçbir şey yok. Google, en az bir kez teslimatı Pub/Sub'da bütün abonelik tiplerinin varsayılanı olarak belgeliyor ve abonelerden tekrar-güvenlik bekliyor. Stripe'ın webhook sayfası da uç noktaların aynı olayı zaman zaman birden fazla alacağını, çözümün ise işlenen kimlikleri kaydetmek olduğunu yazıyor. Aynı kısıt, aynı sonuç.

Ürünler arasında değişen, tekrarın bedeli. Tekrarlanan analitik isteği grafiği büker. Tekrarlanan invoice.paid ikinci kutuyu yola çıkarır ya da ikinci lisans anahtarını üretir, üstelik sipariş sisteminin bundan haberi olmaz — her çalışma tek başına doğru görünmüştür.

RFC 9110 kelimenin kendisinde net: aynı istek birkaç kez gönderildiğinde amaçlanan etki, tek kez gönderildiğindeki etkiyle aynıysa yöntem tekrar-güvenlidir. Yani özellik, gönderenin gövdeye ne koyduğuyla değil, alıcının olayla ne yaptığıyla ilgili. Başlık anahtar taşıyabilir; etkiyi iki seferde de aynı kılacak olan alıcıdır.

Alıcı hangi kimlikle tekilleştirmeli?

Tek teslimatta iki kimlik yolculuk eder ve yanlış olanı seçmek, bu tasarımın etrafında şekillendiği arızadır.

X-Webhook-Id, gövdedeki event_id ile aynı değeri taşır, evt_… biçimindedir ve teslimatı adlandırır: tek uç noktanın, tek durum geçişine ait kopyası. O teslimatın her denemesi aynı değeri taşır; elle yeniden oynatma da onu korur.

data içindeki kaynak kimliği inv_…, wdr_… ya da pcd_… biçimindedir ve paranın başına geleni adlandırır. O olaya abone olan her uç nokta aynısını görür, kaynağın sonradan geçtiği her durum da aynısını taşır. Zarfın tamamı yük referansında duruyor.

Yanlış gitmenin iki yolu var. İki uç nokta çalıştıran ve sipariş tablosunu event_id üzerinden anahtarlayan satıcı, tek ödeme için iki farklı kimlik görür ve siparişi iki kez işler. Teslimat tekilleştirmesini fatura kimliği üzerinden anahtarlayan satıcı ise o faturanın ikinci teslimatını engeller; ikinci teslimat invoice.confirming'in ardından gelen invoice.paid olduğu için sipariş hiç teslim edilmez.

Stripe aynı ayrıma öbür uçtan varıyor: tekrarları yakalamak için olay kimliklerini kaydedin, iki ayrı olay nesnesi tek şeyi anlatıyorsa data.object içindeki nesne kimliğiyle olay tipine bakın.

Satır neden işten önce yazılır?

Önce satırı yazmak çökmeye karşı kuraldır ve dar bir pencerede hayat kurtarır. Siparişi teslim edip sonra olayı kaydeden işleyici ikisinin arasında boşluk bırakır. O boşlukta öldürülen süreç, sistemde hiçbir izi olmayan gönderilmiş sipariş bırakır ve bir sonraki teslimat onu yeniden gönderir.

İşleyiciyi ters çevirin. İmzayı doğrulayın, teslimatı ekleyin, cevabı verin, gerisini kendi belirlediğiniz takvimde çalışan bir işçiye bırakın.

create table webhook_delivery (
    event_id     text primary key,          -- evt_…, bu teslimat
    resource_id  text        not null,      -- inv_… / wdr_… / pcd_…, ödemenin kendisi
    event_type   text        not null,
    body         jsonb       not null,
    received_at  timestamptz not null default now(),
    processed_at timestamptz
);

insert into webhook_delivery (event_id, resource_id, event_type, body)
values ($1, $2, $3, $4)
on conflict (event_id) do nothing
returning event_id;

Sıfır satır dönmesi, bu teslimatın zaten kayıtlı olduğunu söyler: 2xx dönün, karar verilecek bir şey kalmamıştır. Tek satır dönmesi işin sizde olduğunu söyler ve gönderdiğiniz 2xx, bir şeyin gönderildiği iddiası değil, baytların alındığı makbuzdur.

Aynı ayrım işleyiciyi 10 saniyelik deneme zaman aşımının içinde tutar. Takılan üçüncü taraf çağrısı da kötü bir günde duran lisans üretimi de, kendi denemeleri olan bir kuyruğun arkasına aittir.

Outbox deseni neyi çözmez?

Tekrarları çözmez; bu da desenin kusuru değil. Durum değişikliğiyle mesajın birlikte çıkması gerekir, hiçbir işlem de veritabanıyla ağı birlikte kapsamaz. Önce satırı yazarsanız, gönderimden önceki çökme mesajı kaybeder. Önce gönderirseniz, yazımdan önceki çökme hiç olmamış bir şeyi duyurur. Buna ikili yazma denir ve standart cevap işlemsel outbox'tır: mesaj, anlattığı olguyla aynı işlemde aynı veritabanına yazılır, bir aktarıcı da onu ağa taşır.

Garantiyi dikkatli okuyun, çünkü ününden dar. Mesaj, ancak ve ancak işlem commit olduysa vardır. Richardson diğer yarısını desenin sorunları arasında sayıyor: aktarıcı mesajı yayımlayabilir, yayımladığını kaydetmeden ölebilir ve yeniden başladığında aynı mesajı yeniden yayımlayabilir.

Takas adını hak ediyor. Kaybolan mesaj hiçbir yerde iz bırakmaz; tekrarlanan mesaj ise ilk seferki kimliğiyle gelir, kimlik de alıcının indeksleyebileceği bir şeydir. Gönderenin outbox çalıştırıp çalıştırmadığı dışarıdan görünmez. Uç noktanıza ulaşan şey, o desenin ürettiği özelliktir.

Olay ne kadar süre geri gelir?

On bir deneme. Aralar bir dakikadan başlayıp sekiz saate çıkıyor ve sonuncusu ilkinden yaklaşık 16 saat sonraya düşüyor; teslimat takvimi deneme deneme yayımlanmış.

Bunu kendi kesintiniz hakkında iki ayrı bilgi olarak okuyun. Dağıtım fark edilmeden geçer, çünkü ilk basamaklar dakikalarla ayrılıyor ve olay kimse panele bakmadan geri geliyor. İş gününe yayılan veritabanı kesintisi öyle değil: döngüsü o kesintinin içinde dolan olaylar başarısız işaretlenir.

Uç noktaya ise hiçbir şey olmaz. On birinci deneme de başarısız olduğunda yalnızca olay başarısız işaretlenir; uç nokta yerinde, etkin ve abone kalır. Sayılan bir hata kotası ya da yeniden açılacak bir anahtar yok. Bütün öğleden sonra kapalı kalan alıcı, canlı uç noktaya ve yeniden oynatmayı bekleyen başarısız olaylar listesine döner.

Elle yeniden oynatmanın ilginç durumu da burada. Kesinti boyunca teslimatları reddeden uç noktanın tablosu boştur, dolayısıyla düzeltmeden sonra oynatılan her olay ona yenidir. Oysa arkadaki iş durumu çoktan doğru olabilir: destekten biri paneli okuyup siparişi ödendi işaretlemiştir. Tekilleştirme tablosu bunu göremez, yalnızca hangi teslimatları okuduğunu bilir. Bu durumu koruyan şey geçişin kendisi — ödendi olarak yazılmış fatura ikinci kez yazılmaz ve kontrol, koruduğu yazımla aynı işlemde durur.

Panel tarafında pencere dar. Webhook'lar ekranı en yeni 100 olayın teslimat durumunu, deneme sayısını ve sıradaki denemesini gösterir, daha geriye sayfalamaz. Arşiv sizin teslimat tablonuz.

Webhook secret'ı yenilenirken ne kırılır?

Rotasyondan sonraki 24 saat boyunca imza başlığı tek v1 yerine iki v1 değeri taşır ve teslimat, ikisinden biriyle eşleşiyorsa geçerlidir. Pencere şunun için var: alıcı yeni secret'ı kendi dağıtım takvimine göre alsın.

v1'i liste değil alan olarak okuyan alıcı, o penceredeki her teslimatı reddeder. Her reddediş merdivende tek denemedir. On altı saat sonra o olaylar başarısız işaretlenir ve görünen ilk belirti genelde hiç gönderilmemiş bir sipariş olur.

Yani v1'i liste olarak ayrıştırın, her adayı zamanlama-güvenli fonksiyonla karşılaştırın ve ilk eşleşmede kabul edin. Neyin hangi sırayla özetlendiği imza sayfasında yazılı. Resmî Paymos SDK'larının hepsi bunu zaten böyle yapıyor ve uygunluk paketi iki değerli başlığı test vektörü olarak sabitliyor; SDK üzerine kurulan entegrasyon davranışı hazır devralır.

Zaman damgasında ise karar sizde. Paymos giden teslimata X-Webhook-Timestamp basar, doğrularken hangi zaman aralığını kabul edeceğinizi alıcı tarafı belirler.

Tekrar-güvenlik nasıl kanıtlanır?

Kodu okuyarak değil, yeniden oynatarak. Sınanan özellik ikinci çalışma hakkında bir iddiadır ve birim testinin üretmeye en az yatkın olduğu şey tam olarak ikinci çalışmadır.

Panelin API Playground'u satıcının kendi kimlikleriyle gerçek HMAC imzalı istek gönderir, yalnızca Sandbox'ta; webhook Playground'u da gerçek teslimat üretir. Kontrolün tamamını staging alıcısına karşı çalıştırmaya bu yeter:

  1. Teslimat gönderin, işleyicinin bitmesini bekleyin, ürettiği sipariş satırını not edin.
  2. Aynı olayı yeniden gönderin. Tablonuza ulaşmalı, kimliği bulmalı ve siparişe dokunmadan 2xx dönmeli.
  3. Alıcıyı 10 saniyelik zaman aşımının ve arkasındaki bir dakikalık beklemenin ötesine kadar askıda tutun, böylece tekrar ilk çalışma sürerken düşsün. Tekil indeksin orada olma nedeni bu çakışmadır.
  4. Alıcıyı düzelttikten sonra başarısız olayı yeniden oynatın ve zaten yazılmış siparişin tek kaldığını doğrulayın.
  5. Teslimat tablonuzu, API'nin ödendi dediği faturalarla karşılaştırın. Eksik satır abonelik ya da güvenlik duvarı sorunudur; çift teslimat anahtar sorunudur.

Beşi de gönderimden önce çalışırsa tekrar merdiveni arka plan gürültüsüne dönüşür. Üçüncü adımı sürüm öncesi kontrol listesinde tutun: onu atlayan ekip tekilleştirmeyi tek istekle test etmiş olur, oysa asıl çakışma iki isteğin aynı anda içeride olduğu saniyede yaşanır.

Tek teslimatta yolculuk eden iki kimlik (Eylül 2026)
SoruX-Webhook-Id (evt_…)data içindeki kaynak kimliği
Neyi adlandırırTek uç noktaya giden tek teslimatıParanın başına geleni: fatura, çıkış, yatırma
Aynı teslimat yeniden deneninceDeğişmezDeğişmez
İki uç nokta aynı olaya abone oluncaİki farklı değerTek değer
Fatura confirming'den paid'e geçinceYeni değerAynı değer
Elle yeniden oynatmadaKendi değerini korurDeğişmez
Ne için kullanılmalıTeslimatı tekilleştirmekSiparişi eşleştirmek

Sık sorulan sorular

Aynı ödeme webhookunu neden iki kez aldım?

Teslimat en az bir kezdir. Zaman aşımına uğrayan deneme, alıcı onu işlemiş olsa bile yeniden denenir; gönderen taraftan bakınca kaybolan istek ile kaybolan cevap aynı görünür. Elle yeniden oynatma da tekrar üretir.

event_id ile mi fatura kimliğiyle mi tekilleştirmeliyim?

Teslimatlar için event_id ile — bu, X-Webhook-Id başlığındaki değerin aynısıdır. Siparişin kendisi için fatura kimliğiyle. event_id tek ödemeye abone iki uç noktada farklıdır; fatura kimliği her yerde ve faturanın geçtiği her durumda aynıdır.

Zaten işlediğim bir olay için ne dönmeliyim?

Hemen 2xx. Tanınan tekrar, başarılı teslimattır. Hata dönmek olayı boş yere tekrar merdivenine geri koyar.

Yeniden oynatılan webhook yeni kimlikle mi gelir?

Gelmez. Yeniden oynatma o teslimatın kendi evt_ kimliğini korur, böylece ilk seferinde kaydeden alıcı tekrarı tek indeks aramasıyla tanır.

Başarısız webhook ne kadar süre yeniden denenir?

Yaklaşık 16 saate yayılan 11 deneme; aralar bir dakikadan sekiz saate çıkar. Sonrasında olay başarısız işaretlenir ve elle yeniden oynatılabilir. Uç noktanın kendisi etkin kalır.

webhookla tetiklenen sipariş teslimatı ne zaman KULLANILMAMALI

  • Alıcı yalnızca dizüstünüzde ya da özel ağda çalışıyorsa teslimatın ineceği yer yoktur. Genel ağdan erişilemeyen hedef reddedilir, oraya götüren yönlendirme de izlenmez; geliştirirken fatura durumunu API'den sorun.
  • Günde birkaç ödeme geliyorsa ve zaten birisi bunlara bakıyorsa; teslimat tablosu, kuyruk ve tekilleştirme anahtarı hacmin kazandırdığından fazla makinedir.
  • Sipariş sistemi tekrarlanan geçişi reddedemiyorsa uç nokta bağlamadan önce onu düzeltin. İki kez çalışacak teslimat yolunun önüne konan anahtar pencereyi daraltır, kapatmaz.
  • Tek olay adından akış istiyorsanız abonelik bunu vermiyor. Kategori bazında çalışır, kategori içindeki adlar ayrı seçilemez ve faturalara abone uç nokta her fatura olayını alır. Dallanma işleyicide olur.

Kaynaklar

  1. 1. HTTP Semantics (RFC 9110), section 9.2.2 Idempotent Methods (accessed 2026-09-15)
  2. 2. Google Cloud Pub/Sub — Subscription overview (at-least-once delivery) (accessed 2026-09-15)
  3. 3. Stripe — Receive Stripe events in your webhook endpoint (accessed 2026-09-15)
  4. 4. Chris Richardson — Pattern: Transactional outbox (accessed 2026-09-15)
  5. 5. Two Generals' Problem — Akkoyunlu, Ekanadham and Huber (1975) (accessed 2026-09-15)

Son gözden geçirme: 15 Eyl 2026

#webhook#tekrar-guvenli#outbox-deseni#entegrasyon#siparis-teslimati
Paylaşın