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'];
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();
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"
}
]
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();
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}"
}
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'];
- 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'];
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)