Sepa::validateIBAN()   A
last analyzed

Complexity

Conditions 5
Paths 7

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 14
dl 0
loc 22
rs 9.4888
c 0
b 0
f 0
cc 5
nc 7
nop 1
1
<?php
2
namespace SKien\Sepa;
3
4
/**
5
 * Package to create sepa-xml File.
6
 *
7
 * #### Providing
8
 * <ul>
9
 * <li> Credit Transfer Initiation (CCT; pain.001.002.03.xsd) </li>
10
 * <li> Direct Debit Initiation (CDD; pain.008.002.02.xsd) </li>
11
 * </ul>
12
 *
13
 * #### Main class of the package
14
 * This class containins some global constants, support for country specific
15
 * validation of IBAN / BIC / CI and language support for the generated
16
 * error messages.
17
 *
18
 * @package Sepa
19
 * @author Stefanius <[email protected]>
20
 * @copyright MIT License - see the LICENSE file for details
21
 */
22
class Sepa
23
{
24
    use SepaHelper;
25
26
    /** Version 2.6 (2012: pain.001.002.03 / pain.008.002.02)*/
27
    const V26 = "2.6";
28
    /** Version 2.9 (2015: pain.001.003.03 / pain.008.003.02)*/
29
    const V29 = "2.9";
30
    /** Version 3.0 (2016: pain.001.001.03 / pain.008.001.02)*/
31
    const V30 = "3.0";
32
33
    /** Credit Transfer Transaction  */
34
    const CCT = "TRF";
35
    /** Direct Debit Transaction     */
36
    const CDD = "DD";
37
38
    /** ID1 validation
39
     * @see SepaHelper::validString()   */
40
    const ID1     = 1;
41
    /** ID2 validation
42
     * @see SepaHelper::validString()   */
43
    const ID2     = 2;
44
    /** MAX35 validation
45
     * @see SepaHelper::validString()   */
46
    const MAX35   = 3;
47
    /** MAX70 validation
48
     * @see SepaHelper::validString()   */
49
    const MAX70   = 4;
50
    /** MAX140 validation
51
     * @see SepaHelper::validString()   */
52
    const MAX140  = 5;
53
    /** MAX1025 validation
54
     * @see SepaHelper::validString()   */
55
    const MAX1025 = 6;
56
57
    /** sequence type first dd sequence     */
58
    const SEQ_FIRST     = "FRST";
59
    /** sequence type recurrent dd sequence */
60
    const SEQ_RECURRENT = "RCUR";
61
    /** sequence type one-off dd sequence   */
62
    const SEQ_ONE_OFF   = "OOFF";
63
    /** sequence type final dd sequence */
64
    const SEQ_FINAL     = "FNAL";
65
66
    /** full validation  */
67
    const V_FULL_VALIDATION         = 0;
68
    /** no validation at all   */
69
    const V_NO_VALIDATION           = 0x001F;
70
    /** no validation of IBAN   */
71
    const V_NO_IBAN_VALIDATION      = 0x0001;
72
    /** no validation of the BIC   */
73
    const V_NO_BIC_VALIDATION       = 0x0002;
74
    /** no validation of the CI   */
75
    const V_NO_CI_VALIDATION        = 0x0004;
76
    /** no validation if no class set for country  */
77
    const V_IGNORE_MISSING_CNTRY    = 0x0008;
78
    /** ignore missing mandatory value   */
79
    const V_IGNORE_MISSING_VALUE    = 0x0010;
80
81
    /** validation succeeded    */
82
    const OK                        = 0;
83
    // error codes for IBAN validation
84
    /** invalid country code   */
85
    const ERR_IBAN_INVALID_CNTRY    = 1;
86
    /** invalid length  */
87
    const ERR_IBAN_INVALID_LENGTH   = 2;
88
    /** iban contains invalid sign(s)   */
89
    const ERR_IBAN_INVALID_SIGN     = 3;
90
    /** wrong checksum  */
91
    const ERR_IBAN_CHECKSUM         = 4;
92
93
    // error codes for BIC validation
94
    /** invalid BIC */
95
    const ERR_BIC_INVALID           = 10;
96
    /** invalid country code   */
97
    const ERR_BIC_INVALID_CNTRY     = 11;
98
99
    // error codes for CI validation
100
    /** invalid country code  */
101
    const ERR_CI_INVALID_CNTRY      = 20;
102
    /** invalid length  */
103
    const ERR_CI_INVALID_LENGTH     = 21;
104
    /** iban contains invalid sign(s)   */
105
    const ERR_CI_INVALID_SIGN       = 22;
106
    /** wrong checksum  */
107
    const ERR_CI_CHECKSUM           = 23;
108
109
    // error codes for payment info validation
110
    const ERR_PMT_NAME_MISSING      = 0x0001;
111
    const ERR_PMT_IBAN_MISSING      = 0x0002;
112
    const ERR_PMT_BIC_MISSING       = 0x0004;
113
    const ERR_PMT_CI_MISSING        = 0x0008;
114
    const ERR_PMT_INVALID_IBAN      = 0x0010;
115
    const ERR_PMT_INVALID_BIC       = 0x0020;
116
    const ERR_PMT_INVALID_CI        = 0x0040;
117
    const ERR_PMT_SEQ_TYPE_MISSING  = 0x0080;
118
    const ERR_PMT_INVALID_SEQ_TYPE  = 0x0100;
119
    const ERR_PMT_MAX               = 0x0100;
120
121
    // error codes for transaction validation
122
    const ERR_TX_NAME_MISSING       = 0x0001;
123
    const ERR_TX_IBAN_MISSING       = 0x0002;
124
    const ERR_TX_BIC_MISSING        = 0x0004;
125
    const ERR_TX_INVALID_IBAN       = 0x0010;
126
    const ERR_TX_INVALID_BIC        = 0x0020;
127
    const ERR_TX_MAND_ID_MISSING    = 0x0200;
128
    const ERR_TX_MAND_DOS_MISSING   = 0x0400;
129
    const ERR_TX_DESCR_MISSING      = 0x0800;
130
    const ERR_TX_ZERO_VALUE         = 0x1000;
131
    const ERR_TX_INVALID_TYPE       = 0x2000;
132
    const ERR_TX_INVALID_MAND_DOS   = 0x4000;
133
    const ERR_TX_MAX                = 0x4000;
134
135
    /** @var array<string>  validation classes for different countries     */
136
    static protected array $aValidation = array();
137
    /** @var int set the validation level. Any combination of the self::V_... flags (default: V_FULL)    */
138
    static protected int $wValidation = 0;
139
140
    /** @var array<string> error messages for IBAN validation   */
141
    static protected array $aIBANError = array();
142
    /** @var array<string> error messages for BIC validation   */
143
    static protected array $aBICError = array();
144
    /** @var array<string> error messages for CI validation   */
145
    static protected array $aCIError = array();
146
    /** @var array<string> error messages for payment info validation   */
147
    static protected array $aPmtInfError = array();
148
    /** @var array<string> error messages for transaction info validation   */
149
    static protected array $aTxInfError = array();
150
151
    /**
152
     * Get the pain version for requested SEPA version and -type.
153
     * The pain version is needed to bind the SEPA XML document to the correct namespace and
154
     * can be used to validate any sepa document against the corrresponding XSD schema.
155
     * @param string $strType
156
     * @param string $strSepaVersion
157
     * @return string
158
     */
159
    public static function getPainVersion(string $strType, string $strSepaVersion = Sepa::V30) : string
160
    {
161
        $aPainVersion = [
162
            self::V26 => [
163
                self::CCT => 'pain.001.002.03',
164
                self::CDD => 'pain.008.002.02',
165
                'year' => '2012',
166
            ],
167
            self::V29 => [
168
                self::CCT => 'pain.001.003.03',
169
                self::CDD => 'pain.008.003.02',
170
                'year' => '2015',
171
            ],
172
            self::V30 => [
173
                self::CCT => 'pain.001.001.03',
174
                self::CDD => 'pain.008.001.02',
175
                'year' => '2016',
176
            ],
177
        ];
178
        if (!isset($aPainVersion[$strSepaVersion])) {
179
            trigger_error('Not supported SEPA Version: ' . $strSepaVersion . ' (Supported versions: ' . implode(', ', array_keys($aPainVersion)) . ')!', E_USER_ERROR);
180
        }
181
        return $aPainVersion[$strSepaVersion][$strType];
182
    }
183
184
    /**
185
     * Initializition of the package.
186
     * This static method must be called before using the package. <br>
187
     * All available country validations are added and the errormessages are
188
     * initialized in english language.
189
     */
190
    public static function init() : void
191
    {
192
        if (count(self::$aValidation) > 0) {
193
            return;
194
        }
195
        self::addValidation('DE', 'SKien\Sepa\CntryValidation\SepaCntryValidationDE');
196
        self::addValidation('CH', 'SKien\Sepa\CntryValidation\SepaCntryValidationCH');
197
        self::addValidation('FR', 'SKien\Sepa\CntryValidation\SepaCntryValidationFR');
198
        self::addValidation('AT', 'SKien\Sepa\CntryValidation\SepaCntryValidationAT');
199
        self::addValidation('LU', 'SKien\Sepa\CntryValidation\SepaCntryValidationLU');
200
        self::addValidation('BE', 'SKien\Sepa\CntryValidation\SepaCntryValidationBE');
201
        self::addValidation('GB', 'SKien\Sepa\CntryValidation\SepaCntryValidationGB');
202
        self::addValidation('EE', 'SKien\Sepa\CntryValidation\SepaCntryValidationEE');
203
        self::addValidation('IT', 'SKien\Sepa\CntryValidation\SepaCntryValidationIT');
204
        self::addValidation('ES', 'SKien\Sepa\CntryValidation\SepaCntryValidationES');
205
        self::addValidation('NL', 'SKien\Sepa\CntryValidation\SepaCntryValidationNL');
206
207
        self::$aIBANError = array(
208
            Sepa::ERR_IBAN_INVALID_CNTRY   => 'The country code of the IBAN is not supported!',
209
            Sepa::ERR_IBAN_INVALID_LENGTH  => 'Invalid length of the IBAN!',
210
            Sepa::ERR_IBAN_INVALID_SIGN    => 'The IBAN contains invalid characters!',
211
            Sepa::ERR_IBAN_CHECKSUM        => 'Invalid IBAN checksum!',
212
        );
213
214
        self::$aBICError = array(
215
            Sepa::ERR_BIC_INVALID          => 'Invalid BIC!',
216
            Sepa::ERR_BIC_INVALID_CNTRY    => 'The country code of the BIC is not supported!',
217
        );
218
219
        self::$aCIError = array(
220
            Sepa::ERR_CI_INVALID_CNTRY     => 'The country code of the CI is not supported!',
221
            Sepa::ERR_CI_INVALID_LENGTH    => 'Invalid length of the CI!',
222
            Sepa::ERR_CI_INVALID_SIGN      => 'The CI contains invalid characters!',
223
            Sepa::ERR_CI_CHECKSUM          => 'Invalid CI checksum!',
224
        );
225
226
        self::$aPmtInfError = array(
227
            Sepa::ERR_PMT_NAME_MISSING      => 'Name missing',
228
            Sepa::ERR_PMT_IBAN_MISSING      => 'IBAN missing',
229
            Sepa::ERR_PMT_BIC_MISSING       => 'BIC missing',
230
            Sepa::ERR_PMT_CI_MISSING        => 'CI missing',
231
            Sepa::ERR_PMT_INVALID_IBAN      => 'Invalid IBAN',
232
            Sepa::ERR_PMT_INVALID_BIC       => 'Invalid BIC',
233
            Sepa::ERR_PMT_INVALID_CI        => 'Invalid CI',
234
            Sepa::ERR_PMT_SEQ_TYPE_MISSING  => 'Sequence type missing',
235
            Sepa::ERR_PMT_INVALID_SEQ_TYPE  => 'Invalid sequence type',
236
        );
237
238
        self::$aTxInfError = array(
239
            Sepa::ERR_TX_NAME_MISSING       => 'Name missing',
240
            Sepa::ERR_TX_IBAN_MISSING       => 'IBAN missing',
241
            Sepa::ERR_TX_BIC_MISSING        => 'BIC missing',
242
            Sepa::ERR_TX_INVALID_IBAN       => 'Invalid IBAN',
243
            Sepa::ERR_TX_INVALID_BIC        => 'Invalid BIC',
244
            Sepa::ERR_TX_MAND_ID_MISSING    => 'SEPA mandate missing',
245
            Sepa::ERR_TX_MAND_DOS_MISSING   => 'Invalid date of the SEPA mandate',
246
            Sepa::ERR_TX_DESCR_MISSING      => 'Usage text missing',
247
            Sepa::ERR_TX_ZERO_VALUE         => 'The value is 0.0 EUR',
248
            Sepa::ERR_TX_INVALID_TYPE       => 'Invalid transaction type',
249
            Sepa::ERR_TX_INVALID_MAND_DOS   => 'Invalid date value',
250
        );
251
    }
252
253
    /**
254
     * Destroing the static arrays.
255
     * This method mainly is provided for use in the PHPUnit TestCases to reset the static object!
256
     * @internal
257
     */
258
    public static function reset() : void
259
    {
260
        self::$aValidation = array();
261
        self::$wValidation = 0;
262
        self::$aIBANError = array();
263
        self::$aBICError = array();
264
        self::$aCIError = array();
265
        self::$aPmtInfError = array();
266
        self::$aTxInfError = array();
267
    }
268
269
    /**
270
     * Add validation to the package.
271
     * The PHP class `$strValidationClass` must implement the `SepaCntryValidation` interface.
272
     * @param string $strCntry  the 2 digit country code
273
     * @param string $strValidationClass    name of the PHP class for validation
274
     */
275
    public static function addValidation(string $strCntry, string $strValidationClass) : void
276
    {
277
        if (isset(self::$aValidation[$strCntry])) {
278
            trigger_error('validation for cntry ' . $strCntry . ' already defined!', E_USER_ERROR);
279
        }
280
        if (!is_subclass_of($strValidationClass, 'SKien\Sepa\CntryValidation\SepaCntryValidation', true)) {
281
            trigger_error('class ' . $strValidationClass . ' must implement SepaCntryValidation interface!', E_USER_ERROR);
282
        }
283
        self::$aValidation[$strCntry] = $strValidationClass;
284
    }
285
286
    /**
287
     * Set the validation level.
288
     * This method can be used to disable some or complete validation. <br>
289
     * > It is recommended to deactivate the validations only for test purposes or if you can
290
     * guarantee the validity of all values based on previous checks. <br>
291
     * A partial deactivation with `Sepa::V_IGNORE_MISSING_CNTRY` may be useful if you sometimes
292
     * have to process data from a country for which there is (still) no validation in the package.
293
     *
294
     * Supported is any combination of:
295
     *
296
     * | Flag                           | Description                               |
297
     * |--------------------------------|-------------------------------------------|
298
     * | `Sepa::V_NO_VALIDATION`        | no validation at all (not recommended!)   |
299
     * | `Sepa::V_NO_IBAN_VALIDATION`   | no validation of IBAN                     |
300
     * | `Sepa::V_NO_BIC_VALIDATION`    | no validation of the BIC                  |
301
     * | `Sepa::V_NO_CI_VALIDATION`     | no validation of the CI                   |
302
     * | `Sepa::V_IGNORE_MISSING_CNTRY` | no validation if no class set for country |
303
     * | `Sepa::V_IGNORE_MISSING_VALUE` | no error on missing mandatory value       |
304
     *
305
     * Default value is full validation:  `Sepa::V_FULL_VALIDATION`
306
     * @param int $wValidation  see the description
307
     */
308
    public static function setValidationLevel(int $wValidation) : void
309
    {
310
        self::$wValidation = $wValidation;
311
    }
312
313
    /**
314
     * Check, if validation level is set.
315
     * @param int $wValidation
316
     * @return bool
317
     * @internal
318
     */
319
    public static function checkValidation(int $wValidation) : bool
320
    {
321
        return (self::$wValidation & $wValidation) != 0;
322
    }
323
324
    /**
325
     * Load error messages from JSON file.
326
     * For different language support, all messages can be loaded from a JSON file.
327
     * > Use the `sepa_errormsg_en.json` or `sepa_errormsg_de.json` contained in the
328
     * package as a starting point for your own translation .
329
     * @param string $strFilename   relative or absolute JSON file
330
     */
331
    public static function loadErrorMsg(string $strFilename = 'sepa_error.json') : void
332
    {
333
        /*
334
        // ... testcode to create sample json file
335
        $aError = array( 'aIBAN' => self::$aIBANError, 'aCI' => self::$aCIError, 'aPmtInf' => self::$aPmtInfError, 'aTxInf' => self::$aTxInfError );
336
        $strJSON = json_encode($aError, JSON_PRETTY_PRINT);
337
        file_put_contents($strFilename, $strJSON);
338
        chmod($strFilename, 0666);
339
        */
340
        if (file_exists($strFilename)) {
341
            $strJson = file_get_contents($strFilename);
342
            $jsonData = json_decode((string)$strJson, true);
343
            if ($jsonData && is_array($jsonData)) {
344
                if (isset($jsonData['aIBAN']) && is_array($jsonData['aIBAN'])) {
345
                    self::$aIBANError = $jsonData['aIBAN'];
346
                }
347
                if (isset($jsonData['aCI']) && is_array($jsonData['aCI'])) {
348
                    self::$aCIError = $jsonData['aCI'];
349
                }
350
                if (isset($jsonData['aPmtInf']) && is_array($jsonData['aPmtInf'])) {
351
                    self::$aPmtInfError = $jsonData['aPmtInf'];
352
                }
353
                if (isset($jsonData['aTxInf']) && is_array($jsonData['aTxInf'])) {
354
                    self::$aTxInfError = $jsonData['aTxInf'];
355
                }
356
            } else {
357
                trigger_error('invalid error message file: ' . $strFilename, E_USER_ERROR);
358
            }
359
        } else {
360
            trigger_error('error message file ' . $strFilename . ' not exist!', E_USER_ERROR);
361
        }
362
    }
363
364
    /**
365
     * Validates given IBAN.
366
     * If the passed value contains any leading/trailing or formating spaces, they all
367
     * will be removed.
368
     * @param string $strIBAN   IBAN to validate
369
     * @return int Sepa::OK or errorcode
370
     */
371
    public static function validateIBAN(string &$strIBAN) : int
372
    {
373
        $strIBAN = str_replace(' ', '', trim(strtoupper($strIBAN)));
374
        if ((self::$wValidation & self::V_NO_IBAN_VALIDATION) != 0) {
375
            return self::OK;
376
        }
377
378
        if (count(self::$aValidation) == 0) {
379
            trigger_error('no country validation specified! (possibly forgotten to call Sepa::init()?)', E_USER_ERROR);
380
        }
381
        $strCntry = substr($strIBAN, 0, 2);
382
        if (!isset(self::$aValidation[$strCntry])) {
383
            if ((self::$wValidation & self::V_IGNORE_MISSING_CNTRY) != 0) {
384
                return Sepa::OK;
385
            } else {
386
                return Sepa::ERR_IBAN_INVALID_CNTRY;
387
            }
388
        }
389
        $strClass = self::$aValidation[$strCntry];
390
        $oValidate = new $strClass($strCntry);
391
392
        return $oValidate->validateIBAN($strIBAN); /** @phpstan-ignore-line */
393
    }
394
395
    /**
396
     * validates given BIC.
397
     * If the passed value contains any leading/trailing or formating spaces, they all
398
     * will be removed. If a 8-digit BIC is passed, it will be expanded to 11 digits using
399
     * 'XXX'.
400
     * @param string $strBIC    BIC to validate
401
     * @return int Sepa::OK or errorcode
402
     */
403
    public static function validateBIC(string &$strBIC) : int
404
    {
405
        $strBIC = str_replace(' ', '', trim(strtoupper($strBIC)));
406
        if (strlen($strBIC) == 8) {
407
            // expand an 8-digit code to 11 digits for validation...
408
            $strBIC .= 'XXX';
409
        }
410
        if ((self::$wValidation & self::V_NO_BIC_VALIDATION) != 0) {
411
            return self::OK;
412
        }
413
414
        if (count(self::$aValidation) == 0) {
415
            trigger_error('no country validation specified! (possibly forgotten to call Sepa::init()?)', E_USER_ERROR);
416
        }
417
        $strCntry = substr($strBIC, 4, 2);
418
        if (!isset(self::$aValidation[$strCntry])) {
419
            if ((self::$wValidation & self::V_IGNORE_MISSING_CNTRY) != 0) {
420
                return Sepa::OK;
421
            } else {
422
                return Sepa::ERR_BIC_INVALID_CNTRY;
423
            }
424
        }
425
        $strClass = self::$aValidation[$strCntry];
426
        $oValidate = new $strClass($strCntry);
427
428
        return $oValidate->validateBIC($strBIC); /** @phpstan-ignore-line */
429
    }
430
431
    /**
432
     * validates given CI (Creditor Scheme Identification).
433
     * If the passed value contains any leading/trailing or formating spaces, they all
434
     * will be removed.
435
     * @param string $strCI     CI to validate
436
     * @return int Sepa::OK or errorcode
437
     */
438
    public static function validateCI(string &$strCI) : int
439
    {
440
        $strCI = str_replace(' ', '', trim(strtoupper($strCI)));
441
        if ((self::$wValidation & self::V_NO_CI_VALIDATION) != 0) {
442
            return self::OK;
443
        }
444
445
        if (count(self::$aValidation) == 0) {
446
            trigger_error('no country validation specified! (possibly forgotten to call Sepa::init()?)', E_USER_ERROR);
447
        }
448
        $strCntry = substr($strCI, 0, 2);
449
        if (!isset(self::$aValidation[$strCntry])) {
450
            if ((self::$wValidation & self::V_IGNORE_MISSING_CNTRY) != 0) {
451
                return Sepa::OK;
452
            } else {
453
                return Sepa::ERR_CI_INVALID_CNTRY;
454
            }
455
        }
456
        $strClass = self::$aValidation[$strCntry];
457
        $oValidate = new $strClass($strCntry);
458
459
        return $oValidate->validateCI($strCI); /** @phpstan-ignore-line */
460
    }
461
462
    /**
463
     * Message to the given errorcode for IBAN / BIC / CI validation errors.
464
     * @param int $iError   the errorcode
465
     * @return string
466
     */
467
    public static function errorMsg(int $iError) : string
468
    {
469
        $aError = self::$aIBANError + self::$aBICError + self::$aCIError;
470
        $strMsg = 'unknown Error (' . $iError . ')!';
471
        if (isset($aError[$iError])) {
472
            $strMsg = $aError[$iError];
473
        }
474
        return $strMsg;
475
    }
476
477
    /**
478
     * Message to the given IBAN errorcode.
479
     * @param int $iError   the errorcode
480
     * @return string
481
     * @internal
482
     */
483
    public static function errorMsgIBAN(int $iError) : string
484
    {
485
        $strMsg = 'unknown Error (' . $iError . ')!';
486
        if (isset(self::$aIBANError[$iError])) {
487
            $strMsg = self::$aIBANError[$iError];
488
        }
489
        return $strMsg;
490
    }
491
492
    /**
493
     * Message to the given BIC errorcode.
494
     * @param int $iError   the errorcode
495
     * @return string
496
     * @internal
497
     */
498
    public static function errorMsgBIC(int $iError) : string
499
    {
500
        $strMsg = 'unknown Error (' . $iError . ')!';
501
        if (isset(self::$aBICError[$iError])) {
502
            $strMsg = self::$aBICError[$iError];
503
        }
504
        return $strMsg;
505
    }
506
507
    /**
508
     * Message to the given CI errorcode.
509
     * @param int $iError   the errorcode
510
     * @return string
511
     * @internal
512
     */
513
    public static function errorMsgCI(int $iError) : string
514
    {
515
        $strMsg = 'unknown Error (' . $iError . ')!';
516
        if (isset(self::$aCIError[$iError])) {
517
            $strMsg = self::$aCIError[$iError];
518
        }
519
        return $strMsg;
520
    }
521
522
    /**
523
     * Message to the given payment info errorcode.
524
     * Used by `SepaPmtInf::getError()` to get localized message.
525
     * @param int $iError
526
     * @param string $strLF
527
     * @return string
528
     * @internal
529
     */
530
    public static function errorMsgPmtInf(int $iError, string $strLF = PHP_EOL) : string
531
    {
532
        $strSep = '';
533
        $strMsg = '';
534
        for ($iCheck = 0x0001; $iCheck <= Sepa::ERR_PMT_MAX; $iCheck <<= 1) {
535
            if (($iError & $iCheck) != 0 && isset(self::$aPmtInfError[$iCheck])) {
536
                $strMsg .= $strSep . self::$aPmtInfError[$iCheck];
537
                $strSep = $strLF;
538
            }
539
        }
540
        return $strMsg;
541
    }
542
543
    /**
544
     * Message to the given transaction info errorcode.
545
     * Used by `SepaTxInf::getError()` to get localized message.
546
     * @param int $iError
547
     * @param string $strLF
548
     * @return string
549
     * @internal
550
     */
551
    public static function errorMsgTxInf(int $iError, string $strLF = PHP_EOL) : string
552
    {
553
        $strSep = '';
554
        $strMsg = '';
555
        for ($iCheck = 0x0001; $iCheck <= Sepa::ERR_TX_MAX; $iCheck <<= 1) {
556
            if (($iError & $iCheck) != 0 && isset(self::$aTxInfError[$iCheck])) {
557
                $strMsg .= $strSep . self::$aTxInfError[$iCheck];
558
                $strSep = $strLF;
559
            }
560
        }
561
        return $strMsg;
562
    }
563
}
564
565