diff --git a/README.md b/README.md index 76fe849..896d611 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,6 @@ Log Laravel requests and responses for statistical purposes and optionally aggre ## Description - - This package lets you: - Check if a VAT number has a correct format diff --git a/config/vat.php b/config/vat.php index 5f8730b..a5fb818 100644 --- a/config/vat.php +++ b/config/vat.php @@ -11,4 +11,27 @@ | */ 'default' => 'stack', + + /* + |-------------------------------------------------------------------------- + | Drivers + |-------------------------------------------------------------------------- + | + | Drivers are the various services that can be used to validate VAT numbers. + | Usually a driver only supports a subset of countries. + | A list of institutions that provide VAT number information can be found + | from the list here: https://dinero.dk/tips/tjek-udenlandsk-cvr/ + | + */ + 'drivers' => [ + 'stack' => [ + 'drivers' => ['cvr_api', 'abstract_api'], + ], + 'cvr_api' => [ + 'access_token' => env('VAT_CVR_API_ACCESS_TOKEN'), + ], + 'abstract_api' => [ + 'api_key' => env('VAT_ABSTRACT_API_KEY'), + ], + ], ]; diff --git a/src/Drivers/CvrApi.php b/src/Drivers/CvrApi.php new file mode 100644 index 0000000..4a735af --- /dev/null +++ b/src/Drivers/CvrApi.php @@ -0,0 +1,139 @@ + [ + '########' + ], + 'NO' => [ + // empty means any format is accepted + ], + ]; + + public static function supports(string $countryCode): bool + { + return array_key_exists(strtoupper($countryCode), self::$formats); + } + + public function __construct( + private ?string $accessToken, // This api actually works without an access token, but then rate limiting is applied. + ) {} + + /** @inheritDoc */ + public function getFormats(string $countryCode): Collection + { + if (! self::supports($countryCode)) { + throw UnsupportedCountryException::unsupported($countryCode); + } + + return Collection::make(self::$formats[strtoupper($countryCode)]); + } + + /** @inheritDoc */ + public function isValidFormat(string $countryCode, string $vatNumber): bool + { + if (! self::supports($countryCode)) { + throw UnsupportedCountryException::unsupported($countryCode); + } + + return $this + ->getFormats($countryCode) + ->whenEmpty( + fn (Collection $collection) => true, + function (Collection $collection) use ($vatNumber): bool { + return $collection->filter(function (string $format) use ($vatNumber) { + return true; // TODO: Fix this + })->isNotEmpty(); + } + ); + } + + /** @inheritDoc */ + public function isValid(string $countryCode, string $vatNumber): bool + { + if (! $this->isValidFormat($countryCode, $vatNumber)) { + return false; + } + + try { + $this->getInformation($countryCode, $vatNumber); + + return true; + } catch (InvalidVatException $e) { + return false; + } catch (VatNotFoundException $e) { + return false; // The driver is "complete" so any number not found is assumed to be invalid + } + } + + /** @inheritDoc */ + public function validate(string $countryCode, string $vatNumber): void + { + // TODO: Implement + } + + /** @inheritDoc */ + public function getInformation(string $countryCode, string $vatNumber): VatInformation + { + $response = $this->searchByVatNumber($countryCode, $vatNumber); + + if ($response->status() === 404) { + throw InvalidVatException::doesNotExist($vatNumber); + } + + if ($response->isSuccessful()) { + throw new DriverUnavailable('CVR API is currently unavailable.'); + } + + return new VatInformation( + company: $response->json('name'), + vatNumber: $response->json('vat'), + contact: null, + address: $response->json('address'), + postalCode: $response->json('zipcode'), + city: $response->json('city'), + state: null, + region: null, + country: $countryCode, + sms: null, + phone: $response->json('phone'), + email: $response->json('email'), + ); + } + + private function getUrl(): string + { + return 'https://cvrapi.dk/api'; + } + + private function getUserAgent(): string + { + // Required format described here: https://cvrapi.dk/documentation + return 'SmartSend - VatService - Anders Bilfeldt anders@smartsend.io'; + } + + private function searchByVatNumber(string $countryCode, string $vatNumber): Response + { + return Http::withUserAgent($this->getUserAgent()) + ->get($this->getUrl(), [ + 'vat' => $vatNumber, + 'country' => $countryCode, + 'version' => 6, + 'format' => 'json', + 'token' => $this->accessToken, + ]); + } +} \ No newline at end of file diff --git a/src/VatServiceInterface.php b/src/VatServiceInterface.php index dd1a312..b882688 100644 --- a/src/VatServiceInterface.php +++ b/src/VatServiceInterface.php @@ -13,6 +13,11 @@ interface VatServiceInterface public static function supports(string $countryCode): bool; /** + * Get the possible formats for the given country code. + * + * A star (*) means any number or letter while a hash (#) + * means any number and a question mark (?) means any letter. + * * @param string $countryCode * @return Collection */