AstroBaaS

Tax & compliance

Reverse Charge

Paid pluginsize Mplanned, not built

Indicative price, not an offer: $199–499/year

Generated from docs/plan/paid/reverse-charge/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.

Merged from duplicate proposals: “Reverse Charge”, “Reverse Charge (Intra-EU VAT)”, “VAT ID and reverse-charge handling (VIES)”

A paid feature for B2B VAT compliance: validate customer VAT IDs via VIES, auto-detect reverse-charge scenarios, suppress VAT on qualifying invoices, and log all reverse-charge decisions for audit. Merchants avoid charging VAT to VAT-registered businesses in other EU countries.

The problem

A German business (VAT ID: DE123456789) buys from a Greek merchant. Under EU reverse-charge rules, the Greek merchant must NOT charge VAT—the German business self-assesses VAT in Germany instead. But the Greek merchant’s shop has no VAT ID field, no validation, no reverse-charge logic. She charges 24% VAT to the German buyer anyway, creating a fraudulent transaction.

What it does

  • Customer-level field: VAT ID (e.g., DE123456789, FR123456789)
  • VIES validation API: check if VAT ID is valid and active (calls European Commission’s VIES service)
  • Business-customer detection: if VAT ID is provided and valid, mark customer as ‘business’
  • Reverse-charge rule engine: if customer is business in different EU country, suppress VAT on checkout
  • Invoice display: if reverse-charge applied, show ‘Reverse charge applies’ and VAT = 0
  • Audit trail: which orders used reverse-charge, which VAT IDs were validated/rejected
  • Admin dashboard: search by VAT ID, see all orders with reverse-charge applied, validation failure log
  • VIES sync: cache VAT ID validation for 24 hours to avoid rate-limiting
  • Support dashboard: merchant can manually override reverse-charge (e.g., if VIES fails but VAT ID is known-good)

What it deliberately does NOT do

Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.

  • VAT ID format validation (e.g., ‘DE’ = 2 letters, then 9 digits)—only VIES validation counts as truth
  • Quarterly reconciliation (e.g., if a VAT ID was valid at checkout but later deactivated, we don’t re-charge)
  • Intra-Community supply rules (specific goods that are always/never reverse-charged)—merchant must categorize
  • Goods in transit or goods supplied on-ship (specialized rules beyond reverse-charge)
  • Accounting integration: we don’t auto-post to accounting software

Data model

Extend Customer: vat_id, vat_id_valid_at, is_business_customer; VIESValidation(vat_id, validation_result, validated_at, expiry_at, country_name); OrderReverseCharge(orderId, vat_id_used, vat_suppressed_amount_minor). Requires customer schema migration.

API

  • POST /customers/:id/validate-vat-id — call VIES, store result
  • GET /customers/:id/vat-info — return VAT ID, validation status, business customer flag
  • GET /orders/:id/reverse-charge — return VAT suppression details if applied
  • GET /vies-validation/history — list all VAT ID validations with results
  • POST /vies-validation/clear-cache — manual cache clear (emergency only)

Every route added here must also appear in src/pages/openapi.json.ts — a test fails the build if it does not.

Admin

Reverse-charge panel: customer list filtered by ‘business customers’, search by VAT ID; validation history (ID, status, validated_at, valid_until); order list with reverse-charge applied (show suppressed VAT amount); failed validations log (ID + reason); manual override button

The seam — why this is paid

Core owns: VAT ID field on customer, reverse-charge detection logic, invoice display. Paid pack owns: VIES validation credential and API calls, handling VIES failures gracefully, per-country override rules, support escalation.

Per-country EU obligation. Requires VIES API credential (Liveware/ViES lookup service). Support commitment includes VIES API failures, VAT format edge cases, and reverse-charge rule auditing.

Dependencies

  • customer schema (vat_id, is_business_customer fields must be added)
  • order schema (reverse_charge_applied boolean, suppressed_tax_minor)
  • destination-based-tax (must know customer country to trigger reverse-charge)
  • VIES API access (external credential; paid pack provides)

Acceptance checks

Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.

  • A VAT ID ‘DE123456789’ is validated via VIES; if valid, customer is marked is_business_customer=true
  • A valid German business customer (is_business_customer=true, destination_country=DE) ordering from Greece is charged VAT 0%
  • A valid German business customer ordering to Spain IS charged Spanish VAT (different country, not exempt)
  • An invalid or expired VAT ID (VIES returns ‘invalid’) stores validation failure, marks customer is_business_customer=false
  • An order with reverse-charge applied shows ‘Reverse charge VAT applied’ on invoice, VAT line item is 0.00
  • VIES validation result is cached for 24 hours; second validation of same ID within 24h does not call VIES again
  • If VIES API is down, merchant can manually mark customer as business (override), and reverse-charge applies
  • Audit log shows: VAT ID ‘DE123456789’ validated on 2025-09-02, expires 2025-09-03, order #12345 applied reverse-charge

Risks

VIES API failures (rate-limit, downtime) could break checkout—must have graceful fallback (manual override or charge VAT anyway). VAT ID format variations across countries (some have 9 digits, some 12)—validation against VIES is only reliable check. Reverse-charge logic edge cases: some goods are never reverse-charged regardless of customer type. Merchants misconfiguring rules and over-charging or under-charging.

Commercial context

Suggested price$199–499/year
Rival anchorMagento ships VAT ID field, VIES validation, and automatic customer-group assignment by VAT status free.

The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.


Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full catalogue and ../../AI-GUIDE.md for how to work on this repository.