The Ibanity team works to make accessing and integrating banking data quick and painless. With our XS2A product, we provide one clear API to access account information and initiate payment requests from hundreds of financial institutions. Our developers take on the task of aggregating, testing and maintaining connections to each of them.
The current sandboxes provided by the banks (if they are even available) are not representative of production PSD2 APIs. For this reason, we need to use real bank accounts to build our connections and make sure they work properly.
The Pain Point
The authentication process varies from bank to bank, but for those in Belgium, it usually entails inserting a debit card into a card reader, entering various "challenge" codes and the card's PIN as prompted, and providing the card reader's responses to the bank.
You may imagine some of the challenges this presented to our team:
- Whenever we needed to test a certain bank connection, we had to track down the physical card and card reader.
- We needed to remember or record the PIN for each of the cards.
- We had to manually push the buttons on the card reader every time. It sounds simple enough, but it's tedious. Doing this over and over during testing, you're bound to make mistakes.
- If we weren't at the office with the card we needed, we had to ask a colleague to execute each action on the card reader and dictate the response.
- It was impossible to automate these interactions in our integration tests - they required manual intervention.
What if there was a way we could share quick and painless access to a variety of bank cards amongst our development team? What if we never had to touch a card reader again, but instead could use a clear and developer-friendly JSON API?
We got to work on the Digibot.
Luckily for us, Philippe Teuwen and Jean-Pierre Szikora from the Université catholique de Louvain had already written a Python script (https://sites.uclouvain.be/EMV-CAP/Application/) to emulate card challenge interactions using an inexpensive USB card reader. We could place each of our test cards into connected card readers and run the script to execute the authentication actions.
To execute a card challenge from the command line, we just needed to provide the index of the card reader, the card’s pin, the challenge type (m1 or m2), and challenge.
For example, for a card in the first card reader slot with the pin “1234”, we could run an M1 (Login) challenge of “123456” with the following command:
EMV_CAP --reader 0 -m1 123456 --pin 1234
Sometimes a challenge has multiple parts. To run an M2 (Sign) challenge which asks the cardholder to enter “1000” followed by “123456” with the same card, we would run the following command:
EMV_CAP --reader 0 -m2 1000 123456 --pin 1234
At Ibanity we are all about building developer-friendly APIs, so it should come as no surprise that we set to work writing a JSON API to expose that functionality. We constructed a small Rails app to expose a few simple endpoints:
GET /financial-institutions to provide the banks with available cards
GET /financial-institutions/:id/cards to provide the cards available for a given bank, with their respective account references, card numbers, and other details
to emulate the two kinds of actions made with the card reader
To store and manage the details for the available cards, we turned to Postgres and the RailsAdmin gem (https://github.com/sferik/rails_admin).
Even though the API wouldn't face heavy use, we needed to prepare for multiple developers to call the Digibot simultaneously. The emulator could only perform one card reader action at a time, so we used Mutex to coordinate and run the script synchronously.
With our app ready to go, we just needed to get it up and running. We didn't need any heavy infrastructure, just a way to run our modest Rails app and handle sporadic traffic from our development team.
It was the perfect job for a Raspberry Pi. We were able to connect our card readers via its USB ports, install Rails and Python, and apply the necessary certificates to connect to our VPN. From then on, we handled updates and code changes via SSH.
To execute the card reader actions, a developer just needs to provide the bank's challenge(s) in the POST request body. The app uses the card's stored PIN and runs the action using the emulator script. This means our developers never need to know the card's PIN and can't make input mistakes on the card reader.
We expose the API on our team's VPN, so our developers can access whichever card they need from wherever they are working. We can also access the API from our integration tests and quickly perform authentication automatically to ensure our applications are working as expected.
Taking it Further
By building the API, we opened the functionality to be used in other creative ways. For example, we created a Chrome extension which reads a bank’s webpage, calls the API to run each requested challenge, and automatically fills the responses. This allowed us to streamline the testing process even more.
For more than two years, the Digibot has found a home on a desk in our office and faithfully responded to our developers across Belgium, letting us focus on making XS2A the best product possible.