danielebarbaro/laravel-vat-eu-validator
A Laravel package for validating European Union VAT numbers via the VIES service. It ships a ready-made validation rule so you can verify VAT IDs in form requests and controllers with a single line, plus helpers for checking format and querying member-state data.
Install and let package discovery wire it up:
composer require danielebarbaro/laravel-vat-eu-validator
php artisan vendor:publish --tag=vatvalidator # confirm the tag from the publish list
Three places to look first:
config/vatvalidator.php — the only file most apps ever touch. It selects the VIES transport (SOAP by default, REST as the alternative), sets timeouts, and wires the client. Drive it from env rather than editing the file, so staging and production can differ.src/Rules — the three bundled validation rules. Read them once, side by side: one is an offline format check (country prefix plus national pattern), one does the format check and a live VIES lookup, and one is the lenient variant that keeps working when VIES is down. Picking between these is the single most consequential decision you make with this package.DanieleB\LaravelVatEuValidator\Facades\VatValidator — the imperative entry point for cases where a validation rule doesn't fit: queue jobs, imports, admin screens, artisan commands.If the app uses Laravel Boost, the package now ships AI guidelines: php artisan boost:install on a fresh setup, or php artisan boost:update --discover on an app that already has Boost. They cover the three rules, the facade, both client configurations and how VIES failures surface — a genuinely useful way to get a correct first draft of a validator without reading the source.
First use case, a B2B registration form:
'vat_number' => ['required', 'string', 'max:20', new VatNumber()],
That gets you a fast, offline rejection of obviously malformed input. Add the live check only when the user commits (see below).
Two-stage validation. Put the format-only rule on the form request and the VIES-backed rule at the point of commitment — checkout, customer approval, invoice creation. You get instant feedback with zero network latency, and you only pay the VIES round-trip once per real conversion.
Normalise before you validate and before you store. Strip spaces, dots and dashes, uppercase everything, and keep the country prefix in the stored value. VIES expects the prefix form, and normalising on the way in saves you from re-cleaning rows later.
Persist the outcome, not just the input. Store the normalised number, the boolean result, and a checked_at timestamp. VIES data changes; a true from eighteen months ago is not evidence of anything. A scheduled job that re-checks active customers on a quarterly cadence is cheap and catches deregistered numbers.
Use the facade for non-form flows. Bulk imports, webhooks and admin overrides don't want a Validator instance:
$result = VatValidator::check($normalisedVatNumber);
Cache hot lookups. A positive result can reasonably live for a day, a negative one for a few hours. VIES is a shared, rate-limited service — treating it as a free local call is how you end up throttled.
Test against a fake, never the live service. Point the container at a stub of the client (or of the validator) in your test suite. If you use the REST transport, Http::fake() covers most of it. A test suite that hits VIES will be slow, flaky, and rude.
Let Boost do the scaffolding. Ask it for "a form request validating an EU VAT number with this package" once the guidelines are installed; you'll get the current rule names and config keys rather than plausible-looking ones from its training data.
Failures surface as exceptions, not false. A validation result is one of three things: valid, invalid, or the service couldn't tell you. This package is careful about the third case, which is exactly why there are three rules. Decide your policy up front: block the signup, or accept it and queue a manual re-check? Most B2B flows should do the latter — losing a customer to a VIES outage is worse than absorbing a small fraud risk.
VIES is a slow external dependency. Expect roughly a second per lookup, plus regular scheduled maintenance windows. Set timeouts in the config, run bulk re-validation in a queue with retries, and log the fault code alongside the country code so you can tell MS_UNAVAILABLE from MS_MAX_CONCURRENT_REQ from a timeout.
Country-code traps. Greece appears as EL in VIES, not GR. Northern Ireland uses XI, and GB is no longer a VIES participant post-Brexit. Numbers that pass your own regex can still fail VIES purely on prefix handling.
Valid ≠ legitimate. VIES only confirms the number is registered. It says nothing about whether the business behind it is who they claim to be, or whether they'll pay. Pair it with a name/address sanity check on high-value flows.
Don't hand-edit the Boost guidelines file. resources/boost/guidelines/core.blade.php is verified by a drift test that cross-checks every class, method, config key, env variable, rule name, translation key and publish tag it mentions against the actual code. If you patch it in vendor/, the next rename silently breaks it and the package's CI is what catches it — not yours. Publish/override it properly if you need to customise.
Guidelines are inert until you ask for them. boost:install on a new app, or boost:update --discover on an existing one. They will not appear just because the package is installed.
laravel/boost is only suggested. Don't add it as a production dependency on this package's account. And note that v3.5.0 changed no runtime behaviour — if you're upgrading from 3.4.x, the only thing to do is re-run boost:update --discover.
How can I help you explore Laravel packages today?