Ana içeriğe geç

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

HeaderAçıklama
X-Api-KeyEntegrasyonun 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-SignatureAş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_METHOD büyük harf (POST).
  • requestPath query 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.