verifyCardNumber — Iranian card-number validation
import { verifyCardNumber } from "@persian-tools/persian-tools";
// CommonJS
const { verifyCardNumber } = require("@persian-tools/persian-tools");
Public export
verifyCardNumber(digits: number | string): boolean | undefined
Behaviour
import { verifyCardNumber } from "@persian-tools/persian-tools";
verifyCardNumber("6037701689095443"); // true
verifyCardNumber("6037 7016 8909 5443"); // true (whitespace stripped first)
verifyCardNumber(6037701689095443); // true (number accepted)
verifyCardNumber("4111111111111111"); // false (Luhn-valid but non-Iranian BIN)
verifyCardNumber("0000000000000000"); // false (all-zero middle blocks)
verifyCardNumber(null as any); // undefined
Algorithm — two-stage validation
- Falsy →
undefined. Truthy check before anything else. - Whitespace strip + format check.
String(input).replace(/\s/g, "").trim()must match/^\d{16}$/. Anything else →false. - All-zero sub-block check. Reject if
digits.slice(1,11)is all zero, ORdigits.slice(10,16)is all zero. - BIN check. The 6-digit BIN prefix (
slice(0,6)) must be iniranianBankPrefixes(src/modules/verifyCardNumber/constants.ts). Non-Iranian Luhn-valid cards (e.g. test Visa numbers like4111...) are rejected here. - Luhn checksum. Standard mod-10, doubling every second digit from the right; if doubled > 9 subtract 9. Sum % 10 must equal 0.
Quirks
- Persian/Arabic digit input is NOT normalized.
"۶۰۳۷۷۰۱۶۸۹۰۹۵۴۴۳"fails the/^\d{16}$/step →false. RunautoConvertDigitsToENfirst if input may contain them. - Return type is
boolean | undefined.undefinedfor falsy input,booleanafter the algorithm runs. Use=== true, not truthy-check. - Non-Iranian cards are rejected even if Luhn-valid. This is intentional. If you need generic Luhn validation (not BIN-gated), use a third-party Luhn library —
verifyCardNumberis specifically Iranian.
Companion functions
For richer card workflows in the same family:
getBankNameFromCardNumber— given a card number, return the issuing bank's Persian name. See its skill.extractCardNumber— pull card numbers out of free-text input. SeeextractCardNumbersskill.
Pipeline for free-text input
import {
autoConvertDigitsToEN,
verifyCardNumber,
getBankNameFromCardNumber,
} from "@persian-tools/persian-tools";
const check = (raw: string) => {
const norm = autoConvertDigitsToEN(raw.trim());
const valid = verifyCardNumber(norm) === true;
return { valid, bank: valid ? getBankNameFromCardNumber(norm) : null };
};
Common pitfalls
- Adding new BINs: edit
iranianBankPrefixesANDgetBankNameFromCardNumber'scardBanktable together. Out-of-sync tables are a recurring source of "validator accepts the card but lookup says unknown bank" bugs. - For test fixtures, generate cards by combining a known BIN (from
iranianBankPrefixes) with a Luhn-valid tail. Don't paste real card numbers.
References
- Tests:
test/verifyCardNumber.spec.ts - Related:
getBankNameFromCardNumber,extractCardNumbersskills - Algorithm: standard Luhn (mod-10) + Iranian BIN whitelist