PHPackages                             ejosterberg/opensalestax - 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. [API Development](/categories/api)
4. /
5. ejosterberg/opensalestax

ActiveLibrary[API Development](/categories/api)

ejosterberg/opensalestax
========================

PHP SDK for the OpenSalesTax engine — open-source US sales tax calculation API.

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

Since May 4Pushed 3w agoCompare

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

READMEChangelog (1)Dependencies (5)Versions (5)Used By (11)

opensalestax-php
================

[](#opensalestax-php)

> PHP SDK for the [OpenSalesTax](https://github.com/ejosterberg/open-sales-tax) engine — the open-source, self-hostable US sales tax calculation API.

[![License](https://camo.githubusercontent.com/39a434c39c97856247fc55ebc90e8cc1cb9871558a37bf1bf83cbaca3be89d69/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d417061636865253230322e302d626c7565)](LICENSE) [![PHP](https://camo.githubusercontent.com/184ea983600c41146ee84f2391d4841a638528091c0262f5e51738e676897fad/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d254532253839254135382e322d373737626234)](composer.json)

**Status:** v0.1 alpha. API surface stable. Tested against engine v0.14 — v0.24.

Why this exists
---------------

[](#why-this-exists)

US sales tax is a mess: ~10,000 jurisdictions, ~50,000 ZIPs, rates change quarterly, taxability rules vary per state per category. The commercial APIs (Avalara, TaxJar, Stripe Tax) charge $0.50–$10+ per transaction or 0.5% of revenue.

[**OpenSalesTax**](https://github.com/ejosterberg/open-sales-tax) is the open-source self-hostable engine. **This SDK** is the PHP wrapper around its v1 HTTP API — `composer require`, point at your engine, get tax.

```
$client = new OpenSalesTax\Client(baseUrl: 'http://your-engine:8080');

$result = $client->calculate(
    address: new Address(zip5: '55401'),
    lineItems: [new LineItem(amount: '100.00', category: 'general')],
);

echo $result->taxTotal;  // "8.025"
```

That's it. ~200 LOC of stateless wrapper code — no business logic, no caching, no surprise dependencies. The complexity lives in the engine; this SDK just calls it.

Install
-------

[](#install)

```
composer require ejosterberg/opensalestax
```

Requires PHP 8.2+ (uses class-level `readonly` syntax for DTOs) and a reachable OpenSalesTax engine (self-host via the [engine's docker-compose](https://github.com/ejosterberg/open-sales-tax)).

Quickstart
----------

[](#quickstart)

```
use OpenSalesTax\Client;
use OpenSalesTax\Address;
use OpenSalesTax\LineItem;

$client = new Client(baseUrl: 'http://localhost:8080');

$result = $client->calculate(
    address: new Address(zip5: '55401'),
    lineItems: [
        new LineItem(amount: '100.00', category: 'general'),
        new LineItem(amount: '50.00', category: 'clothing'),
    ],
);

echo $result->subtotal;  // "150.00"
echo $result->taxTotal;  // "8.025"

foreach ($result->lines as $line) {
    echo "{$line->category}: \${$line->tax}\n";
    if ($line->note !== null) {
        echo "  → {$line->note}\n";   // e.g. "Clothing is non-taxable in Minnesota..."
    }
}
```

API surface
-----------

[](#api-surface)

```
$client = new Client(
    baseUrl: 'http://your-engine:8080',
    apiKey: 'optional-x-api-key',  // null if engine doesn't require auth
    timeoutSeconds: 10.0,
    httpClient: null,              // optional PSR-18 override (Guzzle 7 default)
);

$client->health();                          // HealthResponse{status, version, databaseConnected}
$client->states();                          // StatesResponse{states[StateInfo], total}
$client->rates(zip5: '55401');              // RatesResponse{input, jurisdictions[], combinedRatePct, disclaimer}
$client->calculate($address, $lineItems);   // CalculateResponse{subtotal, taxTotal, lines[], disclaimer}
```

Each line in a `CalculateResponse` carries the per-jurisdiction breakdown:

```
foreach ($result->lines as $line) {
    foreach ($line->jurisdictions as $j) {
        echo "  {$j->type:9} {$j->name:50} {$j->ratePct}% \${$j->tax}\n";
        // state     Minnesota                                    6.875% $6.8750
        // county    Hennepin County                              0.15%  $0.1500
        // city      Minneapolis                                  0.5%   $0.5000
        // district  Hennepin County Transit Sales Tax            0.5%   $0.5000
    }
}
```

Sums reconcile exactly: `$line->tax === sum($line->jurisdictions[*]->tax)`. Use the breakdown for accounting (state/county/city splits); use `$line->tax` for the customer-facing total.

Tax categories
--------------

[](#tax-categories)

Standard categories the engine recognizes: `general` (default), `clothing`, `groceries`, `prescription_drugs`, `prepared_food`, `digital_goods`. Per-state taxability rules apply (e.g. clothing is non-taxable in Minnesota; groceries in most states).

Amounts are decimal strings
---------------------------

[](#amounts-are-decimal-strings)

Amounts are **strings**, not integers (cents) or floats. Strings preserve the engine's exact precision; the engine quantizes per-jurisdiction in fixed-point. Convert from cents in your own code if you need to:

```
$cents = 9999;
$amount = number_format($cents / 100, 2, '.', '');  // "99.99"
new LineItem(amount: $amount, category: 'general');
```

Errors
------

[](#errors)

Flat hierarchy. All errors extend `OpenSalesTax\Exceptions\OpenSalesTaxException`:

- `OpenSalesTaxApiException` — non-2xx HTTP from the engine; carries `statusCode`, `rawBody`, `errorBody`.
- `OpenSalesTaxNetworkException` — transport failure (timeout, DNS); wraps the underlying PSR-18 exception via `getPrevious()`.
- `OpenSalesTaxValidationException` — client-side input rejected before sending (bad ZIP regex, negative amount).

```
try {
    $result = $client->calculate(...);
} catch (OpenSalesTaxApiException $e) {
    error_log("Engine returned {$e->statusCode}: {$e->rawBody}");
} catch (OpenSalesTaxNetworkException $e) {
    error_log("Cannot reach engine: " . $e->getMessage());
}
```

Quality bar
-----------

[](#quality-bar)

- **PHPStan level=max** — zero suppressed errors
- **PHP-CS-Fixer** with PSR-12 + risky rules — zero violations
- **PHPUnit** — 21 unit + integration tests, 54 assertions, all passing
- **GitHub Actions CI** matrix on PHP 8.2 / 8.3 / 8.4
- **DCO sign-off** required on every commit

Engine compatibility
--------------------

[](#engine-compatibility)

This SDK targets the OpenSalesTax v1 HTTP API. Tested against engine **v0.14 — v0.24**. The v1 API surface has been stable across that range. Pin both in production:

```
ejosterberg/opensalestax: ^0.1
opensalestax engine:      v0.20+ (recommended; older versions had a state-bleed bug fixed in v0.22)

```

What this SDK is NOT
--------------------

[](#what-this-sdk-is-not)

- **Not the engine.** See [open-sales-tax](https://github.com/ejosterberg/open-sales-tax) for the calculator itself.
- **Not Stripe-aware.** For a Stripe Tax replacement, layer [opensalestax-stripe-php](https://github.com/ejosterberg/opensalestax-stripe-php) on top.
- **Not a tax-filing service** — calculation only. The merchant remits.
- **Not a caching layer.** Caching is the consumer's job because cache-invalidation policy is platform-specific.

Disclaimer
----------

[](#disclaimer)

> 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.

Contributing
------------

[](#contributing)

DCO sign-off (`git commit -s`) required on every commit. See [CONTRIBUTING.md](CONTRIBUTING.md). Apache 2.0 + SPDX header on every source file.

License
-------

[](#license)

[Apache 2.0](LICENSE).

###  Health Score

44

—

FairBetter than 90% of packages

Maintenance95

Actively maintained with recent releases

Popularity20

Limited adoption so far

Community14

Small or concentrated contributor base

Maturity39

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 ~5 days

Total

4

Last Release

21d ago

PHP version history (2 changes)v0.1.0PHP &gt;=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 (8 commits)")

---

Tags

avalara-alternativeopensalestaxphpsales-taxsdkstripe-taxtaxtaxjar-alternativeapisdktaxsales taxopensalestax

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

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

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

###  Alternatives

[openai-php/client

OpenAI PHP is a supercharged PHP API client that allows you to interact with the Open AI API

5.8k26.1M293](/packages/openai-php-client)[tempest/framework

The PHP framework that gets out of your way.

2.2k31.1k11](/packages/tempest-framework)[theodo-group/llphant

LLPhant is a library to help you build Generative AI applications.

1.7k371.6k5](/packages/theodo-group-llphant)[telnyx/telnyx-php

Official Telnyx PHP SDK — APIs for Voice, SMS, MMS, WhatsApp, Fax, SIP Trunking, Wireless IoT, Call Control, and more. Build global communications on Telnyx's private carrier-grade network.

35729.6k2](/packages/telnyx-telnyx-php)[resend/resend-php

Resend PHP library.

596.2M34](/packages/resend-resend-php)[square/square

Use Square APIs to manage and run business including payment, customer, product, inventory, and employee management.

753.7M26](/packages/square-square)

PHPackages © 2026

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