PHPackages                             ejosterberg/opensalestax-drupal-commerce - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Validation &amp; Sanitization](/categories/validation)
4. /
5. ejosterberg/opensalestax-drupal-commerce

ActiveDrupal-module[Validation &amp; Sanitization](/categories/validation)

ejosterberg/opensalestax-drupal-commerce
========================================

Drupal Commerce 3.x tax type plugin that calculates destination-based US sales tax via a self-hosted OpenSalesTax engine.

v0.1.1(3w ago)00Apache-2.0PHPPHP &gt;=8.2CI passing

Since May 14Pushed 3w agoCompare

[ Source](https://github.com/ejosterberg/opensalestax-drupal-commerce)[ Packagist](https://packagist.org/packages/ejosterberg/opensalestax-drupal-commerce)[ Docs](https://github.com/ejosterberg/opensalestax-drupal-commerce)[ RSS](/packages/ejosterberg-opensalestax-drupal-commerce/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (3)Dependencies (4)Versions (4)Used By (0)

OpenSalesTax for Drupal Commerce
================================

[](#opensalestax-for-drupal-commerce)

> **v0.1.1.** Live-validated against Drupal 11 + Drupal Commerce 3.3.5 on PHP 8.4 ($100 / MN ZIP 55401 → 6 per-jurisdiction adjustments totalling $9.03). Passes 56 unit tests on PHP 8.2–8.4; PHPStan level max clean; composer audit clean. CI green on `main`.

A free, self-hostable Drupal Commerce 3.x tax type plugin that swaps manual tax-rate tables for destination-based US sales tax via the [OpenSalesTax engine](https://github.com/ejosterberg/opensalestax). No per-transaction fees, no SaaS lock-in — merchants run both Drupal Commerce and OpenSalesTax on their own infrastructure.

> **Tax calculations are provided as-is for convenience. The merchant is solely responsible for tax-collection accuracy and remittance to the appropriate jurisdictions. Verify against your state Department of Revenue before remitting.**

What this module does
---------------------

[](#what-this-module-does)

- Registers OpenSalesTax as a Drupal Commerce **Tax Type** plugin (`@CommerceTaxType(id = "opensalestax")`). Drupal Commerce auto-discovers it once the module is enabled.
- When a US/USD order with a 5-digit shipping ZIP reaches the tax pipeline, the plugin calls `POST /v1/calculate` on your engine and writes one tax adjustment per jurisdiction onto the order (so the cart and order screens render "Minnesota State Sales Tax", "Hennepin County Tax", etc. — not a single opaque tax line).
- Caches responses per `(zip5, line-signature)` in Drupal's `cache.default` bin for 24 hours by default.
- Falls back silently (no tax line, no fatal) on non-US, non-USD, missing ZIP, or any engine error.

What this module does NOT do
----------------------------

[](#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, no tax line written).
- Handle tax-exempt customers, customer groups, or per-store-entity configuration. (v0.2+.)
- Tax shipping lines. (v0.2+.)
- Ship with the engine bundled — point it at your own [OpenSalesTax engine](https://github.com/ejosterberg/opensalestax).

Compatibility matrix
--------------------

[](#compatibility-matrix)

Drupal coreDrupal CommercePHPStatus10.3+3.x8.1+tested11.0+3.x8.1+should workThe module hard-pins **calculation-only** behavior — no schema changes, no service overrides. It coexists with Drupal Commerce's built-in flat-rate tax types and applies first when its applies() gate matches.

Install
-------

[](#install)

```
composer require ejosterberg/opensalestax-drupal-commerce
drush en opensalestax_commerce -y
drush cache:rebuild
```

The Composer install transparently pulls in the [`ejosterberg/opensalestax`](https://packagist.org/packages/ejosterberg/opensalestax)PHP SDK.

Configure
---------

[](#configure)

Visit **Commerce → Configuration → OpenSalesTax**(`/admin/commerce/config/opensalestax`).

FieldDefaultPurpose**Engine API URL**(empty)Base URL of your OpenSalesTax engine, e.g. `https://ost.example.com`. Empty = module inert.**API Key (optional)**(empty)`X-API-Key` header value if your engine requires authentication. Stored as a config string; blank-field-on-save preserves the existing key.**Restrict to public IPs (SSRF defense)**ONReject any engine URL whose host resolves to a private, loopback, link-local, CGNAT, or multicast IP. Turn OFF only when the engine is on the same private network as Drupal (e.g. `http://10.x.x.x:8080`).**Cache TTL (seconds)**86400 (24h)How long to cache engine responses per `(zip5, line-signature)`. Minimum 3600.**Engine HTTP timeout (seconds)**10Maximum wait for the engine before falling back.**Fail hard on engine error**OFFWhen ON, an unreachable engine blocks checkout. When OFF (default), the failure is logged and checkout proceeds with no tax line.Then add **OpenSalesTax (Destination-Based US Sales Tax)** as the Tax Type on each store via **Commerce → Configuration → Taxes**.

How it works
------------

[](#how-it-works)

1. At checkout, Drupal Commerce's tax pipeline iterates over enabled tax types and calls `applies($order)` on each.
2. Our plugin's `applies()` short-circuits to `FALSE` on non-US, non-USD, missing ZIP, or missing shipping profile.
3. When `applies()` returns `TRUE`, Drupal Commerce calls `apply($order)`. We normalize the order into `(country, currency, zip5, line_items[])`, look up the cache, and on miss call the engine via the [PHP SDK](https://packagist.org/packages/ejosterberg/opensalestax).
4. For each tax line returned, we write a per-jurisdiction `Drupal\commerce_order\Adjustment` of type `tax` with the jurisdiction's name as label and `opensalestax:` as source ID.
5. Drupal Commerce's totals pipeline picks the adjustments up and renders them.

If anything goes wrong (engine down, timeout, bad payload), and **Fail hard on engine error** is OFF (default), the failure is logged via Drupal's `opensalestax` logger channel and no adjustments are written — checkout proceeds without tax. The merchant then resolves the engine outage at their own pace without customer-visible breakage.

Logging
-------

[](#logging)

All engine interactions log structured metadata (`zip5`, `http_status`, error message) via Drupal's `opensalestax`logger channel. **Customer addresses and full payloads are never logged.** The API key is read from config in memory only at request time and never written to logs.

Development
-----------

[](#development)

```
composer install
composer test                       # PHPUnit unit suite (56 tests)
composer stan                       # PHPStan level max
composer audit                      # composer audit (HIGH+ blocking)
```

CI runs the same three checks plus a DCO sign-off check on PRs.

See [`CONTRIBUTING.md`](CONTRIBUTING.md) for branch model, DCO sign-off, and the quality gate.

Security
--------

[](#security)

See [`SECURITY.md`](SECURITY.md) for responsible-disclosure guidance and [`docs/SECURITY-REVIEW.md`](docs/SECURITY-REVIEW.md) for the threat model with mitigation status.

Related projects
----------------

[](#related-projects)

- [OpenSalesTax engine](https://github.com/ejosterberg/opensalestax)
- [OpenSalesTax PHP SDK](https://github.com/ejosterberg/opensalestax-php)
- [opensalestax-magento](https://github.com/ejosterberg/opensalestax-magento)
- [opensalestax-woocommerce](https://github.com/ejosterberg/opensalestax-woocommerce)
- [opensalestax-vendure](https://github.com/ejosterberg/opensalestax-vendure)
- [opensalestax-medusa](https://github.com/ejosterberg/opensalestax-medusa)
- [opensalestax-saleor](https://github.com/ejosterberg/opensalestax-saleor)

License
-------

[](#license)

Dual-licensed under your choice of [Apache-2.0](LICENSE-APACHE.txt) OR [GPL-2.0-or-later](LICENSE-GPL.txt). See [`LICENSE`](LICENSE) for the dual-declaration. Drupal contrib code lives under GPL-2.0-or-later; this dual license keeps the module eligible for future Drupal.org listing while preserving Apache-2.0 compatibility for downstream redistribution.

DCO sign-off
------------

[](#dco-sign-off)

Every commit signed off with `-s`. CI rejects unsigned commits. See [`CONTRIBUTING.md`](CONTRIBUTING.md).

###  Health Score

36

—

LowBetter than 79% of packages

Maintenance95

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity38

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~3 days

Total

3

Last Release

21d ago

PHP version history (2 changes)v0.1.0-alpha.1PHP ^8.1

v0.1.1PHP &gt;=8.2

### Community

Maintainers

![](https://www.gravatar.com/avatar/b647ae9c341fdf92c36cb479ba7535c2eff9ac92f8614965a3b5953f7b543152?d=identicon)[ejosterberg](/maintainers/ejosterberg)

---

Top Contributors

[![ejosterberg](https://avatars.githubusercontent.com/u/6363457?v=4)](https://github.com/ejosterberg "ejosterberg (7 commits)")

---

Tags

drupaldrupal-commerceopensalestaxphpsales-taxtax-calculationdrupaltaxsales taxdrupal commerceopensalestaxus-tax

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/ejosterberg-opensalestax-drupal-commerce/health.svg)

```
[![Health](https://phpackages.com/badges/ejosterberg-opensalestax-drupal-commerce/health.svg)](https://phpackages.com/packages/ejosterberg-opensalestax-drupal-commerce)
```

###  Alternatives

[matomo/matomo

Matomo is the leading Free/Libre open analytics platform

21.6k38.2k](/packages/matomo-matomo)[aporat/store-receipt-validator

PHP receipt validator for Apple App Store and Amazon Appstore

6503.9M11](/packages/aporat-store-receipt-validator)[tempest/framework

The PHP framework that gets out of your way.

2.2k31.1k11](/packages/tempest-framework)[avalara/avataxclient

Client library for Avalara's AvaTax suite of business tax calculation and processing services. Uses the REST v2 API.

528.3M7](/packages/avalara-avataxclient)[ddeboer/vatin

Validate VAT identification numbers

1413.1M9](/packages/ddeboer-vatin)[api-platform/metadata

API Resource-oriented metadata attributes and factories

244.5M180](/packages/api-platform-metadata)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
