{ "shopify" :

BTW-nummer valideren in Shopify B2B

Kort antwoord

Shopify laat je niet zomaar een live BTW-controle in de checkout hangen. Checkout UI extensions op de checkoutpagina zijn Plus-only en Shopify Functions hebben geen netwerktoegang, dus VIES kan er niet vanuit worden bevraagd. De werkbare route is: valideren vóór of ná de checkout via een app proxy, en het resultaat opslaan in een metafield.

app proxy
# winkel -> jouw app
GET "/apps/kvk/lookup?kvk=68750110"

# jouw app, server-side
curl ".../v1/validate/vat/NL12..B01" \
  -H "Authorization: Bearer KEY"

{
  "number": "NL123456789B01",
  "valid": true
}

Wat Shopify je laat scripten, en wat niet

Dit is de kern van elke Shopify-integratie met bedrijfsdata, en het wordt in bijna elke handleiding weggemoffeld: de checkout is dicht.

Wat je wilKan hetVoorwaarde
Veld op de checkoutpaginaJaCheckout UI extension, Shopify Plus
Externe API-call vanuit de checkoutJaZelfde extension, met network access
Externe API-call vanuit een FunctionNeeFunctions draaien zonder netwerk
Veld op cart- of accountpaginaJaGewoon thema, elk plan
Controle ná de orderJaWebhook op orders/create

Een cart validation of checkout validation Function is WebAssembly zonder netwerktoegang. Die kan dus nooit zelf VIES of ons endpoint bevragen. Wat hij wel kan: een metafield of cart attribute lezen dat jij eerder hebt gevuld en de checkout blokkeren als daar vatValid: false in staat. Dat is de enige manier om harde blokkering in de checkout te krijgen, en het vraagt dat de validatie al eerder heeft plaatsgevonden.

Waar de data hoort te staan

  • Cart attribute voor de lopende order. Verschijnt op de order als extra detail en gaat mee naar je fulfilment.
  • Customer metafield voor herhaalaankopen, bijvoorbeeld custom.kvk_number en custom.vat_valid_at. Zet er een timestamp bij, anders weet je over een jaar niet meer of die true nog iets betekent.
  • Company location als je B2B draait. Daar hoort het fiscale nummer thuis en daar hangt de btw-vrijstelling aan.
  • Order metafield voor het bewijs op het moment van bestellen. Voor e-facturering en een eventuele controle wil je vastleggen wat je toen zag, niet wat nu geldt.

De app proxy, concreet

Een app proxy geeft je een route onder je eigen winkeldomein die naar jouw server wordt doorgestuurd. Zo blijft de API-key server-side en heb je geen CORS-gedoe.

# shopify.app.toml
[app_proxy]
url = "https://jouw-app.example.com/proxy"
subpath = "kvk"
prefix = "apps"

De winkel roept /apps/kvk/lookup?kvk=68750110 aan, Shopify stuurt dat door naar jouw endpoint en hangt er query-parameters plus een signature aan. Verifieer die signature altijd met je app secret voordat je een lookup doet. Doe je dat niet, dan is je proxy een gratis doorgeefluik naar jouw quota voor iedereen die het pad kent.

Jouw handler doet daarna de echte call:

const r = await fetch(`https://api.kvkbase.nl/v1/lookup/${kvk}?enrich=true`, {
  headers: { Authorization: `Bearer ${process.env.KVKBASE_KEY}` },
});
if (r.status === 404) return json({ ok: false, reason: "not_found" }, 200);
const c = await r.json();
return json({
  ok: c.isActive,
  name: c.statutoryName ?? c.name,
  vat: c.vat?.number ?? null,
  vatValid: c.vat?.valid ?? null,
});

Let op de 200 bij een 404 van ons. Een front-end die op een niet-2xx reageert met een generieke foutmelding, vertelt de klant “er ging iets mis” terwijl het antwoord gewoon “dit nummer bestaat niet” is.

Wil je alleen invoergemak op de cart- of accountpagina zonder eigen app, dan kan de widget rechtstreeks in je thema. Die vult naam, adres en BTW-veld in op basis van een zoekopdracht, met een publieke key. Voor de validatie die telt blijf je bij de proxy.

Wat er in productie stukgaat

VIES is er niet. De BTW-controle gaat langs de lidstaatservice en die valt geregeld uit. Je krijgt UPSTREAM_ERROR in plaats van een oordeel. Als je daar hard op blokkeert, ligt je B2B-checkout plat zolang een buitenlandse overheidsdienst offline is. Sla unknown op, laat de order door met btw, en herbeoordeel in de webhook.

Een klant vult het BTW-nummer van de holding in. Bij een groep is de holding vaak wel bekend bij VIES en de bestellende werkmaatschappij niet, of andersom. Het nummer valideert, de bedrijfsnaam matcht niet met de verzendgegevens. Bewaar allebei en signaleer het verschil in plaats van blind te vertrouwen.

De backfill van bestaande klanten loopt vast. Duizenden klantrecords verrijken via losse calls raakt de rate limit binnen een minuut. Gebruik POST /v1/lookup/batch en verwerk in blokken, met een wachttijd tussen de blokken.

Cart attributes overleven een sessie niet altijd. Wist de klant zijn winkelwagen of komt hij terug via een nieuwe sessie, dan is je attribuut weg terwijl de klant denkt dat hij het al had ingevuld. Schrijf de validatie ook naar het customer metafield zodra de klant is ingelogd, en vul de cart daaruit.

Veelgestelde vragen

Hebben jullie een Shopify-app?
Nee. Wij leveren een REST API en een JavaScript-widget. De Shopify-kant bouw je zelf: een custom app met een app proxy, of een themasectie die de widget laadt. Voor één winkel is een custom app genoeg, die hoeft niet door de App Store review.
Kan ik het BTW-nummer in de checkout zelf controleren?
Alleen op Shopify Plus, met een checkout UI extension die netwerktoegang heeft aangevraagd. Op andere plannen kun je de checkoutpagina niet uitbreiden. Daar valideer je op de cart- of accountpagina en geef je het resultaat mee als cart attribute.
Waarom kan een Shopify Function dit niet doen?
Cart- en checkout-validation Functions draaien als WebAssembly zonder netwerktoegang. Ze kunnen geen externe API aanroepen. Ze kunnen wel een metafield of cart attribute lezen dat jij eerder hebt gevuld, en daarop blokkeren.
Werkt de reverse charge automatisch als het BTW-nummer geldig is?
Nee, dat regel je in Shopify zelf. Op B2B stel je de btw-vrijstelling in op de company location, buiten B2B via de tax settings van de klant. Onze API zegt alleen of het nummer bij VIES bekend en geldig is. De fiscale beslissing blijft van jou.

Bijgewerkt:

Eén call geeft je het hele bedrijf

KVK-data, het afgeleide BTW-nummer en een live VIES-controle in één request. Het gratis plan geeft je 50 lookups per maand, zonder creditcard.

50
gratis lookups per maand
1
request in plaats van drie
0
creditcards, contracten of salesgesprekken