银联卡

背景

2000年,中国人民银行为推动联网通用,组织制定了一系列银行卡业务规范和技术标准。其中《银行卡发卡行标识代码及卡号》规定,在国内发行使用的各种人民币卡必须使用“9”字头BIN。

该规定为规范和统一银行卡在国内的发行、使用及联网通用奠定了基础。

随着我国银行卡产业的迅速发展以及国际交流的日益增加,持卡人的境外支付需求越来越多。根据国际标准化组织(ISO)的规定,使用“9”字头BIN的银行卡只能在国内使用。这意味着人民币可自由兑换后,我国国内各发卡机构发行的大量“9”字头BIN银行卡届时可能无法在国外使用。

为了克服“9”字头BIN的银行卡境外使用的局限,在中国人民银行的指导和帮助下,2002年,中国银联代国内各发卡机构统一向ISO(国际标准化组织)申请了800个在国内和国外通行的国际标准BIN(622126至622925)。在取得中国人民银行关于启用银联国际标准发卡行BIN号的批复后,2003年7月,中国银联向各成员机构发布了《关于启用银联国际标准发卡行BIN号的函》,并且为了规范和合理地分配、使用和管理银联国际标准BIN号和已经广为使用的原“9”字头BIN号,中国银联发布了《银联标识卡BIN号分配和管理暂行办法》。从而正式启动了银联国际标准“62”字头BIN号的分配和使用工作,推动各家成员机构开始发行银联标准卡,即具有“银联”标识,卡号前6位采用银联国际标准“62”字头BIN号的银行卡。

该卡符合我国统一的业务规范和技术标准,是我国具有自主知识产权的高品质、国际化民族银行卡品牌。

银联卡的详细规则

银行卡号整体为13-19位,格式如下。

1
2
62XXXX             XXXXXXXXXXXX      X
发卡行识别代码 个人识别码 校验码
  • 前几位是发卡机构识别代码 Issuer Identification Numbers(IIN),又叫发卡行标识代码Bank Identification Number(BIN)。
    一般由6位数字组成,2014年底,国际标准组织(ISO)已经将BIN由6位数字调整到8位数字。但是由于总位数仍然最多19位,所以中间的个人账户号码对应的最大位数由12位减少至10位,所以卡BIN不再只是6位,也需要考虑兼容8位,当然从6位到8位还需要一定的时间去过渡。

    发卡行标识代码第一位,为发卡行业标识号Major Industry Identifier(MII),代表的是发卡机构所处行业。具体分配规则如下:

    • 0 - ISO/TC 68和其他行业分配
    • 1 - 航空业
    • 2 - 航空业,金融业和其他未来行业分配
    • 3 - 旅游业和娱乐业
    • 4 - 银行业和金融业
    • 5 - 银行业和金融业
    • 6 - 商业和银行业/金融业
    • 7 - 石油业和其他未来行业分配
    • 8 - 医疗业,电信业和其他未来行业分配
    • 9 - 由各国标准团体分配

    在国内,中国银联几乎垄断了境内的人民币清算,所以目前国内几乎所有发行的银行借记卡都是银联62开头的借记卡。目前银联标准卡以62开头,各银行再向银联进行卡BIN申请,如622848开头的卡为农行借记卡。我们发现,银联卡不都是以62开头,主要有2种情况。

    • 第一种是双组织卡,如银联、运通双组织卡,一般以信用卡居多,如招行与运通合作的运通卡。
    • 另外一种即早期以9开头的银行卡,这些卡都是国内自行分配的,仅限国内使用,无法与国际进行接轨。以下为部分常见卡组织发行的银行卡起始数字:
    卡组织中文名 卡组织英文名 卡号起始
    银联 ChinaUnionPay 62
    运通 AmericanExpress 34、37
    万事达 MasterCard 51-55
    维萨 Visa 4
  • 中间7~18位是个人识别码。
    个人识别码的位数依赖于银行卡的整体位数,如果银行卡整体位数是19位,那么个人识别码为12位,银行卡整体位数是13位,那么个人识别码为6位。

  • 最后一位是校验码。校验码是用来校验银行卡的正确与否的,采用 Luhn算法,亦称模10算法。计算方法如下:

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

    举例:6259 6508 7177 209(不含校验码的银行卡号)

    1. 第一步:6*2=12,5*2=10,6*2=12,0*2=0,7*2=14,7*2=14,2*2=4,9*2=18
    2. 第二步:1+2 + 1+0 + 1+2 + 0 + 1+4 + 1+4 + 4 + 1+8 = 30, 30 + 2+9+5+8+1+7+0 = 62
    3. 第三步:10-2=8
    4. 所以,校验码是8,完整的卡号应该是6259650871772098。

    举例:622576000922952,(不含校验码的银行卡号)

    值得指出的是,目前银联卡几乎都支持校验码算法,但是也不排除极个别不支持此算法的,如杭州银行早期发行的西湖卡

那为啥国内用的基本都是银联卡呢?又为什么都是62开头呢?

其实要理解国内的银行卡大多是银联卡,又为什么都是62开头,还需要了解一下中国人民银行、中国银联、中国银行间的关系以及中国银联与国外的visa之间的关系。

  • 中国人民银行,即央行,是负责印货币的部门,但是印货币不是它的主要职责,它最重要的是制定和执行货币政策,防范和化解金融风险,维护金融稳定;
  • 中国银联是中国人民银行批准设立的中国的银行卡联合组织,通过银联跨行交易清算系统,实现商业银行系统间的互联互通和资源共享,进而使银行卡得以跨银行、跨地区和跨境使用。它的主要作用是,跨行的结算。比方说你的卡是建设银行的,你的朋友的卡是工商银行的,你要转账到你朋友那里,就是先把你建设银行的钱给银联,银联再把这个钱划拨给你朋友的工商银行。

所以这里小伙伴们知道为什么,你的银行卡上都有银联的标志了吧!中国人民银行联合中国的银行成立的一个银行间清算组织,当然这些银行都用它啦!

再说说为什么银行卡的前两位是62~。

早在20世纪80年代,VISA、万事达、JCB等国际卡组织相继进入中国,那时候的中国还没有银联,2002年中国银联才正式成立,银联成立的时候,是作为VISA、万事达国际银行卡组织的成员,那时候发的银行卡,卡上有两个标志,一个是银联,一个是VISA这样的其他标志,不像现在这样只有银联一个标志。

当时这样的两种标志的银行卡大多以“3”、“4”或“5”开头,并不是以银联的BIN号“62”开头,这样的话,会有一个问题,要知道类似于银联这样的组织,有很大部分的盈利来来自于每笔生意的提成,比如线下POS收单业务,收单方、发卡行、银联一直按照7:2:1的比例,来分成由商家承担的手续费。当一张卡上有两种标志的时候,那么谁来收取银联类的那部分提成呢?

所以随着中国银联的发展,中国银联逐渐和VISA这些组织形成了竞争的关系,以及一些两种标志的银行卡存在一些技术安全的问题,所以慢慢的两种标志的银行卡逐渐被取消,到现在银行卡片上都只有一种标志,而中国银联的银行卡,中国银联去向ISO申请的发卡标识代码就是62开头,所以现在的银联卡才都是62开头的。

校验代码

BankCardUtils.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class BankCardUtils {
/*
校验过程:
1、从卡号最后一位数字开始,逆向将奇数位(1、3、5等等)相加。
2、从卡号最后一位数字开始,逆向将偶数位数字,先乘以2(如果乘积为两位数,将个位十位数字相加,即将其减去9),再求和。
3、将奇数位总和加上偶数位总和,结果应该可以被10整除。
*/

/**
* 校验银行卡卡号
*/
public static boolean checkBankCard(String bankCard) {
if (bankCard.length() < 15 || bankCard.length() > 19) {
return false;
}
char bit = getBankCardCheckCode(bankCard.substring(0, bankCard.length() - 1));
if (bit == 'N') {
return false;
}
return bankCard.charAt(bankCard.length() - 1) == bit;
}

/**
* 从不含校验位的银行卡卡号采用 Luhm 校验算法获得校验位
*
* @param nonCheckCodeBankCard
* @return
*/
public static char getBankCardCheckCode(String nonCheckCodeBankCard) {
if (nonCheckCodeBankCard == null || nonCheckCodeBankCard.trim().length() == 0
|| !nonCheckCodeBankCard.matches("\\d+")) {
//如果传的不是数据返回N
return 'N';
}
char[] chs = nonCheckCodeBankCard.trim().toCharArray();
int luhmSum = 0;
for (int i = chs.length - 1, j = 0; i >= 0; i--, j++) {
int k = chs[i] - '0';
if (j % 2 == 0) {
k *= 2;
k = k / 10 + k % 10;
}
luhmSum += k;
}
return (luhmSum % 10 == 0) ? '0' : (char) ((10 - luhmSum % 10) + '0');
}
}

银联卡
https://flepeng.github.io/198-常识-银联卡/
作者
Lepeng
发布于
2021年3月12日
许可协议