diff --git a/lib/faker/default/bank.rb b/lib/faker/default/bank.rb index 563ad888e6..e19364474b 100644 --- a/lib/faker/default/bank.rb +++ b/lib/faker/default/bank.rb @@ -158,15 +158,15 @@ def compile_bsb_number end # Calculates the mandatory checksum in 3rd and 4th characters in IBAN format - # source: https://en.wikipedia.org/wiki/International_Bank_Account_Number#Validating_the_IBAN + # source: https://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_IBAN_check_digits def iban_checksum(country_code, account) # Converts letters to numbers according the iban rules, A=10..Z=35 account_to_number = "#{account}#{country_code}00".upcase.chars.map do |d| d =~ /[A-Z]/ ? (d.ord - 55).to_s : d end.join.to_i - # This is answer to (iban_to_num + checksum) % 97 == 1 - checksum = (1 - account_to_number) % 97 + # This is the correct answer to (iban_to_num + checksum) % 97 == 1 + checksum = 98 - (account_to_number % 97) # Use leftpad to make the size always to 2 checksum.to_s.rjust(2, '0') diff --git a/test/faker/default/test_faker_bank.rb b/test/faker/default/test_faker_bank.rb index a4edb159cb..2bfc2a9f9d 100644 --- a/test/faker/default/test_faker_bank.rb +++ b/test/faker/default/test_faker_bank.rb @@ -68,11 +68,43 @@ def test_iban_rand_country assert_match(/^[A-Z]{2}\d{2}[A-Z\d]{10,30}$/, @tester.iban(country_code: nil)) end + def test_iban_checksum + # Sourced IBANs from https://www.iban.com/structure + accounts = { + AL: { account: '202111090000000001234567', check_digit: '35' }, # AL35202111090000000001234567 + BY: { account: 'AKBB10100000002966000000', check_digit: '86' }, # BY86AKBB10100000002966000000 + CY: { account: '002001950000357001234567', check_digit: '21' }, # CY21002001950000357001234567 + DO: { account: 'ACAU00000000000123456789', check_digit: '22' }, # DO22ACAU00000000000123456789 + EG: { account: '0002000156789012345180002', check_digit: '80' }, # EG800002000156789012345180002 + FR: { account: '30006000011234567890189', check_digit: '76' }, # FR7630006000011234567890189 + GB: { account: 'BUKB20201555555555', check_digit: '33' }, # GB33BUKB20201555555555 + HU: { account: '116000060000000012345676', check_digit: '93' }, # HU93116000060000000012345676 + IT: { account: 'X0542811101000000123456', check_digit: '60' }, # IT60X0542811101000000123456 + JO: { account: 'CBJO0000000000001234567890', check_digit: '71' }, # JO71CBJO0000000000001234567890 + KW: { account: 'CBKU0000000000001234560101', check_digit: '81' }, # KW81CBKU0000000000001234560101 + LB: { account: '000700000000123123456123', check_digit: '92' }, # LB92000700000000123123456123 + MD: { account: 'EX000000000001234567', check_digit: '21' }, # MD21EX000000000001234567 + NL: { account: 'ABNA0123456789', check_digit: '02' }, # NL02ABNA0123456789 + PL: { account: '105000997603123456789123', check_digit: '10' }, # PL10105000997603123456789123 + QA: { account: 'QNBA000000000000693123456', check_digit: '54' }, # QA54QNBA000000000000693123456 + RU: { account: '04452560040702810412345678901', check_digit: '02' }, # RU0204452560040702810412345678901 + SC: { account: 'MCBL01031234567890123456USD', check_digit: '74' }, # SC74MCBL01031234567890123456USD + TR: { account: '0010009999901234567890', check_digit: '32' }, # TR320010009999901234567890 + UA: { account: '3052992990004149123456789', check_digit: '90' }, # UA903052992990004149123456789 + VG: { account: 'ABVI0000000123456789', check_digit: '07' } # VG07ABVI0000000123456789 + } + + accounts.each do |country_code, data| + assert_equal data[:check_digit], @tester.send(:iban_checksum, country_code.to_s, data[:account]) + end + end + # Andorra def test_iban_ad account = @tester.iban(country_code: 'ad') assert_equal(24, account.length) assert_match(/^#{IBAN_HEADER}\d{8}[A-Z0-9]{12}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # United Arab Emirates @@ -80,6 +112,7 @@ def test_iban_ae account = @tester.iban(country_code: 'ae') assert_equal(23, account.length) assert_match(/^#{IBAN_HEADER}\d{19}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Albania @@ -87,6 +120,7 @@ def test_iban_al account = @tester.iban(country_code: 'al') assert_equal(28, account.length) assert_match(/^#{IBAN_HEADER}\d{8}[A-Z0-9]{16}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Austria @@ -94,6 +128,7 @@ def test_iban_at account = @tester.iban(country_code: 'at') assert_equal(20, account.length) assert_match(/^#{IBAN_HEADER}\d{16}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Azerbaijan, Republic of @@ -101,6 +136,7 @@ def test_iban_az account = @tester.iban(country_code: 'az') assert_equal(28, account.length) assert_match(/^#{IBAN_HEADER}[A-Z]{4}[A-Z0-9]{20}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Bosnia @@ -108,6 +144,7 @@ def test_iban_ba account = @tester.iban(country_code: 'ba') assert_equal(20, account.length) assert_match(/^#{IBAN_HEADER}\d{16}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Belgium @@ -115,6 +152,7 @@ def test_iban_be account = @tester.iban(country_code: 'be') assert_equal(16, account.length) assert_match(/^#{IBAN_HEADER}\d{12}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Bulgaria @@ -122,6 +160,7 @@ def test_iban_bg account = @tester.iban(country_code: 'bg') assert_equal(22, account.length) assert_match(/^#{IBAN_HEADER}[A-Z]{4}\d{6}[A-Z0-9]{8}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Bahrain @@ -129,6 +168,7 @@ def test_iban_bh account = @tester.iban(country_code: 'bh') assert_equal(22, account.length) assert_match(/^#{IBAN_HEADER}[A-Z]{4}[A-Z0-9]{14}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Brazil @@ -136,6 +176,7 @@ def test_iban_br account = @tester.iban(country_code: 'br') assert_equal(29, account.length) assert_match(/^#{IBAN_HEADER}[0-9]{8}[0-9]{5}[0-9]{10}[A-Z]{1}[A-Z0-9]{1}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Switzerland @@ -143,6 +184,7 @@ def test_iban_ch account = @tester.iban(country_code: 'ch') assert_equal(21, account.length) assert_match(/^#{IBAN_HEADER}\d{5}[A-Z0-9]{12}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Costa Rica @@ -150,6 +192,7 @@ def test_iban_cr account = @tester.iban(country_code: 'cr') assert_equal(22, account.length) assert_match(/^#{IBAN_HEADER}0\d{3}\d{14}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Cyprus @@ -157,6 +200,7 @@ def test_iban_cy account = @tester.iban(country_code: 'cy') assert_equal(28, account.length) assert_match(/^#{IBAN_HEADER}\d{8}[A-Z0-9]{16}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Czech Republic @@ -164,6 +208,7 @@ def test_iban_cz account = @tester.iban(country_code: 'cz') assert_equal(24, account.length) assert_match(/^#{IBAN_HEADER}\d{20}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Germany @@ -171,6 +216,7 @@ def test_iban_de account = @tester.iban(country_code: 'de') assert_equal(22, account.length) assert_match(/^#{IBAN_HEADER}\d{18}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Denmark @@ -178,6 +224,7 @@ def test_iban_dk account = @tester.iban(country_code: 'dk') assert_equal(18, account.length) assert_match(/^#{IBAN_HEADER}\d{14}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Dominican Republic @@ -185,6 +232,7 @@ def test_iban_do account = @tester.iban(country_code: 'do') assert_equal(28, account.length) assert_match(/^#{IBAN_HEADER}[A-Z]{4}\d{20}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Estonia @@ -192,6 +240,7 @@ def test_iban_ee account = @tester.iban(country_code: 'ee') assert_equal(20, account.length) assert_match(/^#{IBAN_HEADER}\d{16}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Spain @@ -199,6 +248,7 @@ def test_iban_es account = @tester.iban(country_code: 'es') assert_equal(24, account.length) assert_match(/^#{IBAN_HEADER}\d{20}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Finland @@ -206,6 +256,7 @@ def test_iban_fi account = @tester.iban(country_code: 'fi') assert_equal(18, account.length) assert_match(/^#{IBAN_HEADER}\d{14}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Faroe Islands @@ -213,20 +264,27 @@ def test_iban_fo account = @tester.iban(country_code: 'fo') assert_equal(18, account.length) assert_match(/^#{IBAN_HEADER}\d{14}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # France def test_iban_fr - account = @tester.iban(country_code: 'fr') - assert_equal(27, account.length) - assert_match(/^#{IBAN_HEADER}\d{10}[A-Z0-9]{11}\d{2}$/, account) + 100.times do + account = @tester.iban(country_code: 'fr') + assert_equal(27, account.length) + assert_match(/^#{IBAN_HEADER}\d{10}[A-Z0-9]{11}\d{2}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' + end end # United Kingdom def test_iban_gb - account = @tester.iban(country_code: 'gb') - assert_equal(22, account.length) - assert_match(/^#{IBAN_HEADER}[A-Z]{4}\d{14}$/, account) + 100.times do + account = @tester.iban(country_code: 'gb') + assert_equal(22, account.length) + assert_match(/^#{IBAN_HEADER}[A-Z]{4}\d{14}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' + end end # Georgia @@ -234,6 +292,7 @@ def test_iban_ge account = @tester.iban(country_code: 'ge') assert_equal(22, account.length) assert_match(/^#{IBAN_HEADER}[A-Z]{2}\d{16}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Gibraltar @@ -241,6 +300,7 @@ def test_iban_gi account = @tester.iban(country_code: 'gi') assert_equal(23, account.length) assert_match(/^#{IBAN_HEADER}[A-Z]{4}[A-Z0-9]{15}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Greenland @@ -248,6 +308,7 @@ def test_iban_gl account = @tester.iban(country_code: 'gl') assert_equal(18, account.length) assert_match(/^#{IBAN_HEADER}\d{14}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Greece @@ -255,6 +316,7 @@ def test_iban_gr account = @tester.iban(country_code: 'gr') assert_equal(27, account.length) assert_match(/^#{IBAN_HEADER}\d{7}[A-Z0-9]{16}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Guatemala @@ -262,6 +324,7 @@ def test_iban_gt account = @tester.iban(country_code: 'gt') assert_equal(28, account.length) assert_match(/^#{IBAN_HEADER}[A-Z0-9]{4}\d{2}\d{2}[A-Z0-9]{16}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Croatia @@ -269,6 +332,7 @@ def test_iban_hr account = @tester.iban(country_code: 'hr') assert_equal(21, account.length) assert_match(/^#{IBAN_HEADER}\d{17}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Hungary @@ -276,6 +340,7 @@ def test_iban_hu account = @tester.iban(country_code: 'hu') assert_equal(28, account.length) assert_match(/^#{IBAN_HEADER}\d{24}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Ireland @@ -283,6 +348,7 @@ def test_iban_ie account = @tester.iban(country_code: 'ie') assert_equal(22, account.length) assert_match(/^#{IBAN_HEADER}[A-Z]{4}\d{14}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Israel @@ -290,6 +356,7 @@ def test_iban_il account = @tester.iban(country_code: 'il') assert_equal(23, account.length) assert_match(/^#{IBAN_HEADER}\d{19}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Iceland @@ -297,6 +364,7 @@ def test_iban_is account = @tester.iban(country_code: 'is') assert_equal(26, account.length) assert_match(/^#{IBAN_HEADER}\d{22}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Italy @@ -304,6 +372,7 @@ def test_iban_it account = @tester.iban(country_code: 'it') assert_equal(27, account.length) assert_match(/^#{IBAN_HEADER}[A-Z]\d{10}[A-Z0-9]{12}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Kuwait @@ -311,6 +380,7 @@ def test_iban_kw account = @tester.iban(country_code: 'kw') assert_equal(30, account.length) assert_match(/^#{IBAN_HEADER}[A-Z]{4}\d{22}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Kazakhstan @@ -318,6 +388,7 @@ def test_iban_kz account = @tester.iban(country_code: 'kz') assert_equal(20, account.length) assert_match(/^#{IBAN_HEADER}[0-9]{3}[A-Z0-9]{13}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Lebanon @@ -325,6 +396,7 @@ def test_iban_lb account = @tester.iban(country_code: 'lb') assert_equal(28, account.length) assert_match(/^#{IBAN_HEADER}\d{4}[A-Z0-9]{20}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Liechtenstein @@ -332,6 +404,7 @@ def test_iban_li account = @tester.iban(country_code: 'li') assert_equal(21, account.length) assert_match(/^#{IBAN_HEADER}\d{5}[A-Z0-9]{12}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Lithuania @@ -339,6 +412,7 @@ def test_iban_lt account = @tester.iban(country_code: 'lt') assert_equal(20, account.length) assert_match(/^#{IBAN_HEADER}\d{16}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Luxembourg @@ -346,6 +420,7 @@ def test_iban_lu account = @tester.iban(country_code: 'lu') assert_equal(20, account.length) assert_match(/^#{IBAN_HEADER}\d{3}[A-Z0-9]{13}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Latvia @@ -353,6 +428,7 @@ def test_iban_lv account = @tester.iban(country_code: 'lv') assert_equal(21, account.length) assert_match(/^#{IBAN_HEADER}[A-Z]{4}[A-Z0-9]{13}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Monaco @@ -360,6 +436,7 @@ def test_iban_mc account = @tester.iban(country_code: 'mc') assert_equal(27, account.length) assert_match(/^#{IBAN_HEADER}\d{10}[A-Z0-9]{11}\d{2}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Moldova @@ -367,14 +444,15 @@ def test_iban_md account = @tester.iban(country_code: 'md') assert_equal(24, account.length) assert_match(/^#{IBAN_HEADER}[A-Z]{2}[A-Z0-9]{18}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Montenegro def test_iban_me account = @tester.iban(country_code: 'me') - assert_equal(22, account.length) assert_match(/^#{IBAN_HEADER}\d{18}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Macedonia @@ -382,6 +460,7 @@ def test_iban_mk account = @tester.iban(country_code: 'mk') assert_equal(19, account.length) assert_match(/^#{IBAN_HEADER}\d{3}[A-Z0-9]{10}\d{2}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Mauritania @@ -389,6 +468,7 @@ def test_iban_mr account = @tester.iban(country_code: 'mr') assert_equal(27, account.length) assert_match(/^#{IBAN_HEADER}\d{23}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Malta @@ -396,6 +476,7 @@ def test_iban_mt account = @tester.iban(country_code: 'mt') assert_equal(31, account.length) assert_match(/^#{IBAN_HEADER}[A-Z]{4}\d{5}[A-Z0-9]{18}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Mauritius @@ -403,6 +484,7 @@ def test_iban_mu account = @tester.iban(country_code: 'mu') assert_equal(30, account.length) assert_match(/^#{IBAN_HEADER}[A-Z]{4}\d{19}[A-Z]{3}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Netherlands @@ -410,6 +492,7 @@ def test_iban_nl account = @tester.iban(country_code: 'nl') assert_equal(18, account.length) assert_match(/^#{IBAN_HEADER}[A-Z]{4}\d{10}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Norway @@ -417,6 +500,7 @@ def test_iban_no account = @tester.iban(country_code: 'no') assert_equal(15, account.length) assert_match(/^#{IBAN_HEADER}\d{11}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Pakistan @@ -424,6 +508,7 @@ def test_iban_pk account = @tester.iban(country_code: 'pk') assert_equal(24, account.length) assert_match(/^#{IBAN_HEADER}[A-Z]{4}[A-Z0-9]{16}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Poland @@ -431,6 +516,7 @@ def test_iban_pl account = @tester.iban(country_code: 'pl') assert_equal(28, account.length) assert_match(/^#{IBAN_HEADER}\d{8}[A-Z0-9]{16}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Palestinian Territory, Occupied @@ -438,6 +524,7 @@ def test_iban_ps account = @tester.iban(country_code: 'ps') assert_equal(29, account.length) assert_match(/^#{IBAN_HEADER}[A-Z]{4}[A-Z0-9]{21}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Portugal @@ -445,6 +532,7 @@ def test_iban_pt account = @tester.iban(country_code: 'pt') assert_equal(25, account.length) assert_match(/^#{IBAN_HEADER}\d{21}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Qatar @@ -452,6 +540,7 @@ def test_iban_qa account = @tester.iban(country_code: 'qa') assert_equal(29, account.length) assert_match(/^#{IBAN_HEADER}[A-Z]{4}[A-Z0-9]{21}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Romania @@ -459,6 +548,7 @@ def test_iban_ro account = @tester.iban(country_code: 'ro') assert_equal(24, account.length) assert_match(/^#{IBAN_HEADER}[A-Z]{4}[A-Z0-9]{16}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Serbia @@ -466,6 +556,7 @@ def test_iban_rs account = @tester.iban(country_code: 'rs') assert_equal(22, account.length) assert_match(/^#{IBAN_HEADER}\d{18}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Saudi Arabia @@ -473,6 +564,7 @@ def test_iban_sa account = @tester.iban(country_code: 'sa') assert_equal(24, account.length) assert_match(/^#{IBAN_HEADER}\d{2}[A-Z0-9]{18}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Sweden @@ -480,6 +572,7 @@ def test_iban_se account = @tester.iban(country_code: 'se') assert_equal(24, account.length) assert_match(/^#{IBAN_HEADER}\d{20}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Slovenia @@ -487,6 +580,7 @@ def test_iban_si account = @tester.iban(country_code: 'si') assert_equal(19, account.length) assert_match(/^#{IBAN_HEADER}\d{15}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Slovakia @@ -494,6 +588,7 @@ def test_iban_sk account = @tester.iban(country_code: 'sk') assert_equal(24, account.length) assert_match(/^#{IBAN_HEADER}\d{20}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # San Marino @@ -501,6 +596,7 @@ def test_iban_sm account = @tester.iban(country_code: 'sm') assert_equal(27, account.length) assert_match(/^#{IBAN_HEADER}[A-Z]\d{10}[A-Z0-9]{12}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Timor-Leste @@ -508,6 +604,7 @@ def test_iban_tl account = @tester.iban(country_code: 'tl') assert_equal(23, account.length) assert_match(/^#{IBAN_HEADER}\d{19}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Tunisia @@ -515,6 +612,7 @@ def test_iban_tn account = @tester.iban(country_code: 'tn') assert_equal(24, account.length) assert_match(/^#{IBAN_HEADER}\d{20}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Turkey @@ -522,6 +620,7 @@ def test_iban_tr account = @tester.iban(country_code: 'tr') assert_equal(26, account.length) assert_match(/^#{IBAN_HEADER}\d{5}[A-Z0-9]{17}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Ukraine @@ -529,6 +628,7 @@ def test_iban_ua account = @tester.iban(country_code: 'ua') assert_equal(29, account.length) assert_match(/^#{IBAN_HEADER}\d{25}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Virgin Islands, British @@ -536,6 +636,7 @@ def test_iban_vg account = @tester.iban(country_code: 'vg') assert_equal(24, account.length) assert_match(/^#{IBAN_HEADER}[A-Z]{4}\d{16}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end # Kosovo, Republic of @@ -543,6 +644,7 @@ def test_iban_xk account = @tester.iban(country_code: 'xk') assert_equal(20, account.length) assert_match(/^#{IBAN_HEADER}\d{16}$/, account) + assert valid_iban_checksum?(account), 'IBAN checksum is invalid' end def test_iban_invalid @@ -550,4 +652,16 @@ def test_iban_invalid @tester.iban(country_code: 'bad') end end + + private + + # https://en.wikipedia.org/wiki/International_Bank_Account_Number#Validating_the_IBAN + def valid_iban_checksum?(iban) + # Check digit should be between 2..98 + return false unless (2..98).include? iban[2, 2].to_i + + iban = (iban[4..] + iban[0..3]).upcase + iban = iban.chars.map { |char| char =~ /[A-Z]/ ? char.ord - 55 : char }.join + iban.to_i % 97 == 1 + end end