Allow Partners to load your Hosted Checkout in an iframe

Adapt your Klarna integration so that Partners who load your Hosted Checkout page inside their own iframe can still complete the Klarna Purchase Journey, and set the requirements those Partners must follow.
18 min read

Overview

Use this guide if some of your Partners load your Hosted Checkout page inside an iframe on their own checkout page. Your Hosted Checkout page still loads the Klarna web SDK and renders Payment Presentation. The Partner's page owns the iframe around it.
Warning
Klarna recommends that Partners send customers to your Hosted Checkout page, or open it, at the top level, as described in Payment Presentation for Hosted Checkout integration. Framing is supported as a last resort for Partners whose checkout already works this way. Every framed Partner page adds browser restrictions and parent-page code that the Klarna Purchase Journey depends on, which lowers conversion and gives each Partner integration more ways to fail.
Complete the top-level Hosted Checkout integration first. This guide adds what changes when a Partner frames it.

Who does what

Two parties change their pages, and only one of them is you. You configure your Hosted Checkout page and pass the rest on to every Partner who frames it. You can't configure the Partner's page yourself, so the Klarna Purchase Journey only works for a Partner who follows the requirements you give them.
PartyResponsibilities
Acquiring Partner (you)Serves the framed Hosted Checkout page, loads Klarna, sends the Partner page URL to Klarna, and relays resize and redirect events to the Partner's page. Publishes the Partner requirements and checks each Partner against them before go-live.
PartnerOwns the iframe element and the page around it. Configures the iframe, validates messages from your origin, resizes the iframe, and navigates the top-level page when Klarna redirects. Follows the requirements you publish.
Klarna launches the journey in one of two ways, and each way sends different events. When Klarna launches the journey as a redirect, the top-level page leaves the checkout and no further events come from your page:
sequenceDiagram autonumber participant Customer participant Partner as Partner page (top level) participant Frame as Your Hosted Checkout (in iframe) participant SDK as Klarna Web SDK Partner->>Frame: Load Hosted Checkout in iframe Frame->>SDK: Initialize and render Payment Presentation Customer->>SDK: Select Klarna and continue SDK->>Frame: redirect event with redirectUrl Frame->>Partner: postMessage klarna:flow-redirect Partner->>Partner: Validate origin and destination Partner->>Customer: Navigate the top-level page
When the journey runs in a pop-up or inside the iframe, the page stays and the Partner expands the iframe while the journey is open, then restores it:
sequenceDiagram autonumber participant Customer participant Partner as Partner page (top level) participant Frame as Your Hosted Checkout (in iframe) participant SDK as Klarna Web SDK Partner->>Frame: Load Hosted Checkout in iframe Frame->>SDK: Initialize and render Payment Presentation Partner->>Frame: postMessage klarna:frame-rect Customer->>SDK: Select the payment button or Check your purchase power SDK->>Frame: overlayopen event Frame->>Frame: Pin the Klarna container Frame->>Partner: postMessage klarna:flow-open Partner->>Partner: Expand iframe to full viewport Note over Customer,SDK: The journey runs in a pop-up or inside the iframe SDK->>Frame: overlayclose event Frame->>Frame: Remove the pin Frame->>Partner: postMessage klarna:flow-close Partner->>Partner: Restore the iframe

What the customer experience must support

Klarna adapts the Klarna Purchase Journey to the browser it runs in. A framed integration has to support all four of the behaviors below. Each one needs work from you and from the Partner.
  1. 1.
    Klarna can launch the journey as a redirect instead of a pop-up. Klarna does this on phones, tablets and in-app browsers. Klarna emits the redirect event before it navigates your page, both to launch the journey and for any later navigation to a return URL. You relay the URL. The Partner's page navigates to it, because your framed page can't navigate the top-level page on its own. See Handle redirects.
  2. 2.
    Klarna can run the journey inside the iframe. On desktop, Klarna opens a pop-up. If the browser blocks it, Klarna shows a backdrop with a Continue button that retries the pop-up, and if that is blocked too, the journey runs in an iframe over the whole page, inside the browsing context that loaded the Klarna web SDK, which is your framed page. It needs the whole viewport. Klarna fires overlayopen right before it draws anything in the page, and overlayclose after it removes it on the pop-up or in-iframe path. overlayclose may still fire before a redirect completes if the overlay closes while your page is alive; do not depend on it when the journey leaves on a redirect. You signal the Partner's page on each overlay event. The Partner's page expands the iframe to the full viewport and restores it afterwards. Without the expansion, the journey renders inside the original frame box and the customer can't complete the purchase. The page around the iframe, and the Klarna controls in it, stay where they were when the customer clicked. See Resize the iframe.
  3. 3.
    Klarna renders a fullscreen backdrop. The backdrop stops the customer from interacting with the page underneath while the journey is open, and gives them a way back into the flow when the browser blocks the Klarna window or it loses focus. It depends on the same full-viewport expansion. The backdrop is controlled by hideOverlay on the payment button configuration you pass to component() (KlarnaPaymentButtonConfig, alongside initiationMode) and on the Payment Request options (PaymentRequestOptions). Both default to false. Leave them at the default unless you have agreed a custom overlay with Klarna.
  4. 4.
    Klarna needs the Partner's checkout page URL for fraud assessment. Klarna only sees your origin, not the Partner's. You send the Partner page URL to Klarna. See Send the Partner page URL to Klarna.
Info
The resize handlers listen for overlayopen and overlayclose on klarna.Payment and klarna.Messaging, not for the payment button's initiate callback. Klarna emits on the namespace whose API rendered the control: Payment for the payment button, for Payment.presentation links such as Check your purchase power, and for other payment-initiated overlays; Messaging for Messaging.placement overlays. Register the same handler on both namespaces when the framed page uses both APIs. Do not rely on overlayclose on redirect launch; the redirect sequence above is separate. The pin and the wrapper follow the whole Klarna container, not one control in it.

Prerequisites

Before you let Partners frame your Hosted Checkout page:
  • Register the origin of your Hosted Checkout page as an allowed client origin.
  • Agree with each Partner the exact origins both pages validate in their postMessage handlers.
  • Agree with each Partner the HTTPS returnUrl origin their page allows for return navigation.
  • Confirm the Partner can change the iframe attributes, their page headers, and their page scripts.

Configure your Hosted Checkout page (Acquiring Partner)

Your framed page needs the same Content-Security-Policy header as a top-level page, plus frame-ancestors listing the Partner origins allowed to frame it. Cross-Origin-Opener-Policy has no effect on a framed page. It applies to the Partner's top-level page, which is why it's part of the requirements you pass on to your Partners. See Configure the framed document headers.
Serve your Hosted Checkout page from the Partner's origin when you can, for example through a CNAME or reverse proxy. The page is then first-party to the Partner's checkout, which avoids third-party cookie and storage partitioning. This improves reliability. It doesn't isolate the two pages from each other.
In the common setup your page stays on your own origin inside the Partner's checkout, so it's cross-origin and third-party. Browsers partition its cookies and storage, and every resize and redirect has to go through postMessage between the two origins.

Klarna validates the origin that loads the Klarna web SDK

Klarna checks the origin of the document that loads the Klarna web SDK against your allowed client origins. In this integration that's your Hosted Checkout origin. Klarna doesn't see the Partner's origin, or any page above it if the Partner's page is itself framed. Register your own origin. The Partner's origin doesn't need to be registered for this path.

Send the Partner page URL to Klarna (Acquiring Partner)

Because origin validation only sees your origin, send the Partner's top-level page URL as the first point_of_transaction.referrers entry when calling authorizePaymentAPI. Klarna uses it for fraud assessment and monitoring. Klarna doesn't reject a request based on it.
Ask the Partner to pass the full URL to you explicitly, for example as a query parameter on the iframe src. Don't rely on document.referrer: under the browsers' default referrer policy, a cross-origin iframe only receives the Partner's origin, not the page URL.
JAVASCRIPT
1 2 3 4 5 6 7 8 9 10
// The Partner loads the iframe as https://checkout.example.com/klarna?partnerPageUrl=<encoded URL> const partnerPageUrl = new URLSearchParams(window.location.search).get("partnerPageUrl"); const authorizationRequest = { currency: "USD", request_payment_transaction: { amount: 11800 // ...the rest of your authorization request }, point_of_transaction: {

Resize the iframe (Acquiring Partner)

The Klarna Purchase Journey needs the full viewport while it's open. Your framed page can't resize the iframe element, so notify the Partner's page. Two things also have to stay still, or the page jumps when the iframe expands:
  • The page around the iframe. A full-viewport iframe leaves the document flow, and the content below it shifts up into the gap. The Partner keeps a wrapper the size of the iframe in that spot. See Resize handler.
  • The Klarna controls in the iframe: the payment button and any on-site messaging link, such as "Check your purchase power". The expanded iframe starts at the top-left of the screen, so the control the customer clicked moves there from where they clicked it. Pin the container that holds all the Klarna controls, #${containerId}, to the wrapper's position on screen before you post klarna:flow-open. Pinning the container keeps whichever control opened the overlay in place, so you don't need to know which one it was. The Partner sends that position as klarna:frame-rect, and sends it again if the window is resized.
Make the Hosted Checkout document background transparent, so the Partner's page stays visible behind Klarna's backdrop:
CSS
1 2 3 4 5
html, body { background: transparent; margin: 0; }
Register one pair of handlers on the Klarna instance before you mount any Klarna component, and post to the exact Partner origin:
  • Post klarna:flow-open from your overlayopen handler, after you pin the container. Klarna fires overlayopen on klarna.Payment or klarna.Messaging right before it draws the backdrop or the fallback iframe.
  • Post klarna:flow-close from your overlayclose handler, and remove the pin. Klarna fires overlayclose on the same namespace after it removes the backdrop or the fallback iframe. Don't post it when your backend call returns: the journey is still open at that point, and shrinking the iframe then traps the backdrop and the blocked pop-up fallback inside the original frame box.
Expand and restore the iframe on every overlayopen and overlayclose, whichever namespace emitted the event.
JAVASCRIPT
1 2 3 4 5 6 7 8 9 10
const PARENT_ORIGIN = "https://partner.example.com"; const klarnaContainer = document.getElementById(containerId); const klarnaContainerInlineStyle = klarnaContainer.getAttribute("style") || ""; let frameRect = null; const notifyPartner = (type) => window.parent.postMessage({ type }, PARENT_ORIGIN); const pinContainer = () => { if (!frameRect) return; klarnaContainer.style.position = "fixed";
top and left are positions on the customer's screen. They line up with the Klarna container only after the iframe fills the screen, because position: fixed inside your page is relative to the iframe. The container fills the iframe at rest, so its width and height are the wrapper's.
The iframe stays expanded until Klarna removes the backdrop, including when the customer closes the Klarna pop-up themselves.
The Partner's page handles these messages. See Resize handler.

Handle redirects (Acquiring Partner)

Klarna emits the redirect event before every navigation of the page that loaded the Klarna web SDK:
  1. 1.
    Launch: Klarna launches the Klarna Purchase Journey as a redirect, on phones, tablets and in-app browsers. redirectUrl is Klarna-hosted. The top-level page then leaves the checkout, so no further events come from your page.
  2. 2.
    Return URL from initiate: your initiate callback resolves with { returnUrl } instead of a payment request. redirectUrl is that URL.
  3. 3.
    Return after completion: a journey that ran in a pop-up or inside the page completes, and Klarna navigates to the return URL set on the payment request. redirectUrl uses that return origin. If your complete handler returns false, Klarna doesn't navigate and doesn't emit redirect.
Register the handler with klarna.Payment.on("redirect", callback) before you mount the payment button. The callback receives { redirectUrl } and returns a Promise that resolves to true when Klarna should navigate, or false when your integration takes over navigation. If you don't register a handler, Klarna navigates the browsing context that loaded the Klarna web SDK, which is the iframe.
Relay redirectUrl to the Partner's page and resolve false so Klarna doesn't navigate the iframe:
JAVASCRIPT
1 2 3 4 5 6 7 8 9 10
const PARENT_ORIGIN = "https://partner.example.com"; try { klarna.Payment.on("redirect", async ({ redirectUrl }) => { window.parent.postMessage( { type: "klarna:flow-redirect", redirectUrl }, PARENT_ORIGIN ); return false; });
Wrap the registration as shown. Klarna web SDK versions released before the redirect event throw when you register it, and an uncaught error stops the rest of your checkout script, so the payment button never renders.
The Partner's page performs the navigation. See Redirect handler.

Requirements to pass on to your Partners (Partner)

Everything in this section runs on the Partner's page. Publish it as part of your product, for example in your integration documentation and as a script the Partner drops into the page that hosts the iframe. A Partner whose page doesn't have the redirect handler can't complete a purchase once Klarna chooses a redirect, which is the common case on mobile. Treat a Partner who hasn't completed these requirements as not ready to go live with a framed Hosted Checkout.
The Partner:
  • Configures the iframe element with the sandbox tokens and allow directives in Configure the iframe element.
  • Sends Cross-Origin-Opener-Policy: same-origin-allow-popups on their top-level page, or no Cross-Origin-Opener-Policy header, but never same-origin. With same-origin, the Klarna pop-up opened from your iframe loses its link to the checkout and stays on Klarna's loading screen.
  • Grants the delegated permissions in their Permissions-Policy header and adds your origin to frame-src, as described in Configure the parent page headers.
  • Loads your Hosted Checkout page directly in that iframe, not inside a further iframe of their own.
  • Passes their checkout page URL to you, so you can send it to Klarna.
  • Adds the resize and redirect handlers below.
The message contract you publish is the klarna:flow-open, klarna:flow-close, and klarna:flow-redirect message types, the klarna:frame-rect position your page sends back, the redirectUrl payload, and the exact origin each side validates.

Resize handler

On the Partner's page, wrap the iframe in an element the size of the iframe at rest, which is the size of the Klarna container on your Hosted Checkout page. The wrapper stays in the page while the iframe fills the viewport, so the content below it doesn't move. Give the iframe a transparent background, so your page stays visible behind Klarna's backdrop.
HTML
1 2 3 4 5 6 7 8
<div id="klarna-frame-wrapper"> <iframe id="klarna-frame" title="Klarna" src="https://your-checkout.example.com/klarna" style="width:100%;height:100%;border:0;background:transparent;" ></iframe> </div>
Size #klarna-frame-wrapper to the iframe at rest. Validate your origin before resizing, lock scrolling while the iframe fills the viewport, and send the wrapper's position on screen when the iframe loads, when the page scrolls, and again when the window is resized:
JAVASCRIPT
1 2 3 4 5 6 7 8 9 10
const FRAME_ORIGIN = "https://your-checkout.example.com"; const frame = document.getElementById("klarna-frame"); const wrapper = document.getElementById("klarna-frame-wrapper"); const inlineStyle = frame.getAttribute("style") || ""; let expanded = false; const sendFrameRect = () => { if (!frame.contentWindow) return; const rect = wrapper.getBoundingClientRect(); frame.contentWindow.postMessage(

Redirect handler

On the Partner's page, validate the message origin, require HTTPS, and allow both destination types: Klarna hosts for launch, and the agreed returnUrl origin for return.
JAVASCRIPT
1 2 3 4 5 6 7 8 9 10
const FRAME_ORIGIN = "https://your-checkout.example.com"; const RETURN_ORIGIN = "https://partner.example.com"; window.addEventListener("message", function (event) { if (event.origin !== FRAME_ORIGIN) return; if (!event.data || event.data.type !== "klarna:flow-redirect") return; const url = new URL(event.data.redirectUrl); if (url.protocol !== "https:") return;

Test the framed integration

Klarna picks how it runs the Klarna Purchase Journey from the customer's device and browser, so a test on one desktop browser covers only one of those paths. A framed integration can work on desktop and still send phone customers to Klarna inside the iframe. Run every check below with each Partner before go-live, on the Partner's real checkout page with its real headers.

Customer journey checks

#SetupActionExpected result
1Desktop web browser, pop-ups allowedSelect the payment button.The Klarna pop-up opens and shows Klarna. The iframe expands to the full viewport while the journey is open. The page below the iframe stays put, and the payment button stays where the customer clicked it. The iframe is restored after the purchase completes or the customer cancels.
2Desktop web browser, pop-ups blocked for the siteSelect the payment button. Keep pop-ups blocked and select Continue on the Klarna backdrop.The backdrop covers the whole page, not only the iframe. After Continue, Klarna opens over the whole page, not inside the iframe, and the purchase can be completed.
3iPhone (Safari) and Android phone (Chrome)Select the payment button.The whole page goes to Klarna, not only the iframe: the browser's address bar shows Klarna. After the purchase, the customer lands on the Partner's return page as a full page.
4iPad (Safari)Select the payment button.Same as check 1 or 2. iPad Safari identifies itself as a desktop browser.
5In-app browser, for example the checkout link opened from a messaging appSelect the payment button.Same as check 3.
6Every level of framing you support, for example a Partner page that frames a page of yours that frames KlarnaRepeat checks 1 to 3.The same results through every level.
7Desktop web browser, pop-ups blocked for the site, a page with on-site messagingSelect "Check your purchase power". Keep pop-ups blocked and select Continue on the Klarna backdrop.Same as check 2. The iframe expands when the backdrop appears, and is restored when Klarna removes it. The "Check your purchase power" link stays where the customer clicked it.
In check 3, look at the address bar or window.top.location, not only at the screen. Without the Partner's redirect handler, nothing navigates and the checkout looks stuck or goes blank. The test fails visibly, but only if you run it on a phone.
If you register shipping callbacks, the Klarna web SDK uses the pop-up on phones as well, so check 3 behaves like check 1.
If you automate these checks with a headless browser, set a desktop user agent for the desktop checks. Some headless browsers identify themselves in a way the Klarna web SDK treats as a mobile device, so the desktop checks would run the redirect path instead.

Configuration checks

Check your side:
  • Your Hosted Checkout page loads with the CSP and frame-ancestors headers.
  • point_of_transaction.referrers[0] contains the Partner checkout page URL.
  • The redirect handler is registered before the payment button is mounted.
  • The overlayopen and overlayclose handlers are registered before any Klarna component is mounted.
Check the Partner's side:
  • The Partner's top-level page sends Cross-Origin-Opener-Policy: same-origin-allow-popups, or no Cross-Origin-Opener-Policy header.
  • Payment, camera, clipboard, and sharing permissions are available when the flow needs them.
  • The Klarna pop-up can open and isn't restricted by the iframe sandbox.
  • The Partner's page expands and restores the iframe only for messages from your origin, keeps a wrapper in the layout, and locks page scrolling while the iframe fills the viewport.
  • The Partner's page navigates the top-level page on klarna:flow-redirect.
  • A launch redirect accepts an HTTPS Klarna origin, and a return redirect accepts the exact agreed return URL origin.
  • All other redirect destinations and message origins are rejected.

Next steps

Related articles
Payment Presentation for Hosted Checkout integration
Manage allowed client origins for the Klarna Web SDK
Browser security configuration for the Klarna web SDK
Load the Klarna web SDK in your own iframe
Customer initiated Payment Authorization
SupportPartner support•Service Status
Cookies|Terms & Conditions|Copyright Klarna AB 2026
Jump to section...