2026/10/13 8:54:14

ValidX 银行卡BIN码识别:如何判断银行和卡种

ValidX 银行卡BIN码识别:如何判断银行和卡种 银行卡BIN码识别如何判断银行和卡种 目录引言一、BIN 码是什么二、银行卡号的结构MII BIN 账号 校验位三、如何判断卡组织四、如何判断银行和卡种五、匹配实现四种算法选型六、与 ValidX 结合校验与识别各司其职七、实战一个完整的 BIN 识别工具类八、边界与坑总结引言支付表单、绑卡页面、收银台……几乎每个金融场景都要面对一张银行卡号。输入框里敲下 16 位卡号你往往需要立刻知道三件事卡号格式对不对是不是手滑多敲了一位Luhn 校验位兜底哪个银行发的显示中国工商银行而不是未知卡号什么卡种借记卡还是信用卡Visa、MasterCard 还是银联第 1 件事靠Luhn 算法ValidX 的BankCard已内置第 2、3 件事靠BIN 码识别。本文拆解 BIN 码的原理与实现并给出与 ValidX 组合使用的完整方案。一、BIN 码是什么BINBank Identification Number银行识别码是银行卡号的开头6 位数字用来标识发卡机构。它由 ISO 组织统一分配标准依据是ISO/IEC 7812。概念全称位数作用MIIMajor Industry Identifier主要行业标识符1 位标识发卡行业金融/航空/旅游…IIN / BINIssuer Identification Number发卡机构标识6 位标识具体发卡行个人账号Individual Account Number6-12 位银行内部账号校验位Check Digit1 位Luhn 算法校验看一张真实卡号6228480402564890018的拆解622848 040256489001 8 └──┬───┘ └─────┬──────┘ └───── 校验位Luhn │ └─────────────── 银行内部账号可变长度第 7-18 位 └─────────────────────────── BIN前 6 位 农行金穗借记卡注意MII 是 BIN 的第 1 位而不是独立于 BIN 之外的一段。622848这 6 位整体由银联分配给农行识别时直接取卡号前 6 位查表即可不必单独解析 MII本例首字符6是 MII表示金融业 / 银联已包含在 BIN 内。注BIN 严格定义是前 6 位但 2017 年起 ISO 标准允许 IIN 扩展到 8 位。国内银联卡实际分配以 6 位为主识别时取前 6 位即可覆盖绝大多数场景。为什么是前 6 位因为发卡行给卡号编号时有严格的前缀分配制度——每家银行的每种卡产品都锁定了固定的 BIN 段。比如农行的金穗借记卡固定以622848开头看到这 6 位就知道是农行。二、银行卡号的结构MII BIN 账号 校验位对照上面那张拆解图逐段说清楚① MII第 1 位—— 先判断大行业ISO/IEC 7812 规定了第一位数字的行业归属MII行业代表卡组织1、2航空业UATP3旅游、娱乐Amex34/37、Diners36/384银行业Visa5银行业MasterCard51-556银行业、商品Discover、中国银联629国内分配银联早期 955 段看到4开头就知道大概率是 Visa看到62开头就是银联——这就是先按 MII 分行业再按 BIN 分机构的两级识别思路。② BIN第 2-7 位—— 锁死发卡行和卡种同一家银行的不同 BIN 段代表不同卡产品这是卡种识别的根据。例如工行622202→ 牡丹灵通卡借记卡工行622230→ 牡丹信用卡贷记卡招行622588→ 一卡通借记卡招行439188→ 信用卡Visa③ 账号 校验位 —— 银行内部逻辑识别时不用关心但校验位必须参与格式校验Luhn。三、如何判断卡组织只靠前 2-4 位就能判断卡组织卡品牌这是 BIN 识别最基础的一层publicenumCardBrand{VISA,MASTERCARD,AMEX,DINERS,JCB,UNIONPAY,DISCOVER,UNKNOWN}publicstaticCardBranddetectBrand(StringcardNumber){if(cardNumbernull)returnCardBrand.UNKNOWN;StringpcardNumber.replaceAll([\\s-],);if(p.startsWith(4))returnCardBrand.VISA;// 4 开头if(p.startsWith(51)||p.startsWith(52)||p.startsWith(53)||p.startsWith(54)||p.startsWith(55))returnCardBrand.MASTERCARD;// 51-55if(p.startsWith(34)||p.startsWith(37))returnCardBrand.AMEX;// 34/37if(p.startsWith(36)||p.startsWith(38))returnCardBrand.DINERS;// 36/38if(p.startsWith(35))returnCardBrand.JCB;// 35if(p.startsWith(62))returnCardBrand.UNIONPAY;// 62 银联if(p.startsWith(6011)||p.startsWith(65))returnCardBrand.DISCOVER;returnCardBrand.UNKNOWN;}关键规律只有一条银联 62开头16-19 位。判断银联卡startsWith(62)就够了。四、如何判断银行和卡种卡组织是大标签银行和卡种是精确标签。这层就要靠BIN 前缀表。1. 常见银行 BIN 速查表以下是中国大陆常见银行的借记卡 BIN 前缀示例非完整清单银行常见 BIN 前缀卡种示例中国工商银行622202622200牡丹灵通卡借记卡中国建设银行621700622700436742龙卡通借记卡中国农业银行622848622845金穗借记卡中国银行621661622260622262长城电子借记卡招商银行622588621485一卡通借记卡交通银行622258622259太平洋卡借记卡中国邮政储蓄银行621098622188绿卡借记卡兴业银行622908622909借记卡浦发银行622521622522借记卡中信银行622690622691借记卡民生银行622622622623借记卡光大银行622662622663借记卡平安银行622155622156借记卡⚠️ 表中前缀仅用于理解原理。真实生产环境必须维护完整 BIN 库可用银联官方 BIN 表约数万条记录否则会大量误判。2. 借记卡 vs 信用卡怎么区分卡种识别不能靠卡号长度借记卡和信用卡都是 16/19 位长度无法区分必须查 BIN 表。BIN 表每条记录应包含卡品牌 卡种两个字段// 同一银行的两个 BIN卡种不同622202→ 工商银行/借记卡牡丹灵通卡622230→ 工商银行/贷记卡牡丹信用卡卡号6222021234567890和6222301234567890只差 6 位卡种截然不同——这就是为什么要精确匹配 BIN 而不是匹配前 4 位的原因。3. 匹配规则最长前缀优先622848同时是62、6228、622848的前缀识别时必须取最长匹配否则会把农行卡误判成银联/未知银行// 错误先匹配 62 就停 → 只能判断出银联// 正确遍历所有 BIN取能匹配的最长前缀 → 判断出农行金穗卡五、匹配实现四种算法选型BIN 表规模从小到大的四种实现方案适用规模复杂度特点线性遍历 100 条O(n)最简单正则逐条匹配适合教学HashMap 精确匹配任意O(1)将卡号前 6 位截出直接查表仅适用等长 BIN前缀树Trie 1000 条O(k)k卡号长度天然支持变长前缀取最长匹配工业首选二分查找排序表O(log n)前缀区间排序后二分省内存工业实现推荐 Trie 或 HashMap。银联官方 BIN 表约 4 万条Trie 的查询耗时与 BIN 数量无关只跟卡号长度有关且天然解决最长前缀问题。HashMap 版本最直观BIN 等长时MapString,BankInfobinMapnewHashMap();binMap.put(622848,newBankInfo(中国农业银行,借记卡));// 识别截前 6 位查表StringbincleanValue.substring(0,Math.min(6,cleanValue.length()));BankInfoinfobinMap.get(bin);Trie 版本的核心思路把每个 BIN 作为路径插入字典树查询时沿卡号逐字符向下走记录最后一个命中银行节点的深度即为最长匹配。六、与 ValidX 结合校验与识别各司其职很多人会混淆两件事任务算法ValidX 对应能力卡号格式校验是否可被发卡行接受Luhn 校验位 长度 纯数字BankCard注解 / 链式isBankCard()银行卡种识别哪家银行、什么卡BIN 前缀匹配无内置 BIN 库需自定义实现ValidX 的BankCard只做校验、不做识别看源码BankCardValidator.java就明白publicbooleanisValid(Stringvalue,ConstraintValidatorContextcontext){if(valuenull||value.isEmpty()){returntrue;// 空值处理交给NotNull等其他注解处理}// 移除所有空格和连字符StringcleanValuevalue.replaceAll([\\s-],);// 检查是否全部为数字if(!cleanValue.matches(\\d)){returnfalse;}// 检查长度是否符合银行卡号规范通常为13-19位if(cleanValue.length()13||cleanValue.length()19){returnfalse;}// 使用Luhn算法验证银行卡号returnisLuhnValid(cleanValue);}处理流程清晰去空格连字符 → 纯数字 → 13-19 位 → Luhn。校验通过不代表卡一定存在但校验失败一定无效——这是 BIN 识别的前置关卡。正确的组合链路是先校验、后识别用户输入卡号 │ ▼ ① BankCard / isBankCard() ──失败──→ 提示卡号格式不正确 │ 通过 ▼ ② BIN 识别自定义 ──未知──→ 显示该卡号暂不支持 │ 命中 ▼ ③ 返回 { 银行, 卡种, 卡组织 }校验保证号码本身合法识别负责告诉用户这是什么卡两者互补、缺一不可。七、实战一个完整的 BIN 识别工具类结合 ValidX 链式 API给出一个可直接运行的完整示例importio.github.vipxieliang.validx.chain.ValidX;importjava.util.HashMap;importjava.util.Map;/** * 银行卡识别工具先 Luhn 校验再 BIN 识别 */publicclassBankCardInfoUtil{/** BIN 表前缀 - (银行, 卡种, 卡组织) */privatestaticfinalMapString,CardInfoBIN_TABLEnewHashMap();static{BIN_TABLE.put(622202,newCardInfo(中国工商银行,借记卡,银联));BIN_TABLE.put(622230,newCardInfo(中国工商银行,贷记卡,银联));BIN_TABLE.put(621700,newCardInfo(中国建设银行,借记卡,银联));BIN_TABLE.put(622848,newCardInfo(中国农业银行,借记卡,银联));BIN_TABLE.put(622588,newCardInfo(招商银行,借记卡,银联));BIN_TABLE.put(621098,newCardInfo(中国邮政储蓄银行,借记卡,银联));BIN_TABLE.put(401288,newCardInfo(Visa 测试卡,贷记卡,Visa));BIN_TABLE.put(542523,newCardInfo(MasterCard 测试卡,贷记卡,MasterCard));}publicstaticCardInfoparse(StringcardNumber){// ① 先用 ValidX 做 Luhn 校验不合法直接返回 nullif(!ValidX.init().isBankCard(cardNumber).passed()){returnnull;}// ② 归一化后取前 6 位查 BIN 表StringcleancardNumber.replaceAll([\\s-],);Stringbinclean.substring(0,Math.min(6,clean.length()));returnBIN_TABLE.get(bin);}publicstaticvoidmain(String[]args){// 合法且 BIN 命中System.out.println(parse(6228480402564890018));// 中国农业银行/借记卡/银联// 合法但 BIN 未收录622700 开头表中无此段System.out.println(parse(622700888888888882));// nullLuhn 通过BIN 表没有// Luhn 校验失败末位校验位数字 8 误写成 9System.out.println(parse(6228480402564890019));// null// 长度不足System.out.println(parse(12345));// null}staticclassCardInfo{finalStringbankName,cardType,brand;CardInfo(StringbankName,StringcardType,Stringbrand){this.bankNamebankName;this.cardTypecardType;this.brandbrand;}OverridepublicStringtoString(){returnbankName/cardType/brand;}}}示例输出中国农业银行/借记卡/银联 null null null三个关键点isBankCard()返回ValidX对象passed()判断结果——链式 API 天然适合先校验后识别的编排识别失败返回null前端可降级显示该卡号暂不支持而不是直接拒绝——BIN 表永远在增长别把未收录当非法测试卡号也放进 BIN 表401288Visa 测试、542523MasterCard 测试与 ValidX 测试用例中的卡号呼应便于联调进阶注解方式识别BIN 识别不依赖 Bean Validation但你可以把它包成自定义注解与BankCard叠加使用BankCardBankCardInfo(bank中国农业银行)// 自定义约束Luhn 通过后再查 BINprivateStringcardNumber;自定义注解的ConstraintValidator内部直接调用BankCardInfoUtil.parse()判断银行是否匹配BankCard与BankCardInfo各自职责单一任一失败都会产生违规。八、边界与坑1. BIN 表有时效性必须定期更新新卡种、新 BIN 段每年都在增加一次入库管终身必然误判。生产建议银联官方 BIN 表 每月增量更新 未命中时记录日志供分析。2. 别用卡号长度判断卡种16 位既有借记卡也有信用卡长度判断必然出错。卡种只能查 BIN 表。3. 最长前缀匹配622848同时命中62银联、6228、622848农行必须取最长。用 Trie 或先排长度降序再逐条 startsWith都能解决。4. 空格、连字符必须先行归一化用户输入的卡号常带-或空格ValidX 测试用例5425-2334-3010-9903就是典型识别前必须replaceAll([\\s-], )。注意顺序先归一化再取 BIN。5. 测试卡号与真实卡号4012888888881881Visa 官方测试卡、5555555555554444MasterCard 官方测试卡、4532015112830366常见支付网关测试号等是公开测试卡号Luhn 合法但没有真实银行账户。别把它们当有效卡放行支付BIN 识别应加测试卡号黑名单。6. “未收录不等于非法”BIN 表命中失败应提示暂不支持该卡而非卡号错误。Luhn 校验失败才是卡号错误——两件事信号含义不同错误提示也别混。7. 私有 BIN 与预发卡部分银行内部卡如特定行业卡、内部结算卡的 BIN 不在公开表内。如果业务需要覆盖需向银行或银联申请私有 BIN 段说明。总结层级输入输出算法ValidX 支持卡组织识别卡号前 2-4 位Visa / MasterCard / 银联…MII 前缀规则❌需自定义极简单银行识别卡号前 6 位具体银行BIN 前缀表匹配❌需自定义 BIN 库卡种识别BIN 表记录借记卡 / 贷记卡BIN 表字段查询❌需自定义 BIN 库格式校验整张卡号是否合法Luhn 长度 纯数字✅BankCard/isBankCard()一句话总结BankCard回答这个号码写对没有Luhn 校验ValidX 已内置BIN 识别回答这是什么卡前缀匹配自己维护 BIN 表两者组合就能在绑卡页面上做到输入即识别、错误即拦截的完整体验。BIN 识别本身不复杂——本质就是截前 6 位、查表、取最长前缀真正的工程量在于维护一份完整且新鲜的 BIN 库。