Como você detecta o tipo de cartão de crédito com base no número?

Estou tentando descobrir como detectar o tipo de cartão de crédito baseado apenas em seu número. Alguém sabe de uma maneira definitiva e confiável de encontrar isso?

O número do cartão de crédito / débito é chamado de PAN ou Número de conta principal . Os primeiros seis dígitos do PAN são retirados do IIN , ou Número de Identificação do Emissor , pertencentes ao banco emissor (os IINs eram anteriormente conhecidos como BIN – Números de Identificação Bancária – portanto, você pode ver referências a essa terminologia em alguns documentos). Esses seis dígitos estão sujeitos a um padrão internacional, ISO / IEC 7812 , e podem ser usados ​​para determinar o tipo de cartão a partir do número.

Infelizmente, o database ISO / IEC 7812 atual não está disponível publicamente, no entanto, existem listas não oficiais, tanto comerciais quanto gratuitas, inclusive na Wikipedia .

De qualquer forma, para detectar o tipo do número, você pode usar uma expressão regular como as abaixo: Crédito para expressões originais

Visto: ^4[0-9]{6,}$ Os números de cartão Visa começam com 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,}$ Antes de 2016, os números da MasterCard começam com o números 51 a 55, mas isso só detectará cartões de crédito MasterCard ; existem outros cartões emitidos usando o sistema MasterCard que não se enquadram nesta faixa IIN. Em 2016, eles adicionarão números no intervalo (222100-272099).

American Express: ^3[47][0-9]{5,}$ Os números do cartão American Express começam com 34 ou 37.

Diners Club: ^3(?:0[0-5]|[68][0-9])[0-9]{4,}$ Os números de cartão do Diners Club começam com 300 a 305, 36 ou 38. Existem Cartões Diners Club que começam com 5 e têm 16 dígitos. Estas são uma joint venture entre a Diners Club e a MasterCard e devem ser processadas como um MasterCard.

Descubra: ^6(?:011|5[0-9]{2})[0-9]{3,}$ números dos cartões começam com 6011 ou 65.

JCB: ^(?:2131|1800|35[0-9]{3})[0-9]{3,}$ cartões ^(?:2131|1800|35[0-9]{3})[0-9]{3,}$ JCB começam com 2131, 1800 ou 35.

Infelizmente, há vários tipos de cartões processados ​​com o sistema MasterCard que não residem no intervalo IIN da MasterCard (números iniciando 51 … 55); O caso mais importante é o dos cartões Maestro, muitos dos quais foram emitidos a partir das faixas do IIN de outros bancos e, portanto, estão localizados em todo o espaço numérico. Como resultado, pode ser melhor supor que qualquer cartão que não seja de outro tipo que você aceita deve ser um MasterCard .

Importante : os números dos cartões variam em tamanho; Por exemplo, no passado, a Visa emitiu cartões com PANs de 13 dígitos e cartões com PANs de 16 dígitos. A documentação da Visa atualmente indica que ela pode emitir ou pode ter emitido números com entre 12 e 19 dígitos. Portanto, você não deve verificar o comprimento do número do cartão, a não ser para verificar se possui pelo menos 7 dígitos (para um IIN completo mais um dígito de verificação, que deve corresponder ao valor previsto pelo algoritmo Luhn ).

Mais uma dica: antes de processar um PAN do titular do cartão, retire quaisquer caracteres de espaço em branco e de pontuação da input . Por quê? Porque normalmente é muito mais fácil inserir os dígitos em grupos, de forma semelhante à forma como são exibidos na frente de um cartão de crédito real, ou seja,

 4444 4444 4444 4444 

é muito mais fácil entrar corretamente do que

 4444444444444444 

Não há realmente nenhum benefício em castigar o usuário porque eles inseriram caracteres que você não espera aqui.

Isso também implica garantir que os campos de input tenham espaço para pelo menos 24 caracteres, caso contrário, os usuários que entrarem nos espaços ficarão sem espaço. Eu recomendo que você torne o campo amplo o suficiente para exibir 32 caracteres e permitir até 64; Isso dá muito espaço para expansão.

Aqui está uma imagem que dá um pouco mais de insight:

ATUALIZAÇÃO (2014): O método de sum de verificação não parece mais ser uma maneira válida de verificar a autenticidade de um cartão, conforme observado nos comentários sobre essa resposta.

ATUALIZAÇÃO (2016): Mastercard é implementar novos intervalos BIN começando Ach Payment .

verificação de cartão de credito

Em 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 } } } 

Teste de unidade:

 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]); }); }); }); }); 

Atualizado em: 15 de junho de 2016 (como solução final atualmente)

Por favor, note que eu ainda dou o voto para o primeiro é votado, mas para deixar claro que estes são os regexps realmente funciona eu testei com milhares de códigos BIN reais. O mais importante é usar start strings (^), caso contrário ele dará resultados falsos no mundo real!

JCB ^(?:2131|1800|35)[0-9]{0,}$ Início com: 2131, 1800, 35 (3528-3589)

American Express ^3[47][0-9]{0,}$ Iniciar com: 34, 37

Diners Club ^3(?:0[0-59]{1}|[689])[0-9]{0,}$ Início com: 300-305, 309, 36, 38-39

Visa ^4[0-9]{0,}$ Início com: 4

MasterCard ^(5[1-5]|222[1-9]|22[3-9]|2[3-6]|27[01]|2720)[0-9]{0,}$ Inicia com : 2221-2720, 51-55

Maestro ^(5[06789]|6)[0-9]{0,}$ Maestro sempre crescendo no intervalo: 60-69 , iniciado com / não outra coisa, mas iniciar 5 deve ser codificado como mastercard de qualquer maneira. Os cartões Maestro devem ser detectados no final do código porque alguns outros estão no intervalo de 60 a 69. Por favor, olhe o código.

Descobrir ^(6011|65|64[4-9]|62212[6-9]|6221[3-9]|622[2-8]|6229[01]|62292[0-5])[0-9]{0,}$ Descubra bastante difícil codificar, comece com: 6011, 622126-622925, 644-649, 65

Em javascript eu uso essa function. Isso é bom quando você o atribui a um evento onkeyup e dá resultado assim que possível.

 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; } 

Aqui você pode jogar com isso:

http://jsfiddle.net/upN3L/69/

Para o PHP use esta function, isso também detecta alguns cartões sub 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"; } 

Aqui está a function para verificar o tipo de cartão de crédito usando Regex, c #

Veja isso:

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 

Recentemente eu precisei de tal funcionalidade, eu estava portando o Zend Framework Credit Card Validator para Ruby. ruby gem: https://github.com/Fivell/credit_card_validations zend framework: https://github.com/zendframework/zf2/blob/master/library/Zend/Validator/CreditCard.php

Ambos usam intervalos INN para detectar o tipo. Aqui você pode ler sobre INN

De acordo com isso, você pode detectar cartão de crédito alternativamente (sem regexps, mas declarando algumas regras sobre prefixos e tamanho possível)

Então, temos as próximas regras para os cartões mais usados

 ######## 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} ] } 

Em seguida, pesquisando o prefixo e comparando o comprimento, você pode detectar a marca do cartão de crédito. Também não se esqueça do algoritmo de luhn (é descrito aqui http://en.wikipedia.org/wiki/Luhn ).

Aqui está o código Complete C # ou VB para todos os tipos de coisas relacionadas ao CC no codeproject.

  • IsValidNumber
  • GetCardTypeFromNumber
  • GetCardTestNumber
  • PassesLuhnTest

Este artigo está aberto há alguns anos, sem comentários negativos.

Versão compacta do javascript

  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; } } }; 

A resposta de Anatoliy em 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; } } 

Aqui está uma function de class php retorna CCtype por CCnumber.
Este código não valida o cartão ou não executa o algoritmo Luhn apenas para tentar encontrar o tipo de cartão de crédito baseado na tabela desta página . Basicamente usa comprimento CCnumber e prefixo CCcard para determinar o tipo CCcard.

  < ?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; } } ?> 

Não tente detectar o tipo de cartão de crédito como parte do processamento de um pagamento. Você está se arriscando a recusar transactions válidas.

Se você precisar fornecer informações ao seu processador de pagamento (por exemplo, o object de cartão de crédito do PayPal exigir o nome do tipo de cartão ), adivinhe-o com a menor informação disponível, por exemplo,

 $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.'); } 

Essa implementação (usando apenas os dois primeiros dígitos) é suficiente para identificar todos os principais (e no caso do PayPal, todos os esquemas de cartão suportados). Na verdade, você pode pular a exceção completamente e usar como padrão o tipo de cartão mais popular. Deixe o gateway / processador de pagamento avisar se houver um erro de validação em resposta à sua solicitação.

A realidade é que o seu gateway de pagamento não se importa com o valor que você fornece .

Os primeiros números do cartão de crédito podem ser usados ​​para aproximar o fornecedor:

  • Visto: 49,44 ou 47
  • Visa electron: 42, 45, 48, 49
  • Cartão Master: 51
  • Amex: 34
  • Diners: 30, 36, 38
  • JCB: 35

No Card Range Recognition (CRR), uma desvantagem com algoritmos que usam uma série de regex ou outros intervalos codificados, é que os BINs / IINs mudam ao longo do tempo na minha experiência. O co-branding de cartões é uma complicação permanente. Adquirentes / comerciantes de cartões diferentes podem precisar que você trate o mesmo cartão de maneira diferente, dependendo da localização geográfica.

Além disso, nos últimos anos com, por exemplo, cartões UnionPay em circulação mais ampla, os modelos existentes não lidam com novos intervalos que às vezes se intercalam com intervalos mais amplos que eles superam.
Conhecer a geografia que seu sistema precisa cobrir pode ajudar, já que alguns intervalos são restritos para uso em determinados países. Por exemplo, os intervalos 62 incluem alguns sub-intervalos de AAA nos EUA, mas se a sua base de comerciantes estiver fora dos EUA, você poderá tratar todos os 62 como UnionPay.
Você também pode ser solicitado a tratar um cartão de maneira diferente com base no local do comerciante. Por exemplo, para tratar certos cartões do Reino Unido como débito internamente, mas como crédito internacionalmente.

Há um conjunto de regras muito útil mantido por um dos principais bancos adquirentes. Por exemplo, https://www.barclaycard.co.uk/business/files/BIN-Rules-EIRE.pdf e https://www.barclaycard.co.uk/business/files/BIN-Rules-UK.pdf . (Links válidos a partir de junho de 2017, graças ao usuário que forneceu um link para a referência atualizada.) Mas esteja ciente da ressalva de que, embora essas regras de CRR possam representar o universo de Emissão de Cartões conforme aplicável aos comerciantes adquiridos por essa entidade, não inclui, por exemplo, faixas identificadas como CUP / UPI.

Esses comentários se aplicam a cenários de tarja magnética (MagStripe) ou PKE (Entrada de chave de panorama). A situação é diferente novamente no mundo ICC / EMV.

Atualização: Outras respostas nesta página (e também na página vinculada da WikiPedia) têm JCB como sempre 16 long. No entanto, na minha empresa, temos uma equipe dedicada de engenheiros que certifica nossos dispositivos e softwares de PDV em vários bancos e geografias de aquisição. O mais recente Pacote de Certificação de cartões que essa equipe tem da JCB, teve um caso de aprovação para um longo PAN de 19 anos.

Minha solução com 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; } 

Caso 0 seja retornado, o tipo de cartão de crédito não será detectado.

A class “creditcard” deve ser adicionada ao campo de input do cartão de crédito.

Swift 2.1 Versão da resposta de Usman Y. Use uma instrução print para verificar isso, chame por algum valor de string

 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 "" } 

Stripe has provided this fantastic javascript library for card scheme detection. Let me add few code snippets and show you how to use it.

Firstly Include it to your web page as

  

Secondly use the function cardType for detecting the card scheme.

 $(document).ready(function() { var type = $.payment.cardType("4242 4242 4242 4242"); //test card number console.log(type); }); 

Here are the reference links for more examples and demos.

  1. Stripe blog for jquery.payment.js
  2. Repositório Github

I searched around quite a bit for credit card formatting and phone number formatting. Found lots of good tips but nothing really suited my exact desires so I created this bit of code . Você usa assim:

 var sf = smartForm.formatCC(myInputString); var cardType = sf.cardType; 

In swift you can create an enum to detect the credit card type.

 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 } } 

Call the method CreditCardType.cardTypeForCreditCardNumber(“#card number”) which returns CreditCardType enum value.

 // 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; icredit card is invalid"); Type          * $("#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 }, 

Just a little spoon feeding:

 $("#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(""); } else if (regMasterCard.test($(this).val())) { $("#CCImage").html(""); } else if (regAmex.test($(this).val())) { $("#CCImage").html(""); } else if (regDiscover.test($(this).val())) { $("#CCImage").html(""); } else { $("#CCImage").html("NA"); } }); 

Here is an example of some boolean functions written in Python that return True if the card is detected as per the function name.

 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 

The regular expression rules that match the respective card vendors :

  • (4\d{12}(?:\d{3})?) for VISA.
  • (5[1-5]\d{14}) for MasterCard.
  • (3[47]\d{13}) for AMEX.
  • ((?:5020|5038|6304|6579|6761)\d{12}(?:\d\d)?) for Maestro.
  • (3(?:0[0-5]|[68][0-9])[0-9]{11}) for Diners Club.
  • (6(?:011|5[0-9]{2})[0-9]{12}) for Discover.
  • (35[2-8][89]\d\d\d{10}) for JCB.

The first six digits of a card number (including the initial MII digit) are known as the issuer identification number (IIN). These identify the card issuing institution that issued the card to the card holder. The rest of the number is allocated by the card issuer. The card number’s length is its number of digits. Many card issuers print the entire IIN and account number on their card.

Based on the above facts I would like to keep a snippet of JAVA code to identify card brand.

Sample card types

 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"; 

Card Prefixes

 // 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" }; 

Check to see if the input number has any of the given prefixes.

 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; } 

Finally, The Utility method

 /** * 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; } 

Referência

  • Stripe Card Builder

I use https://github.com/bendrucker/creditcards-types/ to detect the credit card type from number. One issue I ran into is discover test number 6011 1111 1111 1117

from https://www.cybersource.com/developers/other_resources/quick_references/test_cc_numbers/ we can see it is a discover number because it starts by 6011. But the result I get from the creditcards-types is “Maestro”. I opened the issue to the author. He replied me very soon and provide this pdf doc https://www.discovernetwork.com/downloads/IPP_VAR_Compliance.pdf From the doc we can see clearly that 6011 1111 1111 1117 does not fall into the range of discover credit card.

Try this.For 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 } 

Usar.

 if (self.checkCardValidation(number: "yourNumber") == true) { print("Card Number valid") }else{ print("Card Number not valid") } 
 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) { } });