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
| Field | Type | Description |
|---|---|---|
notify | String | Event type: transaction |
notifyType | String | Event status: successful, failed, pending |
data.id | Integer | Transaction ID |
data.reference | String | Unique transaction reference |
data.amount | String | Total amount including fees |
data.requested_amount | String | Original amount before fees |
data.currency | String | Currency code (NGN, USD, GHS, KES) |
data.status | String | Transaction status: success, failed, pending |
data.channel | String | Payment channel: card, transfer, mobile_money |
data.fees | String | Transaction processing fees |
data.customer | Object | Customer 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
| Field | Type | Description |
|---|---|---|
notify | String | Event type: payout |
notifyType | String | Event status: successful, failed, pending |
data.id | Integer | Payout ID |
data.reference | String | Unique payout reference |
data.amount | String | Payout amount |
data.fee | String | Payout processing fee |
data.bank_code | String | Recipient bank code |
data.bank_name | String | Recipient bank name |
data.account_number | String | Recipient account number |
data.account_name | String | Recipient account name |
data.status | String | Payout 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
| Field | Type | Description |
|---|---|---|
notify | String | Event type: transaction |
notifyType | String | Event status: successful |
data.type | String | Transaction type: dedicated_account |
data.amount | String | Net amount after fees |
data.requested_amount | String | Original amount credited |
data.fees | String | Processing fees |
data.gateway | String | Bank gateway used |
transferDetails | Object | Transfer information from sender |
transferDetails.originatorname | String | Sender's name |
transferDetails.originatoraccountnumber | String | Sender's account/phone |
Webhook Configuration
Setting Up Webhooks
- Configure Webhook URL: Log into your BudPay dashboard and set your webhook URL in Settings
- Secure Your Endpoint: Ensure your webhook endpoint accepts POST requests and uses HTTPS
- Return 200 Status: Your endpoint should return a 200 HTTP status code upon successful receipt
- 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 GETSample 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
- Verify Webhooks: Implement signature verification to ensure webhooks are from BudPay
- Use HTTPS: Always use HTTPS for your webhook endpoints
- Validate Data: Validate all webhook data before processing
- Idempotency: Handle duplicate webhooks gracefully using reference numbers
- Log Events: Log all webhook events for debugging and audit purposes
Implementation Best Practices
- Quick Response: Respond with 200 status immediately, process asynchronously
- Verify Transactions: Always verify transaction status via API before fulfilling orders
- Handle Retries: Implement idempotent processing to handle retry attempts
- Error Handling: Log errors but still return 200 to prevent retries
- Monitor Webhooks: Set up monitoring for failed webhook deliveries
- 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
- Transaction Verification - Verify transaction status
- API Authentication - API authentication guide
- Error Handling - Handle API errors