Commerce
Cart, checkout sheet kit, orders, and wishlist.
Cart (src/features/cart/)
Modular by design:
use-cart-core.ts— optimistic add/update/remove with operation commit/rollback, MMKV-persisted.use-server-cart.ts— server-authoritative for signed-in users: ConvexgetUserCart+ direct mutations, a local pending store for instant feedback, debounced/coalesced mutation calls.use-has-pending-cart-adds.ts+ the pending store'sinFlightflag block checkout while adds are in flight (a past race shipped half-carts).cart-service.ts— guest→auth cart merge on sign-in.
Checkout (src/features/checkout/)
use-convex-checkout.ts calls the backend action shopify.checkoutActions.prepareCheckout (items, promo code, address). The backend validates inventory, applies discounts, creates the Shopify cart, and returns a checkoutUrl. The native sheet is @shopify/checkout-sheet-kit (use-checkout.ts). Its config lives at module level in config/checkout-config.ts, so the provider effect never re-fires. Order dedup via an MMKV checkout_processed_orders key.
Stamp hints
The bag list footer and the checkout details sheet both show BagStampNote (src/features/loyalty/components/stamps/bag-stamp-note.tsx) to a signed-in buyer. It reads "Spend RM X more to earn a stamp" or "This order earns a stamp", and shows nothing to guests, while campaigns load, or when no campaign runs. getBagStampStatus in src/features/loyalty/utils/stamp-eligibility.ts measures the selection after the applied voucher, the same subtotal the backend tests. It copies the backend's evaluateStampEligibility, and stamp-eligibility.test.ts runs both over the same inputs so the copy cannot drift. One campaign whose minimum the bag meets is enough. Otherwise the note quotes the smallest shortfall.
The order detail screen renders the order's stampOutcome through src/features/orders/components/detail/_internal/stamp-row.tsx, so a buyer sees why an order earned no stamp. The FAQ in src/features/settings/components/support/faq-data.ts explains how to earn one (RM50 subtotal after vouchers on the current campaign).
Orders and wishlist
use-convex-orders, use-mark-received (buyer-confirmed delivery), use-reorder. Wishlist is src/features/wishlist/ with a dedicated remove hook. Reviews live under the product feature plus the (app)/product-reviews/[id] route (photo upload via camera/image-picker).