En algún momento, la mayoría de quienes desarrollan sistemas de pago chocan contra el mismo muro: necesitan valores con forma de IBAN y checksum válido para las pruebas, y no hay una buena forma de escribirlos a mano. Copiar y pegar el mismo puñado de ejemplos genera conjuntos de pruebas frágiles, y usar datos de cuentas de clientes crea riesgos innecesarios de privacidad y operación. Una opción más segura es generar valores sintéticos y mantenerlos aislados de los sistemas de pago reales.
Esta guía explica exactamente cómo hacerlo en Python, JavaScript y PHP. Al terminar tendrás una función pequeña y con pocas dependencias que podrás incorporar a una factoría, a un script de datos semilla o a un módulo de datos de prueba. Si solo necesitas unos pocos números ahora mismo sin escribir código, el generador de Random IBAN los produce al instante y los exporta a CSV, JSON o TXT.
Qué significa realmente «generar un IBAN»
Un IBAN no es un único número opaco: son tres partes concatenadas.
ES 76 21000418450200051332
│ │ └── BBAN (country-specific: bank + branch + account)
│ └───── Check digits (calculated, positions 3–4)
└──────── Country code (ISO 3166-1, positions 1–2)
Así que generar uno significa hacer tres cosas en orden:
- Elegir un país y consultar su longitud de IBAN y la disposición de su BBAN.
- Construir un BBAN aleatorio que se ajuste a esa disposición (la cantidad correcta de dígitos, y letras donde la especificación las permita).
- Calcular los dos dígitos de control con el algoritmo MOD-97 para que el conjunto sea válido.
El primer paso necesita una pequeña tabla de consulta. El segundo es pura generación aleatoria. El tercero es la única parte con lógica real, y es la misma rutina que usarías para validar un IBAN, ejecutada a la inversa.
El núcleo: calcular los dígitos de control
Todos los lenguajes de más abajo comparten la misma rutina de dígitos de control. La regla de la norma ISO 7064 / ISO 13616 es:
- Toma el BBAN, añade el código de país y luego añade
00. - Sustituye cada letra por dos dígitos (
A=10,B=11, …Z=35). - Lee el resultado como un único entero grande y calcula
98 - (entero mod 97). - Rellena ese resultado con ceros hasta dos dígitos.
Ese número rellenado es tu par de dígitos de control. Colócalo entre el código de país y el BBAN, y el IBAN terminado superará la validación MOD-97. Vamos a implementarlo.
Generar un IBAN en Python
Los enteros de precisión arbitraria de Python hacen que esto sea casi trivial: no hace falta ninguna biblioteca de enteros grandes.
import random
# Minimal layout table: country -> (total length, BBAN length)
IBAN_SPECS = {
"DE": 22, "ES": 24, "FR": 27, "GB": 22,
"NL": 18, "IT": 27, "PT": 25, "BE": 16,
}
def _check_digits(country: str, bban: str) -> str:
rearranged = bban + country + "00"
numeric = "".join(
str(ord(c) - 55) if c.isalpha() else c
for c in rearranged
)
check = 98 - (int(numeric) % 97)
return f"{check:02d}"
def generate_iban(country: str = "ES") -> str:
total_len = IBAN_SPECS[country]
bban_len = total_len - 4 # minus country code + check digits
bban = "".join(random.choices("0123456789", k=bban_len))
return country + _check_digits(country, bban) + bban
# Usage
print(generate_iban("DE")) # e.g. DE21 ... (valid checksum)
print([generate_iban("ES") for _ in range(5)])
Este ejemplo mínimo solo modela la longitud total y MOD-97. Su BBAN numérico no cumple el patrón nacional ni los controles domésticos de todos los países de la tabla (Reino Unido, Países Bajos e Italia exigen letras en posiciones concretas, por ejemplo), y un identificador bancario aleatorio no tiene por qué estar asignado ni vigente. Trata la salida como un ejercicio de checksum. Para fixtures con formato nacional, implementa el patrón completo del registro SWIFT y los controles domésticos, o usa RandomIBAN con sus límites documentados.
Generar un IBAN en JavaScript
El único inconveniente en JS es que el número reordenado es demasiado grande para un Number normal. La solución estándar es un bucle de resto acumulado, que mantiene el valor por debajo de 97 en todo momento.
const IBAN_SPECS = {
DE: 22, ES: 24, FR: 27, GB: 22,
NL: 18, IT: 27, PT: 25, BE: 16,
};
function mod97(numericString) {
let remainder = 0;
for (const ch of numericString) {
remainder = (remainder * 10 + Number(ch)) % 97;
}
return remainder;
}
function checkDigits(country, bban) {
const rearranged = bban + country + '00';
const numeric = rearranged.replace(/[A-Z]/g, c =>
(c.charCodeAt(0) - 55).toString()
);
const check = 98 - mod97(numeric);
return String(check).padStart(2, '0');
}
function generateIban(country = 'ES') {
const bbanLen = IBAN_SPECS[country] - 4;
let bban = '';
for (let i = 0; i < bbanLen; i++) {
bban += Math.floor(Math.random() * 10);
}
return country + checkDigits(country, bban) + bban;
}
// Usage
console.log(generateIban('FR'));
console.log(Array.from({ length: 5 }, () => generateIban('NL')));
Si necesitas una aleatoriedad criptográficamente más robusta (rara vez es necesario para datos de prueba), sustituye Math.random() por crypto.getRandomValues(). Para datos de prueba y demostraciones, la versión sencilla es perfectamente adecuada.
Generar un IBAN en PHP
Este ejemplo en PHP necesita la extensión opcional bcmath para el módulo del número grande. Comprueba que esté instalada y habilitada en el entorno donde ejecutes el código.
<?php
const IBAN_SPECS = [
'DE' => 22, 'ES' => 24, 'FR' => 27, 'GB' => 22,
'NL' => 18, 'IT' => 27, 'PT' => 25, 'BE' => 16,
];
function checkDigits(string $country, string $bban): string {
$rearranged = $bban . $country . '00';
$numeric = '';
foreach (str_split($rearranged) as $ch) {
$numeric .= ctype_alpha($ch) ? (string)(ord($ch) - 55) : $ch;
}
$check = 98 - (int) bcmod($numeric, '97');
return str_pad((string) $check, 2, '0', STR_PAD_LEFT);
}
function generateIban(string $country = 'ES'): string {
$bbanLen = IBAN_SPECS[$country] - 4;
$bban = '';
for ($i = 0; $i < $bbanLen; $i++) {
$bban .= random_int(0, 9);
}
return $country . checkDigits($country, $bban) . $bban;
}
// Usage
echo generateIban('IT'), PHP_EOL;
Fíjate en el uso de random_int() en lugar de rand(): es la opción moderna y sin sesgo, y aquí no cuesta nada más.
Usar IBAN generados en un conjunto de pruebas
Una vez que tengas un generador, intégralo en tus pruebas en lugar de esparcir valores literales. Algunos patrones que funcionan bien:
- Factorías y datos de prueba. Llama a
generateIban()dentro de tu factoría (FactoryBoy, un proveedor de Faker, una factory de Laravel) para que cada ejecución de pruebas obtenga fixtures nuevos que superen el checksum. - Datos semilla para staging. Llena las bases de datos de demostración con unos cientos de IBAN de varios países para ejercitar el formato específico de cada uno.
- Casos límite. Mantén un conjunto aparte de cadenas deliberadamente defectuosas (longitud incorrecta, dígito de control erróneo, país no compatible) para comprobar que tu validación las rechaza.
- Determinismo cuando lo necesites. Fija la semilla de tu generador de números aleatorios al inicio de una prueba si quieres IBAN reproducibles; déjala sin fijar para una cobertura de tipo fuzzing.
Sea lo que sea que generes, etiquétalo claramente como sintético. Estos números pueden superar las comprobaciones configuradas de estructura y MOD-97, pero RandomIBAN no verifica si un valor generado coincide con una cuenta asignada. Nunca deben llegar a un sistema de pagos en producción. Consulta nuestra guía sobre cómo almacenar IBAN de forma segura en sistemas de pago para evitar que se mezclen los datos de prueba y los de producción.
Cuándo no escribir tu propio generador
Crear el tuyo propio es estupendo para una integración estrecha, pero no siempre merece la pena. Sáltate el código cuando:
- Necesites un lote rápido ahora mismo: pégalos en segundos desde el generador de Random IBAN y expórtalos a CSV/JSON.
- Necesites una amplia cobertura de países con subestructuras de BBAN correctas (dígitos de control nacionales, posiciones de letras) que una tabla mínima no contempla.
- Estés trabajando fuera de una base de código: preparando una hoja de cálculo, una colección de Postman o documentación.
Y sea cual sea el camino que elijas, valida la salida. Pasa una muestra por el validador de IBAN para confirmar que tu lógica de dígitos de control coincide con la implementación de referencia. Si algo no supera el MOD-97, el error casi siempre está en la conversión de letras a números o en un error de desplazamiento en uno (off-by-one) en la longitud del BBAN.
Preguntas frecuentes
¿Es seguro usar IBAN generados mediante programación?
Son adecuados para validación de formato aislada, QA, demostraciones y documentación si se etiquetan y se bloquean para que no lleguen a vías de pago reales. Superar las comprobaciones de estructura y checksum no demuestra que el valor no esté asignado, así que nunca lo envíes a una transacción real ni a un sistema de producción.
¿Necesito una biblioteca externa para generar un IBAN?
Los ejemplos de Python y JavaScript no necesitan paquetes externos. El ejemplo de PHP requiere que la extensión opcional bcmath esté instalada y habilitada. Una biblioteca especializada o reglas nacionales adicionales pueden seguir siendo útiles si necesitas patrones BBAN completos y no solo longitud y MOD-97.
¿Por qué mi IBAN generado no supera la validación?
Las causas más comunes son una conversión incorrecta de letras a números (debe ser de A=10 a Z=35), olvidar añadir el código de país y 00 antes del paso del módulo, o usar una longitud de BBAN equivocada para el país. Compara un único caso fallido con el validador para aislarlo.
¿Cómo genero IBAN para países con letras en el BBAN?
Amplía el paso del BBAN aleatorio para colocar letras donde la especificación del país las permita; por ejemplo, el código de banco del Reino Unido son cuatro letras. Mantén un patrón por país (qué posiciones son letras y cuáles dígitos) en lugar de una única longitud, y luego rellena cada posición en consecuencia antes de calcular los dígitos de control.
¿Puedo generar miles de IBAN a la vez?
Sí. Las funciones generadoras son baratas de llamar en un bucle, así que producir miles es instantáneo. Si prefieres no ejecutar código, el generador de Random IBAN admite la generación masiva y la exportación con un clic a CSV, JSON y TXT.
¿Cuál es la diferencia entre generar y validar un IBAN?
La validación del checksum comprueba un valor existente confirmando que mod 97 == 1. La generación ejecuta la misma aritmética a la inversa: construyes primero el BBAN y luego calculas 98 - (mod 97) para hallar los dígitos de control que hacen que el valor supere MOD-97. Comparten la misma rutina central; ninguno de los dos pasos demuestra que el BBAN nacional esté completo ni que exista una cuenta.