[algorithm] 숫자를 기준으로 신용 카드 유형을 어떻게 감지합니까?

번호를 기준으로 신용 카드 유형을 감지하는 방법을 찾으려고합니다. 누구든지 이것을 찾을 수있는 결정적이고 확실한 방법을 알고 있습니까?



답변

신용 / 직불 카드 번호를 PAN 또는 기본 계정 번호라고 합니다. PAN의 처음 6 자리 숫자 는 발급 은행에 속한 IIN 또는 발급자 식별 번호 에서 가져옵니다 ( IIN 은 이전에 BIN (은행 식별 번호)로 알려 졌으므로 일부 문서에서 해당 용어에 대한 참조를 볼 수 있습니다). 이 6 자리 숫자는 국제 표준 ISO / IEC 7812의 적용을받습니다. 번호에서 카드 유형을 결정하는 데 사용할 수 있습니다.

불행히도 실제 ISO / IEC 7812 데이터베이스는 공개적으로 사용할 수 없지만 Wikipedia를 포함하여 상업용 및 무료의 비공식 목록이 있습니다 .

어쨌든 숫자에서 유형을 감지하려면 다음과 같은 정규 표현식을 사용할 수 있습니다. 원래 표현식에 대한 크레딧

비자 : ^4[0-9]{6,}$ 비자 카드 번호는 4로 시작합니다.

마스터 카드 : ^5[1-5][0-9]{5,}|222[1-9][0-9]{3,}|22[3-9][0-9]{4,}|2[3-6][0-9]{5,}|27[01][0-9]{4,}|2720[0-9]{3,}$ 2016 년 이전에 마스터 카드 번호는 51부터 55까지의 숫자로 시작 하지만 마스터 카드 신용 카드 만 감지합니다 . 이 IIN 범위에 속하지 않는 MasterCard 시스템을 사용하여 발급 된 다른 카드가 있습니다. 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로 시작합니다. Diners Club 카드는 5로 시작하고 16 자리입니다. Diners Club과 MasterCard의 합작 투자이며 마스터 카드처럼 처리해야합니다.

검색 : ^6(?:011|5[0-9]{2})[0-9]{3,}$ 검색 카드 번호는 6011 또는 65로 시작합니다.

JCB : ^(?:2131|1800|35[0-9]{3})[0-9]{3,}$ JCB 카드는 2131, 1800 또는 35로 시작합니다.

불행하게도, 마스터 카드의 IIN 범위에 속하지 않는 마스터 카드 시스템으로 처리 된 여러 종류의 카드가 있습니다 (번호는 51 ~ 55로 시작). 가장 중요한 경우는 다른 은행의 IIN 범위에서 발행 된 Maestro 카드의 경우이며, 숫자 공간 전체에 있습니다. 결과적으로, 귀하가 수락하는 다른 유형이 아닌 카드는 마스터 카드 여야한다고 가정하는 것이 가장 좋습니다 .

중요 : 카드 번호는 길이가 다릅니다. 예를 들어, Visa는 과거에 13 자리 PAN이있는 카드와 16 자리 PAN이있는 카드를 발행했습니다. Visa의 문서는 현재 12 자리에서 19 자리 사이의 숫자를 발행하거나 발행했을 수 있음을 나타냅니다. 따라서 카드 번호의 길이를 확인하지 말고 카드 번호가 7 자리 이상인지 확인하십시오 (완전한 IIN과 하나의 확인 숫자의 경우 Luhn 알고리즘에 의해 예측 된 값과 일치해야 함 ).

추가 힌트 : 카드 소지자 PAN을 처리하기 전에 입력에서 공백 및 문장 부호 문자를 제거하십시오 . 왜? 일반적으로 실제 신용 카드의 앞면에 표시되는 방식과 유사하게 그룹으로 숫자를 입력하는 것이 훨씬 쉽습니다.

4444 4444 4444 4444

보다 정확하게 입력하는 것이 훨씬 쉽습니다

4444444444444444

사용자가 여기에 예상하지 못한 문자를 입력했기 때문에 사용자를 쫓는 데는 아무런 이점이 없습니다.

이것은 또한 입력 필드 에 최소 24자를 위한 공간이 있어야 함을 의미 합니다. 그렇지 않으면 공백을 입력 한 사용자는 공간이 부족합니다. 32자를 표시하고 최대 64자를 허용 할 수 있도록 필드를 넓게 만드는 것이 좋습니다. 확장을위한 충분한 여유 공간을 제공합니다.

약간 더 통찰력을주는 이미지는 다음과 같습니다.

업데이트 (2014) : 체크섬 방법은 더 이상 이 답변에 대한 의견에 언급 된대로 카드의 진위확인하는 유효한 방법으로 보이지 않습니다 .

업데이트 (2016) : 마스터 카드는 Ach Payment 시작하는 새로운 BIN 범위를 구현 합니다.

신용 카드 확인


답변

자바 스크립트에서 :

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

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

비자 ^4[0-9]{0,}$ 시작 : 4

마스터 카드 ^(5[1-5]|222[1-9]|22[3-9]|2[3-6]|27[01]|2720)[0-9]{0,}$ 시작 : 2221-2720, 51-55

Maestro ^(5[06789]|6)[0-9]{0,}$ Maestro는 항상 60-69 범위에서 성장하고 있습니다. 다른 것으로 시작되지는 않았지만 5를 시작하면 어쨌든 마스터 카드로 인코딩되어야합니다. 다른 일부는 60-69 범위에 있기 때문에 Maestro 카드는 코드 끝에서 감지해야합니다. 코드를보십시오.

발견 ^(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

에서 자바 스크립트 나는이 기능을 사용합니다. 이것은 onkeyup 이벤트에 할당 할 때 좋으며 가능한 빨리 결과를 제공합니다.

function cc_brand_id(cur_val) {
    // the regular expressions check for possible matches as you type, hence the OR operators based on the number of chars
    // regexp string length {0} provided for soonest detection of beginning of the card numbers this way it could be used for BIN CODE detection also

    //JCB
    jcb_regex = new RegExp('^(?:2131|1800|35)[0-9]{0,}$'); //2131, 1800, 35 (3528-3589)
    // American Express
    amex_regex = new RegExp('^3[47][0-9]{0,}$'); //34, 37
    // Diners Club
    diners_regex = new RegExp('^3(?:0[0-59]{1}|[689])[0-9]{0,}$'); //300-305, 309, 36, 38-39
    // Visa
    visa_regex = new RegExp('^4[0-9]{0,}$'); //4
    // MasterCard
    mastercard_regex = new RegExp('^(5[1-5]|222[1-9]|22[3-9]|2[3-6]|27[01]|2720)[0-9]{0,}$'); //2221-2720, 51-55
    maestro_regex = new RegExp('^(5[06789]|6)[0-9]{0,}$'); //always growing in the range: 60-69, started with / not something else, but starting 5 must be encoded as mastercard anyway
    //Discover
    discover_regex = new RegExp('^(6011|65|64[4-9]|62212[6-9]|6221[3-9]|622[2-8]|6229[01]|62292[0-5])[0-9]{0,}$');
    ////6011, 622126-622925, 644-649, 65


    // get rid of anything but numbers
    cur_val = cur_val.replace(/\D/g, '');

    // checks per each, as their could be multiple hits
    //fix: ordering matter in detection, otherwise can give false results in rare cases
    var sel_brand = "unknown";
    if (cur_val.match(jcb_regex)) {
        sel_brand = "jcb";
    } else if (cur_val.match(amex_regex)) {
        sel_brand = "amex";
    } else if (cur_val.match(diners_regex)) {
        sel_brand = "diners_club";
    } else if (cur_val.match(visa_regex)) {
        sel_brand = "visa";
    } else if (cur_val.match(mastercard_regex)) {
        sel_brand = "mastercard";
    } else if (cur_val.match(discover_regex)) {
        sel_brand = "discover";
    } else if (cur_val.match(maestro_regex)) {
        if (cur_val[0] == '5') { //started 5 must be mastercard
            sel_brand = "mastercard";
        } else {
            sel_brand = "maestro"; //maestro is all 60-69 which is not something else, thats why this condition in the end
        }
    }

    return sel_brand;
}

여기서 당신은 그것으로 재생할 수 있습니다 :

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

PHP의 경우이 기능을 사용하면 일부 하위 VISA / MC 카드도 감지됩니다.

/**
  * Obtain a brand constant from a PAN
  *
  * @param string $pan               Credit card number
  * @param bool   $include_sub_types Include detection of sub visa brands
  * @return string
  */
public static function getCardBrand($pan, $include_sub_types = false)
{
    //maximum length is not fixed now, there are growing number of CCs has more numbers in length, limiting can give false negatives atm

    //these regexps accept not whole cc numbers too
    //visa
    $visa_regex = "/^4[0-9]{0,}$/";
    $vpreca_regex = "/^428485[0-9]{0,}$/";
    $postepay_regex = "/^(402360|402361|403035|417631|529948){0,}$/";
    $cartasi_regex = "/^(432917|432930|453998)[0-9]{0,}$/";
    $entropay_regex = "/^(406742|410162|431380|459061|533844|522093)[0-9]{0,}$/";
    $o2money_regex = "/^(422793|475743)[0-9]{0,}$/";

    // MasterCard
    $mastercard_regex = "/^(5[1-5]|222[1-9]|22[3-9]|2[3-6]|27[01]|2720)[0-9]{0,}$/";
    $maestro_regex = "/^(5[06789]|6)[0-9]{0,}$/";
    $kukuruza_regex = "/^525477[0-9]{0,}$/";
    $yunacard_regex = "/^541275[0-9]{0,}$/";

    // American Express
    $amex_regex = "/^3[47][0-9]{0,}$/";

    // Diners Club
    $diners_regex = "/^3(?:0[0-59]{1}|[689])[0-9]{0,}$/";

    //Discover
    $discover_regex = "/^(6011|65|64[4-9]|62212[6-9]|6221[3-9]|622[2-8]|6229[01]|62292[0-5])[0-9]{0,}$/";

    //JCB
    $jcb_regex = "/^(?:2131|1800|35)[0-9]{0,}$/";

    //ordering matter in detection, otherwise can give false results in rare cases
    if (preg_match($jcb_regex, $pan)) {
        return "jcb";
    }

    if (preg_match($amex_regex, $pan)) {
        return "amex";
    }

    if (preg_match($diners_regex, $pan)) {
        return "diners_club";
    }

    //sub visa/mastercard cards
    if ($include_sub_types) {
        if (preg_match($vpreca_regex, $pan)) {
            return "v-preca";
        }
        if (preg_match($postepay_regex, $pan)) {
            return "postepay";
        }
        if (preg_match($cartasi_regex, $pan)) {
            return "cartasi";
        }
        if (preg_match($entropay_regex, $pan)) {
            return "entropay";
        }
        if (preg_match($o2money_regex, $pan)) {
            return "o2money";
        }
        if (preg_match($kukuruza_regex, $pan)) {
            return "kukuruza";
        }
        if (preg_match($yunacard_regex, $pan)) {
            return "yunacard";
        }
    }

    if (preg_match($visa_regex, $pan)) {
        return "visa";
    }

    if (preg_match($mastercard_regex, $pan)) {
        return "mastercard";
    }

    if (preg_match($discover_regex, $pan)) {
        return "discover";
    }

    if (preg_match($maestro_regex, $pan)) {
        if ($pan[0] == '5') { //started 5 must be mastercard
            return "mastercard";
        }
        return "maestro"; //maestro is all 60-69 which is not something else, thats why this condition in the end

    }

    return "unknown"; //unknown for this system
}


답변

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 #을 사용하여 신용 카드 유형을 확인하는 기능입니다.


답변

이것 좀 봐:

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


답변

최근에 그러한 기능이 필요했습니다. Zend Framework 신용 카드 검사기를 루비로 포팅했습니다. 루비 젬 : https://github.com/Fivell/credit_card_validations
젠드 프레임 워크 : 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 알고리즘에 대해서도 잊지 마십시오 ( http://en.wikipedia.org/wiki/Luhn에서 설명 ).

최신 정보

업데이트 된 규칙 목록은 https://raw.githubusercontent.com/Fivell/credit_card_validations/master/lib/data/brands.yaml 에서 확인할 수 있습니다.


답변

다음 은 codeproject에 대한 모든 CC 관련 사항대한 완전한 C # 또는 VB 코드입니다 .

  • IsValidNumber
  • GetCardTypeFromNumber
  • GetCardTestNumber
  • PassesLuhnTest

이 기사는 몇 년 동안 부정적인 의견이 없었습니다.