> ## Documentation Index
> Fetch the complete documentation index at: https://docs.craveup.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Upgrade Guide

> Migrate a Storefront SDK 1.x integration to the direct, capability-safe 2.0.0 contract.

Storefront SDK `2.0.0` is a major version. It replaces the published 1.x browser API-key client with the direct public `/api/v1/storefront` contract. Treat the upgrade as an application migration: use a branch, update all callers together, and verify a sandbox order before production.

Published 1.x artifacts remain a durable npm history. Do not unpublish them or silently replace their contents. They may be deprecated after `2.0.0` is verified, but 2.x does not include an API-key shim, a dual transport, or duplicate names for the same operation. Use the canonical `cart.applyDiscount()`, `cart.removeDiscount()`, and location-scoped `loyalty.ledger(locationId)` methods.

## Install the exact major

```bash theme={null}
pnpm remove @craveup/storefront-sdk
pnpm add --save-exact @craveup/storefront-sdk@2.0.0
```

Commit the updated lockfile. Do not use `latest`, a semver range, a Git URL, or a workspace build in a released storefront.

## Replace configuration

Remove the 1.x browser configuration:

```ts theme={null}
// 1.x — remove this configuration.
createStorefrontClient({
  apiKey: process.env.NEXT_PUBLIC_CRAVEUP_API_KEY!,
});
```

Create a 2.x browser client with public configuration and caller-owned stores:

```ts theme={null}
const storefront = createStorefrontClient({
  baseUrl: process.env.NEXT_PUBLIC_CRAVEUP_API_URL!,
  sessionStore,
  getAuthToken: () => customerTokenStore.get(),
});
```

Create a separate server client with only `baseUrl`; use it for anonymous published reads. Remove browser/mobile API keys, mint endpoints, BFF proxies added only for the old SDK, and hand-built authorization headers.

## Update methods, arguments, and types

| 1.x surface                                                       | 2.0.0 surface                                                                                              |
| ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `locations.getDistance(locationId, input)`                        | `locations.distance(locationId, input)`                                                                    |
| `locations.submitRating(locationId, { cartId, rating, comment })` | `ratings.submit(locationId, cartId, { rating, comment })`                                                  |
| `payments.createIntent(locationId, cartId)`                       | `checkout.createPaymentSession(locationId, cartId)`                                                        |
| `cart.getRecommendations(locationId, cartId)`                     | `cart.recommendations(locationId, cartId)`                                                                 |
| `discounts.apply(locationId, { cartId, code })`                   | `cart.applyDiscount(locationId, cartId, code)`                                                             |
| `discounts.remove(locationId, cartId)`                            | `cart.removeDiscount(locationId, cartId)`                                                                  |
| `client.http.*` and `RequestOptions.skipAuth`                     | Removed; use the typed namespaces and SDK-owned authorization boundaries                                   |
| `StorefrontAnalyticsEventType` enum                               | `StorefrontAnalyticsEvents` constant plus `StorefrontAnalyticsEvent` union                                 |
| Client-supplied `ORDER_PLACED`                                    | Removed; Crave records it after authoritative order creation                                               |
| `StorefrontRecommendedProduct` / `RecommendedProductsResponse`    | `RecommendationProduct` / `RecommendationProduct[]`                                                        |
| `SubmitRatingPayload` / `SubmitRatingResponse`                    | `RatingRequest` / `RatingResponse`                                                                         |
| `GetLocationViaSlugType`                                          | `StorefrontLocation`                                                                                       |
| `LocationAddressDTO`                                              | `DeliveryAddress`                                                                                          |
| `ApiError`                                                        | `StorefrontApiError`, `StorefrontProtocolError`, `StorefrontTimeoutError`, or `StorefrontClientStateError` |

Only `SCAN`, `CART_VIEW`, and `CHECKOUT_VIEW` remain valid client analytics
events. `StorefrontClientStateError` provides a typed local preflight failure,
including `CUSTOMER_AUTH_REQUIRED`, before a request is sent without required
customer authentication.

## Update response handling

| Operation                                                                                                                    | 1.x response                        | 2.0.0 response                                                                                    |
| ---------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------- |
| `orderingSessions.start`                                                                                                     | `{ cartId }` or `{ errorMessage }`  | `{ cart, cartAccessToken? }`; the SDK persists the capability and revision through `sessionStore` |
| `cart.addItem`                                                                                                               | `{ cartId, cart }`                  | `StorefrontCart`                                                                                  |
| `cart.update`                                                                                                                | Partial cart object                 | Authoritative `StorefrontCart`                                                                    |
| `cart.updateOrderTime`, `cart.validateAndUpdateCustomer`, `cart.setDelivery`, `cart.setTable`, `cart.setRoom`, `cart.delete` | `{ success: true }`                 | Authoritative `StorefrontCart`                                                                    |
| Apply or remove discount                                                                                                     | `{ success: true }`                 | Authoritative `StorefrontCart`                                                                    |
| Create PaymentIntent                                                                                                         | `{ clientSecret, stripeAccountId }` | `{ clientSecret }`; Stripe account routing is server-owned                                        |

Do not rely on structural casts to bridge these response changes. Remove
wrapper access such as `result.cart`, replace success-only branches with the
returned authoritative cart, and recompile every caller against 2.0.0. Product
fixtures and adapters must now provide the required `Product.locationId`.
`OrderTimesResponse` exposes labeled `orderDays`; replace the legacy regular-day
`{ from, to }` interval branch and render only the days and intervals returned by
the API.

## Update request validation

* Use only `takeout`, `table_side`, `room_service`, or `delivery` for
  fulfillment. Set table and room identifiers through `cart.setTable()` and
  `cart.setRoom()` rather than a generic cart patch.
* Use `cart.updateOrderTime()` for scheduling. `cart.update()` accepts
  fulfillment and/or a note, not order-date or order-time fields.
* Send exactly one of `amount` or `percentage` to `cart.updateGratuity()`.
* Send a full supported country name such as `United States`, not `US`.
* Send table numbers as numeric strings such as `"12"`, not `"T-12"` or
  `"Patio 3"`.
* Treat `marketplaceId` as optional source attribution such as `"web"` or
  `"mobile"`; it is not a location ID.
* For a signed-in shopper who still has a guest-capability cart, use the named
  `{ includeCustomerContext: true }` request option only when customer-aware
  discount validation or Stripe customer association is needed. Omit it for a
  guest; claimed carts use the customer JWT automatically.

## Required migration

1. Configure the explicit public API origin for the correct environment.
2. Provide a `StorefrontSessionStore` for `{ locationId, cartId, accessToken, revision }`, scoped by canonical API environment, merchant, and location.
3. Create carts through `orderingSessions.start()`. Let the SDK persist the returned capability and authoritative revision; do not copy the token into application state or UI props.
4. Let cart methods attach `X-Cart-Token`, `If-Match`, and `Idempotency-Key`. On `CART_CONFLICT`, refresh and ask the customer to retry; never silently replay intent.
5. Store the merchant-bound customer JWT in versioned, tab-scoped browser storage or `expo-secure-store`, and clear it on logout.
6. Replace legacy payment calls with `checkout.createPaymentSession()`, branch on the selected provider, and use bounded `checkout.getOrderResult()` polling; stop reading the removed `stripeAccountId` response field.
7. Capture a receipt capability from the URL fragment, remove the fragment immediately, and scope storage by environment, merchant, and receipt ID.
8. Register each exact deployed browser origin with Crave. Native apps normally need no CORS registration; Expo web does.
9. Delete the obsolete 1.x client, API-key configuration, wrapper types, adapters, tests, and documentation in the same application change.

## Verification checklist

* Public bundles contain no private Crave or payment-provider secrets.
* Published merchant, location, menu, product, distance, order-time, and gratuity requests carry no customer JWT.
* Wrong, expired, cross-location, and cross-merchant capabilities are denied.
* Stale revisions return `CART_CONFLICT` and are not automatically replayed.
* Only `completed` is treated as checkout success.
* CORS is restricted to the exact deployed storefront origins.
* A clean install, typecheck, production web build or Expo export, and sandbox vertical slice use the exact registry artifact.

If a production 1.x storefront cannot move atomically, keep its pinned 1.x artifact and deployed API path unchanged while a separate 2.0.0 release candidate is verified. Do not make one bundle switch between the two contracts at runtime.

<Snippet file="need-help.mdx" />
