Webhooks
Webhook Verification
Cryptographic HMAC-SHA256 signature verification for real-time asynchronous event notifications.
Depa delivers real-time notifications for payment confirmations, KYC status updates, and ledger events using HTTPS POST webhooks.
To protect against man-in-the-middle attacks and forgery, all incoming webhook requests contain a Depasify-Signature HTTP header. Your server must verify this signature before processing the payload.
The Signature Header Format
The Depasify-Signature header consists of two comma-separated key-value pairs:
Depasify-Signature: t=1714562415,v1=6d4e28e469c5e3d740c83a54b3879fcfd2b8344697924d5e89d81d26ad5df19ft: The Unix timestamp when the webhook was dispatched.v1: The HMAC-SHA256 hex digest computed over${t}.${raw_request_body}using your Webhook Secret.
Verification Code Samples
Node.js / TypeScript
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyWebhookSignature(
rawBody: string,
signatureHeader: string,
secret: string
): boolean {
const parts = Object.fromEntries(
signatureHeader.split(",").map((part) => {
const [key, val] = part.split("=");
return [key.trim(), val.trim()];
})
);
const timestamp = parts["t"];
const receivedSignature = parts["v1"];
if (!timestamp || !receivedSignature) return false;
// Replay attack prevention (reject webhooks older than 5 minutes)
const currentTime = Math.floor(Date.now() / 1000);
if (Math.abs(currentTime - parseInt(timestamp, 10)) > 300) {
return false;
}
const payloadToSign = `${timestamp}.${rawBody}`;
const computedSignature = createHmac("sha256", secret)
.update(payloadToSign)
.digest("hex");
return timingSafeEqual(
Buffer.from(receivedSignature, "hex"),
Buffer.from(computedSignature, "hex")
);
}Python
import hmac
import hashlib
import time
def verify_webhook(raw_body: bytes, signature_header: str, secret: str) -> bool:
pairs = dict(item.split("=") for item in signature_header.split(","))
timestamp = pairs.get("t")
received_sig = pairs.get("v1")
if not timestamp or not received_sig:
return False
# Prevent replay attacks
if abs(time.time() - int(timestamp)) > 300:
return False
payload = f"{timestamp}.".encode("utf-8") + raw_body
computed_sig = hmac.new(
secret.encode("utf-8"),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(received_sig, computed_sig)Ruby
require 'openssl'
def verify_webhook(request_body, signature_header, secret)
parts = signature_header.split(',').to_h { |part| part.split('=') }
timestamp = parts['t']
received_sig = parts['v1']
return false unless timestamp && received_sig
# Reject requests older than 5 minutes
return false if (Time.now.to_i - timestamp.to_i).abs > 300
data = "#{timestamp}.#{request_body}"
computed_sig = OpenSSL::HMAC.hexdigest('SHA256', secret, data)
Rack::Utils.secure_compare(received_sig, computed_sig)
end