Complete IBAN Validation Guide for Developers

IBAN validation is crucial for payment processing applications, banking software, and fintech platforms. This comprehensive guide covers the mod-97 algorithm, implementation examples, validation rules, and best practices for implementing robust IBAN verification in your applications.

What You'll Learn

  • IBAN validation algorithm (mod-97) implementation
  • Country-specific validation rules and formats
  • Error handling and validation feedback
  • Performance optimization techniques
  • API integration best practices
  • Real-world implementation examples

IBAN Validation Algorithm: Mod-97 Implementation

The IBAN validation algorithm follows the ISO 13616 standard using the mod-97 technique. Here's a step-by-step breakdown of how to implement IBAN validation:

Step-by-Step Validation Process

  1. Format Check: Remove spaces and convert to uppercase
  2. Length Validation: Verify IBAN length matches country requirements
  3. Country Code Check: Validate ISO 3166-1 alpha-2 country code
  4. Character Validation: Ensure only alphanumeric characters
  5. Rearrangement: Move first 4 characters to end of string
  6. Character Replacement: Replace letters with numbers (A=10, B=11, etc.)
  7. Mod-97 Calculation: Calculate remainder when divided by 97
  8. Result Check: Valid IBAN if remainder equals 1

JavaScript Implementation Example

function validateIBAN(iban) {
  // Step 1: Format check - remove spaces and convert to uppercase
  const cleanIban = iban.replace(/\s+/g, '').toUpperCase();
  
  // Step 2: Length and basic format validation
  if (!/^[A-Z]{2}[0-9]{2}[A-Z0-9]+$/.test(cleanIban)) {
    return { valid: false, error: 'Invalid IBAN format' };
  }
  
  // Step 3: Country-specific length check
  const countryLengths = {
    'AD': 24, 'AE': 23, 'AL': 28, 'AT': 20, 'AZ': 28,
    'BA': 20, 'BE': 16, 'BG': 22, 'BH': 22, 'BR': 29,
    'BY': 28, 'CH': 21, 'CR': 22, 'CY': 28, 'CZ': 24,
    'DE': 22, 'DK': 18, 'DO': 28, 'EE': 20, 'EG': 29,
    'ES': 24, 'FI': 18, 'FO': 18, 'FR': 27, 'GB': 22,
    'GE': 22, 'GI': 23, 'GL': 18, 'GR': 27, 'GT': 28,
    'HR': 21, 'HU': 28, 'IE': 22, 'IL': 23, 'IS': 26,
    'IT': 27, 'JO': 30, 'KW': 30, 'KZ': 20, 'LB': 28,
    'LC': 32, 'LI': 21, 'LT': 20, 'LU': 20, 'LV': 21,
    'MC': 27, 'MD': 24, 'ME': 22, 'MK': 19, 'MR': 27,
    'MT': 31, 'MU': 30, 'NL': 18, 'NO': 15, 'PK': 24,
    'PL': 28, 'PS': 29, 'PT': 25, 'QA': 29, 'RO': 24,
    'RS': 22, 'SA': 24, 'SE': 24, 'SI': 19, 'SK': 24,
    'SM': 27, 'TN': 24, 'TR': 26, 'UA': 29, 'VG': 24,
    'XK': 20
  };
  
  const countryCode = cleanIban.substring(0, 2);
  const expectedLength = countryLengths[countryCode];
  
  if (!expectedLength) {
    return { valid: false, error: 'Invalid country code' };
  }
  
  if (cleanIban.length !== expectedLength) {
    return { 
      valid: false, 
      error: `Invalid length for ${countryCode}. Expected ${expectedLength}, got ${cleanIban.length}`
    };
  }
  
  // Step 4: Rearrange - move first 4 characters to end
  const rearranged = cleanIban.substring(4) + cleanIban.substring(0, 4);
  
  // Step 5: Replace letters with numbers
  const letterMap = {
    'A': '10', 'B': '11', 'C': '12', 'D': '13', 'E': '14', 'F': '15',
    'G': '16', 'H': '17', 'I': '18', 'J': '19', 'K': '20', 'L': '21',
    'M': '22', 'N': '23', 'O': '24', 'P': '25', 'Q': '26', 'R': '27',
    'S': '28', 'T': '29', 'U': '30', 'V': '31', 'W': '32', 'X': '33',
    'Y': '34', 'Z': '35'
  };
  
  let numericString = '';
  for (let char of rearranged) {
    numericString += letterMap[char] || char;
  }
  
  // Step 6: Mod-97 calculation for large numbers
  function mod97(numStr) {
    let remainder = 0;
    for (let i = 0; i < numStr.length; i++) {
      remainder = (remainder * 10 + parseInt(numStr[i])) % 97;
    }
    return remainder;
  }
  
  const checksum = mod97(numericString);
  
  return {
    valid: checksum === 1,
    error: checksum !== 1 ? 'Invalid check digits' : null,
    country: countryCode,
    length: cleanIban.length
  };
}

// Usage example
console.log(validateIBAN('DE89 3704 0044 0532 0130 00'));
// Output: { valid: true, error: null, country: 'DE', length: 22 }

Country-Specific Validation Rules

Different countries have additional validation rules beyond the basic mod-97 check. Here are some important country-specific considerations:

🇹🇷 Turkey (TR)

  • Length: Exactly 26 characters
  • Bank code: 5 digits (positions 5-9)
  • Reserve digit: 1 digit (position 10)
  • Account number: 16 digits (positions 11-26)
  • Bank codes must be registered with CBRT

🇩🇪 Germany (DE)

  • Length: Exactly 22 characters
  • Bank code (BLZ): 8 digits (positions 5-12)
  • Account number: 10 digits (positions 13-22)
  • BLZ must exist in Deutsche Bundesbank registry
  • Account numbers padded with leading zeros

🇬🇧 United Kingdom (GB)

  • Length: Exactly 22 characters
  • Bank code: 4 letters (positions 5-8)
  • Sort code: 6 digits (positions 9-14)
  • Account number: 8 digits (positions 15-22)
  • Bank codes must be valid UK bank identifiers

🇮🇹 Italy (IT)

  • Length: Exactly 27 characters
  • CIN: 1 check character (position 5)
  • ABI: 5 digits bank code (positions 6-10)
  • CAB: 5 digits branch code (positions 11-15)
  • Additional CIN validation required

Advanced Validation Techniques

Bank Code Validation

For production applications, validate bank codes against official registries:

  • Germany: Deutsche Bundesbank BLZ registry
  • UK: Sort Code Checker database
  • Turkey: Central Bank of Turkey bank codes
  • France: Banque de France RIB validation
  • Italy: Bank of Italy ABI/CAB codes

Performance Optimization

For high-volume applications, consider these optimization strategies:

  • Cache country length validation rules
  • Pre-compile regex patterns for format checking
  • Use lookup tables for letter-to-number conversion
  • Implement early exit strategies for obvious invalid formats
  • Consider WebAssembly for compute-intensive validation

Error Handling Best Practices

Provide meaningful error messages for different validation failures:

  • Format errors: "IBAN must contain only letters and numbers"
  • Length errors: "German IBANs must be exactly 22 characters"
  • Country errors: "Invalid country code: XX"
  • Checksum errors: "Invalid IBAN check digits"
  • Bank code errors: "Bank code not found in registry"

API Integration Examples

REST API Implementation

// Express.js API endpoint for IBAN validation
app.post('/api/validate-iban', (req, res) => {
  const { iban } = req.body;
  
  if (!iban) {
    return res.status(400).json({
      valid: false,
      error: 'IBAN is required',
      code: 'MISSING_IBAN'
    });
  }
  
  try {
    const result = validateIBAN(iban);
    
    res.json({
      valid: result.valid,
      iban: iban.replace(/\s+/g, '').toUpperCase(),
      country: result.country,
      length: result.length,
      error: result.error,
      timestamp: new Date().toISOString()
    });
  } catch (error) {
    res.status(500).json({
      valid: false,
      error: 'Internal validation error',
      code: 'VALIDATION_ERROR'
    });
  }
});

// Example response for valid IBAN
{
  "valid": true,
  "iban": "DE89370400440532013000",
  "country": "DE",
  "length": 22,
  "error": null,
  "timestamp": "2024-01-15T10:30:00.000Z"
}

Frontend Integration

// React hook for IBAN validation
import { useState, useCallback } from 'react';

export function useIBANValidation() {
  const [validationState, setValidationState] = useState({
    valid: null,
    error: null,
    loading: false
  });
  
  const validateIBAN = useCallback(async (iban) => {
    if (!iban) {
      setValidationState({ valid: false, error: 'IBAN is required', loading: false });
      return;
    }
    
    setValidationState({ valid: null, error: null, loading: true });
    
    try {
      const response = await fetch('/api/validate-iban', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ iban })
      });
      
      const result = await response.json();
      setValidationState({
        valid: result.valid,
        error: result.error,
        loading: false
      });
      
      return result;
    } catch (error) {
      setValidationState({
        valid: false,
        error: 'Validation service unavailable',
        loading: false
      });
    }
  }, []);
  
  return { ...validationState, validateIBAN };
}

Testing and Quality Assurance

Test Cases for IBAN Validation

Comprehensive test suite should include:

  • Valid IBANs: Test with known valid IBANs for each supported country
  • Invalid check digits: Modify check digits in valid IBANs
  • Wrong lengths: Test IBANs with incorrect lengths
  • Invalid country codes: Use non-existent country codes
  • Invalid characters: Include special characters and lowercase letters
  • Empty/null values: Test edge cases with empty inputs
  • Whitespace handling: Test IBANs with various whitespace patterns

Sample Test Data

const testCases = [
  // Valid IBANs
  { iban: 'DE89370400440532013000', expected: true, country: 'DE' },
  { iban: 'GB82WEST12345698765432', expected: true, country: 'GB' },
  { iban: 'TR330006200000000000000123', expected: true, country: 'TR' },
  { iban: 'FR1420041010050500013M02606', expected: true, country: 'FR' },
  
  // Invalid check digits
  { iban: 'DE88370400440532013000', expected: false, error: 'Invalid check digits' },
  { iban: 'GB83WEST12345698765432', expected: false, error: 'Invalid check digits' },
  
  // Wrong lengths
  { iban: 'DE8937040044053201300', expected: false, error: 'Invalid length' },
  { iban: 'GB82WEST123456987654321', expected: false, error: 'Invalid length' },
  
  // Invalid country codes
  { iban: 'XX8937040044053201300', expected: false, error: 'Invalid country code' },
  
  // Invalid characters
  { iban: 'DE89-3704-0044-0532-0130-00', expected: false, error: 'Invalid format' },
  { iban: 'de89370400440532013000', expected: true, country: 'DE' }, // Should work after cleanup
];

// Run tests
testCases.forEach((test, index) => {
  const result = validateIBAN(test.iban);
  console.assert(
    result.valid === test.expected, 
    `Test ${index + 1} failed: Expected ${test.expected}, got ${result.valid}`
  );
});

Frequently Asked Questions

What's the difference between IBAN validation and IBAN verification?

IBAN validation checks the format, length, and mathematical correctness using the mod-97 algorithm. IBAN verification goes further by checking if the bank account actually exists, which requires integration with banking networks or third-party services.

How do I handle IBAN validation for unsupported countries?

For countries not in your validation list, you can perform basic format checks (2-letter country code + 2 check digits + alphanumeric BBAN) and mod-97 validation. However, you won't be able to validate country-specific length or bank code formats.

Should I validate IBANs on the frontend or backend?

Implement validation on both frontend (for immediate user feedback) and backend (for security and data integrity). Frontend validation improves user experience, while backend validation ensures data consistency and prevents malicious inputs.

How do I handle large numbers in mod-97 calculation?

For very long IBANs, use iterative mod-97 calculation instead of converting the entire number to integer. Process digits one by one: remainder = (remainder * 10 + digit) % 97. This prevents integer overflow issues.

What's the performance impact of IBAN validation?

IBAN validation is computationally lightweight. The mod-97 calculation is O(n) where n is the IBAN length. For high-volume applications, consider caching validation results and optimizing string operations for better performance.