Skip to content

Instantly share code, notes, and snippets.

@divyangkhatri
Last active September 27, 2025 09:44
Show Gist options
  • Select an option

  • Save divyangkhatri/06ec6aa89ece137e65861cb1ba1cee78 to your computer and use it in GitHub Desktop.

Select an option

Save divyangkhatri/06ec6aa89ece137e65861cb1ba1cee78 to your computer and use it in GitHub Desktop.
kimlpay webhook docs

KimlPay Webhook Documentation

Overview

KimlPay sends HTTP POST requests to your configured webhook endpoints when payment events occur. Each webhook request includes a cryptographic signature that you can use to verify the authenticity of the request.

Webhook Events

Webhooks are triggered for payment transactions with the following statuses:

Success Events

  • completed - Payment has been successfully completed
  • payment-authorized - Payment has been authorized
  • paid - Payment has been paid

Failure Events

  • cancelled - Payment was cancelled by the user
  • rejected - Payment was rejected
  • payment-error - An error occurred during payment processing

Note: Webhooks are NOT sent for transactions with started status.

Webhook Payload

Each webhook request contains the following JSON payload:

{
  "transaction_id": "string",
  "payment_link_id": "string",
  "pl_id": "string",
  "status": "success|failed",
  "total_fees": "string",
  "total_amount": {
    "amount": "string",
    "currency": "string"
  },
  "received_amount": {
    "amount": "string",
    "currency": "string"
  },
  "user_info": {
    "email": "string"
  },
  "payment_info": "object"
}

Field Descriptions

Field Type Description
transaction_id string transaction request id
payment_link_id string Internal payment link identifier
pl_id string Public payment link ID
status string Payment status: success or failed
total_fees string Total fees charged
total_amount.amount string Original payment amount
total_amount.currency string Original payment currency
received_amount.amount string Amount received after fees
received_amount.currency string Received amount currency
user_info.email string Payer's email address
payment_info object Additional payment details about payment method

Request Headers

Each webhook request includes the following headers:

Content-Type: application/json
X-Request-Signature: <base64-encoded-signature>

Signature Verification

Overview

Each webhook request includes a cryptographic signature in the X-Request-Signature header. This signature is generated using RSA-SHA256 and your private key, allowing you to verify that the request originated from KimlPay.

Verification Process

Use the following verifySignature function to verify incoming webhooks:

const crypto = require('crypto');

function verifySignature(payload, signature, publicKey) {
  try {
    // Create a verify object
    const verify = crypto.createVerify('RSA-SHA256');

    // Convert payload to string if it's an object
    let payloadString;
    if (typeof payload === 'object') {
      // Use JSON.stringify with consistent ordering for objects
      payloadString = JSON.stringify(payload);
    } else {
      payloadString = String(payload);
    }

    // Add the original payload data
    verify.update(payloadString, 'utf8');

    // Verify the signature using public key
    const isValid = verify.verify(publicKey, signature, 'base64');

    return isValid;
  } catch (error) {
    console.error('Error verifying signature:', error.message);
    return false;
  }
}

Implementation Example

Here's how to verify webhooks in your endpoint:

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

// Your public key (corresponding to the private key configured in KimlPay)
const PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
YOUR_PUBLIC_KEY_HERE
-----END PUBLIC KEY-----`;

app.use(express.json());

app.post('/webhook', (req, res) => {
  const signature = req.headers['x-request-signature'];
  const payload = req.body;

  // Verify the signature
  const isValid = verifySignature(payload, signature, PUBLIC_KEY);

  if (!isValid) {
    console.log('Invalid signature - webhook rejected');
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Process the verified webhook
  console.log('Webhook verified successfully:', payload);

  // Handle the payment event
  switch (payload.status) {
    case 'success':
      // Handle successful payment
      console.log(`Payment completed: ${payload.pl_id}`);
      break;
    case 'failed':
      // Handle failed payment
      console.log(`Payment failed: ${payload.pl_id}`);
      break;
    default:
      console.log(`Unknown status: ${payload.status}`);
  }

  // Always respond with 200 to acknowledge receipt
  res.status(200).json({ received: true });
});

Security Best Practices

1. Always Verify Signatures

Never process webhook requests without verifying the signature first. This prevents malicious requests from being processed.

2. Use HTTPS Endpoints

Always configure your webhook URLs to use HTTPS to ensure data transmission is encrypted.

3. Validate Payload Data

Even after signature verification, validate that the payload data meets your expected format and business rules.

4. Implement Idempotency

KimlPay may occasionally send duplicate webhooks. Implement idempotency using the transaction_id to prevent duplicate processing.

5. Respond Promptly

Respond to webhook requests within 60 seconds. KimlPay considers requests that take longer than 60 seconds as failed.

6. Handle Errors Gracefully

If your endpoint returns a non-200 status code, KimlPay may retry the webhook. Ensure your endpoint can handle retries safely.

Response Requirements

Your webhook endpoint must:

  1. Return HTTP 200 - Any other status code is considered a failure
  2. Respond within 60 seconds - Longer responses are considered timeouts
  3. Handle retries - Failed webhooks may be retried

Example response:

res.status(200).json({ received: true });

Troubleshooting

Common Issues

  1. Signature Verification Fails

    • Ensure you're using the correct public key
    • Verify the payload is being processed as received (no modifications)
    • Check that you're using the correct signature from the header
  2. Webhook Not Received

    • Verify your endpoint URL is accessible from the internet
    • Check that your server is responding within 60 seconds
    • Ensure your endpoint accepts POST requests
  3. Duplicate Webhooks

    • Implement idempotency checking using transaction_id
    • Store processed webhook IDs to prevent reprocessing

Debugging Tips

  • Log both successful and failed verification attempts
  • Test signature verification with known good payloads
  • Use webhook testing tools to simulate requests
  • Monitor your endpoint's response times and error rates

Support

If you encounter issues with webhook integration, please contact KimlPay support with:

  • Your webhook endpoint URL
  • Sample webhook payload (remove sensitive data)
  • Error messages or logs
  • Timestamp of the issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment