Callback Configuration

POST

Configure callback URLs to receive automatic notifications when transaction status changes. This eliminates the need for polling and provides real-time updates.

Setting Up Callback URL

Method 1: Through Dashboard

  1. Login to your merchant dashboard at https://merchant.swiftrans.id
  2. Navigate to Integrations menu
  3. Find the Callback URL section
  4. Enter your callback endpoint URL
  5. Save the configuration

Method 2: Through API Integration Settings

Contact our support team to configure callback URLs programmatically through API settings.

Callback Request Format

When a transaction status changes, Swiftrans will send an HTTP POST request to your configured callback URL with the following JSON payload:

{
  "transaction_id": "100028355123792503",
  "amount": "10000",
  "status": "Success",
  "reference_id": "REFID153966210",
  "admin_fee": "3500"
}

Request Headers

Header Value Description
Content-Type application/json Request content type

Callback Parameters

Parameter Type Description
transaction_id string System generated transaction ID
amount string Transaction amount (in IDR)
status string Transaction status
reference_id string Your unique reference ID
admin_fee string Administrative fee charged

Status Values

Status Description
Success Transaction completed successfully
Failed Transaction failed

Implementing Callback Endpoint

PHP Example

<?php

// callback.php
header('Content-Type: application/json');

// Get the callback data
$input = file_get_contents('php://input');
$callbackData = json_decode($input, true);

// Validate the callback data
if (!$callbackData || !isset($callbackData['transaction_id']) || !isset($callbackData['reference_id'])) {
    http_response_code(400);
    echo json_encode(['error' => 'Invalid callback data']);
    exit;
}

// Extract callback parameters
$transactionId = $callbackData['transaction_id'];
$amount = $callbackData['amount'];
$status = $callbackData['status'];
$referenceId = $callbackData['reference_id'];
$adminFee = $callbackData['admin_fee'];

// Log the callback for debugging
error_log("Callback received: " . $input);

try {
    // Update your database with the transaction status
    updateTransactionStatus($referenceId, $status, $transactionId);

    // Handle different status types
    switch ($status) {
        case 'Success':
            handleSuccessfulTransaction($referenceId, $transactionId, $amount);
            break;

        case 'Failed':
            handleFailedTransaction($referenceId, $transactionId);
            break;

        case 'Process':
            handleProcessingTransaction($referenceId, $transactionId);
            break;
    }

    // Send success response
    http_response_code(200);
    echo json_encode(['status' => 'received']);

} catch (Exception $e) {
    // Log error
    error_log("Callback processing error: " . $e->getMessage());

    // Send error response (will trigger retry)
    http_response_code(500);
    echo json_encode(['error' => 'Processing failed']);
}

function updateTransactionStatus($referenceId, $status, $transactionId) {
    // Your database update logic here
    // Example with PDO:

    $pdo = new PDO($dsn, $username, $password);
    $stmt = $pdo->prepare("
        UPDATE transactions 
        SET status = ?, transaction_id = ?, updated_at = NOW() 
        WHERE reference_id = ?
    ");
    $stmt->execute([$status, $transactionId, $referenceId]);
}

function handleSuccessfulTransaction($referenceId, $transactionId, $amount) {
    // Handle successful transaction
    // e.g., send notification to user, update user balance, etc.

    // Send notification email
    sendNotificationEmail($referenceId, 'Transaction successful', $amount);

    // Update user balance if applicable
    // creditUserAccount($referenceId, $amount);
}

function handleFailedTransaction($referenceId, $transactionId) {
    // Handle failed transaction
    // e.g., notify user, refund if applicable, etc.

    sendNotificationEmail($referenceId, 'Transaction failed', null);
}

function handleProcessingTransaction($referenceId, $transactionId) {
    // Handle processing status
    // Usually no action needed as transaction is still in progress

    error_log("Transaction $referenceId is still processing");
}

function sendNotificationEmail($referenceId, $subject, $amount) {
    // Your email notification logic here
    error_log("Would send email for $referenceId: $subject");
}
?>

Node.js/Express Example

const express = require('express');
const app = express();

// Middleware to parse JSON
app.use(express.json());

// Callback endpoint
app.post('/callback/@php echo strtolower(App\Helpers\BrandingHelper::getBrandName()); @endphp', async (req, res) => {
    try {
        // Get callback data
        const {
            transaction_id,
            amount,
            status,
            reference_id,
            admin_fee
        } = req.body;

        // Validate required fields
        if (!transaction_id || !reference_id || !status) {
            return res.status(400).json({ error: 'Missing required fields' });
        }

        // Log callback for debugging
        console.log('Callback received:', req.body);

        // Update transaction in database
        await updateTransactionStatus(reference_id, status, transaction_id);

        // Handle different status types
        switch (status) {
            case 'Success':
                await handleSuccessfulTransaction(reference_id, transaction_id, amount);
                break;

            case 'Failed':
                await handleFailedTransaction(reference_id, transaction_id);
                break;

            case 'Process':
                await handleProcessingTransaction(reference_id, transaction_id);
                break;
        }

        // Send success response
        res.status(200).json({ status: 'received' });

    } catch (error) {
        console.error('Callback processing error:', error);

        // Send error response (will trigger retry)
        res.status(500).json({ error: 'Processing failed' });
    }
});

async function updateTransactionStatus(referenceId, status, transactionId) {
    // Your database update logic here
    // Example with a hypothetical database client:

    await db.query(`
        UPDATE transactions 
        SET status = ?, transaction_id = ?, updated_at = NOW() 
        WHERE reference_id = ?
    `, [status, transactionId, referenceId]);
}

async function handleSuccessfulTransaction(referenceId, transactionId, amount) {
    // Handle successful transaction
    console.log(`Transaction ${referenceId} completed successfully`);

    // Send notification
    await sendNotification(referenceId, 'Transaction successful', amount);

    // Update user balance or perform other business logic
    // await creditUserAccount(referenceId, amount);
}

async function handleFailedTransaction(referenceId, transactionId) {
    // Handle failed transaction
    console.log(`Transaction ${referenceId} failed`);

    await sendNotification(referenceId, 'Transaction failed', null);
}

async function handleProcessingTransaction(referenceId, transactionId) {
    // Handle processing status
    console.log(`Transaction ${referenceId} is still processing`);
}

async function sendNotification(referenceId, message, amount) {
    // Your notification logic here (email, SMS, push notification, etc.)
    console.log(`Notification for ${referenceId}: ${message}`);
}

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Callback server running on port ${PORT}`);
});

Python/Flask Example

from flask import Flask, request, jsonify
import json
import logging

app = Flask(__name__)

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

@app.route('/callback/@php echo strtolower(App\Helpers\BrandingHelper::getBrandName()); @endphp', methods=['POST'])
def handle_callback():
    try:
        # Get callback data
        callback_data = request.get_json()

        if not callback_data:
            return jsonify({'error': 'Invalid JSON data'}), 400

        # Extract parameters
        transaction_id = callback_data.get('transaction_id')
        amount = callback_data.get('amount')
        status = callback_data.get('status')
        reference_id = callback_data.get('reference_id')
        admin_fee = callback_data.get('admin_fee')

        # Validate required fields
        if not all([transaction_id, reference_id, status]):
            return jsonify({'error': 'Missing required fields'}), 400

        # Log callback
        logger.info(f"Callback received: {callback_data}")

        # Update transaction status
        update_transaction_status(reference_id, status, transaction_id)

        # Handle different status types
        if status == 'Success':
            handle_successful_transaction(reference_id, transaction_id, amount)
        elif status == 'Failed':
            handle_failed_transaction(reference_id, transaction_id)
        elif status == 'Process':
            handle_processing_transaction(reference_id, transaction_id)

        # Return success response
        return jsonify({'status': 'received'}), 200

    except Exception as e:
        logger.error(f"Callback processing error: {str(e)}")
        return jsonify({'error': 'Processing failed'}), 500

def update_transaction_status(reference_id, status, transaction_id):
    """Update transaction status in database"""
    # Your database update logic here
    logger.info(f"Updating transaction {reference_id} to status {status}")

def handle_successful_transaction(reference_id, transaction_id, amount):
    """Handle successful transaction"""
    logger.info(f"Transaction {reference_id} completed successfully")
    # Send notification, update user balance, etc.

def handle_failed_transaction(reference_id, transaction_id):
    """Handle failed transaction"""
    logger.info(f"Transaction {reference_id} failed")
    # Send notification, handle refund, etc.

def handle_processing_transaction(reference_id, transaction_id):
    """Handle processing transaction"""
    logger.info(f"Transaction {reference_id} is still processing")

if __name__ == '__main__':
    app.run(debug=True, port=5000)

Common Issues

Issue 1: Callback Not Received

Possible Causes:

  • Incorrect callback URL configuration
  • Server not accessible from internet
  • Firewall blocking requests

Solutions:

  • Verify URL accessibility with tools like curl
  • Check server logs for incoming requests
  • Ensure port is open and accessible

Issue 2: Callbacks Failing

Possible Causes:

  • Not returning HTTP 200 status
  • Processing taking too long
  • Server errors in callback handler

Solutions:

  • Always return HTTP 200 for successful processing
  • Implement timeout handling
  • Add proper error handling and logging

Issue 3: Duplicate Processing

Possible Causes:

  • Not implementing idempotency
  • Multiple callback attempts due to failures

Solutions:

  • Check transaction_id before processing
  • Use database constraints to prevent duplicates
  • Return success for already-processed callbacks