Skip to content
← All packages

Tax for Laravel

A self-hostable consumption-tax engine for Laravel. It owns the calculation logic, place-of-supply, reverse-charge, rate application, inclusive and exclusive, and sources only the rate data behind a pluggable contract. No forced third-party tax SaaS.

Tax for Laravel owns the tax calculation logic, place-of-supply, reverse-charge, rate application, inclusive and exclusive pricing, and sources only the rate data behind a pluggable contract. There is no forced third-party calculation SaaS. It is built on Geo for Laravel, so every supply is assessed against a jurisdiction resolved from canonical ISO data. Tax is a function of seller registrations, buyer jurisdiction and product type, never a fuzzy country-name match.

Own the logic, source the data

The engine decides whether and how to tax a supply. The TaxRateSource contract supplies the rate number, whether that is an EU TEDB feed, the us-tax-data dataset or a commercial adapter. A missing rate is refused, never assumed to be zero percent. That boundary is the whole design: the parts that must be correct and auditable live in the package, and the data that changes constantly is versioned separately and fetched at runtime, so a rate change is a dataset release rather than a package upgrade. Pin it at a tagged release for a deterministic build.

What It Does

EU VAT

Article 44, 45 and 58 place-of-supply, intra-EU B2B reverse charge, VIES-validated customer tax IDs, and the Article 59c 10,000 euro micro-business threshold: a below-threshold, non-opted seller charges origin VAT, an opted-in or over-threshold seller charges destination.

National VAT and GST

UK, CH, NO, AU, NZ, MX, SG, TW, UAE, SA, BH, OM, TR, CL, ID, VN, PH, JP, KR, TH and UA, plus India dual GST (IGST versus CGST and SGST) and Malaysia SST.

US Sales Tax

Three gates before a rate applies: the state must resolve, the seller must have nexus in it, and the product must be taxable there, otherwise it returns NotRegistered or Exempt rather than a wrong charge. Rates, 25-category taxability, Wayfair nexus thresholds and intrastate sourcing come from the us-tax-data dataset across all 51 jurisdictions, with 41 states carrying local rate records from official state files.

Canada GST and HST

Province-level combined rate with cross-border B2B self-assessment, resolved at province level with no local tax lookup.

Multi-Entity Routing

Tax depends on which selling entity issues the invoice. SellerRegistrations is the seller side the billing engine supplies per invoice, so the same buyer is taxed correctly whichever entity sells.

Exact Money

Amounts are brick/money and rate maths rounds half-up once. Deny-by-default throughout: no regime or no rate raises an exception, never a silent zero.

Honest coverage

The docs carry the full per-country table with sources and confidence, plus an honest list of jurisdictions omitted until their rate data is verified, a national-VAT batch pending primary-source confirmation, some Pakistani provinces and Brazil. The engine omits rather than ship a rate it cannot stand behind.

Nexus is asserted here from an explicit seller registration, never inferred from one invoice, because economic nexus turns on cumulative sales a single supply cannot carry. Working out where a registration is owed in the first place is the job of Economic Nexus for Laravel, which accumulates activity per state and reports where a threshold has been crossed.

Buyer exemptions

A query can carry a resale, nonprofit or government certificate. Applied deny-by-default over the regime verdict, a valid exemption covering the taxed jurisdiction rewrites a standard line to Exempt, keeping net, zeroing tax, and recording the certificate reference on the assessment. Reverse-charge, not-registered and zero-rated outcomes are left alone, and an expired certificate or one for a different jurisdiction does not exempt. The engine computes the assessment. Capturing and verifying certificates stays your concern.

Contracts all the way down

TaxCalculator, TaxRegime, TaxRateSource, RegimeRegistry, AddressGeocoder, VatIdValidator and ReturnAggregator are all contracts you can bind and override. Rate sources compose: static, remote, caching and chain, and they resolve by taxability category so reduced and zero bands apply when a bound source supplies them. None are fabricated by default. Unmodelled jurisdictions and missing rates are refused, not guessed.

The shipped GeocodioGeocoder resolves US and Canada addresses to state and province. Only the geocoding is taken from Geocodio: the rate and the calculation stay in the engine, and Geocodio's own tax append is not used. Turning on the opt-in rooftop option makes the adapter attach a county FIPS as a locality code, and the rate source then stacks the matched local record onto the state share per that state's rate basis, returning an all-in rate at authoritative confidence. A county code cannot single out a specific address's city or special-district record, so where none matches the engine falls back to the state rate and labels it derived rather than passing it off as all-in. Closing that last gap is a point-to-jurisdiction crosswalk, which is a data question: the stacking is already wired end to end.