Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Vies Client Laravel Package

prometee/vies-client

A PHP client for the European VIES VAT number validation service. It lets Laravel and PHP applications check VAT registration status across EU member states, handle SOAP responses, and integrate VAT validation into forms, checkout, or back-office workflows.

View on GitHub
Deep Wiki
Context7

Getting Started

Requirements: PHP 8.x with the soap extension enabled (ext-soap), outbound HTTPS to ec.europa.eu, and outbound DNS. The extension is not bundled by default in many minimal Docker images, so verify with php -m | grep soap before you do anything else.

Install:

composer require prometee/vies-client

First call — the only 90% use case:

use Vies\Client\ViesClient;

$client = new ViesClient();

try {
    $response = $client->checkVat('FR', '12345678901'); // country code, VAT number (no country prefix)
} catch (\Throwable $e) {
    // transport / SOAP / service error — NOT an invalid VAT
}

if ($response->isValid()) {
    $response->getName();        // legal name as registered
    $response->getAddress();     // registered address (may be a multi-line string)
    $response->getRequestDate(); // \DateTimeInterface of the VIES check
}

Where to look first:

  1. ViesClientInterface — the contract you should depend on (not the concrete class). This is your seam for tests and future swapping to the VIES REST API.
  2. checkVat() vs checkVatApprox() — the latter adds name/address matching flags (getTraderName(), isNameMatch(), isAddressMatch(), isCompanyTypeMatch()).
  3. The exception hierarchy — distinguish transport failure from business answer "invalid". These must not collapse into the same code path.
  4. The bundled tests / fixtures — they are your best reference for actual response shapes, since the WSDL is the real source of truth and it lives on the EU server.

First use case to wire up: a real-time VAT check at B2B account signup or at checkout when a customer checks "reverse charge / intra-EU B2B". Store the result (see below) rather than only gating the form.

Implementation Patterns

Wrap it — never call ViesClient directly from a controller. Define your own VatValidatorInterface with a validate(string $countryCode, string $vatNumber): VatValidation returning your domain object. The package is small and low-activity (few stars, one maintainer, zero dependents); the abstraction costs nothing and pays for itself the day you move to the REST API or a different client.

Cache aggressively with a short TTL. VIES is a shared EU service and the Commission's own guidance is that a check is only evidence for a limited window (commonly 24 hours). Persist countryCode, vatNumber, valid, name, address, requestDate, and a checkedAt column. Re-validate on a schedule (daily/weekly) if the customer is high-value; for long-lived accounts a background re-check is enough. Never hit VIES on every page render.

Treat "service unavailable" as "unverified", not "invalid". This is the single most important pattern. VIES has a well-known uptime problem — it goes down on weekends, around the New Year, and spontaneously during maintenance windows. If you reject orders when VIES returns a SOAP fault, you will lose European revenue every weekend. Model three states: valid, invalid, unknown/error. For unknown, allow the transaction but flag the invoice for manual review, or queue a retry.

Async re-validation via queue. For registration flows, fire a CheckVatJob after the user is created. The synchronous call is fine for checkout, but do not block OTP / onboarding on VIES. If the later check fails, downgrade the customer and notify finance — not the customer.

Use checkVatApprox when you actually need the name. For reverse-charge invoices, the legal name matters. Pass the name and address the customer entered and read isNameMatch() / isAddressMatch(). Do not hard-fail on a name mismatch — legal-name formatting differs wildly (legal suffixes like SAS/GmbH/S.r.l., transliteration of Greek/Cyrillic, accents, abbreviations). Treat a mismatch as a soft signal to prompt the user to correct their details.

Service registration (Symfony):

services:
    Vies\Client\ViesClientInterface:
        factory: ['Vies\Client\ViesClient', 'create']   # or `new ViesClient(...)` via arguments
    App\Vat\ViesVatValidator: ~

If the package's client accepts SOAP options in the constructor, inject them from config so you can control timeouts and WSDL caching without code changes.

Testing. Bind a fake VatValidatorInterface in your test container returning canned valid / invalid / ViesUnavailableException responses. Do not test against the real VIES endpoint in CI — it is slow, flaky, and rate-sensitive.

Audit logging. For invoices you must be able to prove you checked at the time of supply. Log the raw request identifier (getRequestIdentifier() on approx), the requestDate, and the response payload. This is the artefact tax authorities ask for.

Gotchas and Tips

  • ext-soap absence kills this package dead. Class "SoapClient" not found is the #1 first-day error. Add docker-php-ext-install soap in your Dockerfile and ext-soap to composer.json requirements.

  • Pass the country code separately and strip it from the number. VIES wants ('FR', '12345678901'), not ('FR', 'FR12345678901'). Users type it every possible way — normalise (uppercase, strip spaces/dots/-, strip leading country prefix) before calling.

  • Country codes must be uppercase ISO 3166-1 alpha-2. 'fr' will not validate. Also note Greece is EL for VAT purposes in many contexts but VIES accepts GR; and Northern Ireland has XI post-Brexit with special rules — test these explicitly.

  • Timeouts. PHP's default SoapClient has no meaningful timeout and will hang your worker for minutes when VIES is sick. Set connection_timeout and, if the client exposes it, a request timeout of ~5–10s in the options array. Wrap the call behind a circuit breaker if you can.

  • WSDL fetching is a hidden network dependency. The ?wsdl request fires on client construction unless cache_wsdl is enabled. In a container without a writable temp dir, or behind a proxy, this fails with a confusing "failed to load external entity" error. Enable WSDL caching in prod and warm it at deploy time.

  • A SOAP fault is not an answer. checkVat returning valid: false is a business result; a SoapFault or ServiceException is an infrastructure result. Catch and branch on exception type, not on message text. Many integrators accidentally treat faults as invalid and block real customers.

  • Response name/address are frequently empty strings for valid VAT numbers (some member states do not publish them, and Greece/Ireland are notorious). Do not treat an empty name as an invalid VAT.

  • checkVatApprox is not a stronger checkVat. It returns the same validity plus match flags that are advisory. Build your UX around "valid VAT, name needs confirmation" rather than a hard wall.

  • VIES has no published rate limit but will throttle you. The EU service is shared; hammering it for every row in a CSV import will get you blocked or just time out. Batch imports through a queue with a delay, and cache hits so repeats cost nothing.

  • The SOAP service is on a long-term migration path. The Commission has been pushing the JSON/REST endpoints at ec.europa.eu/taxation_customs/vies/rest-api/. Keep the package behind your VatValidatorInterface so swapping later is a one-file change rather than a refactor.

  • Package maturity is low (small star count, single maintainer, no dependents). Pin the version (composer.lock) and read the changelog before upgrading — the last release is recent enough that you should not assume long-term backwards-compatibility promises. Budget a few hours to be able to fork if needed; the surface area you actually use (checkVat, checkVatApprox, the response getters) is tiny.

  • Debugging trick: when you cannot tell whether a failure is yours or VIES', run the raw SoapClient against the WSDL URL directly with trace => true and dump __getLastRequest() / __getLastResponse(). If that works, your ViesClient configuration is the problem; if it doesn't, the service is down and you should stop debugging.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
adachsoft/php-code-style