V1 Webhook Signing (Legacy)
V1 signing verifies webhook authenticity using a signatureHash field in the JSON body, signing only the orderId combined with paymentRequestId (payments) or refundId (refunds).
V2 signing is recommended for all new integrations. V2 signs the entire request body and uses a dedicated signing secret. See the V2 Webhook Signing guide for setup instructions.
How it works
| Details | |
|---|---|
| Signature location | signatureHash field in the JSON body |
| What is signed | orderId | paymentRequestId (payments) or orderId | refundId (refunds) |
| Algorithm | HMAC-SHA256 |
| Key | Your Atoa API secret (the same secret used for API authentication) |
Payment signature verification
To verify a payment webhook:
- Retrieve the
orderIdfrom your server — the one you passed when creating the payment request (notatoaOrderId). - Get the
paymentRequestIdfrom the webhook payload. - Compute
HMAC-SHA256(orderId + "|" + paymentRequestId, atoaSecret). - Compare the result against the
signatureHashfield in the payload.
const crypto = require('crypto');
function verifyV1Signature(orderId, paymentRequestId, atoaSecret, signatureHash) {
const data = orderId + '|' + paymentRequestId;
const generatedSignature = crypto
.createHmac('sha256', atoaSecret)
.update(data)
.digest('hex');
return generatedSignature === signatureHash;
}
// Usage
app.post('/webhook', express.json(), (req, res) => {
const { orderId, paymentRequestId, signatureHash } = req.body;
const yourOrderId = getOrderIdFromYourServer(); // Your original orderId, not atoaOrderId
if (!verifyV1Signature(yourOrderId, paymentRequestId, process.env.ATOA_SECRET, signatureHash)) {
return res.status(401).send('Invalid signature');
}
// Process the event...
res.status(200).send('OK');
});
Refund signature verification
For refund webhooks, the signature uses refundId instead of paymentRequestId:
hmac_sha256(orderId + "|" + refundId, atoaSecret)
The verification logic is identical — just substitute paymentRequestId with refundId from the refund webhook payload.
function verifyRefundSignature(orderId, refundId, atoaSecret, signatureHash) {
const data = orderId + '|' + refundId;
const generatedSignature = crypto
.createHmac('sha256', atoaSecret)
.update(data)
.digest('hex');
return generatedSignature === signatureHash;
}
Why V2 is recommended
V2 signing improves on V1 in several ways:
- Full-body coverage — V2 signs the entire request body, protecting all fields in the payload.
- Dedicated signing secret — V2 uses a separate signing secret rather than your API key, so your credentials stay compartmentalised.
- POS support —
POS_PAYMENT_STATUSwebhooks require V2 signing.
Migration to V2
To migrate from V1 to V2 signing:
- Generate a signing secret from the Atoa Dashboard under Settings → Webhooks.
- Update your webhook verification logic to use the
X-Atoa-Signatureheader instead of thesignatureHashbody field. - Once a signing secret is generated, the
signatureHashandsignaturefields are removed from webhook payloads and aneventTypefield is added.
See the V2 Webhook Signing guide for full setup instructions and code samples.
V1 and V2 cannot be used simultaneously for the same merchant. Generating a signing secret switches all your webhook deliveries to V2.