クレジットカードの種類を純粋にその番号に基づいて検出する方法を見つけようとしています。誰もがこれを見つけるための決定的で信頼できる方法を知っていますか?
クレジット/デビットカード番号は、PAN、またはプライマリアカウント番号と呼ばれます。 PANの最初の6桁は、発行銀行に属するIIN、または発行者識別番号から取得されます(以前はBINと呼ばれていました) —銀行識別番号—そのため、一部の文書でその用語への参照が表示される場合があります。これらの6桁は国際標準 ISO/IEC 7812 の対象であり、番号からカードのタイプを判別するために使用できます。
残念ながら、実際のISO/IEC 7812データベースは公開されていませんが、 Wikipedia を含む商用および無料の非公式リストがあります。
とにかく、数値からタイプを検出するには、次のような正規表現を使用できます。 元の表現のクレジット
Visa:^4[0-9]{6,}$
Visaカード番号は4で始まります。
MasterCard:^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年以前は、MasterCard番号は51から55までの数字で始まり、しかし、これはMasterCardクレジットカードのみを検出します; MasterCardシステムを使用して発行された、このIIN範囲に該当しない他のカードがあります。 2016年には、範囲(222100-272099)の数値を追加します。
American Express:^3[47][0-9]{5,}$
American Expressカード番号は34または37で始まります。
Diners Club:^3(?:0[0-5]|[68][0-9])[0-9]{4,}$
Diners Clubカード番号は300から305、36、または38で始まります。5で始まり16桁のDiners Clubカードがあります。これらはダイナースクラブとマスターカードの合弁会社であり、マスターカードのように処理する必要があります。
Discover:^6(?:011|5[0-9]{2})[0-9]{3,}$
Discoverカード番号は6011または65で始まります。
JCB:^(?:2131|1800|35[0-9]{3})[0-9]{3,}$
JCBカードは2131、1800、または35で始まります。
残念ながら、MasterCardのIIN範囲に含まれないMasterCardシステムで処理されるカードの種類がいくつかあります(51から55で始まる番号)。最も重要なケースは、マエストロカードの場合です。マエストロカードの多くは、他の銀行のIIN範囲から発行されているため、番号スペース全体に配置されています。その結果、受け入れる他のタイプではないカードはすべてMasterCardであると仮定するのが最善かもしれません。
重要:カード番号の長さは異なります。たとえば、過去に発行されたVisaには、13桁のPANを持つカードと16桁のPANを持つカードがありました。 Visaのドキュメントでは現在、12から19桁の数字を発行するか発行した可能性があることが示されています。 したがって、カード番号の長さは、少なくとも7桁あることを確認する以外はチェックしないでください(完全なIINと1つのチェックディジットの場合、予測される値と一致する必要があります Luhnアルゴリズム )。
もう1つのヒント:カード所有者PANを処理する前に、入力から空白文字と句読点文字を削除します。どうして?通常、はるかにであるため、実際のクレジットカードの前面に表示されるのと同様に、数字をグループで入力するのが簡単です。
4444 4444 4444 4444
よりも正確に入力する方がはるかに簡単です
4444444444444444
ユーザーがここで予期しない文字を入力したため、ユーザーを懲らしめることには実際には何のメリットもありません。
これは、入力フィールドに少なくとも24文字のスペースがあることを意味します。そうしないと、スペースを入力するユーザーはスペースを使い果たします。フィールドを広くすることをお勧めします32文字を表示し、最大64文字を許可するのに十分です。拡張のための十分な余裕があります。
ここにもう少し洞察を与えるイメージがあります:
PDATE(2014):チェックサムメソッドカードの真正性を検証する有効な方法ではないようですこの回答のコメントに記載されています。
PDATE(2016): Mastercardは Ach Payment で始まる新しいBIN範囲を実装します。
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|35\d{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)
American Express^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
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から始まるマスターカードとしてコード化する必要があります。Maestroカードはコードの最後で検出する必要があります。他のものは60-69の範囲にあります。コードを見てください。
Discover^(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私はこの関数を使います。これをukeyupイベントに割り当て、できるだけ早く結果を返すときにこれは良いことです。
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;
}
ここであなたはそれで遊ぶことができます:
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
}
public string GetCreditCardType(string CreditCardNumber)
{
Regex regVisa = new Regex("^4[0-9]{12}(?:[0-9]{3})?$");
Regex regMaster = new Regex("^5[1-5][0-9]{14}$");
Regex regExpress = new Regex("^3[47][0-9]{13}$");
Regex regDiners = new Regex("^3(?:0[0-5]|[68][0-9])[0-9]{11}$");
Regex regDiscover = new Regex("^6(?:011|5[0-9]{2})[0-9]{12}$");
Regex regJCB= new Regex("^(?:2131|1800|35\\d{3})\\d{11}$");
if(regVisa.IsMatch(CreditCardNumber))
return "VISA";
else if (regMaster.IsMatch(CreditCardNumber))
return "MASTER";
else if (regExpress.IsMatch(CreditCardNumber))
return "AEXPRESS";
else if (regDiners.IsMatch(CreditCardNumber))
return "DINERS";
else if (regDiscover.IsMatch(CreditCardNumber))
return "DISCOVERS";
else if (regJCB.IsMatch(CreditCardNumber))
return "JCB";
else
return "invalid";
}
これは、正規表現を使ってクレジットカードの種類をチェックする関数です。
これをチェックしてください。
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;
}
これは CC関連のあらゆる種類のC#またはVBコードを完成させる codeproject上。
この記事は、否定的なコメントなしで数年間続いています。
最近そのような機能が必要になったので、Zend Framework Credit Card ValidatorをRubyに移植しました。 Ruby gem: https://github.com/Fivell/credit_card_validations zendフレームワーク: https://github.com/zendframework/zf2/blob/master/library/Zend/Validator/CreditCard.php
どちらもタイプの検出にINN範囲を使用します。 INNについて
これによると、あなたは代わりにクレジットカードを検出することができます(正規表現なしで、しかし接頭辞と可能な長さに関するいくつかの規則を宣言します)
だから私たちは最も使用されるカードのための次のルールがあります
######## most used brands #########
visa: [
{length: [13, 16], prefixes: ['4']}
],
mastercard: [
{length: [16], prefixes: ['51', '52', '53', '54', '55']}
],
amex: [
{length: [15], prefixes: ['34', '37']}
],
######## other brands ########
diners: [
{length: [14], prefixes: ['300', '301', '302', '303', '304', '305', '36', '38']},
],
#There are Diners Club (North America) cards that begin with 5. These are a joint venture between Diners Club and MasterCard, and are processed like a MasterCard
# will be removed in next major version
diners_us: [
{length: [16], prefixes: ['54', '55']}
],
discover: [
{length: [16], prefixes: ['6011', '644', '645', '646', '647', '648',
'649', '65']}
],
jcb: [
{length: [16], prefixes: ['3528', '3529', '353', '354', '355', '356', '357', '358', '1800', '2131']}
],
laser: [
{length: [16, 17, 18, 19], prefixes: ['6304', '6706', '6771']}
],
solo: [
{length: [16, 18, 19], prefixes: ['6334', '6767']}
],
switch: [
{length: [16, 18, 19], prefixes: ['633110', '633312', '633304', '633303', '633301', '633300']}
],
maestro: [
{length: [12, 13, 14, 15, 16, 17, 18, 19], prefixes: ['5010', '5011', '5012', '5013', '5014', '5015', '5016', '5017', '5018',
'502', '503', '504', '505', '506', '507', '508',
'6012', '6013', '6014', '6015', '6016', '6017', '6018', '6019',
'602', '603', '604', '605', '6060',
'677', '675', '674', '673', '672', '671', '670',
'6760', '6761', '6762', '6763', '6764', '6765', '6766', '6768', '6769']}
],
# Luhn validation are skipped for union pay cards because they have unknown generation algoritm
unionpay: [
{length: [16, 17, 18, 19], prefixes: ['622', '624', '625', '626', '628'], skip_luhn: true}
],
dankrot: [
{length: [16], prefixes: ['5019']}
],
rupay: [
{length: [16], prefixes: ['6061', '6062', '6063', '6064', '6065', '6066', '6067', '6068', '6069', '607', '608'], skip_luhn: true}
]
}
次に、プレフィックスを検索して長さを比較することで、クレジットカードのブランドを検出できます。また、luhn algoritmも忘れないでください(ここで説明されています http://en.wikipedia.org/wiki/Luhn )。
アナトリーの答えはPHPで:
public static function detectCardType($num)
{
$re = array(
"visa" => "/^4[0-9]{12}(?:[0-9]{3})?$/",
"mastercard" => "/^5[1-5][0-9]{14}$/",
"amex" => "/^3[47][0-9]{13}$/",
"discover" => "/^6(?:011|5[0-9]{2})[0-9]{12}$/",
);
if (preg_match($re['visa'],$num))
{
return 'visa';
}
else if (preg_match($re['mastercard'],$num))
{
return 'mastercard';
}
else if (preg_match($re['amex'],$num))
{
return 'amex';
}
else if (preg_match($re['discover'],$num))
{
return 'discover';
}
else
{
return false;
}
}
コンパクトジャバスクリプト版
var getCardType = function (number) {
var cards = {
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|35\d{3})\d{11}$/
};
for (var card in cards) {
if (cards[card].test(number)) {
return card;
}
}
};
これはphpクラスの関数で、CCnumberごとにCCtypeを返します。
このコードはカードを検証しないか、またはLuhnアルゴリズムを実行しないで、 このページ の表に基づいてクレジットカードタイプを見つけようとするだけです。基本的にCCnumberの長さとCCカードのプレフィックスを使ってCCカードの種類を決定します。
<?php class CreditcardType
{
public static $creditcardTypes = array(
array('Name'=>'American Express','cardLength'=>array(15),'cardPrefix'=>array('34', '37'))
,array('Name'=>'Maestro','cardLength'=>array(12, 13, 14, 15, 16, 17, 18, 19),'cardPrefix'=>array('5018', '5020', '5038', '6304', '6759', '6761', '6763'))
,array('Name'=>'Mastercard','cardLength'=>array(16),'cardPrefix'=>array('51', '52', '53', '54', '55'))
,array('Name'=>'Visa','cardLength'=>array(13,16),'cardPrefix'=>array('4'))
,array('Name'=>'JCB','cardLength'=>array(16),'cardPrefix'=>array('3528', '3529', '353', '354', '355', '356', '357', '358'))
,array('Name'=>'Discover','cardLength'=>array(16),'cardPrefix'=>array('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'))
,array('Name'=>'Solo','cardLength'=>array(16, 18, 19),'cardPrefix'=>array('6334', '6767'))
,array('Name'=>'Unionpay','cardLength'=>array(16, 17, 18, 19),'cardPrefix'=>array('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'))
,array('Name'=>'Diners Club','cardLength'=>array(14),'cardPrefix'=>array('300', '301', '302', '303', '304', '305', '36'))
,array('Name'=>'Diners Club US','cardLength'=>array(16),'cardPrefix'=>array('54', '55'))
,array('Name'=>'Diners Club Carte Blanche','cardLength'=>array(14),'cardPrefix'=>array('300','305'))
,array('Name'=>'Laser','cardLength'=>array(16, 17, 18, 19),'cardPrefix'=>array('6304', '6706', '6771', '6709'))
);
private function __construct() {}
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;
}
} ?>
クレジットカードの最初の番号は、仕入先の概算に使用できます。
Card Range Recognition(CRR)では、一連の正規表現またはその他のハードコードされた範囲を使用するアルゴリズムの欠点は、BIN/IINが私の経験では時間の経過とともに変化することです。カードの共同ブランド化は進行中の問題です。カードの取得者や加盟店が異なると、同じカードを異なる方法で扱う必要が生じる場合があります。ジオロケーション。
また、ここ数年では、 UnionPayカードはより広く流通していますが、既存のモデルは新しいシリーズには対応していません。
特定の国では使用できる範囲が限られているため、システムがカバーする必要がある地理を知っておくと役立ちます。たとえば、範囲62には米国内のいくつかのAAAサブ範囲が含まれますが、マーチャントベースが米国外にある場合は、すべて62をUnionPayとして扱うことができます。
あなたはまた、商人の居場所に基づいてカードの扱いを変えるように求められるかもしれません。例えば。特定の英国のカードを国内では借方として扱うが、国際的にはクレジットとして扱う。
ある大手買収銀行が管理している非常に有用な一連の規則があります。例えば。 https://www.barclaycard.co.uk/business/files/BIN-Rules-EIRE.pdf および https://www.barclaycard.co.uk/business/files/BIN-Rules- UK.pdf 。ただし、これらのCRRルールは、そのエンティティが取得した加盟店に適用されるカード発行ユニバースを表す可能性があるため、注意が必要です。それは含まれていません。範囲はCUP/UPIとして識別されます。
これらのコメントは、磁気ストライプ(MagStripe)またはPKE(Pan Key Entry)シナリオに適用されます。 ICC/EMVの世界では状況はまた異なります。
更新:このページ(およびリンクされたWikiPediaページ)の他の回答には、常に16の長さがあります。しかし、私の会社には、複数の買収銀行や地域にまたがって当社のPOSデバイスとソフトウェアを認証するエンジニアの専門チームがいます。このチームがJCBから持っている最新のCertification Packのカードには、19の長いPANのパスケースがありました。
支払い処理の一部としてクレジットカードの種類を検出しようとしないでください。あなたは有効な取引を辞退する危険があります。
あなたがあなたの支払い処理者に情報を提供する必要があるなら(例えば、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.');
}
この実装(最初の2桁のみを使用)は、主要な(そしてPaypalの場合はサポートされている)すべてのカードスキームを識別するのに十分です。実際、例外を完全にスキップしてデフォルトの最も人気のあるカードタイプにすることをお勧めします。あなたの要求に応えて検証エラーがあるかどうかをペイメントゲートウェイ/プロセッサに知らせてください。
現実には、あなたの支払いゲートウェイ はあなたが提供する値を気にしないということです 。
Usman Yの答えのSwift 2.1バージョン。 print文を使用して、文字列値による呼び出しを確認します。
print(self.validateCardType(self.creditCardField.text!))
func validateCardType(testCard: String) -> String {
let regVisa = "^4[0-9]{12}(?:[0-9]{3})?$"
let regMaster = "^5[1-5][0-9]{14}$"
let regExpress = "^3[47][0-9]{13}$"
let regDiners = "^3(?:0[0-5]|[68][0-9])[0-9]{11}$"
let regDiscover = "^6(?:011|5[0-9]{2})[0-9]{12}$"
let regJCB = "^(?:2131|1800|35\\d{3})\\d{11}$"
let regVisaTest = NSPredicate(format: "SELF MATCHES %@", regVisa)
let regMasterTest = NSPredicate(format: "SELF MATCHES %@", regMaster)
let regExpressTest = NSPredicate(format: "SELF MATCHES %@", regExpress)
let regDinersTest = NSPredicate(format: "SELF MATCHES %@", regDiners)
let regDiscoverTest = NSPredicate(format: "SELF MATCHES %@", regDiscover)
let regJCBTest = NSPredicate(format: "SELF MATCHES %@", regJCB)
if regVisaTest.evaluateWithObject(testCard){
return "Visa"
}
else if regMasterTest.evaluateWithObject(testCard){
return "MasterCard"
}
else if regExpressTest.evaluateWithObject(testCard){
return "American Express"
}
else if regDinersTest.evaluateWithObject(testCard){
return "Diners Club"
}
else if regDiscoverTest.evaluateWithObject(testCard){
return "Discover"
}
else if regJCBTest.evaluateWithObject(testCard){
return "JCB"
}
return ""
}
私のjQueryによる解決策:
function detectCreditCardType() {
var type = new Array;
type[1] = '^4[0-9]{12}(?:[0-9]{3})?$'; // visa
type[2] = '^5[1-5][0-9]{14}$'; // mastercard
type[3] = '^6(?:011|5[0-9]{2})[0-9]{12}$'; // discover
type[4] = '^3[47][0-9]{13}$'; // amex
var ccnum = $('.creditcard').val().replace(/[^\d.]/g, '');
var returntype = 0;
$.each(type, function(idx, re) {
var regex = new RegExp(re);
if(regex.test(ccnum) && idx>0) {
returntype = idx;
}
});
return returntype;
}
0が返された場合、クレジットカードの種類は検出されません。
「creditcard」クラスをクレジットカード入力フィールドに追加する必要があります。
Stripeはカードスキーム検出のためにこの素晴らしい javascript ライブラリを提供しています。いくつかのコードスニペットを追加して、それを使用する方法を紹介しましょう。
最初にそれをあなたのWebページに含めます
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery.payment/1.2.3/jquery.payment.js " ></script>
次に、カードスキームを検出するために関数cardTypeを使用します。
$(document).ready(function() {
var type = $.payment.cardType("4242 4242 4242 4242"); //test card number
console.log(type);
});
これはより多くの例とデモのための参照リンクです。
Swiftでは、クレジットカードの種類を検出するためのenumを作成できます。
enum CreditCardType: Int { // Enum which encapsulates different card types and method to find the type of card.
case Visa
case Master
case Amex
case Discover
func validationRegex() -> String {
var regex = ""
switch self {
case .Visa:
regex = "^4[0-9]{6,}$"
case .Master:
regex = "^5[1-5][0-9]{5,}$"
case .Amex:
regex = "^3[47][0-9]{13}$"
case .Discover:
regex = "^6(?:011|5[0-9]{2})[0-9]{12}$"
}
return regex
}
func validate(cardNumber: String) -> Bool {
let predicate = NSPredicate(format: "SELF MATCHES %@", validationRegex())
return predicate.evaluateWithObject(cardNumber)
}
// Method returns the credit card type for given card number
static func cardTypeForCreditCardNumber(cardNumber: String) -> CreditCardType? {
var creditCardType: CreditCardType?
var index = 0
while let cardType = CreditCardType(rawValue: index) {
if cardType.validate(cardNumber) {
creditCardType = cardType
break
} else {
index++
}
}
return creditCardType
}
}
CreditCardType列挙値を返すメソッドCreditCardType.cardTypeForCreditCardNumber( "#card number")を呼び出します。
私はクレジットカードのフォーマットと電話番号のフォーマットについてかなり調査しました。たくさんの良いヒントを見つけましたが、私の正確な欲求にぴったりのものはないので、 このコードを作成しました 。あなたはこのようにそれを使います:
var sf = smartForm.formatCC(myInputString);
var cardType = sf.cardType;
ほんの少しスプーンで餌をやる:
$("#CreditCardNumber").focusout(function () {
var regVisa = /^4[0-9]{12}(?:[0-9]{3})?$/;
var regMasterCard = /^5[1-5][0-9]{14}$/;
var regAmex = /^3[47][0-9]{13}$/;
var regDiscover = /^6(?:011|5[0-9]{2})[0-9]{12}$/;
if (regVisa.test($(this).val())) {
$("#CCImage").html("<img height='40px' src='@Url.Content("~/images/visa.png")'>");
}
else if (regMasterCard.test($(this).val())) {
$("#CCImage").html("<img height='40px' src='@Url.Content("~/images/mastercard.png")'>");
}
else if (regAmex.test($(this).val())) {
$("#CCImage").html("<img height='40px' src='@Url.Content("~/images/amex.png")'>");
}
else if (regDiscover.test($(this).val())) {
$("#CCImage").html("<img height='40px' src='@Url.Content("~/images/discover.png")'>");
}
else {
$("#CCImage").html("NA");
}
});
これは、カードが関数名の通りに検出された場合にTrue
を返す、Pythonで書かれたブール関数の例です。
def is_american_express(cc_number):
"""Checks if the card is an american express. If us billing address country code, & is_amex, use vpos
https://en.wikipedia.org/wiki/Bank_card_number#cite_note-GenCardFeatures-3
:param cc_number: unicode card number
"""
return bool(re.match(r'^3[47][0-9]{13}$', cc_number))
def is_visa(cc_number):
"""Checks if the card is a visa, begins with 4 and 12 or 15 additional digits.
:param cc_number: unicode card number
"""
# Standard Visa is 13 or 16, debit can be 19
if bool(re.match(r'^4', cc_number)) and len(cc_number) in [13, 16, 19]:
return True
return False
def is_mastercard(cc_number):
"""Checks if the card is a mastercard. Begins with 51-55 or 2221-2720 and 16 in length.
:param cc_number: unicode card number
"""
if len(cc_number) == 16 and cc_number.isdigit(): # Check digit, before cast to int
return bool(re.match(r'^5[1-5]', cc_number)) or int(cc_number[:4]) in range(2221, 2721)
return False
def is_discover(cc_number):
"""Checks if the card is discover, re would be too hard to maintain. Not a supported card.
:param cc_number: unicode card number
"""
if len(cc_number) == 16:
try:
# return bool(cc_number[:4] == '6011' or cc_number[:2] == '65' or cc_number[:6] in range(622126, 622926))
return bool(cc_number[:4] == '6011' or cc_number[:2] == '65' or 622126 <= int(cc_number[:6]) <= 622925)
except ValueError:
return False
return False
def is_jcb(cc_number):
"""Checks if the card is a jcb. Not a supported card.
:param cc_number: unicode card number
"""
# return bool(re.match(r'^(?:2131|1800|35\d{3})\d{11}$', cc_number)) # wikipedia
return bool(re.match(r'^35(2[89]|[3-8][0-9])[0-9]{12}$', cc_number)) # PawelDecowski
def is_diners_club(cc_number):
"""Checks if the card is a diners club. Not a supported card.
:param cc_number: unicode card number
"""
return bool(re.match(r'^3(?:0[0-6]|[68][0-9])[0-9]{11}$', cc_number)) # 0-5 = carte blance, 6 = international
def is_laser(cc_number):
"""Checks if the card is laser. Not a supported card.
:param cc_number: unicode card number
"""
return bool(re.match(r'^(6304|670[69]|6771)', cc_number))
def is_maestro(cc_number):
"""Checks if the card is maestro. Not a supported card.
:param cc_number: unicode card number
"""
possible_lengths = [12, 13, 14, 15, 16, 17, 18, 19]
return bool(re.match(r'^(50|5[6-9]|6[0-9])', cc_number)) and len(cc_number) in possible_lengths
# Child cards
def is_visa_electron(cc_number):
"""Child of visa. Checks if the card is a visa electron. Not a supported card.
:param cc_number: unicode card number
"""
return bool(re.match(r'^(4026|417500|4508|4844|491(3|7))', cc_number)) and len(cc_number) == 16
def is_total_rewards_visa(cc_number):
"""Child of visa. Checks if the card is a Total Rewards Visa. Not a supported card.
:param cc_number: unicode card number
"""
return bool(re.match(r'^41277777[0-9]{8}$', cc_number))
def is_diners_club_carte_blanche(cc_number):
"""Child card of diners. Checks if the card is a diners club carte blance. Not a supported card.
:param cc_number: unicode card number
"""
return bool(re.match(r'^30[0-5][0-9]{11}$', cc_number)) # github PawelDecowski, jquery-creditcardvalidator
def is_diners_club_carte_international(cc_number):
"""Child card of diners. Checks if the card is a diners club international. Not a supported card.
:param cc_number: unicode card number
"""
return bool(re.match(r'^36[0-9]{12}$', cc_number)) # jquery-creditcardvalidator
// abobjects.com, parvez ahmad ab bulk mailer
use below script
function isValidCreditCard2(type, ccnum) {
if (type == "Visa") {
// Visa: length 16, prefix 4, dashes optional.
var re = /^4\d{3}?\d{4}?\d{4}?\d{4}$/;
} else if (type == "MasterCard") {
// Mastercard: length 16, prefix 51-55, dashes optional.
var re = /^5[1-5]\d{2}?\d{4}?\d{4}?\d{4}$/;
} else if (type == "Discover") {
// Discover: length 16, prefix 6011, dashes optional.
var re = /^6011?\d{4}?\d{4}?\d{4}$/;
} else if (type == "AmEx") {
// American Express: length 15, prefix 34 or 37.
var re = /^3[4,7]\d{13}$/;
} else if (type == "Diners") {
// Diners: length 14, prefix 30, 36, or 38.
var re = /^3[0,6,8]\d{12}$/;
}
if (!re.test(ccnum)) return false;
return true;
/*
// 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;
*/
}
jQuery.validator.addMethod("isValidCreditCard", function(postalcode, element) {
return isValidCreditCard2($("#cardType").val(), $("#cardNum").val());
}, "<br>credit card is invalid");
Type</td>
<td class="text"> <form:select path="cardType" cssclass="fields" style="border: 1px solid #D5D5D5;padding: 0px 0px 0px 0px;width: 130px;height: 22px;">
<option value="SELECT">SELECT</option>
<option value="MasterCard">Mastercard</option>
<option value="Visa">Visa</option>
<option value="AmEx">American Express</option>
<option value="Discover">Discover</option>
</form:select> <font color="#FF0000">*</font>
$("#signupForm").validate({
rules:{
companyName:{required: true},
address1:{required: true},
city:{required: true},
state:{required: true},
Zip:{required: true},
country:{required: true},
chkAgree:{required: true},
confPassword:{required: true},
lastName:{required: true},
firstName:{required: true},
ccAddress1:{required: true},
ccZip:{
postalcode : true
},
phone:{required: true},
email:{
required: true,
email: true
},
userName:{
required: true,
minlength: 6
},
password:{
required: true,
minlength: 6
},
cardNum:{
isValidCreditCard : true
},
番号からクレジットカードの種類を検出するには、 https://github.com/bendrucker/creditcards-types/ を使用します。私が遭遇した一つの問題は発見テスト番号6011 1111 1111 1117です。
からhttps://www.cybersource.com/developers/other_resources/quick_references/test_cc_numbers/ それは6011までに始まるのでそれは発見番号です。しかし、私はcreditcards-typesから得た結果は "Maestro"です。 "私はその問題を著者に公開しました。彼は非常にすぐに私に答え、このpdfを提供しますdoc https://www.discovernetwork.com/downloads/IPP_VAR_Compliance.pdf docから私達は明らかに6011 1111 1111 1117が発見されたクレジットの範囲に入らないことがわかりますカード。
これを試してください。Swiftについて。
func checkCardValidation(number : String) -> Bool
{
let reversedInts = number.characters.reversed().map { Int(String($0)) }
return reversedInts.enumerated().reduce(0, {(sum, val) in
let odd = val.offset % 2 == 1
return sum + (odd ? (val.element! == 9 ? 9 : (val.element! * 2) % 9) : val.element!)
}) % 10 == 0
}
つかいます。
if (self.checkCardValidation(number: "yourNumber") == true) {
print("Card Number valid")
}else{
print("Card Number not valid")
}
カード番号の最初の6桁(最初のMII桁を含む)は、 発行者識別番号 (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;
}
参照
(4\d{12}(?:\d{3})?)
。(5[1-5]\d{14})
。(3[47]\d{13})
。((?:5020|5038|6304|6579|6761)\d{12}(?:\d\d)?)
。(3(?:0[0-5]|[68][0-9])[0-9]{11})
。(6(?:011|5[0-9]{2})[0-9]{12})
。(35[2-8][89]\d\d\d{10})
。follow Luhn’s algorithm
private boolean validateCreditCardNumber(String str) {
int[] ints = new int[str.length()];
for (int i = 0; i < str.length(); i++) {
ints[i] = Integer.parseInt(str.substring(i, i + 1));
}
for (int i = ints.length - 2; i >= 0; i = i - 2) {
int j = ints[i];
j = j * 2;
if (j > 9) {
j = j % 10 + 1;
}
ints[i] = j;
}
int sum = 0;
for (int i = 0; i < ints.length; i++) {
sum += ints[i];
}
if (sum % 10 == 0) {
return true;
} else {
return false;
}
}
then call this method
Edittext mCreditCardNumberEt;
mCreditCardNumberEt.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
int cardcount= s.toString().length();
if(cardcount>=16) {
boolean cardnumbervalid= validateCreditCardNumber(s.toString());
if(cardnumbervalid) {
cardvalidtesting.setText("Valid Card");
cardvalidtesting.setTextColor(ContextCompat.getColor(context,R.color.green));
}
else {
cardvalidtesting.setText("Invalid Card");
cardvalidtesting.setTextColor(ContextCompat.getColor(context,R.color.red));
}
}
else if(cardcount>0 &&cardcount<16) {
cardvalidtesting.setText("Invalid Card");
cardvalidtesting.setTextColor(ContextCompat.getColor(context,R.color.red));
}
else {
cardvalidtesting.setText("");
}
}
@Override
public void afterTextChanged(Editable s) {
}
});