Ana içeriğe geç

Webhooks

Receive real-time notifications when payment status changes occur. Webhooks are sent as HTTP POST requests to your configured endpoint.

Overview

When a payment transitions to a final state (SUCCESS, FAILED, REJECTED, or CANCELLED), we send a webhook notification to your registered URL.

Setup

  1. Please contact our support team for webhook activation.
  2. Generate a Webhook Secret Key — this is used to verify the authenticity of incoming webhooks.
  3. Copy and securely store the secret key. It is shown only once.

Webhook Headers

Every webhook request includes the following security headers:

HeaderDescriptionExample
x-request-timeUnix timestamp in milliseconds when the webhook was generated.1715150400000
x-request-signatureHMAC SHA-256 hex digest of timestampMillis:payload.5d41402abc4b2a76b9719d...
x-event-idUnique identifier for the webhook event (UUID).123e4567-e89b-12d3-a456-426614174000
x-event-typeEvent type (e.g., payment.status_changed).payment.status_changed

Webhook Payload

Webhooks are sent as POST requests with a JSON body. The body is the ApiPaymentResponse object:

{
"paymentId": "UUID",
"orderId": "string",
"amount": "number",
"installmentCount": "number",
"currency": "string",
"merchantCommission": "number",
"status": "string",
"transactionType": "string",
"paymentDate": "date",
"cardHolderName": "string",
"pan": "string",
"domInt": "string",
"cardScheme": "string",
"cardType": "string",
"cardSubType": "string",
"loyaltyCode": "string",
"externalTransactionId": "string",
"authCode": "string",
"resultCode": "string",
"resultMessage": "string",
"customerId": "string"
}

The payload is an exact representation of the ApiPaymentResponse object. Please refer to the Payment response field descriptions for more details.


Signature Verification

You must verify the x-request-signature header to ensure the request is authentic and has not been tampered with.

Verification Steps

  1. Extract the x-request-time header (epoch milliseconds).
  2. Extract the x-request-signature header (hex digest).
  3. Construct the signed content: x-request-time + ":" + raw_request_body
  4. Compute HMAC SHA-256 of the signed content using your webhook secret key.
  5. Compare the computed signature with the x-request-signature header value. If they match, the webhook is authentic.
  6. Recommended: Reject webhooks where the timestamp is older than 5 minutes to prevent replay attacks.

Verification Examples

const crypto = require('crypto');

function verifyWebhook(requestBody, timestampHeader, signatureHeader, secret) {
// 1. Construct signed content
const signedContent = timestampHeader + ':' + requestBody;

// 2. Compute expected signature
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signedContent)
.digest('hex');

// 3. Compare signatures (timing-safe)
const isValid = crypto.timingSafeEqual(
Buffer.from(signatureHeader, 'hex'),
Buffer.from(expectedSignature, 'hex')
);

// 4. Check timestamp freshness (5 minutes = 300000ms)
const age = Date.now() - parseInt(timestampHeader);
if (age > 300000) {
return false; // Too old — possible replay attack
}

return isValid;
}

// Express.js webhook handler
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const timestamp = req.headers['x-request-time'];
const signature = req.headers['x-request-signature'];
const body = req.body.toString();

if (!verifyWebhook(body, timestamp, signature, 'your_webhook_secret')) {
return res.status(401).send('Invalid signature');
}

const event = JSON.parse(body);
console.log('Payment status changed:', event.status);

// Process the webhook...

res.status(200).send('OK');
});

Retry Policy

The first delivery attempt is made immediately when the payment reaches a final state. If your endpoint does not respond with an HTTP 2xx status code, we will retry the webhook with exponential backoff:

RetryDelay After FailureCumulative Time
130 seconds~30s
21 minute~1.5m
35 minutes~6.5m
415 minutes~21.5m
51 hour~1.4h
64 hours~5.4h
712 hours~17.4h
8+24 hours~41.4h

Retries will continue for up to 48 hours after the initial event. After this period, the webhook is permanently marked as failed.


Best Practices

ipucu

Always verify webhook signatures and respond quickly to prevent unnecessary retries.

  • Respond quickly: Return a 2xx response within 5 seconds. Process the webhook data asynchronously if needed.
  • Verify signatures: Always verify the x-request-signature header using the x-request-time header before processing the webhook.
  • Handle duplicates: Webhooks may be delivered more than once. Use the x-event-id header or paymentId field to deduplicate.
  • Use HTTPS: Your webhook URL must use HTTPS.
  • Check timestamp: Reject webhooks where the timestamp is older than 5 minutes to prevent replay attacks.
  • Don't rely solely on webhooks: For critical flows, use the webhook as a notification and verify the payment status by calling the appropriate API endpoint (e.g., GET /api/payment/{paymentId} or GET /api/checkout/{checkoutId}).