Payment Orchestrator
3D Form

Kartla Ödeme — 3D Secure

3D Secure, kart çekilmeden önce alıcının işlemi kendi bankasıyla onaylamasını sağlar (SMS kodu, banka uygulaması onayı, biyometri). Sorumluluk devri (liability shift) istediğinizde, 3D politikanız gerektirdiğinde ya da banka doğrulama istediğinde bu akış çalışır. Platform işlemi uygun sağlayıcıya yönlendirir ve banka gidiş-dönüşünü sizin yerinize yürütür.

Bu sayfa Checkout Formu yolu içindir. POST /payments/form/3d ucunu Checkout Form SDK'sı alıcının tarayıcısından kendisi çağırır ve alıcıyı bankaya da kendisi yönlendirir; sizin bu uca istek yazmanız gerekmez. Yazacağınız kod, sunucunuzda intent oluşturmak, formu sayfanıza mount etmek ve sonucu karşılamaktır → Checkout Formu.

Kart verisini kendi sunucunuzdan gönderiyorsanız bu sayfa sizin için değildir → Kart — 3D Secure.

Tüm tutarlar minor unit (kuruş) cinsinden tam sayıdır: "amount": 10050 = ₺100,50. Ondalık ya da float göndermeyin.

Nasıl çalışır

Intent oluşturun (sunucunuz)

Step-Up kimlik doğrulamasıyla POST /payments/intents çağırın ve sonucun döneceği callbackUrl'i verin. Yanıttaki form token'ı yaklaşık 15 dakika geçerlidir ve formun açılacağı origin'e bağlıdır. Bütün alanlar ve 3D ayarları → Checkout Formu.

Formu mount edin (tarayıcı)

Token'ı SDK'ya verip formu sayfanıza yerleştirin. SDK açılışta oturum kanıtını alır (POST /payments/form/session-init) ve risk analizini başlatır (POST /payments/form/live-analyze). Kanıt pg_checkout_session çerezine yazılır; karta dokunan çağrılar bu kanıt olmadan reddedilir.

Alıcı kartı girer

Form kartın marka ve ailesini çözer, taksit tablosunu gösterir ve kartı izole bir iframe'de tek kullanımlık bir token'a çevirir (POST /payments/form/tokenize). Kart numarası sayfanıza hiç açılmaz. Taksit seçilirse çekim tutarı, gösterilen teklife bağlanır → Taksit & BIN.

SDK 3D Secure ödemesini başlatır

SDK POST /payments/form/3d çağırır. Ödeme AWAITING_3D durumuna geçer ve yanıtta banka yönlendirmesi döner.

Alıcı bankada doğrulanır

SDK alıcıyı GET /payments/threed/:token adresine gönderir. Bu adres kimlik doğrulaması istemez; yoldaki tek kullanımlık token kimliğin kendisidir. Alıcı bankanın 3D Secure sayfasında işlemi onaylar.

Platform çekimi yapar

Banka sonucu platforma döndürür. Platform sonucu doğrular ve yalnız doğrulama başarılıysa çekimi sağlayıcıda gerçekleştirir.

Alıcı sonuç sayfanıza döner

Alıcının tarayıcısı, intent'te verdiğiniz callbackUrl'e imzalı bir form POST'uyla gönderilir → Sonuç sayfanıza gelen imzalı POST.

Webhook ile teyit edin

payment.captured ya da payment.failed webhook endpoint'inize teslim edilir. Siparişi bu olaya ya da GET /payments/:id sorgusuna göre kapatın.

⚠️

Başarılı yönlendirme, başarılı ödeme demek değildir. 3D Secure'da iki ayrı aşama vardır: alıcının bankaya kendini doğrulaması ve paranın çekilmesi. Ödeme doğrulamayı geçip çekimde düşebilir (yetersiz bakiye, banka reddi, limit). "Alıcı bankadan döndü" diye ürün göndermeyin; payment.captured webhook'unu ya da GET /payments/:id teyidini bekleyin.

Form 3D Secure'u ne zaman kullanır

Hangi ucun çalışacağını intent'in 3D kararı belirler. Karar intent oluşturulurken force3D, settings.threeD ve panel 3D politikasından çözülür ve intent'e sabitlenir → Kartla Ödeme — 2D › 3D kararı.

Form şu durumlarda 3D Secure ile çeker:

  • Intent'in 3D kararı 3D'dir. SDK doğrudan POST /payments/form/3d çağırır.
  • Karar 2D'dir ama ödemeyi yönlendiren sağlayıcı 2D satış yapamaz. Platform çekimi 3D Secure'a yükseltir ve ödemeyi escalationReason: "PROVIDER_2D_UNSUPPORTED" ile işaretler → Kartla Ödeme — 2D › Sağlayıcı 2D yapamadığında.

İki durumda da akış bu sayfada anlatıldığı gibi ilerler.

Uç başvurusu — POST /payments/form/3d

Bu bölüm başvuru içindir: gövdeyi SDK kurar ve gönderir.

Kimlik doğrulama Authorization başlığıyla değil, gövdedeki form token'ı ve oturum kanıtıyla (pg_checkout_session çerezi ya da checkoutSessionProof) yapılır. Gövde POST /payments/form/2d ile aynıdır → alan tablosu.

Örnek gövde

SDK'nın gönderdiği gövde (secure-fields modunda, 3 taksit):

{
  "token": "<intent yanıtındaki form token'ı>",
  "installment": 3,
  "paymentCards": {
    "secureCardToken": "<tokenize yanıtındaki cardToken>"
  },
  "paymentChannel": "WEB",
  "browserFingerprint": "<parmak izi>",
  "installmentQuoteToken": "<taksit tablosundaki teklif token'ı>"
}

installmentQuoteToken taksitli ödemede gönderilir; çekilen tutar, alıcıya gösterilen teklife bağlanır.

Gövdeyi Checkout Form SDK'sı kurar. SDK, alıcıya gösterdiği toplamı da formun iç alanı olarak ekler; bu sizin göndereceğiniz bir tutar değildir. Çekilen tutarı sunucu, intent'teki amount'tan ve taksitte bağlayıcı teklifteki vade farkından hesaplar.

Yanıt

Banka gidiş-dönüşü tamamlanana kadar ödeme AWAITING_3D durumunda bekler. Yanıt status: "partial" (HTTP 202) ile döner:

{
  "status": "partial",
  "code": "AWAITING_3D",
  "message": "Payment processed",
  "data": {
    "id": "9c6f2b1a-4e8d-4a3f-b5e6-2d7c1a9b8e30",
    "orderId": "ORD-9988",
    "amount": 10350,
    "currency": "TRY",
    "status": "AWAITING_3D",
    "threeDRedirectUrl": "/api/v1/payments/threed/<tek kullanımlık token>"
  },
  "statusCode": 202,
  "requestId": "…"
}

data.amount çekilen tutardır: intent'teki amount ile sistemin taksit teklifinden eklediği vade farkının toplamı. Örnekte intent amount'u 10050, 3 taksitte çekilen tutar 10350'dir.

AlanAçıklama
data.threeDRedirectUrlAlıcının gönderileceği adres. Göreli gelebilir; o durumda gateway köküne göre çözülür. Tek kullanımlıktır.
data.threeDRedirectMethodVarsa yönlendirme yöntemi (GET ya da POST).
data.threeDRedirectDataYalnız POST yönlendirmesinde: bankaya gönderilecek form alanları.

SDK yönlendirmeyi kendisi yapar. AWAITING_3D'de terk edilen ödemeyi arka plandaki temizlik işi devralır: sağlayıcıya gerçek sonucu sorar ve ödemeyi gerçek nihai durumuna bağlar (çekim yoksa FAILED, çekim gerçekleşmişse CAPTURED).

Sonuç sayfanıza gelen imzalı POST

Banka gidiş-dönüşünden sonra alıcının tarayıcısı, intent'te verdiğiniz callbackUrl'e bir HTML form POST'uyla (application/x-www-form-urlencoded) gönderilir. Aynı POST, başarılı 2D çekimden sonra da gelir. Gövde şu alanları taşır:

AlanAnlamı
paymentIdÖdeme kimliği
orderIdIntent oluştururken verdiğiniz sipariş referansı (yalnız intent'te doluysa gönderilir)
statussuccess, failure ya da pending (sonuç henüz kesinleşmedi)
success"true" / "false"
codeMakine tarafından okunan sonuç kodu
messageİnsan tarafından okunan sonuç mesajı
_timestampİmzalama zamanı, ISO 8601
_signaturev2=<hex>: ${paymentId}.${_timestamp}.${canonical} üzerinde HMAC-SHA256, hex özet

Kanonik dizgi (canonical), _signature dışındaki bütün alanların (_timestamp dahil) anahtara göre sıralanıp key=value biçiminde yazılması ve satır sonuyla (\n) birleştirilmesiyle oluşur.

⚠️

Bu POST bilgilendiricidir; teslimat kararı için ona güvenmeyin. _signature ve _timestamp platformun ürettiği bütünlük alanlarıdır; merchant tarafında bu imzayı doğrulayacağınız bir sır dağıtılmaz. Siparişi bu POST'taki hiçbir alana dayanarak ödenmiş saymayın; nihai durumu GET /payments/:id ile ya da payment.captured webhook'uyla teyit edin.

Bu POST'u alıcının arayüzünü yönetmek için kullanın (sonuç sayfası göstermek, oturumu sürdürmek). Tarayıcı callbackUrl'inize hiç ulaşamayabilir (kapatılan sekme, bağlantı kaybı); webhook ise her durumda gönderilir.

Durum geçişleri

NeredenNereyeNe zaman
INITIATEDAWAITING_3D3D Secure ödemesi kabul edildi, yönlendirme üretildi
AWAITING_3DCAPTUREDBanka doğrulaması ve çekim başarılı
AWAITING_3DFAILEDAlıcı doğrulamayı geçemedi ya da terk etti, veya çekim reddedildi
AWAITING_3DNEEDS_RECONCILIATIONSonuç belirlenemedi (ör. bankanın dönüşü kayboldu); mutabakat işi sağlayıcıya yeniden sorup nihai durumu bağlar

Kart saklama

Formda kart, alıcı "kartımı kaydet" kutusunu işaretlediğinde ve çekim 3D Secure ile başarıyla tamamlandığında saklanır. Bunun için:

  • Kart saklama, panelinizdeki form ayarlarında açık olmalıdır.
  • Intent'i customerId (ya da cardUserKey) ile oluşturmalısınız; kart bu anahtarın altına kaydedilir.

Ödeme yanıtı alıcının tarayıcısına gittiği için kalıcı kart referansı burada dönmez. Ödeme tamamlandıktan sonra kartları sunucunuzdan GET /payments/cards ile okuyun → Kart Saklama.

Ön provizyon

Formda ön provizyonu ayrıca istemezsiniz: intent'i POST /payments/intents ile oluştururken "preauth": true gönderin. Alıcı banka doğrulamasını tamamladığında para çekilmez; ödeme CAPTURED yerine AUTHORIZED olur ve payment.authorized gönderilir.

  • Sağlayıcının ön provizyonu 3D Secure ile de desteklemesi gerekir. Uygun sağlayıcı kalmazsa ödeme FAILED olur ve ret kodu PREAUTH_3DS_NOT_SUPPORTED'dır.
  • 3D Secure ile ön provizyonu destekleyen hiçbir sağlayıcı yoksa istek 422 payment.preauth.three_ds_unsupported ile reddedilir. Talimat hiçbir koşulda anında çekime dönüştürülmez.

Capture, void ve 7 günlük süre: Ön Provizyon, Capture & İptal.

Hatalar

Hata yanıtları ortak hata zarfını kullanır; message'a göre değil code'a göre dallanın → Hata Yönetimi.

DurumKodNe yapmalı
Gövde doğrulamadan geçmedi400 VALIDATION_ERRORGüncel SDK'yı kullanın; gövdeyi SDK kurar.
Form token'ı geçersiz401 payment.checkout.session_invalidYeni intent oluşturup formu yeniden mount edin.
Form token'ının süresi doldu (yaklaşık 15 dakika)401 payment.checkout.session_expiredYeni intent oluşturup formu yeniden mount edin.
Form, intent'in bağlı olduğu origin dışında açıldı401 payment.checkout.origin_mismatchIntent'i, formu açtığınız sayfanın origin'iyle oluşturun → Checkout Formu.
Oturum kanıtı yok ya da geçersiz401 payment.checkout.proof_missingFormu SDK ile açın. Çerez taşınamayan ortamlarda kanıt checkoutSessionProof ile gövdede gönderilir.
Oturum kanıtı bu oturuma ait değil401 payment.checkout.proof_mismatchFormu yeniden açın.
Form token'ı bu ödemeye ait değil401 payment.checkout.token_mismatchFormu intent yanıtındaki token'la yeniden açın.
Tek kullanımlık kart token'ı geçersiz ya da kullanılmış401 payment.secure_card.invalid_tokenAlıcı kartı yeniden girer; SDK yeni token alır.
İşlem güvenlik kontrolünden geçemedi409 payment.intent.live_risk_blockedYeniden deneme sonucu değiştirmez.
Bu ödeme denemesi kapandı (ödeme sonuçlandı)409 payment.intent.attempt_closedYeni intent oluşturup formu yeniden açın.
Intent kart dışı bir yöntemle açılmış (ör. paymentMethod: "WALLET")409 payment.method.intent_not_cardBu intent kartla ödenemez; intent'i paymentMethod göndermeden ya da "CARD" ile oluşturun. Intent'e dokunulmadı, kendi yönteminden ödenebilir → Checkout Formu — Payment.
3D Secure ile ön provizyon desteklenmiyor422 payment.preauth.three_ds_unsupported ya da ödeme FAILED, kod PREAUTH_3DS_NOT_SUPPORTEDPara bloke edilmedi; bkz. Ön Provizyon.
Yönlendirme token'ının süresi doldu ya da kullanıldıGET /payments/threed/:token hata sayfası gösterirYeni intent oluşturup ödemeyi yeniden başlatın.
Banka doğrulaması başarısızÖdeme FAILED; imzalı POST'ta status: failure; payment.failedAlıcının yeniden ödemesi için yeni intent oluşturun.
Doğrulama başarılı, çekim reddedildiÖdeme FAILEDReddi alıcıya gösterin; teslimat yapmayın.
Taksit teklifi geçersiz ya da süresi dolduÖdeme FAILED, kod INSTALLMENT_QUOTE_REJECTEDYeni intent oluşturun; alıcı taksiti güncel tablodan yeniden seçer.
Formun alıcıya gösterdiği toplam, sunucunun hesapladığı çekim tutarıyla eşleşmiyorÖdeme FAILED, kod CLIENT_PRICE_DIVERGENCEFormun güncel SDK ile açıldığından emin olun ve yeni intent oluşturun.
Geçici altyapı hatası503 payment.dependency.unavailable, payment.credential.resolution_failed, payment.lock.unavailableGeçicidir; alıcı kısa süre sonra yeniden deneyebilir.

Webhook'lar

EventNe zaman
payment.capturedDoğrulama ve çekim tamamlandı, para çekildi
payment.failedDoğrulama ya da çekim başarısız (çekimsiz sonuçlanan terk edilmiş 3D Secure oturumları dahil)
payment.authorizedÖn provizyonlu intent'te doğrulama tamamlandı ve tutar bloke edildi (AUTHORIZED); para henüz çekilmedi

Teslimat ve imza doğrulaması → Webhook'lar.

Sonraki adımlar