Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Created Hungarian IBAN adapter and validator
- Loading branch information
Showing
5 changed files
with
255 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
<?php | ||
|
||
namespace Rikudou\Iban\Iban; | ||
|
||
use InvalidArgumentException; | ||
use Rikudou\Iban\Helper\ToStringIbanTrait; | ||
use Rikudou\Iban\Helper\Utils; | ||
use Rikudou\Iban\Validator\CompoundValidator; | ||
use Rikudou\Iban\Validator\GenericIbanValidator; | ||
use Rikudou\Iban\Validator\HungarianIbanValidator; | ||
use Rikudou\Iban\Validator\ValidatorInterface; | ||
|
||
class HungarianIbanAdapter implements IbanInterface | ||
{ | ||
use ToStringIbanTrait; | ||
|
||
/** | ||
* @var string | ||
*/ | ||
private $account; | ||
|
||
/** | ||
* @var string|NULL | ||
*/ | ||
private $iban = null; | ||
|
||
public function __construct(string $account) | ||
{ | ||
$this->account = $account; | ||
} | ||
|
||
/** | ||
* Returns the resulting IBAN. | ||
* | ||
* @return string | ||
*/ | ||
public function asString(): string | ||
{ | ||
if ($this->iban === null) { | ||
$accountNumber = strtoupper((string) preg_replace('/[\s\-]+/', '', $this->account)); | ||
|
||
if (!in_array(strlen($accountNumber), [16, 24])) { | ||
throw new InvalidArgumentException('Account number length is not valid. It either has to consists of 16 or 24 numbers.'); | ||
} | ||
|
||
$accountNumber = str_pad($accountNumber, 24, '0'); | ||
|
||
$checkString = (string) preg_replace_callback(['/[A-Z]/', '/^[0]+/'], function ($matches) { | ||
if (substr($matches[0], 0, 1) !== '0') { // may be multiple leading 0's | ||
return base_convert($matches[0], 36, 10); | ||
} | ||
|
||
return ''; | ||
}, $accountNumber . 'HU00'); | ||
|
||
$mod = (int) Utils::bcmod($checkString, '97'); | ||
$code = (string) (98 - $mod); | ||
|
||
$this->iban = sprintf( | ||
'HU%s%s', | ||
str_pad($code, 2, '0', STR_PAD_LEFT), | ||
$accountNumber | ||
); | ||
} | ||
|
||
return $this->iban; | ||
} | ||
|
||
/** | ||
* Returns the validator that checks whether the IBAN is valid. | ||
* | ||
* @return ValidatorInterface|NULL | ||
*/ | ||
public function getValidator(): ?ValidatorInterface | ||
{ | ||
return new CompoundValidator( | ||
new HungarianIbanValidator($this->account), | ||
new GenericIbanValidator($this) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
<?php | ||
|
||
namespace Rikudou\Iban\Validator; | ||
|
||
/** | ||
* @see https://www.ecbs.org/Download/Tr201v3.9.pdf | ||
*/ | ||
class HungarianIbanValidator implements ValidatorInterface | ||
{ | ||
private const WEIGHTS = [ | ||
9, | ||
7, | ||
3, | ||
1, | ||
]; | ||
|
||
/** @var string */ | ||
private $accountNumber; | ||
|
||
public function __construct(string $accountNumber) | ||
{ | ||
$this->accountNumber = (string) preg_replace('/[\s\-]+/', '', $accountNumber); | ||
} | ||
|
||
public function isValid(): bool | ||
{ | ||
if (!in_array(strlen($this->accountNumber), [16, 24])) { | ||
return false; | ||
} | ||
|
||
$fullAccountNumber = str_pad($this->accountNumber, 24, '0'); | ||
|
||
$bankBranchPart = substr($fullAccountNumber, 0, 8); | ||
$accountNumberPart = substr($fullAccountNumber, 8); | ||
|
||
return $this->checkGroup($bankBranchPart) | ||
&& $this->checkGroup($accountNumberPart); | ||
} | ||
|
||
private function checkGroup(string $group): bool | ||
{ | ||
$length = strlen($group) - 1; | ||
$expectedChecksum = (int) substr($group, $length, 1); | ||
|
||
$sum = 0; | ||
for ($i = 0; $i < $length; $i++) { | ||
$weight = self::WEIGHTS[$i % count(self::WEIGHTS)]; | ||
$sum += (int) $group[$i] * $weight; | ||
} | ||
|
||
$lastDigit = $sum % 10; | ||
if ($lastDigit === 0) { | ||
$lastDigit = 10; | ||
} | ||
|
||
$actualChecksum = 10 - $lastDigit; | ||
|
||
return $actualChecksum === $expectedChecksum; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
<?php | ||
|
||
namespace Rikudou\Iban\Tests; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
use Rikudou\Iban\Iban\HungarianIbanAdapter; | ||
use Rikudou\Iban\Validator\ValidatorInterface; | ||
|
||
class HungarianIbanAdapterTest extends TestCase | ||
{ | ||
public function testAccountsWithoutPrefix() | ||
{ | ||
$accounts = [ | ||
// same account, different formats | ||
'11773016-11111018' => 'HU42 1177 3016 1111 1018 0000 0000', | ||
'11773016 11111018' => 'HU42 1177 3016 1111 1018 0000 0000', | ||
'1177301611111018' => 'HU42 1177 3016 1111 1018 0000 0000', | ||
'11773016-11111018-00000000' => 'HU42 1177 3016 1111 1018 0000 0000', | ||
'11773016 11111018 00000000' => 'HU42 1177 3016 1111 1018 0000 0000', | ||
'117730161111101800000000' => 'HU42 1177 3016 1111 1018 0000 0000', | ||
|
||
'12092309-80000008' => 'HU43 1209 2309 8000 0008 0000 0000', | ||
'12092309-80000008-00000000' => 'HU43 1209 2309 8000 0008 0000 0000', | ||
'12092309-00582130-00400001' => 'HU49 1209 2309 0058 2130 0040 0001', | ||
'10918001-00000117-21150000' => 'HU38 1091 8001 0000 0117 2115 0000', | ||
]; | ||
|
||
foreach ($accounts as $account => $iban) { | ||
$iban = str_replace(' ', '', $iban); | ||
|
||
$this->assertEquals($iban, $this->getAdapter($account)->asString()); | ||
$this->assertEquals($iban, strval($this->getAdapter($account))); | ||
} | ||
} | ||
|
||
public function testGetValidator() | ||
{ | ||
$this->assertInstanceOf(ValidatorInterface::class, $this->getAdapter(1325090010, 3030)->getValidator()); | ||
} | ||
|
||
/** | ||
* @param string $account | ||
* | ||
* @return HungarianIbanAdapter | ||
*/ | ||
private function getAdapter(string $account): HungarianIbanAdapter | ||
{ | ||
return new HungarianIbanAdapter($account); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
<?php | ||
|
||
namespace Rikudou\Iban\Tests; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
use Rikudou\Iban\Validator\HungarianIbanValidator; | ||
|
||
class HungarianIbanValidatorTest extends TestCase | ||
{ | ||
public function testIsValid() | ||
{ | ||
foreach ($this->getAccounts() as $account) { | ||
$this->assertEquals($account['valid'], (new HungarianIbanValidator($account['account']))->isValid()); | ||
} | ||
} | ||
|
||
private function getAccounts() | ||
{ | ||
return [ | ||
// short account | ||
[ | ||
'account' => '11773016-11111018', | ||
'valid' => true, | ||
], | ||
[ | ||
'account' => '12092309-80000008', | ||
'valid' => true, | ||
], | ||
// long account | ||
[ | ||
'account' => '11773016-11111018-00000000', | ||
'valid' => true, | ||
], | ||
[ | ||
'account' => '12092309-00582130-00400001', | ||
'valid' => true, | ||
], | ||
[ | ||
'account' => '10918001-00000117-21150000', | ||
'valid' => true, | ||
], | ||
// long account, spaces instead of dashes | ||
[ | ||
'account' => '11773016 11111018 00000000', | ||
'valid' => true, | ||
], | ||
// random invalid account | ||
[ | ||
'account' => '12345678-00000005-87654321', | ||
'valid' => false, | ||
], | ||
// wrong length | ||
[ | ||
'account' => '11773016-11111018-0000001', | ||
'valid' => false, | ||
], | ||
[ | ||
'account' => '11773016-11111018-000000015', | ||
'valid' => false, | ||
], | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters