luhn算法仅验证信用卡号数字序列是否符合国际校验规则,需先清洗非数字字符并确保长度13–19位,再执行加权模10运算,结果能被10整除即通过。

用 Luhn 算法校验信用卡号是否格式合法
直接上结论:Laravel 本身不内置信用卡号校验,但可以用 Luhn 算法快速验证输入的卡号是否「可能有效」——它只检查数字序列是否符合国际通用的校验规则,不验证卡是否真实存在或能否扣款。
常见错误现象:1234 5678 9012 3456 这种明显乱填的字符串没被拦住;或者把带空格/短横线的卡号(如 4532-0151-1283-0366)直接喂给算法导致校验失败。
- 先用
preg_replace('/[^0-9]/', '', $input)清洗掉所有非数字字符 - 长度必须在 13–19 位之间(主流卡组织要求),
strlen($cleaned) 19可提前返回 false - 算法本身只需遍历一次字符串,性能无压力,但别在模型
mutator里反复调用(比如每次getCardNumberAttribute都跑一遍) - 推荐封装成独立的
isValidCreditCard(string $number): bool辅助函数,避免逻辑散落在Request或Controller
Laravel FormRequest 中集成 Luhn 校验
别写自定义 Rule 类——太重。直接在 rules() 方法里用闭包校验更轻量、可读性更好。
使用场景:用户提交订单时填写的 card_number 字段需要即时反馈格式问题。
- 在
authorize()后、rules()内直接写闭包:'card_number' => ['required', 'string', function ($attribute, $value, $fail) { if (! app(CardValidator::class)->isValid($value)) { $fail('信用卡号格式不正确。'); } }] - 注意:闭包里的
$value是原始输入,仍需清洗后再传给Luhn校验逻辑 - 不要在
withValidator()里做这个判断——它晚于基本类型校验,错误消息顺序会混乱 - 如果项目已用
laravel-validation-rules包,里面有个CreditCardrule,但底层仍是Luhn,且默认不清洗字符,得自己 wrap 一层
为什么不用正则匹配卡号前缀 + 长度?
因为只靠 ^4[0-9]{12}(?:[0-9]{3})?$(Visa)这类正则,漏掉大量有效卡型,也拦不住伪造的卡号。Luhn 是唯一能跨卡组织、低成本筛掉 90% 明显错误输入的通用手段。
参数差异:Luhn 不关心发卡行、BIN 段、过期时间,也不查 CVV;它只对纯数字字符串做加权模 10 运算。
- 示例:卡号
4532015112830366→ 倒序后偶数位 ×2 → 大于 9 则减 9 → 所有数字求和 → 能被 10 整除即通过 - 兼容性影响:所有 PHP 版本都支持,无扩展依赖;但别在前端 JS 里复现同一套逻辑——浏览器环境精度风险高,应以服务端为准
- 别把
Luhn当风控手段:它防不了复制粘贴的真卡号,也防不了测试卡(如4242 4242 4242 4242),仅作基础输入保护
生产环境容易忽略的边界点
真正上线后出问题的,往往不是算法本身,而是上下文处理失当。
- API 请求体字段名可能叫
credit_card、pan、cardNumber,统一清洗和校验入口前务必确认字段映射,别硬写死card_number - 数据库存储时若做了脱敏(如只存后 4 位),校验必须在脱敏前完成;否则
**** **** **** 1234过不去Luhn - 测试时别只用
4242424242424242,多试几个公开测试号,比如5555555555554444(Mastercard)、378282246310005(American Express) - 日志里严禁记录原始卡号,哪怕只是调试用的
Log::info——Luhn 校验逻辑里一旦有dd()或Log,就是安全红线
最麻烦的其实是前后端对「卡号输入格式」的默契不一致:前端自动加空格,后端没 strip 就直接校验,结果永远 fail。这事得约好清洗时机,而不是在 Luhn 函数里补救。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











