Проверка номера кредитной карты Regex

Я тестирую одно приложение, в котором шаблон Regex соответствует кредитной карте, тогда такие цифры должны быть выделены. Я использую сайт http://regexpal.com/ для создания тестовых номеров кредитных карт для моего тестирования. мое требование состоит в том, чтобы иметь действительные номера кредитных карт, которые могут иметь между собой "-" и / или ",". Мне не удалось создать такое число, как при тестировании с использованием сайта

http://regexpal.com/.

Мне нужно несколько кредитных номеров со сценариями ниже

  1. действительный номер кредитной карты, который может иметь "-" между любыми цифрами.
  2. действительный номер кредитной карты, который может иметь "," между любой цифрой.
  3. действительный номер кредитной карты, который может иметь комбинацию "," или "-" между любыми цифрами.

16 ответов

Решение

Удалить все , а также - и другие не цифры из строки в первую очередь.

Затем используйте это регулярное выражение, которое соответствует картам Visa, MasterCard, American Express, Diners Club, Discover и JCB:

^(?:4[0-9]{12}(?:[0-9]{3})?|[25][1-7][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$

Обычные регулярные выражения поставщиков кредитных карт:

  • Amex Card: ^3[47][0-9]{13}$
  • BCGlobal: ^(6541|6556)[0-9]{12}$
  • Карта Карт Бланш: ^389[0-9]{11}$
  • Карта Diners Club: ^3(?:0[0-5]|[68][0-9])[0-9]{11}$
  • Откройте карту: ^65[4-9][0-9]{13}|64[4-9][0-9]{13}|6011[0-9]{12}|(622(?:12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|9[01][0-9]|92[0-5])[0-9]{10})$
  • Платежная карта Insta: ^63[7-9][0-9]{13}$
  • Карта JCB: ^(?:2131|1800|35\d{3})\d{11}$
  • KoreanLocalCard: ^9[0-9]{15}$
  • Лазерная карта: ^(6304|6706|6709|6771)[0-9]{12,15}$
  • Карта Маэстро: ^(5018|5020|5038|6304|6759|6761|6763)[0-9]{8,15}$
  • Mastercard: ^(5[1-5][0-9]{14}|2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12}))$
  • Сольная карта: ^(6334|6767)[0-9]{12}|(6334|6767)[0-9]{14}|(6334|6767)[0-9]{15}$
  • Переключатель карты: ^(4903|4905|4911|4936|6333|6759)[0-9]{12}|(4903|4905|4911|4936|6333|6759)[0-9]{14}|(4903|4905|4911|4936|6333|6759)[0-9]{15}|564182[0-9]{10}|564182[0-9]{12}|564182[0-9]{13}|633110[0-9]{10}|633110[0-9]{12}|633110[0-9]{13}$
  • Union Pay Card: ^(62[0-9]{14,17})$
  • Кредитные карты: ^4[0-9]{12}(?:[0-9]{3})?$
  • Visa Master Card: ^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})$

2019

ДЕЛАТЬ. НЕ. ИСПОЛЬЗОВАНИЕ. РЕГЕКС!!! (с 3 восклицательными знаками)


Из комментариев я должен выделить комментарий PeteWiFi:

Просто дружеское предупреждение, вас ждет мир боли, если вы попытаетесь подобрать конкретные схемы и длины карт таким образом. Например, Switch не существует с 2002 года, Laser был отозван в 2014 году, Visa должна выпустить 19-разрядные карты, а MasterCard сейчас выпускается в диапазонах 2xxxxx, просто чтобы выделить пару проблем с этим подходом. Регулярное выражение хорошо для базового "выглядит ли это как номер карты", но не намного дальше.

Если вы хотите использовать регулярные выражения просто для того, чтобы узнать марку карты для визуального использования (отображать логотип или этикетку Visa), это нормально. Но если от этого зависит логика вашего кода, не используйте регулярные выражения и не используйте сторонние плагины / библиотеки!

Regex обнаруживает номера карт быстро и легко (настолько привлекательным, я знаю!). Но, в конечном счете, ваш проект столкнется со многими серьезными и трудно решаемыми ошибками. Эмитенты карт продолжают вводить новые шаблоны номеров карт, либо отменяют старые, либо могут полностью закрыться. Кто знает.


Решение

Создайте свое собственное решение (желательно без регулярных выражений) на основе некоторых официальных страниц, которые часто обновляются, например, этой страницы в Википедии.

Что касается "-", ".", "Пробел" и всех других шумов, просто удалите все эти нецифровые символы, вы можете использовать это (на основе этого ответа):

$number = preg_replace("/[^0-9]/", "", "4111-1111 1111.1111");
// Output: 4111111111111111

Вот некоторые технические детали и много подводных камней.

Принятый ответ хорош, но для размещения нового MasterCard BIN я считаю, что его необходимо обновить до:

^(?:4[0-9]{12}(?:[0-9]{3})?|[25][1-7][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$

(критическая часть существа [25][1-7][0-9]{14}, поскольку первая цифра теперь может быть либо 2, либо 5, а вторая цифра может быть до 7)

Пожалуйста, поправьте меня, если я ошибаюсь!

Для дебетовой карты Rupay: ^6[0-9]{15}$

Вот мой метод обнаружения сети карт (обновлен в 2020 г.):

function getCardBrandId($pan)
    {
        $regs = [
            ELECTRON => "/^(4026|417500|4405|4508|4844|4913|4917)\d+$/",
            MAESTRO  => "/^(?:50|5[6-9]|6[0-9])\d+$/",
            DANKORT  => "/^(5019|4571)\d+$/",
            CUP      => "/^(62|81)\d+$/",
            VISA     => "/^4[0-9]\d+$/",
            DINERS   => "/^(?:5[45]|36|30[0-5]|3095|3[8-9])\d+$/",
            MC       => "/^(?:5[1-5]|222[1-9]|22[3-9][0-9]|2[3-6][0-9][0-9]|27[0-1][0-9]|2720)\d+$/",
            AMEX     => "/^(34|37)\d+$/",
            DISCOVER => "/^6(?:011|22(12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|9[01][0-9]|92[0-5])|5|4|2[4-6][0-9]{3}|28[2-8][0-9]{2})\d+$/",
            JCB      => "/^(?:35[2-8][0-9])\d+$/",
            INTERPAY => "/^(636)\d+$/",
            KOREAN   => "/^9[0-9]\d+$/",
            MIR      => "/^(?:220[0-4])\d+$/",
        ];


        foreach ($regs as $brand => $reg) {
            if (preg_match($reg, $pan)) {
                return $brand;
            }
        }

        return "Unknown";
    }

Regex для ведущих карточных сетей

Мастер-карта(2-лотковая, 5-лотковая):"(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}"

Виза: "^ 4 [0-9] {6,} $"

Diner's Club: "(^ 30 [0-5] [0-9] {11} $) | (^ (36 | 38) [0-9] {12} $)"

American Express: "^ [34 | 37] [0-9] {14} $"

JCB: "(^ 3 [0-9] {15} $) | (^ (2131 | 1800) [0-9] {11} $)"

Обнаружение: "^6011-?\ D {4}-?\ D {4}-?\ D {4} $"

В дополнение ко всему вышесказанному, вот регулярное выражение для новых MasterCards, которое включает в себя 2221-2720 БИН:

^5[1-5][0-9]{0,14}|^(222[1-9]|2[3-6]\\d{2}|27[0-1]\\d|2720)[0-9]{0,12}

Обратите внимание, это регулярное выражение будет соответствовать, если пользователь начнет вводить цифры карты, которые соответствуют MasterCard. Например, если пользователь введет "222185", регулярное выражение будет соответствовать, потому что нет другого типа карты, которая начинается с "2221". Это регулярное выражение может пригодиться, если вы хотите отобразить тип карты при вводе первых цифр карты.

Кроме того, если вы хотите, чтобы "постфактум" соответствовал, вы можете изменить последнюю часть с {0,14} а также {0,12} в {14} а также {12}:

^5[1-5][0-9]{14}|^(222[1-9]|2[3-6]\\d{2}|27[0-1]\\d|2720)[0-9]{12}

Для всех, кто пытается сделать это с помощью Swift (iOS), я создал небольшой проект, который не использует RegEx, который выполняет проверку префикса CC, проверку проверки цифр (с использованием алгоритма Луна) и несколько других интересных вещей. Его очень просто изменить, чтобы добавить новые типы карт и диапазоны номеров, не зная сложных RegEx. Это похоже на то, что делает @evilReiko в своем ответе.

100% бесплатно. Полностью открытый исходный код.

https://github.com/ethanwa/credit-card-scanner-and-validator

Я придумал регулярное выражение, которое учитывает тире и пробелы. Проверьте это здесь: https://regex101.com/r/Rx2iWD/1

Чтобы разрешить запятые (что я считаю необычным), просто добавьте их в sep определение.

В PHP:

$ccPatt = '/
    (?(DEFINE)
        (?<sep> [ -]?)
    )
    (?<!\d)(?:
      \d{4} (?&sep) \d{4} (?&sep) \d{4} (?&sep) \d{4}               # 16 digits
    | \d{3} (?&sep) \d{3} (?&sep) \d{3} (?&sep) \d (?&sep) \d{3}    # 13 digits
    | \d{4} (?&sep) \d{6} (?&sep) \d{4}                             # 14 digits
    | \d{4} (?&sep) \d{6} (?&sep) \d{5}                             # 15 digit card
    )(?!\d)
/xu';

First Data проверяет 15 цифр для Amex и 16 для visa, mc, Discover, diners и jcb, поэтому я отправляю им номер карты только в том случае, если номер длиной 15 или 16 цифр, с использованием этого:

^[0-9]{15}(?:[0-9]{1})?$

Регулярное выражение для всех типов карт

      ^(3[47][0-9]{13}|(6541|6556)[0-9]{12}|389[0-9]{11}|3(?:0[0-5]|[68][0-9])[0-9]{11}|65[4-9][0-9]{13}|64[4-9][0-9]{13}|6011[0-9]{12}|(622(?:12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|9[01][0-9]|92[0-5])[0-9]{10})|63[7-9][0-9]{13}|(?:2131|1800|35\d{3})\d{11}|9[0-9]{15}|(6304|6706|6709|6771)[0-9]{12,15}|(5018|5020|5038|6304|6759|6761|6763)[0-9]{8,15}|(5[1-5][0-9]{14}|2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12}))|(6334|6767)[0-9]{12}|(6334|6767)[0-9]{14}|(6334|6767)[0-9]{15}|(4903|4905|4911|4936|6333|6759)[0-9]{12}|(4903|4905|4911|4936|6333|6759)[0-9]{14}|(4903|4905|4911|4936|6333|6759)[0-9]{15}|564182[0-9]{10}|564182[0-9]{12}|564182[0-9]{13}|633110[0-9]{10}|633110[0-9]{12}|633110[0-9]{13}|(62[0-9]{14,17})|4[0-9]{12}(?:[0-9]{3})?|(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}))$

Проверить можно здесь https://regex101.com/r/37S1iV/1

Regx для карты Rupay:

(508 [5-9] [0-9] {12}) | (6069 [8-9] [0-9] {11}) | (607 [0-8] [0-9] {12}) | (6079 [0-8] [0-9] {11}) | (608 [0-5] [0-9] {12}) | (6521 [5-9] [0-9] {11}) | (652 [2-9] [0-9] {12}) | (6530 [0-9] {12}) | (6531 [0-4] [0-9] {11})

с использованием серии бинов: 508500 - 508999, 606985 - 606999, 607000 - 607899, 607900 - 607984, 608001 - 608500, 652150 --- 652199, 652200 --- 652999, 653000 --- 653099, 653100 --- 653149,

Для нескольких запросов в комментариях, запрашивающих решение для значений, разделенных пробелами, это может быть достигнуто довольно легко:

      \b(\d{4}\s\d{4}\s\d{4}\s\d{4}$)\b

Меня действительно раздражало, что люди используют регулярное выражение в 2021 году для этой задачи. Большинство номеров кредитных карт соответствуют алгоритму Луна, поэтому просто получите символы, относящиеся к банку и т. Д., А для фактического номера кредитной карты используйте это общее решение. Вы не можете просто грубо форсировать эту проблему. Я реорганизовал алгоритм Луна в Python 3 в современном стиле. Лучше использовать этот относительно быстрый цикл, также производительность, вероятно, будет лучше, чем у регулярного выражения.

          def check_luhn(cardNo: str) -> bool:
        res = 0
         
        for i, digit in enumerate(cardNo[::-1]):
            d = ord(digit) - ord('0')

            # multiply if it's a second digit
            d = d * 2 if i % 2 == 1 else d

            # if digit get's higher than 10 sum the resulting digits
            res += d // 10 + d % 10
         
        return res % 10 == 0

Вот этот ответ в формате кода JavaScript.

        let card_types = {
    "Amex Card": /^3[47][0-9]{13}$/,
    "BCGlobal": /^(6541|6556)[0-9]{12}$/,
    "Carte Blanche Card": /^389[0-9]{11}$/,
    "Diners Club Card": /^3(?:0[0-5]|[68][0-9])[0-9]{11}$/,
    "Discover Card": /^65[4-9][0-9]{13}|64[4-9][0-9]{13}|6011[0-9]{12}|(622(?:12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|9[01][0-9]|92[0-5])[0-9]{10})$/,
    "Insta Payment Card": /^63[7-9][0-9]{13}$/,
    "JCB Card": /^(?:2131|1800|35\d{3})\d{11}$/,
    "KoreanLocalCard": /^9[0-9]{15}$/,
    "Laser Card": /^(6304|6706|6709|6771)[0-9]{12,15}$/,
    "Maestro Card": /^(5018|5020|5038|6304|6759|6761|6763)[0-9]{8,15}$/,
    "Mastercard":/ ^(5[1-5][0-9]{14}|2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12}))$/,
    "Solo Card": /^(6334|6767)[0-9]{12}|(6334|6767)[0-9]{14}|(6334|6767)[0-9]{15}$/,
    "Switch Card": /^(4903|4905|4911|4936|6333|6759)[0-9]{12}|(4903|4905|4911|4936|6333|6759)[0-9]{14}|(4903|4905|4911|4936|6333|6759)[0-9]{15}|564182[0-9]{10}|564182[0-9]{12}|564182[0-9]{13}|633110[0-9]{10}|633110[0-9]{12}|633110[0-9]{13}$/,
    "Union Pay Card": /^(62[0-9]{14,17})$/,
    "Visa Card": /^4[0-9]{12}(?:[0-9]{3})?$/,
    "Visa Master Card": /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})$/,
  };
Другие вопросы по тегам