ibericode/vat
Validate European VAT numbers and retrieve company details via VIES and other services with this PHP library. It provides a clean API, Laravel integration, caching, and multi-country support, making VAT checks reliable and easy to add to your app.
Architecture fit. ibericode/vat is a small, framework-agnostic PHP library with three concerns: VAT ID validation against VIES, VAT rate lookup by country, and gross/net price calculation. That narrow surface makes it a good fit as an infrastructure adapter in a hexagonal or service-layer design — it sits behind an interface we own (VatValidator, TaxRateProvider) rather than leaking into controllers, models, or Blade templates. It does not impose a container, config system, or ORM, so it does not fight Laravel's architecture. The trade-off is that everything Laravel-shaped — service provider, config publishing, caching, retries, queueing, events — is ours to build.
Integration feasibility. Low effort to get to "hello world": composer require, instantiate the validator/rates/calculator classes, call them. No migrations, no published config, no facades. Realistic first-pass integration is 2–4 developer days including an adapter, a config file, cache wiring, and tests. The complexity is not in the library; it is in the policies we wrap around it (caching, fallback, audit, rounding).
Technical risk. The dominant risk is the external dependency, not the code:
ext-soap availability in our runtime images and the package's PHP version constraints need verification early — that is the classic day-three surprise.Key questions.
Stack fit. Wrap, do not sprinkle. Introduce a VatService (or two: VatValidationService, TaxRateService) bound via a small service provider, with a config/vat.php for cache TTLs, HTTP/SOAP timeout, retry count, and fallback rate behavior. Call it from form requests, jobs, and checkout actions only through the interface. Use Laravel's cache for validation results keyed by normalized VAT ID plus country — long TTL for confirmed-valid, short TTL for confirmed-invalid, and no caching of transport errors except a short negative cache to protect VIES during an outage. Push revalidation to a queued job where the UX allows, so the synchronous path is only the first check. Emit events (VatValidationFailed, VatValidationUnavailable) so observability and customer messaging are decoupled from the library.
Migration path. If replacing a hardcoded rates table or a prior tax provider: (1) define the internal interface and adapt the current implementation to it first; (2) add the ibericode/vat adapter behind the same interface; (3) dual-run in shadow mode and diff rates and validation outcomes over a representative period; (4) backfill or reconcile stored rates; (5) cut over per flow, checkout validation first, invoicing last. Keeping the interface stable makes the library itself replaceable — which matters given the maintenance risk.
Compatibility. Verify PHP constraint against our runtime, confirm ext-soap (or whatever transport the version uses) is installed in every image including CI and queue workers, and check Composer conflict risk with existing HTTP/SOAP clients. Because it is framework-agnostic, Laravel version is not a constraint — but that also means no Laravel 11+ package discovery conveniences, so our provider registration is manual. Pin the version and treat rate-data updates as deliberate, tested upgrades rather than composer update drift.
Sequencing. Spike and validate VIES reachability from our network → adapter + contract tests (fake VIES at the boundary, never in CI against live VIES) → caching and timeout/circuit-breaker policy → checkout integration with an explicit "pending validation" state → persistence of validation evidence for invoicing → rate refresh process and diff tests → dashboards and alerts on validation failure rate and VIES latency.
Maintenance. Two independent rhythms. Application maintenance is trivial — stable, small API, infrequent breaking changes. Data maintenance is the real burden: rate changes are legislative events, and the package's bundled rates only move when a release lands. We need a scheduled check (monthly at minimum, plus known EU rate-change dates) that diffs the package's rates against a maintained expectation and fails loudly. Also budget for VIES-side changes — endpoint behavior and throttling have shifted before and will again.
Support. Errors will reach customer support as opaque failures unless we define a taxonomy at the adapter boundary: invalid format, invalid/unregistered, service unavailable, timeout, rate-limited. Each maps to distinct copy for the customer and a distinct runbook step for support. Without that, every VIES incident becomes an engineering escalation.
Scaling. Rate lookup and calculation are local and effectively free — cache the rates map in memory per request or in the framework cache. Validation is the only scaling constraint: it is a synchronous third-party call against a throttled public service. Cache hit rate is the entire scalability story; a well-tuned cache turns near-100% of repeat B2B customers into zero external calls. Concurrent outbound calls should be bounded so a traffic spike cannot turn into a self-inflicted VIES ban.
Failure modes. (a) VIES outage blocking checkout — mitigate with soft-fail plus deferred revalidation and a business-approved grace policy. (b) Stale rates producing mispriced or non-compliant invoices — mitigate with the diff check and a documented override path. (c) Rounding drift breaking reconciliation — mitigate by fixing a rounding policy at the domain layer and testing against finance's expected totals. (d) Silent acceptance of invalid VAT IDs due to error conflation — mitigate with strict error typing and alerting. (e) Egress/DNS/firewall changes quietly breaking the outbound call — mitigate with a synthetic canary check. (f) Maintainer abandonment — mitigated by the adapter boundary and a documented fork plan.
Ramp-up. A mid-level Laravel developer is productive in half a day on the library API and one to two days on the adapter and caching layer. The genuine ramp is domain, not code: reverse charge, place-of-supply, evidence retention, and rounding conventions require one to two weeks of pairing with finance/tax stakeholders. Plan onboarding around the tax rules, not the package.
TPM recommendation. Adopt as a component, not a platform. It is a cheap, MIT-licensed, low-coupling way to get VIES validation and default EU rates. It is not a tax engine and should never be the system of record for tax determination or evidence. Invest the saved integration effort into the adapter boundary, caching and resilience policy, audit persistence, and the rate-diff process — those are where the operational risk actually lives.
How can I help you explore Laravel packages today?