Skip to content

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, …} with amount omitted — the only difference is the request body and that the response carries payment_url instead of the assigned_* fields. See section 5.5.
  • Withdraw uses the same helper: POST /paymentapi/v1/withdrawals with 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-Nonce must be unique per request — a reused nonce is rejected with 401 Nonce replay detected. The examples generate a random 32-hex-char nonce each call.
  • Clock skew: X-Timestamp must be within ±5 minutes of server time; keep your server clock NTP-synced.
  • To send a per-transaction X-Callback-URL header, remember the same-host restriction in section 4.