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

Company Info Laravel Package

worksome/company-info

Laravel package for looking up company details by VAT number. It offers a unified API across multiple data providers like VIES and Virk, returning consistent DTOs for names, addresses, and registration info so you can easily swap or combine sources.

View on GitHub
Deep Wiki
Context7

Getting Started

worksome/company-info is a thin Laravel wrapper around public company registries (VIES for EU VAT numbers, national registries such as Denmark's CVR, etc.). You give it a country + registration/VAT number, it gives you back a normalized company record. It is not a CRM or an enrichment service — it does one lookup, synchronously, per call.

First steps:

  1. composer require worksome/company-info — on Laravel 13 the floor is now ^1.5 (v1.5.0 is purely an L13 compatibility bump over 1.4.0; no API changes).
  2. Publish the config: php artisan vendor:publish --provider="Worksome\CompanyInfo\CompanyInfoServiceProvider" (or the company-info tag), then open config/company-info.php. This is the file that matters — it defines which drivers are available and which credentials/headers each one needs. Some registries (e.g. CVR) require a descriptive User-Agent; VIES needs nothing but is rate-limited and flaky.
  3. Add the relevant env vars to .env and .env.example. Because config is cached in production, never call env() outside the config file.
  4. Smoke-test from php artisan tinker:
use Worksome\CompanyInfo\Facades\CompanyInfo;

$company = CompanyInfo::lookup('DK', '12345678');

$company->name;    // "Worksome ApS"
$company->address; // street, postcode, city

Best first use case: a "enter your CVR/VAT number" field on a billing/onboarding form that autofills company name and address. Start with a single driver and a single country; add breadth later. Do the actual lookup in a queued job or an axios call to your own endpoint — never inline in a page render, because registry APIs regularly take 2–10 seconds.

Implementation Patterns

Wrap the facade in your own action. The facade is convenient but leaks a third-party API surface across your codebase. Put a single LookupCompany action behind an interface, inject it, and have it return your own DTO (or the package DTO if you're happy to couple). Everything else — caching, retries, fallbacks, logging — lives in one place.

Normalize before you call. Registries are unforgiving about formatting. Strip spaces/dashes, uppercase, and split the country prefix off a VAT number (DK12345678 → country DK, number 12345678). Validate the shape locally first (DK CVR = 8 digits, etc.) so you don't burn a network call on input you can reject yourself.

Cache aggressively. Company registration data changes on the order of years, not minutes. Cache::remember("company-info:{$country}:{$number}", now()->addDays(7), fn () => ...) cuts the vast majority of calls and removes the registry as a runtime dependency for repeat visitors. Cache the successful result only, and keep the failure cache short (a few minutes) if you cache failures at all.

Fallbacks, not a single point of failure. National registries are faster and return richer data; VIES is EU-wide but slow and frequently degraded. Try the national driver first, fall back to VIES when it's configured for that country, and record which source answered. The package's driver abstraction makes this a loop over config, not a rewrite.

Queue the lookup. A LookupCompanyInformation job that fills in the company fields on your Customer/BillingDetails model, with a status column (pending/verified/unverified) so the UI can show progress. This is also what makes the feature testable and retryable.

Persist raw alongside normalized. Store the mapped fields (name, address, vat_number, verified_at) and the raw payload in a JSON column. When a registry changes its response shape, or when finance asks "what did we see in March", you'll have the answer.

Front-end integration. A tiny POST /companies/lookup controller returning { name, address, city, postal_code }, called from Alpine/Livewire/Inertia on blur of the VAT field, with debounce. Keep the controller dumb; all logic lives in the action.

Testing. Bind a fake implementation of your interface in the container, or use the package's faking mechanism / Http::fake() for the underlying client. CI should never touch a real registry — tests become slow and non-deterministic, and some drivers rate-limit by IP.

Gotchas and Tips

  • A failed lookup is not an invalid number. VIES in particular returns "service unavailable" for perfectly valid VAT numbers during outages. Model three states — verified, invalid, unverified — and let users proceed with an unverified number rather than blocking signup. Finance/accounting can reconcile later.
  • Timeouts are the default failure mode. Configure a short HTTP timeout and one retry with backoff, then give up and mark as unverified. Never let a registry stall a request that a user is waiting on.
  • Exceptions are coarse. Wrap the package's exceptions in your own (CompanyNotFoundException, RegistryUnavailableException) at the action boundary so callers don't import vendor exception classes.
  • User-Agent and credentials. Some registries reject requests with a generic or missing User-Agent, and the error surfaces as a confusing 4xx. Set a descriptive one (app name + contact URL) in the config.
  • Country codes are ISO-3166 alpha-2, not VAT prefixes and not lowercase. Normalize input early; a stray dk or Denmark will fail in unhelpful ways.
  • Config caching bites. If a driver works in tinker and not in production, it's almost always php artisan config:cache having captured stale or missing env values.
  • Rate limits are per-IP and undocumented. If you're doing bulk imports, throttle (RateLimiter / Bus::batch with a delay) rather than firing hundreds of lookups.
  • GDPR-adjacent data. Danish CVR and similar return personal data for sole traders (enkeltmandsvirksomhed). Don't dump raw payloads into logs or error trackers.
  • Extension point: adding a registry is a driver implementation plus a config entry — do that only if the package genuinely lacks coverage; otherwise prefer resolving the company from your own stored data and using the package purely at the point of onboarding.
  • Version pinning: pin ^1.5 if you're on Laravel 13. Nothing in 1.5.0 changes behaviour for existing 1.4.x users, so an upgrade during an L12 → L13 migration is low-risk — but that's an infrastructure release, not a reason to adopt the package.
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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib