Overview
Webhooks enable automated notifications for specific gateway events. Receive real-time updates about transaction processing, settlement status, recurring billing, chargebacks, and account activity without polling the API.
Webhooks allow your application to respond immediately to gateway events, improving automation and customer experience.
How Webhooks Work
Configure Webhook URL
Set up your webhook endpoint URL in the merchant control panel
Event Occurs
When a subscribed event occurs, the gateway sends an HTTP POST request
Receive Notification
Your server receives the webhook payload with event details
Respond with 200
Return HTTP 200 status to acknowledge receipt
Process Event
Process the event data and update your application
Configuration
Configure webhooks in the merchant control panel:
Settings > Webhooks
Webhook Settings
Setting Description URL Your webhook endpoint (must be HTTPS) Events Select which events to receive Secret Shared secret for signature verification Retry Logic Automatic retry on failure (up to 5 attempts)
Webhook URLs must use HTTPS. HTTP endpoints are not supported for security reasons.
Event Types
Transaction Events
Check Events
Recurring Events
Settlement Events
Chargeback Events
Card Updater Events
Notifications for transaction lifecycle events. Sale Success Triggered when a sale transaction is approved. {
"event" : "transaction.sale.success" ,
"transaction_id" : "123456789" ,
"amount" : "99.99" ,
"currency" : "USD" ,
"status" : "approved" ,
"timestamp" : "2025-10-30T10:30:00Z"
}
Transaction Declined Triggered when a transaction is declined. {
"event" : "transaction.declined" ,
"transaction_id" : "123456790" ,
"amount" : "50.00" ,
"decline_reason" : "Insufficient funds" ,
"timestamp" : "2025-10-30T10:31:00Z"
}
ACH/eCheck status notifications. Check Settlement Triggered when checks are prepared for deposit. {
"event" : "check.settlement" ,
"transaction_id" : "123456789" ,
"amount" : "100.00" ,
"settlement_date" : "2025-10-31" ,
"timestamp" : "2025-10-30T10:30:00Z"
}
Check Return Triggered when a check is returned/rejected. {
"event" : "check.return" ,
"transaction_id" : "123456789" ,
"amount" : "100.00" ,
"return_code" : "R01" ,
"return_reason" : "Insufficient funds" ,
"timestamp" : "2025-11-01T09:00:00Z"
}
Check Late Return Triggered for returns after standard processing windows. {
"event" : "check.late_return" ,
"transaction_id" : "123456789" ,
"amount" : "100.00" ,
"return_code" : "R10" ,
"days_late" : 5 ,
"timestamp" : "2025-11-05T09:00:00Z"
}
Subscription and recurring billing notifications. Subscription Added {
"event" : "subscription.added" ,
"subscription_id" : "SUB123456" ,
"customer_vault_id" : "CUST123" ,
"plan_id" : "PLAN001" ,
"amount" : "29.99" ,
"frequency" : "monthly" ,
"timestamp" : "2025-10-30T10:30:00Z"
}
Subscription Updated {
"event" : "subscription.updated" ,
"subscription_id" : "SUB123456" ,
"changes" : {
"amount" : "39.99" ,
"plan_id" : "PLAN002"
},
"timestamp" : "2025-10-30T11:00:00Z"
}
Subscription Deleted {
"event" : "subscription.deleted" ,
"subscription_id" : "SUB123456" ,
"reason" : "customer_request" ,
"timestamp" : "2025-10-30T12:00:00Z"
}
Recurring Payment Success {
"event" : "recurring.payment.success" ,
"subscription_id" : "SUB123456" ,
"transaction_id" : "123456789" ,
"amount" : "29.99" ,
"timestamp" : "2025-10-30T10:30:00Z"
}
Recurring Payment Failed {
"event" : "recurring.payment.failed" ,
"subscription_id" : "SUB123456" ,
"amount" : "29.99" ,
"failure_reason" : "Card declined" ,
"retry_attempt" : 1 ,
"timestamp" : "2025-10-30T10:30:00Z"
}
Batch settlement notifications. Batch Completion Triggered when a settlement batch completes. {
"event" : "batch.completed" ,
"batch_id" : "20251030" ,
"transaction_count" : 150 ,
"total_amount" : "15000.00" ,
"settlement_date" : "2025-10-31" ,
"timestamp" : "2025-10-30T23:59:59Z"
}
Batch Failure Triggered when batch processing fails. {
"event" : "batch.failed" ,
"batch_id" : "20251030" ,
"failure_reason" : "Processor communication error" ,
"timestamp" : "2025-10-30T23:59:59Z"
}
Chargeback notifications. Chargeback Received {
"event" : "chargeback.received" ,
"chargeback_id" : "CB123456" ,
"transaction_id" : "123456789" ,
"amount" : "99.99" ,
"reason_code" : "4853" ,
"reason" : "Cardholder dispute" ,
"due_date" : "2025-11-15" ,
"timestamp" : "2025-10-30T10:30:00Z"
}
Chargeback Won {
"event" : "chargeback.won" ,
"chargeback_id" : "CB123456" ,
"transaction_id" : "123456789" ,
"amount" : "99.99" ,
"timestamp" : "2025-11-20T10:30:00Z"
}
Chargeback Lost {
"event" : "chargeback.lost" ,
"chargeback_id" : "CB123456" ,
"transaction_id" : "123456789" ,
"amount" : "99.99" ,
"timestamp" : "2025-11-20T10:30:00Z"
}
Automatic Card Updater notifications. Update Success {
"event" : "card_updater.success" ,
"customer_vault_id" : "CUST123" ,
"old_expiration" : "1025" ,
"new_expiration" : "1027" ,
"timestamp" : "2025-10-30T10:30:00Z"
}
{
"event" : "card_updater.contact_customer" ,
"customer_vault_id" : "CUST123" ,
"card_last_four" : "1111" ,
"reason" : "Card expired" ,
"timestamp" : "2025-10-30T10:30:00Z"
}
Account Closed {
"event" : "card_updater.account_closed" ,
"customer_vault_id" : "CUST123" ,
"card_last_four" : "1111" ,
"timestamp" : "2025-10-30T10:30:00Z"
}
Webhook Payload Structure
All webhook payloads follow this structure:
{
"event" : "event.type.name" ,
"webhook_id" : "WH123456789" ,
"timestamp" : "2025-10-30T10:30:00Z" ,
"data" : {
// Event-specific data
},
"signature" : "sha256_hash_of_payload"
}
Common Fields
Field Description eventEvent type identifier webhook_idUnique webhook delivery ID timestampEvent occurrence time (ISO 8601) dataEvent-specific payload signatureHMAC signature for verification
Implementation Example
PHP
Node.js/Express
Python/Flask
<? php
// webhook.php
// Get the raw POST body
$payload = file_get_contents ( 'php://input' );
$data = json_decode ( $payload , true );
// Verify signature
$signature = $_SERVER [ 'HTTP_X_WEBHOOK_SIGNATURE' ];
$calculated = hash_hmac ( 'sha256' , $payload , 'YOUR_WEBHOOK_SECRET' );
if ( ! hash_equals ( $signature , $calculated )) {
http_response_code ( 401 );
die ( 'Invalid signature' );
}
// Process the event
switch ( $data [ 'event' ]) {
case 'transaction.sale.success' :
handleSuccessfulSale ( $data [ 'data' ]);
break ;
case 'subscription.added' :
handleNewSubscription ( $data [ 'data' ]);
break ;
case 'chargeback.received' :
handleChargeback ( $data [ 'data' ]);
break ;
default :
error_log ( 'Unhandled event: ' . $data [ 'event' ]);
}
// Respond with 200
http_response_code ( 200 );
echo 'OK' ;
function handleSuccessfulSale ( $transaction ) {
// Update order status
$order_id = $transaction [ 'order_id' ];
updateOrderStatus ( $order_id , 'paid' );
// Send confirmation email
sendConfirmationEmail ( $transaction );
}
?>
Security
Signature Verification
Always verify webhook signatures to ensure authenticity:
$signature = $_SERVER [ 'HTTP_X_WEBHOOK_SIGNATURE' ];
$payload = file_get_contents ( 'php://input' );
$calculated = hash_hmac ( 'sha256' , $payload , 'YOUR_WEBHOOK_SECRET' );
if ( ! hash_equals ( $signature , $calculated )) {
http_response_code ( 401 );
die ( 'Invalid signature' );
}
Never process webhooks without signature verification. This prevents malicious requests from compromising your application.
Best Practices
Configure your webhook endpoint to use HTTPS. The gateway will not send webhooks to HTTP endpoints.
Always verify the HMAC signature before processing webhook data. if ( ! hash_equals ( $expected , $actual )) {
return 401 ;
}
Return HTTP 200 within 5 seconds. Process time-consuming operations asynchronously. // Respond immediately
http_response_code ( 200 );
echo 'OK' ;
fastcgi_finish_request (); // PHP
// Process in background
processWebhookAsync ( $data );
Track webhook IDs to prevent duplicate processing. $webhook_id = $data [ 'webhook_id' ];
if ( isProcessed ( $webhook_id )) {
return 200 ; // Already processed
}
processWebhook ( $data );
markProcessed ( $webhook_id );
Log all received webhooks for debugging and audit purposes. logWebhook ([
'webhook_id' => $data [ 'webhook_id' ],
'event' => $data [ 'event' ],
'timestamp' => time (),
'payload' => $payload
]);
Retry Logic
The gateway implements automatic retry logic for failed webhook deliveries:
Initial Attempt
Webhook sent immediately when event occurs
Retry 1 (1 minute)
If no 200 response, retry after 1 minute
Retry 2 (5 minutes)
If still failing, retry after 5 minutes
Retry 3 (30 minutes)
Retry after 30 minutes
Retry 4 (2 hours)
Retry after 2 hours
Final Retry (24 hours)
Final retry attempt after 24 hours
After 5 failed attempts, the webhook is marked as failed. You can manually retry from the merchant control panel.
Testing Webhooks
Local Testing with ngrok
Use ngrok to test webhooks on your local machine:
# Start ngrok
ngrok http 3000
# Use the HTTPS URL in webhook configuration
https://abc123.ngrok.io/webhook
Manual Testing
Test your webhook endpoint manually:
curl -X POST https://yoursite.com/webhook \
-H "Content-Type: application/json" \
-H "X-Webhook-Signature: SIGNATURE_HERE" \
-d '{
"event": "transaction.sale.success",
"webhook_id": "TEST123",
"timestamp": "2025-10-30T10:30:00Z",
"data": {
"transaction_id": "123456789",
"amount": "99.99"
}
}'
Test Mode Events
In sandbox mode, trigger test events:
Merchant Control Panel > Webhooks > Test Events
Send test webhooks for each event type to verify your implementation.
Monitoring
Monitor webhook delivery in the merchant control panel:
Settings > Webhooks > Delivery Log
View:
Delivery status (success/failure)
Response codes
Retry attempts
Payload details
Error messages
Common Issues
Possible causes:
Firewall blocking gateway IPs
HTTPS certificate issues
Webhook URL misconfigured
Solution: Check server logs, verify URL, ensure HTTPS
Signature Verification Failed
Possible causes:
Wrong webhook secret
Payload modified
Encoding issues
Solution: Verify secret, use raw payload for signature
Possible causes:
Retry logic triggered
Network issues
Solution: Implement idempotency using webhook_id
Possible causes:
Processing takes too long
Server overload
Solution: Respond quickly, process asynchronously
Next Steps