Integrating Banked with a Nuxt.js webstore
View this example project on Github
Banked enables anyone to pay directly with their bank accounts, from topping up a wallet in an app to paying for clothes. In this post we'll show you all the steps necessary to go from only taking credit cards, to enabling the next generation of secure, fast online payments.
Our example store is a Nuxt.js app, it has a static list of products (three types of sneakers) and two screens: a landing page that shows the products; and a cart page where customers can view their cart and checkout.
The code we'll be working through is available on Github (released under an MIT license) and you can deploy your own version of the example store to Heroku with one click, via the button in the README.md. You can also view the deployed store and see how it works!
For the purposes of this post, we'll assume the store already exists, it lists products and enables customers to pay for things with a credit card checkout. As such, we're going to be making two changes to the code:
- Adding a button to the checkout page, with some logic to redirect a user to Banked's checkout flow
- An API route, which takes a JSON representation of the customer's cart, makes an API call to Banked with it and creates a payment, then returning the checkout URL so the front-end can redirect to it
Adding the Checkout with Banked button
The checkout page before we start is straightforward, it has two columns in its layout (when viewed with a large screen):
- On the left it takes an array of cart items from the appropriate Vuex store and renders them
- On the right it shows a total for the cart, a checkbox to apply a discount (which makes everything cost number of items in cart * 0.01) and a button to checkout
The old checkout button doesn't actually do anything in the example store, as it's not relevant for our purposes, but it's implemented like this:
In our example store we use tailwind.css to manage its appearance, in this context it's done using the Tailwind "btn" and "mt-4" classes, it also uses Vue's built in event handlers to attach to it's click event (via @click). When a user clicks on it it calls an (unimplemented, in this example) component method called "creditCartCheckout".
We want to add another button (below our existing checkout button) to enable our customers to pay with Banked. Doing so is pretty straightforward, our code above becomes:
This uses the same tailwind.css classes, but we've added a custom "btn-banked" class, which enables us to give it a nicer appearance with CSS:
This tells Tailwind to apply its inbuilt white text style, and make the text 'xl'. We can also add some extra styling to our button to entice our users to use Banked by offering them free shipping, and because Banked has saved so much money on credit card fees we can afford to do it!
With our button looking good, we can implement the "checkout" method in our Vue component. It needs to do four things:
- Prevent the default behaviour of a button being submitted, we don't want the page to refresh if our button is wrapped in a "<form>" tag
- Make an HTTP POST to our (currently nonexistent!) API, in the process sending a JSON representation of the users cart
- When the API returns a link to a Banked checkout, redirect the user to the URL
- If there's an error with our API then show the user a message about what's happened
We don't want to implement the request primitive ourselves, so we'll use axios, which is conveniently included in Nuxt as a plugin. Our implementation starts off looking like this:
You'll notice we've added the "async" keyword to our function definition, which enables us to use "await" within our function. We call "axios.post" to send data to our backend API, which we'll assume for now is located at "/api/v1/checkout".
The cart variable we serialise is an array of cart items (see "./store/cart.js" in the Github repo for more information) in a format like this:
We also set a 1000 millisecond timeout on our request, so if something does go wrong we don't leave the user with no information about the issue; we're also just logging the error if there is one.
If we want to give the user a better experience than needing to open their development console to see what's wrong (which we should!) we also set a value into our Vue component.
We can then add a message to our cart page to help our customer know if something's gone wrong:
You can ignore the Tailwind utility classes being added to make it look nice, the important functional element is the v-if="error" which will show this component if there's an error.
So now we have a nice looking front-end for our store!
But it doesn't work end-to-end yet 😞
Creating the API route to interact with Banked's API
We now need to implement our store's API to interact with Banked's API to create a payment: how does the end-to-end flow work?
We need this API for several reasons:
- To use our secret Banked API keys to create a request as we don't want those to be in the client, anyone could use them to impersonate us!
- We want to create the payment in a controlled, secure environment. You don't want users intercepting the call to Banked and change the price to a single penny!
- There is additional information necessary to create a Banked payment besides the items and the total amount, such as the account number and sort code of the destination account. We don't want that publicly available either!
We're going to be using a combination of Nuxt's built-in serverMiddleware and the Node.js framework ExpressJS. We chose this approach because of how easy it is to integrate with Nuxt's build and deployment toolchain.
To tell Nuxt to register our API route and where to find our handler, we need to add some configuration to ./nuxt.config.js:
What this does is tell Nuxt to load the "./server/api.js" file and expose the routes it declares. Our first implementation of "./server/api.js" is only a few lines:
It imports Express as a dependency, initialises it and declares a single route. The export contains an object with two properties:
- "path" is the base path Nuxt will mount and direct traffic to, we choose "/api/v1"
- "handler" is the Express instance Nuxt will use to route traffic and mount the server, and we pass in the Express app created in the lines above
We're also creating a single route which extends the base path of "/api/v1" to add "/api/v1/checkout", it only accepts an HTTP POST. The handler then logs the body of the POST request to the console and returns a JSON object to our front-end, which now sort of works end-to-end:
'Sort of' 😬
Implementing the interaction with Banked's API is the next step, so we can return the proper URL to our front-end. Before we do that, we shouldsign-up for a Banked Developer account and get our API key and secret
With our keys in hand we can install the official Banked Node library with "npm install @banked/node". The initial implementation looks like this:
There are a few important things to look at:
- We're initialising the Banked node library with API key and secret,and sourcing them from environment variables we'll set before our Nuxt app runs. You can access and generate API keys via the Banked Console
- We're wrapping everything in a try/catch, so we can log what happens if something goes wrong.
When we run this you'll quickly realise something has gone wrong, we're passing our cart payload directly to Banked without any of the other information Banked needs to create a payment, doh! So let's add that functionality:
We've added our "hydrateRequest" function that wraps and enhances our cart with the additional information needed to create a payment request in Banked (as described in our developer docs). There are some other variables we use as part of the implementation we source from environment variables, through "process.env" these are:
- "BASE_URL" is a string representing the domain where this site is deployed (e.g. "https://example.com" or "https://localhost:3000"). This is used for constructing the callback URLs Banked's hosted checkout will redirect to on success or error of the payment
- "PAYEE_NAME" the name associated with the bank account payments will be made into
- "ACCOUNT_NUMBER" the bank account number the payments will be made into
- "SORT_CODE" is the sort-code of the account the payments will be made into
If we set these environment variables and run the request from the front-end it should now work as expected! 🎉
That's it! Hopefully you've seen in this post how easy it is to add fast, secure direct bank payments to your store or app. You can signup for an account and getting testing in less than a minute.
The code for this post is all available on Github, where you can fork and play it as you see fit. There's also a test suite built you can look at for further information on how this example store works!