By Andrew Wong, Developer Advocate
Intro
When it comes to processing payments, Adyen provides a webhook to help customers (our platform’s merchants) build a complete online checkout integration.
In this blog, we’ll check out how webhooks work, and how you can implement and test webhook notifications in your checkout integration.
What is a webhook?
In short, using a webhook is a great way for your applications to receive events or notifications from a service.
To illustrate, think about your favorite local bookstore. Before you ever make a trip out to them, you’d prefer to know whether or not a certain book is in stock. One way to find out is to call the bookstore yourself and ask them directly.
While this certainly works, one drawback is that you may have to make repeated calls to find out the availability of your favorite book. This wouldn’t be a great use of your (nor the store’s) time and resources. What if the book tends to sell out quickly? Having information about the book’s availability the moment it’s restocked could greatly increase your chances of coming home with a new novel.
In the context of software development, calling the bookstore is like making an API call and receiving a response. For both situations, it’s still a manual process, and you receive information only when you ask for it.
However, what if the bookstore calls you when they finally have the book in-stock? This would save time for both parties, as information about any in-stock status changes would only be sent whenever there’s new information to share in the first place.
This is the philosophy behind a webhook: a way to receive new information or data proactively without having to repeatedly poll a service. Now, how does this look like in real-world applications?
Real-world application of webhooks
Imagine the “read receipts” in your favorite messaging app. For the most part, it’s made possible by taking advantage of an event webhook. That is, whenever your sent message is opened by the other application, an HTTP POST request is made to a specified URL. In turn, your application is notified with data that the message has been read, and can then run business logic for rendering the read receipt. There’s no need for your application to continuously check if your message has been read, as it’ll be automatically notified when the read status has been updated.
All in all, using a webhook is ideal whenever you need to run some business logic in your application based on an event happening in another application.
Adyen notification webhooks
Webhooks are essential in the financial services space due to the asynchronous information flow of banks, payment methods, and other financial systems. Adyen provides a webhook to communicate this crucial information to our customers as soon it is available. This information includes:
Payment status updates
Events that are not triggered by a request from the customer side. This includes events such as a shopper-initiated chargeback.
Requests that are processed asynchronously. For example, in the case of many local payment methods such as iDEAL, the outcome of the payment may take several hours to confirm. We send the customer a notification the second new information becomes available.
Integrating the webhook
There are two main steps to configure Adyen’s notification webhooks:
Creating an endpoint on your server. This includes acknowledging the notification sent by Adyen, verifying its signature, storing the notification, and applying your business logic.
Setting up notifications in Customer Area.
Let’s check out how to set up notifications in a test online Checkout application built in React and Express. We’ll also be using ngrok, which helps provide a public URL to tunnel into localhost as we test our integration.
Before writing our first line of code, be sure to clone and install the test application. You can find instructions for getting everything up and running, including generating the required account keys, in the test application’s README.
Creating an endpoint on your server
Once your project is up and running (i.e., accessible on port 8080), the first step is to expose and configure an endpoint on the server. In the Express server file, we’ll create an HTTP POST route to /api/webhook/notification:
app.post('/api/webhook/notification', async (req, res) => {
res.send('[accepted]');
try {
} catch (err) {}
});
As notifications come in, we’ll first acknowledge the notifications with an [accepted] response. This helps ensure that our application server continues to properly accept notifications:
res.send('[accepted]');
Next, we’ll need to retrieve the array of notification request items from the POST body, which looks something like this:
[
{
"NotificationRequestItem": {
"additionalData": {
...
},
"eventCode": "AUTHORISATION",
"success": "true",
"eventDate": "2019-06-28T18:03:50+01:00",
"merchantAccountCode": "YOUR_MERCHANT_ACCOUNT",
"pspReference": "7914073381342284",
"merchantReference": "YOUR_REFERENCE",
"amount": {
"value": 1130,
"currency": "EUR"
}
}
}
]
Note that some fields included in the NotificationRequestItem object depend on the type of event that triggered the notification. For example, notifications triggered by a request to refund will be different than a notification triggered by a chargeback request.
Next, we need to iterate over the list of notification request items, and process them based on the type of event the notification item is associated with (i.e., its eventCode). As we iterate through the list, however, we’ll also want to verify its HMAC (hash-based message authentication code) signatures. This helps protect the application server from unauthorised notifications, such as any data that may have been modified during transmission. Since our application leverages the Adyen Node.js server-side library to interact with Adyen’s API, we can conveniently import and instantiate the built-in HMAC validator class to do just that:
const { hmacValidator } = require('[@adyen/api-library](http://twitter.com/adyen/api-library)');
const validator = new hmacValidator();
Next, we’ll begin processing each notification request item:
const notificationRequestItems = req.body.notificationItems;
notificationRequestItems.forEach(item => {
if (validator.validateHMAC(item.NotificationRequestItem, process.env.ADYEN_HMAC_KEY)) {
const eventCode = item.NotificationRequestItem.eventCode;
// Your business logic here (i.e., process the notification based on the eventCode)
} else {
// Non-valid NotificationRequest
console.log('Non-valid NotificationRequest');
}
});
As we iterate through the list of notification request items, we use our instance of the HMAC validator to validate each notification request item, passing in the object itself, as well as our HMAC key (referenced above in an environment variable). This secret key enables HMAC-signed notifications, and we’ll generate it shortly in the next steps of this blog.
If the notification request item is valid, we run some business logic based on the event code. For example, the notification may detail a successful payment, or inform you that a new report (for accounting purposes) is available. Your application can then take any action it needs to based on this new information.
All together, our endpoint should look something like the following:
app.post('/api/webhook/notifications', async (req, res) => {
res.send('[accepted]');
try {
const validator = new hmacValidator();
const notificationRequestItems = req.body.notificationItems;
// Be sure to store your notifications in a database (not shown here)
notificationRequestItems.forEach(item => {
if (validator.validateHMAC(item.NotificationRequestItem, process.env.ADYEN_HMAC_KEY)) {
const eventCode = item.NotificationRequestItem.eventCode;
// Your business logic here (i.e., process the notification based on the eventCode)
// An example for a payment notification:
if (eventCode === 'AUTHORISATION') {
// This notification is for a payment.
if (item.NotificationRequestItem.success === 'true') {
// Payment was successful!
} else {
// Payment was refused! :(
}
}
} else {
console.log('Non-valid NotificationRequest');
}
});
} catch (err) {
console.error(`Error: ${err.message}, error code: ${err.errorCode}`);
res.status(err.statusCode).json(err.message);
}
});
At this point, our server endpoint has been exposed and configured. The issue however, is that our application is currently being served on localhost. To set up the webhook, we’ll need a public URL to the application. This is where we leverage the ngrok tool, which helps us tunnel (i.e., forward) to our local application from a publicly-accessible URL.
Follow the instructions to install ngrok and connect your account, then run ngrok from the command line on port 8080:
./ngrok http 8080
If everything’s running correctly, you should see something like this output in the terminal:
<!-- Terminal output:
ngrok by [@inconshreveable](http://twitter.com/inconshreveable) (Ctrl+C to quit)
Session Status online
Account Sample User (Plan: Free)
Version 2.3.35
Region United States (us)
Web Interface [http://127.0.0.1:4040](http://127.0.0.1:4040)
Forwarding [http://08a73a49c263.ngrok.io](http://08a73a49c263.ngrok.io) -> [http://localhost:8080](http://localhost:8080)
Forwarding [https://08a73a49c263.ngrok.io](https://08a73a49c263.ngrok.io) -> [http://localhost:8080](http://localhost:8080)
Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00
-->
Note the public URL as referenced by “Forwarding” in the output. We’ll use this value as we head into the Adyen Customer Area for webhook configuration.
Setting up notifications in Customer Area
The next step is to provide your server’s details, as well as customize the information you want to receive in notifications. To get started, first log in to Customer Area. On the top right, navigate to Account > Server communication. Then, next to Standard Notification, click Add.
Under Transport, supply the following information:
URL: Your server’s public URL. This is the “Forwarding” value in the ngrok terminal output above.
SSL: For a test environment, use NO_SSL (HTTP, TEST only)
Method: JSON
Active: ✓
Finally, under Additional Settings, select the information you want to receive in notifications. For example, adding the acquirer result allows you to receive standardized mapping of the acquirer results for AVS and CVC checks. For more information on shopper and transaction information, check out Additional settings in our documentation.
The final step is to select Generate new HMAC key. This allows you to receive HMAC-signed notifications, which will verify the integrity of notifications using these HMAC signatures. After generating the HMAC key, assign its value to an environment variable (i.e., process.env.ADYEN_HMAC_KEY as referenced in the above server code).
At this point, the server and server communication settings have been fully set. Before we jump into testing, be sure to select Save Configuration at the bottom of the page.
Testing the webhook
To make sure that we can receive notifications, we’ll first need to test them. While still on the current server settings page, scroll to the bottom and toggle which notifications you want to test. Then, select Test Configuration.
If everything was set up correctly, you should see something like the following at the top of the page:
At this point, it’s a wrap! Feel free to further configure your server communication, and continue to apply any business logic in your server code as you see fit.
Getting started
Implementing Adyen notification webhooks is crucial for a successful integration with Adyen. To get started, feel free to check out the quick start in documentation. To see everything in action, first make sure you have the proper keys and credentials from a test account, then clone one of our example integrations.
As always, you are always welcome to visit our forum or reach out directly if you have any comments or feedback along the way.
Technical careers at Adyen
We are on the lookout for talented engineers and technical people to help us build the infrastructure of global commerce!
Developer newsletter
Get updated on new blog posts and other developer news.
Originally published at https://www.adyen.com on March 29, 2021.
Top comments (0)