Verify Signatures
Every webhook from Aurax Pay is signed with HMAC-SHA256 using your webhook secret. Always verify the signature before processing a webhook — this prevents attackers from sending fake events to your endpoint.
⚠️Never skip signature verification in production. Without it, anyone who discovers your webhook URL can trigger actions in your system.
Your webhook secret
Find your webhook secret in Business Settings → Webhooks in your dashboard. It looks like: whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Store it as an environment variable: AURAX_WEBHOOK_SECRET
Verification algorithm
Aurax Pay computes the signature as:
HMAC-SHA256(rawRequestBody, webhookSecret)
The result is hex-encoded and sent in the X-Aurax-Signature header.
Verification examples
Node.js
"token-keyword">const crypto = require("token-string">'crypto') "token-keyword">function verifyAuraxSignature(rawBody, signatureHeader, secret) { "token-keyword">const expected = crypto .createHmac("token-string">'sha256', secret) .update(rawBody) // rawBody must be a Buffer, not parsed JSON .digest("token-string">'hex') // Use timingSafeEqual to prevent timing attacks "token-keyword">const a = Buffer."token-keyword">from(expected, "token-string">'utf8') "token-keyword">const b = Buffer."token-keyword">from(signatureHeader || "token-string">'', "token-string">'utf8') "token-keyword">if (a.length !== b.length) "token-keyword">return false "token-keyword">return crypto.timingSafeEqual(a, b) } // In your Express route, use express.raw() to get the raw body: app.post("token-string">'/webhooks/aurax', express.raw({ type: "token-string">'application/json' }), (req, res) => { "token-keyword">const sig = req.headers["token-string">'x-aurax-signature'] "token-keyword">if (!verifyAuraxSignature(req.body, sig, process.env.AURAX_WEBHOOK_SECRET)) { "token-keyword">return res.status(400).send("token-string">'Bad signature') } "token-keyword">const event = JSON.parse(req.body) res.json({ received: true }) // process event... } )
Python / FastAPI
"token-keyword">import hmac "token-keyword">import hashlib "token-keyword">from fastapi "token-keyword">import Request, HTTPException "token-keyword">async def verify_signature(request: Request) -> bool: raw_body = "token-keyword">await request.body() signature = request.headers.get("token-string">'x-aurax-signature', "token-string">'') secret = os.environ["token-string">'AURAX_WEBHOOK_SECRET'].encode() expected = hmac."token-keyword">new(secret, raw_body, hashlib.sha256).hexdigest() "token-keyword">return hmac.compare_digest(expected, signature) @app.post("token-string">'/webhooks/aurax') "token-keyword">async def handle_webhook(request: Request): "token-keyword">if not "token-keyword">await verify_signature(request): raise HTTPException(status_code=400, detail="token-string">'Invalid signature') event = "token-keyword">await request.json() # process event... "token-keyword">return { "token-string">'received': True }
PHP
"token-keyword">function verifyAuraxSignature(string $rawBody, string $signature, string $secret): bool { $expected = hash_hmac("token-string">'sha256', $rawBody, $secret); "token-keyword">return hash_equals($expected, $signature); } $rawBody = file_get_contents("token-string">'php://input'); $signature = $_SERVER["token-string">'HTTP_X_AURAX_SIGNATURE'] ?? "token-string">''; $secret = getenv("token-string">'AURAX_WEBHOOK_SECRET'); "token-keyword">if (!verifyAuraxSignature($rawBody, $signature, $secret)) { http_response_code(400); exit("token-string">'Invalid signature'); } $event = json_decode($rawBody, true); // process event... http_response_code(200); echo json_encode(["token-string">'received' => true]);
✅Use
timingSafeEqual (or equivalent) for comparison — regular string equality is vulnerable to timing attacks that can leak your secret.Common mistakes
- Parsing JSON before verifying — always verify against the raw bytes, not the parsed object
- Using string
===comparison instead of a constant-time function - Exposing your webhook secret in client-side code or logs
- Not returning
200fast enough — Aurax Pay will retry if no response in 10 seconds