Verify Webhook
Verify the integrity of webhook requests using the signature header.
Overview
To ensure the integrity of webhook data, every webhook request includes a digital signature in the header. You should verify this signature using your Secret Key before processing the request.
Verification Steps
- Get the JSON payload from the request.
- Retrieve the
signaturefrom the request headers. - Alphabetically sort the payload by keys.
- Convert the sorted payload to a JSON string.
- Generate an HMAC-SHA256 signature using your Secret Key (from API Config) as the key.
- Compare your generated signature with the received signature.
Code Examples
- PHP
- Node.js
- Python
- C# .NET
- Java
- Golang
- Kotlin
// Payload received from webhook
$payload = [
"status" => "failed",
"type" => "sale",
"amount" => "55.00",
"reference" => "DazuknyVyG",
"transaction_id" => "A49dfkqvw",
"payment_method" => "Card (VISA)",
"created_at" => "2025-02-25 05:18:56",
"processor_name" => "Trust Payments",
"currency" => "USD"
];
// Fetch API Secret Text from settings/config
$apiSecret = getenv('API_SECRET') ?: 'default_secret'; // Replace with your actual config fetching logic
// Received signature (from the request headers or body)
$receivedSignature = "your_received_signature_here"; // Replace with the actual signature
// Step 1: Sort the payload by keys alphabetically
ksort($payload);
// Step 2: Convert payload to JSON string
$payloadJson = json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
// Step 3: Generate HMAC signature using SHA-256
$generatedSignature = hash_hmac('sha256', $payloadJson, $apiSecret);
// Step 4: Verify the signature
if (hash_equals($generatedSignature, $receivedSignature)) {
echo "Signature is valid!";
} else {
echo "Signature is invalid!";
}
const crypto = require('crypto');
// Payload received from webhook
let payload = {
status: "failed",
type: "sale",
amount: "55.00",
reference: "DazuknyVyG",
transaction_id: "A49dfkqvw",
payment_method: "Card (VISA)",
created_at: "2025-02-25 05:18:56",
processor_name: "Trust Payments",
currency: "USD"
};
// Fetch API Secret Text from settings/config
const apiSecret = process.env.API_SECRET || 'default_secret'; // Replace with your actual config fetching logic
// Received signature (from the request headers or body)
const receivedSignature = "your_received_signature_here";
// Step 1: Sort the payload by keys alphabetically
payload = Object.keys(payload).sort().reduce((obj, key) => {
obj[key] = payload[key];
return obj;
}, {});
// Step 2: Convert payload to JSON string
const payloadJson = JSON.stringify(payload);
// Step 3: Generate HMAC signature using SHA-256
const generatedSignature = crypto.createHmac('sha256', apiSecret)
.update(payloadJson)
.digest('hex');
// Step 4: Verify the signature
if (generatedSignature === receivedSignature) {
console.log("Signature is valid!");
} else {
console.log("Signature is invalid!");
}
import hmac
import hashlib
import json
import os
# Payload received from webhook
payload = {
"status": "failed",
"type": "sale",
"amount": "55.00",
"reference": "DazuknyVyG",
"transaction_id": "A49dfkqvw",
"payment_method": "Card (VISA)",
"created_at": "2025-02-25 05:18:56",
"processor_name": "Trust Payments",
"currency": "USD"
}
# Fetch API Secret Text from settings/config
api_secret = os.getenv('API_SECRET', 'default_secret') # Replace with your actual config fetching logic
# Received signature (from the request headers or body)
received_signature = "your_received_signature_here"
# Step 1: Sort the payload by keys alphabetically
sorted_payload = dict(sorted(payload.items()))
# Step 2: Convert payload to JSON string
payload_json = json.dumps(sorted_payload, separators=(',', ':'))
# Step 3: Generate HMAC signature using SHA-256
generated_signature = hmac.new(
api_secret.encode('utf-8'),
payload_json.encode('utf-8'),
hashlib.sha256
).hexdigest()
# Step 4: Verify the signature
if hmac.compare_digest(generated_signature, received_signature):
print("Signature is valid!")
else:
print("Signature is invalid!")
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Newtonsoft.Json;
class Program
{
static void Main()
{
// Payload received from webhook
var payload = new Dictionary<string, string>
{
{ "status", "failed" },
{ "type", "sale" },
{ "amount", "55.00" },
{ "reference", "DazuknyVyG" },
{ "transaction_id", "A49dfkqvw" },
{ "payment_method", "Card (VISA)" },
{ "created_at", "2025-02-25 05:18:56" },
{ "processor_name", "Trust Payments" },
{ "currency", "USD" }
};
// Fetch API Secret Text from settings/config
string apiSecret = Environment.GetEnvironmentVariable("API_SECRET") ?? "default_secret"; // Replace with your actual config fetching logic
// Received signature (from the request headers or body)
string receivedSignature = "your_received_signature_here";
// Step 1: Sort the payload by keys alphabetically
var sortedPayload = payload.OrderBy(kvp => kvp.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
// Step 2: Convert payload to JSON string
string payloadJson = JsonConvert.SerializeObject(sortedPayload);
// Step 3: Generate HMAC signature using SHA-256
using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(apiSecret)))
{
byte[] hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(payloadJson));
string generatedSignature = BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
// Step 4: Verify the signature
if (generatedSignature == receivedSignature)
{
Console.WriteLine("Signature is valid!");
}
else
{
Console.WriteLine("Signature is invalid!");
}
}
}
}
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import com.fasterxml.jackson.databind.ObjectMapper;
public class SignatureVerifier {
public static void main(String[] args) throws Exception {
// Payload received from webhook
Map<String, String> payload = new HashMap<>();
payload.put("status", "failed");
payload.put("type", "sale");
payload.put("amount", "55.00");
payload.put("reference", "DazuknyVyG");
payload.put("transaction_id", "A49dfkqvw");
payload.put("payment_method", "Card (VISA)");
payload.put("created_at", "2025-02-25 05:18:56");
payload.put("processor_name", "Trust Payments");
payload.put("currency", "USD");
// Fetch API Secret Text from settings/config
String apiSecret = System.getenv("API_SECRET") != null ? System.getenv("API_SECRET") : "default_secret"; // Replace with your actual config fetching logic
// Received signature (from the request headers or body)
String receivedSignature = "your_received_signature_here";
// Step 1: Sort the payload by keys alphabetically
Map<String, String> sortedPayload = new TreeMap<>(payload);
// Step 2: Convert payload to JSON string
ObjectMapper objectMapper = new ObjectMapper();
String payloadJson = objectMapper.writeValueAsString(sortedPayload);
// Step 3: Generate HMAC signature using SHA-256
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] hashBytes = sha256_HMAC.doFinal(payloadJson.getBytes(StandardCharsets.UTF_8));
String generatedSignature = bytesToHex(hashBytes);
// Step 4: Verify the signature
if (generatedSignature.equals(receivedSignature)) {
System.out.println("Signature is valid!");
} else {
System.out.println("Signature is invalid!");
}
}
private static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
}
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"sort"
)
func main() {
// Payload received from webhook
payload := map[string]string{
"status": "failed",
"type": "sale",
"amount": "55.00",
"reference": "DazuknyVyG",
"transaction_id": "A49dfkqvw",
"payment_method": "Card (VISA)",
"created_at": "2025-02-25 05:18:56",
"processor_name": "Trust Payments",
"currency": "USD",
}
// Fetch API Secret Text from settings/config
apiSecret := os.Getenv("API_SECRET")
if apiSecret == "" {
apiSecret = "default_secret" // Replace with your actual config fetching logic
}
// Received signature (from the request headers or body)
receivedSignature := "your_received_signature_here" // Replace with the actual signature
// Step 1: Sort the payload by keys alphabetically
keys := make([]string, 0, len(payload))
for k := range payload {
keys = append(keys, k)
}
sort.Strings(keys)
sortedPayload := make(map[string]string, len(payload))
for _, k := range keys {
sortedPayload[k] = payload[k]
}
// Step 2: Convert payload to JSON string
payloadJSON, err := json.Marshal(sortedPayload)
if err != nil {
fmt.Println("Error marshalling payload:", err)
return
}
// Step 3: Generate HMAC signature using SHA-256
h := hmac.New(sha256.New, []byte(apiSecret))
h.Write(payloadJSON)
generatedSignature := hex.EncodeToString(h.Sum(nil))
// Step 4: Verify the signature
if hmac.Equal([]byte(generatedSignature), []byte(receivedSignature)) {
fmt.Println("Signature is valid!")
} else {
fmt.Println("Signature is invalid!")
}
}
import java.security.MessageDigest
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import java.util.*
import com.fasterxml.jackson.databind.ObjectMapper
fun main() {
// Payload received from webhook
val payload = mapOf(
"status" to "failed",
"type" to "sale",
"amount" to "55.00",
"reference" to "DazuknyVyG",
"transaction_id" to "A49dfkqvw",
"payment_method" to "Card (VISA)",
"created_at" to "2025-02-25 05:18:56",
"processor_name" to "Trust Payments",
"currency" to "USD"
)
// Fetch API Secret Text from settings/config
val apiSecret = System.getenv("API_SECRET") ?: "default_secret" // Replace with your actual config fetching logic
// Received signature (from the request headers or body)
val receivedSignature = "your_received_signature_here" // Replace with the actual signature
// Step 1: Sort the payload by keys alphabetically
val sortedPayload = payload.toSortedMap()
// Step 2: Convert payload to JSON string
val objectMapper = ObjectMapper()
val payloadJson = objectMapper.writeValueAsString(sortedPayload)
// Step 3: Generate HMAC signature using SHA-256
val generatedSignature = generateHmacSha256(payloadJson, apiSecret)
// Step 4: Verify the signature
if (generatedSignature == receivedSignature) {
println("Signature is valid!")
} else {
println("Signature is invalid!")
}
}
// Function to generate HMAC-SHA256 signature
fun generateHmacSha256(data: String, secret: String): String {
val mac = Mac.getInstance("HmacSHA256")
val secretKeySpec = SecretKeySpec(secret.toByteArray(Charsets.UTF_8), "HmacSHA256")
mac.init(secretKeySpec)
val hmacBytes = mac.doFinal(data.toByteArray(Charsets.UTF_8))
return bytesToHex(hmacBytes)
}
// Helper function to convert byte array to hex string
fun bytesToHex(bytes: ByteArray): String {
val hexString = StringBuilder()
for (b in bytes) {
hexString.append(String.format("%02x", b))
}
return hexString.toString()
}
Important Notes
JSON Formatting
Ensure you use standard JSON formatting with no extra spaces when generating the signature string (e.g., in Python use separators=(',', ':')).