12 KiB
IBAN Registry - Single Source of Truth
This directory contains the official SWIFT IBAN Registry data and tools to parse it into test fixtures for the IbanEx library.
🎯 Purpose
This is the single source of truth for IBAN validation rules, formats, and test data. All test cases should be derived from this official registry to ensure accuracy and compliance with international standards.
📁 Files
Source Data
iban-registry-100.txt- Official SWIFT IBAN Registry (Release 100) in TXT format- Source: https://www.swift.com/standards/data-standards/iban
- Contains: 89 base countries + territories = 105 total country codes
- Format: Tab-separated values with CRLF line endings
- Encoding: Latin-1 (ISO-8859-1)
Processing Scripts
get_iban_registry.py- Original script to fetch from SWIFT website (requires network)parse_local_registry.py- Recommended parser for localiban-registry-100.txtfile
Generated Fixtures
iban_registry_full.json(88 KB) - Complete registry with all fieldsiban_test_fixtures.json(81 KB) - Simplified fixtures for testing
🚀 Quick Start
Generate Test Fixtures
cd docs/international_wide_ibans
python3 parse_local_registry.py
Output:
✓ Parsed 89 records
✓ Processed 105 country codes
✓ Generated fixtures for 105 countries
✓ SEPA countries: 53
✓ Saved: iban_registry_full.json
✓ Saved: iban_test_fixtures.json
Use in Elixir Tests
# In your test setup
defmodule IbanEx.RegistryFixtures do
@fixtures_path "docs/international_wide_ibans/iban_test_fixtures.json"
@external_resource @fixtures_path
@fixtures @fixtures_path
|> File.read!()
|> Jason.decode!()
def all_valid_ibans do
@fixtures["valid_ibans"]
|> Enum.map(fn {_code, data} -> data["electronic"] end)
end
def valid_iban(country_code) do
@fixtures["valid_ibans"][country_code]["electronic"]
end
def country_spec(country_code) do
@fixtures["country_specs"][country_code]
end
def sepa_countries do
@fixtures["country_specs"]
|> Enum.filter(fn {_code, spec} -> spec["sepa"] end)
|> Enum.map(fn {code, _spec} -> code end)
end
end
📊 Registry Statistics
Coverage
- Total Countries/Territories: 105
- SEPA Countries: 53
- Non-SEPA Countries: 52
IBAN Length Distribution
| Length | Count | Example Countries |
|---|---|---|
| 15 | 1 | Norway (NO) - Shortest |
| 16 | 1 | Belgium (BE) |
| 18 | 8 | Denmark (DK), Finland (FI), Greenland (GL), Faroe Islands (FO) |
| 19 | 2 | Mongolia (MN), Slovakia (SK) |
| 20 | 8 | Austria (AT), Estonia (EE), Kosovo (XK) |
| 21 | 4 | Switzerland (CH), Croatia (HR), Latvia (LV), Lithuania (LT) |
| 22 | 13 | Germany (DE), Bulgaria (BG), Georgia (GE), Bahrain (BH) |
| 23 | 7 | UAE (AE), Israel (IL), Iraq (IQ), Iceland (IS), Qatar (QA), El Salvador (SV) |
| 24 | 11 | Andorra (AD), Czech Republic (CZ), Spain (ES), Poland (PL), Romania (RO), San Marino (SM) |
| 25 | 3 | Libya (LY), Portugal (PT), Serbia (RS) |
| 26 | 2 | Italy (IT), Yemen (YE) |
| 27 | 20 | France (FR), Greece (GR), Burundi (BI), many territories |
| 28 | 12 | Albania (AL), Azerbaijan (AZ), Cyprus (CY), Dominican Republic (DO), Nicaragua (NI) |
| 29 | 5 | Brazil (BR), Egypt (EG), Pakistan (PK), Qatar (QA) |
| 30 | 4 | Jordan (JO), Kuwait (KW), Mauritius (MU) |
| 31 | 2 | Malta (MT), Sweden (SE) |
| 32 | 1 | Saint Lucia (LC) |
| 33 | 1 | Russia (RU) - Longest |
Special Characteristics
Shortest IBAN:
- NO (Norway) - 15 characters
- Example:
NO9386011117947
Longest IBAN:
- RU (Russian Federation) - 33 characters
- Example:
RU0304452522540817810538091310419
SEPA Countries Include Territories:
- FR (France): GF, GP, MQ, YT, RE, PM, BL, MF
- GB (United Kingdom): IM, JE, GG
- FI (Finland): AX (Åland Islands)
- PT (Portugal): Azores, Madeira
- ES (Spain): AX (listed separately)
📋 Data Structure
Valid IBANs (iban_test_fixtures.json)
{
"valid_ibans": {
"DE": {
"electronic": "DE89370400440532013000",
"print": "DE89 3704 0044 0532 0130 00",
"country_name": "Germany"
}
},
"country_specs": {
"DE": {
"country_name": "Germany",
"iban_length": 22,
"bban_length": 18,
"iban_spec": "DE2!n8!n10!n",
"bban_spec": "8!n10!n",
"sepa": true,
"positions": {
"bank_code": {
"start": 0,
"end": 8,
"pattern": "8!n",
"example": "37040044"
},
"branch_code": {
"start": 8,
"end": 8,
"pattern": "",
"example": ""
},
"account_code": {
"start": 8,
"end": 18,
"example": "0532013000"
}
},
"effective_date": "Jul-07"
}
},
"metadata": {
"total_countries": 105,
"sepa_countries": 53,
"source": "SWIFT IBAN Registry",
"format_version": "TXT Release 100"
}
}
Full Registry (iban_registry_full.json)
Contains additional fields:
other_territories: List of territory codes coveredparent_country: For territories, reference to parent country- Complete BBAN structure specifications
- All position information with patterns and examples
🔍 Pattern Specifications
IBAN and BBAN structures use these format codes:
n- Numeric digits (0-9)a- Uppercase alphabetic letters (A-Z)c- Alphanumeric characters (A-Z, 0-9)!- Fixed length indicator- Number - Length of the field
Examples:
DE2!n8!n10!n= DE + 2 check digits + 8 numeric (bank) + 10 numeric (account)FR2!n5!n5!n11!c2!n= FR + 2 check digits + 5n (bank) + 5n (branch) + 11c (account) + 2n (check)
✅ Test Coverage Validation
Using Registry for Comprehensive Tests
defmodule IbanEx.RegistryValidationTest do
use ExUnit.Case, async: true
@fixtures "docs/international_wide_ibans/iban_test_fixtures.json"
|> File.read!()
|> Jason.decode!()
describe "validate against official SWIFT registry" do
test "all registry IBANs parse successfully" do
for {code, data} <- @fixtures["valid_ibans"] do
iban = data["electronic"]
assert {:ok, parsed} = IbanEx.Parser.parse(iban),
"Failed to parse official IBAN for #{code}: #{iban}"
assert parsed.country_code == code
end
end
test "all registry IBANs pass validation" do
for {_code, data} <- @fixtures["valid_ibans"] do
iban = data["electronic"]
assert {:ok, _} = IbanEx.Validator.validate(iban),
"Validation failed for official IBAN: #{iban}"
end
end
test "all registry IBANs have correct length" do
for {code, data} <- @fixtures["valid_ibans"] do
iban = data["electronic"]
spec = @fixtures["country_specs"][code]
assert String.length(iban) == spec["iban_length"],
"Length mismatch for #{code}: expected #{spec["iban_length"]}, got #{String.length(iban)}"
end
end
test "SEPA countries match registry" do
sepa_countries = @fixtures["country_specs"]
|> Enum.filter(fn {_code, spec} -> spec["sepa"] end)
|> Enum.map(fn {code, _} -> code end)
|> MapSet.new()
# Compare with your implementation
our_sepa = IbanEx.Country.sepa_countries() |> MapSet.new()
assert MapSet.equal?(sepa_countries, our_sepa),
"SEPA country mismatch. Missing: #{inspect(MapSet.difference(sepa_countries, our_sepa))}, Extra: #{inspect(MapSet.difference(our_sepa, sepa_countries))}"
end
end
end
Regression Test Generation
defmodule IbanEx.RegistryRegressionTest do
use ExUnit.Case, async: true
@fixtures "docs/international_wide_ibans/iban_test_fixtures.json"
|> File.read!()
|> Jason.decode!()
for {code, data} <- @fixtures["valid_ibans"] do
@tag :registry
test "#{code} - #{data["country_name"]}: parses and validates" do
iban = unquote(data["electronic"])
# Parse
assert {:ok, parsed} = IbanEx.Parser.parse(iban)
assert parsed.country_code == unquote(code)
# Validate
assert {:ok, _} = IbanEx.Validator.validate(iban)
# Round-trip
formatted = IbanEx.Formatter.compact(parsed)
assert formatted == iban
end
end
end
🔄 Updating the Registry
When to Update
- SWIFT releases new IBAN Registry version
- New countries added to IBAN system
- Existing country specifications change
- SEPA membership changes
Update Process
-
Download latest registry:
- Visit: https://www.swift.com/standards/data-standards/iban
- Download "IBAN Registry (TXT)" file
- Save as
iban-registry-XXX.txt(where XXX is version)
-
Update references:
mv iban-registry-XXX.txt iban-registry-100.txt # Update to new version -
Regenerate fixtures:
python3 parse_local_registry.py -
Run regression tests:
mix test --only registry -
Review changes:
git diff iban_test_fixtures.json -
Update IbanEx country modules if needed:
- Compare new specs with existing
lib/iban_ex/country/*.exfiles - Add new countries as needed
- Update changed specifications
- Compare new specs with existing
📖 References
Official Sources
- SWIFT IBAN Registry: https://www.swift.com/standards/data-standards/iban
- IBAN Standard (ISO 13616): https://www.iso.org/standard/81090.html
- SEPA: https://www.europeanpaymentscouncil.eu/
Additional Resources
- IBAN Structure: https://en.wikipedia.org/wiki/International_Bank_Account_Number
- Modulo 97 Check Digit: ISO/IEC 7064, MOD 97-10
- SWIFT Standards: https://www.swift.com/standards
🧪 Test Data Best Practices
DO ✅
- Use registry data for valid IBAN examples
- Generate invalid IBANs by mutating valid ones from registry
- Test all countries from the registry
- Verify IBAN length against registry specifications
- Check SEPA status from registry metadata
- Use official examples for documentation
DON'T ❌
- Hard-code IBAN examples without verifying against registry
- Assume IBAN length is constant across countries
- Skip testing edge cases (shortest: NO, longest: RU)
- Ignore territory codes (they share parent country rules)
- Test with outdated IBAN formats
🔧 Troubleshooting
Parser Issues
Problem: ValueError: max() arg is an empty sequence
- Cause: File encoding mismatch or wrong line endings
- Solution: Ensure file is Latin-1 encoded with CRLF endings
Problem: Missing country codes
- Cause: Incorrect tab parsing
- Solution: Verify
\tseparator and handle empty cells
Fixture Generation
Problem: Some countries missing in output
- Cause: Empty country code or IBAN example
- Solution: Check source file for completeness
Problem: Position ranges incorrect
- Cause: Off-by-one error in range parsing
- Solution: Verify 1-indexed to 0-indexed conversion
📝 License & Attribution
- Source Data: © SWIFT - Society for Worldwide Interbank Financial Telecommunication
- Usage: For validation and testing purposes in accordance with SWIFT standards
- Parser Script: Open source, provided as-is
🤝 Contributing
When adding tests based on this registry:
- Reference the specific registry version used (e.g., "Release 100")
- Include the generation date in test documentation
- Link test cases to registry entries by country code
- Document any discrepancies between registry and implementation
Last Updated: 2025-01-29
Registry Version: Release 100
Total Countries: 105
Parser Version: 1.0.0