Erste Version des SEPA-Mandat-Webinterface
Bernd Wurst

Bernd Wurst commited on 2013-12-20 19:13:16
Zeige 23 geänderte Dateien mit 2074 Einfügungen und 1 Löschungen.

... ...
@@ -0,0 +1,35 @@
1
+This file lists specific errors observed in the official IBAN specification.
2
+
3
+In addition to the specific errors, there persist basic outstanding
4
+matters that cause issue for implementers:
5
+ - Non 1:1 mapping of records to countries (eg: French territories, etc.)
6
+ - Mixing of free text and hard data in specification fields
7
+ - Lack of validation of information in fields prior to publishing
8
+ - Lack of synchronisation between TXT and PDF versions of the registry
9
+
10
+2011-07-16
11
+----------
12
+ - No information for Kuwait past sixth column
13
+ - Total absence of information regarding Kazakhstan
14
+ - '1.00001E+15' instead of a valid BBAN example for Lithuania
15
+ - Repeated IBAN example in human format instead of IBAN format-
16
+   specification for UAE
17
+ - Incorrect domestic example for Bulgaria, Kazakhstan, Latvia,
18
+   Lithuania, Luxembourg, Macedonia, Mauritius, Romania, San Marino
19
+   (complete, human-format IBAN instead of domestic example)
20
+
21
+Early 2012
22
+----------
23
+ - Inconsistent record ordering (KW, KZ)
24
+ - Inconsistent capitalization (DK)
25
+ - Continued presence of incorrect domestic examples
26
+
27
+February 2013
28
+-------------
29
+ - Deployment of unparseable special values such as "Not in use" (FI).
30
+ - Still(!) missing a registry entry for 2010's 'new' entry of Khazakstan
31
+
32
+September 2013
33
+--------------
34
+ - Azerbaijan, Brazil, Costa Rica, Palestine, Virgin Islands 'SEPA 
35
+   Country' field in PDF (yes/no) is completely blank
... ...
@@ -0,0 +1,63 @@
1
+By unix tradition, this file outlines information that may be useful to
2
+people who wish to modify the php-iban project. It outlines some basic
3
+information around design decisions and other considerations.
4
+
5
+ Procedural style
6
+ ----------------
7
+   The code is written in PHP's original procedural style, and does
8
+   not require or assume any OO model. This is unix tradition and
9
+   should ease any integration pains due to objectspace mismatches.
10
+   In addition, it should make it easy for users both within an OO
11
+   or a procedural codebase to make use of the library. An OO wrapper
12
+   has been supplied to make things more familiar for those who are
13
+   only exposed to OO PHP: please try to keep it in synch with the
14
+   procedural (main) library where possible.
15
+
16
+ Registry maintenance
17
+ --------------------
18
+   The 'convert-registry.php' tool found in the 'utils/' subdirectory
19
+   is intended to assist with the automatic conversion of the SWIFT-
20
+   provided 'IBAN Registry' text files to the format required to
21
+   support php-iban execution. Why is there a new format, and why is it
22
+   distributed with php-iban instead of being generated on the fly
23
+   from SWIFT-provided data files? There are a few reasons:
24
+
25
+    - Error correction
26
+      If errors are discovered in the official specification then they
27
+      can be resolved by us. There are (or have been) known errors
28
+      with the official IBAN Registry. (See COMEDY-OF-ERRORS)
29
+
30
+    - Exclusion correction
31
+      If exclusions are discovered in the official specification then
32
+      they can be resolved by us. There are (or have been) known 
33
+      exclusions from the official IBAN Registry. (See COMEDY-OF-ERRORS)
34
+
35
+    - Efficiency
36
+      Because pattern matching is a core part of the functionality of
37
+      php-iban, and the pattern algorithms distributed by SWIFT are
38
+      (rather strangely) not in regular expression format, using their
39
+      files directly would result in a fairly significant startup
40
+      penalty as pattern conversion would be required (at each
41
+      invocation!) unless a caching strategy were deployed, which would
42
+      create additional complexity and sources of bugs (in addition,
43
+      due to the previous two points automatic conversion is not
44
+      presently possible ... and may never be!)
45
+
46
+    - Maintainability
47
+      Distribution of a modified registry along with php-iban places
48
+      the burden of registry maintenance on with the package 
49
+      maintainer(s) rather than with the user. This is better for
50
+      users who, if they really want, can still hack their local copy.
51
+
52
+   Note that due to points one and two, the 'convert-registry.php' tool
53
+   is insufficient to produce a correct 'registry.txt' file.  (You may
54
+   wish to review the differences between your newly generated file
55
+   and the original with the 'diff' tool in order to ascertain what
56
+   has changed.)
57
+
58
+   A closing point on the registry: obviously, if any new fields are
59
+   added, then it is best to append them to the end of the registry
60
+   (rightmost, new field) in order to preserve backwards compatibility
61
+   instead of re-ordering the fields which would break older installs.
62
+   (The internal '_iban_load_registry()' function re-orders these fields
63
+   at load time in order to simplify runtime debugging, anyway.)
... ...
@@ -0,0 +1,165 @@
1
+		   GNU LESSER GENERAL PUBLIC LICENSE
2
+                       Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+
9
+  This version of the GNU Lesser General Public License incorporates
10
+the terms and conditions of version 3 of the GNU General Public
11
+License, supplemented by the additional permissions listed below.
12
+
13
+  0. Additional Definitions. 
14
+
15
+  As used herein, "this License" refers to version 3 of the GNU Lesser
16
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
17
+General Public License.
18
+
19
+  "The Library" refers to a covered work governed by this License,
20
+other than an Application or a Combined Work as defined below.
21
+
22
+  An "Application" is any work that makes use of an interface provided
23
+by the Library, but which is not otherwise based on the Library.
24
+Defining a subclass of a class defined by the Library is deemed a mode
25
+of using an interface provided by the Library.
26
+
27
+  A "Combined Work" is a work produced by combining or linking an
28
+Application with the Library.  The particular version of the Library
29
+with which the Combined Work was made is also called the "Linked
30
+Version".
31
+
32
+  The "Minimal Corresponding Source" for a Combined Work means the
33
+Corresponding Source for the Combined Work, excluding any source code
34
+for portions of the Combined Work that, considered in isolation, are
35
+based on the Application, and not on the Linked Version.
36
+
37
+  The "Corresponding Application Code" for a Combined Work means the
38
+object code and/or source code for the Application, including any data
39
+and utility programs needed for reproducing the Combined Work from the
40
+Application, but excluding the System Libraries of the Combined Work.
41
+
42
+  1. Exception to Section 3 of the GNU GPL.
43
+
44
+  You may convey a covered work under sections 3 and 4 of this License
45
+without being bound by section 3 of the GNU GPL.
46
+
47
+  2. Conveying Modified Versions.
48
+
49
+  If you modify a copy of the Library, and, in your modifications, a
50
+facility refers to a function or data to be supplied by an Application
51
+that uses the facility (other than as an argument passed when the
52
+facility is invoked), then you may convey a copy of the modified
53
+version:
54
+
55
+   a) under this License, provided that you make a good faith effort to
56
+   ensure that, in the event an Application does not supply the
57
+   function or data, the facility still operates, and performs
58
+   whatever part of its purpose remains meaningful, or
59
+
60
+   b) under the GNU GPL, with none of the additional permissions of
61
+   this License applicable to that copy.
62
+
63
+  3. Object Code Incorporating Material from Library Header Files.
64
+
65
+  The object code form of an Application may incorporate material from
66
+a header file that is part of the Library.  You may convey such object
67
+code under terms of your choice, provided that, if the incorporated
68
+material is not limited to numerical parameters, data structure
69
+layouts and accessors, or small macros, inline functions and templates
70
+(ten or fewer lines in length), you do both of the following:
71
+
72
+   a) Give prominent notice with each copy of the object code that the
73
+   Library is used in it and that the Library and its use are
74
+   covered by this License.
75
+
76
+   b) Accompany the object code with a copy of the GNU GPL and this license
77
+   document.
78
+
79
+  4. Combined Works.
80
+
81
+  You may convey a Combined Work under terms of your choice that,
82
+taken together, effectively do not restrict modification of the
83
+portions of the Library contained in the Combined Work and reverse
84
+engineering for debugging such modifications, if you also do each of
85
+the following:
86
+
87
+   a) Give prominent notice with each copy of the Combined Work that
88
+   the Library is used in it and that the Library and its use are
89
+   covered by this License.
90
+
91
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
92
+   document.
93
+
94
+   c) For a Combined Work that displays copyright notices during
95
+   execution, include the copyright notice for the Library among
96
+   these notices, as well as a reference directing the user to the
97
+   copies of the GNU GPL and this license document.
98
+
99
+   d) Do one of the following:
100
+
101
+       0) Convey the Minimal Corresponding Source under the terms of this
102
+       License, and the Corresponding Application Code in a form
103
+       suitable for, and under terms that permit, the user to
104
+       recombine or relink the Application with a modified version of
105
+       the Linked Version to produce a modified Combined Work, in the
106
+       manner specified by section 6 of the GNU GPL for conveying
107
+       Corresponding Source.
108
+
109
+       1) Use a suitable shared library mechanism for linking with the
110
+       Library.  A suitable mechanism is one that (a) uses at run time
111
+       a copy of the Library already present on the user's computer
112
+       system, and (b) will operate properly with a modified version
113
+       of the Library that is interface-compatible with the Linked
114
+       Version. 
115
+
116
+   e) Provide Installation Information, but only if you would otherwise
117
+   be required to provide such information under section 6 of the
118
+   GNU GPL, and only to the extent that such information is
119
+   necessary to install and execute a modified version of the
120
+   Combined Work produced by recombining or relinking the
121
+   Application with a modified version of the Linked Version. (If
122
+   you use option 4d0, the Installation Information must accompany
123
+   the Minimal Corresponding Source and Corresponding Application
124
+   Code. If you use option 4d1, you must provide the Installation
125
+   Information in the manner specified by section 6 of the GNU GPL
126
+   for conveying Corresponding Source.)
127
+
128
+  5. Combined Libraries.
129
+
130
+  You may place library facilities that are a work based on the
131
+Library side by side in a single library together with other library
132
+facilities that are not Applications and are not covered by this
133
+License, and convey such a combined library under terms of your
134
+choice, if you do both of the following:
135
+
136
+   a) Accompany the combined library with a copy of the same work based
137
+   on the Library, uncombined with any other library facilities,
138
+   conveyed under the terms of this License.
139
+
140
+   b) Give prominent notice with the combined library that part of it
141
+   is a work based on the Library, and explaining where to find the
142
+   accompanying uncombined form of the same work.
143
+
144
+  6. Revised Versions of the GNU Lesser General Public License.
145
+
146
+  The Free Software Foundation may publish revised and/or new versions
147
+of the GNU Lesser General Public License from time to time. Such new
148
+versions will be similar in spirit to the present version, but may
149
+differ in detail to address new problems or concerns.
150
+
151
+  Each version is given a distinguishing version number. If the
152
+Library as you received it specifies that a certain numbered version
153
+of the GNU Lesser General Public License "or any later version"
154
+applies to it, you have the option of following the terms and
155
+conditions either of that published version or of any later version
156
+published by the Free Software Foundation. If the Library as you
157
+received it does not specify a version number of the GNU Lesser
158
+General Public License, you may choose any version of the GNU Lesser
159
+General Public License ever published by the Free Software Foundation.
160
+
161
+  If the Library as you received it specifies that a proxy can decide
162
+whether future versions of the GNU Lesser General Public License shall
163
+apply, that proxy's public statement of acceptance of any version is
164
+permanent authorization for you to choose that version for the
165
+Library.
... ...
@@ -0,0 +1,207 @@
1
+php-iban README
2
+---------------
3
+
4
+php-iban is a library for parsing and validating International Bank
5
+Account Number (IBAN) information in PHP.
6
+
7
+It also validates Internet International Bank Account Number
8
+(IIBAN) as specified at http://tools.ietf.org/html/draft-iiban-01
9
+(see also http://www.ifex-project.org/our-proposals/iiban)
10
+
11
+php-iban lives at http://code.google.com/p/php-iban
12
+
13
+  What is an IBAN?
14
+  ----------------
15
+    An IBAN is basically a standardised way of explaining a bank
16
+    account number that works across borders.  Its structure is:
17
+
18
+     <Two Letter ISO Country Code> + <Two Digit Checksum] + <BBAN>
19
+    
20
+    BBAN is the term used to describe the national-level format
21
+    for a bank account number, which varies between countries
22
+    (and was sometimes created just to get IBAN connectivity!).
23
+    Note that a BBAN may have its own checksum algorithm.
24
+
25
+    IBAN provides basic protection, using the checksum, against
26
+    transcription (ie: human copying) errors. It also provides
27
+    a registry of valid destination countries and their BBAN
28
+    formats. Thus, when you ask php-iban to 'validate' an IBAN
29
+    it ensures that these checks are passed. However, it cannot
30
+    ensure that a bank account actually exists - the only party
31
+    who can do that is the receiving bank or country.
32
+
33
+    IBAN was invented in Europe, however its usage is growing
34
+    rapidly to other parts of the world. Thus, the future of
35
+    this library looks pretty good.
36
+
37
+    For further information, please see 'docs/ISO13616.pdf' or
38
+    visit Wikipedia at http://en.wikipedia.org/wiki/IBAN
39
+
40
+  What is an IIBAN?
41
+  -----------------
42
+   An Internet IBAN (IIBAN) identifies an internet-based financial
43
+   endpoint in a manner that is superset-compatible with the existing
44
+   European Committee for Banking Standards (ECBS) International Bank
45
+   Account Number (IBAN) standard [ISO13616].
46
+
47
+   For more information see http://tools.ietf.org/html/draft-iiban-00
48
+   and http://www.ifex-project.org/our-proposals/iiban
49
+
50
+   To disable IIBAN support from your installation, simply remove
51
+   the second ('AA|...') line from the top of the registry.txt file.
52
+
53
+  Execution environment
54
+  ---------------------
55
+    At present the library merely requires a PHP engine to be present
56
+    and has no external dependencies.  It is recommended that your
57
+    PHP engine is configured to disable verbose warnings for events
58
+    such as access of unknown array indexes, though this should be
59
+    standard on almost any PHP deployment today. Any PHP engine
60
+    in use today should be compatible, though PHP3 or PHP4 execution
61
+    environments may require minor modifications (specifically,
62
+    some string functions may have changed).
63
+
64
+  Installation
65
+  ------------
66
+    Simply copy 'php-iban.php' and 'registry.txt' to an appropriate
67
+    location for your project. The location of the files will affect
68
+    the 'require_once()' line used to load the library from your
69
+    codebase, and may have relevance security (see 'Security' below).
70
+    Note that 'php-iban.php' expects to find 'registry.txt' in the
71
+    same directory as itself.
72
+
73
+  Security
74
+  --------
75
+    Following best practice for library files, the location chosen 
76
+    for the php-iban.php and registry.txt files should ideally be 
77
+    outside of any web-accessible directories. Thus, if your 
78
+    web project lives in /var/www/myproject/htdocs/ then it would
79
+    be preferably to place php-iban in /var/www/myproject or some
80
+    other directory that is not visible to regular web users.
81
+    
82
+    Secondly, neither file should be writable by the web server
83
+    itself in order to prevent compromise of the execution path
84
+    (ie: getting hacked). So, for example if your web server runs
85
+    as user 'www', group 'www', you can ensure that the web server
86
+    has minimal required privileges against the files as follows
87
+    (note that you will need to be root to execute these commands):
88
+
89
+     # chown <myuser> php-iban registry.txt  # where <myuser> is a
90
+                                             # non-root user that
91
+                                             # is not 'www'.
92
+     # chgrp www php-iban registry.txt       # set group to 'www'
93
+     # chmod ugo-rwx php-iban registry.txt   # remove privileges
94
+     # chmod g+r php-iban registry.txt       # allow 'www' group
95
+                                             # to read the files
96
+
97
+    Obviously the above do not apply if you are using PHP in a 
98
+    non web-oriented project (eg: a cronjob or daemon), a usage
99
+    of the language that is apparently growing - but limited.
100
+
101
+  Using the library
102
+  -----------------
103
+    Basic invocation is as follows:
104
+
105
+      # include the library
106
+      require_once('/path/to/php-iban.php'); # /path/to/ is optional
107
+
108
+      # use some library function or other...
109
+      if(!verify_iban($iban_to_verify)) {
110
+       # blame the user...
111
+      }
112
+
113
+    Note that because the library is designed in a procedural manner
114
+    rather than an object-oriented manner it should be easy to 
115
+    integrate with a wide variety of established codebases and 
116
+    PHP interpreter versions.
117
+
118
+  Using the library's OO wrapper
119
+  ------------------------------
120
+    Because many new PHP programmers seems to learn the language via
121
+    books that only teach OO based programming and are thus unfamiliar
122
+    with procedural PHP (and often relatively inexperienced as 
123
+    programmers, too) an OO wrapper-library has been provided.
124
+
125
+    ======================= READ THIS =================================
126
+    However *you should avoid excessive use of OO*. For some thought
127
+    provoking discussions of the negative aspects of overusing OO,
128
+    please refer to 'Coders at Work' and 'The Art of UNIX Programming'.
129
+    (OO is great for some problems, but simply pointless for most.)
130
+    ===================================================================
131
+
132
+    Anyway, to use the OO wrapper supplied, invocation is as follows:
133
+
134
+      # include the OO wrapper to the library
135
+      require_once('/path/to/oophp-iban.php'); # /path/to is optional
136
+
137
+      # instantiate an IBAN object
138
+      $myIban = new IBAN('AZ12345678901234');
139
+      if(!$myIban->Verify()) {
140
+       # blame the user...
141
+      }
142
+
143
+  Documentation
144
+  -------------
145
+    There are three types of functions provided by the library:
146
+
147
+     - IBAN-level functions
148
+
149
+         These are functions that operate on an IBAN. All of these
150
+         functions accept either machine format or human format
151
+         IBANs as input. Typically they return facts about an IBAN
152
+         as their output (for example whether it is valid or not,
153
+         or the BBAN (national-level) portion of the IBAN), though
154
+         some of them may perform other functions (eg: fixing a
155
+         broken IBAN checksum). These functions are named 'iban_*'
156
+         with the exception of the most commonly used function,
157
+         'verify_iban()', and excepting the country-level functions.
158
+
159
+         (Regarding the object oriented wrapper - all of these
160
+          functions are implemented as methods on IBAN objects)
161
+
162
+     - IBAN country-level functions
163
+         These functions return information about countries that are
164
+         part of the IBAN standard. They each take the two letter 
165
+         ISO country code at the beginning of an IBAN as their
166
+         argument. They are named 'iban_country_*', with the 
167
+         exception of 'iban_countries()' which returns a list of
168
+         the known IBAN countries. (For example, functions that 
169
+         return an example IBAN or BBAN for the country, or the 
170
+         name of the country.)
171
+
172
+         (Regarding the object oriented wrapper - all of these
173
+          functions are implemented as methods on IBANCountry
174
+          objects, except for 'iban_countries()' which is 
175
+          implemented as the Countries() method on the IBAN class)
176
+
177
+     - Internal functions
178
+         These functions begin with '_iban_*' and can be ignored.
179
+
180
+         (Regarding the object oriented wrapper - these functions
181
+          are not present)
182
+
183
+    Please refer to either http://code.google.com/p/php-iban or the
184
+    commented source code of php-iban itself for the complete list of
185
+    which functions are available. Of course, in unix style one could
186
+    achieve the same in a pinch as follows (instant documentation!):
187
+     $ grep function php-iban.php
188
+     $ egrep '(Class|function)' oophp-iban.php
189
+
190
+  Community
191
+  ---------
192
+    You are encouraged to contribute bugs, feedback and suggestions 
193
+    through the project's website.
194
+
195
+    Particularly if you deploy php-iban in a commercial setting, you are
196
+    STRONGLY encouraged to join the project's mailing list, which can
197
+    be found via the website. Joining the mailing list ensures that you
198
+    can be made aware of important updates. Important updates include:
199
+     - Updates to new registry editions (support new countries that have
200
+       been added to the IBAN system)
201
+     - Bug fixes 
202
+     - Security updates
203
+
204
+    The email list receives almost no traffic and as a 'Google Group' is
205
+    well protected from spam, so don't worry about junk in your inbox.
206
+
207
+Thanks for choosing php-iban! You have excellent taste in software ;)
... ...
@@ -0,0 +1,11 @@
1
+To get the latest ISO13616 IBAN registry as a .txt file
2
+for parsing, go to:
3
+ http://www.swift.com/solutions/messaging/information_products/directory_products/iban_format_registry/index.page?lang=en
4
+... then follow the '.txt format' link.
5
+
6
+Unfortunately, it has been noticed in 2011 that the official .txt file is
7
+not an accurate representation of the PDF format specification, and lacks
8
+some information. It is hoped that in future SWIFT will be more careful to
9
+publish only correct standards information.
10
+
11
+(For more information on this subject, see the HACKING file)
... ...
@@ -0,0 +1,9 @@
1
+Interpreting the SEPA Field
2
+===========================
3
+
4
+Note that some IIBAN providers may in fact provide SEPA connectivity.
5
+
6
+Thus, implementers are reminded to consider the reported SEPA status
7
+of a country within the registry as confirming rather than negating
8
+the potential SEPA status of a potential financial institution who
9
+may be party to a proposed transaction.
... ...
@@ -0,0 +1,21 @@
1
+TODO
2
+----
3
+
4
+ - Finnish record suggests position at which to pad domestic account
5
+   numbers in order to reach a BBAN / IBAN, this data could be of
6
+   some use for certain applications and would be nice to include
7
+ - Addition of information regarding preferred human-level formatting
8
+   for each country's BBAN - worthwhile?
9
+ - Addition of 'date effective' information for records such as BH
10
+   and the UAE that published data in advance of deployment
11
+ - Support for calculating or validating any known national (sub-
12
+   BBAN-level) checksum algorithms?
13
+ - URLs to national-level BBAN format specifications
14
+ - Consider adding a library of localised forms and abbreviations
15
+   for account number portions, for example Austria and Germany seem
16
+   to have 'Kontonummer' (KTO) for account number, and 'Bankleitzah'
17
+   (BLZ) for bank identifier. This could assist greatly with 
18
+   deployments requiring international and/or constrained input.
19
+ - Consider building a library of national-level bank or payment
20
+   institution identifier codes. This would be rather large and an
21
+   optional extension.
... ...
@@ -0,0 +1,49 @@
1
+ ; formalities
2
+ input-roman = number / letter
3
+ number      = c-0 / c-1 / c-2 / c-3 / c-4 / c-5 / c-6 / c-7 / c-8 / c-9
4
+ letter      = c-a / c-b / c-c / c-d / c-e / c-f / c-g / c-h / c-i / c-j
5
+                   / c-k / c-l / c-m / c-n / c-o / c-p / c-q / c-r / c-s
6
+                   / c-t / c-u / c-v / c-w / c-x / c-y / c-z
7
+
8
+ ; possible sources of mistranscribed numbers
9
+ c-0 = "O" / "6" / "D" / "G"
10
+ c-1 = "I" / "L" / "7" / "2" / "Z"
11
+ c-2 = "Z" / "7" / "P" / "E" / "1"
12
+ c-3 = "8" / "B"
13
+ c-4 = "G" / "U"
14
+ c-5 = "S" / "7"
15
+ c-6 = "0" / "O" / "8" / "G" / "C" / "B" / "D"
16
+ c-7 = "J" / "I" / "1" / "L"
17
+ c-8 = "B" / "3" / "6"
18
+ c-9 = "G" / "Y" / "O" / "0" / "D"
19
+
20
+ ; possible sources of mistranscribed letters
21
+ c-a = "G" / "Q" / "O" / "0"
22
+ c-b = "6" / "3" / "8" / "P" / "0" / "O"
23
+ c-c = "R" / "6" / "I" / "L" / "O" / "0"
24
+ c-d = "0" / "O" / "9" / "Q" / "G" / "6" / "A"
25
+ c-e = "F" / "G" / "0" / "2" / "K" / "Z" / "S" / "O"
26
+ c-f = "E" / "K" / "T" / "P" / "Y" / "4" / "B" / "7" / "1"
27
+ c-g = "9" / "Q" / "8" / "6" / "0" / "C" / "4" / "O"
28
+ c-h = "B" / "N" / "A" / "4" / "6" / "M" / "W" / "F" / "R" / "T" / "X"
29
+ c-i = "1" / "L" / "7" / "J" / "2" / "T" / "Z"
30
+ c-j = "I" / "7" / "2" / "9" / "1" / "U" / "T" / "Q" / "P" / "Y" / "Z"
31
+           / "L" / "S"
32
+ c-k = "F" / "X" / "H" / "R" 
33
+ c-l = "1" / "2" / "7" / "C" / "I" / "J" / "R" / "T" / "Y" / "Z"
34
+ c-m = "H" / "8" / "E" / "3" / "N" / "V" / "W" 
35
+ c-n = "H" / "R" / "C" / "2" / "4" / "M" / "O" / "P" / "K" / "T" / "Z"
36
+ c-o = "0" / "6" / "9" / "A" / "D" / "G" / "C" / "E" / "B" / "N" / "P"
37
+           / "Q" / "R"
38
+ c-p = "F" / "4" / "8" / "2" / "B" / "J" / "R" / "N" / "O" / "T" / "Y"
39
+ c-q = "O" / "G" / "9" / "Y" / "1" / "7" / "L"
40
+ c-r = "K" / "B" / "V" / "C" / "1" / "L" / "2"
41
+ c-s = "5" / "6" / "9" / "B" / "G" / "Q" / "A" / "Y"
42
+ c-t = "1" / "4" / "7" / "F" / "I" / "J" / "L" / "P" / "X" / "Y"
43
+ c-u = "V" / "N" / "A" / "4" / "9" / "W" / "Y"
44
+ c-v = "U" / "R" / "N"
45
+ c-w = "M" / "N" / "U" / "V"
46
+ c-x = "K" / "F" / "4" / "T" / "V" / "Y"
47
+ c-y = "G" / "V" / "J" / "I" / "4" / "9" / "T" / "F" / "Q" / "1"
48
+ c-z = "2" / "1" / "L" / "R" / "I" / "7" / "V" / "3" / "4"
49
+
... ...
@@ -0,0 +1,157 @@
1
+<?php
2
+
3
+# OO wrapper for 'php-iban.php'
4
+Class IBAN {
5
+
6
+ function __construct($iban = '') {
7
+  require_once('php-iban.php'); # load the procedural codebase
8
+  $this->iban = $iban;
9
+ }
10
+
11
+ public function Verify($iban='') {
12
+  if($iban!='') { return verify_iban($iban); }
13
+  return verify_iban($this->iban);
14
+  # we could throw exceptions of various types, but why - does it really
15
+  # add anything? possibly some slightly better user feedback potential.
16
+  # however, this can be written by hand by performing individual checks
17
+  # ala the code in verify_iban() itself where required, which is likely
18
+  # almost never. for the increased complexity and
19
+  # maintenance/documentation cost, i say, therefore: no. no exceptions.
20
+ }
21
+
22
+ public function MistranscriptionSuggestions() {
23
+  return iban_mistranscription_suggestions($this->iban);
24
+ }
25
+
26
+ public function MachineFormat() {
27
+  return iban_to_machine_format($this->iban);
28
+ }
29
+
30
+ public function HumanFormat() {
31
+  return iban_to_human_format($this->iban);
32
+ }
33
+
34
+ public function Country($iban='') {
35
+  return iban_get_country_part($this->iban);
36
+ }
37
+
38
+ public function Checksum($iban='') {
39
+  return iban_get_checksum_part($this->iban);
40
+ }
41
+
42
+ public function BBAN() {
43
+  return iban_get_bban_part($this->iban);
44
+ }
45
+
46
+ public function VerifyChecksum() {
47
+  return iban_verify_checksum($this->iban);
48
+ }
49
+
50
+ public function FindChecksum() {
51
+  return iban_find_checksum($this->iban);
52
+ }
53
+
54
+ public function SetChecksum() {
55
+  $this->iban = iban_set_checksum($this->iban);
56
+ }
57
+
58
+ public function ChecksumStringReplace() {
59
+  return iban_checksum_string_replace($this->iban);
60
+ }
61
+
62
+ public function Parts() {
63
+  return iban_get_parts($this->iban);
64
+ }
65
+
66
+ public function Bank() {
67
+  return iban_get_bank_part($this->iban);
68
+ }
69
+
70
+ public function Branch() {
71
+  return iban_get_branch_part($this->iban);
72
+ }
73
+
74
+ public function Account() {
75
+  return iban_get_account_part($this->iban);
76
+ }
77
+
78
+ public function Countries() {
79
+  return iban_countries();
80
+ }
81
+}
82
+
83
+# IBANCountry
84
+Class IBANCountry {
85
+
86
+ # constructor with code
87
+ function __construct($code = '') {
88
+  $this->code = $code;
89
+ }
90
+
91
+ public function Name() {
92
+  return iban_country_get_country_name($this->code);
93
+ }
94
+
95
+ public function DomesticExample() {
96
+  return iban_country_get_domestic_example($this->code);
97
+ }
98
+
99
+ public function BBANExample() {
100
+  return iban_country_get_bban_example($this->code);
101
+ }
102
+
103
+ public function BBANFormatSWIFT() {
104
+  return iban_country_get_bban_format_swift($this->code);
105
+ }
106
+
107
+ public function BBANFormatRegex() {
108
+  return iban_country_get_bban_format_regex($this->code);
109
+ }
110
+
111
+ public function BBANLength() {
112
+  return iban_country_get_bban_length($this->code);
113
+ }
114
+
115
+ public function IBANExample() {
116
+  return iban_country_get_iban_example($this->code);
117
+ }
118
+
119
+ public function IBANFormatSWIFT() {
120
+  return iban_country_get_iban_format_swift($this->code);
121
+ }
122
+
123
+ public function IBANFormatRegex() {
124
+  return iban_country_get_iban_format_regex($this->code);
125
+ }
126
+
127
+ public function IBANLength() {
128
+  return iban_country_get_iban_length($this->code);
129
+ }
130
+
131
+ public function BankIDStartOffset() {
132
+  return iban_country_get_bankid_start_offset($this->code);
133
+ }
134
+
135
+ public function BankIDStopOffset() {
136
+  return iban_country_get_bankid_stop_offset($this->code);
137
+ }
138
+
139
+ public function BranchIDStartOffset() {
140
+  return iban_country_get_branchid_start_offset($this->code);
141
+ }
142
+
143
+ public function BranchIDStopOffset() {
144
+  return iban_country_get_branchid_stop_offset($this->code);
145
+ }
146
+
147
+ public function RegistryEdition() {
148
+  return iban_country_get_registry_edition($this->code);
149
+ }
150
+
151
+ public function IsSEPA() {
152
+  return iban_country_is_sepa($this->code);
153
+ }
154
+
155
+}
156
+
157
+?>
... ...
@@ -0,0 +1,486 @@
1
+<?php
2
+
3
+# PHP IBAN - http://code.google.com/p/php-iban - LGPLv3
4
+
5
+# Global flag by request
6
+$__disable_iiban_gmp_extension=false;
7
+
8
+# Verify an IBAN number.  Returns true or false.
9
+#  NOTE: Input can be printed 'IIBAN xx xx xx...' or 'IBAN xx xx xx...' or machine 'xxxxx' format.
10
+function verify_iban($iban) {
11
+
12
+ # First convert to machine format.
13
+ $iban = iban_to_machine_format($iban);
14
+
15
+ # Get country of IBAN
16
+ $country = iban_get_country_part($iban);
17
+
18
+ # Test length of IBAN
19
+ if(strlen($iban)!=iban_country_get_iban_length($country)) { return false; }
20
+
21
+ # Get checksum of IBAN
22
+ $checksum = iban_get_checksum_part($iban);
23
+
24
+ # Get country-specific IBAN format regex
25
+ $regex = '/'.iban_country_get_iban_format_regex($country).'/';
26
+
27
+ # Check regex
28
+ if(preg_match($regex,$iban)) {
29
+  # Regex passed, check checksum
30
+  if(!iban_verify_checksum($iban)) { 
31
+   return false;
32
+  }
33
+ }
34
+ else {
35
+  return false;
36
+ }
37
+
38
+ # Otherwise it 'could' exist
39
+ return true;
40
+}
41
+
42
+# Convert an IBAN to machine format.  To do this, we
43
+# remove IBAN from the start, if present, and remove
44
+# non basic roman letter / digit characters
45
+function iban_to_machine_format($iban) {
46
+ # Uppercase and trim spaces from left
47
+ $iban = ltrim(strtoupper($iban));
48
+ # Remove IIBAN or IBAN from start of string, if present
49
+ $iban = preg_replace('/^I?IBAN/','',$iban);
50
+ # Remove all non basic roman letter / digit characters
51
+ $iban = preg_replace('/[^a-zA-Z0-9]/','',$iban);
52
+ return $iban;
53
+}
54
+
55
+# Convert an IBAN to human format. To do this, we
56
+# simply insert spaces right now, as per the ECBS
57
+# (European Committee for Banking Standards) 
58
+# recommendations available at:
59
+# http://www.europeanpaymentscouncil.eu/knowledge_bank_download.cfm?file=ECBS%20standard%20implementation%20guidelines%20SIG203V3.2.pdf 
60
+function iban_to_human_format($iban) {
61
+ # First verify validity, or return
62
+ if(!verify_iban($iban)) { return false; }
63
+ # Add spaces every four characters
64
+ $human_iban = '';
65
+ for($i=0;$i<strlen($iban);$i++) {
66
+  $human_iban .= substr($iban,$i,1);
67
+  if(($i>0) && (($i+1)%4==0)) { $human_iban .= ' '; }
68
+ }
69
+ return $human_iban;
70
+}
71
+
72
+# Get the country part from an IBAN
73
+function iban_get_country_part($iban) {
74
+ $iban = iban_to_machine_format($iban);
75
+ return substr($iban,0,2);
76
+}
77
+
78
+# Get the checksum part from an IBAN
79
+function iban_get_checksum_part($iban) {
80
+ $iban = iban_to_machine_format($iban);
81
+ return substr($iban,2,2);
82
+}
83
+
84
+# Get the BBAN part from an IBAN
85
+function iban_get_bban_part($iban) {
86
+ $iban = iban_to_machine_format($iban);
87
+ return substr($iban,4);
88
+}
89
+
90
+# Check the checksum of an IBAN - code modified from Validate_Finance PEAR class
91
+function iban_verify_checksum($iban) {
92
+ # convert to machine format
93
+ $iban = iban_to_machine_format($iban);
94
+ # move first 4 chars (countrycode and checksum) to the end of the string
95
+ $tempiban = substr($iban, 4).substr($iban, 0, 4);
96
+ # subsitutute chars
97
+ $tempiban = iban_checksum_string_replace($tempiban);
98
+ # mod97-10
99
+ $result = iban_mod97_10($tempiban);
100
+ # checkvalue of 1 indicates correct IBAN checksum
101
+ if ($result != 1) {
102
+  return false;
103
+ }
104
+ return true;
105
+}
106
+
107
+# Find the correct checksum for an IBAN
108
+#  $iban  The IBAN whose checksum should be calculated
109
+function iban_find_checksum($iban) {
110
+ $iban = iban_to_machine_format($iban);
111
+ # move first 4 chars to right
112
+ $left = substr($iban,0,2) . '00'; # but set right-most 2 (checksum) to '00'
113
+ $right = substr($iban,4);
114
+ # glue back together
115
+ $tmp = $right . $left;
116
+ # convert letters using conversion table
117
+ $tmp = iban_checksum_string_replace($tmp);
118
+ # get mod97-10 output
119
+ $checksum = iban_mod97_10_checksum($tmp);
120
+ # return 98 minus the mod97-10 output, left zero padded to two digits
121
+ return str_pad((98-$checksum),2,'0',STR_PAD_LEFT);
122
+}
123
+
124
+# Set the correct checksum for an IBAN
125
+#  $iban  IBAN whose checksum should be set
126
+function iban_set_checksum($iban) {
127
+ $iban = iban_to_machine_format($iban);
128
+ return substr($iban,0,2) . iban_find_checksum($iban) . substr($iban,4);
129
+}
130
+
131
+# Character substitution required for IBAN MOD97-10 checksum validation/generation
132
+#  $s  Input string (IBAN)
133
+function iban_checksum_string_replace($s) {
134
+ $iban_replace_chars = range('A','Z');
135
+ foreach (range(10,35) as $tempvalue) { $iban_replace_values[]=strval($tempvalue); }
136
+ return str_replace($iban_replace_chars,$iban_replace_values,$s);
137
+}
138
+
139
+# Same as below but actually returns resulting checksum
140
+function iban_mod97_10_checksum($numeric_representation) {
141
+ $checksum = intval(substr($numeric_representation, 0, 1));
142
+ for ($position = 1; $position < strlen($numeric_representation); $position++) {
143
+  $checksum *= 10;
144
+  $checksum += intval(substr($numeric_representation,$position,1));
145
+  $checksum %= 97;
146
+ }
147
+ return $checksum;
148
+}
149
+
150
+# Perform MOD97-10 checksum calculation ('Germanic-level effiency' version - thanks Chris!)
151
+function iban_mod97_10($numeric_representation) {
152
+ global $__disable_iiban_gmp_extension;
153
+ # prefer php5 gmp extension if available
154
+ if(!($__disable_iiban_gmp_extension) && function_exists('gmp_intval')) { return gmp_intval(gmp_mod(gmp_init($numeric_representation, 10),'97')) === 1; }
155
+
156
+/*
157
+ # old manual processing (~16x slower)
158
+ $checksum = intval(substr($numeric_representation, 0, 1));
159
+ for ($position = 1; $position < strlen($numeric_representation); $position++) {
160
+  $checksum *= 10;
161
+  $checksum += intval(substr($numeric_representation,$position,1));
162
+  $checksum %= 97;
163
+ }
164
+ return $checksum;
165
+ */
166
+
167
+ # new manual processing (~3x slower)
168
+ $length = strlen($numeric_representation);
169
+ $rest = "";
170
+ $position = 0;
171
+ while ($position < $length) {
172
+        $value = 9-strlen($rest);
173
+        $n = $rest . substr($numeric_representation,$position,$value);
174
+        $rest = $n % 97;
175
+        $position = $position + $value;
176
+ }
177
+ return ($rest === 1);
178
+}
179
+
180
+# Get an array of all the parts from an IBAN
181
+function iban_get_parts($iban) {
182
+ return array(
183
+         'country'	=>      iban_get_country_part($iban),
184
+ 	 'checksum'	=>	iban_get_checksum_part($iban),
185
+	 'bban'		=>	iban_get_bban_part($iban),
186
+ 	 'bank'		=>	iban_get_bank_part($iban),
187
+	 'country'	=>	iban_get_country_part($iban),
188
+	 'branch'	=>	iban_get_branch_part($iban),
189
+	 'account'	=>	iban_get_account_part($iban)
190
+        );
191
+}
192
+
193
+# Get the Bank ID (institution code) from an IBAN
194
+function iban_get_bank_part($iban) {
195
+ $iban = iban_to_machine_format($iban);
196
+ $country = iban_get_country_part($iban);
197
+ $start = iban_country_get_bankid_start_offset($country);
198
+ $stop = iban_country_get_bankid_stop_offset($country);
199
+ if($start!=''&&$stop!='') {
200
+  $bban = iban_get_bban_part($iban);
201
+  return substr($bban,$start,($stop-$start+1));
202
+ }
203
+ return '';
204
+}
205
+
206
+# Get the Branch ID (sort code) from an IBAN
207
+function iban_get_branch_part($iban) {
208
+ $iban = iban_to_machine_format($iban);
209
+ $country = iban_get_country_part($iban);
210
+ $start = iban_country_get_branchid_start_offset($country);
211
+ $stop = iban_country_get_branchid_stop_offset($country);
212
+ if($start!=''&&$stop!='') {
213
+  $bban = iban_get_bban_part($iban);
214
+  return substr($bban,$start,($stop-$start+1));
215
+ }
216
+ return '';
217
+}
218
+
219
+# Get the (branch-local) account ID from an IBAN
220
+function iban_get_account_part($iban) {
221
+ $iban = iban_to_machine_format($iban);
222
+ $country = iban_get_country_part($iban);
223
+ $start = iban_country_get_branchid_stop_offset($country);
224
+ if($start=='') {
225
+  $start = iban_country_get_bankid_stop_offset($country);
226
+ }
227
+ if($start!='') {
228
+  $bban = iban_get_bban_part($iban);
229
+  return substr($bban,$start+1);
230
+ }
231
+ return '';
232
+}
233
+
234
+# Get the name of an IBAN country
235
+function iban_country_get_country_name($iban_country) {
236
+ return _iban_country_get_info($iban_country,'country_name');
237
+}
238
+
239
+# Get the domestic example for an IBAN country
240
+function iban_country_get_domestic_example($iban_country) {
241
+ return _iban_country_get_info($iban_country,'domestic_example');
242
+}
243
+
244
+# Get the BBAN example for an IBAN country
245
+function iban_country_get_bban_example($iban_country) {
246
+ return _iban_country_get_info($iban_country,'bban_example');
247
+}
248
+
249
+# Get the BBAN format (in SWIFT format) for an IBAN country
250
+function iban_country_get_bban_format_swift($iban_country) {
251
+ return _iban_country_get_info($iban_country,'bban_format_swift');
252
+}
253
+
254
+# Get the BBAN format (as a regular expression) for an IBAN country
255
+function iban_country_get_bban_format_regex($iban_country) {
256
+ return _iban_country_get_info($iban_country,'bban_format_regex');
257
+}
258
+
259
+# Get the BBAN length for an IBAN country
260
+function iban_country_get_bban_length($iban_country) {
261
+ return _iban_country_get_info($iban_country,'bban_length');
262
+}
263
+
264
+# Get the IBAN example for an IBAN country
265
+function iban_country_get_iban_example($iban_country) {
266
+ return _iban_country_get_info($iban_country,'iban_example');
267
+}
268
+
269
+# Get the IBAN format (in SWIFT format) for an IBAN country
270
+function iban_country_get_iban_format_swift($iban_country) {
271
+ return _iban_country_get_info($iban_country,'iban_format_swift');
272
+}
273
+
274
+# Get the IBAN format (as a regular expression) for an IBAN country
275
+function iban_country_get_iban_format_regex($iban_country) {
276
+ return _iban_country_get_info($iban_country,'iban_format_regex');
277
+}
278
+
279
+# Get the IBAN length for an IBAN country
280
+function iban_country_get_iban_length($iban_country) {
281
+ return _iban_country_get_info($iban_country,'iban_length');
282
+}
283
+
284
+# Get the BBAN Bank ID start offset for an IBAN country
285
+function iban_country_get_bankid_start_offset($iban_country) {
286
+ return _iban_country_get_info($iban_country,'bban_bankid_start_offset');
287
+}
288
+
289
+# Get the BBAN Bank ID stop offset for an IBAN country
290
+function iban_country_get_bankid_stop_offset($iban_country) {
291
+ return _iban_country_get_info($iban_country,'bban_bankid_stop_offset');
292
+}
293
+
294
+# Get the BBAN Branch ID start offset for an IBAN country
295
+function iban_country_get_branchid_start_offset($iban_country) {
296
+ return _iban_country_get_info($iban_country,'bban_branchid_start_offset');
297
+}
298
+
299
+# Get the BBAN Branch ID stop offset for an IBAN country
300
+function iban_country_get_branchid_stop_offset($iban_country) {
301
+ return _iban_country_get_info($iban_country,'bban_branchid_stop_offset');
302
+}
303
+
304
+# Get the registry edition for an IBAN country
305
+function iban_country_get_registry_edition($iban_country) {
306
+ return _iban_country_get_info($iban_country,'registry_edition');
307
+}
308
+
309
+# Is the IBAN country a SEPA member?
310
+function iban_country_is_sepa($iban_country) {
311
+ return _iban_country_get_info($iban_country,'country_sepa');
312
+}
313
+
314
+# Get the list of all IBAN countries
315
+function iban_countries() {
316
+ global $_iban_registry;
317
+ return array_keys($_iban_registry);
318
+}
319
+
320
+# Given an incorrect IBAN, return an array of zero or more checksum-valid
321
+# suggestions for what the user might have meant, based upon common
322
+# mistranscriptions.
323
+function iban_mistranscription_suggestions($incorrect_iban) {
324
+ 
325
+ # abort on ridiculous length input (but be liberal)
326
+ $length = strlen($incorrect_iban);
327
+ if($length<5 || $length>34) { return array('(length bad)'); }
328
+
329
+ # abort if mistranscriptions data is unable to load
330
+ if(!_iban_load_mistranscriptions()) { return array('(failed to load)'); }
331
+
332
+ # init
333
+ global $_iban_mistranscriptions;
334
+ $suggestions = array();
335
+
336
+ # we have a string of approximately IBAN-like length.
337
+ # ... now let's make suggestions.
338
+ $numbers = array('0','1','2','3','4','5','6','7','8','9');
339
+ for($i=0;$i<$length;$i++) {
340
+  # get the character at this position
341
+  $character = substr($incorrect_iban,$i,1);
342
+  # for each known transcription error resulting in this character
343
+  foreach($_iban_mistranscriptions[$character] as $possible_origin) {
344
+   # if we're:
345
+   #  - in the first 2 characters (country) and the possible replacement
346
+   #    is a letter
347
+   #  - in the 3rd or 4th characters (checksum) and the possible
348
+   #    replacement is a number
349
+   #  - later in the string
350
+   if(($i<2 && !in_array($possible_origin,$numbers)) ||
351
+      ($i>=2 && $i<=3 && in_array($possible_origin,$numbers)) ||
352
+      $i>3) {
353
+    # construct a possible IBAN using this possible origin for the
354
+    # mistranscribed character, replaced at this position only
355
+    $possible_iban = substr($incorrect_iban,0,$i) . $possible_origin .  substr($incorrect_iban,$i+1);
356
+    # if the checksum passes, return it as a possibility
357
+    if(verify_iban($possible_iban)) {
358
+     array_push($suggestions,$possible_iban);
359
+    }
360
+   }
361
+  }
362
+ }
363
+
364
+ # now we check for the type of mistransposition case where all of
365
+ # the characters of a certain type within a string were mistransposed.
366
+ #  - first generate a character frequency table
367
+ $char_freqs = array();
368
+ for($i=0;$i<strlen($incorrect_iban);$i++) {
369
+  if(!isset($char_freqs[substr($incorrect_iban,$i,1)])) {
370
+   $char_freqs[substr($incorrect_iban,$i,1)] = 1;
371
+  }
372
+  else {
373
+   $char_freqs[substr($incorrect_iban,$i,1)]++;
374
+  }
375
+ }
376
+ #  - now, for each of the characters in the string...
377
+ foreach($char_freqs as $char=>$freq) {
378
+  # if the character occurs more than once
379
+  if($freq>1) {
380
+   # check the 'all occurrences of <char> were mistranscribed' case
381
+   foreach($_iban_mistranscriptions[$char] as $possible_origin) {
382
+    $possible_iban = str_replace($char,$possible_origin,$incorrect_iban);
383
+    if(verify_iban($possible_iban)) {
384
+     array_push($suggestions,$possible_iban);
385
+    }
386
+   }
387
+  }
388
+ }
389
+
390
+ return $suggestions;
391
+}
392
+
393
+
394
+##### internal use functions - safe to ignore ######
395
+
396
+# Load the IBAN registry from disk.
397
+global $_iban_registry;
398
+$_iban_registry = array();
399
+_iban_load_registry();
400
+function _iban_load_registry() {
401
+ global $_iban_registry;
402
+ # if the registry is not yet loaded, or has been corrupted, reload
403
+ if(!is_array($_iban_registry) || count($_iban_registry)<1) {
404
+  $data = file_get_contents(dirname(__FILE__) . '/registry.txt');
405
+  $lines = explode("\n",$data);
406
+  array_shift($lines); # drop leading description line
407
+  # loop through lines
408
+  foreach($lines as $line) {
409
+   if($line!='') {
410
+    # split to fields
411
+    $old_display_errors_value = ini_get('display_errors');
412
+    ini_set('display_errors',false);
413
+    $old_error_reporting_value = ini_get('error_reporting');
414
+    ini_set('error_reporting',false);
415
+    list($country,$country_name,$domestic_example,$bban_example,$bban_format_swift,$bban_format_regex,$bban_length,$iban_example,$iban_format_swift,$iban_format_regex,$iban_length,$bban_bankid_start_offset,$bban_bankid_stop_offset,$bban_branchid_start_offset,$bban_branchid_stop_offset,$registry_edition,$country_sepa) = explode('|',$line);
416
+    ini_set('display_errors',$old_display_errors_value);
417
+    ini_set('error_reporting',$old_error_reporting_value);
418
+    # assign to registry
419
+    $_iban_registry[$country] = array(
420
+                                'country'			=>	$country,
421
+ 				'country_name'			=>	$country_name,
422
+				'country_sepa'			=>	$country_sepa,
423
+ 				'domestic_example'		=>	$domestic_example,
424
+				'bban_example'			=>	$bban_example,
425
+				'bban_format_swift'		=>	$bban_format_swift,
426
+				'bban_format_regex'		=>	$bban_format_regex,
427
+				'bban_length'			=>	$bban_length,
428
+				'iban_example'			=>	$iban_example,
429
+				'iban_format_swift'		=>	$iban_format_swift,
430
+				'iban_format_regex'		=>	$iban_format_regex,
431
+				'iban_length'			=>	$iban_length,
432
+				'bban_bankid_start_offset'	=>	$bban_bankid_start_offset,
433
+				'bban_bankid_stop_offset'	=>	$bban_bankid_stop_offset,
434
+				'bban_branchid_start_offset'	=>	$bban_branchid_start_offset,
435
+				'bban_branchid_stop_offset'	=>	$bban_branchid_stop_offset,
436
+				'registry_edition'		=>	$registry_edition
437
+                               );
438
+   }
439
+  }
440
+ }
441
+}
442
+
443
+# Get information from the IBAN registry by example IBAN / code combination
444
+function _iban_get_info($iban,$code) {
445
+ $country = iban_get_country_part($iban);
446
+ return _iban_country_get_info($country,$code);
447
+}
448
+
449
+# Get information from the IBAN registry by country / code combination
450
+function _iban_country_get_info($country,$code) {
451
+ global $_iban_registry;
452
+ $country = strtoupper($country);
453
+ $code = strtolower($code);
454
+ if(array_key_exists($country,$_iban_registry)) {
455
+  if(array_key_exists($code,$_iban_registry[$country])) {
456
+   return $_iban_registry[$country][$code];
457
+  }
458
+ }
459
+ return false;
460
+}
461
+
462
+# Load common mistranscriptions from disk.
463
+function _iban_load_mistranscriptions() {
464
+ global $_iban_mistranscriptions;
465
+ # do not reload if already present
466
+ if(is_array($_iban_mistranscriptions) && count($_iban_mistranscriptions) == 36) { return true; }
467
+ $_iban_mistranscriptions = array();
468
+ $file = dirname(__FILE__) . '/mistranscriptions.txt';
469
+ if(!file_exists($file) || !is_readable($file)) { return false; }
470
+ $data = file_get_contents($file);
471
+ $lines = explode("\n",$data);
472
+ foreach($lines as $line) {
473
+  # match lines with ' c-<x> = <something>' where x is a word-like character
474
+  if(preg_match('/^ *c-(\w) = (.*?)$/',$line,$matches)) {
475
+   # normalize the character to upper case
476
+   $character = strtoupper($matches[1]);
477
+   # break the possible origins list at '/', strip quotes & spaces
478
+   $chars = explode(' ',str_replace('"','',preg_replace('/ *?\/ *?/','',$matches[2])));
479
+   # assign as possible mistranscriptions for that character
480
+   $_iban_mistranscriptions[$character] = $chars;
481
+  }
482
+ }
483
+ return true;
484
+}
485
+
486
+?>
... ...
@@ -0,0 +1,79 @@
1
+country_code|country_name|domestic_example|bban_example|bban_format_swift|bban_format_regex|bban_length|iban_example|iban_format_swift|iban_format_regex|iban_length|bban_bankid_start_offset|bban_bankid_stop_offset|bban_branchid_start_offset|bban_branchid_stop_offset|registry_edition|country_sepa
2
+AA|IIBAN (Internet)|0011123Z5678|0011123Z5678|12!a|^[A-Z0-9]{12}$|12|AA120011123Z5678|AA2!n12!a|^AA(\d{2})([A-Z0-9]{12})$|16|0|3|||N/A|0
3
+AL|Albania|0000000235698741|212110090000000235698741|8!n16!c|^(\d{8})([A-Za-z0-9]{16})$|24|AL47212110090000000235698741|AL2!n8!n16!c|^AL(\d{2})(\d{8})([A-Za-z0-9]{16})$|28|0|2|3|6|2011-06-20|0
4
+AD|Andorra|2030200359100100|00012030200359100100|4!n4!n12!c|^(\d{4})(\d{4})([A-Za-z0-9]{12})$|20|AD1200012030200359100100|AD2!n4!n4!n12!c|^AD(\d{2})(\d{4})(\d{4})([A-Za-z0-9]{12})$|24|0|3|4|7|2011-06-20|0
5
+AT|Austria|19043-234573201|1904300234573201|5!n11!n|^(\d{5})(\d{11})$|16|AT611904300234573201|AT2!n5!n11!n|^AT(\d{2})(\d{5})(\d{11})$|20|0|4|||2011-06-20|1
6
+AX|Aland Islands|123456-785|12345600000785|6!n7!n1!n|^(\d{6})(\d{7})(\d{1})$|14|AX2112345600000785|AX2!n6!n7!n1!n|^AX(\d{2})(\d{6})(\d{7})(\d{1})$|18|0|2|||2013-09-05|1
7
+AZ|Azerbaijan|NABZ00000000137010001944|NABZ00000000137010001944|4!a20!c|^([A-Z]{4})([A-Za-z0-9]{20})$|24|AZ21NABZ00000000137010001944|AZ2!n4!a20!c|^AZ(\d{2})([A-Z]{4})([A-Za-z0-9]{20})$|28|0|3|||2012-05-29|0
8
+BH|Bahrain|00001299123456|BMAG00001299123456|4!a14!c|^([A-Z]{4})([A-Za-z0-9]{14})$|22|BH67BMAG00001299123456|BH2!n4!a14!c|^BH(\d{2})([A-Z]{4})([A-Za-z0-9]{14})$|22|0|3|||2012-05-29|0
9
+BE|Belgium|539-0075470-34|539007547034|3!n7!n2!n|^(\d{3})(\d{7})(\d{2})$|12|BE68539007547034|BE2!n3!n7!n2!n|^BE(\d{2})(\d{3})(\d{7})(\d{2})$|16|0|2|||2011-06-20|1
10
+BA|Bosnia and Herzegovina|199-044-00012002-79|1990440001200279|3!n3!n8!n2!n|^(\d{3})(\d{3})(\d{8})(\d{2})$|16|BA391290079401028494|BA2!n3!n3!n8!n2!n|^BA(\d{2})(\d{3})(\d{3})(\d{8})(\d{2})$|20|0|2|3|5|2011-06-20|0
11
+BR|Brazil|0009795493C1|00360305000010009795493P1|8!n5!n10!n1!a1!c|^(\d{8})(\d{5})(\d{10})([A-Z]{1})([A-Za-z0-9]{1})$|25|BR2300360305000010009795493P1BR1800000000141455123924100C2|BR2!n8!n5!n10!n1!a1!c|^BR(\d{2})(\d{8})(\d{5})(\d{10})([A-Z]{1})([A-Za-z0-9]{1})$|29|0|7|8|12|2013-06-20|0
12
+BG|Bulgaria|BNBG 9661 1020 3456 78|BNBG96611020345678|4!a4!n2!n8!c|^([A-Z]{4})(\d{4})(\d{2})([A-Za-z0-9]{8})$|18|BG80BNBG96611020345678|BG2!n4!a4!n2!n8!c|^BG(\d{2})([A-Z]{4})(\d{4})(\d{2})([A-Za-z0-9]{8})$|22|0|3|4|7|2011-06-20|1
13
+CR|Costa Rica|1026284066|15202001026284066|3!n14!n|^(\d{3})(\d{14})$|7|CR91202001026284066|CR2!n3!n14!n|^CR(\d{2})(\d{3})(\d{14})$|21|0|2|||2012-05-29|0
14
+HR|Croatia|1001005-1863000160|10010051863000160|7!n10!n|^(\d{7})(\d{10})$|17|HR1210010051863000160|HR2!n7!n10!n|^HR(\d{2})(\d{7})(\d{10})$|21|0|6|||2011-06-20|0
15
+CY|Cyprus|1200527600|002001280000001200527600|3!n5!n16!c|^(\d{3})(\d{5})([A-Za-z0-9]{16})$|24|CY17002001280000001200527600|CY2!n3!n5!n16!c|^CY(\d{2})(\d{3})(\d{5})([A-Za-z0-9]{16})$|28|0|2|3|7|2011-06-20|1
16
+CZ|Czech Republic|19-2000145399/0800|08000000192000145399|4!n6!n10!n|^(\d{4})(\d{6})(\d{10})$|20|CZ6508000000192000145399|CZ2!n4!n6!n10!n|^CZ(\d{2})(\d{4})(\d{6})(\d{10})$|24|0|3|4|9|2011-06-20|1
17
+DK|Denmark|0040 0440116243, 6460 0001631634, 6471 0001000206|00400440116243, 64600001631634, 64710001000206|4!n9!n1!n|^(\d{4})(\d{9})(\d{1})$|14|DK5000400440116243|DK2!n4!n9!n1!n|^DK(\d{2})(\d{4})(\d{9})(\d{1})$|18|0|3|||2011-06-20|1
18
+FO|Faroe Islands|0040 0440116243, 6460 0001631634, 6471 0001000206|00400440116243, 64600001631634, 64710001000206|4!n9!n1!n|^(\d{4})(\d{9})(\d{1})$|14|FO2000400440116243|FO2!n4!n9!n1!n|^FO(\d{2})(\d{4})(\d{9})(\d{1})$|18|0|3|||2011-06-20|1
19
+GL|Greenland|0040 0440116243, 6460 0001631634, 6471 0001000206|00400440116243, 64600001631634, 64710001000206|4!n9!n1!n|^(\d{4})(\d{9})(\d{1})$|14|GL2000400440116243|GL2!n4!n9!n1!n|^GL(\d{2})(\d{4})(\d{9})(\d{1})$|18|0|3|||2011-06-20|1
20
+DO|Dominican Republic|1212453611324|AGR00000001212453611324|4!c20!n|^([A-Za-z0-9]{4})(\d{20})$|24|DO28BAGR00000001212453611324|DO2!n4!c20!n|^DO(\d{2})([A-Za-z0-9]{4})(\d{20})$|28|0|3|||2011-06-20|0
21
+EE|Estonia|221020145685|2200221020145685|2!n2!n11!n1!n|^(\d{2})(\d{2})(\d{11})(\d{1})$|16|EE382200221020145685|EE2!n2!n2!n11!n1!n|^EE(\d{2})(\d{2})(\d{2})(\d{11})(\d{1})$|20|0|1|||2011-06-20|1
22
+FI|Finland|123456-785|12345600000785|6!n7!n1!n|^(\d{6})(\d{7})(\d{1})$|14|FI2112345600000785|FI2!n6!n7!n1!n|^FI(\d{2})(\d{6})(\d{7})(\d{1})$|18|0|2|||2013-08-05|1
23
+FR|France|20041 01005 0500013M026 06|20041010050500013M02606|5!n5!n11!c2!n|^(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|23|FR1420041010050500013M02606|FR2!n5!n5!n11!c2!n|^FR(\d{2})(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|27|0|4|5|9|2013-08-28|1
24
+BL|Saint Barthelemy|20041 01005 0500013M026 06|20041010050500013M02606|5!n5!n11!c2!n|^(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|23|BL9820041010050500013M02606|BL2!n5!n5!n11!c2!n|^BL(\d{2})(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|27|0|4|5|9|2013-02-08|0
25
+GF|French Guyana|20041 01005 0500013M026 06|20041010050500013M02606|5!n5!n11!c2!n|^(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|23|GF4120041010050500013M02606|GF2!n5!n5!n11!c2!n|^GF(\d{2})(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|27|0|4|5|9|2013-08-28|1
26
+GP|Guadelope|20041 01005 0500013M026 06|20041010050500013M02606|5!n5!n11!c2!n|^(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|23|GP1120041010050500013M02606|GP2!n5!n5!n11!c2!n|^GP(\d{2})(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|27|0|4|5|9|2013-08-28|1
27
+MF|Saint Martin (French Part)|20041 01005 0500013M026 06|20041010050500013M02606|5!n5!n11!c2!n|^(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|23|MF9820041010050500013M02606|MF2!n5!n5!n11!c2!n|^MF(\d{2})(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|27|0|4|5|9|2013-02-08|0
28
+MQ|Martinique|20041 01005 0500013M026 06|20041010050500013M02606|5!n5!n11!c2!n|^(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|23|MQ5120041010050500013M02606|MQ2!n5!n5!n11!c2!n|^MQ(\d{2})(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|27|0|4|5|9|2013-08-28|1
29
+RE|Reunion|20041 01005 0500013M026 06|20041010050500013M02606|5!n5!n11!c2!n|^(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|23|RE4220041010050500013M02606|RE2!n5!n5!n11!c2!n|^RE(\d{2})(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|27|0|4|5|9|2013-08-28|1
30
+PF|French Polynesia|20041 01005 0500013M026 06|20041010050500013M02606|5!n5!n11!c2!n|^(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|23|PF5720041010050500013M02606|PF2!n5!n5!n11!c2!n|^PF(\d{2})(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|27|0|4|5|9|2011-06-20|0
31
+TF|French Southern Territories|20041 01005 0500013M026 06|20041010050500013M02606|5!n5!n11!c2!n|^(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|23|TF2120041010050500013M02606|TF2!n5!n5!n11!c2!n|^TF(\d{2})(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|27|0|4|5|9|2011-06-20|0
32
+YT|Mayotte|20041 01005 0500013M026 06|20041010050500013M02606|5!n5!n11!c2!n|^(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|23|YT3120041010050500013M02606|YT2!n5!n5!n11!c2!n|^YT(\d{2})(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|27|0|4|5|9|2013-08-28|1
33
+NC|New Caledonia|20041 01005 0500013M026 06|20041010050500013M02606|5!n5!n11!c2!n|^(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|23|NC8420041010050500013M02606|NC2!n5!n5!n11!c2!n|^NC(\d{2})(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|27|0|4|5|9|2011-06-20|0
34
+PM|Saint Pierre et Miquelon|20041 01005 0500013M026 06|20041010050500013M02606|5!n5!n11!c2!n|^(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|23|PM3620041010050500013M02606|PM2!n5!n5!n11!c2!n|^PM(\d{2})(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|27|0|4|5|9|2013-08-28|1
35
+WF|Wallis and Futuna Islands|20041 01005 0500013M026 06|20041010050500013M02606|5!n5!n11!c2!n|^(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|23|WF9120041010050500013M02606|WF2!n5!n5!n11!c2!n|^WF(\d{2})(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|27|0|4|5|9|2011-06-20|0
36
+GE|Georgia|0000000101904917|NB0000000101904917|2!a16!n|^([A-Z]{2})(\d{16})$|18|GE29NB0000000101904917|GE2!n2!a16!n|^GE(\d{2})([A-Z]{2})(\d{16})$|22|0|1|||2011-06-20|0
37
+DE|Germany|37040044-532013000|370400440532013000|8!n10!n|^(\d{8})(\d{10})$|18|DE89370400440532013000|DE2!n8!n10!n|^DE(\d{2})(\d{8})(\d{10})$|22|0|7|||2011-06-20|1
38
+GI|Gibraltar|0000 00007099 453|NWBK000000007099453|4!a15!c|^([A-Z]{4})([A-Za-z0-9]{15})$|19|GI75NWBK000000007099453|GI2!n4!a15!c|^GI(\d{2})([A-Z]{4})([A-Za-z0-9]{15})$|23|0|3|||2011-06-20|1
39
+GR|Greece|01250000000012300695|01101250000000012300695|3!n4!n16!c|^(\d{3})(\d{4})([A-Za-z0-9]{16})$|23|GR1601101250000000012300695|GR2!n3!n4!n16!c|^GR(\d{2})(\d{3})(\d{4})([A-Za-z0-9]{16})$|27|0|2|3|6|2011-06-20|1
40
+GT|Guatemala|01020000001210029690|TRAJ01020000001210029690|4!c20!c|^([A-Za-z0-9]{4})([A-Za-z0-9]{20})$|24|GT82TRAJ01020000001210029690|GT2!n4!c20!c|^GT(\d{2})([A-Za-z0-9]{4})([A-Za-z0-9]{20})$|28|0|3|||2012-05-29|0
41
+HU|Hungary|11773016-11111018-00000000|117730161111101800000000|3!n4!n1!n15!n1!n|^(\d{3})(\d{4})(\d{1})(\d{15})(\d{1})$|24|HU42117730161111101800000000|HU2!n3!n4!n1!n15!n1!n|^HU(\d{2})(\d{3})(\d{4})(\d{1})(\d{15})(\d{1})$|28|0|2|3|6|2011-06-20|1
42
+IS|Iceland|0159-26-007654-551073-0339|0159260076545510730339|4!n2!n6!n10!n|^(\d{4})(\d{2})(\d{6})(\d{10})$|22|IS140159260076545510730339|IS2!n4!n2!n6!n10!n|^IS(\d{2})(\d{4})(\d{2})(\d{6})(\d{10})$|26|0|3|6|11|2011-06-20|1
43
+IE|Ireland|93-11-52 12345678|AIBK93115212345678|4!a6!n8!n|^([A-Z]{4})(\d{6})(\d{8})$|18|IE29AIBK93115212345678|IE2!n4!a6!n8!n|^IE(\d{2})([A-Z]{4})(\d{6})(\d{8})$|22|0|3|4|9|2011-06-20|1
44
+IL|Israel|10-800-99999999|100800000099999000|3!n3!n13!n|^(\d{3})(\d{3})(\d{13})$|19|IL620108000000099999999|IL2!n3!n3!n13!n|^IL(\d{2})(\d{3})(\d{3})(\d{13})$|23|0|2|3|5|2011-06-20|0
45
+IT|Italy|X 05428 11101 000000123456|X0542811101000000123456|1!a5!n5!n12!c|^([A-Z]{1})(\d{5})(\d{5})([A-Za-z0-9]{12})$|23|IT60X0542811101000000123456|IT2!n1!a5!n5!n12!c|^IT(\d{2})([A-Z]{1})(\d{5})(\d{5})([A-Za-z0-9]{12})$|27|0|5|6|10|2011-06-20|1
46
+KZ|Kazakhstan|125K ZT50 0410 0100|125KZT5004100100|3!n13!c|^(\d{3})([A-Za-z0-9]{13})$|16|KZ86125KZT5004100100|KZ2!n3!n13!c|^KZ(\d{2})(\d{3})([A-Za-z0-9]{13})$|20|0|2|||2011-06-20|0
47
+KW|Kuwait|CBKU0000000000001234560101|CBKU0000000000001234560101|4!a22!c|^([A-Z]{4})([A-Za-z0-9]{22})$|26|KW81CBKU0000000000001234560101|KW2!n4!a22!n|^KW(\d{2})([A-Z]{4})(\d{22})$|30|0|3|||2011-06-20|0
48
+LV|Latvia|BANK 0000 4351 9500 1|BANK0000435195001|4!a13!c|^([A-Z]{4})([A-Za-z0-9]{13})$|17|LV80BANK0000435195001|LV2!n4!a13!c|^LV(\d{2})([A-Z]{4})([A-Za-z0-9]{13})$|21|0|3|||2011-06-20|1
49
+LB|Lebanon|01 001 901229114|0999 0000 0001 0019 0122 9114|4!n20!c|^(\d{4})([A-Za-z0-9]{20})$|24|LB62099900000001001901229114|LB2!n4!n20!c|^LB(\d{2})(\d{4})([A-Za-z0-9]{20})$|28|0|3|||2011-06-20|0
50
+LI|Liechtenstein|8810 2324013AA|088100002324013AA|5!n12!c|^(\d{5})([A-Za-z0-9]{12})$|19|LI21088100002324013AA|LI2!n5!n12!c|^LI(\d{2})(\d{5})([A-Za-z0-9]{12})$|21|0|4|||2012-05-29|1
51
+LT|Lithuania|1000 0111 0100 1000|10000011101001000|5!n11!n|^(\d{5})(\d{11})$|16|LT121000011101001000|LT2!n5!n11!n|^LT(\d{2})(\d{5})(\d{11})$|20|0|4|||2011-06-20|1
52
+LU|Luxembourg|0019 4006 4475 0000|0019400644750000|3!n13!c|^(\d{3})([A-Za-z0-9]{13})$|16|LU280019400644750000|LU2!n3!n13!c|^LU(\d{2})(\d{3})([A-Za-z0-9]{13})$|20|0|2|||2011-06-20|1
53
+MK|Macedonia|300 0000000424 25|250120000058984|3!n10!c2!n|^(\d{3})([A-Za-z0-9]{10})(\d{2})$|15|MK07250120000058984|MK2!n3!n10!c2!n|^MK(\d{2})(\d{3})([A-Za-z0-9]{10})(\d{2})$|19|0|2|||2012-05-29|0
54
+MT|Malta|12345MTLCAST001S|MALT011000012345MTLCAST001S|4!a5!n18!c|^([A-Z]{4})(\d{5})([A-Za-z0-9]{18})$|27|MT84MALT011000012345MTLCAST001S|MT2!n4!a5!n18!c|^MT(\d{2})([A-Z]{4})(\d{5})([A-Za-z0-9]{18})$|31|0|3|4|8|2011-06-20|1
55
+MR|Mauritania|00020 00101 00001234567 53|00020001010000123456753|5!n5!n11!n2!n|^(\d{5})(\d{5})(\d{11})(\d{2})$|23|MR1300020001010000123456753|MR135!n5!n11!n2!n|^MR13(\d{5})(\d{5})(\d{11})(\d{2})$|27|0|4|5|9|2011-06-20|0
56
+MU|Mauritius|BOMM 0101 1010 3030 0200 000M UR|BOMM0101101030300200000MUR|4!a2!n2!n12!n3!n3!a|^([A-Z]{4})(\d{2})(\d{2})(\d{12})(\d{3})([A-Z]{3})$|26|MU17BOMM0101101030300200000MUR|MU2!n4!a2!n2!n12!n3!n3!a|^MU(\d{2})([A-Z]{4})(\d{2})(\d{2})(\d{12})(\d{3})([A-Z]{3})$|30|0|5|6|7|2011-06-20|0
57
+MD|Moldova|00225100013104168|AG000225100013104168|2!c18!c|^([A-Za-z0-9]{2})([A-Za-z0-9]{18})$|20|MD24AG000225100013104168|MD2!n20!c|^MD(\d{2})([A-Za-z0-9]{20})$|24|0|1|||2012-09-09|0
58
+MC|Monaco|0011111000h|11222 00001 01234567890 30|5!n5!n11!c2!n|^(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|23|MC5811222000010123456789030|MC2!n5!n5!n11!c2!n|^MC(\d{2})(\d{5})(\d{5})([A-Za-z0-9]{11})(\d{2})$|27|0|4|5|9|2011-06-20|1
59
+ME|Montenegro|505 0000123456789 51|505000012345678951|3!n13!n2!n|^(\d{3})(\d{13})(\d{2})$|18|ME25505000012345678951|ME2!n3!n13!n2!n|^ME(\d{2})(\d{3})(\d{13})(\d{2})$|22|0|2|||2011-06-20|0
60
+NL|The Netherlands|041 71 64 300|ABNA0417164300|4!a10!n|^([A-Z]{4})(\d{10})$|14|NL91ABNA0417164300|NL2!n4!a10!n|^NL(\d{2})([A-Z]{4})(\d{10})$|18|0|3|4|3|2013-06-20|1
61
+NO|Norway|8601 11 17947|86011117947|4!n6!n1!n|^(\d{4})(\d{6})(\d{1})$|11|NO9386011117947|NO2!n4!n6!n1!n|^NO(\d{2})(\d{4})(\d{6})(\d{1})$|15|0|3|||2011-06-20|1
62
+PK|Pakistan|00260101036360|SCBL0000001123456702|4!a16!c|^([A-Z]{4})([A-Za-z0-9]{16})$|20|PK36SCBL0000001123456702|PK2!n4!a16!c|^PK(\d{2})([A-Z]{4})([A-Za-z0-9]{16})$|24|0|3|||2012-05-29|0
63
+PL|Poland|61 1090 1014 0000 0712 1981 2874|109010140000071219812874|8!n16!n|^(\d{8})(\d{16})$|24|PL61109010140000071219812874|PL2!n8!n16n|^PL(\d{2})(\d{8})(\d{1,16})$|28|0|7|||2011-06-20|1
64
+PS|Palestine|400123456702|PALS000000000400123456702|4!a21!c|^([A-Z]{4})([A-Za-z0-9]{21})$|25|PS92PALS000000000400123456702|PS2!n4!a21!c|^PS(\d{2})([A-Z]{4})([A-Za-z0-9]{21})$|29|0|3|||2013-09-05|0
65
+PT|Portugal|0002.0123.12345678901.54|000201231234567890154|4!n4!n11!n2!n|^(\d{4})(\d{4})(\d{11})(\d{2})$|21|PT50000201231234567890154|PT2!n4!n4!n11!n2!n|^PT(\d{2})(\d{4})(\d{4})(\d{11})(\d{2})$|25|0|3|4|7|2013-09-05|1
66
+RO|Romania|AAAA 1B31 0075 9384 0000|AAAA1B31007593840000|4!a16!c|^([A-Z]{4})([A-Za-z0-9]{16})$|20|RO49AAAA1B31007593840000|RO2!n4!a16!c|^RO(\d{2})([A-Z]{4})([A-Za-z0-9]{16})$|24|0|3|||2011-06-20|1
67
+SM|San Marino|U032 2509 8000 0000 0270 100|U0322509800000000270100|1!a5!n5!n12!c|^([A-Z]{1})(\d{5})(\d{5})([A-Za-z0-9]{12})$|23|SM86U0322509800000000270100|SM2!n1!a5!n5!n12!c|^SM(\d{2})([A-Z]{1})(\d{5})(\d{5})([A-Za-z0-9]{12})$|27|0|5|6|10|2011-06-20|0
68
+SA|Saudi Arabia|608010167519|80000000608010167519|2!n18!c|^(\d{2})([A-Za-z0-9]{18})$|20|SA0380000000608010167519|SA2!n2!n18!c|^SA(\d{2})(\d{2})([A-Za-z0-9]{18})$|24|0|1|||2012-05-29|0
69
+RS|Serbia|260-0056010016113-79|260005601001611379|3!n13!n2!n|^(\d{3})(\d{13})(\d{2})$|18|RS35260005601001611379|RS2!n3!n13!n2!n|^RS(\d{2})(\d{3})(\d{13})(\d{2})$|22|0|2|||2011-06-20|0
70
+SK|Slovak Republic|19-8742637541/1200|12000000198742637541|4!n6!n10!n|^(\d{4})(\d{6})(\d{10})$|20|SK3112000000198742637541|SK2!n4!n6!n10!n|^SK(\d{2})(\d{4})(\d{6})(\d{10})$|24|0|3|4|9|2011-06-20|1
71
+SI|Slovenia|2633 0001 2039 086|263300012039086|5!n8!n2!n|^(\d{5})(\d{8})(\d{2})$|15|SI56191000000123438|SI2!n5!n8!n2!n|^SI(\d{2})(\d{5})(\d{8})(\d{2})$|19|0|1|2|4|2012-09-09|1
72
+ES|Spain|2100 0418 45 0200051332|21000418450200051332|4!n4!n1!n1!n10!n|^(\d{4})(\d{4})(\d{1})(\d{1})(\d{10})$|20|ES9121000418450200051332|ES2!n4!n4!n1!n1!n10!n|^ES(\d{2})(\d{4})(\d{4})(\d{1})(\d{1})(\d{10})$|24|0|3|4|7|2013-09-05|1
73
+SE|Sweden|1234 12 3456 1|5000 0000 0583 9825 7466|3!n16!n1!n|^(\d{3})(\d{16})(\d{1})$|20|SE4550000000058398257466|SE2!n3!n16!n1!n|^SE(\d{2})(\d{3})(\d{16})(\d{1})$|24|0|2|||2011-06-20|1
74
+CH|Switzerland|762 1162-3852.957|00762011623852957|5!n12!c|^(\d{5})([A-Za-z0-9]{12})$|17|CH9300762011623852957|CH2!n5!n12!c|^CH(\d{2})(\d{5})([A-Za-z0-9]{12})$|21|0|4|||2011-06-20|1
75
+TN|Tunisia|10 006 0351835984788 31|10006035183598478831|2!n3!n13!n2!n|^(\d{2})(\d{3})(\d{13})(\d{2})$|20|TN5910006035183598478831|TN592!n3!n13!n2!n|^TN59(\d{2})(\d{3})(\d{13})(\d{2})$|24|0|1|2|4|2011-06-20|0
76
+TR|Turkey|0061 01299 1234567890123456789|0006100519786457841326|5!n1!c16!c|^(\d{5})([A-Za-z0-9]{1})([A-Za-z0-9]{16})$|22|TR330006100519786457841326|TR2!n5!n1!c16!c|^TR(\d{2})(\d{5})([A-Za-z0-9]{1})([A-Za-z0-9]{16})$|26|0|4|||2011-06-20|0
77
+AE|United Arab Emirates|1234567890123456|0331234567890123456|3!n16!n|^(\d{3})(\d{16})$|19|AE070331234567890123456|AE2!n3!n16!n|^AE(\d{2})(\d{3})(\d{16})$|23|0|2|||2011-06-20|0
78
+GB|United Kingdom|60-16-13 31926819|NWBK60161331926819|4!a6!n8!n|^([A-Z]{4})(\d{6})(\d{8})$|18|GB29NWBK60161331926819|GB2!n4!a6!n8!n|^GB(\d{2})([A-Z]{4})(\d{6})(\d{8})$|22|0|3|4|9|2011-06-20|1
79
+VG|British Virgin Islands|00000 12 345 678 901|VPVG0000012345678901|4!a16!n|^([A-Z]{4})(\d{16})$|20|VG96VPVG0000012345678901|VG2!n4!a16!n|^VG(\d{2})([A-Z]{4})(\d{16})$|24|0|3|||2012-05-29|0
... ...
@@ -0,0 +1,268 @@
1
+<?php
2
+
3
+# this script converts the IBAN_registry.txt file's entries to registry.txt format (php-iban's required internal format).
4
+
5
+# init
6
+require_once(dirname(dirname(__FILE__)) . '/php-iban.php');
7
+date_default_timezone_set('UTC'); # mutes a warning
8
+
9
+# read registry
10
+$data = file_get_contents('IBAN_Registry.txt');
11
+if($data == '') { die("Couldn't read IBAN_Registry.txt - try downloading from the location described in the REGISTRY-URL file."); }
12
+
13
+# print header line
14
+print "country_code|country_name|domestic_example|bban_example|bban_format_swift|bban_format_regex|bban_length|iban_example|iban_format_swift|iban_format_regex|iban_length|bban_bankid_start_offset|bban_bankid_stop_offset|bban_branchid_start_offset|bban_branchid_stop_offset|registry_edition|country_sepa\n";
15
+
16
+# break in to lines
17
+$lines = preg_split('/[\r\n]+/',$data);
18
+
19
+# display
20
+foreach($lines as $line) {
21
+ # if it's not a blank line, and it's not the header row
22
+ if($line != '' && !preg_match('/SEPA Country/',$line)) {
23
+  # extract individual tab-separated fields
24
+  $bits = explode("\t",$line);
25
+  # remove quotes and superfluous whitespace on fields that have them.
26
+  for($i=0;$i<count($bits);$i++) {
27
+   $bits[$i] = preg_replace('/^"(.*)"$/','$1',$bits[$i]);
28
+   $bits[$i] = preg_replace('/^ */','',$bits[$i]);
29
+   $bits[$i] = preg_replace('/ *$/','',$bits[$i]);
30
+  }
31
+  # assigned fields to named variables
32
+  list($country_name,$country_code,$domestic_example,$bban,$bban_structure,$bban_length,$bban_bi_position,$bban_bi_length,$bban_bi_example,$bban_example,$iban,$iban_structure,$iban_length,$iban_electronic_example,$iban_print_example,$country_sepa,$contact_details) = $bits;
33
+  # sanitise
34
+  $country_code = strtoupper(substr($country_code,0,2));       # sanitise comments away
35
+  $bban_structure = preg_replace('/[:;]/','',$bban_structure); # errors seen in Germany, Hungary entries
36
+  $iban_structure = preg_replace('/, .*$/','',$iban_structure); # duplicates for FO, GL seen in DK
37
+  $iban_electronic_example = preg_replace('/, .*$/','',$iban_electronic_example); # duplicates for FO, GL seen in DK
38
+  if($country_code=='MU') {
39
+   $iban_electronic_example = str_replace(' ','',$iban_electronic_example); # MU example has a spurious space
40
+  }
41
+  if($country_code=='CZ') {
42
+   $iban_electronic_example = preg_replace('/ \w{10,}+$/','',$iban_electronic_example); # extra example for CZ
43
+   $iban_print_example = preg_replace('/^(CZ.. .... .... .... .... ....).*$/','$1',$iban_print_example); # extra example
44
+  }
45
+  if($country_code=='FI') {
46
+   # remove additional example
47
+   $iban_electronic_example = preg_replace('/ or .*$/','',$iban_electronic_example);
48
+   # fix bban example to remove verbosity and match domestic example
49
+   $bban = '12345600000785';
50
+  }
51
+  $iban_print_example = preg_replace('/, .*$/','',$iban_print_example); # DK includes FO and GL examples in one record
52
+
53
+ # debugging
54
+ if(false) {
55
+  print "[$country_name ($country_code)]\n";
56
+  print "Domestic account number example: $domestic_example\n";
57
+  print "BBAN structure:                  $bban_structure\n";
58
+  print "BBAN length:                     $bban_length\n";
59
+  print "BBAN bank identifier position:   $bban_bi_position\n";
60
+  print "BBAN bank identifier length:     $bban_bi_length\n";
61
+  print "BBAN bank identifier example:    $bban_bi_example\n";
62
+  print "BBAN example:                    $bban_example\n";
63
+  print "IBAN structure:                  $iban_structure\n";
64
+  print "IBAN length:                     $iban_length\n";
65
+  print "IBAN electronic format example:  $iban_electronic_example\n";
66
+  print "IBAN print format example:       $iban_print_example\n";
67
+  print "SEPA country:                    $country_sepa\n";
68
+  print "Contact details:                 $contact_details\n\n";
69
+ }
70
+
71
+  # calculate $bban_regex from $bban_structure
72
+  $bban_regex = swift_to_regex($bban_structure);
73
+  # calculate $iban_regex from $iban_structure
74
+  $iban_regex = swift_to_regex($iban_structure);
75
+  # calculate numeric $bban_length
76
+  $bban_length = preg_replace('/[^\d]/','',$bban_length);
77
+  # calculate numeric $iban_length
78
+  $iban_length = preg_replace('/[^\d]/','',$iban_length);
79
+  # calculate bban_bankid_<start|stop>_offset
80
+  # .... First we have to parse the freetext $bban_bi_position, eg: 
81
+  # Bank Identifier 1-3, Branch Identifier
82
+  # Position 1-2
83
+  # Positions 1-2
84
+  # Positions 1-3
85
+  # Positions 1-3 ;Branch is not available
86
+  # Positions 1-3, Branch identifier
87
+  # Positions 1-3, Branch identifier positions
88
+  # Positions 1-4
89
+  # Positions 1-4, Branch identifier
90
+  # Positions 1-4, Branch identifier positions
91
+  # Positions 1-5
92
+  # Positions 1-5 (positions 1-2 bank identifier; positions 3-5 branch identifier). In case of payment institutions Positions 1-5, Branch identifier positions
93
+  # Positions 1-6,  Branch identifier positions
94
+  # Positions 1-6. First two digits of bank identifier indicate the bank or banking group (For example, 1 or 2 for Nordea, 31 for Handelsbanken, 5 for cooperative banks etc)
95
+  # Positions 1-7
96
+  # Positions 1-8
97
+  # Positions 2-6, Branch identifier positions
98
+  # positions 1-3, Branch identifier positions
99
+  #
100
+  #  ... our algorithm is as follows:
101
+  #   - find all <digit>-<digit> tokens
102
+  preg_match_all('/(\d)-(\d\d?)/',$bban_bi_position,$matches);
103
+  #   - discard overlaps ({1-5,1-2,3-5} becomes {1-2,3-5})
104
+  $tmptokens = array();
105
+  for($j=0;$j<count($matches[0]);$j++) {
106
+   #print "tmptokens was... " . print_r($tmptokens,1) . "\n";
107
+   $from = $matches[1][$j];
108
+   $to = $matches[2][$j];
109
+   #      (if we don't yet have a match starting here, or it goes further,
110
+   #       overwrite the match-from-this-position record)
111
+   if(!isset($tmptokens[$from]) || $to < $tmptokens[$from]) {
112
+    $tmptokens[$from] = $to;
113
+   }
114
+  }
115
+  unset($matches); # done
116
+  #   - assume the token starting from position 1 is the bank identifier
117
+  #     (or, if it does not exist, the token starting from position 2)
118
+  $bban_bankid_start_offset = 0;              # decrement 1 on assignment
119
+  if(isset($tmptokens[1])) {
120
+   $bban_bankid_stop_offset = $tmptokens[1]-1; # decrement 1 on assignment
121
+   unset($tmptokens[1]);
122
+  }
123
+  else {
124
+   $bban_bankid_stop_offset = $tmptokens[2]-1; # decrement 1 on assignment
125
+   unset($tmptokens[2]);
126
+  }
127
+  #   - assume any subsequent token, if present, is the branch identifier.
128
+  $tmpkeys = array_keys($tmptokens);
129
+  $start = array_shift($tmpkeys);
130
+  unset($tmpkeys); # done
131
+  $bban_branchid_start_offset='';
132
+  $bban_branchid_stop_offset='';
133
+  if($start!= '') {
134
+   # we have a branch identifier!
135
+   $bban_branchid_start_offset=$start-1;
136
+   $bban_branchid_stop_offset=$tmptokens[$start]-1;
137
+  }
138
+  else {
139
+   # (note: this codepath occurs for around two thirds of all records)
140
+   # we have not yet found a branch identifier. HOWEVER, we can analyse the
141
+   # structure of the BBAN to determine whether there is more than one
142
+   # remaining non-tiny field (tiny fields on the end of a BBAN typically
143
+   # being checksums) and, if so, assume that the first/shorter one is the
144
+   # branch identifier.
145
+   $reduced_bban_structure = preg_replace('/^\d+![nac]/','',$bban_structure);
146
+   $tokens = swift_tokenize($reduced_bban_structure,1);
147
+   # discard any tokens of length 1 or 2
148
+   for($t=0;$t<count($tokens[0]);$t++) {
149
+    if($tokens[1][$t] < 3) {
150
+     $tokens['discarded'][$t] = 1;
151
+    }
152
+   }
153
+   # interesting fields are those that are not discarded...
154
+   if(!isset($tokens['discarded'])) {
155
+    $interesting_field_count = count($tokens[0]); }
156
+   else {
157
+    $interesting_field_count = (count($tokens[0])-count($tokens['discarded']));
158
+   }
159
+   # ...if we have at least two of them, there's a branchid-type field
160
+   if($interesting_field_count >= 2) {
161
+    # now loop through until we assign the branchid start offset
162
+    # (this occurs just after the first non-discarded field)
163
+    $found=0;
164
+    for($f=0; (($found==0) && ($f<count($tokens[0]))); $f++) {
165
+     # if this is a non-discarded token, of >2 length...
166
+     if((!isset($tokens['discarded'][$f]) || $tokens['discarded'][$f] != 1) && $tokens[1][$f]>2) {
167
+      # ... then assign.
168
+      $pre_offset = $bban_bankid_stop_offset+1; # this is the offset before we reduced the structure to remove the bankid field
169
+      $bban_branchid_start_offset = $pre_offset + $tokens['offset'][$f];
170
+      $bban_branchid_stop_offset  = $pre_offset + $tokens['offset'][$f] + $tokens[1][$f] - 1; # decrement by one on assignment
171
+      $found=1;
172
+     }
173
+    }
174
+   }
175
+  }
176
+
177
+  # calculate 1=Yes, 0=No for $country_sepa
178
+  # NOTE: This is buggy due to the free inclusion of random text by the registry publishers.
179
+  #       Notably it requires modification for places like Finland and Portugal where these
180
+  #       comments are known to exist.
181
+  if(strtolower($country_sepa)=='yes') { $country_sepa=1; } else { $country_sepa = 0; }
182
+  # set registry edition
183
+  $registry_edition = date('Y-m-d');
184
+
185
+  # now prepare generate our registry lines...
186
+  $to_generate = array($country_code=>$country_name);
187
+  if($country_code == 'DK') {
188
+   $to_generate = array('DK'=>$country_name,'FO'=>'Faroe Islands','GL'=>'Greenland');
189
+  }
190
+  elseif($country_code == 'FR') {
191
+   $to_generate = array('FR'=>$country_name,'BL'=>'Saint Barthelemy','GF'=>'French Guyana','GP'=>'Guadelope','MF'=>'Saint Martin (French Part)','MQ'=>'Martinique','RE'=>'Reunion','PF'=>'French Polynesia','TF'=>'French Southern Territories','YT'=>'Mayotte','NC'=>'New Caledonia','PM'=>'Saint Pierre et Miquelon','WF'=>'Wallis and Futuna Islands');
192
+  }
193
+
194
+  # output loop
195
+  foreach($to_generate as $country_code=>$country_name) {
196
+   # fixes for fields duplicating country code
197
+   #print "CHECKSUM-BEFORE[$country_code] = $iban_electronic_example\n";
198
+   $iban_electronic_example = iban_set_checksum($country_code .  substr($iban_electronic_example,2));
199
+   #print "CHECKSUM-AFTER[$country_code]  = $iban_electronic_example\n";
200
+   $iban_structure = $country_code . substr($iban_structure,2);
201
+   $iban_regex = '^' . $country_code . substr($iban_regex,3);
202
+   # output
203
+   print "$country_code|$country_name|$domestic_example|$bban_example|$bban_structure|$bban_regex|$bban_length|$iban_electronic_example|$iban_structure|$iban_regex|$iban_length|$bban_bankid_start_offset|$bban_bankid_stop_offset|$bban_branchid_start_offset|$bban_branchid_stop_offset|$registry_edition|$country_sepa\n";
204
+  }
205
+
206
+ }
207
+}
208
+
209
+# swift_to_regex()
210
+#  converts the SWIFT IBAN format specifications to regular expressions
211
+#  eg: 4!n6!n1!n -> ^(\d{4})(\d{6})(\d{1})$
212
+function swift_to_regex($swift) {
213
+ # first find tokens
214
+ $matches = swift_tokenize($swift);
215
+ # now replace bits
216
+ $tr = '^' . $swift . '$';
217
+ # loop through each matched token
218
+ for($i=0;$i<count($matches[0]);$i++) {
219
+  # calculate replacement
220
+  $replacement = '(TOKEN)';
221
+  # type 'n'
222
+  if($matches[3][$i] == 'n') { 
223
+   $replacement = '(\d{length})';
224
+  }
225
+  # type 'c'
226
+  elseif($matches[3][$i] == 'c') {
227
+   $replacement = '([A-Za-z0-9]{length})';
228
+  }
229
+  # type 'a'
230
+  elseif($matches[3][$i] == 'a') {
231
+   $replacement = '([A-Z]{length})';
232
+#' . $matches[1][$i] . '})';
233
+  }
234
+  else {
235
+   print "unknown type: $matches[3][$i]\n";
236
+   exit(1);
237
+  }
238
+  # now add length indicator to the token
239
+  $length = '(LENGTH)';
240
+  if($matches[2][$i] == '!') {
241
+    $length = $matches[1][$i];
242
+  }
243
+  else {
244
+   $length = '1,' . $matches[1][$i];
245
+  }
246
+  $replacement = preg_replace('/length/',$length,$replacement,1);
247
+  # finally, replace the entire token with the replacement
248
+  $tr = preg_replace('/' . $matches[0][$i] . '/',$replacement,$tr,1);
249
+ }
250
+ return $tr;
251
+}
252
+
253
+# swift_tokenize()
254
+#  fetch individual tokens in a swift structural string
255
+function swift_tokenize($string,$calculate_offsets=0) {
256
+ preg_match_all('/((?:\d*?[1-2])?\d)(!)?([anc])/',$string,$matches);
257
+ if($calculate_offsets) {
258
+  $current_offset=0;
259
+  for($i=0;$i<count($matches[0]);$i++) {
260
+   $matches['offset'][$i] = $current_offset;
261
+   $current_offset+=$matches[1][$i];
262
+  }
263
+  #print "ANALYSE[raw]: " . join(',',$matches['offset']);
264
+ }
265
+ return $matches;
266
+}
267
+
268
+?>
... ...
@@ -0,0 +1,91 @@
1
+<?php
2
+
3
+# Basic test script for the object-oriented version of the php-iban library
4
+#  - Exit status is 0 in case of success, or the number of failed tests
5
+#    in case of failure
6
+
7
+# Engine configuration
8
+#  - first we enable error display
9
+ini_set('display_errors',1);
10
+#  - next we ensure that all errors are displayed
11
+ini_set('error_reporting',E_ALL);
12
+
13
+# include the object oriented version of the library itself
14
+require_once(dirname(dirname(__FILE__)) . '/oophp-iban.php');
15
+
16
+# display registry contents
17
+#print_r($_iban_registry);
18
+
19
+# init
20
+$errors=0;
21
+
22
+# Try to validate an invalid IBAN
23
+$iban = "(@#(*@*ZV-This is NOT an IBAN!";
24
+$myIban = new IBAN($iban);
25
+if($myIban->verify()) {
26
+ print "ERROR: An invalid IBAN was validated!\n";
27
+ $errors++;
28
+}
29
+print "Hooray! - Invalid IBAN successfully rejected.\n\n";
30
+
31
+# Broken IIBAN
32
+$broken_iiban = 'AA12011123ZS6';
33
+$myIban = new IBAN($broken_iiban);
34
+$suggestions = $myIban->MistranscriptionSuggestions();
35
+if(count($suggestions)) {
36
+ print "Hooray!  Successfully derived '" . implode(',',$suggestions) . "' as likely transcription error source suggestion(s) for the incorrect IBAN $broken_iiban.\n";
37
+}
38
+else {
39
+ print "ERROR: Not able to ascertain suggested transcription error source(s) for $broken_iiban.\n";
40
+}
41
+print "\n";
42
+
43
+# Get list of countries
44
+$countries = $myIban->Countries();
45
+
46
+# Loop through the registry's examples, validating
47
+foreach($countries as $countrycode) {
48
+
49
+ # instantiate
50
+ $myCountry = new IBANCountry($countrycode);
51
+
52
+ # start section
53
+ print "[$countrycode: " . $myCountry->Name() . "]\n";
54
+
55
+ # output remaining country properties
56
+ print "Is a SEPA member? ";
57
+ if($myCountry->IsSEPA()) { print "Yes"; } else { print "No"; }
58
+ print ".\n";
59
+
60
+ # get example iban
61
+ $myIban = new IBAN($myCountry->IBANExample());
62
+
63
+ # output example iban properties one by one
64
+ print "Example IBAN: " . $myIban->HumanFormat() . "\n";
65
+ print " - country  " . $myIban->Country() . "\n";
66
+ print " - checksum " . $myIban->Checksum() . "\n";
67
+ print " - bban     " . $myIban->BBAN() . "\n";
68
+ print " - bank     " . $myIban->Bank() . "\n";
69
+ print " - branch   " . $myIban->Branch() . "\n";
70
+ print " - account  " . $myIban->Account() . "\n";
71
+ 
72
+ # output all properties
73
+ #$parts = $myIban->Parts();
74
+ #print_r($parts);
75
+ 
76
+ # verify
77
+ print "\nChecking validity... ";
78
+ if($myIban->Verify()) {
79
+  print "IBAN $myIban->iban is valid.\n";
80
+ }
81
+ else {
82
+  print "ERROR: IBAN $myIban->iban is invalid.\n";
83
+  $errors++;
84
+ }
85
+
86
+ print "\n";
87
+}
88
+
89
+exit($errors);
90
+
91
+?>
... ...
@@ -0,0 +1,86 @@
1
+<?php
2
+
3
+# Basic test script for the php-iban library
4
+#  - Exit status is 0 in case of success, or the number of failed tests
5
+#    in case of failure
6
+
7
+# Engine configuration
8
+#  - first we enable error display
9
+ini_set('display_errors',1);
10
+#  - next we ensure that all errors are displayed
11
+ini_set('error_reporting',E_ALL);
12
+
13
+# include the library itself
14
+require_once(dirname(dirname(__FILE__)) . '/php-iban.php');
15
+
16
+# display registry contents
17
+#print_r($_iban_registry);
18
+
19
+# init
20
+$errors=0;
21
+
22
+# Try to validate an invalid IBAN
23
+$iban = "(@#(*@*ZV-This is NOT an IBAN!";
24
+if(verify_iban($iban)) {
25
+ print "ERROR: An invalid IBAN was validated!\n";
26
+ $errors++;
27
+}
28
+print "Hooray! - Invalid IBAN successfully rejected.\n\n";
29
+
30
+# Broken IIBAN
31
+$broken_iiban = 'AA12011123ZS6';
32
+$suggestions = iban_mistranscription_suggestions($broken_iiban);
33
+if(count($suggestions)) {
34
+ print "Hooray!  Successfully derived '" . implode(',',$suggestions) . "' as likely transcription error source suggestion(s) for the incorrect IBAN $broken_iiban.\n";
35
+}
36
+else {
37
+ print "ERROR: Not able to ascertain suggested transcription error source(s) for $broken_iiban.\n";
38
+}
39
+print "\n";
40
+
41
+# Loop through the registry's examples, validating
42
+foreach($_iban_registry as $country) {
43
+
44
+ # get country code
45
+ $countrycode = $country['country'];
46
+
47
+ # start section
48
+ print "[$countrycode: " . iban_country_get_country_name($countrycode) . "]\n";
49
+
50
+ # output remaining country properties
51
+ print "Is a SEPA member? ";
52
+ if(iban_country_is_sepa($countrycode)) { print "Yes"; } else { print "No"; }
53
+ print ".\n";
54
+
55
+ # get example iban
56
+ $iban = $country['iban_example'];
57
+
58
+ # output example iban properties one by one
59
+ print "Example IBAN: " . iban_to_human_format($iban) . "\n";
60
+ print " - country  " . iban_get_country_part($iban) . "\n";
61
+ print " - checksum " . iban_get_checksum_part($iban) . "\n";
62
+ print " - bban     " . iban_get_bban_part($iban) . "\n";
63
+ print " - bank     " . iban_get_bank_part($iban) . "\n";
64
+ print " - branch   " . iban_get_branch_part($iban) . "\n";
65
+ print " - account  " . iban_get_account_part($iban) . "\n";
66
+ 
67
+ # output all properties
68
+ #$parts = iban_get_parts($iban);
69
+ #print_r($parts);
70
+ 
71
+ # verify
72
+ print "\nChecking validity... ";
73
+ if(verify_iban($iban)) {
74
+  print "IBAN $iban is valid.\n";
75
+ }
76
+ else {
77
+  print "ERROR: IBAN $iban is invalid.\n";
78
+  $errors++;
79
+ }
80
+
81
+ print "\n";
82
+}
83
+
84
+exit($errors);
85
+
86
+?>
... ...
@@ -193,6 +193,11 @@ function in_homedir($path)
193 193
   return strncmp($_SESSION['userinfo']['homedir'], $path, count($_SESSION['userinfo']['homedir'])) == 0;
194 194
 }
195 195
 
196
+function check_date( $input )
197
+{
198
+  return (bool) preg_match("/[0-9]{4}-(0?[1-9]|11|12)-([012]?[0-9]|30|31)/", $input);
199
+}
200
+
196 201
 
197 202
 function check_emailaddr( $input )
198 203
 {
... ...
@@ -72,5 +72,35 @@ if (! $show_paid) {
72 72
 output('<p>'.internal_link('upcoming', 'Zukünftige Rechnungsposten anzeigen').'</p>');
73 73
 
74 74
 
75
+output('<h3>Bezahlung per Lastschrift</h3>');
76
+
77
+output('<p>Gerne buchen wir Ihre Beiträge von Ihrem Konto ab. Bei Lastschriftzahlung werden Sie durch Zustellung einer Rechnung informiert, die Abbuchung erfolgt dann eine Woche später.</p>');
78
+
79
+$mandate = get_sepamandate();
80
+if ($mandate) {
81
+  output('<p>Folgende Mandate sind bisher erteilt worden (momentan gültiges Mandat ist Fett dargestellt):</p>
82
+<table>
83
+<tr><th>Mandatsreferenz</th><th>IBAN</th><th>Gültigkeit</th></tr
84
+');
85
+  foreach ($mandate as $m) {
86
+    $gueltig = 'ab '.$m['gueltig_ab'];
87
+    if ($m['gueltig_bis']) {
88
+      $gueltig = $m['gueltig_ab'].' - '.$m['gueltig_bis'];
89
+    }
90
+    $aktiv = false;
91
+    if ($m['gueltig_ab'] <= date('Y-m-d') && ($m['gueltig_bis'] == NULL || $m['gueltig_bis'] >= date('Y-m-d'))) {
92
+      $aktiv = true;
93
+    }
94
+    output('<tr><td'.($aktiv ? ' style="font-weight: bold;"' : '').'>'.internal_link('sepamandat_detail', $m['mandatsreferenz'], 'ref='.$m['mandatsreferenz']).'</td><td>'.$m['iban'].'</td><td>'.$gueltig.'</td></tr>');
95
+  }
96
+  output('</table>');
97
+}
98
+
99
+
100
+addnew('sepamandat', 'Erteilen Sie uns ein Lastschrift-Mandat');
101
+
102
+output('<p>Sie können Ihr Mandat jederzeit widerrufen. Senden Sie uns dazu bitte eine entsprechende E-Mail.</p>');
103
+
104
+
75 105
 
76 106
 ?>
... ...
@@ -17,6 +17,9 @@ Nevertheless, in case you use a significant part of this code, we ask (but not r
17 17
 require_once('inc/base.php');
18 18
 require_once('inc/security.php');
19 19
 
20
+include("external/php-iban/php-iban.php");
21
+
22
+
20 23
 function my_invoices()
21 24
 {
22 25
   $c = (int) $_SESSION['customerinfo']['customerno'];
... ...
@@ -161,6 +164,63 @@ function generate_bezahlcode_image($id)
161 164
 }
162 165
 
163 166
 
167
+function get_sepamandate() 
168
+{
169
+  $cid = (int) $_SESSION['customerinfo']['customerno'];
170
+  $result = db_query("SELECT id, mandatsreferenz, erteilt, medium, gueltig_ab, gueltig_bis, erstlastschrift, kontoinhaber, adresse, iban, bic, bankname FROM kundendaten.sepamandat WHERE kunde={$cid}");
171
+  $ret = array();
172
+  while ($entry = mysql_fetch_assoc($result)) {
173
+    array_push($ret, $entry);
174
+  }
175
+  return $ret;
176
+}
177
+
178
+
179
+function yesterday($date) 
180
+{
181
+  $date = mysql_real_escape_string($date);
182
+  $result = db_query("SELECT '{$date}' - INTERVAL 1 DAY");
183
+  return mysql_fetch_array($result)[0];
184
+}
185
+
186
+
187
+function invalidate_sepamandat($id, $date) 
188
+{
189
+  $cid = (int) $_SESSION['customerinfo']['customerno'];
190
+  $id = (int) $id;
191
+  $date = mysql_real_escape_string($date);
192
+  db_query("UPDATE kundendaten.sepamandat SET gueltig_bis='{$date}' WHERE id={$id} AND kunde={$cid}");
193
+}
194
+
195
+
196
+function sepamandat($name, $adresse, $iban, $bankname, $bic, $gueltig_ab)
197
+{
198
+  $cid = (int) $_SESSION['customerinfo']['customerno'];
199
+  if ($gueltig_ab < date('Y-m-d')) {
200
+    system_failure('Das Mandat kann nicht rückwirkend erteilt werden. Bitte geben Sie ein Datum in der Zukunft an.');
201
+  }
202
+  $alte_mandate = get_sepamandate();
203
+  $referenzen = array();
204
+  foreach ($alte_mandate as $mandat) {
205
+    if ($mandat['gueltig_bis'] == NULL || $mandat['gueltig_bis'] >= $gueltig_ab) {
206
+      DEBUG('Altes Mandat wird für ungültig erklärt.');
207
+      DEBUG($mandat);
208
+      invalidate_sepamandat($mandat['id'], yesterday($gueltig_ab));
209
+    }
210
+    array_push($referenzen, $mandat['mandatsreferenz']);
211
+  }
212
+  $counter = 1;
213
+  $referenz = sprintf('K%04d-M%03d', $cid, $counter);
214
+  while (in_array($referenz, $referenzen)) {
215
+    $counter++;
216
+    $referenz = sprintf('K%04d-M%03d', $cid, $counter);
217
+  }
218
+  DEBUG('Nächste freie Mandatsreferenz: '. $referenz);
219
+
220
+  $today = date('Y-m-d');
221
+  db_query("INSERT INTO kundendaten.sepamandat (mandatsreferenz, kunde, erteilt, medium, gueltig_ab, kontoinhaber, adresse, iban, bic, bankname) VALUES ('{$referenz}', {$cid}, '{$today}', 'online', '{$gueltig_ab}', '{$name}', '{$adresse}', '{$iban}', '{$bic}', '{$bankname}')");
222
+}
223
+
224
+
164 225
 
165
-# bank://singlepaymentsepa?name=SCHOKOKEKS.ORG%20GBR&reason=RE%20256%20KD%2032%20vom%202008-03-01&iban=DE91602911200041512006&bic=GENODES1VBK&amount=45%2C00
166 226
 ?>
... ...
@@ -0,0 +1,82 @@
1
+<?php
2
+/*
3
+This file belongs to the Webinterface of schokokeks.org Hosting
4
+
5
+Written 2008-2013 by schokokeks.org Hosting, namely
6
+  Bernd Wurst <bernd@schokokeks.org>
7
+  Hanno Böck <hanno@schokokeks.org>
8
+
9
+To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty.
10
+
11
+You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see 
12
+http://creativecommons.org/publicdomain/zero/1.0/
13
+
14
+Nevertheless, in case you use a significant part of this code, we ask (but not require, see the license) that you keep the authors' names in place and return your changes to the public. We would be especially happy if you tell us what you're going to do with this code.
15
+*/
16
+
17
+require_role(ROLE_CUSTOMER);
18
+
19
+require('invoice.php');
20
+require_once("inc/debug.php");
21
+global $debugmode;
22
+
23
+$section = 'invoice_current';
24
+
25
+
26
+if ($_GET['action'] == 'new')
27
+{
28
+  check_form_token('sepamandat_neu');
29
+  
30
+  $gueltig_ab = $_REQUEST['gueltig_ab'];
31
+  if ($gueltig_ab == 'datum') {
32
+    $gueltig_ab = $_REQUEST['gueltig_ab_datum_year'].'-'.$_REQUEST['gueltig_ab_datum_month'].'-'.$_REQUEST['gueltig_ab_datum_day'];
33
+  }
34
+  if (! check_date($gueltig_ab)) {
35
+    system_failure('Konnte das Datum nicht auslesen');
36
+  }
37
+  DEBUG('Gültig ab: '.$gueltig_ab);
38
+
39
+  if (empty($_REQUEST['kontoinhaber'])) {
40
+    system_failure('Bitte geben Sie den Kontoinhaber so an, wie dies bei Ihrer Bank hinterlegt ist.');
41
+  }
42
+  $name = $_REQUEST['kontoinhaber'];
43
+  DEBUG('Kontoinhaber:'.$name);
44
+
45
+  if (empty($_REQUEST['adresse'])) {
46
+    system_failure('Bitte geben Sie die Adresse des Kontoinhabers an.');
47
+  }
48
+  $adresse = $_REQUEST['adresse'];
49
+  DEBUG('Adresse: '.$adresse);
50
+
51
+  if (empty($_REQUEST['iban'])) {
52
+    system_failure('Es wurde keine IBAN angegeben.');
53
+  }
54
+  $iban = $_REQUEST['iban'];
55
+  if (! verify_iban($iban)) {
56
+    system_failure("Die IBAN scheint nicht korrekt zu sein!");
57
+  }
58
+  DEBUG('IBAN: '.$iban);
59
+
60
+  $bankname = $_REQUEST['bankname'];
61
+  if (empty($_REQUEST['bankname'])) {
62
+    system_failure('Bitte geben Sie den Namen der Bank an.');
63
+  }
64
+  DEBUG('Bank: '.$bankname);
65
+
66
+  $bic = NULL;
67
+  if (empty($_REQUEST['bic'])) {
68
+    if (substr($iban, 0, 2) == 'DE') {
69
+      $bic=NULL;
70
+    } else {
71
+      system_failure('Sie haben keinen BIC angegeben. Für Konten außerhalb Deutschlands ist ein BIC weiterhin erforderlich.');
72
+    }
73
+  } else {
74
+    $bic = $_REQUEST['bic'];
75
+  } 
76
+  DEBUG('BIC: '.$bic);
77
+
78
+  
79
+  sepamandat($name, $adresse, $iban, $bankname, $bic, $gueltig_ab);
80
+  
81
+  redirect($prefix.'go/invoice/current');
82
+}
... ...
@@ -0,0 +1,75 @@
1
+<?php
2
+/*
3
+This file belongs to the Webinterface of schokokeks.org Hosting
4
+
5
+Written 2008-2013 by schokokeks.org Hosting, namely
6
+  Bernd Wurst <bernd@schokokeks.org>
7
+  Hanno Böck <hanno@schokokeks.org>
8
+
9
+To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty.
10
+
11
+You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see 
12
+http://creativecommons.org/publicdomain/zero/1.0/
13
+
14
+Nevertheless, in case you use a significant part of this code, we ask (but not require, see the license) that you keep the authors' names in place and return your changes to the public. We would be especially happy if you tell us what you're going to do with this code.
15
+*/
16
+
17
+require_once('invoice.php');
18
+
19
+require_role(ROLE_CUSTOMER);
20
+$section = 'invoice_current';
21
+
22
+title('Erteilung eines Mandats zur SEPA-Basis-Lastschrift');
23
+
24
+output('<p>Ich ermächtige die Firma schokokeks.org GbR, Zahlungen von meinem Konto mittels Lastschrift
25
+einzuziehen. Zugleich weise ich mein Kreditinstitut an, die von der Firma schokokeks.org GbR auf mein
26
+Konto gezogenen Lastschriften einzulösen.</p>
27
+<p>Hinweis: Ich kann innerhalb von acht Wochen, beginnend mit dem Belastungsdatum, die Erstattung des
28
+belasteten Betrages verlangen. Es gelten dabei diemit meinem Kreditinstitut vereinbarten Bedingungen.
29
+Insbesondere fallen bei Zurückweisung einer gerechtfertigten Abbuchung i.d.R. Gebühren an.</p>');
30
+
31
+$name = $_SESSION['customerinfo']['name'];
32
+if ($_SESSION['customerinfo']['company']) {
33
+  if ($_SESSION['customerinfo']['name']) {
34
+    $name = $_SESSION['customerinfo']['company'] .' / '. $_SESSION['customerinfo']['name'];
35
+  } else {
36
+    $name = $_SESSION['customerinfo']['company'];
37
+  }
38
+}
39
+output('<p>Dieses Mandat gilt für Forderungen bzgl. der Kundennummer <strong>'.$_SESSION['customerinfo']['customerno'].'</strong> ('.$name.'). Sämtliche Forderungen werden mindestens 6 Bankarbeitstage vor Fälligkeit angekündigt. Diese Ankündigung erfolgt in der Regel durch Zusendung einer Rechnung per E-Mail.</p>');
40
+
41
+
42
+
43
+$first_date = date('Y-m-d');
44
+$invoices = my_invoices();
45
+foreach ($invoices as $i) {
46
+  if ($i['bezahlt'] == 0 && $i['datum'] < $first_date) {
47
+    $first_date = $i['datum'];
48
+  }
49
+}
50
+
51
+$html = '<h4>Gültigkeit des Mandats</h4>';
52
+$checked = False;
53
+if ($first_date != date('Y-m-d')) {
54
+  $checked = True;
55
+  $html .= '<p><input type="radio" id="gueltig_ab_'.$first_date.'" name="gueltig_ab" value="'.$first_date.'" checked="checked" /><label for="gueltig_ab_'.$first_date.'">Dieses Mandat gilt <strong>ab '.$first_date.'</strong> (Alle bisher offenen Forderungen werden ebenfalls abgebucht)</label></p>';
56
+}
57
+$html .= '<p><input type="radio" id="gueltig_ab_heute" name="gueltig_ab" value="'.date('Y-m-d').'" '.($checked ? '' : 'checked="checked"').' /><label for="gueltig_ab_heute">Dieses Mandat gilt <strong>ab heute</strong> ('.date('Y-m-d').')</label></p>';
58
+$html .= '<p><input type="radio" id="gueltig_ab_datum" name="gueltig_ab" value="datum" /><label for="gueltig_ab_datum">Dieses Mandat gilt <strong>erst ab</strong></label> '.html_datepicker("gueltig_ab_datum", time()).' (Ein eventuell zuvor erteiltes Mandat wird zu diesem Datum automatisch ungültig.)</p>';
59
+
60
+$html .= '<h4>Ihre Bankverbindung</h4>';
61
+$html .= '<table>
62
+<tr><td><label for="kontoinhaber">Name des Kontoinhabers:</label></td><td><input type="text" name="kontoinhaber" id="kontoinhaber" value="'.$_SESSION['customerinfo']['name'].'" /></td></tr>
63
+<tr><td><label for="adresse">Adresse des Kontoinhabers:</label></td><td><textarea cols="50" lines="2" name="adresse" id="adresse"></textarea></td></tr>
64
+<tr><td><label for="iban">IBAN:</label></td><td><input type="text" name="iban" id="iban" /></td></tr>
65
+<tr><td><label for="bankname">Name der Bank:</label></td><td><input type="text" name="bankname" id="bankname" /></td></tr>
66
+<tr><td><label for="bic">BIC:</label></td><td><input type="text" name="bic" id="bic" /></td></tr>
67
+</table>';
68
+
69
+$html .= '<p><input type="submit" value="Mandat erteilen" /></p>';
70
+
71
+
72
+output(html_form('sepamandat_neu', 'save', 'action=new', $html));
73
+
74
+
75
+?>
... ...
@@ -0,0 +1,93 @@
1
+<?php
2
+/*
3
+This file belongs to the Webinterface of schokokeks.org Hosting
4
+
5
+Written 2008-2013 by schokokeks.org Hosting, namely
6
+  Bernd Wurst <bernd@schokokeks.org>
7
+  Hanno Böck <hanno@schokokeks.org>
8
+
9
+To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty.
10
+
11
+You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see 
12
+http://creativecommons.org/publicdomain/zero/1.0/
13
+
14
+Nevertheless, in case you use a significant part of this code, we ask (but not require, see the license) that you keep the authors' names in place and return your changes to the public. We would be especially happy if you tell us what you're going to do with this code.
15
+*/
16
+
17
+require_once('invoice.php');
18
+
19
+require_role(ROLE_CUSTOMER);
20
+$section = 'invoice_current';
21
+
22
+title('Daten Ihres Lastschrift-Mandats');
23
+
24
+$ref = $_REQUEST['ref'];
25
+$mandate = get_sepamandate();
26
+$m = NULL;
27
+
28
+foreach ($mandate as $man) {
29
+  if ($man['mandatsreferenz'] == $ref) {
30
+    $m = $man;
31
+  }
32
+}
33
+if (! $m) {
34
+  system_failure('Konnte das Mandat nicht finden.');
35
+}
36
+
37
+if ($m['medium'] == 'legacy') {
38
+  output('<p>Sie hatten uns vor längerer Zeit eine Einzugsermächtigung ausgesprochen. Wir haben diese selbstständig in das nachstehende SEPA-Mandat überführt.</p>');
39
+} else {
40
+  $medium = 'über unser Webinterface';
41
+  switch ($m['medium']) {
42
+    case 'email': 
43
+      $medium = 'per E-Mail';
44
+      break;
45
+    case 'fax':
46
+      $medium = 'per Fax';
47
+      break;
48
+    case 'post':
49
+      $medium = 'per Post';
50
+      break;
51
+  }
52
+  output('<p>Wir haben das nachstehende Mandat am '.$m['erteilt'].' '.$medium.' entgegen genommen.</p>');
53
+}
54
+output('<h3>Stammdaten</h3>
55
+<dl>
56
+<dt>Mandatsreferenz</dt><dd>'.$m['mandatsreferenz'].'</dd>
57
+<dt>Unsere Gläubiger-ID<dt><dd>'.config('glaeubiger_id').'</dd>
58
+</dl>');
59
+
60
+output('<h3>Gültigkeit</h3>');
61
+
62
+$gueltigkeit = 'ab '.$m['gueltig_ab'];
63
+if ($m['gueltig_bis']) {
64
+  $gueltigkeit = 'von '.$m['gueltig_ab'].' bis '.$m['gueltig_bis'];
65
+}
66
+if ($m['gueltig_ab'] <= date('Y-m-d') && ($m['gueltig_bis'] == NULL || $m['gueltig_bis'] >= date('Y-m-d'))) {
67
+  output('<p>Das Mandat ist momentan gültig ('.$gueltigkeit.').</p>');
68
+} elseif ($m['gueltig_ab'] > date('Y-m-d')) {
69
+  output('<p>Das Mandat ist noch nicht gültig ('.$gueltigkeit.').</p>');
70
+} else {
71
+  output('<p>Das Mandat ist erloschen ('.$gueltigkeit.').</p>');
72
+}
73
+
74
+if ($m['erstlastschrift'] == NULL) {
75
+  output('<p>Es wurden bisher keine Abbuchungen mit Bezug auf dieses Mandat durchgeführt.</p>');
76
+} elseif ($m['letztelastschrift'] != NULL) {
77
+  output('<p>Dieses Mandat wurde zuletzt für eine Abbuchung am '.$m['letztelastschrift'].' in Anspruch genommen.</p>');
78
+}
79
+
80
+
81
+output('<h3>Kontodaten</h3>
82
+<dl>
83
+<dt>Kontoinhaber</dt><dd>'.$m['kontoinhaber'].'</dd>
84
+<dt>Adresse des Kontoinhabers</dt><dd>'.nl2br($m['adresse']).'</dd>
85
+<dt>IBAN</dt><dd>'.$m['iban'].'</dd>
86
+<dt>Name der Bank</dt><dd>'.$m['bankname'].'</dd>
87
+<dt>BIC</dt><dd>'.$m['bic'].'</dd>
88
+</dl>');
89
+
90
+
91
+output('<p>'.internal_link('current', 'Zurück').'</p>');
92
+
93
+?>
0 94