Today, apps leverage communications with multiple servers (or systems) to deliver engaging features for users e.g. when a user subscribes to a newsletter, they receive a notification (usually an email) acknowledging this action. Here, there is a considerable amount of synchronization with external systems to get that notification. A good way to achieve this sync is to use webhooks.
What are Webhooks?
Webhooks are notifications sent between apps or different parts of the same app created by an event (or action). A good example is a debit alert for a completed transaction. For a more technical definition, webhooks are a form of a callback for your application. They typically contain information about the event that triggered them.
Webhooks are important because they can help you get real-time information for your app. In our last example, the source (payment gateway) shares transaction information via HTTP request, usually with a signature to the receiving system (the app).
Just like an API call, there are two parties responsible for the flow of webhooks. The webhook provider represents the system that sends the data (usually in serialized form-encoded JSON or XML formats) to the receiving application (webhook consumer). With webhooks, you can build complex flows and be sure that you don’t miss updates needed for your user journey. They are also easier to set up than to poll for an event update.
Do I Need Webhooks?
If your application communicates with an external system, there is a high chance that you would need to implement webhooks. Webhooks are very important for systems making synchronous API calls e.g. Bank transfers, mobile money and recurring payments (subscriptions).
Working With Webhooks
Consuming a webhook
The first step here is to expose an endpoint to receive a webhook notification. Here is a simple way to do that.
app.post("/flw-webhook", async (req, res) => {
const payload = req.body;
console.log(payload);
res.status(200).end();
});
Webhooks differ depending on the sender. It is best practice to stick to the webhook specifications made available by the webhook provider on their documentation. In general, most webhook will post data as JSON or XML format.
To consume webhooks from Flutterwave, update your webhook settings on your dashboard. To do this:
- Log into your Flutterwave dashboard
- Navigate to settings
- Select webhook from the settings menu and make updates to your webhook URL and preferences.
Securing your webhook
Webhooks deliver important updates to your app through a publicly accessible endpoint. This means that a malicious third party can send the wrong information to your app. Follow these tips to prevent this from happening:
- Enforce TLS (HTTPS) connections
- Verify your webhook with tokens in your URL e.g. /flw-webhook?auth=token
- Implement signature verification.
Flutterwave provides an additional measure to verify the authenticity of Webhook. We allow you to specify a hash (verif-hash) that can be used to verify the source of the webhook. Here is a simple implementation of signature verification.
app.post("/flw-webhook", async (req, res) => {
Try{
// Check for the signature
const secretHash = process.env.FLW_SECRET_HASH;
const signature = req.headers["verif-hash"];
If (!signature || signature !== secretHash){
// This response is not from Flutterwave; discard
return res.status(401).end();
}
const payload = req.body;
console.log(payload);
res.status(200).end();
} catch (err) {
console.log(err.code);
console.log(err.response.body);
}
});
Handling duplicates
There are cases where you receive multiple webhooks for the same event. While this would seldom occur it’s important that you build for idempotency to handle duplicates. In order to handle this, query the transaction status using the verify endpoint and create a logic with the following consideration:
- Check for the existence of the transaction in your database.
- Check for changes in the status of the transaction.
- Update the new transaction status.
This will ensure that you don’t give value for the same transaction twice.
const payload = req.body;
const existingEvent = await flw.Transaction.fetch({tx_ref: payload.data.tx_ref});
// n.b. the Transaction.fetch method is from the Node v3 library.
if(existingEvent.status === payload.status){
// The status hasn't changed so it's a duplicate, discard
res.status(200).end();
}
// Record this event
await PaymentEvent.save(payload);
// Process event
Debugging webhook errors
Errors with webhooks can happen for a number of reasons. Some common causes include:
- Wrong implementation of webhooks.
- Server refusing to accept requests (server error).
- Missing or incomplete webhook settings on your Flutterwave dashboard.
You can resolve the webhook errors by consulting the API docs and testing the webhook server to receive requests. You can read more about troubleshooting webhook errors here.
If you need support using webhooks with your integration kindly send us an email at developers@flutterwavego.com, we’ll be excited to help. We also have a slack community where we share valuable product information with developers and help them resolve issues, click here to join the community.