Oyun Kitabı
Ödendi Ama Sipariş Yok: İyileştirme (Odendi Ama Siparis Yok İyilestirme)
Olay müdahale kılavuzu: müşteri ücretlendirildi ama sipariş oluşmadı; çok-niyetli sepet karmaşası; dedup'ı dikkatle temizlemek.
Dağıtık Ödeme Motoru (Distributed Payment Engine)
Bolum 15 / 22
Capture ile complete arasındaki boşluğu kapatan dağıtık ödeme mimarisi serisi.
Önceki bölümde mutabakat worker'ının sürüklenmeyi nasıl tespit ettiğini gördük. Bu bölüm, o sürüklenmenin en can sıkıcı biçimini ele alıyor: müşteri PSP tarafında gerçekten ücretlendirildi, ama sistemde karşılığında hiçbir sipariş yok.
Bu senaryo panik üretir çünkü iki kötü çözüm de cazip görünür: hemen refund etmek (müşteri belki de siparişi gerçekten istiyordu, gereksiz bir iade-tekrar deneme döngüsü başlatır) veya sessizce yeni bir sipariş oluşturmak (hangi sepete, hangi anlaşmaya karşılık geldiğini bilmeden yanlış bir sipariş üretme riski).
PSP kaydı: Charge #789 → Succeeded, amount: 249.00
Local kayıt: (hiçbir Order veya Payment satırı yok)
│
▼
Bu paranın hangi sepete ait olduğunu belirle
│
▼
Sipariş oluştur (heal) VEYA güvenle refund et
Kavramlar ilk geçtiği yerde
📦 Orphan Charge
PSP tarafında başarılı ama local sistemde hiçbir kayda bağlanamayan ücretlendirme.
📦 Multi-Intent Cart
Aynı sepet için birden fazla ödeme intent'i oluşturulmuş durum (örn. kullanıcı sayfayı iki kez yeniledi).
📦 Heal (İyileştirme)
Orphan charge'ı doğru sepete/siparişe geriye dönük olarak bağlama işlemi.
📦 Correlation ID
Bir ödeme intent'ini oluşturduğu sepete/isteğe bağlayan, ödeme akışının başında üretilen kimlik.
Orphan charge, çoğunlukla correlation id'nin bir yerde kaybolmasından doğar: intent oluşturulurken kaydedilen id, sipariş oluşturma adımına ulaşmadan süreç kesintiye uğrar.
Olay müdahale kılavuzu: ilk adımlar
Bir orphan charge tespit edildiğinde (genellikle mutabakat worker'ı veya müşteri şikayeti aracılığıyla), ilk adım asla aksiyon almak değil, correlation zincirini yeniden kurmaktır:
1. PSP'nin charge metadata'sından correlation id'yi çıkar
2. Bu id ile local sistemde bir cart/intent kaydı ara
3. Kayıt bulunduysa → hangi aşamada kesintiye uğradığını belirle
4. Kayıt bulunamadıysa → refund'a yönel (heal edilecek bir hedef yok)
Correlation id her zaman PSP'nin metadata alanına yazılmalıdır (charge oluşturulurken); bu, orphan charge'ları geriye dönük çözülebilir kılan en önemli tasarım kararıdır.
Multi-intent cart: hangi para hangi siparişe ait
Bir müşteri ödeme sayfasını iki kez açtıysa (sekme yenileme, çift tıklama, ağ gecikmesi sonrası tekrar deneme), aynı sepet için iki farklı payment intent oluşabilir. Eğer her ikisi de PSP tarafında başarılı olursa, sistemin karşılaştığı soru artık 'kayıp bir ödeme' değil, 'hangi ödeme kazanan, diğerine ne olacak' sorusudur.
Cart #A
├─ Intent #1 → PSP: Succeeded
└─ Intent #2 → PSP: Succeeded (aynı sepet, iki farklı charge)
Burada doğru davranış, sepeti kilitleyip (aynı sepete karşı yeni intent oluşturmayı engelleyerek) yalnızca bir intent'i 'kazanan' seçmek ve diğerini otomatik olarak refund etmektir — ikisini de siparişe bağlamak çift ücretlendirme yaratır.
Dedup'ı dikkatle temizlemek: neden 'agresif' silme riskli
Orphan charge'ları temizlerken en büyük tuzak, dedup mantığını 'aynı tutar, aynı müşteri, yakın zaman' gibi gevşek bir kritere dayandırmaktır. Bu kriter, aslında iki farklı bilinçli siparişi (müşteri gerçekten iki farklı şey satın aldı) yanlışlıkla tek bir sipariş gibi ele alabilir.
❌ Gevşek dedup
Aynı müşteri + aynı tutar + 5 dakika içinde → tek sipariş say
✓ Sıkı dedup
Aynı correlation id VEYA aynı idempotency key → tek sipariş say
Dedup kararı, her zaman sistemin kendi ürettiği bir kimliğe (correlation id, idempotency key) dayanmalı; tutar ve zaman gibi dolaylı sinyaller yalnızca ek bir doğrulama katmanı olarak kullanılmalıdır, birincil kriter olarak değil.
Heal etmek mi, refund etmek mi: karar kriteri
| Durum | Doğru aksiyon |
|---|---|
| Correlation id ile eşleşen, tamamlanmamış bir cart bulundu | Heal — siparişi oluştur, ödemeyi bağla |
| Correlation id hiçbir kayıtla eşleşmiyor | Refund — bağlanacak hedef yok |
| Aynı sepete iki başarılı intent | Birini heal et, diğerini refund et |
| Cart zaten başka bir ödemeyle tamamlanmış | Refund — çift ücretlendirme |
Her heal işlemi, kendi audit kaydını üretmelidir: kim/hangi süreç ne zaman, hangi kanıta dayanarak bu siparişi oluşturdu. Bu kayıt, ileride aynı senaryonun tekrarlanmasını önlemek için de kullanılır.
Sık karıştırılan ayrımlar
❌ Orphan charge her zaman refund edilmeli
✓ Correlation id ile eşleşen bir hedef varsa heal etmek doğru olabilir
❌ Aynı tutar + aynı müşteri = aynı sipariş
✓ Dedup, sistemin kendi ürettiği kimliğe (correlation id) dayanmalı
❌ Multi-intent bir hata durumudur, göz ardı edilebilir
✓ Multi-intent normal bir kullanıcı davranışıdır (yenileme, çift tıklama), tasarlanmalıdır
Heal ile refund kararının karşılaştırması
| Kriter | Heal | Refund |
|---|---|---|
| Correlation id eşleşmesi | Var | Yok veya belirsiz |
| Müşteri deneyimi | Sipariş görünür, kesinti hissedilmez | Para geri döner, sipariş yok |
| Risk | Yanlış eşleşme riski | Müşteri memnuniyetsizliği riski |
Olay müdahalesi kontrol listesi
- Orphan charge'ın PSP metadata'sında bir correlation id var mı? Yoksa bu, tasarım hatasının işaretidir.
- Dedup kararı, tutar/zaman gibi dolaylı sinyallere mi, yoksa sistemin ürettiği kimliğe mi dayanıyor?
- Multi-intent senaryosunda sepet, ilk başarılı intent sonrası kilitleniyor mu?
- Her heal işlemi, kim/ne zaman/hangi kanıt bilgisiyle bir audit kaydı üretiyor mu?
- Refund kararı öncesi, gerçekten hiçbir hedef bulunamadığı iki kez doğrulanıyor mu?
Bu yazıdan aklında ne kalmalı
- Orphan charge'ları çözmenin anahtarı, correlation id'nin ödeme akışının en başında güvenle üretilmesidir.
- Multi-intent cart normal bir senaryodur; sepet kilitleme ve tek kazanan intent seçimiyle tasarlanmalıdır.
- Dedup kararı asla tutar/zaman gibi dolaylı sinyallere dayanmamalı, her zaman kimliğe dayanmalıdır.
- Heal etmek mi refund etmek mi sorusu, kanıta dayalı bir karar olmalı, refleks bir aksiyon olmamalıdır.
Panik anında en güvenli aksiyon, hızlı bir düzeltme değil; doğru kanıtı bulana kadar hiçbir şey yapmamaktır.
Bir sonraki bölümde bu tür sürüklenmelerin köküne iniyoruz: neden PSP, sipariş ve finans arasında dağıtık bir transaction (2PC) kurmak bir tuzaktır, ve saga + mutabakat neden gerçek cevaptır.
SSS
Sık sorulan sorular
Orphan Charge nedir?
PSP tarafında başarılı ama local sistemde hiçbir kayda bağlanamayan ücretlendirme.
Multi-Intent Cart nedir?
Aynı sepet için birden fazla ödeme intent'i oluşturulmuş durum (örn. kullanıcı sayfayı iki kez yeniledi).
"Orphan charge her zaman refund edilmeli" doğru mu?
Correlation id ile eşleşen bir hedef varsa heal etmek doğru olabilir
Bu bölüm neyi sabitler?
Bu senaryo panik üretir çünkü iki kötü çözüm de cazip görünür: hemen refund etmek (müşteri belki de siparişi gerçekten istiyordu, gereksiz bir iade-tekrar deneme döngüsü başlatır) veya sessizce yeni bir sipariş oluşturmak (hangi sepete, hangi anlaşmaya karşılık geldiğini bilmeden yanlış bir sipariş üretme riski). Orphan charge'ları çözmenin anahtarı, correlation id'nin ödeme akışının en başında güvenle üretilmesidir. Önceki bölümde mutabakat worker'ının sürüklenmeyi nasıl tespit ettiğini gördük. Bu bölüm, o sürüklenmenin en can sıkıcı biçimini ele alıyor: müşteri PSP tarafında gerçekten ücretlendirildi, ama sistemde karşılığında hiçbir sipariş yok.
Ogrenilen Muhendislik Prensipleri
- Orphan charge'ları çözmenin anahtarı, correlation id'nin akışın başında güvenle üretilmesidir.
- Dedup kararı her zaman kimliğe dayanmalı, tutar/zaman gibi dolaylı sinyallere değil.
- Panikte en güvenli aksiyon, doğru kanıtı bulana kadar hiçbir şey yapmamaktır.
Okumaya devam et
Okumaya devam et
Seride sonraki yazi
Neden Eventual Consistency Dağıtık Transaction'dan Üstün
PSP, sipariş ve finans arasında 2PC kurmak bir tuzaktır. Saga ve mutabakat, dağıtık ödeme tutarlılığının gerçek cevabıdır.
Seride sonraki yazi
Ödeme Mutabakat Worker İnşası
Sweeper'lar drift'i nasıl iyileştirir: PSP başarılı derken local kayıt expired olabilir; aged FinalizePending nasıl toparlanır.
Ayni seriden
Webhook Altında Optimistic Concurrency
Webhook ile senkron yanıt aynı ödemeye aynı anda dokunduğunda version token ve lease nasıl yarışı çözer? Bayat okumanın terminal ödemede client secret…