Tax Service

tax-service is a Quarkus service that owns tax zone configuration, rate management, and tax calculation for ShopSTAR3. It is stateless at calculation time — it does not store applied taxes. The final tax breakdown is persisted by order-service on the order record. External provider credentials are stored in Vault.

Provider Abstraction#

All tax calculation — whether built-in or delegated to an external engine — goes through a single TaxProvider interface:

interface TaxProvider {
    TaxCalculationResult calculate(TaxCalculationRequest request);
}

Built-in Adapters#

AdapterNotes
BuiltInTaxProviderEvaluates zone and rate configuration stored in the database
AvalaraTaxProviderDelegates to Avalara AvaTax REST API
TaxJarProviderDelegates to TaxJar SmartCalcs REST API

The active provider per store is set in tax_provider_configs. A store using an external provider still stores its zone and rate config in the database as a fallback reference, but the calculation result comes from the external engine.

Product Tax Classes#

Each catalog product carries a taxClass field that tax-service uses to select the correct rate within a matched zone.

Tax ClassTypical Use
STANDARDDefault — most physical goods
REDUCEDReduced rate goods — food, books (jurisdiction-dependent)
ZERO_RATEDExports and specific exempt categories; tax line shows 0%
EXEMPTNo tax applied — medical goods, charity items

Shipping can be assigned a tax class by the store admin (STANDARD or EXEMPT depending on the jurisdiction).

Data Model#

tax_zoneszone_id UUID PK, store_id, name, country_code CHAR(2), state_code (nullable), postal_pattern (nullable, supports wildcards), priority INT (higher wins when multiple zones match), is_active.

tax_ratesrate_id UUID PK, zone_id FK, store_id, tax_class VARCHAR (STANDARD / REDUCED / ZERO_RATED / EXEMPT), rate NUMERIC (e.g. 0.1000 = 10%), name (e.g. “GST”, “VAT”), is_compound BOOLEAN (stacked jurisdictions), is_inclusive BOOLEAN (price already includes tax), is_active.

tax_provider_configsconfig_id UUID PK, store_id, provider_type (BUILTIN / AVALARA / TAXJAR), is_active, settings JSONB (non-sensitive: company code, environment, commit mode).

Compound rates#

A zone can carry multiple rates with is_compound = true to model stacked tax jurisdictions (e.g. federal tax applied first, then state tax applied on the tax-inclusive subtotal). Rates within a zone are evaluated in priority order.

Inclusive tax#

When is_inclusive = true, the displayed price already contains tax. The service extracts the tax portion as: tax = price − (price ÷ (1 + rate)). The line item amount returned to checkout-service remains unchanged; only the tax breakdown is updated.

Customer Exemptions#

Customer-level tax exemptions (e.g. reseller certificates) are stored in customer-service. checkout-service enriches the TaxCalculationRequest with an is_tax_exempt flag. When the flag is true, tax-service returns zero tax for all line items and shipping regardless of zone/rate configuration.

Calculation Flow#

checkout-service calls CalculateTax during the order summary step. The sequence for the built-in provider:

  1. Resolve the matching zone for the shipping address (most specific postal pattern wins).
  2. If is_tax_exempt, return all-zero response immediately.
  3. For each line item, look up the rate for (zone_id, taxClass). Apply inclusive or exclusive calculation. Stack compound rates sequentially.
  4. Apply the same logic to the shipping line using the store’s configured shipping tax class.
  5. Return the full breakdown: per-line tax amounts, shipping tax, total tax, and a rate breakdown array (name, rate, amount) for display.

gRPC Interface#

MethodCallerPurpose
CalculateTax(TaxRequest)checkout-serviceReturn line-level and order-level tax breakdown
message TaxRequest {
  string store_id           = 1;
  Address shipping_addr     = 2;
  Address billing_addr      = 3;
  bool is_tax_exempt        = 4;
  repeated TaxLineItem items = 5;
  string shipping_amount    = 6;
  string shipping_tax_class = 7;
}

Kafka Topics#

tax-service does not produce or consume Kafka events. It is a synchronous, stateless calculation service.

Vault Configuration#

Key pathPurpose
ss3/kv/tax-service/db.urlPostgreSQL JDBC URL
ss3/kv/tax-service/db.usernameDatabase username
ss3/kv/tax-service/db.passwordDatabase password
ss3/kv/tax-service/avalara.account-idAvalara account ID
ss3/kv/tax-service/avalara.license-keyAvalara license key
ss3/kv/tax-service/taxjar.tokenTaxJar API token
ss3/kv/shared/Shared OTel config