Open-source project
laravel/vat-calculator avatar
laravel/vat-calculator

vat-calculator, the EU VAT rules Laravel will not give you for free

Handle all the hard stuff related to EU MOSS tax/vat regulations, the way it should be.

1,286 stars106 forksPHPMIT

At a glance

What is it?
vat-calculator is a PHP package that calculates gross prices from net prices using EU VAT rates, validates VAT numbers against the EU VIES SOAP service, and integrates with Laravel and Cashier or runs standalone. Its own warning is blunt: it makes no promises that the taxes it calculates are correct, and it tells you to talk to an accountant.
Who is it for?
vat-calculator suits a PHP shop selling into the EU that needs a starting point for gross pricing, a country rate table and a VAT number check, since the standalone usage path is small and needs no framework.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 8 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A disclaimer that comes before the features

The most important line in this README is the warning at the top, and it appears before any example. This package does not provide any promises for correctly calculated taxes. You are still responsible for making sure that any calculated tax is correct for your use case, and if you are uncertain whether a certain tax is correct, it is best that you talk to an accountant.

That warning is unusual and worth taking at face value, because the whole premise of the package is that EU VAT is hard. The description says it handles all the hard stuff related to EU MOSS tax and vat regulations, the way it should be, and the topics list names the eu-moss-tax topic explicitly. MOSS is the Mini One Stop Shop scheme, the EU mechanism for charging VAT on digital services sold to consumers in other member states, and the reason it exists is that the rules are genuinely tangled.

The package is by Marcel Pociot, and the repository lives under the laravel organisation. The install name on Packagist is mpociot/vat-calculator, which is the older naming convention and is worth noticing if you are searching for it. The licence is MIT, the default branch is 3.x, and the release tags are v3.26.0 from 2026-05-05, 3.25.0 from 2025-11-24 and 3.24.0 from 2025-10-02. The last push was on 2026-09-28, so the branch is still being worked on.

The requirements are modest. PHP 7.3 or higher, and optionally Laravel 6.0 or higher, which means it can be used without a framework at all.

Gross price calculation, and the company case where net comes back

The core call takes a net price and a country code, and returns the gross price. The README shows the whole surface in one block:

php
// Easy to use!
VatCalculator::calculate(24.00, $countryCode = 'DE');
VatCalculator::calculate(24.00, $countryCode, $postalCode);
VatCalculator::calculate(71.00, 'DE', '41352', $isCompany = true);
VatCalculator::getTaxRateForLocation('NL');

// Check validity of a VAT number
VatCalculator::isValidVATNumber('NL123456789B01');

The third parameter is the postal code of the customer, and the fourth is a boolean indicating whether the customer is a company or a private person. The rule that makes this a real business calculation rather than a multiplication is the one that follows: if the customer is a company, which you should check by validating the VAT number, the net price gets returned. So the flag means reverse charge, and the caller is expected to establish it by validating the number first rather than by trusting the customer.

After the calculation the package exposes the intermediate values, which is the part that makes it usable in an invoice or a receipt:

php
$grossPrice = VatCalculator::calculate(24.00, 'DE'); // 28.56
$taxRate = VatCalculator::getTaxRate(); // 0.19
$netPrice = VatCalculator::getNetPrice(); // 24.00
$taxValue = VatCalculator::getTaxValue(); // 4.56

And you can ask for every rate a country has, which is how you find the reduced rate:

php
VatCalculator::getTaxRatesForCountry('DE'); // ["high" => 0.19, "low" => 0.07]

The high and low keys are the important detail. A country with more than one rate means you have to decide which one applies to a given product, and the package gives you the table but not the decision. A book at 7% and a restaurant at 19% in the same country is the kind of thing the caller has to know.

The facade form is the Laravel one. Outside Laravel you create an instance and call the methods on it, and the README is explicit that all the documentation examples use the Facade code, so make sure not to call the methods as if they were static methods:

php
use Mpociot\VatCalculator\VatCalculator;

$vatCalculator = new VatCalculator();
$vatCalculator->setBusinessCountryCode('DE');
$grossPrice = $vatCalculator->calculate(49.99, $countryCode = 'LU');

Note the setBusinessCountryCode call in the standalone example, which is absent from the facade examples. That is the one piece of setup a standalone user has to do themselves.

VIES validation, and the SOAP call that can fail

The VAT number validation is the part of this package with a hard external dependency, and the README is clear about both the format and the failure mode.

Before validating, you can check whether the country even requires you to collect VAT in the first place:

php
if (VatCalculator::shouldCollectVAT('DE')) {
    // This country code requires VAT collection...
}

The number itself should be in the format specified by VIES, the EU's VAT information exchange service, and the package truncates the number and removes non relevant characters and whitespace automatically, so you do not have to sanitise the input yourself.

The service relies on a third party SOAP API provided by the EU, and if that API is unavailable for whatever reason a VATCheckUnavailableException is thrown. That is the honest way to model it, because the alternative, returning false, would be indistinguishable from an invalid number and would silently let a reverse-charge sale through without a valid VAT number.

php
try {
    $validVAT = VatCalculator::isValidVATNumber('NL 123456789 B01');
} catch (VATCheckUnavailableException $e) {
    // The VAT check API is unavailable...
}

The alternative to the network call is a format-only check, and the README is explicit about what it does and does not give you:

php
// This check will return false because no connection to VIES could be made...
$validVAT = VatCalculator::isValidVATNumber('NL 123456789 B01');

// This check will return true because only the format is checked...
$validVAT = VatCalculator::isValidVatNumberFormat('NL 123456789 B01');

That distinction is the one to get right in your own code. A format check confirms the string looks like a VAT number, and it is useful when you do not want to wait for a response, but it confirms nothing about the customer. The README's own example makes the point twice, with the same number returning false from the network check and true from the format check.

The details lookup follows the same pattern, with the same exception, and returns a structure with the country code, the number, the request date, the validity flag, the name and the address.

UK VAT numbers are a different system, and the README says so

The UK left the EU VAT system after Brexit, and the package handles it as a separate case rather than pretending the VIES service covers it. The README notes that validating UK VAT numbers requires registering your application with the HMRC Developer Hub and following the official HMRC API documentation for details on authentication and setup.

The reason is visible in the response format. A UK lookup returns an array with a name, the VAT number and a nested address, where the address is broken into a line, a postcode and a country code:

php
try {
    $vat_details = VatCalculator::getVATDetails('GB 553557881');
    print_r($vat_details);
} catch (VATCheckUnavailableException $e) {
    // The VAT check API is unavailable...
}

Compare that with the EU response, which comes back as a stdClass object with countryCode, vatNumber, requestDate, valid, name and address as flat properties.

The two shapes are different, so any code that consumes the result of getVATDetails has to handle both structures, and that is a real integration detail that the type of the return value does not make obvious from the method signature.

There is also a naming inconsistency worth knowing before you grep. The README uses isValidVATNumber in some places and isValidVatNumberFormat in others, and the case of the acronym differs between the two method names. If you are copying from the README, copy exactly.

The documentation points at the HMRC API documentation for the service itself, and the CHANGELOG.md in the repository root is where changes to the country data and the service integrations will show up. The UPGRADE.md file is what the README tells you to read when upgrading the library, which matters because the default branch is 3.x and the tag naming has shifted between v3.26.0 and 3.25.0.

Where this fits, and what it does not do for you

The value of a package like this is the table, the wiring and the edge cases, and the edge cases are where the complexity lives. Rates differ by country and by product category. Some countries have no rate at all for some things. A business customer in a member state is reverse charged if the number validates. And the validation itself depends on a government SOAP service in another jurisdiction.

The alternative to using a package is to keep a table yourself, and the honest comparison is that a hand kept table is a spreadsheet that has to be updated whenever a government changes a rate. The package moves that maintenance into a config directory in your repository and gives you a version history for it through the changelog, and it gives you the reverse charge logic and the VIES integration for free.

The limits are equally clear. The package does not decide which rate applies to your product. It does not tell you whether your business registration, your place of establishment or your thresholds mean you should be collecting VAT in a given country at all, since that is the MOSS question and it is a legal determination. It does not file anything with a tax authority. And it explicitly does not promise the calculation is right.

There is also a versioning question, because the package is version 3 and the default branch is 3.x. The UPGRADE.md guide exists because there were breaking changes along the way, and Laravel 6.0 is the stated floor for the integration, which is a library version from 2019. The core code needs only PHP 7.3, so the constraint is on the framework integration rather than on the calculation logic, and a standalone user is unaffected by it.

The honest summary is that this package removes the mechanical work and leaves the judgement, which is the correct division of labour for a tax calculation, and which is also why the disclaimer is at the top of the page rather than at the bottom.

Editorial conclusion

vat-calculator suits a PHP shop selling into the EU that needs a starting point for gross pricing, a country rate table and a VAT number check, since the standalone usage path is small and needs no framework. It is the wrong choice if you need a compliance guarantee, because the package itself disclaims it and points you to an accountant, and it is the wrong choice if you cannot accept a runtime dependency on the EU SOAP service, since the number validation and the detail lookup both throw VATCheckUnavailableException when that API is down. Before adopting it, read the warning at the top of the README, check the UPGRADE.md guide since the default branch is 3.x, and confirm the country rate data in config/ still matches the rates you are charging.

Frequently asked questions

How do I install the vat-calculator package?

Run composer require mpociot/vat-calculator. It needs PHP 7.3 or higher, and Laravel 6.0 or higher is optional, since the package can be used standalone by creating a VatCalculator instance and setting the business country code.

How do I calculate a gross price with vat-calculator?

Call the calculate method with a net price and a country code, and optionally a postal code and a boolean for whether the customer is a company. If the customer is a company, which you should establish by validating their VAT number, the net price is returned instead of the gross price.

What happens if the EU VIES service is unavailable?

The README states that the service relies on a third party SOAP API provided by the EU, and that if it is unavailable a VATCheckUnavailableException is thrown. You can also use isValidVatNumberFormat for a format-only check that makes no network call.

How are UK VAT numbers handled differently?

Validating UK VAT numbers requires registering your application with the HMRC Developer Hub and following the HMRC API documentation for authentication and setup. The details returned for a UK number also have a different shape, with a nested address containing a line, a postcode and a country code.

Is the tax calculation in this package guaranteed to be correct?

The README carries a warning stating that the package does not provide any promises for correctly calculated taxes, that you remain responsible for making sure any calculated tax is correct for your use case, and that if you are uncertain you should talk to an accountant.

Official sources

  1. Issues
  2. laravel/vat-calculator on GitHub
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/laravel-vat-calculator.svg)](https://hysenlabs.com/projects/laravel-vat-calculator)

Was this useful?