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.
| Alan | Açıklama |
|---|---|
data.threeDRedirectUrl | Alı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.threeDRedirectMethod | Varsa yönlendirme yöntemi (GET ya da POST). |
data.threeDRedirectData | Yalnı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:
| Alan | Anlamı |
|---|---|
paymentId | Ödeme kimliği |
orderId | Intent oluştururken verdiğiniz sipariş referansı (yalnız intent'te doluysa gönderilir) |
status | success, failure ya da pending (sonuç henüz kesinleşmedi) |
success | "true" / "false" |
code | Makine tarafından okunan sonuç kodu |
message | İnsan tarafından okunan sonuç mesajı |
_timestamp | İmzalama zamanı, ISO 8601 |
_signature | v2=<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
| Nereden | Nereye | Ne zaman |
|---|---|---|
INITIATED | AWAITING_3D | 3D Secure ödemesi kabul edildi, yönlendirme üretildi |
AWAITING_3D | CAPTURED | Banka doğrulaması ve çekim başarılı |
AWAITING_3D | FAILED | Alıcı doğrulamayı geçemedi ya da terk etti, veya çekim reddedildi |
AWAITING_3D | NEEDS_RECONCILIATION | Sonuç 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 dacardUserKey) 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
FAILEDolur ve ret koduPREAUTH_3DS_NOT_SUPPORTED'dır. - 3D Secure ile ön provizyonu destekleyen hiçbir sağlayıcı yoksa istek
422 payment.preauth.three_ds_unsupportedile 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.
| Durum | Kod | Ne yapmalı |
|---|---|---|
| Gövde doğrulamadan geçmedi | 400 VALIDATION_ERROR | Güncel SDK'yı kullanın; gövdeyi SDK kurar. |
| Form token'ı geçersiz | 401 payment.checkout.session_invalid | Yeni intent oluşturup formu yeniden mount edin. |
| Form token'ının süresi doldu (yaklaşık 15 dakika) | 401 payment.checkout.session_expired | Yeni intent oluşturup formu yeniden mount edin. |
| Form, intent'in bağlı olduğu origin dışında açıldı | 401 payment.checkout.origin_mismatch | Intent'i, formu açtığınız sayfanın origin'iyle oluşturun → Checkout Formu. |
| Oturum kanıtı yok ya da geçersiz | 401 payment.checkout.proof_missing | Formu SDK ile açın. Çerez taşınamayan ortamlarda kanıt checkoutSessionProof ile gövdede gönderilir. |
| Oturum kanıtı bu oturuma ait değil | 401 payment.checkout.proof_mismatch | Formu yeniden açın. |
| Form token'ı bu ödemeye ait değil | 401 payment.checkout.token_mismatch | Formu 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_token | Alıcı kartı yeniden girer; SDK yeni token alır. |
| İşlem güvenlik kontrolünden geçemedi | 409 payment.intent.live_risk_blocked | Yeniden deneme sonucu değiştirmez. |
| Bu ödeme denemesi kapandı (ödeme sonuçlandı) | 409 payment.intent.attempt_closed | Yeni intent oluşturup formu yeniden açın. |
Intent kart dışı bir yöntemle açılmış (ör. paymentMethod: "WALLET") | 409 payment.method.intent_not_card | Bu 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 desteklenmiyor | 422 payment.preauth.three_ds_unsupported ya da ödeme FAILED, kod PREAUTH_3DS_NOT_SUPPORTED | Para bloke edilmedi; bkz. Ön Provizyon. |
| Yönlendirme token'ının süresi doldu ya da kullanıldı | GET /payments/threed/:token hata sayfası gösterir | Yeni intent oluşturup ödemeyi yeniden başlatın. |
| Banka doğrulaması başarısız | Ödeme FAILED; imzalı POST'ta status: failure; payment.failed | Alıcının yeniden ödemesi için yeni intent oluşturun. |
| Doğrulama başarılı, çekim reddedildi | Ödeme FAILED | Reddi alıcıya gösterin; teslimat yapmayın. |
| Taksit teklifi geçersiz ya da süresi doldu | Ödeme FAILED, kod INSTALLMENT_QUOTE_REJECTED | Yeni 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_DIVERGENCE | Formun 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.unavailable | Geçicidir; alıcı kısa süre sonra yeniden deneyebilir. |
Webhook'lar
| Event | Ne zaman |
|---|---|
payment.captured | Doğrulama ve çekim tamamlandı, para çekildi |
payment.failed | Doğ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
- Kartla Ödeme — 2D: banka doğrulaması olmadan çekim ve 3D kararının kaynakları
- Checkout Formu: intent oluşturma,
callbackUrl,force3Dvesettings.threeD - Kart Saklama: kayıtlı kartları okuma ve formda gösterme
- Taksit & BIN: gösterilen tutar, çekilen tutardır
- İade: çekim sonrası para iadesi