diff --git a/packages/globalpayments-3ds/src/index.ts b/packages/globalpayments-3ds/src/index.ts index d0bcb8b..8900c4b 100644 --- a/packages/globalpayments-3ds/src/index.ts +++ b/packages/globalpayments-3ds/src/index.ts @@ -180,8 +180,17 @@ export async function handleInitiateAuthentication( throw new Error("Invalid challenge state. Missing challenge URL"); } + // Browsers can sometimes malform the URL's query string parameters, + // turning `&` into `&` which messes up the query string parameter + // and causes the challenge page to load incorrectly. + // + // We use the textarea element as a way to decode the HTML entities + // and grab the expected text/URL. + var url = document.createElement("textarea"); + url.innerHTML = data.challenge.requestUrl; + const response = await postToIframe( - data.challenge.requestUrl, + url.value, [ { name: "creq", value: data.challenge.encodedChallengeRequest }, // TODO: support session data diff --git a/packages/globalpayments-3ds/src/interfaces.ts b/packages/globalpayments-3ds/src/interfaces.ts index e510a99..81e6e8f 100644 --- a/packages/globalpayments-3ds/src/interfaces.ts +++ b/packages/globalpayments-3ds/src/interfaces.ts @@ -56,7 +56,7 @@ export interface IChallengeWindowOptions { displayMode?: DisplayModeType; encodedChallengeRequest?: string; hide?: boolean; - origin?: string; + origin?: string | string[]; requestUrl?: string; response?: IMessageEventData; target?: string | Element; @@ -76,7 +76,7 @@ export interface ICreditCardData { export interface IIframeData { iframe?: Element; - origin?: string; + origin?: string | string[]; timeout?: number; } diff --git a/packages/globalpayments-3ds/src/lib/post-to-iframe.ts b/packages/globalpayments-3ds/src/lib/post-to-iframe.ts index 294c852..1426ed1 100644 --- a/packages/globalpayments-3ds/src/lib/post-to-iframe.ts +++ b/packages/globalpayments-3ds/src/lib/post-to-iframe.ts @@ -113,6 +113,15 @@ function getWindowMessageEventHandler( return; } + if (data.origin) { + const allowedOrigins = Array.isArray(data.origin) ? data.origin : [data.origin]; + // include some defaults from Global Payments' sandbox + allowedOrigins.push(...['https://api.sandbox.globalpay-ecommerce.com']); + if (!allowedOrigins.includes(e.origin)) { + return; + } + } + ensureIframeClosed(data.timeout || 0); resolve(e.data); };