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

# Build a custom checkout with the Storefront SDK

> Use @shoppexio/storefront to wire your own frontend to Shoppex checkout.

You're building a custom storefront — your own Next.js / Nuxt / React app, your own design,
your own product browsing flow. You want Shoppex to handle the cart, the checkout, and the
payment, without writing any of that yourself.

This tutorial covers the **`@shoppexio/storefront` SDK**. By the end you'll have a working
cart and a real checkout that redirects to `checkout.shoppex.io` for the actual payment.

## What `@shoppexio/storefront` does

The SDK is a thin browser-side client around Shoppex's public storefront API. It handles:

* Fetching products, categories, and variants from your shop.
* Local cart state with `addToCart` / `updateCartItem` / `removeFromCart`.
* Server-side cart pricing via `quoteCart` (taxes, discounts, totals).
* Creating a checkout: POST to Shoppex, get back a redirect URL.

What it explicitly does **not** do:

* Render any checkout UI. The actual payment happens on `checkout.shoppex.io`.
* Use API keys. The SDK runs on public browser origins and is auth-less by design —
  Shoppex's storefront endpoints are CORS-public and identified by the shop slug, not a key.

## What you'll need

* A Shoppex shop. Your shop slug (the part before `.shoppex.io`).
* A frontend project — anything that runs JavaScript. We'll use Next.js for this tutorial,
  but the SDK works in Vite, Remix, Nuxt, plain HTML, etc.

## Step 1 — Install the SDK

```bash theme={"system"}
npm install @shoppexio/storefront
# or
bun add @shoppexio/storefront
```

## Step 2 — Initialize

In any client-side module (use `"use client"` if you're in a Next.js App Router project):

```ts theme={"system"}
import { shoppex } from '@shoppexio/storefront';

shoppex.init('your-shop-slug');
```

That's it. The SDK is now wired to your shop. If you need to point at a non-production
environment, pass an options object:

```ts theme={"system"}
shoppex.init({
  storeId: 'your-shop-slug',
  apiUrl: 'https://api.shoppex.io',          // default
  checkoutUrl: 'https://checkout.shoppex.io', // default
});
```

## Step 3 — Fetch and render products

```ts theme={"system"}
const products = await shoppex.getProducts();

products.forEach((p) => {
  console.log(p.title, p.price, p.id);
});
```

Render however you like. Each product has the fields you'd expect: `id`, `title`,
`description`, `price`, `currency`, `images`, `variants`.

For a single product detail page:

```ts theme={"system"}
const product = await shoppex.getProduct(productId);
```

## Step 4 — Add to cart

```ts theme={"system"}
await shoppex.addToCart(productId, variantId, quantity);

const cart = shoppex.getCart();
// { items: [{ productId, variantId, quantity, price, ... }], total: ... }
```

Cart state lives in the browser (localStorage). On every change, you can re-render your
cart UI.

To get an authoritative total with taxes, coupons applied:

```ts theme={"system"}
const quote = await shoppex.quoteCart({ coupon: 'BF25' });
// { subtotal, discount, tax, total, lineItems: [...] }
```

`quoteCart` calls Shoppex's server — use it on the cart/checkout page where the buyer needs
to see real totals, not on every cart modification.

## Step 5 — Checkout

When the buyer is ready to pay:

```ts theme={"system"}
const result = await shoppex.checkout({
  email: buyerEmail,         // optional but recommended for prefill
  coupon: 'BF25',            // optional
  affiliateCode: 'partner-x', // optional
  locale: 'en',              // optional
});

if (result.success) {
  window.location.href = result.redirectUrl;
}
```

Under the hood the SDK:

1. POSTs your current cart to `https://api.shoppex.io/v1/storefront/invoices/from-cart`.
2. Receives an invoice ID and a checkout URL of the form
   `https://checkout.shoppex.io/invoice/{invoiceId}`.
3. Returns it to you.

You redirect the buyer there. They complete the payment on Shoppex's hosted checkout.

If you'd rather build the URL without redirecting (e.g. open in a new tab):

```ts theme={"system"}
const url = await shoppex.buildCheckoutUrl({ email: buyerEmail });
window.open(url, '_blank');
```

## Step 6 — Post-payment

Shoppex hosts the success and failure pages. The buyer doesn't come back to your app by
default after paying — they're on `checkout.shoppex.io` looking at their order.

To bring them back, you have two options:

* **Storefront-level setting** — on `yourshop.shoppex.io`, configure a custom post-checkout
  redirect URL in your shop settings.
* **Per-checkout** — pass `success_url` / `cancel_url` via the more advanced
  [Developer API checkout session endpoint](#what-about-the-developer-api-checkout-endpoint)
  (see below).

For fulfillment notifications, set up a webhook (see
[Webhook handler in a Cloudflare Worker](/developers/tutorials/webhook-cloudflare-worker)).

## Starter project

Shoppex publishes `apps/storefront-starter/` — a working Next.js 16.2 example wired with the
SDK. It's the fastest way to see the end-to-end flow in real code. Open the
`shoppex.config.ts`, point it at your shop slug via `NEXT_PUBLIC_SHOPPEX_SHOP_SLUG`, and
run `bun dev`.

## What about the Developer API checkout endpoint?

The Storefront SDK uses Shoppex's **storefront** API — auth-less, browser-safe, CORS-public.
For server-to-server scenarios where you want Shoppex to act on a typed invoice spec, use the
Developer API instead:

* `POST /dev/v1/payments` — create an invoice directly.
* `POST /dev/v1/checkout/sessions` — Stripe-style alias that returns `{ id, url }`.

Both require an API key (`Authorization: Bearer shx_...`), and both redirect the buyer to
`checkout.shoppex.io/invoice/{id}` for the actual payment. See the
[API Reference](/api-reference/introduction) for the request/response shapes.

The choice between SDK and Developer API:

* **SDK** — you're building a frontend on the buyer's side. No server-side code. Public.
* **Developer API** — you're acting from your own backend, possibly invoicing buyers without
  them ever seeing a storefront (e.g. a Discord bot collecting payments, an internal admin
  tool generating one-off invoices).

## Common pitfalls

* **No tree-shaking the SDK.** It's deliberately one client object (`shoppex`) so all
  modules share state. Don't try to `import { getCart }` independently — call via the
  initialized client.
* **CORS errors during local dev.** If you see `Access-Control-Allow-Origin` errors, you're
  probably hitting `localhost:3000` against a shop slug that doesn't exist. Verify the slug.
* **Cart desync across tabs.** The cart lives in localStorage. If a buyer has two tabs open,
  changes in one don't auto-propagate to the other until they refresh.

<Card title="Reference: Storefront SDK" href="/sdk/introduction">
  Full method reference and types.
</Card>
