¿Alguna vez te has preguntado cómo un sitio web sabe instantáneamente si el número de tu tarjeta de crédito es falso o tiene un error antes de procesar el pago? La respuesta es el Algoritmo de Luhn.
También conocido como el algoritmo de módulo 10 mod 10, fue creado por el científico de IBM Hans Peter Luhn en 1954. Es una fórmula de suma de verificación (checksum) simple pero poderosa, utilizada para validar una variedad de números de identificación.
¿Dónde se utiliza?
- Tarjetas de Crédito: Visa, MasterCard, American Express. 💳
- IMEI: Números de identidad de teléfonos móviles.
- Seguridad Social: En algunos países para validar documentos nacionales.
- Números de cuenta bancaria
- Códigos de barras de algunos productos
Cómo funciona el algoritmo (Paso a paso)
El algoritmo valida un número basándose en cálculos matemáticos sobre sus dígitos de derecha a izquierda.
- Empezar por la derecha: Desde el último dígito (el dígito de control), muévete hacia la izquierda.
- Duplicar cada segundo dígito: Empezando por el segundo dígito desde la derecha, duplica el valor de cada segundo dígito.
- Ajustar resultados: Si al duplicar un número el resultado es mayor a 9 (por ejemplo, $8 \times 2 = 16$), suma los dígitos del resultado ($1 + 6 = 7$) o simplemente réstale 9 ($16 - 9 = 7$).
- Sumar todo: Suma todos los dígitos finales (tanto los que duplicaste como los que dejaste igual).
-
Verificación final: Si el total de la suma termina en 0 (es decir,
total % 10 == 0), entonces el número es válido.
Ejemplo Práctico:
-
Tomemos como ejemplo el número:
4539 1488 0343 6467Paso 1 — Separar el dígito de control
El último dígito (
7en nuestro ejemplo) es el dígito de control (check digit). Lo apartamos temporalmente.4 5 3 9 1 4 8 8 0 3 4 3 6 4 6 [7] ↑ dígito de controlPaso 2 — Duplicar los dígitos en posición par (de derecha a izquierda)
Empezando desde el dígito más a la derecha del número restante y moviéndonos a la izquierda, duplicamos los dígitos en posición par (2ª, 4ª, 6ª…):
Posición: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Dígito: 4 5 3 9 1 4 8 8 0 3 4 3 6 4 6 Acción: ×2 ×2 ×2 ×2 ×2 ×2 ×2 ×2 Resultado: 4 10 3 18 1 8 8 16 0 6 4 6 6 8 6Paso 3 — Restar 9 si el resultado es mayor que 9
10 → 10 - 9 = 1 18 → 18 - 9 = 9 16 → 16 - 9 = 7 6 → 6 = 6Equivalente a sumar los dígitos del resultado:
10 → 1+0 = 1.4 1 3 9 1 8 8 7 0 6 4 6 6 8 6Paso 4 — Sumar todos los dígitos
4+1+3+9+1+8+8+7+0+6+4+6+6+8+6 = 77Paso 5 — Añadir el dígito de control y verificar
77 + 7 = 84Si el resultado es divisible entre 10 → número válido ✅
84 % 10 = 4 ← ¡No es 0!Eso significa que
4539 1488 0343 6467no pasaría la validación. Usemos un número real válido:4532015112830366
Ejemplo completo con número real válido
Número:
4532 0151 1283 0366Posición (der→izq) Dígito Acción Resultado Si >9, -9 1 (control) 6 — — — 2 6 ×2 12 3 3 3 — 3 — 4 0 ×2 0 0 5 3 — 3 — 6 8 ×2 16 7 7 2 — 2 — 8 1 ×2 2 2 9 1 — 1 — 10 5 ×2 10 1 11 1 — 1 — 12 0 ×2 0 0 13 2 — 2 — 14 3 ×2 6 6 15 5 — 5 — 16 4 ×2 8 8 Suma = 3+3+0+3+7+2+2+1+1+0+2+6+5+8 = 44
44 + 6 (dígito de control) = 50
50 % 10 = 0 → ✅ Válido
¿Cuántos números válidos existen? Dado un número de N dígitos, aproximadamente 1 de cada 10 números aleatorios pasará la validación de Luhn. Esto significa que un atacante que intente adivinar números de tarjeta a ciegas tendría una tasa de éxito del ~10% solo en la validación básica, antes de cualquier verificación adicional del banco.
¿Puedo usar Luhn para validar tarjetas de crédito reales en mi app? Sí, como primera capa de validación en el cliente. Sin embargo, la validación definitiva siempre debe hacerse contra la red de pago (Visa, Mastercard, etc.) a través de un procesador de pagos certificado (Stripe, Redsys, Adyen…). Luhn solo descarta números imposibles, no confirma que la tarjeta exista o tenga fondos. ¿Por qué empezar a contar desde la derecha y no desde la izquierda? Porque el número de dígitos puede variar entre emisores (14, 15 o 16 dígitos). Anclando el conteo desde la derecha, el dígito de control siempre es la posición 1 (impar, sin duplicar), y el patrón de duplicación se mantiene constante independientemente de la longitud total. ¿El algoritmo cambia si el número tiene un número impar de dígitos? No. La lógica es idéntica: se empieza desde el dígito de control hacia la izquierda, duplicando los dígitos en posición par. La paridad total del número no afecta el resultado. ¿Cómo se genera un número de tarjeta válido de prueba? Se elige un prefijo según el emisor (p. ej. 4 para Visa), se rellena con dígitos aleatorios hasta N-1 posiciones y se calcula el último dígito con luhnGenerate. Los números de prueba oficiales como 4111 1111 1111 1111 se generaron exactamente así. ¿Existe algún checksum más potente para tarjetas? No se necesita. La validación criptográfica real ocurre en la capa de red de pago, no en el número de tarjeta en sí. Luhn existe solo para filtrar errores de escritura, y para ese propósito específico es perfectamente adecuado.
Prefijos de tarjetas más comunes
Emisor Prefijo Longitud Visa 4 16 dígitos Mastercard 51-55, 2221-2720 16 dígitos American Express 34, 37 15 dígitos Discover 6011, 622126-622925 16 dígitos Diners Club 300-305, 36, 38 14 dígitos Todos estos números, sin excepción, pasan la validación de Luhn.
{% include_relative luhn_interactive_validator.html %}
Enlaces relacionados
-
Ver en GitHub (interfaz):
https://github.com/enfaseterminal/cacharreria/blob/main/validador-numero-tarjeta-credito/luhn_interactive_validator.html -
Abrir raw (para guardar):
https://raw.githubusercontent.com/enfaseterminal/cacharreria/main/validador-numero-tarjeta-credito/luhn_interactive_validator.html
Ejemplos en Lenguajes de Programación
1. Python
Python es excelente para esto por su facilidad para manejar listas y comprensiones.
Python
def luhn_validator(card_number):
digits = [int(d) for d in str(card_number)]
# Tomamos todos menos el último para procesar, luego sumamos el último
checksum = digits[-1]
payload = digits[:-1][::-1]
for i, d in enumerate(payload):
if i % 2 == 0:
d *= 2
if d > 9: d -= 9
checksum += d
return checksum % 10 == 0
# Prueba
print(luhn_validator(79927398713)) # True
2. JavaScript
Ideal para validaciones en el frontend antes de enviar formularios.
JavaScript
function validateLuhn(number) {
let sum = 0;
let shouldDouble = false;
// Recorrer de derecha a izquierda
for (let i = number.length - 1; i >= 0; i--) {
let digit = parseInt(number.charAt(i));
if (shouldDouble) {
digit *= 2;
if (digit > 9) digit -= 9;
}
sum += digit;
shouldDouble = !shouldDouble;
}
return (sum % 10) === 0;
}
console.log(validateLuhn("79927398713")); // true
3. Java
Una versión robusta utilizando tipos de datos estándar.
Java
public class Luhn {
public static boolean check(String number) {
int sum = 0;
boolean alternate = false;
for (int i = number.length() - 1; i >= 0; i--) {
int n = Integer.parseInt(number.substring(i, i + 1));
if (alternate) {
n *= 2;
if (n > 9) n -= 9;
}
sum += n;
alternate = !alternate;
}
return (sum % 10 == 0);
}
}
4. C++
Ideal para sistemas de alto rendimiento o embebidos donde la eficiencia de memoria es clave.
C++
#include <iostream>
#include <string>
#include <algorithm>
bool validateLuhn(const std::string& cardNumber) {
int sum = 0;
bool isSecond = false;
// Recorremos desde el final hacia el principio
for (int i = cardNumber.length() - 1; i >= 0; i--) {
int d = cardNumber[i] - '0';
if (isSecond) {
d = d * 2;
if (d > 9) d -= 9;
}
sum += d;
isSecond = !isSecond;
}
return (sum % 10 == 0);
}
int main() {
std::string test = "79927398713";
std::cout << (validateLuhn(test) ? "Válido" : "Inválido") << std::endl;
return 0;
}
5. PHP
Muy utilizado en plataformas de e-commerce para validar datos de tarjetas antes de enviarlos a la pasarela de pago.
PHP
function validateLuhn($number) {
$sum = 0;
$shouldDouble = false;
// Invertir y recorrer
for ($i = strlen($number) - 1; $i >= 0; $i--) {
$digit = (int)$number[$i];
if ($shouldDouble) {
$digit *= 2;
if ($digit > 9) {
$digit -= 9;
}
}
$sum += $digit;
$shouldDouble = !$shouldDouble;
}
return ($sum % 10 === 0);
}
echo validateLuhn("79927398713") ? "Válido" : "Inválido";
6. Swift
La opción estándar para aplicaciones iOS. Útil para validar números de tarjeta en el Apple Wallet o apps financieras.
Swift
func isValidLuhn(_ number: String) -> Bool {
let reversedDigits = number.compactMap { Int(String($0)) }.reversed()
var sum = 0
for (index, digit) in reversedDigits.enumerated() {
if index % 2 == 1 {
let doubled = digit * 2
sum += doubled > 9 ? doubled - 9 : doubled
} else {
sum += digit
}
}
return sum % 10 == 0
}
print(isValidLuhn("79927398713")) // true
7. Rust
Para quienes buscan seguridad de memoria y velocidad extrema. Aquí usamos un enfoque funcional.
Rust
fn validate_luhn(number: &str) -> bool {
number.chars()
.rev()
.enumerate()
.map(|(i, c)| {
let mut digit = c.to_digit(10).unwrap() as i32;
if i % 2 == 1 {
digit *= 2;
if digit > 9 { digit -= 9; }
}
digit
})
.sum::<i32>() % 10 == 0
}
fn main() {
let card = "79927398713";
println!("Es válido: {}", validate_luhn(card));
}
8. Go (Golang)
Go es muy eficiente para procesar grandes volúmenes de datos. Aquí usamos un enfoque directo y tipado.
Go
package main
import "fmt"
func validateLuhn(number string) bool {
var sum int
shouldDouble := false
// Recorremos el string desde el final al principio
for i := len(number) - 1; i >= 0; i-- {
// Convertimos el byte de carácter ASCII a su valor entero
digit := int(number[i] - '0')
if shouldDouble {
digit *= 2
if digit > 9 {
digit -= 9
}
}
sum += digit
shouldDouble = !shouldDouble
}
return sum%10 == 0
}
func main() {
fmt.Println(validateLuhn("79927398713")) // true
}
9. C#
En C#, podemos usar LINQ para hacer el código extremadamente compacto y declarativo, lo que lo hace muy legible.
C#
using System;
using System.Linq;
public class LuhnValidator {
public static bool IsValid(string number) {
return number.Reverse()
.Select((c, i) => (int)char.GetNumericValue(c) * (i % 2 == 1 ? 2 : 1))
.Select(n => n > 9 ? n - 9 : n)
.Sum() % 10 == 0;
}
}
// Uso:
// Console.WriteLine(LuhnValidator.IsValid("79927398713"));
10. Ruby
Ruby brilla por su capacidad de escribir código que parece lenguaje natural. Es ideal para scripts de validación rápidos.
Ruby
def valid_luhn?(number)
digits = number.chars.map(&:to_i).reverse
sum = digits.each_with_index.map do |digit, index|
if index.odd?
val = digit * 2
val > 9 ? val - 9 : val
else
digit
end
end.sum
(sum % 10).zero?
end
puts valid_luhn?("79927398713") # true
11. C
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int luhn_check(const char *number) {
int digits[20], len = 0;
// Extraer solo dígitos
for (int i = 0; number[i] != '\0' && len < 20; i++) {
if (isdigit(number[i])) {
digits[len++] = number[i] - '0';
}
}
if (len < 2) return 0;
int total = 0;
int double_digit = 0;
// Recorrer de derecha a izquierda
for (int i = len - 1; i >= 0; i--) {
int d = digits[i];
if (double_digit) {
d *= 2;
if (d > 9) d -= 9;
}
total += d;
double_digit = !double_digit;
}
return total % 10 == 0;
}
int main(void) {
printf("%d\n", luhn_check("4532015112830366")); // 1 (válido) ✅
printf("%d\n", luhn_check("1234567890123456")); // 0 (inválido) ❌
return 0;
}
12. Kotlin
fun luhnCheck(number: String): Boolean {
val digits = number.filter { it.isDigit() }.map { it.digitToInt() }
if (digits.size < 2) return false
val total = digits
.reversed()
.mapIndexed { i, d ->
if (i % 2 == 1) {
val doubled = d * 2
if (doubled > 9) doubled - 9 else doubled
} else d
}
.sum()
return total % 10 == 0
}
fun luhnGenerate(partial: String): String {
val digits = partial.filter { it.isDigit() }.map { it.digitToInt() }
val total = digits
.reversed()
.mapIndexed { i, d ->
if (i % 2 == 0) {
val doubled = d * 2
if (doubled > 9) doubled - 9 else doubled
} else d
}
.sum()
val checkDigit = (10 - (total % 10)) % 10
return "$partial$checkDigit"
}
// Ejemplos
fun main() {
println(luhnCheck("4532015112830366")) // true ✅
println(luhnCheck("1234567890123456")) // false ❌
println(luhnGenerate("453201511283036")) // 4532015112830366
}
13. SQL (PostgreSQL)
CREATE OR REPLACE FUNCTION luhn_check(number TEXT)
RETURNS BOOLEAN AS $$
DECLARE
digits INT[];
total INT := 0;
d INT;
i INT;
len INT;
BEGIN
-- Extraer solo dígitos y convertir a array
SELECT ARRAY(
SELECT substring(number, s, 1)::INT
FROM generate_series(1, length(number)) AS s
WHERE substring(number, s, 1) ~ '[0-9]'
) INTO digits;
len := array_length(digits, 1);
IF len < 2 THEN RETURN FALSE; END IF;
FOR i IN 1..len LOOP
-- Iterar de derecha a izquierda
d := digits[len - i + 1];
IF i % 2 = 1 THEN
d := d * 2;
IF d > 9 THEN d := d - 9; END IF;
END IF;
total := total + d;
END LOOP;
RETURN total % 10 = 0;
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT;
-- Uso
SELECT luhn_check('4532015112830366'); -- true ✅
SELECT luhn_check('1234567890123456'); -- false ❌
Fórmulas Luhn para Excel y Google Sheets
Implementaciones completas del algoritmo de validación de números de tarjeta en hojas de cálculo, desde fórmulas universales modernas hasta compatibilidad con versiones antiguas.
Requisitos previos
Antes de copiar cualquier fórmula, elimina espacios y guiones del número de tarjeta. El número debe estar en una celda como texto puro: 4532015112830366, no 4532 0151 1283 0366.
Para limpiar la celda automáticamente, usa:
=SUSTITUIR(SUSTITUIR(A1," ",""),"-","")
(en versiones en inglés: SUBSTITUTE)
Fórmula universal — Excel 365 y Google Sheets modernos
Funciona con tarjetas de cualquier longitud (14, 15, 16 dígitos). Requiere la función LET y SECUENCIA / SEQUENCE.
Google Sheets
=LET(
raw, SUSTITUIR(SUSTITUIR(A2," ",""),"-",""),
len, LARGO(raw),
pos, SECUENCIA(1,len),
d, VALOR(EXTRAE(raw,pos,1)),
desde_d, len-pos+1,
d2, SI(RESIDUO(desde_d,2)=0, d*2, d),
adj, SI(d2>9, d2-9, d2),
SI(RESIDUO(SUMA(adj),10)=0,"✅ VÁLIDO","❌ INVÁLIDO")
)
Excel 365 (en inglés)
=LET(
raw, SUBSTITUTE(SUBSTITUTE(A2," ",""),"-",""),
len, LEN(raw),
pos, SEQUENCE(1,len),
d, VALUE(MID(raw,pos,1)),
from_right, len-pos+1,
d2, IF(MOD(from_right,2)=0, d*2, d),
adj, IF(d2>9, d2-9, d2),
IF(MOD(SUM(adj),10)=0,"✅ VALID","❌ INVALID")
)
Excel 365 (en español)
=LET(
raw, SUSTITUIR(SUSTITUIR(A2," ",""),"-",""),
len, LARGO(raw),
pos, SECUENCIA(1,len),
d, VALOR(EXTRAE(raw,pos,1)),
desde_d, len-pos+1,
d2, SI(RESIDUO(desde_d,2)=0, d*2, d),
adj, SI(d2>9, d2-9, d2),
SI(RESIDUO(SUMA(adj),10)=0,"✅ VÁLIDO","❌ INVÁLIDO")
)
LAMBDA reutilizable — definirla una vez, usarla en toda la hoja
Guarda la función con un nombre y úsala como cualquier otra fórmula nativa.
Cómo guardar una LAMBDA en Excel 365
- Ve a Fórmulas → Administrador de nombres → Nueva
- Nombre:
LUHN - Se refiere a:
=LAMBDA(num,
LET(
raw, SUBSTITUTE(SUBSTITUTE(num," ",""),"-",""),
len, LEN(raw),
pos, SEQUENCE(1,len),
d, VALUE(MID(raw,pos,1)),
fr, len-pos+1,
d2, IF(MOD(fr,2)=0,d*2,d),
adj, IF(d2>9,d2-9,d2),
IF(MOD(SUM(adj),10)=0,"✅ VÁLIDO","❌ INVÁLIDO")
)
)
Uso posterior: =LUHN(A2) — igual que cualquier función nativa.
Cómo guardar una LAMBDA en Google Sheets
- Ve a Datos → Funciones con nombre → Añadir función
- Nombre:
LUHN - Argumentos:
num - Definición:
=LET(
raw, SUSTITUIR(SUSTITUIR(num," ",""),"-",""),
len, LARGO(raw),
pos, SECUENCIA(1,len),
d, VALOR(EXTRAE(raw,pos,1)),
fr, len-pos+1,
d2, SI(RESIDUO(fr,2)=0,d*2,d),
adj, SI(d2>9,d2-9,d2),
SI(RESIDUO(SUMA(adj),10)=0,"✅ VÁLIDO","❌ INVÁLIDO")
)
Uso posterior: =LUHN(A2)
Validación en masa — rangos completos
Google Sheets: BYROW para columnas enteras
=BYROW(A2:A100, LAMBDA(celda,
SI(ESBLANCO(celda), "",
LET(
raw, SUSTITUIR(SUSTITUIR(celda," ",""),"-",""),
len, LARGO(raw),
pos, SECUENCIA(1,len),
d, VALOR(EXTRAE(raw,pos,1)),
fr, len-pos+1,
d2, SI(RESIDUO(fr,2)=0,d*2,d),
adj, SI(d2>9,d2-9,d2),
SI(RESIDUO(SUMA(adj),10)=0,"✅ VÁLIDO","❌ INVÁLIDO")
)
)
))
Esta fórmula va en una única celda (p. ej. B2) y rellena automáticamente toda la columna B con el resultado de cada número en A2:A100.
Excel 365: MAP para rangos
=MAP(A2:A100, LAMBDA(celda,
IF(ISBLANK(celda), "",
LET(
raw, SUBSTITUTE(SUBSTITUTE(celda," ",""),"-",""),
len, LEN(raw),
pos, SEQUENCE(1,len),
d, VALUE(MID(raw,pos,1)),
fr, len-pos+1,
d2, IF(MOD(fr,2)=0,d*2,d),
adj, IF(d2>9,d2-9,d2),
IF(MOD(SUM(adj),10)=0,"✅ VÁLIDO","❌ INVÁLIDO")
)
)
))
Fórmulas SUMPRODUCTO — sin LET (Excel 2016–2019)
Para versiones de Excel que no tienen LET ni SEQUENCE. Las fórmulas son específicas para cada longitud de tarjeta.
Tarjetas de 16 dígitos (Visa, Mastercard, Discover)
Pega el número sin espacios en A2.
=SI(
RESIDUO(
SUMAPRODUCTO(
SI(VALOR(EXTRAE(A2,{1,3,5,7,9,11,13,15},1))*2>9,
VALOR(EXTRAE(A2,{1,3,5,7,9,11,13,15},1))*2-9,
VALOR(EXTRAE(A2,{1,3,5,7,9,11,13,15},1))*2)
)
+SUMAPRODUCTO(VALOR(EXTRAE(A2,{2,4,6,8,10,12,14,16},1))),
10)=0,
"✅ VÁLIDO", "❌ INVÁLIDO"
)
En inglés:
=IF(
MOD(
SUMPRODUCT(
IF(VALUE(MID(A2,{1,3,5,7,9,11,13,15},1))*2>9,
VALUE(MID(A2,{1,3,5,7,9,11,13,15},1))*2-9,
VALUE(MID(A2,{1,3,5,7,9,11,13,15},1))*2)
)
+SUMPRODUCT(VALUE(MID(A2,{2,4,6,8,10,12,14,16},1))),
10)=0,
"✅ VALID", "❌ INVALID"
)
Tarjetas de 15 dígitos (American Express)
=SI(
RESIDUO(
SUMAPRODUCTO(
SI(VALOR(EXTRAE(A2,{2,4,6,8,10,12,14},1))*2>9,
VALOR(EXTRAE(A2,{2,4,6,8,10,12,14},1))*2-9,
VALOR(EXTRAE(A2,{2,4,6,8,10,12,14},1))*2)
)
+SUMAPRODUCTO(VALOR(EXTRAE(A2,{1,3,5,7,9,11,13,15},1))),
10)=0,
"✅ VÁLIDO", "❌ INVÁLIDO"
)
Tarjetas de 14 dígitos (Diners Club)
=SI(
RESIDUO(
SUMAPRODUCTO(
SI(VALOR(EXTRAE(A2,{2,4,6,8,10,12,14},1))*2>9,
VALOR(EXTRAE(A2,{2,4,6,8,10,12,14},1))*2-9,
VALOR(EXTRAE(A2,{2,4,6,8,10,12,14},1))*2)
)
+SUMAPRODUCTO(VALOR(EXTRAE(A2,{1,3,5,7,9,11,13},1))),
10)=0,
"✅ VÁLIDO", "❌ INVÁLIDO"
)
Fórmula con celdas auxiliares — versiones muy antiguas (Excel 2010–2013)
Para hojas que no admiten arrays literales {...}, la solución más robusta es usar una fila de columnas auxiliares ocultas.
Estructura de ejemplo para un número de 16 dígitos en A1:
| Celda | Fórmula | Descripción |
|---|---|---|
| B1 | =EXTRAE($A$1,1,1)*1 |
Dígito 1 |
| C1 | =EXTRAE($A$1,2,1)*1 |
Dígito 2 |
| … | … | … |
| Q1 | =EXTRAE($A$1,16,1)*1 |
Dígito 16 |
| B2 | =SI(B1*2>9,B1*2-9,B1*2) |
Dígito 1 duplicado |
| C2 | =C1 |
Dígito 2 sin cambio |
| D2 | =SI(D1*2>9,D1*2-9,D1*2) |
Dígito 3 duplicado |
| … | (alternar) | … |
| B3 | =RESIDUO(SUMA(B2:Q2)+Q1,10)=0 |
Resultado final |
Oculta las filas 1 y 2 una vez configuradas. Solo la celda B3 muestra el resultado.
Fórmula compacta solo para verificar el dígito de control
Si ya tienes los primeros N-1 dígitos y solo quieres calcular qué dígito de control añadir:
Google Sheets / Excel 365 — Generar dígito de control
=LET(
raw, SUSTITUIR(SUSTITUIR(A2," ",""),"-",""),
len, LARGO(raw),
pos, SECUENCIA(1,len),
d, VALOR(EXTRAE(raw,pos,1)),
fr, len-pos+2,
d2, SI(RESIDUO(fr,2)=0,d*2,d),
adj, SI(d2>9,d2-9,d2),
RESIDUO(10-RESIDUO(SUMA(adj),10),10)
)
Esta fórmula toma los primeros 15 dígitos de una tarjeta y devuelve el dígito de control que completaría un número válido.
Detección del emisor por prefijo
Combina la validación Luhn con la detección automática del tipo de tarjeta:
=SI(Y(LARGO(A2)=16, IZQUIERDA(A2,1)="4"), "Visa",
SI(Y(LARGO(A2)=16, O(VALOR(IZQUIERDA(A2,2))>=51, VALOR(IZQUIERDA(A2,2))<=55)), "Mastercard",
SI(Y(LARGO(A2)=15, O(IZQUIERDA(A2,2)="34", IZQUIERDA(A2,2)="37")), "American Express",
SI(Y(LARGO(A2)=16, IZQUIERDA(A2,4)="6011"), "Discover",
"Desconocido"
)
)
)
)
Tabla de compatibilidad
| Versión | LET + SEQUENCE | SUMPRODUCT array | BYROW/MAP | LAMBDA |
|---|---|---|---|---|
| Excel 2010–2013 | ❌ | ⚠️ limitado | ❌ | ❌ |
| Excel 2016–2019 | ❌ | ✅ | ❌ | ❌ |
| Excel 2021 | ✅ | ✅ | ❌ | ✅ |
| Microsoft 365 | ✅ | ✅ | ✅ | ✅ |
| Google Sheets | ✅ | ✅ | ✅ | ✅ |
Números de prueba
Estos números son seguros para probar tus fórmulas — pasan Luhn pero no son tarjetas reales:
| Tipo | Número | Resultado esperado |
|---|---|---|
| Visa 16 dígitos | 4532015112830366 |
✅ VÁLIDO |
| Mastercard 16 dígitos | 5425233430109903 |
✅ VÁLIDO |
| Amex 15 dígitos | 374251018720955 |
✅ VÁLIDO |
| Discover 16 dígitos | 6011000990139424 |
✅ VÁLIDO |
| Inválido genérico | 1234567890123456 |
❌ INVÁLIDO |
| Dígito cambiado | 4532015112830367 |
❌ INVÁLIDO |
Las fórmulas en español usan la localización española de Excel/Sheets (punto y coma como separador de argumentos en algunas configuraciones regionales). Si tu instalación usa coma como separador, sustitúyelos en consecuencia.
Para generar un número de tarjeta válido (o cualquier número que cumpla con el algoritmo de Luhn), la estrategia es siempre la misma:
- Generar todos los dígitos excepto el último (el dígito de control) de forma aleatoria.
- Calcular la suma de Luhn de esos dígitos según las reglas habituales.
- Calcular el dígito de control necesario para que la suma total sea múltiplo de 10.
Aquí tienes cómo implementarlo en tres lenguajes representativos:
1. Python (Enfoque funcional)
Python
import random
def generate_luhn_number(base_number_str):
# Calcular la suma de los dígitos dados según Luhn
digits = [int(d) for d in base_number_str]
# Invertimos para aplicar la regla de "cada segundo dígito"
payload = digits[::-1]
total = 0
for i, d in enumerate(payload):
# En el cálculo, los dígitos del payload ya están en posiciones impares (index 0, 2, 4...)
# si queremos que el nuevo número (índice 0) sea el "check digit",
# debemos tratar los índices pares como "dobles"
val = d * 2 if i % 2 == 0 else d
total += (val - 9 if val > 9 else val)
# El check digit es lo que falta para llegar al siguiente múltiplo de 10
check_digit = (10 - (total % 10)) % 10
return base_number_str + str(check_digit)
# Ejemplo: Generar un número de 15 dígitos al azar y calcular el 16vo
base = "".join([str(random.randint(0, 9)) for _ in range(15)])
print(f"Número válido: {generate_luhn_number(base)}")
2. JavaScript (Enfoque imperativo)
JavaScript
function generateLuhn(baseNumber) {
let sum = 0;
let shouldDouble = true; // Empezamos duplicando el primer dígito desde la derecha
// Recorremos de derecha a izquierda
for (let i = baseNumber.length - 1; i >= 0; i--) {
let digit = parseInt(baseNumber[i]);
if (shouldDouble) {
digit *= 2;
if (digit > 9) digit -= 9;
}
sum += digit;
shouldDouble = !shouldDouble;
}
let checkDigit = (10 - (sum % 10)) % 10;
return baseNumber + checkDigit;
}
// Ejemplo
let randomBase = Math.floor(Math.random() * 1000000000000000).toString();
console.log("Número válido:", generateLuhn(randomBase));
3. C# (Enfoque moderno)
C#
public static string GenerateLuhn(string baseNumber) {
int sum = 0;
bool doubleDigit = true;
for (int i = baseNumber.Length - 1; i >= 0; i--) {
int n = int.Parse(baseNumber[i].ToString());
if (doubleDigit) {
n *= 2;
if (n > 9) n -= 9;
}
sum += n;
doubleDigit = !doubleDigit;
}
int checkDigit = (10 - (sum % 10)) % 10;
return baseNumber + checkDigit;
}
La lógica matemática detrás del generador
La clave está en esta línea que verás repetida en todos los lenguajes: check_digit = (10 - (sum % 10)) % 10
-
sum % 10: Obtiene el residuo actual de la suma. -
10 - (sum % 10): Calcula cuánto falta para llegar a la siguiente decena. -
% 10: Se aplica para el caso especial en que la suma ya es múltiplo de 10 (donde el residuo es 0, y10 - 0nos daría 10, pero necesitamos un solo dígito, es decir,0).
Nota sobre Seguridad 🏴☠️
Generar números con el Algoritmo de Luhn es excelente para validar formularios o generar IDs internos. Sin embargo, recuerda:
Advertencia: El algoritmo de Luhn es un método de detección de errores, no es un método de cifrado ni seguridad. Generar números que pasen el test de Luhn no significa que sean números de tarjetas reales, pero nunca debes generar ni usar secuencias de números que imiten tarjetas reales en entornos de producción sin autorización, ya que podrías colisionar con cuentas reales existentes.