Skip to main content

Build a vendor integration

A complete COPE integration is three pieces: a small amount of dashboard configuration, a buyer-facing checkout page, and a backend webhook receiver that confirms each payment.
Try it deployed in a few minutes: copecart/integration-samples is a Next.js app with both checkout styles, a webhook receiver, and Vercel / Railway deploy buttons in the README.

1. Configure the business

In the COPE dashboard, gather three values before writing any code:
SettingWherePurpose
Publishable key (cope_pk_live_...)Settings → Developers → API keysSafe to ship in the browser. Used by the Checkout SDK.
Product UUID (prod_...)CatalogThe product the buyer will purchase.
Embed origin (iframe checkout only)Settings → Checkout → Embed domainsThe exact parent origin that will host the iframe, for example https://shop.example.com.
Embed origins use scheme + host (+ port) only — no path, query, or fragment. https://shop.example.com and https://www.shop.example.com are different origins. Embed origins and success_url / cancel_url are independent allowlists. Registering an origin authorizes a parent page to iframe the checkout (and registers the domain with Stripe Payment Method Domains so browser wallets work). The success and cancel URLs are validated separately when the checkout is created and must be HTTPS. Failures look different too — an unregistered embed origin produces a browser CSP block (frame-ancestors 'none'), while an invalid success_url produces an API error on cart creation.

2. Add checkout to your page

Install the Checkout SDK. Iframe checkout requires ^0.2.0 or later for mountCheckout; hosted (redirect) checkout works on any 0.x.
pnpm add @copecart/sdk
The setup is the same for both checkout styles. Build a cart, add a line, create the checkout session — only the last call differs.
import { CopeCart } from "@copecart/sdk"

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

const cart = await cope.createCart({ currency: "EUR" })
await cope.addLine(cart.id, { product_id: "prod_..." })

const checkout = await cope.checkout(cart.id, {
  embed_origin: window.location.origin, // iframe only — omit for redirect
  success_url: "https://shop.example.com/thank-you",
  cancel_url: "https://shop.example.com/cart",
  consents: [{ type: "buyer_tos" }],
})
For hosted (redirect) checkout, send the buyer to COPE:
cope.redirectToCheckout(checkout)
For embedded (iframe) checkout, add a container element to the page where the iframe should mount:
<div id="cope-checkout-frame"></div>
Then mount the checkout on it:
cope.mountCheckout("#cope-checkout-frame", checkout, {
  fallback: "redirect",
  onReady: () => showCheckoutFrame(),
  onSuccess: () => showOrderConfirmation(),
  onError: ({ code, retryable }) => reportCheckoutError(code, retryable),
})
In both styles, COPE redirects the buyer to success_url once payment completes. Treat that landing page as cosmetic — the authoritative payment signal arrives on your backend as a webhook (next step). See Embedded hosted checkout for the full mount contract, every callback, and the security model. The Checkout SDK overview covers the cart-building APIs.

3. Receive signed webhooks

The thank-you page is decorative. A buyer can land there directly and the order_uuid in the URL can be forged. Treat the signed webhook delivery to your backend as the only authoritative payment signal — that is where you grant access, write to your database, and notify other systems. Stand up an HTTPS endpoint and register it once with COPE using a server-side cope_sk_... key — that is the secret counterpart of the publishable cope_pk_... you put in the browser. Only the publishable key is safe to ship to a client; the secret key stays on your server and is rotated periodically. Once the endpoint is registered, COPE will POST every relevant event to your URL. The receiver should:
  • Verify the X-Cope-Signature HMAC against the raw request body.
  • Ack 2xx immediately. Push real work to a queue.
  • Dedupe by X-Cope-Event-Id2xx does not guarantee at-most-once delivery.
Consume webhooks shows the recommended receiver shape. Webhook signing is the exact HMAC contract, and Webhook event types lists every event you might handle.

Before going live

A handful of things easy to miss until the first real buyer trips on them:
  • The webhook handler grants access — not the thank-you page.
  • Rotate the secret API key (cope_sk_...) and the webhook signing secret on a schedule. Store both server-side only.
  • For iframe checkout, every parent origin you deploy from — preview, staging, production, custom domains — must be registered separately under Settings → Checkout. They are different origins to the browser.