如何根据数字检测信用卡类型?
我试图弄清楚如何根据纯粹的数字来检测信用卡的类型。 有没有人知道一个确定的,可靠的方式来找到这个?
信用卡/借记卡号码被称为PAN或主帐号。 PAN的前六位数字取自属于发行银行的IIN或发卡行识别号码(IIN以前称为BIN - 银行识别号码 - 因此您可能会在某些文件中看到对该术语的参考)。 这六位数字必须符合国际标准ISO / IEC 7812,并且可以用来从号码中确定卡的类型。
不幸的是,实际的ISO / IEC 7812数据库没有公开可用,但是有非官方的名单,包括商业和免费的,包括维基百科。
无论如何,要从数字中检测出类型,可以使用如下所示的正则表达式:对原始表达式使用Credit
签证: ^4[0-9]{6,}$
Visa卡号码以4开头。
万事达卡: ^5[1-5][0-9]{5,}|222[1-9][0-9]{3,}|22[3-9][0-9]{4,}|2[3-6][0-9]{5,}|27[01][0-9]{4,}|2720[0-9]{3,}$
2016年之前,数字51到55, 但这只会检测万事达信用卡 ; 使用MasterCard系统发行的其他卡不属于此IIN范围。 2016年,他们将增加数字(222100-272099)。
美国运通: ^3[47][0-9]{5,}$
美国运通卡号码以34或37开头。
大来卡: ^3(?:0[0-5]|[68][0-9])[0-9]{4,}$
Diners Club卡号以300到305,36或38开头。大来卡以5开头,有16位数字。 这些是Diners Club和MasterCard之间的合资企业,应该像万事达卡一样处理。
发现: ^6(?:011|5[0-9]{2})[0-9]{3,}$
发现卡号以6011或65开头。
JCB: ^(?:2131|1800|35[0-9]{3})[0-9]{3,}$
JCB卡以2131,1800或35开头。
不幸的是,使用MasterCard系统处理的多种卡类型不在MasterCard的IIN范围内(数字从51 ... 55开始); 最重要的情况是Maestro卡,其中许多卡是从其他银行的IIN卡发行的,因此位于整个号码空间。 因此, 最好假设任何您接受的其他类型的卡都必须是万事达卡 。
重要提示 :卡号的长度有所不同, 例如,Visa过去曾发行过带有13位数字PAN和具有16位PAN的卡片。 Visa的文件目前表明它可能发出或可能已经发出12到19位数字。 因此,您不应该检查卡号的长度,除了验证它是否至少有7位数字 (对于完整的IIN加上一个校验位,应该与Luhn算法预测的值相匹配)。
另一个提示: 在处理持卡人PAN之前,从输入中去掉任何空格和标点符号 。 为什么? 因为在组中输入数字通常容易得多,类似于它们在实际信用卡的正面显示的方式,即,
4444 4444 4444 4444
比正确输入要容易得多
4444444444444444
对用户进行惩罚是没有任何好处的,因为他们已经输入了你不期望的字符。
这也意味着确保您的输入字段至少有24个字符的空间,否则输入空格的用户将耗尽空间。 我建议你让字段足够宽,以显示32个字符,并允许多达64; 这为扩展提供了充足的空间。
以下是一张让您更深入了解的图像:
更新(2014):校验和方法不再是验证卡片真实性的有效方式,正如对此答案的评论中所述。
更新(2016年):万事达卡将实施新的BIN范围启动Ach付款。
在javascript中:
function detectCardType(number) {
var re = {
electron: /^(4026|417500|4405|4508|4844|4913|4917)d+$/,
maestro: /^(5018|5020|5038|5612|5893|6304|6759|6761|6762|6763|0604|6390)d+$/,
dankort: /^(5019)d+$/,
interpayment: /^(636)d+$/,
unionpay: /^(62|88)d+$/,
visa: /^4[0-9]{12}(?:[0-9]{3})?$/,
mastercard: /^5[1-5][0-9]{14}$/,
amex: /^3[47][0-9]{13}$/,
diners: /^3(?:0[0-5]|[68][0-9])[0-9]{11}$/,
discover: /^6(?:011|5[0-9]{2})[0-9]{12}$/,
jcb: /^(?:2131|1800|35d{3})d{11}$/
}
for(var key in re) {
if(re[key].test(number)) {
return key
}
}
}
单元测试:
describe('CreditCard', function() {
describe('#detectCardType', function() {
var cards = {
'8800000000000000': 'UNIONPAY',
'4026000000000000': 'ELECTRON',
'4175000000000000': 'ELECTRON',
'4405000000000000': 'ELECTRON',
'4508000000000000': 'ELECTRON',
'4844000000000000': 'ELECTRON',
'4913000000000000': 'ELECTRON',
'4917000000000000': 'ELECTRON',
'5019000000000000': 'DANKORT',
'5018000000000000': 'MAESTRO',
'5020000000000000': 'MAESTRO',
'5038000000000000': 'MAESTRO',
'5612000000000000': 'MAESTRO',
'5893000000000000': 'MAESTRO',
'6304000000000000': 'MAESTRO',
'6759000000000000': 'MAESTRO',
'6761000000000000': 'MAESTRO',
'6762000000000000': 'MAESTRO',
'6763000000000000': 'MAESTRO',
'0604000000000000': 'MAESTRO',
'6390000000000000': 'MAESTRO',
'3528000000000000': 'JCB',
'3589000000000000': 'JCB',
'3529000000000000': 'JCB',
'6360000000000000': 'INTERPAYMENT',
'4916338506082832': 'VISA',
'4556015886206505': 'VISA',
'4539048040151731': 'VISA',
'4024007198964305': 'VISA',
'4716175187624512': 'VISA',
'5280934283171080': 'MASTERCARD',
'5456060454627409': 'MASTERCARD',
'5331113404316994': 'MASTERCARD',
'5259474113320034': 'MASTERCARD',
'5442179619690834': 'MASTERCARD',
'6011894492395579': 'DISCOVER',
'6011388644154687': 'DISCOVER',
'6011880085013612': 'DISCOVER',
'6011652795433988': 'DISCOVER',
'6011375973328347': 'DISCOVER',
'345936346788903': 'AMEX',
'377669501013152': 'AMEX',
'373083634595479': 'AMEX',
'370710819865268': 'AMEX',
'371095063560404': 'AMEX'
};
Object.keys(cards).forEach(function(number) {
it('should detect card ' + number + ' as ' + cards[number], function() {
Basket.detectCardType(number).should.equal(cards[number]);
});
});
});
});
更新日期:2016年6月15日 (目前为最终解决方案)
请注意,我甚至为顶级投票选举投票,但要明确这些是实际正常工作的正则表达式,我使用数千个真正的BIN代码对其进行了测试。 最重要的是使用起始字符串(^),否则它会在现实世界中给出错误的结果!
JCB ^(?:2131|1800|35)[0-9]{0,}$
起始于: 2131,1800,35(3528-3589)
美国运通 ^3[47][0-9]{0,}$
起始于: 34,37
Diners Club ^3(?:0[0-59]{1}|[689])[0-9]{0,}$
开始于: 300-305,309,36,38-39
Visa ^4[0-9]{0,}$
开始于: 4
MasterCard ^(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必须被编码为^(5[06789]|6)[0-9]{0,}$
。 Maestro卡必须在代码的末尾被检测到,因为其他一些卡的范围在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 type $pan Credit card number
* @param type $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
}
链接地址: http://www.djcxy.com/p/1905.html