DEV Community

RevClamp Enterprise
RevClamp Enterprise

Posted on Originally published at revclamp.com

Why EU B2B Checkouts Falsely Charge 17%-27% VAT When National VIES Databases Are Busy (The HTTP 200 OK Soft-Failure Trap)

When an enterprise buyer in Germany or Italy enters a valid VAT number at B2B checkout, they expect a 0% intra-community reverse-charge invoice once cross-border supply conditions (sellerCountry !== buyerCountry) are satisfied.

Yet one of the most common distributed-systems bugs in European B2B commerce happens inside the European Commission's VIES REST API (POST https://ec.europa.eu/taxation_customs/vies/rest-api/check-vat-number). When a Member State's national tax database hits its concurrency limit or undergoes maintenance, VIES returns HTTP 200 OK with actionSucceed: false and an errorWrappers array:

{
  "actionSucceed": false,
  "errorWrappers": [
    {
      "error": "MS_MAX_CONCURRENT_REQ"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Because the HTTP status is 200, standard HTTP retry middleware never triggers. And because data.valid is omitted (undefined) when actionSucceed is false (or mapped to valid: false in naive wrappers), any checkout handler that checks if (!res.ok || !data.valid) misclassifies a temporary government database outage as an unregistered VAT ID—and falsely charges 17%–27% VAT.

1. Classifying VIES Soft-Failures vs. Permanent Rejections

A production VIES integration must inspect errorWrappers[0].error before evaluating data.valid:

VIES Response Class Exact Error Codes / Payload Condition Checkout & Fulfillment Action
TRANSIENT_VIES_ERRORS MS_UNAVAILABLE (Member State Service Unavailable), MS_MAX_CONCURRENT_REQ (Member State Maximum Concurrent Requests), GLOBAL_MAX_CONCURRENT_REQ (Global Maximum Concurrent Requests), SERVICE_UNAVAILABLE, TIMEOUT, SERVER_BUSY, NETWORK_ERROR Never block checkout (checkoutBlocked: false); place order in PENDING_ASYNC_RETRY (fulfillmentHold: true) with exponential backoff 60 * 2 ** (attempt - 1) up to MAX_VIES_ATTEMPTS = 4
PERMANENT_VIES_ERRORS INVALID_INPUT, INVALID_REQUESTER_INFO (Invalid Requester Information — seller requester VAT configuration error), VAT_BLOCKED Place order in HOLD_INVALID_VAT (vat_audit:invalid_vat_id); flag INVALID_REQUESTER_INFO as SELLER_REQUESTER_CONFIG_ERROR
Unregistered VAT valid: false with empty errorWrappers Place order in HOLD_INVALID_VAT so finance can request a valid VAT ID or collect standard VAT
Verified + Consultation ID valid: true + non-empty requestIdentifier (consultationNumber) Approve VERIFIED_ZERO_RATED (fulfillmentHold: false) and archive SHA-256 consultation digest

2. Prefix Normalization (Greece GR to EL, Northern Ireland XI Goods, Great Britain GB Exclusion) & 3-State Circuit Breaker

Before calling VIES, normalize ISO country prefixes: Greece uses ISO-3166 GR in billing forms, but VIES strictly requires EL (GR123456789 to EL123456789). Under the Windsor Framework, Northern Ireland B2B goods transactions use the XI prefix (27 EU Member States + XI = 28 VIES prefixes), whereas post-Brexit GB VAT numbers are excluded from VIES.

const TRANSIENT_VIES_ERRORS = new Set([
  'MS_UNAVAILABLE',
  'SERVICE_UNAVAILABLE',
  'MS_MAX_CONCURRENT_REQ',
  'GLOBAL_MAX_CONCURRENT_REQ',
  'TIMEOUT',
  'SERVER_BUSY',
  'NETWORK_ERROR',
]);

const PERMANENT_VIES_ERRORS = new Set([
  'INVALID_INPUT',
  'INVALID_REQUESTER_INFO',
  'VAT_BLOCKED',
]);

const MAX_VIES_ATTEMPTS = 4; // 1 initial + up to 3 async retries

export function evaluateViesConsultation({ data, rawCc, attempt = 1 }) {
  const normCc = rawCc === 'GR' ? 'EL' : rawCc; // NI B2B goods use 'XI'; 'GB' is non-VIES
  const errCode = data?.errorWrappers?.[0]?.error ?? '';

  if (PERMANENT_VIES_ERRORS.has(errCode)) {
    return { checkoutBlocked: false, fulfillmentHold: true, status: 'HOLD_INVALID_VAT', error: errCode, normCc };
  }
  if (!data || TRANSIENT_VIES_ERRORS.has(errCode) || typeof data.valid !== 'boolean') {
    if (attempt >= MAX_VIES_ATTEMPTS) {
      return { checkoutBlocked: false, fulfillmentHold: true, status: 'HOLD_MANUAL_REVIEW_RETRY_EXHAUSTED', attempt };
    }
    return {
      checkoutBlocked: false,
      fulfillmentHold: true,
      status: 'PENDING_ASYNC_RETRY',
      backoffSec: Math.min(3600, 60 * 2 ** (attempt - 1)),
    };
  }
  if (data.valid === true) {
    const consultationId = String(data.requestIdentifier || data.consultationNumber || '').trim();
    return consultationId
      ? { checkoutBlocked: false, fulfillmentHold: false, status: 'VERIFIED_ZERO_RATED', consultationId }
      : { checkoutBlocked: false, fulfillmentHold: true, status: 'VERIFIED_MISSING_CONSULTATION_ID' };
  }
  return { checkoutBlocked: false, fulfillmentHold: true, status: 'HOLD_INVALID_VAT' };
}
Enter fullscreen mode Exit fullscreen mode

3. Why requestIdentifier Requires Seller Requester Credentials

To substantiate 0% intra-community supply during a tax audit, recording valid: true is not enough: you need the official VIES requestIdentifier (consultationNumber). VIES only populates requestIdentifier when your check-vat-number payload includes requesterMemberStateCode and requesterNumber (the seller's own EU/XI VAT ID). If valid === true returns without requestIdentifier, our state machine routes the order to VERIFIED_MISSING_CONSULTATION_ID until a full consultation record (timestamp + requester/buyer VAT + SHA-256 response digest) is archived.

Scope note: This post describes distributed-systems engineering patterns for VIES API resilience and audit logging; it is not tax or legal advice.

Top comments (0)