Stripe EU VAT ID verification: getting the answer before payment
Stripe verifies an eu_vat tax ID against VIES (and a gb_vat against HMRC) after you add it to a customer: asynchronously, reported by the customer.tax_id.updated event with a status of pending, verified, unverified or unavailable. In Checkout, the number is checked for format during the session, not for validity, and checkout.session.completed can arrive before the verification ends. Stripe Tax applies reverse charge when the format is right, whatever the government answer. If you want the VIES answer before you decide, check the number yourself first.
- Stripe verifies
eu_vatIDs with VIES, retries during VIES downtime, and reportsverification.statusthroughcustomer.tax_id.updated. - Status
unavailablemeans you must verify manually, in Stripe’s words. - Checkout: format only during the session; validity afterwards; guest customers get no verification result or event.
- Stripe documents no consultation number on the tax ID object.
Check first, then create the customer
Collect the VAT number in your own step before Checkout (or before an invoice), check it, and only then create the customer with the tax ID. You know the answer at the moment you choose between reverse charge and charging VAT, and you keep the date and the consultation number with the customer. Stripe still runs its own verification afterwards; yours is the one dated at the moment of the sale.
// Your own form collects the VAT number first; the Checkout Session then reuses it.
const API = "https://veriport.io/api/v1";
async function checkVat(number) {
const res = await fetch(API + "/vat/validate?number=" + encodeURIComponent(number) +
"&requester=" + encodeURIComponent(process.env.OWN_VAT_NUMBER), {
headers: { Authorization: "Bearer " + process.env.VERIPORT_KEY },
});
const body = await res.json();
if (res.status === 503) return { decision: "retry_or_charge_vat" }; // VIES down, nothing cached
if (!res.ok) throw new Error(body.code);
return { decision: body.valid ? "reverse_charge" : "charge_vat", evidence: {
valid: body.valid, requestDate: body.requestDate, kind: body.source.kind,
consultationNumber: body.consultationNumber ?? null,
} };
}
// const r = await checkVat(form.vatNumber);
// if (r.decision === "reverse_charge") {
// customer = await stripe.customers.create({ email, tax_id_data: [{ type: "eu_vat", value: form.vatNumber }],
// metadata: { vat_checked_on: r.evidence.requestDate, vies_consultation: r.evidence.consultationNumber ?? "" } });
// }What to keep
Customer metadata is enough for the date and the consultation number; with record=true on a paid tier the check is also kept on your Veriport account.
| Field | Why |
|---|---|
valid | The VIES answer for the number on the day asked. |
requestDate, source.checkedAt | When VIES answered (live) or when the cached answer was obtained. |
source.kind, source.stale | live or cache; stale: true when the member state was down and a dated earlier answer was served. |
consultationNumber | The Commission’s reference for this check, when you sent requester (your own VAT number) on a paid tier. |
name, address | What the member state publishes; Germany and a few others publish neither. |
Questions
- Is Stripe’s verification wrong?
- No. It is asynchronous by design and retries through VIES outages. The question is timing: whether you need the answer before the payment, and a dated record of it, or whether the format check at payment is enough for your business.
- What about UK VAT numbers?
- Stripe verifies
gb_vatwith HMRC. Veriport checks EU numbers on VIES and Northern Ireland (XI) numbers; Great Britain numbers are not on VIES.
100 calls a month, no card; consultation numbers and kept records from $9.
Get a free keySources
- Stripe: customer tax IDs (eu_vat verified with VIES, asynchronous, customer.tax_id.updated, pending / verified / unverified / unavailable) — Stripe, consulted 2026-11-12
- Stripe Checkout: collect tax IDs (format checked during the session; validity verified after) — Stripe, consulted 2026-11-12
- VIES on-the-Web endpoint: OpenAPI contract (POST /check-vat-number, GET /check-status; requester fields for the consultation number) — European Commission, DG TAXUD, consulted 2026-11-12