create-cartbase 0.1.19 → 0.1.21
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 -21
- package/README.md +25 -25
- package/dist/index.js +20 -20
- package/package.json +24 -24
- package/template/app/docs/auth.md +105 -105
- package/template/app/docs/carts.md +23 -4
- package/template/app/docs/categories.md +194 -194
- package/template/app/docs/checkout.md +714 -714
- package/template/app/docs/components.md +288 -31
- package/template/app/docs/consent.md +91 -91
- package/template/app/docs/deploy.md +197 -197
- package/template/app/docs/gift-cards.md +153 -153
- package/template/app/docs/metaobjects.md +126 -126
- package/template/app/docs/orders.md +221 -221
- package/template/app/docs/regions.md +269 -269
- package/template/app/docs/reviews.md +258 -223
- package/template/app/docs/search.md +227 -227
- package/template/app/docs/store.md +23 -1
- package/template/app/docs/subscriptions.md +148 -148
- package/template/app/docs/variables.md +331 -315
- package/template/app/package.json +1 -1
- package/template/app/postcss.config.cjs +11 -11
- package/template/app/src/app/checkout/checkout-empty.tsx +36 -0
- package/template/app/src/app/checkout/checkout-page-client.tsx +80 -73
- package/template/app/src/app/checkout/error.tsx +23 -0
- package/template/app/src/app/checkout/page.tsx +16 -7
- package/template/app/src/app/globals.css +26 -26
- package/template/app/src/app/layout.tsx +126 -126
- package/template/app/src/app/page.tsx +37 -37
- package/template/app/src/app/products/[handle]/page.tsx +89 -89
- package/template/app/src/app/search/page.tsx +33 -33
- package/template/app/src/lib/browser-client.ts +35 -35
- package/template/app/src/lib/catalog.ts +120 -120
- package/template/app/src/lib/server-client.ts +25 -25
- package/template/app/src/lib/store-client.ts +16 -16
|
@@ -1,269 +1,269 @@
|
|
|
1
|
-
# Regions
|
|
2
|
-
|
|
3
|
-
Catalog-context primitives a storefront resolves at boot: regions feed the
|
|
4
|
-
pricing context (`region_id` → region currency), currencies tell you what the
|
|
5
|
-
store has enabled, locales drive the language switcher and the client's
|
|
6
|
-
`x-locale` header. All reads are anonymous. Money is EUR decimal major units
|
|
7
|
-
everywhere.
|
|
8
|
-
|
|
9
|
-
SDK module: `@cartbase/storefront/api/regions`.
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## GET /api/store/regions
|
|
14
|
-
|
|
15
|
-
- **Purpose** — list the store's regions; a storefront usually picks one at
|
|
16
|
-
boot (or by shopper choice) and passes its id/currency as the pricing
|
|
17
|
-
context on catalog reads.
|
|
18
|
-
- **Auth** — anon: `x-client-id` required.
|
|
19
|
-
- **Request** — `GET /api/store/regions`
|
|
20
|
-
|
|
21
|
-
```jsonc
|
|
22
|
-
// query (all optional)
|
|
23
|
-
{
|
|
24
|
-
"q": "bulg", // case-insensitive substring on name
|
|
25
|
-
"currency_code": "eur", // exact match, lowercased server-side
|
|
26
|
-
"limit": 50, // 1–200, default 50
|
|
27
|
-
"offset": 0
|
|
28
|
-
}
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
- **Response** — ordered by name. NOTE: Cartbase regions carry NO embedded
|
|
32
|
-
`countries` array — country/tax scope lives server-side in
|
|
33
|
-
`tax_regions`.
|
|
34
|
-
|
|
35
|
-
```jsonc
|
|
36
|
-
{
|
|
37
|
-
"regions": [
|
|
38
|
-
{
|
|
39
|
-
"id": "reg_01tst000000000000000000001",
|
|
40
|
-
"name": "Bulgaria",
|
|
41
|
-
"currency_code": "eur", // lowercase — feeds the pricing context
|
|
42
|
-
"automatic_taxes": true,
|
|
43
|
-
"metadata": null,
|
|
44
|
-
"created_at": "2026-07-01T00:00:00.000Z",
|
|
45
|
-
"updated_at": "2026-07-01T00:00:00.000Z"
|
|
46
|
-
}
|
|
47
|
-
],
|
|
48
|
-
"count": 1,
|
|
49
|
-
"offset": 0,
|
|
50
|
-
"limit": 50
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
- **Working curl**
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
REGIONS=$(curl -sf "$BASE/api/store/regions" -H "x-client-id: $CLIENT_ID")
|
|
58
|
-
echo "$REGIONS" | grep -q '"regions"'
|
|
59
|
-
echo "$REGIONS" | grep -q '"count"'
|
|
60
|
-
REGION_ID=$(echo "$REGIONS" | grep -o '"id":"reg_[^"]*"' | head -1 | cut -d'"' -f4)
|
|
61
|
-
test -n "$REGION_ID"
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
- **Errors** — 400 `missing_client_id` (header absent/empty),
|
|
65
|
-
400 `validation_failed` (bad limit/offset).
|
|
66
|
-
- **SDK** — `listRegions(client, query?)`.
|
|
67
|
-
- **Components** — region/currency selector (see components.md).
|
|
68
|
-
- **Settings** — Admin → Settings → Regions.
|
|
69
|
-
|
|
70
|
-
```bash
|
|
71
|
-
# Auth contract: no x-client-id → 400 missing_client_id
|
|
72
|
-
STATUS=$(curl -s -o /dev/null -w '%{http_code}' "$BASE/api/store/regions")
|
|
73
|
-
test "$STATUS" = 400
|
|
74
|
-
curl -s "$BASE/api/store/regions" | grep -q '"code":"missing_client_id"'
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
---
|
|
78
|
-
|
|
79
|
-
## GET /api/store/regions/:id
|
|
80
|
-
|
|
81
|
-
- **Purpose** — retrieve one region (e.g. re-hydrate the shopper's stored
|
|
82
|
-
choice).
|
|
83
|
-
- **Auth** — anon: `x-client-id` required.
|
|
84
|
-
- **Request** — `GET /api/store/regions/{region_id}` — no query.
|
|
85
|
-
- **Response** — `{ "region": { ...same shape as the list rows... } }`
|
|
86
|
-
- **Working curl**
|
|
87
|
-
|
|
88
|
-
```bash
|
|
89
|
-
REGION=$(curl -sf "$BASE/api/store/regions/$REGION_ID" -H "x-client-id: $CLIENT_ID")
|
|
90
|
-
echo "$REGION" | grep -q '"region"'
|
|
91
|
-
echo "$REGION" | grep -q '"currency_code"'
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
- **Errors** — 404 `not_found` (unknown id, soft-deleted, or another
|
|
95
|
-
tenant's region — invisible, not forbidden).
|
|
96
|
-
|
|
97
|
-
```bash
|
|
98
|
-
STATUS=$(curl -s -o /dev/null -w '%{http_code}' \
|
|
99
|
-
"$BASE/api/store/regions/reg_doesnotexist$RUN" -H "x-client-id: $CLIENT_ID")
|
|
100
|
-
test "$STATUS" = 404
|
|
101
|
-
curl -s "$BASE/api/store/regions/reg_doesnotexist$RUN" \
|
|
102
|
-
-H "x-client-id: $CLIENT_ID" | grep -q '"code":"not_found"'
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
- **SDK** — `retrieveRegion(client, regionId)`.
|
|
106
|
-
- **Components** — region/currency selector.
|
|
107
|
-
- **Settings** — Admin → Settings → Regions.
|
|
108
|
-
|
|
109
|
-
---
|
|
110
|
-
|
|
111
|
-
## GET /api/store/countries
|
|
112
|
-
|
|
113
|
-
- **Purpose** — the countries a shopper may choose at checkout.
|
|
114
|
-
|
|
115
|
-
**The rule is the platform's, not your storefront's.** The store's own
|
|
116
|
-
Markets decide where it sells, and a store that has decided nothing yet
|
|
117
|
-
sells to the whole world. So this endpoint answers with:
|
|
118
|
-
|
|
119
|
-
- the countries the store's regions declare, when it has declared any
|
|
120
|
-
(`"restricted": true`), or
|
|
121
|
-
- the whole 250-entry ISO catalogue, when it has declared none
|
|
122
|
-
(`"restricted": false`).
|
|
123
|
-
|
|
124
|
-
Either way your code hardcodes no country list and invents no fallback of
|
|
125
|
-
its own. A merchant changes the answer in Admin → Settings → Markets →
|
|
126
|
-
Regions, where a region owns its countries.
|
|
127
|
-
|
|
128
|
-
```jsonc
|
|
129
|
-
// query (all optional)
|
|
130
|
-
{
|
|
131
|
-
"q": "bulg", // case-insensitive substring on the displayed name
|
|
132
|
-
"limit": 300, // 1–300, default 300 — the whole catalogue in one call
|
|
133
|
-
"offset": 0
|
|
134
|
-
}
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
- **Response** — ordered by `display_name`, the name a shopper reads.
|
|
138
|
-
|
|
139
|
-
```jsonc
|
|
140
|
-
{
|
|
141
|
-
"countries": [
|
|
142
|
-
{
|
|
143
|
-
"iso_2": "bg", // lowercase alpha-2: the cart address value
|
|
144
|
-
"display_name": "Bulgaria", // what a shopper reads
|
|
145
|
-
"name": "BULGARIA", // the catalogue's uppercase form
|
|
146
|
-
"region_id": "reg_01..." // null when the store has not placed it
|
|
147
|
-
}
|
|
148
|
-
],
|
|
149
|
-
"count": 250,
|
|
150
|
-
"offset": 0,
|
|
151
|
-
"limit": 300,
|
|
152
|
-
"restricted": false
|
|
153
|
-
}
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
```bash
|
|
157
|
-
# every country the store offers, and which of the two lists you received
|
|
158
|
-
curl -s "$BASE/api/store/countries" -H "x-client-id: $CLIENT_ID" \
|
|
159
|
-
| grep -q '"restricted"'
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
- **SDK** — `listCountries(client, query?)` from
|
|
163
|
-
`@cartbase/storefront/api/regions`.
|
|
164
|
-
- **Components** — the checkout address form's country select.
|
|
165
|
-
`useCheckoutOrchestration` CALLS THIS FOR YOU when you do not pass a
|
|
166
|
-
`countries` list, so a new store offers every country with no wiring;
|
|
167
|
-
pass your own list only to override the store's answer. The chosen
|
|
168
|
-
country's flag is drawn by `CountryFlag`
|
|
169
|
-
(`@cartbase/storefront/common/country-flag`).
|
|
170
|
-
- **Settings** — Admin → Settings → Markets → Regions.
|
|
171
|
-
|
|
172
|
-
---
|
|
173
|
-
|
|
174
|
-
## GET /api/store/currencies
|
|
175
|
-
|
|
176
|
-
- **Purpose** — list the currencies ENABLED on this store (the shared
|
|
177
|
-
currency catalog filtered by the store's `store_currencies` links). Use
|
|
178
|
-
for formatting metadata (symbol, decimal digits).
|
|
179
|
-
- **Auth** — anon: `x-client-id` required.
|
|
180
|
-
- **Request** — query `{ code?, limit?, offset? }` (`code` exact,
|
|
181
|
-
lowercased; `limit` 1–200, default 50).
|
|
182
|
-
- **Response** — ordered by code; a store with no enabled currencies
|
|
183
|
-
returns an empty list.
|
|
184
|
-
|
|
185
|
-
```jsonc
|
|
186
|
-
{
|
|
187
|
-
"currencies": [
|
|
188
|
-
{
|
|
189
|
-
"code": "eur",
|
|
190
|
-
"name": "Euro",
|
|
191
|
-
"symbol": "€",
|
|
192
|
-
"symbol_native": "€",
|
|
193
|
-
"decimal_digits": 2,
|
|
194
|
-
"rounding": 0,
|
|
195
|
-
"created_at": "2026-07-01T00:00:00.000Z",
|
|
196
|
-
"updated_at": "2026-07-01T00:00:00.000Z"
|
|
197
|
-
}
|
|
198
|
-
],
|
|
199
|
-
"count": 1,
|
|
200
|
-
"offset": 0,
|
|
201
|
-
"limit": 50
|
|
202
|
-
}
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
- **Working curl**
|
|
206
|
-
|
|
207
|
-
```bash
|
|
208
|
-
CURRENCIES=$(curl -sf "$BASE/api/store/currencies" -H "x-client-id: $CLIENT_ID")
|
|
209
|
-
echo "$CURRENCIES" | grep -q '"currencies"'
|
|
210
|
-
echo "$CURRENCIES" | grep -q '"code":"eur"' # the dev store enables EUR
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
- **Errors** — 400 `missing_client_id`, 400 `validation_failed`.
|
|
214
|
-
- **SDK** — `listCurrencies(client, query?)`.
|
|
215
|
-
- **Components** — price formatting helpers.
|
|
216
|
-
- **Settings** — Admin → Settings → Store → currencies (enable/disable +
|
|
217
|
-
default).
|
|
218
|
-
|
|
219
|
-
---
|
|
220
|
-
|
|
221
|
-
## GET /api/store/currencies/:code
|
|
222
|
-
|
|
223
|
-
- **Purpose** — retrieve one currency by code (case-insensitive).
|
|
224
|
-
- **Auth** — anon: `x-client-id` required.
|
|
225
|
-
- **Request** — `GET /api/store/currencies/{code}` — no query.
|
|
226
|
-
- **Response** — `{ "currency": { ...same shape as the list rows... } }`
|
|
227
|
-
- **Working curl**
|
|
228
|
-
|
|
229
|
-
```bash
|
|
230
|
-
CURRENCY=$(curl -sf "$BASE/api/store/currencies/eur" -H "x-client-id: $CLIENT_ID")
|
|
231
|
-
echo "$CURRENCY" | grep -q '"currency"'
|
|
232
|
-
echo "$CURRENCY" | grep -q '"symbol"'
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
- **Errors** — 404 `not_found`.
|
|
236
|
-
- **CODE-TRUTH NOTE** — unlike the list, the single read is NOT filtered by
|
|
237
|
-
the store's enabled set: any currency in the shared catalog resolves.
|
|
238
|
-
Treat the LIST as the authority on what the store supports.
|
|
239
|
-
- **SDK** — `retrieveCurrency(client, code)`.
|
|
240
|
-
- **Components** — price formatting helpers.
|
|
241
|
-
- **Settings** — Admin → Settings → Store → currencies.
|
|
242
|
-
|
|
243
|
-
---
|
|
244
|
-
|
|
245
|
-
## GET /api/store/locales
|
|
246
|
-
|
|
247
|
-
- **Purpose** — the store's supported locale codes; drives the language
|
|
248
|
-
switcher and the value your client sends as `x-locale`.
|
|
249
|
-
- **Auth** — anon: `x-client-id` required.
|
|
250
|
-
- **Request** — no query.
|
|
251
|
-
- **Response** — default locale FIRST, then alphabetical:
|
|
252
|
-
|
|
253
|
-
```jsonc
|
|
254
|
-
{ "locales": ["en", "bg"] }
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
- **Working curl**
|
|
258
|
-
|
|
259
|
-
```bash
|
|
260
|
-
LOCALES=$(curl -sf "$BASE/api/store/locales" -H "x-client-id: $CLIENT_ID")
|
|
261
|
-
echo "$LOCALES" | grep -q '"locales"'
|
|
262
|
-
echo "$LOCALES" | grep -q '"en"'
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
- **Errors** — 400 `missing_client_id`.
|
|
266
|
-
- **SDK** — `listLocales(client)`.
|
|
267
|
-
- **Components** — locale switcher; `StorefrontClient`'s `getLocale` hook.
|
|
268
|
-
- **Settings** — Admin → Settings → Store → locales (each store manages
|
|
269
|
-
its own locale list in `store_locales`).
|
|
1
|
+
# Regions
|
|
2
|
+
|
|
3
|
+
Catalog-context primitives a storefront resolves at boot: regions feed the
|
|
4
|
+
pricing context (`region_id` → region currency), currencies tell you what the
|
|
5
|
+
store has enabled, locales drive the language switcher and the client's
|
|
6
|
+
`x-locale` header. All reads are anonymous. Money is EUR decimal major units
|
|
7
|
+
everywhere.
|
|
8
|
+
|
|
9
|
+
SDK module: `@cartbase/storefront/api/regions`.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## GET /api/store/regions
|
|
14
|
+
|
|
15
|
+
- **Purpose** — list the store's regions; a storefront usually picks one at
|
|
16
|
+
boot (or by shopper choice) and passes its id/currency as the pricing
|
|
17
|
+
context on catalog reads.
|
|
18
|
+
- **Auth** — anon: `x-client-id` required.
|
|
19
|
+
- **Request** — `GET /api/store/regions`
|
|
20
|
+
|
|
21
|
+
```jsonc
|
|
22
|
+
// query (all optional)
|
|
23
|
+
{
|
|
24
|
+
"q": "bulg", // case-insensitive substring on name
|
|
25
|
+
"currency_code": "eur", // exact match, lowercased server-side
|
|
26
|
+
"limit": 50, // 1–200, default 50
|
|
27
|
+
"offset": 0
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- **Response** — ordered by name. NOTE: Cartbase regions carry NO embedded
|
|
32
|
+
`countries` array — country/tax scope lives server-side in
|
|
33
|
+
`tax_regions`.
|
|
34
|
+
|
|
35
|
+
```jsonc
|
|
36
|
+
{
|
|
37
|
+
"regions": [
|
|
38
|
+
{
|
|
39
|
+
"id": "reg_01tst000000000000000000001",
|
|
40
|
+
"name": "Bulgaria",
|
|
41
|
+
"currency_code": "eur", // lowercase — feeds the pricing context
|
|
42
|
+
"automatic_taxes": true,
|
|
43
|
+
"metadata": null,
|
|
44
|
+
"created_at": "2026-07-01T00:00:00.000Z",
|
|
45
|
+
"updated_at": "2026-07-01T00:00:00.000Z"
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
"count": 1,
|
|
49
|
+
"offset": 0,
|
|
50
|
+
"limit": 50
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- **Working curl**
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
REGIONS=$(curl -sf "$BASE/api/store/regions" -H "x-client-id: $CLIENT_ID")
|
|
58
|
+
echo "$REGIONS" | grep -q '"regions"'
|
|
59
|
+
echo "$REGIONS" | grep -q '"count"'
|
|
60
|
+
REGION_ID=$(echo "$REGIONS" | grep -o '"id":"reg_[^"]*"' | head -1 | cut -d'"' -f4)
|
|
61
|
+
test -n "$REGION_ID"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- **Errors** — 400 `missing_client_id` (header absent/empty),
|
|
65
|
+
400 `validation_failed` (bad limit/offset).
|
|
66
|
+
- **SDK** — `listRegions(client, query?)`.
|
|
67
|
+
- **Components** — region/currency selector (see components.md).
|
|
68
|
+
- **Settings** — Admin → Settings → Regions.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Auth contract: no x-client-id → 400 missing_client_id
|
|
72
|
+
STATUS=$(curl -s -o /dev/null -w '%{http_code}' "$BASE/api/store/regions")
|
|
73
|
+
test "$STATUS" = 400
|
|
74
|
+
curl -s "$BASE/api/store/regions" | grep -q '"code":"missing_client_id"'
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## GET /api/store/regions/:id
|
|
80
|
+
|
|
81
|
+
- **Purpose** — retrieve one region (e.g. re-hydrate the shopper's stored
|
|
82
|
+
choice).
|
|
83
|
+
- **Auth** — anon: `x-client-id` required.
|
|
84
|
+
- **Request** — `GET /api/store/regions/{region_id}` — no query.
|
|
85
|
+
- **Response** — `{ "region": { ...same shape as the list rows... } }`
|
|
86
|
+
- **Working curl**
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
REGION=$(curl -sf "$BASE/api/store/regions/$REGION_ID" -H "x-client-id: $CLIENT_ID")
|
|
90
|
+
echo "$REGION" | grep -q '"region"'
|
|
91
|
+
echo "$REGION" | grep -q '"currency_code"'
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
- **Errors** — 404 `not_found` (unknown id, soft-deleted, or another
|
|
95
|
+
tenant's region — invisible, not forbidden).
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
STATUS=$(curl -s -o /dev/null -w '%{http_code}' \
|
|
99
|
+
"$BASE/api/store/regions/reg_doesnotexist$RUN" -H "x-client-id: $CLIENT_ID")
|
|
100
|
+
test "$STATUS" = 404
|
|
101
|
+
curl -s "$BASE/api/store/regions/reg_doesnotexist$RUN" \
|
|
102
|
+
-H "x-client-id: $CLIENT_ID" | grep -q '"code":"not_found"'
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
- **SDK** — `retrieveRegion(client, regionId)`.
|
|
106
|
+
- **Components** — region/currency selector.
|
|
107
|
+
- **Settings** — Admin → Settings → Regions.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## GET /api/store/countries
|
|
112
|
+
|
|
113
|
+
- **Purpose** — the countries a shopper may choose at checkout.
|
|
114
|
+
|
|
115
|
+
**The rule is the platform's, not your storefront's.** The store's own
|
|
116
|
+
Markets decide where it sells, and a store that has decided nothing yet
|
|
117
|
+
sells to the whole world. So this endpoint answers with:
|
|
118
|
+
|
|
119
|
+
- the countries the store's regions declare, when it has declared any
|
|
120
|
+
(`"restricted": true`), or
|
|
121
|
+
- the whole 250-entry ISO catalogue, when it has declared none
|
|
122
|
+
(`"restricted": false`).
|
|
123
|
+
|
|
124
|
+
Either way your code hardcodes no country list and invents no fallback of
|
|
125
|
+
its own. A merchant changes the answer in Admin → Settings → Markets →
|
|
126
|
+
Regions, where a region owns its countries.
|
|
127
|
+
|
|
128
|
+
```jsonc
|
|
129
|
+
// query (all optional)
|
|
130
|
+
{
|
|
131
|
+
"q": "bulg", // case-insensitive substring on the displayed name
|
|
132
|
+
"limit": 300, // 1–300, default 300 — the whole catalogue in one call
|
|
133
|
+
"offset": 0
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
- **Response** — ordered by `display_name`, the name a shopper reads.
|
|
138
|
+
|
|
139
|
+
```jsonc
|
|
140
|
+
{
|
|
141
|
+
"countries": [
|
|
142
|
+
{
|
|
143
|
+
"iso_2": "bg", // lowercase alpha-2: the cart address value
|
|
144
|
+
"display_name": "Bulgaria", // what a shopper reads
|
|
145
|
+
"name": "BULGARIA", // the catalogue's uppercase form
|
|
146
|
+
"region_id": "reg_01..." // null when the store has not placed it
|
|
147
|
+
}
|
|
148
|
+
],
|
|
149
|
+
"count": 250,
|
|
150
|
+
"offset": 0,
|
|
151
|
+
"limit": 300,
|
|
152
|
+
"restricted": false
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# every country the store offers, and which of the two lists you received
|
|
158
|
+
curl -s "$BASE/api/store/countries" -H "x-client-id: $CLIENT_ID" \
|
|
159
|
+
| grep -q '"restricted"'
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
- **SDK** — `listCountries(client, query?)` from
|
|
163
|
+
`@cartbase/storefront/api/regions`.
|
|
164
|
+
- **Components** — the checkout address form's country select.
|
|
165
|
+
`useCheckoutOrchestration` CALLS THIS FOR YOU when you do not pass a
|
|
166
|
+
`countries` list, so a new store offers every country with no wiring;
|
|
167
|
+
pass your own list only to override the store's answer. The chosen
|
|
168
|
+
country's flag is drawn by `CountryFlag`
|
|
169
|
+
(`@cartbase/storefront/common/country-flag`).
|
|
170
|
+
- **Settings** — Admin → Settings → Markets → Regions.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## GET /api/store/currencies
|
|
175
|
+
|
|
176
|
+
- **Purpose** — list the currencies ENABLED on this store (the shared
|
|
177
|
+
currency catalog filtered by the store's `store_currencies` links). Use
|
|
178
|
+
for formatting metadata (symbol, decimal digits).
|
|
179
|
+
- **Auth** — anon: `x-client-id` required.
|
|
180
|
+
- **Request** — query `{ code?, limit?, offset? }` (`code` exact,
|
|
181
|
+
lowercased; `limit` 1–200, default 50).
|
|
182
|
+
- **Response** — ordered by code; a store with no enabled currencies
|
|
183
|
+
returns an empty list.
|
|
184
|
+
|
|
185
|
+
```jsonc
|
|
186
|
+
{
|
|
187
|
+
"currencies": [
|
|
188
|
+
{
|
|
189
|
+
"code": "eur",
|
|
190
|
+
"name": "Euro",
|
|
191
|
+
"symbol": "€",
|
|
192
|
+
"symbol_native": "€",
|
|
193
|
+
"decimal_digits": 2,
|
|
194
|
+
"rounding": 0,
|
|
195
|
+
"created_at": "2026-07-01T00:00:00.000Z",
|
|
196
|
+
"updated_at": "2026-07-01T00:00:00.000Z"
|
|
197
|
+
}
|
|
198
|
+
],
|
|
199
|
+
"count": 1,
|
|
200
|
+
"offset": 0,
|
|
201
|
+
"limit": 50
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
- **Working curl**
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
CURRENCIES=$(curl -sf "$BASE/api/store/currencies" -H "x-client-id: $CLIENT_ID")
|
|
209
|
+
echo "$CURRENCIES" | grep -q '"currencies"'
|
|
210
|
+
echo "$CURRENCIES" | grep -q '"code":"eur"' # the dev store enables EUR
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
- **Errors** — 400 `missing_client_id`, 400 `validation_failed`.
|
|
214
|
+
- **SDK** — `listCurrencies(client, query?)`.
|
|
215
|
+
- **Components** — price formatting helpers.
|
|
216
|
+
- **Settings** — Admin → Settings → Store → currencies (enable/disable +
|
|
217
|
+
default).
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## GET /api/store/currencies/:code
|
|
222
|
+
|
|
223
|
+
- **Purpose** — retrieve one currency by code (case-insensitive).
|
|
224
|
+
- **Auth** — anon: `x-client-id` required.
|
|
225
|
+
- **Request** — `GET /api/store/currencies/{code}` — no query.
|
|
226
|
+
- **Response** — `{ "currency": { ...same shape as the list rows... } }`
|
|
227
|
+
- **Working curl**
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
CURRENCY=$(curl -sf "$BASE/api/store/currencies/eur" -H "x-client-id: $CLIENT_ID")
|
|
231
|
+
echo "$CURRENCY" | grep -q '"currency"'
|
|
232
|
+
echo "$CURRENCY" | grep -q '"symbol"'
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
- **Errors** — 404 `not_found`.
|
|
236
|
+
- **CODE-TRUTH NOTE** — unlike the list, the single read is NOT filtered by
|
|
237
|
+
the store's enabled set: any currency in the shared catalog resolves.
|
|
238
|
+
Treat the LIST as the authority on what the store supports.
|
|
239
|
+
- **SDK** — `retrieveCurrency(client, code)`.
|
|
240
|
+
- **Components** — price formatting helpers.
|
|
241
|
+
- **Settings** — Admin → Settings → Store → currencies.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## GET /api/store/locales
|
|
246
|
+
|
|
247
|
+
- **Purpose** — the store's supported locale codes; drives the language
|
|
248
|
+
switcher and the value your client sends as `x-locale`.
|
|
249
|
+
- **Auth** — anon: `x-client-id` required.
|
|
250
|
+
- **Request** — no query.
|
|
251
|
+
- **Response** — default locale FIRST, then alphabetical:
|
|
252
|
+
|
|
253
|
+
```jsonc
|
|
254
|
+
{ "locales": ["en", "bg"] }
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
- **Working curl**
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
LOCALES=$(curl -sf "$BASE/api/store/locales" -H "x-client-id: $CLIENT_ID")
|
|
261
|
+
echo "$LOCALES" | grep -q '"locales"'
|
|
262
|
+
echo "$LOCALES" | grep -q '"en"'
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
- **Errors** — 400 `missing_client_id`.
|
|
266
|
+
- **SDK** — `listLocales(client)`.
|
|
267
|
+
- **Components** — locale switcher; `StorefrontClient`'s `getLocale` hook.
|
|
268
|
+
- **Settings** — Admin → Settings → Store → locales (each store manages
|
|
269
|
+
its own locale list in `store_locales`).
|