Store OS, Part 1: Commerce Agents on Workers and D1
Building TITAN's cart, checkout, and card-to-card payment flow on Cloudflare Workers and D1 — the first two posts in a series about building our own store on the stack we sell.
Why we're writing this
TITAN is our own 3D-print storefront — not a client engagement, our own money, our own inventory, built on React Router 7, Cloudflare Workers, D1, KV, and R2, the exact platform we sell. "Store OS" is the build log for it: real decisions, real bugs, no marketing gloss. TITAN isn't live yet — this post covers session 3 (cart and checkout) and session 8 (the automation engine), written while the build is still pre-launch.
The commerce core
Cart lines don't store price — only { variantId, qty }. Every read recomputes the live
price and drops anything that isn't genuinely in stock. That decision paid off mid-session:
manual testing showed a pre-order product could otherwise sit in a cart at a stale price
indefinitely. Recomputing on every read closed that gap for free.
Card-to-card as a first-class payment method
Iran has no access to international card gateways. Card-to-card bank transfer — a customer wires money directly and an admin manually confirms it — isn't a fallback here, it's the primary payment rail. The interesting part is a small detail: every order gets a random 1–99 Toman offset added to its transfer amount. That's the whole trick — it makes each pending transfer's exact amount unique, so an admin matching an incoming bank transfer to an order can do it by amount alone, no manual reference-number chasing.
The Zarinpal gateway integration is fully built alongside it, sitting behind a feature flag until Enamad merchant approval lands. That's a deliberate choice, not an oversight — card-to-card is the only live payment path today because it's the only one that's actually approved to run.
The bug that mattered: PDF fonts
The invoice generator uses pdf-lib, which embeds exactly the font bytes it's given.
The initial font assets were the Fontsource .woff2 files — valid in a browser, not a
valid embedded PDF font program. The build succeeded, typecheck passed, and the code
looked correct. It wasn't until we rendered the actual output PDF with a real renderer
that FT_New_Memory_Face: unknown file format surfaced and the invoice page came out
visually blank. The lesson that stuck: a font that renders in a browser and a font that
embeds in a PDF are not the same claim, and only rendering the real artifact catches the
difference.
Ten agents, one cron trigger, no shared blast radius
Session 8 wired the automation engine: one Cloudflare Cron Trigger firing every 15 minutes, fanning out to ten independent jobs — abandoned-cart recovery, the card-to-card sentinel that watches the verification queue and pings the owner when something's been pending too long, restock alerts, a Claude-drafted feasibility note for new custom-order requests (price always comes from a deterministic formula — the model never sets it), draft replies for open support tickets, a daily Telegram ops digest, a weekly content-draft agent that writes but never publishes on its own, and nightly D1 backups. Each job is wrapped in its own try/catch and writes its own status row every tick, regardless of whether it did anything — so one agent's bad day never takes the others down with it, and a silent failure never disappears without a trace.
What's next
Part 2 covers session 4 — SMS-OTP auth built for how Iranian shoppers actually verify their identity, and the rate-limit design that keeps it from being abused. The full build story, warts and all, is at /work/titan. If you want a commerce platform built the same way — real agents, an escalation path for everything that shouldn't run unsupervised — see how we approach e-commerce builds.
Want the checklist for your own build?
Tell us what you're working on — we respond with an architecture sketch within 48 hours.
Read next: Store OS, Part 2: SMS-OTP Auth for the Iranian Market