vellTRDocs

Payments

Accept card payments and TR QR. Create checkout sessions, charge tokenized cards (3DS 2.0), preauth/capture/void, and refund.

8 endpoints
POST/v1/payments/sessionsCore

Create a hosted-checkout session. The session holds the amount, description, and redirect URLs; the buyer completes the card (or TR QR) flow on the Kvell hosted page. Set mode="qr" to mint a BKM/TCMB TR QR payment instead of a card checkout.

Request Parameters

NameTypeRequiredDescription
transactionIdstringRequiredUnique merchant transaction id (idempotency fence, unique per merchant)
amountintegerOptionalAmount in kuruş (1/100 TRY). >=100 for mode="payment"/"qr"; omit for mode="card_update_only"
currencystringRequiredISO 4217, always "TRY" in Phase 1
descriptionstringOptionalTransaction description shown on the checkout page
returnUrlstringRequiredRedirect URL after the buyer finishes (success or failure)
callbackUrlstringOptionalOverride the store default webhook URL for this payment
installmentEnabledbooleanOptionalShow the installment picker on the hosted page
customerIdstringOptionalBind the session to a saved customer (enables save-card)
modestringOptional"payment" (default) | "card_update_only" | "qr"
splitsarrayOptionalMarketplace split config [{submerchantId, amountMinor}] — payment mode only
JSONRequest Example
{
  "transactionId": "ORDER-2026-001",
  "amount": 15000,
  "currency": "TRY",
  "description": "Order #1234",
  "returnUrl": "https://merchant.example.com/return"
}
JSONResponse Example
{
  "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"
}
POST/v1/payments/cardCore

Charge a card using a vault token (never a raw PAN — tokenize first via POST /v1/cards/tokens). Handles the 3DS 2.0 challenge dance. Set capture=false for a preauthorization (Slice 9): the row lands at status="authorized" with no ledger entries until a follow-up /capture or /void.

Request Parameters

NameTypeRequiredDescription
transactionIdstringRequiredSession transaction id (idempotency fence)
amountintegerRequiredAmount in kuruş
currencystringRequiredAlways "TRY"
cardTokenstringRequiredVault token (tok_…) from POST /v1/cards/tokens
installmentCountintegerOptionalInstallment count 1..12 (re-checked against installment-service)
threeDSecurebooleanOptionalForce 3DS (default true)
customerIdstringOptionalSave-card is honored only for a session-bound customer
saveCardbooleanOptionalAttach the card to the customer after capture
capturebooleanOptionalAuto-capture (default true); false = preauth
JSONRequest Example
{
  "transactionId": "ORDER-2026-001",
  "amount": 15000,
  "currency": "TRY",
  "cardToken": "tok_9f8e7d6c5b4a",
  "installmentCount": 3
}
JSONResponse Example
{
  "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"
}
POST/v1/payments/{id}/3ds-callbackOptional

Complete a 3DS challenge. The hosted checkout posts the ACS pares + the correlationId returned from the card charge. Idempotent — a duplicate submit returns the winner's row byte-for-byte.

Request Parameters

NameTypeRequiredDescription
paresstringRequiredSigned ACS assertion from the 3DS challenge
correlationIdstringRequiredValue round-tripped from the card charge response
JSONRequest Example
{
  "pares": "eyJjaGFsbGVuZ2VJZCI6Ii4uLiJ9.9a8b7c6d",
  "correlationId": "c0rr-3f1c-9a2e-6b4d"
}
JSONResponse Example
{
  "id": "pay_1a2b3c4d5e6f",
  "status": "captured",
  "amount": 15000,
  "currency": "TRY",
  "commission": 449,
  "netAmount": 14551,
  "authCode": "123456",
  "completedAt": "2026-04-13T10:00:05Z"
}
POST/v1/payments/{id}/captureRecommended

Capture a preauthorized payment (full amount only — partial/multi-capture is Phase 2). Idempotent against an already-captured row.

JSONRequest Example
{}
JSONResponse Example
{
  "id": "pay_1a2b3c4d5e6f",
  "status": "captured",
  "amount": 15000,
  "currency": "TRY",
  "commission": 449,
  "netAmount": 14551,
  "refundableAmount": 15000,
  "completedAt": "2026-04-13T10:05:00Z"
}
POST/v1/payments/{id}/voidRecommended

Release an uncaptured authorization. No ledger entries are posted (no money moved on Kvell's books). Idempotent against an already-voided row.

Request Parameters

NameTypeRequiredDescription
reasonstringOptionalFree text (≤500 chars), surfaced on the payment.voided event
JSONRequest Example
{
  "reason": "Order abandoned"
}
JSONResponse Example
{
  "id": "pay_1a2b3c4d5e6f",
  "status": "voided",
  "amount": 15000,
  "currency": "TRY"
}
POST/v1/payments/{id}/refundCore

Refund a captured payment fully or partially. 200 when the refund settled synchronously; 202 when it is in-flight (the refund-reconciler completes it — do not mint a fresh Idempotency-Key on 202). Supports an optional Idempotency-Key header.

Request Parameters

NameTypeRequiredDescription
amountintegerOptionalPartial refund amount in kuruş (<= refundableAmount). Omit for a full refund.
reasonstringOptionalRefund reason (truncated at 500 chars)
JSONRequest Example
{
  "amount": 5000,
  "reason": "Customer request"
}
JSONResponse Example
{
  "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"
  }
}
GET/v1/payments/{id}Core

Retrieve the full payment object.

JSONResponse Example
{
  "id": "pay_1a2b3c4d5e6f",
  "merchantId": "mrc_8a1b2c3d4e5f",
  "transactionId": "ORDER-2026-001",
  "status": "captured",
  "amount": 15000,
  "currency": "TRY",
  "commission": 449,
  "netAmount": 14551,
  "cardMask": "415565******4321",
  "cardBrand": "visa",
  "cardType": "credit",
  "bin": "415565",
  "installmentCount": 3,
  "threeDSecure": true,
  "refundAmount": 0,
  "refundableAmount": 15000,
  "createdAt": "2026-04-13T10:00:00Z",
  "completedAt": "2026-04-13T10:00:02Z"
}
GET/v1/payments/{id}/public-statusRecommended

Buyer-safe status poll — no auth (the paymentId UUID is the bearer). Used by the QR buyer page and the card-flow success poll. Returns only a narrow field set (never PAN, fee, merchant id, or auth code).

JSONResponse Example
{
  "paymentId": "pay_1a2b3c4d5e6f",
  "status": "captured"
}

Category Summary

Total Endpoints

8

Base Domain

api.pay.kvell.group