import React, { useCallback, useState, useEffect, useMemo, useRef } from 'react'; import { useDispatch } from 'react-redux'; import { useAppSelector } from '@akinon/next/redux/hooks'; import { Icon } from '@akinon/next/components'; import { getPosError } from '@akinon/next/utils'; import { useMasterpassAccount } from '../hooks/useMasterpassAccount'; import { useMasterpassPayment } from '../hooks/useMasterpassPayment'; import { useMasterpassScript } from '../hooks/useMasterpassScript'; import { useSetMasterpassRestBinNumberMutation } from '../redux/api'; import { setLastCheckedBin, clearNewCardFormData, setError, clearAllErrors, setLocale, setCurrency } from '../redux/reducer'; import { getPaymentPending, clearPaymentPending } from '../utils/payment-utils'; import type { CreditCardFormData } from '../utils/validation-schemas'; import type { MasterpassRestOptionCustomRender, MasterpassRestOptionTexts } from '../types/custom-render.types'; import type { MasterpassEnvironment } from '../utils/constants'; import LinkModal from '../components/link-modal'; import OTPModal from '../components/otp-modal'; import CardList from '../components/card-list'; import ConfirmationModal from '../components/confirmation-modal'; import InstallmentList from '../components/installment-list'; import CreditCardForm from '../components/credit-card-form'; import PaymentMethodSelector from '../components/payment-method-selector'; import InformationModal from '../components/information-modal'; import RewardSelectionModal from '../components/reward-selection-modal'; import type { RewardItem } from '../types/payment.types'; import mpBlackLogo from '../assets/img/masterpass-black-logo.png'; interface MasterpassRestOptionProps { locale?: string; currency?: string; sdkUrl?: string; environment?: MasterpassEnvironment; texts?: MasterpassRestOptionTexts; customRender?: MasterpassRestOptionCustomRender; enableRewards?: boolean; } const defaultTexts: MasterpassRestOptionTexts = { title: 'Masterpass Payment', newCardTitle: 'New Card Payment', selectCardTitle: 'Select a Card', installmentOptionsTitle: 'Installment Options', enterCardDetailsTitle: 'Enter Card Details', loadingMessage: 'Initializing payment system...', scriptLoadingMessage: 'Loading payment provider...', selectCardMessage: 'Select a card to see installment options', enterCardMessage: 'Enter your card number to see installment options', enterCardSubMessage: 'Installment options will appear automatically as you type', enterDigitsMessage: 'Enter at least 6 digits to see installment options', noInstallmentMessage: 'No installment options available for this card', loadingInstallmentMessage: 'Loading installment options...', removeCardMessage: 'Are you sure you want to remove <{cardAlias}> card from Masterpass infrastructure?', removeCardConfirmText: 'Yes', removeCardCancelText: 'No', removeCardLoadingText: 'Removing...', dismissButtonText: 'Dismiss', paymentErrorTitle: 'Payment Error', paymentErrorDefaultMessage: 'An error occurred during payment processing.', paymentMethodTitle: 'Payment Method', paymentMethodDescription: "Choose how you'd like to pay", savedCardsText: 'Saved Cards', savedCardsDescription: 'Use one of your saved cards ({count} available)', newCardText: 'New Card', newCardDescription: 'Enter a new card for payment', noPaymentMethodsText: 'No payment methods available', failedToSaveCardText: 'Failed to save card', paymentFailedText: 'Payment failed', otpHandlerNotAvailableText: 'OTP handler not available', failedToSelectCardText: 'Failed to select card', failedToSelectInstallmentText: 'Failed to select installment', cardNumberRequiredText: 'Card number is required', cardNumberInvalidText: 'Please enter a valid card number', expiryDateRequiredText: 'Expiry date is required', expiryDateInvalidText: 'Please enter a valid expiry date (MM/YY)', expiryDateExpiredText: 'Card has expired', cvvRequiredText: 'CVV is required', cvvInvalidText: 'CVV must be 3 or 4 digits', cardholderNameRequiredText: 'Cardholder name is required', cardholderNameTooShortText: 'Name must be at least 2 characters', cardholderNameTooLongText: 'Name must not exceed 50 characters', cardholderNameInvalidText: 'Name can only contain letters and spaces', cardAliasRequiredText: 'Card alias is required when saving card', cardAliasTooShortText: 'Card alias must be at least 2 characters', cardAliasTooLongText: 'Card alias must not exceed 20 characters', creditCardInfoTitle: 'Credit Card Information', checkingCardText: 'Checking card...', cardNumberLabel: 'Card Number', cardNumberPlaceholder: '1234 5678 9012 3456', cardholderNameLabel: 'Cardholder Name', cardholderNamePlaceholder: 'John Doe', expiryDateLabel: 'Expiry Date', expiryDatePlaceholder: 'MM/YY', cvcLabel: 'CVC/CVV', cvcPlaceholder3: '123', cvcPlaceholder4: '1234', saveCardLabel: 'Save this card for future payments', saveCardTermsPrefix: 'I have read the', saveCardTermsLink: 'Terms and Conditions', saveCardTermsSuffix: ', I want to save my card to Masterpass.', masterpassSecurityPrefix: 'Your card information is securely stored in', masterpassSecurityLink: 'Masterpass', masterpassSecuritySuffix: ", Mastercard's digital payment infrastructure.", cardAliasLabel: 'Card Nickname (optional)', cardAliasPlaceholder: 'My Visa Card', addCardButton: 'Add Card', cancelButton: 'Cancel', secureText: 'Secure', cvcHelpText: '{length}-digit security code on the {side} of your card', cvcFrontText: 'front', cvcBackText: 'back', defaultCardText: 'Default', expiresSoonText: 'Expires Soon', cvcCardLabel: 'CVC/CVV', secureCardText: 'Secure', cvcCardHelpText: '{length}-digit security code on the {side} of your card', confirmText: 'Confirm', cancelText: 'Cancel', loadingText: 'Loading...', installmentOptionsText: 'Installment Options', cardTypeLabel: 'Card Type:', singlePaymentText: 'Single Payment', installmentsText: '{count} Installments', noInterestText: 'No Interest', perMonthText: '/month', proceedToPaymentText: 'Proceed to Payment', processingPaymentText: 'Processing Payment...', cvcRequiredText: 'Please enter your CVC to proceed with payment', linkModalDescription: 'Would you like to view the cards saved in your Masterpass account that was created with the phone number ', linkAccountButton: 'Yes, I want it', linkModalCancelButton: 'No, I don\'t want it', whatIsMasterpassLink: 'What is Masterpass?', rtaVerificationTitle: 'Security Verification', rtaVerificationDescription: 'To continue with your transaction, please enter the verification code sent to your phone.', rtaVerificationPlaceholder: 'Verification Code', bankOtpVerificationTitle: 'Bank Authorization Required', bankOtpVerificationDescription: 'Please enter the one-time password sent by your bank to complete the verification.', bankOtpVerificationPlaceholder: 'Bank OTP Code', phoneVerificationTitle: 'Verify Your Phone Number', phoneVerificationDescription: 'Enter the verification code sent to your phone to confirm your Masterpass account.', phoneVerificationPlaceholder: 'SMS Verification Code', cvvVerificationTitle: 'Card Security Code Required', cvvVerificationDescription: 'Please enter the 3-digit security code (CVV) located on the back of your card to continue.', cvvVerificationPlaceholder: 'CVV', cvvVerificationHelperText: 'The CVV is the 3-digit number on the back of your card.', otpVerificationDescription: 'Please enter the verification code sent to your phone.', verifyButton: 'Verify', otpModalCancelButton: 'Cancel', otpModalErrorText: 'An unexpected error occurred. Please try again.', otpModalResendOtpButton: 'Resend OTP', sessionExpiredTitle: 'Session Expired', sessionExpiredMessage: 'Your session has expired due to inactivity. Please restart the process to continue.', sessionExpiredSecondaryMessage: 'For security reasons, sessions are only valid for a limited time.', sessionExpiredButton: 'Start Again', rewardModalTitle: 'Use Card Rewards', rewardModalDescription: 'Select the rewards you want to apply to this order.', rewardModalEmptyMessage: 'No rewards available for this card.', rewardModalConfirmText: 'Apply', rewardModalCancelText: 'Cancel', rewardModalLoadingText: 'Applying...', rewardOpenButtonText: 'Use Rewards', rewardSelectedSummaryText: '{count} reward(s) applied', rewardAvailableSummaryText: '{count} reward(s) available', rewardClearText: 'Clear', rewardCategorySpecialText: 'Special Rewards', rewardCategoryGeneralText: 'General Rewards', rewardFailedToLoadText: 'Failed to load rewards', rewardFailedToSelectText: 'Failed to apply rewards', rewardCappedNoticeText: '{amount} will be applied' }; const mergeTexts = ( customTexts: MasterpassRestOptionTexts, defaultTexts: MasterpassRestOptionTexts ): MasterpassRestOptionTexts => { return { ...defaultTexts, ...customTexts }; }; const MasterpassRestOption: React.FC = ({ locale = 'en', currency = 'usd', sdkUrl, environment, texts = defaultTexts, customRender, enableRewards = false }) => { const mergedTexts = React.useMemo( () => mergeTexts(texts, defaultTexts), [texts] ); const dispatch = useDispatch(); const { paymentState, modalState, accountStatus, lastCheckedBin, isTokenLoading, locale: stateLocale, currency: stateCurrency, newCardFormData, error } = useAppSelector((state) => state.masterpassRest); const [paymentMethod, setPaymentMethod] = useState< 'stored_card' | 'new_card' >('stored_card'); const [cvc, setCvc] = useState(''); const [isProcessingPayment, setIsProcessingPayment] = useState(false); // Initialized during render (not in an effect) so consumers see the real // value before their own mount effects run. const [isPaymentPending, setIsPaymentPending] = useState(() => typeof window === 'undefined' ? false : Boolean(getPaymentPending()) ); const dismissPaymentPending = useCallback(() => { clearPaymentPending(); setIsPaymentPending(false); }, []); const [setMasterpassRestBinNumber] = useSetMasterpassRestBinNumberMutation(); const { isCheckoutLoading, isInstallmentLoading, isPrepareLoading, isFinalizeLoading, isRewardsQueryLoading, isRewardsSelectLoading, updatePaymentState, handleCardSelect, handleInstallmentSelect, processPayment, processDirectPayment, finalizePaymentAfterVerification, openRewardModal, closeRewardModal, confirmRewards, payableAmount } = useMasterpassPayment({ enableRewards }); const { accountData, updateModalState, handleLinkConfirm, handleOTPSubmit, handleRemoveCard, confirmRemoveCard, handleAddCard, resetData } = useMasterpassAccount(); const onScriptError = useCallback( (error: Error) => { dispatch( setError( 'Failed to load payment provider script. Please try again later.' ) ); }, [dispatch] ); const { isScriptLoaded } = useMasterpassScript({ locale: stateLocale || locale, sdkUrl, environment, onScriptError }); useEffect(() => { if (locale !== stateLocale) { dispatch(setLocale(locale)); } if (currency !== stateCurrency) { dispatch(setCurrency(currency)); } }, [locale, currency, stateLocale, stateCurrency, dispatch]); useEffect(() => { const hasCards = accountData?.result?.cards && accountData.result.cards.length > 0; if (accountStatus?.isAccountNotLinked || accountStatus?.shouldShowDirectForm) { setPaymentMethod('new_card'); } else if (hasCards) { setPaymentMethod('stored_card'); } else { setPaymentMethod('new_card'); } }, [accountStatus?.isAccountNotLinked, accountStatus?.shouldShowDirectForm, accountData?.result?.cards]); const handleBinChange = useCallback( async (bin: string) => { if (bin === lastCheckedBin) { return; } dispatch(setLastCheckedBin(bin)); try { const apiPayload = { is_masterpass_stored_card: false, bin_number: bin }; const result = await setMasterpassRestBinNumber(apiPayload).unwrap(); if (result.context_list?.[0]?.page_context) { const pageContext = result.context_list[0].page_context; updatePaymentState({ installments: pageContext.installments || [], cardType: pageContext.card_type }); } else { updatePaymentState({ installments: [], cardType: null }); } } catch (error) { updatePaymentState({ installments: [], cardType: null }); } }, [setMasterpassRestBinNumber, lastCheckedBin, dispatch, updatePaymentState] ); useEffect(() => { const error = getPosError(); if (error && Object.keys(error).length > 0) { dispatch(setError(error)); // The completion request already ran and failed, so the order number it // was bound to is settled and the flow may restart from scratch. dismissPaymentPending(); } return () => { resetData(); dispatch(clearAllErrors()); }; }, [dispatch, resetData, dismissPaymentPending]); const getErrorInfo = (error: any) => { if (error && typeof error === 'object') { if (error.non_field_errors) { return { message: error.non_field_errors, type: mergedTexts.paymentErrorTitle }; } if (error.three_d_secure) { return { message: error.three_d_secure, type: '3D Secure Error' }; } const errorKeys = Object.keys(error); if (errorKeys.length > 0) { const firstKey = errorKeys[0]; return { message: error[firstKey], type: firstKey .replace(/_/g, ' ') .replace(/\b\w/g, (l) => l.toUpperCase()) }; } } if (typeof error === 'string') { return { message: error, type: mergedTexts.paymentErrorTitle }; } return null; }; const handleSaveCard = useCallback(async (cardData: CreditCardFormData) => { try { dispatch(clearAllErrors()); const addCardResult = await handleAddCard({ cardNumber: cardData.cardNumber, cardholderName: cardData.cardholderName, expiryDate: cardData.expiryDate, cvv: cardData.cvv, cardAlias: cardData.cardAlias || '' }); if ( !addCardResult.success && !addCardResult.requires3D && !addCardResult.requiresOTP ) { dispatch( setError(addCardResult.message || mergedTexts.failedToSaveCardText) ); return; } } catch (error) { dispatch(setError(error.message || mergedTexts.failedToSaveCardText)); } }, [dispatch, handleAddCard, mergedTexts.failedToSaveCardText]); const handleProceedToPayment = useCallback(async () => { if (isProcessingPayment) return; setIsProcessingPayment(true); try { dispatch(clearAllErrors()); const result = paymentMethod === 'new_card' ? await processDirectPayment() : await processPayment(); if (result.requiresRedirect) { return; } if (result.requiresOTP && result.otpType) { updateModalState({ showOTPModal: true, otpType: result.otpType, otpContext: 'payment' }); return; } if (!result.success && result.message) { dispatch(setError(result.message)); } } catch (error) { const errorMessage = error instanceof Error ? error.message : mergedTexts.paymentFailedText; dispatch(setError(errorMessage)); } finally { setIsProcessingPayment(false); } }, [isProcessingPayment, dispatch, paymentMethod, processDirectPayment, processPayment, updateModalState, mergedTexts.paymentFailedText]); const handleOTPSubmitWithErrorHandling = useCallback(async (otp: string) => { if (!handleOTPSubmit) return { success: false, message: mergedTexts.otpHandlerNotAvailableText }; const result = await handleOTPSubmit(otp, mergedTexts); // A payment-context OTP leaves the Masterpass transaction open; the backend // only learns about it once the completion request is sent. if (result?.success && result?.isPaymentContext) { const finalizeResult = await finalizePaymentAfterVerification( result.verificationData ); if (!finalizeResult.success) { dispatch( setError(finalizeResult.message || mergedTexts.paymentFailedText) ); } return finalizeResult; } return result; }, [ handleOTPSubmit, finalizePaymentAfterVerification, dispatch, mergedTexts ]); const handleCardSelectWithErrorHandling = useCallback(async (card: any) => { try { dispatch(clearAllErrors()); dismissPaymentPending(); await handleCardSelect(card); } catch (error) { const errorMessage = error instanceof Error ? error.message : mergedTexts.failedToSelectCardText; dispatch(setError(errorMessage)); } }, [dispatch, dismissPaymentPending, handleCardSelect, mergedTexts.failedToSelectCardText]); const handleInstallmentSelectWithErrorHandling = useCallback(async (installment: any) => { try { dispatch(clearAllErrors()); dismissPaymentPending(); await handleInstallmentSelect(installment); } catch (error) { const errorMessage = error instanceof Error ? error.message : mergedTexts.failedToSelectInstallmentText; dispatch(setError(errorMessage)); } }, [dispatch, dismissPaymentPending, handleInstallmentSelect, mergedTexts.failedToSelectInstallmentText]); const handleConfirmRewards = useCallback( async (selected: RewardItem[]) => { const result = await confirmRewards(selected); if (!result.success) { dispatch( setError(result.message || mergedTexts.rewardFailedToSelectText) ); } }, [confirmRewards, dispatch, mergedTexts.rewardFailedToSelectText] ); const handleCvcChange = useCallback((newCvc: string) => { setCvc(newCvc); updatePaymentState({ cvc: newCvc }); }, [updatePaymentState]); const handlePaymentMethodChange = useCallback((method: 'stored_card' | 'new_card') => { dismissPaymentPending(); setPaymentMethod(method); updatePaymentState({ selectedCard: null, selectedInstallment: null, installments: [], cardType: null, cvc: '' }); setCvc(''); dispatch(setLastCheckedBin('')); dispatch(clearNewCardFormData()); }, [dispatch, dismissPaymentPending, updatePaymentState]); const customRenderRef = useRef(customRender); customRenderRef.current = customRender; const memoizedComponents = useMemo(() => { const wrapRenderFunction =

( renderFn?: (props: P) => React.ReactElement ): React.ComponentType

| undefined => { if (!renderFn) return undefined; return renderFn as React.ComponentType

; }; return { PaymentMethodSelector: wrapRenderFunction(customRenderRef.current?.paymentMethodSelector) || PaymentMethodSelector, CardList: wrapRenderFunction(customRenderRef.current?.cardList) || CardList, CreditCardForm: wrapRenderFunction(customRenderRef.current?.creditCardForm) || CreditCardForm, InstallmentList: wrapRenderFunction(customRenderRef.current?.installmentList) || InstallmentList, LinkModal: wrapRenderFunction(customRenderRef.current?.linkModal) || LinkModal, OTPModal: wrapRenderFunction(customRenderRef.current?.otpModal) || OTPModal, ConfirmationModal: wrapRenderFunction(customRenderRef.current?.confirmationModal) || ConfirmationModal, RewardSelectionModal: wrapRenderFunction(customRenderRef.current?.rewardSelectionModal) || RewardSelectionModal }; }, [ customRender?.paymentMethodSelector, customRender?.cardList, customRender?.creditCardForm, customRender?.installmentList, customRender?.linkModal, customRender?.otpModal, customRender?.confirmationModal, customRender?.rewardSelectionModal ]); const hasStoredCards = accountData?.result?.cards && accountData.result.cards.length > 0; const shouldShowDirectForm = (accountStatus?.shouldShowDirectForm ?? false) || !accountData || !accountData.result?.cards || accountData.result.cards.length === 0; const onCloseError = useCallback(() => dispatch(clearAllErrors()), [dispatch]); const fallbackOTPSubmit = useCallback(async () => ({ success: false }), []); const fullRenderProps = useMemo(() => ({ paymentMethod, hasStoredCards, shouldShowDirectForm, cvc, error, isPaymentPending, dismissPaymentPending, accountData, accountStatus, modalState, paymentState, handlePaymentMethodChange, handleCardSelect: handleCardSelectWithErrorHandling, updateModalState, handleRemoveCard, confirmRemoveCard, handleCvcChange, handleSaveCard, handleBinChange, handleInstallmentSelect: handleInstallmentSelectWithErrorHandling, handleProceedToPayment, handleLinkConfirm, handleOTPSubmit: handleOTPSubmitWithErrorHandling || handleOTPSubmit || fallbackOTPSubmit, onCloseError, isCheckoutLoading, isInstallmentLoading, isPrepareLoading, isFinalizeLoading, isProcessingPayment, isRewardsQueryLoading, isRewardsSelectLoading, openRewardModal, closeRewardModal, handleConfirmRewards, payableAmount, texts: mergedTexts, components: memoizedComponents }), [ paymentMethod, hasStoredCards, shouldShowDirectForm, cvc, error, isPaymentPending, dismissPaymentPending, accountData, accountStatus, modalState, paymentState, handlePaymentMethodChange, handleCardSelectWithErrorHandling, updateModalState, handleRemoveCard, confirmRemoveCard, handleCvcChange, handleSaveCard, handleBinChange, handleInstallmentSelectWithErrorHandling, handleProceedToPayment, handleLinkConfirm, handleOTPSubmit, fallbackOTPSubmit, onCloseError, isCheckoutLoading, isInstallmentLoading, isPrepareLoading, isFinalizeLoading, isProcessingPayment, isRewardsQueryLoading, isRewardsSelectLoading, openRewardModal, closeRewardModal, handleConfirmRewards, payableAmount, mergedTexts, memoizedComponents ]); if (isTokenLoading || !accountStatus || !modalState) { if (customRender?.loadingState) { return customRender.loadingState({ message: mergedTexts.loadingMessage }); } return (

{mergedTexts.loadingMessage}

); } if (!isScriptLoaded) { if (customRender?.loadingState) { return customRender.loadingState({ message: mergedTexts.scriptLoadingMessage }); } return (

{mergedTexts.scriptLoadingMessage}

); } if (customRender?.fullRender) { return customRender.fullRender(fullRenderProps); } return (
{hasStoredCards && !shouldShowDirectForm && (
{customRender?.paymentMethodSelector ? ( customRender.paymentMethodSelector({ cards: accountData.result.cards, selectedMethod: paymentMethod, onMethodChange: handlePaymentMethodChange, texts: mergedTexts }) ) : ( )}
)}
{paymentMethod === 'stored_card' && hasStoredCards && !shouldShowDirectForm ? ( <>

{mergedTexts.selectCardTitle}

{customRender?.cardList ? ( customRender.cardList({ cards: accountData.result.cards, onCardSelect: handleCardSelectWithErrorHandling, selectedCard: paymentState.selectedCard, onRemove: handleRemoveCard || (() => {}), removingCardId: modalState?.removingCardId, cvc: cvc, onCvcChange: handleCvcChange, ...(enableRewards ? { availableRewards: paymentState.availableRewards, selectedRewards: paymentState.selectedRewards, isLoadingRewards: paymentState.isLoadingRewards || isRewardsQueryLoading, isConfirmingRewards: isRewardsSelectLoading, onOpenRewardModal: openRewardModal, onConfirmRewards: handleConfirmRewards, rewardCurrency: stateCurrency || currency, rewardPayableAmount: payableAmount } : {}), texts: mergedTexts }) ) : ( {})} removingCardId={modalState?.removingCardId} cvc={cvc} onCvcChange={handleCvcChange} {...(enableRewards ? { availableRewards: paymentState.availableRewards, selectedRewards: paymentState.selectedRewards, isLoadingRewards: paymentState.isLoadingRewards || isRewardsQueryLoading, isConfirmingRewards: isRewardsSelectLoading, onOpenRewardModal: openRewardModal, onConfirmRewards: handleConfirmRewards, rewardCurrency: stateCurrency || currency, rewardPayableAmount: payableAmount } : {})} texts={mergedTexts} /> )} ) : ( <>

{shouldShowDirectForm ? mergedTexts.enterCardDetailsTitle : mergedTexts.newCardTitle}

{customRender?.creditCardForm ? ( customRender.creditCardForm({ onSaveCard: handleSaveCard, isLoading: isCheckoutLoading, showSaveOption: !accountStatus?.isAccountNotLinked, onBinChange: handleBinChange, texts: mergedTexts }) ) : ( )} )}

{mergedTexts.installmentOptionsTitle}

{paymentState?.installments?.length > 0 && paymentState?.cardType ? ( customRender?.installmentList ? ( customRender.installmentList({ installments: paymentState.installments, cardType: paymentState.cardType, onInstallmentSelect: handleInstallmentSelectWithErrorHandling, selectedInstallment: paymentState.selectedInstallment, isLoading: isInstallmentLoading || isPrepareLoading, onProceedToPayment: handleProceedToPayment, paymentLoading: isProcessingPayment || isPrepareLoading || isFinalizeLoading, texts: mergedTexts }) ) : ( ) ) : (
{isCheckoutLoading ? (

{mergedTexts.loadingInstallmentMessage}

) : customRender?.emptyState ? ( customRender.emptyState({ paymentMethod, cardNumberLength: newCardFormData?.cardNumber?.replace(/\D/g, '').length || 0, isCheckoutLoading }) ) : (
{(() => { if (paymentMethod === 'stored_card') { return (

{mergedTexts.selectCardMessage}

); } const cardNumberLength = newCardFormData?.cardNumber?.replace(/\D/g, '').length || 0; if (cardNumberLength === 0) { return ( <>

{mergedTexts.enterCardMessage}

{mergedTexts.enterCardSubMessage}

); } else if (cardNumberLength < 6) { return (

{mergedTexts.enterDigitsMessage}

); } else { return (

{mergedTexts.noInstallmentMessage}

); } })()}
)}
)}
{error && (customRender?.errorDisplay ? ( customRender.errorDisplay({ error, onDismiss: () => dispatch(clearAllErrors()), getErrorInfo }) ) : (

{getErrorInfo(error)?.type || mergedTexts.paymentErrorTitle}

{getErrorInfo(error)?.message || mergedTexts.paymentErrorDefaultMessage}

))} {customRender?.linkModal ? ( customRender.linkModal({ open: modalState?.showLinkModal ?? false, onClose: () => updateModalState({ showLinkModal: false }), onConfirm: handleLinkConfirm || (() => {}), texts: mergedTexts }) ) : ( updateModalState({ showLinkModal: false })} onConfirm={handleLinkConfirm || (() => {})} texts={mergedTexts} /> )} {customRender?.otpModal ? ( customRender.otpModal({ open: modalState?.showOTPModal ?? false, onClose: () => updateModalState({ showOTPModal: false }), onSubmit: handleOTPSubmitWithErrorHandling, type: modalState?.otpType ?? 'OTP', responseCode: modalState?.verificationData?.result?.responseCode, description: modalState?.verificationData?.result?.description, texts: mergedTexts }) ) : ( updateModalState({ showOTPModal: false })} onSubmit={handleOTPSubmitWithErrorHandling} type={modalState?.otpType ?? 'OTP'} responseCode={modalState?.verificationData?.result?.responseCode} description={modalState?.verificationData?.result?.description} texts={mergedTexts} /> )} {customRender?.confirmationModal ? ( customRender.confirmationModal({ open: !!modalState?.cardToDelete, onClose: () => updateModalState({ cardToDelete: null }), onConfirm: confirmRemoveCard || (() => {}), title: Masterpass, message: mergedTexts.removeCardMessage.replace( '{cardAlias}', modalState?.cardToDelete?.cardAlias || '' ), confirmText: mergedTexts.removeCardConfirmText, cancelText: mergedTexts.removeCardCancelText, isLoading: !!modalState?.removingCardId, loadingText: mergedTexts.removeCardLoadingText, texts: mergedTexts }) ) : ( updateModalState({ cardToDelete: null })} onConfirm={confirmRemoveCard || (() => {})} title={Masterpass} message={mergedTexts.removeCardMessage.replace( '{cardAlias}', modalState?.cardToDelete?.cardAlias || '' )} confirmText={mergedTexts.removeCardConfirmText} cancelText={mergedTexts.removeCardCancelText} isLoading={!!modalState?.removingCardId} loadingText={mergedTexts.removeCardLoadingText} texts={mergedTexts} /> )} updateModalState({ showInformationModal: false, informationModalData: null })} data={modalState?.informationModalData} /> {enableRewards && (customRender?.rewardSelectionModal ? ( customRender.rewardSelectionModal({ open: modalState?.showRewardModal ?? false, onClose: closeRewardModal, onConfirm: handleConfirmRewards, rewards: paymentState.availableRewards, selectedRewards: paymentState.selectedRewards, isLoading: isRewardsSelectLoading, currency: stateCurrency || currency, payableAmount, texts: mergedTexts }) ) : ( ))}
); }; export default MasterpassRestOption;