Signature Key Verification

This section provides implementation examples for verifying the authenticity of requests using a signature key, which helps ensure the integrity and security of the received data (e.g., webhook events). The signature is usually included in the request headers and must be validated by computing a hash using a shared secret key. Below are examples in different languages.

.NET

using System.Security.Cryptography;
using System.Text;

var jsonMessage = "[CALLBACK MESSAGE]";

var headers = new Dictionary<string, string>()
{
    { "X-Signature", $"sha256=yu2OvBe3Gyq1Nz/4R6KO8F3KpGCuW7VhH9yUPhYtNRU="},
    { "X-Signature-Timestamp", "1762181943494" }
};

var signature = headers.GetValueOrDefault("X-Signature")!["sha256=".Length..];

var signatureKey = "4cde378d-43b6-405f-94aa-55c010d4d42a";

var unixTimeMilliseconds = headers.GetValueOrDefault("X-Signature-Timestamp");

string message = $"{jsonMessage}.{unixTimeMilliseconds}";

using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(signatureKey));

var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(message));
        
var result = Convert.ToBase64String(hash);

if (result.Equals(signature))
    Console.WriteLine("Signature is valid");
else
    Console.WriteLine("INVALID SIGNATURE!");

.PHP

<?php
// Callback message (exact raw JSON body as string)
$jsonMessage = '[CALLBACK MESSAGE]';

// Headers received
$headers = [
    'X-Signature' => 'sha256=yu2OvBe3Gyq1Nz/4R6KO8F3KpGCuW7VhH9yUPhYtNRU=',
    'X-Signature-Timestamp' => '1762181943494'
];

// Extract signature and timestamp
$signatureHeader = $headers['X-Signature'];
$signature = substr($signatureHeader, strlen('sha256='));
$timestamp = $headers['X-Signature-Timestamp'];

// Merchant’s shared secret key
$signatureKey = '4cde378d-43b6-405f-94aa-55c010d4d42a';

// Build message: JSON + "." + timestamp
$message = $jsonMessage . '.' . $timestamp;

// Compute HMAC SHA256
$computedHash = hash_hmac('sha256', $message, $signatureKey, true);

// Encode to Base64
$result = base64_encode($computedHash);

// Compare (constant-time safe comparison)
if (hash_equals($result, $signature)) {
    echo "Signature is valid\n";
} else {
    echo "INVALID SIGNATURE!\n";
}
?>

node.js

import crypto from "crypto";

// Callback message (exact raw JSON string)
const jsonMessage = "[CALLBACK MESSAGE]";

// Headers received
const headers = {
  "X-Signature": "sha256=yu2OvBe3Gyq1Nz/4R6KO8F3KpGCuW7VhH9yUPhYtNRU=",
  "X-Signature-Timestamp": "1762181943494",
};

// Extract signature and timestamp
const signatureHeader = headers["X-Signature"];
const signature = signatureHeader.substring("sha256=".length);
const timestamp = headers["X-Signature-Timestamp"];

// Shared secret key
const signatureKey = "4cde378d-43b6-405f-94aa-55c010d4d42a";

// Build message: JSON + "." + timestamp
const message = `${jsonMessage}.${timestamp}`;

// Compute HMAC SHA256
const hmac = crypto.createHmac("sha256", signatureKey);
hmac.update(message, "utf8");
const computedSignature = hmac.digest("base64");

// Constant-time comparison
if (crypto.timingSafeEqual(Buffer.from(computedSignature), Buffer.from(signature))) {
  console.log("Signature is valid");
} else {
  console.log("INVALID SIGNATURE!");
}

Last updated