HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonModerate

Python IBAN validation

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
validationpythoniban

Problem

I wrote a simple Python IBAN validator, which I'd like some reviews on.

INTRO

Quoting from wikipedia, we know that:


The International Bank Account Number (IBAN) is an internationally
agreed system of identifying bank accounts across national borders to
facilitate the communication and processing of cross border
transactions with a reduced risk of transcription errors.

Now, an IBAN may look like this: GB82WEST12345698765432

There are three algorithms to validate an IBAN:

  • Validating the IBAN



  • Generating IBAN check digits



  • Modulo operation on IBAN



I used only the first 2 methods in my program.

  1. Validating the IBAN



Description (from wiki):


An IBAN is validated by converting it into an integer and performing a
basic mod-97 operation (as described in ISO 7064) on it. If the IBAN
is valid, the remainder equals 1. The algorithm of IBAN
validation is as follows:


Check that the total IBAN length is correct as per the country. If
not, the IBAN is invalid Move the four initial characters to the end
of the string Replace each letter in the string with two digits,
thereby expanding the string, where A = 10, B = 11, ..., Z = 35
Interpret the string as a decimal integer and compute the remainder of
that number on division by 97 If the remainder is 1, the check digit
test is passed and the IBAN might be valid.


Example (fictitious United Kingdom bank, sort code 12-34-56, account
number 98765432):

• IBAN:                   GB82WEST12345698765432  
• Rearrange:              WEST12345698765432GB82  
• Convert to integer:     3214282912345698765432161182    
• Compute remainder:      3214282912345698765432161182    mod 97 = 1


  1. Generating IBAN check digits



Description (from wiki):


According to the ECBS "generation of the IBAN shall be the exclusive
responsibility of the bank/branch servicing the account".[8] The ECBS
document replicates part of the ISO/IEC 7064:2003 standard as a method
for generating che

Solution

Changes I'd make, from the top down:

  • Use string with enumerate to make the dictionary. Rather than manually making one.



  • letters is a constant, so use LETTERS.



  • Make a new function to change the layout of an IBAN and change it to a number.



  • Rename chech_validation_chars_iban to the name generate_iban_check_digits as it's not a check, it's a transform.



  • Change the format to use strings, {:0>2}, and remove the if.



  • Change validate_iban to check if it's a valid IBAN, and so return a boolean.



  • Use and to reduce the amount of ifs.



This resulted in:

import string
LETTERS = {ord(d): str(i) for i, d in enumerate(string.digits + string.ascii_uppercase)}

def _number_iban(iban):
    return (iban[4:] + iban[:4]).translate(LETTERS)

def generate_iban_check_digits(iban):
    number_iban = _number_iban(iban[:2] + '00' + iban[4:])
    return '{:0>2}'.format(98 - (int(number_iban) % 97))

def valid_iban(iban):
    return int(_number_iban(iban)) % 97 == 1

if __name__ == '__main__':
    my_iban = 'RO13RZBR0000060007134800'
    if generate_iban_check_digits(my_iban) == my_iban[2:4] and valid_iban(my_iban):
        print('IBAN ok!\n')
    else:
        print('IBAN not ok!\n')


There is one glairing problem with this, the specification states that it allows both upper and lowercase input:


Replace the letters in the string with digits, expanding the string as necessary, such that A or a = 10, B or b = 11, and Z or z = 35. Each alphabetic character is therefore replaced by 2 digits

However we only allow uppercase. To amend this I'd change the letters dict to include the lowercase letters.
But makes the code a little harder to read, and not as clean.
And so the cleanest way would probably be to use itertools.chain, with the method we're using now.

_LETTERS = chain(enumerate(string.digits + string.ascii_uppercase),
                 enumerate(string.ascii_lowercase, 10))
LETTERS = {ord(d): str(i) for i, d in _LETTERS}

Code Snippets

import string
LETTERS = {ord(d): str(i) for i, d in enumerate(string.digits + string.ascii_uppercase)}


def _number_iban(iban):
    return (iban[4:] + iban[:4]).translate(LETTERS)


def generate_iban_check_digits(iban):
    number_iban = _number_iban(iban[:2] + '00' + iban[4:])
    return '{:0>2}'.format(98 - (int(number_iban) % 97))


def valid_iban(iban):
    return int(_number_iban(iban)) % 97 == 1


if __name__ == '__main__':
    my_iban = 'RO13RZBR0000060007134800'
    if generate_iban_check_digits(my_iban) == my_iban[2:4] and valid_iban(my_iban):
        print('IBAN ok!\n')
    else:
        print('IBAN not ok!\n')
_LETTERS = chain(enumerate(string.digits + string.ascii_uppercase),
                 enumerate(string.ascii_lowercase, 10))
LETTERS = {ord(d): str(i) for i, d in _LETTERS}

Context

StackExchange Code Review Q#135366, answer score: 13

Revisions (0)

No revisions yet.