From 92a66a749f9477315e0fbfdddddfae829ba82992 Mon Sep 17 00:00:00 2001 From: ortegafernando Date: Sat, 22 Jun 2024 08:01:49 +0200 Subject: [PATCH 01/10] Add TIN check --- README.md | 4 +- example.php | 98 ++++++++++++++++++++++++++++++--- src/Tin/Exception.php | 13 +++++ src/Tin/Provider/EuropaTIN.php | 75 +++++++++++++++++++++++++ src/Tin/Provider/Providable.php | 19 +++++++ src/Tin/Validatable.php | 15 +++++ src/Tin/ValidatorTIN.php | 88 +++++++++++++++++++++++++++++ src/Vat/Provider/EuropaVAT.php | 75 +++++++++++++++++++++++++ src/Vat/Validator.php | 5 ++ 9 files changed, 381 insertions(+), 11 deletions(-) create mode 100644 src/Tin/Exception.php create mode 100644 src/Tin/Provider/EuropaTIN.php create mode 100644 src/Tin/Provider/Providable.php create mode 100644 src/Tin/Validatable.php create mode 100644 src/Tin/ValidatorTIN.php create mode 100644 src/Vat/Provider/EuropaVAT.php diff --git a/README.md b/README.md index e076429..fe7040a 100644 --- a/README.md +++ b/README.md @@ -50,9 +50,9 @@ require 'src/autoloader.php'; ```php use PH7\Eu\Vat\Validator; -use PH7\Eu\Vat\Provider\Europa; +use PH7\Eu\Vat\Provider\EuropaVAT; -$oVatValidator = new Validator(new Europa, '0472429986', 'BE'); +$oVatValidator = new Validator(new EuropaVAT, '0472429986', 'BE'); if ($oVatValidator->check()) { $sRequestDate = $oVatValidator->getRequestDate(); diff --git a/example.php b/example.php index b745e49..81aa14e 100644 --- a/example.php +++ b/example.php @@ -1,24 +1,26 @@ - * @copyright (c) 2017-2023, Pierre-Henry Soria. All Rights Reserved. + * @author Pierre-Henry Soria + * @copyright (c) 2017-2019, Pierre-Henry Soria. All Rights Reserved. * @license GNU General Public License; */ require 'src/autoloader.php'; -use PH7\Eu\Vat\Provider\Europa; +use PH7\Eu\Vat\Provider\EuropaVAT; use PH7\Eu\Vat\Validator; -$sEuVatNumber = '0472429986'; // EU VAT number -$sEuCountryCode = 'BE'; // EU two-letter country code +use PH7\Eu\Tin\Provider\EuropaTIN; +use PH7\Eu\Tin\ValidatorTIN; -$oVatValidator = new Validator(new Europa, $sEuVatNumber, $sEuCountryCode); +$oVatValidator = new Validator(new EuropaVAT, '0472429986', 'BE'); + +echo $oVatValidator->all() . '
'; +echo 'Check: ' . ($oVatValidator->check() ? 'true' : 'false') . '
'; if ($oVatValidator->check()) { $sRequestDate = $oVatValidator->getRequestDate(); - - // Optional - explicitly format the date to d-m-Y format + // Optional, format the date $sFormattedRequestDate = (new DateTime)->format('d-m-Y'); echo 'Business Name: ' . $oVatValidator->getName() . '
'; @@ -27,5 +29,83 @@ echo 'Member State: ' . $oVatValidator->getCountryCode() . '
'; echo 'VAT Number: ' . $oVatValidator->getVatNumber() . '
'; } else { - echo 'Invalid VAT number'; + echo 'Invalid VAT number' . '
'; } + +echo '
'; + +$oVatValidatorInvalid = new Validator(new EuropaVAT, '047242998', 'BE'); + +echo $oVatValidatorInvalid->all() . '
'; +echo 'Check: ' . ($oVatValidatorInvalid->check() ? 'true' : 'false') . '
'; + +if ($oVatValidatorInvalid->check()) { + $sRequestDate = $oVatValidatorInvalid->getRequestDate(); + // Optional, format the date + $sFormattedRequestDate = (new DateTime)->format('d-m-Y'); + + echo 'Business Name: ' . $oVatValidatorInvalid->getName() . '
'; + echo 'Address: ' . $oVatValidatorInvalid->getAddress() . '
'; + echo 'Request Date: ' . $sFormattedRequestDate . '
'; + echo 'Member State: ' . $oVatValidatorInvalid->getCountryCode() . '
'; + echo 'VAT Number: ' . $oVatValidatorInvalid->getVatNumber() . '
'; +} else { + echo 'Invalid VAT number' . '
'; +} + +echo '
'; + +$oTinValidator = new ValidatorTIN(new EuropaTIN, '78888888S', 'ES'); + +echo $oTinValidator->all() . '
'; + +if ($oTinValidator->check()) { + $sRequestDate = $oTinValidator->getRequestDate(); + // Optional, format the date + $sFormattedRequestDate = (new DateTime)->format('d-m-Y'); + echo 'Request Date: ' . $sFormattedRequestDate . '
'; + echo 'TIN Number: ' . $oTinValidator->getTinNumber() . '
'; + +} else { + echo 'Invalid TIN number' . '
'; + echo 'Structure: ' . ($oTinValidator->checkStructure() ? 'true' : 'false'). '
'; + echo 'Syntax: ' . ($oTinValidator->checkSyntax() ? 'true' : 'false') . '
'; +} + +echo '
'; + +$oTinValidatorInvalid = new ValidatorTIN(new EuropaTIN, '78888888R', 'ES'); + +echo $oTinValidatorInvalid->all() . '
'; + +if ($oTinValidatorInvalid->check()) { + $sRequestDate = $oTinValidatorInvalid->getRequestDate(); + // Optional, format the date + $sFormattedRequestDate = (new DateTime)->format('d-m-Y'); + echo 'Request Date: ' . $sFormattedRequestDate . '
'; + echo 'TIN Number: ' . $oTinValidatorInvalid->getTinNumber() . '
'; + +} else { + echo 'Invalid TIN number' . '
'; + echo 'Structure: ' . ($oTinValidatorInvalid->checkStructure() ? 'true' : 'false') . '
'; + echo 'Syntax: ' . ($oTinValidatorInvalid->checkSyntax() ? 'true' : 'false') . '
'; +} + +echo '
'; + +$oTinValidatorInvalid2 = new ValidatorTIN(new EuropaTIN, '7888S8888', 'ES'); + +echo $oTinValidatorInvalid2->all() . '
'; + +if ($oTinValidatorInvalid2->check()) { + $sRequestDate = $oTinValidatorInvalid2->getRequestDate(); + // Optional, format the date + $sFormattedRequestDate = (new DateTime)->format('d-m-Y'); + echo 'Request Date: ' . $sFormattedRequestDate . '
'; + echo 'TIN Number: ' . $oTinValidatorInvalid2->getTinNumber() . '
'; + +} else { + echo 'Invalid TIN number' . '
'; + echo 'Structure: ' . ($oTinValidatorInvalid2->checkStructure() ? 'true' : 'false') . '
'; + echo 'Syntax: ' . ($oTinValidatorInvalid2->checkSyntax() ? 'true' : 'false') . '
'; +} \ No newline at end of file diff --git a/src/Tin/Exception.php b/src/Tin/Exception.php new file mode 100644 index 0000000..abb65c7 --- /dev/null +++ b/src/Tin/Exception.php @@ -0,0 +1,13 @@ + + * @copyright (c) 2017-2019, Pierre-Henry Soria. All Rights Reserved. + * @license GNU General Public License; + */ + +namespace PH7\Eu\Tin; + +class Exception extends \Exception +{ + +} diff --git a/src/Tin/Provider/EuropaTIN.php b/src/Tin/Provider/EuropaTIN.php new file mode 100644 index 0000000..82c1b20 --- /dev/null +++ b/src/Tin/Provider/EuropaTIN.php @@ -0,0 +1,75 @@ + + * @copyright (c) 2017-2022, Pierre-Henry Soria. All Rights Reserved. + * @license GNU General Public License; + */ + +declare(strict_types=1); + +namespace PH7\Eu\Tin\Provider; + +use PH7\Eu\Tin\Exception; +use SoapClient; +use SoapFault; +use stdClass; + +class EuropaTIN implements Providable +{ + public const EU_TIN_API = 'https://ec.europa.eu'; + public const EU_TIN_WSDL_ENDPOINT = '/taxation_customs/tin/services/checkTinService.wsdl'; + + private const IMPOSSIBLE_CONNECT_API_MESSAGE = 'Impossible to connect to the Europa TIN SOAP: %s'; + private const IMPOSSIBLE_RETRIEVE_DATA_MESSAGE = 'Impossible to retrieve the TIN details: %s'; + + /** @var SoapClient */ + private $oClient; + + /** + * EuropaTIN Provider constructor + * + * @throws Exception + */ + public function __construct() + { + try { + $this->oClient = new SoapClient($this->getApiUrl()); + } catch (SoapFault $oExcept) { + throw new Exception( + sprintf(self::IMPOSSIBLE_CONNECT_API_MESSAGE, $oExcept->faultstring), + 0, + $oExcept + ); + } + } + + public function getApiUrl(): string + { + return static::EU_TIN_API . static::EU_TIN_WSDL_ENDPOINT; + } + + /** + * Send the TIN number and country code to europa.eu API and get the data. + * + * @param int|string $sTinNumber The TIN number + * @param string $sCountryCode The country code + * + * @return stdClass The TIN number's details. + * + * @throws Exception + */ + public function getResource($sTinNumber, string $sCountryCode): stdClass + { + try { + $aDetails = [ + 'countryCode' => strtoupper($sCountryCode), + 'tinNumber' => $sTinNumber + ]; + return $this->oClient->checkTin($aDetails); + } catch (SoapFault $oExcept) { + throw new Exception( + sprintf(self::IMPOSSIBLE_RETRIEVE_DATA_MESSAGE, $oExcept->faultstring) + ); + } + } +} diff --git a/src/Tin/Provider/Providable.php b/src/Tin/Provider/Providable.php new file mode 100644 index 0000000..86766a6 --- /dev/null +++ b/src/Tin/Provider/Providable.php @@ -0,0 +1,19 @@ + + * @copyright (c) 2017-2019, Pierre-Henry Soria. All Rights Reserved. + * @license GNU General Public License; + */ + +declare(strict_types=1); + +namespace PH7\Eu\Tin\Provider; + +use stdClass; + +interface Providable +{ + public function getApiUrl(): string; + + public function getResource($sTinNumber, string $sCountryCode): stdClass; +} diff --git a/src/Tin/Validatable.php b/src/Tin/Validatable.php new file mode 100644 index 0000000..be442c9 --- /dev/null +++ b/src/Tin/Validatable.php @@ -0,0 +1,15 @@ + + * @copyright (c) 2017-2019, Pierre-Henry Soria. All Rights Reserved. + * @license GNU General Public License; + */ + +namespace PH7\Eu\Tin; + +interface Validatable +{ + public function check(): bool; + + public function sanitize(): void; +} diff --git a/src/Tin/ValidatorTIN.php b/src/Tin/ValidatorTIN.php new file mode 100644 index 0000000..3d301d4 --- /dev/null +++ b/src/Tin/ValidatorTIN.php @@ -0,0 +1,88 @@ + + * @copyright (c) 2017-2019, Pierre-Henry Soria. All Rights Reserved. + * @license GNU General Public License; + */ + +declare(strict_types=1); + +namespace PH7\Eu\Tin; + +use PH7\Eu\Tin\Provider\Providable; +use stdClass; + +class ValidatorTIN implements Validatable +{ + /** @var int|string */ + private $sTinNumber; + + /** @var string */ + private $sCountryCode; + + /** @var stdClass */ + private $oResponse; + + /** + * @param Providable $oProvider The API that checks the TIN no. + * @param int|string $sTinNumber The TIN number. + * @param string $sCountryCode The country code. + */ + public function __construct(Providable $oProvider, $sTinNumber, string $sCountryCode) + { + $this->sTinNumber = $sTinNumber; + $this->sCountryCode = $sCountryCode; + + $this->sanitize(); + $this->oResponse = $oProvider->getResource($this->sTinNumber, $this->sCountryCode); + } + + + public function all(): string + { + return json_encode($this->oResponse); + } + + /** + * Check if the TIN number is valid or not + * + * @return bool + */ + public function check(): bool + { + return ($this->checkStructure() and $this->checkSyntax()); + } + + public function checkStructure(): bool + { + return (bool)$this->oResponse->validStructure; + } + + public function checkSyntax(): bool + { + return (bool)$this->oResponse->validSyntax; + } + + public function getRequestDate(): string + { + return $this->oResponse->requestDate ?? ''; + } + + public function getCountryCode(): string + { + return $this->oResponse->countryCode ?? ''; + } + + public function getTinNumber(): string + { + return $this->oResponse->tinNumber ?? ''; + } + + public function sanitize(): void + { + $aSearch = [$this->sCountryCode, '-', '_', '.', ',', ' ']; + $this->sTinNumber = trim(str_replace($aSearch, '', $this->sTinNumber)); + $this->sCountryCode = strtoupper($this->sCountryCode); + } + +} diff --git a/src/Vat/Provider/EuropaVAT.php b/src/Vat/Provider/EuropaVAT.php new file mode 100644 index 0000000..c82d83d --- /dev/null +++ b/src/Vat/Provider/EuropaVAT.php @@ -0,0 +1,75 @@ + + * @copyright (c) 2017-2022, Pierre-Henry Soria. All Rights Reserved. + * @license GNU General Public License; + */ + +declare(strict_types=1); + +namespace PH7\Eu\Vat\Provider; + +use PH7\Eu\Vat\Exception; +use SoapClient; +use SoapFault; +use stdClass; + +class EuropaVAT implements Providable +{ + public const EU_VAT_API = 'https://ec.europa.eu'; + public const EU_VAT_WSDL_ENDPOINT = '/taxation_customs/vies/checkVatService.wsdl'; + + private const IMPOSSIBLE_CONNECT_API_MESSAGE = 'Impossible to connect to the Europa VAT SOAP: %s'; + private const IMPOSSIBLE_RETRIEVE_DATA_MESSAGE = 'Impossible to retrieve the VAT details: %s'; + + /** @var SoapClient */ + private $oClient; + + /** + * EuropaVAT Provider constructor + * + * @throws Exception + */ + public function __construct() + { + try { + $this->oClient = new SoapClient($this->getApiUrl()); + } catch (SoapFault $oExcept) { + throw new Exception( + sprintf(self::IMPOSSIBLE_CONNECT_API_MESSAGE, $oExcept->faultstring), + 0, + $oExcept + ); + } + } + + public function getApiUrl(): string + { + return static::EU_VAT_API . static::EU_VAT_WSDL_ENDPOINT; + } + + /** + * Send the VAT number and country code to europa.eu API and get the data. + * + * @param int|string $sVatNumber The VAT number + * @param string $sCountryCode The country code + * + * @return stdClass The VAT number's details. + * + * @throws Exception + */ + public function getResource($sVatNumber, string $sCountryCode): stdClass + { + try { + $aDetails = [ + 'countryCode' => strtoupper($sCountryCode), + 'vatNumber' => $sVatNumber + ]; + return $this->oClient->checkVat($aDetails); + } catch (SoapFault $oExcept) { + throw new Exception( + sprintf(self::IMPOSSIBLE_RETRIEVE_DATA_MESSAGE, $oExcept->faultstring) + ); + } + } +} diff --git a/src/Vat/Validator.php b/src/Vat/Validator.php index 8e94faf..b70890b 100644 --- a/src/Vat/Validator.php +++ b/src/Vat/Validator.php @@ -37,6 +37,11 @@ public function __construct(Providable $oProvider, $sVatNumber, string $sCountry $this->oResponse = $oProvider->getResource($this->sVatNumber, $this->sCountryCode); } + public function all(): string + { + return json_encode($this->oResponse); + } + /** * Check if the VAT number is valid or not * From 0e90c48d40b845b44331e6d640aba24129219b37 Mon Sep 17 00:00:00 2001 From: ortegafernando Date: Sat, 22 Jun 2024 21:01:52 +0200 Subject: [PATCH 02/10] Update EuropaTIN.php --- src/Tin/Provider/EuropaTIN.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tin/Provider/EuropaTIN.php b/src/Tin/Provider/EuropaTIN.php index 82c1b20..908ce4c 100644 --- a/src/Tin/Provider/EuropaTIN.php +++ b/src/Tin/Provider/EuropaTIN.php @@ -65,7 +65,7 @@ public function getResource($sTinNumber, string $sCountryCode): stdClass 'countryCode' => strtoupper($sCountryCode), 'tinNumber' => $sTinNumber ]; - return $this->oClient->checkTin($aDetails); + return $this->oClient->checkVat($aDetails); } catch (SoapFault $oExcept) { throw new Exception( sprintf(self::IMPOSSIBLE_RETRIEVE_DATA_MESSAGE, $oExcept->faultstring) From ddd719b170520da28f3df7ea0c9ce7283a89755c Mon Sep 17 00:00:00 2001 From: ortegafernando Date: Sat, 22 Jun 2024 21:46:00 +0200 Subject: [PATCH 03/10] Update EuropaTIN.php --- src/Tin/Provider/EuropaTIN.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tin/Provider/EuropaTIN.php b/src/Tin/Provider/EuropaTIN.php index 908ce4c..82c1b20 100644 --- a/src/Tin/Provider/EuropaTIN.php +++ b/src/Tin/Provider/EuropaTIN.php @@ -65,7 +65,7 @@ public function getResource($sTinNumber, string $sCountryCode): stdClass 'countryCode' => strtoupper($sCountryCode), 'tinNumber' => $sTinNumber ]; - return $this->oClient->checkVat($aDetails); + return $this->oClient->checkTin($aDetails); } catch (SoapFault $oExcept) { throw new Exception( sprintf(self::IMPOSSIBLE_RETRIEVE_DATA_MESSAGE, $oExcept->faultstring) From b9033e25e4e92fbc3a4db38e43d95aeb1cc4f82f Mon Sep 17 00:00:00 2001 From: ortegafernando Date: Mon, 24 Jun 2024 07:24:49 +0200 Subject: [PATCH 04/10] Create ValidatorTestTin.php --- tests/Tin/ValidatorTestTin.php | 165 +++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 tests/Tin/ValidatorTestTin.php diff --git a/tests/Tin/ValidatorTestTin.php b/tests/Tin/ValidatorTestTin.php new file mode 100644 index 0000000..be4c58d --- /dev/null +++ b/tests/Tin/ValidatorTestTin.php @@ -0,0 +1,165 @@ + + * @copyright (c) 2017-2021, Pierre-Henry Soria. All Rights Reserved. + * @license GNU General Public License; + */ + +declare(strict_types=1); + +namespace PH7\Eu\Tests\Tin; + +use PH7\Eu\Tin\Exception; +use PH7\Eu\Tin\Provider\EuropaTin; +use PH7\Eu\Tin\Provider\Providable; +use PH7\Eu\Tin\ValidatorTin; +use Phake; +use Phake_IMock; +use PHPUnit\Framework\TestCase; +use stdClass; + +class ValidatorTestTin extends TestCase +{ + /** + * @dataProvider validTinNumbers + * + * @param int|string $sTinNumber The TIN number + * @param string $sCountryCode The country code + */ + public function testValidTinNumbers($sTinNumber, string $sCountryCode): void + { + try { + $oTinValidator = new ValidatorTIN(new EuropaTIN, $sTinNumber, $sCountryCode); + $this->assertTrue($oTinValidator->check()); + } catch (Exception $oExcept) { + $this->assertIsResponseFailure($oExcept); + } + } + + /** + * @dataProvider invalidTinNumbers + * + * @param int|string $sTinNumber The TIN number + * @param string $sCountryCode The country code + */ + public function testInvalidTinNumbers($sTinNumber, string $sCountryCode): void + { + try { + $oTinValidator = new ValidatorTIN(new EuropaTIN, $sTinNumber, $sCountryCode); + $this->assertFalse($oTinValidator->check()); + } catch (Exception $oExcept) { + $this->assertIsResponseFailure($oExcept); + } + } + + /** + * @dataProvider validTinNumberDetails + */ + public function testValidTinNumberStatus(stdClass $oTinDetails): void + { + $oValidator = $this->setUpAndMock($oTinDetails); + $this->assertTrue($oValidator->check()); + } + + /** + * @dataProvider invalidTinNumberDetails + */ + public function testInvalidTinNumberStatus(stdClass $oTinDetails): void + { + $oValidator = $this->setUpAndMock($oTinDetails); + $this->assertFalse($oValidator->check()); + } + + /** + * @dataProvider validTinNumberDetails + */ + public function testCountryCode(stdClass $oTinDetails): void + { + $oValidator = $this->setUpAndMock($oTinDetails); + $this->assertEquals('BE', $oValidator->getCountryCode()); + } + + /** + * @dataProvider validTinNumberDetails + */ + public function testTinNumber(stdClass $oTinDetails): void + { + $oValidator = $this->setUpAndMock($oTinDetails); + $this->assertEquals('0472429986', $oValidator->getTinNumber()); + } + + /** + * @dataProvider validTinNumberDetails + */ + public function testRequestDate(stdClass $oTinDetails): void + { + $oValidator = $this->setUpAndMock($oTinDetails); + $this->assertEquals('2017-01-22+01:00', $oValidator->getRequestDate()); + } + + public function testResource(): void + { + try { + $oEuropaTINProvider = new EuropaTIN; + $this->assertInstanceOf(stdClass::class, $oEuropaTINProvider->getResource('0472429986', 'BE')); + } catch (Exception $oExcept) { + $this->assertIsResponseFailure($oExcept); + } + } + + public function validTinNumberDetails(): array + { + $oData = new stdClass; + $oData->valid = true; + $oData->countryCode = 'BE'; + $oData->TinNumber = '0472429986'; + $oData->requestDate = '2017-01-22+01:00'; + return [ + [$oData] + ]; + } + + public function invalidTinNumberDetails(): array + { + $oData = new stdClass; + $oData->valid = false; + + return [ + [$oData] + ]; + } + + public function validTinNumbers(): array + { + return [ + ['78888888S', 'ES'], + ['9763375H', 'IE'], + ['RSSMRA85T10A562S', 'IT'] + ]; + } + + public function invalidTinNumbers(): array + { + return [ + [243852752, 'UK'], // Has to be 'GB' + [29672050085, 'FRANCE'], + ['blablabla', 'DE'] + ]; + } + + private function setUpAndMock(stdClass $oTinDetails): Phake_IMock + { + $oProvider = Phake::mock(Providable::class); + Phake::when($oProvider)->getResource(Phake::anyParameters())->thenReturn($oTinDetails); + $oValidator = Phake::partialMock(ValidatorTIN::class, $oProvider, '78888888S', 'ES'); + Phake::verify($oValidator)->sanitize(); + Phake::verify($oProvider)->getResource('78888888S', 'ES'); + + return $oValidator; + } + + private function assertIsResponseFailure(Exception $oExcept): void + { + $this->assertRegexp('/^Impossible to retrieve the TIN details/', $oExcept->getMessage()); + } +} From d6cd9ef23b53d045002455f7ad9313d12693024e Mon Sep 17 00:00:00 2001 From: ortegafernando Date: Mon, 24 Jun 2024 07:26:39 +0200 Subject: [PATCH 05/10] Create EuropaTINTest.php --- tests/Tin/Provider/EuropaTINTest.php | 29 ++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/Tin/Provider/EuropaTINTest.php diff --git a/tests/Tin/Provider/EuropaTINTest.php b/tests/Tin/Provider/EuropaTINTest.php new file mode 100644 index 0000000..1293971 --- /dev/null +++ b/tests/Tin/Provider/EuropaTINTest.php @@ -0,0 +1,29 @@ + + * @copyright (c) 2017-2021, Pierre-Henry Soria. All Rights Reserved. + * @license GNU General Public License; + */ + +declare(strict_types=1); + +namespace PH7\Eu\Tests\Tin\Provider; + +use PH7\Eu\Tin\Provider\EuropaTIN; +use PHPUnit\Framework\TestCase; + +class ProviderTestTIN extends TestCase +{ + /** @var Europa */ + private $oEuropa; + + protected function setUp(): void + { + $this->oEuropa = new EuropaTIN; + } + + public function testApiUrl(): void + { + $this->assertEquals(EuropaTIN::EU_TIN_API_URL . EuropaTIN::EU_TIN_WSDL_ENDPOINT, $this->oEuropa->getApiUrl()); + } +} From 33072a10974fd7da2ddfa89bab2bc7f62294e9b9 Mon Sep 17 00:00:00 2001 From: ortegafernando Date: Mon, 24 Jun 2024 08:03:15 +0200 Subject: [PATCH 06/10] Add strict mode (not change anything in TIN number) --- src/Tin/ValidatorTIN.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Tin/ValidatorTIN.php b/src/Tin/ValidatorTIN.php index 3d301d4..117fe5e 100644 --- a/src/Tin/ValidatorTIN.php +++ b/src/Tin/ValidatorTIN.php @@ -27,13 +27,14 @@ class ValidatorTIN implements Validatable * @param Providable $oProvider The API that checks the TIN no. * @param int|string $sTinNumber The TIN number. * @param string $sCountryCode The country code. + * @param boolean $strict Strict sanitize version, not change anything in TIN number */ - public function __construct(Providable $oProvider, $sTinNumber, string $sCountryCode) + public function __construct(Providable $oProvider, $sTinNumber, string $sCountryCode, bool $strict = false) { $this->sTinNumber = $sTinNumber; $this->sCountryCode = $sCountryCode; - $this->sanitize(); + $this->sanitize($strict); $this->oResponse = $oProvider->getResource($this->sTinNumber, $this->sCountryCode); } @@ -78,11 +79,13 @@ public function getTinNumber(): string return $this->oResponse->tinNumber ?? ''; } - public function sanitize(): void + public function sanitize(bool $strict): void { - $aSearch = [$this->sCountryCode, '-', '_', '.', ',', ' ']; - $this->sTinNumber = trim(str_replace($aSearch, '', $this->sTinNumber)); + if (!$strict) { + $aSearch = [$this->sCountryCode, '-', '_', '.', ',', ' ']; + $this->sTinNumber = trim(str_replace($aSearch, '', $this->sTinNumber)); + } $this->sCountryCode = strtoupper($this->sCountryCode); - } + } } From 9130817ee8d990a0b0f3827d6462fd47694de47b Mon Sep 17 00:00:00 2001 From: ortegafernando Date: Mon, 24 Jun 2024 08:03:24 +0200 Subject: [PATCH 07/10] Add strict mode (not change anything in VAT number) --- src/Vat/Validator.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Vat/Validator.php b/src/Vat/Validator.php index b70890b..e1cfffd 100644 --- a/src/Vat/Validator.php +++ b/src/Vat/Validator.php @@ -27,13 +27,14 @@ class Validator implements Validatable * @param Providable $oProvider The API that checks the VAT no. and retrieve the VAT registration's details. * @param int|string $sVatNumber The VAT number. * @param string $sCountryCode The country code. + * @param boolean $strict Strict sanitize version, not change anything in VAT number */ - public function __construct(Providable $oProvider, $sVatNumber, string $sCountryCode) + public function __construct(Providable $oProvider, $sVatNumber, string $sCountryCode, bool $strict = false) { $this->sVatNumber = $sVatNumber; $this->sCountryCode = $sCountryCode; - $this->sanitize(); + $this->sanitize($strict); $this->oResponse = $oProvider->getResource($this->sVatNumber, $this->sCountryCode); } @@ -77,10 +78,12 @@ public function getVatNumber(): string return $this->oResponse->vatNumber ?? ''; } - public function sanitize(): void + public function sanitize(bool $strict): void { - $aSearch = [$this->sCountryCode, '-', '_', '.', ',', ' ']; - $this->sVatNumber = trim(str_replace($aSearch, '', $this->sVatNumber)); + if (!$strict) { + $aSearch = [$this->sCountryCode, '-', '_', '.', ',', ' ']; + $this->sVatNumber = trim(str_replace($aSearch, '', $this->sVatNumber)); + } $this->sCountryCode = strtoupper($this->sCountryCode); } From 1c736029053b1ea7bc3fb81ee9fc7ab19a216638 Mon Sep 17 00:00:00 2001 From: ortegafernando Date: Mon, 24 Jun 2024 08:08:23 +0200 Subject: [PATCH 08/10] Add strict mode --- README.md | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fe7040a..e2dda36 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,17 @@ -# EU VAT Number Validator +# EU VAT and TIN Number Validator -A simple and clean PHP class that validates EU VAT numbers against the central ec.europa.eu database (using the official europa API). +A simple and clean PHP class that validates EU VAT and TIN numbers against the central ec.europa.eu database (using the official europa API). ![EU VATIN validator; EU Flag](eu-flag.svg) ## The Problem -Validate VAT numbers might be difficult and if you use a validation pattern to check if the format is valid, you are never sure if the VAT registration number is still valid. +Validate VAT and TIN numbers might be difficult and if you use a validation pattern to check if the format is valid, you are never sure if the VAT registration number is still valid. ## The Solution -This [PHP VAT validator library](https://github.com/pH-7/eu-vat-validator) uses real-time data feeds from individual EU member states' VAT systems so you are sure of the validity of the number and avoid fraud with expired or wrong VAT numbers. +This [PHP VAT validator library](https://github.com/pH-7/eu-vat-validator) uses real-time data feeds from individual EU member states' VAT and TIN systems so you are sure of the validity of the number and avoid fraud with expired or wrong VAT numbers. For example, this kind of validation can be very useful on online payment forms. @@ -72,8 +72,19 @@ if ($oVatValidator->check()) { ## Optimization (Suggestion) -Depending of the use of this library, it could be handy to cache the result specifically for each specified VAT number. +Depending of the use of this library, it could be handy to cache the result specifically for each specified VAT or TIN number. +## Strict mode + +By default this librery clean VAT or TIN numbers before checking them (it cleans by deleting from VAT or TIN numbers: country Code and these special characters: '-', '_', '.', ',', ' '). + +If you don't want, you can check numbers in strict mode, just by calling check function with option value TRUE (default value is FALSE). In the above example, you only need to change this line: +```php +if ($oVatValidator->check(true)) { +``` +```php +if ($oVatValidator->check(strict: true)) { +``` ## Requirements From 8c4fe49d7de7dcd45d327bb396f1c26b713def75 Mon Sep 17 00:00:00 2001 From: ortegafernando Date: Tue, 25 Jun 2024 06:39:08 +0200 Subject: [PATCH 09/10] Add COUNTRY_NOT_VALID --- src/Tin/Provider/EuropaTIN.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Tin/Provider/EuropaTIN.php b/src/Tin/Provider/EuropaTIN.php index 82c1b20..06a4a15 100644 --- a/src/Tin/Provider/EuropaTIN.php +++ b/src/Tin/Provider/EuropaTIN.php @@ -16,6 +16,9 @@ class EuropaTIN implements Providable { + protected const TIN_EU_COUNTRY_LIST = ['AT','BE','BG','CY','CZ','DE','DK','EE','EL','ES','FI','FR','HR','HU','IE','IT','LU','LV','LT','MT','NL','PL','PT','RO','SE','SI','SK']; + public const COUNTRY_NOT_VALID = 'Country not valid in Europa TIN Service: %s'; + public const EU_TIN_API = 'https://ec.europa.eu'; public const EU_TIN_WSDL_ENDPOINT = '/taxation_customs/tin/services/checkTinService.wsdl'; @@ -60,6 +63,11 @@ public function getApiUrl(): string */ public function getResource($sTinNumber, string $sCountryCode): stdClass { + if (!in_array(strtoupper($sCountryCode), self::TIN_EU_COUNTRY_LIST)) { + throw new Exception( + sprintf(self::COUNTRY_NOT_VALID, strtoupper($sCountryCode)) + ); + } try { $aDetails = [ 'countryCode' => strtoupper($sCountryCode), From 783b2081f1e6663b8f13e5e63c5371a5138b583d Mon Sep 17 00:00:00 2001 From: ortegafernando Date: Tue, 25 Jun 2024 06:40:03 +0200 Subject: [PATCH 10/10] Add COUNTRY_NOT_VALID --- src/Vat/Provider/EuropaVAT.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Vat/Provider/EuropaVAT.php b/src/Vat/Provider/EuropaVAT.php index c82d83d..3dadbfc 100644 --- a/src/Vat/Provider/EuropaVAT.php +++ b/src/Vat/Provider/EuropaVAT.php @@ -16,6 +16,9 @@ class EuropaVAT implements Providable { + protected const VAT_EU_COUNTRY_LIST = ['AT','BE','BG','CY','CZ','DE','DK','EE','EL','ES','FI','FR','HR','HU','IE','IT','LU','LV','LT','MT','NL','PL','PT','RO','SE','SI','SK','XI']; + private const COUNTRY_NOT_VALID = 'Country not valid in Europa VAT Service: %s'; + public const EU_VAT_API = 'https://ec.europa.eu'; public const EU_VAT_WSDL_ENDPOINT = '/taxation_customs/vies/checkVatService.wsdl'; @@ -60,6 +63,11 @@ public function getApiUrl(): string */ public function getResource($sVatNumber, string $sCountryCode): stdClass { + if (!in_array(strtoupper($sCountryCode), self::VAT_EU_COUNTRY_LIST)) { + throw new Exception( + sprintf(self::COUNTRY_NOT_VALID, strtoupper($sCountryCode)) + ); + } try { $aDetails = [ 'countryCode' => strtoupper($sCountryCode),