Guides
Verify signatures
Every webhook delivery is signed with HMAC-SHA512 using your endpoint's signing secret. Verifying the signature is the only way to be sure a request came from TagPay and not from an attacker who guessed your URL.
Signing
TagPay computes HMAC-SHA512(raw_body, signing_secret), hex-encodes the result, and ships it in the x-tagpay-signature header. The secret is unique per webhook endpoint — find it in Dashboard → Developers → Webhooks → [your endpoint].
Verifying
Recompute the HMAC over the raw body with your signing secret, then compare in constant time against the header. Constant-time comparison prevents timing-attack leaks of the signature.
Node.js
import crypto from 'crypto';export function verifyTagPaySignature(rawBody: string,signature: string,secret: string,): boolean {const expected = crypto.createHmac('sha512', secret).update(rawBody).digest('hex');return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));}
Python
import hmacimport hashlibdef verify_tagpay_signature(raw_body: bytes, signature: str, secret: str) -> bool:expected = hmac.new(secret.encode(), raw_body, hashlib.sha512).hexdigest()return hmac.compare_digest(signature, expected)
PHP
<?phpfunction verify_tagpay_signature(string $rawBody, string $signature, string $secret): bool {$expected = hash_hmac('sha512', $rawBody, $secret);return hash_equals($signature, $expected);}
Examples
Express middleware showing raw-body capture, signature verification, and a fire-and-forget enqueue so the handler can ack the webhook in under 15 seconds:
import express from 'express';import { verifyTagPaySignature } from './verify';const app = express();// IMPORTANT: capture the raw body BEFORE express.json() parses it.app.post('/webhooks/tagpay',express.raw({ type: 'application/json' }),(req, res) => {const signature = req.header('x-tagpay-signature') ?? '';const rawBody = req.body.toString('utf8');if (!verifyTagPaySignature(rawBody, signature, process.env.TAGPAY_WEBHOOK_SECRET!)) {return res.status(401).send('bad signature');}const event = JSON.parse(rawBody);// enqueue, then ackqueue.enqueue(event);res.status(200).send('ok');},);