Skip to main content

Webhook

Webhooks in KidaPay allow your application to automatically receive real-time notifications whenever important events happen in the payment lifecycle.

Instead of constantly polling the API to check if an order has been paid, KidaPay will send a POST request to your server with the event details as soon as the status changes.


📌 How Webhooks Work​

  1. You configure a webhook_url in your Merchant Portal or when creating an order.
  2. Whenever an event occurs (e.g. payment.success, payment.failed, payment.cancelled), KidaPay sends an HTTP POST request to your webhook endpoint.
  3. Your server validates the request using the API Key you set in the portal.
  4. Your system processes the event (e.g. marking an order as paid, notifying the customer).

🔑 Why Use Webhooks?​

  • Real-time updates → Instantly know when a payment succeeds, fails, or is cancelled.
  • Automation → Update your order database automatically without manual checks.
  • Reliability → Even if the customer closes the payment page, you still get the final result via webhook.

Kidapay Webhook Signature Verification​

This explains how to verify webhook signatures sent by Kidapay to ensure the authenticity and integrity of webhook payloads.

Overview​

Kidapay signs all webhook payloads using HMAC-SHA256 with your API key. Each webhook request includes signature headers that you should verify before processing the payload.

Signature Headers​

Each webhook request includes the following headers:

  • x-kidapay-signature: The HMAC-SHA256 signature in the format sha256={signature}
  • x-kidapay-timestamp: Unix timestamp (in seconds) when the webhook was sent
  • Content-Type: application/json

Webhook Payload Structure​

The webhook payload contains the following fields:

{
"order_id": "string",
"merchant_order_id": "string",
"status": "string",
"payment_status": "string",
"amount": "number",
"currency": "string",
"pay_currency": "string",
"pay_amount": "number",
"pay_network": "string",
"title": "string",
"description": "string",
"callback_url": "string",
"webhook_url": "string"
}

Signature Verification Process​

Step 1: Extract Headers and Payload​

Extract the signature, timestamp, and raw JSON payload from the webhook request.

Step 2: Create Signature String​

Concatenate the timestamp and payload with a period (.) separator:

signatureString = timestamp + "." + rawJsonPayload

Step 3: Generate Expected Signature​

Using your API key, generate an HMAC-SHA256 signature:

expectedSignature = HMAC-SHA256(apiKey, signatureString)

Step 4: Compare Signatures​

Compare the expected signature with the signature from the x-kidapay-signature header (without the sha256= prefix).

Implementation Examples​

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

function verifyKidapaySignature(req, res, next) {
const signature = req.headers['x-kidapay-signature'];
const timestamp = req.headers['x-kidapay-timestamp'];
const rawBody = JSON.stringify(req.body);
const apiKey = process.env.KIDAPAY_API_KEY; // Your API key

if (!signature || !timestamp) {
return res.status(400).json({ error: 'Missing signature headers' });
}

// Extract signature (remove 'sha256=' prefix)
const receivedSignature = signature.replace('sha256=', '');

// Create signature string
const signatureString = `${timestamp}.${rawBody}`;

// Generate expected signature
const expectedSignature = crypto
.createHmac('sha256', apiKey)
.update(signatureString, 'utf8')
.digest('hex');

// Compare signatures
if (receivedSignature !== expectedSignature) {
return res.status(401).json({ error: 'Invalid signature' });
}

next();
}

// Use middleware
app.post('/webhook', express.raw({ type: 'application/json' }), verifyKidapaySignature, (req, res) => {
// Process verified webhook
const payload = JSON.parse(req.body);
console.log('Verified webhook:', payload);
res.status(200).send('OK');
});

Security Best Practices​

1. Always Verify Signatures Never process webhook payloads without verifying the signature first.

2. Use Constant-Time Comparison Use cryptographically secure comparison functions (like crypto.timingSafeEqual in Node.js or hash_equals in PHP) to prevent timing attacks.

3. Validate Timestamp Consider implementing timestamp validation to prevent replay attacks:

const TOLERANCE = 300; // 5 minutes tolerance

function isTimestampValid(timestamp) {
const webhookTime = parseInt(timestamp);
const currentTime = Math.floor(Date.now() / 1000);
return Math.abs(currentTime - webhookTime) <= TOLERANCE;
}

4. Keep API Keys Secure

  • Store API keys in environment variables
  • Never commit API keys to version control
  • Rotate API keys regularly
  • Use different API keys for different environments

5. Handle Raw Request Body Ensure you're using the raw request body exactly as received (before any parsing or modification) for signature verification.

6. Implement Idempotency Use the order_id or merchant_order_id to implement idempotent webhook processing and avoid duplicate processing.

Troubleshooting​

Common Issues​

  1. Signature Mismatch

    • Ensure you're using the raw request body
    • Verify the API key is correct
    • Check that you're including the sha256= prefix removal
    • Ensure proper string encoding (UTF-8)
  2. Missing Headers

    • Verify your webhook endpoint is receiving the signature headers
    • Check for case sensitivity in header names
  3. Timestamp Issues

    • Ensure timestamp is treated as a string, not parsed as integer
    • Check for proper concatenation format: timestamp.payload

Testing​

You can test your webhook signature verification using tools like ngrok and manually crafting test webhooks with known signatures.

Support​

If you encounter issues with webhook signature verification, please contact Kidapay support with:

  • Your API key ID (not the actual key)
  • Example webhook payload
  • Your signature verification code
  • Error messages or unexpected behavior details