VAT Number and EU VIES Validation for WooCommerce, by Naked Cat Plugins, adds a VAT number field to your WooCommerce checkout, validates it against the European Commission’s VIES service, and removes VAT from qualifying B2B orders inside the EU. This page covers how it decides, what it stores, and how to configure it.
Table of Contents
Support
How to request technical support?
If the issue you need technical support with is not covered on this page, click the link next to the license key, on the plugin settings, and fill out the ticket with your request.
Where do I report security vulnerabilities found in this plugin?
You can report any security bugs found in the source code of this plugin through the Patchstack Vulnerability Disclosure Program.
The Patchstack team will assist you with verification, CVE assignment and take care of notifying us.
Getting started
What does this plugin actually do?
Three things, in order:
- Collects a VAT number at the checkout, on both the classic and the Blocks checkout, and in My Account.
- Validates it: the format for its country, then the check digit, then the European Commission’s VIES service.
- Decides whether the order qualifies for the intra-Community B2B exemption, and if it does, removes the VAT.
It records why, on every order, so you can reconstruct the decision later.
What does it not do?
It decides who is exempt. It does not decide what rate to charge. When it exempts a customer, WooCommerce charges them no tax. When it does not, your normal WooCommerce tax rates apply, whatever those are. Entering and maintaining those rates is your responsibility: use WooCommerce Tax, or enter them by hand under WooCommerce > Settings > Tax.
It never removes VAT for a customer outside the EU. If a non-EU customer is charged nothing, that is because you have set a zero rate for their country, not because of this plugin.
Where are the settings?
WooCommerce > Settings > Tax > VAT Number and VIES.
Getting started guide
The settings screen is WooCommerce > Settings > Tax > VAT Number and VIES, and this walks down it in order.
Before you start: your tax rates
This plugin decides who is exempt. It does not create tax rates. So two things need to be right before any of it means anything:
- Your shop’s country, under WooCommerce > Settings > General. That is the country the exemption is judged against, so a wrong one makes every decision wrong.
- Taxes enabled, and rates entered under WooCommerce > Settings > Tax, for the countries you sell to.
Note: if a customer does not qualify for the exemption, those rates are what they pay. If you have no rates entered, they pay nothing, and that is not this plugin’s doing.
Licence
Install the plugin and insert the provided license code. The rest of the settings appear disabled until the license is active.
Your responsibility
Straight after the licence there is one checkbox to accept, and until you do the plugin does nothing at all: no field on the checkout, no validation, and no VAT removed. The rest of the settings do not appear either.
It is there because of what this plugin does. It applies rules and keeps evidence, it does not give legal, tax or accounting advice, it cannot know your circumstances, and it can be wrong. What your shop charges, what it exempts and what you declare stay yours to answer for, with your accountant.
The moment you accept is recorded, with the version whose wording you saw.
The readiness check
Once the licence is active, the top of the screen lists nine checks. Green means there is nothing to do, amber means it works but probably not the way you intended, and red means no order can be exempted until you fix it.
- WooCommerce version. Green on WooCommerce 9.0 or newer. Update WooCommerce.
- Taxes are enabled. Green when tax calculation is on, under WooCommerce > Settings > General. With it off there is no VAT on the order to remove.
- Tax follows the address that decides. Green when WooCommerce charges tax from the same address this plugin judges on, the shipping one. Set Calculate tax based on to the customer shipping address under WooCommerce > Settings > Tax. Amber if you sell only virtual products, where the two are the same address anyway.
- The shop is in the EU VAT area. Green when your shop country is an EU member state, under WooCommerce > Settings > General, and your shop address is not in a part of one that sits outside the EU VAT area. There is no intra-EU exemption to apply from outside the EU, and none to apply from any of the territories listed further down either. If your shop is in one of those, this stays red and no order is ever exempted, which is correct: see the question about it below.
- Territories can still be told apart. Green when your checkout asks for the postcode, and the state in the countries that need it, which is what separates the Canary Islands from mainland Spain. Amber when it has stopped asking for them or stopped requiring them, because the territory rules cannot work without them.
- VIES validation is on. Green when VIES validation below is not set to “Do not validate”. With it off nothing is ever confirmed, and a confirmation is required before VAT comes off.
- Your own VAT number is confirmed. Green when your shop VAT country and number are filled in below and VIES confirmed them. Save them again to re-check.
- The field reaches the checkout. Always green on a classic checkout. On a Blocks checkout, green when the VAT number block is on the page set as your checkout under WooCommerce > Settings > Advanced. Add it in the block editor if it is missing, or nobody is ever asked for a number.
- No other VAT plugin is active. Green when this is the only one running. Deactivate the others: each asks the customer separately and the last one to run decides.
Your own VAT number
Fill in your shop’s VAT country and VAT number.
Do this even though the plugin works without it. It is what makes the European Commission return a consultation number with each check, the timestamped proof that you checked, which is the difference between validating a number and being able to show later that you did.
The number is checked against VIES the moment you save it and the result appears underneath, so a typo tells you straight away. A number VIES cannot read is refused rather than saved, because it would break every check your shop makes.
The VAT number field
Choose whether the field is asked for Optionally, Always, Always, and registered in VIES, or Never, and which customers see it. Optionally, shown to Every country, suits most shops and is the default.
Note: “Always, and registered in VIES” is stricter than it sounds. It turns away real businesses that never enrolled for intra-EU trade, and every private customer using their own tax number. Only pick it if you genuinely sell to nobody else.
If you are switching from another VAT plugin, deactivate that plugin now. Do not run two. Your existing numbers are read automatically, so nothing is lost by deactivating it and nothing needs importing. See “Which plugins can it read numbers from?” below for the list, and “Should I run this alongside my old VAT plugin?” for why running both is a bad idea.
Validation
Leave check digit validation on: it catches most typos instantly, without waiting for a network round trip.
Leave VIES validation on as well. With it off the plugin never removes VAT at all, whatever conditions you set for removing VAT, because a VAT number confirmed in VIES is a substantive condition of the exemption and not a formality.
The setting for what happens if VIES is unreachable is best left on “accept the number but do not remove VAT”. Refusing orders during an outage at the European Commission turns their downtime into your lost sales.
VAT removal
Pick your conditions for removing VAT. Recommended is the sensible default and is deliberately stricter than the VAT Directive’s literal minimum, so a triangulation case is not silently zero-rated.
Read the note above it about the shipping address deciding, not the billing address, and check that matches how you actually ship.
Cache
The default of one day is fine. It affects checkout speed only: the number is always re-checked live when the order is placed.
Finally: place a test order
The step people skip, and the only one that proves the setup works.
Add something to the cart and check out with a VAT number from an EU country other than your own, shipped to that same country. A real number from a customer or a supplier invoice works best. You should see the VAT come off the total and a line saying so.
Then open that order in the admin. The VAT number panel shows what was checked, when, what VIES answered, whether VAT was removed and why. If that panel reads correctly, you are set up.
Note: the exemption does not apply if you have not submitted a correct recapitulative statement, the European Community Sales List, for the supply. WooCommerce > EC Sales List builds it for you from the orders you exempted, and filing it is still yours to do. See “Where do I find the European Community Sales List?” below.
Every quarter: the European Community Sales List
Setup is done. The recurring part is the recapitulative statement, the European Community Sales List, at WooCommerce > EC Sales List. It opens on last quarter, the one you are usually filing.
The first table is what you file, one line per customer VAT number. The second is the proof behind it, including the consultation number VIES issued for each check. Both export to CSV. See “Where do I find the European Community Sales List?” below for what is counted and why an order might be missing.
The VAT number field
Where does the field appear?
On the classic checkout it is added to the billing section. On the Blocks checkout it is a block, added to your checkout page automatically the first time the plugin runs, positioned after the billing address. You can move it in the block editor like any other block.
If you remove it, it stays removed. The plugin places it once and never puts it back.
Why can I not find the VAT number block in the inserter?
Two things are going on, and the second one catches everybody.
Search for VAT, VIES, IVA, NIF or contribuinte. The block is called VAT number, but its name is translated, so on a Portuguese or German admin the English word matches nothing. It now carries the acronyms as search keywords, untranslated, so any of them finds it whatever language your admin is in.
Click inside the checkout form first. This is the part that makes people give up. The block can only live inside the Checkout Fields area, which is how WooCommerce knows it belongs there, and WordPress only offers a block in the inserter where it is allowed to go. So if you click the plus at the top of the page and search, you will find nothing, whatever you type. Put the cursor inside the checkout form, in the fields column, and search again.
If you would rather not hunt for it at all, the readiness check will place it for you. See the next question.
The block is missing from my blocks checkout. How do I get it back?
The readiness check on the settings screen notices, and offers to do it: WooCommerce > Settings > Tax > VAT number, look for “The field reaches the checkout”. If your checkout is a blocks checkout and the block is not on it, that check turns red and gives you an Add it to the checkout button. One click and it goes back, after the address fields, and you can move it afterwards.
Two ways to end up here, and both are ordinary. You installed the plugin while running the classic checkout and switched to blocks later. Or you removed the block and changed your mind.
The plugin only places the block once, on its own, the first time it runs with an active licence. It never puts it back by itself after that, not even when you deactivate and reactivate, because that is exactly when somebody who removed it deliberately would find it back without asking. Offered, never taken.
Can I delete the block from the blocks checkout?
Not by accident, which is the point. The block is locked against deletion and free to move, the same way WooCommerce’s own EU VAT block behaves.
Deleting it is the one action in that editor whose consequence you cannot see from the editor: the field disappears from the checkout, so numbers stop being collected, VAT stops being removed, and nothing anywhere tells you why. Dragging it somewhere else in the form carries no such risk, so moving is left alone entirely.
If you genuinely want it gone, you have two ways. Unlock it from the block’s own options menu, the three dots, and then delete it. Or better, set Ask for a VAT number to Never in the settings, which removes the field from both checkouts at once and stops the validation with it. The setting is the supported way; deleting the block only affects the blocks checkout and leaves the classic one as it was.
Can I give the field a heading and a line of explanation?
Yes. Select the block in the checkout editor and type over the heading, and add the optional line of text underneath it if you want one. Both behave like the headings WooCommerce’s own checkout sections have, and they sit with the same spacing.
The heading starts out following the field label you set in the settings, including its translations, and keeps following it for as long as you leave it alone. Type your own and the block keeps yours from then on. The line of text underneath is empty unless you write something.
Can I make the field required?
Yes, under “Ask for a VAT number”:
- Optionally suits most shops, since it serves business and consumer customers alike.
- Always suits a shop that sells only to businesses. An order cannot be placed without a number.
- Always, and registered in VIES is stricter than it sounds. VIES lists only those registered for intra-EU trade, so a real company with a perfectly valid national VAT number is turned away if it never enrolled, and so is any private customer using their own tax number. Sole traders and smaller businesses often have not enrolled either. Choose it only if you genuinely sell to nobody else.
- Never hides the field and stops all validation and VAT removal.
Do customers have to type the country prefix?
No. The field accepts a number with or without its prefix. Without one, the number is read against the customer’s billing country.
By default the prefix is added when the number is stored, so 123456789 entered by a Portuguese customer is stored as PT123456789. You can turn that off, though we recommend against it.
The prefix is what makes a stored number self-describing. With it, the number says which country issued it wherever it ends up. Without it, 123456789 is just nine digits, and anything reading it later, an invoice, an accounting export, another plugin, has to guess the country from the billing address. That guess is wrong for any customer whose VAT number was issued somewhere other than where they are billed. This plugin itself is unaffected either way, because it records the country in a field of its own, so the cost falls entirely on whatever you send the number to next.
Which customers see the field?
Set by “Show the field to customers billing in”: every country, EU plus the UK and Switzerland, or EU countries only. Every country is the default, and showing the field costs nothing, since nothing outside the EU is ever exempted whatever is typed in it.
Be careful with EU countries only: it hides the field from Northern Ireland. A Northern Irish customer’s country is the United Kingdom as far as WooCommerce is concerned, so that option also hides everything this plugin does for XI numbers.
Can I require a company name with it?
Yes. When switched on, a customer who enters a VAT number must also fill in the billing company field. It applies to both checkouts.
Validation
What is checked, and in what order?
Cheapest first, and nothing further is tried once something fails:
- The field is not empty.
- Your licence is active.
- The country is one we recognise.
- The number matches that country’s format.
- The check digit is correct.
- VIES, if it is switched on and your shop is in the EU.
VIES is never called speculatively. A number that fails any earlier step never reaches it.
This plugin was built for performance, and this is where most of it comes from. VIES is a public service on the other side of the internet, and a call to it is slower than everything else the checkout does put together. The format and check-digit tests are pure arithmetic and cost nothing measurable, so the great majority of wrong numbers are caught and answered instantly, without your customer waiting on a network round trip. Being lighter on a free public service is a welcome side effect, not the reason.
Which countries get check-digit validation?
The check digit is verified for every country this plugin recognises: all 27 EU member states, plus Northern Ireland, the United Kingdom, Switzerland and Norway.
Each rule was verified before it was enforced. Most are taken from a specification published by a tax authority, and the rest were measured against real numbers at scale, taken from the issuing country’s own register: 4.7 million of them for France, 453,682 for Latvia, 40,316 for Czechia. A branch we cannot check honestly stays out rather than being guessed at, which is what the next paragraph is about.
Three branches inside otherwise verified countries are left on shape alone, on purpose, and all three are numbers belonging to individuals rather than companies. Bulgarian ten digit numbers: since January 2024 the revenue agency issues sole traders a service number whose format it has never published, so any rule would be a guess. Latvian personal codes issued since July 2017: they begin 32 and carry no date of birth, so the date check older rules apply would reject real people. Czech nine and ten digit personal numbers, for the same reason. In every case a number we cannot check honestly is sent to VIES rather than refused.
A British number given for a Northern Ireland delivery gets a nudge, not a rejection. A Northern Irish trader’s XI registration is the same number they had as a GB registration, so typing the old prefix is the natural mistake. When the number starts GB, the delivery address is in Northern Ireland and the order carries nothing but goods, the checkout says so and suggests the XI prefix. It never converts the number itself: the XI prefix is your customer’s own statement that they are registered in Northern Ireland for this trade, most British registrations are not, and treating an ordinary export as an intra-Community supply would put it on your recapitulative statement wrongly.
We only implement an algorithm where it is documented and verifiable. Guessing would reject real customers.
What does “not registered in VIES” mean?
It means that business is not listed in the EU system for checking VAT numbers used for trade between EU countries. It does not mean the number is invalid. VIES lists only those registered for intra-EU trade, so a perfectly valid national VAT number is simply absent from it if its holder never enrolled. That includes private individuals, who in several countries have a VAT or tax number of their own and are never listed.
The practical consequence is that VAT is not removed. The order goes through as normal.
What happens if VIES is unavailable?
VIES goes down. It is a free public service and outages are ordinary.
- If we hold a recent stored result for that number, it is used, and the order records that it came from a stored answer rather than a live one.
- If we do not, the setting “If VIES is unreachable” decides: accept the number but do not remove VAT (the default), or refuse the order. Refusing is marked not recommended, because an outage at the European Commission becomes lost sales for you.
An outage is never recorded as an invalid number. Those are different facts and the plugin keeps them apart everywhere.
Why should I enter my own VAT number?
Because it is the difference between checking a number and being able to prove you checked it.
VIES returns a consultation number only when the query identifies the shop making it. That consultation number is the recognised proof that the check took place, at that moment, for that number. Without your own VAT number in the settings, validation still works and VAT is still removed, but no proof is produced.
Your number is sent on exactly one check per order, the one recorded against it. The checks that happen while a customer is still editing the checkout deliberately go without it: they return the same answer, and asking for a formal consultation number every time somebody pauses typing would mint legal records nobody will ever read.
The plugin checks your number against VIES when you save it, and tells you what it found.
It refuses to save a number VIES cannot read, one with the wrong shape or a failed check digit, and keeps whatever you had before. That is deliberate: VIES rejects the whole query when it cannot parse who is asking, so a typo here would quietly stop validation working for every customer on your site, not just for one.
A number that is simply not listed in VIES is saved normally. It costs you nothing: your checks still run and still receive a consultation number.
Removing VAT
When is VAT removed?
Every level requires all of this: your shop is in the EU, the goods go to a different EU country, the VAT number was confirmed by VIES, and the number was issued by a country other than your own.
That last condition is the law, not a preference. A customer registered in your own country is a domestic sale even when the goods travel abroad.
On top of that, the “Conditions for removing VAT” setting adds:
- Directive minimum: nothing further. This is what the VAT Directive literally requires. It allows a customer registered in one EU country to buy from you and have the order shipped to a different one, which is legal on the face of the text but is a triangulation case you may not want handled automatically.
- Recommended: the VAT number must also have been issued by the country you are shipping to. Stricter than the Directive, and it blocks the triangulation case above rather than silently zero-rating it.
- Strictest: the billing address country must match the VAT number as well.
What about the Canary Islands, Åland, Livigno and other territories outside the EU VAT area?
Some parts of EU member states sit outside the EU VAT area. They are in the country, and the country is in the EU, but a sale going there is an export, not an intra-Community supply, whatever VAT number the customer holds and however cleanly VIES confirms it.
So the plugin does not exempt those sales, records why on the order, and keeps them off your European Community Sales List, where an export does not belong.
It does not zero rate them either. Zero rating an export depends on evidence that the goods left the customs territory, which this plugin cannot see, and the rate is your tax table’s business. If you sell to any of these territories, set a 0% rate for them under WooCommerce > Settings > Tax. Until you do, those orders are charged your ordinary domestic rate.
Which territories are recognised
| Territory | Country | Province | Postcodes |
|---|---|---|---|
| Canary Islands | Spain | Las Palmas, Santa Cruz de Tenerife | 35, 38 |
| Ceuta | Spain | Ceuta | 51 |
| Melilla | Spain | Melilla | 52 |
| Åland Islands | Finland | none exists | 22000 to 22999 |
| Heligoland | Germany | none exists | 27498 |
| Büsingen am Hochrhein | Germany | none exists | 78266 |
| Livigno | Italy | too coarse to use | 23041 |
| Campione d’Italia | Italy | too coarse to use | 22061 |
| Mount Athos | Greece | none exists | 63086 |
For the Spanish territories, either signal on its own is enough and the two are not required to agree. An address can arrive through an import or an API with only one of them filled in, a shop can hide the province field, and a customer can pick the wrong province from a list. When they disagree the likeliest explanation is incomplete data, and treating an export as an intra-Community supply is the more expensive of the two mistakes.
Finland and Germany have no province field for WooCommerce to fill in, and Italy’s is the province, which is far larger than the two comuni concerned, so for Åland, Heligoland, Büsingen, Livigno and Campione the postcode is the only signal. That is a real limitation rather than an oversight: if your checkout does not collect a postcode, or a customer leaves it out, those addresses are not recognised and are treated as ordinary mainland Finland or Germany. Nothing can be inferred from the country alone without misclassifying every mainland order along with it. If you sell to those territories, keep the postcode field required.
For Livigno and Campione d’Italia the postcodes above are the ones in use since 18 June 2018. Both towns had different codes before that, and those old codes now belong to ordinary, fully taxed Italian addresses, so the plugin deliberately does not recognise them. Recognising them would zero rate somebody else’s normal order.
A few setups offer Åland as a country of its own, AX, rather than Finland plus a postcode. That works too, by a different route: AX is not an EU VAT country at all, so the sale is refused as a destination outside the EU rather than as a territory. The outcome and the paperwork are the same.
What does not change
Nothing else about the field. It is still shown, still required if you made it required, and the number is still checked against VIES, because a customer there holds an ordinary VAT number of their country and you may still want it on the invoice and on your export paperwork. Only the exemption is refused.
The order’s VAT number panel says so in words and names the territory, so an order placed today can still be explained years from now.
The French overseas departments (Guadeloupe, Martinique, Réunion, Mayotte, French Guiana) need no rule of their own: WooCommerce gives each of them its own country, and none of those is an EU VAT country, so a sale there is already refused as a destination outside the EU. The same is true of the other overseas collectivities.
Monaco goes the other way and is worth knowing about: it is not an EU member state, but VAT law treats trade with it as trade with France, and its businesses hold French VAT numbers. So a sale to Monaco is an ordinary intra-Community supply and the plugin exempts it like any other French one.
The Italian waters of Lake Lugano are excluded too and have no postcode of their own, so no address-based rule can find them. They are tracked, and each one is a small addition rather than a change to how the decision is made.
Where do I find all of this in one place?
WooCommerce > Status, in the section named after this plugin. It lists every readiness check with its result, and a line saying which address fields the territory rules can count on in each country, in the form ES state=ok postcode=ok.
That is the section to copy into a support message. It answers most of what we would otherwise have to ask you.
What if my checkout does not ask for the postcode or the province?
Then these territories cannot be recognised, and the readiness check on the settings screen says so.
The table above shows which field identifies each one. Spain’s three can be recognised from either the province or the postcode, so losing one of the two is survivable there. The other six are identified by the postcode alone, so for those it is the whole answer.
If a plugin, a filter or a theme has made those fields optional or hidden, a sale to one of these places looks exactly like a sale to the mainland, and it will be treated as an intra-Community supply: VAT removed, and the sale on your European Community Sales List. That is not something this plugin can work around, and neither could any other. The fix is to require the field.
My shop is in one of those territories. Why is VAT never removed from any order?
Because it cannot be, on any order, ever.
A shop established in any of the territories listed above is itself outside the EU VAT area. It does not make intra-Community supplies at all, so there is no order it could apply the exemption to: not to a VAT registered business in another member state, not with a VIES confirmed number, not on any settings combination. There is no setting that changes this, because there is no situation in which the exemption applies.
This is the plugin working, not failing. Everything except the exemption carries on:
- The VAT number field appears at your checkout as normal.
- The check digit validation runs.
- The number is confirmed against VIES.
- It is stored on the order and on the customer’s account, for your invoicing and your export paperwork.
- Your European Community Sales List stays empty, which is correct: it lists intra-Community supplies, and you make none.
In effect the plugin becomes a VAT number field with VIES validation, and decides nothing about tax. For a shop in one of these territories, that is the right answer rather than a reduced one.
The readiness check on the settings screen says so and names the territory, so you can see at a glance that it is deliberate. It is shown in red, because the shop genuinely cannot do the thing the exemption exists to do, and every order records the same reason in words in its VAT number panel.
What about Northern Ireland?
We remove VAT from goods you sell to Northern Ireland. We do not remove it from anything else.
Northern Ireland is a special case. Since the Windsor Framework it has stayed inside the EU VAT rules for goods, while the rest of the United Kingdom has not. Businesses there have VAT numbers that start with XI.
We remove the VAT only when all three of these are true:
- The customer’s VAT number starts with XI, and VIES confirms it.
- The delivery address is in Northern Ireland, which means a postcode starting with BT.
- The order contains physical products and nothing else.
If the first two match but there is a download, a subscription or anything else marked virtual in the basket, we charge VAT on the whole order and say so on the order itself. We would rather charge VAT you can claim back than remove VAT you turn out to owe.
Why we are this careful. Telling a product from a service is a judgement about your shop, and we cannot make it for you. Our rule is a simple one: anything you have marked as virtual is a service, everything else is a product. That is good enough to sort your European Community Sales List into its two columns, where both are shown either way. It is not good enough to decide on its own that you owe no VAT, so we only ever let it answer no.
If your shop sells services as ordinary products, or products as virtual ones, that rule will be wrong for you. It can be changed to match your catalogue: your developer can do it in a line, or ask us.
There is no button to remove VAT from one order by hand. If you need to, change the tax on the order in WooCommerce while it can still be edited, or fix how the products are set up so the order is goods only.
Does this handle the Italian split payment (scissione dei pagamenti)?
No, and it should not.
Split payment applies to supplies to Italian public administrations and certain listed entities. The customer pays the VAT straight to the tax authority instead of to you. It is not an exemption: the invoice still shows the VAT in full and at the normal rate, and nothing about the VAT number changes. Only the route the money takes changes.
So there is nothing here for a VAT number validation plugin to detect or decide. It belongs to your invoicing and accounting, not to the exemption rule. It is mentioned here only so you can be sure the omission is deliberate rather than a gap, since it comes up alongside the other Italian special cases above.
Which address decides, billing or shipping?
The shipping address. Some plugins let you choose between the two. This one does not, because the exemption depends on the goods physically leaving for another EU country, so where they are sent is what matters and where the invoice is addressed is not.
When an order has no separate shipping address, the billing address is the shipping address and is used as one. Orders with nothing to ship, such as downloads, virtual products or services, have no shipping address at all; for those the billing address is used, because it is the only address the order has.
A shop that sells only services or downloads, where nothing is ever dispatched, can make the billing address decide everywhere. See “Can I override how the exemption is decided?” below.
Why is VAT still charged on an order with a valid VAT number?
The most common reasons, in order:
- The order is not leaving the country it is being sold from. That is a domestic sale.
- The VAT number was issued by the same country the order is being sold from.
- The VAT number was not issued by the country the order is being sent to (on Recommended or above).
- The number is not listed in VIES.
- The order is going to a part of an EU country that is outside the EU VAT area, such as the Canary Islands.
The order records which of these applied. Open the order and look at the VAT number panel.
Can I remove VAT for businesses in my own country?
Not from the settings, no. A sale that starts and ends in your country is a domestic sale, and the exemption is for goods going to a business in another EU country. A customer being VAT registered does not change that.
Some plugins offer this as a checkbox and some switch it on by default. This one does not, because a setting that quietly contradicts the rule printed above it is worse than no setting.
If your accountant has told you it applies to your particular situation, it can be turned on in code. See “Can I override how the exemption is decided?” below. Once it is on, the settings screen says so, and every order it affects records that this is why VAT was removed.
Does the customer get told why no VAT was charged?
Yes, on every order this plugin exempted. A short note appears with the order totals, directly under the VAT number, on the order confirmation page, in the customer’s account and on the order emails. It reads:
The VAT number was confirmed in the EU VIES service, so no VAT was charged.
It is on by default. Turn it off with the Tell the customer why checkbox, or change the wording, under WooCommerce > Settings > Tax > VAT number, in the “VAT removal” section. Turning it off leaves the wording alone, so switching it back on does not mean typing the sentence again.
It appears only when this plugin actually removed the VAT, and only when the order really carries none. If someone edits the order and recalculates so that VAT is charged again, the note goes rather than sitting above a line of VAT contradicting it.
Before this, an exempted order simply showed a lower total with nothing to explain it, which is a poor thing to send a customer who is going to file it.
What has to appear on the invoice?
A mention of its own, and this plugin does not put it there. None of the pages above is an invoice: they carry no invoice number and none of the other particulars an invoice needs, so the note described above does not satisfy anything and is not meant to.
The rule is Article 226 of the VAT Directive (2006/112/EC), and it asks for two different things depending on the supply:
- For an exempt supply, which is what an intra-Community supply of goods is, point (11) wants a reference to the provision that exempts it. That can be the article of the Directive, the equivalent article of your own country’s law, or any other wording that shows the supply is exempt. In Portugal, for example, goods are invoiced as Isento artigo 14.º do RITI.
- Where the customer is liable for the VAT, which is the usual position for cross-border business to business services, point (11a) wants the literal words “Reverse charge”. Portugal codes that one separately as IVA – autoliquidação.
So the correct mention depends on whether you supply goods or services, and on your own country’s practice, which is why this plugin does not guess at one and ships no citation in the note. Your invoicing plugin issues the invoice and is where the mention belongs. Invoicing with InvoiceXpress already carries the Portuguese exemption reasons and picks them per document.
If your accountant would rather their exact wording also appeared on the order confirmation and emails, put it in the setting described above, replacing the default text.
Do I still have obligations after the plugin removes VAT?
Yes. The exemption does not apply if you have not submitted a correct recapitulative statement, the European Community Sales List, for the supply. The plugin builds that statement for you, under WooCommerce > EC Sales List, but submitting it is yours to do.
European Community Sales List
Where do I find the European Community Sales List?
The recapitulative statement, the European Community Sales List (shortened to EC Sales List in the admin menu, where the full name will not fit), is at WooCommerce > EC Sales List. It asks for WooCommerce’s “view reports” permission rather than the right to change settings, so a bookkeeper can be given the report without being given the plugin.
It is on by default and can be switched off under Recapitulative statement on the settings screen, which hides the report and the triangular question on the order screen. Turn it off if you produce the statement from your accounting software and do not want a second version of the same numbers in wp-admin.
What is in the report?
Two tables covering the same period.
What you file is the statement itself: one line per customer VAT number, goods and services stated separately, triangular supplies marked, and the total for the period.
The proof behind it is one line per order: the consultation number VIES issued, the moment the check really happened, the name the number is registered to, and the rule that allowed the exemption.
Can I rely on the report as it stands?
Treat it as a tool that helps you prepare the statement, not as the statement and not as a legal or accounting document. There are things it cannot know:
- A sale zero rated by something other than this plugin, or than one of the five previous VAT plugins it can read, does not appear, even though it may belong on your statement.
- A line reconstructed from a previous plugin’s order is judged against the conditions for removing VAT your shop uses now, and carries only the proof that plugin kept. See the question about your previous VAT plugin below.
- An order edited after it was placed changes these figures, including for a period you have already filed.
- Amounts taken in another currency are shown as they were taken and are never converted.
- The goods and services split is a heuristic, and whether a supply is triangular is your answer rather than ours.
Check it against your own records before filing, and file under your own responsibility.
How are triangular supplies marked?
By you, on the order. Open it and tick Triangular in the VAT number panel.
Only on orders that carry goods. Article 141 simplifies a chain of supplies of goods, where one consignment moves once and is sold twice. A service, a download or a licence has no consignment: it is located where your customer belongs and reverse charged by them, one supply at a time, so there is nothing to declare as triangular. On an order with nothing physical on it the question is not asked, and a tick saved on one is ignored by the statement. An order carrying both is still asked, because its goods can be a triangular supply.
The rest of it is not worked out for you because it cannot be. A triangular supply under Article 141 is one where your customer bought as an intermediary and had the goods sent straight on to their own customer in a third member state. Nothing in an order records that.
The nearest thing an order does show is that the VAT number and the delivery country differ, and that has innocent explanations, such as a company having its own goods delivered to its own warehouse abroad. So the report flags those orders with Check in the Triangular column and links to them, and leaves the answer to you.
Note that with the conditions for removing VAT on Recommended, an order whose VAT number country is not the destination is not exempted at all, so it never reaches this report. Those orders appear only on Directive minimum.
Which orders does it count?
The ones this plugin exempted, and the ones a previous VAT plugin exempted before this one was installed, in both cases only in the statuses processing and completed. An order that carries a VAT number but was charged VAT is not an intra-Community supply and does not belong in the statement, whichever plugin looked at it.
Developers can change the statuses with the nakedcat_vat_number_ecsl_statuses filter.
Why is an order missing from it?
In this order of likelihood: its status is not one the report counts, VAT was never removed from it, or its date falls outside the period. Open the order and read the VAT number panel: it says whether the order is on the statement, and if not, why.
If the order predates this plugin, see the question below about your previous plugin as well.
One case is worth watching for, and the report points it out above the tables: an order that carries no VAT and is still not on the statement. That happens when the VAT number was edited after the order was placed, since the decision is made again but the tax is deliberately not recalculated. The sale went out zero rated and may belong on your statement even though this report will not put it there.
Does it include orders from my previous VAT plugin?
Yes, and it has to, or your first statement would be short by however much of the period you traded before installing this plugin.
An order is included when VAT was genuinely not charged on it and something corroborates that: the previous plugin recorded that it validated the number, or WooCommerce itself recorded that something exempted that customer on that order. An order created by hand in wp-admin went through neither, so there the absence of VAT is all there is and is taken as the answer. Where WooCommerce recorded that nothing had exempted the customer, the order is left out whatever its total says.
The sale is then judged the same way one of ours is, on the VAT number’s country, the destination, and the territory rules, so a domestic sale or a delivery to the Canary Islands is left out exactly as it would be today.
An order this plugin judged and refused stays off the statement even if a previous plugin flagged it. That refusal is a decision on record, made under your current settings, and where such an order carries no VAT the report lists it above the tables so you can look at it yourself.
Five plugins are read: WooCommerce EU VAT Number, Aelia EU VAT Assistant, Simba EU VAT Compliance, WPFactory EU/UK VAT Validation Manager and Teamwant VIES VAT. InvoiceXpress and NIF (Num. de Contribuinte Português) are not on that list and will not be: neither ever contacts VIES, so neither knows anything about an intra-Community supply. Your customers’ numbers still migrate from both, which is a different job.
Two things to know about these lines, and the report says both on screen when it has any:
-
They carry only the proof that plugin kept, which varies a great deal. Aelia and Simba stored the consultation number, the name VIES returned and when it was asked, and all three appear. WPFactory kept the consultation number and the name but no date. WooCommerce EU VAT Number never obtains a consultation number at all, and Teamwant records nothing beyond the number itself, so those lines show none in those columns. That is not a fault in the reconstruction; there was never anything to record.
If your shop moved from one of these plugins to another before arriving here, the proof is still found. A migration between two VAT plugins moves the number and leaves the evidence where it was, so an order whose number now sits in a later plugin’s field is still credited to the plugin that actually checked it, with that plugin’s consultation number.
-
They are judged against the conditions for removing VAT your shop uses now, because nothing anywhere recorded the ones that applied at the time. If you have changed that setting since, a reconstructed line may differ from what the old plugin decided.
The Source column in the second table names the plugin for each line, and marks the ones worked out from the order itself rather than read from a record. It appears only when there are such lines, so a shop that has only ever used this plugin never sees it. Check these against your own records before you file.
How are refunds treated?
A refund is reported in the period it was granted, not the period of the original order, so a supply made last quarter and refunded this one is a correction on this quarter’s statement. It appears as a negative line and is split between goods and services in the same proportion as the order it came from.
Does it convert currencies?
No. If a period contains supplies in more than one currency the report says so and leaves the amounts alone. Nothing here records the exchange rate that applied on the day of each order, and using today’s rate would put a number on a statutory return that nobody could reproduce later.
Where does an order say whether it is on the statement?
In the VAT number panel on the order screen, under European Community Sales List. An order that is on it says so and offers the triangular question. An order that is not says why, in words: the VAT number is registered in one country and the goods went to another, VIES never confirmed the number, the sale never left your country, and so on.
What is the box asking me to look at things before I file?
The report checks itself before you submit it. When it finds anything, a bordered block appears above the tables headed Look at these before you file this period, listing the orders and linking to them. When it finds nothing, which is the usual case, nothing appears.
Two things go in it:
- Sales that went out with no VAT and are not on the statement. An intra-Community supply going unreported, which usually means a VAT number was edited after the order was placed without the tax being recalculated.
- Orders whose address changed after they were judged. See the next question.
It never changes anything and never stops you filing. It lists what does not add up and leaves the judgement to you, because both cases can be perfectly legitimate and only your own records can say which.
Does the report notice an address I changed later?
Yes, and this is the one place a paid order with a corrected address is pointed out.
If an order’s address is changed after it was placed, and on the new address the answer would be different, the report says so above the tables and links to the orders. It counts the two directions apart, because they are opposite problems:
- On the statement and would not qualify now. A line is being reported that should not be, for example a delivery corrected from Madrid to Las Palmas de Gran Canaria, which moves it outside the EU VAT area.
- Missing from the statement and would qualify now. A supply is going unreported, because the address it was judged on was wrong.
Nothing is changed, and nothing is decided for you. The report never rewrites a decision, which matters most for a period you have already filed: check the orders against your own records before you submit.
It only speaks when the answer really changes. A corrected street name, or a postcode within the same country, does not change whether the sale qualifies, so it stays silent. It compares the two decisions, not the two addresses.
Expect several the first time you look. The comparison is worked out when you open the report rather than stored, so address edits made before this existed appear now. That is a one-off view of something that was already true, not a new problem.
How do I know if a period changed after I filed it?
Exporting the summary records what you took away: the period, the moment, and the figures for each customer. Open that period again later and the report compares the two. It either says nothing has changed since, or names the customers whose lines have and warns that a period you have already filed may need correcting.
That matters because a period is never really closed. An order in it can be refunded, edited or marked triangular months later, and nothing else in WooCommerce would tell you that the statement you filed no longer matches your own records.
Exporting again replaces the record. The last twenty periods are kept, which is five years of quarters. Only the summary export is recorded, since that is the statement; the detail export is the evidence behind it.
Can I export it?
Yes, each table has its own CSV. Amounts are written with a dot as the decimal separator and no thousands separator, whatever your site language, because a decimal comma turns one column into two in many spreadsheets.
Does the plugin file the statement for me?
No, and it does not talk to any tax authority. It produces the figures and the evidence behind them. Submitting the statement is yours to do, in whatever form your country requires.
Orders
Where does the customer see their VAT number?
On the order confirmation page, in their account under the order, and on the order emails, listed with the order totals in the same place WooCommerce shows the payment method. It appears in both classic and block themes, because it is added to WooCommerce’s own order totals table rather than drawn separately.
It is deliberately not added to the billing address. Some plugins do that, and it means the number ends up wherever an address is used, including shipping labels. A VAT number is part of the order, not part of where somebody lives.
On an order this plugin exempted, a second row under it says why no VAT was charged. See “Does the customer get told why no VAT was charged?” above.
Developers can remove the number with the nakedcat_vat_number_show_in_order_totals filter, or change the row with nakedcat_vat_number_order_totals_row. Neither affects the note, which has nakedcat_vat_number_exemption_note and nakedcat_vat_number_exemption_note_row of its own. See the Developers section.
Can I find an order by VAT number?
Yes. Type or paste the number into the search box on the orders list and press enter, with the search set to its usual “All”. It works on both order storage systems.
Can I see and search VAT numbers on the customers list?
Yes. Users has a column showing each customer’s number, and searching that screen matches VAT numbers as well as names and emails, so pasting a number finds the customer it belongs to.
Customers who came from another VAT plugin appear there straight away, before they have ordered again and before you import anything. Both the column and the search read the other plugin’s key as well as ours.
The column is not sortable, on purpose: sorting a list of tax numbers puts every Portuguese customer together and answers no question anybody actually has.
Where do I see what happened?
Two places:
- The orders list has a VAT number column, showing the number and its state.
- The order screen has a VAT number panel with the full record: the number, the status, when it was checked, whether the answer came from a stored result, the VIES consultation number and which of your own VAT numbers obtained it, the trader name and registered address VIES returned, whether VAT was removed, which address decided, the customer’s IP address, and the country that IP resolved to.
What do the icons in the orders column mean?
- Confirmed: the business was registered in VIES when we checked.
- Not listed in VIES: checked, not found. Not an error. VAT was charged as normal.
- Not checked: no VIES check was made for this order.
- Recorded by another plugin: this order predates this plugin. It shows the number that other plugin stored, and nothing else, because this plugin did not check it and did not decide its tax.
Hover any of them and it also tells you whether VAT was removed, because a confirmed number and an exempted sale are two different things. A registered business can be perfectly confirmed and the sale still be taxable: goods going to the Canary Islands, a domestic sale, a number issued by a country other than the one you shipped to, an order to Northern Ireland carrying anything that is not goods.
Green means the VAT came off. Grey means it did not, whatever the reason. So a confirmed number on an order that was still taxed shows a grey tick rather than a green one: the tick because the number really was confirmed, grey because no VAT was removed. It is not an error and is not coloured like one. The order panel says which rule decided it.
What is “Country by IP” on the order?
The country the customer’s IP address resolved to at the moment the order was placed. It is there for your records, and it decides nothing.
It comes from whatever your shop is already set up with, and nothing is ever sent anywhere to work it out: a country header from your CDN if you use one (Cloudflare sets one, for example), or WooCommerce’s own MaxMind geolocation if you have entered a licence key under WooCommerce > Settings > Integration. With neither of those, the row simply does not appear rather than a lookup being sent to some third party with your customer’s IP address in it.
It is worked out once, when the order is placed, and never again. Geolocation databases are rewritten constantly, so asking a year later would answer a different question and pass the answer off as a fact about the past. Editing the number or re-checking it leaves the recorded country alone.
A country that differs from the billing address is completely ordinary. Company networks route through head office, people use VPNs, and customers travel. The panel says so rather than flagging it.
The reason it changes nothing is that for the B2B exemption an IP address is not evidence of anything. What the exemption rests on is the VAT number confirmed in VIES and the goods going to a business in another EU country, and the proof of that, in law, is transport documents, insurance and bank records. An IP address appears nowhere on that list. Where it does count is a different set of rules: business to consumer sales of digital services, which need two non-contradictory pieces of evidence of where the customer is. This plugin does not handle those, so it does not pretend the IP country is doing work it is not.
What is “Worth a look” on the order?
It appears only when three things fail to corroborate one another: the country that issued the VAT number, the billing country, and the country the customer’s connection resolved to. For example a Lithuanian VAT number, a German billing address and a connection from Brazil.
One difference is ordinary and says nothing. A VPN, a company network or somebody travelling produces one on perfectly good orders, which is exactly what the “Country by IP” row already tells you. All three disagreeing is a different shape, and it is worth a glance before you ship.
It changes nothing. The order is exempted or not on the VIES confirmation and where the goods went, and this line has no part in that decision. It never blocks an order, never appears on a customer’s copy, and only ever appears on orders that were actually exempted, because where VAT was charged there is nothing at risk.
Some plugins go further and refuse to exempt at all unless two of the three agree. This one deliberately does not. That rule comes from the digital services regime, which decides whose VAT rate a consumer pays; ours is the business exemption, where the evidence that counts is the VIES confirmation and proof the goods left. Gating on an IP address would turn away legitimate customers behind a VPN or on a business trip.
Can I edit a customer’s VAT number in WP Admin?
Yes. It is on the customer’s own profile screen, under Billing, alongside the address WooCommerce keeps there. Save it the way you would any other field on that page.
What you type is tidied up the same way the checkout tidies it, so lt 100005065913 is stored as LT100005065913 and the country is recorded with it. Clearing the field removes both.
Editing a customer’s profile never changes an existing order. The customer’s current number and the number an order was placed under are different facts, and an order has to keep the one it was placed under.
Can I edit the VAT number on an order?
Yes, in the billing address panel, like any other billing field.
Saving checks the number and records the answer, but never refuses it. You may be transcribing a customer’s paperwork, repairing a bad import, or entering a number for a country we cannot check.
It does not change the tax already on the order. WooCommerce recalculates tax when you ask it to, and not otherwise. Use WooCommerce’s own Recalculate button if you want the new number applied to the totals.
I corrected an order’s address. Does the VAT decision follow?
Not on its own, and that is deliberate. The plugin notices, tells you what changed, and leaves the decision to you.
Changing an address can change whether an order qualifies. Correcting a delivery address from Madrid to Las Palmas de Gran Canaria moves it from mainland Spain to the Canary Islands, which are outside the EU VAT area, so a sale that was an exempt intra-Community supply becomes an export. It happens in the other direction too: an order that was refused can start to qualify once the address is right.
When it speaks
Two conditions, both of which have to be true.
- The answer has to actually change. Correcting a street name, a postcode within the same country, or a typo in the city does not change whether the sale qualifies, so nothing is said. It compares the two decisions, not the two addresses.
- The order must not be paid yet. See the next question for why.
What you see
At the top of the VAT number panel, before the record it is about to contradict:
The address on this order has changed since the VAT was decided. This order was exempted, and on the address it has now it would not be.
The goods went to the Canary Islands, which is outside the EU VAT area, so this is an export rather than an intra-Community supply.
Nothing has been paid yet. Re-deciding corrects the record and this plugin’s exemption flag, and leaves the total alone. To charge the customer the new amount, use WooCommerce’s own Recalculate button in the order items panel above.
[Re-decide on the current address]
Until you press that button, nothing has changed. The order keeps the decision it was given, the total is untouched, and the orders list still shows what it showed before.
What the plugin does when you press it
Exactly four things, and no more:
- Records the decision the current address produces, replacing the one made against the old address.
- Updates the VAT exemption flag it sets on the order, which is the flag WooCommerce reads when you recalculate and that an invoicing plugin reads when it issues the invoice.
- Adds an order note saying which way it went, why, and that the tax was not changed.
- Tells you at the top of the screen, and points you at Recalculate.
What it deliberately does not do
- It does not change the money. Not the total, not the tax, not a refund, not a charge. That is the same rule as editing a VAT number by hand: this plugin decides who is exempt, it does not decide what your customer is charged.
- It does not ask VIES again. The number has not changed, and the check that counts is the one made when the order was placed, because that is the one contemporaneous with the sale. Asking VIES today would answer a different question, about today. If VIES could not be reached when the order was placed, the panel says so instead and sends you to “Check again now” first, because then it is the number standing in the way rather than the address.
What you should do
- Read the reason. It names the rule that changed the answer, so you can tell a genuine correction from a mistyped address you should simply fix back.
- Press Re-decide if the new address is right. If you corrected the address by accident, put it back instead and the warning goes away on its own.
- Then press WooCommerce’s Recalculate, in the order items panel, if the customer should now be charged differently. This is yours to do, not the plugin’s: recalculating re-derives every tax on the order from today’s rates, which is a bigger change than the one you just made, and it should happen because you decided it should.
- Check the invoice if one has already been issued for this order. The plugin corrects its own record; it cannot correct a document that has already gone out.
Why is there no warning on a paid order whose address I changed?
Because you could not act on it there, and acting on it could quietly make things worse.
Three reasons, in order of weight:
- It would rewrite a period you may already have filed. The European Community Sales List reads this decision and records what you exported, so it can tell you a period changed after you filed it. Re-deciding a paid order changes what that statement reports for the quarter the order falls in. That should not happen from a one-click button on an order screen.
- The plugin cannot put the money right. On a paid order that means a credit note in your invoicing software and a conversation with your accountant, neither of which is something this plugin can or should do.
- A warning you cannot act on teaches you to ignore warnings. If every paid order with a corrected address raised one, the ones you can act on would be lost among them.
The sale is not ignored, it is reviewed somewhere more useful. It appears on the European Community Sales List instead, where you meet it while preparing a quarter rather than on an order screen where you can do nothing about it. See “Does the report notice an address I changed later?” in the next section.
Can I re-check a number later?
Yes. The order panel has a “Check again now” button. It tells you it is working while it waits for VIES, and reports what it found at the top of the screen as well as in the order notes.
It does not change the order or its tax. The check made when the order was placed is the one that counts, because that is the one contemporaneous with the supply.
Switching from another plugin
We call this Zero-Touch Migration: your customers’ numbers migrate themselves while the shop runs, so there is nothing to export, nothing to import and no maintenance window. The questions below are the detail.
Will I lose the VAT numbers I already have?
No. The plugin reads numbers stored by other plugins and treats them as if they were its own. Nothing needs importing, and nothing of theirs is touched, so switching is safe and reversible.
Reading another plugin’s data does not require that plugin to be active. Deactivate it: the numbers stay in the database and we still find them.
A returning customer sees their existing number already filled in at the checkout, in My Account and on the address form. The first time they save it, it becomes ours.
Which plugins can it read numbers from?
These, in this order of preference when a customer has a number stored by more than one:
| Plugin |
|---|
| WooCommerce EU VAT Number, the official WooCommerce extension EU VAT Assistant by Aelia EU VAT Compliance by Simba Hosting These three write a customer’s number to the same place, so there is nothing to choose between them. |
| EU/UK VAT Validation Manager by WPFactory, formerly Alg |
| Teamwant VIES VAT for WooCommerce, sold as European VAT Validator |
| InvoiceXpress for WooCommerce, our own |
| NIF (Num. de Contribuinte Português) for WooCommerce, our own free plugin APG NIF/CIF/NIE, free, and stores to the same place |
The order is by how specific each field is, and only matters for a customer who has a number stored by more than one plugin. The fields above exist to hold a VAT number and nothing else, so they are trusted first. The last asks every customer for a number for invoicing, business or not, so it is read only when nothing better is there. Anything already stored by this plugin beats all of them.
On orders those first three do have their own separate fields, so there the order between them is by specificity too.
Aelia and WPFactory both let a site rename their field. Where they do, we ask them for the name rather than assuming it.
Teamwant stores under two different keys depending on which checkout took the order, and both are read. One thing it does that no migration can undo: when your checkout already has a field called vat or billing_vat, from a theme or another plugin, it uses that field instead of adding its own. Numbers collected that way are under that field’s key, not under Teamwant’s, so check --list-sources against your own database before concluding there is nothing to bring across.
wp nakedcat-vat migrate --list-sources prints this list as your own site sees it, including the exact database keys, which is the quickest way to confirm your old plugin is covered.
Do I have to run a migration?
No. There is nothing you need to do.
The migration is continuous and happens by itself, a customer at a time, only when it is needed. Whenever the plugin wants a customer’s VAT number, for the checkout, for My Account or for the address form, it looks for its own first and then at every other plugin’s, and uses whatever it finds. The customer sees their number where they expect it and never knows anything changed. The next time they save it, it quietly becomes ours.
Nothing is copied in advance, nothing runs on activation, and there is no batch job waiting to time out on a large store. A shop with fifty thousand customers is migrated exactly as cheaply as one with fifty, because the work only happens for the customers who actually come back.
Old orders are left as they are. They keep showing the number the other plugin recorded, labelled as such, because that plugin made the check and we cannot vouch for it.
They are not ignored, though. The European Community Sales List reads them too, so the quarter you switched in reports what you actually supplied rather than only the part of it that happened after you installed this plugin. See Does it include orders from my previous VAT plugin? above for which plugins that covers and what those lines can and cannot show.
Can I migrate everything at once anyway?
You can, and there are two fair reasons to want to: you would rather not wait for customers to drift across, or you want to see what is actually there before you commit to switching.
It is optional. If you skip it, everything above still happens.
With WP-CLI:
wp nakedcat-vat migrate # shows what it would do, changes nothing
wp nakedcat-vat migrate --list-sources # what this install would read
wp nakedcat-vat migrate --execute # applies it
wp nakedcat-vat migrate --from=aelia # only from one plugin, see the table below
wp nakedcat-vat migrate --users # customers only
wp nakedcat-vat migrate --orders # orders only
wp nakedcat-vat migrate --limit=50 # a first look
Given neither --users nor --orders, it does both.
--from takes one of these, and nothing else has to be guessed:
--from |
Plugin |
|---|---|
wooeuvat |
WooCommerce EU VAT Number, the official extension |
aelia |
EU VAT Assistant (Aelia) |
simba |
EU/UK VAT Compliance (Simba) |
wpfactory |
EU/UK VAT Validation Manager (WPFactory) |
teamwant |
Teamwant VIES VAT |
invoicexpress |
Invoicing with InvoiceXpress |
nifportugues |
NIF (Num. de Contribuinte Português) |
wp nakedcat-vat migrate --list-sources prints the same table for your own site, with a --from column beside each plugin, so the value is always one command away.
Matching is not case sensitive and a partial is enough, so --from=nif works as well as --from=nifportugues. A plugin’s full name works too, for anything already written that way. A value nothing recognises is refused with the list of what is accepted, rather than quietly reporting that no numbers were found.
On customers, the first three plugins share one database field, so a number under it could have come from any of them and --from=wooeuvat, --from=aelia and --from=simba all reach it. On orders each plugin has its own field, so each value means exactly one plugin.
It deletes nothing. The other plugin keeps its data exactly as it was, so nothing is burned behind you and you can go back if you need to. If you later want the old data tidied away, see the next answer: it is a separate, deliberate step, and not something the site can ever do on its own.
Running it changes nothing about how the plugin behaves. It simply does in one go what would otherwise happen gradually, so the report is worth more than the write: --list-sources and a plain dry run will tell you how many numbers you have and which plugin each came from, before you decide anything.
Orders migrated this way get the number and its country and nothing else. No validation record is written, because we did not make that check and cannot say when the other plugin made theirs.
Can I delete the old plugin’s numbers once they are copied?
Yes, with --delete-source, and please read this before you use it.
wp nakedcat-vat migrate --execute --delete-source
It deletes only the single key each record was actually read from, never the whole list of possible sources, and never anything it did not just copy. It requires --execute, so a dry run can never delete anything, and it asks you to confirm before it starts. --yes skips that prompt, which is only for scripting.
It is not reversible. It is the one thing in this plugin that removes another plugin’s data, which is why it is off by default, why it asks, and why the automatic migration that happens as customers order never does it no matter how it is configured.
Most shops never need it. The old keys sitting there cost nothing, and leaving them is what lets you change your mind. Reach for this only when you have decided you are not going back, and take a database backup first.
Should I run this alongside my old VAT plugin?
No, and not even briefly. Run one VAT plugin at a time.
Two of them will both add a VAT field to your checkout, both try to validate what the customer types, and both decide whether to remove the tax. Your customers may see the field twice, the messages can contradict each other, and which plugin wins the decision about VAT is genuinely unpredictable, because it comes down to the order the two happen to run in. An order can end up exempted by one plugin and taxed by the other on the next page load.
The safe sequence is: install this plugin, deactivate the old one, then check a test order. You do not have to migrate first and you do not have to hurry, because the old plugin’s data stays in the database either way and this plugin reads it whether that plugin is active or not.
If something is wrong, deactivate this one and reactivate the old one. Their data is still there.
Will it overwrite a number I already have?
Never. Anything already stored by this plugin wins over every other source, which also makes the migration safe to run twice.
Speed and caching
Is the checkout slowed down by all this checking?
Very little, by design.
Most numbers never reach VIES at all: the format and check-digit tests run first and answer instantly. When a number does have to be checked, the answer is remembered, so the same customer editing their address, changing country or simply pausing while they type is answered from memory rather than from Brussels.
There are several layers of that memory, from one that lasts a single page load to one shared by everybody on your site, and the plugin works its way outward through them before it considers making a call. None of it needs configuring, and none of it requires a caching plugin or any special hosting: it works on ordinary shared hosting, and takes advantage of a persistent object cache if your host provides one.
How long are results kept?
Confirmed results are kept for as long as you choose, one day by default. This affects checkout speed only: the number is always re-checked live when the order is placed.
Numbers VIES did not recognise are kept for one minute, and that is not configurable. It is long enough to absorb a double submit and short enough that a business whose registration goes live is not told otherwise for the rest of the day.
Outages are never cached.
Whatever you pick, a stored result is still used if VIES is unreachable at the moment an order is placed, because a stale answer beats a failed order.
Developers
Can I check a VAT number from the command line?
Yes:
wp nakedcat-vat validate PT507179633
wp nakedcat-vat validate 507179633 --country=PT
wp nakedcat-vat validate LT100005065913 --proof --format=json
It prints everything that was decided: the status, the reason code, the country and number as they were read, and, when VIES answered, the name and address it returned. It changes nothing, on any order, customer or setting. It answers a question, which makes it the quickest way to settle “is this number actually good” without touching a shop.
Give --country when the number carries no country prefix, or it cannot be attributed to one and is refused. Add --proof to ask VIES for a consultation number too; that needs your own VAT number set and confirmed, and it spends one of the identified lookups, so it is off unless you ask.
A VIES outage is reported as an outage, never as a bad number. That distinction is the whole point of running this during a support conversation.
The same check is also available over the REST endpoint and as a WordPress Ability, which can itself be run from the command line:
wp ability run nakedcat-vat-number/validate-vat-number --input='{"vat_number":"PT507179633"}' --user=1
That route needs --user, because the Ability is gated on manage_woocommerce and WP-CLI runs as nobody by default. The command above does not.
How do I read a customer’s VAT number from my own code?
Four functions, and they are the only part of this plugin you should call. Everything else is internal and may be renamed or removed in any release.
nakedcat_vat_number_from_order( $order ); // '' or e.g. 'PT980405319'
nakedcat_vat_number_country_from_order( $order ); // '' or e.g. 'PT'
nakedcat_vat_number_from_user( $user ); // '' or the customer's current number
nakedcat_vat_number_country_from_user( $user ); // '' or its country
Each takes an object or an ID, and each returns a string, empty when there is nothing to return, so you never have to check a shape before using the result.
function_exists( 'nakedcat_vat_number_from_order' ) is the way to check whether this plugin is active, and each function’s return value has a filter of the same name.
Order or customer? The order records what was true when it was placed. The customer record is their number today, and they may have corrected or removed it since. Use the order when you are describing an order, including old ones, and the customer only when you are preparing something new for them.
Why ask for the country separately? Because it is the country that issued the number, which is not necessarily the billing or shipping country: a business can hold VAT registrations in more than one member state. It is also stored separately from the number, so it still answers correctly on a shop that has turned the country prefix off, where the stored number is bare.
These deliberately do not tell you whether the number was validated. Whether a number was checked, when, against what, and whether that justifies removing VAT are questions with expensive wrong answers, so the plugin keeps that reasoning to itself. What was found is on the order, in the metabox.
They read this plugin’s own data only. A number left behind by another VAT plugin is not returned; bring it across with wp nakedcat-vat migrate so one key is the answer everywhere.
Are there hooks for other plugins?
Yes. These fire so an invoicing or accounting plugin can keep its own copy in step without reading our meta keys:
| Hook | Arguments |
|---|---|
nakedcat_vat_number_order_saved |
$order, $number, $result, $decision, $context |
nakedcat_vat_number_user_saved |
$user_id, $number, $result, $context |
nakedcat_vat_number_order_cleared |
$order, $previous_number |
nakedcat_vat_number_user_cleared |
$user_id |
$context is checkout, admin or account. Only checkout is contemporaneous with the supply, which matters if you are treating the number as proof.
Can I override how the exemption is decided?
Two filters, both off by default, and neither of them recommended.
Both are deliberately kept out of the settings screen. Each one makes the plugin depart from what the VAT Directive actually asks for, so turning one on should be a deliberate act by somebody whose accountant has told them it applies to their situation, not a checkbox somebody ticks while skimming. The defaults follow the law; these do not.
If you turn one on, the responsibility for the tax charged on those orders is yours.
When either is active the settings screen says so, in red, and every order it affects records that it was involved. Neither is hidden from you once it is on.
Use the billing address instead of the shipping address
add_filter( 'nakedcat_vat_number_use_billing_country', '__return_true' );
Not recommended, and almost certainly not needed.
You probably do not need this at all. When an order has no shipping address, which is every order on a services or downloads-only shop, the plugin already uses the billing address, because it is the only address the order has. That happens by itself and always has. A shop that never dispatches anything is therefore already deciding on the billing address without touching this filter.
Which leaves one case: a shop that does collect a shipping address but wants the billing address to decide anyway. That is the case we do not endorse. The exemption depends on the goods physically leaving for another EU country, which is why the shipping address decides, so overriding it means zero-rating on evidence the Directive does not ask for.
There is also an open question underneath all of this. The plugin’s exemption rules are written for goods under Article 138, whereas cross-border B2B services are reverse-charged under Articles 44 and 196 on a different evidential basis. We have not finished working out whether a services shop needs different rules altogether. Until we have, this filter is a workaround, not an answer.
Remove VAT on wholly domestic sales
add_filter( 'nakedcat_vat_number_allow_domestic_exemption', '__return_true' );
Not recommended. Overrides one condition and one only: that the VAT number must have been issued by a country other than your own. Everything else each set of conditions requires still has to be met, including confirmation by VIES.
It applies only when the sale is entirely domestic, meaning your country, the shipping country and the VAT number’s country are all the same. A VAT number from your own country shipping to another EU country stays taxable whatever you set here, because the goods leave from the same country the customer is registered in.
Be aware that this is not how the VAT Directive works. Article 138 exempts goods going to a business in another Member State, and a domestic sale is not that. It exists because some merchants are advised that their particular situation calls for it.
If you were pointed at this because of a domestic reverse charge, read this first. There is a real domestic reverse charge in EU law, and this filter is not it. Articles 199 and 199a of the VAT Directive let a Member State make the customer liable for VAT, but only for a named list of supplies: construction work on immovable property, staff for that work, scrap and waste, goods provided as security, certain immovable property, and under Article 199a mobile phones, integrated circuits, game consoles, tablets and laptops, emission allowances, gas and electricity to dealers, cereals and raw metals. In Portugal these are CIVA article 2.º n.º 1, alíneas i), j) and l).
Three things follow. Each Member State chooses which of those it applies, so there is no single correct list. The Article 199a items are temporary, currently running to 31 December 2026. And the test is different from this plugin’s: it turns on the customer being a taxable person with a right to deduct in that country, not on a VIES confirmation, and in Portugal it does not apply at all if the customer is exempt under article 9.º or 53.º.
This filter is not scoped to any of that. Switch it on and every domestic sale to a VIES-confirmed business in your own country is exempted, across your whole catalogue. The real regimes are deliberately limited to named categories precisely because a catalogue-wide rule would be wrong. If a domestic reverse charge genuinely applies to what you sell, this plugin is not the tool: it decides the intra-Community exemption, and that is a different regime that happens to share a name.
Can I stop the VAT number showing on the thank-you page and emails?
Yes:
add_filter( 'nakedcat_vat_number_show_in_order_totals', '__return_false' );
That removes the row from the order confirmation page, the My Account order view and the order emails, and changes nothing else: the number is still collected, still stored, and still on the order in WP Admin.
To keep it but change how it reads, filter the row instead. It has label and value, and the value is already escaped:
add_filter( 'nakedcat_vat_number_order_totals_row', function ( $row, $order ) {
$row['label'] = 'Tax ID:';
return $row;
}, 10, 2 );
Neither of those touches the exemption note, which is a separate row with filters of its own. See below.
Can I change the note that says why no VAT was charged?
From the settings, yes: WooCommerce > Settings > Tax > VAT number. The Tell the customer why checkbox turns it on and off, on by default, and the field under it holds the wording. Switching it off hides that field rather than clearing it, so switching it back on does not mean typing the sentence again.
In code there are two filters. The first changes the wording, and also receives the recorded exemption decision, so the note can differ by why the order was exempted:
add_filter( 'nakedcat_vat_number_exemption_note', function ( $note, $order, $decision ) {
return 'Isento artigo 14.º do RITI';
}, 10, 3 );
The second changes the whole row, label included, which is what you want if the note is carrying your own legal wording. Return an empty array to drop it:
add_filter( 'nakedcat_vat_number_exemption_note_row', function ( $row, $order ) {
$row['label'] = 'Isenção:';
return $row;
}, 10, 2 );
Whatever you put there, remember it is not the invoice mention. See “What has to appear on the invoice?” above.
Can another plugin decide the field is not needed for one customer?
Yes. “Ask always” is a shop-wide setting, but whether it applies is sometimes a fact about the individual order: a shop that offers “I do not want a VAT number on my invoice” as a checkout choice has to be able to honour it without turning the setting off for everybody.
In PHP, which is what actually decides:
add_filter( 'nakedcat_vat_number_required', function ( $required, $country, $order ) {
if ( $order && $order->get_meta( '_wc_other/your-plugin/no-vat-invoice' ) ) {
return false;
}
return $required;
}, 10, 3 );
On the Blocks checkout there are two matching JavaScript filters, so the shopper sees the same answer rather than being stopped at the end:
wp.hooks.addFilter( 'nakedcat_vat_number.required', 'your-plugin', ( required, context ) =>
context.additionalFields[ 'your-plugin/no-vat-invoice' ] ? false : required
);
wp.hooks.addFilter( 'nakedcat_vat_number.show', 'your-plugin', ( show, context ) =>
context.additionalFields[ 'your-plugin/no-vat-invoice' ] ? false : show
);
context carries billingCountry, additionalFields and the current value, and both filters re-run whenever any of those change, so a checkbox takes effect the moment it is ticked.
Use nakedcat_vat_number.show rather than hiding the field with CSS. A field hidden that way is still on the page as far as the checkout is concerned, so if it is also required the shopper is stopped by an error about a field they cannot see.
Are there other filters?
| Filter | Does |
|---|---|
nakedcat_vat_number_field_priority |
Where the field sits among the classic checkout’s billing fields. Default 120. |
nakedcat_vat_number_block_placement_pages |
Which pages the checkout block is added to on first run. |
nakedcat_vat_number_vies_timeout |
How long to wait for VIES before treating it as unreachable. |
nakedcat_vat_number_exemption_inputs |
The decision inputs, before they are evaluated. |
nakedcat_vat_number_exemption_decision |
The finished decision, after it is made. |
nakedcat_vat_number_checkout_rate_limit |
How many checks one shopper may make while ordering. Default 30. |
nakedcat_vat_number_checkout_rate_window |
The period that allowance covers, in seconds. Default 300. |
nakedcat_vat_number_rest_rate_limit |
The same for the REST endpoint, which allows 20 rather than 30. |
nakedcat_vat_number_rest_rate_window |
The period that allowance covers, in seconds. |
nakedcat_vat_number_palette_commands |
What the plugin adds to the WordPress command palette. |
nakedcat_vat_number_ecsl_statuses |
Which order statuses count as a supply on the European Community Sales List. Default processing and completed. |
nakedcat_vat_number_ecsl_split |
The goods and services split for one order on the European Community Sales List. |
nakedcat_vat_number_goods_only |
Whether an order or cart carries nothing but goods. Decides whether a Northern Ireland order is exempted. |
nakedcat_vat_number_territories |
The set of territories treated as outside the EU VAT area. For adding one we do not ship. |
nakedcat_vat_number_territory_<id> |
Whether one territory is recognised. Return false to treat it as an ordinary part of its country. The ids are es_canarias, es_ceuta, es_melilla, fi_aland, de_heligoland, de_buesingen, it_livigno, it_campione and gr_mount_athos, so the Canary Islands hook is nakedcat_vat_number_territory_es_canarias. |
The four rate limits exist so a shop with an unusual checkout can loosen them, not because they normally need changing. They deliberately never apply to the check made when an order is actually placed: that one has to happen, whatever anybody typed beforehand.
nakedcat_vat_number_ecsl_split receives an array of goods and services that add up to the order’s net, and the order itself. The default treats a virtual product as a service and everything else as goods, with shipping and fees following the goods unless the order is entirely virtual. That is a heuristic, and a shop that knows its own catalogue can answer it properly.
The exemption pair are low level and exist for cases a boolean cannot express, such as a per-product goods-versus-services distinction. Prefer the two named above them: those are introspectable, so the settings screen can report them and the order can record them, while a filter that reshapes an array leaves nothing able to explain what it did.
Where is the data stored?
| Key | |
|---|---|
| Order number | _nakedcat_vat_number |
| Order country | _nakedcat_vat_country |
| Order proof | _nakedcat_vat_validation (JSON) |
| Order exemption decision | _nakedcat_vat_exemption (JSON) |
| Customer number | nakedcat_vat_number |
| Customer country | nakedcat_vat_country |
The proof is where the VIES answer lives: the status and reason, the consultation number, the trader name and address VIES returned, whether the answer came from a stored result, the plugin version that recorded it, and the country the customer’s IP resolved to. The customer’s IP address itself is not copied: WooCommerce already stores it on every order, and a second copy would be one more thing to disclose, export and erase for a value that is one method call away.
Is there a REST endpoint?
POST /wp-json/nakedcat-vat/v1/validate with {"vat_number": "...", "country": "..."}. It requires either the manage_woocommerce capability or an active WooCommerce customer session, and is rate limited. It never returns your own VAT number.
It also tells you how fresh the answer is, which matters more than it sounds. A confirmation can come from a live call made just now, from a stored answer up to the lifetime you configured, or from an expired answer reused because VIES was unreachable. All three are a genuine “yes”, but only the first was obtained now, and anything that has to stand up later should know the difference:
from_cache: the answer came from stored results rather than a call made now.from_stale_cache: the answer was past its lifetime and reused because VIES could not be reached.checked_at: when VIES actually gave that answer, as ISO 8601 in UTC to the second, in the same shape whichever layer answered.checks.viesreportsconfirmed_stalerather thanconfirmedin that last case, so a caller reading onlycheckscannot miss it.
vies_checked says which layer answered, not how fresh it is, so it is true for a stored answer too.
Can I jump to the settings from the command palette?
Yes. Press the command palette shortcut anywhere in WP Admin and start typing “VAT”, and you get two entries: one that opens the plugin’s settings and one that opens this documentation page in a new tab.
WordPress already turns every admin menu and submenu item into a palette command on its own. This plugin’s settings are not a menu item, they are a section inside WooCommerce’s own tax settings, so without this they would never appear. Which screens have a palette at all depends on your WordPress version: recent versions offer it throughout WP Admin, older ones only inside the editors.
Only users who can manage WooCommerce are given the commands, and nakedcat_vat_number_palette_commands changes or removes them.
Does it work with AI agents and MCP clients?
Yes. The plugin registers a nakedcat-vat-number/validate-vat-number ability through the WordPress Abilities API when that is available, returning the full structured result rather than a boolean, so an agent gets the reason a number failed and not merely that it did.
The answer carries the same freshness fields as the REST endpoint, from_cache, from_stale_cache and checked_at, so an agent can tell a confirmation obtained seconds ago from one reused during a VIES outage.
Because it is an ability rather than a private endpoint, anything that exposes this site’s abilities over MCP offers it too. A shop manager can ask an AI client to check a VAT number in conversation, and the client calls the same validator the checkout uses, with the same rules, the same caching and the same rate limits. It is marked read only, non destructive and idempotent, so an agent can tell it is safe to call.
It also reports what each layer of validation decided, as a closed set of codes the ability’s own schema explains, so an agent never has to infer them:
format |
passed, failed, not_checked |
check_digit |
passed, failed, no_algorithm, not_checked |
vies |
confirmed, not_listed, unavailable, disabled, not_applicable, requester_rejected, not_checked |
This matters because the overall status cannot tell two very different situations apart. A number the customer mistyped and a perfectly formed number that simply is not registered for intra-EU trade both come back invalid, and they call for opposite responses: ask the customer to check what they typed, or charge the VAT and carry on. The first reads check_digit: failed, the second check_digit: passed, vies: not_listed.
no_algorithm is not a failure. Every territory this plugin recognises now has a verified check digit rule, so it is left for a country we do not recognise at all, where saying nothing ran is more honest than reporting a pass.
Compatibility
Does it work with the Blocks checkout?
Yes, both checkouts are fully supported, including live totals: the VAT comes off as soon as a qualifying number is entered, without waiting for the order to be placed.
Does it work with WooCommerce Subscriptions?
Yes, and there is one thing worth understanding about it.
The number carries to every renewal, and so does the proof. Subscriptions copies what is on the subscription onto each renewal it raises. The VAT number carries over untouched, which is right: it is the same business. The proof of the VIES check is marked as inherited rather than copied silently, so a renewal raised a year later says “inherited from” and names what it came from, instead of appearing to have been checked on the day it was raised. Only the first order in a subscription carries a check made for that order.
Nothing re-checks a number on its own. A business that stops being registered keeps its exemption on every renewal until somebody looks. That is true of every plugin in this market, and it is deliberate here: a renewal that suddenly has VAT added is a different price from the one the customer agreed to, so the payment would likely fail and the customer has to be told before anything changes. That decision is yours, not the plugin’s.
So you can check one whenever you want. Open the subscription, and the VAT number panel is there with a Check again now button. It asks VIES and writes the answer as a note, and changes nothing else.
If VIES confirms the number, you are offered a second button, Replace with this validation. That is the one that writes: from then on the subscription carries the new validation, and renewals raised afterwards inherit it instead of the older one. The previous validation is kept on file and shown as Before this check, so the record still says who validated it first and when.
If VIES does not confirm it, nothing is changed at all and the panel says so. The number on file keeps being used for renewals until you decide otherwise, which gives you time to speak to the customer about a price that is about to change.
Is the number checked again, or only when the subscription is set up?
Every renewal is checked again, on its own. This runs by itself, with nothing to switch on and nothing to configure.
This is the problem it exists for: a VAT number is checked once, when the subscription starts, and every renewal after that removes VAT on the strength of that one check. A business that stops being registered keeps the exemption for years. Nobody notices, because there is nothing on any screen to see, and no other plugin in this market notices either.
Shortly after a renewal is raised, if that renewal is billing without VAT on a VAT number, this plugin asks VIES again. What happens next depends on the answer, and only one of the four does anything:
- Still confirmed. The stored validation is replaced with the fresher one, on the renewal and on the subscription. That second part is what keeps the next renewal right rather than only this one, because the subscription is the record every future renewal is copied from. Nothing is written in the notes: a “still confirmed” note every month would bury the one note that matters.
- No longer confirmed. A note goes on the renewal, the shop is emailed, and the order screens say so. Nothing else changes at all.
- Outside VIES. The number belongs to a country VIES does not cover, Great Britain above all. Nothing is wrong and nothing is recorded.
- Could not ask. VIES did not answer. It comes back and tries again a few times, then leaves it for the full check to pick up. An outage is never written down as a refusal.
Why does it not just add the VAT back?
Because that would break the payment, and the customer has done nothing wrong.
A customer paying 100 without VAT becomes a customer owing 123. A subscription cannot simply raise what it takes: card payments are taken off-session against a mandate the customer agreed at the old price, and an increase is exactly the case likely to need authentication they are not present to give. PayPal billing agreements are capped at the amount agreed. So adding the VAT automatically produces failed renewals, dunning and cancelled subscriptions, for customers whose only mistake was letting a registration lapse.
That is why the correct behaviour is to tell you and change nothing. You decide whether to speak to the customer, agree a new price, or absorb it. The plugin never re-prices a live subscription behind your back, in either direction: quietly charging less is a VAT liability you did not choose to take on.
Where you see it:
- an order note on the renewal
- an email, naming the number and linking to the renewal and the subscription
- the VAT number column on the orders and subscriptions lists, which shows a warning instead of a tick
- the VAT number panel, which says the number was confirmed when the order was placed and no longer is
Who gets the email?
The site administrator, unless you say otherwise under WooCommerce > Settings > Tax > VAT number, in the Subscriptions section. Put an address there and the reports go to it instead.
Leaving it empty means the site administrator, not “send nothing”. The check runs either way, and this only decides who hears about it. If you want nobody to hear about it, the honest thing is to say so: ask us and we will add a way to turn it off, rather than have an empty box quietly mean silence.
The same address receives the report from the full check below.
The email is sent as one of your shop’s own, from the name and address under WooCommerce > Settings > Emails, so it looks like the rest of your shop’s mail and is not filed as spam from an address nobody recognises.
What is the difference between this and the full check below?
They do different jobs, and the everyday one is the one above.
The renewal check is the routine. It runs on your shop’s own billing dates, spreads itself across the month because your renewals do, needs no terminal and no scheduling, and tells you within minutes of a renewal being raised.
The full check is the backfill. It exists for what the renewal check cannot reach: subscriptions that have not renewed since you installed this plugin, and long billing periods, since a yearly subscription would otherwise go a whole year between checks. It is a WP-CLI command, so it is a tool for a developer or a host with shell access rather than something a shop needs to use.
If you never run the command, the renewal check still covers you from the next renewal onwards. If you want to know where you stand today, run the command once.
Can I check them all at once?
Yes, with WP-CLI. This is the backfill described above: use it to find out where you stand today, rather than waiting for each subscription to renew.
wp nakedcat-vat recheck-subscriptions # every active subscription running VAT free
wp nakedcat-vat recheck-subscriptions --status=active,on-hold # other statuses too
wp nakedcat-vat recheck-subscriptions --notes # record the failures on the subscription
wp nakedcat-vat recheck-subscriptions --all # list every one checked, not only failures
wp nakedcat-vat recheck-subscriptions --limit=25 # a first look at a large shop
It looks only at subscriptions that are actually running without VAT on a VAT number, whether this plugin exempted them or a previous one did, so a shop with thousands of subscriptions asks VIES about the handful that matter. Everything else is skipped, which is faster for you and considerate to a free public service.
It reports four outcomes, and only one of them is bad news:
- Still confirmed. VIES says yes, as it did when the subscription was set up.
- No longer confirmed. VIES says no. This is the one to act on.
- Outside VIES. The number belongs to a country VIES does not cover, Great Britain above all, since it has not been in VIES since Brexit. Nothing is wrong with these and nothing can be learned about them here.
- Could not ask. VIES did not answer. An outage is not a deregistered customer, and reporting one as the other would send you chasing your own clients over nothing.
If you want to know how many subscriptions this will even look at before running anything:
wp nakedcat-vat recheck-subscriptions --count
That shows the funnel, asks VIES nothing and writes nothing:
subscriptions in: active 434
carrying a VAT number 51
of those, billing without VAT 44
of those, still charged VAT 7
Only the ones billing without VAT are re-checked, which is usually a small fraction of a subscription base and is meant to be. A number on a subscription that is still charged VAT, a domestic sale for instance, changes nothing whatever VIES says about it.
On a shop with more than a handful of subscriptions, run it in the background instead:
wp nakedcat-vat recheck-subscriptions --count # what will it even look at
wp nakedcat-vat recheck-subscriptions --schedule # start it
wp nakedcat-vat recheck-subscriptions --schedule --status=active,on-hold # other statuses too
wp nakedcat-vat recheck-subscriptions --progress # how is it going, and how much is left
wp nakedcat-vat recheck-subscriptions --cancel # stop it
That does the same checks a batch at a time, a few minutes apart, which is the polite way to use a free service with a concurrency budget shared by everyone in Europe. It survives the request that started it, and it can be stopped. Only one sweep runs at a time, so a second cannot be started until the first finishes or is cancelled, and a sweep that dies is released automatically rather than blocking the next one forever.
When it finishes it emails you, at the address in the Subscriptions section of the settings or the site administrator if you left it empty, whether or not anything failed. A report that only arrives with bad news cannot be told from a sweep that never ran. The email lists any subscription now billing without VAT on a number VIES will not confirm, with a link to each one.
Notes are written when the answer changes, not on every pass: the first time a number stops confirming, and again if it starts confirming later. A sweep that noted “still confirmed” every month would bury the one note that matters under dozens of identical ones.
It changes nothing, run either way. It never removes an exemption, re-prices a renewal or touches the stored validation, because all three change what a customer is charged. A note is the only thing it ever writes: the run in your terminal writes one only if you ask for it with --notes, and the background sweep writes one when an answer changes. When you have decided what to do about a subscription, open it and use the buttons described above.
What about a subscription set up under my previous VAT plugin?
The panel shows what that plugin recorded, names it, and says plainly that we did not make that check and cannot vouch for it. Check again now works there too, so a number another plugin validated can be confirmed on your own terms and kept, without waiting for the customer to place another order.
wp nakedcat-vat migrate covers subscriptions as well as orders, and says how many of the records it touched were subscriptions. That matters more than the order count: migrating a subscription is the only thing that reaches the renewals it has not raised yet.
Does it work with WPML?
Yes. If you rename the field, that label is registered for translation, so you can give it different wording per language under WPML > String Translation. Left at its default it is translated already, along with every other message the plugin shows.
Nothing else needs translating: VAT numbers are data, not text, and they live on orders and customers, which are not translated in the first place.
Does it work with HPOS?
Yes. High-Performance Order Storage and the legacy post storage are both supported.
Does it work with Shop as Client PRO?
Yes. When a shop manager places an order on a customer’s behalf with Shop as Client PRO, the customer’s VAT number is filled in for them on both checkouts, so nobody has to retype a number the customer already gave you.
It reads the number the same way the customer’s own checkout does, which means a customer whose number is still stored by your previous VAT plugin fills in correctly too, before any migration has touched them.
This needs Shop as Client PRO 8.2 or later.
Does it support Great Britain, Northern Ireland, Norway or Switzerland?
Northern Ireland: yes, for goods. VIES lists XI numbers and the plugin checks them like any other, and it removes VAT when the order is goods being sent to a Northern Ireland address on a confirmed XI number. Services are not covered, because Northern Ireland is inside the EU VAT rules for goods only. See “What about Northern Ireland?” above for exactly when VAT comes off. The format and the check digit are both checked on your own server before VIES is asked, like any other country.
Great Britain, Norway and Switzerland: not yet. None of them is in VIES, and each has its own national service with its own rules. What the plugin does today is recognise the shape of their numbers so one can be collected and kept: set “Show the field to customers billing in” to “EU, plus the UK and Switzerland” (or “Every country”, which is the one that includes Norway), and the number is stored on the order and on the customer for your invoices. It is never validated, and VAT is never removed for those countries. Northern Ireland is the exception described above, and it is the only one.
We plan to add real validation for them, and how soon depends on how many people ask. If you need it, please tell us: get in touch through our pre-sales questions page. It genuinely does decide what gets built next.
Does it need the SOAP extension?
No, and it never falls back to SOAP either. It uses the European Commission’s REST API and nothing else. Some other plugins refuse to activate without the SOAP extension; this one has no PHP extension requirements beyond WordPress’s own.
When VIES cannot be reached, the plugin uses the most recent stored answer for that number rather than a different protocol. See “What happens if VIES is unavailable?” above.
