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

# Checkout Embed SDK

> Add Shoppex checkout as a modal on your existing website, with copy-paste framework snippets

The Checkout Embed SDK opens Shoppex checkout in a modal, directly on your site, with a script tag and no build step.

## What this is (and what it is not)

| Use case                                                                 | Best choice                                                                                                   |
| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| Open checkout in a modal from any page                                   | **Checkout Embed SDK** (`window.Shoppex`) — **Standard and Business**                                         |
| Build a full storefront UI (products/cart/state)                         | [**Storefront SDK**](/developers/storefront-sdk/overview) (`@shoppexio/storefront`)                           |
| Build your own checkout UI with Shoppex handling payment and fulfillment | [**Headless Checkout SDK**](/developers/headless/checkout) (`@shoppexio/checkout-js/headless`) — **Business** |

## Quick start

```html theme={"system"}
<script src="https://checkout.shoppex.io/embed/embed.iife.js" defer></script>
```

<Warning>
  If your site sends a `Content-Security-Policy` header, the embed needs `script-src`, `frame-src`, `connect-src`, and `style-src` entries before any of the snippets below will work. The [reference](/developers/embeds/reference#content-security-policy) has a complete policy you can copy. Do not add an `integrity` attribute to the script tag — the URL is mutable and sends no CORS header, so the browser would block the script.
</Warning>

**Option A: data attributes (fastest)**

```html theme={"system"}
<button
  data-shoppex-shop-id="YOUR_SHOP"
  data-shoppex-product-id="PRODUCT_ID"
  data-shoppex-variant-id="VARIANT_ID"
  data-shoppex-quantity="1"
  data-shoppex-email="customer@example.com"
  data-shoppex-coupon-code="SAVE20"
  data-shoppex-referral-code="CREATOR123"
  data-shoppex-return-url="https://your-site.com/thank-you"
  data-shoppex-metadata='{"source":"landing","campaign":"spring"}'
>
  Buy now
</button>
```

**Option A2: group button**

```html theme={"system"}
<button
  data-shoppex-group-id="GROUP_ID"
  data-shoppex-referral-code="CREATOR123"
  data-shoppex-return-url="https://your-site.com/thank-you"
>
  Open group
</button>
```

**Option B: JavaScript API (more control)**

```html theme={"system"}
<script>
  Shoppex.open({
    shopId: 'YOUR_SHOP',
    items: [{ productId: 'PRODUCT_ID', variantId: 'VARIANT_ID', quantity: 1 }],
    theme: 'auto',
    returnUrl: 'https://your-site.com/thank-you',
    couponCode: 'SAVE20',
    affiliateCode: 'CREATOR123',
    metadata: {
      source: 'landing',
      campaign: 'spring'
    }
  });
</script>
```

## Common events

```javascript theme={"system"}
document.addEventListener('shoppex:success', (event) => {
  console.log('Paid invoice:', event.detail.invoiceId);
});

document.addEventListener('shoppex:error', (event) => {
  console.error('Checkout error:', event.detail.error);
});

document.addEventListener('shoppex:close', (event) => {
  if (!event.detail.completed) console.log('Buyer left without paying');
});
```

The [reference](/developers/embeds/reference#events) lists every event, including the cart, rewards, and diagnostic ones.

## Current behavior to know

* Group embeds work through `data-shoppex-group-id` or `Shoppex.open({ groupId })`.
* Group embeds show a product picker first, then continue into the normal product checkout.
* More than one item routes through cart checkout, which creates the invoice before the modal opens and therefore needs `shopId`.
* Quantity is normalized to `1..999999`.
* Invalid or empty `items` do not open checkout, unless a valid `groupId` is provided.
* `metadata` reaches the invoice as `custom_fields` on every checkout path. Where a key is set by both `data-shoppex-metadata` and `data-shoppex-custom-fields`, the custom field wins — see the [reference](/developers/embeds/reference#metadata).

## If you already have your own product cards

This setup causes the most confusion in real projects, so here is the simple rule.

* Use the **Checkout Embed SDK** when you only need Shoppex to open checkout.
* Use a **product data source** when you also render your own product list, prices, images, stock, or categories.

In plain terms:

* `window.Shoppex` handles the modal checkout.
* If you want to build custom cards around it, your app still needs product data from somewhere.

Common setups:

| What you are building                           | What you need                            |
| ----------------------------------------------- | ---------------------------------------- |
| One buy button on an existing page              | Embed SDK only                           |
| Custom landing page with your own product cards | Embed SDK + product data source          |
| Full storefront with cart/state/search          | Storefront SDK (`@shoppexio/storefront`) |

Typical product data sources:

* Shoppex Storefront API
* Storefront SDK (`@shoppexio/storefront`)
* Your own backend that already proxies Shoppex product data

Important:

* `data-shoppex-product-id` is part of the public Embed SDK integration.
* `data-shoppex-group-id` is also part of the public Embed SDK integration.
* Local helpers, TypeScript types, or `lib/shoppex.ts`-style files are **not** required by Shoppex itself.
* Those files are app-level code you can add to keep your own project organized.

```tsx theme={"system"}
<ProductCard
  title={product.title}
  price={product.price}
  image={product.image}
>
  <button data-shoppex-product-id={product.id}>
    Buy now
  </button>
</ProductCard>
```

Here, your app owns the `ProductCard`, and Shoppex owns the checkout modal.

## Framework snippets

<Note>
  Replace all placeholder values: `PRODUCT_ID`, `GROUP_ID`, `VARIANT_ID`, and `https://your-site.com/thank-you`.
</Note>

### Next.js

```tsx theme={"system"}
'use client';

import Script from 'next/script';

declare global {
  interface Window {
    Shoppex?: {
      open: (options: {
        shopId?: string;
        groupId?: string;
        items: Array<{ productId: string; variantId?: string; quantity?: number }>;
        theme?: 'light' | 'dark' | 'auto';
        returnUrl?: string;
        email?: string;
        couponCode?: string;
        affiliateCode?: string;
        metadata?: Record<string, string>;
      }) => void;
    };
  }
}

export default function BuyButton() {
  const openCheckout = () => {
    window.Shoppex?.open({
      shopId: 'YOUR_SHOP',
      items: [{ productId: 'PRODUCT_ID', variantId: 'VARIANT_ID', quantity: 1 }],
      theme: 'auto',
      returnUrl: 'https://your-site.com/thank-you',
      affiliateCode: 'CREATOR123',
      metadata: { source: 'landing' },
    });
  };

  return (
    <>
      <Script
        src="https://checkout.shoppex.io/embed/embed.iife.js"
        strategy="afterInteractive"
      />
      <button onClick={openCheckout}>Buy now</button>
    </>
  );
}
```

### React SPA

```tsx theme={"system"}
import { useEffect } from 'react';

declare global {
  interface Window {
    Shoppex?: {
      open: (options: {
        groupId?: string;
        items: Array<{ productId: string; variantId?: string; quantity?: number }>;
        theme?: 'light' | 'dark' | 'auto';
        affiliateCode?: string;
      }) => void;
    };
  }
}

export function BuyButton() {
  useEffect(() => {
    const script = document.createElement('script');
    script.src = 'https://checkout.shoppex.io/embed/embed.iife.js';
    script.defer = true;
    document.body.appendChild(script);

    return () => {
      document.body.removeChild(script);
    };
  }, []);

  return (
    <button
      onClick={() =>
        window.Shoppex?.open({
          items: [{ productId: 'PRODUCT_ID', variantId: 'VARIANT_ID', quantity: 1 }],
          theme: 'auto',
          affiliateCode: 'CREATOR123',
        })
      }
    >
      Buy now
    </button>
  );
}
```

### WordPress

Paste this into a Custom HTML block.

```html theme={"system"}
<button
  data-shoppex-product-id="PRODUCT_ID"
  data-shoppex-variant-id="VARIANT_ID"
  data-shoppex-quantity="1"
  data-shoppex-theme="auto"
  data-shoppex-referral-code="CREATOR123"
  data-shoppex-return-url="https://your-site.com/thank-you"
>
  Buy now
</button>

<script src="https://checkout.shoppex.io/embed/embed.iife.js" defer></script>
```

If your theme strips script tags, add the script globally, for example in footer settings or the theme template, and keep only the button markup in content.

### Webflow

Add an Embed element and paste this.

```html theme={"system"}
<button
  data-shoppex-product-id="PRODUCT_ID"
  data-shoppex-variant-id="VARIANT_ID"
  data-shoppex-quantity="1"
  data-shoppex-email="customer@example.com"
  data-shoppex-coupon-code="SAVE20"
  data-shoppex-metadata='{"source":"webflow"}'
>
  Buy now
</button>

<script src="https://checkout.shoppex.io/embed/embed.iife.js" defer></script>
```

If you already load the script site-wide in Webflow project settings, keep only the button in each Embed block.

### Bonus: programmatic multi-item button

```html theme={"system"}
<button id="open-checkout">Checkout bundle</button>
<script src="https://checkout.shoppex.io/embed/embed.iife.js" defer></script>
<script>
  document.getElementById('open-checkout').addEventListener('click', () => {
    Shoppex.open({
      shopId: 'YOUR_SHOP',
      items: [
        { productId: 'PRODUCT_ID_1', variantId: 'VARIANT_1', quantity: 1 },
        { productId: 'PRODUCT_ID_2', quantity: 2 }
      ],
      theme: 'auto',
      couponCode: 'SAVE20'
    });
  });
</script>
```

<Note>
  A multi-item checkout creates the invoice before the modal opens, so `shopId` is required. Without it the modal opens and shows an error, and `shoppex:error` fires.
</Note>

### Group embed button

```html theme={"system"}
<button
  data-shoppex-group-id="GROUP_ID"
  data-shoppex-theme="auto"
  data-shoppex-referral-code="CREATOR123"
  data-shoppex-return-url="https://your-site.com/thank-you"
>
  Open group
</button>

<script src="https://checkout.shoppex.io/embed/embed.iife.js" defer></script>
```

Use this when you want buyers to choose one product from a Shoppex group inside the modal. The group view opens first, and when the buyer picks a product, Shoppex continues into the normal product checkout flow.

### Hybrid: your own product cards + Shoppex checkout

Use this when you already render your own catalog UI and only want Shoppex for the checkout modal.

Fetch product data however you want, render your own cards, and put `data-shoppex-product-id` on the CTA button. Shoppex handles the rest.

```tsx theme={"system"}
'use client';

import Script from 'next/script';

type Product = {
  id: string;
  title: string;
  price: string;
  imageUrl?: string;
};

export function ProductGrid({ products }: { products: Product[] }) {
  return (
    <>
      <Script
        src="https://checkout.shoppex.io/embed/embed.iife.js"
        strategy="afterInteractive"
      />

      <div className="grid gap-4 md:grid-cols-3">
        {products.map((product) => (
          <article key={product.id} className="rounded-xl border p-4">
            {product.imageUrl ? (
              <img src={product.imageUrl} alt={product.title} />
            ) : null}

            <h3>{product.title}</h3>
            <p>{product.price}</p>

            <button
              data-shoppex-product-id={product.id}
              data-shoppex-quantity="1"
              data-shoppex-theme="auto"
            >
              Buy now
            </button>
          </article>
        ))}
      </div>
    </>
  );
}
```

Notes:

* The product card layout is yours, not Shoppex's.
* The checkout modal is Shoppex's.
* You can get `products` from the Storefront API, `@shoppexio/storefront`, or your own backend.
* You do **not** need to copy any specific `lib/shoppex.ts` file from another project. That kind of helper is one possible app structure.

### Event snippet (analytics)

```html theme={"system"}
<script>
  document.addEventListener('shoppex:success', (event) => {
    console.log('Invoice paid:', event.detail.invoiceId);
    // Example: analytics.track('checkout_paid', { invoiceId: event.detail.invoiceId });
  });

  document.addEventListener('shoppex:error', (event) => {
    console.error('Checkout error:', event.detail.error);
  });

  document.addEventListener('shoppex:close', (event) => {
    if (event.detail.completed) return;
    // Example: analytics.track('checkout_abandoned');
  });
</script>
```

<CardGroup cols={2}>
  <Card title="Embed SDK reference" icon="book" href="/developers/embeds/reference">
    Full config, data attributes, API methods, event contracts, CSP, and the production checklist.
  </Card>

  <Card title="Embed demo" icon="laptop-binary" href="https://checkout.shoppex.io/embed/demo.html">
    Interactive demo with real embed patterns.
  </Card>
</CardGroup>
