在 PHP 中验证信用卡的最佳方法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/174730/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the best way to validate a credit card in PHP?
提问by Joe Lencioni
Given a credit card number and no additional information, what is the best way in PHP to determine whether or not it is a valid number?
给定信用卡号并且没有其他信息,PHP 中确定它是否为有效数字的最佳方法是什么?
Right now I need something that will work with American Express, Discover, MasterCard, and Visa, but it might be helpful if it will also work with other types.
现在我需要一些可以与 American Express、Discover、MasterCard 和 Visa 一起使用的东西,但如果它也可以与其他类型一起使用可能会有所帮助。
回答by Ray Hayes
There are three parts to the validation of the card number:
卡号的验证分为三个部分:
- PATTERN- does it match an issuers pattern (e.g. VISA/Mastercard/etc.)
- CHECKSUM- does it actually check-sum (e.g. not just 13 random numbers after "34" to make it an AMEX card number)
- REALLY EXISTS- does it actually have an associated account (you are unlikely to get this without a merchant account)
- PATTERN- 是否匹配发行人模式(例如 VISA/Mastercard/等)
- CHECKSUM- 它实际上是校验和吗(例如,不仅仅是“34”之后的 13 个随机数使其成为 AMEX 卡号)
- 真的存在- 它是否真的有一个关联的帐户(如果没有商家帐户,您不太可能获得此帐户)
Pattern
图案
- MASTERCARD Prefix=51-55, Length=16 (Mod10 checksummed)
- VISA Prefix=4, Length=13 or 16 (Mod10)
- AMEX Prefix=34 or 37, Length=15 (Mod10)
- Diners Club/Carte Prefix=300-305, 36 or 38, Length=14 (Mod10)
- Discover Prefix=6011,622126-622925,644-649,65, Length=16, (Mod10)
- etc. (detailed list of prefixes)
- 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)
- 等(前缀的详细列表)
Checksum
校验和
Most cards use the Luhn algorithm for checksums:
大多数卡片使用 Luhn 算法进行校验和:
Luhn Algorithm described on Wikipedia
There are links to many implementations on the Wikipedia link, including PHP:
维基百科链接上有许多实现的链接,包括 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;
}
?>
回答by ConroyP
From 10 regular expressions you can't live without in 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;
}
Sample input:
样本输入:
$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/>";
}
This gives us
这给了我们
4111 1111 1111 1111 - Visa
回答by PartialOrder
It's probably better NOT to validate in code at your end. Send the card info right over to your payment gateway and then deal with their response. It helps them detect fraud if you don't do anything like Luhn checking first -- let them see the failed attempts.
最后最好不要在代码中进行验证。将卡信息直接发送到您的支付网关,然后处理他们的回复。如果您不先执行 Luhn 检查之类的操作,它会帮助他们检测欺诈行为——让他们看到失败的尝试。
回答by Patrick Desjardins
PHP Code
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";
}
}
Usage
用法
echo validateCC("1738292928284637", "Dinners");
More theoric information can be found here:
更多理论信息可以在这里找到:
回答by Dana
The luhn algorithmis a checksum that can used to validate the format of a lot of credit card formats (and also Canadian social insurance numbers...)
该LUHN算法是可以用于验证了很多信用卡格式的格式校验(以及加拿大的社会保险号码......)
The wikipedia article also links to many different implementations; here's a PHP one:
维基百科文章还链接到许多不同的实现;这是一个PHP:
http://planzero.org/code/bits/viewcode.php?src=luhn_check.phps
http://planzero.org/code/bits/viewcode.php?src=luhn_check.phps
回答by powtac
There is a PEAR package which handles the validation of many financial numbers, also credit card validation: http://pear.php.net/package/Validate_Finance_CreditCard
有一个 PEAR 包可以处理许多财务数字的验证,还有信用卡验证:http: //pear.php.net/package/Validate_Finance_CreditCard
By the way, here are some Test Credit Card Account Numbersby PayPal.
顺便说一下,这里有一些PayPal 的测试信用卡帐号。
回答by parkamark
Just throwing in some further code snippets that others may find useful (not PHP code).
只需添加一些其他人可能觉得有用的其他代码片段(不是 PHP 代码)。
PYTHON(single line code; probably not that efficient)
PYTHON(单行代码;可能效率不高)
To validate:
验证:
>>> 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
To return the required check digit:
要返回所需的校验位:
>>> (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 Functions
MySQL 函数
Functions "ccc" and "ccd" (credit-card-check and credit-card-digit)
函数“ccc”和“ccd”(信用卡检查和信用卡数字)
Note that the "ccc" function has an additional check where if the calculated sum is 0, the returned result will always be FALSE, so an all zero CC number will never validate as being correct (under normal behaviour, it would validate correctly). This feature can be added/removed as required; maybe useful, depending on specific requirements.
请注意,“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;
Functions can then be used directly in SQL queries:
然后可以直接在 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)
回答by Chad Moran
This is only to make sure that the numbers are valid using some basic RegEX patterns.
这只是为了确保使用一些基本的 RegEX 模式时数字是有效的。
Note, this does not check to see if the numbers are in-use by someone.
请注意,这不会检查号码是否被某人使用。
http://www.roscripts.com/How_to_validate_credit_card_numbers-106.html
http://www.roscripts.com/How_to_validate_credit_card_numbers-106.html

