Валидация кредитных карт: алгоритм Луна
Алгоритм Луна, проверка номеров карт, определение типа карты (Visa, MC, Amex), безопасность.
Введение
При вводе номера карты в интернет-магазине вы наверняка замечали: опечатка в одной цифре почти сразу подсвечивается красным. Как сайт понимает, что номер неверен, ещё до обращения в банк? Ответ — алгоритм Луна. Это простая математическая проверка, которая отсеивает большинство случайных ошибок ввода. В статье разберём, как устроен алгоритм, как по номеру определить тип карты и какие ограничения важно учитывать. Проверить карту можно в нашемвалидаторе кредитных карт.
Что такое алгоритм Луна
Алгоритм Луна разработан инженером IBM Хансом Питером Луном в 1954 году. Это контрольная сумма, которая обнаруживает любую одиночную ошибку в цифре номера, а также большинство перестановок соседних цифр (например, когда пользователь случайно набрал «32» вместо «23»).
Алгоритм не гарантирует, что карта реальна — он лишь проверяет, что номер мог быть выдан банком. Однако этого достаточно, чтобы отсеять опечатки ещё до запроса к платёжному шлюзу.
Как работает алгоритм
Шаги вычисления контрольной суммы по Луну:
- Отбросить пробелы и нецифровые символы.
- Начать с последней цифры и двигаться влево.
- Каждую вторую цифру (счёт с конца, начиная с предпоследней) умножить на 2.
- Если результат умножения больше 9, вычесть из него 9 (или сложить цифры результата).
- Сложить все полученные цифры.
- Если сумма делится на 10 без остатка — номер валиден.
Пример для номера 4539 1488 0343 6467
Номер: 4 5 3 9 1 4 8 8 0 3 4 3 6 4 6 7
Удвоить (x2): 8 6 2 8 0 8 8 8
↓1+8=9
Цифры: 8 5 6 9 2 4 7 8 0 3 8 3 3 4 3 7
Сумма = 8+5+6+9+2+4+7+8+0+3+8+3+3+4+3+7 = 80
80 mod 10 = 0 → номер валиденРеализация алгоритма
JavaScript
function luhnCheck(cardNumber) {
// Убираем всё, кроме цифр
const digits = String(cardNumber).replace(/\D/g, '');
if (digits.length < 13 || digits.length > 19) {
return false;
}
let sum = 0;
let shouldDouble = false;
// Идём справа налево
for (let i = digits.length - 1; i >= 0; i--) {
let digit = parseInt(digits[i], 10);
if (shouldDouble) {
digit *= 2;
if (digit > 9) digit -= 9;
}
sum += digit;
shouldDouble = !shouldDouble;
}
return sum % 10 === 0;
}
luhnCheck('4539148803436467'); // true
luhnCheck('4539148803436460'); // falsePython
def luhn_check(card_number: str) -> bool:
digits = [int(c) for c in str(card_number) if c.isdigit()]
if not 13 <= len(digits) <= 19:
return False
total = 0
should_double = False
for digit in reversed(digits):
if should_double:
digit *= 2
if digit > 9:
digit -= 9
total += digit
should_double = not should_double
return total % 10 == 0
print(luhn_check('4539148803436467')) # TrueGo
package main
import (
"fmt"
"unicode"
)
func luhnCheck(cardNumber string) bool {
var digits []int
for _, r := range cardNumber {
if unicode.IsDigit(r) {
digits = append(digits, int(r-'0'))
}
}
if len(digits) < 13 || len(digits) > 19 {
return false
}
sum := 0
shouldDouble := false
for i := len(digits) - 1; i >= 0; i-- {
d := digits[i]
if shouldDouble {
d *= 2
if d > 9 { d -= 9 }
}
sum += d
shouldDouble = !shouldDouble
}
return sum%10 == 0
}
func main() {
fmt.Println(luhnCheck("4539148803436467")) // true
}Определение типа карты по BIN
Первые 6–8 цифр номера карты — это BIN (Bank Identification Number), или IIN. По ним можно определить платёжную систему, банк-эмитент, тип карты (дебетовая или кредитная) и страну. Платёжная система определяется по первым цифрам:
| Платёжная система | Начальные цифры | Длина |
|---|---|---|
| Visa | 4 | 13, 16, 19 |
| Mastercard | 51–55, 2221–2720 | 16 |
| American Express | 34, 37 | 15 |
| Discover | 6011, 622126–622925, 644–649, 65 | 16, 19 |
| МИР | 2200–2204 | 16–19 |
| JCB | 3528–3589 | 16–19 |
| Diners Club | 300–305, 3095, 36, 38, 39 | 14–19 |
Пример определения платёжной системы
function detectCardType(number) {
const n = number.replace(/\D/g, '');
if (/^4/.test(n)) return 'visa';
if (/^(5[1-5]|2(2[2-9]|[3-6]\d|7[01]|720))/.test(n)) return 'mastercard';
if (/^3[47]/.test(n)) return 'amex';
if (/^(6011|65|64[4-9])/.test(n)) return 'discover';
if (/^220[0-4]/.test(n)) return 'mir';
if (/^3(?:0[0-5]|095)/.test(n)) return 'diners';
if (/^(?:352[89]|35[3-8]\d)/.test(n)) return 'jcb';
return 'unknown';
}Что алгоритм НЕ проверяет
Лун-проверка ловит случайные опечатки, но не гарантирует, что карта:
- Реально существует. Можно придумать число, удовлетворяющее формуле Луна, но не привязанное ни к какому счёту.
- Активна. Срок действия и статус карты можно проверить только через платёжный шлюз.
- Имеет средства. Баланс и лимиты — также за пределами алгоритма.
- Не украдена. Проверка по базам украденных карт — отдельная процедура.
Поэтому валидация по Луну — это лишь первая линия обороны. После неё следует проверка срока действия, CVV и, при необходимости, 3-D Secure.
Практическое применение
В веб-формах
Валидация по Луну подключается на этапе ввода. Когда пользователь набирает номер, клиентский JavaScript проверяет сумму и подсвечивает поле, если она не сходится. Это снижает количество ошибочных отправок формы.
В платёжных шлюзах
Серверная проверка также обязательна. Даже если клиентский скрипт пропустил ошибку, шлюз не отправит запрос в банк с невалидным номером — это экономит комиссию за попытку.
В системах учёта
Алгоритм Луна применяют не только для карт. Им нумеруют:
- IMEI телефонов.
- Национальные идентификаторы поставщиков (GS1).
- Номера социального страхования (в некоторых странах).
- Штрих-коды SimCards.
Ограничения
У алгоритма есть несколько ограничений:
- Он не ловит перестановку вида «09 ↔ 90» — обе эти пары дают одинаковый вклад в сумму.
- Он не защищает от подделки — формула публичная.
- Он не проверяет срок действия, имя владельца и CVV.
- Некоторые карты могут иметь нестандартную длину, что усложняет проверки.
Безопасность при работе с номерами карт
- Не храните PAN. Если бизнес-требования не требуют хранения полного номера — не храните его.
- Используйте токенизацию. Многие шлюзы возвращают токен вместо PAN — работайте с ним.
- Маскируйте номер. В логах и UI показывайте только последние 4 цифры:
**** **** **** 6467. - Шифруйте при передаче. Только HTTPS, никакой передачи PAN в URL.
- Соответствуйте PCI DSS. Если вы обрабатываете карты — сертификация обязательна.
- Не логируйте CVV. Это запрещено стандартом PCI DSS.
Регулярные выражения для валидации
Часто валидация номера карты объединяет проверку длины и платёжной системы в одно регулярное выражение:
const CARD_PATTERNS = {
visa: /^4[0-9]{12}(?:[0-9]{3})?(?:[0-9]{3})?$/,
mastercard: /^(5[1-5][0-9]{14}|2(2[2-9][0-9]{12}|[3-6][0-9]{13}|7[01][0-9]{12}|720[0-9]{12}))$/,
amex: /^3[47][0-9]{13}$/,
discover: /^6(?:011|5[0-9]{2})[0-9]{12}$/,
mir: /^220[0-4][0-9]{12,15}$/,
};
function validateCard(number, type) {
const clean = number.replace(/\D/g, '');
const pattern = CARD_PATTERNS[type];
return pattern ? pattern.test(clean) && luhnCheck(clean) : luhnCheck(clean);
}Заключение
Алгоритм Луна — простой, но эффективный способ отсеять опечатки в номере карты до обращения к платёжной системе. Он не заменяет полноценную проверку через шлюз, но существенно снижает количество ошибочных запросов и улучшает пользовательский опыт. Если вы разрабатываете форму оплаты или обрабатываете идентификаторы с контрольной суммой — Лун будет полезен. Проверить любой номер карты можно в нашем валидаторе кредитных карт.
Попробуйте эти инструменты
Похожие статьи
Как создать надёжный пароль: генератор паролей
Правила создания паролей, длина, символы, энтропия, менеджеры паролей, двухфакторная аутентификация.
MD5 хеш: что это и безопасно ли использовать
Алгоритм MD5, хеширование, коллизии, почему MD5 не для паролей, где ещё можно использовать.
SHA-256: безопасное хеширование
Алгоритм SHA-256, применение в блокчейне, SSL, цифровые подписи, сравнение с MD5 и SHA-1.
UUID: генерация уникальных идентификаторов
UUID версии 1-5, GUID, использование в базах данных, распределённых системах, генерация.