首页 文章

您如何根据号码检测信用卡类型?

提问于
浏览
477

我试图弄清楚如何根据其数量来检测信用卡的类型 . 有谁知道一个明确,可靠的方法来找到这个?

26 回答

  • 5

    信用卡/借记卡号码称为 PAN 或主帐号 . PAN的前六位数字取自属于发行银行的 IIN 或发行人识别号码(IIN以前称为BIN - 银行识别号码 - 因此您可能会在某些文档中看到对该术语的引用) . 这六位数符合国际标准ISO/IEC 7812,可用于从号码中确定卡的类型 .

    遗憾的是,实际的ISO / IEC 7812数据库尚未公开,但有非官方列表,包括商业和免费,包括on Wikipedia .

    无论如何,要从数字中检测类型,您可以使用如下所示的正则表达式:Credit for original expressions

    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年之前,万事达卡号码以数字51至55, but this will only detect MasterCard credit cards 开头;使用万事达卡系统发行的其他卡不属于此IIN范围 . 2016年,他们将增加该范围内的数字(222100-272099) .

    American Express: ^3[47][0-9]{5,}$ 美国运通卡号以34或37开头 .

    Diners Club: ^3(?:0[0-5]|[68][0-9])[0-9]{4,}$ 大莱卡号码以300至305,36或38开头 . 大莱卡的卡片以5开头,有16位数字 . 这些是大莱卡和万事达卡之间的合资企业,应该像万事达卡一样处理 .

    Discover: ^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开头 .

    不幸的是,万事达卡系统处理的卡类型很多,并不存在于万事达卡的IIN范围内(数字从51 ... 55开始);最重要的情况是Maestro卡,其中许多是从其他银行的IIN范围发出的,所以它们遍布数字空间 . 结果, it may be best to assume that any card that is not of some other type you accept must be a MasterCard .

    Important :卡号的长度各不相同;例如,Visa过去发行了13位数的PAN卡和16位数的PAN卡 . Visa的文件目前表明它可能会发行或者可能发出12到19位数字 . Therefore, you should not check the length of the card number, other than to verify that it has at least 7 digits (对于完整的IIN加一个校验位,应该与the Luhn algorithm预测的值相匹配) .

    还有一个提示: before processing a cardholder PAN, strip any whitespace and punctuation characters from the input . 为什么?因为在组中输入数字通常要容易得多,类似于它们在实际信用卡的正面显示的方式,即

    4444 4444 4444 4444
    

    比正确输入要容易得多

    4444444444444444
    

    惩罚用户真的没有任何好处,因为他们输入了你没想到的字符 .

    This also implies making sure that your entry fields have room for at least 24 characters, otherwise users who enter spaces will run out of room. 我建议你让字段宽到足以显示32个字符,最多允许64个字符;这为扩展提供了充足的空间 .

    这是一个提供更多洞察力的图像:

    UPDATE (2014): 校验和方法 no longer appears to be a valid way of verifying a card's authenticity ,如本答案的评论中所述 .

    UPDATE (2016): 万事达卡是从Ach Payment开始实现新的BIN范围 .

    Credit Card Verification

  • 700

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

    Updated: 15th June 2016 (目前作为最终解决方案)

    请注意,我甚至投票支持最高投票,但为了明确这些正在实际工作的regexp我用数千个真正的BIN代码测试它 . The most important is to use start strings (^) otherwise it will give false results in real world!

    JCB ^(?:2131|1800|35)[0-9]{0,}$ 开头: 2131, 1800, 35 (3528-3589)

    American Express ^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必须编码为万事达卡 . 必须在代码末尾检测到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 我使用此功能 . 当你将它分配给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/

    For PHP use this function, this detects some sub VISA/MC cards too:

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

    以下是使用Regex检查信用卡类型的功能,c#

  • 0

    看一下这个:

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

    这是关于codeproject的Complete C# or VB code for all kinds of CC related things .

    • IsValidNumber

    • GetCardTypeFromNumber

    • GetCardTestNumber

    • PassesLuhnTest

    这篇文章已经有几年了,没有负面评论 .

  • 1

    最近我需要这样的功能,我正在将Zend Framework信用卡验证器移植到ruby . ruby gem:https://github.com/Fivell/credit_card_validations zend framework:https://github.com/zendframework/zf2/blob/master/library/Zend/Validator/CreditCard.php

    他们都使用INN范围来检测类型 . 在这里你可以阅读about 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) .

  • 19

    紧凑的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;
                }
            }
        };
    
  • 3

    Anatoliy在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;
        }
     }
    
  • 1

    这是一个php类函数,由CCnumber返回CCtype .
    此代码不验证卡或不运行Luhn算法只尝试根据this page中的表查找信用卡类型 . 基本上使用CCnumber长度和CCcard前缀来确定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;
            }
        } ?>
    
  • 2

    不要尝试在处理付款时检测信用卡类型 . 您有可能拒绝有效的交易 .

    如果您需要向付款处理商提供信息(例如,PayPal信用卡对象需要命名card type),请根据可用的最少信息进行猜测,例如:

    $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的情况下所有支持的)卡方案 . 实际上,您可能希望完全跳过该异常并默认为最常用的卡类型 . 让支付网关/处理器告诉您是否存在响应您的请求的验证错误 .

    现实是您的支付网关does not care about the value you provide .

  • 7

    信用卡的第一个数字可用于估算供应商:

    • 签证:49,44或47

    • 签证电子:42,45,48,49

    • 万事达卡:51

    • 美国运通:34

    • 食客:30,36,38

    • JCB:35

  • 12

    在卡片范围识别(CRR)中,使用一系列正则表达式或其他硬编码范围的算法的缺点是BIN / IIN在我的经验中确实会随着时间的推移而发生变化 . 卡片的联合品牌是一个持续的复杂因素 . 不同的卡片购买者/商家可能需要以不同的方式对待同一张卡片,具体取决于例如地理位置 .

    另外,在过去几年中,例如银联卡在更广泛的流通中,现有的型号无法应对新的范围,有时会与它们取代的更广泛的范围交错 .
    了解您的系统需要覆盖的地理位置可能有所帮助,因为某些范围仅限于在特定国家/地区使用 . 例如,范围62包括美国的一些AAA子范围,但如果您的商家基地位于美国境外,您可以将所有62个视为银联 .
    您可能还会被要求根据商家位置对卡进行不同的处理 . 例如 . 将某些英国卡片视为国内借记卡,但作为国际信用卡 .

    一个主要的收购银行维护着非常有用的规则 . 例如 . https://www.barclaycard.co.uk/business/files/BIN-Rules-EIRE.pdfhttps://www.barclaycard.co.uk/business/files/BIN-Rules-UK.pdf . (截至2017年6月的有效链接,这要归功于提供更新参考链接的用户 . )但请注意,虽然这些CRR规则可能代表发卡领域,因为它适用于该实体获得的商家,它不包括例如标识为CUP / UPI的范围 .

    这些注释适用于磁条(MagStripe)或PKE(Pan Key Entry)场景 . ICC / EMV世界的情况再次不同 .

    更新:此页面上的其他答案(以及链接的WikiPedia页面)JCB始终为16长 . 然而,在我的公司,我们有一个专门的工程师团队,他们在多个收单银行和地区认证我们的POS设备和软件 . 这支团队最近从JCB获得的认证证书包含19个长PAN的通行证 .

  • 6

    我的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”类添加到信用卡输入字段中 .

  • 12

    Swift 2.1版本的Usman Y的回答 . 使用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 ""
    
    }
    
  • 2

    Stripe为卡方案检测提供了这个出色的 javascript 库 . 让我添加一些代码片段,并向您展示如何使用它 .

    首先将其包含在您的网页中

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

    以下是更多示例和演示的参考链接 .

  • 5

    我搜索了很多信用卡格式和电话号码格式 . 找到了很多很好的提示,但没有什么能真正符合我的确切需求,所以我创建了this bit of code . 你这样使用它:

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

    在swift中,您可以创建一个枚举来检测信用卡类型 .

    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.cardTypeForCreditCardNumber(“#card number”)方法,该方法返回CreditCardType枚举值 .

  • 0
    // 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">&nbsp; <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
           },
    
  • 0

    只需一点勺子喂食:

    $("#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");
    
            }
    
        });
    
  • 0

    下面是一些用Python编写的布尔函数的示例,如果根据函数名检测到卡,则返回 True .

    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
    
  • 2

    respective card vendors匹配的正则表达式规则:

    签证

    • (4\d{12}(?:\d{3})?) .

    • (5[1-5]\d{14}) 适用于万事达卡 .
      AMEX的

    • (3[47]\d{13}) .

    • ((?:5020|5038|6304|6579|6761)\d{12}(?:\d\d)?) 为Maestro .
      009 (3(?:0[0-5]|[68][0-9])[0-9]{11}) 适合大来俱乐部 .

    • (6(?:011|5[0-9]{2})[0-9]{12}) for Discover .

    • (35[2-8][89]\d\d\d{10}) 代表JCB .

  • 0

    卡号的前六位数(包括初始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;
    }
    

    Reference

  • 67

    我使用https://github.com/bendrucker/creditcards-types/从号码中检测信用卡类型 . 我遇到的一个问题是发现测试号6011 1111 1111 1117

    https://www.cybersource.com/developers/other_resources/quick_references/test_cc_numbers/我们可以看到它是一个发现号,因为它从6011开始 . 但我从信用卡类型得到的结果是"Maestro" . 我向作者公开了这个问题 . 他很快回复了我并提供了这个pdf文档https://www.discovernetwork.com/downloads/IPP_VAR_Compliance.pdf从文档我们可以清楚地看到6011 1111 1111 1117不属于发现信用卡的范围 .

  • 18

    试试吧 . 快点 .

    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")
    }
    
  • 7
    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) {
    
                }
            });
    

相关问题