Commerce (Shop)

A real storefront — products and services, a client-side cart, and three real checkout paths, via the real Adhara Commerce SDK.

/shop is new in this port — not in the Python original. A real storefront: products and services, a client-side cart (localStorage — no server session needed for anonymous shoppers), and checkout — local JSON by default, real Adhara Commerce when configured.

A real SDK layer, not raw HTTP

Unlike blog/events/scheduling/links, the shop goes through the real Adhara SDK’s commerce resource (src/lib/adharaCommerce.ts), not fetch() against raw endpoints — checkout is genuinely complex (payment sessions, line-item pricing), and the SDK already has that logic. See The Adhara SDK for the two real gaps found and fixed there while building this feature.

Backend

  • Local (default) — one JSON file per catalog item under data/content/shop/items/*.json (ships pre-seeded with 4 sample items spanning all 5 product types), edited via npm run shop -- import <file>.
  • Adhara (GRAVITY_SHOP_BACKEND=adhara, or auto-detected once the SDK is installed + ADHARA_WORKSPACE is set) — real Products and Services, fetched via the SDK’s listPublicProducts/getPublicProduct/listPublicServices/getPublicService (no API key needed for browsing).

Three real checkout paths, tried in order

shopStore.checkout() tries these in order — which path is even reachable depends on what’s configured:

  1. Authenticated multi-item (ADHARA_API_KEY set) — real cart checkout via the SDK’s createCheckoutSession, Products only, priced by a specific ProductPrice.id. Whether Services can go through this endpoint isn’t confirmed.
  2. Public single-item (ADHARA_WORKSPACE only, no API key) — real checkout via createPublicCheckout, but only for a cart that resolves to exactly one distinct line item — Adhara has no public multi-item checkout endpoint yet.
  3. Local pending order (always available) — data/content/shop/orders/<id>.json, status pending, no payment taken — for manual follow-up. Same honesty principle as scheduling’s local demo mode: never claim something happened that didn’t.

Payment-provider agnostic

The checkout layer doesn’t name a specific payment processor anywhere in this repo’s code or docs — Adhara’s backend may support more than one in the future, and nothing here should assume it’s locked to one.

A known, confirmed extension point

Adhara’s backend also has a full session-based public shopping cart API (/public/cart/{workspace_slug} — create/read/clear a cart, add/update/remove items, validate, checkout) that would remove path 2’s one-item limit for public storefronts. The SDK doesn’t wrap it yet, and adopting it means moving the cart from this repo’s client-side localStorage (public/js/shop-cart.js) to an Adhara-hosted server session — a real architectural change, not a small patch. See The Adhara SDK for the full list of confirmed-but-unbuilt extension points.

Architecture

src/lib/adharaCommerce.ts (SDK calls) → src/services/shopStore.ts (catalog merge, backend/checkout-path resolution) → src/pages/shop/{index,[slug],cart,checkout,checkout/confirmed,checkout/cancelled}.astro + src/pages/api/shop/{catalog,checkout}.ts + public/js/shop-cart.js.