Каков наилучший способ проверки кредитной карты в PHP?

#php #validation #e-commerce #numbers #credit-card

Вопрос:

Учитывая номер кредитной карты и отсутствие дополнительной информации, каков наилучший способ в PHP определить, является ли этот номер действительным или нет?

Прямо сейчас мне нужно что-то, что будет работать с American Express, Discover, MasterCard и Visa, но было бы полезно, если бы это также работало с другими типами.

Ответ №1:

Проверка номера карты состоит из трех частей:

  1. ШАБЛОН — соответствует ли он шаблону эмитентов (например, VISA/Mastercard/и т.д.)
  2. КОНТРОЛЬНАЯ СУММА — действительно ли это контрольная сумма (например, не просто 13 случайных чисел после «34», чтобы сделать ее номером карты AMEX)
  3. ДЕЙСТВИТЕЛЬНО СУЩЕСТВУЕТ — действительно ли у него есть связанная учетная запись (вы вряд ли получите ее без учетной записи продавца)

Шаблон

  • Префикс MASTERCARD=51-55, Длина=16 (контрольная сумма Mod10)
  • Префикс визы=4, Длина=13 или 16 (Mod10)
  • Префикс AMEX=34 или 37, Длина=15 (Mod10)
  • Префикс Diners Club/Carte=300-305, 36 или 38, Длина=14 (Mod10)
  • Откройте для себя Префикс=6011,622126-622925,644-649,65, Длина=16, (Mod10)
  • и т.д. (подробный список префиксов)

Контрольная сумма

Большинство карт используют алгоритм Луна для контрольных сумм:

Алгоритм Луна, описанный в Википедии

На ссылке Википедии есть ссылки на многие реализации, включая PHP:

 <?
/* Luhn algorithm number checker - (c) 2005-2008 shaman - www.planzero.org *
 * This code has been released into the public domain, however please      *
 * give credit to the original author where possible.                      */

function luhn_check($number) {

  // Strip any non-digits (useful for credit card numbers with spaces and hyphens)
  $number=preg_replace('/D/', '', $number);

  // Set the string length and parity
  $number_length=strlen($number);
  $parity=$number_length % 2;

  // Loop through each digit and do the maths
  $total=0;
  for ($i=0; $i<$number_length; $i  ) {
    $digit=$number[$i];
    // Multiply alternate digits by two
    if ($i % 2 == $parity) {
      $digit*=2;
      // If the sum is two digits, add them together (in effect)
      if ($digit > 9) {
        $digit-=9;
      }
    }
    // Total up the digits
    $total =$digit;
  }

  // If the total mod 10 equals 0, the number is valid
  return ($total % 10 == 0) ? TRUE : FALSE;

}
?>
 

Комментарии:

1. Под шаблоном вы могли бы добавить «Discover», префикс = «6» (возможно, «60»), длина=16

2. MasterCard имеет префикс 51-55, а не 51 или 55 в соответствии с beachnet.com/~hstiles/cardtype.html

3. Эта функция удаляет все не-цифры из строки, поэтому номер карты «ФРЕД» является действительным. Убедитесь, что вы подтвердили, что номер карты содержит только цифры, прежде чем вызывать эту функцию!

4. Номер кредитной карты, состоящий только из 0, также считается действительным

5. @BijuPDais, как упомянуто в тексте — чтобы проверить, действительно ли она существует, вам, скорее всего, придется быть продавцом и на самом деле попытаться выставить счет по карте. Многие действия (например, отели) взимают плату, а затем возвращают небольшую сумму на кредитную карту. Из всех методов проверки — это единственный ВЕРНЫЙ тест на то, действительна ли карта!

Ответ №2:

Из 10 регулярных выражений, без которых вы не можете жить в PHP:

 function check_cc($cc, $extra_check = false){
    $cards = array(
        "visa" => "(4d{12}(?:d{3})?)",
        "amex" => "(3[47]d{13})",
        "jcb" => "(35[2-8][89]ddd{10})",
        "maestro" => "((?:5020|5038|6304|6579|6761)d{12}(?:dd)?)",
        "solo" => "((?:6334|6767)d{12}(?:dd)?d?)",
        "mastercard" => "(5[1-5]d{14})",
        "switch" => "(?:(?:(?:4903|4905|4911|4936|6333|6759)d{12})|(?:(?:564182|633110)d{10})(dd)?d?)",
    );
    $names = array("Visa", "American Express", "JCB", "Maestro", "Solo", "Mastercard", "Switch");
    $matches = array();
    $pattern = "#^(?:".implode("|", $cards).")$#";
    $result = preg_match($pattern, str_replace(" ", "", $cc), $matches);
    if($extra_check amp;amp; $result > 0){
        $result = (validatecard($cc))?1:0;
    }
    return ($result>0)?$names[sizeof($matches)-2]:false;
}
 

Ввод образца:

 $cards = array(
    "4111 1111 1111 1111",
);

foreach($cards as $c){
    $check = check_cc($c, true);
    if($check!==false)
        echo $c." - ".$check;
    else
        echo "$c - Not a match";
    echo "<br/>";
}
 

Это дает нам

4111 1111 1111 1111 - Виза

Комментарии:

1. Где validatecard функция?

2. Этот ответ не сработает для новой идентификации mastercard 2-серии mastercard.us/content/mccom/en-us/issuers/get-support/…

Ответ №3:

Вероятно, лучше НЕ проверять код с вашей стороны. Отправьте информацию о карте прямо на свой платежный шлюз, а затем рассмотрите их ответ. Это поможет им обнаружить мошенничество, если вы сначала не сделаете ничего подобного проверке Луна-пусть они увидят неудачные попытки.

Комментарии:

1. Единственная проблема с этим заключается в том, что для каждой транзакции существует своя стоимость. Он может быть небольшим, но он суммируется, и если кто-то использует большое количество мошеннических номеров карт в вашей системе, сборы могут стать огромными.

Ответ №4:

PHP-код

 function validateCC($cc_num, $type) {

    if($type == "American") {
    $denum = "American Express";
    } elseif($type == "Dinners") {
    $denum = "Diner's Club";
    } elseif($type == "Discover") {
    $denum = "Discover";
    } elseif($type == "Master") {
    $denum = "Master Card";
    } elseif($type == "Visa") {
    $denum = "Visa";
    }

    if($type == "American") {
    $pattern = "/^([34|37]{2})([0-9]{13})$/";//American Express
    if (preg_match($pattern,$cc_num)) {
    $verified = true;
    } else {
    $verified = false;
    }


    } elseif($type == "Dinners") {
    $pattern = "/^([30|36|38]{2})([0-9]{12})$/";//Diner's Club
    if (preg_match($pattern,$cc_num)) {
    $verified = true;
    } else {
    $verified = false;
    }


    } elseif($type == "Discover") {
    $pattern = "/^([6011]{4})([0-9]{12})$/";//Discover Card
    if (preg_match($pattern,$cc_num)) {
    $verified = true;
    } else {
    $verified = false;
    }


    } elseif($type == "Master") {
    $pattern = "/^([51|52|53|54|55]{2})([0-9]{14})$/";//Mastercard
    if (preg_match($pattern,$cc_num)) {
    $verified = true;
    } else {
    $verified = false;
    }


    } elseif($type == "Visa") {
    $pattern = "/^([4]{1})([0-9]{12,15})$/";//Visa
    if (preg_match($pattern,$cc_num)) {
    $verified = true;
    } else {
    $verified = false;
    }

    }

    if($verified == false) {
    //Do something here in case the validation fails
    echo "Credit card invalid. Please make sure that you entered a valid <em>" . $denum . "</em> credit card ";

    } else { //if it will pass...do something
    echo "Your <em>" . $denum . "</em> credit card is valid";
    }


}
 

Использование

 echo validateCC("1738292928284637", "Dinners");
 

Более подробную теоретическую информацию можно найти здесь:

Проверка Кредитной карты — Контрольные цифры

Контрольная сумма

Комментарии:

1. Этот алгоритм может идентифицировать эмитента карты, грубо говоря, он не проверяет, имеет ли значение число.. например, он не проверяет контрольную сумму с помощью алгоритма Луна!

Ответ №5:

Алгоритм Луна-это контрольная сумма, которая может использоваться для проверки формата многих форматов кредитных карт (а также номеров социального страхования Канады…).

Статья в Википедии также ссылается на множество различных реализаций; вот PHP-версия:

http://planzero.org/code/bits/viewcode.php?src=luhn_check.phps

Ответ №6:

Существует пакет PEAR, который обрабатывает проверку многих финансовых номеров, а также проверку кредитной карты: http://pear.php.net/package/Validate_Finance_CreditCard

Кстати, вот несколько тестовых номеров счетов кредитных карт PayPal.

Ответ №7:

Просто добавляю некоторые дополнительные фрагменты кода, которые другие могут счесть полезными (не PHP-код).

PYTHON (однострочный код; вероятно, не настолько эффективен)

Для подтверждения:

 >>> not(sum(map(int, ''.join(str(n*(i%2 1)) for i, n in enumerate(map(int, reversed('1234567890123452')))))))
True
>>> not(sum(map(int, ''.join(str(n*(i%2 1)) for i, n in enumerate(map(int, reversed('1234567890123451')))))))
False
 

Чтобы вернуть требуемую контрольную цифру:

 >>> (10-sum(map(int, ''.join(str(n*(i%2 1)) for i, n in enumerate(map(int, reversed('123456789012345')), start=1)))))
2
>>> (10-sum(map(int, ''.join(str(n*(i%2 1)) for i, n in enumerate(map(int, reversed('234567890123451')), start=1)))))
1
 

Функции MySQL

Функции «ccc» и «ccd» (проверка кредитной карты и цифра кредитной карты)

Обратите внимание, что функция «ccc» имеет дополнительную проверку, при которой, если вычисленная сумма равна 0, возвращаемый результат всегда будет ЛОЖНЫМ, поэтому все нулевое число CC никогда не будет проверяться как правильное (при нормальном поведении оно будет проверяться правильно). Эту функцию можно добавлять/удалять по мере необходимости; возможно, это полезно, в зависимости от конкретных требований.

 DROP FUNCTION IF EXISTS ccc;
DROP FUNCTION IF EXISTS ccd;

DELIMITER //

CREATE FUNCTION ccc (n TINYTEXT) RETURNS BOOL
BEGIN
  DECLARE x TINYINT UNSIGNED;
  DECLARE l TINYINT UNSIGNED DEFAULT length(n);
  DECLARE i TINYINT UNSIGNED DEFAULT l;
  DECLARE s SMALLINT UNSIGNED DEFAULT 0;
  WHILE i > 0 DO
    SET x = mid(n,i,1);
    IF (l-i) mod 2 = 1 THEN
      SET x = x * 2;
    END IF;
    SET s = s   x div 10   x mod 10;
    SET i = i - 1;
  END WHILE;
  RETURN s != 0 amp;amp; s mod 10 = 0;
END;

CREATE FUNCTION ccd (n TINYTEXT) RETURNS TINYINT
BEGIN
  DECLARE x TINYINT UNSIGNED;
  DECLARE l TINYINT UNSIGNED DEFAULT length(n);
  DECLARE i TINYINT UNSIGNED DEFAULT l;
  DECLARE s SMALLINT UNSIGNED DEFAULT 0;
  WHILE i > 0 DO
    SET x = mid(n,i,1);
    IF (l-i) mod 2 = 0 THEN
      SET x = x * 2;
    END IF;
    SET s = s   x div 10   x mod 10;
    SET i = i - 1;
  END WHILE;
  RETURN ceil(s/10)*10-s;
END;
 

Затем функции можно использовать непосредственно в SQL-запросах:

 mysql> SELECT ccc(1234567890123452);
 ----------------------- 
| ccc(1234567890123452) |
 ----------------------- 
|                     1 |
 ----------------------- 
1 row in set (0.00 sec)

mysql> SELECT ccc(1234567890123451);
 ----------------------- 
| ccc(1234567890123451) |
 ----------------------- 
|                     0 |
 ----------------------- 
1 row in set (0.00 sec)

mysql> SELECT ccd(123456789012345);
 ---------------------- 
| ccd(123456789012345) |
 ---------------------- 
|                    2 |
 ---------------------- 
1 row in set (0.00 sec)

mysql> SELECT ccd(234567890123451);
 ---------------------- 
| ccd(234567890123451) |
 ---------------------- 
|                    1 |
 ---------------------- 
1 row in set (0.00 sec)
 

Ответ №8:

Это делается только для того, чтобы убедиться, что числа верны, используя некоторые базовые шаблоны регулярных выражений.

Обратите внимание, что это не проверяет, используются ли номера кем-либо.

http://www.roscripts.com/How_to_validate_credit_card_numbers-106.html