← 中文资源

简体中文版本

银行卡号 Luhn 校验

使用 Luhn 算法校验卡号,容忍空格与连字符,并可计算缺失的校验位。

阅读英文原文

功能说明

该示例用 Luhn 算法校验银行卡号这类数字串。它先去掉空格与连字符,要求长度为 12 到 19 位且全为数字,再从右往左对每隔一位的数字翻倍并求和,总和能被 10 整除即通过。另一个函数可以在已知前若干位时计算最后一位校验码,适合表单实时提示。

关键代码

normalize 只允许字符串或数字输入,非数字字符会抛错,避免把任意文本静默当成空串。luhnSum 从最右位开始,用布尔变量控制是否翻倍,翻倍后大于 9 就减 9,等价于各位数字相加。cardCheckDigit 在部分号码后补 0 再求和,用 10 减去余数再取模得到校验位。

使用注意

Luhn 只能发现单一位错误和大部分相邻换位错误,不能证明卡号真实存在,也不能替代发卡行网络校验。它不校验卡组织、有效期或 CVV,更不应记录完整卡号。测试卡号可以公开使用,生产日志必须脱敏。长度范围可按业务调整,但不要放宽到空串或一位数字。

验证方式

断言 4242 4242 4242 4242 通过,改掉最后一位后失败。用前十五位 424242424242424 计算校验位应得到 2,拼回后再次通过。测试带空格、连字符与纯数字输入结果一致。传入字母、空串、过短或过长数字分别断言抛错或返回 false。

代码示例

export function isValidCardNumber(input) {
  const digits = normalize(input);
  if (digits.length < 12 || digits.length > 19) return false;
  return luhnSum(digits) % 10 === 0;
}

export function cardCheckDigit(partial) {
  const digits = normalize(partial);
  if (digits.length < 11 || digits.length > 18) {
    throw new RangeError("partial number must contain 11 to 18 digits");
  }
  const remainder = luhnSum(`${digits}0`) % 10;
  return String((10 - remainder) % 10);
}

function normalize(input) {
  if (typeof input !== "string" && typeof input !== "number") {
    throw new TypeError("card number must be a string or number");
  }
  const value = String(input).replace(/[\s-]/g, "");
  if (!/^\d*$/.test(value)) {
    throw new Error("card number may only contain digits, spaces and hyphens");
  }
  return value;
}

function luhnSum(digits) {
  let sum = 0;
  let double = false;
  for (let index = digits.length - 1; index >= 0; index -= 1) {
    let value = Number(digits[index]);
    if (double) {
      value *= 2;
      if (value > 9) value -= 9;
    }
    sum += value;
    double = !double;
  }
  return sum;
}

const sample = "4242 4242 4242 4242";
console.log(isValidCardNumber(sample));
console.log(isValidCardNumber("4242424242424241"));
console.log(cardCheckDigit("424242424242424"));