diff --git a/.gitignore b/.gitignore index c7f0489..d76797f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ vendor/ build/ composer.lock .scannerwork/ +.idea/ diff --git a/composer.json b/composer.json index 5337ce1..8cd3dd0 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,6 @@ "description": "EU VAT numbers validation using the VIES Service of the European Commission", "keywords": ["VIES","VAT","EU","EC"], "homepage": "https://github.com/DragonBe/vies", - "minimum-stability": "stable", "license": "MIT", "authors": [ { diff --git a/src/Vies/Validator/ValidatorXI.php b/src/Vies/Validator/ValidatorXI.php new file mode 100644 index 0000000..3d27dea --- /dev/null +++ b/src/Vies/Validator/ValidatorXI.php @@ -0,0 +1,111 @@ + + * @license MIT + */ + +namespace DragonBe\Vies\Validator; + +/** + * This validator was derived from the "ValidatorGB" validator to validate the "XI" VAT numbers of + * "United Kingdom (Northern Ireland)" following the end of the Brexit transition period on December, 31 2020. + * + * It is based on the information at: + * https://www.avalara.com/vatlive/en/vat-news/brexit-northern-ireland-vat-and-eoro--xi--number.html + * + * As more information is published, this validator may be made more precise: + * + * - Remove the logic to validate Isle of Man numbers? + * - Remove the logic to validate 'GD' and 'HA' prefixes? + * + */ + +/** + * Class ValidatorXI + * @package DragonBe\Vies\Validator + * + * VAT format: [C1 C2 C3 C4 C5] + * [C1 C2 C3 C4 C5 C6 C7 C8 C9] + * [C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12] + * + * Range: + * C1 .. C2 Alphanumeric from A to Z or 0 to 9 + * C2 .. C11 Numeric from 0 to 9 + * + * Rules: + * Case 1: + * [C1 C2] Alpha: “GD” or “HA” + * C3 ... C5 Numeric from 0 to 9 + * + * if [C1 C2] = “GD” + * [C3 C4 C5] from 000 to 499 + * + * If [C1 C2] = “HA” + * [C3 C4 C5] from 500 to 999 + * + * Case 2 + * [C1 C2 C3] from 000 to 009 are numbers for Isle of Man + * [C10 C11 C12] > 000 + * [C1 C2 C3 C4 C5 C6 C7 C8 C9] >000000000 + * + * [C8 C9] + * R1 = (8*C1 + 7*C2 + 6*C3 + 5*C4 + 4*C5 + 3*C6 + 2*C7 + C8C9) modulo 97 + * R2 = ((8*C1 + 7*C2 + 6*C3 + 5*C4 + 4*C5 + 3*C6 + 2*C7 + C8C9) + 55) modulo 97 + * Either R1 or R2 must equal to zero. + * + */ +class ValidatorXI extends ValidatorAbstract +{ + /** + * {@inheritdoc} + */ + public function validate(string $vatNumber): bool + { + if (strlen($vatNumber) == 5) { + return $this->validateGovernment($vatNumber); + } + + if (strlen($vatNumber) != 9 && strlen($vatNumber) != 12) { + return false; + } + + $weights = [8, 7, 6, 5, 4, 3, 2]; + $checkVal = $this->sumWeights($weights, $vatNumber); + $checkVal += (int)substr($vatNumber, 7, 2); + + $Result1 = $checkVal % 97; + $Result2 = ($Result1 + 55) % 97; + + return ! ($Result1 * $Result2); + } + + /** + * Validate Government VAT + * + * @param string $vatNumber + * + * @return bool + */ + private function validateGovernment(string $vatNumber): bool + { + $prefix = strtoupper(substr($vatNumber, 0, 2)); + $number = (int) substr($vatNumber, 2, 3); + + // Government departments + if ($prefix == 'GD') { + return $number < 500; + } + + // Health authorities + if ($prefix == 'HA') { + return $number > 499; + } + + return false; + } +} diff --git a/src/Vies/Vies.php b/src/Vies/Vies.php index 3e751e6..1c8509e 100644 --- a/src/Vies/Vies.php +++ b/src/Vies/Vies.php @@ -51,7 +51,7 @@ class Vies const VIES_PORT = 80; const VIES_WSDL = '/taxation_customs/vies/checkVatService.wsdl'; const VIES_TEST_WSDL = '/taxation_customs/vies/checkVatTestService.wsdl'; - const VIES_EU_COUNTRY_TOTAL = 28; + const VIES_EU_COUNTRY_TOTAL = 29; const VIES_TEST_VAT_NRS = [100, 200, 201, 202, 300, 301, 302, 400, 401, 500, 501, 600, 601]; protected const VIES_EU_COUNTRY_LIST = [ @@ -83,6 +83,7 @@ class Vies 'SI' => ['name' => 'Slovenia', 'validator' => Validator\ValidatorSI::class], 'SK' => ['name' => 'Slovakia', 'validator' => Validator\ValidatorSK::class], 'GB' => ['name' => 'United Kingdom', 'validator' => Validator\ValidatorGB::class], + 'XI' => ['name' => 'United Kingdom (Northern Ireland)', 'validator' => Validator\ValidatorXI::class], 'EU' => ['name' => 'MOSS Number', 'validator' => Validator\ValidatorEU::class], ]; diff --git a/tests/Vies/Validator/ValidatorXITest.php b/tests/Vies/Validator/ValidatorXITest.php new file mode 100644 index 0000000..1276ee4 --- /dev/null +++ b/tests/Vies/Validator/ValidatorXITest.php @@ -0,0 +1,31 @@ +validateVatNumber('XI', $vatNumber, $state); + } + + public function vatNumberProvider() + { + return [ + ['925901618', true], + ['GD001', true], + ['HA500', true], + ['434031493', false], + ['12345', false], + ['GD500', false], + ['HA100', false], + ['12345678', false], + ]; + } +}