Skip to documentation
YPYakaApp YakaniPay
Menu

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

  1. Register URL
  2. Receive test ping
  3. Transaction occurs
  4. Receive webhook
  5. Verify signature
  6. Check event and status
  7. Take action
  8. 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.

Code example
{
  "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.

Code example
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

ParameterTypePresenceDescription
schemaVersionStringConstantAPI schema version
requestIdUUID StringRequiredUnique request identifier
timestampStringRequiredRequest date and time
channelNameStringConstantRequest channel
serviceNameStringConstantWEBHOOK_REGISTER
merchantUidStringRequiredMerchant identifier
storeIdStringRequiredMerchant store identifier
hppKeyStringRequiredHPP authentication key
partnerUidStringOptionalWAAFI merchant number
descriptionStringOptionalWebhook usage description
urlStringRequiredPublic HTTPS notification endpoint
eventTypeStringOptionalUse refund for a separate refund-only URL

Response

Code example
{
  "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.

Code example
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.

Code example
{
  "event": "webhook.test",
  "message": "YakaniPay webhook validation ping",
  "merchant_uid": "your-merchant-uid"
}

Example handler

Code example
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.

Code example
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');
  }
}