Handling the 3DS redirect when using an iFrame
If you choose to display the 3D Secure (3DS) challenge inside your own iframe, the final redirect back to your redirect_url will also load inside that iframe. Acquired does not currently send a postMessage event from the hosted 3DS page, so your integration must detect completion of the 3DS flow within your own frontend. This guide explains how to handle the redirect, close the iframe, and continue the checkout experience.
Your redirect_url must be publicly accessible
The 3DS authentication page is served from a public Acquired.com domain. Modern browsers, including Chrome, block redirects from public pages to private or internal network addresses (for example: http://localhost, http://192.168.x.x, http://intranet.local).
If your redirect_url points to a private or internal domain, the browser may prevent the redirect and display a network security warning.
To avoid this:
- Use a publicly accessible HTTPS domain for your redirect_url.
- If you are testing locally, use a tunnelling service such as Ngrok or Cloudflare Tunnel.
- Ensure the domain resolves correctly on the open internet, not only inside your internal network.
A public redirect_url is required for the 3DS flow to complete successfully and for the customer to return to your application.
Receive the POST to your redirect_url
When the customer completes 3DS, Acquired sends a POST request to your redirect_url containing:
order_idstatustimestamphashcard_id
Your redirect_url must be a server endpoint that validates the hash and processes the data.
At this point, the 3DS flow has completed and the customer is being redirected inside your iframe.
Return a page that notifies the parent window
Since the redirect loads inside your iframe, your server should return a small HTML page that sends a message to the parent window using window.postMessage.
Example:
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>3DS Complete</title>
</head>
<body>
<p>Processing…</p>
<script>
const orderId = "{{ORDER_ID}}";
const status = "{{STATUS}}";
window.parent.postMessage(
{
type: "ACQUIRED_3DS_COMPLETE",
orderId: orderId,
status: status
},
"https://<your-domain>"
);
</script>
</body>
</html>- This page must be served by you on your own domain.
- Only your code can send a message from inside the iframe.
- Acquired does not currently emit a postMessage event from the hosted 3DS page.
Listen for the message in your checkout page
In the parent page that created the iFrame, add a message listener:
window.addEventListener("message", (event) => {
if (event.origin !== "https://<your-domain>") return;
if (event.data?.type !== "ACQUIRED_3DS_COMPLETE") return;
const { orderId } = event.data;
// Remove or hide the iframe
closeThreeDsIframe();
// Retrieve the latest transaction status from your backend
fetch(`/payments/status?order_id=${orderId}`)
.then((response) => response.json())
.then((result) => handlePaymentStatus(result));
});The message indicates that the 3DS flow is complete.
Your backend should still be used as the final source of truth using webhook events and transactions?order_id.
Display the final result
After closing the iframe, your checkout page should:
- Request the transaction status from your backend
- Display the correct outcome (success, decline, or processing)
- Update the customer interface accordingly
Webhook updates may still be in progress at this stage, so a brief processing state is expected.
Updated 1 day ago
