资讯动态

2024版银行卡BIN对照表:字段结构、版本更新与支付系统落地实践

发布时间:2026/10/15 14:28:53 来源:尧图企业网站定制
简介银行卡BIN数据2024版是一份用于根据银行卡号前6位快速识别发卡银行的SQL资料面向需要接入银行卡信息校验的开发者、金融风控人员及数据分析人员。数据重点补充了郑州银行、建设银行新卡、地方农信社等机构的最新BIN信息适合在支付系统、账务核对或卡Bin查询工具中直接引用。资源包共1个文件为单个SQL文件整体仅32KB导入数据库或执行查询都非常轻量目前已有1719人学习下载。通过该SQL文件读者可以获取到整理好的2024版银行卡BIN清单免去自行收集和清洗的麻烦按卡号前6位匹配即可返回发卡行也可进一步扩展为接口服务或本地查询表。对于需要维护银行编码库、处理交易流水或做卡片归属统计的场景这份数据能节省不少基础数据准备时间。1. 银行卡BIN2024版先搞懂它是什么再决定要不要投入支付系统里最常被忽略却最要命的一张表就是银行卡BIN表。一笔交易进来卡号前6位一读系统要立刻判断它是借记卡还是信用卡、是境内卡还是境外卡、该走哪条清算通道、风险等级怎么给。这6位数字就是BIN全称是银行卡识别码。所谓“2024版”并不是在旧表上改个年份那么简单它意味着过去一年里新增的号段、发卡行名称的变更、卡组织归属的调整以及8位BIN占比不断提升这一系列口径变化都被固化到了同一份快照里。如果手里还拿着2022年的BIN表上线后就会看到一批新卡被当成“未知卡”拦下来。这篇文章不绕弯子直接把BIN的字段结构、版本差异、存储设计、匹配逻辑和最容易翻车的几个坑讲清楚给做支付路由、风控反欺诈、卡管理系统开发的读者一条能照着落地的路径。2. 从6位数字到业务标签BIN的识别逻辑、字段结构与版本差异2.1 卡号结构与BIN识别逻辑前缀、6位/8位与Luhn校验的关系银行卡号不是一串无规则的纯数字。按行业通行标准卡号长度通常在13到19位之间开头6位被定义为基本BIN开头8位则是扩展BIN。6位BIN是最早起的历史标准8位BIN是后为了容纳更多发卡机构和产品线而扩展出来的。无论6位还是8位BIN都是由卡组织统一分配并维护的发卡行自己不能随意给自己造一个前缀。理解了这一点就知道BIN表本质上是“卡号段到业务属性”的映射表而不是银行列表。在开发匹配逻辑时最容易搞混的是“BIN校验”和“卡号校验”。Luhn算法是对完整卡号末尾校验位的验证它把卡号各位按规则加权求和判断最后一位是否合法。这个算法会覆盖BIN所在的前缀位但它只能证明“这串数字结构上像是卡号”不能证明“这个BIN真实存在”。实际项目里我一般先跑Luhn过滤手输错误再拿前缀去BIN表匹配两步缺一不可。如果把Luhn结果当成BIN有效性判断就会放进来一批结构合法但前缀根本不存在的假卡号。8位BIN在2024版数据里占比明显提升这带来一个配对细节同一张卡既可以命中6位BIN也可以命中8位BIN但8位BIN往往比6位BIN更精确。比如一个6位BIN可能对应某银行一个城市的贷记卡产品而它的8位扩展能够区分到具体卡片等级。所以查询时不能只存一个长度也不能只查一种长度需要按8位优先、6位兜底的顺序做最长前缀匹配这个逻辑我会在第4章展开。2.2 一条BIN记录包含什么字段说明与常见取值BIN数据从表面看就是一串数字真正有价值的其实是它关联的标签字段。一份能用于生产环境的BIN表至少应该包含下表这些字段。千万不要只存“BIN发卡行名称”两列那样后续做卡等级筛选、预付卡识别、国别判断都得返工。字段说明常见取值示例bin卡号前缀6位或8位数字622202bin_len前缀长度用于区分6位/8位6 / 8card_org卡组织或清算网络某国际卡组织、某国内卡组织card_type卡类型C信用卡D借记卡P预付卡card_level卡产品等级普卡、金卡、白金卡、钛金卡issuer_name发卡行名称注意不是收单机构某银行股份有限公司country发卡国家/地区ISO 3166-1二位码CN、HK、SGis_prepaid是否预付卡0 / 1is_virtual是否虚拟卡0 / 1version_year该条记录所属数据版本年份2024这几个字段直接决定了你能拿BIN做什么。比如风控里需要识别“高风险预付卡”只靠card_type不够因为有些预付卡也会被标成借记卡必须结合is_prepaid和card_org过滤。再比如做国际交易路由时country字段不能信发卡行名称里的分支机构名要用卡组织分配的国别码。我看到有些团队把issuer_name直接抄进风控报告里当国别判断依据结果一家银行在多个国家都有发卡就出现了明显的精度回退。2.3 2024版更新了什么为什么不能拿旧表凑合先说结论银行卡BIN表的同环比变化没有想象中剧烈但“不变”本身就是风险。2024版与2023版之间主要差异集中在三块。第一新增号段。每年都有新发卡的银行产品、联名卡、数字银行卡拿到新BIN不更新就识别不出这些新卡。第二发卡行名称变更。银行合并重组在行业内并不少见合并前的旧BIN记录需要同步到新主体名称下否则风控名单和商户结算名对不上。第三8位BIN的覆盖范围调整。同一个6位BIN下部分卡组织在这两年把产品线细分到8位如果不更新系统拿6位结果去路由可能识别不出“同一张卡在两个卡组织网络下的不同清算路径”。为什么强调“2024版”因为这版本标志着8位BIN已经成为国内大部分新增卡产品的事实标准。我在某模拟项目里对比过2022版和2024版的抽样数据8位BIN的记录占比从约30%上升到接近一半而这个数字在早几年的版本里增长很慢。如果代码还按“BIN永远6位”去截取卡号等于把数据库里一半的新卡都截成了无对应记录的废前缀。2.4 从公开渠道整理一份2024版BIN数据的本地落地步骤大多数团队拿到的并不是一份结构完美的CSV而是从多个源拼接出来的文本。常见做法是先下载卡组织公开的BIN列表再补充发卡行产品资料最后做字段归一。下面这个清洗脚本是我一贯会用到的起点它能把混有逗号、全角空格、多余标题行的原始文本整理成规整的CSV。import csv import re from pathlib import Path def clean_bin_line(raw_line: str): # 去掉全角空格和隐藏字符统一逗号分隔 line raw_line.strip().replace(\u3000, ).replace( , ) line re.sub(r\s, ,, line) parts [p.strip() for p in line.split(,) if p.strip()] if not parts: return None # 从第一段里提取纯数字避免把标题行洗进来 bin_num .join(ch for ch in parts[0] if ch.isdigit()) if len(bin_num) not in (6, 8) or not bin_num.isdigit(): return None return { bin: bin_num, bin_len: len(bin_num), card_org: parts[1] if len(parts) 1 else , card_type: parts[2] if len(parts) 2 else , issuer_name: parts[3] if len(parts) 3 else , } def load_bin_txt_to_csv(src_path: Path, dst_path: Path): rows [] with src_path.open(r, encodingutf-8, errorsignore) as f: for line in f: row clean_bin_line(line) if row: rows.append(row) with dst_path.open(w, encodingutf-8, newline) as f: writer csv.DictWriter(f, fieldnamesrows[0].keys()) writer.writeheader() writer.writerows(rows)这个脚本的参数和逻辑有几个值得注意的点。一是bin_num的提取用正则过滤掉“622202某银行信用卡”这类文本里的非数字部分避免把中英文混排的行误判成数据。二是对长度做了白名单约束bin_len只允许6或8低于6的通常是残缺行高于8的可能是卡号而非BIN这两种情况都应该过滤而不是强行保留。三是顶部标题行会被自动跳过因为标题行的第一段通常不是纯数字。逻辑说明上这里刻意没有做去重因为原始来源可能存在同一个6位BIN和8位BIN都出现的情况去重需要放到数据库层面按bin bin_len version_year做而不是在这个阶段一把梭。3. 2024版BIN数据怎么选、怎么存口径对比、表结构与加载流程3.1 数据源选型官方口径与商用聚合的取舍BIN数据的来源决定了你能信它多少。我接触过的来源大致分三类。第一类是卡组织定期发布的维护清单权威性最高字段包含BIN、卡组织、卡类型、发卡行覆盖率最全但更新节奏不一定跟得上新发卡速度而且部分字段需要你自己推断。第二类是清算网络下发给成员机构的路由表字段更细有8位BIN和产品等级但通常只对本机构成员开放拿不到完整全局数据。第三类是商用数据服务商做好的聚合版字段丰富、格式规整按年或按季度更新适合中小团队买来直接用但问题在于不同服务商的卡组织归属和卡类型口径并不一致同一串BIN可能查出两个结果。选型时我不建议一刀切只用一个源。成熟一点的团队会以官方发布为主表商用聚合作为对照两个源如果对同一BIN给出了不同的card_type就进入人工复核队列。不要以为官方源一定全对卡组织在更新BIN时会有撤销和重新分配旧号段可能被转给新发卡行这种情况下只看最新快照反而会得出错误结论。2024版数据尤其要注意版本切换时要把“已撤销”和“已转派”两种状态分开记录而不是直接从表里删除旧记录。对比维度官方维护清单商用聚合数据权威性高中高字段完整度中高更新频率季度/不定期月度/季度适配生产难度需要自建清洗低常见坑字段变化静默口径不一致3.2 存储表结构设计BIN表与卡号表必须分离BIN数据和业务卡号数据是两种完全不同的访问模式。BIN表是静态维表一个BIN对应最多一条记录更新频率低卡号表是流水数据动辄百万千万行。把BIN作为字段塞进卡号流水表里会导致后续版本更新时不得不全量刷新流水代价极高。常见做法是建一张维表和一个匹配函数运行时只存卡号前缀的命中结果。下面是我比较常用的BIN维表结构看起来简单但对后续增量维护留了足够空间。CREATE TABLE card_bin ( bin CHAR(8) NOT NULL, bin_len TINYINT NOT NULL DEFAULT 6, card_org VARCHAR(16) NOT NULL, card_type CHAR(1) NOT NULL, card_level VARCHAR(16) NULL, issuer_name VARCHAR(64) NOT NULL, country CHAR(2) NOT NULL DEFAULT CN, is_prepaid TINYINT(1) NOT NULL DEFAULT 0, is_virtual TINYINT(1) NOT NULL DEFAULT 0, version_year SMALLINT NOT NULL, updated_at DATETIME NOT NULL, PRIMARY KEY (bin, bin_len, version_year) ) ENGINE InnoDB DEFAULT CHARSET utf8mb4; ALTER TABLE card_bin ADD INDEX idx_card_bin_prefix (bin);这里的主键设计是有讲究的。把bin和bin_len同时放进主键是为了允许同一个数字串同时存在6位和8位两条记录例如622202和62220212可以共存它们的前缀不同业务含义也不同。version_year也放进主键意味着同一BIN在不同版本里可以保留历史状态避免增量更新时直接覆盖掉上一版的属性方便回溯“当时为什么把这笔交易路由成这样”。3.3 初始加载与增量刷新的流程设计加载BIN表最忌讳“拿新文件直接TRUNCATE再全量插入”因为没有对比就发现不了撤销和变更。我更习惯把加载拆成三步备份当前线上版本导入新版本到临时表做两版差异对比后再切换。具体流程我一般这样走。第一步把2024版新文件清洗后导入card_bin_tmp。第二步用SQL求出新增、变更、撤销三个集合。新增是指bin bin_len在新表里有但旧表没有变更是指同一bin bin_len在新旧两表的issuer_name或card_type不一致撤销是指旧表有但新表没有。第三步对变更集合打印明细让业务侧确认无误后再把临时表切换为主表。-- 新增 SELECT t.bin, t.bin_len, t.card_org, t.issuer_name FROM card_bin_tmp t LEFT JOIN card_bin o ON t.bin o.bin AND t.bin_len o.bin_len AND o.version_year 2023 WHERE o.bin IS NULL; -- 变更 SELECT t.bin, t.bin_len, o.card_type AS old_type, t.card_type AS new_type, o.issuer_name AS old_issuer, t.issuer_name AS new_issuer FROM card_bin_tmp t JOIN card_bin o ON t.bin o.bin AND t.bin_len o.bin_len AND o.version_year 2023 WHERE o.card_type t.card_type OR o.issuer_name t.issuer_name;这些SQL的关键在于version_year过滤条件。如果旧表里没有显式的版本年份字段也可以改用updated_at字段比较只要保证每次更新把生效时间写进去即可。参数上新增集合适合直接全量导入变更集合建议按issuer_name的变更做半自动处理因为部分银行更名属于正常业务动作直接覆盖即可但card_type从借记卡变成信用卡则大概率是数据修正或原号段转派需要人工看一眼。4. 把BIN表用起来本地查询、最长前缀匹配与缓存参数4.1 为什么不能直接做等值匹配很多刚接手BIN表的同学会直接写一句SELECT * FROM card_bin WHERE bin LEFT(card_no, 6)然后发现一部分卡查不到。问题出在两个地方一是卡号可能是16位、18位或19位直接取前6位没问题但如果BIN表里只存了8位BIN你的6位截取自然命中不了二是同一张卡可能同时命中6位和8位BIN等值匹配只能返回一条而且没法保证返回的那条就是更精确的。真正可靠的做法是“最长前缀匹配”。以卡号6222021234567890为例先取前8位62220212查8位BIN表如果命中了就返回没命中就退回前6位622202再查。这样做能保证在8位扩展信息可用时不丢失精度在只有6位通用信息时不至于全盘落空。这里还有个细节不能直接对卡号取前8位然后等值匹配因为如果BIN实际长度为6你取8位去匹配也会失败所以必须做成逐级降位的循环。4.2 一个可落地的查询实现Python SQLite下面这段代码是我在手边没有Redis、只有单机SQLite时常用的一套实现。它把BIN表载入内存做8位优先、6位兜底匹配逻辑足够用于本地调试和中小流量服务。import sqlite3 class BinLookup: def __init__(self, db_path: str): self.conn sqlite3.connect(db_path) self._load_to_cache() def _load_to_cache(self): self.bin_8 {} self.bin_6 {} rows self.conn.execute( SELECT bin, bin_len, card_org, card_type, issuer_name, is_prepaid FROM card_bin WHERE version_year 2024 ).fetchall() for r in rows: if r[1] 8: self.bin_8[r[0]] r elif r[1] 6: self.bin_6[r[0]] r def lookup(self, card_no: str) - dict: prefix8 card_no[:8] if prefix8 in self.bin_8: return self._format(self.bin_8[prefix8]) prefix6 card_no[:6] if prefix6 in self.bin_6: return self._format(self.bin_6[prefix6]) return None staticmethod def _format(row) - dict: return { bin: row[0], card_org: row[2], card_type: row[3], issuer_name: row[4], is_prepaid: row[5], }代码里的_load_to_cache是核心它把2024版记录按bin_len拆成两个字典匹配时先查8位字典再查6位字典。参数上要留意card_no[:8]在卡号不足8位时不会报错但前提是上游已经对卡号长度做过滤。lookup返回的字典字段顺序是固定便于后续接入路由规则。这个实现没有处理卡组织再核对真实生产环境中我还会加一层如果card_org命中结果为未知则用卡号前4位再做一次降级匹配那是少数老BIN的兜底方案。4.3 缓存与性能参数查询量上来后怎么调BIN表本身很小2024版全量数据通常也就几万条单机内存完全放得下真正的并发压力主要来自查询路径上的锁竞争和DB往返。以每秒几百笔交易为例直接查MySQL也能扛但交易量一旦过万就必须把热点查询移到缓存层。我的推荐参数是本地进程内缓存使用LRU容量设成BIN表全量条数的两倍过期时间设为24小时Redis缓存使用Hash结构key按卡组织分片过期时间设为6到12小时。要注意不要对整张BIN表做全局过期因为每天新增的卡交易可能集中在少数新号段上全局过期会导致缓存雪崩。更好的做法是缓存key用bin:2024:{intro_len}:{bin}更新版本时通过切换年份前缀实现一次性热切换。参数配置建议说明本地LRU容量BIN记录数 x 2给热点BIN留出余量本地缓存过期24小时避免长尾数据长期占用Redis缓存过期6小时与增量更新节奏对齐匹配降级策略8位未命中降6位必须可配置开关版本切换方式前缀加版本年份避免覆盖式写缓存5. BIN数据落地避坑指南5个最容易翻车的点5.1 现象2024年新发的卡在系统里被识别成“未知卡”有次压测模拟新发卡交易发现一批卡返回“未识别卡组织”排查后发现线上库的version_year还停留在2022年。新卡号段在旧表里根本没有对应记录于是走入了统一兜底分支被风控当高风险卡拦截。原因不是代码逻辑错了而是数据版本落后。解决方式是把BIN表更新纳入常规发布流程每个季度做一次增量比对并且把“未知BIN”单独计到监控指标里一旦比例超过0.1%立刻告警。5.2 现象同一个卡号前缀查出发卡行矛盾生产环境里出现过同一BIN在库里同时挂着两个不同发卡行的情况。原因有几种数据源在更新时没有清理旧记录导致新旧版本重复导入或者是6位BIN与8位BIN落在同一张表里而查询没有区分bin_len把62220212的8位记录和622202的6位记录混在一起展示。解决方式是在主键上强制bin bin_len version_year查询时不允许只按bin做条件。这个坑最隐蔽因为单条记录看起来都合理只有放到一起才矛盾。5.3 现象境内卡被识别成境外卡有同事反馈某银行卡的清算通道被切到了境外网络查了下发现BIN表里country字段被填成了HK。原因是原始来源里这一个号段是一款双币卡产品发卡行注册地在港澳但卡片实际在境内发行流通。解决方式不能把发卡行的注册地直接当成消费地country要按卡组织分配的“卡片发行地区”字段来判断。修复后还要把该号段加入境内卡白名单避免数据源再次更新时被覆盖。5.4 现象清洗数据时误删了一批有效BIN自建清洗脚本只按前6位去重会导致同一8位BIN的前6位相同但后两位不同的多张卡产品被当成重复行删掉。我见过一个团队用GROUP BY left(bin, 6)做去重结果2024版里新增的8位BIN全被合并成老6位BIN。解决方式去重键必须是bin bin_len6位和8位天然是不同记录另外清洗阶段不要做任何聚合操作只做字段提取和格式归一。5.5 现象被安全合规团队约谈BIN数据虽然不包含完整卡号但通过BIN配合卡号区间可以缩小卡号范围风控系统里如果同时存了BIN和完整卡号的映射关系就构成敏感数据滥用。有项目在BIN表里冗余存了card_no_sample和masked_card_no本意是方便测试结果被判定为批量持有卡信息。解决方式生产环境BIN表禁止保存任何完整卡号或可逆的卡号样本测试数据用生产卡号加盐脱敏后单独放不在BIN维表里留字段。这条看似与BIN技术无关但往往是项目被叫停的直接原因。6. 进阶用校验位自检BIN数据维护一份不依赖供应商的2024版BIN库虽然BIN表是从外部拿来的但你自己完全可以给它加一道质量闸门。这里分享一个我一直在用的小习惯每次拿到新版本先抽一批真实卡号做Luhn校验再按卡组织前缀规则做二次复核而不是直接信数据源。具体做法是准备一个只含掩码样本的测试文件例如将完整卡号替换为“前6位后4位加星号”的格式运行时仅用于校验不落库。对每条样本先用Luhn计算整卡是否合法再用前6位去查库最后比对卡类型和发卡行名称。如果出现Luhn通过但BIN查不到或者BIN查到但卡类型和样本明显不符就把这条记录扔进待复核表。这样做的价值在于当商用数据源缺失某个新号段时你能从样本里先发现而不是等用户投诉。def luhn_ok(card_no: str) - bool: digits [int(c) for c in card_no if c.isdigit()] checksum 0 parity len(digits) % 2 for i, d in enumerate(digits): if i % 2 parity: d * 2 if d 9: d - 9 checksum d return checksum % 10 0这个函数的参数只要一个卡号字符串内部会自动忽略非数字字符。它不判断卡号长度只验证校验位所以先配合长度白名单使用。维护2024版BIN库这件事说到底就是把它当成一个常规数据资产对待版本要留痕变更要对比未知状态要监控。我曾经因为只更新了BIN列表却没有同步更新卡类型字段把一批信用卡当成储蓄卡走了错误的清算路径排查了大半夜。那之后我养成了一个习惯每次换版先跑一遍差异SQL把card_type变更的条目打印出来逐条看再让业务确认一次。这个动作很简单但能挡住绝大多数数据版本引出的疑难杂症希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑