import assertString from './util/assertString'; /** * List of country codes with * corresponding IBAN regular expression * Reference: https://en.wikipedia.org/wiki/International_Bank_Account_Number */ var ibanRegexThroughCountryCode = { AD: /^(AD[0-9]{2})\d{8}[A-Z0-9]{12}$/, AE: /^(AE[0-9]{2})\d{3}\d{16}$/, AL: /^(AL[0-9]{2})\d{8}[A-Z0-9]{16}$/, AT: /^(AT[0-9]{2})\d{16}$/, AZ: /^(AZ[0-9]{2})[A-Z0-9]{4}\d{20}$/, BA: /^(BA[0-9]{2})\d{16}$/, BE: /^(BE[0-9]{2})\d{12}$/, BG: /^(BG[0-9]{2})[A-Z]{4}\d{6}[A-Z0-9]{8}$/, BH: /^(BH[0-9]{2})[A-Z]{4}[A-Z0-9]{14}$/, BR: /^(BR[0-9]{2})\d{23}[A-Z]{1}[A-Z0-9]{1}$/, BY: /^(BY[0-9]{2})[A-Z0-9]{4}\d{20}$/, CH: /^(CH[0-9]{2})\d{5}[A-Z0-9]{12}$/, CR: /^(CR[0-9]{2})\d{18}$/, CY: /^(CY[0-9]{2})\d{8}[A-Z0-9]{16}$/, CZ: /^(CZ[0-9]{2})\d{20}$/, DE: /^(DE[0-9]{2})\d{18}$/, DK: /^(DK[0-9]{2})\d{14}$/, DO: /^(DO[0-9]{2})[A-Z]{4}\d{20}$/, DZ: /^(DZ\d{24})$/, EE: /^(EE[0-9]{2})\d{16}$/, EG: /^(EG[0-9]{2})\d{25}$/, ES: /^(ES[0-9]{2})\d{20}$/, FI: /^(FI[0-9]{2})\d{14}$/, FO: /^(FO[0-9]{2})\d{14}$/, FR: /^(FR[0-9]{2})\d{10}[A-Z0-9]{11}\d{2}$/, GB: /^(GB[0-9]{2})[A-Z]{4}\d{14}$/, GE: /^(GE[0-9]{2})[A-Z0-9]{2}\d{16}$/, GI: /^(GI[0-9]{2})[A-Z]{4}[A-Z0-9]{15}$/, GL: /^(GL[0-9]{2})\d{14}$/, GR: /^(GR[0-9]{2})\d{7}[A-Z0-9]{16}$/, GT: /^(GT[0-9]{2})[A-Z0-9]{4}[A-Z0-9]{20}$/, HR: /^(HR[0-9]{2})\d{17}$/, HU: /^(HU[0-9]{2})\d{24}$/, IE: /^(IE[0-9]{2})[A-Z0-9]{4}\d{14}$/, IL: /^(IL[0-9]{2})\d{19}$/, IQ: /^(IQ[0-9]{2})[A-Z]{4}\d{15}$/, IR: /^(IR[0-9]{2})0\d{2}0\d{18}$/, IS: /^(IS[0-9]{2})\d{22}$/, IT: /^(IT[0-9]{2})[A-Z]{1}\d{10}[A-Z0-9]{12}$/, JO: /^(JO[0-9]{2})[A-Z]{4}\d{22}$/, KW: /^(KW[0-9]{2})[A-Z]{4}[A-Z0-9]{22}$/, KZ: /^(KZ[0-9]{2})\d{3}[A-Z0-9]{13}$/, LB: /^(LB[0-9]{2})\d{4}[A-Z0-9]{20}$/, LC: /^(LC[0-9]{2})[A-Z]{4}[A-Z0-9]{24}$/, LI: /^(LI[0-9]{2})\d{5}[A-Z0-9]{12}$/, LT: /^(LT[0-9]{2})\d{16}$/, LU: /^(LU[0-9]{2})\d{3}[A-Z0-9]{13}$/, LV: /^(LV[0-9]{2})[A-Z]{4}[A-Z0-9]{13}$/, MA: /^(MA[0-9]{26})$/, MC: /^(MC[0-9]{2})\d{10}[A-Z0-9]{11}\d{2}$/, MD: /^(MD[0-9]{2})[A-Z0-9]{20}$/, ME: /^(ME[0-9]{2})\d{18}$/, MK: /^(MK[0-9]{2})\d{3}[A-Z0-9]{10}\d{2}$/, MR: /^(MR[0-9]{2})\d{23}$/, MT: /^(MT[0-9]{2})[A-Z]{4}\d{5}[A-Z0-9]{18}$/, MU: /^(MU[0-9]{2})[A-Z]{4}\d{19}[A-Z]{3}$/, MZ: /^(MZ[0-9]{2})\d{21}$/, NL: /^(NL[0-9]{2})[A-Z]{4}\d{10}$/, NO: /^(NO[0-9]{2})\d{11}$/, PK: /^(PK[0-9]{2})[A-Z0-9]{4}\d{16}$/, PL: /^(PL[0-9]{2})\d{24}$/, PS: /^(PS[0-9]{2})[A-Z0-9]{4}\d{21}$/, PT: /^(PT[0-9]{2})\d{21}$/, QA: /^(QA[0-9]{2})[A-Z]{4}[A-Z0-9]{21}$/, RO: /^(RO[0-9]{2})[A-Z]{4}[A-Z0-9]{16}$/, RS: /^(RS[0-9]{2})\d{18}$/, SA: /^(SA[0-9]{2})\d{2}[A-Z0-9]{18}$/, SC: /^(SC[0-9]{2})[A-Z]{4}\d{20}[A-Z]{3}$/, SE: /^(SE[0-9]{2})\d{20}$/, SI: /^(SI[0-9]{2})\d{15}$/, SK: /^(SK[0-9]{2})\d{20}$/, SM: /^(SM[0-9]{2})[A-Z]{1}\d{10}[A-Z0-9]{12}$/, SV: /^(SV[0-9]{2})[A-Z0-9]{4}\d{20}$/, TL: /^(TL[0-9]{2})\d{19}$/, TN: /^(TN[0-9]{2})\d{20}$/, TR: /^(TR[0-9]{2})\d{5}[A-Z0-9]{17}$/, UA: /^(UA[0-9]{2})\d{6}[A-Z0-9]{19}$/, VA: /^(VA[0-9]{2})\d{18}$/, VG: /^(VG[0-9]{2})[A-Z0-9]{4}\d{16}$/, XK: /^(XK[0-9]{2})\d{16}$/ }; /** * Check if the country codes passed are valid using the * ibanRegexThroughCountryCode as a reference * * @param {array} countryCodeArray * @return {boolean} */ function hasOnlyValidCountryCodes(countryCodeArray) { var countryCodeArrayFilteredWithObjectIbanCode = countryCodeArray.filter(function (countryCode) { return !(countryCode in ibanRegexThroughCountryCode); }); if (countryCodeArrayFilteredWithObjectIbanCode.length > 0) { return false; } return true; } /** * Check whether string has correct universal IBAN format * The IBAN consists of up to 34 alphanumeric characters, as follows: * Country Code using ISO 3166-1 alpha-2, two letters * check digits, two digits and * Basic Bank Account Number (BBAN), up to 30 alphanumeric characters. * NOTE: Permitted IBAN characters are: digits [0-9] and the 26 latin alphabetic [A-Z] * * @param {string} str - string under validation * @param {object} options - object to pass the countries to be either whitelisted or blacklisted * @return {boolean} */ function hasValidIbanFormat(str, options) { // Strip white spaces and hyphens var strippedStr = str.replace(/[\s\-]+/gi, '').toUpperCase(); var isoCountryCode = strippedStr.slice(0, 2).toUpperCase(); var isoCountryCodeInIbanRegexCodeObject = (isoCountryCode in ibanRegexThroughCountryCode); if (options.whitelist) { if (!hasOnlyValidCountryCodes(options.whitelist)) { return false; } var isoCountryCodeInWhiteList = options.whitelist.includes(isoCountryCode); if (!isoCountryCodeInWhiteList) { return false; } } if (options.blacklist) { var isoCountryCodeInBlackList = options.blacklist.includes(isoCountryCode); if (isoCountryCodeInBlackList) { return false; } } return isoCountryCodeInIbanRegexCodeObject && ibanRegexThroughCountryCode[isoCountryCode].test(strippedStr); } /** * Check whether string has valid IBAN Checksum * by performing basic mod-97 operation and * the remainder should equal 1 * -- Start by rearranging the IBAN by moving the four initial characters to the end of the string * -- Replace each letter in the string with two digits, A -> 10, B = 11, Z = 35 * -- Interpret the string as a decimal integer and * -- compute the remainder on division by 97 (mod 97) * Reference: https://en.wikipedia.org/wiki/International_Bank_Account_Number * * @param {string} str * @return {boolean} */ function hasValidIbanChecksum(str) { var strippedStr = str.replace(/[^A-Z0-9]+/gi, '').toUpperCase(); // Keep only digits and A-Z latin alphabetic var rearranged = strippedStr.slice(4) + strippedStr.slice(0, 4); var alphaCapsReplacedWithDigits = rearranged.replace(/[A-Z]/g, function (_char) { return _char.charCodeAt(0) - 55; }); var remainder = alphaCapsReplacedWithDigits.match(/\d{1,7}/g).reduce(function (acc, value) { return Number(acc + value) % 97; }, ''); return remainder === 1; } export default function isIBAN(str) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; assertString(str); return hasValidIbanFormat(str, options) && hasValidIbanChecksum(str); } export var locales = Object.keys(ibanRegexThroughCountryCode);