"Argh!", the programmer who just began learning how to use fintech APIs yells in exasperation. Although he found a documentation for the API he's using, it's either inadequate or too technical for a novice. Rumour has it that he quit and is now a comedian. Does this sound like you?
This is one of the first in a series of articles on using the Flutterwave API as a backend developer/engineer. It will be a part of a larger set of articles focused on financial technology (AKA fintech) platforms' APIs.
Alright, now that we've gotten that out of the way, let's write a Python script to retrieve a Nigerian bank account name from just an account number and a bank name
Why this article?
If you're like the programmer/developer in the first paragraph, then you understand the need for a gentle guiding hand.
I aim, in this series of articles, to simplify the usage the API by means of entry-level-friendly small projects.
Who should read this article?
Everyone, I hope! Okay, well, I do assume that my readers have some experience programming in Python.
It is also expected that you already have a live developer account on Flutterwave.
Can we get started now?
Yes please.
To get started, visit Flutterwave's developer site and create an account. You'll need to submit necessary documents to begin using Flutterwave for real transactions. Copy your CLIENT_SECRET
and CLIENT_ID
keys and store them somewhere. We'll get back to that later.
Next, ensure you have Python 3 installed locally on your PC. Once that's done, proceed to install the following libraries:
pip3 install requests python-dotenv
Now create a folder and name it, say "account_lookup". In this folder, create a Python file with a name of your choosing. I suggest account_lookup.py
.
Next, we create another file named keys.env
in that same folder. Remember the CLIENT_SECRET
and CLIENT_ID
we copied then? Let's save it in keys.env
as follows:
CLIENT_SECRET="<your client secret>"
CLIENT_ID="<your client ID>"
What next?
Back to Python! Open up that file, account_lookup.py
in your favourite IDE (I assume that's VScode).
Let's import some libraries:
import os
import path
import dotenv # Used to load environment (.env) files
import requests
Next we load the environment file:
base_dir = pathlib.Path(__file__).parent # Equivalent to your working directory
dotenv.load_dotenv(f"{base_dir}/keys.env")
Then we declare some URLs for our APIs
BASE_URL = "https://api.flutterwave.cloud/f4bexperience"
token_url = "https://idp.flutterwave.com/realms/flutterwave/protocol/openid-connect/token"
acc_lookup_url = f"{BASE_URL}/banks/account-resolve"
To use the banks/account-resolve
endpoint, as well as most other endpoints that require a POST or GET request, we first need to get an authentication token. This token will further be used to authenticate our requests.
To get this token, let's create a function named get_access_token()
:
def get_access_token():
headers = {
"content-type": "application/x-www-form-urlencoded",
}
data = {
"client_secret": os.getenv("FLW_CLIENT_SECRET"),
"client_id": os.getenv("FLW_CLIENT_ID"),
"grant_type": "client_credentials"
}
token_response = requests.post(token_url, headers=headers, data=data).json()
access_token = token_response["access_token"]
return access_token
Right now, there really isn't much complicated going on yet. However, it is important to note a few things:
- See that
data
dictionary? Those three keysclient_secret
,client_id
andgrant_type
must be present! - Some possible issues you might encounter include omitting any of those fields, and not actually getting the right response due to a weak internet connection. Believe me, that happens.
At this point you can test if the get_access_token()
function works by calling it and printing its output. Assuming you've done that, we'll move on now.
Next, we need to get a dictionary of bank codes and their associated bank names. These codes are simply unique codes assigned by the CBN, used to identify banks.
To get this, we create another function named get_bank_codes()
. I suggest you read this article for a list of some bank codes.
access_token = get_access_token()
def get_bank_codes():
headers = {
"content-type": "application/json",
"authorization": f"BEARER {access_token}"
}
response_data = requests.get(f"{BASE_URL}/banks", headers=headers, params={"country":"NG"})
# Alternatively...
# response_data = requests.get(f"{BASE_URL}/banks?country=NG", headers=headers)
bank_codes = response_data.json()['data']
return bank_codes
The usual flow: Declare some headers – make the request with those headers (in this case, a GET request) – get the response – filter what we need.
At this point you might have noticed I left out error handling. It's not like errors don't occur, but I decided that for a simple script like this where the only errors likely to occur are errors due to poor internet, I could leave it out.
Furthermore, in a real application, you wouldn't want to always make an HTTP request to get the available bank codes. That would be time-consuming. It's best to save them the first time it's called, to your database, perhaps updating this after a long period of time, although I doubt bank codes can be changed. Such a change would seem catastrophic for applications that rely on the current set of codes.
Finally, let's create the function that gets an account name. This function takes two parameters:
-
account_number
: A string of numbers. This consists of 10 numbers in Nigeria. -
bank_name_or_code
: A string which either contains numbers only and is therefore treated as a bank code, or a string of letters to be treated as a bank name.
def get_account_name(account_number, bank_name_or_code):
bank_codes = get_bank_codes()
get_code_from_name = lambda name: next((bank["code"] for bank in bank_codes if bank["name"]\
.lower()\
.lstrip()\
.rstrip() == name.lower()), None)
bank_code = bank_name_or_code
# If this raises a Value error, the argument provided is most likely a bank name
try: int(bank_name_or_code)
except ValueError: bank_code = get_code_from_name(bank_name_or_code)
# We'll only get here if you specified a wrong bank name or code
if not bank_code:
raise ValueError('''
Bank name not found. Try another name or perhaps check your spelling.
You can also try the following:
1. Try alternative names: For example, "Guaranty Trust Bank" instead of "GT Bank", or vice-versa.
2. Use the CBN bank code instead.
''')
We aren't done with that function yet, but let's just take a moment to note that what we've been doing so far in this function, is try to get the bank code. Why is this so necessary? That's just because the API request for the account name look-up expects a bank code and not a bank name. However, to make the function more accommodating, we included support for bank names.
Now, let's continue:
request_body = {
"account": {
"code": bank_code,
"number": account_number,
},
"currency": "NGN"
}
response = requests.post(acc_lookup_url, headers={
"content-type": "application/json",
"accept": "application/json",
"authorization": f"BEARER {access_token}"
},
json=request_body).json()
if response["status"] == "success":
return response["data"]["account_name"]
elif response["status"] == "failed":
return response["error"]["message"]
And that's that. What you need to do is take note of the formats for both the headers and the request body. Take note too of the content_type
and accept
headers. For our case, we have set it such that we send a JSON in the request body, and likewise we accept JSON too. A little bit of "matching one's energy" if you ask me :)
To put it all together:
import os
import path
import dotenv # Used to load environment (.env) files
import requests
base_dir = pathlib.Path(__file__).parent # Equivalent to your working directory
dotenv.load_dotenv(f"{base_dir}/keys.env")
BASE_URL = "https://api.flutterwave.cloud/f4bexperience"
token_url = "https://idp.flutterwave.com/realms/flutterwave/protocol/openid-connect/token"
acc_lookup_url = f"{BASE_URL}/banks/account-resolve"
def get_access_token():
headers = {
"content-type": "application/x-www-form-urlencoded",
}
data = {
"client_secret": os.getenv("FLW_CLIENT_SECRET"),
"client_id": os.getenv("FLW_CLIENT_ID"),
"grant_type": "client_credentials"
}
token_response = requests.post(token_url, headers=headers, data=data).json()
access_token = token_response["access_token"]
return access_token
access_token = get_access_token()
def get_bank_codes():
headers = {
"content-type": "application/json",
"authorization": f"BEARER {access_token}"
}
response_data = requests.get(f"{BASE_URL}/banks", headers=headers, params={"country":"NG"})
# Alternatively...
# response_data = requests.get(f"{BASE_URL}/banks?country=NG", headers=headers)
bank_codes = response_data.json()['data']
return bank_codes
def get_account_name(account_number, bank_name_or_code):
bank_codes = get_bank_codes()
get_code_from_name = lambda name: next((bank["code"] for bank in bank_codes if bank["name"]\
.lower()\
.lstrip()\
.rstrip() == name.lower()), None)
bank_code = bank_name_or_code
# If this raises a Value error, the argument provided is most likely a bank name
try: int(bank_name_or_code)
except ValueError: bank_code = get_code_from_name(bank_name_or_code)
# We'll only get here if you specified a wrong bank name or code
if not bank_code:
raise ValueError('''
Bank name not found. Try another name or perhaps check your spelling.
You can also try the following:
1. Try alternative names: For example, "Guaranty Trust Bank" instead of "GT Bank", or vice-versa.
2. Use the CBN bank code instead.
''')
request_body = {
"account": {
"code": bank_code,
"number": account_number,
},
"currency": "NGN"
}
response = requests.post(acc_lookup_url, headers={
"content-type": "application/json",
"accept": "application/json",
"authorization": f"BEARER {access_token}"
},
json=request_body).json()
if response["status"] == "success":
return response["data"]["account_name"]
elif response["status"] == "failed":
return response["error"]["message"]
# To test what we've done so far
print(get_account_name("2411193506", "057")) #057 is Zenith bank.
That's it! You can check the full project at my GitHub repo.
Is this the end?
For this article, yes, but please, please, follow me for future articles on Flutterwave API and other fintech APIs.
Please can I get reviews?
Before we part till next time, please drop a comment—be it a question, a review, a correction...whatsoever helpful. I'd really appreciate it. This aids my growth, and encourages me to keep writing.
I'm a Back-end Engineer with more than 3 years of experience in the field. I recently shifted towards Fintech but I also love attempting to build applications that tend towards STEM fields. Please reach out to me via email: francisali692@gmail.com.
Thanks!
Top comments (0)