Sample Clients¶
10. Sample Client Code¶
Each example signs and sends a request, covering both POST (JSON body) and GET (empty body — the BODY line in the signed message is an empty string). The same helper works for every /paymentapi/v1/* endpoint; only the path and payload change. Callback verification examples are in section 6.2.
10.1 PHP¶
<?php
const BASE_URL = 'https://api.360cuzdan.com';
const API_KEY = '<api-key>';
const SECRET_KEY = '<secret-key>';
function cuzdan360Request(string $method, string $path, ?array $payload = null): array
{
$body = $payload === null ? '' : json_encode($payload, JSON_UNESCAPED_SLASHES);
$timestamp = (string) time();
$nonce = bin2hex(random_bytes(16));
// METHOD \n PATH \n BODY \n TIMESTAMP \n NONCE
$message = implode("\n", [$method, $path, $body, $timestamp, $nonce]);
$signature = hash_hmac('sha256', $message, SECRET_KEY);
$ch = curl_init(BASE_URL . $path);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-API-Key: ' . API_KEY,
'X-Signature: ' . $signature,
'X-Timestamp: ' . $timestamp,
'X-Nonce: ' . $nonce,
],
]);
if ($body !== '') {
curl_setopt($ch, CURLOPT_POSTFIELDS, $body); // send the exact string you signed
}
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
// Deposit
$deposit = cuzdan360Request('POST', '/paymentapi/v1/deposits', [
'amount' => '1000',
'bank_code' => '1',
'first_name' => 'Test',
'last_name' => 'User',
'username' => 'testuser',
'user_id' => 'user001',
'reference' => 'TX-DEP-001',
'method' => 'fast',
]);
if ($deposit['success']) {
// show assigned_bank_name / assigned_holder_name / assigned_iban to the user
} else {
// $deposit['message'], e.g. "Bu tutar icin uygun hesap yok"
}
// Status check (reconciliation)
$status = cuzdan360Request('GET', '/paymentapi/v1/transactions/by-reference/TX-DEP-001');
10.2 Node.js (18+)¶
const crypto = require('crypto')
const BASE_URL = 'https://api.360cuzdan.com'
const API_KEY = '<api-key>'
const SECRET_KEY = '<secret-key>'
async function cuzdan360Request(method, path, payload) {
const body = payload ? JSON.stringify(payload) : ''
const timestamp = Math.floor(Date.now() / 1000).toString()
const nonce = crypto.randomBytes(16).toString('hex')
// METHOD \n PATH \n BODY \n TIMESTAMP \n NONCE
const message = [method, path, body, timestamp, nonce].join('\n')
const signature = crypto.createHmac('sha256', SECRET_KEY).update(message).digest('hex')
const res = await fetch(BASE_URL + path, {
method,
headers: {
'Content-Type': 'application/json',
'X-API-Key': API_KEY,
'X-Signature': signature,
'X-Timestamp': timestamp,
'X-Nonce': nonce,
},
body: body || undefined, // send the exact string you signed
})
return res.json()
}
// Deposit
const deposit = await cuzdan360Request('POST', '/paymentapi/v1/deposits', {
amount: '1000',
bank_code: '1',
first_name: 'Test',
last_name: 'User',
username: 'testuser',
user_id: 'user001',
reference: 'TX-DEP-001',
method: 'fast',
})
// Status check (reconciliation)
const status = await cuzdan360Request('GET', '/paymentapi/v1/transactions/by-reference/TX-DEP-001')
10.3 Python¶
import hashlib
import hmac
import json
import secrets
import time
import requests
BASE_URL = "https://api.360cuzdan.com"
API_KEY = "<api-key>"
SECRET_KEY = "<secret-key>"
def cuzdan360_request(method: str, path: str, payload: dict | None = None) -> dict:
body = "" if payload is None else json.dumps(payload, separators=(",", ":"))
timestamp = str(int(time.time()))
nonce = secrets.token_hex(16)
# METHOD \n PATH \n BODY \n TIMESTAMP \n NONCE
message = "\n".join([method, path, body, timestamp, nonce])
signature = hmac.new(SECRET_KEY.encode(), message.encode(), hashlib.sha256).hexdigest()
resp = requests.request(
method,
BASE_URL + path,
headers={
"Content-Type": "application/json",
"X-API-Key": API_KEY,
"X-Signature": signature,
"X-Timestamp": timestamp,
"X-Nonce": nonce,
},
data=body or None, # send the exact string you signed
timeout=30,
)
return resp.json()
# Deposit
deposit = cuzdan360_request("POST", "/paymentapi/v1/deposits", {
"amount": "1000",
"bank_code": "1",
"first_name": "Test",
"last_name": "User",
"username": "testuser",
"user_id": "user001",
"reference": "TX-DEP-001",
"method": "fast",
})
# Status check (reconciliation)
status = cuzdan360_request("GET", "/paymentapi/v1/transactions/by-reference/TX-DEP-001")
Notes¶
- The examples show the direct flow (they send
amount) because it exercises the signing helper end to end in one call. For the recommended hosted flow, keep the same helper and send{"redirect": true, …}withamountomitted — the only difference is the request body and that the response carriespayment_urlinstead of theassigned_*fields. See section 5.5. - Withdraw uses the same helper:
POST /paymentapi/v1/withdrawalswith the fields from section 5.2. - Sign exactly what you send. All three helpers serialize the payload once and use the same string for both the signature and the request body.
X-Noncemust be unique per request — a reused nonce is rejected with 401Nonce replay detected. The examples generate a random 32-hex-char nonce each call.- Clock skew:
X-Timestampmust be within ±5 minutes of server time; keep your server clock NTP-synced. - To send a per-transaction
X-Callback-URLheader, remember the same-host restriction in section 4.