Webhooks
A-ZPay sends a webhook when an operator approves or rejects a transaction.
Headers
X-AZPay-Event: deposit.approved
X-AZPay-Event-Id: evt_...
X-AZPay-Timestamp: 1778940000
X-AZPay-Signature: <hmac sha256>
The webhook signature uses the same canonical string as partner API requests:
<timestamp>.POST.<webhook_path>.<raw_json_body>.<hashSecret>
Use the casino’s apiSecret as the HMAC key and append the casino’s hashSecret as the final canonical-string segment. Verify against the raw JSON body before parsing.
Verify and process a webhook
import crypto from 'node:crypto';
function verifyWebhook(req, rawBody) {
const timestamp = req.header('x-azpay-timestamp');
const signature = req.header('x-azpay-signature');
const path = '/azpay/webhook';
const canonical = [timestamp, 'POST', path, rawBody, process.env.AZPAY_HASH_SECRET].join('.');
const expected = crypto.createHmac('sha256', process.env.AZPAY_API_SECRET).update(canonical).digest('hex');
return crypto.timingSafeEqual(Buffer.from(signature, 'hex'), Buffer.from(expected, 'hex'));
}
app.post('/azpay/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
const rawBody = req.body.toString('utf8');
if (!verifyWebhook(req, rawBody)) return res.sendStatus(401);
const payload = JSON.parse(rawBody);
if (payload.status === 'approved') await applySettlement(payload);
res.sendStatus(200);
});<?php
$rawBody = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_AZPAY_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_AZPAY_SIGNATURE'] ?? '';
$path = '/azpay/webhook';
$canonical = implode('.', [$timestamp, 'POST', $path, $rawBody, getenv('AZPAY_HASH_SECRET')]);
$expected = hash_hmac('sha256', $canonical, getenv('AZPAY_API_SECRET'));
if (!hash_equals($expected, $signature)) {
http_response_code(401);
exit;
}
$payload = json_decode($rawBody, true);
if ($payload['status'] === 'approved') {
applySettlement($payload);
}
http_response_code(200);Payload
{
"transactionId": "txn_...",
"externalReference": "casino-deposit-1001",
"type": "deposit",
"status": "approved",
"amountCents": 10000,
"requestedAmountCents": 10000,
"actualAmountCents": 9900,
"amountDifferenceCents": -100,
"commissionCents": 990,
"netAmountCents": 8910,
"playerAmountCents": 8910,
"balanceImpactCents": 8910,
"currency": "TRY",
"referenceCode": "BP-A1B2C3D4"
}
For deposit.approved, amountCents and requestedAmountCents are the casino-requested amount. actualAmountCents is the gross amount confirmed by the A-ZPay operator and can be lower or higher. Credit the player by playerAmountCents after verifying the signature.
Idempotency
Store X-AZPay-Event-Id and process it once. Return a 2xx response only after your database transaction commits. A-ZPay stores delivery attempts and can retry failed events.