"Live production e-commerce site — Stripe as the actual source of truth for product data, not just a payment processor bolted onto a CMS"

getStripeCatalog(), with a local fallback if STRIPE_SECRET_KEY is unsetcheckout.session.completedSet — silently broken in serverless, since state doesn't persist across function invocations. Fixed by calling stripe.checkout.sessions.retrieve() directly against the Stripe API instead of keeping local state. The general lesson: "remember this happened" state in a serverless route needs an external store, not an in-process one.Most e-commerce builds pair a headless CMS with Stripe bolted on just for checkout — which means two systems to keep in sync: change a price or retire a size in the CMS, and you have to remember to make the same change in Stripe, or the storefront and the actual checkout drift apart. I collapsed that into one system: product metadata (slug, status, sizes, story) lives directly on the Stripe Product object, and getStripeCatalog() autopages through Stripe's product list and treats that as the entire catalog. There's nothing else to keep in sync because there's nothing else. The tradeoff is real — Stripe's metadata fields aren't built to be a CMS, no rich content blocks, size limits — but Hana-Bi's product pages are simple enough that a real CMS would have been solving a problem I didn't have.
The first version of session verification kept a Set of confirmed session IDs in memory, checked on each request. It worked in local dev and broke silently in production — a customer would complete checkout, land on the confirmation page, and get told their session wasn't verified. The bug: every serverless function invocation can spin up a fresh instance with its own memory, so the Set that recorded the session as verified in one invocation didn't exist in the next one handling the confirmation page. It wasn't a code bug in the traditional sense — the logic was correct, it just assumed a persistent process that serverless doesn't give you. The fix was to stop trying to remember anything myself: call stripe.checkout.sessions.retrieve() directly against the Stripe API on each check, so verification asks the source of truth instead of a local cache of it. The lesson generalizes: any "remember this happened" state in a serverless route needs to live in something external, never in-process.

