You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

417 lines
19 KiB

4 years ago
  1. (function (root, factory) {
  2. if (typeof define === 'function' && define.amd) {
  3. // AMD. Register as an anonymous module.
  4. define(['exports'], factory);
  5. } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
  6. // CommonJS
  7. factory(exports);
  8. } else {
  9. // Browser globals
  10. factory(root.IBAN = {});
  11. }
  12. }(this, function(exports){
  13. // Array.prototype.map polyfill
  14. // code from https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/map
  15. if (!Array.prototype.map){
  16. Array.prototype.map = function(fun /*, thisArg */){
  17. "use strict";
  18. if (this === void 0 || this === null)
  19. throw new TypeError();
  20. var t = Object(this);
  21. var len = t.length >>> 0;
  22. if (typeof fun !== "function")
  23. throw new TypeError();
  24. var res = new Array(len);
  25. var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
  26. for (var i = 0; i < len; i++)
  27. {
  28. // NOTE: Absolute correctness would demand Object.defineProperty
  29. // be used. But this method is fairly new, and failure is
  30. // possible only if Object.prototype or Array.prototype
  31. // has a property |i| (very unlikely), so use a less-correct
  32. // but more portable alternative.
  33. if (i in t)
  34. res[i] = fun.call(thisArg, t[i], i, t);
  35. }
  36. return res;
  37. };
  38. }
  39. var A = 'A'.charCodeAt(0),
  40. Z = 'Z'.charCodeAt(0);
  41. /**
  42. * Prepare an IBAN for mod 97 computation by moving the first 4 chars to the end and transforming the letters to
  43. * numbers (A = 10, B = 11, ..., Z = 35), as specified in ISO13616.
  44. *
  45. * @param {string} iban the IBAN
  46. * @returns {string} the prepared IBAN
  47. */
  48. function iso13616Prepare(iban) {
  49. iban = iban.toUpperCase();
  50. iban = iban.substr(4) + iban.substr(0,4);
  51. return iban.split('').map(function(n){
  52. var code = n.charCodeAt(0);
  53. if (code >= A && code <= Z){
  54. // A = 10, B = 11, ... Z = 35
  55. return code - A + 10;
  56. } else {
  57. return n;
  58. }
  59. }).join('');
  60. }
  61. /**
  62. * Calculates the MOD 97 10 of the passed IBAN as specified in ISO7064.
  63. *
  64. * @param iban
  65. * @returns {number}
  66. */
  67. function iso7064Mod97_10(iban) {
  68. var remainder = iban,
  69. block;
  70. while (remainder.length > 2){
  71. block = remainder.slice(0, 9);
  72. remainder = parseInt(block, 10) % 97 + remainder.slice(block.length);
  73. }
  74. return parseInt(remainder, 10) % 97;
  75. }
  76. /**
  77. * Parse the BBAN structure used to configure each IBAN Specification and returns a matching regular expression.
  78. * A structure is composed of blocks of 3 characters (one letter and 2 digits). Each block represents
  79. * a logical group in the typical representation of the BBAN. For each group, the letter indicates which characters
  80. * are allowed in this group and the following 2-digits number tells the length of the group.
  81. *
  82. * @param {string} structure the structure to parse
  83. * @returns {RegExp}
  84. */
  85. function parseStructure(structure){
  86. // split in blocks of 3 chars
  87. var regex = structure.match(/(.{3})/g).map(function(block){
  88. // parse each structure block (1-char + 2-digits)
  89. var format,
  90. pattern = block.slice(0, 1),
  91. repeats = parseInt(block.slice(1), 10);
  92. switch (pattern){
  93. case "A": format = "0-9A-Za-z"; break;
  94. case "B": format = "0-9A-Z"; break;
  95. case "C": format = "A-Za-z"; break;
  96. case "F": format = "0-9"; break;
  97. case "L": format = "a-z"; break;
  98. case "U": format = "A-Z"; break;
  99. case "W": format = "0-9a-z"; break;
  100. }
  101. return '([' + format + ']{' + repeats + '})';
  102. });
  103. return new RegExp('^' + regex.join('') + '$');
  104. }
  105. /**
  106. *
  107. * @param iban
  108. * @returns {string}
  109. */
  110. function electronicFormat(iban){
  111. return iban.replace(NON_ALPHANUM, '').toUpperCase();
  112. }
  113. /**
  114. * Create a new Specification for a valid IBAN number.
  115. *
  116. * @param countryCode the code of the country
  117. * @param length the length of the IBAN
  118. * @param structure the structure of the underlying BBAN (for validation and formatting)
  119. * @param example an example valid IBAN
  120. * @constructor
  121. */
  122. function Specification(countryCode, length, structure, example){
  123. this.countryCode = countryCode;
  124. this.length = length;
  125. this.structure = structure;
  126. this.example = example;
  127. }
  128. /**
  129. * Lazy-loaded regex (parse the structure and construct the regular expression the first time we need it for validation)
  130. */
  131. Specification.prototype._regex = function(){
  132. return this._cachedRegex || (this._cachedRegex = parseStructure(this.structure))
  133. };
  134. /**
  135. * Check if the passed iban is valid according to this specification.
  136. *
  137. * @param {String} iban the iban to validate
  138. * @returns {boolean} true if valid, false otherwise
  139. */
  140. Specification.prototype.isValid = function(iban){
  141. return this.length == iban.length
  142. && this.countryCode === iban.slice(0,2)
  143. && this._regex().test(iban.slice(4))
  144. && iso7064Mod97_10(iso13616Prepare(iban)) == 1;
  145. };
  146. /**
  147. * Convert the passed IBAN to a country-specific BBAN.
  148. *
  149. * @param iban the IBAN to convert
  150. * @param separator the separator to use between BBAN blocks
  151. * @returns {string} the BBAN
  152. */
  153. Specification.prototype.toBBAN = function(iban, separator) {
  154. return this._regex().exec(iban.slice(4)).slice(1).join(separator);
  155. };
  156. /**
  157. * Convert the passed BBAN to an IBAN for this country specification.
  158. * Please note that <i>"generation of the IBAN shall be the exclusive responsibility of the bank/branch servicing the account"</i>.
  159. * This method implements the preferred algorithm described in http://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_IBAN_check_digits
  160. *
  161. * @param bban the BBAN to convert to IBAN
  162. * @returns {string} the IBAN
  163. */
  164. Specification.prototype.fromBBAN = function(bban) {
  165. if (!this.isValidBBAN(bban)){
  166. throw new Error('Invalid BBAN');
  167. }
  168. var remainder = iso7064Mod97_10(iso13616Prepare(this.countryCode + '00' + bban)),
  169. checkDigit = ('0' + (98 - remainder)).slice(-2);
  170. return this.countryCode + checkDigit + bban;
  171. };
  172. /**
  173. * Check of the passed BBAN is valid.
  174. * This function only checks the format of the BBAN (length and matching the letetr/number specs) but does not
  175. * verify the check digit.
  176. *
  177. * @param bban the BBAN to validate
  178. * @returns {boolean} true if the passed bban is a valid BBAN according to this specification, false otherwise
  179. */
  180. Specification.prototype.isValidBBAN = function(bban) {
  181. return this.length - 4 == bban.length
  182. && this._regex().test(bban);
  183. };
  184. var countries = {};
  185. function addSpecification(IBAN){
  186. countries[IBAN.countryCode] = IBAN;
  187. }
  188. addSpecification(new Specification("AD", 24, "F04F04A12", "AD1200012030200359100100"));
  189. addSpecification(new Specification("AE", 23, "F03F16", "AE070331234567890123456"));
  190. addSpecification(new Specification("AL", 28, "F08A16", "AL47212110090000000235698741"));
  191. addSpecification(new Specification("AT", 20, "F05F11", "AT611904300234573201"));
  192. addSpecification(new Specification("AZ", 28, "U04A20", "AZ21NABZ00000000137010001944"));
  193. addSpecification(new Specification("BA", 20, "F03F03F08F02", "BA391290079401028494"));
  194. addSpecification(new Specification("BE", 16, "F03F07F02", "BE68539007547034"));
  195. addSpecification(new Specification("BG", 22, "U04F04F02A08", "BG80BNBG96611020345678"));
  196. addSpecification(new Specification("BH", 22, "U04A14", "BH67BMAG00001299123456"));
  197. addSpecification(new Specification("BR", 29, "F08F05F10U01A01", "BR9700360305000010009795493P1"));
  198. addSpecification(new Specification("CH", 21, "F05A12", "CH9300762011623852957"));
  199. addSpecification(new Specification("CR", 22, "F04F14", "CR72012300000171549015"));
  200. addSpecification(new Specification("CY", 28, "F03F05A16", "CY17002001280000001200527600"));
  201. addSpecification(new Specification("CZ", 24, "F04F06F10", "CZ6508000000192000145399"));
  202. addSpecification(new Specification("DE", 22, "F08F10", "DE89370400440532013000"));
  203. addSpecification(new Specification("DK", 18, "F04F09F01", "DK5000400440116243"));
  204. addSpecification(new Specification("DO", 28, "U04F20", "DO28BAGR00000001212453611324"));
  205. addSpecification(new Specification("EE", 20, "F02F02F11F01", "EE382200221020145685"));
  206. addSpecification(new Specification("ES", 24, "F04F04F01F01F10", "ES9121000418450200051332"));
  207. addSpecification(new Specification("FI", 18, "F06F07F01", "FI2112345600000785"));
  208. addSpecification(new Specification("FO", 18, "F04F09F01", "FO6264600001631634"));
  209. addSpecification(new Specification("FR", 27, "F05F05A11F02", "FR1420041010050500013M02606"));
  210. addSpecification(new Specification("GB", 22, "U04F06F08", "GB29NWBK60161331926819"));
  211. addSpecification(new Specification("GE", 22, "U02F16", "GE29NB0000000101904917"));
  212. addSpecification(new Specification("GI", 23, "U04A15", "GI75NWBK000000007099453"));
  213. addSpecification(new Specification("GL", 18, "F04F09F01", "GL8964710001000206"));
  214. addSpecification(new Specification("GR", 27, "F03F04A16", "GR1601101250000000012300695"));
  215. addSpecification(new Specification("GT", 28, "A04A20", "GT82TRAJ01020000001210029690"));
  216. addSpecification(new Specification("HR", 21, "F07F10", "HR1210010051863000160"));
  217. addSpecification(new Specification("HU", 28, "F03F04F01F15F01", "HU42117730161111101800000000"));
  218. addSpecification(new Specification("IE", 22, "U04F06F08", "IE29AIBK93115212345678"));
  219. addSpecification(new Specification("IL", 23, "F03F03F13", "IL620108000000099999999"));
  220. addSpecification(new Specification("IS", 26, "F04F02F06F10", "IS140159260076545510730339"));
  221. addSpecification(new Specification("IT", 27, "U01F05F05A12", "IT60X0542811101000000123456"));
  222. addSpecification(new Specification("KW", 30, "U04A22", "KW81CBKU0000000000001234560101"));
  223. addSpecification(new Specification("KZ", 20, "F03A13", "KZ86125KZT5004100100"));
  224. addSpecification(new Specification("LB", 28, "F04A20", "LB62099900000001001901229114"));
  225. addSpecification(new Specification("LC", 32, "U04F24", "LC07HEMM000100010012001200013015"));
  226. addSpecification(new Specification("LI", 21, "F05A12", "LI21088100002324013AA"));
  227. addSpecification(new Specification("LT", 20, "F05F11", "LT121000011101001000"));
  228. addSpecification(new Specification("LU", 20, "F03A13", "LU280019400644750000"));
  229. addSpecification(new Specification("LV", 21, "U04A13", "LV80BANK0000435195001"));
  230. addSpecification(new Specification("MC", 27, "F05F05A11F02", "MC5811222000010123456789030"));
  231. addSpecification(new Specification("MD", 24, "U02A18", "MD24AG000225100013104168"));
  232. addSpecification(new Specification("ME", 22, "F03F13F02", "ME25505000012345678951"));
  233. addSpecification(new Specification("MK", 19, "F03A10F02", "MK07250120000058984"));
  234. addSpecification(new Specification("MR", 27, "F05F05F11F02", "MR1300020001010000123456753"));
  235. addSpecification(new Specification("MT", 31, "U04F05A18", "MT84MALT011000012345MTLCAST001S"));
  236. addSpecification(new Specification("MU", 30, "U04F02F02F12F03U03", "MU17BOMM0101101030300200000MUR"));
  237. addSpecification(new Specification("NL", 18, "U04F10", "NL91ABNA0417164300"));
  238. addSpecification(new Specification("NO", 15, "F04F06F01", "NO9386011117947"));
  239. addSpecification(new Specification("PK", 24, "U04A16", "PK36SCBL0000001123456702"));
  240. addSpecification(new Specification("PL", 28, "F08F16", "PL61109010140000071219812874"));
  241. addSpecification(new Specification("PS", 29, "U04A21", "PS92PALS000000000400123456702"));
  242. addSpecification(new Specification("PT", 25, "F04F04F11F02", "PT50000201231234567890154"));
  243. addSpecification(new Specification("RO", 24, "U04A16", "RO49AAAA1B31007593840000"));
  244. addSpecification(new Specification("RS", 22, "F03F13F02", "RS35260005601001611379"));
  245. addSpecification(new Specification("SA", 24, "F02A18", "SA0380000000608010167519"));
  246. addSpecification(new Specification("SE", 24, "F03F16F01", "SE4550000000058398257466"));
  247. addSpecification(new Specification("SI", 19, "F05F08F02", "SI56263300012039086"));
  248. addSpecification(new Specification("SK", 24, "F04F06F10", "SK3112000000198742637541"));
  249. addSpecification(new Specification("SM", 27, "U01F05F05A12", "SM86U0322509800000000270100"));
  250. addSpecification(new Specification("ST", 25, "F08F11F02", "ST68000100010051845310112"));
  251. addSpecification(new Specification("TL", 23, "F03F14F02", "TL380080012345678910157"));
  252. addSpecification(new Specification("TN", 24, "F02F03F13F02", "TN5910006035183598478831"));
  253. addSpecification(new Specification("TR", 26, "F05F01A16", "TR330006100519786457841326"));
  254. addSpecification(new Specification("VG", 24, "U04F16", "VG96VPVG0000012345678901"));
  255. addSpecification(new Specification("XK", 20, "F04F10F02", "XK051212012345678906"));
  256. // Angola
  257. addSpecification(new Specification("AO", 25, "F21", "AO69123456789012345678901"));
  258. // Burkina
  259. addSpecification(new Specification("BF", 27, "F23", "BF2312345678901234567890123"));
  260. // Burundi
  261. addSpecification(new Specification("BI", 16, "F12", "BI41123456789012"));
  262. // Benin
  263. addSpecification(new Specification("BJ", 28, "F24", "BJ39123456789012345678901234"));
  264. // Ivory
  265. addSpecification(new Specification("CI", 28, "U01F23", "CI17A12345678901234567890123"));
  266. // Cameron
  267. addSpecification(new Specification("CM", 27, "F23", "CM9012345678901234567890123"));
  268. // Cape Verde
  269. addSpecification(new Specification("CV", 25, "F21", "CV30123456789012345678901"));
  270. // Algeria
  271. addSpecification(new Specification("DZ", 24, "F20", "DZ8612345678901234567890"));
  272. // Iran
  273. addSpecification(new Specification("IR", 26, "F22", "IR861234568790123456789012"));
  274. // Jordan
  275. addSpecification(new Specification("JO", 30, "A04F22", "JO15AAAA1234567890123456789012"));
  276. // Madagascar
  277. addSpecification(new Specification("MG", 27, "F23", "MG1812345678901234567890123"));
  278. // Mali
  279. addSpecification(new Specification("ML", 28, "U01F23", "ML15A12345678901234567890123"));
  280. // Mozambique
  281. addSpecification(new Specification("MZ", 25, "F21", "MZ25123456789012345678901"));
  282. // Quatar
  283. addSpecification(new Specification("QA", 29, "U04A21", "QA30AAAA123456789012345678901"));
  284. // Senegal
  285. addSpecification(new Specification("SN", 28, "U01F23", "SN52A12345678901234567890123"));
  286. // Ukraine
  287. addSpecification(new Specification("UA", 29, "F25", "UA511234567890123456789012345"));
  288. var NON_ALPHANUM = /[^a-zA-Z0-9]/g,
  289. EVERY_FOUR_CHARS =/(.{4})(?!$)/g;
  290. /**
  291. * Utility function to check if a variable is a String.
  292. *
  293. * @param v
  294. * @returns {boolean} true if the passed variable is a String, false otherwise.
  295. */
  296. function isString(v){
  297. return (typeof v == 'string' || v instanceof String);
  298. }
  299. /**
  300. * Check if an IBAN is valid.
  301. *
  302. * @param {String} iban the IBAN to validate.
  303. * @returns {boolean} true if the passed IBAN is valid, false otherwise
  304. */
  305. exports.isValid = function(iban){
  306. if (!isString(iban)){
  307. return false;
  308. }
  309. iban = electronicFormat(iban);
  310. var countryStructure = countries[iban.slice(0,2)];
  311. return !!countryStructure && countryStructure.isValid(iban);
  312. };
  313. /**
  314. * Convert an IBAN to a BBAN.
  315. *
  316. * @param iban
  317. * @param {String} [separator] the separator to use between the blocks of the BBAN, defaults to ' '
  318. * @returns {string|*}
  319. */
  320. exports.toBBAN = function(iban, separator){
  321. if (typeof separator == 'undefined'){
  322. separator = ' ';
  323. }
  324. iban = electronicFormat(iban);
  325. var countryStructure = countries[iban.slice(0,2)];
  326. if (!countryStructure) {
  327. throw new Error('No country with code ' + iban.slice(0,2));
  328. }
  329. return countryStructure.toBBAN(iban, separator);
  330. };
  331. /**
  332. * Convert the passed BBAN to an IBAN for this country specification.
  333. * Please note that <i>"generation of the IBAN shall be the exclusive responsibility of the bank/branch servicing the account"</i>.
  334. * This method implements the preferred algorithm described in http://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_IBAN_check_digits
  335. *
  336. * @param countryCode the country of the BBAN
  337. * @param bban the BBAN to convert to IBAN
  338. * @returns {string} the IBAN
  339. */
  340. exports.fromBBAN = function(countryCode, bban){
  341. var countryStructure = countries[countryCode];
  342. if (!countryStructure) {
  343. throw new Error('No country with code ' + countryCode);
  344. }
  345. return countryStructure.fromBBAN(electronicFormat(bban));
  346. };
  347. /**
  348. * Check the validity of the passed BBAN.
  349. *
  350. * @param countryCode the country of the BBAN
  351. * @param bban the BBAN to check the validity of
  352. */
  353. exports.isValidBBAN = function(countryCode, bban){
  354. if (!isString(bban)){
  355. return false;
  356. }
  357. var countryStructure = countries[countryCode];
  358. return countryStructure && countryStructure.isValidBBAN(electronicFormat(bban));
  359. };
  360. /**
  361. *
  362. * @param iban
  363. * @param separator
  364. * @returns {string}
  365. */
  366. exports.printFormat = function(iban, separator){
  367. if (typeof separator == 'undefined'){
  368. separator = ' ';
  369. }
  370. return electronicFormat(iban).replace(EVERY_FOUR_CHARS, "$1" + separator);
  371. };
  372. exports.electronicFormat = electronicFormat;
  373. /**
  374. * An object containing all the known IBAN specifications.
  375. */
  376. exports.countries = countries;
  377. }));