@godaddy/gd-commerce-storefront 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.
@@ -0,0 +1,101 @@
1
+ # Server API contract
2
+
3
+ The browser calls JSON endpoints under `/api/commerce` on its own origin. The consuming application provides these routes. This browser package contains no server credentials, platform configuration reader, merchant provisioning, or GraphQL transport. Express applications can use the companion `@godaddy/gd-commerce-server` package; other hosts can implement this contract directly.
4
+
5
+ Server implementations can use GoDaddy Commerce APIs or their existing integration, but must return these shapes and enforce the same purchase rules. Returning a similar-looking GraphQL mutation result is insufficient: cart mutations return a complete refreshed cart.
6
+
7
+ ## Requests and failures
8
+
9
+ - Requests use same-origin browser credentials. JSON writes set `Content-Type: application/json`.
10
+ - Cart, catalog and checkout requests include `X-Commerce-Scope` from `/config`. Validate that scope against the server's current store/channel binding, especially before writes. Treat it as a stale-binding guard, not authorization. Authenticate and authorize requests independently.
11
+ - Read requests use `cache: no-store`, cancellation, and a 15-second timeout. The initial cart creation/add request is also a write and is never retried automatically.
12
+ - Return a non-2xx response with `{ "error": "A useful customer-facing message" }` for failure. HTML error pages are also handled as failures.
13
+ - Only cart reads with HTTP 404 or 410 clear an expired saved cart. Network errors, 401/403/409/5xx, and other failures preserve the ID and block writes until hydration succeeds.
14
+ - Resolve prices and availability on the server. The cart does not calculate shipping, taxes, or discounts; it explains that they are calculated at checkout. Browser SKU names and quantities are input, not pricing authority. Protect mutations against CSRF as appropriate for the host application's authentication.
15
+
16
+ ## Endpoints
17
+
18
+ | Method and path | Input | Successful JSON response |
19
+ | --- | --- | --- |
20
+ | `GET /config` | None | `{ cartScope, currencyCode }` |
21
+ | `GET /products?first=6&after=<cursor>` | Optional opaque cursor | `{ skuGroups: Connection<SKUGroup> }` |
22
+ | `GET /products/:id` | URI-encoded product ID | `{ skuGroup: SKUGroup \| null }` |
23
+ | `GET /products/:id?attributeValues=blue&attributeValues=large` | Repeated selected attribute **names**, not IDs | `{ skuGroup: SKUGroup \| null }` with matching SKU connection |
24
+ | `GET /cart/:id` | URI-encoded cart ID | `{ cart: CartOrder \| null }` |
25
+ | `POST /cart` | `{ lineItems: [{ skuId, name, quantity }] }` | `{ cart: CartOrder }` |
26
+ | `POST /cart/:id/items` | `{ skuId, name, quantity }` | `{ cart: CartOrder }` |
27
+ | `PATCH /cart/:id/items/:itemId` | `{ quantity }` | `{ cart: CartOrder }` |
28
+ | `DELETE /cart/:id/items/:itemId` | No body | `{ cart: CartOrder }` |
29
+ | `POST /cart/:id/discounts` | `{ discountCodes: string[] }` | `{ cart: CartOrder }` |
30
+ | `POST /checkout` | `{ draftOrderId, returnUrl, successUrl }` | `{ url: string }` |
31
+
32
+ `/checkout` is required only when checkout is enabled. `/discounts` is needed if the host uses `applyDiscount`. The standard catalog/detail/cart flow uses the other routes. All product/cart IDs in request paths are encoded.
33
+
34
+ ## Configuration
35
+
36
+ `cartScope` is a nonempty opaque identifier for the effective store/channel/currency binding. It is not a secret. Rotate it when that binding changes so a saved cart cannot cross stores or currencies. `currencyCode` is a three-letter uppercase ISO 4217 code, for example `USD`. Money integers use that currency's smallest unit: USD 1234 is $12.34; JPY 1234 is ¥1,234. The cart shows the draft-order subtotal and the message “Shipping, taxes, and discounts are calculated at checkout.” The message has the stable `commerce-cart-checkout-adjustments-note` class so a host can hide it without changing the component.
37
+
38
+ The browser rechecks configuration on window focus when stale. Return current server configuration rather than a browser-selected store. Persisted IDs use the package-specific key documented in the README; migration from another application's storage keys belongs to that application's integration.
39
+
40
+ ## Product responses
41
+
42
+ Exported `SKUGroup`, `SKU`, `Connection`, `SkuGroupsResult` and `SkuGroupResult` describe the public response types. Connections use `{ edges: [{ node }], totalCount?, pageInfo? }`.
43
+
44
+ #### Example simple product
45
+
46
+ ```json
47
+ {
48
+ "skuGroup": {
49
+ "id": "mug",
50
+ "label": "Studio mug",
51
+ "description": "A ceramic mug.",
52
+ "priceRange": { "min": 2400, "max": 2400 },
53
+ "mediaObjects": { "edges": [{ "node": { "type": "IMAGE", "url": "/mug.jpg" } }] },
54
+ "skus": {
55
+ "totalCount": 1,
56
+ "pageInfo": { "hasNextPage": false },
57
+ "edges": [{ "node": {
58
+ "id": "mug-blue",
59
+ "prices": { "edges": [{ "node": { "value": { "value": 2400, "currencyCode": "USD" } } }] },
60
+ "inventoryCounts": { "edges": [{ "node": { "type": "AVAILABLE", "quantity": 8 } }] }
61
+ } }]
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ For options, return `attributes.edges[].node` with `name`, `label`, and `values.edges[].node` containing `name` and `label`. For example, the attribute `color` has values named `blue` and `clay`. The browser sends those value names as repeated `attributeValues` parameters. Return the group with a SKU connection filtered to that exact combination. Zero or multiple results cannot be purchased. Include accurate `totalCount` and `hasNextPage`; a truncated one-item result must never look like a complete match.
68
+
69
+ Products without attribute definitions can offer explicit SKU selection only when all SKUs are returned with unique IDs and unique nonempty labels/names. Ambiguous or incomplete collections cannot be purchased. No inventory records means untracked inventory; records without an `AVAILABLE` quantity mean unavailable. Supply appropriate records for physical inventory.
70
+
71
+ ## Cart responses
72
+
73
+ A cart is a draft order. Return its `id`, complete `lineItems`, and authoritative `totals` after every mutation. Cart line items have their own `id`, separate from `skuId`. A new-cart/add response must include at least one line item. Update/delete/discount responses must retain the cart ID, even when deletion leaves the cart empty.
74
+
75
+ #### Example cart
76
+
77
+ ```json
78
+ {
79
+ "cart": {
80
+ "id": "draft-123",
81
+ "lineItems": [{
82
+ "id": "line-456", "skuId": "mug-blue", "name": "Studio mug", "quantity": 2,
83
+ "totals": { "subTotal": { "value": 4800, "currencyCode": "USD" } }
84
+ }],
85
+ "totals": {
86
+ "subTotal": { "value": 4800, "currencyCode": "USD" },
87
+ "total": { "value": 4800, "currencyCode": "USD" }
88
+ }
89
+ }
90
+ }
91
+ ```
92
+
93
+ Optional totals include `shippingTotal`, `taxTotal`, and `discountTotal`. Optional line details include `productAssetUrl` and `selectedOptions: [{ attribute, values: string[] }]`. The drawer shows supplied totals; it does not calculate authoritative prices from catalog values. A missing total disables checkout.
94
+
95
+ ## Checkout boundary
96
+
97
+ The client refreshes the cart before checkout and sends its ID, an absolute catalog `returnUrl`, and an absolute `successUrl` with `orderId` appended. The server must verify the cart's binding, contents, merchant readiness, and allowed return origins/paths before creating a session. Return an HTTPS checkout URL. The client rejects missing or non-HTTPS URLs and performs a browser navigation to the returned URL.
98
+
99
+ When using `@godaddy/gd-commerce-server`, configure the router's `checkoutReturnUrls.returnUrls` with the absolute catalog return URL and `checkoutReturnUrls.successUrls` with the absolute success-page URL. The package matches these exact destinations and permits an additional `orderId` parameter on success URLs. Missing policy disables HTTP checkout. Public HTTP requests accept only cart or SKU checkout; non-catalog amounts belong in a trusted server handler.
100
+
101
+ Do not treat `checkoutSuccessPath`, a client-provided URL, the redirect itself, or a draft-order response as proof that payment succeeded. Verify payment using the checkout/payment service or trusted webhook state. Expire or reject paid/closed drafts on subsequent cart reads so returning customers cannot reuse a completed cart.
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@godaddy/gd-commerce-storefront",
3
+ "version": "0.1.0",
4
+ "description": "Opinionated React storefront templates for GoDaddy Commerce",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "GoDaddy.com Operating Company, LLC",
8
+ "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "dist",
11
+ "docs",
12
+ "LICENSE.md"
13
+ ],
14
+ "sideEffects": [
15
+ "**/*.css"
16
+ ],
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js",
21
+ "default": "./dist/index.js"
22
+ },
23
+ "./styles.css": "./dist/styles.css",
24
+ "./package.json": "./package.json"
25
+ },
26
+ "scripts": {
27
+ "build": "tsdown && node scripts/build-css.mjs",
28
+ "typecheck": "tsc --noEmit",
29
+ "test": "vitest run",
30
+ "lint": "biome check src",
31
+ "lint:fix": "biome check --write src",
32
+ "prepublishOnly": "pnpm build"
33
+ },
34
+ "peerDependencies": {
35
+ "@tanstack/react-query": "^5.0.0",
36
+ "react": "^18.2.0 || ^19.0.0",
37
+ "react-dom": "^18.2.0 || ^19.0.0",
38
+ "react-router": "^7.0.0 || ^8.3.0"
39
+ },
40
+ "dependencies": {
41
+ "@radix-ui/react-dialog": "^1.1.6"
42
+ },
43
+ "devDependencies": {
44
+ "@biomejs/biome": "^2.3.2",
45
+ "@tailwindcss/cli": "^4.1.10",
46
+ "@tanstack/react-query": "^5.66.0",
47
+ "@testing-library/jest-dom": "^6.9.1",
48
+ "@testing-library/react": "^16.3.2",
49
+ "@testing-library/user-event": "^14.6.1",
50
+ "@types/node": "^22.13.1",
51
+ "@types/react": "^19.0.8",
52
+ "@types/react-dom": "^19.0.3",
53
+ "jsdom": "^26.0.0",
54
+ "postcss": "^8.5.3",
55
+ "react": "^19",
56
+ "react-dom": "^19",
57
+ "react-router": "^7.0.0",
58
+ "tailwindcss": "^4.1.4",
59
+ "tailwindcss-v3": "npm:tailwindcss@^3.4.19",
60
+ "tsdown": "^0.15.6",
61
+ "typescript": "~5.7.3",
62
+ "vitest": "5.0.0"
63
+ },
64
+ "publishConfig": {
65
+ "access": "public",
66
+ "registry": "https://registry.npmjs.org/"
67
+ },
68
+ "repository": {
69
+ "type": "git",
70
+ "url": "git+https://github.com/godaddy/javascript.git",
71
+ "directory": "packages/commerce-storefront"
72
+ }
73
+ }