Callbacks
Receive real-time notifications about transaction status changes.
Overview
WiaPay sends HTTP POST requests to your callback URL whenever a transaction status changes. These callbacks contain the transaction details and a signature for verification.
Important
You may receive multiple callbacks for the same transaction (e.g., status changes from pending to processing to completed). Make sure to handle callbacks idempotently - process each unique status change only once.
Callback URL Configuration
Your callback URL is configured when you register with WiaPay. You can also specify a custom callbackUrl per transaction when creating deposit requests.
| Requirement | Description |
|---|---|
| Protocol | HTTPS only (SSL/TLS required) |
| Method | POST |
| Content-Type | application/json |
| Response | Return 2xx status code to acknowledge receipt |
| Timeout | 30 seconds |
Callback Headers
Each callback includes these headers for verification:
| Header | Description |
|---|---|
X-Signature | HMAC-SHA256 signature |
X-Timestamp | Unix timestamp in seconds |
Content-Type | application/json |
User-Agent | WiaPay-Callback/1.0 |
Deposit Callback Payload
{
"transactionId": "TXN-abc123def456",
"processId": "ORDER-12345",
"type": "deposit",
"status": "completed",
"amount": 1000,
"currency": "TRY",
"completedAt": "2024-01-15T12:15:00.000Z",
"timestamp": 1705320900
}Deposit Callback Fields
| Field | Type | Description |
|---|---|---|
transactionId | string | WiaPay transaction ID |
processId | string | Your transaction ID |
type | string | Transaction type: deposit or withdrawal |
status | string | Transaction status: completed, failed, cancelled |
amount | number | Confirmed transaction amount (may differ from requested) |
currency | string | Currency code |
completedAt | string | Completion time (ISO 8601) - only for completed status |
failureReason | string | Reason for failure - only for failed status |
timestamp | number | Callback timestamp |
Withdrawal Callback Payload
{
"transactionId": "TXN-xyz789abc123",
"processId": "WITHDRAW-12345",
"type": "withdrawal",
"status": "completed",
"amount": 5000,
"currency": "TRY",
"completedAt": "2024-01-15T12:30:00.000Z",
"timestamp": 1705321800
}Signature Verification
Always verify the callback signature to ensure the request is from WiaPay. The signature is calculated using HMAC-SHA256 with your Callback Secret.
signature = HMAC-SHA256(JSON.stringify(payload), callbackSecret)Node.js Verification Example
const crypto = require('crypto');
const express = require('express');
const app = express();
const CALLBACK_SECRET = 'your-callback-secret';
// Verify callback signature
function verifySignature(payload, signature) {
const expectedSignature = crypto
.createHmac('sha256', CALLBACK_SECRET)
.update(JSON.stringify(payload))
.digest('hex');
return signature === expectedSignature;
}
// Callback endpoint
app.post('/webhook/wiapay', express.json(), (req, res) => {
const signature = req.headers['x-signature'];
const payload = req.body;
// Verify signature
if (!verifySignature(payload, signature)) {
console.error('Invalid signature');
return res.status(401).json({ error: 'Invalid signature' });
}
// Process the callback
console.log('Received callback:', payload);
switch (payload.status) {
case 'completed':
// Credit the user's balance
creditUserBalance(payload.processId, payload.amount);
break;
case 'failed':
// Handle failed transaction
handleFailedTransaction(payload.processId, payload.failureReason);
break;
case 'cancelled':
// Handle cancelled transaction
handleCancelledTransaction(payload.processId);
break;
}
// Acknowledge receipt
res.status(200).json({ received: true });
});
app.listen(3000);PHP Verification Example
<?php
$callbackSecret = 'your-callback-secret';
// Get headers and body
$signature = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
$payload = file_get_contents('php://input');
$data = json_decode($payload, true);
// Verify signature
$expectedSignature = hash_hmac('sha256', $payload, $callbackSecret);
if (!hash_equals($expectedSignature, $signature)) {
http_response_code(401);
echo json_encode(['error' => 'Invalid signature']);
exit;
}
// Process the callback
switch ($data['status']) {
case 'completed':
// Credit the user's balance
creditUserBalance($data['processId'], $data['amount']);
break;
case 'failed':
// Handle failed transaction
handleFailedTransaction($data['processId'], $data['failureReason'] ?? null);
break;
case 'cancelled':
// Handle cancelled transaction
handleCancelledTransaction($data['processId']);
break;
}
// Acknowledge receipt
http_response_code(200);
echo json_encode(['received' => true]);Retry Policy
If your endpoint doesn't respond with a 2xx status code, WiaPay will retry the callback:
| Attempt | Delay |
|---|---|
| 1st retry | 5 minutes |
| 2nd retry | 15 minutes |
| 3rd retry | 1 hour |
Best Practices
- Always verify the signature before processing
- Return 200 OK as quickly as possible
- Process the callback asynchronously if needed
- Handle duplicate callbacks idempotently
- Log all callbacks for debugging
Transaction Statuses
| Status | Description | Action Required |
|---|---|---|
completed | Transaction completed successfully | Credit user balance / mark order as paid |
failed | Transaction failed | Show error to user, allow retry |
cancelled | Transaction cancelled by user or system | Release any holds, notify user |
expired | Transaction expired (timeout) | Release any holds, allow new transaction |