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

# Embedding the checkout

> Put the checkout in your own site, and the allowlist that decides which sites may.

An embedded checkout is the hosted page in an iframe on your site. The payer
never leaves, and what they approve is still rendered on our origin, where your
page cannot read or rewrite it.

Use [`@reclack/widget`](https://docs.reclack.com/sdks/checkout-widget) to put it there. This page is
about the part that is not JavaScript: which sites are allowed to frame you,
and what a browser does about it.

![A merchant checkout page with the Reclack checkout embedded in it, sized to fit the page rather than scrolling inside a box.](https://mintcdn.com/reclack/pYMqvoKirFyG4lbf/images/embed-inline.png?fit=max\&auto=format\&n=pYMqvoKirFyG4lbf\&q=85\&s=b5470a04832a85f1877d94665de3bded)

An embedded checkout: your page around it, ours inside it

## Allow your origin

Set the list in the dashboard under **Developers**, then **Embedding**.

| State | What happens |
| - | - |
| No origins listed | Any site may embed your checkout. This is what every workspace starts as |
| One or more listed | Those sites may embed it. Every other site gets a page saying so instead of a payment form |

An origin is a scheme and a host, nothing more: `https://acme.dev`. Paths,
query strings and trailing slashes are dropped, only `http` and `https` are
accepted, and up to twenty origins can be listed.

> [!NOTE]
>
> `http://localhost:3000` and `https://localhost:3000` are different origins, as
> are `https://acme.dev` and `https://www.acme.dev`. List each one you actually
> embed from, staging included.

## What the browser enforces

Every document we serve answers
`Content-Security-Policy: frame-ancestors 'none'` unless it was opened by the
widget. A hand-written `<iframe src="https://pay.reclack.com/chk_...">` loads
nothing at all, and neither does a framed receipt or billing portal.

```js theme={null}
// This works: launch adds what the checkout needs to be embeddable.
Reclack().launch(reference, { mode: 'inline', container: '#checkout' })
```

That is the first of two checks, and it is the browser's. The second is the
checkout's own: before it draws anything payable it compares the site framing
it against your list, and refuses if it does not match.

> [!WARNING]
>
> The checkout reads the framing origin from the browser, never from a message
> the embedding page sends. A page that claims to be an allowed origin is still
> measured against what the browser reports it is. A frame that hides where it
> came from is refused once a list exists.

## What your page is told

The two halves talk over a single `MessagePort`, handed to the frame and
addressed to one origin. Your page learns four things:

| Callback | What it carries |
| - | - |
| `onReady` | The checkout has drawn something |
| `onSuccess` | `{ reference, transaction, redirectUrl }` |
| `onError` | A message a person can read |
| `onClose` | They closed it without paying |

Nothing else crosses. Your page is not told the amount, the payer's address or
which wallet they used, because it is another origin and the less it knows
about what happened in there the less there is to get wrong. Read the payment
from [the API](https://docs.reclack.com/api-reference/checkouts/payer-get.md) or the
[webhook](https://docs.reclack.com/webhooks/overview.md) instead.

The frame also reports the height it needs, which the widget applies, so your
page scrolls as one rather than a box scrolling inside a page.

## Sizing, palette and return

* **Height** is handled for you. The frame grows and shrinks with its content.
* **Palette** follows the merchant's setting unless your page asks for one. See
  [Appearance](https://docs.reclack.com/guides/appearance.md).
* **Return** is yours to do. Set `redirect_url` on the session and `onSuccess`
  hands it back as `redirectUrl` for you to navigate to, or ignore it and move
  the payer along yourself.

## When something does not appear

> [accordion] The frame is blank
>
> The document refused to be framed. Check that you are launching through
> the widget rather than writing your own `<iframe>`, and look in the console
> for a `frame-ancestors` refusal.

> [accordion] It says this site cannot show this checkout
>
> The checkout loaded and found the framing origin is not on the list. Add
> the exact origin in **Developers**, then **Embedding**, and reload. The
> message is deliberate: a merchant with a misconfigured list gets an
> explanation rather than an empty box.
>
> ![The embedded checkout showing a refusal: this site cannot show this checkout, with a line saying the merchant has not allowed it to be embedded here.](https://mintcdn.com/reclack/pYMqvoKirFyG4lbf/images/embed-refused.png?fit=max\&auto=format\&n=pYMqvoKirFyG4lbf\&q=85\&s=a69157b7bbb474692b43ca54f3ea7b74)
>
> A frame from an origin that is not on the list

> [accordion] It works in one browser and not another
>
> Check the iframe's referrer policy. Where a browser cannot name the parent
> origin itself, the checkout falls back to the referrer, and a frame that
> suppresses it cannot be identified once a list exists.

> [accordion] A wallet extension cannot be seen inside the frame
>
> Wallets inject per document, and the frame gets its own copy in every
> browser we have tested. One browser withholds the provider from a frame
> that does not ask for it, which the widget's own frame attributes cover, so
> use `launch` rather than a frame of your own.

## Or do not embed at all

Redirecting to the hosted checkout needs no allowlist, no iframe and no script
on your payment page. The trade is that the payer leaves your site. See
[Hosted checkout](https://docs.reclack.com/guides/checkout.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.