Validation Engine

Validation Pipeline

A 13-step request processing pipeline with 4-layer abuse protection, intelligent caching, circuit breaker pattern, and dual counter system for accurate billing.

1
13-Step Request Flow
Every validation request traverses this pipeline from ingress to response
Request Arrives
1

IP Throttle

App Proxy endpoints only. Limits to 5 requests/min per IP per shop. In-memory Map storage.

Blocked if exceeded
2

Shop Rate Limit

Plan-based limits: Free=10/min, Starter=30/min, Pro=60/min, Enterprise=300/min.

Blocked if exceeded
3

Format Validation

Regex pattern match against 28 country formats. Invalid format returns 200 response immediately. No counters are incremented.

Early return if invalid
4

Dedup Check

Same VAT number submitted within 60 seconds returns cached result. No counters increment, no DB or VIES query.

Dedup 60s window
5

Supabase Cache Check

Query vat_cache table for existing validation result. 24-hour TTL on cached entries.

Cache lookup
6

Cache Hit Path

If cache hit: increment monthly_validations++ and return cached result. VIES is never called. Quota is preserved.

Return cached
7

VIES Quota Check

Verify monthly_vies_calls < plan limit. Free=50, Starter=500, Pro=5000, Enterprise=unlimited.

Blocked if over quota
8

Circuit Breaker Check

If circuit is OPEN (5 consecutive VIES failures), skip VIES call and return format_only result.

format_only if open
9

VIES API Call

HTTP XML (SOAP) request to EU VIES service. 10s timeout, exponential backoff retry (2s, 4s, 8s).

External API
10

Cache Update

UPSERT VIES response into vat_cache table with current timestamp. 24h TTL begins.

Cache write
11

VIES Counter Increment

monthly_vies_calls++ is incremented ONLY for real VIES API calls. Cache hits do not affect this counter.

Billing counter
12

Validation Counter + Log

monthly_validations++ incremented and validation result logged for analytics and dashboard reporting.

Analytics counter
13

Return Result

Final validation response returned to the client with status, company name, address, and validation metadata.

Response sent
2
Format Validator
27 EU countries + XI (Northern Ireland) with regex pattern matching
AT ^ATU\d{8}$
BE ^BE0\d{9}$
BG ^BG\d{9,10}$
CY ^CY\d{8}[A-Z]$
CZ ^CZ\d{8,10}$
DE ^DE\d{9}$
DK ^DK\d{8}$
EE ^EE\d{9}$
EL ^EL\d{9}$
ES ^ES[A-Z0-9]\d{7}[A-Z0-9]$
FI ^FI\d{8}$
FR ^FR[A-Z0-9]{2}\d{9}$
HR ^HR\d{11}$
HU ^HU\d{8}$
IE ^IE(\d{7}[A-Z]{1,2}|\d[A-Z]\d{5}[A-Z])$
IT ^IT\d{11}$
LT ^LT(\d{9}|\d{12})$
LU ^LU\d{8}$
LV ^LV\d{11}$
MT ^MT\d{8}$
NL ^NL\d{9}B\d{2}$
PL ^PL\d{10}$
PT ^PT\d{9}$
RO ^RO\d{2,10}$
SE ^SE\d{12}$
SI ^SI\d{8}$
SK ^SK\d{10}$
XI ^XI(\d{9}|\d{12}|GD\d{3}|HA\d{3})$

Input Normalization

1
Convert to UPPERCASE
2
Remove spaces
3
Remove dashes (-)
4
Remove dots (.)
3
VIES API Client
Lightweight HTTP XML client with retry logic and exponential backoff
Endpoint
https://ec.europa.eu/taxation_customs/vies/services/checkVatService
Protocol
HTTP XML (SOAP)
Timeout
10 seconds
Retry Strategy
Exponential Backoff: 2s, 4s, 8s
Dependencies
None (no soap package)
Error Code Retry? Description
INVALID_INPUT No Valid VIES response indicating invalid VAT number
GLOBAL_MAX_CONCURRENT_REQ Yes VIES global rate limit reached
MS_MAX_CONCURRENT_REQ Yes Member state service is busy
SERVICE_UNAVAILABLE Yes VIES service temporarily unavailable
MS_UNAVAILABLE Yes Member state service is down
TIMEOUT Yes Request timed out waiting for response
4
Abuse Protection
4 defensive layers + circuit breaker pattern for resilient validation
1
IP Throttling
  • Scope: App Proxy endpoints only
  • Limit: 5 req/min/IP/shop
  • Storage: In-memory Map
  • Key format: ${shopDomain}:${ip}
2
Shop Rate Limit
  • Scope: All validation endpoints
  • Free: 10 req/min
  • Starter: 30 req/min
  • Pro: 60 req/min
  • Enterprise: 300 req/min
  • Storage: In-memory Map
  • Key format: shopDomain
3
VIES Quota
  • Monthly VIES call limits by plan
  • Free: 50 / Starter: 500
  • Pro: 5,000 / Enterprise: unlimited
  • Stored in Supabase shops.monthly_vies_calls
  • Auto-reset on month boundary (calendar month, UTC)
4
Dedup Cache
  • In-memory cache, 60 second TTL
  • Key format: ${shopDomain}:${normalizedVat}
  • Hit: no counters increment
  • Hit: no DB or VIES query executed
  • Protects against rapid repeat submissions
CB
Circuit Breaker Pattern
Prevents cascading failures when VIES is unstable
CLOSED
Normal operation
All VIES calls allowed
5 consecutive failures
OPEN
VIES blocked for 60s
Returns format_only
After 60 seconds
HALF_OPEN
Allow 1 test request
Success → CLOSED
Errors That Trip Breaker
  • TIMEOUT
  • SERVICE_UNAVAILABLE
  • MS_UNAVAILABLE
  • GLOBAL_MAX_CONCURRENT_REQ
Errors That Do NOT Count
  • INVALID_INPUT — valid VIES response, service is healthy
5
Dual Counter System
Separate counters for analytics and billing ensure accurate quota tracking

monthly_validations

Incremented on every successful validation request, including cache hits. Tracks total volume of validation activity across all sources.
Purpose: Analytics and dashboard reporting

monthly_vies_calls

Incremented ONLY when a real VIES API call is made. Cache hits, format-only results, and dedup hits do NOT increment this counter.
Purpose: Billing enforcement and quota management
6
Attack Scenarios
How the pipeline handles common abuse patterns
Bot sends 10,000 fake VATs
Layer 1: IP throttle (5/min) + Layer 2: shop limit (free=10/min)
Same VAT sent 100 times
Layer 4: Dedup 60s window, 99 requests served free from memory
50 different valid VATs
Layer 3: monthly_vies_calls hits 50 limit on free plan, quota exhausted
VIES down, continuous retry
Circuit breaker: 5 errors triggers OPEN state, 60s VIES block
Cache hits consume quota
Smart quota: cache hits increment monthly_validations only, not VIES calls
Distributed bot (multiple IPs)
Layer 2: shop-based rate limit catches all traffic regardless of source IP
Format-invalid spam flood
Step 3: Format validation fails early, no counters increment at all
7
Cache Layer
Supabase-backed persistent cache with automatic cleanup
Storage
Supabase vat_cache
TTL
24 Hours
Write Strategy
UPSERT on vat_number
Cleanup
pg_cron daily @ 03:00 UTC
Cache hit skips VIES entirely, preserving monthly VIES quota for new validations
8
Orchestrator: validateVat()
The core function that coordinates the entire validation pipeline
1
Format Validation
Normalize input (uppercase, strip spaces/dashes/dots), extract country code, match against regex pattern for the country.
Invalid format → return format_only
Valid → continue
2
Check Cache
Query Supabase vat_cache for an existing result. If found and within 24h TTL, return the cached validation result immediately.
Cache hit → return cached result
Cache miss → continue
3
Skip VIES Check
Evaluate whether VIES should be skipped: quota exceeded, circuit breaker OPEN, or shop settings disable live validation.
Skip VIES → return format_only
Proceed → continue
4
Call VIES API
Execute the VIES SOAP request with 10s timeout and exponential backoff retry. On success, UPSERT the result into cache and return.
Success → cache + return result
Failure → continue to error
5
VIES Failed
All retries exhausted. Update circuit breaker failure count. Return error response with appropriate error code and user-facing message.
Return error response