AZA-ZPayA-ZPay bank-transfer API

Partner API Reference

This reference is partner-only. It documents the signed machine-to-machine API that casino developers integrate. Non-partner endpoints are not part of this reference.

X-Api-KeyX-TimestampX-SignatureHash secret suffixPartner-scoped only

Authentication

Every request below must be signed with the casino’s apiKey, apiSecret, and hashSecret.

<timestamp>.<METHOD>.<path>.<raw_body>.<hashSecret>

For GET requests, raw_body is an empty string but the final .<hashSecret> segment is still required. Legacy signatures that omit hashSecret are rejected.

Calculate a request signature
import crypto from 'node:crypto';

const API_KEY = 'pk_live_xxx';
const API_SECRET = 'sk_live_xxx';
const HASH_SECRET = 'hs_live_xxx';

function signRequest(method, path, body = '') {
const timestamp = Math.floor(Date.now() / 1000).toString();
const canonical = [timestamp, method.toUpperCase(), path, body, HASH_SECRET].join('.');
const signature = crypto.createHmac('sha256', API_SECRET).update(canonical).digest('hex');
return {
  'content-type': 'application/json',
  'x-api-key': API_KEY,
  'x-timestamp': timestamp,
  'x-signature': signature,
};
}
<?php
$apiKey = 'pk_live_xxx';
$apiSecret = 'sk_live_xxx';
$hashSecret = 'hs_live_xxx';

function signRequest(string $method, string $path, string $body = ''): array {
  global $apiKey, $apiSecret, $hashSecret;
  $timestamp = (string) time();
  $canonical = implode('.', [$timestamp, strtoupper($method), $path, $body, $hashSecret]);
  $signature = hash_hmac('sha256', $canonical, $apiSecret);
  return [
      'content-type: application/json',
      'x-api-key: ' . $apiKey,
      'x-timestamp: ' . $timestamp,
      'x-signature: ' . $signature,
  ];
}

Customer identity fields

For both deposits and withdrawals, customer must include:

  • id: stable casino-side player ID.
  • username: casino username shown to operators.
  • fullName: player’s full legal/name-on-account value shown to operators.

Optional fields such as email, firstName, and lastName may also be sent, but they do not replace fullName.

Global amount limits

A-ZPay admins configure platform-wide TRY minimum and maximum amounts for deposits and withdrawals. Requests outside those limits are rejected before any hosted payment instructions or operator queue item is created.

  • Deposit below minimum: 400 { "error": "Deposit amount is below the platform minimum" }.
  • Deposit above maximum: 400 { "error": "Deposit amount exceeds the platform maximum" }.
  • Withdrawal below minimum: 400 { "error": "Withdrawal amount is below the platform minimum" }.
  • Withdrawal above maximum: 400 { "error": "Withdrawal amount exceeds the platform maximum" }.

Validate the amount in your cashier before calling the API so players see the allowed range before they attempt to send funds.

Requests

POST/v1/deposits

Create a TRY bank-transfer deposit and receive the hosted payment URL for the player.

signed partner APIdeposithosted URL returned
  • Required: amount, externalReference, redirectUrl, customer.id, customer.username, customer.fullName.
  • currency defaults to TRY; bank-account routing is global round-robin across all casinos.
  • The requested amount can differ from the operator-confirmed amount. Credit the player only from the signed webhook fields.
Create deposit
const path = '/v1/deposits';
const payload = {
amount: '1000.00',
currency: 'TRY',
externalReference: 'casino-deposit-1001',
redirectUrl: 'https://casino.example/cashier/deposits/1001',
customer: {
  id: 'player-42',
  username: 'luckyplayer',
  fullName: 'Ayse Yilmaz',
  email: 'player@example.com'
}
};
const body = JSON.stringify(payload);
const response = await fetch('https://api.azpay.example' + path, {
method: 'POST',
headers: signRequest('POST', path, body),
body
});
const result = await response.json();
// result.transaction.hostedUrl is the URL to open for the player.
<?php
$path = '/v1/deposits';
$payload = [
  'amount' => '1000.00',
  'currency' => 'TRY',
  'externalReference' => 'casino-deposit-1001',
  'redirectUrl' => 'https://casino.example/cashier/deposits/1001',
  'customer' => [
      'id' => 'player-42',
      'username' => 'luckyplayer',
      'fullName' => 'Ayse Yilmaz',
      'email' => 'player@example.com',
  ],
];
$body = json_encode($payload, JSON_UNESCAPED_SLASHES);
$ch = curl_init('https://api.azpay.example' . $path);
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_HTTPHEADER => signRequest('POST', $path, $body),
  CURLOPT_POSTFIELDS => $body,
  CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
// $result['transaction']['hostedUrl'] is the player URL.
POST/v1/withdrawals

Create a TRY withdrawal request for A-ZPay operator approval.

signed partner APIwithdrawaloperator reviewed
  • Required: amount, externalReference, customer.id, customer.username, customer.fullName.
  • Required payout details: withdrawalAccount.accountHolderName and withdrawalAccount.iban.
  • Approval/rejection is delivered by signed webhook; no hosted payment-page API is exposed to partners.
Create withdrawal
const path = '/v1/withdrawals';
const payload = {
amount: '750.00',
currency: 'TRY',
externalReference: 'casino-withdrawal-2001',
customer: { id: 'player-42', username: 'luckyplayer', fullName: 'Ayse Yilmaz' },
withdrawalAccount: {
  bankName: 'Garanti BBVA',
  accountHolderName: 'Ayse Yilmaz',
  iban: 'TR640006200027700006789011'
}
};
const body = JSON.stringify(payload);
const response = await fetch('https://api.azpay.example' + path, {
method: 'POST',
headers: signRequest('POST', path, body),
body
});
const result = await response.json();
<?php
$path = '/v1/withdrawals';
$payload = [
  'amount' => '750.00',
  'currency' => 'TRY',
  'externalReference' => 'casino-withdrawal-2001',
  'customer' => ['id' => 'player-42', 'username' => 'luckyplayer', 'fullName' => 'Ayse Yilmaz'],
  'withdrawalAccount' => [
      'bankName' => 'Garanti BBVA',
      'accountHolderName' => 'Ayse Yilmaz',
      'iban' => 'TR640006200027700006789011',
  ],
];
$body = json_encode($payload, JSON_UNESCAPED_SLASHES);
$ch = curl_init('https://api.azpay.example' . $path);
curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_HTTPHEADER => signRequest('POST', $path, $body), CURLOPT_POSTFIELDS => $body, CURLOPT_RETURNTRANSFER => true]);
$result = json_decode(curl_exec($ch), true);
GET/v1/transactions/:id

Return a single transaction that belongs to the authenticated casino API key.

partner scopedsingle transaction
Fetch transaction
const transactionId = 'txn_abc123';
const path = '/v1/transactions/' + encodeURIComponent(transactionId);
const response = await fetch('https://api.azpay.example' + path, {
method: 'GET',
headers: signRequest('GET', path, '')
});
const result = await response.json();
<?php
$transactionId = 'txn_abc123';
$path = '/v1/transactions/' . rawurlencode($transactionId);
$ch = curl_init('https://api.azpay.example' . $path);
curl_setopt_array($ch, [CURLOPT_HTTPHEADER => signRequest('GET', $path, ''), CURLOPT_RETURNTRANSFER => true]);
$result = json_decode(curl_exec($ch), true);
GET/partner/balance

Return the casino’s signed reconciliation balance.

partner scopedreconciliationbalance
  • Approved deposits, approved withdrawals, settled payouts, and manual adjustments contribute to the casino ledger.
  • Pending payout requests are reserved from available balance until approved or rejected.
Fetch balance
const path = '/partner/balance';
const response = await fetch('https://api.azpay.example' + path, {
method: 'GET',
headers: signRequest('GET', path, '')
});
const { balance } = await response.json();
<?php
$path = '/partner/balance';
$ch = curl_init('https://api.azpay.example' . $path);
curl_setopt_array($ch, [CURLOPT_HTTPHEADER => signRequest('GET', $path, ''), CURLOPT_RETURNTRANSFER => true]);
$balance = json_decode(curl_exec($ch), true)['balance'];
GET/partner/transactions

Return partner-scoped transaction history for reconciliation.

partner scopedhistoryfilters
  • Filters: status, type, q, from, to, page, and pageSize.
  • q searches IDs, references, usernames, full names, and other customer text.
List transactions
const query = new URLSearchParams({ type: 'deposit', status: 'approved', page: '1', pageSize: '25' });
const path = '/partner/transactions?' + query.toString();
const response = await fetch('https://api.azpay.example' + path, {
method: 'GET',
headers: signRequest('GET', path, '')
});
const { transactions, pagination } = await response.json();
<?php
$query = http_build_query(['type' => 'deposit', 'status' => 'approved', 'page' => 1, 'pageSize' => 25]);
$path = '/partner/transactions?' . $query;
$ch = curl_init('https://api.azpay.example' . $path);
curl_setopt_array($ch, [CURLOPT_HTTPHEADER => signRequest('GET', $path, ''), CURLOPT_RETURNTRANSFER => true]);
$result = json_decode(curl_exec($ch), true);

Webhooks

A-ZPay sends signed webhooks for approved/rejected deposits and withdrawals. Verify webhook signatures with the same canonical string pattern and append the same casino hashSecret as the final segment. Treat webhooks as the source of truth for player balance updates.

Deposit approvals can contain a different actual received amount than the original requested amount. Webhook payloads include:

  • requestedAmountCents: amount your cashier requested.
  • actualAmountCents: gross amount the A-ZPay operator confirmed as received. It may be lower or higher.
  • amountDifferenceCents: actualAmountCents - requestedAmountCents.
  • playerAmountCents: amount to credit after A-ZPay commission. Use this for player balance updates.
Receive and verify webhook
import crypto from 'node:crypto';

function verifyWebhook(req, rawBody) {
const timestamp = req.headers['x-azpay-timestamp'];
const signature = req.headers['x-azpay-signature'];
const path = '/webhook/azpay';
const canonical = [timestamp, 'POST', path, rawBody, HASH_SECRET].join('.');
const expected = crypto.createHmac('sha256', API_SECRET).update(canonical).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature, 'hex'), Buffer.from(expected, 'hex'))) {
  throw new Error('invalid webhook signature');
}
const event = JSON.parse(rawBody);
if (event.event === 'deposit.approved') {
  const tx = event.data;
  // requestedAmountCents and actualAmountCents can differ.
  // Credit exactly playerAmountCents.
  creditPlayer(tx.customer.id, tx.playerAmountCents, tx.transactionId);
}
}
<?php
$rawBody = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_AZPAY_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_AZPAY_SIGNATURE'] ?? '';
$path = '/webhook/azpay';
$canonical = implode('.', [$timestamp, 'POST', $path, $rawBody, $hashSecret]);
$expected = hash_hmac('sha256', $canonical, $apiSecret);
if (!hash_equals($expected, $signature)) {
  http_response_code(401);
  exit('invalid webhook signature');
}
$event = json_decode($rawBody, true);
if ($event['event'] === 'deposit.approved') {
  $tx = $event['data'];
  // requestedAmountCents and actualAmountCents can differ.
  // Credit exactly playerAmountCents.
  creditPlayer($tx['customer']['id'], $tx['playerAmountCents'], $tx['transactionId']);
}
http_response_code(200);