Signature Verification
Every webhook request includes a signature header that you should verify to ensure the request is genuinely from Ikawaari.
How It Works
Each webhook endpoint has a unique signing secret (starts with whsec_). Ikawaari uses this secret to generate an HMAC-SHA256 signature included in the Ikawaari-Signature header.
The secret is shown only once at creation or after rotation. If you no longer have it, rotate the secret and update your merchant backend. This secret is different from your Ikawaari API key.
Verification must use the exact raw body received by your server. Do not parse and re-serialize JSON before computing the signature: whitespace, ordering, or encoding changes can make a valid request fail verification.
Verification Steps
1. Extract the signature
Ikawaari-Signature: t=1708300800,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
2. Prepare the signed payload
{timestamp}.{raw_body}
3. Compute the expected signature
- Node.js
- Python
import Ikawaari from '@ikawaari/sdk';
const client = new Ikawaari({ apiKey: process.env.IKAWAARI_API_KEY });
const event = client.webhooks.constructEvent(
rawBody,
request.headers['ikawaari-signature'],
process.env.IKAWAARI_WEBHOOK_SECRET
);
With Express, expose the raw body on this route:
app.post('/webhooks/ikawaari', express.raw({ type: 'application/json' }), (req, res) => {
const event = client.webhooks.constructEvent(
req.body.toString('utf8'),
req.headers['ikawaari-signature'],
process.env.IKAWAARI_WEBHOOK_SECRET
);
res.sendStatus(200);
});
Without the SDK:
const crypto = require('crypto');
function verifyWebhookSignature(payload, header, secret) {
const [tPart, vPart] = header.split(',');
const timestamp = tPart.split('=')[1];
const signature = vPart.split('=')[1];
const signedPayload = `${timestamp}.${payload}`;
const expected = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
// Timing-safe comparison
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
import hmac
import hashlib
def verify_webhook_signature(payload: str, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
timestamp = parts["t"]
signature = parts["v1"]
signed_payload = f"{timestamp}.{payload}"
expected = hmac.new(
secret.encode(), signed_payload.encode(), hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
4. Reject stale events
Check that the timestamp is within 5 minutes of the current time to prevent replay attacks.
Always verify webhook signatures in production. Never trust webhook payloads without verification.