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

# Checkout SDK overview

> Use the COPE Checkout SDK to build custom product and cart pages that hand buyers off to hosted checkout by redirect or iframe.

# Checkout SDK

The COPE Checkout SDK is a browser SDK for buyer checkout flows. It lets you fetch product data, build a cart, calculate final prices, create a hosted checkout session, and either redirect the buyer to COPE checkout or mount that checkout inside your page.

Use the SDK when your site owns the product page or shopping experience and COPE owns payment collection, tax calculation, order creation, and payment lifecycle events.

## Install

<CodeGroup>
  ```bash npm theme={"theme":{"light":"github-light","dark":"github-dark"}}
  npm install @copecart/sdk
  ```

  ```bash pnpm theme={"theme":{"light":"github-light","dark":"github-dark"}}
  pnpm add @copecart/sdk
  ```
</CodeGroup>

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { CopeCart } from "@copecart/sdk"

const cope = new CopeCart({
  publishableKey: "cope_pk_live_...",
})
```

For pages without a bundler, load the global build:

```html theme={"theme":{"light":"github-light","dark":"github-dark"}}
<script src="https://unpkg.com/@copecart/sdk@latest/dist/index.global.js"></script>
<script>
  const cope = new CopeCart.CopeCart({
    publishableKey: "cope_pk_live_...",
  })
</script>
```

## Configuration

| Option            | Required | Default              | Notes                                                                                   |
| ----------------- | -------- | -------------------- | --------------------------------------------------------------------------------------- |
| `publishableKey`  | Yes      | -                    | Business publishable key. It starts with `cope_pk_` and is safe to use in browser code. |
| `baseUrl`         | No       | COPE production API  | Override only when COPE support gives you an environment-specific API URL.              |
| `checkoutBaseUrl` | No       | The `baseUrl` origin | Used to validate checkout URLs returned by the API.                                     |

The SDK requires HTTPS except for `http://localhost` during development.

## Basic redirect flow

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { CopeCart } from "@copecart/sdk"

const cope = new CopeCart({
  publishableKey: "cope_pk_live_...",
})

const product = await cope.getProduct("prd_...")
const cart = await cope.createCart({ currency: product.currency })

await cope.addLine(cart.id, {
  product_id: product.id,
  plan_id: product.payment_plans[0].id,
  quantity: 1,
})

await cope.setBuyerIdentity(cart.id, {
  email: "buyer@example.com",
  tax_location: {
    country: "DE",
    postal_code: "10115",
  },
})

await cope.reprice(cart.id)

const checkout = await cope.checkout(cart.id, {
  success_url: "https://your-site.example/thank-you",
  cancel_url: "https://your-site.example/cart",
  consents: [{ type: "buyer_tos" }],
})

cope.redirectToCheckout(checkout)
```

After successful payment, COPE redirects to `success_url` with `order_id` appended:

```txt theme={"theme":{"light":"github-light","dark":"github-dark"}}
https://your-site.example/thank-you?order_id=ord_...
```

## Redirect URLs

`success_url` and `cancel_url` must be registered for the business before you can pass them to `checkout()`. Register them in the COPE dashboard under **Settings → SDK**, on the API Settings page, in the **Redirect URLs** section — up to 10 of each.

Passing a URL that is not registered fails the checkout call with `422` and a field error per offending value. The `message` points at the same screen:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "errors": [
    {
      "code": "invalid_redirect_url",
      "field": "success_url",
      "message": "success_url is not in the SDK integration allowlist. Register it at Settings → API → Redirect URLs."
    }
  ]
}
```

### Matching is exact

A registered entry is compared to the value you send as a **complete URL string**. It is not an origin match and not a path prefix, so every one of these is rejected when only `https://shop.example.com/thank-you` is registered:

| Value sent to `checkout()`                     | Result                    |
| ---------------------------------------------- | ------------------------- |
| `https://shop.example.com/thank-you`           | Matches                   |
| `https://shop.example.com`                     | Rejected — different URL  |
| `https://shop.example.com/thank-you/`          | Rejected — trailing slash |
| `https://shop.example.com/thank-you?status=ok` | Rejected — query string   |

If your landing page needs its own query parameters, register the full URL including them. You do not need to register the `?order_id=` variant: COPE appends `order_id` after the value has been matched.

### What you can register

* HTTPS only. `http://` is rejected, including `http://localhost`, so redirect URLs cannot point at a local development server even though the SDK itself accepts `http://localhost` as a page origin.
* A public host. Loopback and private addresses such as `localhost`, `127.0.0.1`, `10.x.x.x`, `172.16-31.x.x`, `192.168.x.x`, and IPv6 `::1` or unique-local addresses are rejected.
* No credentials in the URL (`https://user:pass@…`) and no backslashes.

To exercise redirect completion from a development machine, use an HTTPS tunnel and register the tunnel URL.

### Omitting them

`success_url` and `cancel_url` are optional on `checkout()`. When you omit one, COPE uses the first URL registered for that field. When you send one, it must be registered.

Embedded checkout signals completion through postMessage events rather than a redirect, so iframe integrations can omit both. Keep at least one of each registered anyway: when `mountCheckout()` falls back with `fallback: "redirect"`, the buyer continues on COPE hosted checkout, and that page still needs a success and cancel destination to return them to afterwards.

## Embedded checkout

To keep the buyer on your page, create checkout with `embed_origin` and mount it with `mountCheckout()`.

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
const checkout = await cope.checkout(cart.id, {
  embed_origin: window.location.origin,
  consents: [{ type: "buyer_tos" }],
})

const mounted = cope.mountCheckout("#cope-checkout", checkout, {
  fallback: "redirect",
  onReady: () => {
    document.querySelector("#cope-checkout")?.removeAttribute("hidden")
  },
  onSuccess: () => {
    window.location.href = "/thank-you"
  },
  onError: ({ code }) => {
    console.error("COPE checkout iframe error", code)
  },
})
```

Read the full [embedded checkout guide](./embedded-checkout) before launching iframe checkout. It covers registered embed origins, iframe security, postMessage events, and fallback behavior.

## Core methods

| Method                                     | Purpose                                                                        |
| ------------------------------------------ | ------------------------------------------------------------------------------ |
| `getProduct(productId)`                    | Fetch public product details and payment plans.                                |
| `createCart(payload)`                      | Create a cart and store the checkout credential needed for later cart updates. |
| `addLine(cartId, payload)`                 | Add a product, payment plan, and quantity.                                     |
| `setBuyerIdentity(cartId, payload)`        | Set buyer location and contact data for tax and checkout.                      |
| `reprice(cartId)`                          | Calculate taxes, shipping, discounts, and final totals.                        |
| `checkout(cartId, payload)`                | Create a hosted checkout session.                                              |
| `redirectToCheckout(checkout)`             | Navigate the browser to hosted checkout.                                       |
| `mountCheckout(target, checkout, options)` | Mount hosted checkout inside an iframe.                                        |
| `cancelCheckout(checkoutId)`               | Cancel an open checkout session.                                               |
| `destroy()`                                | Abort in-flight requests, remove mounted iframes, and clear SDK cart state.    |

## Errors

The SDK exposes typed errors:

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
import {
  CopeApiError,
  CopeCartExpiredError,
  CopeNetworkError,
} from "@copecart/sdk"

try {
  await cope.reprice(cart.id)
} catch (error) {
  if (error instanceof CopeApiError) {
    console.log(error.status, error.code, error.errors)
  }

  if (error instanceof CopeCartExpiredError) {
    const replacement = await cope.createCart({ currency: "EUR" })
  }

  if (error instanceof CopeNetworkError) {
    console.log("Retry later")
  }
}
```

Treat 4xx API errors as permanent for the same payload. Fix the input and retry with a new request. The SDK retries selected transient network or server failures with backoff.
