From 63b0732d154f7235d8238271586f148a92dd50f3 Mon Sep 17 00:00:00 2001 From: PavlenkoM Date: Thu, 7 Aug 2025 12:19:40 +0300 Subject: [PATCH] feat(payment): Stripe OCS ACH confirmation modal --- .../src/stripe-ocs/StripeOCSPaymentMethod.tsx | 62 ++++++++++++++++++- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/packages/stripe-integration/src/stripe-ocs/StripeOCSPaymentMethod.tsx b/packages/stripe-integration/src/stripe-ocs/StripeOCSPaymentMethod.tsx index a137ee0ee5..fdd69508b1 100644 --- a/packages/stripe-integration/src/stripe-ocs/StripeOCSPaymentMethod.tsx +++ b/packages/stripe-integration/src/stripe-ocs/StripeOCSPaymentMethod.tsx @@ -1,6 +1,14 @@ import { PaymentInitializeOptions } from '@bigcommerce/checkout-sdk'; import { noop, some } from 'lodash'; -import React, { FunctionComponent, useCallback, useContext, useEffect, useRef } from 'react'; +import React, { + createRef, + FunctionComponent, + useCallback, + useContext, + useEffect, + useRef, + useState, +} from 'react'; import { HostedWidgetPaymentComponent } from '@bigcommerce/checkout/hosted-widget-integration'; import { @@ -12,10 +20,15 @@ import { PaymentMethodResolveId, toResolvableComponent, } from '@bigcommerce/checkout/payment-integration-api'; -import { AccordionContext } from '@bigcommerce/checkout/ui'; +import { AccordionContext, LoadingOverlay, Modal } from '@bigcommerce/checkout/ui'; import { getAppearanceForOCSElement, getFonts } from './getStripeOCSStyles'; +interface StripeConfirmationModalRef { + contentRef: React.RefObject; + cancelPaymentConfirmation?: () => void; +} + const StripeOCSPaymentMethod: FunctionComponent = ({ paymentForm, checkoutState, @@ -25,6 +38,13 @@ const StripeOCSPaymentMethod: FunctionComponent = ({ ...rest }) => { const collapseStripeElement = useRef<() => void>(); + const confirmationModalRef = useRef({ + contentRef: createRef(), + cancelPaymentConfirmation: noop, + }); + const [isLoadingIframe, setIsLoadingIframe] = useState(false); + const [paymentConfirmationPageContent, setPaymentConfirmationPageContent] = + useState(); const { onToggle, selectedItemId } = useContext(AccordionContext); const methodSelector = `${method.gateway}-${method.id}`; const containerId = `${methodSelector}-component-field`; @@ -57,6 +77,26 @@ const StripeOCSPaymentMethod: FunctionComponent = ({ } = checkoutState; const checkout = getCheckout(); + const cancelConfirmationModalFlow = useCallback(() => { + setPaymentConfirmationPageContent(undefined); + + if (confirmationModalRef.current.cancelPaymentConfirmation) { + confirmationModalRef.current.cancelPaymentConfirmation(); + confirmationModalRef.current.cancelPaymentConfirmation = undefined; + } + }, []); + + const appendPaymentPageContent = useCallback(() => { + if (confirmationModalRef.current.contentRef.current && paymentConfirmationPageContent) { + paymentConfirmationPageContent.addEventListener('load', () => { + setIsLoadingIframe(false); + }); + confirmationModalRef.current.contentRef.current.appendChild( + paymentConfirmationPageContent, + ); + } + }, [paymentConfirmationPageContent]); + const initializeStripePayment = useCallback( async (options: PaymentInitializeOptions) => { return checkoutService.initializePayment({ @@ -79,6 +119,11 @@ const StripeOCSPaymentMethod: FunctionComponent = ({ handleClosePaymentMethod: (collapseElement: () => void) => { collapseStripeElement.current = collapseElement; }, + loadConfirmationIframe(content: HTMLIFrameElement, cancel: () => void) { + setPaymentConfirmationPageContent(content); + setIsLoadingIframe(true); + confirmationModalRef.current.cancelPaymentConfirmation = cancel; + }, }, }); }, @@ -175,7 +220,20 @@ const StripeOCSPaymentMethod: FunctionComponent = ({ setValidationSchema={setValidationSchema} signOut={checkoutService.signOutCustomer} /> + {renderCheckoutThemeStylesForStripeOCS()} + + + +
+ + ); };