ejosterberg/opensalestax-sylius
Sylius bundle that replaces Sylius's built-in tax calculation with destination-based US sales tax computed by a self-hosted OpenSalesTax engine.
Free, self-hostable, no per-transaction fees, no SaaS lock-in. The merchant runs both Sylius and the OpenSalesTax engine on their own infrastructure.
v0.1.0-alpha.1 — initial release. Single-line tax calculation
through a Sylius TaxCalculationStrategy, USD/US gating,
fail-soft engine error handling, optional per-state nexus filter.
composer require ejosterberg/opensalestax-sylius
Register the bundle in config/bundles.php:
return [
// ...existing bundles...
OpenSalesTax\Sylius\OpenSalesTaxSyliusBundle::class => ['all' => true],
];
Add a config file at config/packages/opensalestax_sylius.yaml:
opensalestax_sylius:
engine_url: '%env(OSTAX_ENGINE_URL)%' # e.g. http://10.32.161.126:8080
api_key: '%env(default::OSTAX_API_KEY)%'
timeout_seconds: 5.0
fail_hard: false
default_category: general
nexus_states: [] # e.g. ['MN', 'WI'] — empty = collect everywhere
cache_ttl_seconds: 3600
Then in Sylius admin (Configuration → Channels → <your channel> → Tax Calculation Strategy), select OpenSalesTax. Place a
test order shipping to a US ZIP — destination-based tax appears
on the order.
OstaxTaxationStrategy::applyTaxes() once per
order during checkout recalculation.OrderItemUnit, it asks OstaxCalculator for the
tax amount. The calculator gates on USD-only, US-only,
nexus-state filter, then calls the OST engine via
POST /v1/calculate.tax labeled
OpenSalesTax.Cache: each (zip, category, cents) tuple is memoized in Symfony's
cache.app PSR-6 pool for cache_ttl_seconds (default 1 hour).
| Order shape | What happens |
|---|---|
| USD, US ship-to, valid 5-digit ZIP, in-nexus | Engine called; tax adjustment added |
| Non-USD currency | 0.0 tax — Sylius's other strategies / built-in TaxRate rows handle it |
| Non-US ship-to country | 0.0 tax (constitution §5) |
| Missing or invalid ZIP | 0.0 tax |
nexus_states set + ship-to state not on list |
0.0 tax |
Engine unreachable, fail_hard: false (default) |
0.0 tax + warning logged |
Engine unreachable, fail_hard: true |
Throws — surfaces as a Sylius checkout error |
| Key | Type | Default | Description |
|---|---|---|---|
engine_url |
string | (required) | Base URL of the OST engine. Must be http:// or https://. |
api_key |
string|null | null |
Optional API key (sent as X-API-Key header). |
timeout_seconds |
float | 5.0 |
Per-request timeout. |
fail_hard |
bool | false |
When true, engine errors throw and surface as Sylius errors. Default: fail-soft (return 0, log warning). |
default_category |
enum | general |
OST category sent for line items with no per-product mapping. One of general, clothing, groceries, prescription_drugs, prepared_food, digital_goods. |
nexus_states |
string[] | [] |
Allowlist of US state codes (e.g. ['MN', 'WI']). Empty = collect everywhere. |
cache_ttl_seconds |
int | 3600 |
Cache TTL for engine results. 0 disables caching. |
| Sylius | OST engine | This bundle |
|---|---|---|
^1.13 |
v0.14+ (v1 API) |
0.1.x |
Dual-licensed under your choice of:
See LICENSE for the SPDX declaration. The dual
licensing exists to keep the OpenSalesTax portfolio's licensing
footprint consistent across ecosystems.
See CONTRIBUTING.md. DCO sign-off required on
every commit.
This bundle calculates US sales tax. It does not file returns, remit collected tax, or validate exemption certificates against state DOR systems. The merchant remains responsible for filing and remittance.
How can I help you explore Laravel packages today?