Webhooks

Webhooks

This section contains the recommended flow for integrating BudPay Callback/Webhook services when using the BudPay RESTful API. The webhook service enables BudPay to send your system an update on certain activities.


Overview

Webhooks allow you to receive real-time notifications about events that happen in your BudPay account. When an event occurs, BudPay sends an HTTP POST request to the webhook URL you've configured.

Supported Events

BudPay sends webhook notifications for the following activities:

  • transaction - Payment transactions (card, bank transfer, etc.)
  • virtual_account - Virtual account credits
  • payout - Bank transfer payouts

Sample Transaction Webhook

When a transaction is completed, BudPay sends a webhook with the following structure:

{
    "notify": "transaction",
    "notifyType": "successful",
    "data": {
        "id": 1050471,
        "currency": "USD",
        "amount": "5.22",
        "reference": "482208088163205800",
        "ip_address": null,
        "channel": "card",
        "type": "transaction",
        "domain": "live",
        "fees": "0.19836",
        "plan": null,
        "requested_amount": "5.22",
        "status": "success",
        "card_attempt": 1,
        "settlement_batchid": null,
        "message": "Approved",
        "metadata": "MID: DPPSPBDNG|15173|5399|CAPTURED|Approved|SUCCESS|520149xxxxxx1019|MASTERCARD|2022-08-08T12:59:17.516Z|2022-08-08|175714|USD|222012015173|13203834|UBAS3I01",
        "created_at": "2022-08-08T12:58:09.000000Z",
        "updated_at": "2022-08-08T12:59:23.000000Z",
        "paid_at": "2022-08-08 13:59:23",
        "customer": {
            "id": 427351,
            "first_name": null,
            "last_name": null,
            "email": "melasbah83@gmail.com",
            "phone": null,
            "domain": "live",
            "customer_code": "CUS_6kj536tc0kx9brb",
            "metadata": "{}",
            "status": "active"
        }
    }
}

Transaction Webhook Fields

FieldTypeDescription
notifyStringEvent type: transaction
notifyTypeStringEvent status: successful, failed, pending
data.idIntegerTransaction ID
data.referenceStringUnique transaction reference
data.amountStringTotal amount including fees
data.requested_amountStringOriginal amount before fees
data.currencyStringCurrency code (NGN, USD, GHS, KES)
data.statusStringTransaction status: success, failed, pending
data.channelStringPayment channel: card, transfer, mobile_money
data.feesStringTransaction processing fees
data.customerObjectCustomer information

Sample Payout Webhook

When a payout is completed, BudPay sends a webhook with the following structure:

{
    "notify": "payout",
    "notifyType": "successful",
    "data": {
        "id": 4552,
        "reference": "BUD_trf_4fe1v.....",
        "sessionid": "110021221004050251.....",
        "currency": "NGN",
        "amount": "500000",
        "fee": "100",
        "bank_code": "090405",
        "bank_name": "Rolez Microfinance Bank",
        "account_number": "0000222293",
        "account_name": "Samuel Bud",
        "narration": "my narration",
        "domain": "live",
        "status": "success",
        "settled_by": null,
        "subaccount": null,
        "created_at": "2022-10-04T05:03:00.0000000000Z",
        "updated_at": "2022-10-04T05:03:03.000000Z"
    }
}

Payout Webhook Fields

FieldTypeDescription
notifyStringEvent type: payout
notifyTypeStringEvent status: successful, failed, pending
data.idIntegerPayout ID
data.referenceStringUnique payout reference
data.amountStringPayout amount
data.feeStringPayout processing fee
data.bank_codeStringRecipient bank code
data.bank_nameStringRecipient bank name
data.account_numberStringRecipient account number
data.account_nameStringRecipient account name
data.statusStringPayout status: success, failed, pending

Sample Virtual Account Webhook

When a virtual account receives a credit, BudPay sends a webhook with the following structure:

{
    "data": {
        "id": 14457138,
        "fees": "30",
        "plan": null,
        "type": "dedicated_account",
        "amount": "20",
        "domain": "live",
        "status": "success",
        "channel": "dedicated_account",
        "gateway": "wema",
        "message": "Account credited successfully",
        "paid_at": "01/01/2025 15:03:44",
        "currency": "NGN",
        "customer": {
            "id": 7608196,
            "email": "2532-adetunjioluwakayode@gmail.com",
            "phone": "08031975397",
            "domain": "live",
            "status": "active",
            "metadata": "{}",
            "last_name": "Adetunji",
            "first_name": "Kayode",
            "customer_code": "CUS_ff0ondr3rxekzsd"
        },
        "reference": "100033250101140325860393638601",
        "created_at": "2025-01-01T15:03:44",
        "ip_address": null,
        "business_id": 30,
        "customer_id": 7608196,
        "card_attempt": 0,
        "requested_amount": "50.00"
    },
    "notify": "transaction",
    "notifyType": "successful",
    "transferDetails": {
        "amount": "50.00",
        "bankcode": "100033",
        "bankname": "PALMPAY",
        "craccount": "4051242100",
        "narration": "OLUWAKAYODE AYORINDE ADETUNJI:8031975397",
        "sessionid": "100033250101140325860393638601",
        "craccountname": "Inclusive 1 / Kayode Adetunji",
        "originatorname": "OLUWAKAYODE AYORINDE ADETUNJI",
        "paymentReference": "100033250101140325860393638601",
        "originatoraccountnumber": "8031975397"
    }
}

Virtual Account Webhook Fields

FieldTypeDescription
notifyStringEvent type: transaction
notifyTypeStringEvent status: successful
data.typeStringTransaction type: dedicated_account
data.amountStringNet amount after fees
data.requested_amountStringOriginal amount credited
data.feesStringProcessing fees
data.gatewayStringBank gateway used
transferDetailsObjectTransfer information from sender
transferDetails.originatornameStringSender's name
transferDetails.originatoraccountnumberStringSender's account/phone

Webhook Configuration

Setting Up Webhooks

  1. Configure Webhook URL: Log into your BudPay dashboard and set your webhook URL in Settings
  2. Secure Your Endpoint: Ensure your webhook endpoint accepts POST requests and uses HTTPS
  3. Return 200 Status: Your endpoint should return a 200 HTTP status code upon successful receipt
  4. Handle Retries: BudPay will retry failed webhooks up to 3 times

Webhook URL Requirements

⚠️

Important: Your webhook URL must be publicly accessible and respond with HTTP 200 status within 30 seconds.

  • Must use HTTPS (HTTP not allowed in production)
  • Must be publicly accessible (no localhost)
  • Must respond within 30 seconds
  • Must return HTTP 200 for successful processing

Implementing Webhook Handler

Example Webhook Handler (Node.js)

const express = require('express');
const app = express();
 
app.use(express.json());
 
app.post('/webhook/budpay', (req, res) => {
    const { notify, notifyType, data } = req.body;
    
    // Verify webhook authenticity (recommended)
    // const isValid = verifyWebhookSignature(req);
    // if (!isValid) return res.status(401).send('Invalid signature');
    
    // Handle different event types
    switch(notify) {
        case 'transaction':
            handleTransaction(data, notifyType);
            break;
        case 'payout':
            handlePayout(data, notifyType);
            break;
        case 'virtual_account':
            handleVirtualAccount(data, notifyType);
            break;
        default:
            console.log('Unknown webhook type:', notify);
    }
    
    // Always respond with 200
    res.status(200).json({ received: true });
});
 
function handleTransaction(data, status) {
    if (status === 'successful') {
        // Update order status
        // Send confirmation email
        // Fulfill order
        console.log(`Transaction ${data.reference} completed`);
    }
}
 
function handlePayout(data, status) {
    if (status === 'successful') {
        // Update payout status
        // Notify recipient
        console.log(`Payout ${data.reference} completed`);
    }
}
 
function handleVirtualAccount(data, status) {
    if (status === 'successful') {
        // Credit customer wallet
        // Send notification
        console.log(`Virtual account credited: ${data.reference}`);
    }
}
 
app.listen(3000, () => console.log('Webhook handler running on port 3000'));

Resend Webhook

Resend transaction webhook using transaction reference number.

Endpoint

GET https://api.budpay.com/api/v2/resend_transaction_webhook/{reference}

Sample Request

curl https://api.budpay.com/api/v2/resend_transaction_webhook/BUD_1673600359168063493 \
-H "Authorization: Bearer YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-X GET

Sample Response

{
    "success": true,
    "message": "Webhook Resent Successfully"
}

Best Practices

Tip: Always verify transactions using the verification API before fulfilling orders, even after receiving a webhook.

Security Best Practices

  1. Verify Webhooks: Implement signature verification to ensure webhooks are from BudPay
  2. Use HTTPS: Always use HTTPS for your webhook endpoints
  3. Validate Data: Validate all webhook data before processing
  4. Idempotency: Handle duplicate webhooks gracefully using reference numbers
  5. Log Events: Log all webhook events for debugging and audit purposes

Implementation Best Practices

  1. Quick Response: Respond with 200 status immediately, process asynchronously
  2. Verify Transactions: Always verify transaction status via API before fulfilling orders
  3. Handle Retries: Implement idempotent processing to handle retry attempts
  4. Error Handling: Log errors but still return 200 to prevent retries
  5. Monitor Webhooks: Set up monitoring for failed webhook deliveries
  6. Test Thoroughly: Test webhook handlers in test mode before going live

Troubleshooting

Common Issues

Webhooks Not Received

  • Verify webhook URL is publicly accessible
  • Check if URL uses HTTPS (required in production)
  • Ensure endpoint responds within 30 seconds
  • Check firewall settings

Duplicate Webhooks

  • Implement idempotent processing using reference numbers
  • Check webhook logs in dashboard for retry attempts
  • Ensure endpoint returns 200 status code

Webhook Verification Failed

  • Verify API keys are correct
  • Check webhook signature validation logic
  • Review webhook payload structure

Next Steps