@base44/app-plugin-commerce 0.1.0
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/LICENSE +21 -0
- package/README.md +117 -0
- package/base44/agents/commerce/StoreAdmin.jsonc +64 -0
- package/base44/entities/commerce.Cart.jsonc +73 -0
- package/base44/entities/commerce.Coupon.jsonc +113 -0
- package/base44/entities/commerce.Customer.jsonc +96 -0
- package/base44/entities/commerce.DownloadPermission.jsonc +54 -0
- package/base44/entities/commerce.EmailLog.jsonc +43 -0
- package/base44/entities/commerce.Order.jsonc +287 -0
- package/base44/entities/commerce.OrderNote.jsonc +31 -0
- package/base44/entities/commerce.OrderRefund.jsonc +64 -0
- package/base44/entities/commerce.PaymentGateway.jsonc +48 -0
- package/base44/entities/commerce.Product.jsonc +291 -0
- package/base44/entities/commerce.ProductAttribute.jsonc +39 -0
- package/base44/entities/commerce.ProductAttributeTerm.jsonc +38 -0
- package/base44/entities/commerce.ProductCategory.jsonc +51 -0
- package/base44/entities/commerce.ProductReview.jsonc +48 -0
- package/base44/entities/commerce.ProductTag.jsonc +30 -0
- package/base44/entities/commerce.ProductVariation.jsonc +167 -0
- package/base44/entities/commerce.ShippingClass.jsonc +30 -0
- package/base44/entities/commerce.ShippingZone.jsonc +41 -0
- package/base44/entities/commerce.ShippingZoneMethod.jsonc +84 -0
- package/base44/entities/commerce.StoreSettings.jsonc +23 -0
- package/base44/entities/commerce.TaxClass.jsonc +23 -0
- package/base44/entities/commerce.TaxRate.jsonc +68 -0
- package/base44/entities/commerce.Webhook.jsonc +57 -0
- package/base44/entities/commerce.WebhookDelivery.jsonc +45 -0
- package/base44/functions/commerce/admin-coupons/entry.ts +100 -0
- package/base44/functions/commerce/admin-customers/entry.ts +141 -0
- package/base44/functions/commerce/admin-orders/entry.ts +396 -0
- package/base44/functions/commerce/admin-orders/helpers.ts +246 -0
- package/base44/functions/commerce/admin-products/entry.ts +506 -0
- package/base44/functions/commerce/admin-refunds/entry.ts +158 -0
- package/base44/functions/commerce/admin-reports/entry.ts +283 -0
- package/base44/functions/commerce/admin-reviews/entry.ts +66 -0
- package/base44/functions/commerce/admin-tools/entry.ts +261 -0
- package/base44/functions/commerce/admin-webhooks/entry.ts +52 -0
- package/base44/functions/commerce/payment-webhook/entry.ts +135 -0
- package/base44/functions/commerce/payments/entry.ts +238 -0
- package/base44/functions/commerce/seed-store/defaults.ts +162 -0
- package/base44/functions/commerce/seed-store/entry.ts +310 -0
- package/base44/functions/commerce/seed-store/sample-data.ts +349 -0
- package/base44/functions/commerce/storefront-account/entry.ts +207 -0
- package/base44/functions/commerce/storefront-cart/cart-pricing.ts +258 -0
- package/base44/functions/commerce/storefront-cart/entry.ts +283 -0
- package/base44/functions/commerce/storefront-catalog/entry.ts +459 -0
- package/base44/functions/commerce/storefront-checkout/cart-pricing.ts +258 -0
- package/base44/functions/commerce/storefront-checkout/entry.ts +485 -0
- package/base44/shared/commerce/auth.ts +60 -0
- package/base44/shared/commerce/coupons.ts +257 -0
- package/base44/shared/commerce/data/continents.ts +75 -0
- package/base44/shared/commerce/data/countries.ts +307 -0
- package/base44/shared/commerce/data/currencies.ts +46 -0
- package/base44/shared/commerce/email-templates.ts +240 -0
- package/base44/shared/commerce/emails.ts +225 -0
- package/base44/shared/commerce/money.ts +66 -0
- package/base44/shared/commerce/orders.ts +251 -0
- package/base44/shared/commerce/payments.ts +495 -0
- package/base44/shared/commerce/reviews.ts +36 -0
- package/base44/shared/commerce/scan.ts +57 -0
- package/base44/shared/commerce/sequence.ts +35 -0
- package/base44/shared/commerce/settings.ts +57 -0
- package/base44/shared/commerce/shipping.ts +215 -0
- package/base44/shared/commerce/stock.ts +227 -0
- package/base44/shared/commerce/stripe.ts +463 -0
- package/base44/shared/commerce/tax.ts +136 -0
- package/base44/shared/commerce/totals.ts +314 -0
- package/base44/shared/commerce/webhooks.ts +116 -0
- package/package.json +37 -0
- package/scripts/install.js +156 -0
- package/skills/commerce/SKILL.md +62 -0
- package/skills/commerce/docs/api-admin.md +186 -0
- package/skills/commerce/docs/api-storefront.md +408 -0
- package/skills/commerce/installation-guidelines.md +91 -0
- package/skills/commerce/post-installation.md +157 -0
- package/skills/commerce/references/emails.md +13 -0
- package/skills/commerce/references/guest-access-security.md +18 -0
- package/skills/commerce/references/limits-and-performance.md +16 -0
- package/skills/commerce/references/media-and-downloads.md +4 -0
- package/skills/commerce/references/online-payments.md +201 -0
- package/skills/commerce/references/product-render.md +87 -0
- package/skills/commerce/references/scheduled-work.md +19 -0
- package/skills/commerce/references/storefront-product-page.md +83 -0
- package/skills/commerce/references/webhooks.md +8 -0
- package/src/commerce/admin/README.md +107 -0
- package/src/commerce/admin/bot/Markdown.jsx +138 -0
- package/src/commerce/admin/bot/StoreAdminBot.jsx +249 -0
- package/src/commerce/admin/bot/pipe-tables.js +116 -0
- package/src/commerce/admin/components/AddressForm.jsx +78 -0
- package/src/commerce/admin/components/ConfirmDialog.jsx +52 -0
- package/src/commerce/admin/components/CountrySelect.jsx +81 -0
- package/src/commerce/admin/components/DataTable.jsx +192 -0
- package/src/commerce/admin/components/DateRangePicker.jsx +91 -0
- package/src/commerce/admin/components/EmptyState.jsx +17 -0
- package/src/commerce/admin/components/MediaUploader.jsx +116 -0
- package/src/commerce/admin/components/MetaDataEditor.jsx +45 -0
- package/src/commerce/admin/components/MoneyInput.jsx +50 -0
- package/src/commerce/admin/components/PageHeader.jsx +29 -0
- package/src/commerce/admin/components/RichTextarea.jsx +21 -0
- package/src/commerce/admin/components/SearchSelect.jsx +142 -0
- package/src/commerce/admin/components/StatusBadge.jsx +17 -0
- package/src/commerce/admin/context/BasePathContext.jsx +26 -0
- package/src/commerce/admin/context/SettingsContext.jsx +207 -0
- package/src/commerce/admin/hooks/useAsync.js +46 -0
- package/src/commerce/admin/hooks/useDebounce.js +11 -0
- package/src/commerce/admin/hooks/useMoney.js +52 -0
- package/src/commerce/admin/hooks/usePagedList.js +83 -0
- package/src/commerce/admin/hooks/usePaymentProvider.js +27 -0
- package/src/commerce/admin/hooks/useRealtime.js +129 -0
- package/src/commerce/admin/index.jsx +34 -0
- package/src/commerce/admin/layout/AccessDenied.jsx +54 -0
- package/src/commerce/admin/layout/AdminLayout.jsx +33 -0
- package/src/commerce/admin/layout/AuthGuard.jsx +84 -0
- package/src/commerce/admin/layout/Sidebar.jsx +130 -0
- package/src/commerce/admin/layout/Topbar.jsx +94 -0
- package/src/commerce/admin/lib/api.js +55 -0
- package/src/commerce/admin/lib/constants.js +157 -0
- package/src/commerce/admin/lib/format.js +27 -0
- package/src/commerce/admin/lib/geo-data.js +125 -0
- package/src/commerce/admin/lib/order-utils.js +147 -0
- package/src/commerce/admin/lib/paths.js +35 -0
- package/src/commerce/admin/lib/product-utils.js +55 -0
- package/src/commerce/admin/pages/Dashboard.jsx +245 -0
- package/src/commerce/admin/pages/coupons/CouponEditor.jsx +565 -0
- package/src/commerce/admin/pages/coupons/CouponsList.jsx +172 -0
- package/src/commerce/admin/pages/customers/CustomerEditor.jsx +318 -0
- package/src/commerce/admin/pages/customers/CustomersList.jsx +169 -0
- package/src/commerce/admin/pages/orders/OrderEditor.jsx +952 -0
- package/src/commerce/admin/pages/orders/OrdersList.jsx +227 -0
- package/src/commerce/admin/pages/orders/components/AddProductDialog.jsx +149 -0
- package/src/commerce/admin/pages/orders/components/DownloadPermissionsPanel.jsx +119 -0
- package/src/commerce/admin/pages/orders/components/LineItemsTable.jsx +208 -0
- package/src/commerce/admin/pages/orders/components/OrderNotesPanel.jsx +123 -0
- package/src/commerce/admin/pages/orders/components/PaymentPanel.jsx +199 -0
- package/src/commerce/admin/pages/orders/components/RefundPanel.jsx +239 -0
- package/src/commerce/admin/pages/orders/components/TotalsBox.jsx +52 -0
- package/src/commerce/admin/pages/products/AttributeTerms.jsx +180 -0
- package/src/commerce/admin/pages/products/Attributes.jsx +183 -0
- package/src/commerce/admin/pages/products/Categories.jsx +236 -0
- package/src/commerce/admin/pages/products/ProductEditor.jsx +267 -0
- package/src/commerce/admin/pages/products/ProductsList.jsx +391 -0
- package/src/commerce/admin/pages/products/Reviews.jsx +255 -0
- package/src/commerce/admin/pages/products/Tags.jsx +150 -0
- package/src/commerce/admin/pages/products/components/ProductDataPanel.jsx +132 -0
- package/src/commerce/admin/pages/products/components/PublishBox.jsx +101 -0
- package/src/commerce/admin/pages/products/components/TaxonomyPanel.jsx +243 -0
- package/src/commerce/admin/pages/products/components/tabs/AdvancedTab.jsx +48 -0
- package/src/commerce/admin/pages/products/components/tabs/AttributesTab.jsx +208 -0
- package/src/commerce/admin/pages/products/components/tabs/DownloadsTab.jsx +91 -0
- package/src/commerce/admin/pages/products/components/tabs/ExternalTab.jsx +41 -0
- package/src/commerce/admin/pages/products/components/tabs/GeneralTab.jsx +103 -0
- package/src/commerce/admin/pages/products/components/tabs/InventoryTab.jsx +93 -0
- package/src/commerce/admin/pages/products/components/tabs/LinkedTab.jsx +102 -0
- package/src/commerce/admin/pages/products/components/tabs/ShippingTab.jsx +86 -0
- package/src/commerce/admin/pages/products/components/tabs/VariationsTab.jsx +377 -0
- package/src/commerce/admin/pages/reports/Reports.jsx +416 -0
- package/src/commerce/admin/pages/settings/EmailsSettings.jsx +240 -0
- package/src/commerce/admin/pages/settings/GeneralSettings.jsx +232 -0
- package/src/commerce/admin/pages/settings/InventorySettings.jsx +146 -0
- package/src/commerce/admin/pages/settings/PaymentsSettings.jsx +260 -0
- package/src/commerce/admin/pages/settings/ProductsSettings.jsx +118 -0
- package/src/commerce/admin/pages/settings/SettingsLayout.jsx +53 -0
- package/src/commerce/admin/pages/settings/ShippingSettings.jsx +304 -0
- package/src/commerce/admin/pages/settings/ShippingZoneEditor.jsx +514 -0
- package/src/commerce/admin/pages/settings/TaxRatesTable.jsx +231 -0
- package/src/commerce/admin/pages/settings/TaxSettings.jsx +281 -0
- package/src/commerce/admin/pages/settings/useGroupForm.jsx +76 -0
- package/src/commerce/admin/pages/status/WebhookEditor.jsx +296 -0
- package/src/commerce/admin/pages/status/Webhooks.jsx +53 -0
- package/src/commerce/admin/routes.jsx +151 -0
- package/src/commerce/utils/index.js +19 -0
- package/src/commerce/utils/shipping-promos.js +99 -0
- package/src/commerce/utils/variants.js +411 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: commerce
|
|
3
|
+
description: Base44 Commerce template — 24 commerce.* entities, 16 commerce/* backend functions (storefront + admin APIs + online payments), the shared commerce engine under base44/shared/commerce/, the store admin UI mounted at /admin, and the commerce/StoreAdmin agent (admin copilot). Read before working on store features — the admin UI, storefront building, Stripe wiring, scheduled maintenance, emails, webhooks, downloads, or scaling limits.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Commerce
|
|
7
|
+
|
|
8
|
+
Operational guidance for extending, operating and building on the Base44 Commerce Template. This file is the map: it stays short, and each topic links to a focused reference under [`skills/commerce/references/`](./references/) — open one only when your task touches that area. The API references live in [`skills/commerce/docs/`](./docs/).
|
|
9
|
+
|
|
10
|
+
> **If you are a Base44 agent working inside the runtime, read this first:**
|
|
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
|
+
> - **Don't weaken the admin gating.** The UI guard, entity RLS and function guards form three enforcement layers — see [`skills/commerce/post-installation.md`](./post-installation.md) — keep all of them intact when changing routes or schemas.
|
|
13
|
+
|
|
14
|
+
## IMPORTANT — first-time installation
|
|
15
|
+
|
|
16
|
+
If the template was just installed (or you are installing it right now), read [`skills/commerce/post-installation.md`](./post-installation.md) **before anything else**: embedding the admin pages, the three-layer admin-role enforcement (do not weaken), **asking the user how the store's data should be created** (generate a real catalog / generic demo data / nothing — §2), and registering the template + skill in the app's `AGENTS.md`. The full install-from-scratch steps are in [`skills/commerce/installation-guidelines.md`](./installation-guidelines.md).
|
|
17
|
+
|
|
18
|
+
## Working on the UI
|
|
19
|
+
|
|
20
|
+
- **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 [`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).
|
|
21
|
+
- **Storefront** — **no visitor UI ships**; the storefront *API* is complete (token-based cart), plus framework-free helpers in [`src/commerce/utils/`](../../src/commerce/utils/) (`variants.js`, `shipping-promos.js` — import from `@/commerce/utils`). Build against [`skills/commerce/docs/api-storefront.md`](./docs/api-storefront.md). Navigation comes from three actions — `list-categories` (tree), `list-tags` (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); **tags are the most-skipped part of it and belong in both views**.
|
|
22
|
+
|
|
23
|
+
### If you build a storefront, these four are not optional
|
|
24
|
+
|
|
25
|
+
Agents keep shipping storefronts that miss these, and each one breaks buying outright. Do them; the details are in [`docs/api-storefront.md`](./docs/api-storefront.md) and [`references/storefront-product-page.md`](./references/storefront-product-page.md).
|
|
26
|
+
|
|
27
|
+
1. **Variant products need one selector per attribute — and the selection must resolve to a variation.** `get-product` gives `product.attributes` (the axes: entries with `variation: true`) and `variations` (the combinations). Render a control per axis, never a list of combinations, then send the resolved `variation_id`:
|
|
28
|
+
```js
|
|
29
|
+
import { resolveSelection, defaultSelection, selectOption } from "@/commerce/utils";
|
|
30
|
+
const view = resolveSelection(product, variations, selection); // axes, availability, price, addToCart
|
|
31
|
+
await inv("commerce/storefront-cart", { action: "add-item", cart_token, ...view.addToCart, quantity });
|
|
32
|
+
```
|
|
33
|
+
`add-item` **rejects a variable product without `variation_id`** (`400 variation_required`), so a page that ignores this cannot sell anything.
|
|
34
|
+
|
|
35
|
+
2. **Checkout must present shipping options and send a choice.** After `set-shipping-address`, read `shipping_status` on the cart: `auto_selected` (one option, already applied) · `chosen` · `choice_required` → **you must show `available_shipping_methods` and call `choose-shipping-method`** · `none_available` → say so. `place-order` refuses with `400 shipping_method_required` until then — that is not a bug to work around.
|
|
36
|
+
|
|
37
|
+
3. **Take card payments, and build `/order-received`.** (Ask the user to connect the provider *after* the store is set up — it's their step in the dashboard, and a catalog plus a shipping method is what makes a test payment provable.) Choosing the online gateway returns `payment.checkout_url` — redirect there. Every payment link comes back to `/order-received`, which **you must implement**: call `commerce/payments` `complete-return` with the query params and render its `state`. Without that page a customer pays into a 404 and the order is never marked paid.
|
|
38
|
+
|
|
39
|
+
4. **Never advertise what isn't configured.** "Free shipping over €150" must come from a real `free_shipping` zone method — `shipping-promos.js` reads it; no rule means no banner.
|
|
40
|
+
|
|
41
|
+
All functions return the envelope `{ success, data }` (or `{ success, error, code }`); with the SDK the body is on `res.data`:
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
const res = await base44.functions.invoke("commerce/storefront-catalog", { action: "list-products", per_page: 12 });
|
|
45
|
+
const { products, has_next } = res.data.data; // res.data = envelope, .data = payload
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Topic references
|
|
49
|
+
|
|
50
|
+
Open the matching file under `skills/commerce/references/` only when a task touches its area:
|
|
51
|
+
|
|
52
|
+
| Topic | Read when the task involves | Reference |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| Product rendering (list + page) | what to show in a product grid vs. the product page, field availability across `list-products`/`get-product`, **tags in both views**, variable pricing on cards, adding a page-only field to the listing call | [`references/product-render.md`](./references/product-render.md) |
|
|
55
|
+
| 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) |
|
|
56
|
+
| Online payments | **any storefront or checkout work** — card payments ship implemented (hosted page, payment links, signed webhook, refunds) behind a provider-neutral utility wired to Stripe; connect a provider to go live, or implement one adapter to use another | [`references/online-payments.md`](./references/online-payments.md) |
|
|
57
|
+
| Scheduled work | recurring maintenance — stock-hold release, abandoned-cart cleanup, webhook-log pruning, counter-drift repair | [`references/scheduled-work.md`](./references/scheduled-work.md) |
|
|
58
|
+
| Emails | transactional order emails, per-type overrides, deliverability, the email log | [`references/emails.md`](./references/emails.md) |
|
|
59
|
+
| Webhooks | outbound webhooks, HMAC signing, delivery log, auto-disable behavior | [`references/webhooks.md`](./references/webhooks.md) |
|
|
60
|
+
| Images & downloads | catalog image uploads, downloadable products, private files & signed URLs | [`references/media-and-downloads.md`](./references/media-and-downloads.md) |
|
|
61
|
+
| Limits & performance | pagination caps, search, no-transaction consequences, record size, reports scaling | [`references/limits-and-performance.md`](./references/limits-and-performance.md) |
|
|
62
|
+
| Guest access & security | public storefront functions, `cart_token`/`order_key` handling, RLS boundaries | [`references/guest-access-security.md`](./references/guest-access-security.md) |
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Admin API Reference
|
|
2
|
+
|
|
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
|
+
|
|
5
|
+
## Data-access contract
|
|
6
|
+
|
|
7
|
+
Two access styles. **Reads are direct** entity SDK calls; **mutations with side effects go through `commerce/admin-*` functions**; simple config entities are **direct CRUD** (protected by admin-only RLS).
|
|
8
|
+
|
|
9
|
+
| Resource | Reads | Writes | Why |
|
|
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.ProductTag | direct | **direct CRUD**, or `commerce/admin-products` `save-term`/`delete-term`/`list-terms` (the API/agent path) | slug uniqueness per taxonomy |
|
|
18
|
+
| commerce.ProductAttribute, commerce.ProductAttributeTerm | direct | **direct CRUD**, or `commerce/admin-products` `save-term`/`delete-term`/`list-terms` (the API/agent path) | slug uniqueness; attribute delete cascades its terms |
|
|
19
|
+
| commerce.ShippingClass | direct | **direct CRUD** | plain config; RLS enforces admin-only |
|
|
20
|
+
| commerce.TaxClass, commerce.TaxRate, commerce.ShippingZone, commerce.ShippingZoneMethod, commerce.PaymentGateway | direct | **direct CRUD** | config; consumed by the pricing engine at read time |
|
|
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
|
+
All entities carry admin-only RLS on the operations they restrict (catalog entities are public-read, admin-write; transactional entities are admin-only on all ops). A direct write from a non-admin is rejected by the backend regardless of the UI.
|
|
26
|
+
|
|
27
|
+
## Invocation & envelope
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
const res = await base44.functions.invoke("commerce/admin-products", { action: "search", q: "shirt", limit: 20 });
|
|
31
|
+
const { rows, has_next } = res.data.data; // res.data = { success, data }; .data = payload
|
|
32
|
+
```
|
|
33
|
+
Success: `{ success: true, data }`. Failure: `{ success: false, error, code }` with the HTTP status set. Auth: every `commerce/admin-*` function calls `requireAdmin()` → **401** if not signed in, **403** if signed in without the `admin` role, before any data access (via the service role).
|
|
34
|
+
|
|
35
|
+
## Pagination & search
|
|
36
|
+
|
|
37
|
+
- No total-count API. List endpoints use **limit + skip** with a **`limit+1`** probe: request one extra row; if it comes back, there's a next page. `search` actions return `{ rows, has_next }`.
|
|
38
|
+
- Entity `filter` is **exact-match only** — free-text search is server-side (`search` actions scan + JS-match), so use those for name/email/code lookups rather than `filter`.
|
|
39
|
+
- Status-tab counts come from dedicated count actions (`commerce/admin-orders` `status-counts`), not from search.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## commerce/admin-products
|
|
44
|
+
|
|
45
|
+
Actions: `save` · `delete` · `batch` · `duplicate` · `set-stock` · `search` · `save-term` · `delete-term` · `list-terms`
|
|
46
|
+
|
|
47
|
+
- **`save`** — `{ product, variations? }`. Upserts the product (create if no `id`); when `variations` is provided, diffs them (create/update/delete-missing).
|
|
48
|
+
> **Creating a variable product takes all four of these**, and the last two must be in *this* call: `type: "variable"`; the attribute listed **on the product** as `attributes: [{ attribute_id, name, position, visible: true, variation: true, options: [...] }]`; 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. A `commerce.ProductAttribute` record on its own changes nothing — the product stays simple and cannot be added to a cart as a variant (`400 variation_required`). Set the parent's `regular_price` to the lowest variation price so catalog cards and price filters work. 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 and `stock_status` when stock is managed; updates category/tag `count`; rolls parent stock up for variable products; fires `product.created`/`product.updated`. → `{ product, variations }`.
|
|
49
|
+
- **`delete`** — `{ id }`. Cascades variations, decrements counts, fires `product.deleted`.
|
|
50
|
+
- **`batch`** — `{ create?: [], update?: [], delete?: [] }` (≤100 total) → per-item results.
|
|
51
|
+
- **`duplicate`** — `{ id }` → new draft copy (name "(Copy)", suffixed SKU, reset sales/ratings) incl. variations.
|
|
52
|
+
- **`set-stock`** — `{ id, variation_id?, quantity }`. Sets quantity, re-derives status, sends low/out-of-stock admin emails on threshold crossings.
|
|
53
|
+
- **`search`** — `{ q?, category_id?, type?, stock_status?, status?, sort?, limit?, skip? }` → `{ rows, has_next }`. `category_id` includes descendant categories.
|
|
54
|
+
- **`save-term`** — `{ taxonomy, term }` → the term. Upserts one taxonomy record. `taxonomy` is `"category"` | `"tag"` | `"attribute"` | `"attribute-term"`, and `term` takes the fields for that one:
|
|
55
|
+
|
|
56
|
+
| taxonomy | entity | `term` fields |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| `category` | commerce.ProductCategory | `id?, name, slug?, description?, parent_id?, image?, menu_order?` |
|
|
59
|
+
| `tag` | commerce.ProductTag | `id?, name, slug?, description?` |
|
|
60
|
+
| `attribute` | commerce.ProductAttribute | `id?, name, slug?, type?, order_by?, has_archives?` |
|
|
61
|
+
| `attribute-term` | commerce.ProductAttributeTerm | `id?, attribute_id` (**required**, must exist)`, name, slug?, description?, menu_order?` |
|
|
62
|
+
|
|
63
|
+
Slug is derived from the name and made unique within that entity — for an attribute term, unique **within its attribute**. A category's `parent_id` pointing at itself is coerced to `""` (`categoryWithDescendants` would loop). This is how a non-UI caller (notably the StoreAdmin agent) creates the records that `category_ids`/`tag_ids`/`attributes[].attribute_id` reference — assigning an id is useless if the record can't be created. **A variable product needs its `commerce.ProductAttribute` and terms to exist first**, so create those, then `save` the product with `attributes[]`/`variations[]`.
|
|
64
|
+
- **`delete-term`** — `{ taxonomy, id, detach? }` → `{ deleted, detached, terms_deleted }`. Deleting an **attribute** always deletes its terms (a term outliving its attribute is unreachable); `detach: true` additionally strips the attribute from every product's `attributes[]`. For a category or tag, products keep the id by default (the admin UI behaves the same, and the storefront skips ids that no longer resolve); `detach: true` strips it from every product first.
|
|
65
|
+
- **`list-terms`** — `{ taxonomy, q?, attribute_id?, limit?, skip? }` → `{ rows, has_next }`. Categories and attribute terms sort by `menu_order`, tags and attributes by `name`; `attribute_id` filters attribute terms to one attribute. Use it to reuse an existing record instead of creating a duplicate.
|
|
66
|
+
|
|
67
|
+
## commerce/admin-orders
|
|
68
|
+
|
|
69
|
+
Actions: `create-draft` · `create` · `update` · `preview` · `update-status` · `bulk-status` · `status-counts` · `search` · `recalculate` · `apply-coupon` · `remove-coupon` · `add-note` · `delete-note` · `send-email` · `grant-download` · `revoke-download` · `delete` · `release-expired-holds`
|
|
70
|
+
|
|
71
|
+
- **`create-draft`** — no payload → empty `pending` admin order (with `order_number`/`order_key`), no lifecycle effects yet. Used by "Add order".
|
|
72
|
+
- **`create`** — `{ items, coupon_codes?, fees?, billing?, shipping?, customer_id?, chosen_shipping_method?, payment_method?, customer_note?, status?, reduce_stock? }`. Prices via the same totals engine as checkout, then transitions to `status` (default `pending`). `reduce_stock: false` on a pending order fires creation effects (new_order email, `order.created`) **without** reducing stock.
|
|
73
|
+
- **`update`** — `{ order_id, patch }`. The **patch** shape:
|
|
74
|
+
- Any time: `billing`, `shipping`, `customer_id`, `customer_note`, `payment_method`, `meta_data`, `status`.
|
|
75
|
+
- **Line edits require pending/on-hold** (else `409 order_locked`): `items` (specs `{ product_id, variation_id?, quantity, price_override? }`), `fees`, `coupon_codes`, `chosen_shipping_method`. Providing any of these — or changing addresses — triggers a full reprice.
|
|
76
|
+
- A `status` in the patch is delegated to the transition engine last (fires the side effects below); otherwise an `order.updated` webhook fires.
|
|
77
|
+
- **`preview`** — `{ order_id, patch }`. **Reads only — writes nothing, fires no webhooks.** Prices the same patch shape `update` takes and returns `{ fields }`: the Order fields `update` *would* have written (`line_items` with per-line tax, `shipping_lines`, `fee_lines`, `coupon_lines`, `tax_lines` and every total). Allowed on locked orders, since nothing is persisted. Only the pricing keys matter (`items`, `fees`, `shipping_lines`, `billing`, `shipping`); the rest are ignored. This is how the admin order editor shows live totals for **unsaved** edits — add a product and the tax, discount and grand total update before saving — without reimplementing tax and coupon rules on the client. Sharing `repriceOptsFromPatch()` with `update` is what guarantees the preview equals the saved result.
|
|
78
|
+
- **`update-status`** — `{ order_id, status, note? }` → runs the transition engine (side-effect matrix below).
|
|
79
|
+
- **`bulk-status`** — `{ ids, status }` → `{ results: [{ id, success, error? }] }`.
|
|
80
|
+
- **`status-counts`** — no payload → `{ all, pending, processing, "on-hold", completed, cancelled, refunded, failed }` (includes the `all` key). Powers the list tabs.
|
|
81
|
+
- **`search`** — `{ q?, status?, date_min?, date_max?, sort?, limit?, skip? }` → `{ rows, has_next }`. `q` matches order number, billing name, billing email.
|
|
82
|
+
- **`recalculate`** — `{ order_id, reprice_from_catalog? }` → re-runs tax + totals on the current lines.
|
|
83
|
+
- **`apply-coupon` / `remove-coupon`** — `{ order_id, code }` (validates, then reprices; `409 order_locked` if not editable, `409 already_applied`).
|
|
84
|
+
- **`add-note`** — `{ order_id, note, is_customer_note? }` (a customer note emails the customer) · **`delete-note`** — `{ note_id }`.
|
|
85
|
+
- **`send-email`** — `{ order_id, type }` (re-send an order email, e.g. `customer_invoice`; `force`d).
|
|
86
|
+
- **`grant-download`** — `{ order_id, product_id }` (creates `commerce.DownloadPermission`s for the product's files) · **`revoke-download`** — `{ permission_id }`.
|
|
87
|
+
- **`delete`** — `{ order_id }` — only `pending`/`cancelled`/`failed` (`409 order_locked` otherwise); cascades notes/refunds/permissions; fires `order.deleted`.
|
|
88
|
+
- **`release-expired-holds`** — cancels unpaid orders past `hold_expires_at` and restores their stock → `{ released }`.
|
|
89
|
+
|
|
90
|
+
### Order status side-effect matrix
|
|
91
|
+
|
|
92
|
+
Applied by the transition engine; each effect is flag-guarded so a re-entered status never double-fires.
|
|
93
|
+
|
|
94
|
+
| Entering | Stock | Coupons | Dates | Emails | Webhook | Downloads |
|
|
95
|
+
|---|---|---|---|---|---|---|
|
|
96
|
+
| pending (create) | reduce + set `hold_expires_at` | — | — | `new_order`→admin | `order.created` | — |
|
|
97
|
+
| processing | ensure reduced; clear hold | count usage | `date_paid` if unset | `processing`→customer (+`new_order` if unsent) | `order.updated` | grant if virtual-only |
|
|
98
|
+
| on-hold | ensure reduced; clear hold | count usage | — | `on_hold_order`→customer | `order.updated` | — |
|
|
99
|
+
| completed | ensure reduced | count usage | `date_completed` (+`date_paid`) | `completed_order`→customer | `order.updated` | grant |
|
|
100
|
+
| cancelled | restore if reduced | uncount if counted | — | `cancelled_order`→admin | `order.updated` | revoke |
|
|
101
|
+
| failed | restore | uncount | — | `failed_order`→admin+customer | `order.updated` | — |
|
|
102
|
+
| refunded | (per-refund restock) | keep counted | — | `refunded_order`→customer | `order.updated` | revoke |
|
|
103
|
+
|
|
104
|
+
`total_sales` increments on first stock reduction, decrements on restore.
|
|
105
|
+
|
|
106
|
+
## commerce/admin-refunds
|
|
107
|
+
|
|
108
|
+
Actions: `create` · `delete`
|
|
109
|
+
|
|
110
|
+
- **`create`** — `{ order_id, amount, reason?, line_items?, restock_items?, refund_payment? }`. `line_items` specs: `{ line_id, quantity, refund_total, refund_tax? }`. Validates `amount` ≤ remaining refundable (`400 amount_exceeds_refundable`, `400 invalid_amount`). Restocks per line when `restock_items`. Updates `order.total_refunded`; a full refund transitions the order to `refunded`, otherwise sends `partial_refund` + `order.updated`. With **`refund_payment: true`** the money goes back through the payment provider **first** — a failed provider refund writes nothing locally, so there is never a phantom refund — and the response carries `gateway_refund: { provider, id, status, amount }` with the record's `refunded_payment: true`. An order that wasn't paid online rejects it with `400 no_online_payment`. → `{ refund, order, gateway_refund? }`.
|
|
111
|
+
- **`delete`** — `{ refund_id }`. Reverses `total_refunded`; adds a note warning that restocked items are **not** auto-un-restocked.
|
|
112
|
+
|
|
113
|
+
## commerce/admin-coupons
|
|
114
|
+
|
|
115
|
+
Actions: `save` · `delete` · `batch` · `search`
|
|
116
|
+
|
|
117
|
+
- **`save`** — `{ coupon }`. Lowercases + enforces unique `code` (`duplicate_code`); percent amount ≤ 100. Fires `coupon.created`/`updated`.
|
|
118
|
+
- **`delete`** — `{ id }` (fires `coupon.deleted`) · **`batch`** — `{ create?, update?, delete? }` · **`search`** — `{ q?, limit?, skip? }` → `{ rows, has_next }` (matches code/description).
|
|
119
|
+
|
|
120
|
+
## commerce/admin-customers
|
|
121
|
+
|
|
122
|
+
Actions: `save` · `delete` · `search` · `invite` · `recalculate-stats`
|
|
123
|
+
|
|
124
|
+
- **`save`** — `{ customer }`. Unique email; links guest→registered. Fires `customer.*`.
|
|
125
|
+
- **`delete`** — `{ id, reassign_orders_to_guest? }`.
|
|
126
|
+
- **`search`** — `{ q?, limit?, skip? }` → `{ rows, has_next }` (name/email/username).
|
|
127
|
+
- **`invite`** — `{ email }` → `base44.users.inviteUser(email)`, links `user_id`.
|
|
128
|
+
- **`recalculate-stats`** — `{ id }` → recomputes `orders_count`, `total_spent`, `is_paying_customer` from the customer's paid orders.
|
|
129
|
+
|
|
130
|
+
## commerce/admin-reviews
|
|
131
|
+
|
|
132
|
+
Actions: `set-status` · `update` · `delete`. Each recomputes the product's `average_rating`/`rating_count` from approved reviews.
|
|
133
|
+
- **`set-status`** — `{ id, status }` (`approved`|`hold`|`spam`|`trash`) · **`update`** — `{ id, rating?, review? }` · **`delete`** — `{ id }`.
|
|
134
|
+
|
|
135
|
+
## commerce/admin-webhooks
|
|
136
|
+
|
|
137
|
+
Actions: `test` · `redeliver` (webhook definitions themselves are direct `commerce.Webhook` CRUD).
|
|
138
|
+
- **`test`** — `{ webhook_id }` → POSTs a sample payload, logs a `commerce.WebhookDelivery`, returns `{ delivery_id, response_code, success, duration_ms }`.
|
|
139
|
+
- **`redeliver`** — `{ delivery_id }` → re-sends the original request body.
|
|
140
|
+
|
|
141
|
+
## commerce/admin-reports
|
|
142
|
+
|
|
143
|
+
All actions scan orders on demand (counted = `date_paid` set, or status `processing`/`completed`). See the commerce skill reference `skills/commerce/references/limits-and-performance.md` for scaling.
|
|
144
|
+
|
|
145
|
+
| Action | Payload | Returns |
|
|
146
|
+
|---|---|---|
|
|
147
|
+
| `summary` | — | `{ sales_today, sales_month, orders_by_status, low_stock_count, out_of_stock_count, top_seller }` where `sales_*` = `{ gross_sales, net_sales, orders, items, tax, shipping, discount, refunds }` |
|
|
148
|
+
| `sales` | `{ date_min?, date_max?, interval? }` (`day`\|`week`\|`month`) | `{ totals: <agg>, series: [{ period, ...agg }] }` (net = gross − refunds − tax − shipping) |
|
|
149
|
+
| `top-sellers` | `{ date_min?, date_max?, limit? }` | `{ rows: [{ product_id, name, sku, quantity, net_revenue }] }` |
|
|
150
|
+
| `stock` | — | `{ low_stock: [...], out_of_stock: [...] }` |
|
|
151
|
+
| `orders-totals` | — | `{ [status]: count }` |
|
|
152
|
+
| `products-totals` | — | `{ total, by: { [type]: count } }` |
|
|
153
|
+
| `customers-totals` | — | `{ total, guests, registered, paying }` |
|
|
154
|
+
| `coupons-totals` | — | `{ total, by: { [discount_type]: count } }` |
|
|
155
|
+
| `reviews-totals` | — | `{ total, by: { [status]: count } }` |
|
|
156
|
+
| `categories-totals` / `tags-totals` | — | `{ total, terms: [{ id, name, count }] }` |
|
|
157
|
+
| `attributes-totals` | — | `{ total, attributes: [{ id, name, terms }] }` |
|
|
158
|
+
|
|
159
|
+
## commerce/admin-tools
|
|
160
|
+
|
|
161
|
+
Actions: `status` · `payment-connector-status` · `admin-email-recipients` · `recount-terms` · `recount-coupon-usage` · `recalculate-customer-stats-all` · `prune-webhook-deliveries` · `clear-abandoned-carts` · `regenerate-download-permissions`
|
|
162
|
+
|
|
163
|
+
- **`status`** — `{ template_version, seeded, settings_groups, counts: { <Entity>: n | "1000+" }, checks: [...] }` — mini system-status; also the seeded/health check for install verification.
|
|
164
|
+
- **`payment-connector-status`** — no payload → `{ provider, provider_label, gateway_slug, connected, error?, connector }`. Whether an online payment provider is usable **right now**, answered by the payment utility for whichever provider is wired — so UI derives payment readiness instead of hardcoding a "not set up" notice, and shows "No payment provider connected" rather than a brand. Connectors are service-role only, hence the round trip; any failure reports `connected: false`. A provider connected **after** the function's last deploy reads as `connected: false` until the functions are redeployed — env vars are injected at deploy time ([`references/online-payments.md`](../references/online-payments.md) §2). (`connector` repeats `provider` for callers written against the older shape.)
|
|
165
|
+
- **`admin-email-recipients`** — no payload → `{ recipients: string[], source: "settings" | "admin_users", admin_users: string[] }`. Where admin notifications go **right now**: `recipients` is the configured `emails.admin_recipients`, or the app's admin users when that is empty (the runtime fallback), with `source` saying which. `admin_users` is returned either way, so Settings → Emails can show the fallback as the field's placeholder even while explicit recipients are set. A client can't resolve it itself — listing users needs service role.
|
|
166
|
+
- **`recount-terms`** — repairs category/tag/term `count`.
|
|
167
|
+
- **`recount-coupon-usage`** — repairs `usage_count`/`used_by`.
|
|
168
|
+
- **`recalculate-customer-stats-all`** — repairs all customers' `orders_count`/`total_spent`.
|
|
169
|
+
- **`prune-webhook-deliveries`** — `{ keep_days }`.
|
|
170
|
+
- **`clear-abandoned-carts`** — `{ older_than_days }`.
|
|
171
|
+
- **`regenerate-download-permissions`** — `{ order_id }`.
|
|
172
|
+
|
|
173
|
+
## commerce/payments
|
|
174
|
+
|
|
175
|
+
Actions: `status` · `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)).
|
|
176
|
+
|
|
177
|
+
- **`status`** (admin) — `{ provider, provider_label, gateway_slug, connected, error? }`.
|
|
178
|
+
- **`create-link`** — `{ order_id }` → `{ provider, session_id, url, expires_at? }`: a provider-hosted payment page for an unpaid order, i.e. the **payment link** to send a customer. Accepts an order with **any** payment method (including none, as admin-created orders start) and switches it onto the online gateway, logging the change — refunds key off `payment_method`, so this keeps the order honest about how it was paid. `409 already_paid`, `400 online_payments_disabled`, `503 payment_provider_unavailable`.
|
|
179
|
+
- **`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`** — `{ order_id, order_key, payment?, return_url? }` → `{ state, paid, already_confirmed, status, order, payment_link }`: what the storefront's **mandatory** `/order-received` page calls — confirm, progress the order, and hand back a render-ready result plus a fresh payment link while unpaid.
|
|
181
|
+
|
|
182
|
+
`commerce/payment-webhook` is the provider's signed callback (raw body, no `action` envelope) and needs `PAYMENT_WEBHOOK_SECRET`; it refuses every request until that is set. Provider specifics live in `base44/shared/commerce/payments.ts` — see [`../references/online-payments.md`](../references/online-payments.md).
|
|
183
|
+
|
|
184
|
+
## commerce/seed-store
|
|
185
|
+
|
|
186
|
+
Not action-routed. Body `{ with_sample_data?: boolean, store_name?: string }`. **`store_name` is required** when the `general` 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 becomes the store name *and*, by inheritance, the "from" 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 and (if `with_sample_data` and the store has no products) a sample catalog. On an already-seeded store a passed `store_name` fills a **blank** name and never overwrites one the merchant chose. → `{ seeded: { settings_groups, gateways, tax_classes, zones, zone_methods }, sample_data: {...counts} | false, store_name: { value, action: "created" | "filled" | "unchanged" | "kept_existing" } }`.
|