Oyun Kitabı

Değişmez Ödeme Snapshot Tasarımı: Sepeti Donduran Karar (Degismez Odeme Snapshot Tasarimi)

Ödeme başlarken sepeti canlı okumak, tutarı ve para birimini kararsız bırakır. Intent anında donan bir snapshot olmadan finalization güvenilir çalışmaz.

Dağıtık Ödeme Motoru (Distributed Payment Engine)

Bolum 4 / 22

Capture ile complete arasındaki boşluğu kapatan dağıtık ödeme mimarisi serisi.

Distributed payment engine architecture diagram

Sepet hareket eden bir hedeftir

Bir müşteri ödeme ekranına geldiğinde sepetteki fiyat, kampanya ve stok bilgisi hâlâ değişebilir durumdadır: kampanya süresi dolabilir, fiyat güncellenebilir, aynı müşteri başka bir sekmede sepeti değiştirebilir. Ödeme intent'i oluşturulduğu anda bu bilgiyi dondurmazsanız, PSP'ye giden tutar ile finalization'da beklenen tutar farklı iki gerçeği temsil edebilir.

Sepet (canlı, değişken)  --intent anında--> Payment Snapshot (donmuş, değişmez)
                                                    ↓
                                    PSP'ye gönderilen tutar = snapshot'taki tutar

Bu bölüm, neden “canlı sepete geri dönmenin” bir kısayol değil bir risk olduğunu ve immutable snapshot'ın bunu nasıl çözdüğünü anlatıyor.

Kavramlar ilk geçtiği yerde

📦 Payment Intent
Müşterinin ödeme yapma niyetini temsil eden, PSP'ye gönderilecek tutarı ve para birimini taşıyan kayıt.

📦 Snapshot
Bir anlık gerçeğin (fiyat, miktar, vergi, indirim) o an olduğu haliyle donmuş, sonradan değişmeyen kopyası.

📦 Price-at-Intent
Ödeme başlatıldığı anda geçerli olan fiyatın, sonraki kampanya veya fiyat değişikliklerinden bağımsız olarak korunması ilkesi.

📦 Basket Drift
Sepetin, ödeme süreci ilerlerken (kullanıcı başka bir sekmede değişiklik yaparak veya arka plan işleriyle) snapshot'tan farklılaşması.

📦 Amount Reconciliation
PSP'den gelen gerçek tahsilat tutarının, snapshot'taki beklenen tutarla karşılaştırılması.

Snapshot, sepetin bir “fotoğrafı” değildir; sepetin o anki halinin, sonradan değişemeyecek bir hukuki kayıt gibi dondurulmasıdır.

Neden canlı sepet okumak yanıltıcıdır

Bazı sistemler, ödeme intent'i oluşturulduktan sonra finalization aşamasında “güncel tutarı” almak için sepete tekrar bakar. Bu yaklaşım cazip görünür çünkü “en güncel veri” kullanıldığı hissini verir; ama gerçekte iki farklı zaman noktasındaki iki farklı gerçeği karıştırır.

t0: Müşteri ödeme başlatır  → Sepet: 3 ürün, 450 TL, kampanya aktif
t1: PSP'ye 450 TL isteği gider
t2: Kampanya süresi dolar, sepet artık 480 TL gösterir
t3: Finalization, sepete tekrar bakar → 480 TL bekler ama PSP 450 TL tahsil etmiştir

Bu uyumsuzluk, finalization saga'sının hangi tutarı “doğru” kabul edeceği konusunda karar veremeyen bir duruma düşmesine yol açar. Sonuç: manuel inceleme, müşteri şikayeti veya sessizce yanlış finans kaydı.

Snapshot'ın çözdüğü problem

Doğru yaklaşım, ödeme intent'i oluşturulduğu anda sepetin o anki halini — tutar, para birimi, satır kalemleri, vergi, indirim — ayrı ve değişmez bir kayıt olarak dondurmaktır. Bu kayıt, sepet tablosundan bağımsızdır; sepet değişse, kampanya bitse, ürün fiyatı güncellenmiş olsa bile snapshot aynı kalır.

PaymentSnapshot
  amount: 450.00
  currency: TRY
  lines: [...]
  createdAt: t0
  (sepet t1, t2, t3'te değişse de bu kayıt asla değişmez)

PSP'ye gönderilen tutar her zaman snapshot'tan okunur, canlı sepetten değil. Finalization saga'sı da stok düşme, finans kaydı, sipariş onayı adımlarında hep bu snapshot'a referans verir. Bu sayede sepetin geleceği ne olursa olsun, ödeme ile ilgili tüm kararlar aynı, sabit gerçeğe dayanır.

Tutar ve para birimi doğrulaması

Snapshot yalnızca dondurmakla kalmaz; PSP'den dönen gerçek tahsilat tutarını da doğrulamanız gerekir. PSP'nin cevabındaki tutar ile snapshot'taki tutar eşleşmiyorsa (ör. bir yuvarlama farkı, bir para birimi dönüşüm hatası veya bir entegrasyon kusuru), bu event otomatik olarak “tamamlandı” işaretlenmemeli; bir uyuşmazlık kuyruğuna düşmelidir.

PSP cevabı: amount=450.00, currency=TRY
Snapshot:   amount=450.00, currency=TRY
            → eşleşti, işleme devam

PSP cevabı: amount=449.99, currency=TRY
Snapshot:   amount=450.00, currency=TRY
            → eşleşmedi, finalization DURDURULUR, inceleme kuyruğuna düşer

Bu doğrulama adımı, hem entegrasyon hatalarını hem de olası kurcalama (tampering) senaryolarını erken yakalar.

“Canlı sepete geri dönme” cazibesi

Bazı ekipler, snapshot mekanizması kurulduktan sonra bile “ama stok değişmiş olabilir, güncel veriye bakalım” diyerek finalization sırasında sepete geri döner. Bu, snapshot'ın amacını baltalar: stok kontrolü ayrı bir adım olarak (ve kendi başarısızlık/telafi mantığıyla) ele alınmalıdır; tutar ve para birimi kararı asla canlı veriye geri dönmemelidir. İkisini birbirine karıştırmak, dondurulmuş bir hukuki kaydı yeniden tartışmaya açmak gibidir.

Bu bölümde en çok karışan eşleştirmeler

❌ Finalization'da sepete tekrar bakmak, “en güncel veri” kullanmaktır
✓ Finalization'da sepete tekrar bakmak, iki farklı zaman noktasındaki gerçeği karıştırmaktır

❌ Snapshot sadece bir loglama/audit kaydıdır
✓ Snapshot, ödeme ve finalization kararlarının tek referans kaynağıdır

❌ PSP'den gelen tutar her zaman beklenen tutarla eşleşir, doğrulama gereksizdir
✓ Tutar/para birimi uyuşmazlığı otomatik değil, kuyruğa düşen bir olay olmalıdır

❌ Stok güncelliği ile ödeme tutarı aynı mekanizmayla kontrol edilebilir
✓ Stok kontrolü ayrı bir adımdır; tutar kararı asla canlı veriye dönmemelidir

Snapshot tasarımınızı denetleme listesi

  1. PSP'ye gönderdiğiniz tutar, canlı sepetten mi yoksa donmuş bir snapshot kaydından mı okunuyor?
  2. Finalization saga'sındaki her adım, hangi kaynaktan (snapshot mı, canlı tablo mu) tutar/miktar okuyor?
  3. PSP'den dönen tutar ile snapshot'taki tutar uyuşmazsa ne oluyor? Otomatik mi geçiyor, yoksa duruyor mu?
  4. Snapshot oluşturulduktan sonra sepet tablosu değişse bile snapshot'ın değişmediğini test ettiniz mi?
  5. Snapshot'ın para birimi alanı, PSP'ye gönderilen para birimiyle her zaman aynı mı; dönüşüm varsa bu adım nerede loglanıyor?

Bu beş sorudan birine bile net cevap veremiyorsanız, sisteminizde muhtemelen “sessiz tutar kayması” riski var.

Bu bölümden aklında kalması gerekenler

  1. Ödeme intent'i oluşturulduğu anda sepetin tutarı, satırları ve para birimi ayrı ve değişmez bir snapshot olarak dondurulmalıdır.
  2. Finalization saga'sı hiçbir adımda canlı sepete geri dönmemeli; her karar snapshot'a referans vermelidir.
  3. PSP'den dönen gerçek tutar, snapshot'taki beklenen tutarla karşılaştırılmalı; uyuşmazlık otomatik geçmemeli, incelemeye düşmelidir.
  4. Stok güncelliği kontrolü ayrı bir sorumluluktur; tutar/para birimi kararıyla karıştırılmamalıdır.

Sepet geleceğe açık bir taslaktır; ödeme ise geçmişte donmuş bir karardır. Bu ikisini aynı kayıttan okumak, iki farklı zamanı tek bir gerçek gibi göstermektir.

SSS

Sık sorulan sorular

Payment Intent nedir?

Müşterinin ödeme yapma niyetini temsil eden, PSP'ye gönderilecek tutarı ve para birimini taşıyan kayıt.

Snapshot nedir?

Bir anlık gerçeğin (fiyat, miktar, vergi, indirim) o an olduğu haliyle donmuş, sonradan değişmeyen kopyası.

"Finalization'da sepete tekrar bakmak, “en güncel veri” kullanmaktır" doğru mu?

Finalization'da sepete tekrar bakmak, iki farklı zaman noktasındaki gerçeği karıştırmaktır

Bu bölüm neyi sabitler?

Bu bölüm, neden “canlı sepete geri dönmenin” bir kısayol değil bir risk olduğunu ve immutable snapshot'ın bunu nasıl çözdüğünü anlatıyor. Ödeme intent'i oluşturulduğu anda sepetin tutarı, satırları ve para birimi ayrı ve değişmez bir snapshot olarak dondurulmalıdır. Bir müşteri ödeme ekranına geldiğinde sepetteki fiyat, kampanya ve stok bilgisi hâlâ değişebilir durumdadır: kampanya süresi dolabilir, fiyat güncellenebilir, aynı müşteri başka bir sekmede sepeti değiştirebilir. Ödeme intent'i oluşturulduğu anda bu bilgiyi dondurmazsanız, PSP'ye giden tutar ile finalization'da beklenen tutar farklı iki gerçeği temsil edebilir.

Ogrenilen Muhendislik Prensipleri

  • Ödeme intent'i oluştuğu anda sepetin tutarı, satırları ve para birimi değişmez bir snapshot olarak dondurulmalıdır.
  • Finalization kararları hiçbir zaman canlı sepete geri dönmemeli; her zaman snapshot'a referans vermelidir.
  • PSP'den dönen tutar ile snapshot uyuşmazsa, bu otomatik değil, incelemeye düşen bir olay olmalıdır.

Okumaya devam et

Okumaya devam et

Seride sonraki yazi

Seride sonraki yazi

Ayni seriden

Paylaş