Skip to main content
Version: v1.4.x Legacy

Webhook Verification

Webhook Verification Implementation

This document outlines the implementation of the webhook verification mechanism on the merchant system. It describes how to verify webhook messages received from the Payment Gateway by using RSA signature verification to ensure data integrity and authenticity.

Overview

When a webhook is sent from the Payment Gateway, it includes a header X-Signature containing a base64-encoded RSA signature. This signature is generated using the Payment Gateway’s private key. The merchant system verifies the webhook by reconstructing a data string from the payload and using the previously obtained public key to validate the signature. If the verification succeeds, the webhook is deemed authentic and can be processed securely.

Prerequisites

  • Public Key: The merchant must first obtain the Payment Gateway's public key in PEM format. This key is used during the verification process. you can request The Payment Gateway Public Key by contacting the Payment Gateway Support

  • Dev Environment: The provided implementation example uses Node.js modules such as crypto, fs, and path.

  • Payment Gateway Specifications: Follow the Payment Gateway documentation for any additional guidelines regarding webhook format and signature generation.

Webhook Payload and Signature

Example Webhook Payload

The webhook payload is a JSON object that includes essential payment details. For instance:

{
"requestId": "20250113-212519-073",
"paymentId": "b91e8d70-1ab7-4275-85a2-61f7dbb31410",
"status": "SUCCESS",
"amount": 10000,
"currency": "IQD",
"creationDate": "2025-01-13T21:25:19"
}

X-Signature Header

Along with the payload, the webhook request includes an X-Signature header. This header carries a base64-encoded RSA signature that is used to verify the authenticity of the payload.

Signature Verification Process

The verification process involves two main steps:

Step 1: Data Preparation

A specific data string is constructed by concatenating selected fields from the payload. The process is as follows:

  1. Payment ID: Directly used (or a placeholder if missing).
  2. Amount: Converted to a string with a .000 suffix to represent the formatted amount.
  3. Currency: Directly used.
  4. Creation Date: Directly used.
  5. Status: Directly used.

Each field is separated by the pipe | character. For example, for the payload above, the resulting data string would be:

b91e8d70-1ab7-4275-85a2-61f7dbb31410|10000.000|IQD|2025-01-13T21:25:19|SUCCESS

Step 2: Signature Verification

The verification is performed using the RSA SHA256 algorithm:

  1. Decode the Signature: Convert the base64-encoded signature into a buffer.

  2. Create a Verifier: Utilize Node.js crypto module to create a verifier with the sha256 algorithm.

  3. Update the Verifier: Provide the verifier with the constructed data string.

  4. Verify the Signature: Compare the decoded signature against the data string using the public key.

If the signature is valid, the webhook is considered authentic.

Code Example

Below is the complete Node.js code implementing the webhook verification:

const crypto = require('crypto');
const fs = require('fs');
const path = require('path');

// Import the Payment Gateway public key (.PEM format)
const publicKey = fs.readFileSync(path.join('public-key.pem'), 'utf8');

// Example of received payload from the webhook message
const requestPayload = {
requestId: '20250113-212519-073',
paymentId: 'b91e8d70-1ab7-4275-85a2-61f7dbb31410',
status: 'SUCCESS',
amount: 10000,
currency: 'IQD',
creationDate: '2025-01-13T21:25:19',
};

// Received X-Signature from request headers
const receivedSignature = `<Request_X_Signature>`;

// Step 1: Prepare the data segments for signature verification
const fields = [
requestPayload.paymentId || '-',
requestPayload.amount ? requestPayload.amount.toString() + '.000' : '-',
requestPayload.currency || '-',
requestPayload.creationDate || '-',
requestPayload.status || '-',
];

// Join data segments with the symbol "|" to create a single string
const dataString = fields.join('|');

// Step 2: Verify the signature
function verifySignature(data, signature, publicKey) {
const signatureBuffer = Buffer.from(signature, 'base64');
const verifier = crypto.createVerify('sha256');
verifier.update(data);
verifier.end();
return verifier.verify(publicKey, signatureBuffer);
}

const isValid = verifySignature(dataString, receivedSignature, publicKey);

if (isValid) {
console.log('Signature is valid.');
} else {
console.log('Signature is invalid.');
}

Best Practices

  • Secure Storage: Ensure the public key is stored securely and is not exposed to unauthorized parties.

  • Error Handling: Implement robust error handling to manage scenarios where the signature verification fails. Logging: Maintain logs for both successful and failed verification attempts to assist in monitoring and debugging.

  • Data Consistency: Verify that the webhook payload structure is consistent with expectations; any deviation may result in a failed signature verification.

Conclusion

By implementing RSA signature verification as described, the merchant system can securely validate that the webhooks received from the Payment Gateway are authentic and untampered. This process significantly enhances the security of the payment processing workflow. For additional details or troubleshooting, please refer to the Payment Gateway’s official documentation or contact their support team.