API reference
Webhooks
Real-time transaction notifications
Webhooks are HTTP callbacks sent to your server when a transaction completes. They provide transaction updates without repeated status polling.
Quick start
- Register URL
- Receive test ping
- Transaction occurs
- Receive webhook
- Verify signature
- Check event and status
- Take action
- Return 200 OK
Understanding the payload
Use event for the transaction type and payment.status for the outcome. Statuses include APPROVED, FAILED, DECLINED, CANCELED, EXPIRED, and TIMEOUT.
{
"event": "authorization",
"payment": {
"status": "APPROVED",
"amount": 100.50,
"reference_id": "ORDER-123"
}
}1. Register a webhook
Register a publicly accessible HTTPS URL. Include your HPP credentials in the request. A separate refund-only URL may be registered using eventType.
POST /asm
{
"schemaVersion": "1.0",
"requestId": "{{$guid}}",
"timestamp": "{{$timestamp}}",
"channelName": "WEB",
"serviceName": "WEBHOOK_REGISTER",
"serviceParams": {
"merchantUid": {{MERCHANT_UID}},
"storeId": {{STORE_ID}},
"hppKey": {{HPP_KEY}},
"url": "https://api.example.com/webhook",
"eventType": "refund",
"description": "Refund notifications"
}
}Request parameters
| Parameter | Type | Presence | Description |
|---|---|---|---|
schemaVersion | String | Constant | API schema version |
requestId | UUID String | Required | Unique request identifier |
timestamp | String | Required | Request date and time |
channelName | String | Constant | Request channel |
serviceName | String | Constant | WEBHOOK_REGISTER |
merchantUid | String | Required | Merchant identifier |
storeId | String | Required | Merchant store identifier |
hppKey | String | Required | HPP authentication key |
partnerUid | String | Optional | WAAFI merchant number |
description | String | Optional | Webhook usage description |
url | String | Required | Public HTTPS notification endpoint |
eventType | String | Optional | Use refund for a separate refund-only URL |
Response
{
"schemaVersion": "1.0",
"responseCode": "2001",
"errorCode": "0",
"responseMsg": "Webhook created successfully",
"params": { "secret": "your_secret" }
}2. Get all webhooks
Retrieve the currently registered webhook endpoints.
POST /asm
{
"schemaVersion": "1.0",
"requestId": "{{$guid}}",
"timestamp": "{{$timestamp}}",
"channelName": "WEB",
"serviceName": "WEBHOOK_LIST",
"serviceParams": {
"merchantUid": "{{MERCHANT_UID}}",
"storeId": {{STORE_ID}},
"hppKey": "{{HPP_KEY}}"
}
}3. Update a webhook
Use WEBHOOK_UPDATE with the webhookId to change its URL or description.
4. Delete a webhook
Use WEBHOOK_DELETE with the webhookId to remove an endpoint.
5. Test your webhook
Registration triggers a validation request with User-Agent WPNotifyService-Test and Content-Type application/json. Return 200 OK.
{
"event": "webhook.test",
"message": "YakaniPay webhook validation ping",
"merchant_uid": "your-merchant-uid"
}Example handler
app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
const rawBody = req.body;
const body = JSON.parse(rawBody.toString('utf8'));
if (body.event === 'webhook.test') {
return res.status(200).send('OK');
}
try {
verifyWebhookSignature(rawBody, req.headers['x-yakanipay-signature'], process.env.WEBHOOK_SECRET);
if (body.event === 'authorization') await handleAuthorization(body);
if (body.event === 'refund') await handleRefund(body);
return res.status(200).send('OK');
} catch (error) {
return res.status(400).send('Invalid webhook');
}
});Security: HMAC verification
Verify every real webhook against the exact raw request body before processing it.
const crypto = require('crypto');
function verifyWebhookSignature(rawBody, signature, secret) {
if (!signature || !secret) throw new Error('Missing signature or secret');
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
const received = Buffer.from(signature, 'hex');
const trusted = Buffer.from(expected, 'hex');
if (received.length !== trusted.length || !crypto.timingSafeEqual(received, trusted)) {
throw new Error('Invalid webhook signature');
}
}