Geliştiriciler/Başlarken
Kimlik doğrulama
Her API isteği Merchant ID'nizi ve API anahtarınızı taşır.
Sipariş verirken (POST/orders) ayrıca bir imza gönderilir. Böylece kimse siparişi değiştiremez veya tekrarlayamaz.
İlgili: Ürünler ve siparişler · Güvenlik · README
#Kimlik bilgileri
| Bilgi | Örnek | Gizli mi? |
|---|---|---|
| Merchant ID | M00000001 | Hayır. Kim olduğunuzu gösterir. |
| API anahtarı | cvb2b_... | Evet. Hem şifreniz hem de imzalama anahtarınızdır. |
- Merchant ID, hesabınız onaylandığında CardV tarafından verilir.
- API anahtarını Portal'da bir Owner oluşturur (bkz. API anahtarları).
- Live ve Sandbox'ın anahtarları ayrıdır. 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
Her istekte şu iki başlığı gönderin:
X-Merchant-Id: M00000001
X-Api-Key: cvb2b_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxPOST/orders isteğinde bunlara ek olarak şu üçünü de gönderin:
X-Timestamp: 1790000000
X-Nonce: 0123456789abcdef0123456789abcdef
X-Signature: fd55fa31a3b6817d5368903dbb4bad4458381e024c17e688580bac96577acfedX-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, Sipariş imzalama bölümünde anlatılıyor. Küçük harfli hex olmalıdır.
GET isteklerinde imza gerekmez.
#Sipariş imzalama
Siparişinizi bir kez JSON metnine çevirin. İmzalayacağınız ve göndereceğiniz bayt dizisi tam olarak budur.
Gövdenin özetini alın:
BODY_HASH= gövdenin SHA-256 özeti, küçük harfli hex olarak.Şu beş satırı yeni satır karakteriyle (
\n) birleştirin. Sonuna yeni satır eklemeyin:POST /api/v1/orders <X-Timestamp> <X-Nonce> <BODY_HASH>Metni imzalayın:
X-Signature= bu metnin HMAC-SHA256 değeri. Anahtar olarak API anahtarınızın tamamını kullanın. Sonucu küçük harfli hex olarak yazın.
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 gövdeyi imzaladıktan sonra değiştirmediğinden emin olun.
Test verisi. Kodunuzu bu değerlerle kontrol edin. Anahtar sahtedir.
API key cvb2b_TEST_ONLY_not_a_real_key_0123456789abcdef
Method POST
Path /api/v1/orders
X-Timestamp 1790000000
X-Nonce 0123456789abcdef0123456789abcdefGövde (108 bayt, tek satır, sonunda yeni satır yok):
{"external_order_id":"TEST-0001","items":[{"sku_id":"S000001","quantity":1,"expected_unit_price":"9.2500"}]}Beklenen sonuçlar:
BODY_HASH b0545ae25d54b219f27d8bd90e4dcbf26cf0d491f53982da67b8eef0a1a59960
X-Signature fd55fa31a3b6817d5368903dbb4bad4458381e024c17e688580bac96577acfed#Kod örnekleri
Tüm örnekler test verisindeki sonucu üretir. Anahtarı 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.2500"}]}'
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_API_KEY" -hex | sed 's/^.*= //')
curl -sS -X POST "$BASE_URL$REQ_PATH" \
-H "Content-Type: application/json" \
-H "X-Merchant-Id: $CARDV_MERCHANT_ID" \
-H "X-Api-Key: $CARDV_API_KEY" \
-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.
Python (requests)
import hashlib, hmac, json, os, secrets, time
import requests
BASE_URL = "https://sandbox.cardv.net"
MERCHANT_ID = os.environ["CARDV_MERCHANT_ID"]
API_KEY = os.environ["CARDV_API_KEY"]
AUTH = {"X-Merchant-Id": MERCHANT_ID, "X-Api-Key": API_KEY}
def sign(path: str, body: bytes, ts: str, nonce: str) -> str:
body_hash = hashlib.sha256(body).hexdigest()
text = "\n".join(["POST", path, ts, nonce, body_hash])
return hmac.new(API_KEY.encode(), text.encode(), hashlib.sha256).hexdigest()
def cardv_get(path: str, params=None) -> requests.Response:
return requests.get(BASE_URL + path, params=params, headers=AUTH, timeout=30)
def cardv_post(path: str, payload: dict) -> requests.Response:
body = json.dumps(payload, separators=(",", ":"), ensure_ascii=False).encode()
ts, nonce = str(int(time.time())), secrets.token_hex(16)
headers = {
**AUTH,
"Content-Type": "application/json",
"X-Timestamp": ts,
"X-Nonce": nonce,
"X-Signature": sign(path, body, ts, nonce),
}
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.2500"}],
})
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_MERCHANT_ID, CARDV_API_KEY } = process.env;
const AUTH = { "X-Merchant-Id": CARDV_MERCHANT_ID, "X-Api-Key": CARDV_API_KEY };
function sign(path, body, timestamp, nonce) {
const bodyHash = crypto.createHash("sha256").update(body).digest("hex");
const text = ["POST", path, timestamp, nonce, bodyHash].join("\n");
return crypto.createHmac("sha256", CARDV_API_KEY).update(text, "utf8").digest("hex");
}
async function cardvPost(path, payload) {
const body = Buffer.from(JSON.stringify(payload), "utf8"); // serialize once
const timestamp = String(Math.floor(Date.now() / 1000));
const nonce = crypto.randomBytes(16).toString("hex");
const headers = {
...AUTH,
"Content-Type": "application/json",
"X-Timestamp": timestamp,
"X-Nonce": nonce,
"X-Signature": sign(path, body, timestamp, nonce),
};
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.2500" }],
}));PHP (yalnızca imzalama)
<?php
function cardv_sign(
string $apiKey, string $path, string $body, string $ts, string $nonce
): string {
$text = implode("\n", ['POST', $path, $ts, $nonce, hash('sha256', $body)]);
return hash_hmac('sha256', $text, $apiKey); // lowercase hex
}
// Send exactly $body.
$body = json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$ts = (string) time();
$nonce = bin2hex(random_bytes(16));
$signature = cardv_sign($apiKey, '/api/v1/orders', $body, $ts, $nonce);#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ı |
|---|---|
| Merchant ID veya API anahtarı eksik ya da hatalı | İki başlığı ve ortamı 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 yedi 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 sipariş isteğinde yeni bir rastgele nonce kullanın. |
| Çok fazla istek (429) | Retry-After içindeki saniye kadar bekleyip tekrar deneyin. |
Önemli:
- Bir sipariş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 yedi uç noktayı kullanabilir, fazlasını değil. Buna sipariş vermek ve kodları okumak da dahildir. Bu yüzden anahtarı 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: Anahtara bir ad verin. Portal anahtarı henüz göstermez.
- Görüntüleyin (Reveal): CardV size 6 haneli bir kod e-postalar. Kod 10 dakika geçerlidir (5 deneme hakkı). Kodu girince anahtarın tamamı görünür. Hemen kaydedin. Her görüntüleme kayda geçer.
- 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.
Kesinti yaşamadan anahtar değiştirmek için:
- Yeni bir anahtar oluşturun ve görüntüleyin.
- 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.
Entegrasyonla ilgili sorunuz mu var? Merchant ID’nizi ve sipariş ya da istek kimliğini ekleyerek [email protected] adresine yazın.