Return To Blogs
Connect

Validate UK Bank Accounts with an OpenAPI Python Client

This blog looks at how you can validate UK bank accounts with automated client generation, specifically the creation of a Python OpenApi client to allow the consumption of Unified Software’s BankVal Enhanced web services. OpenAPI (formerly known as Swagger) is a technology which documents RESTful APIs to allow easy integration into your existing systems.

The BankVal Enhanced OpenAPI documents are located at either:
https://www.unifiedservices.co.uk/services/enhanced/openapi.json or https://www.unifiedservices.co.uk/services/enhanced/openapi.yaml depending on which format is preferred.

Python Logo

Creating the client code

The first step in creating the client code is to download the OpenApi document from Unified Software and save it in a directory on your dev machine. Here the OpenAPITools openapi-generator-cli tool is used which is available from GitHub, follow the instructions there to install onto your system, we are using Linux but OpenAPI generator works on Windows too. There are also other client generation tools such as the online one at https://editor.swagger.io. Please note the clients generated by different generators will not necessarily operate in the same way, please refer to the relevant documentation.

Once the tool is installed open a terminal window in the directory you downloaded the OpenAPI document to. Then in the terminal simple run the following command:

npx @openapitools/openapi-generator-cli generate -i bve.json -g python -o /path/to/swaggerclients/pythonclient

Where /path/to/swaggerclients/pythonclient should be where you want to create your client.

This will create a client with the following structure :

openapi_client/
├── api_client.py
├── apis
│   ├── __init__.py
│   ├── paths
│   │   ├── enhanced_bankvalint.py
│   │   ├── enhanced_bankval.py
│   │   ├── enhanced_check.py
│   │   └── __init__.py
│   ├── path_to_api.py
│   ├── tags
│   │   ├── default_api.py
│   │   └── __init__.py
│   └── tag_to_api.py
├── configuration.py
├── exceptions.py
├── __init__.py
├── model
│   ├── address.py
│   ├── address.pyi
│   ├── address_val.py
│   ├── address_val.pyi
│   ├── address_val_response.py
│   ├── address_val_response.pyi
│   ├── anomaly_detector_response.py
│   ├── anomaly_detector_response.pyi
│   ├── bank_info.py
│   ├── bank_info.pyi
│   ├── bank_val_uk_response_object.py
│   ├── bank_val_uk_response_object.pyi
│   ├── bve_request_payload.py
│   ├── bve_request_payload.pyi
│   ├── bve_response.py
│   ├── bve_response.pyi
│   ├── bvieiban_request_payload.py
│   ├── bvieiban_request_payload.pyi
│   ├── bvie_response.py
│   ├── bvie_response.pyi
│   ├── bvieswift_request_payload.py
│   ├── bvieswift_request_payload.pyi
│   ├── check_request_payload.py
│   ├── check_request_payload.pyi
│   ├── check_response.py
│   ├── check_response.pyi
│   ├── contact.py
│   ├── contact.pyi
│   ├── credentials.py
│   ├── credentials.pyi
│   ├── email_val_response.py
│   ├── email_val_response.pyi
│   ├── enhanced.py
│   ├── enhanced.pyi
│   ├── history_check.py
│   ├── history_check.pyi
│   ├── iban_anomaly_detector_response.py
│   ├── iban_anomaly_detector_response.pyi
│   ├── iban_result.py
│   ├── iban_result.pyi
│   ├── __init__.py
│   ├── phone_val_response.py
│   ├── phone_val_response.pyi
│   ├── reason_map.py
│   ├── reason_map.pyi
│   ├── service_error.py
│   ├── service_error.pyi
│   ├── swift_check.py
│   ├── swift_check.pyi
│   ├── swift_check_response.py
│   ├── swift_check_response.pyi
│   ├── uk_account_request.py
│   └── uk_account_request.pyi
├── models
│   └── __init__.py
├── paths
│   ├── enhanced_bankval
│   │   ├── __init__.py
│   │   ├── post.py
│   │   └── post.pyi
│   ├── enhanced_bankvalint
│   │   ├── __init__.py
│   │   ├── post.py
│   │   └── post.pyi
│   ├── enhanced_check
│   │   ├── __init__.py
│   │   ├── post.py
│   │   └── post.pyi
│   └── __init__.py
├── rest.py
└── schemas.py

Using the client code

Copy the openapi_client into a directory in your codebase where you will be able to import it from. We are using a test project created in PyCharm to demonstrate how you would use the generated code to call BankVal Enhanced (BVE).

For our project we copy the openapi client code into the root of our project directory. Then create a function that will take a sortcode and account number and validate them returning the response from BVE. For simplicity we are just going to validate UK bank accounts by calling the sortcode and account number validation service in BVE there are many more KYC checks avaliable in BVE including address lookup/telephone validation etc.

As the client needs to provide failover from unifiedsoftware.co.uk to unifiedservices.co.uk (or vice versa) our demo will use two functions with the call to the servers abstracted out and a separate function to create the call to the service which we’ve called validate_sortcode_account.

First the function to create the call sets up the post data as shown below :

def validate_sortcode_account(sc: str, acc: str):
    primary_url = "https://www.unifiedservices.co.uk/services"
    secondary_url = "https://www.unifiedsoftware.co.uk/services"
    credentials = Credentials(
        uname="UNAME_FROM_UNIFIED",
        pin="PIN_FROM_UNIFIED",)
    account = UKAccountRequest(
        sortcode=sc,
        account=acc,
    )
    bverp=BVERequestPayload(
        credentials=credentials,
        account=account,
    )
    response = call_service(bverp, primary_url)
    if not response:
        print("calling backup")
        response = call_service(bverp, secondary_url)
    return response
 

This function takes the sortcode and account to be validated .Then it builds the credentials and account objects, adds them to a BVE request payload object and passes it, with the server url, to the function that calls the actual service. If the first call fails it calls the secondary server. The return is a BVEResponse Object.

Calling the Server

The function to call the actual service is called call_service and takes the URL of the service and the created BVERequestPayload object and is shown below:

 def call_service(bverp: BVERequestPayload, server_url: str) -> BVEResponse:
    configuration = openapi_client.Configuration(
        host=server_url
    )
    configuration.retries = 1
    with openapi_client.ApiClient(configuration) as api_client:
        api_instance = default_api.DefaultApi(api_client)
    try:
        api_response = api_instance.bankval(body=bverp)
        print(api_response)
        bve_response: BVEResponse = BVEResponse(api_response.body)
        if isinstance(bve_response, BVEResponse):
            return bve_response
        elif "Error" in bve_response:
            # Add additional error handling/logging here.
            service_error = bve_response["Error"]
            return False
    except Exception as e:
        print("Exception when calling DefaultApi->bankval: %s\n" % e)
        return False

This function takes the BVERequestPayload object created in the calling function and the url and calls the service. It returns either the BVEResponse or False if there is an issue.

These functions are created in a file we’ve called BankVal.py. To use them import the validate_sortcode_account function. Then call it passing the sortcode and account details to be validated.

Consuming the service

The example below shows how to retreive the data returned such as the validation result and the bacsstatus flags. Please see the BankVal Enhanced user guide for more details on the returned data.

    bve_response = validate_sortcode_account("090666", "12345678")
    if "BankValUK" in bve_response:
        bankvaluk = bve_response.get_item_oapg("BankValUK")
        result = bankvaluk['result']
        print(result);
        if result == "VALID":
            print(bankvaluk["bacsstatus"])
            print(bankvaluk["bacsdrdisallowed"])
            print(bankvaluk["bacsddiflg"])
            print(bankvaluk["transposedsortcode"])
            print(bankvaluk['transposedaccount'])
    elif "Error" in bve_response:
        error_response = bve_response.get_item_oapg("Error")
        print(error_response)

The above calls validate_sortcode_account then checks to see if the response contains a BankValUK, or Error response, then handles accordingly. For this blog only the account number and sortcode checker service are called. Another service such as AddressVal Unified Software’s address validation service could also called. Then there would also be an address_val_response object in the bve_response, which would be accessed by calling the below.

    if "AddressVal" in bve_response:         
        addressval = bve_response.get_item_oapg("AddressVal")

To request a demo account please click here to try it yourself.