Stripe EU VAT: Validate Tax IDs Before Charging Customers
Stripe EU VAT integration guide: validate EU VAT numbers server-side before applying zero-rate B2B exemptions in Stripe Checkout. Ensures you only exempt valid registered EU businesses from VAT.
When you sell B2B across EU borders, EU VAT Directive 2006/112/EC requires you to verify that the buyer holds a valid VAT registration before you can apply zero-rate (reverse charge). Skipping this check and zero-rating an invoice to an unregistered buyer creates a VAT liability that falls on your business, not the customer.
The practical flow is: at checkout the buyer enters their VAT number, your server calls GET /api/v1/validate/:country/:vat, and if the response contains status: 'active' you call stripe.customers.update({ tax_exempt: 'reverse' }) to flip the customer's tax treatment. Stripe then applies zero VAT on all future invoices for that customer. The TaxID response also returns company_name and company_address, which you should store in Stripe customer metadata for audit purposes.
VIES, the underlying EU service, can return status: 'service_unavailable' for some member-state nodes at any time. Build a fallback so that an unavailable VIES does not silently zero-rate the customer; the safest approach is to charge normal VAT and allow the customer to reclaim it, rather than to wrongly exempt an unverified number.
Stripe Tax automates VAT rate calculation and filing across 30+ countries, but it depends entirely on the tax_exempt flag you set on the customer object to determine reverse-charge eligibility. There is no built-in mechanism in Stripe that checks whether a VAT number is actually registered in VIES. A buyer can enter a plausible-looking number — even a correctly formatted one belonging to another company — and Stripe will zero-rate the invoice without complaint. TaxID queries the live VIES registry in real time and returns the name and address registered with the member state's tax authority, so you can cross-check the declared company details before applying zero-rate.
EU Invoice Directive 2006/112/EC Article 226 requires that zero-rated B2B invoices carry the annotation 'Reverse charge'. Stripe automatically adds this notation when tax_exempt: 'reverse' is set on the customer, so you do not need to handle invoice annotation separately. What you do need to handle is the correction flow: if a customer's VAT number later turns out to be invalid, you must issue a corrective invoice with VAT and pay the difference to the tax authority. The original buyer cannot be held liable for the tax — the liability sits with the seller.
Subscription businesses face an additional challenge: VAT registrations change. Companies deregister voluntarily, get cancelled by the tax authority, or restructure across borders. A number valid at signup may be revoked six months later. For any recurring billing integration, add a background job that re-validates all reverse-charge customers — weekly for high-revenue accounts, monthly for everyone else — and flags any returning status: 'invalid' for your finance team to review before the next invoice cycle.
Implementation steps
- 1
Collect VAT number at checkout
Add a dedicated VAT number input to your Stripe Checkout custom form or Payment Element. Place it in the billing-address section and label it clearly as 'EU VAT number' so buyers know it is optional for consumers but required for B2B reverse-charge treatment.
- 2
Validate via TaxID API
On form submission, call GET /api/v1/validate/:country/:vat from your server (never from the browser, to protect your API key). Parse the JSON response: check status === 'active' before proceeding; if status is 'invalid' return a form error, and if status is 'service_unavailable' log a warning and fall back to standard VAT rather than silently exempting.
- 3
Apply zero-rate if valid
When the API returns status: 'active', call stripe.customers.update({ id: customerId, tax_exempt: 'reverse' }) to enable reverse-charge treatment on the Stripe customer object. This causes Stripe Tax to suppress VAT on all subsequent invoices for that customer and adds the reverse-charge annotation required by EU invoice rules.
- 4
Store result on Stripe customer metadata
Persist the validated VAT number, the company_name and company_address from the TaxID response, and the validation timestamp in stripe.customers.update({ metadata: { vat_number, vat_company_name, vat_validated_at } }). This gives you an auditable trail showing the VAT was valid at the time of the transaction, which is required under EU record-keeping obligations.
- 5
Set up periodic re-validation
For subscription products, schedule a background job that re-calls GET /api/v1/validate/:country/:vat for every customer with tax_exempt: 'reverse' at least once a month. Stripe webhooks (customer.updated) can trigger a sync when a customer's details change, but they will not detect a VIES deregistration unless you actively re-check. Flag customers returning status: 'invalid' for your finance team before the next billing cycle.
Code example
Node.js
// After collecting the VAT number from your Stripe checkout form
const validateVAT = async (country, vatNumber) => {
const res = await fetch(
`http://localhost:3000/api/v1/validate/${country}/${vatNumber}`,
{ headers: { 'Authorization': `Bearer ${process.env.TAXID_API_KEY}` } }
);
return res.json();
};
// In your Stripe payment_intent creation handler
const { valid, company_name } = await validateVAT('DE', req.body.vatNumber);
if (valid) {
// Customer is a valid EU business — apply zero-rate / reverse charge
await stripe.customers.update(customerId, {
tax_exempt: 'reverse',
metadata: { vat_number: req.body.vatNumber, vat_company: company_name }
});
}Python
import requests
import stripe
def validate_vat(country: str, vat_number: str) -> dict:
response = requests.get(
f"http://localhost:3000/api/v1/validate/{country}/{vat_number}",
headers={"Authorization": f"Bearer {TAXID_API_KEY}"}
)
return response.json()
# In your checkout handler
result = validate_vat("DE", request.json["vat_number"])
if result["valid"]:
stripe.Customer.modify(customer_id, tax_exempt="reverse",
metadata={"vat_number": vat_number, "company": result["company_name"]}
)API response
The TaxID API returns a consistent JSON response for every validation request:
{
"valid": true,
"status": "active",
"country_code": "DE",
"vat_number": "123456789",
"company_name": "Example GmbH",
"company_address": "Musterstraße 1, 10115 Berlin",
"request_date": "2026-05-10T00:00:00.000Z",
"cached": false,
"request_id": "req_01j..."
}Error handling
The API uses a consistent Stripe-style error format. Always handle service_unavailable separately — VIES has occasional downtime and you should not reject valid customers during outages.
activeVAT number is valid and the business is registered
invalidVAT number format is wrong or not registered in VIES
service_unavailableVIES or the national system is temporarily down — retry later
Frequently asked questions
How often should I re-validate EU VAT numbers?
Validate at every checkout for one-off transactions. For subscription customers, run a background job that re-checks all customers with tax_exempt: 'reverse' at least monthly. VAT registrations can be cancelled between billing cycles, and a tax audit will ask for proof that the number was valid at the time of each invoice — not just at signup.
What should I do when VIES returns service_unavailable?
Never silently zero-rate an unverified number. The safest fallback is to charge standard local VAT and issue a credit note once VIES recovers and confirms the number is valid. Alternatively, display a message asking the buyer to retry in a few minutes. Log every service_unavailable response with the customer ID and timestamp so you can follow up.
Does Stripe Tax validate EU VAT numbers automatically?
No. Stripe Tax calculates and collects the correct VAT based on the customer's location and the tax_exempt flag, but it does not call VIES or any other registry to check that a VAT number is real or currently registered. You must call an independent validation API — such as TaxID — before setting tax_exempt: 'reverse' on the Stripe customer.
Which EU countries can I validate?
TaxID validates VAT numbers for all 27 EU member states via VIES: Austria (AT), Belgium (BE), Bulgaria (BG), Croatia (HR), Cyprus (CY), Czech Republic (CZ), Denmark (DK), Estonia (EE), Finland (FI), France (FR), Germany (DE), Greece (EL), Hungary (HU), Ireland (IE), Italy (IT), Latvia (LV), Lithuania (LT), Luxembourg (LU), Malta (MT), Netherlands (NL), Poland (PL), Portugal (PT), Romania (RO), Slovakia (SK), Slovenia (SI), Spain (ES), and Sweden (SE).
Can I call the TaxID API directly from the browser?
No. Always call the TaxID API from your server-side code, never from client-side JavaScript. A browser request would expose your API key to anyone who inspects network traffic. Pass the VAT number to a backend endpoint, validate there, and return only the result (valid: true/false) to the frontend.
Further reading
EU VAT Number Validation: The Complete Developer Guide (2026)
VIES is SOAP-based, unreliable, and has no caching. This guide explains how EU VAT validation works end-to-end, how to h…
B2B VAT Exemption in EU SaaS: What Your Billing System Must Do
Reverse charge sounds simple but the implementation details trip up most SaaS billing engineers. Here is what your syste…
SaaS VAT Compliance: A Developer's Complete Guide for EU Markets
Selling a SaaS product to EU customers means navigating B2B vs B2C VAT rules, determining the place of supply, registeri…
API reference for Stripe:
Stripe VAT Validation API
Validate EU VAT numbers before applying Stripe reverse-charge
Evaluating EU VAT APIs? Compare TaxID with:
Related use cases
UK VAT validation in Shopify B2B
Validate UK VAT numbers for B2B customers in Shopify. Required under UK Making Tax Digital rules for...
WooCommerce Spain NIF/CIF validation
Validate Spanish NIF and CIF numbers in WooCommerce checkout. Automatically apply B2B tax exemptions...
EU VAT compliance for SaaS billing
Handle EU VAT for SaaS subscriptions. Validate customer VAT numbers at signup, determine B2B vs B2C ...