From 9d8849e437386e11f2bc26c8fbee140aa1fd4db7 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Mon, 24 May 2021 22:14:43 +0200 Subject: [PATCH] Validate UK VAT numbers --- composer.json | 3 + src/Mpociot/VatCalculator/VatCalculator.php | 70 +++++++++++++++------ tests/VatCalculatorTest.php | 18 ++++++ 3 files changed, 73 insertions(+), 18 deletions(-) diff --git a/composer.json b/composer.json index 171b51e..2244a83 100644 --- a/composer.json +++ b/composer.json @@ -24,6 +24,9 @@ "orchestra/testbench": "~3.5|^4.0|^5.0|^6.0", "phpunit/phpunit": "^6.5|^7.5|^8.5|^9.5" }, + "suggest": { + "ext-curl": "Required when you need to validate UK VAT numbers." + }, "autoload": { "classmap": [ "src/controllers/Controller.php" diff --git a/src/Mpociot/VatCalculator/VatCalculator.php b/src/Mpociot/VatCalculator/VatCalculator.php index b4315ca..b8940df 100644 --- a/src/Mpociot/VatCalculator/VatCalculator.php +++ b/src/Mpociot/VatCalculator/VatCalculator.php @@ -382,6 +382,11 @@ class VatCalculator */ protected $businessCountryCode; + /** + * @var string + */ + protected $ukValidationEndpoint = 'https://api.service.hmrc.gov.uk'; + /** * @param \Illuminate\Contracts\Config\Repository */ @@ -664,10 +669,10 @@ public function isValidVATNumber($vatNumber) $details = self::getVATDetails($vatNumber); if ($details) { - return $details->valid; - } else { - return false; + return is_array($details) ? isset($details['vatNumber']) : $details->valid; } + + return false; } /** @@ -682,25 +687,43 @@ public function getVATDetails($vatNumber) $vatNumber = str_replace([' ', "\xC2\xA0", "\xA0", '-', '.', ','], '', trim($vatNumber)); $countryCode = substr($vatNumber, 0, 2); $vatNumber = substr($vatNumber, 2); - $this->initSoapClient(); - $client = $this->soapClient; - if ($client) { - try { - $result = $client->checkVat([ - 'countryCode' => $countryCode, - 'vatNumber' => $vatNumber, - ]); - - return $result; - } catch (SoapFault $e) { - if (isset($this->config) && $this->config->get('vat_calculator.forward_soap_faults')) { - throw new VATCheckUnavailableException($e->getMessage(), $e->getCode(), $e->getPrevious()); - } + if (strtoupper($countryCode) === 'GB' && extension_loaded('curl')) { + $curl_handle = curl_init(); + curl_setopt($curl_handle, CURLOPT_URL, "$this->ukValidationEndpoint/organisations/vat/check-vat-number/lookup/$vatNumber"); + curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, 2); + curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curl_handle, CURLOPT_USERAGENT, 'VatCalculator'); + $result = curl_exec($curl_handle); + curl_close($curl_handle); + + $response = json_decode($result, true, 512, JSON_OBJECT_AS_ARRAY); + + if (isset($response['code']) && $response['code'] === 'NOT_FOUND') { return false; } + + return $response['target']; + } else { + $this->initSoapClient(); + $client = $this->soapClient; + + if ($client) { + try { + return $client->checkVat([ + 'countryCode' => $countryCode, + 'vatNumber' => $vatNumber, + ]); + } catch (SoapFault $e) { + if (isset($this->config) && $this->config->get('vat_calculator.forward_soap_faults')) { + throw new VATCheckUnavailableException($e->getMessage(), $e->getCode(), $e->getPrevious()); + } + + return false; + } + } + throw new VATCheckUnavailableException('The VAT check service is currently unavailable. Please try again later.'); } - throw new VATCheckUnavailableException('The VAT check service is currently unavailable. Please try again later.'); } /** @@ -731,4 +754,15 @@ public function setSoapClient($soapClient) { $this->soapClient = $soapClient; } + + /** + * @return $this + * @internal This method is not covered by our BC policy. + */ + public function testing() + { + $this->ukValidationEndpoint = 'https://test-api.service.hmrc.gov.uk'; + + return $this; + } } diff --git a/tests/VatCalculatorTest.php b/tests/VatCalculatorTest.php index 9da6597..b2271c8 100644 --- a/tests/VatCalculatorTest.php +++ b/tests/VatCalculatorTest.php @@ -479,6 +479,24 @@ public function testCannotValidateVATNumberWhenServiceIsDown() $vatCalculator->isValidVATNumber($vatNumber); } + public function testCanValidateValidUKVATNumber() + { + $config = m::mock('Illuminate\Contracts\Config\Repository'); + + $config->shouldReceive('has') + ->once() + ->with('vat_calculator.business_country_code') + ->andReturn(false); + + $result = new \stdClass(); + $result->valid = true; + + $vatNumber = 'GB 553557881'; + $vatCalculator = new VatCalculator($config); + $result = $vatCalculator->testing()->isValidVATNumber($vatNumber); + $this->assertTrue($result); + } + /** * @link https://tools.tracemyip.org/search--country/germany */