php-vies-soap
A lightweight, type-safe PHP package for validating EU VAT numbers via the European Commission's VIES (VAT Information Exchange System) SOAP service.
Install
composer require omisai/vies-soapPHP VIES SOAP
A lightweight, type-safe PHP package for validating EU VAT numbers via the European Commission's VIES (VAT Information Exchange System) SOAP service.
Features
- ✅ Type-safe: Full PHP 8.1+ type declarations and modern enum support
- ✅ Comprehensive validation: Built-in country code and VAT number validation
- ✅ Production ready: Supports both production and test VIES services
- ✅ Error handling: Structured exceptions for SOAP faults and validation errors
- ✅ Approximate matching: Advanced VAT validation with trader details
- ✅ All EU countries: Support for all 27 EU member states plus Northern Ireland
- ✅ Tested: 100% test coverage with Pest PHP testing framework
Requirements
- PHP 8.1 or higher
- SOAP extension (
ext-soap) enabled
Installation
Install the package via Composer:
composer require omisai/vies-soap
Quick Start
Basic VAT Validation
use Omisai\ViesSoap\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";
}
Using the Test Service
For development and testing, use the VIES test service:
use Omisai\ViesSoap\ViesClient;
use Omisai\ViesSoap\ViesConfig;
$client = new ViesClient(ViesConfig::test());
$response = $client->checkVat('DE', '100'); // Test VAT number
var_dump($response->valid); // true for test numbers
Configuration
Environment Configuration
use Omisai\ViesSoap\ViesConfig;
// Production environment (default)
$config = ViesConfig::production();
// Test environment
$config = ViesConfig::test();
// Custom WSDL URL
$config = new ViesConfig('https://custom-vies-service.com/service.wsdl');
// With SOAP options
$config = ViesConfig::production([
'connection_timeout' => 30,
'trace' => true,
'cache_wsdl' => WSDL_CACHE_NONE,
]);
SOAP Client Options
You can pass additional SOAP client options to customize the HTTP requests:
use Omisai\ViesSoap\ViesClient;
use Omisai\ViesSoap\ViesConfig;
$options = [
'connection_timeout' => 30,
'trace' => true,
'cache_wsdl' => WSDL_CACHE_BOTH,
'user_agent' => 'MyApp/1.0',
'proxy_host' => 'proxy.example.com',
'proxy_port' => 8080,
];
$config = ViesConfig::production($options);
$client = new ViesClient($config);
API Reference
ViesClient
The main client class for interacting with VIES services.
Constructor
public function __construct(
ViesConfig $config = new ViesConfig(),
?SoapClientFactoryInterface $clientFactory = null,
?VatNumberValidator $validator = null,
)
Methods
checkVat(string $countryCode, string $vatNumber): CheckVatResponse
Validates a VAT number and returns basic information.
Parameters:
$countryCode: Two-letter EU country code (e.g., 'DE', 'FR', 'NL')$vatNumber: The VAT number to validate
Returns: CheckVatResponse object
Example:
$response = $client->checkVat('NL', '123456789B01');
echo "Country: {$response->countryCode}\n";
echo "VAT Number: {$response->vatNumber}\n";
echo "Valid: " . ($response->valid ? 'Yes' : 'No') . "\n";
echo "Request Date: {$response->requestDate->format('Y-m-d')}\n";
echo "Company Name: {$response->name}\n";
echo "Address: {$response->address}\n";
checkVatApprox(CheckVatApproxRequest $request): CheckVatApproxResponse
Performs approximate VAT validation with trader details for enhanced verification.
Parameters:
$request: ACheckVatApproxRequestobject with trader information
Returns: CheckVatApproxResponse object
Example:
use Omisai\ViesSoap\DTO\CheckVatApproxRequest;
$request = new CheckVatApproxRequest(
countryCode: 'NL',
vatNumber: '123456789B01',
traderName: 'Example B.V.',
traderStreet: 'Main Street 123',
traderPostcode: '1234AB',
traderCity: 'Amsterdam',
requesterCountryCode: 'DE', // Optional: Your own VAT country
requesterVatNumber: '123456789', // Optional: Your own VAT number
);
$response = $client->checkVatApprox($request);
echo "Valid: " . ($response->valid ? 'Yes' : 'No') . "\n";
echo "Request ID: {$response->requestIdentifier}\n";
echo "Name Match: {$response->traderNameMatch}\n";
echo "Address Match: {$response->traderStreetMatch}\n";
Data Transfer Objects (DTOs)
CheckVatResponse
Contains the response from a basic VAT check.
Properties:
countryCode: string - The validated country codevatNumber: string - The validated VAT numberrequestDate: DateTimeImmutable - When the request was madevalid: bool - Whether the VAT number is validname: ?string - Company name (if available)address: ?string - Company address (if available)
CheckVatApproxResponse
Contains the response from an approximate VAT check with detailed matching information.
Properties:
countryCode: stringvatNumber: stringrequestDate: DateTimeImmutablevalid: booltraderName: ?stringtraderCompanyType: ?stringtraderAddress: ?stringtraderStreet: ?stringtraderPostcode: ?stringtraderCity: ?stringtraderNameMatch: ?string - Match confidence for nametraderCompanyTypeMatch: ?string - Match confidence for company typetraderStreetMatch: ?string - Match confidence for streettraderPostcodeMatch: ?string - Match confidence for postcodetraderCityMatch: ?string - Match confidence for cityrequestIdentifier: string - Unique identifier for the request
CheckVatApproxRequest
Request object for approximate VAT validation.
Constructor parameters:
countryCode: string (required)vatNumber: string (required)traderName: ?stringtraderCompanyType: ?stringtraderStreet: ?stringtraderPostcode: ?stringtraderCity: ?stringrequesterCountryCode: ?string - Your country's code for the requestrequesterVatNumber: ?string - Your VAT number for the request
Supported Countries
The package supports all current EU member states plus Northern Ireland (XI):
| Code | Country | Code | Country | Code | Country |
|---|---|---|---|---|---|
| AT | Austria | EL | Greece | MT | Malta |
| BE | Belgium | ES | Spain | NL | Netherlands |
| BG | Bulgaria | FI | Finland | PL | Poland |
| CY | Cyprus | FR | France | PT | Portugal |
| CZ | Czechia | HR | Croatia | RO | Romania |
| DE | Germany | HU | Hungary | SE | Sweden |
| DK | Denmark | IE | Ireland | SI | Slovenia |
| EE | Estonia | IT | Italy | SK | Slovakia |
| LV | Latvia | XI | Northern Ireland | ||
| LT | Lithuania | ||||
| LU | Luxembourg |
Country Code Validation
use Omisai\ViesSoap\Enum\EuropeanUnionCountry;
// Check if a country code is valid
$isValid = EuropeanUnionCountry::isEuropeanUnionCountryCode('DE'); // true
$isValid = EuropeanUnionCountry::isEuropeanUnionCountryCode('US'); // false
// Get all supported countries
$countries = EuropeanUnionCountry::cases();
foreach ($countries as $country) {
echo "{$country->name}: {$country->value}\n";
}
Error Handling
The package provides structured exception handling for different error scenarios.
Validation Errors
use Omisai\ViesSoap\Exceptions\ViesValidationException;
use Omisai\ViesSoap\ViesClient;
$client = new ViesClient();
try {
$response = $client->checkVat('INVALID', '123');
} catch (ViesValidationException $e) {
echo "Validation error: {$e->getMessage()}\n";
// Handle invalid country code or VAT number format
}
SOAP Service Errors
use Omisai\ViesSoap\Exceptions\ViesSoapException;
use Omisai\ViesSoap\ViesClient;
$client = new ViesClient();
try {
$response = $client->checkVat('DE', '123456789');
} catch (ViesSoapException $e) {
echo "SOAP error: {$e->getMessage()}\n";
echo "Fault code: {$e->getFaultCode()}\n";
echo "Fault string: {$e->getFaultString()}\n";
// Handle service unavailability, invalid requests, etc.
}
Common Exception Types
ViesValidationException: Invalid country code or VAT number formatViesSoapException: SOAP service errors (service unavailable, invalid requests, etc.)
Advanced Usage
Custom SOAP Client
You can inject custom SOAP client implementations for testing or advanced scenarios:
use Omisai\ViesSoap\ViesClient;
use Omisai\ViesSoap\Soap\SoapClientFactoryInterface;
class CustomSoapClientFactory implements SoapClientFactoryInterface
{
// Implement your custom factory
}
$client = new ViesClient(
clientFactory: new CustomSoapClientFactory()
);
Custom Validation
use Omisai\ViesSoap\ViesClient;
use Omisai\ViesSoap\Validation\VatNumberValidator;
class CustomVatNumberValidator extends VatNumberValidator
{
// Implement custom validation logic
}
$client = new ViesClient(
validator: new CustomVatNumberValidator()
);
Batch Processing
use Omisai\ViesSoap\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);
Testing
Run the test suite using Pest:
composer test
Testing with Mock Data
The package includes test helpers for mocking SOAP responses:
use Omisai\ViesSoap\ViesClient;
use Tests\Support\FakeSoapClient;
use Tests\Support\FakeSoapClientFactory;
// Create a fake SOAP response
$soapResponse = (object) [
'countryCode' => 'DE',
'vatNumber' => '123456789',
'requestDate' => '2024-01-01',
'valid' => true,
'name' => 'Test Company GmbH',
'address' => 'Test Street 123, 12345 Test City',
];
$fakeClient = new FakeSoapClient($soapResponse);
$client = new ViesClient(
clientFactory: new FakeSoapClientFactory($fakeClient)
);
$response = $client->checkVat('DE', '123456789');
// Response will contain the fake data
Performance Considerations
- The VIES service has rate limits - avoid excessive requests
- SOAP 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
Limitations
- Requires internet connection to VIES service
- SOAP extension must be enabled in PHP
- Service may be unavailable during maintenance windows
- Rate limiting applies to prevent abuse
- Some countries may have additional validation rules
Contributing
Please see CONTRIBUTING.md for details on how to contribute to this project.
Security
If you discover any security-related issues, please email [email protected] instead of using the issue tracker.
License
This package is open-sourced software licensed under the MIT license.
Sponsoring
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!