Kimlik Doğrulama
Kaptan Partner API'nin veri düzlemi (/partner/1.0/...) her isteğe per-request HMAC-SHA256 imzası uygular. İmza; timestamp, HTTP metodu, istek yolu ve body içeriğine bağlıdır — böylece hem kimlik doğrulama hem de replay koruması sağlanır.
Base URL
https://<host>/partner/1.0/...
Tüm veri-düzlemi çağrıları POST + JSON gövdedir. /api gibi bir önek yoktur; yol doğrudan /partner/1.0/ ile başlar (ör. /partner/1.0/pos/card/pay).
Header'lar
| Header | Açıklama |
|---|---|
X-Api-Key | Entegrasyonun public anahtarı. Önek ok_live_ (canlı) veya ok_test_ (sandbox). ok_test_ anahtarları sandbox ortamına yönlenir. |
X-Timestamp | İstek zamanı, epoch milisaniye (unix-millis). Sunucu saatinden ±5 dakika dışındaki değerler stale timestamp ile reddedilir. |
X-Signature | Aşağıdaki algoritmayla üretilen, küçük harf hex HMAC-SHA256 imzası. |
İmza Algoritması
İmza, aşağıdaki kanonik string üzerinden hesaplanır:
1. bodyHash = hex( SHA-256( ham istek body byte'ları ) )
(boş body → boş byte dizisinin SHA-256'sı)
2. canonical = X-Timestamp + "\n" + HTTP_METHOD + "\n" + requestPath + "\n" + bodyHash
(requestPath örn. "/partner/1.0/pos/card/pay")
3. X-Signature = hex( HmacSHA256( secret, canonical ) ) // küçük harf
secret, entegrasyon oluşturulduğunda yalnızca bir kez gösterilir; sonradan tekrar alınamaz. Güvenli saklamak entegratörün sorumluluğundadır.HTTP_METHODbüyük harf (POST).requestPathquery string içermez; yalnızca yol kısmıdır.- Hex çıktılar küçük harf olmalıdır.
Replay Koruması
İmza; timestamp + body'ye bağlı olduğundan 5 dakikalık pencere içinde tekildir. Aynı X-Signature pencere içinde ikinci kez görülürse istek replay detected ile reddedilir. Her istekte yeni bir X-Timestamp kullanın (bu, imzayı da değiştirir).
IP Allowlist & Geçerlilik
Bir entegrasyon, izin verilen IP adreslerini kısıtlayabilir ve bir son kullanım (expiry) tarihi tanımlayabilir. İzinli olmayan bir IP'den gelen veya süresi geçmiş anahtarla yapılan istekler reddedilir.
Java Örneği
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.HexFormat;
String apiKey = System.getenv("KAPTAN_API_KEY"); // ok_live_... / ok_test_...
String secret = System.getenv("KAPTAN_API_SECRET"); // yalnızca create anında görülür
String method = "POST";
String path = "/partner/1.0/pos/card/pay";
String body = "{\"referenceCode\":\"ORD-1001\",\"paymentMethod\":\"CARD\",\"amount\":100.00,\"currency\":\"TRY\"}";
String timestamp = String.valueOf(System.currentTimeMillis());
// 1) bodyHash
byte[] bodyBytes = body.getBytes(StandardCharsets.UTF_8);
String bodyHash = HexFormat.of().formatHex(
MessageDigest.getInstance("SHA-256").digest(bodyBytes));
// 2) canonical string
String canonical = timestamp + "\n" + method + "\n" + path + "\n" + bodyHash;
// 3) HMAC-SHA256 (lowercase hex)
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
String signature = HexFormat.of().formatHex(
mac.doFinal(canonical.getBytes(StandardCharsets.UTF_8)));
// Header'lar: X-Api-Key: apiKey, X-Timestamp: timestamp, X-Signature: signature
Node.js Örneği
const crypto = require('crypto');
const apiKey = process.env.KAPTAN_API_KEY; // ok_live_... / ok_test_...
const secret = process.env.KAPTAN_API_SECRET; // yalnızca create anında görülür
const method = 'POST';
const path = '/partner/1.0/pos/card/pay';
const body = JSON.stringify({
referenceCode: 'ORD-1001',
paymentMethod: 'CARD',
amount: 100.0,
currency: 'TRY',
});
const timestamp = Date.now().toString();
// 1) bodyHash
const bodyHash = crypto.createHash('sha256').update(body, 'utf8').digest('hex');
// 2) canonical string
const canonical = `${timestamp}\n${method}\n${path}\n${bodyHash}`;
// 3) HMAC-SHA256 (lowercase hex)
const signature = crypto.createHmac('sha256', secret).update(canonical, 'utf8').digest('hex');
// fetch(`https://<host>${path}`, {
// method,
// headers: {
// 'X-Api-Key': apiKey,
// 'X-Timestamp': timestamp,
// 'X-Signature': signature,
// 'Content-Type': 'application/json',
// },
// body,
// });
Yeniden Kullanılabilir İstemci
Aşağıdaki yardımcılar imzayı hesaplar ve isteği gönderir. KAPTAN_API_KEY, KAPTAN_API_SECRET ve KAPTAN_BASE_URL ortam değişkenlerinden okurlar; böylece endpoint örnekleri kısa kalır. Endpoint sayfalarındaki Node.js ve Java örnekleri bu yardımcıları varsayar.
Node.js
import crypto from 'node:crypto';
const BASE = process.env.KAPTAN_BASE_URL; // https://<host>
const API_KEY = process.env.KAPTAN_API_KEY; // ok_live_... / ok_test_...
const SECRET = process.env.KAPTAN_API_SECRET; // yalnızca create anında görülür
// path örn. "/partner/1.0/pos/card/pay"
export async function kaptanRequest(path, bodyObj) {
const body = JSON.stringify(bodyObj);
const timestamp = Date.now().toString();
// 1) bodyHash
const bodyHash = crypto.createHash('sha256').update(body, 'utf8').digest('hex');
// 2) canonical string
const canonical = `${timestamp}\nPOST\n${path}\n${bodyHash}`;
// 3) HMAC-SHA256 (lowercase hex)
const signature = crypto.createHmac('sha256', SECRET).update(canonical, 'utf8').digest('hex');
const res = await fetch(BASE + path, {
method: 'POST',
headers: {
'X-Api-Key': API_KEY,
'X-Timestamp': timestamp,
'X-Signature': signature,
'Content-Type': 'application/json',
},
body,
});
if (!res.ok) {
throw new Error(`Kaptan API ${res.status}: ${await res.text()}`);
}
return res.json();
}
Java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.HexFormat;
public class KaptanClient {
private final HttpClient http = HttpClient.newHttpClient();
private final String baseUrl; // https://<host>
private final String apiKey; // ok_live_... / ok_test_...
private final String secret; // yalnızca create anında görülür
public KaptanClient(String baseUrl, String apiKey, String secret) {
this.baseUrl = baseUrl;
this.apiKey = apiKey;
this.secret = secret;
}
// path örn. "/partner/1.0/pos/card/pay"
public String post(String path, String jsonBody) throws Exception {
String timestamp = String.valueOf(System.currentTimeMillis());
// 1) bodyHash
byte[] bodyBytes = jsonBody.getBytes(StandardCharsets.UTF_8);
String bodyHash = HexFormat.of().formatHex(
MessageDigest.getInstance("SHA-256").digest(bodyBytes));
// 2) canonical string
String canonical = timestamp + "\n" + "POST" + "\n" + path + "\n" + bodyHash;
// 3) HMAC-SHA256 (lowercase hex)
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
String signature = HexFormat.of().formatHex(
mac.doFinal(canonical.getBytes(StandardCharsets.UTF_8)));
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + path))
.header("X-Api-Key", apiKey)
.header("X-Timestamp", timestamp)
.header("X-Signature", signature)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofByteArray(bodyBytes))
.build();
HttpResponse<String> response =
http.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() / 100 != 2) {
throw new RuntimeException(
"Kaptan API " + response.statusCode() + ": " + response.body());
}
return response.body();
}
}
Kullanım:
const res = await kaptanRequest('/partner/1.0/pos/card/pay', { referenceCode: 'ORD-1001' });
KaptanClient client = new KaptanClient(
System.getenv("KAPTAN_BASE_URL"),
System.getenv("KAPTAN_API_KEY"),
System.getenv("KAPTAN_API_SECRET"));
İşletme Kapsamı
İşlemler, API anahtarınızın tanımlı olduğu işletme kapsamında gerçekleşir. İstek gövdesinde ayrıca bir işletme bilgisi göndermenize gerek yoktur; kapsam API anahtarınızdan belirlenir.