Webhook

Webhook Authenticity Verification

To ensure data integrity, SHA-256 hashing is used. SHA-256 generates a fixed-size, unique hash from input data. This hash serves as a secure way to verify that data hasn't been altered.

When Decrypted, you are expected to compare with the other properties sent in the webhook.

Method to decode SHA-256 Hashing

Below is the C# method for decoding SHA-256 Hashing:

public PaymentNotificationModel DecryptPaymentNotification(string encryptedData, string integrationKey, string merchantCode)
{
     try {
         string decryptedData = DecryptString(encryptedData, integrationKey, merchantCode);    
         return JsonConvert.DeserializeObject<PaymentNotificationModel>(decryptedData);
     }
     catch (Exception ex)
     {
         Console.WriteLine($"Error in DecryptPaymentNotification: {ex.Message}");
         throw;
     }
}

public string DecryptString(string cipherText, string integrationKey, string merchantCode)
{
    try
    {
        byte[] cipherBytes = Convert.FromBase64String(cipherText);

        using (Aes aesAlg = Aes.Create())
        {
            aesAlg.Key = ConvertToBase64Bytes(integrationKey, 32);
            aesAlg.IV = ConvertToBase64Bytes(merchantCode, 16);
            aesAlg.Mode = CipherMode.CBC;
            aesAlg.Padding = PaddingMode.PKCS7;

            ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

            using (MemoryStream msDecrypt = new MemoryStream(cipherBytes))
            {
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                    {
                        return srDecrypt.ReadToEnd();
                    }
                }
            }
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Error in DecryptString: {ex.Message}");
        throw;
    }
}


private static byte[] ConvertToBase64Bytes(string input, int requiredLength)
{
    try
    {
        byte[] bytes = Convert.FromBase64String(input);
        return AdjustLength(bytes, requiredLength);
    }
    catch (FormatException)
    {
        byte[] bytes = Encoding.UTF8.GetBytes(input);
        return AdjustLength(bytes, requiredLength);
    }
}

private static byte[] AdjustLength(byte[] inputBytes, int requiredLength)
{
    if (inputBytes.Length == requiredLength)
    {
        return inputBytes;
    }
    else if (inputBytes.Length > requiredLength)
    {
        return inputBytes.Take(requiredLength).ToArray();
    }
    else
    {
        byte[] paddedBytes = new byte[requiredLength];
        Array.Copy(inputBytes, paddedBytes, inputBytes.Length);
        return paddedBytes;
    }
}


//Notification Class
public class PaymentNotificationModel
{
    public string Hash { get; set; }  //A URL Encoded String containing the encryption hash of the remaining properties
    public TransactionData Transaction { get; set; }
    public CustomerData Customer { get; set; }
    public OrderData Order { get; set; }
    public string Message { get; set; }
    public string Code { get; set; }
}


public class TransactionData
{
    public string MerchantCode { get; set; }
    public string PaymentReference { get; set; }
    public string MerchantReference { get; set; }
    public string SessionId { get; set; }
    public DateTimeOffset Date { get; set; }
}

public class CustomerData
{
   public CustomerAccountData Account { get; set; }
   public CustomerCardData Card { get; set; }
   public CustomerUssdData Ussd { get; set; }
}

public class OrderData
{
    public decimal Amount { get; set; }
    public string Currency { get; set; }
    public string Description { get; set; }
}

public class CustomerAccountData
{
    public string Name { get; set; }
    public string VirtualAccountNumber { get; set; }
    public string Bank { get; set; }
    public string SenderBankCode { get; set; }
    public string SenderBankName { get; set; }
    public string SenderAccountNumber { get; set; }
    public string SenderName { get; set; }
}

public class CustomerCardData
{
    public string Name { get; set; }
    public string Processor { get; set; }
    public string SenderCardNumber { get; set; }
    public string SenderName { get; set; }
}

public class CustomerUssdData
{
    public string Name { get; set; }
    public string UssdCode { get; set; }
    public string SenderPhoneNumber { get; set; }
}

Ensure you use this method to verify the authenticity of messages received via webhooks.

Integrate this comparison logic into your message validation workflow to ensure secure communication and data integrity between your application and webhook provider

Sample Webhook for Bank Transfer Transactions

{
  "hash": 'G7qXy30d2kumijJaWqt0rB9y3cP9Ic7S70PvAIrCeVVg7HqY227IqrjSgbqwGrH7kwZkaZbSMbOHNTYh3fEBlxyarP1NIhjAMmSwDSb0HFLKIirKrd9wlH2aZ3VgGu7IOYRzbhDNu1qRSOzRP9sudLiyWsewphO3tUTK5odnO7k/7C00Y/cdT8sjRarP3cYE1wPbvyxrvYz3b8o47qMJp1hjmuAJ1N6TIzdQSlhdOmCi+6u4DxGzDiwxaZmpBC+/e1exWyn2Z1EpyNIbeltXRkwH2WRKtnrNs0cx2XFotr5ZqbljydtzKle9et0Fq3YaLS3tRDQE5YtAx4g6cEMajLJpKtqvWN9JLiUM4wCuvvKNNbIHBTGtkrL48qfusyxz/Z5K20j6pu9G2ljf+48osxA8gGbZM/LS3i8oCNKpkMNpNK7HNU4jrxsftxnGWvJ9CuVDSyend4JvoR7DFn1RTfQtlUvq4zboLNRPSuIqCN0X2tf42mtAsqDYJ+/Oi/LPzivY1om2VY6geC6esRTwHFp72o/iHhgcdjfaTQ4MMb/c+LOBIFpZo3p4P7nca5+OvSA5n2WxeBSgYBwvmRGJfIP0Fo+tWkApWmTJb99m5/XUEMZe4DQb7ErME5+ziFZgD+cbx7k9P28GIAUy6+gwiofAyMT71DsD0DqIVqqNxaNHK2dUUv3EcwVuT/sNkS9MawsZQ19uWMGizQC4B2PoudjCEHMeZ5ncHSAUSCr885KpOGaipZ2QRk1cS7gNFpzltzvh1BWwN+wRHrkFlMLTuKa/Ar+OQphMQdWDbUwmIm0=',
  "transaction": {
    "merchantCode": 'tes0000449',
    "paymentReference": '5745026a75014748900d5f73a5782f2c',
    "merchantReference": '13aa51f8-e9ad-4755-aae7-e9e640a7f917',
    "sessionId": '000098345623416543789079876545',
    "date": '2024-11-28T13:06:59.16172+00:00'
  },
  "customer": {
    "account": {
      "name": 'Daddydof LTD',
      "virtualAccountNumber": '5558095312',
      "bank": 'Stanbic IBTC Bank',
      "senderBankCode": '058',
      "senderBankName": 'Guaranty Trust Bank',
      "senderAccountNumber": '0207654637',
      "senderName": 'CHAMS SWITCH'
    }
  },
  "order": { 
    "amount": 100, 
    "currency": 'NGN', 
    "description": 'Test Payment' 
  },
  "message": 'Successful',
  "code": '00'
}

Sample Webhook for Card Option


{
  "hash": 'G7qXy30d2kumijJaWqt0rB9y3cP9Ic7S70PvAIrCeVVg7HqY227IqrjSgbqwGrH7kwZkaZbSMbOHNTYh3fEBlxyarP1NIhjAMmSwDSb0HFLKIirKrd9wlH2aZ3VgGu7IOYRzbhDNu1qRSOzRP9sudLiyWsewphO3tUTK5odnO7k/7C00Y/cdT8sjRarP3cYE1wPbvyxrvYz3b8o47qMJp1hjmuAJ1N6TIzdQSlhdOmCi+6u4DxGzDiwxaZmpBC+/e1exWyn2Z1EpyNIbeltXRkwH2WRKtnrNs0cx2XFotr5ZqbljydtzKle9et0Fq3YaLS3tRDQE5YtAx4g6cEMajLJpKtqvWN9JLiUM4wCuvvKNNbIHBTGtkrL48qfusyxz/Z5K20j6pu9G2ljf+48osxA8gGbZM/LS3i8oCNKpkMNpNK7HNU4jrxsftxnGWvJ9CuVDSyend4JvoR7DFn1RTfQtlUvq4zboLNRPSuIqCN0X2tf42mtAsqDYJ+/Oi/LPzivY1om2VY6geC6esRTwHFp72o/iHhgcdjfaTQ4MMb/c+LOBIFpZo3p4P7nca5+OvSA5n2WxeBSgYBwvmRGJfIP0Fo+tWkApWmTJb99m5/XUEMZe4DQb7ErME5+ziFZgD+cbx7k9P28GIAUy6+gwiofAyMT71DsD0DqIVqqNxaNHK2dUUv3EcwVuT/sNkS9MawsZQ19uWMGizQC4B2PoudjCEHMeZ5ncHSAUSCr885KpOGaipZ2QRk1cS7gNFpzltzvh1BWwN+wRHrkFlMLTuKa/Ar+OQphMQdWDbUwmIm0=',
  "transaction": {
    "merchantCode": 'tes0000449',
    "paymentReference": '5745026a75014748900d5f73a5782f2c',
    "merchantReference": '13aa51f8-e9ad-4755-aae7-e9e640a7f917',
    "sessionId": '24120162112504276181',
    "date": '2024-11-28T13:06:59.16172+00:00'
  },
  "customer": {
    "card": {
      "name": 'Daddydof LTD',
      "processor": 'GT Card',
      "senderCardNumber": '5200*****1096',
      "senderName": 'Test Account'
    }
  },
  "order": { 
    "amount": 100, 
    "currency": 'NGN', 
    "description": 'Test Payment' 
  },
  "message": 'Successful',
  "code": '00'
}

Sample Webhook for USSD Transactions

{
  "hash": 'G7qXy30d2kumijJaWqt0rB9y3cP9Ic7S70PvAIrCeVVg7HqY227IqrjSgbqwGrH7kwZkaZbSMbOHNTYh3fEBlxyarP1NIhjAMmSwDSb0HFLKIirKrd9wlH2aZ3VgGu7IOYRzbhDNu1qRSOzRP9sudLiyWsewphO3tUTK5odnO7k/7C00Y/cdT8sjRarP3cYE1wPbvyxrvYz3b8o47qMJp1hjmuAJ1N6TIzdQSlhdOmCi+6u4DxGzDiwxaZmpBC+/e1exWyn2Z1EpyNIbeltXRkwH2WRKtnrNs0cx2XFotr5ZqbljydtzKle9et0Fq3YaLS3tRDQE5YtAx4g6cEMajLJpKtqvWN9JLiUM4wCuvvKNNbIHBTGtkrL48qfusyxz/Z5K20j6pu9G2ljf+48osxA8gGbZM/LS3i8oCNKpkMNpNK7HNU4jrxsftxnGWvJ9CuVDSyend4JvoR7DFn1RTfQtlUvq4zboLNRPSuIqCN0X2tf42mtAsqDYJ+/Oi/LPzivY1om2VY6geC6esRTwHFp72o/iHhgcdjfaTQ4MMb/c+LOBIFpZo3p4P7nca5+OvSA5n2WxeBSgYBwvmRGJfIP0Fo+tWkApWmTJb99m5/XUEMZe4DQb7ErME5+ziFZgD+cbx7k9P28GIAUy6+gwiofAyMT71DsD0DqIVqqNxaNHK2dUUv3EcwVuT/sNkS9MawsZQ19uWMGizQC4B2PoudjCEHMeZ5ncHSAUSCr885KpOGaipZ2QRk1cS7gNFpzltzvh1BWwN+wRHrkFlMLTuKa/Ar+OQphMQdWDbUwmIm0=',
  "transaction": {
    "merchantCode": 'tes0000449',
    "paymentReference": '5745026a75014748900d5f73a5782f2c',
    "merchantReference": '13aa51f8-e9ad-4755-aae7-e9e640a7f917',
    "sessionId": '24123456922502801181',
    "date": '2024-11-28T13:06:59.16172+00:00'
  },
  "customer": {
    "ussd": {
      "name": 'Chams Switch | Daddydof LTD',
      "ussdCode": '*737*000*5187#',
      "senderPhoneNumber": '0804***4346'
    }
  },
  "order": { 
    "amount": 100, 
    "currency": 'NGN', 
    "description": 'Test Payment' 
  },
  "message": 'Successful',
  "code": '00'
}

Expected Webhook Response Format

When your system receives and handles a webhook notification, it must respond with the following object to confirm successful processing:

{
    "status": "success", 
    "message": "Webhook Notification Successful"
}

Last updated