DEV Community

Cover image for Enhancing Finance Experiences with Open Banking
Matei Tudose
Matei Tudose

Posted on

Enhancing Finance Experiences with Open Banking

If you are a developer interested in developing finance apps, I think you've heard of "Open Banking". If that doesn't ring a bell with you, let me explain. According to Wikipedia:

Open Banking allows for financial data to be shared between banks and third-party service providers through the use of APIs.

Whether you'd want to create a KYC workflow for your customers or help small businesses receive payments directly to their bank accounts, Open Banking APIs are the solution.

Let's say you want to approve consumers for loans. You could use Open Banking to connect to their bank accounts and check their balances and spending habits. Using ML algorithms, you could then offer clients variable interest rates, depending on their financial status.

Personal experiences developing Payfren - an Open Banking "Revolut" alternative

Ever since this initiative of opening up banking APIs to devs started in my home country, Romania, I had this idea of developing a Revolut alternative, but with some twists:

  • to avoid needing banking licenses, funds would be stored in the users' bank accounts

  • the UX had to be on the same level as Revolut: payments, accounts overview, basically everything

One of the disadvantages of Open Banking is that there are a lot of APIs to integrate. Fortunately for me, I used the GoCardless Bank Account Data product (formerly Nordigen). GoCardless aggregates all of these APIs into one, making it easier to use for my mobile app. There is a free tier limited to 50 bank connections, and it is the only one that I know of to have a free level.

As a tech stack, I used:

  • Expo

  • Tamagui (check them out, I'll write an article about them soon)

  • React Query

  • Zustand

  • Supabase

You can find the source code here: https://github.com/payfren/app

Authorization flows - get access to balances and transactions

To get access to sensitive data such as banking information using the GoCardless Account Data API, you need to go through several stages:

Get an Authorization Token for future requests

You need to use the credentials in your developer console to get this token. You must do all these requests server-side , to prevent leaking these keys:

const tokenResponse = await fetch("https://ob.nordigen.com/api/v2/token/new/", {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
            "Accept": "application/json",
        },
        body: JSON.stringify({
            "secret_id": secretId,
            "secret_key": secretKey,
        }),
    });

// Get the authorization token that allows us to create a requisition
const tokenData = await tokenResponse.json();
const authToken: string = tokenData['access'];
Enter fullscreen mode Exit fullscreen mode

You will now need the returned access token for every request that you make.

Get a list of banks

You can get a list of banks in a specific country, in this example, Romania:

const response = await fetch("https://ob.nordigen.com/api/v2/institutions/?country=ro", {
        method: "GET",
        headers: {
            "accept": "application/json",
            "Authorization": 'Bearer ' + authToken,
        },
    });

const data = await response.json();
Enter fullscreen mode Exit fullscreen mode

This list will look like this:

[
   {
      "id":"ABNAMRO_ABNAGB2LXXX",
      "name":"ABN AMRO Bank Commercial",
      "bic":"ABNAGB2LXXX",
      "transaction_total_days":"540",
      "countries":[
         "GB"
      ],
      "logo":"https://cdn.nordigen.com/ais/ABNAMRO_FTSBDEFAXXX.png"
   },
   {
      "..."
   },
   {
      "id":"REVOLUT_REVOGB21",
      "name":"Revolut",
      "bic":"REVOGB21",
      "transaction_total_days":"730",
      "countries":[
         "GB"
      ],
      "logo":"https://cdn.nordigen.com/ais/REVOLUT_REVOGB21.png"
   }
]
Enter fullscreen mode Exit fullscreen mode

You need to create a UI for your users to prompt them to select a bank. Make this easier by using the CDN links provided by GoCardless for the bank logos.

Optional - Create an end-user agreement

If you'd like to customise the duration of the requisition or the abilities that the user will grant you (see balances, transactions, IBANs), you should create a custom agreement.

Create a link for the user to authorise the mandate

The most important part is to create the link to authorise the requisition. You need to provide:

  • institution_id (of a bank from Step 2)

  • redirect (a link where the user should be returned after completing the process)

There are other optional parameters, such as:

  • reference (a unique ID provided by you to easily identify the user in your database)

  • agreement (ID that you get from Step 3, otherwise leave blank)

  • user_language

 const response = await fetch("https://ob.nordigen.com/api/v2/requisitions/", {
        method: "POST",
        headers: {
            "accept": "application/json",
            "Content-Type": "application/json",
            "Authorization": 'Bearer ' + authToken,
        },
        body: JSON.stringify({
            "redirect": "payfren://home?finished_consent_flow=true",
            "institution_id": institution_id,
            "user_language": "RO",
        }),
    });

const data = await response.json();
Enter fullscreen mode Exit fullscreen mode

redirect can be a weblink or your mobile app deep link (in my case it is a deep link to the Payfren app).

The request returns something like this:

{
   "id":"8126e9fb-93c9-4228-937c-68f0383c2df7",
   "redirect":"http://www.yourwebpage.com",
   "status":{
      "short":"CR",
      "long":"CREATED",
      "description":"Requisition has been succesfully created"
   },
   "agreements":"2dea1b84-97b0-4cb4-8805-302c227587c8",
   "accounts":[

   ],
   "reference":"124151",
   "user_language":"EN",
   "link":"https://ob.gocardless.com/psd2/start/3fa85f64-5717-4562-b3fc-2c963f66afa6/{$INSTITUTION_ID}"
}
Enter fullscreen mode Exit fullscreen mode

Keep the id saved somewhere, you will need it to list bank accounts. As an alternative, you will receive the id via the redirect deep link, as a parameter in the link (?ref=REQUISITION_ID).

Get bank accounts data (depending on what permissions the user has granted)

If your user has passed Strong Customer Authentication and approved your requisition, you now have access to the requested scopes. You can get this data by making yet another 2 API requests :

  • Get the accounts linked using the requisition ID from Step 4:
const requisitionStatus = await fetch(`https://ob.nordigen.com/api/v2/requisitions/${consent_id}/`, {
        method: "GET",
        headers: {
            "Accept": "application/json",
            "Authorization": `Bearer ${authToken}`,
        }
    });

const requisitionStatusData = await requisitionStatus.json();
const requisitionAccounts = requisitionStatusData['accounts'];
Enter fullscreen mode Exit fullscreen mode
  • Use the IDs of the accounts to get data from them:
 const balanceResponse = await fetch(`https://ob.nordigen.com/api/v2/accounts/${accountId}/balances/`, {
    method: "GET",
    headers: {
        "Content-Type": "application/json",
        "Accept": "application/json",
        "Authorization": `Bearer ${authToken}`,
    }
 });

 // Handle errors when calling using a Supabase serverless function,
 // you may ignore this
 if (balanceResponse.status !== 200) {
    console.log("Error while getting the balances data from Nordigen: ", await balanceResponse.json());
    return new Response(
        JSON.stringify({
            error: "Error while getting the accounts data",
            message: balanceResponse.statusText,
        }),
        {
            status: 500,
            headers: {
                "content-type": "application/json; charset=UTF-8",
            },
        }
    );
 }

 const balanceData = await balanceResponse.json();
 account.balance = balanceData['balances'][0]['balanceAmount']['amount'];
 account.currency = balanceData['balances'][0]['balanceAmount']['currency'];
Enter fullscreen mode Exit fullscreen mode

We now have the balance of the account and the currency!

Similarly, you can also get transactions using a separate API request.

Main advantages and disadvantages

Open Banking can be used to facilitate a lot of complicated processes, such as:

  • KYC workflows

  • Payments without cards (low fees, reduced fraud)

  • Treasury management

As we can see in other countries, cardless payments have become a big trend. Merchants prefer them because of the reduced fees and fraud, and people because they are safer (and sometimes more convenient, depending on the target customer base).

But why isn't Open Banking widely adopted?

  • Hard for devs to get the necessary approvals to get full access to such APIs

  • Expensive at first, but it can scale well (price-wise)

  • Many costs to test and ensure the safety of users ( money is at stake! )

Conclusion

I believe that Open Banking will play a bigger role than it does today. It is still a fresh initiative that needs more traction. And of course, it needs a lot more devs developing using it.

Top comments (0)