Каков наилучший способ проверки кредитной карты в PHP?
Учитывая номер кредитной карты и отсутствие дополнительной информации, каков наилучший способ в PHP определить, является ли это действительным номером или нет?
Прямо сейчас мне нужно что-то, что будет работать с American Express, Discover, MasterCard и Visa, но может быть полезно, если оно также будет работать с другими типами.
9 answers
Проверка номера карты состоит из трех частей:
- ШАБЛОН - соответствует ли он шаблону эмитентов (например, VISA/Mastercard/и т.д.)
- КОНТРОЛЬНАЯ СУММА - действительно ли это контрольная сумма (например, не просто 13 случайных чисел после "34", чтобы сделать ее номером карты AMEX)
- ДЕЙСТВИТЕЛЬНО СУЩЕСТВУЕТ - действительно ли у него есть связанная учетная запись (вы вряд ли получите ее без продавца учетная запись)
Шаблон
- Префикс 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;
}
?>
Из 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
Вероятно, лучше НЕ проверять код с вашей стороны. Отправьте информацию о карте прямо на свой платежный шлюз, а затем разберитесь с их ответом. Это помогает им обнаружить мошенничество, если вы сначала не сделаете ничего подобного проверке Луна - пусть они увидят неудачные попытки.
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);
}
Алгоритм лун - это контрольная сумма, которая может использоваться для проверки формата множества форматов кредитных карт (а также номеров канадского социального страхования...)
Статья в Википедии также содержит ссылки на множество различных реализаций; вот одна из них на PHP:
Http://planzero.org/code/bits/viewcode.php?src=luhn_check.phps
Существует пакет PEAR, который обрабатывает проверку многих финансовых показателей, а также проверку кредитной карты: http://pear.php.net/package/Validate_Finance_CreditCard
Кстати, вот некоторые Номера счетов тестовых кредитных карт через PayPal.
Просто добавляю некоторые дополнительные фрагменты кода, которые другие могут счесть полезными (не 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)
Это делается только для того, чтобы убедиться, что числа верны, используя некоторые базовые шаблоны регулярных выражений.
Обратите внимание, что это не проверяет, используются ли номера кем-либо.
Http://www.roscripts.com/How_to_validate_credit_card_numbers-106.html