Calculate IBAN

For my project I need to extract bank data (especially BIC or BLZ, account number) from IBAN and create IBAN from account information. Has anyone done something like this alreade?

Ask claude

The Deutsche Bundesbank has a long PDF with all rules how to create an IBAN from BLZ and BIC, it depends on the bank which of those many rules applies…

Well, I have that in my FileMaker plugin, but not in the Xojo one.
I could bring it over.

Formatting or IBANs, validation and checksum calculation.

1 Like

Would be nice to have, especialy validation and checksum calculation….

quite simple in fact.

Public Function VerifyIBAN(anIBAN as String) As Boolean
  ' nouvelle méthode 2024
  
  ' Supprimer les espaces de l'IBAN
  anIBAN = anIBAN.ReplaceAll(" ", "")
  
  ' Vérifier si la longueur de l'IBAN est valide (doit être de 27 caractères pour les IBAN standard européens)
  If anIBAN.Length <> 27 Then
    Return False
  End If
  
  ' Vérifier si l'IBAN commence par le code pays
  Dim countryCode As String = anIBAN.Left(2)
  If Not VerifierPaysIBAN(countryCode) Then
    Return False
  End If
  
  ' Déplacer les 4 premiers caractères à la fin
  anIBAN = anIBAN.Mid(5)// + anIBAN.Left(4)
  
  ' Convertir les lettres en nombres
  Dim convertedIBAN As String = ""
  For i As Integer = 1 To anIBAN.Length
    Dim ch As String = anIBAN.Mid(i, 1)
    If ch >= "A" And ch <= "Z" Then
      Dim newChar As String
      Select Case ch
      Case "A","J"
        newChar = "1"
      Case "B","K","S"
        newChar = "2"
      Case "C","L","T"
        newChar = "3"
      Case "D","M","U"
        newChar = "4"
      Case "E","N","V"
        newChar = "5"
      Case "F","O","W"
        newChar = "6"
      Case "G","P","X"
        newChar = "7"
      Case "H","Q","Y"
        newChar = "8"
      Case "I","R","Z"
        newChar = "9"
      End Select
      convertedIBAN = convertedIBAN + newChar
    Else
      convertedIBAN = convertedIBAN + ch
    End If
  Next
  
  Dim codeBanque As Integer = convertedIBAN.Middle(0,5).ToInteger
  Dim codeGuichet As Integer = convertedIBAN.Middle(5,5).ToInteger
  Dim noCompte As Integer = convertedIBAN.Middle(10,11).ToInteger
  Dim cleRib As Integer = convertedIBAN.Middle(21,2).ToInteger
  
  Dim k As Integer = (89*codeBanque)+(15*codeGuichet)+(3*noCompte)
  k = k Mod 97
  k = 97 - k
  Return (cleRib = k)
  
End Function

and

Public Function VerifierPaysIBAN(iban As String) As Boolean
  ' Liste des codes de pays reconnus selon la norme ISO 3166-1 alpha-2
  Dim paysConnus() As String = Array ("AD", "AE", "AF", "AG", "AI", "AL", "AM", "AO", "AQ", "AR", _
  "AS", "AT", "AU", "AW", "AX", "AZ", "BA", "BB", "BD", "BE", _
  "BF", "BG", "BH", "BI", "BJ", "BL", "BM", "BN", "BO", "BQ", _
  "BR", "BS", "BT", "BV", "BW", "BY", "BZ", "CA", "CC", "CD", _
  "CF", "CG", "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR", _
  "CU", "CV", "CW", "CX", "CY", "CZ", "DE", "DJ", "DK", "DM", _
  "DO", "DZ", "EC", "EE", "EG", "EH", "ER", "ES", "ET", "FI", _
    "FJ", "FK", "FM", "FO", "FR", "GA", "GB", "GD", "GE", "GF", _
    "GG", "GH", "GI", "GL", "GM", "GN", "GP", "GQ", "GR", "GS", _
    "GT", "GU", "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU", _
    "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS", "IT", _
    "JE", "JM", "JO", "JP", "KE", "KG", "KH", "KI", "KM", "KN", _
    "KP", "KR", "KW", "KY", "KZ", "LA", "LB", "LC", "LI", "LK", _
    "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "ME", _
    "MF", "MG", "MH", "MK", "ML", "MM", "MN", "MO", "MP", "MQ", _
    "MR", "MS", "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", _
    "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP", "NR", "NU", _
    "NZ", "OM", "PA", "PE", "PF", "PG", "PH", "PK", "PL", "PM", _
    "PN", "PR", "PS", "PT", "PW", "PY", "QA", "RE", "RO", "RS", _
    "RU", "RW", "SA", "SB", "SC", "SD", "SE", "SG", "SH", "SI", _
    "SJ", "SK", "SL", "SM", "SN", "SO", "SR", "SS", "ST", "SV", _
    "SX", "SY", "SZ", "TC", "TD", "TF", "TG", "TH", "TJ", "TK", _
    "TL", "TM", "TN", "TO", "TR", "TT", "TV", "TW", "TZ", "UA", _
    "UG", "UM", "US", "UY", "UZ", "VA", "VC", "VE", "VG", "VI", _
    "VN", "VU", "WF", "WS", "YE", "YT", "ZA", "ZM", "ZW")
    
    ' Extraire les deux premières lettres de l'IBAN
    Dim countryCode As String = iban.Left(2)
    
    ' Vérifier si le code de pays est dans la liste des pays connus
    For Each pays As String In paysConnus
      If countryCode = pays Then
        Return True
      End If
    Next
    
    ' Si le code de pays n'est pas dans la liste des pays connus
    Return False
End Function

1 Like

Long live Claude. :wink:

not claude …
I started it “manually”, then made it “beautify” by claude.
especially to list all the countries strings…
:slight_smile:

Since this thread gave me an idea that I can use in one of our company apps, I expanded it a bit this morning using Claude.ai.

The previous version was hard-coded for French IBANs. The current version is suitable for many countries. However, it doesn’t return a true/false result after verification, but rather the bank code, BIC, account number, and the bank’s identifier (name). This requires a mySQL database table with the corresponding information (which I can’t provide here).

Public Function VerifyIBAN(anIBAN As String) As String()
  // Remove all spaces and convert to uppercase for normalization
  anIBAN = anIBAN.ReplaceAll(" ", "").Uppercase
  
  // Validate country code against known ISO 3166-1 alpha-2 codes
  If Not VerifyIBANCountry(anIBAN.Left(2)) Then
    Return Nil
  End If
  
  // Validate IBAN length based on country-specific rules
  If Not IsValidIBANLength(anIBAN) Then
    Return Nil
  End If
  
  // Rearrange: move first 4 characters to the end (IBAN standard)
  Dim rearranged As String = anIBAN.Middle(4) + anIBAN.Left(4)
  
  // Convert letters to digits: A=10, B=11, ..., Z=35
  Dim numeric As String = ""
  For i As Integer = 0 To rearranged.Length - 1
    Dim ch As String = rearranged.Middle(i, 1)
    If ch >= "A" And ch <= "Z" Then
      numeric = numeric + Str(Asc(ch) - 55)
    Else
      numeric = numeric + ch
    End If
  Next
  
  // Perform MOD-97 iteratively to avoid integer overflow on long numeric strings
  Dim remainder As Integer = 0
  For i As Integer = 0 To numeric.Length - 1
    remainder = (remainder * 10 + numeric.Middle(i, 1).ToInteger) Mod 97
  Next
  
  If remainder <> 1 Then
    Return Nil
  End If
  
  // Extract bank code and account number based on country code
  Dim countryCode As String = anIBAN.Left(2)
  Dim bankCode As String = ""
  Dim accountNumber As String = ""
  
  Select Case countryCode
  Case "DE"
    // German IBAN: positions 4-11 = bank code (BLZ), 12-21 = account number
    bankCode = anIBAN.Middle(4, 8)
    accountNumber = anIBAN.Middle(12, 10)
  Case "AT"
    // Austrian IBAN: positions 4-8 = bank code, 9-19 = account number
    bankCode = anIBAN.Middle(4, 5)
    accountNumber = anIBAN.Middle(9, 11)
  Case "CH"
    // Swiss IBAN: positions 4-8 = bank code, 9-20 = account number
    bankCode = anIBAN.Middle(4, 5)
    accountNumber = anIBAN.Middle(9, 12)
  Case "FR"
    // French IBAN: positions 4-8 = bank code, 13-24 = account number
    bankCode = anIBAN.Middle(4, 5)
    accountNumber = anIBAN.Middle(13, 11)
  Case "GB"
    // British IBAN: positions 4-7 = bank code, 8-21 = account number
    bankCode = anIBAN.Middle(4, 4)
    accountNumber = anIBAN.Middle(8, 14)
  Case Else
    // Generic fallback: first 8 chars after check digits as bank code
    bankCode = anIBAN.Middle(4, 8)
    accountNumber = anIBAN.Middle(12)
  End Select
  
  // Query the banks table to retrieve BIC and bank name by bank code (BLZ)
  Var db As New Module_WAGDatenbank.WAG3Connection
  
  Try
    
    db.Connect
    
    Var rs As Rowset = db.SelectSQL("SELECT bic, bezeichnung FROM wag5_banken WHERE bankleitzahl = ? LIMIT 1", bankCode.ConvertEncoding(Encodings.ISOLatin1))
    If rs Is Nil Or rs.AfterLastRow Then
      Return Nil
    End If
    
    // Build and return result array: [bankCode, accountNumber, bic, bankName]
    Dim result() As String
    result.Add(bankCode)
    result.Add(accountNumber)
    result.Add(rs.Column("bic").StringValue.DefineEncoding(Encodings.ISOLatin1))
    result.Add(rs.Column("bezeichnung").StringValue.DefineEncoding(Encodings.ISOLatin1))
    
    rs.Close
    
    Return result
    
  Catch err As DatabaseException
    
  End Try
  
  Return Nil
  
End Function

Private Function VerifyIBANCountry(iban As String) As Boolean
  // Liste der gemäß ISO 3166-1 Alpha-2-Standard anerkannten Ländercodes
  Dim paysConnus() As String = Array ("AD", "AE", "AF", "AG", "AI", "AL", "AM", "AO", "AQ", "AR", _
  "AS", "AT", "AU", "AW", "AX", "AZ", "BA", "BB", "BD", "BE", _
  "BF", "BG", "BH", "BI", "BJ", "BL", "BM", "BN", "BO", "BQ", _
  "BR", "BS", "BT", "BV", "BW", "BY", "BZ", "CA", "CC", "CD", _
  "CF", "CG", "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR", _
  "CU", "CV", "CW", "CX", "CY", "CZ", "DE", "DJ", "DK", "DM", _
  "DO", "DZ", "EC", "EE", "EG", "EH", "ER", "ES", "ET", "FI", _
    "FJ", "FK", "FM", "FO", "FR", "GA", "GB", "GD", "GE", "GF", _
    "GG", "GH", "GI", "GL", "GM", "GN", "GP", "GQ", "GR", "GS", _
    "GT", "GU", "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU", _
    "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS", "IT", _
    "JE", "JM", "JO", "JP", "KE", "KG", "KH", "KI", "KM", "KN", _
    "KP", "KR", "KW", "KY", "KZ", "LA", "LB", "LC", "LI", "LK", _
    "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "ME", _
    "MF", "MG", "MH", "MK", "ML", "MM", "MN", "MO", "MP", "MQ", _
    "MR", "MS", "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", _
    "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP", "NR", "NU", _
    "NZ", "OM", "PA", "PE", "PF", "PG", "PH", "PK", "PL", "PM", _
    "PN", "PR", "PS", "PT", "PW", "PY", "QA", "RE", "RO", "RS", _
    "RU", "RW", "SA", "SB", "SC", "SD", "SE", "SG", "SH", "SI", _
    "SJ", "SK", "SL", "SM", "SN", "SO", "SR", "SS", "ST", "SV", _
    "SX", "SY", "SZ", "TC", "TD", "TF", "TG", "TH", "TJ", "TK", _
    "TL", "TM", "TN", "TO", "TR", "TT", "TV", "TW", "TZ", "UA", _
    "UG", "UM", "US", "UY", "UZ", "VA", "VC", "VE", "VG", "VI", _
    "VN", "VU", "WF", "WS", "YE", "YT", "ZA", "ZM", "ZW")
    
    // Extrahieren Sie die ersten beiden Buchstaben der IBAN.
    Dim countryCode As String = iban.Left(2)
    
    // Prüfen Sie, ob der Ländercode in der Liste der bekannten Länder enthalten ist.'
    For Each pays As String In paysConnus
      If countryCode = pays Then
        Return True
      End If
    Next
    
    // Wenn der Ländercode nicht in der Liste der bekannten Länder enthalten ist
    Return False
End Function

Private Function IsValidIBANLength(anIBAN As String) As Boolean
  // Validate IBAN length per country code according to the official IBAN registry.
  // Each country defines a fixed total length for its IBANs.
  // Returns False if the country is unknown or the length does not match.
  Dim countryCode As String = anIBAN.Left(2)
  Dim expectedLength As Integer
  
  Select Case countryCode
  Case "AD"
    expectedLength = 24
  Case "AE"
    expectedLength = 23
  Case "AL"
    expectedLength = 28
  Case "AT"
    expectedLength = 20
  Case "AZ"
    expectedLength = 28
  Case "BA"
    expectedLength = 20
  Case "BE"
    expectedLength = 16
  Case "BG"
    expectedLength = 22
  Case "BH"
    expectedLength = 22
  Case "BR"
    expectedLength = 29
  Case "BY"
    expectedLength = 28
  Case "CH"
    expectedLength = 21
  Case "CR"
    expectedLength = 22
  Case "CY"
    expectedLength = 28
  Case "CZ"
    expectedLength = 24
  Case "DE"
    expectedLength = 22
  Case "DJ"
    expectedLength = 27
  Case "DK"
    expectedLength = 18
  Case "DO"
    expectedLength = 28
  Case "EE"
    expectedLength = 20
  Case "EG"
    expectedLength = 29
  Case "ES"
    expectedLength = 24
  Case "FI"
    expectedLength = 18
  Case "FO"
    expectedLength = 18
  Case "FR"
    expectedLength = 27
  Case "GB"
    expectedLength = 22
  Case "GE"
    expectedLength = 22
  Case "GI"
    expectedLength = 23
  Case "GL"
    expectedLength = 18
  Case "GR"
    expectedLength = 27
  Case "GT"
    expectedLength = 28
  Case "HR"
    expectedLength = 21
  Case "HU"
    expectedLength = 28
  Case "IE"
    expectedLength = 22
  Case "IL"
    expectedLength = 23
  Case "IQ"
    expectedLength = 23
  Case "IS"
    expectedLength = 26
  Case "IT"
    expectedLength = 27
  Case "JO"
    expectedLength = 30
  Case "KW"
    expectedLength = 30
  Case "KZ"
    expectedLength = 20
  Case "LB"
    expectedLength = 28
  Case "LC"
    expectedLength = 32
  Case "LI"
    expectedLength = 21
  Case "LT"
    expectedLength = 20
  Case "LU"
    expectedLength = 20
  Case "LV"
    expectedLength = 21
  Case "LY"
    expectedLength = 25
  Case "MC"
    expectedLength = 27
  Case "MD"
    expectedLength = 24
  Case "ME"
    expectedLength = 22
  Case "MK"
    expectedLength = 19
  Case "MR"
    expectedLength = 27
  Case "MT"
    expectedLength = 31
  Case "MU"
    expectedLength = 30
  Case "NI"
    expectedLength = 28
  Case "NL"
    expectedLength = 18
  Case "NO"
    expectedLength = 15
  Case "PK"
    expectedLength = 24
  Case "PL"
    expectedLength = 28
  Case "PS"
    expectedLength = 29
  Case "PT"
    expectedLength = 25
  Case "QA"
    expectedLength = 29
  Case "RO"
    expectedLength = 24
  Case "RS"
    expectedLength = 22
  Case "RU"
    expectedLength = 33
  Case "SA"
    expectedLength = 24
  Case "SC"
    expectedLength = 31
  Case "SD"
    expectedLength = 18
  Case "SE"
    expectedLength = 24
  Case "SI"
    expectedLength = 19
  Case "SK"
    expectedLength = 24
  Case "SM"
    expectedLength = 27
  Case "ST"
    expectedLength = 25
  Case "SV"
    expectedLength = 28
  Case "TL"
    expectedLength = 23
  Case "TN"
    expectedLength = 24
  Case "TR"
    expectedLength = 26
  Case "UA"
    expectedLength = 29
  Case "VA"
    expectedLength = 22
  Case "VG"
    expectedLength = 24
  Case "XK"
    expectedLength = 20
  Case Else
    Return False
  End Select
  
  Return anIBAN.Length = expectedLength
  
End Function

1 Like

I didn’t notice that IBAN had different lengths depending on the country…

2 Likes

I wasn’t aware of that either, until Claude.ai pointed it out to me.

A tip for anyone wanting to set up a suitable MySQL table: Many banks offer a CSV file containing information about banks in their own country.
For Germany, for example, Deutsche Bank offers something like this at Bankleitzahlen | Deutsche Bundesbank.

you also have a lot of them here

1 Like

Just a short reminder: BIC codes are changing from time to time - in Germany the Deutsche Bundesbank sends out a list of changes every three months…

So if you rely on a correct list you have to update it frequently…

1 Like

Thanks to everybody, seems as I can get on now!

1 Like