ejosterberg / module-opensalestax
Magento 2 module — destination-based US sales tax via the self-hosted OpenSalesTax engine
Package info
github.com/ejosterberg/opensalestax-magento
Type:magento2-module
pkg:composer/ejosterberg/module-opensalestax
Requires
- php: ^8.1
- psr/log: ^1.0 || ^2.0 || ^3.0
Requires (Dev)
- bitexpert/phpstan-magento: ^0.31
- magento/magento-coding-standard: ^32.0
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.5
- dev-main
- v1.3.13
- v1.3.12
- v1.3.11
- v1.3.10
- v1.3.9
- v1.3.8
- v1.3.7
- v1.3.6
- v1.3.5
- v1.3.4
- v1.3.3
- v1.3.2
- v1.3.1
- v1.3.0
- v1.2.0
- v1.1.0
- v1.0.0
- v0.1.0
- dev-dependabot/composer/magento/magento-coding-standard-tw-40
- dev-dependabot/github_actions/actions/cache-5
- dev-dependabot/github_actions/actions/upload-artifact-7
- dev-dependabot/github_actions/actions/setup-node-6
- dev-dependabot/github_actions/actions/checkout-6
This package is auto-updated.
Last update: 2026-05-19 18:47:01 UTC
README
v0.1.0 alpha. Installable; passes its unit tests; not yet validated against a real Magento storefront. See
kickoff/andspecs/for the build plan.
A free, self-hostable Magento 2 module that swaps Magento's built-in tax calculator for the OpenSalesTax engine on US-destination, USD checkouts. No per-transaction fees, no SaaS lock-in — merchants run both Magento and OpenSalesTax on their own infrastructure.
What this module does
- Hooks
Magento\Tax\Model\Calculation::getRatevia a plugin (seespecs/decisions/001-tax-extension-point.md) to substitute Magento's tax-table rate with the rate computed by your OpenSalesTax engine for the customer's destination. - Hooks
Magento\Quote\Model\Quote\Address\Total\Tax::collectto surface per-jurisdiction tax breakdown ("Minnesota State Sales Tax", "Hennepin County Tax", ...) in the cart and order summary screens. - Falls back to Magento's built-in tax tables on non-US destinations, non-USD currencies, or any engine error (default fail-soft behavior).
What this module does NOT do
- File or remit tax (calculation only — the merchant remits)
- Validate addresses
- Handle non-USD currencies or non-US destinations (passes those through to Magento)
- Validate tax-exempt customer certificates against state DORs
- Ship with the engine bundled — point it at your own OpenSalesTax engine
Requirements
- Magento 2
^2.4.6(PHP 8.1+) - A reachable OpenSalesTax engine instance (v0.22.0 or later)
Install
composer require ejosterberg/module-opensalestax bin/magento module:enable EJOsterberg_OpenSalesTax bin/magento setup:upgrade bin/magento setup:di:compile bin/magento cache:clean
Configure
Stores → Configuration → Sales → Tax → OpenSalesTax
| Field | Path | Default | Purpose |
|---|---|---|---|
| Engine API URL | osstax/general/api_url |
(empty) | Base URL of your OST engine, e.g. https://ost.example.com |
| API Token (optional) | osstax/general/api_token |
(empty) | Bearer token if your engine requires authentication. Stored encrypted in core_config_data. |
| Fail Hard on Engine Error | osstax/general/fail_hard |
No | If Yes, an unreachable engine blocks checkout. If No, the module falls back to Magento's tax tables and logs a warning. |
While api_url is empty the module is inert — Magento's built-in tax calc handles everything.
How it works
- At checkout, Magento builds the cart totals. The totals pipeline reaches
Magento\Quote\Model\Quote\Address\Total\Tax::collect. - Our plugin's
beforeCollectchecks the gate (configured? USD? shipping to US?). If all three are yes, it builds an OST engine payload from the quote and callsPOST /v1/calculate. - The engine returns per-line tax + per-jurisdiction breakdown. We cache this in a request-scoped registry keyed by quote id.
- Magento then asks
Calculation::getRatefor the rate to apply per line. Our plugin reads from the registry and returns the OST-derived effective rate, which Magento applies in the usual way. - Our
afterCollectwrites the per-jurisdiction breakdown onto the totals object so cart and order screens display individual jurisdictions instead of a single opaque tax line.
If any check fails (non-US, non-USD, engine down without fail-hard), control returns silently to Magento's built-in tax calc.
Logging
All engine interactions log structured metadata (quote id, line count, HTTP status, RTT in milliseconds) via Magento's Psr\Log\LoggerInterface. Customer addresses and full payloads are never logged. The API token is decrypted in memory only at request time and never written to logs.
Development
composer install
composer check # runs phpunit + phpstan + phpcs + composer audit
See CONTRIBUTING.md for branch model, DCO sign-off, and the quality gate.
License
Dual-licensed under your choice of Apache-2.0 OR GPL-2.0-or-later. See LICENSE.