Oyun Kitabı
Ödeme Hata Taksonomisi (Odeme Hata Taksonomisi)
Timeout, 429, 5xx, business decline ve infrastructure hatası aynı şey değildir. Her kategori farklı bir retry politikası ister.
Dağıtık Ödeme Motoru (Distributed Payment Engine)
Bolum 11 / 22
Capture ile complete arasındaki boşluğu kapatan dağıtık ödeme mimarisi serisi.
Önceki bölümde PaymentFailed gibi semantik event'lerin provider'ın statü kodlarını gizlediğini gördük. Fakat tek bir PaymentFailed event'i bile yeterli değildir; çünkü 'başarısız' kelimesi çok farklı gerçekleri kapsar.
Bir kartın reddedilmesi, bir ağ timeout'u, PSP'nin 429 döndürmesi ve PSP'nin 500 döndürmesi hepsi 'başarısız' olarak görünebilir; ama her biri tamamen farklı bir aksiyon gerektirir. Bu bölüm, bu farkları görünür kılan bir taksonomi kurmayı ele alıyor.
PaymentFailed
├─ Business Decline (kart reddedildi — retry etme)
├─ Timeout (belirsiz sonuç — dikkatli retry)
├─ Rate Limited (429) (çok istek — backoff ile retry)
└─ Infrastructure (5xx) (provider tarafı arıza — retry)
Kavramlar ilk geçtiği yerde
📦 Business Decline
PSP'nin, kartın kendisiyle ilgili bir nedenle isteği reddetmesi: yetersiz bakiye, dolandırıcılık şüphesi.
📦 Transient Hata
Aynı isteğin tekrar denenmesi mantıklı olan, geçici bir arıza: timeout, 5xx, 429.
📦 Permanent Hata
Tekrar denemenin sonucu değiştirmeyeceği hata: geçersiz kart numarası, desteklenmeyen para birimi.
📦 Belirsiz Sonuç
İsteğin PSP'ye ulaşıp ulaşmadığının bilinmediği durum: bağlantı timeout'u.
Business decline'ı retry etmek zaman kaybıdır; belirsiz sonucu retry etmemek ise gerçek bir kaçırılmış ödeme riskidir. Taksonomi, bu iki riski birbirinden ayırmak için var.
Dört temel kategori
Business decline: PSP isteği aldı, işledi ve kararını verdi — kart reddedildi. Bu bir sistem hatası değildir, bir iş kararıdır. Tekrar denemek sonucu değiştirmez; kullanıcıya başka bir ödeme yöntemi önermek gerekir.
Timeout / belirsiz sonuç: İstek gönderildi ama yanıt hiç gelmedi. Burada tehlikeli olan şudur — ödeme PSP tarafında gerçekleşmiş olabilir, sadece yanıt sizde kaybolmuştur. Bu kategori kör bir 'tekrar dene' ile ele alınamaz; önce durum sorgulanmalı (idempotency key ile), sonra karar verilmelidir.
Rate limited (429): PSP, istek hacmini kontrol etmek için sizi geçici olarak reddediyor. Bu bir hata değil, bir sinyal. Hemen tekrar denemek durumu kötüleştirir; backoff ile beklemek gerekir.
Infrastructure (5xx): PSP'nin kendi tarafında bir arıza var. İstek işlenmemiştir, tekrar denemek genellikle güvenlidir — ama sürekli 5xx alınıyorsa bu bir circuit breaker sinyalidir.
Hata alındı
│
├─ PSP isteği net biçimde reddetti mi? → Business Decline → retry etme
│
├─ Yanıt hiç gelmedi mi? → Belirsiz Sonuç → önce durumu sorgula
│
├─ 429 mu? → Rate Limited → backoff ile retry
│
└─ 5xx mi? → Infrastructure → retry, ama circuit breaker'ı izle
Neden tek bir 'retry et' kuralı yeterli değil
Tüm hataları aynı retry mantığıyla ele alan bir worker iki şekilde başarısız olur: business decline'ları anlamsızca tekrar dener (kullanıcı deneyimini geciktirir, bazen kart ağı limitlerini zorlar), ya da belirsiz sonuçları hiç retry etmeden bırakır (gerçekte başarılı olmuş bir ödemeyi sistemin unutmasına yol açar). Taksonomi, her hatayı doğru kutuya koyarak bu iki riski de azaltır.
Taksonomiyi kodda nasıl temsil ederiz
Semantik event'in failureReason alanı, provider'ın ham hata metnini değil, bu dört kategoriden birini taşımalıdır. Provider gateway, ham hatayı bu kategoriye eşlemekle sorumludur — bu, önceki bölümdeki çeviri katmanının bir uzantısıdır.
| failureReason | Retry uygun mu | Aksiyon |
|---|---|---|
| BusinessDecline | Hayır | Kullanıcıya başka yöntem öner |
| AmbiguousTimeout | Önce sorgula | Durum sorgusu sonra karar |
| RateLimited | Evet | Backoff ile retry |
| InfrastructureError | Evet | Retry + circuit breaker izle |
Sık karıştırılan ayrımlar
❌ Her hata retry edilmelidir
✓ Business decline retry edilmemelidir; sonuç değişmez
❌ Timeout = hata yok, sadece tekrar dene
✓ Timeout = belirsizlik; önce gerçek durum sorgulanmalı
❌ 429 bir arızadır
✓ 429 bir sinyaldir; sistem sizi kasıtlı olarak yavaşlatıyor
Kategoriler arası hızlı karşılaştırma
| Kategori | Sonuç belli mi | Retry mantıklı mı | Tipik neden |
|---|---|---|---|
| Business Decline | Evet | Hayır | Kart, bakiye, dolandırıcılık |
| Timeout | Hayır | Önce sorgula | Ağ, PSP yavaşlığı |
| Rate Limited | Evet | Evet (bekleyerek) | Hacim kontrolü |
| Infrastructure | Evet | Evet | PSP tarafı arıza |
Taksonomi kurarken kontrol listesi
- Provider'ın döndürdüğü her hata kodu, dört kategoriden birine açıkça eşleniyor mu?
- Eşlenmemiş yeni bir hata kodu geldiğinde sistem varsayılan olarak durumu mu sorguluyor, yoksa körlemesine mi retry ediyor? (Doğru varsayılan: durumu sorgula.)
- Timeout senaryosunda, retry öncesi durum sorgusu (status check) gerçekten uygulanıyor mu?
- 429 için backoff süresi, PSP'nin
Retry-Afterbaşlığını (varsa) dikkate alıyor mu? - 5xx sıklığı, circuit breaker'ı tetikleyecek bir metrik olarak izleniyor mu?
- Business decline sonrası kullanıcı akışı, retry döngüsüne hiç girmiyor mu?
Bu yazıdan aklında ne kalmalı
- 'Başarısız' tek bir durum değildir; en az dört farklı aksiyon gerektiren dört farklı gerçektir.
- Business decline retry edilmez; timeout körlemesine retry edilmez, önce sorgulanır.
- 429 bir sinyal, 5xx bir arızadır; ikisi de retry edilir ama farklı disiplinle.
- Taksonomi, hatayı ham provider metninden değil, kendi
failureReasonenum'unuzdan okunabilir kılar.
Bir retry politikası, hatayı anlamadan yazıldıysa; işe yaramaz olmakla kalmaz, sessizce zarar verir.
Bir sonraki bölümde bu dört kategorinin her biri için gerçek retry algoritmasını — backoff, jitter, cap ve circuit breaker'ı — kuracağız.
SSS
Sık sorulan sorular
Business Decline nedir?
PSP'nin, kartın kendisiyle ilgili bir nedenle isteği reddetmesi: yetersiz bakiye, dolandırıcılık şüphesi.
Transient Hata nedir?
Aynı isteğin tekrar denenmesi mantıklı olan, geçici bir arıza: timeout, 5xx, 429.
"Her hata retry edilmelidir" doğru mu?
Business decline retry edilmemelidir; sonuç değişmez
Bu bölüm neyi sabitler?
Bir kartın reddedilmesi, bir ağ timeout'u, PSP'nin 429 döndürmesi ve PSP'nin 500 döndürmesi hepsi 'başarısız' olarak görünebilir; ama her biri tamamen farklı bir aksiyon gerektirir. Bu bölüm, bu farkları görünür kılan bir taksonomi kurmayı ele alıyor. 'Başarısız' tek bir durum değildir; en az dört farklı aksiyon gerektiren dört farklı gerçektir. Önceki bölümde `PaymentFailed` gibi semantik event'lerin provider'ın statü kodlarını gizlediğini gördük. Fakat tek bir `PaymentFailed` event'i bile yeterli değildir; çünkü 'başarısız' kelimesi çok farklı gerçekleri kapsar.
Ogrenilen Muhendislik Prensipleri
- Başarısız tek bir durum değildir; her kategori farklı bir aksiyon gerektirir.
- Belirsiz sonuç körlemesine retry edilmez, önce sorgulanır.
- 429 bir sinyaldir, arıza değil — disiplinle beklenir.
Okumaya devam et
Okumaya devam et
Seride sonraki yazi
Ödeme Worker'ları İçin Retry Algoritmaları
Exponential backoff, jitter, cap, defer ile retry farkı ve circuit breaker — bir önceki bölümdeki taksonomiyi çalışan koda dönüştürün.
Seride sonraki yazi
Ham Sağlayıcı Verisi Yerine Anlamsal Olay (Semantic Event)
Provider gateway'in aldığı webhook, downstream'e PSP'nin event adıyla mı, yoksa PaymentCaptured/PaymentFailed gibi semantik bir olayla mı ulaşmalı?
Ayni seriden
SDK (Software Development Kit) Sızdırmadan Provider Abstraction: Gateway'in Sınırı
Provider gateway PSP SDK'sını nasıl sahiplenir, checkout orchestrator neden yalnızca semantik bir arayüz görmelidir? Kart ve wallet akışlarının farklı…