我想知道如何仅根据信用卡的号码来判断信用卡的类型。有谁知道确定可靠的方法吗?
当前回答
卡号的前六位数字(包括初始信息信息) 数字)被称为发行者识别号码(IIN)。这些 识别向该卡发行该卡的发卡机构 持有人。其余的数字由发卡机构分配。的 卡号的长度是它的位数。许多发卡机构打印 他们卡上的整个IIN和账号。
基于以上事实,我想保留一段JAVA代码来识别卡片品牌。
样卡类型
public static final String AMERICAN_EXPRESS = "American Express";
public static final String DISCOVER = "Discover";
public static final String JCB = "JCB";
public static final String DINERS_CLUB = "Diners Club";
public static final String VISA = "Visa";
public static final String MASTERCARD = "MasterCard";
public static final String UNKNOWN = "Unknown";
卡前缀
// Based on http://en.wikipedia.org/wiki/Bank_card_number#Issuer_identification_number_.28IIN.29
public static final String[] PREFIXES_AMERICAN_EXPRESS = {"34", "37"};
public static final String[] PREFIXES_DISCOVER = {"60", "62", "64", "65"};
public static final String[] PREFIXES_JCB = {"35"};
public static final String[] PREFIXES_DINERS_CLUB = {"300", "301", "302", "303", "304", "305", "309", "36", "38", "39"};
public static final String[] PREFIXES_VISA = {"4"};
public static final String[] PREFIXES_MASTERCARD = {
"2221", "2222", "2223", "2224", "2225", "2226", "2227", "2228", "2229",
"223", "224", "225", "226", "227", "228", "229",
"23", "24", "25", "26",
"270", "271", "2720",
"50", "51", "52", "53", "54", "55"
};
检查输入的数字是否有任何给定的前缀。
public String getBrand(String number) {
String evaluatedType;
if (StripeTextUtils.hasAnyPrefix(number, PREFIXES_AMERICAN_EXPRESS)) {
evaluatedType = AMERICAN_EXPRESS;
} else if (StripeTextUtils.hasAnyPrefix(number, PREFIXES_DISCOVER)) {
evaluatedType = DISCOVER;
} else if (StripeTextUtils.hasAnyPrefix(number, PREFIXES_JCB)) {
evaluatedType = JCB;
} else if (StripeTextUtils.hasAnyPrefix(number, PREFIXES_DINERS_CLUB)) {
evaluatedType = DINERS_CLUB;
} else if (StripeTextUtils.hasAnyPrefix(number, PREFIXES_VISA)) {
evaluatedType = VISA;
} else if (StripeTextUtils.hasAnyPrefix(number, PREFIXES_MASTERCARD)) {
evaluatedType = MASTERCARD;
} else {
evaluatedType = UNKNOWN;
}
return evaluatedType;
}
最后,效用方法
/**
* Check to see if the input number has any of the given prefixes.
*
* @param number the number to test
* @param prefixes the prefixes to test against
* @return {@code true} if number begins with any of the input prefixes
*/
public static boolean hasAnyPrefix(String number, String... prefixes) {
if (number == null) {
return false;
}
for (String prefix : prefixes) {
if (number.startsWith(prefix)) {
return true;
}
}
return false;
}
参考
条纹卡生成器
其他回答
这里有完整的c#或VB代码,用于各种CC相关的东西。
IsValidNumber GetCardTypeFromNumber GetCardTestNumber PassesLuhnTest
这篇文章已经发布了几年,没有任何负面评论。
看看这个:
http://www.breakingpar.com/bkp/home.nsf/0/87256B280015193F87256CC70060A01B
function isValidCreditCard(type, ccnum) {
/* Visa: length 16, prefix 4, dashes optional.
Mastercard: length 16, prefix 51-55, dashes optional.
Discover: length 16, prefix 6011, dashes optional.
American Express: length 15, prefix 34 or 37.
Diners: length 14, prefix 30, 36, or 38. */
var re = new Regex({
"visa": "/^4\d{3}-?\d{4}-?\d{4}-?\d",
"mc": "/^5[1-5]\d{2}-?\d{4}-?\d{4}-?\d{4}$/",
"disc": "/^6011-?\d{4}-?\d{4}-?\d{4}$/",
"amex": "/^3[47]\d{13}$/",
"diners": "/^3[068]\d{12}$/"
}[type.toLowerCase()])
if (!re.test(ccnum)) return false;
// Remove all dashes for the checksum checks to eliminate negative numbers
ccnum = ccnum.split("-").join("");
// Checksum ("Mod 10")
// Add even digits in even length strings or odd digits in odd length strings.
var checksum = 0;
for (var i = (2 - (ccnum.length % 2)); i <= ccnum.length; i += 2) {
checksum += parseInt(ccnum.charAt(i - 1));
}
// Analyze odd digits in even length strings or even digits in odd length strings.
for (var i = (ccnum.length % 2) + 1; i < ccnum.length; i += 2) {
var digit = parseInt(ccnum.charAt(i - 1)) * 2;
if (digit < 10) { checksum += digit; } else { checksum += (digit - 9); }
}
if ((checksum % 10) == 0) return true;
else return false;
}
这是一个php类函数,通过CCnumber返回CCtype。 此代码不验证卡或不运行Luhn算法仅尝试查找基于本页表的信用卡类型。基本上使用CCnumber长度和CCcard前缀来确定CCcard类型。
<?php
class CreditcardType
{
public static $creditcardTypes = [
[
'Name' => 'American Express',
'cardLength' => [15],
'cardPrefix' => ['34', '37'],
], [
'Name' => 'Maestro',
'cardLength' => [12, 13, 14, 15, 16, 17, 18, 19],
'cardPrefix' => ['5018', '5020', '5038', '6304', '6759', '6761', '6763'],
], [
'Name' => 'Mastercard',
'cardLength' => [16],
'cardPrefix' => ['51', '52', '53', '54', '55'],
], [
'Name' => 'Visa',
'cardLength' => [13, 16],
'cardPrefix' => ['4'],
], [
'Name' => 'JCB',
'cardLength' => [16],
'cardPrefix' => ['3528', '3529', '353', '354', '355', '356', '357', '358'],
], [
'Name' => 'Discover',
'cardLength' => [16],
'cardPrefix' => ['6011', '622126', '622127', '622128', '622129', '62213','62214', '62215', '62216', '62217', '62218', '62219','6222', '6223', '6224', '6225', '6226', '6227', '6228','62290', '62291', '622920', '622921', '622922', '622923','622924', '622925', '644', '645', '646', '647', '648','649', '65'],
], [
'Name' => 'Solo',
'cardLength' => [16, 18, 19],
'cardPrefix' => ['6334', '6767'],
], [
'Name' => 'Unionpay',
'cardLength' => [16, 17, 18, 19],
'cardPrefix' => ['622126', '622127', '622128', '622129', '62213', '62214','62215', '62216', '62217', '62218', '62219', '6222', '6223','6224', '6225', '6226', '6227', '6228', '62290', '62291','622920', '622921', '622922', '622923', '622924', '622925'],
], [
'Name' => 'Diners Club',
'cardLength' => [14],
'cardPrefix' => ['300', '301', '302', '303', '304', '305', '36'],
], [
'Name' => 'Diners Club US',
'cardLength' => [16],
'cardPrefix' => ['54', '55'],
], [
'Name' => 'Diners Club Carte Blanche',
'cardLength' => [14],
'cardPrefix' => ['300', '305'],
], [
'Name' => 'Laser',
'cardLength' => [16, 17, 18, 19],
'cardPrefix' => ['6304', '6706', '6771', '6709'],
],
];
public static function getType($CCNumber)
{
$CCNumber = trim($CCNumber);
$type = 'Unknown';
foreach (CreditcardType::$creditcardTypes as $card) {
if (! in_array(strlen($CCNumber), $card['cardLength'])) {
continue;
}
$prefixes = '/^(' . implode('|', $card['cardPrefix']) . ')/';
if (preg_match($prefixes, $CCNumber) == 1) {
$type = $card['Name'];
break;
}
}
return $type;
}
}
更新日期:2016年6月15日(目前为最终解决方案)
请注意,我甚至放弃投票给一个被投票最多的,但为了明确这些是regexp实际上是有效的,我用数千个真实的BIN代码测试了它。最重要的是使用开始字符串(^),否则在现实世界中会给出错误的结果!
JCB ^(?:2131|1800|35)[0-9]{0,}$开始:2131,1800,35 (3528-3589)
美国运通^3[47][0-9]{0,}$以:34,37开头
大莱卡^ 3(?:0(0-59){1}|[689])[0 - 9]{0}$开头:300 - 305年,309年,36岁,38-39
Visa ^4[0-9]{0,}$以:4开头
万事达卡^(5[1 - 5]| 222(1 - 9)| 22(3 - 9)| 2(3 - 6)| 27[01]| 2720)[0 - 9]{0}$开头:2221 - 2720,51-55
Maestro ^(5[06789]|6)[0-9]{0,}$ Maestro总是在范围内增长:60-69,以/开始,但开始5必须编码为万事达卡。大师卡必须在代码的末尾被检测到,因为其他一些卡的范围在60-69之间。请看代码。
发现^(6011|65|64[4-9]|62212[6-9]|6221[3-9]|622[2-8]|6229[01]|62292[0-5])[0-9]{0,}$发现很难编码,从:6011,622126 - 622925,644 -649,65开始
在javascript中,我使用这个函数。当你把它分配给一个onkeyup事件并尽快给出结果时,这是很好的。
function cc_brand_id(cur_val) {
// the regular expressions check for possible matches as you type, hence the OR operators based on the number of chars
// regexp string length {0} provided for soonest detection of beginning of the card numbers this way it could be used for BIN CODE detection also
//JCB
jcb_regex = new RegExp('^(?:2131|1800|35)[0-9]{0,}$'); //2131, 1800, 35 (3528-3589)
// American Express
amex_regex = new RegExp('^3[47][0-9]{0,}$'); //34, 37
// Diners Club
diners_regex = new RegExp('^3(?:0[0-59]{1}|[689])[0-9]{0,}$'); //300-305, 309, 36, 38-39
// Visa
visa_regex = new RegExp('^4[0-9]{0,}$'); //4
// MasterCard
mastercard_regex = new RegExp('^(5[1-5]|222[1-9]|22[3-9]|2[3-6]|27[01]|2720)[0-9]{0,}$'); //2221-2720, 51-55
maestro_regex = new RegExp('^(5[06789]|6)[0-9]{0,}$'); //always growing in the range: 60-69, started with / not something else, but starting 5 must be encoded as mastercard anyway
//Discover
discover_regex = new RegExp('^(6011|65|64[4-9]|62212[6-9]|6221[3-9]|622[2-8]|6229[01]|62292[0-5])[0-9]{0,}$');
////6011, 622126-622925, 644-649, 65
// get rid of anything but numbers
cur_val = cur_val.replace(/\D/g, '');
// checks per each, as their could be multiple hits
//fix: ordering matter in detection, otherwise can give false results in rare cases
var sel_brand = "unknown";
if (cur_val.match(jcb_regex)) {
sel_brand = "jcb";
} else if (cur_val.match(amex_regex)) {
sel_brand = "amex";
} else if (cur_val.match(diners_regex)) {
sel_brand = "diners_club";
} else if (cur_val.match(visa_regex)) {
sel_brand = "visa";
} else if (cur_val.match(mastercard_regex)) {
sel_brand = "mastercard";
} else if (cur_val.match(discover_regex)) {
sel_brand = "discover";
} else if (cur_val.match(maestro_regex)) {
if (cur_val[0] == '5') { //started 5 must be mastercard
sel_brand = "mastercard";
} else {
sel_brand = "maestro"; //maestro is all 60-69 which is not something else, thats why this condition in the end
}
}
return sel_brand;
}
在这里你可以玩它:
http://jsfiddle.net/upN3L/69/
对于PHP使用这个函数,它也会检测一些子VISA/MC卡:
/**
* Obtain a brand constant from a PAN
*
* @param string $pan Credit card number
* @param bool $include_sub_types Include detection of sub visa brands
* @return string
*/
public static function getCardBrand($pan, $include_sub_types = false)
{
//maximum length is not fixed now, there are growing number of CCs has more numbers in length, limiting can give false negatives atm
//these regexps accept not whole cc numbers too
//visa
$visa_regex = "/^4[0-9]{0,}$/";
$vpreca_regex = "/^428485[0-9]{0,}$/";
$postepay_regex = "/^(402360|402361|403035|417631|529948){0,}$/";
$cartasi_regex = "/^(432917|432930|453998)[0-9]{0,}$/";
$entropay_regex = "/^(406742|410162|431380|459061|533844|522093)[0-9]{0,}$/";
$o2money_regex = "/^(422793|475743)[0-9]{0,}$/";
// MasterCard
$mastercard_regex = "/^(5[1-5]|222[1-9]|22[3-9]|2[3-6]|27[01]|2720)[0-9]{0,}$/";
$maestro_regex = "/^(5[06789]|6)[0-9]{0,}$/";
$kukuruza_regex = "/^525477[0-9]{0,}$/";
$yunacard_regex = "/^541275[0-9]{0,}$/";
// American Express
$amex_regex = "/^3[47][0-9]{0,}$/";
// Diners Club
$diners_regex = "/^3(?:0[0-59]{1}|[689])[0-9]{0,}$/";
//Discover
$discover_regex = "/^(6011|65|64[4-9]|62212[6-9]|6221[3-9]|622[2-8]|6229[01]|62292[0-5])[0-9]{0,}$/";
//JCB
$jcb_regex = "/^(?:2131|1800|35)[0-9]{0,}$/";
//ordering matter in detection, otherwise can give false results in rare cases
if (preg_match($jcb_regex, $pan)) {
return "jcb";
}
if (preg_match($amex_regex, $pan)) {
return "amex";
}
if (preg_match($diners_regex, $pan)) {
return "diners_club";
}
//sub visa/mastercard cards
if ($include_sub_types) {
if (preg_match($vpreca_regex, $pan)) {
return "v-preca";
}
if (preg_match($postepay_regex, $pan)) {
return "postepay";
}
if (preg_match($cartasi_regex, $pan)) {
return "cartasi";
}
if (preg_match($entropay_regex, $pan)) {
return "entropay";
}
if (preg_match($o2money_regex, $pan)) {
return "o2money";
}
if (preg_match($kukuruza_regex, $pan)) {
return "kukuruza";
}
if (preg_match($yunacard_regex, $pan)) {
return "yunacard";
}
}
if (preg_match($visa_regex, $pan)) {
return "visa";
}
if (preg_match($mastercard_regex, $pan)) {
return "mastercard";
}
if (preg_match($discover_regex, $pan)) {
return "discover";
}
if (preg_match($maestro_regex, $pan)) {
if ($pan[0] == '5') { //started 5 must be mastercard
return "mastercard";
}
return "maestro"; //maestro is all 60-69 which is not something else, thats why this condition in the end
}
return "unknown"; //unknown for this system
}
不要试图检测信用卡类型作为处理支付的一部分。您正在冒着拒绝有效事务的风险。
如果您需要向您的支付处理器提供信息(例如,PayPal信用卡对象需要命名卡类型),那么从可用的最少信息中猜测它,例如。
$credit_card['pan'] = preg_replace('/[^0-9]/', '', $credit_card['pan']);
$inn = (int) mb_substr($credit_card['pan'], 0, 2);
// @see http://en.wikipedia.org/wiki/List_of_Bank_Identification_Numbers#Overview
if ($inn >= 40 && $inn <= 49) {
$type = 'visa';
} else if ($inn >= 51 && $inn <= 55) {
$type = 'mastercard';
} else if ($inn >= 60 && $inn <= 65) {
$type = 'discover';
} else if ($inn >= 34 && $inn <= 37) {
$type = 'amex';
} else {
throw new \UnexpectedValueException('Unsupported card type.');
}
这个实现(只使用前两位数字)足以识别所有主要的(在PayPal的情况下是所有受支持的)卡片方案。实际上,您可能希望完全跳过异常,并默认使用最流行的卡片类型。让支付网关/处理器告诉您在响应您的请求时是否存在验证错误。
现实情况是,你的支付网关并不关心你提供的价值。