Skip to main content

Webhooks

Callback Format

When a transaction's status changes, we'll send a POST request to your configured callback URL. The payload is a JSON object that contains transaction details.

important

Always process the raw JSON as received. The payload structure may be extended with additional fields in the future.

Example Callback

{
"TransactionId": "txn_mhuph5pq",
"Status": "Captured",
"Amount": 25.00,
"Currency": "USD",
"Reference": "abc_1234567890",
"DeclineReason": null,
"CreatedAt": "2025-10-03T06:29:55.7233604Z",
"ProviderTransactionId": "ps_96f1df74-252c-456a-90eb-5c6f557b47b6",
"ProviderStatus": "Captured",
"ProviderMessage": "Payment completed successfully",
"UserDefinedField1": "Custom Value 1",
"UserDefinedField2": "Custom Value 2",
"UserDefinedField3": "Custom Value 3"
}
Important

Always use the Status field as the authoritative source of truth for transaction state. While ProviderStatus and ProviderMessage provide additional context from the payment processor, your business logic should always be based on the Status field.

Decline Reasons

When a transaction has Status: "Declined" or Status: "Failed", the DeclineReason field provides details about why the transaction was not successful.

Common Decline Reasons

Decline ReasonDescription
Transaction ExpiredCustomer did not complete the payment within the allowed time window
Payment Cancelled By UserCustomer explicitly cancelled the payment
Declined By BankThe issuing bank declined the transaction
Insufficient FundsCustomer's account does not have sufficient balance
Provider ErrorThe payment processor encountered a technical error
Country Not Supported On Payment MethodThe customer's country is not supported by the selected payment method
Currency Not SupportedThe transaction currency is not supported by the payment method
Payment Method Not AvailableThe selected payment method is temporarily unavailable
Invalid Card DetailsCard number, CVV, or expiry date is invalid
Card ExpiredThe payment card has expired
3DS Authentication FailedCustomer failed 3D Secure verification
Suspected FraudTransaction flagged by fraud detection system
Daily Limit ExceededTransaction exceeds customer's daily spending limit
Restricted CardCard is restricted by the issuing bank
tip

Always display the DeclineReason to your customer when available, as it helps them understand what action to take (e.g., use a different card, contact their bank, etc.).

Security

All webhook requests are signed using your client secret. The signature is included in the X-ZepoPay-Signature header. We also include your client ID in the X-ZepoPay-Client-Id header for additional verification.

Verifying Webhooks

# Using OpenSSL to verify HMAC signature (Base64 format)
echo -n "$REQUEST_BODY" | openssl dgst -sha256 -hmac "your_client_secret" -binary | base64

Status Messages

StatusDescription
PendingThe transaction is awaiting processing
CapturedThe transaction has been successfully completed
DeclinedThe transaction was declined by the payment processor
RefundedThe transaction has been refunded to the customer
FailedThe transaction could not be processed successfully
AuthorizedThe payment has been authorized but not yet captured
ChargebackThe customer has disputed the transaction and requested a chargeback

Best Practices

1. Response Timing

  • Return HTTP 200 within 5 seconds to prevent retries
  • Acknowledge receipt immediately, process later

2. Idempotency & Deduplication

  • Check TransactionId against processed webhooks in your database
  • Handle duplicate deliveries gracefully (same transaction may trigger multiple webhooks)
  • Use database constraints or locks to prevent double-processing

3. Security Validation

  • Always verify X-ZepoPay-Signature using HMAC-SHA256 with raw body
  • Validate X-ZepoPay-Client-Id matches your credentials
  • Reject requests with invalid/missing signatures (return 400/401)

4. Data Handling

  • Parse JSON only after signature verification
  • Store raw webhook payload for audit/debugging
  • Log verification failures with timestamp and IP

5. Retry & Error Handling

  • Return 200 only after successfully queuing for processing
  • Non-200 responses trigger exponential backoff retries
  • Alert on repeated failures for same transaction

Testing Webhooks

Local Development

  • Use tunneling tools (ngrok, localtunnel, Cloudflare Tunnel) to expose localhost
  • Ensure your endpoint is publicly accessible over HTTPS
  • Configure the tunnel URL as your webhook endpoint in sandbox

Sandbox Environment

  • Create test transactions with small amounts to trigger webhook events
  • Monitor webhook delivery logs in your dashboard
  • Test all transaction states: Pending, Captured, Declined, Failed
  • Verify signature validation works with sandbox credentials

Testing Checklist

  • ✅ Signature verification passes for valid requests
  • ✅ Signature verification fails for tampered payloads
  • ✅ Duplicate webhooks are handled idempotently
  • ✅ Endpoint returns 200 within timeout window
  • ✅ Failed processing jobs retry correctly
  • ✅ Error logs capture failed verifications