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

StackOverflow https://stackoverflow.com/questions/174730

Вопрос

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

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

Это было полезно?

Решение

Процесс подтверждения номера карты состоит из трех частей:

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

Узор

  • Префикс MASTERCARD=51-55, Длина = 16 (контрольная сумма Mod10)
  • Префикс VISA=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;

}
?>

Другие советы

От 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

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

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

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

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

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

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

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

}

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

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

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

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

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

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

ПИТОН (однострочный код;вероятно, не настолько эффективно)

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

>>> 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, возвращаемый результат всегда будет FALSE, поэтому полностью нулевое число 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)

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top