@base44/app-plugin-commerce 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +6 -6
  2. package/base44/agents/commerce/StoreAdmin.jsonc +1 -1
  3. package/base44/entities/commerce.OrderRefund.jsonc +1 -1
  4. package/base44/entities/commerce.PaymentGateway.jsonc +1 -1
  5. package/base44/entities/commerce.Webhook.jsonc +1 -1
  6. package/base44/functions/commerce/admin-products/entry.ts +1 -1
  7. package/base44/functions/commerce/admin-reports/entry.ts +1 -1
  8. package/base44/functions/commerce/payments/entry.ts +2 -2
  9. package/base44/functions/commerce/seed-store/defaults.ts +1 -1
  10. package/base44/functions/commerce/storefront-catalog/entry.ts +1 -1
  11. package/base44/functions/commerce/storefront-checkout/entry.ts +1 -1
  12. package/base44/shared/commerce/card-payment.stripe.ts +29 -9
  13. package/base44/shared/commerce/card-payment.ts +1 -1
  14. package/base44/shared/commerce/payments.ts +2 -2
  15. package/base44/shared/commerce/scan.ts +1 -1
  16. package/base44/shared/commerce/sequence.ts +2 -2
  17. package/package.json +1 -1
  18. package/scripts/install.js +1 -1
  19. package/skills/commerce/SKILL.md +36 -26
  20. package/skills/commerce/docs/api-storefront.md +6 -6
  21. package/skills/commerce/install/01-install.md +2 -2
  22. package/skills/commerce/install/02-storefront.md +355 -99
  23. package/skills/commerce/install/03-data.md +5 -5
  24. package/skills/commerce/references/catalog-rendering.md +6 -6
  25. package/skills/commerce/references/online-payments.md +5 -6
  26. package/skills/commerce/references/reviews.md +5 -5
  27. package/src/commerce/admin/README.md +6 -3
  28. package/src/commerce/admin/layout/AuthGuard.jsx +1 -1
  29. package/src/commerce/admin/pages/products/Reviews.jsx +1 -1
  30. package/src/commerce/admin/pages/settings/InventorySettings.jsx +1 -1
  31. package/src/commerce/admin/pages/settings/PaymentsSettings.jsx +1 -1
  32. package/src/commerce/storefront/index.js +45 -33
  33. package/src/commerce/storefront/useCartLine.js +37 -0
  34. package/src/commerce/storefront/useCheckout.jsx +18 -6
  35. package/src/commerce/storefront/useOrderReturn.js +36 -10
  36. package/src/commerce/storefront/useProduct.js +68 -0
  37. package/src/commerce/utils/index.js +9 -6
  38. package/src/commerce/utils/shipping-promos.js +2 -2
  39. package/src/commerce/utils/specs.js +26 -0
  40. package/src/commerce/utils/variants.js +49 -2
  41. package/src/commerce/storefront/blocks/AddToCartBlock.jsx +0 -86
  42. package/src/commerce/storefront/blocks/AddressFieldsBlock.jsx +0 -96
  43. package/src/commerce/storefront/blocks/BreadcrumbsBlock.jsx +0 -52
  44. package/src/commerce/storefront/blocks/CartLinesBlock.jsx +0 -98
  45. package/src/commerce/storefront/blocks/CheckoutBlock.jsx +0 -247
  46. package/src/commerce/storefront/blocks/CouponFieldBlock.jsx +0 -84
  47. package/src/commerce/storefront/blocks/OrderReceivedBlock.jsx +0 -129
  48. package/src/commerce/storefront/blocks/ProductGalleryBlock.jsx +0 -66
  49. package/src/commerce/storefront/blocks/ProductSpecsBlock.jsx +0 -33
  50. package/src/commerce/storefront/blocks/ProductStripBlock.jsx +0 -55
  51. package/src/commerce/storefront/blocks/QuantityStepper.jsx +0 -62
  52. package/src/commerce/storefront/blocks/ReviewsBlock.jsx +0 -191
  53. package/src/commerce/storefront/blocks/TotalsBlock.jsx +0 -42
  54. package/src/commerce/storefront/blocks/VariantSelectorBlock.jsx +0 -81
  55. package/src/commerce/storefront/blocks/index.js +0 -44
package/README.md CHANGED
@@ -8,11 +8,11 @@ It provides a full-featured **commerce data model and behavior** (variant-driven
8
8
 
9
9
  - **20 entities** — Products (a product sells variants when it carries attributes; no type field), variations, categories, ribbons, attributes + values, reviews, orders (embedded line/shipping/tax/fee/coupon lines), order notes, refunds, coupons, customers, Shipping & Tax Locations (shipping rates + tax groups per location), payment gateways, store settings, webhooks + deliveries, carts, download permissions, email log.
10
10
  - **16 backend functions** — 9 admin (`commerce/admin-products`, `commerce/admin-orders`, `commerce/admin-refunds`, `commerce/admin-coupons`, `commerce/admin-customers`, `commerce/admin-reviews`, `commerce/admin-webhooks`, `commerce/admin-reports`, `commerce/admin-tools`), 4 storefront (`commerce/storefront-catalog`, `commerce/storefront-cart`, `commerce/storefront-checkout`, `commerce/storefront-account`), 2 payment (`commerce/payments`, `commerce/payment-webhook`), and an idempotent `commerce/seed-store` — one call seeds the business defaults **and the whole catalog** (products with attributes in, variants/categories/taxonomy created internally).
11
- - **Payments: manual methods work out of the box; online cards are opt-in** — the seed enables the manual `offline` method (bank transfer, cash on delivery, pickup: on-hold + instructions, no code) and leaves the `card` gateway **disabled**. The order side of card payments *is* premade — checkout routing, payment links for unpaid orders, two idempotent confirmation paths (customer return + webhook) and refund records — so a store that opts in wires a provider by implementing **four functions in one file**, `base44/shared/commerce/card-payment.ts`. **For Stripe there is nothing to write**: `base44/shared/commerce/card-payment.stripe.ts` is a complete implementation — copy it over the stub, store the secret, register the webhook, enable the gateway. Any other provider (PayPal, Adyen, a local PSP) follows the same shape. Enable the gateway only with a provider behind it, or checkout answers `503 no_card_payment_provider`. The rule and timing: [`skills/commerce/install/03-data.md`](./skills/commerce/install/03-data.md); provider mechanics: [`skills/commerce/references/online-payments.md`](./skills/commerce/references/online-payments.md). The admin can add more manual methods in Settings → Payments.
11
+ - **Payments: manual methods work out of the box; online cards are opt-in** — the seed enables the manual `offline` method (bank transfer, cash on delivery, pickup: on-hold + instructions, no code) and leaves the `card` gateway **disabled**. The order side of card payments *is* premade — checkout routing, payment links for unpaid orders, two idempotent confirmation paths (customer return + webhook) and refund records — so a store that opts in wires a provider by implementing **four functions in one file**, `base44/shared/commerce/card-payment.ts`. **For Stripe there is nothing to write**: `base44/shared/commerce/card-payment.stripe.ts` is a complete implementation used as-is — copy it over the stub and enable the gateway. Any other provider (PayPal, Adyen, a local PSP) follows the same shape. Enable the gateway only with a provider behind it, or checkout answers `503 no_card_payment_provider`. The rule and timing: [`skills/commerce/install/03-data.md`](./skills/commerce/install/03-data.md); provider mechanics: [`skills/commerce/references/online-payments.md`](./skills/commerce/references/online-payments.md). The admin can add more manual methods in Settings → Payments.
12
12
  - **Shared commerce engine** (`base44/shared/commerce/`) — totals, tax, shipping, coupons, stock, order lifecycle, webhook dispatch (HMAC-signed), emails, card-payment plumbing, plus static country/currency/continent data.
13
13
  - **Admin UI** (`src/commerce/admin/`) — a React/Tailwind/shadcn admin with a familiar store back-office information architecture: dashboard, orders, products, coupons, customers, reports, and full settings including webhooks. Admin-role gated.
14
14
  - **Storefront helpers** (`src/commerce/utils/`) — framework-free, dependency-free modules for the shopfront you build: `storefront.js` is the API client (`createStorefront(base44)` — cart-token lifecycle, cached store-info, catalog/cart/checkout/reviews/return-page calls); `variants.js` maps an attribute selection (Size, Color) onto a `ProductVariation` and back, plus per-option availability and price ranges; `price.js` encodes the from-price and price-range rules; `totals.js` projects a cart *or* an order into one summary shape; `address-spec.js` is the checkout address form as data; `images.js` normalizes catalog images; `shipping-promos.js` reads the store's real free-shipping configuration so "Free shipping over €150" states a configured rule rather than an invented number.
15
- - **Storefront React layer** (`src/commerce/storefront/`) — **two tiers.** The *identity* surfaces stay yours to design (home, collection grid, product-page layout, product card, theme — the kit ships no markup for them). The *commodity* surfaces ship as **blocks** that inherit your theme and can be restyled, slotted or replaced: `CheckoutBlock`, `CartLinesBlock`, `TotalsBlock`, `CouponFieldBlock`, `QuantityStepper`, `OrderReceivedBlock`, `ReviewsBlock`, `AddressFieldsBlock`, plus the product page's internals (`VariantSelectorBlock`, `AddToCartBlock`, `ProductGalleryBlock`, `ProductSpecsBlock`, `BreadcrumbsBlock`, `ProductStripBlock`). Each is a thin composition of the hooks underneath: `StorefrontProvider`, `useProductList`/`useCategories`/`useRibbons`, `useProduct`/`useAddToCart`/`useProductPrice`/`useProductGallery`, `useProductReviews`, `useCart`/`useCartLine`/`useCoupon`, `useCheckout` + `useAddressForm`/`useTotalsLines`/`useCheckoutBlockers`, the headless `ShippingMethodPicker`/`PaymentMethodPicker`, `useOrderReturn`, and `useStorefrontSeo`. Needs React and nothing else.
15
+ - **Storefront React layer** (`src/commerce/storefront/`) — **headless: the logic is premade, the UI never is.** Nothing in the layer renders markup or carries CSS; every element, class and word of copy in the storefront you build is yours, so a brief like "make it feel like <site>" applies to the whole store, checkout included. What ships is every piece of logic that is the same in all stores, as hooks returning complete view-models: `StorefrontProvider`, `useProductList`/`useCategories`/`useRibbons`, `useProduct`/`useAddToCart`/`useAddToCartButton`/`useProductPrice`/`useProductGallery` (+ the `variantAxes`/`productSpecs` render-model helpers), `useProductReviews`, `useCart`/`useCartLine`/`useCoupon`, `useCheckout` + `useAddressForm`/`useTotalsLines`/`useCheckoutBlockers`, `useOrderReturn`, and `useStorefrontSeo` — plus three render-prop components that stay just as headless (`ShippingMethodPicker`/`PaymentMethodPicker` for the two checkout choices that are store data, `CartLine` for per-row cart bindings). Each hook's doc comment states the render rules that keep a store correct (an unbuyable variant option renders disabled, not hidden; a receipt page must render `paymentInstructions`; …). Needs React and nothing else.
16
16
  - **StoreAdmin agent + bot** — an AI copilot (`base44/agents/commerce/StoreAdmin.jsonc`, registered as `commerce/StoreAdmin`) with the `commerce/*` functions attached directly as tools (calls run as the chatting user → `requireAdmin()` still applies), variant-aware order editing, plus a chat panel in the admin sidebar with GFM markdown-table rendering.
17
17
  - **Docs** — this README plus the commerce skill folder [`skills/commerce/`](./skills/commerce/): [`SKILL.md`](./skills/commerce/SKILL.md) is the map every agent starts from (and the only path the platform needs to know); [`install/`](./skills/commerce/install/) holds the three stage files that are the whole install (`01-install` → `02-storefront` → `03-data`, each read at the moment its work starts and dropped when its checklist passes); [`references/`](./skills/commerce/references/) holds per-topic guides opened only on demand; [`docs/`](./skills/commerce/docs/) holds the data-model map ([`entities.md`](./skills/commerce/docs/entities.md)) and the two API references. The whole folder is installed into the app at `.agents/skills/commerce/` so agents pick it up natively.
18
18
 
@@ -32,8 +32,8 @@ base44-commerce-template/
32
32
  │ └── commerce/
33
33
  │ ├── admin/ React admin UI (copy into your app's src/commerce/)
34
34
  │ ├── utils/ storefront helpers — API client, variants, price/totals rules
35
- │ └── storefront/ storefront React layer — hooks (cart, checkout, catalog,
36
- └── blocks/ reviews, SEO) + default markup for the commodity UI
35
+ │ └── storefront/ storefront React layer — headless hooks (cart, checkout,
36
+ catalog, reviews, SEO); no markup or CSS ships
37
37
  ├── scripts/
38
38
  │ └── install.js static installer (run from <app>/examples/commerce/scripts/)
39
39
  ├── skills/
@@ -42,7 +42,7 @@ base44-commerce-template/
42
42
  │ ├── SKILL.md the map: what to read, when, and what to skip
43
43
  │ ├── install/ the whole install, in three staged files
44
44
  │ │ ├── 01-install.md files, admin mount, role gating, the schedule
45
- │ │ ├── 02-storefront.md storefront pages on the hooks + blocks
45
+ │ │ ├── 02-storefront.md storefront pages on the headless hooks
46
46
  │ │ └── 03-data.md seeding, shipping zones, images, payments decision
47
47
  │ ├── references/ opened on demand (catalog rendering, shipping & tax,
48
48
  │ │ online payments, reviews, store settings, emails,
@@ -105,7 +105,7 @@ If you build on Base44's hosted platform, use the Base44 agent/MCP to write the
105
105
  ## What's NOT included
106
106
 
107
107
  - **No storefront *design*.** The parts of a shopfront that carry a brand — the home page, the collection grid, the product card, the product page's layout, the theme — ship as nothing at all, on purpose: that is the work a build should spend its effort on. Everything under those surfaces does ship: the storefront API, the hooks, and default markup for the commodity UI (checkout, cart, totals, coupon field, reviews, order-received, and the product page's internals), all restylable and replaceable — see [`skills/commerce/install/02-storefront.md`](./skills/commerce/install/02-storefront.md) for how the two tiers fit together, [`skills/commerce/references/catalog-rendering.md`](./skills/commerce/references/catalog-rendering.md) for what each catalog call returns, and [`skills/commerce/docs/api-storefront.md`](./skills/commerce/docs/api-storefront.md) for the raw API.
108
- - **No payment provider — and cards are off by default.** The order side of card payments is premade (see above), but charging a card needs a provider, so `commerce/seed-store` enables the manual **`offline`** method (bank transfer, cash on delivery, pickup — no code, no credentials) and leaves the **`card`** gateway **switched off**. **Enable cards only if a provider is wired, or is about to be** — implement the four functions in `base44/shared/commerce/card-payment.ts` (for Stripe, copy the shipped `card-payment.stripe.ts` over it see `skills/commerce/references/online-payments.md`) and enable the gateway via the seed's `payment_methods: ["offline", "card"]`; enabled with nothing behind it, checkout answers `503 no_card_payment_provider`. The rule and why it belongs at the end of a build rather than its start: `skills/commerce/install/03-data.md`.
108
+ - **No payment provider — and cards are off by default.** The order side of card payments is premade (see above), but charging a card needs a provider, so `commerce/seed-store` enables the manual **`offline`** method (bank transfer, cash on delivery, pickup — no code, no credentials) and leaves the **`card`** gateway **switched off**. **Enable cards only if a provider is wired, or is about to be** — implement the four functions in `base44/shared/commerce/card-payment.ts`, or for Stripe copy the shipped `card-payment.stripe.ts` over it and use it as-is (`skills/commerce/references/online-payments.md`), then enable the gateway via the seed's `payment_methods: ["offline", "card"]`; enabled with nothing behind it, checkout answers `503 no_card_payment_provider`. The rule and why it belongs at the end of a build rather than its start: `skills/commerce/install/03-data.md`.
109
109
  - **No scheduled workflows shipped.** Base44 *does* have a scheduler, but this template ships no workflow files — time-based jobs (stock-hold release, cart expiry, webhook-log pruning) run **opportunistically** where possible, and for the rest you (or the Base44 agent) create scheduled workflows that call `commerce/admin-tools`/`commerce/admin-orders` actions — see [`skills/commerce/references/operations.md`](./skills/commerce/references/operations.md).
110
110
 
111
111
  ## Next steps
@@ -10,7 +10,7 @@
10
10
  {
11
11
  "name": "StoreAdmin",
12
12
  "description": "Store administration copilot for the commerce template: manage products, orders, refunds, coupons, customers, reviews, reports and maintenance.",
13
- "instructions": "You are StoreAdmin, the store administration copilot for this shop's back office. You help store operators inspect and manage the store: products, orders, refunds, coupons, customers, reviews, webhooks, reports, and maintenance.\n\n## How you access the store\nEvery tool takes a JSON body of the form {\"action\": \"<action>\", ...payload} (exception: commerce/seed-store takes {store_name, currency?, weight_unit?, dimension_unit?, with_sample_data?, products?, coupons?, locations?} with no action key — store_name is REQUIRED on a first seed, and products can bootstrap a whole catalog in one call, variants and ribbons included; see .agents/skills/commerce/docs/api-admin.md) and responds {success, data} or {success:false, error, code}. Search/list actions return {rows, has_next} using limit+skip pagination (there are no total counts). Use search actions for free-text lookups (product name, customer email, coupon code, order number).\n\nStore configuration (settings, Shipping & Tax Locations, payment gateways, webhook definitions) is not editable through your tools — see \"Sending the operator to a screen\" below.\n\n## Sending the operator to a screen\nSome configuration is only editable in the admin UI. When one of those is asked for, say plainly that you cannot change it from chat, name the screen, and give a link the operator can click — never just \"do it manually\", and never imply you tried and failed. Do not blame permissions or a security error: the reason is simply that the UI is the only place that configuration is edited.\n\nLinks use the `admin:` scheme with a path from the table below — `[Settings → Shipping & Tax](admin:settings/shipping-tax)`. The chat resolves that to wherever the admin is mounted, so never write `/store-admin/...` yourself.\n\n| Ask | Screen | Link |\n|---|---|---|\n| Currency, measurement units, payment return path | Settings → General | admin:settings/general |\n| Stock thresholds, hold minutes | Settings → Inventory | admin:settings/inventory |\n| Locations, shipping rates, tax groups, shipping tax | Settings → Shipping & Tax | admin:settings/shipping-tax |\n| Enabling a gateway, offline bank details | Settings → Payments | admin:settings/payments |\n| Store name (subjects + sender), admin + stock notification recipients, per-email overrides | Settings → Emails | admin:settings/emails |\n| Auto-approve reviews toggle | Products → Reviews | admin:products/reviews |\n| Creating or deleting a webhook (you CAN test and redeliver) | Settings → Webhooks | admin:settings/webhooks |\n\nExample: \"Tax rates aren't something I can change from here — they live per location. Open [Settings → Shipping & Tax](admin:settings/shipping-tax), edit the location and add the rate, then tell me and I'll re-check the order's totals.\"\n\nCatalog taxonomy is the opposite: you CAN create categories, ribbons, attributes and attribute values yourself with commerce/admin-products save-term. Never send the operator to a screen to create one — link to admin:products/categories if they want to review the category list by hand. Ribbons and attributes have no screen of their own: attributes are edited in the Attributes section of a product's Price & Inventory section, ribbons in the Ribbons card of the product sidebar — so link to the product (admin:products) instead.\n\n## Product variants — be careful\nA product that carries attributes is sold through its variants (there is no product type), and every variant can differ in attributes (e.g. size/color), SKU, price and stock.\n- NEVER pick a variation automatically. When an order line, stock change, or download grant involves a product that carries attributes, first fetch its variations with commerce/storefront-catalog {\"action\":\"get-product\",\"id\":...} (returns {product, variations}), present them in a table (attributes, SKU, price, stock status), and ask the operator which variation to use — then include that variation_id in the item spec.\n- If the operator already named an exact variation (by SKU or full attribute combination), match it against the fetched variations and confirm the match in your reply; if the description is partial or matches more than one variation, ask.\n- Order item specs for commerce/admin-orders create/update are {product_id, variation_id?, quantity, price_override?} — variation_id is REQUIRED for a product with attributes.\n- The same applies to commerce/admin-products set-stock (pass variation_id to change a variation's stock, not the parent's).\n\n## Behavior\n- Be concise and operational. Confirm before destructive or irreversible operations (delete, refund, bulk-status, prune, clear-abandoned-carts) by restating what will happen and asking the user to confirm — unless the user's message already explicitly confirms it.\n- When showing lists or reports, format them as GitHub-flavored markdown tables (| col | col | with a |---| separator row). Keep tables ≤ 8 columns; prefer the most decision-relevant fields (name/number, status, total, date). Format money with the store currency.\n- After a mutation, report exactly what changed (ids, statuses, totals) and surface any error/code verbatim.\n- If a request is ambiguous (which order? which product?), search first and present the candidates in a table, then ask.\n- For store health questions, start with commerce/admin-tools {\"action\":\"status\"} and commerce/admin-reports {\"action\":\"summary\"}.\n- Payments: for an unpaid order paid online, commerce/payments create-link {order_id} gives a payment page link to send the customer, and verify {order_id} re-checks whether the money arrived. If no payment provider is connected, say \"no payment provider is connected\" and that connecting one enables card payments — don't name or troubleshoot a specific provider. Never invent a payment link or claim an order is paid without verifying.\n- You act with store-operator privileges; do not attempt to weaken or bypass access controls, and never expose secrets (webhook secrets, tokens).",
13
+ "instructions": "You are StoreAdmin, the store administration copilot for this shop's back office. You help store operators inspect and manage the store: products, orders, refunds, coupons, customers, reviews, webhooks, reports, and maintenance.\n\n## How you access the store\nEvery tool takes a JSON body of the form {\"action\": \"<action>\", ...payload} (exception: commerce/seed-store takes {store_name, currency?, weight_unit?, dimension_unit?, with_sample_data?, products?, coupons?, locations?} with no action key — store_name is REQUIRED on a first seed, and products can bootstrap a whole catalog in one call, variants and ribbons included; see the commerce skill's docs/api-admin.md) and responds {success, data} or {success:false, error, code}. Search/list actions return {rows, has_next} using limit+skip pagination (there are no total counts). Use search actions for free-text lookups (product name, customer email, coupon code, order number).\n\nStore configuration (settings, Shipping & Tax Locations, payment gateways, webhook definitions) is not editable through your tools — see \"Sending the operator to a screen\" below.\n\n## Sending the operator to a screen\nSome configuration is only editable in the admin UI. When one of those is asked for, say plainly that you cannot change it from chat, name the screen, and give a link the operator can click — never just \"do it manually\", and never imply you tried and failed. Do not blame permissions or a security error: the reason is simply that the UI is the only place that configuration is edited.\n\nLinks use the `admin:` scheme with a path from the table below — `[Settings → Shipping & Tax](admin:settings/shipping-tax)`. The chat resolves that to wherever the admin is mounted, so never write `/store-admin/...` yourself.\n\n| Ask | Screen | Link |\n|---|---|---|\n| Currency, measurement units, payment return path | Settings → General | admin:settings/general |\n| Stock thresholds, hold minutes | Settings → Inventory | admin:settings/inventory |\n| Locations, shipping rates, tax groups, shipping tax | Settings → Shipping & Tax | admin:settings/shipping-tax |\n| Enabling a gateway, offline bank details | Settings → Payments | admin:settings/payments |\n| Store name (subjects + sender), admin + stock notification recipients, per-email overrides | Settings → Emails | admin:settings/emails |\n| Auto-approve reviews toggle | Products → Reviews | admin:products/reviews |\n| Creating or deleting a webhook (you CAN test and redeliver) | Settings → Webhooks | admin:settings/webhooks |\n\nExample: \"Tax rates aren't something I can change from here — they live per location. Open [Settings → Shipping & Tax](admin:settings/shipping-tax), edit the location and add the rate, then tell me and I'll re-check the order's totals.\"\n\nCatalog taxonomy is the opposite: you CAN create categories, ribbons, attributes and attribute values yourself with commerce/admin-products save-term. Never send the operator to a screen to create one — link to admin:products/categories if they want to review the category list by hand. Ribbons and attributes have no screen of their own: attributes are edited in the Attributes section of a product's Price & Inventory section, ribbons in the Ribbons card of the product sidebar — so link to the product (admin:products) instead.\n\n## Product variants — be careful\nA product that carries attributes is sold through its variants (there is no product type), and every variant can differ in attributes (e.g. size/color), SKU, price and stock.\n- NEVER pick a variation automatically. When an order line, stock change, or download grant involves a product that carries attributes, first fetch its variations with commerce/storefront-catalog {\"action\":\"get-product\",\"id\":...} (returns {product, variations}), present them in a table (attributes, SKU, price, stock status), and ask the operator which variation to use — then include that variation_id in the item spec.\n- If the operator already named an exact variation (by SKU or full attribute combination), match it against the fetched variations and confirm the match in your reply; if the description is partial or matches more than one variation, ask.\n- Order item specs for commerce/admin-orders create/update are {product_id, variation_id?, quantity, price_override?} — variation_id is REQUIRED for a product with attributes.\n- The same applies to commerce/admin-products set-stock (pass variation_id to change a variation's stock, not the parent's).\n\n## Behavior\n- Be concise and operational. Confirm before destructive or irreversible operations (delete, refund, bulk-status, prune, clear-abandoned-carts) by restating what will happen and asking the user to confirm — unless the user's message already explicitly confirms it.\n- When showing lists or reports, format them as GitHub-flavored markdown tables (| col | col | with a |---| separator row). Keep tables ≤ 8 columns; prefer the most decision-relevant fields (name/number, status, total, date). Format money with the store currency.\n- After a mutation, report exactly what changed (ids, statuses, totals) and surface any error/code verbatim.\n- If a request is ambiguous (which order? which product?), search first and present the candidates in a table, then ask.\n- For store health questions, start with commerce/admin-tools {\"action\":\"status\"} and commerce/admin-reports {\"action\":\"summary\"}.\n- Payments: for an unpaid order paid online, commerce/payments create-link {order_id} gives a payment page link to send the customer, and verify {order_id} re-checks whether the money arrived. If no payment provider is connected, say \"no payment provider is connected\" and that connecting one enables card payments — don't name or troubleshoot a specific provider. Never invent a payment link or claim an order is paid without verifying.\n- You act with store-operator privileges; do not attempt to weaken or bypass access controls, and never expose secrets (webhook secrets, tokens).",
14
14
  "tool_configs": [
15
15
  {
16
16
  "function_name": "commerce/admin-products",
@@ -21,7 +21,7 @@
21
21
  "refunded_payment": {
22
22
  "type": "boolean",
23
23
  "default": false,
24
- "description": "True when a real refund was made at the payment provider (via shared/commerce/card-payment.ts refundCardPayment — see .agents/skills/commerce/references/online-payments.md)"
24
+ "description": "True when a real refund was made at the payment provider (via shared/commerce/card-payment.ts refundCardPayment — see the commerce skill's references/online-payments.md)"
25
25
  },
26
26
  "restock_items": {
27
27
  "type": "boolean",
@@ -35,7 +35,7 @@
35
35
  },
36
36
  "settings": {
37
37
  "type": "object",
38
- "description": "Per-gateway settings. offline: {account_details: [{account_name, account_number, bank_name, sort_code, iban, bic}]}; card: {} — provider config lives in code and backend secrets, never here; see .agents/skills/commerce/references/online-payments.md"
38
+ "description": "Per-gateway settings. offline: {account_details: [{account_name, account_number, bank_name, sort_code, iban, bic}]}; card: {} — provider config lives in code and backend secrets, never here; see the commerce skill's references/online-payments.md"
39
39
  }
40
40
  },
41
41
  "required": ["slug"],
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "secret": {
36
36
  "type": "string",
37
- "description": "HMAC-SHA256 signing secret (see .agents/skills/commerce/references/webhooks.md on storage tradeoffs)"
37
+ "description": "HMAC-SHA256 signing secret (see the commerce skill's references/operations.md on storage tradeoffs)"
38
38
  },
39
39
  "api_version": {
40
40
  "type": "string",
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Actions: save | delete | batch | duplicate | set-stock | search |
6
6
  * save-term | delete-term | list-terms
7
- * Body: { action, ...payload } — see .agents/skills/commerce/docs/api-admin.md.
7
+ * Body: { action, ...payload } — see the commerce skill's docs/api-admin.md.
8
8
  */
9
9
  import { createClientFromRequest } from "npm:@base44/sdk";
10
10
  import { HttpError, requireAdmin } from "../../../shared/commerce/auth.ts";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * commerce/admin-reports — on-demand report aggregation over orders.
3
3
  * Fine to ~10k orders; larger stores should materialize stats
4
- * (see .agents/skills/commerce/references/operations.md).
4
+ * (see the commerce skill's references/operations.md).
5
5
  *
6
6
  * Actions: summary | sales | top-sellers | stock |
7
7
  * orders-totals | products-totals | customers-totals | coupons-totals |
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * commerce/payments — online payment for an order.
3
3
  *
4
- * The provider specifics live in the two implementable files (see
5
- * `.agents/skills/commerce/references/online-payments.md`); this function is
4
+ * The provider specifics live in the two implementable files (see the commerce
5
+ * skill's references/online-payments.md); this function is
6
6
  * the premade API the storefront and the admin use.
7
7
  *
8
8
  * Actions:
@@ -42,7 +42,7 @@ export const SETTINGS_DEFAULTS: Array<{ group_id: string; values: Record<string,
42
42
  values: {
43
43
  // The one review switch the server enforces; every other review policy
44
44
  // (login-gated, verified-only, required rating) belongs to the storefront
45
- // UI — see .agents/skills/commerce/references/reviews.md.
45
+ // UI — see the commerce skill's references/reviews.md.
46
46
  auto_approve_reviews: false,
47
47
  },
48
48
  },
@@ -375,7 +375,7 @@ async function getStoreInfo(sr: any): Promise<any> {
375
375
  * signed-in session supplies the email (and can't spoof another); a guest
376
376
  * passes `email` in the payload. Stricter policies (login-gated forms,
377
377
  * verified-buyers-only, required ratings) are the storefront's to enforce in
378
- * its UI — see .agents/skills/commerce/references/reviews.md. The one server switch is
378
+ * its UI — see the commerce skill's references/reviews.md. The one server switch is
379
379
  * auto-approval; everything else submits as `hold` for moderation.
380
380
  */
381
381
  async function submitReview(sr: any, p: any, user: any): Promise<any> {
@@ -220,7 +220,7 @@ async function placeOrder(sr: any, req: Request, user: any, payload: any): Promi
220
220
  // (4) customer upsert by billing email
221
221
  const notices: string[] = [];
222
222
  if (payload.create_account && !user) {
223
- notices.push("account_creation_requires_login"); // see .agents/skills/commerce/references/guest-access-security.md
223
+ notices.push("account_creation_requires_login"); // see the commerce skill's references/guest-access-security.md
224
224
  }
225
225
  const customer = await upsertCustomer(sr, billing, shippingAddress, user);
226
226
 
@@ -11,13 +11,14 @@
11
11
  * "base44/shared/commerce/card-payment.ts",
12
12
  * );
13
13
  *
14
- * then (1) store the merchant's Stripe **secret key** as the `STRIPE_SECRET_KEY`
15
- * app secret and redeploy the backend so the functions see it, (2) register
16
- * `https://<app-domain>/functions/commerce/payment-webhook` with Stripe for
17
- * `checkout.session.completed` — there is no signing secret to store, see
18
- * `parseWebhook` below and (3) enable the `card` gateway
19
- * (`commerce/seed-store` with `payment_methods: ["offline", "card"]`), which is
20
- * what makes any of this visible at checkout.
14
+ * and **enable the `card` gateway** `commerce/seed-store` with
15
+ * `payment_methods: ["offline", "card"]`. It ships disabled, so that is what
16
+ * makes card payment visible at checkout.
17
+ *
18
+ * That is the whole job. This file assumes the app is connected to Stripe and
19
+ * reads the secret key the connection publishes (`stripeSecret()` below); how
20
+ * the app gets connected is not this file's concern and nothing here needs
21
+ * filling in.
21
22
  *
22
23
  * NEVER patch this file (or the stub it replaces) with partial edits. A
23
24
  * find_replace that leaves the originals behind gives every commerce function a
@@ -56,10 +57,29 @@ export interface CardPaymentPage {
56
57
  * the secret answers a clean 503 at checkout instead of failing to boot every
57
58
  * commerce function that imports it.
58
59
  */
60
+ /**
61
+ * Stripe's secret key, as published to the backend environment by the app's
62
+ * Stripe connection. The names below are the conventional ones; if the key
63
+ * arrives under a different name, this list is the only thing to change.
64
+ */
65
+ const STRIPE_KEY_VARS = ["STRIPE_SECRET_KEY", "STRIPE_API_KEY", "STRIPE_KEY"];
66
+
67
+ const stripeSecret = () => {
68
+ for (const name of STRIPE_KEY_VARS) {
69
+ const value = Deno.env.get(name);
70
+ if (value) return value;
71
+ }
72
+ return null;
73
+ };
74
+
59
75
  const stripe = () => {
60
- const key = Deno.env.get("STRIPE_SECRET_KEY");
76
+ const key = stripeSecret();
61
77
  if (!key) {
62
- throw new HttpError(503, "Card payments are not configured — the STRIPE_SECRET_KEY secret is missing.", "no_card_payment_provider");
78
+ throw new HttpError(
79
+ 503,
80
+ `Card payments are not configured — no Stripe secret key in the backend environment (looked for ${STRIPE_KEY_VARS.join(", ")}).`,
81
+ "no_card_payment_provider",
82
+ );
63
83
  }
64
84
  return new Stripe(key);
65
85
  };
@@ -11,7 +11,7 @@
11
11
  * NO PROVIDER SHIPS WITH THE TEMPLATE, and card payments are off by default
12
12
  * (the `card` gateway is seeded disabled) — this file stays stubs unless a
13
13
  * store deliberately opts into online cards. When one does, any provider
14
- * works: `.agents/skills/commerce/references/online-payments.md` has the
14
+ * works: the commerce skill's references/online-payments.md has the
15
15
  * rules, plus a complete Stripe implementation to paste over this file if
16
16
  * Stripe is the provider chosen. Another provider implements the same four
17
17
  * functions against its own API.
@@ -7,8 +7,8 @@
7
7
  * event). No provider ships with the template and the `card` gateway is
8
8
  * seeded disabled, so this half is dormant until a store opts into online
9
9
  * cards. Any provider works; the rules — and a complete Stripe
10
- * implementation, for that one common choice — are in
11
- * `.agents/skills/commerce/references/online-payments.md`.
10
+ * implementation, for that one common choice — are in the commerce skill's
11
+ * references/online-payments.md.
12
12
  *
13
13
  * Everything here — return URLs, storing the payment reference on the order,
14
14
  * idempotent confirmation that moves the order to processing, refund routing —
@@ -2,7 +2,7 @@
2
2
  * Full-collection scan helper. Base44's filter() is exact-match with a 5k page
3
3
  * cap and no total count, so server-side search/aggregation loops pages of 500.
4
4
  * Keep `cap` sane — reports over very large stores should move to a
5
- * materialized stats entity (see .agents/skills/commerce/references/operations.md).
5
+ * materialized stats entity (see the commerce skill's references/operations.md).
6
6
  */
7
7
 
8
8
  export interface ScanOpts {
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * NOTE ON CONCURRENCY: Base44 has no transactions, so nextOrderNumber() is a
5
5
  * read-max-then-increment and two simultaneous checkouts could race. The window
6
- * is tiny and order ids (not numbers) are the primary key; see
7
- * .agents/skills/commerce/references/operations.md for mitigations.
6
+ * is tiny and order ids (not numbers) are the primary key; see the commerce
7
+ * skill's references/operations.md for mitigations.
8
8
  */
9
9
 
10
10
  const ORDER_NUMBER_START = 1001;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@base44/app-plugin-commerce",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Base44 Commerce plugin — entities, backend functions, shared commerce engine, admin UI and the commerce skill, shipped as copyable source",
5
5
  "keywords": [
6
6
  "base44",
@@ -203,7 +203,7 @@
203
203
  " 1. No deps to add: sonner, recharts and react-markdown ship with the default\n" +
204
204
  " Base44 template — check package.json and npm i only what is truly missing\n" +
205
205
  ' 2. Mount the admin router: <Route path="/store-admin/*" element={<AdminApp />} />\n' +
206
- " plus the mandatory /order-received route (<OrderReceivedBlock/>)\n" +
206
+ " plus the mandatory /order-received route (useOrderReturn + your markup)\n" +
207
207
  " 3. Seed the store — one commerce/seed-store call (store_name required) takes the\n" +
208
208
  " catalog, currency, shipping locations and payment methods\n" +
209
209
  " 4. CLI installs only: npx base44 agents push (the hosted runtime syncs agents on write)"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: commerce
3
- description: Base44 Commerce — commerce.* entities, commerce/* backend functions (storefront + admin APIs, payments, webhooks), the shared engine under base44/shared/commerce/, the Store Management UI at /store-admin, and the storefront hooks + UI blocks under src/commerce/. Read before working on store features: installing, building a storefront, seeding a catalog, shipping zones, payments, or the admin.
3
+ description: Base44 Commerce — commerce.* entities, commerce/* backend functions (storefront + admin APIs, payments, webhooks), the shared engine under base44/shared/commerce/, the Store Management UI at /store-admin, and the headless storefront hooks under src/commerce/. Read before working on store features: installing, building a storefront, seeding a catalog, shipping zones, payments, or the admin.
4
4
  ---
5
5
 
6
6
  # Commerce
@@ -11,9 +11,19 @@ Nothing else in this skill needs to be open right now.
11
11
  The kit ships, already written and tested: 20 `commerce.*` entities, the
12
12
  `commerce/*` backend functions (storefront API, admin API, payments, webhooks,
13
13
  emails), the shared commerce engine, a complete admin back office at
14
- `/store-admin`, the `commerce/StoreAdmin` copilot, and the storefront's logic
15
- and commodity UI (`@/commerce/storefront`, `@/commerce/utils`). None of that
16
- costs you generation — your work is the *store*, not the plumbing.
14
+ `/store-admin`, the `commerce/StoreAdmin` copilot, and the storefront's entire
15
+ logic layer (`@/commerce/storefront`, `@/commerce/utils`). None of that costs
16
+ you generation — your work is the store's *UI and data*, not the plumbing.
17
+
18
+ **Paths in this skill.** Every skill file named anywhere — in these docs, in a
19
+ checklist, in a code comment — is written **relative to this skill's folder**
20
+ (`.agents/skills/commerce/` in an installed app). So `references/online-payments.md`
21
+ means `.agents/skills/commerce/references/online-payments.md`, and
22
+ `install/03-data.md` means `.agents/skills/commerce/install/03-data.md`. Resolve
23
+ them from the skill folder, never from the directory of the file that mentioned
24
+ them — a comment in `src/…` naming `install/03-data.md` is not
25
+ `src/install/03-data.md`. (Markdown links between skill files are ordinary
26
+ relative links and resolve as written.)
17
27
 
18
28
  ## Installing right now?
19
29
 
@@ -42,25 +52,25 @@ and each one says when a reference is genuinely needed.
42
52
  step, and the decision plus the timing live in
43
53
  [`install/03-data.md`](./install/03-data.md).
44
54
 
45
- ## The storefront: two tiers
55
+ ## The storefront: logic is premade, UI never is
46
56
 
47
- **Identity surfaces are yours to design** — the home page, the collection grid,
48
- the product page's layout, the product card, the theme. That is where a brief
49
- like "make it feel like <site>" lives, and the kit deliberately ships no markup
50
- for it.
57
+ **The UI is yours, all of it** — every page, element, class and word of copy,
58
+ from the home page to the checkout's place-order button. That is where a brief
59
+ like "make it feel like <site>" lives, and the kit deliberately ships **no
60
+ markup and no CSS anywhere**: there are no premade components to drop in or
61
+ restyle. You design the storefront the way you would any app.
51
62
 
52
- **Commodity surfaces ship as working blocks** that inherit your theme — checkout,
53
- cart lines, totals, the coupon field, reviews, order-received, and the product
54
- page's internals (variant selector, gallery, specs, breadcrumbs, product
55
- strips). Every store's version of these is functionally identical. Restyle them
56
- with your CSS, swap one region with `slots`, or drop to the hooks each block is
57
- built from when the design needs different structure but never hand-roll their
58
- logic.
63
+ **The logic ships as headless hooks** (`@/commerce/storefront`) — checkout
64
+ repricing from the address, variant resolution, cart state, coupon redemption,
65
+ review policies, order-return verification. Every store's version of these is
66
+ functionally identical and hand-writing them is where storefront bugs cluster,
67
+ so **never re-implement what a hook does**. Each hook returns a complete
68
+ view-model a `status` to branch on, ready-to-map arrays, handlers, error
69
+ objects — that your markup renders:
59
70
 
60
71
  ```jsx
61
- import { CheckoutBlock } from "@/commerce/storefront";
62
- // the whole checkout page body:
63
- <main className="mx-auto max-w-3xl px-6 py-16"><CheckoutBlock /></main>
72
+ import { useCart, CartLine, useTotalsLines } from "@/commerce/storefront";
73
+ const { status, lines } = useCart(); // branch on status, map lines into YOUR rows
64
74
  ```
65
75
 
66
76
  The admin UI (`src/commerce/admin/`) is finished and **also yours to change** —
@@ -68,16 +78,16 @@ restyle it, add pages, rework flows. To understand the backend it talks to, read
68
78
  [`docs/api-admin.md`](./docs/api-admin.md).
69
79
 
70
80
  Four rules the API enforces — a storefront that skips them cannot complete a
71
- purchase. In a React app the blocks and hooks above already implement all four;
72
- the API-level statements are in
81
+ purchase. In a React app the hooks implement all four; your markup just has to
82
+ render what they hand back. The API-level statements are in
73
83
  [`docs/api-storefront.md`](./docs/api-storefront.md) for non-React clients:
74
84
 
75
85
  1. A product with variants needs **one selector per attribute**, resolved to a
76
- `variation_id` (`<VariantSelectorBlock/>` / `useProduct`).
86
+ `variation_id` (`useProduct` + `variantAxes`).
77
87
  2. Checkout must **recalculate shipping from the address and send a choice**
78
- (`<CheckoutBlock/>` / `useCheckout`).
88
+ (`useCheckout` — automatic).
79
89
  3. **`/order-received` must exist** and render the return state, including a
80
- manual order's payment instructions (`<OrderReceivedBlock/>`).
90
+ manual order's payment instructions (`useOrderReturn`).
81
91
  4. **Never advertise what isn't configured** — no free-shipping banner without a
82
92
  real rate, no coupon codes without a field to redeem them in.
83
93
 
@@ -103,7 +113,7 @@ so you can answer "would that file help?" without paying for it.
103
113
  | [`references/catalog-rendering.md`](./references/catalog-rendering.md) | which fields each catalog call returns, variant edge cases | the install's product list/page chunks already render correct cards, prices and selectors | 12K |
104
114
  | [`references/shipping-and-tax.md`](./references/shipping-and-tax.md) | zones beyond the standard recipe, taxes, editing locations later | "€X in a region, €Y worldwide" is inline in `install/03-data.md` | 8K |
105
115
  | [`references/online-payments.md`](./references/online-payments.md) | the store opted into cards and you are wiring the provider **now** | the decision and its timing are in `install/03-data.md`; wiring Stripe is a one-file copy, not code to write | 9K |
106
- | [`references/reviews.md`](./references/reviews.md) | moderation, or a policy beyond the `policy` prop | `<ReviewsBlock/>` covers list + form + policies | 4K |
116
+ | [`references/reviews.md`](./references/reviews.md) | moderation, or a policy beyond the `policy` prop | `useProductReviews` covers list + form + policies | 4K |
107
117
  | [`references/store-settings.md`](./references/store-settings.md) | changing store behavior through settings keys | the seeded defaults are right for a new store | 5K |
108
118
  | [`references/emails.md`](./references/emails.md) | order-email recipients, subjects, per-type overrides, the log | transactional emails already send | 5K |
109
119
  | [`references/admin-product-form.md`](./references/admin-product-form.md) | editing the shipped product editor | the editor works as shipped | 4K |
@@ -119,4 +129,4 @@ opened at the moment it is used. Open one reference when its task starts, take
119
129
  what you need, and when a stage's checklist passes, record that stage's
120
130
  carry-forward lines and treat the file as gone. If a reference and this map
121
131
  disagree, the reference wins — but if a *rule* appears in code (a hook's return
122
- value, a block's markup), the code wins over both.
132
+ value, its doc comment), the code wins over both.
@@ -2,7 +2,7 @@
2
2
 
3
3
  The raw HTTP/SDK surface behind a customer-facing shopfront. **A React storefront should not call most of it directly** — `src/commerce/storefront/` ships the logic layer, and this file is the reference for what lies beyond it, for non-React and headless clients, and for the exact payloads and error codes.
4
4
 
5
- **Two tiers.** The *identity* surfaces home, the collection grid, the product page's layout, the product card, the themeare yours to design; build them on the hooks (`useProductList`, `useProduct`, `useProductPrice`, `useStorefrontSeo`). The *commodity* surfaces checkout, cart, order-received, reviews, and the product page's internals ship as **blocks** that inherit your theme (`CheckoutBlock`, `CartLinesBlock`, `TotalsBlock`, `CouponFieldBlock`, `OrderReceivedBlock`, `ReviewsBlock`, `VariantSelectorBlock`, …). Restyle or replace them; never hand-roll their logic. Framework-free helpers (API client, variant resolution, free-shipping rules) live in `src/commerce/utils/`.
5
+ **Logic is premade, UI never is.** In a React app, every surface here has a headless hook in `@/commerce/storefront``useProductList`, `useProduct` (+ `variantAxes`, `useAddToCartButton`), `useCart`/`useCartLine`/`useCoupon`, `useCheckout` (+ `useAddressForm`, the pickers), `useOrderReturn`, `useProductReviews`, `useStorefrontSeo`. The hooks own the API calls and the branching below and hand you a view-model; **all markup and styling are yours** nothing in the kit renders UI. Never hand-roll a hook's logic. Framework-free helpers (API client, variant resolution, price and totals rules, free-shipping rules) live in `src/commerce/utils/`.
6
6
 
7
7
  ## Contents
8
8
 
@@ -49,7 +49,7 @@ Four public functions — **`storefront-catalog`**, **`storefront-cart`**, **`st
49
49
  | **Digital products** | entitlement-checked downloads with remaining-count, expiry, signed URLs for private files | [`get-download`](#commercestorefront-account) |
50
50
  | **Store config** | currency, units, catalog/cart price display — honour it instead of hardcoding | [`get-store-info`](#get-store-info) |
51
51
 
52
- Not in the backend: the visitor UI (identity tier yours; commodity tier the blocks), and the card-payment provider integration, which only a store that opts into cards needs — cards are off by default, the decision and its timing live in [`../install/03-data.md`](../install/03-data.md), the provider code in [`../references/online-payments.md`](../references/online-payments.md). Payment methods and the currency are admin-owned data — always render them from `get-store-info`, never a hardcoded list, and format prices with `Intl.NumberFormat(undefined, { style: "currency", currency })`.
52
+ Not in the backend: the visitor UI (all of it is yours the hooks above carry the logic), and the card-payment provider integration, which only a store that opts into cards needs — cards are off by default, the decision and its timing live in [`../install/03-data.md`](../install/03-data.md), the provider code in [`../references/online-payments.md`](../references/online-payments.md). Payment methods and the currency are admin-owned data — always render them from `get-store-info`, never a hardcoded list, and format prices with `Intl.NumberFormat(undefined, { style: "currency", currency })`.
53
53
 
54
54
  ## Conventions
55
55
 
@@ -131,7 +131,7 @@ await cat({ category_id, sort: "popularity", per_page: 4 }); // top in catego
131
131
  ```
132
132
  **Errors:** `404 not_found` (missing / not published / hidden).
133
133
 
134
- > **`variations[]` is not a list of choices to show.** Build **one control per `product.attributes[]` entry** (Size, Color, …) — every attribute is an axis — and resolve the combination to a variation client-side; `add-item` needs that `variation_id`. Variant prices come from `variations[]`, never from `product.price` (the parent's price is a rolled-up from-price). A React page gets all of this from `useProduct` + `useAddToCart` (render `view.axes`, gate on `view.purchasable`) or `<VariantSelectorBlock/>`; a non-React client uses the framework-free resolver:
134
+ > **`variations[]` is not a list of choices to show.** Build **one control per `product.attributes[]` entry** (Size, Color, …) — every attribute is an axis — and resolve the combination to a variation client-side; `add-item` needs that `variation_id`. Variant prices come from `variations[]`, never from `product.price` (the parent's price is a rolled-up from-price). A React page gets all of this from `useProduct` + `useAddToCartButton` (map `variantAxes(view, pick)` into your controls, gate on `view.purchasable`); a non-React client uses the framework-free resolver:
135
135
  > ```js
136
136
  > import { resolveSelection, defaultSelection, selectOption } from "@/commerce/utils";
137
137
  > const view = resolveSelection(product, variations, selection); // selection from defaultSelection(), updated via selectOption()
@@ -159,7 +159,7 @@ No payload. Returns `{ "attributes": [ { ...attribute, "terms": [ ...values ] }
159
159
  ### `submit-review`
160
160
  **Payload:** `{ product_id, reviewer?, review, rating }`.
161
161
 
162
- > A React storefront gets the whole reviews surface — this call, the paginated list from `get-product`, refresh, and the store's moderation policy — from **`useProductReviews`** / **`<ReviewsBlock/>`**; don't hand-roll it. Read on for the raw contract.
162
+ > A React storefront gets the whole reviews surface — this call, the paginated list from `get-product`, refresh, and the store's moderation policy — from **`useProductReviews`**; don't hand-roll it. Read on for the raw contract.
163
163
 
164
164
  **Public by default: anyone can review with an email address — no login.** A guest passes `email`; for a signed-in caller the session email always wins (the payload cannot impersonate). `reviewer` is the display name only, defaulting to the account's `full_name` then the email's local part. `rating` is optional (0–5). `verified` is derived from the email's order history. Status is `hold` unless `products.auto_approve_reviews` — the one server-side switch, so a hardcoded "awaiting approval" message is wrong when it is on. Stricter policies (login-gated, verified buyers only, rating required) are a `policy` option on the hook, not code you write: [`../references/reviews.md`](../references/reviews.md).
165
165
 
@@ -318,7 +318,7 @@ Separate function, same guest-bearer rule: `order_id` + `order_key` (an admin ma
318
318
  | `complete-return` | `{ order_id, order_key, payment?, return_url? }` | `{ state: "paid"\|"cancelled"\|"unpaid", paid, already_confirmed, status, order, payment_link, payment_instructions }` — the whole return flow in one call: confirms with the provider, progresses the order, adds a fresh `payment_link: { url, reference }` while a card order is unpaid, and re-supplies `payment_instructions: { type, description, account_details }` for unpaid **manual** orders so bank details render on every visit. `payment` is only a hint — a hand-edited `?payment=success` can never yield `paid`. **`order` carries flat totals** (`order.total`, `order.shipping_total`); there is no `order.totals` — that shape belongs to the cart view |
319
319
  | `verify` | `{ order_id, order_key }` | `{ paid, already_confirmed, status, order }` — the same confirmation without the render-ready extras. **Idempotent**; money is asked of the provider about the reference stored on the order |
320
320
 
321
- > **The `/order-received` route is mandatory** (requirement 4): without it a paying customer lands on a 404 *and* the order is never marked paid. It only has to call `complete-return` and render its three states — `<OrderReceivedBlock/>` / `useOrderReturn` do exactly that.
321
+ > **The `/order-received` route is mandatory** (requirement 4): without it a paying customer lands on a 404 *and* the order is never marked paid. It only has to call `complete-return` and render its three states — `useOrderReturn` does exactly that (your markup renders the states, including `paymentInstructions`).
322
322
 
323
323
  Provider callbacks land on `commerce/payment-webhook` (server-to-server) — the second confirmation path, for buyers who pay and close the tab; whichever path runs second is a no-op. That function is premade (it verifies through the provider's API, so no signing secret). Wiring a provider: [`../references/online-payments.md`](../references/online-payments.md).
324
324
 
@@ -354,7 +354,7 @@ Two access modes: **auth** (Base44 session) or **`order_key` bearer** (guest tra
354
354
 
355
355
  ## Walkthrough A — guest checkout
356
356
 
357
- Raw-call sequence for a non-React client (a React app gets this from `useCart` + `CheckoutBlock`).
357
+ Raw-call sequence for a non-React client (a React app gets this from `useCart` + `useCheckout`).
358
358
 
359
359
  ```js
360
360
  const inv = (fn, payload) => base44.functions.invoke(fn, payload).then(r => r.data.data);
@@ -21,7 +21,7 @@ What lands in the app, how the admin gets mounted, and the order to do the rest
21
21
  | `base44/entities/commerce.*.jsonc` | 20 entity schemas, all admin-only RLS |
22
22
  | `base44/functions/commerce/` + `shared/` + `agents/` | 16 functions, the engine, the StoreAdmin copilot |
23
23
  | `src/commerce/admin/` | the finished admin app — **don't validate it, it ships tested** |
24
- | `src/commerce/storefront/` + `utils/` | the hooks and blocks you build against ([`./02-storefront.md`](./02-storefront.md)) |
24
+ | `src/commerce/storefront/` + `utils/` | the headless hooks you build the storefront UI on ([`./02-storefront.md`](./02-storefront.md)) |
25
25
  | `.agents/skills/commerce/` | these docs |
26
26
 
27
27
  <details>
@@ -61,7 +61,7 @@ import { Navigate } from "react-router-dom";
61
61
 
62
62
  - **The `/*` splat is required.** The admin renders nested routes; a bare `path="/store-admin"` matches only the dashboard and every deeper link 404s. Mounting elsewhere: `<AdminApp basePath="/backoffice" />` — the prefix *without* the splat.
63
63
  - **Give `/` something.** A blank Base44 app has no `/` route, so after mounting only the admin the app's own URL renders "page not found", which reads exactly like a broken install. Redirect until the storefront exists.
64
- - **`/order-received` is mandatory**, even for a store that only ever takes offline payments. Every payment link (checkout, the admin's payment link, emails) returns there, and confirming is what marks an order paid — without the route a paying customer hits a 404 and the order stays unpaid. The page body is one block, `<OrderReceivedBlock />` ([`./02-storefront.md`](./02-storefront.md)). A different path must be set in Settings → General → *Payment return path*.
64
+ - **`/order-received` is mandatory**, even for a store that only ever takes offline payments. Every payment link (checkout, the admin's payment link, emails) returns there, and confirming is what marks an order paid — without the route a paying customer hits a 404 and the order stays unpaid. The page is one hook, `useOrderReturn()`, plus your markup for its states ([`./02-storefront.md`](./02-storefront.md)). A different path must be set in Settings → General → *Payment return path*.
65
65
 
66
66
  ## Admin-role enforcement — do not weaken
67
67