dragonbe/vies
PHP Composer package to validate EU VAT numbers via the European Commission VIES SOAP service. Returns country code, VAT number, request date, validity, and (when available) registered company name and address. GDPR-friendly: no data stored by the package.
Installation
composer require dragonbe/vies:^2.3.2
Add the service provider in config/app.php:
'providers' => [
// ...
Dragonbe\Vies\ViesServiceProvider::class,
],
Basic Usage
Inject the Dragonbe\Vies\Facades\Vies facade or service into your controller/service:
use Dragonbe\Vies\Facades\Vies;
$response = Vies::validate('BE123456789'); // Updated Belgian format support
First Use Case Validate a VAT number in a registration form with updated country-specific rules:
public function validateVat(Request $request)
{
$vatNumber = $request->input('vat_number');
$response = Vies::validate($vatNumber);
if ($response->isValid()) {
return response()->json(['valid' => true]);
}
return response()->json(['valid' => false, 'errors' => $response->getErrors()], 400);
}
Dragonbe\Vies\Facades\Vies – Primary entry point for all operations.Dragonbe\Vies\Response – Contains validation results, errors, and metadata.config/vies.php – Adjust timeout, cache settings, or proxy configurations.BE (Belgium) and CY (Cyprus) VAT numbers.Validation Workflow
$response = Vies::validate('CY612345678'); // Updated CY format support (can start with 6)
if ($response->isValid()) {
// Proceed with business logic
} else {
// Handle errors (e.g., invalid format, non-existent VAT)
}
Batch Validation Useful for bulk processing (e.g., importing supplier data):
$vatNumbers = ['BE123456789', 'CY612345678', 'FR456789123'];
$results = Vies::validateBatch($vatNumbers);
foreach ($results as $result) {
if (!$result->isValid()) {
Log::error("Invalid VAT: {$result->getVatNumber()}", $result->getErrors());
}
}
Caching Responses
Reduce API calls by caching responses (configured in config/vies.php):
// Manually cache a response
Vies::cache('BE123456789', $response, now()->addHours(1));
Integration with Laravel Validation Extend Laravel’s validator for seamless form handling:
use Dragonbe\Vies\Rules\ValidVat;
$request->validate([
'vat_number' => ['required', new ValidVat],
]);
Queue Delayed Validations Offload validation to a queue job for long-running processes:
ValidateVatJob::dispatch($vatNumber)->delay(now()->addMinutes(5));
Logging Invalid Responses Log failed validations for auditing:
if (!$response->isValid()) {
Log::channel('vies_errors')->info($response->toArray());
}
Country-Specific Format Validation
Use the updated format validation for BE and CY VAT numbers:
if (!Vies::isValidFormat('BE123')) {
return response()->json(['error' => 'Invalid Belgian VAT format'], 422);
}
Custom Error Handling
Override default error messages in your language files (resources/lang/en/vies.php):
return [
'invalid_format_be' => 'Belgian VAT numbers must be in the format BE0123456789.',
'invalid_format_cy' => 'Cypriot VAT numbers must start with 6 or 9 and be in the format CY612345678.',
];
Rate Limiting The VIES API has rate limits (e.g., 1 request per second). Batch requests or cache aggressively to avoid throttling.
Vies::validateBatch() and implement exponential backoff for retries.Country-Specific Formats
Updated formats for BE (Belgium) and CY (Cyprus) VAT numbers:
BE0123456789.6 or 9 (e.g., CY612345678 or CY912345678).
Validate locally first:if (!Vies::isValidFormat('CY512345678')) { // Will fail (must start with 6 or 9)
return response()->json(['error' => 'Invalid Cypriot VAT format'], 422);
}
Heartbeat Issues The VIES API's heartbeat mechanism (used to check service availability) is no longer working as expected. Monitor API responses for unexpected failures.
try {
$response = Vies::validate('BE123456789');
} catch (\Dragonbe\Vies\Exceptions\ViesException $e) {
Log::error("VIES API heartbeat failure: " . $e->getMessage());
}
Caching Stale Data VIES responses can become stale. Set short cache TTLs (e.g., 1 hour) and invalidate on failures:
Vies::forget('BE123456789'); // Clear cache if validation fails unexpectedly
Enable Debug Mode
Add to config/vies.php:
'debug' => env('APP_DEBUG', false),
This logs raw API responses to storage/logs/vies.log.
Mock Responses for Testing
Use the Vies::shouldReceive() method in PHPUnit:
Vies::shouldReceive('validate')
->once()
->with('BE123456789')
->andReturn(new Response(true, [], 'BE123456789'));
Custom Response Handlers
Extend the Dragonbe\Vies\Response class to add business-specific logic:
class CustomViesResponse extends \Dragonbe\Vies\Response
{
public function isTrustedSupplier()
{
return $this->isValid() && in_array($this->getCountryCode(), ['BE', 'CY', 'FR']);
}
}
Override API Endpoint For testing or custom APIs, bind a custom client in the service provider:
$this->app->bind('vies.client', function () {
return new \GuzzleHttp\Client(['base_uri' => 'https://test-vies-api.example.com']);
});
Add Metadata to Responses
Extend the validate() method via a decorator pattern:
$response = Vies::validate('BE123456789');
$response->setMetadata(['supplier_id' => 123, 'country_rules' => 'BE']);
Webhook for Validation Events
Listen for validation events (e.g., vies.validated, vies.failed) in EventServiceProvider:
protected $listen = [
'Dragonbe\Vies\Events\ValidationSucceeded' => [
'App\Listeners\LogValidVat',
],
];
Custom Country-Specific Rules Extend the package to handle additional country-specific validations:
Vies::extendCountryRules(function ($country) {
if ($country === 'BE') {
return 'BE\d{10}';
}
if ($country === 'CY') {
return 'CY[69]\d{8}';
}
return null;
});
How can I help you explore Laravel packages today?