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

# Headless Checkout SDK

> Build your own checkout UI with @shoppexio/checkout-js/headless while Shoppex handles payment sessions, webhooks, invoices, and fulfillment.

The Headless Checkout SDK is for merchants who want a fully custom checkout page on their own domain. You own the layout, fields, and buyer experience. Shoppex still creates the invoice, starts the payment session, handles 3DS or redirects, receives webhooks, and fulfills the order.

Use this when the hosted checkout page or modal embed is not flexible enough.

<Info>
  Headless checkout requires an active Business plan.
</Info>

## What You Need

* A storefront or app page on your own domain.
* An active Business plan.
* A headless checkout publishable key from the dashboard.
* At least one allowed origin for the site that will call the browser API.
* `@shoppexio/checkout-js` installed in your frontend app.

## Dashboard Setup

Open **Dashboard -> Settings -> Developer -> Headless Checkout**.

<Steps>
  <Step title="Generate a publishable key">
    Click **Generate key**. Copy the `pk_live_...` key immediately. The full key is shown only once.
  </Step>

  <Step title="Add your storefront origin">
    Add the exact origin that hosts your checkout page, for example `https://yourstore.com`.
  </Step>

  <Step title="Add local development origins">
    For local testing, add the exact local origin too, for example `http://localhost:3000`.
  </Step>
</Steps>

An origin is only the scheme, host, and optional port. Do not include a path.

| URL you paste                | Saved origin            |
| ---------------------------- | ----------------------- |
| `https://vyy.gg/checkout`    | `https://vyy.gg`        |
| `https://www.vyy.gg`         | `https://www.vyy.gg`    |
| `http://localhost:3000/cart` | `http://localhost:3000` |

If your site works on both `https://vyy.gg` and `https://www.vyy.gg`, add both origins.

<Warning>
  Never put a secret `shx_...` API key in browser code. Headless checkout uses `pk_live_...` or `pk_test_...` publishable keys only.
</Warning>

## Install

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

## React Example

```tsx theme={"system"}
import {
  ShoppexCheckoutProvider,
  useCheckoutSession,
  useStripePaymentSession,
  ShoppexPoweredBy,
} from '@shoppexio/checkout-js/headless/react';

function CheckoutForm() {
  const { session, loading, error } = useCheckoutSession({
    product_id: 'PROD_123',
    email: 'customer@example.com',
    quantity: 1,
  });

  const stripe = useStripePaymentSession(session?.id, {
    returnUrl: 'https://yourstore.com/thanks',
  });

  if (loading) return <p>Preparing checkout...</p>;
  if (error) return <p>{error.message}</p>;
  if (!session) return null;

  return (
    <form
      onSubmit={(event) => {
        event.preventDefault();
        void stripe.confirm();
      }}
    >
      <h1>{session.product?.title ?? 'Checkout'}</h1>
      <p>Total: {session.breakdown.total} {session.currency}</p>

      <div ref={stripe.mountRef} />

      <button type="submit" disabled={!stripe.ready}>
        Pay now
      </button>

      <ShoppexPoweredBy />
    </form>
  );
}

export default function Page() {
  return (
    <ShoppexCheckoutProvider publishableKey="pk_live_your_key_here">
      <CheckoutForm />
    </ShoppexCheckoutProvider>
  );
}
```

<Note>
  `ShoppexPoweredBy` is required unless your account has a server-side white-label entitlement. The SDK checks that the badge is visible before payment confirmation.
</Note>

## API Shape

The browser SDK sends the publishable key in the `x-shoppex-publishable-key` header. Shoppex also checks the request `Origin` header against your allowed origins.

The SDK can:

* create a checkout session
* read the current session status
* apply or remove coupons
* set tips
* select add-ons
* start a gateway payment session
* fetch delivery after the payment is complete

The session response includes line items, totals, available payment gateways, shop branding, terms, and delivery state. Render against the gateway `kind`, not the provider name, so your UI works across card, redirect, crypto, balance, and manual payment flows.

## Troubleshooting

### Generate key fails

Check these first:

* The shop has an active Business plan.
* You are using the shop owner account or a team member with permission to manage webhooks/developer settings.
* Your dashboard session is fresh. Refresh the page or log out and back in.

If it still fails, open browser DevTools -> Network and inspect:

```text theme={"system"}
POST /v1/dashboard/checkout/headless/rotate-key
```

Common responses:

| Status                       | Meaning                                                                      |
| ---------------------------- | ---------------------------------------------------------------------------- |
| `403 BUSINESS_PLAN_REQUIRED` | The shop is not on an active Business plan.                                  |
| `401`                        | The dashboard session or shop permission is invalid.                         |
| `500`                        | Backend or database issue. Contact support with the request id if available. |

### Add origin fails

Use a full `http://` or `https://` origin. These are valid:

```text theme={"system"}
https://vyy.gg
https://www.vyy.gg
http://localhost:3000
```

These should not be saved as origins:

```text theme={"system"}
vyy.gg
https://vyy.gg/checkout
ftp://vyy.gg
```

The dashboard normalizes paths away before saving, but the backend still rejects non-http origins and malformed values.

Inspect this request if the UI only shows a generic error:

```text theme={"system"}
PUT /v1/dashboard/checkout/headless/origins
```

### Browser API calls fail

Check the browser response code:

| Status                       | Meaning                                                                                            |
| ---------------------------- | -------------------------------------------------------------------------------------------------- |
| `401`                        | Missing, malformed, or unknown publishable key. Make sure it starts with `pk_live_` or `pk_test_`. |
| `403 ORIGIN_NOT_ALLOWED`     | The calling site's origin is not in the allowed origins list.                                      |
| `403 BUSINESS_PLAN_REQUIRED` | Headless checkout is not enabled for the shop's current plan.                                      |

## Related Docs

<CardGroup cols={2}>
  <Card title="Headless Commerce Overview" icon="sitemap" href="/headless/overview">
    Pick the right integration shape for custom storefronts, apps, and backend flows.
  </Card>

  <Card title="Checkout Embed SDK" icon="window-maximize" href="/guides/embeds">
    Use the hosted modal when you do not need a fully custom checkout UI.
  </Card>

  <Card title="Storefront SDK" icon="browser" href="/sdk/introduction">
    Read public product, cart, and storefront data from browser code.
  </Card>

  <Card title="Webhooks" icon="webhook" href="/guides/webhooks">
    Fulfill orders and update your app after Shoppex receives payment events.
  </Card>
</CardGroup>
