Kart Ödemelerini Kabul Etme
Barındırılan (hosted) checkout oturumları, doğrudan API ile kart tahsilatı ve uçtan uca 3DS 2.0 doğrulama akışı.
Kvell ile kart ödemesi kabul etmenin iki yolu vardır: tüm kart formunu Kvell'in barındırılan checkout'una devretmek, ya da kartı kendiniz toplayıp sunucunuzdan bir token'ı tahsil etmek. İkisi de aynı yerde biter — bir payment.captured webhook'u — bu yüzden akışın ne kadarının sahibi olmak istediğinize göre seçin.
Bir entegrasyon yolu seçin
| Barındırılan checkout oturumu | Doğrudan API | |
|---|---|---|
| Kart formu | Kvell tarafından barındırılır | Siz oluşturursunuz (veya bir SDK'nın Elements benzeri bileşenini kullanırsınız) |
| 3DS 2.0 doğrulaması | Tamamen Kvell tarafından yönetilir | Alıcıyı yönlendirir ve geri dönüşü siz işlersiniz |
| Taksit seçici | installmentEnabled: true olduğunda otomatik olarak gösterilir | Taksit API'sini siz çağırır, seçiciyi siz oluşturursunuz |
| PCI kapsamı | Minimal — kart numarası hiçbir zaman ön yüzünüze veya sunucunuza ulaşmaz | Ham kart verisinin sunucunuza dokunmasına izin vermemelisiniz; tarayıcıda tokenize edin |
| En uygun olduğu durum | En hızlı entegrasyon, standart checkout akışları | Özel checkout arayüzleri, native mobil, kayıtlı kart akışları |
Tutarlar her iki yolda da her zaman kuruş cinsinden (1 TL'nin 1/100'ü) tam sayıdır — 15000 demek 150,00 TL demektir. Para birimi her zaman "TRY" olur.
Yol A: Barındırılan checkout oturumu
1. Bir oturum oluşturun
curl -X POST https://api.pay.kvell.group/v1/payments/sessions \
-H "X-Api-Key: pk_live_a1b2c3d4e5f6g7h8" \
-H "X-Timestamp: 2026-04-13T10:00:00Z" \
-H "X-Signature: <hesaplanan imza>" \
-H "Content-Type: application/json" \
-d '{
"transactionId": "ORDER-2026-001",
"amount": 15000,
"currency": "TRY",
"description": "Order #1234",
"returnUrl": "https://merchant.example.com/return"
}'transactionId sizin kendi sipariş id'nizdir ve aynı zamanda idempotency güvencesi görevi görür — üye işyeri başına benzersizdir, dolayısıyla aynı siparişi aynı id ile tekrar denemek her zaman güvenlidir. Bu endpoint'te bilinmesi gereken birkaç alan daha:
| Alan | Açıklama |
|---|---|
installmentEnabled | Barındırılan sayfada taksit seçiciyi göster — bkz. Taksitli Ödemeler |
customerId | Oturumu kayıtlı bir müşteriye bağlar, kart kaydetme onay kutusunu etkinleştirir |
callbackUrl | Yalnızca bu ödeme için mağazanızın varsayılan webhook URL'sini geçersiz kılar |
mode | "payment" (varsayılan), "card_update_only" veya kart formu yerine bir TR QR ödemesi için "qr" |
Yanıt, barındırılan sayfanızdır:
{
"sessionId": "3f1c9a2e-6b4d-4e8a-9c1f-2a7b8c9d0e1f",
"checkoutUrl": "https://pay.kvell.group/s/3f1c9a2e-6b4d-4e8a-9c1f-2a7b8c9d0e1f",
"expiresAt": "2026-04-13T10:30:00Z",
"merchantId": "mrc_8a1b2c3d4e5f",
"amount": 15000,
"currency": "TRY",
"status": "active"
}2. Alıcıyı yönlendirin
Alıcının tarayıcısını checkoutUrl'e gönderin. Oturum, oluşturulduktan 30 dakika sonra sona erer (expiresAt) — alıcı checkout'u bundan daha uzun sürede tamamlarsa yenisini oluşturun.
3. Kvell kart formunu ve 3DS'i yönetir
Barındırılan sayfa kartı toplar, ihraççı gerektirdiğinde 3DS 2.0 doğrulamasını çalıştırır ve etkinleştirdiyseniz taksit seçiciyi gösterir. Bunların hiçbirini siz uygulamazsınız — bu yolun tüm amacı budur.
4. Alıcı returnUrl'inize geri döner
Kvell, işlemi başarılı ya da başarısız olsun, alıcıyı bitirdiğinde geri yönlendirir. Bu yönlendirmeye dayanarak asla bir siparişi ödendi olarak işaretlemeyin — alıcı, yönlendirme tetiklenmeden sekmeyi kapatabilir ya da işlemi hiç tamamlamadan manuel olarak geri gidebilir. Yönlendirmeyi bir "işleniyor" ekranı göstermek için bir ipucu olarak ele alın ve gerçek sonucu payment.captured webhook olayından doğrulayın (bkz. Webhook'lar).
5. Durumu doğrulayın
Backend gidiş-dönüşü olmadan alıcıya yönelik bir durum sayfası için, herkese açık durum endpoint'ini yoklayın — ödeme id'sinin kendisi yetki kanıtı olduğundan imza veya API anahtarı gerektirmez:
curl https://api.pay.kvell.group/v1/payments/pay_1a2b3c4d5e6f/public-status{
"paymentId": "pay_1a2b3c4d5e6f",
"status": "captured"
}Bu, alıcının tarayıcısından doğrudan çağırmak için güvenli olacak şekilde bilinçli olarak dardır — tutar yok, kart bilgisi yok, üye işyeri id'si yok. Sunucunuzdan tam kaydı almak için imzalı GET /v1/payments/{id} endpoint'ini kullanın.
Yol B: Doğrudan API entegrasyonu
1. Kartı tarayıcıda tokenize edin
Kart alanlarını kendi formunuzda toplayın ve doğrudan tarayıcıdan, yalnızca genel anahtarınızı kullanarak Kvell'e gönderin — ham kart numarası hiçbir zaman sunucunuza dokunmaz:
curl -X POST https://api.pay.kvell.group/v1/cards/tokens \
-H "X-Api-Key: pk_live_a1b2c3d4e5f6g7h8" \
-H "Content-Type: application/json" \
-d '{
"pan": "4155650000000007",
"expMonth": 12,
"expYear": 2028,
"cvv": "123"
}'{
"token": "tok_9f8e7d6c5b4a",
"last4": "0007",
"bin": "415565",
"brand": "visa"
}Token, tek bir mağazaya bağlıdır ve kendi sunucunuza vermek için güvenlidir — mağazanızın bağlamı dışında işe yaramaz.
2. Token'ı sunucunuzdan tahsil edin
Bu çağrı, para hareketi yapan bir sunucudan-sunucuya çağrı olduğu için gizli anahtarınızla imzalanır:
curl -X POST https://api.pay.kvell.group/v1/payments/card \
-H "X-Api-Key: pk_live_a1b2c3d4e5f6g7h8" \
-H "X-Timestamp: 2026-04-13T10:00:00Z" \
-H "X-Signature: <hesaplanan imza>" \
-H "Content-Type: application/json" \
-d '{
"transactionId": "ORDER-2026-001",
"amount": 15000,
"currency": "TRY",
"cardToken": "tok_9f8e7d6c5b4a",
"installmentCount": 3
}'Bilinmesi gereken birkaç parametre:
| Alan | Varsayılan | Notlar |
|---|---|---|
installmentCount | 1 | 1–12, kartın BIN'i ve tutarı için uygun planlara karşı sunucu tarafında yeniden doğrulanır |
threeDSecure | true | 3DS doğrulamasını zorunlu kılar; özel bir nedeniniz olmadıkça açık bırakın |
capture | true | Anında tahsilat yerine bir ön yetkilendirme (preauth) yerleştirmek için false yapın — aşağıya bakın |
saveCard | false | Başarılı bir tahsilattan sonra kartı customerId'ye ekler — yalnızca tahsilat, zaten bağlı bir customerId'si olan bir oturuma bağlıysa geçerlidir |
Kart bir 3DS doğrulamasına ihtiyaç duymuyorsa (aşağıya bakın), sonucu hemen alırsınız:
{
"paymentId": "pay_1a2b3c4d5e6f",
"status": "captured",
"amount": 15000,
"currency": "TRY",
"commission": 449,
"netAmount": 14551,
"cardMask": "415565******4321",
"cardBrand": "visa",
"cardType": "credit",
"installmentCount": 3,
"threeDSecure": true,
"authCode": "123456",
"createdAt": "2026-04-13T10:00:00Z",
"completedAt": "2026-04-13T10:00:02Z"
}3. 3DS 2.0 doğrulamasını işleyin
İhraççı bir doğrulama gerektiriyorsa yanıt farklı görünür — status alanına göre dallanın:
{
"status": "three_d_redirect",
"threeDUrl": "https://acs.example.com/challenge/c0rr-3f1c-9a2e-6b4d",
"correlationId": "c0rr-3f1c-9a2e-6b4d"
}Alıcının tarayıcısını threeDUrl'e yönlendirin. Doğrulamayı tamamladıktan sonra ACS, sorgu dizesinde bir pares doğrulaması ve aynı correlationId ile onu geri yönlendirir — tahsilatı tamamlamak için ikisini de gönderin:
curl -X POST https://api.pay.kvell.group/v1/payments/pay_1a2b3c4d5e6f/3ds-callback \
-H "X-Api-Key: pk_live_a1b2c3d4e5f6g7h8" \
-H "X-Timestamp: 2026-04-13T10:05:00Z" \
-H "X-Signature: <hesaplanan imza>" \
-H "Content-Type: application/json" \
-d '{
"pares": "eyJjaGFsbGVuZ2VJZCI6Ii4uLiJ9.9a8b7c6d",
"correlationId": "c0rr-3f1c-9a2e-6b4d"
}'Bu çağrı idempotent'tir — alıcının tarayıcısı geri dönüşü iki kez gönderirse, ikinci çağrı ikinci bir tahsilat denemesi yerine aynı nihai sonucu döndürür.
150 TL'nin (15000 kuruş) altındaki işlemler, Türkiye'deki düzenleyici taban gereği 3DS doğrulamasını tamamen atlayabilir. Her tahsilatın bir yönlendirme tetikleyeceğini varsaymak yerine, her zaman tahsilat yanıtındaki
statusalanına göre dallanın.
4. Ödeme durumunu okuyun
| Durum | Anlamı |
|---|---|
new | Az önce oluşturuldu, henüz bankaya bir çağrı yapılmadı |
authorized | Banka bir ön yetkilendirmeyi onayladı (capture: false); ledger'a hiçbir kayıt düşülmedi, tutar tutuluyor ama hareket etmedi |
captured | Para hareket etti — nihai başarı durumu |
failed | Banka tarafından reddedildi veya 3DS doğrulaması başarısız oldu/süresi doldu |
refunded | Tamamen iade edildi (aşağıya bakın) |
Ön yetkilendirme ve tahsilat
Para hareket ettirmeden bir tutma (hold) yerleştirmek için tahsilat sırasında capture: false gönderin — "sipariş anında yetkilendir, kargoya verme anında tahsil et" gibi akışlar için kullanışlıdır. Ödeme status: "authorized" durumunda başlar.
Tutarı almaya hazır olduğunuzda:
curl -X POST https://api.pay.kvell.group/v1/payments/pay_1a2b3c4d5e6f/capture \
-H "X-Api-Key: pk_live_a1b2c3d4e5f6g7h8" \
-H "X-Timestamp: 2026-04-13T10:05:00Z" \
-H "X-Signature: <hesaplanan imza>"Tahsilat yalnızca tam tutar üzerinden yapılır (kısmi tahsilat henüz yok) ve zaten tahsil edilmiş bir kayıt için idempotent'tir. Tutarı hiç almayacaksanız, bunun yerine tutmayı serbest bırakın:
curl -X POST https://api.pay.kvell.group/v1/payments/pay_1a2b3c4d5e6f/void \
-H "X-Api-Key: pk_live_a1b2c3d4e5f6g7h8" \
-H "X-Timestamp: 2026-04-13T10:05:00Z" \
-H "X-Signature: <hesaplanan imza>" \
-H "Content-Type: application/json" \
-d '{"reason": "Order abandoned"}'Bir void hiçbir parayı hareket ettirmez — ledger'ınıza hiçbir şey düşülmez ve alıcının ihraççısı kendi tarafındaki tutmayı serbest bırakır.
İadeler
Tahsil edilmiş bir ödemeyi tamamen veya kısmen iade edin:
curl -X POST https://api.pay.kvell.group/v1/payments/pay_1a2b3c4d5e6f/refund \
-H "X-Api-Key: pk_live_a1b2c3d4e5f6g7h8" \
-H "X-Timestamp: 2026-04-13T14:00:00Z" \
-H "X-Signature: <hesaplanan imza>" \
-H "Content-Type: application/json" \
-d '{"amount": 5000, "reason": "Customer request"}'Tam iade için amount alanını atlayın; kısmi iade için (GET /v1/payments/{id}'den gelen refundableAmount ile sınırlı olacak şekilde) ekleyin.
{
"paymentId": "pay_1a2b3c4d5e6f",
"status": "captured",
"refundAmount": 5000,
"refundableAmount": 10000,
"lastRefund": {
"refundId": "rfn_7c8d9e0f1a2b",
"status": "completed",
"amount": 5000,
"bankRefundId": "rfn_mock_44182",
"createdAt": "2026-04-13T14:00:00Z",
"completedAt": "2026-04-13T14:00:01Z"
}
}İade senkron olarak tamamlandığında 200, hâlâ işlemdeyken (banka bacağı asenkron olarak bitiyorken) 202 alırsınız. 202 durumunda yeni bir Idempotency-Key üretip yeniden denemeyin — lastRefund.status alanı completed olana kadar GET /v1/payments/{id}'yi yoklayın ya da payment.refunded webhook'unu bekleyin.
Red ve hataları işleme
Bir banka reddi veya iş kuralı reddi, standart hata zarfını (envelope) döndürür:
{
"code": "KVELL-PAY-4001",
"message": "Payment declined by issuing bank.",
"requestId": "7b9e5c21-4a3f-4e8a-b9d2-1f4a8c9e5b10"
}4xxx aralığındaki kodlar entegrasyonunuzdaki bir hata değil, iş kuralı sonuçlarıdır — gerçek bir ret, uygun olmayan bir taksit sayısı (bkz. Taksitli Ödemeler) veya eskimiş bir 3DS oturumu hepsi buraya düşer. Bir alıcı sonucu itiraz ederse destekle ilişkilendirebilmek için requestId'yi siparişle birlikte loglayın.
Reddedilen veya iptal edilen (void) bir ödeme, ledger'ınıza hiçbir şey düşürmez — sizin tarafınızda sallantıda kalan kısmi bir tahsilat ya da bekleyen bir tutma olmaz.
Sonraki adımlar
| Rehber | Neyi kapsar |
|---|---|
| Taksitli Ödemeler | BIN sorgulama, seçici ve komisyon hesabı |
| Webhook'lar | Yönlendirmelere güvenmek yerine payment.captured'ı güvenilir şekilde doğrulama |
| Sandbox Ortamında Test Etme | X-Sim-Scenario ile belirli sonuçları zorlama — retler, 3DS doğrulamaları, zaman aşımları |
| API Referansı — Payments | Her payments endpoint'indeki her alan |