API Reference

Webhook Signature

Ecom sign the events it sends to your endpoints by including a signature in each event’s  " X-Webhook-Signature" header. This allows you to verify that the events were sent by Ecom, not by a third party.

Ecom generates signatures using a hash-based message authentication code (HMAC) with SHA-256. To prevent downgrade attacks, you should order the data model properties alphabetic with its values then encrypt them by the secret key then compare the generated signature with Webhook Signature to make sure that this request is from our side.

  1. Order all data properties in alphabetic and case insensitive.
  2. Create one string from the data after ordering it to be like that key=value,key2=value2 ...
  3. Encode the secret key and ordered data with UTF-8.
  4. Encrypt the string using HMAC SHA-256 with the secret key from the portal in API Credential tab.
  5. Compare the signature header with the encrypted hash string. If they are equal, then the request is valid and from the Ecom side.

🚧

If the value of any property is null, remove it before apply the sorting , this step provided in the below examples

Examples :

class WebhookService {
  generateHmacSignature(payload, webhookSecretKey) {
    const payloadString = this.sortObjectKeys(payload);

    const hmac = createHmac('sha256', webhookSecretKey);
    hmac.update(payloadString);
    return hmac.digest('hex');
  }

  sortObjectKeys(obj) {
    const entries = Object.entries(obj);
    return entries.filter(([_, value]) => value !== null)  // Remove null values
     			 .map(([key, value]) => [key.toLowerCase(), value]) // lowercase keys
           .sort(([keyA], [keyB]) => keyA.localeCompare(keyB)) // sort
    		   .map(([key, value]) => `${key}=${value}`)
           .join('&');
  }
}

// Usage
const webhookService = new WebhookService();
const receivedFromEcom = {
  "timestamp": "2025-02-04T12:12:12Z",
  "eventType": "TRANSACTION_STATUS_CHANGED",
  "data": {
    "ecomId": "4011738671117962347",
    "paymentStatus": "INITIATED",
    "product": "E_API",
    "amount": "100.5050",
    "currency": "KWD",
    "ecomReference": "E_A_25A00189",
    "productPaymentToken": "353fa038-393a-4d44-b7d2-31e2f5c6ca8d",
    "merchantReference": "string",
    "customerFullName": "Ali",
    "customerPhoneCode": "+965",
    "customerPhoneNumber": "66778899",
    "paymentMethod": "KNET"
  }
};
const webhookSecretKey = 'my_secret_key';
// generate signature of data object received from ecom 
const signature = webhookService.generateHmacSignature(receivedFromEcom.data, webhookSecretKey);

// compare against signature u got from x-webhook-signature	
// if it match then everything is ok
<?php

class WebhookService {
    public function generateHmacSignature($payload, $webhookSecretKey) {
        $payloadString = $this->sortObjectKeys($payload);
        return hash_hmac('sha256', $payloadString, $webhookSecretKey);
    }

    private function sortObjectKeys($obj) {
			// Remove null values and convert keys to lowercase
       $filtered = [];
        foreach ($obj as $key => $value) {
            if ($value !== null) {
                $filtered[strtolower($key)] = $value;
            }
        }
        ksort($filtered);
        $entries = [];
        foreach ($filtered as $key => $value) {
            $entries[] = $key . '=' . (is_array($value) ? json_encode($value) : $value);
        }
        return implode('&', $entries);
    }
}

// Usage
$webhookService = new WebhookService();
$receivedFromEcom = [
    "timestamp" => "2025-02-04T12:12:12Z",
    "eventType" => "TRANSACTION_STATUS_CHANGED",
    "data" => [
        "ecomId" => "4011738671117962347",
        "paymentStatus" => "INITIATED",
        "product" => "E_API",
        "amount" => "100.5050",
        "currency" => "KWD",
        "ecomReference" => "E_A_25A00189",
        "productPaymentToken" => "353fa038-393a-4d44-b7d2-31e2f5c6ca8d",
        "merchantReference" => "string",
        "customerFullName" => "Ali",
        "customerPhoneCode" => "+965",
        "customerPhoneNumber" => "66778899",
        "paymentMethod" => "KNET",
    ]
];

$webhookSecretKey = 'my_secret_key';
$signature = $webhookService->generateHmacSignature($receivedFromEcom["data"], $webhookSecretKey);

// compare against signature u got from x-webhook-signature	
// if it match then everything is o

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Newtonsoft.Json;

public class WebhookService
{
    public string GenerateHmacSignature(Dictionary<string, object> payload, string webhookSecretKey)
    {
        string payloadString = SortObjectKeys(payload);
        using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(webhookSecretKey)))
        {
            byte[] hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payloadString));
            return BitConverter.ToString(hash).Replace("-", "").ToLower();
        }
    }

    private string SortObjectKeys(Dictionary<string, object> obj)
    {
       var sortedKeys = obj
        .Where(entry => entry.Value != null) // Remove nulls
 			  .Select(entry => new KeyValuePair<string, object>(
              entry.Key.ToLowerInvariant(), // lowercase key
              entry.Value
         ))
        .OrderBy(entry => entry.Key);

        return string.Join("&", sortedKeys.Select(entry => $"{entry.Key}={entry.Value}"));
    }
}

// Usage
class Program
{
    static void Main()
    {
        var webhookService = new WebhookService();
        var receivedFromEcom = new Dictionary<string, object>
        {
            {"timestamp", "2025-02-04T12:12:12Z"},
            {"eventType", "TRANSACTION_STATUS_CHANGED"},
            {"data", new Dictionary<string, object>
                {
                    {"ecomId", "4011738671117962347"},
                    {"paymentStatus", "INITIATED"},
                    {"product", "E_API"},
                    {"amount", "100.5050"},
                    {"currency", "KWD"},
                    {"ecomReference", "E_A_25A00189"},
                    {"productPaymentToken", "353fa038-393a-4d44-b7d2-31e2f5c6ca8d"},
                    {"merchantReference", "string"},
                    {"customerFullName", "Ali"},
                    {"customerPhoneCode", "+965"},
                    {"customerPhoneNumber", "66778899"},
                    {"paymentMethod", "KNET"}
                }
            }
        };

        string webhookSecretKey = "my_secret_key";
        string signature = webhookService.GenerateHmacSignature(
            (Dictionary<string, object>)receivedFromEcom["data"], webhookSecretKey);
      // compare against signature u got from x-webhook-signature	
			// if it match then everything is o

    }
}