Webhook Security

Integrators can utilise a client key to verify a webhook's authenticity. The client key is used to digitally sign the payloads, and the signature is used to confirm the webhook's authenticity, further protecting your platform from malicious activities.

Register a client key

To receive secure requests, register a client key via:

Create Webhook API

  • add clientKey parameter in the request body.
{
  "url": "https://companyName.net/callback",
  "eventTypes": [
    "invoice_created",
    "customer_create",
    "subscription_activate",
    "subscription_payment_reactivate",
    "payment_method_invalid",
    "payment_method_replaced",
    "invoice_paid",
    "invoice_past_due",
    "subscription_create",
    "subscription_cancel",
    "invoice_batch_created",
    "invoice_batch_processing",
    "invoice_batch_success",
    "invoice_batch_invoice_failed"
  ],
  "clientKey": "{{clientKey}}"
}

Update Webhook API

  • set updateSecurity to true in request body.
  • add clientKey parameter in request body.
{
  "webhookId": "2c4e8865-1e9f-4dcb-88d4-c36f3cc9ea83",
  "clientKey": "{{clientKey}}",
  "updateSecurity": true  
}

Verifying webhook authenticity

Ezypay uses HMAC SHA-1 to compute a signature, using the client key and request payload as arguments. The signature is then included in the X-Ezypay-Signature header.

To verify the signature:

  1. Retrieve X-Ezypay-Signature from headers.
  2. Obtain the raw payload body from the HTTP request.
  3. Compute the HMAC SHA-1 hash string using the client key and raw payload.
  4. Compare this string to the X-Ezypay-Signature.

Code examples on computing HMAC SHA-1 hash from some common languages are as follows.

import org.apache.commons.codec.digest.HmacUtils;

public class Example {
  public String computeSignature(String clientKey, String payload) {
    return HmacUtils.hmacSha1Hex(clientKey, payload);
  }
}
using System.Security.Cryptography;

namespace Example {
    public class Signature {
        public string computeSignature(string clientKey, string payload) {
            byte[] clientKeyBytes = System.Text.Encoding.UTF8.GetBytes(clientKey);
            byte[] payloadBytes = System.Text.Encoding.UTF8.GetBytes(payload);
            
            HMACSHA1 hmac = new HMACSHA1(clientKeyBytes);
            byte[] hashValue = hmac.ComputeHash(payloadBytes);
          
            return BitConverter.ToString(hashValue).ToLower().Replace("-", "");
        }
    }
}
function computeSignature(clientKey, payload) {
  var crypto = require('crypto');
  var hmac = crypto.createHmac('sha1', clientKey).update(payload);
  return hmac.digest('hex');
}
import hashlib
import hmac

def computeSignature(clientKey, payload):
  utf8 = "utf-8"
  clientKeyBytes = bytes(clientKey).encode(utf8)
  payloadBytes = bytes(payload).encode(utf8)
  computedHmac = hmac.new(clientKeyBytes, payloadBytes, digestmod=hashlib.sha1)
  
  return computedHmac.digest().encode("hex")
<?php
  function computeSignature($clientKey, $payload) {
    return hash_hmac('sha1', $payload, $clientKey, false);
  }
?>
require 'openssl'

def computeSignature(clientKey, payload)
  digest = OpenSSL::Digest.new('sha1')
  return OpenSSL::HMAC.hexdigest(digest, clientKey, payload)
end
use Digest::HMAC_SHA1;

sub compute_signature {
  my ($clientKey, $payload) = @_;
  my $hmac = Digest::HMAC_SHA1->new($clientKey);
  $hmac->add($payload);
  
  return $hmac->hexdigest;
}

The payload is the raw body of the HTTP request, meaning it is the JSON data in its raw form. Do not parse, re-encode, or manipulate it in any way; use it as is.

Testing signature verification

You can test the signature verification process using the following reference example:

  • client key = key
  • payload = {"requestId":"4cb74646-4d30-4d6a-ac71-7a26a4a623d3","merchantId":"aad25d2b-85f2-40cf-8b6d-44b5439a220f","eventType":"INVOICE_BATCH_CREATED","createdOn":"2024-07-11T06:42:28.296","data":{"id":"26055389-927b-41b9-bc13-77a50887db5c","batchReference":"tyj56","createdOn":"2024-07-11T06:42:28.135","status":"SUBMITTED"}}

The hexadecimal HMAC SHA-1 produces:

  • Expected output = 6354ecd501ca4c87da2b42872949c7fa02fefd89