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.

RequirementDescription
ProtocolHTTPS only (SSL/TLS required)
MethodPOST
Content-Typeapplication/json
ResponseReturn 2xx status code to acknowledge receipt
Timeout30 seconds

Callback Headers

Each callback includes these headers for verification:

HeaderDescription
X-SignatureHMAC-SHA256 signature
X-TimestampUnix timestamp in seconds
Content-Typeapplication/json
User-AgentWiaPay-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

FieldTypeDescription
transactionIdstringWiaPay transaction ID
processIdstringYour transaction ID
typestringTransaction type: deposit or withdrawal
statusstringTransaction status: completed, failed, cancelled
amountnumberConfirmed transaction amount (may differ from requested)
currencystringCurrency code
completedAtstringCompletion time (ISO 8601) - only for completed status
failureReasonstringReason for failure - only for failed status
timestampnumberCallback 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:

AttemptDelay
1st retry5 minutes
2nd retry15 minutes
3rd retry1 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

StatusDescriptionAction Required
completedTransaction completed successfullyCredit user balance / mark order as paid
failedTransaction failedShow error to user, allow retry
cancelledTransaction cancelled by user or systemRelease any holds, notify user
expiredTransaction expired (timeout)Release any holds, allow new transaction