Skip to main content
Embedded checkout runs the entire Creem payment flow inside your own site instead of sending customers to a hosted page. Drop in a small script, open checkout as a modal or inline iframe, and get a callback when payment completes.
Creem embedded checkout as a modal overlay on a merchant's site

Creem embedded checkout shown as a modal overlay on a merchant's site

How it works

  1. Create a checkout session on your server with your secret API key (see Checkout API) and read its checkoutUrl.
  2. Load the Creem embed script on your page.
  3. Open the checkout as an overlay or inline, and handle onComplete.
You create the session server-side (secret key) and pass the resulting URL to the browser — the URL is safe to expose, and completion is always confirmed server-side via webhook.

1. Create a checkout session (server-side)

Every embed needs a checkout URL, created on your server with your secret API key so the price and product can’t be tampered with. Use the Checkout API or one of its SDKs and read checkoutUrl (the raw REST API returns it as checkout_url):
Then embed that URL with a framework SDK, the script loader, or a raw iframe — pick one.

2. Choose an embed path

Use exactly one of the three paths below — framework SDKs, the script loader, or a raw iframe.

Framework SDKs

First-class packages for React (≥18), Vue (≥3), and Svelte (≥4) with typed props and lifecycle events. They share a framework-agnostic core, @creem_io/embed, which you can also use directly in vanilla JS.
Also available: the useCreemCheckout() hook and the CreemEmbedCheckout.create() promise API. Full reference →
Every SDK takes the same options as the loader below (theme, locale) and emits the same ready + completed events. On completion the embed behaves exactly like the hosted checkout’s return page:
  • Product has a Return URL → a short confirmation screen, then the top window navigates there (~3s).
  • No Return URL → a “View order” button; the embed stays open until the customer closes it.
To keep customers fully inline, call close() inside onComplete — it dismisses the embed and cancels the pending redirect. Cancelling the redirect from close() requires @creem_io/embed ≥ 0.3.3 (or @creem_io/react / /vue / /svelte ≥ 0.2.3); on older versions close() dismisses the modal but the redirect still fires.
Embedded checkout success screen: 'Thank you for your payment' with a 'Returning to Merchant in 2s' countdown button

Product has a Return URL — a confirmation screen with a 'Returning to Merchant' countdown, then the redirect.

Embedded checkout success screen: 'Thank you for your payment' with a 'View Order' button

No Return URL — a 'View order' button; the embed stays open until the customer closes it (or you call close() in onComplete).

Open on click (dynamic sessions)

The components above need the checkoutUrl at render time. For a pricing grid — many products, where you don’t want to pre-create a session for each one on page load — create the session on demand and open it imperatively with the useCreemCheckout() hook:
Vue exposes the same useCreemCheckout() composable. In vanilla JS (or any framework), use the promise-based CreemEmbedCheckout.create({ checkoutUrl }), which resolves once the checkout has rendered.

Script loader

For non-framework apps, or when you want a global Creem object. No build step — drop in the loader script and open checkout from any framework or plain HTML.

Overlay

1

Add the script

2

Open checkout on click

The overlay shows the confirmation screen on success; call Creem.close() from onComplete to dismiss it — this also cancels the pending redirect to the Return URL, keeping the customer on your page.

Inline

Mount checkout inside a container on your page:
onComplete is UX-only. It fires in the browser and can be spoofed — use it to close the modal or show a success state. Grant entitlements / fulfil orders from your webhook handler (verified server-side), never from onComplete.

Declarative data attributes

Any element with data-creem-checkout opens the overlay on click — no JS wiring, but it still relies on the loader script above:
Optionally set the theme and language with data-creem-theme and data-creem-locale:

Raw iframe

The lowest-level option — no loader, no SDK. Use it when you only need inline display and will handle the lifecycle yourself, or don’t need callbacks:
The allow="payment *; publickey-credentials-get *" attribute is required — without it, digital wallets, passkeys, and 3-D Secure challenges can’t run inside the iframe. (The framework SDKs and the script loader set this for you.)

Presentation — theme & language

Works with the framework SDKs and the script loader. openCheckout and mount accept two presentation options, appended to the checkout URL for you:
By default the checkout follows the customer’s browser language. Pass locale to force a specific one (e.g. to match your own site’s language). Unsupported locales fall back to English. See the supported languages for the full list.

Affiliate attribution

If you run the Creem Affiliate Program, embedded checkout still credits referred sales to the right affiliate — and the SDKs and script loader handle it for you. Here’s what happens, and the one edge case worth knowing. How it works
  • An affiliate link (creem.io/affiliate?code=…) sends the visitor to your site and appends a creem_ref token to the landing URL.
  • On the hosted checkout, attribution rides a first-party cookie on Creem’s domain — nothing to do.
  • Inside the embed, the checkout runs in a cross-site iframe, so the browser doesn’t send that cookie there — in any browser. Attribution instead rides the token: the SDK/loader reads creem_ref from your page, persists it in your site’s own first-party storage, and forwards it into the checkout iframe, so it works uniformly everywhere. No code needed for the common case.
creem_ref is an opaque, signed token — it identifies the click, not the affiliate. Don’t try to parse an affiliate code out of it, and you don’t need to read or forward it yourself: the SDKs and loader do it automatically. (It mirrors client_reference_id in Stripe-based tools like Rewardful and Tolt.)
Edge case: separate landing and checkout pages The token arrives on whatever page the affiliate link points to. The SDK captures it automatically when it runs on that page. But if a visitor lands on /?creem_ref=…, then navigates to another page (say /pricing) before opening checkout, and your app drops the query string on that navigation, the token is no longer on the URL. If it wasn’t captured before that navigation, attribution is lost — in every browser, since the embed relies on the token, not the cookie. To cover this, call captureAffiliateRef() once early in your app (e.g. a root layout) so the token is captured on the landing page and stored for later:
The script loader (creem.io/embed.js) captures the token automatically on every page it loads on — so if you include the <script> site-wide (not just on the checkout page), there’s nothing to add.
Manage it yourself (optional) We persist creem_ref in localStorage by default. Prefer your own storage — sessionStorage, your own cookie, or server-side? Read the value off the landing URL, store it however you like, and append it to the checkoutUrl you pass to the SDK. The SDK won’t overwrite a creem_ref you’ve already set on the URL, so your value wins:

API reference

The framework SDKs (@creem_io/react, /vue, /svelte) accept these same options — as props/args, with onComplete/onClose surfaced as the complete/close events where idiomatic.

Creem.openCheckout(options)

Opens checkout in a modal overlay.
checkoutUrl
string
required
The checkout session URL from the Checkout API.
theme
'light' | 'dark'
Color theme for the checkout. Defaults to light.
locale
string
BCP47 language tag (e.g. 'fr', 'pt-BR') to force the checkout language. Defaults to the customer’s browser language; unsupported locales fall back to English.
onReady
() => void
Called once the checkout UI has rendered and is ready for input.
onComplete
(detail) => void
Called when payment completes. detail = { checkoutId, orderId?, orderNo?, redirect?, redirectUrl? }redirectUrl is the merchant success URL, if set.
onClose
() => void
Called when the overlay is dismissed by the customer.
Returns a handle { close() } — call close() to dismiss the overlay programmatically.

Creem.mount(options)

Mounts checkout inline. Same options as openCheckout, plus:
container
string | HTMLElement
required
The element (or a CSS selector) to mount the checkout iframe into. The loader accepts a CSS selector; the npm @creem_io/embed mount requires an HTMLElement (pass document.querySelector('#…')).
Returns a handle { destroy() } — call destroy() to unmount the inline checkout and remove its listener (important in SPAs, e.g. on component unmount).

Creem.close()

Programmatically closes the overlay.

captureAffiliateRef()

An SDK export (@creem_io/embed, /react, /vue, /svelte) — not a method on the loader, which captures automatically. Reads the affiliate creem_ref token from the current URL, persists it to your site’s first-party storage, and returns the active token (from the URL, or a previously stored one), or null. openCheckout/mount already do this; call it directly only to capture on a landing page the visitor reaches before opening checkout (see Affiliate attribution). Safe anywhere — a no-op during SSR and when no token is present. Returns string | null.

Events

Under the hood, the checkout posts messages to the parent window over its lifecycle. The loader handles these for you (onReady, onComplete); if you build a fully custom integration (e.g. a raw iframe), listen for them directly:
source
string
Always "creem-embed".
version
number
Protocol version (currently 1).
type
string
"ready" when the UI has rendered, then "completed" on payment.
Digital wallets. Google Pay works inside the embedded iframe. Apple Pay does not — this is an Apple platform restriction, not an embed limitation. In a cross-origin iframe, Safari only presents the Apple Pay sheet when the top-level page’s domain (your site) is registered as an Apple Pay merchant domain and the merchant session is validated against that top-level domain, not the iframe’s. Your site isn’t registered under Creem’s Apple Pay account, so Safari suppresses the button — Google Pay has no equivalent top-level-domain requirement, which is why it still appears. Card and 3-D Secure payments always work in the embed. If Apple Pay is critical for you, use the hosted checkout page, where payment runs on Creem’s own registered domain.