Geliştiriciler/Başlarken
Kimlik doğrulama
Her API isteği anahtar kimliğinizi ve bir imzayı taşır. İmza, isteğin sizden geldiğini kanıtlar. Böylece kimse isteği değiştiremez veya tekrarlayamaz. İmzalama sırrınızın kendisi asla gönderilmez.
İlgili: Ürünler ve siparişler · Güvenlik · README
#Kimlik bilgileri
| Bilgi | Örnek | Gizli mi? |
|---|---|---|
| Anahtar kimliği (Key ID) | ck_live_... | Hayır. Hangi anahtarı kullandığınızı gösterir. |
| İmzalama sırrı | cs_live_... | Evet. İmzayı bununla atarsınız. Sunucunuzdan asla çıkmaz. |
| Merchant ID | M00000001 | Hayır. API isteklerinde isteğe bağlıdır. |
- Anahtarları Portal'da bir Owner oluşturur (bkz. API anahtarları).
- Portal, imzalama sırrını anahtarı oluşturduğunuzda yalnızca bir kez gösterir. CardV dahil hiç kimse onu tekrar gösteremez.
- Live anahtarları
ck_live_vecs_live_ile başlar. Sandbox anahtarlarıck_test_vecs_test_ile başlar. Bir ortamın anahtarı diğerinde asla çalışmaz. - API, ancak CardV işletmenizi onayladıktan sonra çalışır.
Onaydan sonra
GET/accountyanıtında"api_access_enabled": truegörünür.
#Başlıklar
GET istekleri dahil her istekte şu dört başlığı gönderin:
X-Key-Id: ck_live_xxxxxxxxxxxxxxxxxxxxxxxx
X-Timestamp: 1790000000
X-Nonce: 0123456789abcdef0123456789abcdef
X-Signature: e6e45d09ed271d8d721701b560d56a6f5104fd8a52aaad5380166f432bba6e89X-Key-Id, anahtarınızın herkese açık kimliğidir.X-Timestamp, saniye cinsinden şu anki Unix zamanıdır. CardV'nin saatinden en fazla 5 dakika (300 saniye) farklı olabilir.X-Nonce, bir daha asla kullanmayacağınız rastgele bir değerdir. 32 karakterlik rastgele bir hex değer kullanın.X-Signature, İstek imzalama bölümünde anlatılıyor. Küçük harfli hex olmalıdır.
İsterseniz X-Merchant-Id da gönderebilirsiniz. Gönderirseniz anahtarla eşleşmelidir.
#İstek imzalama
İstek gövdenizi bir kez bayt dizisine çevirin. İmzalayacağınız ve göndereceğiniz bayt dizisi tam olarak budur.
GETisteğinin gövdesi boştur.Gövdenin özetini alın:
BODY_HASH= gövdenin SHA-256 özeti, küçük harfli hex olarak. Boş gövde için bu değere3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855olur.Şu beş satırı yeni satır karakteriyle (
\n) birleştirin. Sonuna yeni satır eklemeyin:<METHOD> <PATH with query string> <X-Timestamp> <X-Nonce> <BODY_HASH>METHODbüyük harfle yazılır, örneğinGETveyaPOST. Yol, sorgu dizesini tam olarak gönderdiğiniz haliyle içerir, örneğin/api/v1/skus?limit=50.Metni imzalayın:
X-Signature= bu metnin HMAC-SHA256 değeri. Anahtar olarak imzalama sırrınızı kullanın. Sonucu küçük harfli hex olarak yazın.
Mobil hat yükleme istekleri de aynı şekilde imzalanır, ancak kendi yollarıyla, örneğin /api/v1/recharge/orders.
En sık yapılan hata, JSON'un bir halini imzalayıp başka bir halini göndermektir.
Örneğin {"a":1} ile {"a": 1} farklı bayt dizileridir.
Kullandığınız HTTP kütüphanesinin imzaladıktan sonra gövdeyi değiştirmediğinden veya sorgu dizesini yeniden sıralamadığından emin olun.
Test verisi. Kodunuzu bu değerlerle kontrol edin. Sır sahtedir.
Signing secret cs_test_TEST_ONLY_not_a_real_secret_0123456789abcdef
X-Timestamp 1790000000
X-Nonce 0123456789abcdef0123456789abcdefBir GET isteği:
Method GET
Path /api/v1/skus?limit=50
BODY_HASH e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
X-Signature 58f0362c355a6624fff9f0b84d47591c570263908832294375b7bf27731628e5/api/v1/orders adresine şu gövdeyle bir POST isteği (106 bayt, tek satır, sonunda yeni satır yok):
{"external_order_id":"TEST-0001","items":[{"sku_id":"S000001","quantity":1,"expected_unit_price":"9.25"}]}BODY_HASH 5814ffcdadfa9eb69348150f9fd6895c00b4c37cf6cd0202b83174354645023e
X-Signature e6e45d09ed271d8d721701b560d56a6f5104fd8a52aaad5380166f432bba6e89#Kod örnekleri
Tüm örnekler test verisindeki sonuçları üretir. Sırrı koda yazmayın, gizli bilgi deponuzdan okuyun.
cURL (bash + OpenSSL)
BASE_URL="https://sandbox.cardv.net"
REQ_PATH="/api/v1/orders" # do not call this variable PATH
BODY='{"external_order_id":"SHOP-10001","items":'
BODY+='[{"sku_id":"S000001","quantity":1,"expected_unit_price":"9.25"}]}'
TS=$(date +%s)
NONCE=$(openssl rand -hex 16)
BODY_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | sed 's/^.*= //')
SIG=$(printf 'POST\n%s\n%s\n%s\n%s' "$REQ_PATH" "$TS" "$NONCE" "$BODY_HASH" \
| openssl dgst -sha256 -hmac "$CARDV_SIGNING_SECRET" -hex | sed 's/^.*= //')
curl -sS -X POST "$BASE_URL$REQ_PATH" \
-H "Content-Type: application/json" \
-H "X-Key-Id: $CARDV_KEY_ID" \
-H "X-Timestamp: $TS" \
-H "X-Nonce: $NONCE" \
-H "X-Signature: $SIG" \
--data-raw "$BODY"-d @file yerine --data-raw kullanın. -d yeni satırları siler ve gönderilen baytlar imzayla eşleşmez.
Bir GET isteği için GET, sorgu dizesiyle birlikte yol ve boş gövdenin özetini imzalayın, gövde göndermeyin.
Python (requests)
import hashlib, hmac, json, os, secrets, time
import requests
BASE_URL = "https://sandbox.cardv.net"
KEY_ID = os.environ["CARDV_KEY_ID"]
SIGNING_SECRET = os.environ["CARDV_SIGNING_SECRET"]
def signed_headers(method: str, path: str, body: bytes) -> dict:
ts, nonce = str(int(time.time())), secrets.token_hex(16)
body_hash = hashlib.sha256(body).hexdigest()
text = "\n".join([method, path, ts, nonce, body_hash])
signature = hmac.new(SIGNING_SECRET.encode(), text.encode(), hashlib.sha256).hexdigest()
return {"X-Key-Id": KEY_ID, "X-Timestamp": ts, "X-Nonce": nonce, "X-Signature": signature}
def cardv_get(path: str) -> requests.Response:
# Put the query string in `path` so the signed path and the sent path are identical.
return requests.get(BASE_URL + path, headers=signed_headers("GET", path, b""), timeout=30)
def cardv_post(path: str, payload: dict) -> requests.Response:
body = json.dumps(payload, separators=(",", ":"), ensure_ascii=False).encode()
headers = {**signed_headers("POST", path, body), "Content-Type": "application/json"}
return requests.post(BASE_URL + path, data=body, headers=headers, timeout=30)
resp = cardv_post("/api/v1/orders", {
"external_order_id": "SHOP-10001",
"items": [{"sku_id": "S000001", "quantity": 1, "expected_unit_price": "9.25"}],
})
print(resp.status_code, resp.json())
order_id = resp.json()["order"]["order_id"]
print(cardv_get(f"/api/v1/orders/{order_id}").json()["status"])Node.js 18+ (yerleşik fetch)
import crypto from "node:crypto";
const BASE_URL = "https://sandbox.cardv.net";
const { CARDV_KEY_ID, CARDV_SIGNING_SECRET } = process.env;
function signedHeaders(method, path, body) {
const timestamp = String(Math.floor(Date.now() / 1000));
const nonce = crypto.randomBytes(16).toString("hex");
const bodyHash = crypto.createHash("sha256").update(body).digest("hex");
const text = [method, path, timestamp, nonce, bodyHash].join("\n");
const signature = crypto.createHmac("sha256", CARDV_SIGNING_SECRET).update(text, "utf8").digest("hex");
return { "X-Key-Id": CARDV_KEY_ID, "X-Timestamp": timestamp, "X-Nonce": nonce, "X-Signature": signature };
}
async function cardvGet(path) {
const res = await fetch(BASE_URL + path, { headers: signedHeaders("GET", path, Buffer.alloc(0)) });
return { status: res.status, body: await res.json() };
}
async function cardvPost(path, payload) {
const body = Buffer.from(JSON.stringify(payload), "utf8"); // serialize once
const headers = { ...signedHeaders("POST", path, body), "Content-Type": "application/json" };
const res = await fetch(BASE_URL + path, { method: "POST", headers, body });
return { status: res.status, body: await res.json() };
}
console.log(await cardvPost("/api/v1/orders", {
external_order_id: "SHOP-10001",
items: [{ sku_id: "S000001", quantity: 1, expected_unit_price: "9.25" }],
}));
console.log(await cardvGet("/api/v1/balance"));PHP (yalnızca imzalama)
<?php
function cardv_sign(
string $secret, string $method, string $path, string $body, string $ts, string $nonce
): string {
$text = implode("\n", [$method, $path, $ts, $nonce, hash('sha256', $body)]);
return hash_hmac('sha256', $text, $secret); // lowercase hex
}
// Send exactly $body. For a GET call use $body = ''.
$body = json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$ts = (string) time();
$nonce = bin2hex(random_bytes(16));
$signature = cardv_sign($signingSecret, 'POST', '/api/v1/orders', $body, $ts, $nonce);
// Headers: X-Key-Id, X-Timestamp, X-Nonce, X-Signature#Hatalar
Bu hataların tümü HTTP 403 döner (401 değil). Tek istisna istek limitidir, o 429 döner. Yanıt gövdesi şöyle görünür:
{"detail": "Invalid HMAC signature."}| Sorun | Ne yapmalı |
|---|---|
| Bilinmeyen veya devre dışı anahtar kimliği | X-Key-Id değerini ve ortamı (Live veya Sandbox) kontrol edin. |
| API erişimi açık değil | CardV'nin işletmenizi onaylamasını bekleyin. |
| Sunucunuzun IP adresine izin verilmiyor | IP adresini Portal'daki IP izin listesine ekleyin. |
| Uç nokta yalnızca Portal'da kullanılabiliyor | Portal'ı kullanın. Anahtarla yalnızca on altı uç nokta çalışır. |
| İmza eksik veya hatalı | İmzalama kodunuzu düzeltin. Test verisiyle deneyin. |
| Zaman damgası çok eski veya çok ileri | Sunucu saatinizi senkronize edin (NTP). |
| Nonce daha önce kullanılmış | Her istekte yeni bir rastgele nonce kullanın. |
| Çok fazla istek (429) | Retry-After içindeki saniye kadar bekleyip tekrar deneyin. |
Önemli:
- Bir isteği yeniden gönderirken zaman damgası, nonce ve imzayı yeniden oluşturun. Gövde aynı kalsın. Bkz. güvenli yeniden deneme.
- İmza hatalı olsa bile nonce kullanılmış sayılır.
- CardV başarısız denemeleri kaydeder, sayıları artarsa ekibini uyarır.
#IP izin listesi
IP izin listesi, anahtarınızı yalnızca sizin sunucularınızın kullanmasını sağlar. Listeyi Portal'dan yönetirsiniz (Owner).
- Kullanmak zorunlu değildir. Hiç kural yoksa her IP adresinden gelen istek kabul edilir.
- En az bir kural varsa, diğer IP adreslerinden gelen istekler HTTP 403 alır.
- Kurallar
203.0.113.10/32(IPv4) veya2001:db8::/48(IPv6) biçimindedir. - Liste yalnızca API anahtarıyla yapılan istekler için geçerlidir, Portal girişini etkilemez.
- Listeyi açmadan önce NAT ağ geçitleri ve yedek bölgeler dahil tüm sunucu IP adreslerinizi ekleyin.
#API anahtarları
Bir API anahtarı, Kısaca API bölümündeki on altı uç noktayı kullanabilir, fazlasını değil. Buna sipariş vermek ve kodları okumak da dahildir. Bu yüzden imzalama sırrını bir Owner şifresi gibi koruyun.
Anahtarları yalnızca Owner yönetebilir: Portal'da Integrations → API keys bölümünden. Bu işlemi Live ve Sandbox için ayrı ayrı yapın.
- Oluşturun: Bir anahtar oluşturun ve ona bir ad verin. Portal, anahtar kimliğini ve imzalama sırrını gösterir. Sırrı hemen gizli bilgi deponuza kopyalayın. Sır yalnızca bu bir kez gösterilir.
- Sırrı yeniden oluşturun: İmzalama sırrını kaybederseniz yeniden oluşturun. Anahtar kimliği aynı kalır. Eski sır hemen çalışmayı bırakır; bu yüzden yeni sırrı vakit kaybetmeden tüm sunucularınıza dağıtın.
- Devre dışı bırakın: Artık ihtiyacınız olmayan anahtarı kapatın. Bu işlem anında ve kalıcı olarak uygulanır. Ardından devre dışı bırakılan anahtarı listeden kaldırabilirsiniz. CardV anahtarın geçmişini saklar.
Kesinti yaşamadan anahtar değiştirmek için:
- Yeni bir anahtar oluşturun ve sırrını kaydedin.
- Yeni anahtarı tüm sunucularınıza dağıtın.
- Eski anahtarın artık kullanılmadığını Portal'dan kontrol edin.
- Eski anahtarı devre dışı bırakın.
Anahtarları en az yılda bir kez ve erişimi olan biri ayrıldığında mutlaka değiştirin. Bir anahtarın sızmış olabileceğinden şüpheleniyorsanız önce anahtarı kapatın, sonra inceleyin.
#Eski (legacy) anahtarlar
Ekim 2026'dan önce oluşturulan anahtarlar cvb2b_ ile başlar ve eski başlıkları,
yani X-Merchant-Id ve X-Api-Key başlıklarını kullanır. Bu anahtarlarda yalnızca POST istekleri imzalanır.
Eski anahtar her istekle birlikte gönderilir, bu yüzden sızması daha kolaydır.
Live ve Sandbox artık bu anahtarları kabul etmiyor. Şimdi yeni bir anahtara geçin: Portal'da yeni bir anahtar oluşturun, kodunuzu yukarıdaki başlıklara geçirin, ardından eski anahtarı devre dışı bırakın.
Entegrasyonla ilgili sorunuz mu var? Merchant ID’nizi ve sipariş ya da istek kimliğini ekleyerek [email protected] adresine yazın.