A lightweight, type-safe PHP package for validating EU VAT numbers via the European Commission's VIES (VAT Information Exchange System) REST service.
- Type-safe: PHP 8.1+ type declarations and modern enum support
- Clean architecture: DTOs, validation, and HTTP adapters
- Production + test modes: Switch services via
ViesConfig - Error handling: Structured exceptions for validation and REST errors
- Approximate matching: Trader detail matching support
- All EU countries: EU member states plus Northern Ireland
- Tested: Pest-based test suite
- PHP 8.1 or higher
- ext-curl
- ext-json
- ext-mbstring
Install the package via Composer:
composer require omisai/vies-restuse Omisai\ViesRest\ViesClient;
$client = new ViesClient();
$response = $client->checkVat('DE', '123456789');
if ($response->valid) {
echo "✅ Valid VAT: {$response->countryCode}{$response->vatNumber}\n";
echo "Company: {$response->name}\n";
echo "Address: {$response->address}\n";
} else {
echo "❌ Invalid VAT number\n";
}use Omisai\ViesRest\ViesClient;
use Omisai\ViesRest\ViesConfig;
$client = new ViesClient(ViesConfig::test());
$response = $client->checkVat('DE', '100');
var_dump($response->valid); // true for test numbersuse Omisai\ViesRest\ViesConfig;
$production = ViesConfig::production();
$test = ViesConfig::test();
$custom = ViesConfig::production(baseUrl: 'https://custom-vies.example.com');use Omisai\ViesRest\ViesClient;
use Omisai\ViesRest\ViesConfig;
$options = [
'timeout' => 30,
'connect_timeout' => 10,
'headers' => [
'User-Agent' => 'MyApp/1.0',
],
];
$client = new ViesClient(ViesConfig::production(options: $options));Validates a VAT number and returns basic information.
Performs approximate validation with trader details.
Returns availability info for member states.
use Omisai\ViesRest\DTO\CheckVatRequest;
$request = new CheckVatRequest(
countryCode: 'NL',
vatNumber: '123456789B01',
traderName: 'Example B.V.',
traderStreet: 'Main Street 123',
traderPostalCode: '1234AB',
traderCity: 'Amsterdam',
requesterMemberStateCode: 'DE',
requesterNumber: '123456789',
);echo $response->countryCode;
echo $response->vatNumber;
echo $response->requestDate->format('Y-m-d');
echo $response->valid ? 'Yes' : 'No';use Omisai\ViesRest\Enum\EuropeanUnionCountry;
$isValid = EuropeanUnionCountry::isEuropeanUnionCountryCode('DE'); // true
$isValid = EuropeanUnionCountry::isEuropeanUnionCountryCode('US'); // falseuse Omisai\ViesRest\Exceptions\ViesValidationException;
use Omisai\ViesRest\ViesClient;
$client = new ViesClient();
try {
$client->checkVat('INVALID', '123');
} catch (ViesValidationException $e) {
echo "Validation error: {$e->getMessage()}\n";
}use Omisai\ViesRest\Exceptions\ViesApiException;
use Omisai\ViesRest\ViesClient;
$client = new ViesClient();
try {
$client->checkVat('DE', '123456789');
} catch (ViesApiException $e) {
echo "API error: {$e->getMessage()}\n";
echo "Status code: {$e->getStatusCode()}\n";
}use Omisai\ViesRest\ViesClient;
use Omisai\ViesRest\Http\HttpClientFactoryInterface;
class CustomHttpClientFactory implements HttpClientFactoryInterface
{
public function create(string $baseUrl, array $options = [])
{
// Return a custom adapter implementing HttpClientInterface
}
}
$client = new ViesClient(clientFactory: new CustomHttpClientFactory());use Omisai\ViesRest\ViesClient;
use Omisai\ViesRest\Validation\VatNumberValidator;
class CustomVatNumberValidator extends VatNumberValidator
{
// Implement custom validation logic
}
$client = new ViesClient(validator: new CustomVatNumberValidator());use Omisai\ViesRest\ViesClient;
$client = new ViesClient();
$vatNumbers = [
['DE', '123456789'],
['NL', '123456789B01'],
['FR', '12345678901'],
];
$results = [];
foreach ($vatNumbers as [$country, $vat]) {
try {
$response = $client->checkVat($country, $vat);
$results[] = [
'country' => $response->countryCode,
'vat' => $response->vatNumber,
'valid' => $response->valid,
'name' => $response->name,
];
} catch (Exception $e) {
$results[] = [
'country' => $country,
'vat' => $vat,
'error' => $e->getMessage(),
];
}
}
print_r($results);Run the test suite using Pest:
composer test- The VIES service has rate limits - avoid excessive requests
- REST calls are synchronous and may take 1-5 seconds
- Consider caching valid VAT numbers to reduce API calls
- Use the test service for development to avoid affecting production quotas
- Requires internet connection to VIES service
- Service may be unavailable during maintenance windows
- Rate limiting applies to prevent abuse
- Some countries may have additional validation rules
Please see CONTRIBUTING.md for details on how to contribute to this project.
Please see SECURITY.md for details on reporting security vulnerabilities.
This package is open-sourced software licensed under the MIT license.
If you find this package useful, please consider sponsoring the development: Sponsoring on GitHub
Your support helps us maintain and improve this open-source project!