Refunds and disputes were recorded under an in-process mutex, and the renewal guard was a read then a write under another. Two processes over one store, or a webhook beside the billing cycle, could each read the invoice and each write it back, the second wiping the first's refund. A swap writes only while the stored entity's updatedAt is still the one read (insert-if-absent for a new or deleted entity) and otherwise reads again: db.Swapper on SQLite and Postgres as one statement, datastore.Swap, mixin.Model.Swap. Refund and dispute recording swap the invoice or payment record in, and idempotencykey.Begin swaps its first sighting and its stale re-claim, so a guarded money move runs once across replicas. The returns and lockPeriod mutexes are gone. lockFunds stays: api/transaction mounts only in standalone commerce, whose store has one writer. The tests open one store file through two handles, each with its own write lock: 24 refunds delivered twice to both, with dispute reports between them, land once each; 32 sightings of one key proceed once. A blind write in the refund recorder fails the first test every run.
commerce
Checkout, billing, pricing, invoicing — the light commerce router for the Hanzo platform. NOT in PCI-DSS scope (PAN data lives in hanzoai/vault).
Quick start
docker run -p 8001:8001 ghcr.io/hanzoai/commerce:latest
What this is
commerce handles orders, products, subscriptions, invoices, and customer records for every Hanzo deployment. It is CDE-connected, not CDE-in-scope: card data is tokenized at hanzoai/vault and commerce only ever sees the token. The service is production multi-tenant (37+ X-Org-Id call sites), IAM-integrated, and already exposes func Mount(*zip.App, cloud.Deps) error — it is one of the three reference implementations of the HIP-0106 Mount contract.
Specs
Implements:
- HIP-0037 AI Cloud Platform (billing surface)
- HIP-0106 Unified Cloud Binary (commerce subsystem — already exposes
Mount())
Architecture
client -> gateway -> commerce (zip.App)
|
orders / products / subscriptions / invoices
|
+-----------+-----------+
| |
hanzoai/vault billing provider
(CDE — tokenizes PAN) (Stripe etc., webhook in)
|
per-tenant data via base (HIP-0302)
Development
sh scripts/fetch-plans.sh # vendor the @hanzo/plans catalog (go:embed)
go run ./cmd/commerce -dev # API on 127.0.0.1:8090
Data lives under COMMERCE_DIR (default ./commerce_data), one SQLite file per
org. The build needs cgo with the sqlite_math_functions tag, or
CGO_ENABLED=0 for the pure-Go SQLite backend.
Test
export GOWORK=off CGO_ENABLED=1 GOFLAGS=-tags=sqlite_math_functions
go vet ./...
go test ./billing/... ./payment/...
go run github.com/onsi/ginkgo/v2/ginkgo -r test/
The same gates run in CI from hanzo.yml.
License
MIT OR Apache-2.0 (LICENSE, LICENSE-MIT, LICENSE-APACHE) per HIP-0137 One License; vendored third-party terms are in NOTICE.