create-cartbase 0.1.6 → 0.1.8
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 +13 -8
- package/dist/index.js +28 -20
- package/package.json +21 -21
- package/template/app/AGENTS.md +35 -0
- package/template/app/CLAUDE.md +24 -7
- package/template/app/docs/BUILD-A-STOREFRONT.md +233 -226
- package/template/app/docs/README.md +1 -0
- package/template/app/docs/components.md +8 -7
- package/template/app/docs/deploy.md +15 -10
- package/template/app/docs/platform.md +1 -1
- package/template/app/docs/store.md +47 -0
- package/template/app/docs/variables.md +3 -3
- package/template/app/next.config.ts +10 -14
- package/template/app/package.json +4 -3
- package/template/app/src/app/checkout/checkout-page-client.tsx +0 -20
- package/template/app/src/app/globals.css +1 -1
- package/template/app/src/app/layout.tsx +59 -18
- package/template/app/src/lib/config.ts +30 -21
- package/template/app/next-env.d.ts +0 -6
- package/template/app/smoke.mjs +0 -158
- package/template/app/src/app/checkout/mypos-demo-tab.tsx +0 -101
- package/template/app/src/app/gallery/[slug]/page.tsx +0 -172
- package/template/app/src/app/gallery/_components/specimens.tsx +0 -254
- package/template/app/src/app/gallery/_components/status.tsx +0 -47
- package/template/app/src/app/gallery/_lib/catalog.ts +0 -59
- package/template/app/src/app/gallery/_lib/registry.ts +0 -401
- package/template/app/src/app/gallery/design-system/page.tsx +0 -353
- package/template/app/src/app/gallery/layout.tsx +0 -83
- package/template/app/src/app/gallery/page.tsx +0 -81
- package/template/app/tsconfig.tsbuildinfo +0 -1
|
@@ -1,226 +1,233 @@
|
|
|
1
|
-
# Build a storefront
|
|
2
|
-
|
|
3
|
-
**This runbook takes you from a blank Next.js app to a completed checkout
|
|
4
|
-
against your Cartbase store.** It is written to be followed by a developer
|
|
5
|
-
or handed to a coding agent as-is. Each step names the domain doc that
|
|
6
|
-
carries the full contracts (shapes, curls, error codes, settings). Follow
|
|
7
|
-
the steps in order — later steps assume earlier wiring exists. If anything
|
|
8
|
-
here is unclear or wrong, it's a documentation bug — report it.
|
|
9
|
-
|
|
10
|
-
What you need before starting
|
|
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
|
-
|
|
36
|
-
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
@
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
the
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
- [
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
`/collections
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
[
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
(
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
## Step
|
|
200
|
-
|
|
201
|
-
[
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
- [ ]
|
|
217
|
-
-
|
|
218
|
-
- [ ]
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
[
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
1
|
+
# Build a storefront
|
|
2
|
+
|
|
3
|
+
**This runbook takes you from a blank Next.js app to a completed checkout
|
|
4
|
+
against your Cartbase store.** It is written to be followed by a developer
|
|
5
|
+
or handed to a coding agent as-is. Each step names the domain doc that
|
|
6
|
+
carries the full contracts (shapes, curls, error codes, settings). Follow
|
|
7
|
+
the steps in order — later steps assume earlier wiring exists. If anything
|
|
8
|
+
here is unclear or wrong, it's a documentation bug — report it.
|
|
9
|
+
|
|
10
|
+
What you need before starting: ONE value, the store's publishable key.
|
|
11
|
+
Your Cartbase admin hands it out on the **Storefront** page (and lists it
|
|
12
|
+
under Settings → API keys).
|
|
13
|
+
|
|
14
|
+
| Input | Example | Where it goes |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| Publishable API key | `pk_…` | `NEXT_PUBLIC_CARTBASE_PUBLISHABLE_KEY` |
|
|
17
|
+
|
|
18
|
+
The key names the store on its own and scopes the catalog to the key's
|
|
19
|
+
sales channels. The API origin is the platform's, `https://admin.cartbase.ai`
|
|
20
|
+
(`NEXT_PUBLIC_CARTBASE_URL` only overrides it for a local or staging
|
|
21
|
+
platform). The store's id is not an input: hosted builds receive it, and
|
|
22
|
+
`GET /api/store/store` answers it for anything that needs it.
|
|
23
|
+
|
|
24
|
+
Sanity-check the store before writing any code:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# The regions listing is the cheapest liveness + auth probe.
|
|
28
|
+
curl -sf "$BASE/api/store/regions" -H "x-publishable-api-key: $PUBLISHABLE_KEY" | grep -q '"regions"'
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Step 1 — Scaffold + package
|
|
32
|
+
|
|
33
|
+
Create the Next.js app (App Router) and add the package:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# doc-noexec — scaffolding happens in YOUR repo, not against the API.
|
|
37
|
+
bunx create-next-app@latest my-store --ts --app --tailwind
|
|
38
|
+
cd my-store && bun add @cartbase/storefront
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- The package is **source-shipped TypeScript** — add
|
|
42
|
+
`transpilePackages: ["@cartbase/storefront"]` to `next.config` or nothing
|
|
43
|
+
from it will compile.
|
|
44
|
+
- **Styling is two imports and nothing else.** Tailwind 4 is CSS-first, so
|
|
45
|
+
there is no config file and no preset to register. In your `globals.css`:
|
|
46
|
+
|
|
47
|
+
```css
|
|
48
|
+
@import "tailwindcss";
|
|
49
|
+
@import "@cartbase/storefront/theme";
|
|
50
|
+
|
|
51
|
+
/* The package ships TypeScript source, so Tailwind must scan it or the
|
|
52
|
+
components render unstyled. */
|
|
53
|
+
@source "../../node_modules/@cartbase/storefront/src";
|
|
54
|
+
|
|
55
|
+
/* Dark mode is class-based: put `.dark` on <html>. */
|
|
56
|
+
@custom-variant dark (&:where(.dark, .dark *));
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The theme **ships filled**, so the store renders designed before you choose
|
|
60
|
+
a single value. To make it yours, override the token values (`--primary`,
|
|
61
|
+
`--background`, `--radius-base`, the font families) after the imports.
|
|
62
|
+
Never redefine the token *names*: components reference them, and the whole
|
|
63
|
+
library repaints from the values alone.
|
|
64
|
+
- Pin the package version — storefronts never float `latest`.
|
|
65
|
+
- Monorepo caveat: when the app lives in a workspace, set
|
|
66
|
+
`outputFileTracingRoot` in `next.config` — Next infers the root from
|
|
67
|
+
the nearest stray lockfile, and a wrong root silently breaks
|
|
68
|
+
page-segment hydration in dev (buttons render but nothing responds).
|
|
69
|
+
|
|
70
|
+
## Step 2 — The client seam
|
|
71
|
+
|
|
72
|
+
Construct ONE `StorefrontClient` per scope and pass it to every SDK call
|
|
73
|
+
(all SDK functions take the client as first argument — see any domain doc's
|
|
74
|
+
SDK line):
|
|
75
|
+
|
|
76
|
+
- **Server** (RSC, server actions): construct per request; `getAuthToken`
|
|
77
|
+
reads the customer session cookie; `getLocale` reads the locale cookie.
|
|
78
|
+
- **Browser**: construct once; token from your session store.
|
|
79
|
+
|
|
80
|
+
Auth model (recap — full detail in [auth.md](auth.md)):
|
|
81
|
+
`x-publishable-api-key` always (it names the store and scopes the catalog
|
|
82
|
+
and carts to the key's sales channels; the SDK sends it for you);
|
|
83
|
+
`authorization: Bearer <jwt>` once a customer is logged in. `x-client-id`
|
|
84
|
+
is the platform's own door and is accepted in the key's place; the
|
|
85
|
+
endpoint docs' curls use it because the docs harness runs as the platform.
|
|
86
|
+
|
|
87
|
+
**CORS (bites every browser client):** the store API sends NO CORS
|
|
88
|
+
headers today, so browser-side SDK calls (cart drawer mutations, the
|
|
89
|
+
whole checkout orchestration) only work same-origin. Unless your
|
|
90
|
+
storefront is served from the Cartbase deployment origin itself, proxy the
|
|
91
|
+
store surface through your own origin — in Next.js one rewrite does it —
|
|
92
|
+
and give the BROWSER client `window.location.origin` as `baseUrl`
|
|
93
|
+
(server-side calls hit `NEXT_PUBLIC_CARTBASE_URL` directly and are
|
|
94
|
+
unaffected; see `examples/storefront/next.config.ts` for the working
|
|
95
|
+
rewrite):
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
// next.config.ts — proxy browser SDK traffic to the API origin
|
|
99
|
+
async rewrites() {
|
|
100
|
+
return [{ source: "/api/store/:path*",
|
|
101
|
+
destination: `${process.env.NEXT_PUBLIC_CARTBASE_URL}/api/store/:path*` }]
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Step 3 — Store configuration bootstrap
|
|
106
|
+
|
|
107
|
+
Fetch once at layout level, cache per the docs' cache headers:
|
|
108
|
+
|
|
109
|
+
1. [regions.md](regions.md) — regions (→ region_id for pricing + payment
|
|
110
|
+
providers), currencies, supported locales.
|
|
111
|
+
2. [integrations.md](integrations.md) — `GET /api/store/integrations`: which
|
|
112
|
+
carriers are enabled (pickup points/lockers for checkout), COD fee
|
|
113
|
+
presence, and the **tracking block** (pixel/GA4/GTM/ads public ids).
|
|
114
|
+
3. [consent.md](consent.md) — the CMP config for the consent banner.
|
|
115
|
+
|
|
116
|
+
## Step 4 — Layout: consent, tracking, navigation
|
|
117
|
+
|
|
118
|
+
Order inside `<body>` matters (contracts in [components.md](components.md)
|
|
119
|
+
and [consent.md](consent.md)):
|
|
120
|
+
|
|
121
|
+
1. `<ConsentInit>` FIRST child of body — static and synchronous, never
|
|
122
|
+
awaits a fetch (first-hit consent race otherwise).
|
|
123
|
+
2. Tracking mounts (`<MetaPixel>`, `<GA4>`, GTM) — gated on the consent
|
|
124
|
+
state and fed by the integrations tracking block, never by env vars.
|
|
125
|
+
3. Navigation from [menus.md](menus.md) — `main-menu` / `footer` handles;
|
|
126
|
+
an unknown handle 404s and must render as "no nav", never crash.
|
|
127
|
+
|
|
128
|
+
## Step 5 — Catalog
|
|
129
|
+
|
|
130
|
+
- [products.md](products.md) — listing + PDP. Always pass a pricing context
|
|
131
|
+
(`currency_code` or `region_id`) or prices come back undecorated; render
|
|
132
|
+
`variant.calculated_price`, fall back to base `prices[]`. **Never cache a
|
|
133
|
+
`calculated_price` response shared when a customer JWT was present** —
|
|
134
|
+
prices vary by customer group.
|
|
135
|
+
- [collections.md](collections.md) — collection pages use the membership
|
|
136
|
+
endpoint (`/collections/:id/products`) which honors the admin's sort.
|
|
137
|
+
- [categories.md](categories.md) — category tree, tags, types.
|
|
138
|
+
- [search.md](search.md) — search page: `q` + facets from the response
|
|
139
|
+
(render buckets, apply via the documented query params), typo-tolerant,
|
|
140
|
+
synonym-aware. Related products on the PDP come from the same doc.
|
|
141
|
+
- SEO: `seo_title`/`seo_description` fields with title/description
|
|
142
|
+
fallbacks; path conventions are `/products/<handle>`,
|
|
143
|
+
`/collections/<handle>`, `/categories/<handle>`.
|
|
144
|
+
|
|
145
|
+
## Step 6 — Content
|
|
146
|
+
|
|
147
|
+
- [content.md](content.md) — `/pages/<handle>` and `/blogs/<handle>` routes;
|
|
148
|
+
body HTML is server-sanitized, safe to render raw. Every store seeds
|
|
149
|
+
policy pages (`privacy-policy`, `terms-of-service`, `refund-policy`,
|
|
150
|
+
`shipping-policy`) — link them in the footer.
|
|
151
|
+
- [metaobjects.md](metaobjects.md) — merchant-defined content (size charts
|
|
152
|
+
via the product→metafield→metaobject chain).
|
|
153
|
+
- [redirects.md](redirects.md) — call ONLY from your `not-found` handler;
|
|
154
|
+
301 when `to_path` is non-null. Never on regular page loads.
|
|
155
|
+
|
|
156
|
+
## Step 7 — Cart
|
|
157
|
+
|
|
158
|
+
[carts.md](carts.md): create the cart lazily on first add-to-cart with the
|
|
159
|
+
region + (optionally) sales channel; persist `cart.id` in a cookie; all cart
|
|
160
|
+
mutations return the decorated cart — totals are SERVER truth, render them
|
|
161
|
+
verbatim, never compute client-side. Line items, quantity updates, deletes,
|
|
162
|
+
and the customer-attach call after login are all in that doc.
|
|
163
|
+
[gift-cards.md](gift-cards.md): the apply/remove endpoints + the three
|
|
164
|
+
decoration fields (`gift_cards[]`, `gift_card_total`,
|
|
165
|
+
`gift_card_remainder`) your summary UI must render.
|
|
166
|
+
|
|
167
|
+
## Step 8 — Checkout
|
|
168
|
+
|
|
169
|
+
[checkout.md](checkout.md) is the authoritative sequence. In brief:
|
|
170
|
+
|
|
171
|
+
1. List shipping options and payment providers **with `cart_id`** — the
|
|
172
|
+
server filters both through the merchant's checkout rules; your UI never
|
|
173
|
+
hides methods on its own.
|
|
174
|
+
2. Carrier pickers (office/locker) come from the integrations config
|
|
175
|
+
([integrations.md](integrations.md)); the chosen point goes into
|
|
176
|
+
`carrier_metadata`.
|
|
177
|
+
3. Buy click = `prepare-checkout` (ONE call: address + shipping method +
|
|
178
|
+
payment session at the final amount) → for card: Stripe
|
|
179
|
+
`confirmPayment(client_secret)` → `complete`. For COD: `complete`
|
|
180
|
+
directly. Gift-card-covered carts skip the provider entirely.
|
|
181
|
+
4. Handle the documented failure codes (`checkout_method_hidden`,
|
|
182
|
+
`account_required`, `gift_card_insufficient_balance`, cart-vs-order
|
|
183
|
+
union on complete) — each has a UI recovery path described in the doc.
|
|
184
|
+
5. `sync-payment-amount` after any total-changing edit on the payment step;
|
|
185
|
+
`refresh-payment-if-terminal` only from Elements `loaderror` / aged-cart
|
|
186
|
+
mount.
|
|
187
|
+
6. Order confirmation renders from [orders.md](orders.md)
|
|
188
|
+
(`/orders/display/:displayId` embeds items, fulfillments, tracking).
|
|
189
|
+
|
|
190
|
+
## Step 9 — Customer accounts
|
|
191
|
+
|
|
192
|
+
[auth.md](auth.md): passwordless email-code login (request → verify →
|
|
193
|
+
Bearer session). [customers.md](customers.md): profile, addresses,
|
|
194
|
+
order history, issued documents (invoices). Respect `accounts_mode`
|
|
195
|
+
(store setting): `required` blocks guest checkout with `403
|
|
196
|
+
account_required`; `disabled` means render no account UI at all. Gate B2B
|
|
197
|
+
content on `customer.account_status === "approved"`.
|
|
198
|
+
|
|
199
|
+
## Step 10 — Reviews
|
|
200
|
+
|
|
201
|
+
[reviews.md](reviews.md): the PDP widget (`/reviews/widget` — aggregate +
|
|
202
|
+
first page + display options in one call) and the token wizard route for
|
|
203
|
+
email CTA links (`/review/<token>`): validate token → submit rating+body →
|
|
204
|
+
optional photo step mints the reward code. Resume rules are in the doc.
|
|
205
|
+
|
|
206
|
+
## Step 11 — Tracking events
|
|
207
|
+
|
|
208
|
+
[components.md](components.md) tracking section: fire client events via the
|
|
209
|
+
package helpers; **Purchase MUST use `eventID = "purchase_" +
|
|
210
|
+
order.display_id`** so Meta dedupes browser Pixel against the server CAPI
|
|
211
|
+
event; write the attribution keys (fbp/fbc/anon-id/ga session) into
|
|
212
|
+
`cart.metadata` (consent-gated) so server events inherit them.
|
|
213
|
+
|
|
214
|
+
## Step 12 — Go-live checklist
|
|
215
|
+
|
|
216
|
+
- [ ] Pricing context passed on every catalog surface; no shared caching of
|
|
217
|
+
JWT-priced responses.
|
|
218
|
+
- [ ] Consent banner renders for an unconfigured store (defaults are
|
|
219
|
+
server-applied); tags mount only behind consent.
|
|
220
|
+
- [ ] Checkout completes: card (Stripe live), COD (fee renders from config),
|
|
221
|
+
gift card (partial + full cover).
|
|
222
|
+
- [ ] Not-found handler consults redirects; policy pages linked.
|
|
223
|
+
- [ ] Order confirmation + emails render totals identical to the cart.
|
|
224
|
+
- [ ] Locale switching keeps cart + session; EUR everywhere.
|
|
225
|
+
- [ ] The store's publishable key (if any) is set — B2B catalogs are
|
|
226
|
+
key-scoped and look "missing products" without it.
|
|
227
|
+
|
|
228
|
+
## Step 13 — Ship it
|
|
229
|
+
|
|
230
|
+
[deploy.md](deploy.md): send the app to Cartbase hosting — permanent
|
|
231
|
+
preview URL on every deploy, **Publish** to go live, deploy history as
|
|
232
|
+
rollback. Hosted storefronts get the three inputs above injected
|
|
233
|
+
automatically, so there is nothing to configure.
|
|
@@ -58,6 +58,7 @@ One file per domain. For each endpoint, in order:
|
|
|
58
58
|
| [search.md](search.md) | Search, facets, related products |
|
|
59
59
|
| [collections.md](collections.md) | Collections + membership listings |
|
|
60
60
|
| [categories.md](categories.md) | Categories, tags, types |
|
|
61
|
+
| [store.md](store.md) | The store's identity: name, slug, brand |
|
|
61
62
|
| [regions.md](regions.md) | Regions, currencies, locales |
|
|
62
63
|
| [carts.md](carts.md) | Cart lifecycle + line items |
|
|
63
64
|
| [gift-cards.md](gift-cards.md) | Gift-card tender on carts |
|
|
@@ -217,7 +217,7 @@ pure UI over the theme tokens.
|
|
|
217
217
|
---
|
|
218
218
|
|
|
219
219
|
## Family: lib (`@cartbase/storefront/lib/*`)
|
|
220
|
-
Pure helpers — no React except `
|
|
220
|
+
Pure helpers — no React except `price`, no fetches. The SDK never
|
|
221
221
|
invents server truths: prices/totals arrive computed from the API
|
|
222
222
|
(`variant.calculated_price`, `cart.total`); these helpers only select and
|
|
223
223
|
format.
|
|
@@ -226,10 +226,11 @@ format.
|
|
|
226
226
|
- `lib/money` — `convertToLocale({amount, currency_code, …})` Intl currency
|
|
227
227
|
formatting (amounts are decimal EUR major units per the store contract);
|
|
228
228
|
`noDivisionCurrencies`.
|
|
229
|
-
- `lib/
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
229
|
+
- `lib/price` — `<Price amount currencyCode className />`: the amount in its
|
|
230
|
+
own currency, formatted through `convertToLocale`. Every price in the
|
|
231
|
+
library renders through it. It shows ONE currency; a store that wants a
|
|
232
|
+
second one displayed adds it in its own locale layer rather than in
|
|
233
|
+
every price.
|
|
233
234
|
- `lib/cart-helpers` — `isProductLine` / `isFeeLine` / `productTotal` /
|
|
234
235
|
`productItemCount` / `findFeeLine` + `COD_FEE_METADATA_KEY`. THE single
|
|
235
236
|
source of truth for hiding the backend-injected COD-fee line in cart
|
|
@@ -412,7 +413,7 @@ code-first through the error-copy maps, never raw API strings.
|
|
|
412
413
|
`fields.billingDetails.address` override — the strict-completeness
|
|
413
414
|
IntegrationError fix). `PaymentButton` = the Buy button: re-entry-guarded
|
|
414
415
|
click → `performBuyClick`, cycling processing narration, translated
|
|
415
|
-
inline errors,
|
|
416
|
+
inline errors, Price total.
|
|
416
417
|
- **SDK calls** — renders `checkout.listPaymentProviders` results via the
|
|
417
418
|
hook's `hasCard`/`hasCod`; the click path runs the hook's calls.
|
|
418
419
|
- **Props contract** — hook state + `buyButtonNotReady(Reason?)`,
|
|
@@ -964,7 +965,7 @@ component also takes a `labels` prop pick.
|
|
|
964
965
|
### `<OrderTotals totals currencyCode items? methodFeeLabel? />` — `order/order-totals`
|
|
965
966
|
|
|
966
967
|
- **Purpose** — the money breakdown: Subtotal / Shipping (FREE badge at
|
|
967
|
-
0) / COD fee / Discount (negated) / Tax / Total, all via `
|
|
968
|
+
0) / COD fee / Discount (negated) / Tax / Total, all via `Price`.
|
|
968
969
|
- **Data seam** — `OrderTotalsSource` (the decorated cart satisfies it:
|
|
969
970
|
`item_subtotal`, `shipping_subtotal`, `discount_total`, `tax_total`,
|
|
970
971
|
`total`, `payment_method_fee_total`, `payment_method_fee_label`); the summary snapshot adapts
|
|
@@ -5,7 +5,10 @@ Cartbase builds and hosts your storefront. You send the app's source files
|
|
|
5
5
|
running site: first on a **permanent preview URL**, then, when you press
|
|
6
6
|
**Publish** (or call the publish endpoint), on the store's live domain.
|
|
7
7
|
You never touch build servers, DNS, or hosting configuration; the platform
|
|
8
|
-
provisions all of it
|
|
8
|
+
provisions all of it the moment the store is created, and makes the first
|
|
9
|
+
deploy itself: the store's starting point, a complete storefront on your
|
|
10
|
+
own (still empty) catalog, already running on the preview link. Pull it
|
|
11
|
+
with the CLI, change anything, deploy it back.
|
|
9
12
|
|
|
10
13
|
Two URLs exist per store, both created automatically:
|
|
11
14
|
|
|
@@ -56,24 +59,25 @@ Error codes you can hit: `empty_bundle`, `bundle_too_large`,
|
|
|
56
59
|
|
|
57
60
|
## Environment — provided, not configured
|
|
58
61
|
|
|
59
|
-
Hosted storefronts receive
|
|
60
|
-
|
|
62
|
+
Hosted storefronts receive their store's values automatically at build
|
|
63
|
+
time; do **not** put them in the bundle (`.env*` files are blocked
|
|
61
64
|
anyway):
|
|
62
65
|
|
|
63
66
|
| Variable | Value |
|
|
64
67
|
|---|---|
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
68
|
+
| `NEXT_PUBLIC_CARTBASE_PUBLISHABLE_KEY` | The store's publishable key, the one input a storefront needs |
|
|
69
|
+
| `NEXT_PUBLIC_CARTBASE_URL` | The platform origin (a constant; injected so a hosted build never guesses) |
|
|
70
|
+
| `NEXT_PUBLIC_CARTBASE_CLIENT_ID` | The store's id, the platform's own door; a storefront never needs to copy it |
|
|
68
71
|
|
|
69
72
|
Only these public values ever reach a storefront build — secret keys are
|
|
70
73
|
never injected, so code that expects one is a design error.
|
|
71
74
|
|
|
72
75
|
## Deploy to preview
|
|
73
76
|
|
|
74
|
-
`POST /api/admin/storefront/deploys` — the one ingestion door.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
`POST /api/admin/storefront/deploys` — the one ingestion door. Hosting is
|
|
78
|
+
provisioned when the store is born, so every call is just a deploy; on a
|
|
79
|
+
store whose hosting setup failed, the first call resumes it (a few extra
|
|
80
|
+
seconds).
|
|
77
81
|
|
|
78
82
|
```bash
|
|
79
83
|
# doc-noexec — admin-session auth; run from an authenticated context.
|
|
@@ -153,7 +157,8 @@ curl -s "$BASE/api/admin/storefront"
|
|
|
153
157
|
}
|
|
154
158
|
```
|
|
155
159
|
|
|
156
|
-
`storefront` is
|
|
160
|
+
`storefront` is present from the store's birth (`null` only on a store
|
|
161
|
+
created before hosting-at-birth that has never deployed). `status:
|
|
157
162
|
"provisioning"` / `"failed"` (with `last_error`) describe hosting setup,
|
|
158
163
|
not builds; a failed provisioning resumes automatically on the next
|
|
159
164
|
deploy attempt.
|
|
@@ -88,7 +88,7 @@ window.Cartbase").
|
|
|
88
88
|
```tsx
|
|
89
89
|
// app/layout.tsx, inside <body>, first children:
|
|
90
90
|
<ConsentInit />
|
|
91
|
-
<PlatformInit storeId={
|
|
91
|
+
<PlatformInit storeId={store.id} /> // from GET /api/store/store (store.md)
|
|
92
92
|
```
|
|
93
93
|
|
|
94
94
|
Also build-time/render-only — no store-API curl to demonstrate; the exact
|