@base44/app-plugin-commerce 0.1.15 → 0.1.17
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.
- package/README.md +11 -8
- package/base44/functions/commerce/seed-store/defaults.ts +6 -4
- package/base44/functions/commerce/seed-store/entry.ts +23 -18
- package/base44/functions/commerce/seed-store/seed-catalog.ts +150 -77
- package/base44/shared/commerce/card-payment.ts +1 -1
- package/base44/shared/commerce/payments.ts +1 -1
- package/package.json +1 -1
- package/scripts/install.js +8 -2
- package/skills/commerce/SKILL.md +9 -8
- package/skills/commerce/docs/api-admin.md +2 -2
- package/skills/commerce/docs/api-storefront.md +4 -4
- package/skills/commerce/installation-guidelines.md +4 -3
- package/skills/commerce/post-installation.md +341 -272
- package/skills/commerce/references/online-payments.md +5 -3
- package/src/commerce/admin/pages/settings/PaymentsSettings.jsx +1 -1
- package/src/commerce/storefront/StorefrontProvider.jsx +216 -0
- package/src/commerce/storefront/address.js +70 -0
- package/src/commerce/storefront/index.js +51 -0
- package/src/commerce/storefront/pickers.jsx +90 -0
- package/src/commerce/storefront/useCheckout.jsx +279 -0
- package/src/commerce/storefront/useOrderReturn.js +56 -0
- package/src/commerce/utils/index.js +6 -0
- package/src/commerce/utils/storefront.js +9 -2
package/skills/commerce/SKILL.md
CHANGED
|
@@ -11,33 +11,34 @@ Operational guidance for extending, operating and building on the Base44 Commerc
|
|
|
11
11
|
> - **Don't read the whole codebase up front.** Start with this file, then open only the files your current task touches plus the matching reference below, pulling in more as you go. Reading everything first just burns context.
|
|
12
12
|
> - **Don't weaken the admin gating.** The UI guard, entity RLS and function guards form three enforcement layers — see [`.agents/skills/commerce/post-installation.md`](./post-installation.md) — keep all of them intact when changing routes or schemas.
|
|
13
13
|
> - **Entity names are dotted — SDK access is bracket syntax only:** `base44.entities["commerce.PaymentGateway"]` (schema file: `base44/entities/commerce.PaymentGateway.jsonc`). `commerce__PaymentGateway` / `PaymentGateway` don't exist. Gateway on/off is seed data anyway: `commerce/seed-store` `payment_methods`.
|
|
14
|
+
> - **Online card payments are optional and off by default.** The seed enables the manual `offline` method and leaves the `card` gateway disabled. **Enable cards only if a payment provider is wired — or will be, in the same stretch of work** (one file): enabled with nothing behind it answers `503 no_card_payment_provider` at checkout. Choosing to add them is fine, but it belongs at the *end* of an install, once there's a working store to point at — nothing else depends on it, so never open a build by asking a user for a provider key. [`post-installation.md` §4.1](./post-installation.md#41-off-by-default--enable-only-with-a-provider-wired) has the rule and the timing.
|
|
14
15
|
|
|
15
16
|
## IMPORTANT — first-time installation
|
|
16
17
|
|
|
17
|
-
If the template was just installed (or you are installing it right now), read [`.agents/skills/commerce/post-installation.md`](./post-installation.md) **before anything else — and, unless the user has a special requirement, read nothing else**: it alone covers embedding the admin pages, the three-layer admin-role enforcement (do not weaken), seeding the store's data — **one `commerce/seed-store` call takes the whole catalog** (products with attributes; variants, categories, ribbons and Shipping & Tax Locations created internally — §
|
|
18
|
+
If the template was just installed (or you are installing it right now), read [`.agents/skills/commerce/post-installation.md`](./post-installation.md) **before anything else — and, unless the user has a special requirement, read nothing else**: it alone covers embedding the admin pages, the three-layer admin-role enforcement (do not weaken), seeding the store's data — **one `commerce/seed-store` call takes the whole catalog** (products with attributes; variants, categories, ribbons and Shipping & Tax Locations created internally — §3) — **card payments as an optional, late step** (§4.1: off by default — the seed enables offline payment and leaves cards off; enable cards only with a provider wired or about to be, and raise it at the end of the install, never its start — then §4.2's complete Stripe implementation to paste over `shared/commerce/card-payment.ts`; the payment webhook is premade), and the **storefront quick start** (§2): logic-only chunks for product list → product page → optional cart → checkout. Its §0 schedules the whole install: **storefront components are written while image generation and the seed call (parallel writes — usually a few seconds) run — never after them, and never behind a payment-provider round-trip**. The references below and the API docs are for requests that go beyond that happy path, not for the install. The full install-from-scratch steps are in [`.agents/skills/commerce/installation-guidelines.md`](./installation-guidelines.md).
|
|
18
19
|
|
|
19
20
|
## Working on the UI
|
|
20
21
|
|
|
21
22
|
- **Admin UI** (`src/commerce/admin/`) — a complete store back office ships with the template, and it is **yours to change**: restyle it, add or remove pages, rework flows, extend it however the app needs. To understand the backend it talks to, read [`.agents/skills/commerce/docs/api-admin.md`](./docs/api-admin.md) — every admin function/action plus the direct-entity-CRUD contract. The only invariant is the admin-role gating (see above).
|
|
22
|
-
- **Storefront** — **no visitor UI ships
|
|
23
|
+
- **Storefront** — **no visitor UI ships** (no visual components at all — the shopfront's look is the app's to design); the storefront *API* is complete (token-based cart), and the storefront *logic* ships on two levels: framework-free helpers in [`src/commerce/utils/`](../../src/commerce/utils/) (`storefront.js` client, `variants.js`, `shipping-promos.js` — import from `@/commerce/utils`) and the React layer in [`src/commerce/storefront/`](../../src/commerce/storefront/) (import from `@/commerce/storefront`): `StorefrontProvider` (one client, one store-info cache, ONE shared cart), `useCart`, `useCheckout` (guided checkout — automatic shipping/tax recalculation when the address is complete, shipping/payment choice, a gated `placeOrder`), headless `ShippingMethodPicker`/`PaymentMethodPicker`, and `useOrderReturn` for the mandatory `/order-received` page. **In a React app, build cart/checkout on these hooks — don't hand-roll their logic.** Catalog views stay thin by design (client calls + variant helpers — the design freedom lives there). Start from the logic-only quick start in [`post-installation.md` §2](./post-installation.md#2-storefront-quick-start--logic-only) — it covers the whole buy path; go to [`.agents/skills/commerce/docs/api-storefront.md`](./docs/api-storefront.md) for anything beyond it. Navigation comes from three actions — `list-categories` (tree), `list-ribbons` (flat, with counts) and `list-attributes` (filter UIs). What to render in the grid vs. the product page — and which fields only one of the two calls returns — is [`references/product-render.md`](./references/product-render.md); **ribbons are the most-skipped part of it and belong in both views**.
|
|
23
24
|
|
|
24
25
|
### If you build a storefront, these four are not optional
|
|
25
26
|
|
|
26
|
-
Agents keep shipping storefronts that miss these, and each one breaks buying outright.
|
|
27
|
+
Agents keep shipping storefronts that miss these, and each one breaks buying outright. **In a React app, rules 2 and 3 are already implemented — `useCheckout` and `useOrderReturn` from `@/commerce/storefront` (post-installation.md §2.4); use them instead of re-deriving the logic.** The rules below stay stated in API terms so non-React (or non-hook) storefronts can follow them too; details in [`docs/api-storefront.md`](./docs/api-storefront.md) and [`references/storefront-product-page.md`](./references/storefront-product-page.md).
|
|
27
28
|
|
|
28
29
|
1. **Products with variants need one selector per attribute — and the selection must resolve to a variation.** `get-product` gives `product.attributes` (every entry is an axis — there is no product type) and `variations` (the combinations). Render a control per axis, never a list of combinations, then send the resolved `variation_id`:
|
|
29
30
|
```js
|
|
30
31
|
import { resolveSelection, defaultSelection, selectOption } from "@/commerce/utils";
|
|
31
32
|
const view = resolveSelection(product, variations, selection); // axes, availability, price, addToCart
|
|
32
|
-
await
|
|
33
|
+
await addItem(view.addToCart); // useCart(); or add-item with cart_token + ...view.addToCart
|
|
33
34
|
```
|
|
34
35
|
`add-item` **rejects a product with attributes unless it gets a `variation_id`** (`400 variation_required`), so a page that ignores this cannot sell anything.
|
|
35
36
|
|
|
36
|
-
2. **Checkout must
|
|
37
|
+
2. **Checkout must recalculate shipping from the address and send a choice.** `useCheckout` does all of it: it calls `set-shipping-address` automatically once the address is complete (recalculating options, cost and tax; `400 shipping_not_available` → its `addressError`), resolves `shipping_status`, and blocks `placeOrder` until the choice is made. Hand-rolled flows must do the same by hand: call `set-shipping-address` as soon as the customer provides an address, then read `shipping_status` on the returned cart — `auto_selected` (one option, already applied) · `chosen` · `choice_required` → **show `available_shipping_methods` and call `choose-shipping-method`** · `missing_address` → collect the address first (a single-location store shows its options even before one). `place-order` refuses with `400 shipping_method_required` until then — that is not a bug to work around. (`chosen_shipping_method` on the cart is the rate **id**, a string — display it by looking up its entry's `title`/`cost`, never by rendering the id.)
|
|
37
38
|
|
|
38
|
-
3. **Handle card payments, and build `/order-received
|
|
39
|
+
3. **Handle card payments if the store offers them, and build `/order-received` either way.** Render whatever gateways `get-store-info` reports — a default-seeded store offers `offline` only (cards are off by default, and enabled only with a provider wired: [`post-installation.md` §4.1](./post-installation.md#41-off-by-default--enable-only-with-a-provider-wired)), so never hardcode a card option. Choosing the card gateway returns `payment.checkout_url` — `useCheckout().placeOrder` redirects there for you (until a payment provider is implemented — one file, Stripe paste-in in [`post-installation.md` §4](./post-installation.md#42-wiring-a-provider--one-file) — it answers `503 no_card_payment_provider`; offer the other methods). Every payment link comes back to `/order-received`, which **you must implement**: one `useOrderReturn()` call rendered by `status` (hand-rolled: `commerce/payments` `complete-return` with the query params). Without that page a customer pays into a 404 and the order is never marked paid. Also persist the `cart_token` from **every** cart response (the provider/client do this) — `add-item` silently starts a fresh cart when the stored token is stale.
|
|
39
40
|
|
|
40
|
-
4. **Never advertise what isn't configured.** "Free shipping over €150" must come from a real shipping rate that is free or carries a `free_over` threshold on a Shipping & Tax Location. Locations are admin-only data, so a storefront cannot read them: the live answer is the cart's `available_shipping_methods` after
|
|
41
|
+
4. **Never advertise what isn't configured — and never configure what can't be reached.** "Free shipping over €150" must come from a real shipping rate that is free or carries a `free_over` threshold on a Shipping & Tax Location. Locations are admin-only data, so a storefront cannot read them: the live answer is the cart's `available_shipping_methods` after the address is set, and `shipping-promos.js` normalizes the rules wherever the records *are* in hand. No rule means no banner. **Coupons are the same rule in reverse:** codes are admin-only data that a customer can only use by typing them, so a store with coupons needs a code field — `useCart().applyCoupon`, in the cart or, when there's no cart page, in the checkout — rendering `discount_total` and invalid codes inline. No field means no coupons: don't seed them, don't name a code in the copy ([`post-installation.md` §2.3/§2.4](./post-installation.md#23-cart)).
|
|
41
42
|
|
|
42
43
|
All functions return the envelope `{ success, data }` (or `{ success, error, code }`); with the SDK the body is on `res.data`:
|
|
43
44
|
|
|
@@ -56,7 +57,7 @@ Open the matching file under `.agents/skills/commerce/references/` only when a t
|
|
|
56
57
|
| Variant selection | attribute-level selectors, resolving a selection to a variation, availability states, incomplete-selection pricing, add-to-cart contract | [`references/storefront-product-page.md`](./references/storefront-product-page.md) |
|
|
57
58
|
| Reviews | stars on cards and the product page, the review list + submit form (public by email, backend ships complete), the auto-approve toggle, UI-enforced policies (login-gated, verified-only, required rating) | [`references/reviews.md`](./references/reviews.md) |
|
|
58
59
|
| Admin product form | changing the product editor — its stacked sections are **Price & Inventory** (tax group, then the attributes, then a row per variant, or a single *Base price* row when there are none), **Modifiers** (`meta_data`), **Downloads**, **Linked products**; one **Visible** toggle drives `status`. Variants reconcile from the attribute values automatically: no generate step, no per-variant delete. Weight and dimensions are per variant. | [`references/admin-product-form.md`](./references/admin-product-form.md) |
|
|
59
|
-
| Online payments | **any storefront or checkout work** — the order side (checkout, confirmation, payment links, refund records) *and* the payment webhook are premade; wiring a provider means implementing the four functions in one file (`shared/commerce/card-payment.ts`; Stripe = paste [`post-installation.md` §
|
|
60
|
+
| Online payments | **any storefront or checkout work** — the order side (checkout, confirmation, payment links, refund records) *and* the payment webhook are premade; wiring a provider means implementing the four functions in one file (`shared/commerce/card-payment.ts`; Stripe = paste [`post-installation.md` §4](./post-installation.md#42-wiring-a-provider--one-file)) | [`references/online-payments.md`](./references/online-payments.md) |
|
|
60
61
|
| Scheduled work | recurring maintenance — stock-hold release, abandoned-cart cleanup, webhook-log pruning, counter-drift repair | [`references/scheduled-work.md`](./references/scheduled-work.md) |
|
|
61
62
|
| Emails | transactional order emails, per-type overrides, deliverability, the email log | [`references/emails.md`](./references/emails.md) |
|
|
62
63
|
| Webhooks | outbound webhooks, HMAC signing, delivery log, auto-disable behavior | [`references/webhooks.md`](./references/webhooks.md) |
|
|
@@ -182,8 +182,8 @@ Actions: `create-link` · `complete-return` · `verify` — the admin side of on
|
|
|
182
182
|
|
|
183
183
|
## commerce/seed-store
|
|
184
184
|
|
|
185
|
-
Not action-routed. Body `{ store_name?, currency?, weight_unit?, dimension_unit?, payment_methods?, with_sample_data?, products?, coupons?, locations? }`. **`payment_methods`** (gateway slugs, e.g. `["card"]`) enables exactly the listed gateways and disables every other gateway row — the one-call way to honor "card-only"/"offline-only"; explicit values win on re-runs, unknown slugs fail as `400 invalid_payload`, and the response reports `payment_methods: { enabled, disabled }` (`null` when not passed). **`store_name` is required** when the `emails` group doesn't exist yet (**400** `store_name_required` otherwise) — pass the app's name **as the platform shows it** — ask the user or read it from the dashboard. `base44/config.jsonc` → `name` is *not* authoritative: it can still say `New App` for an app the platform calls `Canvas`. A backend function can't read either, its environment being only `BASE44_APP_ID`. It lands in `emails.store_name` — one setting serving as both the store name in email subjects and the sender name on every transactional email; a nameless store renders subjects like `[]: New order #1002`, which is why seeding refuses one. Requires admin. Runs a **canary schema check** first — on any incompatibility returns **422** `{ success:false, code:"schema_incompatible", errors:[{ entity, error }] }` and writes nothing. Otherwise seeds defaults idempotently, then the catalog. On an already-seeded store a passed `store_name` fills a **blank** name and never overwrites one the merchant chose. **`currency`** (ISO code, validated against the shared currencies table) and **`weight_unit`/`dimension_unit`** set the `general` group; unknown values fail as **400** `invalid_payload`. Unlike `store_name` they **always win** — there is no blank state to distinguish a merchant's choice from the seeded default, so passing one on a re-run updates the store. Prices are formatted with `Intl.NumberFormat` — the currency is a value, there are no format settings.
|
|
185
|
+
Not action-routed. Body `{ store_name?, currency?, weight_unit?, dimension_unit?, payment_methods?, with_sample_data?, products?, coupons?, locations? }`. **`payment_methods`** (gateway slugs, e.g. `["offline", "card"]`) enables exactly the listed gateways and disables every other gateway row — the one-call way to honor "card-only"/"offline-only"/"both"; explicit values win on re-runs, unknown slugs fail as `400 invalid_payload`, and the response reports `payment_methods: { enabled, disabled }` (`null` when not passed). **Omitted, the seed enables `offline` and leaves `card` disabled** — cards are off by default, so passing `"card"` is what turns them on: only do so with a provider wired in `shared/commerce/card-payment.ts`, or about to be, since an enabled card option with nothing behind it answers `503 no_card_payment_provider` at checkout ([`post-installation.md` §4.1](../post-installation.md#41-off-by-default--enable-only-with-a-provider-wired)). **`store_name` is required** when the `emails` group doesn't exist yet (**400** `store_name_required` otherwise) — pass the app's name **as the platform shows it** — ask the user or read it from the dashboard. `base44/config.jsonc` → `name` is *not* authoritative: it can still say `New App` for an app the platform calls `Canvas`. A backend function can't read either, its environment being only `BASE44_APP_ID`. It lands in `emails.store_name` — one setting serving as both the store name in email subjects and the sender name on every transactional email; a nameless store renders subjects like `[]: New order #1002`, which is why seeding refuses one. Requires admin. Runs a **canary schema check** first — on any incompatibility returns **422** `{ success:false, code:"schema_incompatible", errors:[{ entity, error }] }` and writes nothing. Otherwise seeds defaults idempotently, then the catalog. On an already-seeded store a passed `store_name` fills a **blank** name and never overwrites one the merchant chose. **`currency`** (ISO code, validated against the shared currencies table) and **`weight_unit`/`dimension_unit`** set the `general` group; unknown values fail as **400** `invalid_payload`. Unlike `store_name` they **always win** — there is no blank state to distinguish a merchant's choice from the seeded default, so passing one on a re-run updates the store. Prices are formatted with `Intl.NumberFormat` — the currency is a value, there are no format settings.
|
|
186
186
|
|
|
187
|
-
**`products`** is the one-call catalog bootstrap — the worked example and full semantics are in [`../post-installation.md` §
|
|
187
|
+
**`products`** is the one-call catalog bootstrap — the worked example and full semantics are in [`../post-installation.md` §3.1](../post-installation.md#31-the-products-payload). Each entry references categories/ribbons/attributes by **display name** (taxonomy is get-or-created: slugs/codes derived, existing records matched case-insensitively and reused, with the stored casing canonicalized into the product). `attributes: [{ name, options }]` (or `{ <name>: [options] }`) declares the variant axes; `variations: [{ options: { <name>: <option> }, ...overrides }]` lists the stocked combinations — omit it to auto-generate **all** combinations, each inheriting the product-level price/sale fields. A variation with its own `stock_quantity` gets `manage_stock: "yes"`; without one it draws on the parent's pooled stock (`"parent"`). Variation SKUs are synthesized from the parent SKU when absent. Parent `price`/`regular_price`/`on_sale` are rolled up from the cheapest publishable variant, `stock_status` derived, category/ribbon counts maintained — same helpers as `admin-products` `save`, but **no `product.created` webhooks are dispatched** (bootstrap precedes webhook subscribers; use `admin-products` for webhook-visible creates). Payload problems fail before any write as **400** `invalid_payload` with `errors: [{ path, error }]`; an explicit variation SKU already in use is **409** `duplicate_sku`; a mid-write failure rolls back everything the call created (**500** `catalog_seed_failed`) without touching reused taxonomy. **Re-runs converge**: a product whose `sku` (or, without one, derived slug) already exists is skipped and reported, so retries never duplicate. Limits: ≤100 products, ≤50 variations per product, ≤500 variations per call, ≤50 coupons/locations. `coupons` is a thin passthrough (code lowercased, skip-if-exists); **`locations`** creates Shipping & Tax Locations (`{ name, countries?: ["IL"], shipping_rates: [{ name, cost, free_over? }], tax_groups?: [{ name, rates: [{ name, rate }] }], shipping_tax?: { type: "percent"|"fixed", value } }` — the default `Products` tax group is added when missing; skip-if-exists by name). Passing any `locations` suppresses the seeded "Rest of the world" fallback — yours become the store's only locations; without them the free-shipping fallback is seeded. `with_sample_data: true` seeds the template's demo catalog instead (only when the store has zero products) and cannot be combined with the catalog keys.
|
|
188
188
|
|
|
189
189
|
→ `{ seeded: { settings_groups, gateways, locations }, sample_data: {...} | false, catalog: { categories|ribbons|attributes|terms: { created, reused }, products_created, products_skipped, variations_created, coupons, locations, products: [{ name, id, slug, sku, variation_count } | { name, skipped: true, reason: "sku_exists"|"slug_exists", existing_id }] } | null, store_name: { value, action: "created" | "filled" | "unchanged" | "kept_existing" }, currency: { value, action: "created" | "updated" | "unchanged" } | null }`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Storefront API Reference
|
|
2
2
|
|
|
3
|
-
Everything needed to build a customer-facing shopfront (or headless client) against the Base44 Commerce Template. No visitor UI ships with the template — this is your integration surface.
|
|
3
|
+
Everything needed to build a customer-facing shopfront (or headless client) against the Base44 Commerce Template. No visitor UI ships with the template — this is your integration surface. Storefront *logic* does ship on two levels: the framework-free helpers in `src/commerce/utils/` (API client, variant selection, free-shipping rules) and the React layer in `src/commerce/storefront/` (shared-cart provider, guided-checkout hook, headless shipping/payment pickers, order-received hook) — in a React app, build cart/checkout on those hooks and use this reference for what lies beyond them.
|
|
4
4
|
|
|
5
5
|
## Required behaviors — a storefront that skips these cannot sell
|
|
6
6
|
|
|
@@ -27,7 +27,7 @@ The backend is considerably richer than a minimal "grid → cart → pay" shop.
|
|
|
27
27
|
| **Taxonomy navigation** | category tree, **ribbon list with counts**, attributes + terms for filter UIs | [`list-categories`](#list-categories), [`list-ribbons`](#list-ribbons), [`list-attributes`](#list-attributes) |
|
|
28
28
|
| **Customer reviews** | paginated reviews per product with **average rating + rating count**, `verified` owner flag, and **review submission by anyone with an email address** (no login); plus "my reviews" | [`get-product`](#get-product), [`submit-review`](#submit-review), [`my-reviews`](#commercestorefront-account) |
|
|
29
29
|
| **Cart** | guest carts via `cart_token`, add/update/remove, quantity merging, `sold_individually` caps, live re-pricing, stock revalidation | [`commerce/storefront-cart`](#commercestorefront-cart) |
|
|
30
|
-
| **Coupons** | apply/remove by code with full server-side validation (eligibility, limits, per-user usage) | [`apply-coupon`](#commercestorefront-cart) |
|
|
30
|
+
| **Coupons** | apply/remove by code with full server-side validation (eligibility, limits, per-user usage) — codes are admin-only data, so a store with coupons needs a **code field** somewhere in the buy path (cart or checkout) or they can never be redeemed | [`apply-coupon`](#commercestorefront-cart) |
|
|
31
31
|
| **Shipping** | address → matched location → **selectable shipping rates with live costs**, free-over thresholds | [`set-shipping-address`](#commercestorefront-cart) |
|
|
32
32
|
| **Tax** | inclusive/exclusive pricing, per-location tax groups, itemized display — all resolved server-side | [`get-store-info`](#get-store-info) |
|
|
33
33
|
| **Stock states** | in stock / out of stock / **on backorder**, low-stock signalling, configurable out-of-stock hiding | [`list-products`](#list-products) |
|
|
@@ -36,7 +36,7 @@ The backend is considerably richer than a minimal "grid → cart → pay" shop.
|
|
|
36
36
|
| **Digital products** | entitlement-checked downloads with remaining-count, expiry, and signed URLs for private files | [`get-download`](#commercestorefront-account) |
|
|
37
37
|
| **Store config** | currency (format it with `Intl.NumberFormat`), units, catalog/cart price display — read it and honour it instead of hardcoding | [`get-store-info`](#get-store-info) |
|
|
38
38
|
|
|
39
|
-
Two things that are **not** in the backend and are yours to build: the visitor UI itself, and the payment provider integration — four functions in one file (Stripe = paste [`post-installation.md` §
|
|
39
|
+
Two things that are **not** in the backend and are yours to build: the visitor UI itself, and the payment provider integration — four functions in one file (Stripe = paste [`post-installation.md` §4](../post-installation.md#42-wiring-a-provider--one-file); other providers: [`references/online-payments.md`](../references/online-payments.md)). Payment methods and the currency are admin-owned data — always render them from `get-store-info` rather than a hardcoded list, and format prices with `Intl.NumberFormat(undefined, { style: "currency", currency })`.
|
|
40
40
|
|
|
41
41
|
## Conventions
|
|
42
42
|
|
|
@@ -74,7 +74,7 @@ Bootstrap data for a storefront. No payload.
|
|
|
74
74
|
```
|
|
75
75
|
`settings` is a safe projection — only display/behavior keys, never admin config.
|
|
76
76
|
|
|
77
|
-
`payment_gateways` is **every gateway the admin has enabled** (sorted by their `order`) — the Payments settings switch is the single source of truth. `online: true` marks the card option (`place-order` answers with a payment page to redirect to; `503 no_card_payment_provider`
|
|
77
|
+
`payment_gateways` is **every gateway the admin has enabled** (sorted by their `order`) — the Payments settings switch is the single source of truth, so render this list and never a hardcoded one. The sample above shows both shapes, but a **default-seeded store reports `offline` alone**: cards are off by default and appear only once the gateway is enabled — which should only happen with a provider wired ([`post-installation.md` §4.1](../post-installation.md#41-off-by-default--enable-only-with-a-provider-wired)) — so expect the one-method case in a fresh store. `online: true` marks the card option (`place-order` answers with a payment page to redirect to; `503 no_card_payment_provider` if it was enabled with no provider behind it); everything else settles manually.
|
|
78
78
|
|
|
79
79
|
### `list-products`
|
|
80
80
|
**Payload** (all optional): `search`, `category_id` (includes descendants), `ribbon_id`, `attribute_id` + `attribute_term`, `min_price`, `max_price`, `featured` (bool), `on_sale` (bool), `in_stock_only` (bool), `sort` (`-created_date`|`name`|`price`|`-price`|`popularity`|`rating`, default `-created_date`), `page` (default 1), `per_page` (default 12, max 100).
|
|
@@ -27,6 +27,7 @@ Relative to the script's own folder (`examples/commerce/scripts/`), it copies:
|
|
|
27
27
|
| `../base44/agents/commerce/` | `../../../base44/agents/commerce/` |
|
|
28
28
|
| `../src/commerce/admin/` | `../../../src/commerce/admin/` |
|
|
29
29
|
| `../src/commerce/utils/` | `../../../src/commerce/utils/` |
|
|
30
|
+
| `../src/commerce/storefront/` | `../../../src/commerce/storefront/` |
|
|
30
31
|
| `../skills/commerce/` | `../../../.agents/skills/commerce/` |
|
|
31
32
|
|
|
32
33
|
Directories are merged: files owned by the template are overwritten (re-running after a template update is safe); everything else in your app is left untouched. Files the template has since **renamed or retired** are deleted on install (it reports each one) — otherwise stale guidance would sit in `.agents/skills/` forever, and agents read whatever is there. The skill folder carries all the documentation — `SKILL.md`, this file, `post-installation.md`, the topic references in `references/` and the API docs in `docs/` — so the installed app gets it at `.agents/skills/commerce/` where agents pick it up natively; the template repo itself also stays under `examples/commerce/` for reference.
|
|
@@ -34,7 +35,7 @@ Directories are merged: files owned by the template are overwritten (re-running
|
|
|
34
35
|
**Manual.** Equivalently, copy by hand:
|
|
35
36
|
|
|
36
37
|
1. Copy `base44/entities/commerce.*`, `base44/functions/commerce/*`, `base44/shared/commerce/*` and `base44/agents/commerce/*` into your app's `base44/` dir (merge, don't overwrite unrelated files). `shared/` is bundled into every function at deploy time.
|
|
37
|
-
2. Copy `src/commerce/admin/` → `src/commerce/admin/`
|
|
38
|
+
2. Copy `src/commerce/admin/` → `src/commerce/admin/`, `src/commerce/utils/` → `src/commerce/utils/` (framework-free storefront helpers — API client, variant selection; no deps) and `src/commerce/storefront/` → `src/commerce/storefront/` (the storefront React layer — shared-cart provider, guided-checkout hook, headless pickers; needs React only).
|
|
38
39
|
3. Copy `skills/commerce/` → `.agents/skills/commerce/` (the commerce skill — `SKILL.md`, this file, `post-installation.md`, the `references/` topic guides and the `docs/` API references — for agents working on the app).
|
|
39
40
|
|
|
40
41
|
Confirm your `base44/config.jsonc` `entitiesDir`/`functionsDir` point at these folders (the defaults do).
|
|
@@ -47,7 +48,7 @@ Confirm your `base44/config.jsonc` `entitiesDir`/`functionsDir` point at these f
|
|
|
47
48
|
4. Check the app's `package.json` for `sonner`, `recharts` and `react-markdown`, and run `npm i` **only** for the ones actually absent — all three ship with the default Base44 template, so the normal outcome is no install at all. Do not re-install a package that is already a dependency. Nothing else is needed. Verify the shadcn primitives listed in [`src/commerce/admin/README.md`](../../src/commerce/admin/README.md) exist in your app.
|
|
48
49
|
5. Mount the admin router (see [`post-installation.md`](./post-installation.md)).
|
|
49
50
|
6. Grant your user the `admin` role.
|
|
50
|
-
7. Seed the store's data — one `commerce/seed-store` call creates the business defaults and, via its `products` payload, the whole catalog (or `with_sample_data: true` for the generic demo; or neither for defaults only). Skipping the call entirely leaves the operator the **Initialize store defaults** first-run screen. See [`post-installation.md`](./post-installation.md) §
|
|
51
|
+
7. Seed the store's data — one `commerce/seed-store` call creates the business defaults and, via its `products` payload, the whole catalog (or `with_sample_data: true` for the generic demo; or neither for defaults only). The defaults include payment methods: the manual **`offline`** option enabled and **`card` switched off**. Cards are optional and not part of installing — enable them only with a payment provider wired, or about to be, or checkout answers `503 no_card_payment_provider` ([`post-installation.md`](./post-installation.md) §4.1). Skipping the call entirely leaves the operator the **Initialize store defaults** first-run screen. See [`post-installation.md`](./post-installation.md) §3; any seed call marks the business as ready and the first-run screen never appears.
|
|
51
52
|
|
|
52
53
|
Check the install at any time:
|
|
53
54
|
|
|
@@ -85,7 +86,7 @@ The template ships an AI copilot for store operators:
|
|
|
85
86
|
|
|
86
87
|
## 4. Building on it — the four storefront requirements
|
|
87
88
|
|
|
88
|
-
If your work includes a customer-facing shopfront, the quick start in [`post-installation.md` §
|
|
89
|
+
If your work includes a customer-facing shopfront, the quick start in [`post-installation.md` §2](./post-installation.md#2-storefront-quick-start--logic-only) already walks all four requirements — you don't need any other reading. In short: attribute-level variant selectors resolved to a `variation_id`; shipping options presented and chosen; the payment methods the store actually enables rendered from `get-store-info` (a default store offers `offline` only — cards are off unless enabled with a provider wired, [`post-installation.md`](./post-installation.md) §4.1) with the card redirect handled if they're on, plus an `/order-received` page; and no offers the store isn't configured for. In a React app the shipped `@/commerce/storefront` hooks implement the shipping and order-received requirements outright — build on them rather than re-deriving the flow. The API enforces the first three — a storefront that skips them cannot complete a purchase. (Day-2 deep dives: [`SKILL.md` → *these four are not optional*](./SKILL.md) and [`docs/api-storefront.md`](./docs/api-storefront.md)'s *Required behaviors* table.)
|
|
89
90
|
|
|
90
91
|
## 5. Next steps
|
|
91
92
|
|