Skip to content

ExciseEngine

ExciseEngine connects Vectis to the separate ExciseEngine excise-tax service for per-jurisdiction excise tax (vape, tobacco, and similar regulated industries). It calculates API-first against the Engine's GraphQL API, falls back to a locally-synced rate engine when the API is unavailable, supports seven rate-type formulas plus three additional compatibility rate types, and rounds with ROUND_HALF_EVEN banker's rounding.

At a glance

Extension id excise_engine
Category tax
Version 1.0.0
Provides Tax calculation strategy (stage 200), Temporal sync workflow + 24 h schedule, admin GraphQL queries/mutations, admin page + nav + quick actions, Cmd-K search indexes, extension-owned ee_* tables
External account ExciseEngine account — API URL, API key + secret, industry ID, ship-from location ID

What It Provides

  • Excise calculation strategy — a TaxCalculationStrategy registered under the name excise_engine at stage 200, so it runs after sales tax (stage 100, e.g. TaxJar) in the stage-sorted tax engine. Only lines whose product traits carry excise_engine_tax_class_slug (a legacy tax-class trait, exciseiq_tax_class_slug, is also honored) are taxed. Emits one excise-type tax line per jurisdiction component so the audit trail shows e.g. state and federal components separately.
  • API path — calls the Engine's calculateTax GraphQL mutation to get a quote; the quote id is cached in Valkey (ee:quote:{channel_id}:{account_id}, 30-minute TTL) so it can be finalized when the order is placed.
  • Local fallback engine — when the API is unavailable and Fallback on API Failure is on, tax is computed from locally-synced tables. Rate type formulas: ad_valorem, per_unit, minimum, compound, tiered, flat_cost_per_order, percentage_of_price_x_milliliters, plus the additional compatibility types ad_valorem_cost, flat_per_ml, and flat_per_mfg_weight_oz.
  • Order lifecycle handlers — event-bus subscriptions (priority 70): order.placed finalizes the cached quote into an Engine transaction (finalizeQuote), order.status_changed to cancelled cancels it (cancelTransaction), and order.modified rebuilds and resubmits the cart (amendTransaction). The Engine transaction id is stored on the order's metadata under ee_transaction_id.
  • Temporal workflow + scheduleSyncExciseEngineDataWorkflow runs the sync_excise_engine_data activity on the registered schedule vectis-sync-excise-engine-data (every 24 hours), pulling locations, tax classes, tax regions, and tax rates from the Engine into the local cache tables.
  • Admin GraphQL — queries exciseEngineTaxClasses, exciseEngineLocations, exciseEngineTaxRegions, exciseEngineSyncLogs; mutations exciseEngineTestConnection, exciseEngineTriggerSync, and exciseEngineCalculateTax (an ad-hoc spot-check calculator). The mutations require the settings.edit permission.
  • Admin UI — a nav item ExciseEngine in the Tax section linking to /extensions/excise_engine, with tabs Configuration, Overview, Tax Classes, Locations, Tax Regions, Sync Log, and Calculator. Six Cmd-K quick actions (Sync Now, Calculator, Tax Classes, Tax Regions, Sync Log, Settings) link to the page and its tabs. Products get an Excise Taxes tab panel for assigning tax classes and unit attributes.
  • Cmd-K search indexes — federated search over synced tax classes, tax regions, and locations (handy for verifying state coverage).
  • Extension-owned tablesee_tax_classes, ee_locations, ee_tax_regions, ee_tax_rates, and ee_sync_log, managed by the extension's own migration chain (the ee_* names are grandfathered — new extensions must use the ext_<name>_ prefix).

Setup

  1. Enable the extension for your channel under Settings > Extensions.
  2. Open Tax > ExciseEngine in the admin nav and switch to the Configuration tab. Enter the API URL, API Key, API Secret, Industry ID, and Ship-From Location ID (plus Store ID for multi-store Engine accounts).
  3. Test the connection — exciseEngineTestConnection verifies the credentials by fetching your industry's tax classes.
  4. Run an initial sync — the Sync Now button on the extension page (the Sync ExciseEngine Now quick action jumps there), or the exciseEngineTriggerSync mutation — to populate locations, tax classes, regions, and rates. Check progress on the Sync Log tab.
  5. Assign an excise tax class to each taxable product on its Excise Taxes tab — the strategy only taxes lines whose product traits carry excise_engine_tax_class_slug.

Configuration Reference

Key Type Required Secret Description
api_url string Yes No ExciseEngine GraphQL API base URL (e.g. https://api.eiq.example.com)
api_key string Yes Yes ExciseEngine API key for Bearer authentication
api_secret string Yes Yes ExciseEngine API secret for Bearer authentication
industry_id string Yes No UUID of your industry in ExciseEngine (e.g. vape, tobacco)
ship_from_location_id string Yes No ExciseEngine UUID of your warehouse / origin location
store_id string No No Optional ExciseEngine Store UUID (for multi-store accounts)
calculate_tax_by enum (shipping, billing) No No Address to use for jurisdiction resolution; default shipping. billing requires the tax engine to pass billing address (pending core support)
fallback_on_api_failure boolean No No Use locally-synced rates when the API is unavailable (default true)
sync_schedule enum (hourly, daily, weekly) No No How often to sync tax data from ExciseEngine (default daily)
debug_logging boolean No No Enable verbose logging for troubleshooting (default false)
tax_scope enum (product, variant) No No Whether excise tax fields are configured per product (all variants share one set) or per variant (default product)
use_core_cost_field boolean No No Use the product's built-in cost field for cost-based tax calculations; disable to show a separate cost field on each product's Excise Taxes tab (default true)

Secret fields are stored Fernet-encrypted and read back through the typed config accessor that decrypts them; they are masked in the admin.

Permissions

  • tax.excise_engine.manage — also gates all six Cmd-K quick actions.

Permissions are upserted at boot from the manifest; assigning them to roles is currently a manual step.

Operational Notes

  • Auth format — the client authenticates with Authorization: Bearer {api_key}:{api_secret} against {api_url}/graphql, with a 15 s request timeout.
  • Rate limiting — on HTTP 429 the client records the Retry-After window (default 60 s) and short-circuits further calls until it elapses.
  • Fail-soft calculation — API errors are logged and the strategy falls back to the local engine when Fallback on API Failure is enabled; with it disabled, no excise lines are emitted for that calculation.
  • Rounding — both the API and fallback paths round amounts to 4 decimal places with ROUND_HALF_EVEN.
  • Quote TTL — the cached quote expires after 30 minutes. If the order is placed after expiry, the order.placed handler finds no quote to finalize and no Engine transaction is created for that order.
  • Sync interval is fixed — the Temporal schedule registered by the extension runs every 24 hours; the sync_schedule setting does not change the registered interval. The manual sync mutation falls back to running the sync activity in-process when Temporal is unreachable.
  • Fallback region matching — supports exclusion zones (is_exclude regions remove matching locations from the positive set), postal-code-range regions for jurisdictions finer than the Engine's location tree, and per-customer-group rates: a region with a customer_group only matches accounts in that group (matched by CustomerGroup slug); regions with no group match everyone.
  • Location resolution — the shipping address resolves to an Engine location by county name first, then by state ISO code (US-<state>); the fallback engine walks the location's parent chain to collect all applicable regions.

For the admin spot-check tool, see the Excise Tax Calculator; for the usage-level overview, Tax; for the developer view, Building Extensions.