> ## 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.

# Build a Custom Storefront

> Scaffold and extend a direct, capability-safe Crave ordering experience.

## 1. Scaffold with the CLI

```bash theme={null}
npx @craveup/cli init
```

The CLI may authenticate you to discover or validate merchant resources, but it does not write a private credential into the generated browser app.

## 2. Configure public values

```env theme={null}
NEXT_PUBLIC_CRAVEUP_API_URL=https://api.craveup.com
NEXT_PUBLIC_CRAVEUP_LOCATION_ID=loc_456def
NEXT_PUBLIC_CRAVEUP_MERCHANT_SLUG=downtown-pizza
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_xxx
```

The API origin is configuration, not a credential. Use the sandbox origin and
publishable key in non-production environments. These merchant and location
variables are standalone/local overrides. The hosted template resolves the
merchant from its controlled host/subdomain and the location from the
`[locationId]` route.

## 3. Create separate browser and server clients

Browser code supplies a tab-scoped cart `sessionStore` and `getAuthToken`. Server Components create a client with the same explicit `baseUrl` and perform anonymous published reads only.

```ts theme={null}
import { createStorefrontClient } from "@craveup/storefront-sdk";

export const storefront = createStorefrontClient({
  baseUrl: process.env.NEXT_PUBLIC_CRAVEUP_API_URL!,
  sessionStore,
  getAuthToken,
});
```

## 4. Follow the capability lifecycle

1. Load published merchant, location, and menu data anonymously.
2. Start an ordering session with a generated idempotency key.
3. Store the returned cart capability in versioned `sessionStorage`, scoped by canonical API environment, merchant, and location.
4. Let the SDK attach the capability, `If-Match`, and idempotency headers.
5. On `CART_CONFLICT`, reload the cart before asking the shopper to retry.
6. Clear the capability after claim, deletion, expiry, or terminal checkout handling.

## 5. Checkout

```ts theme={null}
const payment = await storefront.checkout.createPaymentSession(
  locationId,
  cartId,
  { includeCustomerContext: true },
);
```

Use `includeCustomerContext` only when a signed-in shopper still has a guest
capability and the selected provider needs customer association. Omit it for
guest checkout; a claimed cart uses customer authentication automatically.
Render Square Web Payments or Stripe Elements based on `payment.provider`, then poll `checkout.getOrderResult`. Treat
`payment_pending` and `order_pending` as nonterminal; render success only for
`completed`; render an actionable failure for `failed`.

## 6. Customer and receipt access

Login and OTP requests must include `merchantSlug`. Customer resources use the returned JWT, stored by API environment and merchant. Receipt links use a purpose-limited fragment capability: remove the fragment immediately, keep the token in tab-scoped storage keyed by API environment, merchant, and receipt ID, and pass it through `receipts.get`.

## 7. Deploy

* Configure the exact storefront origin in the API CORS allowlist.
* Keep provider and private integration credentials on the API service.
* Run tests, lint, type checking, a production build, and a static-bundle secret scan.
* Verify a real published catalog read and a sandbox ordering flow before promoting production.

See [Deployment](/guides/deployment) and the [Storefront SDK](/getting-started/storefront-sdk) for the full contract.
