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 Bundle Laravel Package

prometee/vies-client-bundle

Symfony bundle that integrates a VIES client for validating EU VAT numbers against the official European Commission service, offering a simple service-based API for checking VAT registration status directly in your Symfony applications.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require prometee/vies-client-bundle

Requires PHP with ext-soap enabled (php -m | grep soap). The bundle is a thin Symfony wrapper around prometee/vies-client, which itself wraps the EU VIES SOAP endpoint (https://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl).

Register it manually if Flex doesn't do it (likely — it has few dependents):

// config/bundles.php
return [
    // ...
    Prometee\VIESClientBundle\VIESClientBundle::class => ['all' => true],
];

Where to look first, in this order:

  1. vendor/prometee/vies-client-bundle/ — README.md, then DependencyInjection/Configuration.php (the real config tree) and Resources/config/services.* (service ids and aliases).
  2. bin/console debug:container vies --show-private and bin/console debug:autowiring vies — confirm the exact service id/alias before you type-hint anything.
  3. vendor/prometee/vies-client/src/ — the ViesClient class and the CheckVatResponse model; that's where the method signatures and response getters actually live.

First use case — validate a VAT number in a controller or command:

use Prometee\VIESClient\Soap\Client\ViesClient;

final class VatController
{
    public function __construct(private ViesClient $vies) {}

    public function __invoke(string $countryCode, string $vatNumber): Response
    {
        $response = $this->vies->checkVat(strtoupper($countryCode), $vatNumber);

        return new JsonResponse([
            'valid'       => $response->isValid(),
            'name'        => $response->getName(),
            'address'     => $response->getAddress(),
            'requestDate' => $response->getRequestDate()?->format(DATE_ATOM),
        ]);
    }
}

If autowiring fails, wire it explicitly in config/services.yaml using the id you found with debug:container.

Implementation Patterns

Wrap it in your own port. The bundle's value is DI + config; don't let VIES types leak into your domain.

interface VatNumberValidatorInterface
{
    public function validate(string $countryCode, string $vatNumber): VatValidationResult;
}

Map the response to a small value object with a status enum: VALID, INVALID, UNAVAILABLE, MALFORMED_INPUT. The adapter is the only place that catches \SoapFault and knows about CheckVatResponse. Everything else (forms, checkout, onboarding) depends on the interface and is trivially testable.

Symfony Validator constraint. Wrap the interface in a Constraint + ConstraintValidator so VAT fields validate like any other field:

#[Attribute]
final class ValidVatNumber extends Constraint
{
    public string $unavailableMessage = 'VAT service is temporarily unavailable, please retry.';
}

Decide explicitly whether UNAVAILABLE is a validation failure or a pass-through — for B2B signup it's usually the latter, with the account flagged "pending VAT verification".

Async for anything bulk. A single checkVat takes 300 ms–5 s and occasionally hangs. Dispatch a ValidateVatNumber message via Symfony Messenger, store an entity with countryCode, vatNumber, valid, requestDate, checkedAt, rawResponse. Run that transport with low concurrency — VIES publishes global concurrency limits and will fault with GLOBAL_MAX_CONCURRENT_REQ.

Two-tier caching. Positive results: cache ~24h–7 days and refresh with a cron, because validity changes (deregistrations, insolvencies). Negative results: short cache (1h) to stop repeated hammering of bad input. Use cache.app with key vies:{country}:{number}; don't cache UNAVAILABLE at all.

Config over code. Publish/tune the bundle config for WSDL URL and SOAP timeouts rather than touching the client:

# config/packages/vies_client.yaml  (confirm the root key in the bundle's Configuration.php)
vies_client:
    wsdl: 'https://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl'
    soap_options:
        connection_timeout: 3
        timeout: 8
        trace: true   # dev only

Decorate, don't fork. Add logging, metrics, and caching by decorating the client service instead of editing call sites:

services:
    App\Vies\LoggingViesClient:
        decorates: 'prometee.vies.client'  # verify the id
        arguments: ['@.inner', '@logger']

Use checkVatApprox for onboarding. If you already collected a company name/address, the approximate check gives far stronger B2B evidence than a bare validity boolean.

Gotchas and Tips

VIES is frequently down — design for it. Outages are routine and surface as \SoapFault, not as valid=false. Catch SoapFault and inspect the message; SERVICE_UNAVAILABLE, MS_UNAVAILABLE, GLOBAL_MAX_CONCURRENT_REQ, and MS_MAX_CONCURRENT_REQ all mean "try later", while INVALID_INPUT means your input is wrong. Never let a SOAP fault crash checkout — degrade to "pending verification".

Country codes are a trap. Uppercase ISO-2, but Greece is EL in VIES (not GR), and Northern Ireland is XI. Pass the number without the country prefix. VIES will happily fault on a lowercase code.

Invalid VAT is not a fault. A syntactically fine but unregistered number returns valid=false with empty name/address — but so can a faulting member-state backend. Check the fault first, then the boolean; otherwise you'll mark legitimate businesses invalid during an outage.

Timeouts. SoapClient has no meaningful default timeout of its own — it falls back to default_socket_timeout (60s). Always set connection_timeout and timeout in the options, or one flaky request will pin a PHP-FPM worker.

Store requestDate, not now(). The response carries the timestamp of the actual national-DB consultation; that's the legally meaningful evidence, not your server clock.

WSDL caching. If soap.wsdl_cache_enabled is off, each client instantiation re-fetches the WSDL. Keep the client as a shared service and leave WSDL caching on outside dev.

Low-maturity package. 4 stars / 0 dependents means small surface and little battle-testing. Pin a exact version ("prometee/vies-client-bundle": "x.y.z") and read the changelog before upgrading Symfony majors. Good news: it's thin — if it breaks, replacing it is one service definition around prometee/vies-client.

Never call VIES in CI. Tests should bind a stub implementation of your validator interface (or a MockViesClient in config/services_test.yaml / when@dev). Hitting the live service will make your suite flaky and may get you throttled.

Debugging SOAP. With trace: true, log __getLastRequest() / __getLastResponse(). If you need the raw body on faults, construct with exceptions: false and inspect __getLastResponse() — but remember exceptions behaviour isn't standard Symfony HTTP client behaviour, so keep it isolated to your adapter.

Corporate networks. SoapClient ignores HTTP_PROXY; if you're behind a proxy, pass a stream_context via the SOAP options. Also ensure the OpenSSL CA bundle is current, or you'll get cryptic "Could not connect to host" errors.

Consider the official REST API. VIES now offers a REST endpoint; the SOAP service is legacy-but-supported. For a new build, weigh the REST API directly against this wrapper — the bundle's main benefit is Symfony wiring, not protocol abstraction.

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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource