mibo/vat
mibo/vat is a Laravel/PHP package for VAT handling, offering a simple API to manage VAT rates and calculate net, tax, and gross amounts. It helps keep VAT logic clean, configurable, and easy to integrate into your applications.
Install via Composer and let Laravel's package discovery register the service provider:
composer require mibo/vat
php artisan vendor:publish --tag=vat-config # check README; tag name may differ
First place to look is the published config/vat.php and the package's README.md — with a low-adoption package (0 stars, 0 dependents), treat the source as the source of truth. Confirm the exact entry point before wiring it in: check src/ for a facade, a service class, or a Vat model/trait, and note whether calls hit VIES live or resolve from a cached table.
The first use case to build against is the classic EU B2B flow: a customer enters a VAT ID at checkout, you validate it, store the result, and if valid on a cross-border B2B sale, apply the reverse-charge rule (0% VAT, invoice marked "reverse charge"). Get that end-to-end before touching rate lookup for B2C.
// Verify the actual namespace/method names against src/ before assuming these
$result = Vat::validate('DE123456789');
if ($result->isValid()) {
$order->vat_id = 'DE123456789';
$order->vat_verified = now();
$order->reverse_charge = $customerCountry !== $sellerCountry;
}
Validate on write, not on read. VAT ID status changes; VIES can flake out. Store vat_id, vat_valid, vat_checked_at, and the raw response (or a hash of it) on the customer/order. Re-check on significant events (account creation, first order, an explicit "revalidate" admin action) rather than every page load.
Centralise validation in a single service. Wrap the package in a small VatValidator app service so you can swap implementations later and inject caching, logging, and retry logic in one place instead of scattering it.
class VatValidator
{
public function check(string $vatId, ?string $countryCode = null): VatResult
{
return Cache::remember("vat:{$vatId}", now()->addHours(24), function () use ($vatId, $countryCode) {
$result = Vat::validate($vatId, $countryCode);
Log::info('vat.validate', ['vat_id' => $vatId, 'valid' => $result->isValid()]);
return $result;
});
}
}
Derive country from the ID prefix, but cross-check. VAT IDs carry a two-letter country prefix (DE, FR, IT, NL, AT, etc.); validate that prefix against the customer's billing country and reject mismatches — this catches fat-fingered IDs and is a cheap first gate before hitting VIES.
Reverse charge needs both conditions. A valid VAT ID alone does not mean reverse charge applies. You need: B2B sale, valid VAT ID, and cross-border (seller country ≠ customer country). Keep those three checks explicit in your order pipeline and test each branch.
Cache aggressively and deliberately. VIES is a shared external SOAP service; hammering it per page view is how you get rate-limited. Cache per VAT ID for 24h–7d depending on risk tolerance, and never cache a negative result as long as a positive one.
Rate lookup for invoicing. If the package exposes VAT rates per country, resolve them at invoice-generation time and snapshot the rate plus the resolved country on the invoice line — never re-derive from live data when rendering a historical invoice.
Events and async. If VIES latency is a problem at checkout, validate synchronously only for the "is this ID well-formed and does it pass a best-effort check" step, and queue a full VIES confirmation. Send a follow-up email and flag the order if it later fails.
testing/local environments — many packages ship a null client or a fixture; verify it.timeout and whether the package retries. Defaults often favour correctness over latency; set a short timeout (2–5s) for synchronous checkout calls and move retries to the queue.Validator or Client contract) in the service provider; if one exists, bind your own implementation via app()->bind() to add caching/retry/logging without forking. If not, wrap the facade as shown above — that's your stable seam.composer.json, read the changelog before upgrading, and keep the wrapper thin so replacing the package is a day's work, not a rewrite.info level. When a customer claims their ID is valid but your system rejected it, you need to see the exact VIES payload, not just the boolean.How can I help you explore Laravel packages today?