@base44/app-plugin-commerce 0.1.20 → 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.
- package/README.md +25 -22
- package/base44/agents/commerce/StoreAdmin.jsonc +1 -1
- package/base44/entities/commerce.OrderRefund.jsonc +1 -1
- package/base44/entities/commerce.PaymentGateway.jsonc +1 -1
- package/base44/entities/commerce.Webhook.jsonc +1 -1
- package/base44/functions/commerce/admin-products/entry.ts +1 -1
- package/base44/functions/commerce/admin-reports/entry.ts +1 -1
- package/base44/functions/commerce/payments/entry.ts +2 -2
- package/base44/functions/commerce/seed-store/defaults.ts +1 -1
- package/base44/functions/commerce/seed-store/entry.ts +34 -0
- package/base44/functions/commerce/seed-store/seed-catalog.ts +39 -5
- package/base44/functions/commerce/storefront-catalog/entry.ts +1 -1
- package/base44/functions/commerce/storefront-checkout/entry.ts +1 -1
- package/base44/shared/commerce/card-payment.stripe.ts +198 -0
- package/base44/shared/commerce/card-payment.ts +1 -1
- package/base44/shared/commerce/payments.ts +2 -2
- package/base44/shared/commerce/scan.ts +1 -1
- package/base44/shared/commerce/sequence.ts +2 -2
- package/package.json +1 -1
- package/scripts/install.js +24 -14
- package/skills/commerce/SKILL.md +117 -51
- package/skills/commerce/docs/api-admin.md +89 -28
- package/skills/commerce/docs/api-storefront.md +113 -126
- package/skills/commerce/docs/entities.md +137 -0
- package/skills/commerce/install/01-install.md +101 -0
- package/skills/commerce/install/02-storefront.md +444 -0
- package/skills/commerce/install/03-data.md +162 -0
- package/skills/commerce/references/admin-product-form.md +10 -0
- package/skills/commerce/references/catalog-rendering.md +110 -0
- package/skills/commerce/references/emails.md +49 -12
- package/skills/commerce/references/guest-access-security.md +18 -5
- package/skills/commerce/references/online-payments.md +49 -149
- package/skills/commerce/references/operations.md +52 -0
- package/skills/commerce/references/reviews.md +31 -16
- package/skills/commerce/references/shipping-and-tax.md +110 -0
- package/skills/commerce/references/store-admin-agent.md +21 -0
- package/skills/commerce/references/store-settings.md +49 -0
- package/src/commerce/admin/README.md +6 -3
- package/src/commerce/admin/layout/AuthGuard.jsx +1 -1
- package/src/commerce/admin/pages/products/Reviews.jsx +1 -1
- package/src/commerce/admin/pages/settings/InventorySettings.jsx +1 -1
- package/src/commerce/admin/pages/settings/PaymentsSettings.jsx +1 -1
- package/src/commerce/storefront/StorefrontProvider.jsx +106 -20
- package/src/commerce/storefront/index.js +74 -24
- package/src/commerce/storefront/internal/useAsyncData.js +86 -0
- package/src/commerce/storefront/useAddressForm.js +96 -0
- package/src/commerce/storefront/useCartLine.js +221 -0
- package/src/commerce/storefront/useCheckout.jsx +18 -6
- package/src/commerce/storefront/useOrderReturn.js +36 -10
- package/src/commerce/storefront/useProduct.js +295 -0
- package/src/commerce/storefront/useProductGallery.js +74 -0
- package/src/commerce/storefront/useProductList.js +153 -0
- package/src/commerce/storefront/useProductPrice.js +58 -0
- package/src/commerce/storefront/useProductReviews.js +242 -0
- package/src/commerce/storefront/useStorefrontSeo.js +204 -0
- package/src/commerce/storefront/useTotalsLines.js +109 -0
- package/src/commerce/utils/address-spec.js +89 -0
- package/src/commerce/utils/images.js +45 -0
- package/src/commerce/utils/index.js +22 -7
- package/src/commerce/utils/price.js +95 -0
- package/src/commerce/utils/shipping-promos.js +2 -2
- package/src/commerce/utils/specs.js +26 -0
- package/src/commerce/utils/storefront.js +47 -3
- package/src/commerce/utils/totals.js +110 -0
- package/src/commerce/utils/variants.js +58 -3
- package/skills/commerce/installation-guidelines.md +0 -93
- package/skills/commerce/post-installation.md +0 -496
- package/skills/commerce/references/limits-and-performance.md +0 -16
- package/skills/commerce/references/media-and-downloads.md +0 -4
- package/skills/commerce/references/product-render.md +0 -89
- package/skills/commerce/references/scheduled-work.md +0 -19
- package/skills/commerce/references/storefront-product-page.md +0 -83
- package/skills/commerce/references/webhooks.md +0 -10
|
@@ -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
|
-
*
|
|
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
|
|
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
|
-
*
|
|
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.
|
|
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",
|
package/scripts/install.js
CHANGED
|
@@ -25,12 +25,11 @@
|
|
|
25
25
|
* overwritten (so re-running after a template update is safe), everything
|
|
26
26
|
* else in the app is left untouched.
|
|
27
27
|
*
|
|
28
|
-
* This is only the static part of the install.
|
|
29
|
-
* ../skills/commerce/
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* documentation into the app.
|
|
28
|
+
* This is only the static part of the install. Everything after it lives in
|
|
29
|
+
* ../skills/commerce/install/01-install.md (deps, the /store-admin mount, the
|
|
30
|
+
* admin-role gating), which routes on to 02-storefront.md and 03-data.md at the
|
|
31
|
+
* right moments; ../skills/commerce/SKILL.md is the map for all later work. The
|
|
32
|
+
* skill folder carries all of that documentation into the app.
|
|
34
33
|
*
|
|
35
34
|
* Written with dynamic import() and process.argv[1] (instead of require/
|
|
36
35
|
* __dirname) so it runs unchanged whether the host app's package.json is
|
|
@@ -132,6 +131,17 @@
|
|
|
132
131
|
["src", "commerce", "admin", "pages", "settings", "TaxRatesTable.jsx"],
|
|
133
132
|
["src", "commerce", "admin", "pages", "settings", "ShippingSettings.jsx"],
|
|
134
133
|
["src", "commerce", "admin", "pages", "settings", "ShippingZoneEditor.jsx"],
|
|
134
|
+
// The skill's docs were restructured into stage files (install/01..03) plus a
|
|
135
|
+
// smaller reference set. Stale guidance is worse than none — agents read
|
|
136
|
+
// whatever is in .agents/skills/ — so every retired doc goes, by name.
|
|
137
|
+
[".agents", "skills", "commerce", "post-installation.md"], // → install/01..03
|
|
138
|
+
[".agents", "skills", "commerce", "installation-guidelines.md"], // → install/01-install.md + references/store-admin-agent.md
|
|
139
|
+
[".agents", "skills", "commerce", "references", "product-render.md"], // → references/catalog-rendering.md
|
|
140
|
+
[".agents", "skills", "commerce", "references", "storefront-product-page.md"], // → references/catalog-rendering.md
|
|
141
|
+
[".agents", "skills", "commerce", "references", "scheduled-work.md"], // → references/operations.md
|
|
142
|
+
[".agents", "skills", "commerce", "references", "limits-and-performance.md"], // → references/operations.md
|
|
143
|
+
[".agents", "skills", "commerce", "references", "webhooks.md"], // → references/operations.md
|
|
144
|
+
[".agents", "skills", "commerce", "references", "media-and-downloads.md"], // → docs/api-storefront.md + install/03-data.md
|
|
135
145
|
];
|
|
136
146
|
let retiredRemoved = 0;
|
|
137
147
|
for (const parts of RETIRED) {
|
|
@@ -186,16 +196,16 @@
|
|
|
186
196
|
(retiredRemoved ? ` (${retiredRemoved} retired file${retiredRemoved === 1 ? "" : "s"} removed)` : ""),
|
|
187
197
|
);
|
|
188
198
|
console.log(
|
|
189
|
-
"\nNext
|
|
199
|
+
"\nNext: read .agents/skills/commerce/install/01-install.md and follow it.\n" +
|
|
200
|
+
"It is the whole install, and it says when to open 02-storefront.md and\n" +
|
|
201
|
+
"03-data.md — read nothing else up front.\n" +
|
|
202
|
+
"\n" +
|
|
190
203
|
" 1. No deps to add: sonner, recharts and react-markdown ship with the default\n" +
|
|
191
|
-
" Base44 template — check package.json and npm i only what is truly missing
|
|
192
|
-
" do not re-install packages already listed as dependencies\n" +
|
|
204
|
+
" Base44 template — check package.json and npm i only what is truly missing\n" +
|
|
193
205
|
' 2. Mount the admin router: <Route path="/store-admin/*" element={<AdminApp />} />\n' +
|
|
194
|
-
"
|
|
195
|
-
"
|
|
196
|
-
"
|
|
197
|
-
" call (store_name required) takes the whole catalog via products, or the demo data\n" +
|
|
198
|
-
" via with_sample_data, or defaults only (post-installation.md §3)\n" +
|
|
206
|
+
" plus the mandatory /order-received route (useOrderReturn + your markup)\n" +
|
|
207
|
+
" 3. Seed the store — one commerce/seed-store call (store_name required) takes the\n" +
|
|
208
|
+
" catalog, currency, shipping locations and payment methods\n" +
|
|
199
209
|
" 4. CLI installs only: npx base44 agents push (the hosted runtime syncs agents on write)"
|
|
200
210
|
);
|
|
201
211
|
})();
|
package/skills/commerce/SKILL.md
CHANGED
|
@@ -1,66 +1,132 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: commerce
|
|
3
|
-
description: Base44 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 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
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
8
|
+
**This file is the map. It tells you what to read, when, and what you can skip.**
|
|
9
|
+
Nothing else in this skill needs to be open right now.
|
|
10
|
+
|
|
11
|
+
The kit ships, already written and tested: 20 `commerce.*` entities, the
|
|
12
|
+
`commerce/*` backend functions (storefront API, admin API, payments, webhooks,
|
|
13
|
+
emails), the shared commerce engine, a complete admin back office at
|
|
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.)
|
|
27
|
+
|
|
28
|
+
## Installing right now?
|
|
29
|
+
|
|
30
|
+
Read **[`install/01-install.md`](./install/01-install.md)** and follow it. It
|
|
31
|
+
routes you to `install/02-storefront.md` when you start the UI and
|
|
32
|
+
`install/03-data.md` when you seed the catalog — in that order, at those
|
|
33
|
+
moments. Read nothing else up front: those three files are the whole install,
|
|
34
|
+
and each one says when a reference is genuinely needed.
|
|
35
|
+
|
|
36
|
+
## Four things to hold from the start
|
|
37
|
+
|
|
38
|
+
- **Entity names are dotted; SDK access is bracket syntax only** —
|
|
39
|
+
`base44.entities["commerce.Product"]`. `commerce__Product` and `Product` do
|
|
40
|
+
not exist. The full data-model map is [`docs/entities.md`](./docs/entities.md);
|
|
41
|
+
never scan `base44/entities/` to find a name.
|
|
42
|
+
- **Store configuration is declared in one seed call**, not assembled by editing
|
|
43
|
+
records: catalog, shipping zones ("€20 in Europe, €100 worldwide" is a
|
|
44
|
+
six-line `locations` payload), currency, payment methods. See
|
|
45
|
+
[`install/03-data.md`](./install/03-data.md).
|
|
46
|
+
- **Don't weaken the admin gating.** Three layers — the UI guard, entity RLS,
|
|
47
|
+
and `requireAdmin()` in every admin function — see
|
|
48
|
+
[`install/01-install.md`](./install/01-install.md). Keep all three when you
|
|
49
|
+
touch routes or schemas.
|
|
50
|
+
- **Online card payments are optional and off by default.** A seeded store takes
|
|
51
|
+
manual (`offline`) payment and works end to end. Cards are a late, deliberate
|
|
52
|
+
step, and the decision plus the timing live in
|
|
53
|
+
[`install/03-data.md`](./install/03-data.md).
|
|
54
|
+
|
|
55
|
+
## The storefront: logic is premade, UI never is
|
|
56
|
+
|
|
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.
|
|
62
|
+
|
|
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:
|
|
70
|
+
|
|
71
|
+
```jsx
|
|
72
|
+
import { useCart, CartLine, useTotalsLines } from "@/commerce/storefront";
|
|
73
|
+
const { status, lines } = useCart(); // branch on status, map lines into YOUR rows
|
|
74
|
+
```
|
|
36
75
|
|
|
37
|
-
|
|
76
|
+
The admin UI (`src/commerce/admin/`) is finished and **also yours to change** —
|
|
77
|
+
restyle it, add pages, rework flows. To understand the backend it talks to, read
|
|
78
|
+
[`docs/api-admin.md`](./docs/api-admin.md).
|
|
38
79
|
|
|
39
|
-
|
|
80
|
+
Four rules the API enforces — a storefront that skips them cannot complete a
|
|
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
|
|
83
|
+
[`docs/api-storefront.md`](./docs/api-storefront.md) for non-React clients:
|
|
40
84
|
|
|
41
|
-
|
|
85
|
+
1. A product with variants needs **one selector per attribute**, resolved to a
|
|
86
|
+
`variation_id` (`useProduct` + `variantAxes`).
|
|
87
|
+
2. Checkout must **recalculate shipping from the address and send a choice**
|
|
88
|
+
(`useCheckout` — automatic).
|
|
89
|
+
3. **`/order-received` must exist** and render the return state, including a
|
|
90
|
+
manual order's payment instructions (`useOrderReturn`).
|
|
91
|
+
4. **Never advertise what isn't configured** — no free-shipping banner without a
|
|
92
|
+
real rate, no coupon codes without a field to redeem them in.
|
|
42
93
|
|
|
43
|
-
All functions return the envelope `{ success, data }
|
|
94
|
+
All backend functions return the envelope `{ success, data }`; with the SDK the
|
|
95
|
+
payload is `res.data.data`:
|
|
44
96
|
|
|
45
97
|
```js
|
|
46
98
|
const res = await base44.functions.invoke("commerce/storefront-catalog", { action: "list-products", per_page: 12 });
|
|
47
|
-
const { products, has_next } = res.data.data;
|
|
99
|
+
const { products, has_next } = res.data.data;
|
|
48
100
|
```
|
|
49
101
|
|
|
50
|
-
##
|
|
51
|
-
|
|
52
|
-
Open
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
|
58
|
-
|
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
|
|
|
65
|
-
|
|
|
66
|
-
|
|
|
102
|
+
## Where to look for what
|
|
103
|
+
|
|
104
|
+
Open a file when its work starts — not while planning. The last column is there
|
|
105
|
+
so you can answer "would that file help?" without paying for it.
|
|
106
|
+
|
|
107
|
+
| Topic | Open when | Already covered without opening | Size |
|
|
108
|
+
|---|---|---|---|
|
|
109
|
+
| [`install/01-install.md`](./install/01-install.md) | installing — it routes you to 02 and 03 | — | 8K |
|
|
110
|
+
| [`install/02-storefront.md`](./install/02-storefront.md) | building storefront pages | — | 14K |
|
|
111
|
+
| [`install/03-data.md`](./install/03-data.md) | seeding the catalog, shipping, payments decision | — | 14K |
|
|
112
|
+
| [`docs/entities.md`](./docs/entities.md) | any direct entity read/write, or "which entity holds X" | function-mediated flows never need it; the addressing rule is above | 11K |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
116
|
+
| [`references/reviews.md`](./references/reviews.md) | moderation, or a policy beyond the `policy` prop | `useProductReviews` covers list + form + policies | 4K |
|
|
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 |
|
|
118
|
+
| [`references/emails.md`](./references/emails.md) | order-email recipients, subjects, per-type overrides, the log | transactional emails already send | 5K |
|
|
119
|
+
| [`references/admin-product-form.md`](./references/admin-product-form.md) | editing the shipped product editor | the editor works as shipped | 4K |
|
|
120
|
+
| [`references/store-admin-agent.md`](./references/store-admin-agent.md) | changing the StoreAdmin copilot or its tools | it registers and works on install | 3K |
|
|
121
|
+
| [`references/guest-access-security.md`](./references/guest-access-security.md) | **adding your own function or entity**, or questions about RLS/identity | the shipped functions already handle guest access | 5K |
|
|
122
|
+
| [`references/operations.md`](./references/operations.md) | scheduled maintenance, scaling limits, outbound webhooks | nothing here is needed to ship a store | 5K |
|
|
123
|
+
| [`docs/api-storefront.md`](./docs/api-storefront.md) | filters, customer accounts, refunds, a non-React client — anything past the happy path | the entire buy path | 40K |
|
|
124
|
+
| [`docs/api-admin.md`](./docs/api-admin.md) | changing admin pages, automating against admin functions, the full seed contract | — | 28K |
|
|
125
|
+
|
|
126
|
+
**The read budget.** Content you pull into context is re-read on every later
|
|
127
|
+
call, so a file opened while planning costs many times what the same file costs
|
|
128
|
+
opened at the moment it is used. Open one reference when its task starts, take
|
|
129
|
+
what you need, and when a stage's checklist passes, record that stage's
|
|
130
|
+
carry-forward lines and treat the file as gone. If a reference and this map
|
|
131
|
+
disagree, the reference wins — but if a *rule* appears in code (a hook's return
|
|
132
|
+
value, its doc comment), the code wins over both.
|
|
@@ -2,27 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
For building store automation or an alternative admin UI against the Base44 Commerce Template. The bundled admin UI (`src/commerce/admin/`) uses exactly this surface.
|
|
4
4
|
|
|
5
|
-
## Data
|
|
6
|
-
|
|
7
|
-
Two
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|---|---|---|---|
|
|
11
|
-
| commerce.Product, commerce.ProductVariation | direct (`filter`/`get`/`list`) | **`commerce/admin-products`** | derived pricing/stock, taxonomy counts, webhooks |
|
|
12
|
-
| commerce.Order, commerce.OrderNote | direct | **`commerce/admin-orders`** | lifecycle side effects (stock, emails, webhooks, dates) |
|
|
13
|
-
| commerce.OrderRefund | direct | **`commerce/admin-refunds`** | restock, totals, refund status transition |
|
|
14
|
-
| commerce.Coupon | direct | **`commerce/admin-coupons`** | code normalization/uniqueness, webhooks |
|
|
15
|
-
| commerce.Customer | direct | **`commerce/admin-customers`** | email uniqueness, invite/link, stats |
|
|
16
|
-
| commerce.ProductReview | direct | **`commerce/admin-reviews`** | rating recalculation |
|
|
17
|
-
| commerce.ProductCategory, commerce.ProductRibbon | direct | **direct CRUD**, or `commerce/admin-products` `save-term`/`delete-term`/`list-terms` (the API/agent path) | category slug uniqueness; ribbon get-or-create by name |
|
|
18
|
-
| commerce.ProductAttribute, commerce.ProductAttributeTerm | direct | **direct CRUD**, or `commerce/admin-products` `save-term`/`delete-term`/`list-terms` (the API/agent path) | attribute `code` uniqueness; value rename rewrites products; attribute delete cascades its values |
|
|
19
|
-
| commerce.ShippingTaxLocation | direct | **direct CRUD** | config; consumed by the pricing engine at read time |
|
|
20
|
-
| commerce.PaymentGateway | direct | **`commerce/seed-store`** `payment_methods` to switch methods on/off; **direct CRUD** to change a record (add or rename a manual option, its `description`, bank accounts, `order`) | one seed call converges every gateway row to the given set, so "enable cards", "card-only" and "offline-only" are one idempotent call; the record's own fields are ordinary config — the admin's Settings → Payments screen edits them directly |
|
|
21
|
-
| commerce.StoreSettings | direct | **direct CRUD** (one record per `group_id`) | grouped config |
|
|
22
|
-
| commerce.Webhook | direct | **direct CRUD** (+ `commerce/admin-webhooks` for test/redeliver) | definition is data; dispatch is engine |
|
|
23
|
-
| commerce.WebhookDelivery, commerce.EmailLog | direct (read-only logs) | written by the engine | audit logs |
|
|
24
|
-
|
|
25
|
-
**Every entity is admin-only on read *and* write** — including the catalog. Storefront access goes exclusively through the `commerce/storefront-*` functions, which run with the service role and project only what is safe; a direct entity read or write from a non-admin is rejected by the backend regardless of the UI. See [`references/guest-access-security.md`](../references/guest-access-security.md).
|
|
5
|
+
## Data access
|
|
6
|
+
|
|
7
|
+
Two styles. **Reads are direct** entity SDK calls. **Mutations with side effects go through `commerce/admin-*` functions**; plain config entities are direct CRUD. Which entity is which, its key fields, its derived fields and the exact write path is the catalog table in [**`entities.md`**](./entities.md) — including the addressing rule (`base44.entities["commerce.<Name>"]`, bracket syntax only) that a guessed spelling gets wrong.
|
|
8
|
+
|
|
9
|
+
**Every entity is admin-only on read *and* write**, catalog included. Storefront access goes exclusively through the `commerce/storefront-*` functions, which run with the service role and project only what is safe; a direct entity read or write from a non-admin is rejected by the backend regardless of the UI. See [`../references/guest-access-security.md`](../references/guest-access-security.md).
|
|
26
10
|
|
|
27
11
|
## Invocation & envelope
|
|
28
12
|
|
|
@@ -45,7 +29,7 @@ Success: `{ success: true, data }`. Failure: `{ success: false, error, code }` w
|
|
|
45
29
|
Actions: `save` · `delete` · `batch` · `duplicate` · `set-stock` · `search` · `save-term` · `delete-term` · `list-terms`
|
|
46
30
|
|
|
47
31
|
- **`save`** — `{ product, variations? }`. Upserts the product (create if no `id`); when `variations` is provided, diffs them (create/update/delete-missing).
|
|
48
|
-
> **Selling variants takes both of these in *this* call**: the attribute listed **on the product** as `attributes: [{ attribute_id, name, position, options: [...] }]`, plus a `variations` array with one entry per stocked combination (each `{ attributes: [{ attribute_id, name, option }], sku, regular_price, manage_stock: "yes", stock_quantity, status: "publish" }`) and `default_attributes` for the pre-selected combination. There is **no `type` field**: carrying attributes is what makes a product sell variants, so a `commerce.ProductAttribute` record on its own changes nothing — and a product listing an attribute with no variations cannot be added to a cart at all (`400 variation_required`). Descriptive properties belong in `meta_data`, not `attributes`. **Don't set the parent's price** —
|
|
32
|
+
> **Selling variants takes both of these in *this* call**: the attribute listed **on the product** as `attributes: [{ attribute_id, name, position, options: [...] }]`, plus a `variations` array with one entry per stocked combination (each `{ attributes: [{ attribute_id, name, option }], sku, regular_price, manage_stock: "yes", stock_quantity, status: "publish" }`) and `default_attributes` for the pre-selected combination. There is **no `type` field**: carrying attributes is what makes a product sell variants, so a `commerce.ProductAttribute` record on its own changes nothing — and a product listing an attribute with no variations cannot be added to a cart at all (`400 variation_required`). Descriptive properties belong in `meta_data`, not `attributes`. **Don't set the parent's price** — it is rolled up from the cheapest publishable variant on every save ([derived fields](./entities.md#derived-fields--never-write-these)). Enforces SKU + slug uniqueness across products *and* variations (auto-suffixes slug on collision; `duplicate_sku` on SKU clash); derives `price`/`on_sale` from the sale window, `stock_status` when stock is managed, and category/ribbon `count`; fires `product.created`/`product.updated`. → `{ product, variations }`.
|
|
49
33
|
- **`delete`** — `{ id }`. Cascades variations, decrements counts, fires `product.deleted`.
|
|
50
34
|
- **`batch`** — `{ create?: [], update?: [], delete?: [] }` (≤100 total) → per-item results.
|
|
51
35
|
- **`duplicate`** — `{ id }` → new draft copy (name "(Copy)", suffixed SKU, reset sales/ratings) incl. variations.
|
|
@@ -142,7 +126,7 @@ Actions: `test` · `redeliver` (webhook definitions themselves are direct `comme
|
|
|
142
126
|
|
|
143
127
|
## commerce/admin-reports
|
|
144
128
|
|
|
145
|
-
All actions scan orders on demand (counted = `date_paid` set, or status `processing`/`completed`).
|
|
129
|
+
All actions scan orders on demand (counted = `date_paid` set, or status `processing`/`completed`). Scaling: [`../references/operations.md`](../references/operations.md).
|
|
146
130
|
|
|
147
131
|
| Action | Payload | Returns |
|
|
148
132
|
|---|---|---|
|
|
@@ -175,16 +159,93 @@ Actions: `status` · `admin-email-recipients` · `recount-terms` · `recount-cou
|
|
|
175
159
|
|
|
176
160
|
Actions: `create-link` · `complete-return` · `verify` — the admin side of online payments (the same function serves customers, who authorize with `order_key` instead; see [`api-storefront.md`](./api-storefront.md)).
|
|
177
161
|
|
|
178
|
-
- **`create-link`** — `{ order_id }` → `{ url, reference }`: a provider-hosted payment page for an unpaid order
|
|
162
|
+
- **`create-link`** — `{ order_id }` → `{ url, reference }`: a provider-hosted payment page for an unpaid order — the **payment link** to send a customer. Accepts an order on **any** payment method (including none, as admin-created orders start) and switches it onto the card gateway, logging the change: refunds key off `payment_method`, so this keeps the order honest about how it was paid. `409 already_paid`, `400 card_payments_disabled`, `503 no_card_payment_provider`.
|
|
179
163
|
- **`verify`** — `{ order_id }` → `{ paid, already_confirmed, status, order }`: re-asks the provider and moves the order to `processing` when the money is there. Idempotent.
|
|
180
|
-
- **`complete-return`** —
|
|
164
|
+
- **`complete-return`** — what the storefront's mandatory `/order-received` page calls; full contract in [`api-storefront.md`](./api-storefront.md#commercepayments--online-payment-for-an-order).
|
|
181
165
|
|
|
182
166
|
`commerce/payment-webhook` is the provider's server-to-server callback (raw body, no `action` envelope). The function is **premade** — it validates every event through `card-payment.ts`'s `parseWebhook` and never trusts an event body on its own: an unverified event only names an order, and payment is verified through `checkCardPaymentPaid` against the provider's API, so no signing secret is needed. Until `card-payment.ts` is implemented it answers `400 webhook_not_implemented`; events it can't tie to an order are acknowledged with `200 { ignored: true }` so providers don't retry. See [`../references/online-payments.md`](../references/online-payments.md).
|
|
183
167
|
|
|
184
168
|
## commerce/seed-store
|
|
185
169
|
|
|
186
|
-
Not action-routed
|
|
170
|
+
Not action-routed — the body **is** the payload. Requires admin. This is the **canonical seed contract**; the worked example lives in [`../install/03-data.md`](../install/03-data.md).
|
|
171
|
+
|
|
172
|
+
**Order of operations**, so a failure says how much was written: (1) validate — pure, so bad input is **`400 invalid_payload`** carrying every problem at once in `errors: [{ path, error }]`, before anything runs; (2) **canary schema probe** per entity the seeder writes — any incompatibility is **`422 schema_incompatible`** with `errors: [{ entity, error }]`, nothing written; (3) defaults, idempotently (settings groups, gateways, and the fallback location **only when the payload brings no `locations`**); (4) the catalog, rolling back everything this call created on a mid-write failure (**`500 catalog_seed_failed`**), reused taxonomy untouched.
|
|
173
|
+
|
|
174
|
+
### Payload — store setup
|
|
175
|
+
|
|
176
|
+
| Key | Type | Effect |
|
|
177
|
+
|---|---|---|
|
|
178
|
+
| `store_name` | string | **Required on a first seed** (the `emails` group doesn't exist yet) — otherwise **`400 store_name_required`**. Lands in `emails.store_name`: both the name in email subjects and the sender name, so a nameless store renders `[]: New order #1002`. On an already-seeded store it **fills a blank** name and never overwrites one the merchant chose. |
|
|
179
|
+
| `currency` | ISO code | Sets `general.currency`, validated against the shared currencies table. Prices are formatted with `Intl.NumberFormat` — there are no format settings. |
|
|
180
|
+
| `weight_unit` | `kg`\|`g`\|`lbs`\|`oz` | Sets `general.weight_unit`. |
|
|
181
|
+
| `dimension_unit` | `cm`\|`m`\|`mm`\|`in`\|`yd` | Sets `general.dimension_unit`. |
|
|
182
|
+
| `payment_methods` | non-empty array of gateway slugs, e.g. `["card"]` | Enables exactly these and **disables every other gateway row**, so "card-only"/"offline-only"/"both" is one idempotent call, no entity editing. Unknown slugs fail `400 invalid_payload` listing the known ones. |
|
|
183
|
+
| `with_sample_data` | boolean | The template's demo catalog, **only when the store has zero products**. Cannot combine with `products`/`coupons`/`locations`. |
|
|
184
|
+
| `products` `coupons` `locations` | arrays | The catalog payload — below. |
|
|
185
|
+
|
|
186
|
+
`currency`, the units and `payment_methods` **always win**, first seed and re-runs alike: there is no blank state distinguishing a merchant's choice from the default, so passing one is an instruction. `store_name` is the exception (fill-blank only) — pass the app's name **as the platform shows it**: `base44/config.jsonc` → `name` is not authoritative (it can say `New App` for an app the platform calls `Canvas`) and a function's env is only `BASE44_APP_ID`, so ask the user or read the dashboard.
|
|
187
|
+
|
|
188
|
+
**Omit `payment_methods` and the seed enables `offline`, leaving `card` disabled** — cards are off by default and only belong on with a provider wired, since an enabled card option with nothing behind it answers `503 no_card_payment_provider` at checkout. The decision: [`../install/03-data.md`](../install/03-data.md).
|
|
189
|
+
|
|
190
|
+
### Payload — `products` / `coupons`
|
|
191
|
+
|
|
192
|
+
The one-call catalog bootstrap. Entries reference categories/ribbons/attributes **by display name** — taxonomy is get-or-created (slugs/codes derived, existing records matched case-insensitively and reused, their casing canonicalized into the product), so no id juggling.
|
|
193
|
+
|
|
194
|
+
| Key | Shape | Notes |
|
|
195
|
+
|---|---|---|
|
|
196
|
+
| `categories` `ribbons` | `["Dresses"]` | display names; created if new, reused if not |
|
|
197
|
+
| `attributes` | `[{ name, options }]` or `{ <name>: [options] }` | declares the variant axes |
|
|
198
|
+
| `variations` | `[{ options: { <name>: <option> }, ...overrides }]` | the stocked combinations. **Omit it to auto-generate all** combinations, each inheriting the product-level price/sale fields |
|
|
199
|
+
| `default_options` | `{ <name>: <option> }` | the pre-selected combination |
|
|
200
|
+
| everything else | `commerce.Product` fields | `name` (required), `slug`, `sku`, `status`, `regular_price`, `sale_price`, `images`, `featured`, `virtual`, `downloadable`, `downloads`, `tax_status`, `tax_group`, `stock_quantity`, `weight`, `dimensions`, `meta_data` |
|
|
201
|
+
|
|
202
|
+
A variation with its own `stock_quantity` gets `manage_stock: "yes"`; without one it draws on the parent's pooled stock (`"parent"`), and its SKU is synthesized from the parent's when absent. Parent price and `stock_status` are derived by the same helpers as `save` ([derived fields](./entities.md#derived-fields--never-write-these)), but **no `product.created` webhooks fire** — bootstrap precedes subscribers, so use `admin-products` for webhook-visible creates. **Re-runs converge**: a product whose `sku` (or, with none, derived slug) already exists is skipped and reported, so retries never duplicate; an explicit variation SKU already in use is **`409 duplicate_sku`**. `coupons` is a thin passthrough (code lowercased, skip-if-exists).
|
|
187
203
|
|
|
188
|
-
|
|
204
|
+
**Limits:** ≤100 products, ≤50 variations per product, ≤500 variations per call, ≤50 coupons, ≤50 locations.
|
|
205
|
+
|
|
206
|
+
### Payload — `locations`
|
|
207
|
+
|
|
208
|
+
Seeding `locations` is **the** way to set shipping up. Each entry becomes a `commerce.ShippingTaxLocation`, skip-if-exists **by name**.
|
|
209
|
+
|
|
210
|
+
| Key | Shape | Notes |
|
|
211
|
+
|---|---|---|
|
|
212
|
+
| `name` | string, required | the skip-if-exists match key |
|
|
213
|
+
| `countries` | `["IL", "DE"]` | ISO country codes → `{ type: "country" }` regions |
|
|
214
|
+
| `continents` | `["EU"]` | one of `AF` `AN` `AS` `EU` `NA` `OC` `SA`, sparing you a 51-code country list. `EU` is the **continent** Europe, not the European Union. An unknown code fails listing all seven |
|
|
215
|
+
| `regions` | `[{ type, code }]` | raw escape hatch (`country`\|`state`\|`continent`; state codes are `US:CA`) |
|
|
216
|
+
| `rest_of_world` | `true` | the **catch-all** every unmatched address falls to. It has no regions, so combining it with `countries`/`continents`/`regions` is a contradiction and fails validation |
|
|
217
|
+
| `order` | integer | match priority, **ascending**. Defaults to the **payload position**, so the array reads as the priority; an explicit value wins |
|
|
218
|
+
| `shipping_rates` | `[{ id?, name, cost, free_over? }]` | one checkout choice each. `id` is minted from the location + rate names when omitted, and must stay stable (carts and orders reference the chosen rate by it). `free_over` = discounted items subtotal at which it becomes free. Negative `cost` fails |
|
|
219
|
+
| `tax_groups` | `[{ name, rates: [{ name, rate }] }]` | a `Products` group is prepended when missing (products pick one by name via `tax_group`). A `rate` outside 0–100 fails |
|
|
220
|
+
| `shipping_tax` | `{ type: "percent"\|"fixed", value }` | tax on the shipping line |
|
|
221
|
+
|
|
222
|
+
```js
|
|
223
|
+
locations: [
|
|
224
|
+
{ name: "Europe", continents: ["EU"], shipping_rates: [{ name: "Standard", cost: 20 }] },
|
|
225
|
+
{ name: "Worldwide", rest_of_world: true, shipping_rates: [{ name: "International", cost: 100 }] },
|
|
226
|
+
]
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**A payload carrying `locations` suppresses the seeded `Rest of the world` fallback** (free standard delivery, `order: 999`) — yours become the store's only shipping data, since a second no-region location beside them would be a fallback nobody asked for: shadowed today, live the moment theirs is reordered or deleted. The cost: **locations with no catch-all ship only to the listed regions**, every other address getting `shipping_not_available` at checkout. Plausibly intended, so not an error — it returns as `warnings: ["no_catchall_location: …"]`. Full model: [`../references/shipping-and-tax.md`](../references/shipping-and-tax.md).
|
|
230
|
+
|
|
231
|
+
### Response
|
|
232
|
+
|
|
233
|
+
```js
|
|
234
|
+
{
|
|
235
|
+
seeded: { settings_groups, gateways, locations }, // counts created by this call
|
|
236
|
+
sample_data: {…} | false, // same shape as `catalog`
|
|
237
|
+
catalog: { // null when no catalog keys were passed
|
|
238
|
+
categories, ribbons, attributes, terms, // each { created, reused }
|
|
239
|
+
products_created, products_skipped, variations_created,
|
|
240
|
+
coupons: { created, skipped }, locations: { created, skipped },
|
|
241
|
+
products: [ { name, id, slug, sku, variation_count }
|
|
242
|
+
| { name, skipped: true, reason: "sku_exists"|"slug_exists", existing_id } ],
|
|
243
|
+
} | null,
|
|
244
|
+
store_name: { value, action: "created"|"filled"|"unchanged"|"kept_existing" },
|
|
245
|
+
currency: { value, action: "created"|"updated"|"unchanged" } | null,
|
|
246
|
+
payment_methods: { enabled: [slug], disabled: [slug] } | null, // null when not passed
|
|
247
|
+
warnings: [ "no_catchall_location: …" ], // ALWAYS present; empty = nothing to flag
|
|
248
|
+
}
|
|
249
|
+
```
|
|
189
250
|
|
|
190
|
-
|
|
251
|
+
Record `products[].slug` — those are the URLs your storefront routes to — and read `warnings` unconditionally.
|