Adapter Değil Runtime: HSRC Pay Sandbox Network ve Provider Strategy Mimarisi
Ödeme sistemlerinde en büyük yanılgılardan biri şu: "Bir provider daha ekleriz, biter."
Gerçekte hiçbir zaman öyle olmaz.
Bir PSP yeni bir auth modeli ister. Diğeri form-urlencoded body bekler. Bir banka XML/SOAP konuşur. Bir başka sağlayıcı JSON API verir ama hata modelini HTTP status'tan değil response body içindeki özel bir alandan okutur. Bir provider 3DS akışında HTML form döner, diğeri redirect URL üretir, diğeri SDK state'i taşır. Capture davranışı farklıdır. Refund semantiği farklıdır. Void için gereken alan farklıdır. Webhook retry modeli farklıdır. Sandbox ortamı ise çoğu zaman production davranışının sadeleştirilmiş, eksik veya rastgele çalışan bir kopyasıdır.
Bu yüzden HSRC Pay'i sadece "payment gateway" ya da "provider adapter koleksiyonu" olarak düşünmek eksik olur.
HSRC Pay'in asıl iddiası başka bir yerde duruyor: ödeme davranışını programlanabilir, test edilebilir ve provider-independent bir runtime katmanına taşımak.
Biz bir ödeme formu yapmadık; ödeme davranışını programlanabilir hale getiren bir runtime inşa ettik.
Bu runtime'ın içinde payment method, provider, provider config, transaction mapping, routing plan, sandbox issuer, 3DS action, decline/error normalization, capture/refund/void lifecycle ve webhook delivery aynı modelin parçaları olarak ele alınıyor. Amaç tek bir sağlayıcıyı daha bağlamak değil; sağlayıcı karmaşasını yönetilebilir bir execution layer'a dönüştürmek.
Klasik ödeme entegrasyonu neden dağılır?
İlk provider entegrasyonu genelde hızlı görünür. Bir SDK kurulur, birkaç endpoint yazılır, ödeme alınır. İkinci provider geldiğinde abstraction ihtiyacı başlar. Üçüncü provider geldiğinde adapter pattern devreye girer. Beşinci provider'dan sonra ise adapter tek başına yetmez.
Çünkü ödeme sağlayıcıları sadece farklı endpoint'ler değildir. Her biri farklı bir davranış setidir.
- Auth modeli farklıdır: API key, HMAC signature, basic auth, body hash, timestamp signature.
- Request modeli farklıdır: JSON, form post, XML, SOAP, SDK call, redirect flow.
- Response modeli farklıdır: success alanı, status code, provider-specific error code, nested transaction object.
- Lifecycle farklıdır: auth, capture, void, refund, query payment her provider'da aynı sırayla çalışmaz.
- 3DS farklıdır: redirect, hosted form, challenge HTML, callback GET/POST, provider session token.
- Sandbox farklıdır: bazı providerlar sandbox'ta production'a benzer davranmaz, bazıları decline senaryolarını sınırlı sunar.
- Webhook davranışı farklıdır: imza, retry, event type, payload shape ve ordering garantisi değişir.
Bu noktada "her provider için bir class yazalım" yaklaşımı büyümeyi taşımamaya başlar. Çünkü class sayısı arttıkça ödeme davranışı koda gömülür. Kod büyür, test alanı dağılır, provider değiştirmek zorlaşır, merchant bazlı routing/failover yapmak maliyetli hale gelir.
HSRC Pay bu noktada adapter seviyesinde kalmak yerine runtime seviyesine çıkar.
Payment method ve provider aynı şey değildir
HSRC Pay mimarisindeki kritik ayrımlardan biri payment method ile provider'ın birbirinden ayrılmasıdır.
Payment method, müşterinin ödeme enstrümanını veya kanalını ifade eder: kredi kartı, debit card, prepaid card, banka transferi, wallet, alternative payment method gibi.
Provider ise bu ödeme akışını işleyen rail, PSP, banka, sandbox strategy veya execution backend olabilir.
Bu ayrım küçük görünür ama sistem tasarımında büyük sonuç doğurur. Çünkü aynı payment method farklı providerlar üzerinden işlenebilir. Aynı provider birden fazla network veya method destekleyebilir. Aynı merchant, aynı ödeme için farklı provider config'lerine sahip olabilir.
Runtime ödeme isteği geldiğinde şunu sorar:
Bu payment method, bu currency, bu secure mode, bu installment, bu issuer/country bilgisiyle hangi provider config'leri tarafından işlenebilir?
Bu soru klasik adapter mimarisinden daha zengindir. Burada sadece "Stripe class'ını çağır" ya da "Bank adapter'ını çalıştır" yoktur. Burada provider capability, payment method metadata, merchant config, routing rule ve runtime strategy birlikte değerlendirilir.
HSRC Pay'in provider modeli bu yüzden pratikte şu katmanlarla düşünülür:
Provider (catalog)
-> Provider method
-> Transaction mapping
-> strategy tipi: SDK | MAPPER | CONFIG
-> strategyIdentifier (kodda kayıtlı stratejiye işaret)
-> capabilities: secure mode, installment, issuer, country, currencyÖzetle: dış API'den gelen ödeme, uygun paymentProviderConfig adayıyla birleşir. Provider tarafı ödeme yöntemine, para birimine, secure mode ve auto-capture (dolayısıyla transaction tipi) gibi bağlama göre doğru transaction mapping satırını seçer. O satırdaki strategyIdentifier, runtime içinde kayıtlı stratejilerle eşleştirilir. Yani tekil bir "adapter çağır" çizgisinden çok, catalog, eşleme, strateji seçimi, charge oluşturma ve stratejinin pay / resume çağrılması.
Transaction mapping ve strategy seçimi (orkestrasyon runtime)
"Yeni sağlayıcıyı bir JSON veya YAML dosyası bırakarak sisteme ekle" gibi saf deklaratif, dosya-merkezli ekleme modeli bu metnin anlattığı gerçek değildir. Akışta provider tarafı katalog, yapılandırma ve eşleme verisiyle modellenir; dışa dönük entegrasyon taşıyıcıları provider strategy sınıflarında (tip olarak SDK / MAPPER / CONFIG) toplanır. strategyIdentifier ve ortam (sandbox / prod) hangi strateji örneğinin seçileceğini belirler. Ödeme yürütmesi PaymentOrchestrator aday listesi üzerinden, charge oluşturulduktan sonra ilgili stratejinin uygulanmasıyla ilerler.
Aşağıdakiler, okunabilirlik için yoğunlaştırılmış halka açık kavram özeti, iç veritabanı alan adı veya tam eşleme uygulaması değildir:
- Provider method ve transaction mapping, hangi işlem türü ve yetenek (secure mode, taksit, ülke / para birimi) öbeğinin söz konusu ödeme için anlamlı olduğunu taşır.
- Strateji tipi (
SDK/MAPPER/CONFIG) vestrategyIdentifiermapping satırıyla birlikte gelir. - Aday işleyici, eşleşen
strategyIdentifierve sandbox / prod ayrımı ile kayıtlı stratejiler arasında eşleşme arar; eşleşme yoksa bu noktada ilerleme, tipik hata cümlesine dönüşür. - Buradaki "runtime" vurgusu, kararın alınamaz sınıf hiyerarşilerine dağıtılmadan routing plan, charge ve strateji çağrı hattıyla verilmesidir.
Aynı temel soru cümlesi korunur:
Bu ödeme
auto_captureileTXN_PAYmi, yoksaTXN_AUTHeğrisi mi? Yöntem, ağ, secure mode ve taksit bağlamında bu adayın mapping'i buna izin veriyor mu?
Uygunluk yoksa sonraki routing adayına geçiş söz konusu olabilir. Bu, yalnızca tek uç noktaya tıkılmış bir gateway değil, planlanmış adaylar ve normalize sonuç sözleşmesi üzerinden ilerleyen payment orchestration yaklaşımıdır.
SDK, Mapper ve Config stratejileri
HSRC Pay, provider execution tarafında tek entegrasyon kalıbına kilitlenmez. Model, stratejileri EStrategyType cinsinden sınıflar: SDK, MAPPER ve CONFIG (kod sözleşmesinde tür olarak durur; tüm ekleme hikayesi buna indirgenmez).
SDK stratejisi, sağlayıcının kendi client veya özel çağrı düzeni gerektirdiği taraflar için uygundur.
MAPPER stratejisi, request / response dönüştürmesinin strateji içinde, ortak bir mapper sözleşmesiyle yürütüldüğü taraflar içindir.
CONFIG etiketi, modelde üçüncü bir sınıf olarak yer alır; fikir, tüm ağır entegrasyonun aynı şekle zorlanmamasıdır. Bu, dışarıdan "JSON'la doldurulur" anlamında deklaratif bir ekleme vaadi değildir; hangi türde stratejinin ne zaman devreye gireceği transaction mapping ve strateji kaydıyla bağlanır.
Bazı sağlayıcılar özel client ister, bazıları HTTP tabanlı mapper hattıyla, bazıları ileride CONFIG sınıfına bakan düzenlere ayrılabilir. Fark, çerçeveyi açanın runtime ve strategy dispatcher olmasıdır: destek eşleşmesi, sonra kayıtlı stratejinin uygulanması, sonra success / requires_action / declined / error gibi normalize kind cümlelerine dönüştürme.
Kontrol hâlâ merkezi modelde kalır: hangi transaction türü için hangi eşleşme, hangi strategyIdentifier ve bu ödeme / charge / webhook ömrü nasıl tek sözleşmede tutulacak.
Sandbox Network: oyuncak değil, üretim öncesi mühendislik alanı
Sandbox çoğu ödeme sisteminde "test kartı gir, success dönsün" seviyesinde kalır.
HSRC Pay sandbox'ı böyle görmüyor.
Sandbox payment network yaklaşımı, gerçek para hareketi yapmadan ödeme lifecycle disiplinini simüle etmeye odaklanır. Burada simulated issuer'lar, BIN/IIN kayıtları, test kartları, sandbox-only providerlar ve fake bank/wallet kurumları birlikte çalışır.
Bu kurumlar gerçek finansal kuruluş iddiası taşımaz. Bunlar sandbox-only simulated issuer ve provider modelidir. Amaç gerçek dünyadaki çeşitliliği production'a dokunmadan test edilebilir hale getirmektir.
Örneğin sandbox içinde farklı network'ler ve issuer profilleri modellenebilir:
- VISA / Mastercard / TROY / AMEX / Discover benzeri network ayrımları
- TR, US, EU gibi ülke/issuer context'leri
- Credit, debit, prepaid gibi method türleri
- Installment destekleyen veya desteklemeyen rails
- 3DS isteyen veya istemeyen senaryolar
- Decline, soft decline, timeout, temporary failure gibi hata davranışları
Testlerde ağa ve profillere göre ayrılmış sandbox kartı veya BIN türevleri dokümantasyon ve arayüz yönergeleri üzerinden paylaşılır. Bu blogda, tam veya bire bir kart numarası, BIN listesi veya üretimle ilişkili hassas numara ayrıntısı yok; amaç örneklerin dışa taşınmamasıdır. Tüm bu örnekler production kartı değildir; yalnızca sandbox'ta simüle davranış üretmek içindir.
Bu yaklaşımın değeri şurada: merchant sadece "ödeme başarılı oluyor mu?" sorusunu test etmez. Şunları da test eder:
- 3DS challenge gelirse checkout ne yapıyor?
- Provider decline dönerse UI nasıl davranıyor?
- İlk provider başarısız olursa routing plan sıradaki provider'a geçiyor mu?
- Refund create edildiğinde sistem lifecycle'ı doğru koruyor mu?
- Webhook delivery retry davranışı idempotent mi?
- Sandbox ve prod data boundary karışıyor mu?
Sandbox böyle tasarlandığında bir demo alanı değil, üretim öncesi mühendislik sahası olur.
Prod ve sandbox ayrımı
HSRC Pay monorepo yapısında sandbox / prod ayrımı sadece arayüzdeki bir anahtar değildir. Use-case ve repository tarafında, sandboxMode bayrağına göre ayrı veri yolları, provider strategy ortamı (sandbox / prod) ve katalog eşleşmeleri seçilir; bu ayrım uçtan uca hissedilir.
Runtime, sandboxMode context'iyle doğru repository ve doğru strategy ortamını seçer. Bu sayede sandbox ödeme, sandbox provider catalog ve sandbox data üzerinde çalışırken; production tarafı ayrı repository ve ayrı provider strategy dünyasında kalır.
Bu ayrım regülasyon açısından da kritik bir disiplin sağlar. Sandbox gerçek para hareketi yapmaz. Sandbox issuer'lar ve fake providerlar gerçek finansal kuruluş iddiası taşımaz. Ama production benzeri lifecycle kavramlarını simüle eder:
- Payment created
- Requires payment method
- Requires action
- Authorized
- Captured
- Declined
- Refunded
- Expired
- Webhook delivery
- Routing plan attempts
Bu dil önemlidir. HSRC Pay "sandbox'ta bankayız" demez. "Sandbox environment içinde ödeme davranışını simüle ediyoruz" der. Bu hem teknik olarak doğru hem de güven veren bir çerçevedir.
Routing: tek provider'a bağımlı olmayan ödeme akışı
Payment orchestration'ın kalbi routing'dir.
HSRC Pay'te routing plan, ödeme sırasında çalıştırılacak provider config candidate'larını snapshot olarak tutar. Runtime bu candidate listesini sırayla işleyebilir. Her candidate bir provider config'e, provider identifier'a, score'a, reason'a ve timeout gibi execution bilgilerine sahip olabilir.
Routing iki ana ruhu destekler:
Merchant Direct: Tek eligible config varsa doğrudan o config çalışır.
Autopilot: Birden fazla eligible provider config varsa runtime scoring ve routing rule mantığıyla candidate listesi üretir.
Bu model merchant için operasyonel bağımsızlık sağlar. Merchant tek bir provider'a mahkum kalmaz. Provider maliyeti, ödeme method support'u, installment capability, issuer/country matching ve merchant routing rule'ları birlikte değerlendirilebilir.
Bu şunu mümkün kılar:
Bu ödeme için en uygun sağlayıcı hangisi?
Ama daha önemlisi:
Bu sağlayıcı bu transaction için gerçekten uygun mu?
HSRC Pay önce capability matching yapar, sonra routing plan üretir. Bu fark önemlidir. Çünkü düşük maliyetli bir provider, o ödeme method'unu veya secure mode'u desteklemiyorsa iyi candidate değildir. Runtime önce ödeme gerçekliğini kabul eder, sonra optimizasyon yapar.
3DS, requires action ve resume akışı
3DS ödeme sistemlerinin en karmaşık alanlarından biridir. Çünkü ödeme tek request-response ile bitmeyebilir. Provider bir action isteyebilir. Kullanıcı redirect olabilir. Callback GET veya POST ile gelebilir. HTML form dönebilir. Runtime daha sonra kaldığı yerden devam etmek zorunda kalabilir.
HSRC Pay bu durumu normal bir ödeme sapması olarak değil, payment lifecycle'ın bir parçası olarak ele alır.
Strategy sonucu requires_action döndüğünde runtime bunu core sistemin next_action modeline dönüştürür. Payment ve charge state buna göre güncellenir. Callback geldiğinde resume akışı charge, payment method, payment, routing plan, used provider ve used config context'ini tekrar kurar. Sonra ilgili strategy'nin resume davranışı çalışır.
Bu sayede 3DS, UI tarafında "özel bir hack" değil, runtime'ın beklediği bir state olur.
Bu yaklaşım developer için büyük fark yaratır. Çünkü developer şunu ister:
- Tek confirmation modeli
- Tek next_action modeli
- Tek callback/resume yaklaşımı
- Tek normalized result
- Tek event/webhook davranışı
Provider ne dönerse dönsün merchant'ın görmek istediği şey budur: ödeme şimdi başarılı mı, declined mı, failed mı, yoksa kullanıcı action mı tamamlamalı?
Decline, error ve retry ayrımı
Ödeme sistemlerinde "failed" demek yeterli değildir.
Bir network timeout ile kartın yetersiz bakiye nedeniyle reddedilmesi aynı şey değildir. Bir provider 500 döndüğünde retry mantıklı olabilir. Ama kart hard decline döndüyse başka provider denemek bile anlamsız olabilir. Soft decline bazı senaryolarda tekrar denenebilir. 3DS required ise ödeme başarısız değil, action bekliyordur.
HSRC Pay runtime bu ayrımı result kind üzerinden normalize eder:
successrequires_actiondeclinederror
Bu sade görünür ama arkasında kritik bir model vardır. Provider-specific hata dünyası merchant-facing normalized modele çevrilir. Böylece developer her provider için ayrı hata parse etmek zorunda kalmaz.
Provider response'undan gelen timeout, network error, temporary failure gibi durumlar retryable error olarak ele alınabilir. Business decline, invalid card, insufficient funds gibi durumlar declined olarak normalize edilebilir.
Bu separation, ödeme kalitesini artırır. Çünkü sistem ne zaman başka candidate'a geçeceğini, ne zaman ödeme lifecycle'ını declined olarak kapatacağını ve ne zaman kullanıcı action bekleyeceğini daha net bilir.
Capture, refund, void lifecycle
HSRC Pay modelinde ödeme sadece "pay" değildir.
Bir payment lifecycle içinde auth, capture, void, refund, query payment gibi transaction tipleri ayrı anlam taşır. Auto-capture açık olduğunda başarılı ödeme captured state'e ilerleyebilir. Auto-capture kapalıysa authorized state oluşur ve capture ayrı bir operasyon haline gelir. Refund ise captured charge üzerinden yürür ve kendi status modeline sahiptir.
Bu modelin varlığı merchant için önemlidir. Çünkü gerçek ödeme operasyonu checkout ekranında bitmez. Operasyon ekibi refund ister. Finans ekibi captured/refunded amount görmek ister. Support ekibi hangi charge'ın hangi provider config ile işlendiğini anlamak ister. Developer webhook üzerinden state değişikliklerini takip etmek ister.
HSRC Pay'in charge modelinde authorized, captured ve refunded amount ayrımı bulunur. Routing plan ve provider config snapshot bilgisi charge seviyesinde korunur. Bu da operasyonel izlenebilirlik için güçlü bir temel oluşturur.
Webhook ve event delivery
Payment runtime'ın dış dünyaya konuşma şekli webhook'tur.
HSRC Pay tarafında webhook delivery ayrı bir worker ve queue mantığıyla ele alınır. Event payload'ları merchant endpoint'lerine gönderilirken idempotency, retry attempt ve delivery lifecycle gibi kavramlar önem kazanır.
Buradaki amaç sadece "bir HTTP POST atalım" değildir. Ödeme event'i merchant sistemi için finansal operasyon tetikleyebilir. Bu yüzden delivery davranışı izlenebilir, tekrar denenebilir ve mümkün olduğunca idempotent olmalıdır.
Developer açısından değer net: provider ne olursa olsun event modeli normalize edilmelidir. Merchant tarafı her provider'ın webhook payload'unu ayrı ayrı öğrenmek zorunda kalmamalıdır.
Monorepo'nun anlattığı şey
HSRC Pay monorepo yapısı da ürünün sadece bir dashboard olmadığını gösterir.
apps/backend payment API, domain resource'lar, use-case'ler, routing ve orchestration mantığını taşır.
apps/checkout-session hosted checkout deneyimi için ayrı bir yüzeydir.
apps/dashboard merchant operasyon alanını temsil eder.
apps/homepage ürünün dış yüzüdür.
apps/worker webhook/event delivery gibi async işleri taşır.
apps/vault ve packages/vault-client, hassas veri ve tokenization boundary'si için ayrı bir servis fikrini gösterir.
apps/system-dashboard, provider catalog ve system-level yönetim yüzeyine işaret eder.
apps/auth-server, auth boundary'nin ayrı düşünüldüğünü gösterir.
packages/providers-spec, paylaşılan ETxnType, transaction eşleştirme ve (örnek olarak) yöntem - transaction - capability tiplerini TypeScript sözleşmesi olarak tutan pakettir. Bu, dışa JSON / YAML bırakıp çalışan ayrı bir "tanım motoru" değil; türlerin monorepoda tekilleştirilmesidir.
packages/prisma, domain model ve lifecycle state'lerinin kalıcı şemasını taşır.
packages/jobs, async job contract'larını paylaşır.
packages/api-client ve packages/backend-api-client, developer experience tarafını destekler.
monitoring, infra, data ve diagrams gibi alanlar ise bunun tek ekranlık bir SaaS değil, altyapı odaklı bir fintech platformu olarak düşünüldüğünü gösterir.
Bu mimari dil yatırımcı için şunu söyler: HSRC Pay sadece ödeme alan bir web sitesi değil; provider karmaşasını runtime düzeyinde yöneten bir infrastructure company yaklaşımıdır.
Developer için değer
Developer'ın ödeme entegrasyonunda istediği şey sadeliktir ama bu sadelik provider gerçekliğini inkar etmemelidir.
HSRC Pay developer'a şu fikri sunar:
Tek API. Tek lifecycle. Tek normalized response/event model.
Provider farklı olabilir. 3DS farklı olabilir. Refund davranışı farklı olabilir. Ama developer'ın uygulama katmanında görmek istediği contract aynı kalmalıdır.
Bu, entegrasyon maliyetini azaltır. Çünkü developer her provider'ın özel hata kodlarını, webhook payload'unu, capture/refund varyasyonunu ve 3DS callback detaylarını ayrı ayrı uygulamak zorunda kalmaz. Runtime bu karmaşıklığı merkezi modelde karşılar.
Merchant için değer
Merchant için ödeme altyapısı satış kaybı, operasyon yükü ve provider bağımlılığı meselesidir.
Tek provider'a bağlı kalmak risklidir. Provider downtime yaşarsa checkout etkilenir. Maliyet değişirse pazarlık gücü azalır. Yeni pazara girerken farklı payment method gerekebilir. Sandbox yetersizse production'a çıkmadan önce gerçek senaryolar test edilemez.
HSRC Pay merchant'a provider bağımlılığını azaltan operasyonel bir katman sunmayı hedefler:
- Multi-provider payments
- Payment routing
- Sandbox payment network
- 3DS simulation
- Decline scenario testing
- Refund/capture lifecycle
- Webhook delivery observability
- Provider-independent payment model
Bu, merchant'ın ödeme operasyonunu tek entegrasyonun içine sıkıştırmaz. Ödemeyi yönetilebilir bir altyapı katmanına taşır.
Partner ve yatırımcı için değer
Fintech infrastructure şirketleri sadece endpoint satmaz. Model satar. Runtime satar. Operasyonel kaldıraç satar.
HSRC Pay'in stratejik tarafı burada güçlü: sağlayıcı çeşitliliği arttıkça, normalize lifecycle ve strateji modelinin değeri büyür. Her sınır, tek parça yeni bir sınıf olarak da yazılabilir; aynı zamanda transaction mapping, capability sözleşmesi ve strateji kaydı üzerinden tekrar kullanılır. Özelleşmiş uçlar SDK veya mapper stratejileriyle taşınabilir. Sandbox ağı, bunların canlı paraya girmenin önünde, kontrollü kapanış alanı sağlamayı hedefler.
Bu, ölçeklenebilir bir payment orchestration mimarisidir.
HSRC Pay'in hedefi bir sağlayıcının yerine geçmek değil; sağlayıcıların karmaşasını yönetilebilir, test edilebilir ve programlanabilir hale getirmek.
Bugünün ödeme dünyasında kazanan sadece "ödeme alan" sistem olmayacak. Kazanan, provider bağımlılığını azaltan, sandbox'ı ciddi mühendislik alanına çeviren, merchant'a routing ve lifecycle kontrolü veren, developer'a tek model sunan altyapılar olacak.
Bu yazı ne değildir?
- Bu yazı gerçek sağlayıcı credential'ları, imza, anahtar veya özel entegrasyon sırlarını açıklamaz.
- Sandbox sağlayıcılar, issuer profilleri ve ağa özgü uydurma isimler gerçek finansal kurum veya ağ sözleşmeleri anlamında okunmamalıdır; simülasyon hedeflidir.
- Sandbox ağı, gerçek para hareketi, settlement veya kart şebekesi mutabakatı üretmez; üretim yeteneği, lisans ve regülasyon konuları bu metnin kapsamı dışındadır.
- Halka açık anlatı kavramları içindir; depo şemasının tüm ayrıntıları, iç eşleme tabloları, tam kayıt listeleri ve operasyonel iç süreçler kasıtlı biçimde dışarıda bırakılmıştır.
Payment gateway değil.
Payment runtime.