Context
Wave 10 put aTaxProvider port in @stackra/pricing/contracts, a rate-table engine (taxByRates) and a memory
driver; the US sales-tax provider driver was not built, and the row was marked “new, not filed”. A container port is
bound once per deployment, but which tax engine serves a sale depends on the market and, under “managed or bring your
own”, on the tenant: the 2026-10-26 rounds set Stripe Tax as the managed default and Avalara as an add-on, with a
tenant’s own account allowed (US-46, M3-4, R3-SO-19).
Decision
- Home.
TaxProviderstays in@stackra/pricing/contracts, because tax is computed on the Market and pricing owns Markets (0244). No new package and no separate tax package. - Two ways to answer. A market whose own tables suffice (VAT and GST markets) answers with
taxByRatesover the pack’s rate table. A market that needs a provider (US sales tax) answers through a provider. Which one is pack data:market-pack:<code>.tax.engine(ratesorprovider). - Providers are Connections. A tax provider is an Integration whose Connection implements a
TaxGatewayinterface (quote, and record and void a finalised transaction where the provider keeps one), in@stackra/pricing/contracts. The productionTaxProviderdriver resolves the Connection throughProviderResolverunder thetaxcapability, so a tenant’s own account wins and the managed Connection serves otherwise. Provider drivers live with the integrations connectors, not in pricing. - First providers. Stripe Tax is the managed default where a pack’s engine is
provider; Avalara is an add-on and an own-account option. Both are pack and catalogue data (market-pack:us.providers.tax,provider-category:tax), never code. - Snapshot on the document. The quote, the provider and its transaction reference are stored on the order or invoice, so a reprint or a refund never recomputes tax with today’s rates.
- A missing rate is an error. As built: a line whose class has no rate refuses; it is never taxed at zero.
- Values. Pack data: rate tables, tax engine per market, tax providers, registration thresholds. Catalogue data:
provider-category:taxmodes and the add-on price. Settings: the tenant’s tax registrations and product tax classes. Rule sets: tax per line exceptions (config-model section 4). Code: the port,taxByRatesand the drivers.
Consequences
- A VAT market needs no provider and no network call to price a sale.
- A US tenant can bring its own Avalara account without a release.
- Finalising and refunding an invoice in a provider market adds a provider call, which the invoicing workflow makes.
Enforcement
planned —@stackra/pricing tests: the driver picks taxByRates or the resolved Connection from pack data only, and a
missing rate refuses.
Open questions
- Record and void timing. Whether recording happens at invoice finalisation or at payment capture is the OS invoicing workflow’s choice and is not decided here.
Supersedes and sources
Supersedes: none. Sources:notes/handover/build-wave10-2026-10-25.md (commerce primitives builder, not built item 2,
question 2); notes/handover/review-round-3/stackra-os.md (R3-SO-19: pricing question 2);
packages/commerce/pricing/src/contracts/ports/tax-provider.port.ts.
Amendment 2026-10-26: decisions 3 and 4, US tax providers (STA-1280, 0443)
- Decision 3. “Provider drivers live with the integrations connectors” means the OS integrations service: the
Stripe Tax, Avalara and TaxJar Integrations are OS code.
TaxProvider,TaxGatewayandtaxByRatesstay in@stackra/pricing/contracts(owner R3-8). - Decision 4. The owner confirmed decision 4 on 2026-10-26 (R3-3), which overrides R2-12 for US tax: Stripe Tax is
the managed default where a pack’s engine is
provider, and Avalara and TaxJar are own-account connectors (TaxJar is added; Avalara is an own-account connector rather than an add-on on Figentra’s account). Both stay pack and catalogue data (market-pack:us.providers.tax,provider-category:tax).