Files
platform/tax/tax.go
T
2026-06-28 17:52:49 +02:00

63 lines
2.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package tax provides the tax computation seam: a Provider interface
// (mirroring PaymentProvider and ShippingProvider patterns), the shared Compute
// formula, and a stateless Static provider for dev/test.
//
// SCALE: all tax rates are integer **basis points of a percent** — rate × 100,
// so 25% = 2500, 12.5% = 1250, 8.1% = 810. This is the single platform-wide
// scale (Price.VatRates, CartItem.Tax, OrderLine.tax_rate, the payment-provider
// wire). Integer basis points support fractional VAT rates (Ireland 13.5%,
// France 5.5%, Finland 25.5%) exactly without ever using a float.
package tax
// Provider computes taxes for orders and line items.
type Provider interface {
// Name returns the provider name for identification (logging, metrics).
Name() string
// Compute computes the VAT portion from a gross (inc-VAT) total and tax
// rate. Both total and return value are in minor currency units (öre).
// rateBp is basis points of a percent (2500 = 25%, 1250 = 12.5%).
//
// Ex-VAT-first integer math: exVat = total*10000/(10000+rateBp); tax = total - exVat.
// When rateBp <= 0 the result is 0 (untaxed / zero-rated items).
Compute(total int64, rateBp int) int64
// DefaultTaxRate returns the default VAT rate for a country in basis points
// (2500 = 25%). Used for deliveries and items where no explicit rate was
// recorded at add-to-cart time. Returns 0 if the country is unknown/untaxed.
DefaultTaxRate(country string) int
}
// Compute returns the VAT portion of an inc-VAT total using integer (cent)
// arithmetic only, ex-VAT-first so exVat + tax == total exactly and the result
// matches the payment-provider convention (no per-line rounding drift):
//
// exVat = total * 10000 / (10000 + rateBp)
// tax = total - exVat
//
// rateBp is basis points of a percent (2500 = 25%, 1250 = 12.5%) — the single
// platform-wide rate scale. When rateBp <= 0 the result is 0 (untaxed).
func Compute(total int64, rateBp int) int64 {
if rateBp <= 0 {
return 0
}
exVat := total * 10000 / int64(10000+rateBp)
return total - exVat
}
// Static is a stateless, always-available Provider that simply runs the
// formula without any country-specific rates. Use for local dev, tests,
// and as a no-dependency default.
type Static struct{}
var _ Provider = (*Static)(nil)
// NewStatic returns a new Static provider.
func NewStatic() *Static { return &Static{} }
func (p *Static) Name() string { return "static" }
func (p *Static) Compute(total int64, taxRate int) int64 {
return Compute(total, taxRate)
}
func (p *Static) DefaultTaxRate(_ string) int { return 0 }