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

Overview

VatCalculator

Tests Code Style Latest Stable Version Total Downloads

Handle all the hard stuff related to EU MOSS tax/vat regulations, the way it should be. Integrates with Laravel and Cashier — or in a standalone PHP application.

// Easy to use!
$countryCode = VatCalculator::getIPBasedCountry();
VatCalculator::calculate(24.00, $countryCode);
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');

Requirements

  • PHP 5.5 or higher
  • (optional) Laravel 5.0 or higher

Installation

Install the package with composer:

composer require mpociot/vat-calculator

Standalone

You can also use this package without Laravel. Simply create a new instance of the VatCalculator and use it. All documentation examples use the Laravel Facade code, so make sure not to call the methods as if they were static methods.

use Mpociot\VatCalculator\VatCalculator;

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

Usage

Calculate the gross price

To calculate the gross price use the calculate method with a net price and a country code as parameters.

$grossPrice = VatCalculator::calculate( 24.00, 'DE' );

The third parameter is the postal code of the customer.

As a fourth parameter, you can pass in a boolean indicating whether the customer is a company or a private person. If the customer is a company, which you should check by validating the VAT number, the net price gets returned.

$grossPrice = VatCalculator::calculate( 24.00, 'DE', '12345', $isCompany = true );

Receive more information

After calculating the gross price you can extract more information from the VatCalculator.

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

Validate EU VAT numbers

Prior to validating your customers VAT numbers, you can use the shouldCollectVAT method to check if the country code requires you to collect VAT in the first place.

if (VatCalculator::shouldCollectVAT('DE')) {

}

To validate your customers VAT numbers, you can use the isValidVATNumber method. The VAT number should be in a format specified by the VIES. The given VAT numbers will be truncated and non relevant characters / whitespace will automatically be removed.

This service relies on a third party SOAP API provided by the EU. If, for whatever reason, this API is unavailable a VATCheckUnavailableException will be thrown.

try {
    $validVAT = VatCalculator::isValidVATNumber('NL 123456789 B01');
} catch( VATCheckUnavailableException $e ){
    // Please handle me
}

Get EU VAT number details

To get the details of a VAT number, you can use the getVATDetails method. The VAT number should be in a format specified by the VIES. The given VAT numbers will be truncated and non relevant characters / whitespace will automatically be removed.

This service relies on a third party SOAP API provided by the EU. If, for whatever reason, this API is unavailable a VATCheckUnavailableException will be thrown.

try {
    $vat_details = VatCalculator::getVATDetails('NL 123456789 B01');
    print_r($vat_details);
    /* Outputs
    stdClass Object
    (
        [countryCode] => NL
        [vatNumber] => 123456789B01
        [requestDate] => 2017-04-06+02:00
        [valid] => false
        [name] => Name of the company
        [address] => Address of the company
    )
    */
} catch( VATCheckUnavailableException $e ){
    // Please handle me
}

Laravel Validator Extension

If you want to include the VAT number validation directly in your existing Form Requests / Validations, use the vat_number validation rule.

Example:

$rules = array(
    'first_name'  => 'required',
    'last_name'   => 'required',
    'company_vat' => 'vat_number'
);

$validator = Validator::make(Input::all(), $rules);

Important: The validator extension returns false when the VAT ID Check SOAP API is unavailable.

Cashier integration

If you want to use this package in combination with Laravel Cashier you can let your billable model use the BillableWithinTheEU trait. Because this trait overrides the getTaxPercent method of the Billable trait, we have to explicitly tell our model to do so.

use Laravel\Cashier\Billable;
use Mpociot\VatCalculator\Traits\BillableWithinTheEU;
use Laravel\Cashier\Contracts\Billable as BillableContract;

class User extends Model implements BillableContract
{
    use Billable, BillableWithinTheEU {
        BillableWithinTheEU::taxPercentage insteadof Billable;
    }

    protected $dates = ['trial_ends_at', 'subscription_ends_at'];
}

By using the BillableWithinTheEU trait, your billable model has new methods to set the tax rate for the billable model.

Set everything in one command:

  • setTaxForCountry($countryCode, $company = false)

Or use the more readable, chainable approach:

  • useTaxFrom($countryCode) — Use the given countries tax rate
  • asIndividual() — The billable model is not a company (default)
  • asBusiness() — The billable model is a valid company

So in order to set the correct tax percentage prior to subscribing your customer, consider the following workflow:

$user = User::find(1);

// For individuals use:
$user->useTaxFrom('NL');

// For business customers with a valid VAT ID, use:
$user->useTaxFrom('NL')->asBusiness();

$user->subscription('monthly')->create($creditCardToken);

Get the IP based Country of your user

Right now you'll need to show your users a way to select their country - probably a drop down - to use this country for the VAT calculation.

This package has a small helper function, that tries to lookup the Country of the user, based on the IP they have.

$countryCode = VatCalculator::getIPBasedCountry();

The $countryCode will either be false, if the service is unavailable, or the country couldn't be looked up. Otherwise the variable contains the two-letter country code, which can be used to prefill the user selection.

Frontend Integration

vat_calculator.js

Phew - so you know how to use this class, built your fancy payment form and now...? Well - you want to display the correct prices to your users and want it to update dynamically. So go ahead, add some routes, write some Javascript and in no time you'll be up and running, right?

Or you use the built in routes and vat_calculator.js library.

The VatCalculator JS library will automatically:

  • Calculate taxes whenever the selected country value changes
  • Automatically validate VAT-IDs / VAT numbers and use it for the calculation
  • Prefill the user's country with the IP based country

The Javascript library has no dependencies on third party frameworks.

In order to use the Javascript helper you need to publish the package files first. Go ahead and type:

php artisan vendor:publish --provider="Mpociot\VatCalculator\VatCalculatorServiceProvider"

Now you have a file called vat_calculator.js in your public/js folder.

Integrating it in your payment form

Add the published javascript file to your payment form.

<head>
    ...
    <script type="text/javascript" src="/js/vat_calculator.js"></script>
</head>

By default, the VatCalculator JS script is looking for a form with the ID payment-form. This form needs a data-amount attribute specifying the amount to use for the tax calculation in cents (just like Stripe uses it).

So your form should look like this, when you would calculate the taxes for 24.99 €

<form method="post" id="payment-form" data-amount="2499">

Next up, you need a dropdown to let your users select their billing country. This select field needs the data-vat="country" attribute, so that the VatCalculator JS knows, where to look for country codes.

Since there are also quite a few VAT rate exceptions for specific regions or cities, it is highly recommended to add an input field to collect postal codes. This field needs a data-vat="postal-code" attribute.

And last but not least, to automatically validate VAT Numbers / VAT IDs you can have an input field with the data-vat="vat_number" attribute specified.

So your form will look like this:

<form method="POST" id="payment-form" data-amount="2499">
    <div class="form-row">
        <label>
            <span>Country</span>
            <select data-vat="country">
                <option value="US">United States</option>
                <option value="GB">United Kingdom</option>
                <option value="DE">Germany</option>
                <option value="FR">France</option>
                <option value="IT">Italy</option>
                <option value="ES">Spain</option>
                <option value="CA">Canada</option>
                <option value="AU">Australia</option>
            </select>
        </label>
    </div>
    
    <div class="form-row">
        <label>
            <span>Postal Code</span>
            <input data-vat="postal-code"/>
        </label>
    </div>
    
    <div class="form-row">
        <label>
            <span>VAT Number</span>
            <input data-vat="vat-number"/>
        </label>
    </div>
</form>

Extra fields

To display the live tax calculation, you can use the classes vat-subtotal, vat-taxrate, vat-taxes and vat-total on any DOM element and VatCalculator JS will automatically set the inner HTML content for you.

Example:

<strong>Subtotal</strong>: € <span class="vat-subtotal"></span>
<strong>Tax rate</strong>: <span class="vat-taxrate"></span>%
<strong>Taxes</strong>: € <span class="vat-taxes"></span>
<strong>Total</strong>: € <span class="vat-total"></span>

Form attributes

Attribute Description Required
data-amount Use this attribute on the form you want to use for live calculation. It's the price in cent used for the calculation. Yes

Form fields

In order to calculate the right taxes, you need to add some extra inputs to your payment form. All these fields need to have a data-vat attribute. You need to include at least the country.

Attribute Description Required
country Customer’s country (2-letter ISO code). Yes
postal-code Customer's postal code No Highly recommended
vat-number Billing VAT number No

Advanced usage

Use a different form selector

Use VATCalculator.init('#my-selector') to initialize the live calculation on a different form.

Use a custom formatter function to modify calculation result HTML

Use VATCalculator.setCurrencyFormatter to use a different method to format the calculated values for the HTML output. This function will receive the calculation result as a parameter.

Example:

VATCalculator.setCurrencyFormatter(function(value){
    return value.toFixed(2) + ' €';
});

Trigger calculation manually

Call VATCalculator.calculate() to trigger the calculation manually. For example when you change the data-amount attribute on your form.

Preconfigured routes

In order for VatCalculator JS to work properly, these routes will be added to your application. If you don't want to use the Javascript library, you can of course disable the routes in the configuration file.

Method Route Usage
GET vatcalculator/tax-rate-for-location/{country}/{postal-code} Returns the VAT / tax rate for the given country (2-letter ISO code).
GET vatcalculator/country-code Returns the 2-letter ISO code based from the IP address.
GET vatcalculator/validate-vat-id/{vat_id} Validates the given VAT ID
GET vatcalculator/calculate Calculates the gross price based on the parameters: netPrice, country and vat_number

Configuration

By default, the VatCalculator has all EU VAT rules predefined, so that it can easily be updated, if it changes for a specific country.

If you need to define other VAT rates, you can do so by publishing the configuration and add more rules.

The configuration file also determines whether you want to use the VatCalculator JS routes or not.

Important: Be sure to set your business country code in the configuration file, to get correct VAT calculation when selling to business customers in your own country.

To publish the configuration files, run the vendor:publish command

php artisan vendor:publish --provider="Mpociot\VatCalculator\VatCalculatorServiceProvider"

This will create a vat_calculator.php in your config directory.

Changelog

Check out the CHANGELOG in this repository for all the recent changes.

Maintainers

VatCalculator is maintained by Dries Vints. Originally created by Marcel Pociot.

License

VatCalculator is open-sourced software licensed under the MIT license.

Comments
  • Incorrect handling of Norway?

    Incorrect handling of Norway?

    Hi,

    Thanks for your great vat-calculator, we use it as part of spark.laravel.com. And now run into an issue with a customer from Norway (outside EU) and we are in The Netherlands (within EU). The customer is being charged VAT but says that he shouldn't. And as far as I can see he's correct, regardless if he's a business or individual. Does this need to be changed in the code?

    Thanks in advance!

    bug 
    opened by sxdj 15
  • 504 Gateway Time-out: What can I do against this?

    504 Gateway Time-out: What can I do against this?

    How do you handle a request that leads to a 504 Gateway Time-out? Probably because VIES is down.

    This issue thread speaks about a timeout, but does it even work? https://github.com/driesvints/vat-calculator/issues/36

    Because when the user starts a request, the website just loads for 30 seconds and throws me a 504 Gateway Time-out error. Not so satisfying for a user.

    opened by pmochine 8
  • Every VAT-ID is invalid?

    Every VAT-ID is invalid?

    Hello,

    I am using Laravel Spark with Stripe. Now I mentioned that every VAT-ID seems to be invalid? That's too bad, I even used my own address.

    At least, I am getting the error:

    The provided VAT number is invalid

    bug 
    opened by ahoiroman 8
  • Added functionality for high and low tax types

    Added functionality for high and low tax types

    In the Netherlands we have more than one tax rate.

    High: 21% and Low: 6%.

    https://www.belastingdienst.nl/wps/wcm/connect/bldcontentnl/belastingdienst/zakelijk/internationaal/btw_voor_buitenlandse_ondernemers/btw_berekenen/btw_tarieven/

    I've added this functionality to this repository to help Dutch users.

    opened by SebastiaanKloos 8
  • Setting own company's country?

    Setting own company's country?

    Hi Marcel,

    First, thank you for this very useful package.

    One feature that seems to be missing, is having the possibility to set your 'home' country. If I understand the EU VAT rules correctly, VAT should be charged to companies based in your own country. So, if my company is based in Belgium and I sell to another Belgian company, I have to charge VAT. Right?

    How should I handle this situation with this package? It seems VAT 0% is applied to all companies, regardless of the country. If it's not in there yet, I can see if I can make a pull request one of the coming days if you like.

    opened by webcraft 8
  • Support date-based VAT rates

    Support date-based VAT rates

    Support date-based VAT rates in calculate(), calculateNet(), getTaxRateForLocation()

    Pass a DateTime/DateTimeImmutable object and the lib will find a rate for that date, or for current date if not specified.

    From now on, new or changed VAT rates should be added using the since key which has the same structure as the "parent" key (rate, rates keys). No existing values should be changed unless they're wrong.

    Reverts #92, the default DE rate is back to 0.19 and the new rate (0.16) is valid only for the interval specified.

    Fix #94

    This is backported from my "standalone" and "modernized" fork https://github.com/spaze/vat-calculator/pull/5

    opened by spaze 7
  • Method [validateVatNumber] does not exist.

    Method [validateVatNumber] does not exist.

    Hello,

    I just installed the package, added the service provider and alias to the app.php config file.

    I would like to validate an input with the vat_number validation rule. For this I added 'required|vat_number' in the rules for that field.

    However with this I'm getting the following error:

    Method [validateVatNumber] does not exist.
    

    When checking the VatCalculatorServiceProvider class, the registerValidatorExtension method get's called, however the VatCalculatorValidatorExtension@validateVatNumber does not.

    I'm using laravel 5.1.32, with package version 1.6.3.

    Did I miss something ?

    Thank you,

    Edit:

    I've found that when I comment the following:

    private function registerMaxFolderSizeValidator()
        {
            Validator::resolver(function ($translator, $data, $rules, $messages, $attributes) {
                return new MaxFolderSizeValidator($translator, $data, $rules, $messages, $attributes);
            });
        }
    

    here: https://github.com/AsgardCms/Media/blob/master/Providers/MediaServiceProvider.php#L80

    The Vat Validation rules works. Somehow laravel doesn't like multiple resolvers ?

    opened by nWidart 7
  • Calculate net from gross amount?

    Calculate net from gross amount?

    We'd like to make sure that all of our customers are billed a fixed amount, regardless of their country. This means we'd need to take the gross subscription cost and calculate the net amount from it. Is there any way to do it or plans to support it? AFAIK, this should be a fairly standard practice in EU - you always display the price with VAT.

    Let's say I have a subscription plan that's priced at gross 15€/month, and my business is located in Germany. Since VAT rate in Germany is 19%, the net price would be 15€ - 2,39€ = 12,61€.

    1. Customer is from Germany - price is 15€ (including 19% or 2,39€ VAT)
    2. Customer is from Finland, does not have a VAT number - price is 15€ (including 24% or 2,9€ VAT)
    3. Customer is from outside of EU - price is 12,61€ (VAT is always 0% when exporting)
    4. Customer is from any EU country (except from Germany), and has a valid VAT number - price is 12,61€ (VAT is always 0% in this case)

    The above calculations are based on the input we've had from our accountant. They are basically the same as your library already does, with one exception - the base price is a gross price, not a net price. This does, admittedly, make the calculation a bit trickier, but then again is fair and easier to understand for customers.

    opened by ragulka 6
  • Timeout option

    Timeout option

    Hello,

    Yesterday the VIES service was down and I had my website blocked on loading when I was using the class VatCalculator. It appear that the SoapClient was in kind of timeout as the VIES service was down.

    Any luck to add a timeout param?

    enhancement 
    opened by deStrO 6
  • Question regarding MOSS thresholds

    Question regarding MOSS thresholds

    I make reference to https://ec.europa.eu/taxation_customs/business/vat/modernising-vat-cross-border-ecommerce_en#heading_2, specifically the first threshold which states that:

    [business having an annual turnover of] up to EUR 10 000 TBE supplies remain subject to the VAT rules of the Member State of the supplier.

    Is this accounted for anywhere in the calculator? As I found no way to specify what my turnover is, or if this threshold should apply. Alternatively, is there a way to identify whether a country is part of the MOSS system or not? Eg. UK, while still having 20% VAT, is no longer part of MOSS and therefore I cannot apply this threshold to UK customers.

    Thanks!

    opened by caruanas 5
  • SoapClient in the __construct()?

    SoapClient in the __construct()?

    Is that really necessary to init SoapClient in the constructor? I mean, if I need vat calculator for other reasons (check vat rate) do i really have to load 'wsdl' all the time? What about to use setSoapClient() for that reason?

    opened by galexth 5
  • Cashier v13 support

    Cashier v13 support

    Cashier Stripe v13 has removed the taxPercentage because it bumps to the latest Stripe API version which has removed all support of tax_percentage in favor of its new Tax Rate API.

    Migrating to the new API will be quite an undertaking and some design decisions would need to be made. Will we auto-create/retrieve Tax Rates?

    enhancement 
    opened by driesvints 0
  • Include requestor VAT number

    Include requestor VAT number

    Hi,

    I would like to be able to pass along our own VAT number to the VIES validation service. When doing so, the VIES validation service returns a reference of the check. This reference can be kept by us to 'proof' we validated the VAT number with VIES, in case we ever have an issue with the tax officers.

    It would be nice that we can optionally send the $requestorVAT and get that reference as return.

    THank you for considering this.

    enhancement 
    opened by denjaland 1
Releases(3.2.0)
Owner
Dries Vints
I work for @laravel, maintain @laravelio and organise @fullstackbelgium & @fullstackeurope. Created @blade-ui-kit.
Dries Vints
laravel-vat is a package that contains the Laravel related wiring code for ibericode/vat

laravel-vat is a package that contains the Laravel related wiring code for ibericode/vat, helping you deal with VAT legislation for businesses based in the EU.

Danny van Kooten 117 Dec 5, 2022
My aim is to make a complete website that should have all the essential parts a website should have.

Gaming-Ninja I aim to make a complete website that should have all the essential parts a website should have. https://gamingninja-3399.000webhostapp.c

Anand Jaiswar 3 Nov 23, 2022
A small PHP library for validating VAT identification numbers (VATINs).

VATIN A small PHP library for validating VAT identification numbers (VATINs). Installation This library is available on Packagist: $ composer require

David de Boer 128 Oct 27, 2022
Validate your input data in a simple way, an easy way and right way. no framework required. For simple or large. project.

wepesi_validation this module will help to do your own input validation from http request POST or GET. INTEGRATION The integration is the simple thing

Boss 4 Dec 17, 2022
Easy way to upload Laravel model related files from the request.

Easy way to upload laravel model related file from the requset.

Emon Khan 5 Dec 15, 2022
Laravel and Lumen Auto Hard Deleter

Laravel Auto Hard Deleter This package deletes soft deleted rows automatically after a time interval that you define. For Laravel and Lumen 6, 7, 8 In

Siavash Bamshadnia 38 Dec 28, 2022
A laravel package to handle model specific additional meta fields in an elegant way.

Laravel Meta Fields A php package for laravel framework to handle model meta data in a elegant way. Installation Require the package using composer: c

Touhidur Rahman 26 Apr 5, 2022
Cache-purge-helper - Additional instances where nginx-helper and lscache plugin should be purged.

cache-purge-helper Additional instances where nginx-helper and lscache plugin should be purged. Install Extract the zip file. Upload them to /wp-conte

Jordan 10 Oct 5, 2022
This package should help you with creating and managing a Laravel DDD Application

This package should help you with creating and managing a Laravel DDD Application. This package is heavily inspired by "Laravel beyond CRUD" from Spatie.

J. Regner 158 Dec 25, 2022
This template should help get you started developing with laravel 9 + Vue 3 in Vite + Tailwind

simple-project This template should help get you started developing with laravel 9 + Vue 3 in Vite + Tailwind

Yemeni Open Source 4 Oct 5, 2022
Geo-related tools PHP 7.3+ library built atop Geocoder and React libraries

Geotools Geotools is a PHP geo-related library, built atop Geocoder and React libraries. Features Batch geocode & reverse geocoding request(s) in seri

The League of Extraordinary Packages 1.3k Dec 27, 2022
Advance features / Topics related to Laravel

Laravel Advance Topics Notes Includes : v1.0.0 - Service Container v1.1.0 - View Composer v1.2.0 - Polymorphic Relationships v1.3.0 - Custom Facade Im

Aadhar Gaur 1 Jan 16, 2022
A multitool library offering access to recommended security related libraries, standardised implementations of security defences, and secure implementations of commonly performed tasks.

SecurityMultiTool A multitool library offering access to recommended security related libraries, standardised implementations of security defences, an

Pádraic Brady 131 Oct 30, 2022
Keyword Generator Tool helps you discover keyword opportunities related to your query input.

This plugin simply helps you discover keyword opportunities related to your query input. Installation Download the zip file of the repository or clone

WP Refers 1 May 3, 2022
This package aims to help you standardize all your API responses in a simple and structured way.

Laravel API Response This package aims to help you standardize all your API responses in a simple and structured way. By default, the stucture of the

Kode Pandai 6 Dec 6, 2022
A laravel package to handle sanitize process of model data to create/update model records.

Laravel Model UUID A simple package to sanitize model data to create/update table records. Installation Require the package using composer: composer r

null 66 Sep 19, 2022
A simple laravel package to handle multiple key based model route binding

Laravel Model UUID A simple package to handle the multiple key/column based route model binding for laravel package Installation Require the package u

null 13 Mar 2, 2022
A laravel package to handle cascade delete and restore on model relations.

Laravel Model Soft Cascade A laravel package to handle cascade delete and restore on model relations. This package not only handle the cascade delete

Touhidur Rahman 18 Apr 29, 2022
Laravel 4.* and 5.* service providers to handle PHP errors, dump variables, execute PHP code remotely in Google Chrome

Laravel 4.* service provider for PHP Console See https://github.com/barbushin/php-console-laravel/releases/tag/1.2.1 Use "php-console/laravel-service-

Sergey 73 Jun 1, 2022