diff --git a/docs/apm-ui-system.md b/docs/apm-ui-system.md index 783f1316..50b86e52 100644 --- a/docs/apm-ui-system.md +++ b/docs/apm-ui-system.md @@ -574,14 +574,6 @@ const apm = client.apm.authorization(container, { timeout: 900, // Timeout in seconds (15 minutes) allowCancelation: true // Allow cancellation during confirmation }, - - // Redirect step (web only): optional keys match Context.ts FlowData.redirect (see sdks-embedded-components.md) - redirect: { - enableHeadlessMode: false, - // silentFailureView: false, - // showHeadlessLoader: true, - // actionOverlayMountParent: document.getElementById('overlay-root'), // HTMLElement | null - }, // Success screen settings success: { diff --git a/package.json b/package.json index 0fbe768b..17a90cd5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "processout.js", - "version": "1.8.8", + "version": "1.8.7", "description": "ProcessOut.js is a JavaScript library for ProcessOut's payment processing API.", "scripts": { "build:processout": "tsc -p src/processout && uglifyjs --compress --keep-fnames --ie8 dist/processout.js -o dist/processout.js", diff --git a/src/apm/Context.ts b/src/apm/Context.ts index 35c9f49f..9ef4adde 100644 --- a/src/apm/Context.ts +++ b/src/apm/Context.ts @@ -39,30 +39,6 @@ module ProcessOut { /** Whether user must take action to dismiss success screen (default: false) */ requiresAction: boolean } - - /** - * Client-side redirect behaviour (web APM). Aligns with mobile - * `RedirectConfiguration.enableHeadlessMode`: skip the intermediate - * “Continue to payment” / Pay button and open the PSP flow as soon as the - * redirect step is shown. - */ - redirect?: { - enableHeadlessMode?: boolean - /** - * When headless, emit `failure` for `handleAction` errors but do not render the in-widget - * error screen (mobile-style; merchant handles UX). Popup-blocked fallback UI is unchanged. - * Default false for backward compatibility. - */ - silentFailureView?: boolean - /** - * When headless, show the loader while opening the PSP. Default true; set false for no in-widget chrome. - */ - showHeadlessLoader?: boolean - /** - * Append `ActionHandler` iframe modal / new-window overlay to this element instead of `document.body`. - */ - actionOverlayMountParent?: HTMLElement | null - } } export type TokenizationUserData = TokenizationFlowData & FlowData diff --git a/src/apm/Page.ts b/src/apm/Page.ts index 0fc6b1c5..99707b72 100644 --- a/src/apm/Page.ts +++ b/src/apm/Page.ts @@ -91,14 +91,11 @@ module ProcessOut { }) } - criticalFailure( - { - title, - code, - message, - }: { message: string, title: string, code?: string, }, - options?: { renderErrorView?: boolean }, - ) { + criticalFailure({ + title, + code, + message, + }: { message: string, title: string, code?: string, }) { ContextImpl.context.events.emit("failure", { failure: { code: code || 'processout-js.internal-error', @@ -107,11 +104,7 @@ module ProcessOut { paymentState: this.state }) - if (options && options.renderErrorView === false) { - return - } - - this.render(APMViewError, { + ContextImpl.context.page.render(APMViewError, { title: title || "Unable to connect", message: message || "An unexpected error occurred. We're working to fix this issue, please check back later or contact support if you need assistance.", hideRefresh: true diff --git a/src/apm/index.ts b/src/apm/index.ts index 1da9b951..42b481f0 100644 --- a/src/apm/index.ts +++ b/src/apm/index.ts @@ -33,17 +33,13 @@ module ProcessOut { requiresAction: false, autoDismissDuration: 3, manualDismissDuration: 60, - ...(data.success || {}), + ...data.success, }, confirmation: { requiresAction: false, timeout: MIN_15 / 1000, allowCancelation: true, - ...(data.confirmation || {}), - }, - redirect: { - enableHeadlessMode: false, - ...(data.redirect || {}), + ...data.confirmation, }, logger: { error: (options: Omit[0], 'stack'>) => { diff --git a/src/apm/views/Redirect.ts b/src/apm/views/Redirect.ts index 310faa62..b83bb521 100644 --- a/src/apm/views/Redirect.ts +++ b/src/apm/views/Redirect.ts @@ -6,95 +6,26 @@ module ProcessOut { } export class APMViewRedirect extends APMViewImpl { - /** After a retryable headless error (e.g. pop-up blocked), show the normal Pay / Cancel UI. */ - private headlessManualFallback = false - - styles = css` - .redirect-headless-loading { - justify-content: center; - align-items: center; - flex-direction: column; - gap: 8px; - min-height: 120px; - } - .redirect-headless-empty { - min-height: 0; - margin: 0; - padding: 0; - overflow: hidden; - } - ` - - protected componentDidMount(): void { - const headless = ContextImpl.context.redirect && ContextImpl.context.redirect.enableHeadlessMode - if (headless && !this.headlessManualFallback) { - this.handleRedirectClick() - } - } - handleRedirectClick() { ContextImpl.context.events.emit('redirect-initiated') - const pm = this.props.config.payment_method - const actionOptions = pm - ? new ActionHandlerOptions( - pm.gateway_name.toLowerCase(), - pm.logo && pm.logo.light_url ? pm.logo.light_url.raster : undefined, - ) - : new ActionHandlerOptions() - const redir = ContextImpl.context.redirect - if (redir && redir.actionOverlayMountParent != null) { - actionOptions.overlayMountParent = redir.actionOverlayMountParent - } ContextImpl.context.poClient.handleAction( - this.props.config.redirect.url, + this.props.config.redirect.url, () => { ContextImpl.context.events.emit('redirect-completed') ContextImpl.context.page.load(APIImpl.getCurrentStep) }, (err) => { - const failure = { - message: err.message, - code: err.code, - } - const headless = ContextImpl.context.redirect && ContextImpl.context.redirect.enableHeadlessMode - if (headless) { - if (!this.headlessManualFallback && failure.code === 'customer.popup-blocked') { - this.headlessManualFallback = true - this.forceUpdate() - return + ContextImpl.context.events.emit('failure', { + failure: { + message: err.message, + code: err.code } - if (this.headlessManualFallback) { - ContextImpl.context.events.emit('failure', { failure }) - return - } - const silentFailureView = !!(redir && redir.silentFailureView) - ContextImpl.context.page.criticalFailure( - { - title: 'Could not open payment', - code: failure.code, - message: failure.message, - }, - { renderErrorView: !silentFailureView }, - ) - return - } - ContextImpl.context.events.emit('failure', { failure }) - }, - actionOptions, + }) + } ) } render() { - const headless = ContextImpl.context.redirect && ContextImpl.context.redirect.enableHeadlessMode - if (headless && !this.headlessManualFallback) { - const showLoader = - !ContextImpl.context.redirect || ContextImpl.context.redirect.showHeadlessLoader !== false - if (showLoader) { - return page({ className: 'redirect-headless-loading' }, Loader()) - } - return page({ className: 'redirect-headless-empty', 'aria-hidden': 'true' }) - } - const redirectLabel = `Pay ${formatCurrency(this.props.config.invoice.amount, this.props.config.invoice.currency)}`; return ( Main({ diff --git a/src/processout/actionhandler.ts b/src/processout/actionhandler.ts index 4ddbac8b..0ae2b3f1 100644 --- a/src/processout/actionhandler.ts +++ b/src/processout/actionhandler.ts @@ -9,12 +9,10 @@ module ProcessOut { protected element: HTMLElement; protected iframe: HTMLIFrameElement; public closed: boolean; - protected mountParent?: HTMLElement; - constructor(el: HTMLElement, iframe: HTMLIFrameElement, mountParent?: HTMLElement) { + constructor(el: HTMLElement, iframe: HTMLIFrameElement) { this.element = el; this.iframe = iframe; - this.mountParent = mountParent; window.addEventListener("resize", function(event) { var width = Math.max(document.body.clientWidth, @@ -44,7 +42,7 @@ module ProcessOut { document.body.style.overflow = "hidden"; // And finally show the modal to the user - (this.mountParent || document.body).appendChild(this.element); + document.body.appendChild(this.element); } public close() { @@ -82,9 +80,6 @@ module ProcessOut { // gatewayLogo is shown when the action is done on another tab or window public gatewayLogo?: string; - /** When set, iframe modal and new-window overlay are appended here instead of document.body */ - public overlayMountParent?: HTMLElement; - public static ThreeDSChallengeFlow = "three-d-s-challenge-flow"; public static ThreeDSChallengeFlowNoIframe = "three-d-s-challenge-flow-no-iframe"; public static ThreeDSFingerprintFlow = "three-d-s-fingerprint-flow"; @@ -253,11 +248,7 @@ module ProcessOut { iframeWrapper.appendChild(iframe); iframeWrapper.appendChild(buttonWrapper); - this.iframeWrapper = new MockedIFrameWindow( - iframeWrapper, - iframe, - this.options.overlayMountParent, - ); + this.iframeWrapper = new MockedIFrameWindow(iframeWrapper, iframe); button.onclick = function() { this.iframeWrapper.close(); }.bind(this); @@ -473,8 +464,7 @@ module ProcessOut { this.cancel(); }.bind(this), false); - var mount = this.options.overlayMountParent || document.body; - mount.appendChild(ret.topLayer); + document.body.appendChild(ret.topLayer); return ret; }