A session comes back with two URLs, and they are not interchangeable:
Framing url produces a blank box and one console line
(frame-ancestors 'none'), with no request reaching us and nothing in your
logs. If an embed is blank, check which of the two you used first.
Embedding keeps the payer on your site. Doing that alone works fine — the page
can connect its own wallet.
To match the page to your brand, pass theme parameters on the
URL — colours, font, and corner radius, no CSS.
Bridging a connected wallet
If your app has already connected a wallet, making the payer connect a
second time inside the iframe is a real drop-off point. The parent bridge lends
your existing connection to the frame: the iframe asks for signatures, your
provider produces them, and the payer never reconnects.
The iframe bridge is enabled only when the session’s server response contains
the merchant page origin. Add that exact origin when creating a publishable
key in the partner portal; sessions for a partner/environment with no allowed
origin keep the hosted widget working but disable parent-wallet bridging.
Call bridge.stop() when the iframe unmounts, or the listener outlives it.
onPaymentComplete means the payer submitted a transaction — not that
funds have settled. Cross-chain routes still have a bridge leg to finish.
Release goods on the webhook plus a session read, never on
this callback. It is a UI signal, not a settlement signal.
Keeping the bridge in sync
The bridge is constructed with an address and a chain id. If your user switches
either, tear the bridge down and start a new one — otherwise the frame keeps
signing against the wallet state you handed it at mount:
Security
Messages are origin-checked in both directions — the parent validates the
frame’s origin against an allowlist before acting on anything it says, and the
iframe does the same for the parent. The bridge always fetches signable data
from https://server.hypermid.io; it has no caller-supplied API host.
Never pass a secret key here. The bridge is browser code; sessions are created
on your server.