这个正则匹配什么
下面这个组合正则按前缀和长度识别各家主流卡组织:
^(?:4[0-9]{12}(?:[0-9]{3})?
| 5[1-5][0-9]{14}
| 2(?:22[1-9]|2[3-9][0-9]|[3-6][0-9]{2}|7[01][0-9]|720)[0-9]{12}
| 3[47][0-9]{13}
| 6(?:011|5[0-9]{2})[0-9]{12}
| 62[0-9]{14})$
每个分支就是一家卡组织。把它当成一张路由表看,而不是一个浑然一体的规则。
卡组织与它们的前缀
卡号最前面的几位是发卡行识别码(IIN),早先叫银行识别码(BIN)。光看第一位往往能猜到卡组织,更长的前缀才能锁定。上面的正则就是把这套规则编码了进去。
| 卡组织 | 以…开头 | 长度 | 说明 |
|---|---|---|---|
| Visa | 4 | 13 或 16(现在也可能 19) | 那个 (?:[0-9]{3})? 允许 16 位,也兼容老式 13 位。 |
| Mastercard | 51-55 与 2221-2720 | 16 | 2 字头区间是 2016 年加的,老正则会漏。 |
| American Express | 34、37 | 15 | 比别家短,别硬凑 16 位。 |
| Discover | 6011、65 | 16 | 实际还覆盖部分 64x 区间。 |
| 银联 UnionPay | 62 | 16-19 | 中国大陆;长度不固定。 |
长度范围:13 到 19 位
卡号长度并不固定。ISO/IEC 7812 标准允许 13 到 19 位,各家长度还不一样:Amex 是 15 位,Visa 通常是 16 位(但 13 位和现在的 19 位也存在),银联横跨 16 到 19 位。把位数写死成 {16} 的正则会把完全合法的卡拒掉。上面这个正则会按卡组织分别给定长度,正是为了不犯这个错。
正则只能证明形状,证明不了有效
这是能帮你避开真 bug 的关键:正则能确认一个字符串以 4 开头、长度也符合 Visa,却确认不了这个号码是一张真实签发、能用的卡。前缀和长度都是公开常识,谁都能敲出一串符合规则的十六位数字。
真正把"像那么回事的号码"和"有效的号码"区分开的,是校验位,它由 Luhn 算法算出。那是算术,不是模式匹配,正则做不到。
Luhn 算法
Luhn 是个简单的校验和。从最右边的数字(校验位)起,每隔一位把数字乘二;乘完若变成两位数,就减去 9(或把两个数位相加)。把所有数字求和。一个合法号码会让总和是 10 的倍数。
def luhn_ok(num):
digits = [int(ch) for ch in num if ch.isdigit()]
if not digits:
return False
total = 0
parity = len(digits) % 2
for i, d in enumerate(digits):
if i % 2 == parity:
d *= 2
if d > 9:
d -= 9
total += d
return total % 10 == 0
把 Luhn 和卡组织正配合使用:正则告诉你卡组织和应有长度,Luhn 告诉你校验位正确。两者合起来能抓出输错和瞎猜的号码,单用哪一个都不行。
常见错误写法
| 错误写法 | 问题 | 正确写法 |
|---|---|---|
Visa 写成 ^4\d{15}$ | 漏掉了 13 位和 19 位的 Visa 卡。 | ^4[0-9]{12}(?:[0-9]{3})?$ |
| 所有卡组织一个正则,没有 Luhn | 任何看着像的 16 位数字都放行。 | 正则认卡组织 + Luhn 校验。 |
| 忽略 Mastercard 的 2 字头 | 2221-2720 的卡全被拒。 | 把 2 字头分支加进来。 |
| 给 Amex 强凑 16 位 | 所有 15 位 Amex 被拒。 | 按卡组织给长度。 |
| 用正则实现 Luhn | 做不到,Luhn 要算术。 | 用真代码,不是模式。 |
安全:认真对待真实卡号
卡号是敏感信息。几条没有商量余地的规矩:
- 不要在不受你控制的不可信前端里校验真实卡号;客户端校验只管体验,永远不能用来建立信任。
- 绝不要把完整卡号写进日志。要记也最多记最后四位。
- 不要存主账号(PAN),除非你满足 PCI DSS 合规。最安全的卡数据是你从没碰过的数据——用支付机构的令牌化,而不是自己处理原始号码。
- 用各家公开的测试卡号做测试(Visa
4111 1111 1111 1111、Mastercard5500 0000 0000 0004、Amex3782 822463 10005),千万别把真卡拿去跑测试构建。
可以安全使用的测试卡号
开发阶段你需要"长得像卡号"的数据,却又不能碰真卡。各卡组织都公布了固定的测试卡号,它们能通过格式和 Luhn 校验,却被真实授权拒绝。除了上面提到的 Visa、Mastercard、Amex,常见的还有 Discover 的 6011 1111 1111 1117,以及形如 62 开头的银联测试号。把它们在测试里写死,不要随机生成,也别让它们进任何可能摸到真实处理方的构建。在持续集成里跑这些号码,能稳定复现各家卡组织和各种长度分支,不必担心触碰真实资金,也不会因为手滑把测试卡送进生产环境。
什么时候正则就够了
把卡组织正则用在它擅长的地方:用户边输边格式化、挑出对应的卡组织图标、路由到正确的处理方。用 Luhn 在你调用支付接口之前拦掉明显的输入错误。至于"看着靠谱"之外的所有事,都归处理方和发卡行管,不归你的正则管。