Embed Fluveo Checkout in an iframe
Status: test-only. Embedding is available for test-mode Checkout Sessions. Card payments, including 3-D Secure, are exercised in test mode.
You can show the hosted payment page of a
Checkout Session inside an <iframe> on your own
checkout page instead of redirecting to it. The card fields still live on Fluveo’s
page, so card data never reaches your page or your servers.
PCI DSS. An embedded payment page is not the same as a redirect for SAQ A eligibility: with an iframe, the page that hosts it must also be protected against script attacks (PCI SSC FAQ 1588). Confirm your scope with your assessor before you switch from a redirect.
How it works:
- Your server creates a Checkout Session and gets its
url. - Your page puts that
urlin an iframe. The customer pays inside it. - When the payment reaches a final status, the page in the iframe sends your page
a
fluveo.checkout.completedmessage. It does not navigate your page by itself. - Your page asks your server, and your server confirms the outcome with
GET /v1/checkout/sessions/{id}before it fulfils the order.
When the card needs 3-D Secure, the customer leaves your page for the challenge and
comes back on your success_url instead (see 3-D Secure).
1. Create the Checkout Session (server)
Create the session exactly as for a redirect. success_url and cancel_url must be
pages on your site that can finish the order on their own, because a customer who
goes through 3-D Secure returns there as a full page.
The answer has the session id (cs_test_...) and its url
(https://pay.devfluveo.com/c/cs_test_... in test mode). Send both to your page.
Embed the session url only. A Payment Link URL
(/p/...) refuses to be framed.
2. Embed the page (browser)
- Do not sandbox the iframe. That is the setup Fluveo has tested (Chromium).
- If your policy requires
sandbox, we recommendsandbox="allow-scripts allow-same-origin allow-forms allow-top-navigation-by-user-activation". The 3-D Secure step navigates the whole tab, so it needsallow-top-navigation-by-user-activation. This recipe follows from that mechanism; it has not been tested, so try a 3-D Secure test card in each browser you support. - Keep
allow="payment"so browser wallets can use the Payment Request API inside the frame.
3. Listen for the completion message (browser)
When the page in the iframe reaches a final status, it posts one message to its parent:
The message carries no secret, and it is sent to any parent (target origin *).
Anyone who can frame the page can receive it, and any page can post a look-alike
message to yours. So:
- check
event.origin: it must be the origin of the sessionurl(https://pay.devfluveo.comin test mode,https://pay.fluveo.comin live mode); - check
event.sourceis your iframe andcheckout_sessionis the session you created; - never fulfil an order from the message alone. Treat it as “go and check now”.
4. Confirm on your server
Read the session with your secret key and fulfil only when it is paid:
Use the same check on your success_url page. If the customer closes the tab before
either runs, a signed webhook payment_intent.succeeded for the session’s
PaymentIntent (payment_intent on the session) still reaches your server.
3-D Secure
When the card needs 3-D Secure, the challenge does not stay inside the iframe:
- The customer presses Pay inside the iframe.
- The whole tab moves to the bank’s challenge page. Your checkout page is no longer shown.
- After the challenge, the tab returns to the Fluveo page and then goes to your
success_urlas a full page.
No fluveo.checkout.completed message is sent in this case, because the Fluveo page
is no longer in your iframe. Your success_url page confirms the order with step 4.
”Return to merchant” link
Inside an iframe, the paid page does not send the customer anywhere by itself: browsers
block a framed page from navigating your page without a click. It shows a
Return to merchant link to your success_url instead, which opens in the whole tab.
Most integrations navigate on their own after step 4, so the customer rarely needs it.
Checklist
- The session is created on your server; the browser only gets its
idandurl. - The iframe has no
sandbox; or, withsandbox, the four tokens above, and a 3-D Secure test payment works in your browsers. - Your listener checks
event.origin,event.source,typeandcheckout_session. - Fulfilment happens only after
GET /v1/checkout/sessions/{id}showspayment_status: "paid", and only once per session. success_urlandcancel_urlwork as full pages (3-D Secure returns there).