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