通过算法识别和验证信用卡号码
通过算法识别和验证信用卡号码
在本文中,我们将学习如何使用正则表达式从信用卡号识别信用卡类型。然后,我们将了解Luhn算法以及如何使用它来检查信用卡号是否有效。
2. 卡号前几位告诉我们什么?
主账号号码(PAN)是信用卡号的另一个名称。
PAN通常为16位数字长,尽管根据发卡机构的不同,数字的数量可能会有所变化。
目前,发卡机构识别号(IIN)是PAN的前六位数字。它由一个前导数字和五个数字组成。
需要强调的是,这是目前的情况,因为未来可能会发生变化。早在2015年就开始工作,将IIN增加到前八位数字。
让我们看看如何仅通过查看IIN来确定信用卡的类型。
2.1. 首位数字告诉我们什么?
主要行业识别码是卡号的第一位数字。
顾名思义,我们可以通过查看卡号的第一位数字来确定卡所属的行业:
- 1, 2 – 航空公司(等)
- 3 – 旅游和娱乐
- 4, 5 – 银行业务
- 6 – 零售和银行业务
- 7 – 燃料行业
- 8 – 医疗保健和电信
- 9 – 国家机构
- 0 – 其他,为将来保留
现在,让我们看看如何使用IIN识别发卡机构。
2.2. 我们如何从IIN确定卡类型?
自1989年以来,一直有一个国际标准定义了PAN应该如何分配。官方的IIN注册表不是公开可用的。
幸运的是,大多数主要的发卡机构都有广为人知的IIN范围,所以我们可以使用正则表达式将IIN匹配到发卡机构。
在我们查看正则表达式之前,让我们记住IIN范围的列表不断变化。
如果我们正在编写一个应用程序来执行此操作,我们需要考虑我们计划如何保持它最新。
或者,我们可以选择导入几个可用的开源库之一,这些库包含更多的卡类型,并且比我们自己可能管理的更为彻底地测试。例如,使用Stripe API意味着卡处理是由我们管理的。
2.3. 使用正则表达式识别发卡机构
让我们尝试识别Visa卡。
Visa卡号以4开头,因此一个简单的正则表达式来识别Visa卡将是^4[0-9]{0,}$。
请注意,在我们的示例中,我们没有检查数字的长度。到目前为止,我们假设卡号是有效的,因此长度不是我们在这里验证的内容。
可以应用类似的模式来识别其他发卡机构的卡。例如,美国运通卡以34或37开头,所以我们可以使用^3[47][0-9]{0,}$来检测它们。
一些发卡机构有更宽的IIN范围。
我们发现Mastercard的卡通常以51-55开头,然而,在过去的十年中,他们在BIN范围222100-272099内推出了卡片。
这给了我们一个正则表达式^(5[1-5]|222[1-9]|22[3-9]|2[3-6]|27[01]|2720)[0-9]{0,}$。
我们可以使用类似的模式来识别任何已知IIN范围的发卡机构的卡。
3. 卡号中间的数字告诉我们什么?
完整的PAN由三部分组成:发卡机构识别号(IIN),个人账户识别号和校验位。
在IIN和最后一个数字之间,我们有个人账户识别号。
发卡机构定义了这些中间数字的含义,因此它们在不同的发卡机构中将具有不同的含义。
它们指示与卡号相关联的账户类型的信息。
4. 卡号最后一个数字告诉我们什么?
校验位是卡号的最后一个数字。
幸运的是,校验位允许我们使用Luhn算法快速识别无效的卡号。
Hans Peter Luhn在20世纪50年代末开发了Luhn算法。
它用于生成我们今天使用的每一个现代信用卡号,确保每个卡号都具有特定的属性。
Luhn算法使用卡号中的每一位数字,这意味着我们可以使用它轻松地确定给定的卡号是否无效 - 即使只输入了一个数字错误。
这样做意味着我们可以限制不必要的卡处理功能的数量。如果我们要按我们请求的每笔交易收费,这尤其重要!
让我们看看如何在Java应用程序中使用Luhn算法。
4.1. 我们如何使用Luhn算法验证卡号?
让我们经历使用Luhn算法验证给定卡号的步骤。
我们需要获取包括IIN在内的完整信用卡号。
从最右边的数字开始,我们将所有数字相加,对每第二个数字执行特殊步骤。
由于我们从右边开始,我们需要反向循环通过卡号,识别每第二个数字:
for (int i = cardNumber.length() - 1; i >= 0; i--) {
int digit = Integer.parseInt(cardNumber.substring(i, i + 1));
if ((cardNumber.length() - i) % 2 == 0) {
digit = doubleAndSumDigits(digit);
}
sum += digit;
}
对于每第二个数字,我们必须将其加倍,然后再加上剩余的数字。
让我们通过一个短示例(而不是通常的16位数字)来说明这一点 - 让我们检查数字8642是否是有效的卡号。
从最右边的数字开始,我们将加倍每第二个数字:
- 所以对于2(从右边数的第一个数字),没有变化。
- 接下来,我们将第二个数字4加倍,得到8。
- 之后,第三个数字6,没有变化。
- 最后,我们将第四个数字8加倍,得到16。
如果加倍的数字结果是两位数,那么我们需要执行一个额外的步骤以回到一位数 - 我们将这些数字相加以产生一位数,所以对于16,这将是1+6=7。
这一步与减去9相同,所以我们可以在代码中实现它:
private static int doubleAndSumDigits(int digit) {
int ret = digit * 2;
if (ret > 9) {
ret = digit - 9;
}
return ret;
}
最后,以我们的示例为例,让我们将每个数字相加:2 + 8 + 6 + 7 = 23。
如果Luhn算法的结果是10的倍数,那么卡号可能是有效的。
我们将此作为我们检查的结果返回:
return sum % 10 == 0;
在我们的案例中,23不能被10整除,所以8642不是有效的卡号。
在我们的示例中,最后一个数字,2,将是校验位。
对于真正的卡号,校验位是使用Luhn算法计算的。
例如,如果我们将校验位改为9以得到8649,那么Luhn算法的结果是30,可以被10整除,所以8649将通过我们上面的Luhn检查。
4.2. Luhn算法有什么限制吗?
当然,我们的检查并不意味着8649绝对是有效的卡号。尽管它通过了我们的检查,但它可能没有被相关发卡机构发行为实际的卡。
我们唯一能够确定卡号是否真实的方法是询问发卡机构。
Luhn算法仍然为我们提供了一种有用的方式,以确认给定的卡号绝对无效。
然而,有一些边缘情况,我们的Luhn检查将无法检测到卡号中的一个打字错误。
幸运的是,这些边缘情况足够罕见,以至于我们不太可能在现实生活中遇到一个。
最后,Luhn算法不考虑卡号的长度。
实际上,我们知道即使8649通过了我们的Luhn检查,它也太短了,不能是一个真正的信用卡号。
我们可以对卡号的长度进行额外的检查,但必须记住每个发卡机构的号码长度可能会有所不同。
5. 结论
在本文中,我们查看了卡号的每个部分可以告诉我们关于信用卡账户的什么信息。
首先,我们学习了如何通过匹配前导数字上的正则表达式模式来识别发卡机构。接下来,我们了解到我们需要发卡机构特定的信息来理解卡号中间告诉我们关于账户的信息。最后,我们看到了Luhn算法的工作原理,并实现了一些代码来验证给定的卡号。
像往常一样,示例项目可在GitHub上找到。