Billing in micro-dollars, with no double-spend
A token costs a fraction of a cent, so float arithmetic will silently lose your money. Here is the integer accounting model behind the gateway, and the concurrency problem it solves.
Floating point is a bad fit for money. Add a million requests of $0.000675 each in binary floating point and the total drifts. At scale, that drift is a support ticket.
Integers, all the way down
Every balance and every ledger entry is stored as an integer number of micro-dollars, where 1 USD = 1,000,000. Model prices are stored the same way, per 1,000 tokens. The cost of a request is:
cost = ceil(prompt_tokens × input_price / 1000)
+ ceil(completion_tokens × output_price / 1000)
floor = 1 micro-dollar per request
Rounding up per request, rather than per day, keeps the ledger exact and auditable: the sum of all charges always equals the balance movement.
The concurrency problem
A user can run fifty requests in parallel. If each one reads the balance, checks it, calls the model and then writes back a new balance, two concurrent requests will both pass the check and both write — the classic lost update. The user overspends.
We solve it with a Durable Object per user. All balance mutations for a user are serialised through a single object instance, so the check-and-deduct is atomic by construction, not by locking. The reservation is taken before the upstream call and reconciled against actual usage afterwards, because token counts are only known when the response completes.
Why not just use SQL transactions
D1 is a great store for the ledger, but serialising hot-row writes through it puts every request for a busy account behind the same lock, across regions. A Durable Object gives per-user isolation with a single-threaded execution model and no cross-user contention.