Checkout that never surprises
Guest checkout with no passwords. Stock held while the buyer pays. The delivery price follows the address, and the buyer sees the total before every charge.
Tillwire runs a complete online shop from one Go program: catalogue, checkout, payments, couriers, orders, mail and a full back office. Extend it without limit. Put any frontend on it. Run it on your own server.
On large hosted platforms, much of a real shop comes from add-ons, each with its own fee and its own idea of your data. In Tillwire the core is one engine, one database and one set of rules.
Guest checkout with no passwords. Stock held while the buyer pays. The delivery price follows the address, and the buyer sees the total before every charge.
Card gateways, bank transfer, cash on delivery and gift cards. Keys stored encrypted.
Rates, pickup points, booking, labels and tracking, through one courier port.
Up to 3 options a product, custom fields, a media library, and review before a product goes live.
Full-text search with filters and suggestions; collections by hand or by rules.
Order mail, consent, segments, campaigns, A/B subject lines and automatic flows.
A full /admin for orders, catalogue, stock, marketing and mail. Staff roles with rights per screen and per act, and a refusal page that names the missing right.
Order states built from events, refunds, tax invoices and credit notes. Stock by location with a ledger, stock-takes and transfers.
Counted on main on 21 October 2026, from the OpenAPI file, the operations contract and the event list.
Keep your own code in your own folder and never edit an engine file, so each upstream update merges cleanly. Four public doors, each with a written contract and a test that keeps it true.
/api/v1259 operations, idempotent writes, one error format, OpenAPI 3.1.
webhooks23 types as a feed, or as signed webhooks with retries and replay.
engine/extYour tables, /admin screens, rights, approval checks, picture holds.
courier/1A courier as your own HTTP service, proved by one command.
# read products with a scoped token curl -H "Authorization: Bearer ecl_…" \ "https://yourshop.example/api/v1/catalogue/products?limit=2" # the description of every operation, no token curl https://yourshop.example/api/v1/openapi.json
Use code in an error, not the message: the codes are stable and the list only grows.
// Ecl-Signature: t=<unix>,v1=<hex HMAC-SHA256> t, sig := parseSignature(r.Header.Get("Ecl-Signature")) mac := hmac.New(sha256.New, secret) mac.Write([]byte(t + "." + string(body))) if !hmac.Equal(mac.Sum(nil), sig) { w.WriteHeader(401); return } // a repeat has the same Ecl-Event-Id: skip it
8 tries with a growing wait, a delivery log, replay and secret rotation.
func init() { ext.RegisterMigrations("acme-notes", sql) ext.RegisterRight("acme-notes", ext.Right{ Name: "edit", Group: "catalogue", Label: "Edit product notes", }) ext.RegisterAdmin(ext.Module{ Name: "acme-notes", Title: "Product notes", Right: "edit", Menu: true, Handler: notes, }) }
With nothing registered, the engine does not change: a test proves it.
{{/* product page: your layout, the engine's form */}} <h1>{{.Product.Name}}</h1> {{engine.AddToCartForm . "options" "boxes"}} # check it before you ship engine storefront check --templates theme
The engine refuses a theme that breaks the contract, and names each problem.
The engine decides what a page says and does. Your theme decides how it looks. Between them is a versioned contract, checked when the engine starts, so a new look cannot break a payment.
Illustrations of three looks for the same product data. The engine ships no storefront CSS.
Read about themes and the contractNo cluster to tend. Tillwire runs on any host with a disk, a port and an S3-compatible bucket.
your frontendengine/admin, APIbucketOne engine on 8 cores, a store with 50,000 products and 500,000 orders, and no CDN in front. These are the numbers, with where it stops.
through the whole checkout, from add to cart to the paid order page. p95 under 9 ms. 5,489 orders paid in 3 minutes, and no limit reached.
at 135 requests a second, and still no limit reached. Product and category pages: 0.3 to 8 ms each.
where a realistic mix of shop and back office pages passes 500 ms. Buyers shed at that point: 0.
flat for 15 minutes in a mailing to 160,000 contacts: about 21 minutes for all of it. The mailing’s 160,000 mails were ready in 18 s.
while that mailing runs: order and sign-in mail go first. Cart writes stay at 7 to 8 ms.
for the engine at the end of the full load test.
Measured on 21 October 2026 on main, with k6 and the project’s perf runner: a 16-core cloud server, the engine pinned to 8 cores and the load generator to the other 8. A test store of 50,000 products, 150,000 variants, 500,000 orders and 200,000 contacts. No CDN, no TLS, a test payment gateway, mail to a local test server. “p95” means 95 of 100 requests were faster.
The limit is the CPU that renders pages on one server, not the database. Some /admin screens are slow at this volume (the catalogue list takes 5.4 s); the fixes are planned. By calculation from these numbers, not a test: with a CDN that keeps 9 of 10 public pages, a sale at 50 times the traffic of a store with 5,000 visitors a day sends the engine 10 to 25 requests a second, well under the 114 measured.
Today one engine runs one store. The planned next step: extra read-only engines on a copy of the database, behind a load balancer, for the public pages. It is a future project, not yet scheduled.
This site is checked against the code. A feature that is planned says “Coming”. A number appears only when it was counted or measured, with its method.
See what is on main, off by default or comingRun a demo store on your own computer in three commands, then open the guide.