File Coverage

blib/lib/Business/IBAN/Database.pm
Criterion Covered Total %
statement 25 25 100.0
branch n/a
condition n/a
subroutine 7 7 100.0
pod 3 3 100.0
total 35 35 100.0


line stmt bran cond sub pod time code
1             package Business::IBAN::Database;
2 3     3   18 use warnings;
  3         7  
  3         115  
3 3     3   128 use strict;
  3         5  
  3         122  
4              
5 3     3   17 use Hash::Util 'lock_hash';
  3         14  
  3         20  
6 3     3   173 use Exporter 'import';
  3         5  
  3         5082  
7             our @EXPORT = qw/iban_db numify_iban mod97/;
8              
9             my %iban_db = (
10             'AD' => {
11             'country' => 'Andorra',
12             'iban_length' => '24',
13             'iban_structure' => 'AD[0-9]{2}[0-9]{4}[0-9]{4}[A-Za-z0-9]{12}',
14             'is_sepa' => 0,
15             'pattern' => 'AD2!n4!n4!n12!c'
16             },
17             'AE' => {
18             'country' => 'United Arab Emirates',
19             'iban_length' => '23',
20             'iban_structure' => 'AE[0-9]{2}[0-9]{3}[0-9]{16}',
21             'is_sepa' => 0,
22             'pattern' => 'AE2!n3!n16!n'
23             },
24             'AL' => {
25             'country' => 'Albania',
26             'iban_length' => '28',
27             'iban_structure' => 'AL[0-9]{2}[0-9]{8}[A-Za-z0-9]{16}',
28             'is_sepa' => 0,
29             'pattern' => 'AL2!n8!n16!c'
30             },
31             'AT' => {
32             'country' => 'Austria',
33             'iban_length' => '20',
34             'iban_structure' => 'AT[0-9]{2}[0-9]{5}[0-9]{11}',
35             'is_sepa' => 1,
36             'pattern' => 'AT2!n5!n11!n'
37             },
38             'AZ' => {
39             'country' => 'Republic of Azerbaijan',
40             'iban_length' => '28',
41             'iban_structure' => 'AZ[0-9]{2}[A-Z]{4}[A-Za-z0-9]{20}',
42             'is_sepa' => 0,
43             'pattern' => 'AZ2!n4!a20!c'
44             },
45             'BA' => {
46             'country' => 'Bosnia and Herzegovina',
47             'iban_length' => '20',
48             'iban_structure' => 'BA[0-9]{2}[0-9]{3}[0-9]{3}[0-9]{8}[0-9]{2}',
49             'is_sepa' => 0,
50             'pattern' => 'BA2!n3!n3!n8!n2!n'
51             },
52             'BE' => {
53             'country' => 'Belgium',
54             'iban_length' => '16',
55             'iban_structure' => 'BE[0-9]{2}[0-9]{3}[0-9]{7}[0-9]{2}',
56             'is_sepa' => 1,
57             'pattern' => 'BE2!n3!n7!n2!n'
58             },
59             'BG' => {
60             'country' => 'Bulgaria',
61             'iban_length' => '22',
62             'iban_structure' => 'BG[0-9]{2}[A-Z]{4}[0-9]{4}[0-9]{2}[A-Za-z0-9]{8}',
63             'is_sepa' => 1,
64             'pattern' => 'BG2!n4!a4!n2!n8!c'
65             },
66             'BH' => {
67             'country' => 'Kingdom of Bahrain',
68             'iban_length' => '22',
69             'iban_structure' => 'BH[0-9]{2}[A-Z]{4}[A-Za-z0-9]{14}',
70             'is_sepa' => 0,
71             'pattern' => 'BH2!n4!a14!c'
72             },
73             'BR' => {
74             'country' => 'Brazil',
75             'iban_length' => '29',
76             'iban_structure' => 'BR[0-9]{2}[0-9]{8}[0-9]{5}[0-9]{10}[A-Z]{1}[A-Za-z0-9]{1}',
77             'is_sepa' => 0,
78             'pattern' => 'BR2!n8!n5!n10!n1!a1!c'
79             },
80             'CH' => {
81             'country' => 'Switzerland',
82             'iban_length' => '21',
83             'iban_structure' => 'CH[0-9]{2}[0-9]{5}[A-Za-z0-9]{12}',
84             'is_sepa' => 1,
85             'pattern' => 'CH2!n5!n12!c'
86             },
87             'CR' => {
88             'country' => 'Costa Rica',
89             'iban_length' => '21',
90             'iban_structure' => 'CR[0-9]{2}[0-9]{3}[0-9]{14}',
91             'is_sepa' => 0,
92             'pattern' => 'CR2!n3!n14!n'
93             },
94             'CY' => {
95             'country' => 'Cyprus',
96             'iban_length' => '28',
97             'iban_structure' => 'CY[0-9]{2}[0-9]{3}[0-9]{5}[A-Za-z0-9]{16}',
98             'is_sepa' => 1,
99             'pattern' => 'CY2!n3!n5!n16!c'
100             },
101             'CZ' => {
102             'country' => 'Czech Republic',
103             'iban_length' => '24',
104             'iban_structure' => 'CZ[0-9]{2}[0-9]{4}[0-9]{6}[0-9]{10}',
105             'is_sepa' => 1,
106             'pattern' => 'CZ2!n4!n6!n10!n'
107             },
108             'DE' => {
109             'country' => 'Germany',
110             'iban_length' => '22',
111             'iban_structure' => 'DE[0-9]{2}[0-9]{8}[0-9]{10}',
112             'is_sepa' => 1,
113             'pattern' => 'DE2!n8!n10!n'
114             },
115             'DK' => {
116             'country' => 'Denmark',
117             'iban_length' => '18',
118             'iban_structure' => 'DK[0-9]{2}[0-9]{4}[0-9]{9}[0-9]{1}',
119             'is_sepa' => 1,
120             'pattern' => 'DK2!n4!n9!n1!n FO2!n4!n9!n1!n GL2!n4!n9!n1!n'
121             },
122             'DO' => {
123             'country' => 'Dominican Republic',
124             'iban_length' => '28',
125             'iban_structure' => 'DO[0-9]{2}[A-Za-z0-9]{4}[0-9]{20}',
126             'is_sepa' => 0,
127             'pattern' => 'DO2!n4!c20!n'
128             },
129             'EE' => {
130             'country' => 'Estonia',
131             'iban_length' => '20',
132             'iban_structure' => 'EE[0-9]{2}[0-9]{2}[0-9]{2}[0-9]{11}[0-9]{1}',
133             'is_sepa' => 1,
134             'pattern' => 'EE2!n2!n2!n11!n1!n'
135             },
136             'ES' => {
137             'country' => 'Spain',
138             'iban_length' => '24',
139             'iban_structure' => 'ES[0-9]{2}[0-9]{4}[0-9]{4}[0-9]{1}[0-9]{1}[0-9]{10}',
140             'is_sepa' => 1,
141             'pattern' => 'ES2!n4!n4!n1!n1!n10!n'
142             },
143             'FI' => {
144             'country' => 'Finland',
145             'iban_length' => '18',
146             'iban_structure' => 'FI[0-9]{2}[0-9]{6}[0-9]{7}[0-9]{1}',
147             'is_sepa' => 1,
148             'pattern' => 'FI2!n6!n7!n1!n'
149             },
150             'FO' => {
151             'country' => 'Denmark',
152             'iban_length' => '18',
153             'iban_structure' => 'FO[0-9]{2}[0-9]{4}[0-9]{9}[0-9]{1}',
154             'is_sepa' => 1,
155             'pattern' => 'DK2!n4!n9!n1!n FO2!n4!n9!n1!n GL2!n4!n9!n1!n'
156             },
157             'FR' => {
158             'country' => 'France',
159             'iban_length' => '27',
160             'iban_structure' => 'FR[0-9]{2}[0-9]{5}[0-9]{5}[A-Za-z0-9]{11}[0-9]{2}',
161             'is_sepa' => 1,
162             'pattern' => 'FR2!n5!n5!n11!c2!n'
163             },
164             'GB' => {
165             'country' => 'United Kingdom',
166             'iban_length' => '22',
167             'iban_structure' => 'GB[0-9]{2}[A-Z]{4}[0-9]{6}[0-9]{8}',
168             'is_sepa' => 1,
169             'pattern' => 'GB2!n4!a6!n8!n'
170             },
171             'GE' => {
172             'country' => 'Georgia',
173             'iban_length' => '22',
174             'iban_structure' => 'GE[0-9]{2}[A-Z]{2}[0-9]{16}',
175             'is_sepa' => 0,
176             'pattern' => 'GE2!n2!a16!n'
177             },
178             'GI' => {
179             'country' => 'Gibraltar',
180             'iban_length' => '23',
181             'iban_structure' => 'GI[0-9]{2}[A-Z]{4}[A-Za-z0-9]{15}',
182             'is_sepa' => 1,
183             'pattern' => 'GI2!n4!a15!c'
184             },
185             'GL' => {
186             'country' => 'Denmark',
187             'iban_length' => '18',
188             'iban_structure' => 'GL[0-9]{2}[0-9]{4}[0-9]{9}[0-9]{1}',
189             'is_sepa' => 1,
190             'pattern' => 'DK2!n4!n9!n1!n FO2!n4!n9!n1!n GL2!n4!n9!n1!n'
191             },
192             'GR' => {
193             'country' => 'Greece',
194             'iban_length' => '27',
195             'iban_structure' => 'GR[0-9]{2}[0-9]{3}[0-9]{4}[A-Za-z0-9]{16}',
196             'is_sepa' => 1,
197             'pattern' => 'GR2!n3!n4!n16!c'
198             },
199             'GT' => {
200             'country' => 'Guatemala',
201             'iban_length' => '28',
202             'iban_structure' => 'GT[0-9]{2}[A-Za-z0-9]{4}[A-Za-z0-9]{20}',
203             'is_sepa' => 0,
204             'pattern' => 'GT2!n4!c20!c'
205             },
206             'HR' => {
207             'country' => 'Croatia',
208             'iban_length' => '21',
209             'iban_structure' => 'HR[0-9]{2}[0-9]{7}[0-9]{10}',
210             'is_sepa' => 1,
211             'pattern' => 'HR2!n7!n10!n'
212             },
213             'HU' => {
214             'country' => 'Hungary',
215             'iban_length' => '28',
216             'iban_structure' => 'HU[0-9]{2}[0-9]{3}[0-9]{4}[0-9]{1}[0-9]{15}[0-9]{1}',
217             'is_sepa' => 1,
218             'pattern' => 'HU2!n3!n4!n1!n15!n1!n'
219             },
220             'IE' => {
221             'country' => 'Ireland',
222             'iban_length' => '22',
223             'iban_structure' => 'IE[0-9]{2}[A-Z]{4}[0-9]{6}[0-9]{8}',
224             'is_sepa' => 1,
225             'pattern' => 'IE2!n4!a6!n8!n'
226             },
227             'IL' => {
228             'country' => 'Israel',
229             'iban_length' => '23',
230             'iban_structure' => 'IL[0-9]{2}[0-9]{3}[0-9]{3}[0-9]{13}',
231             'is_sepa' => 0,
232             'pattern' => 'IL2!n3!n3!n13!n'
233             },
234             'IS' => {
235             'country' => 'Iceland',
236             'iban_length' => '26',
237             'iban_structure' => 'IS[0-9]{2}[0-9]{4}[0-9]{2}[0-9]{6}[0-9]{10}',
238             'is_sepa' => 1,
239             'pattern' => 'IS2!n4!n2!n6!n10!n'
240             },
241             'IT' => {
242             'country' => 'Italy',
243             'iban_length' => '27',
244             'iban_structure' => 'IT[0-9]{2}[A-Z]{1}[0-9]{5}[0-9]{5}[A-Za-z0-9]{12}',
245             'is_sepa' => 1,
246             'pattern' => 'IT2!n1!a5!n5!n12!c'
247             },
248             'KW' => {
249             'country' => 'Kuwait',
250             'iban_length' => '30',
251             'iban_structure' => 'KW[0-9]{2}[A-Z]{4}[A-Za-z0-9]{22}',
252             'is_sepa' => 0,
253             'pattern' => 'KW2!n4!a22!c'
254             },
255             'KZ' => {
256             'country' => 'Kazakhstan',
257             'iban_length' => '20',
258             'iban_structure' => 'KZ[0-9]{2}[0-9]{3}[A-Za-z0-9]{13}',
259             'is_sepa' => 0,
260             'pattern' => 'KZ2!n3!n13!c'
261             },
262             'LB' => {
263             'country' => 'Lebanon',
264             'iban_length' => '28',
265             'iban_structure' => 'LB[0-9]{2}[0-9]{4}[A-Za-z0-9]{20}',
266             'is_sepa' => 0,
267             'pattern' => 'LB2!n4!n20!c'
268             },
269             'LI' => {
270             'country' => 'Liechtenstein (Principality of)',
271             'iban_length' => '21',
272             'iban_structure' => 'LI[0-9]{2}[0-9]{5}[A-Za-z0-9]{12}',
273             'is_sepa' => 1,
274             'pattern' => 'LI2!n5!n12!c'
275             },
276             'LT' => {
277             'country' => 'Lithuania',
278             'iban_length' => '20',
279             'iban_structure' => 'LT[0-9]{2}[0-9]{5}[0-9]{11}',
280             'is_sepa' => 1,
281             'pattern' => 'LT2!n5!n11!n'
282             },
283             'LU' => {
284             'country' => 'Luxembourg',
285             'iban_length' => '20',
286             'iban_structure' => 'LU[0-9]{2}[0-9]{3}[A-Za-z0-9]{13}',
287             'is_sepa' => 1,
288             'pattern' => 'LU2!n3!n13!c'
289             },
290             'LV' => {
291             'country' => 'Latvia',
292             'iban_length' => '21',
293             'iban_structure' => 'LV[0-9]{2}[A-Z]{4}[A-Za-z0-9]{13}',
294             'is_sepa' => 1,
295             'pattern' => 'LV2!n4!a13!c'
296             },
297             'MC' => {
298             'country' => 'Monaco',
299             'iban_length' => '27',
300             'iban_structure' => 'MC[0-9]{2}[0-9]{5}[0-9]{5}[A-Za-z0-9]{11}[0-9]{2}',
301             'is_sepa' => 1,
302             'pattern' => 'MC2!n5!n5!n11!c2!n'
303             },
304             'MD' => {
305             'country' => 'Republic of Moldova',
306             'iban_length' => '24',
307             'iban_structure' => 'MD[0-9]{2}[A-Za-z0-9]{20}',
308             'is_sepa' => 0,
309             'pattern' => 'MD2!n20!c'
310             },
311             'ME' => {
312             'country' => 'Montenegro',
313             'iban_length' => '22',
314             'iban_structure' => 'ME[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2}',
315             'is_sepa' => 0,
316             'pattern' => 'ME2!n3!n13!n2!n'
317             },
318             'MK' => {
319             'country' => 'Macedonia',
320             'iban_length' => '19',
321             'iban_structure' => 'MK[0-9]{2}[0-9]{3}[A-Za-z0-9]{10}[0-9]{2}',
322             'is_sepa' => 0,
323             'pattern' => 'MK2!n3!n10!c2!n'
324             },
325             'MR' => {
326             'country' => 'Mauritania',
327             'iban_length' => '27',
328             'iban_structure' => 'MR13[0-9]{5}[0-9]{5}[0-9]{11}[0-9]{2}',
329             'is_sepa' => 0,
330             'pattern' => 'MR135!n5!n11!n2!n'
331             },
332             'MT' => {
333             'country' => 'Malta',
334             'iban_length' => '31',
335             'iban_structure' => 'MT[0-9]{2}[A-Z]{4}[0-9]{5}[A-Za-z0-9]{18}',
336             'is_sepa' => 1,
337             'pattern' => 'MT2!n4!a5!n18!c'
338             },
339             'MU' => {
340             'country' => 'Mauritius',
341             'iban_length' => '30',
342             'iban_structure' => 'MU[0-9]{2}[A-Z]{4}[0-9]{2}[0-9]{2}[0-9]{12}[0-9]{3}[A-Z]{3}',
343             'is_sepa' => 0,
344             'pattern' => 'MU2!n4!a2!n2!n12!n3!n3!a'
345             },
346             'NL' => {
347             'country' => 'The Netherlands',
348             'iban_length' => '18',
349             'iban_structure' => 'NL[0-9]{2}[A-Z]{4}[0-9]{10}',
350             'is_sepa' => 1,
351             'pattern' => 'NL2!n4!a10!n'
352             },
353             'NO' => {
354             'country' => 'Norway',
355             'iban_length' => '15',
356             'iban_structure' => 'NO[0-9]{2}[0-9]{4}[0-9]{6}[0-9]{1}',
357             'is_sepa' => 1,
358             'pattern' => 'NO2!n4!n6!n1!n'
359             },
360             'PK' => {
361             'country' => 'Pakistan',
362             'iban_length' => '24',
363             'iban_structure' => 'PK[0-9]{2}[A-Z]{4}[A-Za-z0-9]{16}',
364             'is_sepa' => 0,
365             'pattern' => 'PK2!n4!a16!c'
366             },
367             'PL' => {
368             'country' => 'Poland',
369             'iban_length' => '28',
370             'iban_structure' => 'PL[0-9]{2}[0-9]{8}[0-9]{16}',
371             'is_sepa' => 1,
372             'pattern' => 'PL2!n8!n16!n'
373             },
374             'PS' => {
375             'country' => 'Palestine, State of',
376             'iban_length' => '29',
377             'iban_structure' => 'PS[0-9]{2}[A-Z]{4}[A-Za-z0-9]{21}',
378             'is_sepa' => 0,
379             'pattern' => 'PS2!n4!a21!c'
380             },
381             'PT' => {
382             'country' => 'Portugal',
383             'iban_length' => '25',
384             'iban_structure' => 'PT[0-9]{2}[0-9]{4}[0-9]{4}[0-9]{11}[0-9]{2}',
385             'is_sepa' => 1,
386             'pattern' => 'PT2!n4!n4!n11!n2!n'
387             },
388             'QA' => {
389             'country' => 'Qatar',
390             'iban_length' => '29',
391             'iban_structure' => 'QA[0-9]{2}[A-Z]{4}[A-Za-z0-9]{21}',
392             'is_sepa' => 0,
393             'pattern' => 'QA2!n4!a21!c'
394             },
395             'RO' => {
396             'country' => 'Romania',
397             'iban_length' => '24',
398             'iban_structure' => 'RO[0-9]{2}[A-Z]{4}[A-Za-z0-9]{16}',
399             'is_sepa' => 1,
400             'pattern' => 'RO2!n4!a16!c'
401             },
402             'RS' => {
403             'country' => 'Serbia',
404             'iban_length' => '22',
405             'iban_structure' => 'RS[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2}',
406             'is_sepa' => 0,
407             'pattern' => 'RS2!n3!n13!n2!n'
408             },
409             'SA' => {
410             'country' => 'Saudi Arabia',
411             'iban_length' => '24',
412             'iban_structure' => 'SA[0-9]{2}[0-9]{2}[A-Za-z0-9]{18}',
413             'is_sepa' => 0,
414             'pattern' => 'SA2!n2!n18!c'
415             },
416             'SE' => {
417             'country' => 'Sweden',
418             'iban_length' => '24',
419             'iban_structure' => 'SE[0-9]{2}[0-9]{3}[0-9]{16}[0-9]{1}',
420             'is_sepa' => 1,
421             'pattern' => 'SE2!n3!n16!n1!n'
422             },
423             'SI' => {
424             'country' => 'Slovenia',
425             'iban_length' => '19',
426             'iban_structure' => 'SI[0-9]{2}[0-9]{5}[0-9]{8}[0-9]{2}',
427             'is_sepa' => 1,
428             'pattern' => 'SI2!n5!n8!n2!n'
429             },
430             'SK' => {
431             'country' => 'Slovak Republic',
432             'iban_length' => '24',
433             'iban_structure' => 'SK[0-9]{2}[0-9]{4}[0-9]{6}[0-9]{10}',
434             'is_sepa' => 1,
435             'pattern' => 'SK2!n4!n6!n10!n'
436             },
437             'SM' => {
438             'country' => 'San Marino',
439             'iban_length' => '27',
440             'iban_structure' => 'SM[0-9]{2}[A-Z]{1}[0-9]{5}[0-9]{5}[A-Za-z0-9]{12}',
441             'is_sepa' => 0,
442             'pattern' => 'SM2!n1!a5!n5!n12!c'
443             },
444             'TN' => {
445             'country' => 'Tunisia',
446             'iban_length' => '24',
447             'iban_structure' => 'TN59[0-9]{2}[0-9]{3}[0-9]{13}[0-9]{2}',
448             'is_sepa' => 0,
449             'pattern' => 'TN592!n3!n13!n2!n'
450             },
451             'TR' => {
452             'country' => 'Turkey',
453             'iban_length' => '26',
454             'iban_structure' => 'TR[0-9]{2}[0-9]{5}[A-Za-z0-9]{1}[A-Za-z0-9]{16}',
455             'is_sepa' => 0,
456             'pattern' => 'TR2!n5!n1!c16!c'
457             },
458             'VG' => {
459             'country' => 'Virgin Islands, British',
460             'iban_length' => '24',
461             'iban_structure' => 'VG[0-9]{2}[A-Z]{4}[0-9]{16}',
462             'is_sepa' => 0,
463             'pattern' => 'VG2!n4!a16!n'
464             }
465             );
466              
467             sub iban_db {
468 2     2 1 14 lock_hash(%iban_db);
469 2         167 return \%iban_db;
470             }
471              
472             my %lettermap = do {
473             my $i = 10;
474             map +($_ => $i++), 'A'..'Z';
475             };
476              
477             sub numify_iban {
478 137     137 1 163 my ($iban) = @_;
479              
480 137         276 my $to_check = substr($iban, 4) . substr($iban, 0, 4);
481 137         1142 $to_check =~ s/([A-Za-z])/$lettermap{uc($1)}/g;
482              
483 137         384 return $to_check;
484             }
485              
486             sub mod97 {
487 137     137 1 157 my ($number) = @_;
488              
489             # Max 9 digits, safe for 32bit INT
490 137         267 my ($r, $l) = (0, 9);
491 137         2418 while ($number =~ s/^([0-9]{1,$l})//) {
492 540         1011 $r = $r . $1;
493 540         613 $r %= 97;
494 540         4655 $l = 9 - length($r);
495             }
496 137         548 return $r;
497             }
498              
499             1;
500              
501             =head1 NAME
502              
503             Business::IBAN::Database - Simple database for checking IBANs
504              
505             =head1 SYNOPSIS
506              
507             use Business::IBAN::Database;
508              
509             my $iso3166a2 = uc substr $iban, 0, 2;
510             if (!exists iban_db->{$iso3166a2}) {
511             die "Countrycode '$iso3166a2' not in IBAN.\n";
512             }
513             if (length($iban) != iban_db->{$iso3166a2}{iban_length}) {
514             die "Invalid length for '$iban'.\n";
515             }
516             if ($iban !~ iban_db->{$iso3166a2}{iban_structure}) {
517             die "Invalid pattern for '$iban'.\n";
518             }
519             if (mod97(numify_iban($iban)) != 1) {
520             die "Invalid checksum for '$iban'.\n";
521             }
522              
523             =head1 DESCRIPTION
524              
525             This module was generated from the IBAN_Registry.pdf document supplied by
526             SWIFT version 45 April 2013, with changes from version 46 November 2013.
527              
528             All functions are exported by default.
529              
530             =head2 iban_db()
531              
532             Returns a reference to the "database" of known IBAN entities, keyed on the
533             two letter code for participating countries (See ISO 3166 alpha 2 codes).
534              
535             =head2 numify_iban($iban)
536              
537             Put the first four characters at the end of the string. Transform all letters
538             into numbers. This results in a string of digits [0-9] that can be used as a
539             number.
540              
541             =head2 mod97($number)
542              
543             Returns the remainder of division by 97.
544              
545             =head1 STUFF
546              
547             (c) MMXIII - Abe Timmerman
548              
549             =cut