Introduction
With the ability to deliver automated notifications and trigger events when certain actions take place, webhooks have grown to be an indispensable tool for real-time communication between web applications.
A webhook functionality provided by popular payment gateway BudPay enables businesses to immediately get notified about payment events like successful or unsuccessful transactions. You may automate processes, update order statuses, send email notifications, and take numerous other actions depending on payment events by integrating BudPay webhooks into your application.
This article will explain how to use Node.js and Express to integrate BudPay webhooks, with a practical example to make the process easier to grasp.
Prerequisite
Before proceeding with the integration, please take note of the following prerequisites:
Familiarity with Node.js and npm (Node Package Manager). You can find the Node.js installation guide here, and the npm installation guide here
Understanding of webhooks and their usage
Knowledge of JavaScript and Express framework
Next, install the necessary dependencies:
- Express: A web application framework for Node.js that will handle the webhook endpoint.
-
dotenv: A module that allows loading environment variables from a
.env
file. - Nodemailer: A library used for sending emails from your Node.js server.
- Mongoose An Object Data Modeling (ODM): library for MongoDB and Node.js, which will be used to store data in the database.
To install the dependencies, run the following command:
$ npm install express dotenv nodemailer mongoose
The installation process will download and set up the required packages for your project.
Getting Started
Create a new directory for your project and initialize it as a Node.js project by running the following command in your terminal:
$ mkdir budpay-webhooks
$ cd budpay-webhooks
$ npm init -y
Setup Modules and PORT
Once the dependencies are installed, create an index.js
file in the project directory and open it in your preferred text editor. We will start by requiring the essential modules and configuring Express:
const express = require('express');
require('dotenv').config();
const app = express();
app.use(express.json());
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
In the above code snippet, we import the Express module and load environment variables using dotenv
.
Express is configured to handle JSON request bodies using the express.json()
middleware.
Additionally, we specify the port number that the server will use, either from an environment variable or a default value of 3000.
Construct BudPay Notification Webhook Endpoint
Next, let's construct an endpoint to handle webhook notifications from BudPay. Add the following code to the index.js
file:
app.post('/webhook', (req, res) => {
const data = req.body;
if (event.eventType === 'refund') {
handlePaymentRefund(data);
} else if (event.eventType === 'dispute') {
handlePaymentDispute(data);
} else if (event.eventType === 'transaction') {
handlePaymentTransaction(data);
} else if (event.eventType === 'transaction.recurrent') {
handlePaymentTransactionRecurrent(data);
} else {
handleOtherEvents(event, data);
}
res.status(200).send(`Webhook received successfully. http://localhost:3000/webhooks`);
});
In this code snippet, we define a POST
route /webhook
that will receive incoming webhook notifications from BudPay. By destructuring the event
and data
properties from the request body, we can access the event
type (e.g., refund
or dispute
) and any additional information associated with the event
.
Within the route handler, you can implement custom logic based on the received event
and data
. For example, you might choose to update the order status
in your database, send a confirmation email to the customer, or trigger other relevant actions.
To handle the payment refund event
, update the code as follows:
async function handlePaymentRefund(data) {
const { orderId, amount, customerEmail } = data;
try {
await Order.findOneAndUpdate({ orderId }, { status: 'refund' });
sendPaymentRefundEmail(customerEmail, orderId, amount);
} catch (error) {
console.error('Failed to handle payment refund:', error);
}
console.log(`Payment refund for order ${orderId}. Amount: ${amount}. Customer email: ${customerEmail}`);
}
In the handlePaymentRefund
function, you can update the order status
in the database using Mongoose. Here, we assume you have defined a Mongoose model named Order
and use the findOneAndUpdate
method to update the order status
based on the orderId
received from the webhook.
Additionally, you can send a refund email to the customer using Nodemailer. Define the following function:
function sendPaymentRefundEmail(email, orderId, amount) {
const mailOptions = {
from: 'your-email@example.com',
to: email,
subject: 'Payment Refund',
text: `Your payment of ${amount} has been refunded for order ${orderId}.`,
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
console.error('Failed to send refund email:', error);
} else {
console.log('Refund email sent:', info.response);
}
});
}
In this function, you can customize the email content and configuration according to your needs.
The sendMail
method is used to send the email using the configured transporter.
To configure Nodemailer, you can use the following code:
const transporter = nodemailer.createTransport({
host: "smtp.gmail.com",
port: 465,
secure: true,
auth: {
user: 'your-email@gmail.com',
pass: 'your-password',
},
});
In the code above we make use of Gmail for example, you can use any email service provider of your choice.
Now, replace the 'your-email@gmail.com'
with your Gmail address and 'your-password'
with your Gmail password or an App Password if you have two-factor authentication enabled.
Note: It's important to secure your credentials and avoid hard-coding sensitive information directly in your code. Consider using environment variables or a configuration file to store and retrieve the email credentials securely.
Handling Payment Dispute Events
To handle the payment dispute event
, update the code as follows:
async function handlePaymentDispute(data) {
const { orderId, amount, customerEmail } = data;
try {
await Order.findOneAndUpdate({ orderId }, { status: 'dispute' });
sendPaymentDisputeNotification(customerEmail, orderId, amount);
} catch (error) {
console.error('Failed to handle payment dispute:', error);
}
console.log(`Payment dispute for order ${orderId}. Amount: ${amount}. Customer email: ${customerEmail}`);
}
function sendPaymentDisputeNotification(email, orderId, amount) {
const mailOptions = {
from: 'your-email@example.com',
to: 'support@example.com',
subject: 'Payment Dispute Notification',
text: `Payment of ${amount} for order ${orderId} is on dispute for customer ${email}.`,
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
console.error('Failed to send payment dispute notification:', error);
} else {
console.log('Payment dispute notification sent:', info.response);
}
});
}
The handlePaymentDispute
function updates the order status
in the database to 'dispute'
using Mongoose. It then sends a payment dispute notification to the support team using Nodemailer.
Handling Payment Transaction Events
Similarly, to handle the payment transaction event
, update the code as follows:
async function handlePaymentTransaction(data) {
const { orderId, amount, customerEmail } = data;
try {
await Order.findOneAndUpdate({ orderId }, { status: 'transaction' });
sendPaymentTransactionNotification(customerEmail, orderId, amount);
} catch (error) {
console.error('Failed to handle payment transaction:', error);
}
console.log(`Payment transaction for order ${orderId}. Amount: ${amount}. Customer email: ${customerEmail}`);
}
function sendPaymentTransactionNotification(email, orderId, amount) {
const mailOptions = {
from: 'your-email@example.com',
to: 'support@example.com',
subject: 'Payment Transaction Notification',
text: `Payment of ${amount} for order ${orderId} has been successful for customer ${email}.`,
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
console.error('Failed to send payment transaction notification:', error);
} else {
console.log('Payment transaction notification sent:', info.response);
}
});
}
The handlePaymentTransaction
function updates the order status
in the database to 'transaction'
using Mongoose. It then sends a payment transaction notification to the support team using Nodemailer.
Handling Transaction Recurrent Events
For handling the transaction recurrent event
, update the code as follows:
async function handlePaymentTransactionRecurrent(data) {
const { orderId, amount, customerEmail } = data;
try {
await Order.findOneAndUpdate({ orderId }, { status: 'transaction-recurrent' });
sendPTransactionRecurrentNotification(customerEmail, orderId, amount);
} catch (error) {
console.error('Handle Transaction Recurrent:', error);
}
console.log(`Transaction Recurrent for order ${orderId}. Amount: ${amount}. Customer email: ${customerEmail}`);
}
function sendTransactionRecurrentNotification(email, orderId, amount) {
const mailOptions = {
from: 'your-email@example.com',
to: 'support@example.com',
subject: 'Transaction Recurrent Notification',
text: `Payment of ${amount} for order ${orderId} has recurred for customer ${email}.`,
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
console.error('Transaction Recurrent to send notification:', error);
} else {
console.log('Transaction Recurrent notification sent:', info.response);
}
});
}
The handleTransactionRecurrent
function updates the order status
in the database using Mongoose. It then sends a payment transaction recurrent notification to the support team using Nodemailer.
To use Nodemailer, make sure to configure it with your email service provider's details (SMTP credentials). You can replace the placeholders in the code with the actual values.
Remember to replace 'your-email@example.com'
with the appropriate email addresses for sending emails notifications.
Conclusion
Lastly, configure the webhook URL in BudPay. Create a .env
file in the project directory and add the following line:
WEBHOOK_URL=http://localhost:3000/webhook
If you deploy your application to a production environment, replace the URL with the actual server URL.
To load the environment variables from the .env
file like we did while setting up the modules, add the following code at the top of the index.js
file:
require('dotenv').config();
This configuration allows access to the WEBHOOK_URL
variable through process.env.WEBHOOK_URL
within our code.
With everything set up, you can now test your implementation. Start the server by executing the following command in your terminal:
$ node index.js
Congratulations! You have successfully integrated BudPay webhook notifications into your Node.js and Express application. You are now ready to receive and handle real-time payment events, such as refund, transaction, transaction recurrent and dispute transactions, from BudPay.
Please find below a collection of screenshots showcasing:
- The tested order endpoint
- And the webhook
Feel free to customize the logic inside the webhook route handler to align with your specific use case. You can update the database, send email notifications, trigger additional actions, or implement any other functionality based on the received payment events.
Remember to secure your credentials and sensitive information by using environment variables or a configuration file instead of hard-coding them directly in your code.
For a complete reference and source code, you can visit the GitHub repository associated with this integration.
Note: Please ensure that you adhere to BudPay's terms of service and any applicable legal requirements while implementing the webhook functionality.
Thanks for reading...
Happy Coding!
Top comments (0)