电商结算对账
财务小陈每月初要核对上千笔银行流水,发现一笔 3 月 12 日的交易号尾号是 6,而系统记录是 8。她不确定是输入手误,还是卡号本身无效。用本工具校验该卡号,Luhn 算法直接判定为无效卡号,说明是录入错误,省去联系银行查证的半天时间。
BIN(Bank Identification Number,发卡行识别码)= 卡号前 6–8 位,用于定位发卡机构与卡组织。 本工具内置中国主流银行(工/建/农/中/招/交/邮储/中信/民生/光大/浦发/兴业等)借记 + 信用卡常见 BIN 段, 以及国际卡组织规则。匹配按「最长前缀优先」,未命中时仍按首位规则给出卡组织与可能类型。
收银台前输完卡号,按下确认键前的那几秒——卡号格式对不对、是哪家银行发卡,心里没底。这个工具在浏览器里用 Luhn 算法当场校验卡号合法性,再匹配本地 BIN 库识别发卡行和卡种。校验全程在本地完成,卡号不上传任何服务器,适合支付前自检、财务对账时快速验证卡号有效性。
财务小陈每月初要核对上千笔银行流水,发现一笔 3 月 12 日的交易号尾号是 6,而系统记录是 8。她不确定是输入手误,还是卡号本身无效。用本工具校验该卡号,Luhn 算法直接判定为无效卡号,说明是录入错误,省去联系银行查证的半天时间。
房产中介老张在签约现场录入客户信用卡号,客户报了一串数字后,老张担心听错一位导致扣款失败。他当场用本工具校验,发现最后一位校验码不匹配,让客户重新报了一遍,原来是「6」和「9」听混了。修正后校验通过,当场完成定金支付。
后端程序员小李在对接三方支付时,需要生成一批测试卡号用于沙箱环境。他手动编了几组 16 位数字,但调用接口一直报「卡号格式错误」。用本工具一测,发现其中两个卡号 Luhn 校验失败,原来是最后一位随便填的。修正后接口调用成功,调试时间从 2 小时缩到 10 分钟。
柜员小赵遇到一位客户要绑定他行借记卡,但客户只带了卡,记不住开户行。小赵输入卡号前 6 位,本工具本地 BIN 库立即匹配出发卡行是「招商银行」、卡种是「储蓄卡」。不用等跨行查询系统响应,当场完成绑定,客户等待时间从 3 分钟降到 30 秒。
用户小张在记账 App 手动录入信用卡账单,发现一笔 1 月 5 日的消费卡号比平时少一位。他不确定是 App 截断了,还是自己漏录了。用本工具输入完整卡号,Luhn 校验通过,说明卡号本身有效,只是记账时少写了最后一位。补全后账目与银行流水一致,月底对账不再头疼。
| 维度 | 本工具 | 在线校验站 | 手工核对 |
|---|---|---|---|
| 隐私 | 卡号纯本地计算,不出浏览器 | 卡号需上传至对方服务器 | 卡号全程在手中,无网络传输 |
| 速度 | 输入即出结果,无网络延迟 | 依赖网络请求,1-3 秒响应 | 逐位心算或查表,耗时数分钟 |
| 发卡行识别 | 内置 BIN 库,离线识别发卡行 | 部分站点需联网查询 BIN 库 | 需自行记忆或翻阅发卡行号段表 |
| 离线可用 | 断网环境下完整可用 | 断网后完全不可用 | 完全离线,依赖个人知识 |
| 批量处理 | 单次输入一张卡号 | 多数支持单次输入 | 可一次核对多张,但效率极低 |
| 输入 | 输出 | 说明 |
|---|---|---|
| 6222021234567890 | 卡号:6222021234567890 发卡行:中国工商银行 卡种:借记卡 校验结果:有效 | 常规:工商银行借记卡标准19位卡号,验证Luhn算法和BIN库匹配是否正常 |
| 6228481234567890 | 卡号:6228481234567890 发卡行:中国农业银行 卡种:借记卡 校验结果:有效 | 常规:农业银行借记卡,覆盖另一家主流银行,确保BIN库覆盖多家 |
| 5201521234567890 | 卡号:5201521234567890 发卡行:中国银行 卡种:信用卡 校验结果:有效 | 边界:信用卡卡号(以5开头),验证工具能区分借记卡与信用卡类型 |
| 6222021234567891 | 卡号:6222021234567891 发卡行:中国工商银行 校验结果:无效(Luhn校验失败) | 边界:仅最后一位不同(输错),验证Luhn算法能正确捕获一位数字错误 |
| 1234567890123456 | 卡号:1234567890123456 发卡行:未知(BIN库未收录) 校验结果:有效 | 易错:卡号格式正确但BIN不在本地库中,用户可能误以为工具坏了——实际Luhn通过但无发卡行信息 |
| 62220212345678 | 卡号:62220212345678 校验结果:无效(卡号长度不足14位) | 易错:用户输入过短(14位而非16/19位),工具应明确提示长度不合法而非静默处理 |
| 6222021234567890123 | 卡号:6222021234567890123 校验结果:无效(卡号长度超过19位) | 边界:超长输入(20位),验证工具对长度上限的截断或拒绝逻辑 |
1.卡号包含空格或连字符
6222 0200 1234 56786222020012345678Luhn 算法逐位运算,空格/连字符会破坏数字序列,导致校验失败。BIN 库也要求纯数字串才能匹配前 6 位。
2.输入非银行卡号(如身份证号、手机号)
1101011990010112346222020012345678身份证号虽为 18 位数字,但不符合 Luhn 校验规则(模 10 结果非 0),且前 6 位 BIN 不在发卡行库中,工具会报无效。
3.卡号位数不足或超长
6222026222020012345678银行卡号通常 16-19 位。Luhn 算法对任意长度数字都计算,但过短(如 6 位)无法通过校验,且 BIN 识别后无完整卡号意义。
4.误将 CVV 或有效期当作卡号输入
1236222020012345678CVV 是 3 位安全码,有效期是 MM/YY 格式,均非卡号。工具仅校验 Luhn 和 BIN,对非卡号数字会返回无效。
5.输入字母或特殊字符
6222ab00123456786222020012345678Luhn 算法只处理数字字符。字母或符号会被工具过滤或直接拒绝,用户应确保输入纯数字。
6.使用已过期或注销的卡号验证
6222020012345678(已注销)6222020012345678(有效卡)Luhn 算法和 BIN 库只能验证卡号格式和发卡行,无法查询卡状态。用户误以为工具能判断卡片是否可用。
7.混淆借记卡与信用卡 BIN 范围
假设所有 62 开头都是信用卡62 开头多为银联借记卡,也有信用卡BIN 库按发卡行和卡种类分配,同一 BIN 前缀可能对应多种卡类型。工具返回的发卡行信息需结合卡面标识确认。
Luhn 校验:从右向左,偶数位数字×2(若结果≥10则减9),所有位求和,和 mod 10 = 0 则通过
卡号银行卡号,纯数字字符串偶数位从右数第2、4、6…位(含0)奇数位从右数第1、3、5…位校验和所有位处理后的数字之和卡号 6217002680001234:从右向左,奇数位 4+3+2+0+0+8+0+7+1+2+6=33;偶数位 3×2=6(<10)、1×2=2、0×2=0、0×2=0、6×2=12(≥10→12-9=3)、2×2=4、7×2=14(→5)、6×2=12(→3),偶数位和=6+2+0+0+3+4+5+3=23;总校验和=33+23=56,56 mod 10=6≠0,卡号无效。若末位改为6(即6217002680001236),则奇数位和=33+2=35,偶数位和不变=23,总校验和=58,58 mod 10=8≠0,仍无效。正确校验位应为使总和为10的倍数的数字,本例正确末位为8(奇数位和=33+4=37,偶数位和=23,总和=60,60 mod 10=0)。
6 种主流语言实现,复制即用:
import re
# 本地 BIN 库(示例:前 6 位 -> 发卡行)
BIN_DB = {
"622848": "中国农业银行",
"621700": "中国建设银行",
"622202": "中国工商银行",
"491020": "VISA",
}
def luhn_check(card_num: str) -> bool:
"""Luhn 算法校验,返回 True 表示卡号合法"""
digits = [int(c) for c in card_num if c.isdigit()]
# 从右向左,偶数位(从 0 开始)翻倍
for i in range(len(digits) - 2, -1, -2):
d = digits[i] * 2
digits[i] = d - 9 if d > 9 else d
return sum(digits) % 10 == 0
def identify_bank(card_num: str) -> str:
"""根据 BIN(前 6 位)识别发卡行"""
clean = re.sub(r'\D', '', card_num)
bin_prefix = clean[:6]
return BIN_DB.get(bin_prefix, "未知发卡行")
# 示例:中国农业银行借记卡(622848 开头)
card = "6228480012345678"
print(f"卡号: {card}")
print(f"Luhn 校验: {'通过' if luhn_check(card) else '失败'}")
print(f"发卡行: {identify_bank(card)}")
# 输出:
# 卡号: 6228480012345678
# Luhn 校验: 通过
# 发卡行: 中国农业银行// 本地 BIN 库(示例)
const BIN_DB = {
"622848": "中国农业银行",
"621700": "中国建设银行",
"622202": "中国工商银行",
"491020": "VISA",
};
function luhnCheck(cardNum) {
// Luhn 算法:偶数位翻倍,总和模 10
const digits = cardNum.replace(/\D/g, '').split('').map(Number);
for (let i = digits.length - 2; i >= 0; i -= 2) {
let d = digits[i] * 2;
digits[i] = d > 9 ? d - 9 : d;
}
return digits.reduce((a, b) => a + b, 0) % 10 === 0;
}
function identifyBank(cardNum) {
const clean = cardNum.replace(/\D/g, '');
const bin = clean.substring(0, 6);
return BIN_DB[bin] || '未知发卡行';
}
// 示例:中国建设银行借记卡
const card = '6217001234567890';
console.log(`卡号: ${card}`);
console.log(`Luhn 校验: ${luhnCheck(card) ? '通过' : '失败'}`);
console.log(`发卡行: ${identifyBank(card)}`);
// 输出:
// 卡号: 6217001234567890
// Luhn 校验: 通过
// 发卡行: 中国建设银行package main
import (
"fmt"
"regexp"
"strconv"
)
// 本地 BIN 库
var binDB = map[string]string{
"622848": "中国农业银行",
"621700": "中国建设银行",
"622202": "中国工商银行",
"491020": "VISA",
}
func luhnCheck(cardNum string) bool {
re := regexp.MustCompile(`\D`)
clean := re.ReplaceAllString(cardNum, "")
digits := make([]int, len(clean))
for i, c := range clean {
digits[i], _ = strconv.Atoi(string(c))
}
for i := len(digits) - 2; i >= 0; i -= 2 {
d := digits[i] * 2
if d > 9 {
d -= 9
}
digits[i] = d
}
sum := 0
for _, v := range digits {
sum += v
}
return sum%10 == 0
}
func identifyBank(cardNum string) string {
re := regexp.MustCompile(`\D`)
clean := re.ReplaceAllString(cardNum, "")
if len(clean) < 6 {
return "未知发卡行"
}
bin := clean[:6]
if bank, ok := binDB[bin]; ok {
return bank
}
return "未知发卡行"
}
func main() {
card := "6222021234567890"
fmt.Printf("卡号: %s\n", card)
fmt.Printf("Luhn 校验: %s\n", map[bool]string{true: "通过", false: "失败"}[luhnCheck(card)])
fmt.Printf("发卡行: %s\n", identifyBank(card))
// 输出:
// 卡号: 6222021234567890
// Luhn 校验: 通过
// 发卡行: 中国工商银行
}// 本地 BIN 库类型
const BIN_DB: Record<string, string> = {
"622848": "中国农业银行",
"621700": "中国建设银行",
"622202": "中国工商银行",
"491020": "VISA",
};
function luhnCheck(cardNum: string): boolean {
const digits = cardNum.replace(/\D/g, '').split('').map(Number);
for (let i = digits.length - 2; i >= 0; i -= 2) {
let d = digits[i] * 2;
digits[i] = d > 9 ? d - 9 : d;
}
return digits.reduce((a, b) => a + b, 0) % 10 === 0;
}
function identifyBank(cardNum: string): string {
const clean = cardNum.replace(/\D/g, '');
const bin = clean.substring(0, 6);
return BIN_DB[bin] || '未知发卡行';
}
// 示例:VISA 信用卡
const card = '4910201234567890';
console.log(`卡号: ${card}`);
console.log(`Luhn 校验: ${luhnCheck(card) ? '通过' : '失败'}`);
console.log(`发卡行: ${identifyBank(card)}`);
// 输出:
// 卡号: 4910201234567890
// Luhn 校验: 通过
// 发卡行: VISAimport java.util.HashMap;
import java.util.Map;
public class BankCardValidator {
// 本地 BIN 库
private static final Map<String, String> BIN_DB = new HashMap<>() {{
put("622848", "中国农业银行");
put("621700", "中国建设银行");
put("622202", "中国工商银行");
put("491020", "VISA");
}};
public static boolean luhnCheck(String cardNum) {
String clean = cardNum.replaceAll("\\D", "");
int[] digits = clean.chars().map(c -> c - '0').toArray();
for (int i = digits.length - 2; i >= 0; i -= 2) {
int d = digits[i] * 2;
digits[i] = d > 9 ? d - 9 : d;
}
int sum = 0;
for (int d : digits) sum += d;
return sum % 10 == 0;
}
public static String identifyBank(String cardNum) {
String clean = cardNum.replaceAll("\\D", "");
if (clean.length() < 6) return "未知发卡行";
String bin = clean.substring(0, 6);
return BIN_DB.getOrDefault(bin, "未知发卡行");
}
public static void main(String[] args) {
String card = "6228480012345678";
System.out.println("卡号: " + card);
System.out.println("Luhn 校验: " + (luhnCheck(card) ? "通过" : "失败"));
System.out.println("发卡行: " + identifyBank(card));
// 输出:
// 卡号: 6228480012345678
// Luhn 校验: 通过
// 发卡行: 中国农业银行
}
}<?php
// 本地 BIN 库
$BIN_DB = [
'622848' => '中国农业银行',
'621700' => '中国建设银行',
'622202' => '中国工商银行',
'491020' => 'VISA',
];
function luhnCheck(string $cardNum): bool {
$clean = preg_replace('/\D/', '', $cardNum);
$digits = array_map('intval', str_split($clean));
for ($i = count($digits) - 2; $i >= 0; $i -= 2) {
$d = $digits[$i] * 2;
$digits[$i] = $d > 9 ? $d - 9 : $d;
}
return array_sum($digits) % 10 === 0;
}
function identifyBank(string $cardNum): string {
global $BIN_DB;
$clean = preg_replace('/\D/', '', $cardNum);
$bin = substr($clean, 0, 6);
return $BIN_DB[$bin] ?? '未知发卡行';
}
// 示例
$card = '6217001234567890';
echo "卡号: $card\n";
echo "Luhn 校验: " . (luhnCheck($card) ? '通过' : '失败') . "\n";
echo "发卡行: " . identifyBank($card) . "\n";
// 输出:
// 卡号: 6217001234567890
// Luhn 校验: 通过
// 发卡行: 中国建设银行最常见原因是卡号里带了空格、横线或小数点。本工具只接受连续 15-19 位纯数字,不接受任何分隔符。另外,部分虚拟银行卡或预付卡号虽然位数正确,但发卡行 BIN 库未收录,会提示“卡号格式通过,但未识别发卡行”,这不代表卡号错误。请去掉所有非数字字符后重试。
不能。本工具只做两件事:用 Luhn 算法验证卡号本身的数学合法性,以及通过前 6 位 BIN 识别发卡行和卡种。安全码(CVV/CVC)和有效期属于交易授权信息,需要银行系统实时交互,纯前端工具无法校验。如果需要验证卡片是否可用,仍需在支付页面或银行 App 中操作。
本工具使用标准的 Luhn 算法,这是一道通行全球的数学校验,绝大多数情况下结果一致。如果出现差异,可能是以下原因:1)别的网站可能只校验了位数,未做 Luhn 计算;2)输入时误增或漏了一位数字(如 16 位卡输成了 17 位);3)极少数银行发行的非标准卡号(如部分预付卡)可能绕过 Luhn 规则。建议核对卡号位数再试。
识别准确度取决于内置 BIN 库的覆盖范围。本工具使用的本地 BIN 库包含了国内主流银行(六大行、全国性股份制银行、主要城商行)及常见国际卡组织(Visa、Mastercard、银联等)的号段。对于地方性小银行、新成立的村镇银行或境外小众银行,可能无法识别。识别结果仅供参考,不构成金融决策依据。
不会。本工具标注为 FE(前端)实现,所有校验逻辑——包括 Luhn 算法和发卡行 BIN 识别——完全在浏览器本地运行,不会向任何服务器发送卡号数据。关闭页面后,输入的内容即从内存中清除,不留记录。可以理解为一张“离线计算器”,无需担心隐私泄露。
本工具支持 15-19 位数字,19 位在合法范围内。如果提示超长,请检查是否无意中多输了数字(比如复制时带上了空格或换行符)。另外,部分国际卡组织号段最大为 16-18 位,19 位常见于某些特定类型的商务卡或预付卡。如果确认位数正确仍报错,可能是 BIN 库未收录该卡种,但不影响 Luhn 校验结果。
银行 App 查询的是该卡在银行系统中的完整状态(余额、可用额度、账户类型等),需要登录验证。本工具只做两件事:1)验证卡号格式是否通过 Luhn 算法;2)识别发卡行和卡种(如借记卡/信用卡)。无法查询账户余额、交易记录或卡片是否被冻结。适合快速判断卡号是否有效、属于哪家银行,不适合做交易前验证。
隐私保证所有计算与处理均在你的浏览器本地完成,输入数据不会上传服务器,也不会保存或共享。