SaukiPayDocs
IntegrationsAPI Integration

Webhooks

Get notified in real time when payments, payouts and refunds change status.

Saukipay sends a POST request to your webhook URL whenever a transaction changes status. Use webhooks to update orders and payouts without polling.

Set your webhook URL

Add your webhook URL on the Developer page of the merchant dashboard, for example https://api.yourdomain.com/webhook.

The URL must be publicly reachable. localhost URLs cannot receive events.

Validate the request

Every event carries an ApiKey header containing your secret key. Check it before you process the event, and reject any request where it doesn't match.

import crypto from 'node:crypto';
import express from 'express';

const app = express();
app.use(express.json());

function isFromSaukipay(header: string | undefined) {
  const received = Buffer.from((header ?? '').replace(/^ApiKey /, ''));
  const expected = Buffer.from(process.env.SAUKIPAY_SECRET_KEY!);
  return (
    received.length === expected.length &&
    crypto.timingSafeEqual(received, expected)
  );
}

app.post('/webhook', (req, res) => {
  if (!isFromSaukipay(req.header('ApiKey'))) {
    return res.sendStatus(401);
  }

  // Acknowledge first, then do the work
  res.sendStatus(200);

  const event = req.body;
  if (event.type === 'payment.success') {
    // Verify the transaction, then fulfil the order
  }
});

Respond with 200 OK

  • Return 200 OK as soon as you receive the event. If your handler does slow work, acknowledge first and process afterwards.
  • Any other response is recorded as a failed delivery.
  • Missed events can be requested again through the API.
  • Test your endpoint to confirm you receive the JSON body and return 200 OK.

Event types

EventWhen it's sent
payment.successA payment succeeded, on any channel
payment.failedA payment failed
payment.pendingA payment is pending
transfer.successA payout was delivered
transfer.failedA payout failed
refund.processedA refund is being processed

Example payload

{
  "id": "67ec7641416a8374c865a167",
  "type": "payment.success",
  "created": "2025-09-03T10:40:18.742+00:00",
  "data": {
    "reference": "MFNRCBW0-SW-AFHJTTSA-M",
    "status": "success",
    "amount": 1025,
    "settledAmount": 1000,
    "chargedFee": 25,
    "currency": "NGN",
    "paymentChannel": "card",
    "processorResponse": "Card 3DS verification successfully",
    "ip_address": "203.0.113.24",
    "paidAt": "2025-08-27 14:06:02",
    "customer": {
      "fullName": "John Doe",
      "email": "john.doe@example.com",
      "phoneNumber": "08012345678"
    },
    "paymentDetails": {
      "method": "card",
      "first6Digits": "123456",
      "last4Digits": "1992",
      "cardType": "master",
      "expiry": "04/26"
    },
    "environment": "live",
    "log": {
      "errors": 0,
      "success": true,
      "channel": "card",
      "history": [
        {
          "id": 8422,
          "type": "TRANSACTION RECORDED",
          "message": "success",
          "reference": "METYZHJT-SW-LK12MUJCV7",
          "channel": "S2S"
        }
      ]
    }
  }
}

The data object has the same shape as the verify transaction response.

Verify before you give value

Treat a webhook as a signal, not proof. Call verify transaction with the event's reference before you fulfil an order.

On this page