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


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

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

Author: GEOCHET, 2008-10-06

9 answers

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

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

Шаблон

  • Префикс MASTERCARD=51-55, Длина =16 (контрольная сумма Mod10)
  • Префикс ВИЗЫ =4, Длина =13 или 16 (Mod10)
  • Префикс AMEX=34 или 37, Длина =15 (Mod10)
  • Префикс Diners Club/Меню=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;

}
?>
 139
Author: Ray Hayes, 2014-08-14 00:57:57

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

function check_cc($cc, $extra_check = false){
    $cards = array(
        "visa" => "(4\d{12}(?:\d{3})?)",
        "amex" => "(3[47]\d{13})",
        "jcb" => "(35[2-8][89]\d\d\d{10})",
        "maestro" => "((?:5020|5038|6304|6579|6761)\d{12}(?:\d\d)?)",
        "solo" => "((?:6334|6767)\d{12}(?:\d\d)?\d?)",
        "mastercard" => "(5[1-5]\d{14})",
        "switch" => "(?:(?:(?:4903|4905|4911|4936|6333|6759)\d{12})|(?:(?:564182|633110)\d{10})(\d\d)?\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 && $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 - Visa
 24
Author: ConroyP, 2015-02-07 08:25:58

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

 11
Author: PartialOrder, 2009-03-02 18:06:03

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");

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

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

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

 6
Author: Patrick Desjardins, 2008-10-06 15:30:21

Мы можем использовать следующее для подтверждения кредитной карты. Это прекрасно работает для меня.

protected function luhn($number)
{
    // Force the value to be a string as this method uses string functions.
    // Converting to an integer may pass PHP_INT_MAX and result in an error!
    $number = (string)$number;

    if (!ctype_digit($number)) {
        // Luhn can only be used on numbers!
        return FALSE;
    }

    // Check number length
    $length = strlen($number);

    // Checksum of the card number
    $checksum = 0;

    for ($i = $length - 1; $i >= 0; $i -= 2) {
        // Add up every 2nd digit, starting from the right
        $checksum += substr($number, $i, 1);
    }

    for ($i = $length - 2; $i >= 0; $i -= 2) {
        // Add up every 2nd digit doubled, starting from the right
        $double = substr($number, $i, 1) * 2;

        // Subtract 9 from the double where value is greater than 10
        $checksum += ($double >= 10) ? ($double - 9) : $double;
    }

    // If the checksum is a multiple of 10, the number is valid
    return ($checksum % 10 === 0);
}

protected function ValidCreditcard($number)
{
    $card_array = array(
        'default' => array(
            'length' => '13,14,15,16,17,18,19',
            'prefix' => '',
            'luhn' => TRUE,
        ),
        'american express' => array(
            'length' => '15',
            'prefix' => '3[47]',
            'luhn' => TRUE,
        ),
        'diners club' => array(
            'length' => '14,16',
            'prefix' => '36|55|30[0-5]',
            'luhn' => TRUE,
        ),
        'discover' => array(
            'length' => '16',
            'prefix' => '6(?:5|011)',
            'luhn' => TRUE,
        ),
        'jcb' => array(
            'length' => '15,16',
            'prefix' => '3|1800|2131',
            'luhn' => TRUE,
        ),
        'maestro' => array(
            'length' => '16,18',
            'prefix' => '50(?:20|38)|6(?:304|759)',
            'luhn' => TRUE,
        ),
        'mastercard' => array(
            'length' => '16',
            'prefix' => '5[1-5]',
            'luhn' => TRUE,
        ),
        'visa' => array(
            'length' => '13,16',
            'prefix' => '4',
            'luhn' => TRUE,
        ),
    );

    // Remove all non-digit characters from the number
    if (($number = preg_replace('/\D+/', '', $number)) === '')
        return FALSE;

    // Use the default type
    $type = 'default';

    $cards = $card_array;

    // Check card type
    $type = strtolower($type);

    if (!isset($cards[$type]))
        return FALSE;

    // Check card number length
    $length = strlen($number);

    // Validate the card length by the card type
    if (!in_array($length, preg_split('/\D+/', $cards[$type]['length'])))
        return FALSE;

    // Check card number prefix
    if (!preg_match('/^' . $cards[$type]['prefix'] . '/', $number))
        return FALSE;

    // No Luhn check required
    if ($cards[$type]['luhn'] == FALSE)
        return TRUE;

    return $this->luhn($number);

}
 5
Author: jeeva, 2016-06-01 18:25:21

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

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

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

 3
Author: Dana, 2008-10-06 15:27:50

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

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

 2
Author: powtac, 2008-10-07 11:56:40

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

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

Для проверки:

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

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

>>> (10-sum(map(int, ''.join(str(n*(i%2+1)) for i, n in enumerate(map(int, reversed('123456789012345')), start=1)))))%10
2
>>> (10-sum(map(int, ''.join(str(n*(i%2+1)) for i, n in enumerate(map(int, reversed('234567890123451')), start=1)))))%10
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 && 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)
 0
Author: parkamark, 2015-11-25 16:09:37

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

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

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

 -1
Author: Chad Moran, 2008-10-06 15:23:31