İçeriğe geç
İçeriğe geç

GELİŞTİRİCİLER

API Tahsilat Dokümanı

Ödeme oluşturun, müşterinizi ödeme sayfasına yönlendirin ve sonucu webhook ya da sorgu ile alın.

  • Temel URLhttps://www.cryptoavans.com/api/v3
  • API sürümü3.0.0

Başlarken

Ortam

Gerçek para hareketi olmadan test edin.

Gerçek ödemeler. Canlı anahtar gerekir.

Başlarken

Yeni bir hesaptan ilk canlı ödemeye dört adımda.

  1. Anahtarlarınızı alın

    “Kayıt” ile hesap açın ya da giriş yapın, “API anahtarı ve entegrasyon” sayfasını açıp bir sandbox anahtarı üretin. Önce sunucu IP’nizi ve webhook adresinizi ekleyin. Ayrıntı: Anahtar alma.

  2. Ödeme oluşturun

    İmzalı bir POST /api/v3/payments gönderin (örnek kod var) ve müşteriyi dönen payment_url adresine yönlendirin. Alanlar ve kurallar Ödemeler bölümünde.

  3. Test edin ve sonucu alın

    Test kartıyla bir test ödemesi tamamlayın ve “Sandbox API siparişleri” sayfasından izleyin. Webhook adresinize imzalı bir payment.paid gelir (Webhook’lar); GET /api/v3/payments/{code} ile de okuyabilirsiniz. Siparişi yalnızca durum paid olduğunda tamamlayın.

  4. Canlıya geçin

    İlk sandbox siparişinizden sonra canlı anahtarı üretin, canlı IP listesini ve webhook adresini ekleyin, canlıya geçiş kontrol listesini tamamlayın.

Genel kurallar

Temel URL
https://www.cryptoavans.com/api/v3
Biçim
application/json · UTF-8
Tutarlar
Ondalık dizgiler: itibari para tutarları 2 ondalıklıdır (ör. "250.00"). usdt_amount 6 ondalıklıdır.
Para birimleri
USD EUR AED TRY
Zaman damgaları
UTC · ISO 8601
Hata dili
Accept-Language başlığıyla seçilir; varsayılan Türkçedir.

Bakiye, iade, çekim, itiraz ve webhook teslimatları Hesap işlemleri sayfasında.

Kartı kendi ödeme formunuzda toplamak istiyorsanız Direct API (v4) sayfasına bakın.

Anahtar alma

Sandbox ve canlı ortamın anahtarları, IP listeleri ve webhook adresleri ayrıdır. İki anahtar aynı anda etkin olabilir.

  1. “API anahtarı ve entegrasyon” sayfasını açın.

  2. Üstteki 6 haneli hesap numaranızı alın. İmzalı isteklerde X-CAV-Account-No başlığıyla gönderin.

  3. “Sandbox” kartındaki “IP izin listesi” alanına sunucunuzun genel IP adresini girip “IP ekle” düğmesine basın. IPv4 veya IPv6 adreslerini tek tek ekleyin. CIDR aralığı kabul edilmez.

  4. “HTTPS webhook adresi” alanına herkese açık bir HTTPS adresi yazın. Bu zorunlu alan, anahtar üretilirken kaydedilir. Daha sonra “Webhook kaydet” ile anahtarı yenilemeden adresi değiştirebilirsiniz.

  5. “Anahtar üret” düğmesine basın. API anahtarını ve gizli anahtarı kaydedin, “Gizli anahtarı güvenle kaydettim.” kutusunu işaretleyin, ardından “Kaydettim, kapat” düğmesine basın.

Canlı kartı, en az bir sandbox siparişi oluşturduktan sonra açılır. Ödemenin tamamlanması gerekmez.

  1. Sandbox anahtarınızla en az bir sipariş oluşturun (POST /api/v3/payments). Panelde Canlı kartı açılır.

  2. Canlı kartında sunucularınızın üretim IP adreslerini “IP izin listesi” alanına ekleyin. Liste sandbox listesinden ayrıdır. Boşken tüm canlı istekler reddedilir.

  3. “HTTPS webhook adresi” alanına canlı HTTPS webhook adresinizi yazın. Adres bir sonraki adımda anahtarla birlikte kaydedilir.

  4. “Anahtar üret” düğmesine basın ve canlı gizli anahtarı sunucunuza kaydedin.

  5. Entegrasyona canlı API anahtarını ve gizli anahtarı tanımlayın. API taban adresi ve istek biçimi aynıdır.

IP izin listesi

İstekler yalnızca izin listenizdeki IP adreslerinden kabul edilir. Başka bir adresten gelen istek 403 (9000012) ile reddedilir ve hiçbir işlem yapılmaz.

  • Liste zorunludur. Sandbox ve canlı ayrı liste tutar; ortamın listesi boşken anahtar üretilemez.
  • Yalnızca tek IPv4 ya da IPv6 adresi girilir; aralık (CIDR) yoktur. Sunucunuz hem IPv4 hem IPv6 ile çıkıyorsa ikisini de ekleyin.
  • Geçerli olan, sunucunuzun internete çıkarken kullandığı genel adrestir. Yerel adresler (10.x, 192.168.x) işe yaramaz; NAT ya da proxy arkasındaysanız onun çıkış adresini ekleyin.
  • Adresin yanındaki “IP kaldır” düğmesine basıp onaylayın. Etkin anahtarın son IP’sini kaldırmak için önce başka adres ekleyin veya anahtarı iptal edin.
  • Değişiklikler hemen geçerli olur. Anahtarı yenilemeniz gerekmez.

Test yaptığınız her adresi ekleyin; geliştirme sunucusu ya da ofisinizin çıkış adresi gibi.

Üretim sunucularının adreslerini canlı listeye ekleyin. Sandbox listesi canlı isteklere izin vermez.

Anahtarı yenileme ve iptal

Gizli anahtarı kaybederseniz “Anahtarı yenile” düğmesini kullanın. O ortamın eski anahtarı hemen iptal edilir. Diğer ortamın anahtarı korunur.

  • Her ortamda tek bir etkin anahtar bulunur. “Anahtarı yenile” yeni bir çift üretir ve eskisini hemen iptal eder. Eski anahtarla imzalanan istekler 9000003 ile reddedilir.
  • “Anahtarı iptal et”, yeni anahtar üretmeden anahtarı devre dışı bırakır. İstekler hemen reddedilir.
  • İptal edilen anahtarın gizli anahtarı, onunla oluşturulmuş ödemelerin webhook’larını imzalamaya devam eder. İlgili gizli anahtarı bu ödemeler için güvenle saklayın. Kaybolur veya sızarsa durumlarını güncel anahtarınızla imzalanmış GET istekleriyle doğrulayın; sızan gizli anahtarla imzalanmış webhook’lara güvenmeyin.

Kimlik doğrulama

Kimlik doğrulaması gereken istekleri gizli anahtarınızla HMAC-SHA256 kullanarak imzalayın. İmza zaman damgasını, yöntemi, yolu ve gövde özetini kapsar. GET /api/v3/service-status herkese açıktır ve imza gerektirmez.

Zorunlu başlıklar

BaşlıkAçıklama
X-CAV-Account-No6 haneli hesap numaranız.
X-CAV-Api-KeyÇağırdığınız ortamın (sandbox ya da canlı) API anahtarı.
X-CAV-TimestampGeçerli Unix zamanı, saniye cinsinden 10 hane. 300 saniyeden fazla sapan istek reddedilir.
X-CAV-SignatureKanonik metnin HMAC-SHA256 değeri, 64 küçük harfli hex karakter.
Idempotency-KeyYazma isteklerinde zorunlu. A-Z a-z 0-9 _ - karakterlerinden oluşan 8–128 karakter kullanın. UUID uygundur.
Content-TypeGövdeli isteklerde application/json.

Gizli anahtarın saklanması için Anahtar alma bölümüne bakın.

Bir istek nasıl imzalanır

  1. JSON gövdesini bir kez serileştirin ve sha256(body) değerini küçük harfli hex olarak hesaplayın. Gövdesiz isteklerde sha256("") kullanın.

  2. Kanonik metni timestamp, METHOD, path ve gövde hash'inden oluşturun; satır sonu (\n) ile birleştirin, sona satır sonu eklemeyin. path sorgu dizesi içermez.

  3. HMAC-SHA256(secret, canonical) değerini hesaplayın ve 64 küçük harfli hex karakter olarak kodlayın.

  4. Dört X-CAV-* başlığını (hesap numarası, API anahtarı, zaman damgası, imza) gönderin; yazma isteklerine Idempotency-Key ekleyin.

Kanonik imza metni

timestamp + "\n" + METHOD + "\n" + path + "\n" + hex(sha256(body))
signature = hmac_sha256(secret, canonical)  // 64 lowercase hex characters

path, sorgu dizesi olmayan tam yoldur (ör. /api/v3/payments). Gövdesiz istekte boş dizenin özeti (sha256("")) kullanılır.

Gönderdiğiniz baytların aynısını imzalayın: JSON'u bir kez serileştirin ve aynı dizgiyi hem hash için hem de istek gövdesi olarak kullanın.

Örnek (sahte bilgilerle)

Aşağıdaki tüm değerler uydurmadır: hesap numarası 123456, gizli anahtar sk_test_EXAMPLE. Kendi panelinizdeki değerleri kullanın.

İstek gövdesi

{"amount":"250.00","currency_code":"USD","merchant_reference":"ORDER-2026-0001"}

sha256(body)

280db1ad2f6a457444b4838e9da76682edbd328a1b62f7b9b762852d9c4354f7

Kanonik imza metni

1790000000
POST
/api/v3/payments
280db1ad2f6a457444b4838e9da76682edbd328a1b62f7b9b762852d9c4354f7

X-CAV-Signature

3d3a95fcbda890bb424184c914ef3e2371c43602988cf3a698dc357b8074c5e9

İmzalı istek gönderin

Her örnek bir ödeme oluşturur. Kimlik bilgileri ortam değişkenlerinden okunur. Gizli anahtarı koda yazmayın. Her çalıştırma yeni bir merchant_reference kullanır.

#!/usr/bin/env bash
# export MERCHANT_ACCOUNT_NO=... MERCHANT_API_KEY=... MERCHANT_API_SECRET=...   (sandbox key)
# GET requests: METHOD=GET, REQ_PATH=/api/v3/payments/<code>, BODY='' (sha256 of the empty string), no Content-Type / --data-binary
set -euo pipefail

BASE_URL="https://www.cryptoavans.com"
METHOD="POST"
REQ_PATH="/api/v3/payments"
REFERENCE="ORDER-$(date +%s)-$(openssl rand -hex 4)"   # a new merchant_reference for every payment
BODY="{\"amount\":\"250.00\",\"currency_code\":\"USD\",\"merchant_reference\":\"$REFERENCE\",\"return_url\":\"http://localhost/return\"}"

TIMESTAMP="$(date +%s)"
BODY_SHA256="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
CANONICAL="$(printf '%s\n%s\n%s\n%s' "$TIMESTAMP" "$METHOD" "${REQ_PATH%%\?*}" "$BODY_SHA256")"
# the secret travels in the environment, not on the command line
SIGNATURE="$(printf '%s' "$CANONICAL" | python3 -c 'import hashlib,hmac,os,sys; print(hmac.new(os.environ["MERCHANT_API_SECRET"].encode(), sys.stdin.buffer.read(), hashlib.sha256).hexdigest())')"

curl --silent --show-error --request "$METHOD" "$BASE_URL$REQ_PATH" \
  --header "X-CAV-Account-No: $MERCHANT_ACCOUNT_NO" \
  --header "X-CAV-Api-Key: $MERCHANT_API_KEY" \
  --header "X-CAV-Timestamp: $TIMESTAMP" \
  --header "X-CAV-Signature: $SIGNATURE" \
  --header "Idempotency-Key: $(openssl rand -hex 16)" \
  --header "Content-Type: application/json" \
  --header "Accept: application/json" \
  --data-binary "$BODY"
<?php
$config = [
    'base'    => 'https://www.cryptoavans.com',
    'account' => getenv('MERCHANT_ACCOUNT_NO'),
    'key'     => getenv('MERCHANT_API_KEY'),
    'secret'  => getenv('MERCHANT_API_SECRET'),
];

function signedRequest(array $config, string $method, string $path, ?array $payload = null, ?string $idempotencyKey = null): array
{
    $method = strtoupper($method);
    $signingPath = explode('?', $path, 2)[0];
    // serialise once: the same bytes are hashed and sent
    $body = $payload === null ? '' : json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
    $timestamp = (string) time();
    $canonical = implode("\n", [$timestamp, $method, $signingPath, hash('sha256', $body)]);
    $signature = hash_hmac('sha256', $canonical, $config['secret']);

    $headers = [
        'X-CAV-Account-No: ' . $config['account'],
        'X-CAV-Api-Key: ' . $config['key'],
        'X-CAV-Timestamp: ' . $timestamp,
        'X-CAV-Signature: ' . $signature,
        'Content-Type: application/json',
        'Accept: application/json',
    ];
    if ($idempotencyKey !== null) {
        $headers[] = 'Idempotency-Key: ' . $idempotencyKey;
    }

    $ch = curl_init($config['base'] . $path);
    curl_setopt_array($ch, [
        CURLOPT_CUSTOMREQUEST  => $method,
        CURLOPT_HTTPHEADER     => $headers,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 30,
    ]);
    if ($body !== '') {
        curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
    }
    $response = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    curl_close($ch);
    if ($response === false) {
        throw new RuntimeException('No response; retry the same operation and Idempotency-Key.');
    }

    return [$status, json_decode((string) $response, true)];
}

// a new merchant_reference for every payment: seconds plus a random suffix
[$status, $payment] = signedRequest(
    $config,
    'POST',
    '/api/v3/payments',
    ['amount' => '250.00', 'currency_code' => 'USD', 'return_url' => 'http://localhost/return', 'merchant_reference' => 'ORDER-' . time() . '-' . bin2hex(random_bytes(4))],
    bin2hex(random_bytes(16))
);
echo $status, ' ', json_encode($payment, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), PHP_EOL;

if (!in_array($status, [200, 201], true)) {
    exit(1); // the error code is not a payment code
}

// GET: no body, so the hash is sha256("")
[$status, $fetched] = signedRequest($config, 'GET', '/api/v3/payments/' . $payment['code']);
echo $status, ' ', $fetched['status'] ?? '', PHP_EOL;

Zaman damgası eski olan istek reddedilir. Sunucu saatinizi NTP ile senkron tutun. Aynı IP'den tekrarlanan hatalı imza geçici kilitlemeye (429) yol açar.

İstek kuralları

Idempotency

Her yazma isteğinde Idempotency-Key başlığı gönderin. Yeniden denemeyi güvenli kılar: aynı isteği kaç kez gönderirseniz gönderin işlem bir kez çalışır.

  • Aynı anahtar ve gövdeyle yinelenen istek ilk sonucu döndürür, ikinci bir işlem oluşturmaz. Güncel durum için GET kullanın.
  • Aynı anahtar farklı gövdeyle 9000010 kodlu 409 döndürür; geçersiz anahtar 1000009 kodlu 422 döndürür.
  • Her yeni işlem için yeni bir rastgele anahtar üretin (UUID uygundur) ve yeniden denemede kullanmak üzere sipariş kaydınızla saklayın.

Hız sınırları

İstekler API anahtarı ve IP adresi başına sınırlandırılır. 429 aldığınızda bekleyin ve daha sonra yeniden deneyin.

  • 429 aldığınızda varsa Retry-After süresini bekleyin, sonra üstel geri çekilme ve rastgele sapmayla yeniden deneyin.
  • 503 aldığınızda daha sonra aynı Idempotency-Key ve gövdeyle yeniden deneyin. Diğer 4xx hatalarını yeniden denemeden önce giderin; yeni anahtarı yalnızca yeni işlemde kullanın.

Bir istek reddedilirse code değerini Hata kodları bölümünde bulun. En sık nedenler: hatalı imza (9000001), eski zaman damgası (9000002), yanlış kimlik bilgileri (9000003) ve izin listesinde olmayan IP (9000012).

Ödemeler

Ödeme oluşturun ve durumunu okuyun.

Ödeme durumları

AlanAçıklama
createdÖdeme açıktır. Son zamanı expires_at alanıdır; ödeme sayfası 24 saat geçerlidir.
paidÖdeme tahsil edildi. payment.paid bildirimi gönderilir.
expiredÖdeme sayfasının süresi, ödeme doğrulanmadan doldu.

Güncel durumu GET /api/v3/payments/{code} ile okuyun. Ret sonrasında ödeme created durumundayken müşteri aynı payment_url adresinde yeniden deneyebilir. Ödeme expired ise yeni bir merchant_reference ve Idempotency-Key ile yeni ödeme oluşturun.

Kurallar

  • Tutar, hesabınızın ödeme sınırları içinde olmalıdır. Sınır, tutar ABD dolarına çevrildikten sonra kontrol edilir; varsayılan 3.00 – 1100.00 USD'dir. Dışındaki tutarda API 9000005 kodlu 422 döndürür. Farklı bir sınır için CryptoAvans ile görüşün.
  • merchant_reference en çok 64 karakterdir ve hesap ve ortam başına tekildir. Ödemenin süresi dolsa bile yeniden kullanılamaz (409, 9000007); her yeni ödemede yeni bir referans gönderin.
  • Müşteriyi payment_url adresine normal bir sayfa olarak (üst düzey gezinti) yönlendirin. Ödeme sayfası iframe içinde gösterilemez.
  • return_url isteğe bağlıdır. Ödeme bittiğinde sonuç sayfası müşteriyi bu adrese götüren bir düğme gösterir; sorguya code, status ve gönderdiyseniz merchant_reference eklenir. Otomatik yönlendirme yoktur.
  • Bilinmeyen istek alanları yok sayılır. Ödemeyi imzalı webhook ya da sunucunuzdan GET ile doğrulayın; müşterinin sitenize dönüşü yalnızca bilgi amaçlıdır.

Sandbox'ta payment_url aynı ödeme sayfasını açar; Test ortamı bölümündeki test kartlarından biriyle ödeyin.

Canlıda ödeme gerçek karttan alınır ve valör tarihinden sonra bakiyenize geçer.

POST/api/v3/paymentsIdempotency-Key gerekir

Ödeme oluştur

201 yanıtı payment_url ve expires_at alanlarını içerir. Müşteriyi payment_url adresine yönlendirin; ödeme sayfası 24 saat geçerlidir. 409 / 9000039 dönerse aynı isteği aynı Idempotency-Key ile tekrarlayın.

İstek gövdesi

AlanTürZorunluAçıklama
amountstringZorunlucurrency_code para biriminde, sabit ölçekli ve 2 ondalık haneli ondalık dizge olarak pozitif tutar (örn. "250.00"). Hesabınızın ödeme sınırları içinde olmalıdır (USD cinsinden, diğer para birimleri için anlık kurla çevrim sonrası denetlenir; sınırlarınızı paneldeki API anahtarı ve entegrasyon sayfasında görün); aksi halde istek 422 9000005 döndürür.
currency_codestringZorunluKaynak para birimi.
USDEURAEDTRY
merchant_referencestring | nullİsteğe bağlıKendi sipariş referansınız (en çok 64 karakter). Hesap ve ortam başına tekildir: bir referans, süresi dolmuş bir ödeme için bile yeniden kullanılamaz (409 9000007). İsteğe bağlı.
maxLength 64
webhook_urlstring | nullİsteğe bağlıİmzalı bildirimleri (payment.paid, payment.refunded, payment.failed) alan https:// adresi. Ana bilgisayar adı DNS ile herkese açık bir adrese çözülmelidir (özel ya da ayrılmış IP ve localhost olamaz); adres en çok 500 karakter olmalı ve kullanıcı adı ya da parola içermemelidir. İsteğe bağlı: gönderilmezse imzalayan anahtarınızın ortamı için panelde kayıtlı webhook adresi kullanılır; kayıtlı adres yoksa bildirim gönderilmez.
maxLength 500
merchant_namestring | nullİsteğe bağlıÖdeme ekranında gösterilen ad. İsteğe bağlıdır; varsayılan değer Hesap <account number> biçimindedir (6 haneli hesap numaranız). 120 karakterden uzun bir değer reddedilmez, kesilir.
return_urlstring · uri | nullİsteğe bağlıİsteğe bağlı return_url (en fazla 2048 karakter). Canlı ortam HTTPS ister; sandbox localhost, 127.0.0.1 veya [::1] üzerinde HTTP de kabul eder. Kullanıcı bilgisi, parça, kontrol karakteri ve boşluk yasaktır. Uluslararası alan adları ASCII biçimine dönüştürülür. Geçersiz URL: 422 / 9000038. Ödeme bittiğinde sonuç sayfası müşteriyi bu adrese götüren bir düğme gösterir; sorguya code, status ve gönderdiyseniz merchant_reference eklenir. Otomatik yönlendirme yoktur. Aynı Idempotency-Key ile yeniden denerken adresi değiştirmeyin.
maxLength 2048

İstek gövdesi · Örnek

{
  "amount": "10.00",
  "currency_code": "USD",
  "merchant_reference": "INV-104",
  "merchant_name": "Test Shop",
  "return_url": "https://shop.example.invalid/return?basket=104"
}

limits alanı currency_code, min_amount ve max_amount içerir (null: üst sınır yok). Aşağıdaki tutarlar örnektir, sizin sınırlarınız değildir.

HTTP 422 / 9000005

{
  "type": "https://www.cryptoavans.com/errors/9000005",
  "title": "Invalid amount",
  "status": 422,
  "code": "9000005",
  "correlation_id": "example",
  "detail": "Amount outside the account limits.",
  "limits": {
    "currency_code": "USD",
    "min_amount": "25.00",
    "max_amount": null
  }
}

Yanıt · 201

Ödeme oluşturuldu. Aynı Idempotency-Key ile yeniden gönderilirse bunun yerine 200 döner.

Payment

AlanTürAçıklama
codestringKısa ödeme kodu (4–16 küçük harfli karakter).
merchant_referencestring | nullSipariş referansınız, olduğu gibi geri döner.
statusstringÖdeme yaşam döngüsü durumu: müşteri ödeyene kadar created, ödeyince paid; bağlantı ödenmeden sona ererse expired.
createdpaidexpired
amountstringcurrency_code para biriminde brüt tutar; sabit ölçekli, 2 ondalık haneli ondalık dizge.
currency_codestringBu ödemenin kaynak para birimi; örnekteki para birimini varsaymak yerine dönen değeri okuyun.
USDEURAEDTRY
environmentstringBu ödemenin ait olduğu ortam.
sandboxlive
payment_urlstringMüşteriyi dönen payment_url adresine normal bir sayfa olarak (üst düzey gezinti) yönlendirin; adresi kendiniz üretmeyin veya değiştirmeyin. Sayfa iframe içine gömülemez. Bu API'ye kart verisi gönderilmez.
created_atstring · date-timeOluşturulma zamanı (UTC, ISO-8601).
expires_atstring · date-timeUTC. Ödeme sayfası oluşturulduktan sonra 24 saat geçerlidir. Süre dolunca yeni ödeme oluşturun; yenileme ucu yoktur. Ödemeyi GET /api/v3/payments/{code} veya imzalı webhook ile doğrulayın; müşterinin sitenize dönüşü yalnızca bilgi amaçlıdır.
paid_atstring · date-time | nullÖdendiği zaman ya da null.
payment_url_kindstringHer zaman platform (bizim ödeme sayfamız). Alan mevcut entegrasyonlar için korunur; provider_hosted artık dönmez.
platformprovider_hosted
cardMerchantCardSummary | nullBaşarılı ödeme denemesinin kart bilgisi; ödeme yapılana kadar veya bilgi yoksa null.
failurePaymentFailureSummary | nullÖdeme yapılmadıysa son başarısız deneme, aksi halde null. Kesin sonuç değildir: ödeme expires_at tarihine kadar ödenebilir. attempt_count yeniden denemeler dahil başarısız denemeleri sayar.

Yanıt · Örnek

{
  "code": "a1b2c3d4",
  "merchant_reference": "INV-104",
  "status": "created",
  "amount": "10.00",
  "currency_code": "USD",
  "environment": "live",
  "payment_url": "https://pay.example.com/l/a1b2c3d4",
  "payment_url_kind": "platform",
  "created_at": "2026-10-05T12:00:00Z",
  "expires_at": "2026-10-06T12:00:00Z",
  "paid_at": null,
  "card": null,
  "failure": null
}

Diğer yanıtlar

  • 200İdempotent tekrar: ilk ödeme döner; ikinci bir ödeme oluşturulmaz.
  • 422Hesap sınırları dışındaki tutar (9000005, çevrimden sonra 0.01 altı veya kapasite üstü dahil) için hesap taban para biriminde limits döner. Sandbox aynı denetimi kullanır. Diğer doğrulama hatalarında limits bulunmayabilir.

Olası hatalar

GET/api/v3/payments/{code}

Ödeme durumunu getir

Parametreler

AlanTürZorunluAçıklama
codestring · pathZorunluÖdeme kodu.
pattern ^[a-z0-9]{4,16}$

Yanıt · 200

Ödeme.

Nesne: Payment (alanlar Ödeme oluştur altında listelenir).

Yanıt · Örnek

{
  "code": "ab34cdef",
  "merchant_reference": "ORDER-2026-0001",
  "status": "created",
  "amount": "250.00",
  "currency_code": "USD",
  "environment": "live",
  "payment_url": "https://example.com/l/ab34cdef",
  "created_at": "2026-09-26T09:00:00.000Z",
  "expires_at": "2026-09-27T09:00:00.000Z",
  "paid_at": null,
  "payment_url_kind": "platform"
}

Olası hatalar

Başarısız denemeler

Payment.card başarılı denemenin, failure.card son başarısız denemenin kart bilgisidir. Bulunmayan değerler null olabilir. Kart sahibi adı kişisel veridir; yasal yükümlülüklerinize göre işleyin. Tam kart numarası ve son kullanma tarihi hiçbir zaman verilmez.

payment.failed, ilk başarısız denemede bir kez gönderilir: kart reddi, 3-D Secure başarısızlığı veya ödeyenin iptali. Ödeme created kalır; müşteri expires_at tarihine kadar yeniden deneyebilir, sonra payment.paid gelebilir. Sonraki retler yeni olay olmadan Payment.failure özetini günceller. attempt_count başarısız denemeleri sayar; varsa detail_code bilgi amaçlıdır. Aşağıdaki kararlı failure.code değerlerini kullanın.

PaymentFailure

AlanTürZorunluAçıklama
detail_codestring | nullZorunluÖdeme ağından gelen ayrıntılı yanıt kodu, örneğin 058 veya 511; noktalı sandbox kodları desteklenmeye devam eder. İşlem yoksa: timeout ve detay null. Entegrasyonlar karar verirken kararlı code alanını kullanmalıdır.
pattern ^[0-9]{3}(?:\.[0-9]{3})?$
cardMerchantCardSummary | nullZorunluDoğrulanmış başarılı veya başarısız denemenin isteğe bağlı kart bilgisi. issuing_country ISO 3166-1 alpha-3 kullanır. Tam kart numarası, son kullanma tarihi, güvenlik kodu veya kuruluşa özel kimlikler verilmez.
codestringZorunluSon ödeme işleminden, yoksa doğrulamadan gelen kararlı güvenli neden. 511/51: insufficient_funds; 058/004: authentication_failed; 022: timeout; 001/002: cancelled; 527/501: declined; 526: provider_error. İşlem yoksa veya doğrulama başarılı olup ödeme yapılmadıysa: timeout. Bilinmeyen hatalar: declined. Kuruluş mesajı veya hassas kart verisi yoktur.
declinedinsufficient_fundsauthentication_failedcard_invalidcancelledprovider_errorexpired_cardinvalid_cvvtimeout
occurred_atstring · date-timeZorunluBaşarısız denemenin tamamlanma zamanı, milisaniyeli UTC.

Webhooks

İmzalı JSON bildirimleri payment.paid, payment.failed ve payment.refunded olaylarını kapsar. Yalnız bilinen olay türlerini işleyin, bilinmeyenleri yok sayın; başarısız deneme kesin ödeme sonucu değildir.

Her bildirimi doğrulayın

  1. JSON’u ayrıştırmadan önce ham gövdeyi okuyun. Eksik veya biçimi geçersiz imzayı ya da zaman damgasını reddedin. Örnekler, sunucu saatinizden 300 saniyeden fazla sapan zaman damgalarını reddeder; saati senkron tutun.

  2. Kanonik metni X-CAV-Timestamp, POST, webhook adresinizin yolu (sorgu dizesi olmadan) ve ham gövdenin sha256 değerinden yeniden oluşturun.

  3. Gizli anahtarınızla HMAC-SHA256 hesaplayın ve X-CAV-Signature ile sabit zamanlı karşılaştırın. Ödemeyi oluşturan anahtarın gizli anahtarını kullanın; sonradan anahtarı yenilemiş olsanız bile.

  4. İmzayı doğruladıktan sonra imzalı environment alanını, ödeme kodunu, tutarı ve para birimini siparişinizle eşleştirin. payment.paid / payment.failed ile payment.refunded ayrımını imzalı gövdedeki event_type alanıyla yapın; iade bildirimi sipariş teslimini başlatmamalıdır.

  5. Doğrulanmış gövdedeki event_id ile atomik tekilleştirme yapın. X-CAV-Delivery ile büyük/küçük harfe bakmadan karşılaştırın ve uyuşmazlığı reddedin; başlığın kendisi imzalı değildir. Teslimat sırasına güvenmeyin; beklenmeyen bir olayı durum uçlarından sorgulayarak kontrol edin.

  6. Doğrulanmış olayı kalıcı olarak kaydettikten veya işlemeyi tamamladıktan sonra hızlıca 2xx dönün. Kaydedemiyorsanız yeniden denenebilmesi için başarısız yanıt verin. Önceden kaydedilmiş bir tekrar için 2xx dönün. Yanıt gövdesi dikkate alınmaz ve yönlendirmeler izlenmez.

Herkese açık bir sertifika otoritesinin verdiği geçerli bir TLS sertifikası kullanın; sertifika denetimi kapatılamaz.

İmza doğrulama örnekleri

<?php
// $path is the path of YOUR webhook URL (no query string), e.g. /payment-webhook
function isValidWebhook(string $rawBody, array $headers, string $path, string $secret): bool
{
    $headers = array_change_key_case($headers, CASE_LOWER);
    $timestamp = $headers['x-cav-timestamp'] ?? '';
    $signature = $headers['x-cav-signature'] ?? '';
    if (!is_string($timestamp) || !is_string($signature)
        || preg_match('/^[0-9]{10}$/D', $timestamp) !== 1
        || preg_match('/^[0-9a-f]{64}$/D', $signature) !== 1
        || abs(time() - (int) $timestamp) > 300) {
        return false;
    }
    $canonical = implode("\n", [$timestamp, 'POST', $path, hash('sha256', $rawBody)]);

    return hash_equals(hash_hmac('sha256', $canonical, $secret), $signature);
}

$rawBody = file_get_contents('php://input');
$headers = [
    'X-CAV-Timestamp' => $_SERVER['HTTP_X_CAV_TIMESTAMP'] ?? '',
    'X-CAV-Signature' => $_SERVER['HTTP_X_CAV_SIGNATURE'] ?? '',
];
if (!isValidWebhook($rawBody, $headers, '/payment-webhook', getenv('MERCHANT_API_SECRET'))) {
    http_response_code(401);
    exit;
}

// After verification: compare event_id from the signed body with X-CAV-Delivery (case-insensitive),
// save the event once (deduplicate by event_id), then answer 2xx. On a save error answer 5xx so it is retried.
$event = json_decode($rawBody, true);
http_response_code(204);
const crypto = require("node:crypto");

// path is the path of YOUR webhook URL (no query string), e.g. /payment-webhook
function isValidWebhook(rawBody, headers, path, secret) {
  const timestamp = headers["x-cav-timestamp"] ?? "";
  const signature = headers["x-cav-signature"] ?? "";
  if (typeof timestamp !== "string" || typeof signature !== "string"
      || !/^[0-9]{10}$/.test(timestamp) || !/^[0-9a-f]{64}$/.test(signature)
      || Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp)) > 300) return false;
  const bodyHash = crypto.createHash("sha256").update(rawBody).digest("hex");
  const canonical = [timestamp, "POST", path, bodyHash].join("\n");
  const expected = crypto.createHmac("sha256", secret).update(canonical).digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(signature);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

// rawBody must be the untouched request bytes (Buffer), not re-serialised JSON.
// After verification, deduplicate using event_id from the signed body; check X-CAV-Delivery matches.
// Check event_type and order fields, then durably save before 2xx (see the verification steps).
module.exports = { isValidWebhook };
import hashlib
import hmac
import re
import time


def is_valid_webhook(raw_body: bytes, headers: dict, path: str, secret: str) -> bool:
    """path is the path of YOUR webhook URL (no query string), e.g. /payment-webhook."""
    headers = {key.lower(): value for key, value in headers.items()}
    timestamp = headers.get("x-cav-timestamp", "")
    signature = headers.get("x-cav-signature", "")
    if (not isinstance(timestamp, str) or not isinstance(signature, str)
            or not re.fullmatch(r"[0-9]{10}", timestamp)
            or not re.fullmatch(r"[0-9a-f]{64}", signature)
            or abs(int(time.time()) - int(timestamp)) > 300):
        return False
    canonical = "\n".join([timestamp, "POST", path, hashlib.sha256(raw_body).hexdigest()])
    expected = hmac.new(secret.encode(), canonical.encode(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)


# After verification, deduplicate by event_id from the signed body; check X-CAV-Delivery matches.
# Check event_type and order fields, then durably save before 2xx (see the verification steps).

Yeniden denemeler

2xx dışındaki her yanıt, zaman aşımı (yaklaşık 10 saniye) ya da bağlantı hatası başarısız deneme sayılır. En fazla 6 deneme yapılır; denemeler arasında en az aşağıdaki süreler beklenir. Son başarısız denemeden sonra teslimat failed olarak işaretlenir; POST /api/v3/webhooks/deliveries/{id}/retry ile yeniden kuyruğa alabilirsiniz.

Gövde ve X-CAV-Delivery her denemede aynıdır; X-CAV-Timestamp ve X-CAV-Signature her seferinde yeniden hesaplanır. Teslimat id, X-CAV-Delivery başlığı ve gövdedeki event_id aynı büyük harfli UUID'dir; büyük/küçük harfe bakmadan karşılaştırın.

DenemeÖncesinde bekleme
1—
260 s (1 min)
3300 s (5 min)
4900 s (15 min)
53600 s (1 h)
621600 s (6 h)

Sunucunuza gönderilen bildirimler

OlayNe zaman gönderilir
payment.paidÖdeme tahsil edildi.
payment.failedHâlâ açık olan bir ödemenin ilk başarısız denemesi. Kesin değildir: müşteri yeniden deneyebilir.
payment.refundedBir iade tamamlandı.
POSTpayment.paid

Mağaza adresine imzalı payment.paid bildirimi

Başlık

AlanTürZorunluAçıklama
X-CAV-Eventstring · headerZorunlu
payment.paid
X-CAV-Environmentstring · headerZorunlu
sandboxlive
X-CAV-Deliverystring · uuid · headerZorunluTeslimat kimliği. Yeniden denemelerde değişmez.
X-CAV-Timestampstring · headerZorunlu
pattern ^[0-9]{10}$
X-CAV-Signaturestring · headerZorunlu
pattern ^[0-9a-f]{64}$

İstek gövdesi · Örnek

{
  "event_id": "3BAEB108-41A8-40AE-81EF-0A82AA1132D6",
  "event_type": "payment.paid",
  "environment": "sandbox",
  "occurred_at": "2026-10-05T12:01:00.000Z",
  "payment": {
    "code": "a1b2c3d4",
    "merchant_reference": "INV-104",
    "status": "paid",
    "amount": "10.00",
    "currency_code": "USD",
    "created_at": "2026-10-05T12:00:00.000Z",
    "paid_at": "2026-10-05T12:01:00.000Z",
    "card": {
      "brand": "visa",
      "bin": "400000",
      "last4": "0010",
      "holder_name": "TEST CARDHOLDER",
      "issuing_country": "TUR",
      "issuing_bank": "TEST BANK",
      "funding_type": "credit",
      "category": "STANDARD"
    }
  }
}
POSTpayment.failed

İmzalı ödeme denemesi başarısızlık bildirimi

İstek gövdesi · Örnek

{
  "event_id": "3BAEB108-41A8-40AE-81EF-0A82AA1132D6",
  "event_type": "payment.failed",
  "environment": "sandbox",
  "occurred_at": "2026-10-05T12:01:00.000Z",
  "payment": {
    "code": "a1b2c3d4",
    "merchant_reference": "INV-104",
    "status": "created",
    "amount": "10.00",
    "currency_code": "USD",
    "created_at": "2026-10-05T12:00:00.000Z",
    "expires_at": "2026-10-06T12:00:00.000Z"
  },
  "failure": {
    "code": "declined",
    "occurred_at": "2026-10-05T12:01:00.000Z",
    "detail_code": null,
    "card": {
      "brand": "visa",
      "bin": "400000",
      "last4": "0010",
      "holder_name": "TEST CARDHOLDER",
      "issuing_country": "TUR",
      "issuing_bank": "TEST BANK",
      "funding_type": "credit",
      "category": "STANDARD"
    }
  }
}

Zarf payment.paid ile aynıdır. payment nesnesi paid_at yerine expires_at taşır; failure denemeyi açıklar (Başarısız denemeler).

Teslimatları incelemek veya başarısız olanı yeniden kuyruğa almak için bkz. Webhook teslimatları.

Test ortamı

Sandbox istekleri sanal veriyle çalışır, gerçek para hareketi oluşturmaz. payment_url adresini açın ve aşağıdaki test kartlarından biriyle ödeyin.

Nasıl çalışır

Sandbox anahtarıyla imzalanan her istek yalıtılmış sanal veri üzerinde çalışır. Kurallar, alanlar ve hata kodları canlıyla aynıdır. payment_url, müşterilerinizin canlıda gördüğü ödeme sayfasını açar. Test ödemesi onaylandığında payment.paid bildirimi, environment değeri sandbox olarak, ödemenin webhook adresine gönderilir.

  • Sayfa bir test uyarısı gösterir ve yalnızca aşağıdaki test kartlarını kabul eder; gerçek kart numaraları reddedilir. 3-D Secure kartlarında kimlik doğrulamayı onaylamanızı ya da reddetmenizi ister. API kart numarası kabul etmez; kartı ödeme sayfasında girin.
  • Gerçek para, gerçek kart ya da zincir işlemi yoktur; hiçbir şey canlı bakiyenize yansımaz.

Sandbox test kartları

Bu sentetik numaraları yalnızca sandbox ödemesinin ödeme sayfasında kullanın; canlı ödemede asla kullanmayın. Kart sahibi adına en az 2 harfli herhangi bir ad, bu ay ya da sonrası bir son kullanma tarihi ve 3–4 haneli bir CVV girin.

Kart numarasıSonuç koduDavranış
4000000000000010approved3-D Secure olmadan onaylanır.
4000000000000051declined3-D Secure olmadan reddedilir.
4000000000000002three_ds_approvedÖdeme sayfası bir 3-D Secure adımı gösterir; onaylarsanız ödeme onaylanır, reddederseniz ödeme reddedilir.
4000000000000093three_ds_declinedÖdeme sayfası bir 3-D Secure adımı gösterir; hangi sonucu seçerseniz seçin ödeme reddedilir.
4000000000000044timeoutSonuç beklemede kalır (nihai sonuç yoktur); kesin ret saymayın. Deneme sonradan süresi dolmuş olarak kapatılır.

Reddedilen ödeme created kalır ve ilk başarısız denemesinde bir kez payment.failed gönderir; müşteri yeniden deneyebilir (saatte IP başına 10 deneme). Zaman aşımı kartı ve .77 ile biten tutarlar sonucu beklemede bırakır: sayfa beklemede sonucunu gösterir, webhook gönderilmez ve sonuç elle girilmez. Deneme yaklaşık 30 dakika sonra süresi dolmuş olarak kapatılır; ödeme created kalır ve müşteri yeniden başlayabilir. .77 kuralı ABD doları karşılığı tutara bakar.

İstek günlüğü

Panelde “Sandbox istek günlüğü” son 90 günün sandbox isteklerini gösterir. Kaynak IP adresi 30 gün saklanır. İmza ya da kimlik doğrulaması başarısız olan istekler günlüğe yazılmaz; onları yanıttaki correlation_id ile bulun. Canlı istekler günlüğe yazılmaz.

İade, çekim ve itiraz testleri Hesap işlemleri sayfasında anlatılır.

Hata kodları

Hatalar application/problem+json biçiminde döner. code alanı aşağıdaki kodlardan biridir; destek talebinizde correlation_id değerini paylaşın. title ve detail Accept-Language başlığına göre döner; kodunuzda yalnızca code alanına güvenin.

Hata yanıtı örneği

application/problem+json

{
  "type": "https://www.cryptoavans.com/errors/9000011",
  "title": "Signature headers are missing",
  "status": 400,
  "detail": "X-CAV-Account-No, X-CAV-Api-Key, X-CAV-Timestamp and X-CAV-Signature are mandatory.",
  "code": "9000011",
  "correlation_id": "3BAEB108-41A8-40AE-81EF-0A82AA1132D6"
}

Kod tablosu

Yanıt kaybolursa veya 500 ya da 503 alırsanız işlem sonucu belirsiz olabilir. Aynı Idempotency-Key ve gövdeyle, güncel zaman damgasıyla yeniden imzalayarak deneyin. 503 yanıtındaki Retry-After süresine uyun. Yalnızca istek zaman aşımına uğradığı için yeni ödeme oluşturmayın.

KodHTTPBaşlık
9000001401İmza geçersiz
9000002401Zaman damgası pencere dışında
9000003401Kimlik bilgileri geçersiz
9000004403Hesap işlem yapamaz
9000005422Tutar geçersiz
9000006422Mağaza referansı geçersiz
9000007409Mağaza referansı zaten kullanıldı
9000008422Bildirim adresi geçersiz
9000009404Ödeme veya iade bulunamadı
9000010409Idempotency çakışması
9000011400İmza başlıkları eksik
9000012403IP izinli değil
9000013409Sandbox işlemi kullanılamıyor
9000014409İade/iptal talebi zaten var
9000015404İtiraz bulunamadı
9000016409İtiraza yanıt kabul edilmiyor
9000017422TRC20 adresi geçersiz
9000018422Adres onayı gerekli
9000019422Tutar geçersiz
9000020409Yetersiz bakiye
9000021409Günlük çekim sınırı doldu
9000022409Çekim işleniyor
9000023404Çekim bulunamadı
9000024404Webhook teslimatı bulunamadı
9000025409Yeniden gönderilemez
9000026503Sandbox şeması hazır değil
9000027409Sandbox bakiyesi meşgul
9000030409Sandbox tahsilat bakiyesi geçersiz
9000033409Yetersiz sandbox bakiyesi
9000038422Dönüş adresi geçersiz
9000039409Ödeme hazırlanıyor
9000040403API erişimi kapalı
1000001422Doğrulama hatası ortak
1000002404Kayıt bulunamadı ortak
1000005429İstek sınırı aşıldı ortak
1000006500Sunucu hatası ortak
1000007405Yöntem desteklenmiyor ortak
1000009422Geçersiz idempotency anahtarı ortak
1000011503Servis şu anda erişilemiyor ortak
1000013503Anahtar altyapısı hazır değil ortak
1000014503Hizmet bakımda ortak
1000020422Tutar biçimi geçersiz ortak
1000021422Desteklenmeyen para birimi ortak
1000023422Tutar kapasiteyi aşıyor ortak
1000030422Adres kullanılamaz
3000001503Kur şu anda alınamıyor ortak

Canlıya geçiş kontrol listesi

Canlıya geçmeden önce tüm maddeleri tamamlayın.

  • Sandbox'ta ödeme, webhook, iade ve çekim akışlarını uçtan uca denediniz.

  • Canlı anahtar ve gizli anahtar yalnızca sunucunuzda saklanıyor ve günlüğe yazılmıyor.

  • Canlı IP izin listeniz ve canlı HTTPS webhook adresiniz tanımlı.

  • Webhook imza doğrulaması devrede ve geçersiz imzaları reddediyor; işleyici hızlıca 2xx dönüyor ve tekrarlanan teslimatları tolere ediyor.

  • Siparişler yalnızca webhook ya da GET /payments/{code} paid gösterdiğinde tamamlanıyor; müşterinin tarayıcısına güvenilmiyor.

  • Yazma istekleri Idempotency-Key gönderiyor ve yeniden denemede aynısı kullanılıyor; her ödeme yeni bir merchant_reference alıyor.

  • Sunucu saatiniz NTP ile senkronize.

  • Hesabınıza canlı terminal atandı ve CryptoAvans ekibiyle küçük bir canlı ödeme denemesi yaptınız.

Canlı terminal ve son uçtan uca test için hesap yöneticinizle görüşün. Panele gir