Geliştiriciler/Entegrasyon

Ürünler ve siparişler

Bu rehber satın alma sürecinin tamamını adım adım anlatır: bakiyenizi kontrol edin, bir ürün bulun, fiyatını öğrenin, siparişi verin ve kodları alın.

İlgili: Kimlik doğrulama · Genel kurallar · Webhook'lar

#Hesap ve bakiye

#Hesap

GET/api/v1/account, şirket bilgilerinizi ve API erişiminizin açık olup olmadığını gösterir.

JSON
{
  "merchant_id": "M00000001",
  "name": "Acme Shop",
  "legal_name": "Acme Shop Ltd",
  "tier": "standard",
  "billing_email": "[email protected]",
  "status": "active",
  "kyb_status": "approved",
  "api_access_enabled": true,
  "default_currency": "USD"
}
  • default_currency, cüzdanınızın para birimidir. Ödediğiniz tüm fiyatlar bu para birimindedir.
  • api_access_enabled, CardV işletmenizi onayladığında true olur.

#Bakiye

GET/api/v1/balance, harcayabileceğiniz tutarı gösterir.

JSON
{
  "currency": "USD",
  "balance": "1520.4000",
  "reserved_amount": "0.0000",
  "available_balance": "1520.4000",
  "low_balance_threshold": "200.0000",
  "low_balance_notified_at": null,
  "is_active": true
}
  • available_balance, şu anda harcayabileceğiniz tutardır: balance eksi reserved_amount.
  • Tutarı available_balance değerini aşan bir sipariş reddedilir ve hiçbir ücret alınmaz.
  • low_balance_threshold, düşük bakiye e-postasının gönderileceği seviyedir. Bu değeri Portal'dan ayarlarsınız.
  • Bakiye yüklemek için Portal'ı kullanın.

#Ürünler (SKU'lar)

SKU, satın alabileceğiniz tek bir üründür, örneğin "Steam Wallet 10 USD". Numarası S000456 gibi görünür. Fiyat sorgusu ve sipariş SKU üzerinden yapılır.

GET/api/v1/skus, satın alabileceğiniz SKU'ları listeler. Örnek:

HTTP
GET /api/v1/skus?search=steam&region=US&limit=50&offset=0

Filtreler (hepsi isteğe bağlı):

FiltreÖrnekNeyle eşleşir
searchsteamSKU numarası, ürün adı veya marka
brandSteamMarka adı (büyük/küçük harf fark etmez)
regionUSÜlke kodu veya ülke adı
verticalgift_cardÜrün grubu
product_typepin_codeTeslimat şekli

Sayfalama: limit (varsayılan 100, en fazla 500) ve offset gönderin. offset değerini artırarak, count değerine ulaşana kadar sorgulamaya devam edin.

JSON
{
  "count": 7,
  "limit": 1,
  "results": [
    {
      "sku_id": "S000456",
      "product_id": "P000123",
      "name": "Steam Wallet 10 USD",
      "product_name": "Steam Wallet US",
      "brand": "Steam",
      "region": "US",
      "vertical": "gift_card",
      "product_type": "pin_code",
      "denomination_type": "fixed",
      "denomination_value": "10.0000",
      "face_currency": "USD",
      "merchant_price": "9.2500",
      "settlement_currency": "USD",
      "availability": "available",
      "min_quantity": 1,
      "max_quantity": 100,
      "required_input_schema": [],
      "...": "more fields"
    }
  ],
  "filter_options": {"brands": [], "regions": [], "verticals": []}
}

GET/api/v1/skus/{sku_id}, aynı alanlarla tek bir SKU döner.

En çok işinize yarayacak alanlar:

AlanAnlamı
sku_idFiyat sorgusu ve sipariş için kullandığınız numara.
merchant_priceSize özel adet fiyatı, settlement_currency para biriminde.
availabilityavailable veya unavailable. Yalnızca available olan SKU'ları sipariş edin.
denomination_typefixed veya range. Bkz. sabit ve aralıklı tutarlar.
face_currencyKartın üzerinde yazan para birimi. Cüzdanınızınkinden farklı olabilir.
min_quantity, max_quantityBir sipariş satırında en az ve en fazla kaç adet olabileceği.
product_typepin_code (size bir kod verilir) veya direct_charge (hesaba doğrudan yükleme yapılır).
required_input_schemaDoğrudan yüklemeler için göndermeniz gereken bilgiler.
brand_logo_url, image_urlCardV'de barındırılan görseller ya da "".
description, redemption_instructions, termsMüşterilerinize gösterebileceğiniz metinler.

İpuçları:

  • Yalnızca aktif olan ve hesabınıza açık SKU'ları görürsünüz. Diğer SKU'lar 404 döner.
  • SKU listesini 5–15 dakikada bir güncelleyin. Sipariş vermeden hemen önce mutlaka güncel fiyatı sorgulayın.
  • filter_options, filtrelemede kullanabileceğiniz markaları, bölgeleri ve ürün gruplarını listeler.

#Sabit ve aralıklı tutarlar

SKU'ların çoğunun sabit bir yüz değeri vardır, örneğin 10 USD. Bazı SKU'lar ise aralıklıdır: tutarı müşteriniz seçer, örneğin 5 ile 500 USD arası.

TürFiyat sorgusundaSiparişte
fixedquantity gönderinamount göndermeyin
rangequantity ve amount gönderinamount gönderin

Aralıklı bir SKU'da amount, min_face_value ile max_face_value arasında olmalıdır. Tutar face_currency para birimindedir.

#Doğrudan yüklemeler

Bazı ürünler, müşterinizin hesabına (örneğin bir oyun hesabına) doğrudan yükleme yapar. Bunun için CardV'nin hesap bilgilerine ihtiyacı vardır. Gereken bilgiler SKU'da listelenir:

JSON
"required_input_schema": [
  {"key": "player_id", "label": "Player ID", "required": true},
  {"key": "server", "label": "Server", "required": false}
]

Değerleri, her birinin key adını kullanarak sipariş satırının inputs alanında gönderin:

JSON
"inputs": {"player_id": "123456789", "server": "EU"}
  • "required": false yazmıyorsa alan zorunludur.
  • Zorunlu bir değer eksikse sipariş items hatasıyla reddedilir.
  • Bu değerler müşterinizin kişisel verileridir. Onları koruyun (bkz. Güvenlik).

#Fiyat sorgusu

Fiyat sorgusu, belirli bir adet için güncel fiyatı söyler.

HTTP
GET /api/v1/skus/S000456/quote?quantity=2
GET /api/v1/skus/S000789/quote?quantity=1&amount=25.00
JSON
{
  "sku_id": "S000456",
  "settlement_currency": "USD",
  "merchant_price": "9.2500",
  "quantity": 2,
  "total_price": "18.5000",
  "min_quantity": 1,
  "max_quantity": 100,
  "availability": "available"
}
  • Fiyat sorgusu fiyatı sabitlemez. Fiyatlar her an değişebilir.
  • Kendinizi korumak için sipariş verirken merchant_price değerini expected_unit_price olarak gönderin. Fiyat değiştiyse CardV siparişi reddeder ve hiçbir ücret almaz.
  • Aralık dışındaki bir adet veya tutar, quantity ya da amount hatasıyla HTTP 400 döner.

#Sipariş verme

POST/api/v1/orders, bir veya daha fazla SKU satın alır ve ödemeyi cüzdanınızdan yapar. Bu istek imzalı olmalıdır.

JSON
{
  "external_order_id": "SHOP-20260929-10001",
  "items": [
    {"sku_id": "S000456", "quantity": 2, "expected_unit_price": "9.2500"},
    {"sku_id": "S000789", "amount": "25.00", "expected_unit_price": "23.7500"},
    {
      "sku_id": "S000900",
      "expected_unit_price": "4.9000",
      "inputs": {"player_id": "123456789"}
    }
  ]
}
AlanZorunlu muAnlamı
external_order_idEvetKendi sipariş numaranız, 1–120 karakter. Benzersiz olmalıdır.
itemsEvetBir veya daha fazla sipariş satırı.
items[].sku_idEvetSatın alınacak SKU.
items[].quantityHayırAdet. Varsayılan 1.
items[].amountAralıklı SKU'lardaSatın alınacak yüz değeri.
items[].expected_unit_priceÖnerilirSorguda aldığınız merchant_price. Her zaman gönderin.
items[].inputsDoğrudan yüklemelerdeDoğrudan yüklemeler için hesap bilgileri.

CardV siparişi kabul ettiği anda toplam tutarın tamamını cüzdanınızdan düşer. Teslimat ardından arka planda başlar.

Yanıt HTTP 201 olur:

JSON
{
  "idempotent_replay": false,
  "order": {
    "order_id": "O-00001234",
    "external_order_id": "SHOP-20260929-10001",
    "status": "accepted",
    "total_amount": "46.2000",
    "...": "more fields"
  }
}

order.order_id değerini saklayın. Bu yanıtta hiçbir zaman kod bulunmaz. Kodları daha sonra alırsınız (Siparişi okuma).

#Reddedilen siparişler

Reddedilen bir sipariş HTTP 400 döner ve hiçbir ücret alınmaz. Nedeni hata anahtarından anlaşılır:

AnahtarNedenNe yapmalı
itemsFiyat değişmiş, SKU satışta değil, tutar hatalı veya bir bilgi eksikFiyatı yeniden sorgulayın, sorunu düzeltip tekrar gönderin
balanceCüzdanınızda yeterli para yokPortal'dan bakiye yükleyin
riskSipariş tutarı veya günlük limitiniz aşıldıCardV ile iletişime geçin
external_order_idSipariş numaranız başka bir siparişte zaten kullanılmışBkz. Güvenli yeniden deneme

Fiyat değişikliği örneği:

JSON
{
  "items": "SKU S000456 price changed from 9.2500 to 9.4100 USD; refresh quote and confirm again."
}

Tek bir siparişin tutarı, günlük harcama ve günlük sipariş sayısı için limitleri hesap seviyeniz belirler. Günlük limitler 00:00 UTC'de sıfırlanır. Limitlerinizi CardV'ye sorun.

#Güvenli yeniden deneme

Kendi sipariş numaranız (external_order_id), aynı şeyi iki kez satın almanızı önler. Aynı siparişi aynı sipariş numarasıyla tekrar gönderirseniz CardV sizden tekrar ücret almaz. Bunun yerine mevcut siparişi döner.

GönderdiğinizAldığınız
Yeni bir sipariş numarasıHTTP 201. Yeni bir sipariş. Cüzdanınızdan ödeme alınır.
Aynı sipariş numarası ve aynı siparişHTTP 200 ve "idempotent_replay": true. Mevcut sipariş. Ücret alınmaz.
Aynı sipariş numarası ama farklı bir siparişexternal_order_id için HTTP 400. Hiçbir işlem yapılmaz.

"Aynı sipariş", aynı satırların aynı sırayla ve aynı SKU, adet, tutar ve hesap bilgileriyle gönderilmesi demektir. expected_unit_price gönderiyorsanız ilk siparişteki fiyatla aynı olmalıdır.

Tekrarlanan sipariş, bakiye ve fiyat kontrollerinden önce tanınır. Bu yüzden fiyat o arada değişmiş olsa bile her zaman ilk sipariş döner.

#Güvenli yeniden deneme akışı

Net bir yanıt alamazsanız aynı siparişi yeniden göndermeniz yeterlidir.

Text
R sipariş numarasıyla POST /orders
 ├─ 201 veya 200 → order_id'yi saklayın. İşlem tamam.
 ├─ 400 items / balance / risk → sipariş oluşmadı.
 │      Nedeni düzeltip tekrar gönderin. R'yi yeniden kullanabilirsiniz.
 ├─ 400 external_order_id → R başka bir siparişe ait. Durun ve kontrol edin.
 ├─ 403 imza hatası → yeniden imzalayıp aynı gövdeyi gönderin.
 ├─ 429 → Retry-After kadar bekleyin, yeniden imzalayıp aynı gövdeyi gönderin.
 └─ zaman aşımı, 5xx veya kopan bağlantı
        → aynı gövdeyi aynı R ile tekrar gönderin.
          201 alırsanız ilk istek ulaşmamıştır, 200 alırsanız ulaşmıştır.

Kurallar:

  • Yanıt kayboldu diye asla yeni sipariş numarası üretmeyin. İlk istek aslında ulaştıysa, yeni numara her şeyi iki kez satın almanıza yol açar.
  • Her yeniden gönderimde yeni zaman damgası, nonce ve imza gerekir. Gövde aynı kalır.

#Siparişi okuma

GET/api/v1/orders/{order_id}, siparişi, durumunu ve kodlarını döner.

JSON
{
  "order_id": "O-00001234",
  "external_order_id": "SHOP-20260929-10001",
  "status": "succeeded",
  "currency": "USD",
  "total_amount": "18.5000",
  "created_at": "2026-09-29T08:15:30.123456Z",
  "updated_at": "2026-09-29T08:15:41.004211Z",
  "items": [
    {
      "sku_id": "S000456",
      "product_name": "Steam Wallet US",
      "quantity": 2,
      "unit_price": "9.2500",
      "total_price": "18.5000",
      "delivery_count": 2,
      "deliveries": [{"...": "see Codes below"}]
    }
  ],
  "...": "more fields"
}
  • total_amount, sipariş kabul edildiğinde sizden alınan tutardır.
  • items[].unit_price, bu sipariş için sabitlenen fiyattır.
  • items[].deliveries, kodların tamamını içerir. Bu yanıtı gizli bilgi gibi koruyun.
  • invoice_url ve delivery_file_url, faturanın ve kodları içeren CSV dosyasının yollarıdır. İkisi de yalnızca Portal'da çalışır. API anahtarıyla çağrıldığında HTTP 403 döner.
  • Yanıtta ayrıca şunlar da bulunur: id (eski bir numara, kullanmayın), events (yalnızca görüntüleme amaçlı geçmiş) ve satır bazında teslimat ilerlemesi. Bunları yok sayabilirsiniz.
  • Bilinmeyen bir sipariş numarası HTTP 404 döner.

#Sipariş durumu ve kodlar

#Durum

Text
accepted ──► processing ──► succeeded
                  │
                  ├──► partially_succeeded   (bazı satırlar teslim edildi, bazıları edilmedi)
                  │
                  └──► failed ──► refunded   (para cüzdanınıza iade edildi)
DurumTamamlandı mı?Ne yapmalı
acceptedHayırBekleyin. Ödeme cüzdandan alındı, teslimat henüz başlamadı.
processingHayırBekleyin. Siparişi tekrar vermeyin.
succeededEvetKodları okuyun ve müşterinize iletin.
partially_succeededEvetGelenleri teslim edin. Kalan tutar daha sonra iade edilir.
failedHenüz değilrefunded durumunu bekleyin. Başarısız olması iade yapıldığı anlamına gelmez.
refundedEvetPara cüzdanınıza geri döndü.

Webhook kullanmıyorsanız siparişi şu aralıklarla sorgulayın: 5 saniye sonra, ardından 10 sn, 30 sn, 60 sn, sonra 5 dakikada bir. İstek limitini aşmayın. Siparişlerin çoğu birkaç saniyede tamamlanır. Bazıları elle kontrol gerektirir ve saatler sürebilir.

#Kodlar

Teslim edilen her adet, items[].deliveries içinde bir nesnedir:

JSON
{
  "status": "stored",
  "delivery_type": "card_pin",
  "display_fields": [
    {"key": "card_number", "label": "Card number", "value": "X1234", "kind": "secret"},
    {"key": "pin_code", "label": "PIN", "value": "9876", "kind": "secret"}
  ],
  "redeem_url": "",
  "expiry_date": "2027-09-29",
  "instructions": "Redeem at ...",
  "is_masked": false
}
  • Müşterinize display_fields alanını gösterin: her birinde bir label ve bir value vardır. redeem_url, expiry_date ve instructions boş değilse onları da gösterin.
  • kind, kodlar ve PIN'ler için secret, seri numarası gibi bilgiler için reference olur.
  • delivery_type ne aldığınızı söyler: code, card_pin, link, code_link veya qr. Yeni türler eklenebilir. Bu yüzden gösterimi her zaman display_fields üzerinden kurun.
  • link teslimatlarında kodun kendisi redeem_url adresidir. Gizli tutun.
  • status değeri voided olan bir adedi müşterinize asla vermeyin.
  • Doğrudan yükleme ürünlerinde genellikle teslimat nesnesi olmaz. succeeded, hesaba yüklemenin yapıldığı anlamına gelir.
  • card_number ve pin_code gibi kopyalar ayrı alanlar olarak da gelir. Bunlar boş olabilir.
  • Webhook'lar hiçbir zaman kod içermez. Webhook geldikten sonra siparişi okuyun.

Entegrasyonla ilgili sorunuz mu var? Merchant ID’nizi ve sipariş ya da istek kimliğini ekleyerek [email protected] adresine yazın.