信用卡卡号是怎么回事

常见国际信用卡有VISA/MasterCard/AE/DC/JCB等,根据卡号结构,可以识别出卡种。

先上个干货:

卡号结构

根据ISO标准,银行卡长度一般在13-19位,国际上也有12位的。银联标准卡卡长度一般是在16-19位,双组织卡也有13-19位的。VISA和MasterCard信用卡卡号通常为16为:

xxxx     xxxx     xxxx     xxxx 
  • 1~6:发卡行卡BIN
  • 7~12: 卡顺序号
  • 13:0代表主卡,1-9代表副卡
  • 14:卡接单发出日期
  • 15:代表该卡为第几张卡,补发卡后会加1
  • 16:校验码

发卡行标识代码

发卡行标识代码Bank Identification Number(BIN),又叫发卡机构标识代码Issuer Identification Numbers(IIN)。一般由6位数字组成,2014年底,国际标准组织(ISO)已经将BIN由6位数字调整到8位数字。目前国内银联卡,因银行众多,特别是村镇银行的存在,BIN长度以6位占绝大部分,另外还存在7、8、9、10等位数卡BIN。

发卡行标识代码第一位,为发卡行业标识号Major Industry Identifier(MII),代表的是发卡机构所处行业。具体分配规则如下:
![MajorIndustryIdentifier](https://cdn.jsdelivr.net/gh/lijianfeigeek/PictureBed@main/uPic/Major Industry Identifier.jpeg)

目前银联标准卡以62开头,各银行再向银联进行卡BIN申请,如622848开头的卡为农行借记卡。但实际发现,银联卡不都是以62开头,主要有2种情况:
双组织卡,如银联、运通双组织卡,一般以信用卡居多,如招行与运通合作的运通卡
早期以9开头的银行卡,这些卡都是国内自行分配的,仅限国内使用,无法与国际进行接轨
只要能获取到比较全面的卡BIN数据,就能判断出银行卡发卡行及卡种。目前支付宝、微信等大部分主流需要绑卡的应用都是如此实现。

卡信息

卡组织 卡号长度 卡号起始 卡号范围 备注
VISA 16 40 - 49 400000 - 499999 以前有极少13位卡,现已不发行
MasterCard 16 51 - 59 510000 - 559999
银联 16、19 62 储蓄卡19位(存在少数高级别16位)信用卡16位
AE(American Express) 15 34、37 340000 - 349999370000 - 379999
DC(大莱卡DinersClub) 14 30、36、38、39 300000 - 305999309500 - 309599360000 - 369999380000 - 399999
JCB 16 35 352800 - 358999 日本卡组织

19位银联卡含义:
1、1-6位代表发卡行;
2、7-10位代表发卡地区;
3、第11位代表卡种类;
4、第12-18位为发卡顺序号;
5、第19位为校验位。

校验算法

校验码为银行卡号最后一位,采用LUHN算法,亦称模10算法。计算方法如下:

第一步:从右边第1个数字开始每隔一位乘以2;
第二步: 把在第一步中获得的乘积的各位数字相加,然后再与原号码中未乘2的各位数字相加;
第三步:对于第二步求和值中个位数求10的补数,如果个位数为0则该校验码为0。

举例:6259 6508 7177 209(不含校验码的银行卡号)
第一步:62=12,52=10,62=12,02=0,72=14,72=14,22=4,92=18
第二步:1+2 + 1+0 + 1+2 + 0 + 1+4 + 1+4 + 4 + 1+8 = 30
30 + 2+9+5+8+1+7+0 = 62
第三步:10-2=8
所以,校验码是8,完整的卡号应该是6259650871772098。

#pragma mark - 信用卡校验-Luhn算法
-(BOOL)checkCardNumber:(NSString *)cardNumber{   // 4514617608810943
    int sum = 0;
    NSUInteger cardNumLength = [cardNumber length];
    int i = 0;
    
    while (i < cardNumLength) {
        NSString *subStr = [cardNumber substringWithRange:NSMakeRange(cardNumLength - 1 - i, 1)];
        int subInt = [subStr intValue];
        if (i % 2 != 0) {
            subInt *= 2;
            if(subInt > 9) {
                subInt -= 9;
            }
        }
        sum += subInt;
        i++;
    }
    
    if((sum % 10) == 0) {
        return YES;
    } else {
        return NO;
    }
}

RAC UITextField 每格四位插入空格

[[[self.cardNumTextField rac_signalForControlEvents:UIControlEventEditingChanged] takeUntil:self.rac_prepareForReuseSignal] subscribeNext:^(UITextField * _Nullable textField) {
        NSString *text = [[textField text] stringByReplacingOccurrencesOfString:@" " withString:@""];
        NSCharacterSet *characterSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789\b"];
        NSString *newString = @"";
        while (text.length > 0) {
            NSString *subString = [text substringToIndex:MIN(text.length, 4)];
            newString = [newString stringByAppendingString:subString];
            if (subString.length == 4) {
                newString = [newString stringByAppendingString:@" "];
            }
            text = [text substringFromIndex:MIN(text.length, 4)];
        }
        newString = [newString stringByTrimmingCharactersInSet:[characterSet invertedSet]];
        if ([newString stringByReplacingOccurrencesOfString:@" " withString:@""].length < 21) {
            [textField setText:newString];
            data.cardNumber = [newString stringByReplacingOccurrencesOfString:@" " withString:@""];
        }else{
            textField.text = [textField.text substringToIndex:19];
            data.cardNumber = [textField.text stringByReplacingOccurrencesOfString:@" " withString:@""];
        }
    }];

RAC UITextField 信用卡日期

[[[self.expDateTextField rac_signalForControlEvents:UIControlEventEditingChanged] takeUntil:self.rac_prepareForReuseSignal] subscribeNext:^(UITextField * _Nullable textField) {
        NSString *text = [[textField text] stringByReplacingOccurrencesOfString:@"/" withString:@""];
        NSCharacterSet *characterSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789\b"];
        NSString *newString = @"";
        while (text.length > 0) {
            NSString *subString = [text substringToIndex:MIN(text.length, 2)];
            newString = [newString stringByAppendingString:subString];
            if (subString.length == 2) {
                newString = [newString stringByAppendingString:@"/"];
            }
            text = [text substringFromIndex:MIN(text.length, 2)];
        }
        newString = [newString stringByTrimmingCharactersInSet:[characterSet invertedSet]];
        if ([newString stringByReplacingOccurrencesOfString:@"/" withString:@""].length < 5) {
            [textField setText:newString];
            data.cardDate = textField.text;
        }else{
            textField.text = [textField.text substringToIndex:5];
            data.cardDate = textField.text;
        }
    }];