/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable max-len */ import './style.css' import { ThemeOptions } from '@mui/material' import Alert from '@mui/material/Alert' import CircularProgress from '@mui/material/CircularProgress' import Container from '@mui/material/Container' import { createTheme, ThemeProvider } from '@mui/material/styles' import { CSSProperties } from '@mui/styles' import { Elements, PaymentElement, useElements, useStripe } from '@stripe/react-stripe-js' import { loadStripe, StripeConstructorOptions, StripeElementsOptions, } from '@stripe/stripe-js' import { StripePaymentElementOptions } from '@stripe/stripe-js' import { AxiosError } from 'axios' import _get from 'lodash/get' import _identity from 'lodash/identity' import _isEmpty from 'lodash/isEmpty' import _map from 'lodash/map' import React, { useCallback, useEffect, useMemo, useState } from 'react' import { getPaymentData } from '../../api' import { FEES_STYLES } from '../../constants' import { usePixel } from '../../hooks/usePixel' import { createFixedFloatNormalizer, currencyNormalizerCreator } from '../../normalizers' import { IAddOn, IOrderData, IPaymentField } from '../../types' import { IPaymentPlanConfig, IPaymentPlanConfigCard, } from '../../types/payment-plan-configuration' import { CONFIGS, isBrowser } from '../../utils' import { getQueryVariable } from '../../utils/getQueryVariable' import { Checkbox, Loader } from '../common/index' import TimerWidget from '../timerWidget' import { handlePaymentMiddleWare } from './handlePayment' import { PaymentPlanSection } from './PaymentPlanSection' // Wrapper component to access Stripe hooks inside Elements context const StripeWrapper = ({ options, onStripeReady, onPaymentElementChange, }: { options?: StripePaymentElementOptions; onStripeReady: (stripe: any, elements: any) => void; onPaymentElementChange?: (event: any) => void; }) => { const stripe = useStripe() const elements = useElements() useEffect(() => { if (stripe && elements) { onStripeReady(stripe, elements) } }, [stripe, elements, onStripeReady]) return } export interface IPaymentPage { paymentFields: IPaymentField[]; handlePayment: any; checkoutData: any; formTitle?: string; errorText?: string; onErrorClose?: () => void; onGetPaymentDataSuccess: (value: any) => void; onGetPaymentDataError: (value: AxiosError) => void; onPaymentError: (value: AxiosError, slug?: string) => void; themeOptions?: ThemeOptions & { input?: CSSProperties; checkbox?: CSSProperties; }; elementsOptions?: StripeElementsOptions; paymentElementOptions?: StripePaymentElementOptions; onCountdownFinish?: () => void; enableTimer?: boolean; paymentInfoLabel?: string; orderInfoLabel?: string; displayPaymentButton?: boolean; hidePaymentForm?: boolean; hideFieldsBlock?: boolean; isSinglePageCheckout?: boolean; stripePublishableKey?: string; stripeAccountId?: string; onStripeReady?: (stripe: any, elements: any) => void; onPaymentElementChange?: (event: any) => void; enablePaymentPlan?: boolean; } const initialPaymentPlanConfiguration: IPaymentPlanConfig = { requires_deposit: false, deposit: 0, interval: 0, non_refundable_amount: 0, non_refundable_type: null, has_admin_fee: false, admin_fee: 0, total_installments: 0, price_per_installment: 0, stripe_setup_intent_secret: '', total: 0, saved_card: { last_4_digits: null, stripe_payment_method_id: null, } as IPaymentPlanConfigCard, } const initialOrderValues: IOrderData = { id: '', product_name: '', ticketType: '', quantity: '', price: '', total: '', currency: 'USD', guest_count: '', pay_now: '', add_ons: [] as IAddOn[], cost: '', } const initialReviewValues = { order_details: { id: '', order_hash: '', }, payment_method: { stripe_client_secret: '', stripe_payment_plan_enabled: false, stripe_payment_plan_configuration: {} as any, stripe_publishable_key: '', id: '', name: '', stripeConnectedAccount: '', }, billing_info: {}, event_details: { flagSeatMapAllowed: false, slug: '', }, } export const PaymentContainer = ({ paymentFields = [], handlePayment, formTitle = 'Get Your Tickets', errorText, checkoutData, onErrorClose = _identity, onGetPaymentDataSuccess = _identity, onGetPaymentDataError = _identity, onPaymentError = _identity, themeOptions, elementsOptions, paymentElementOptions, onCountdownFinish = _identity, enableTimer = false, orderInfoLabel = 'Order Review', paymentInfoLabel = 'Order Confirmation', displayPaymentButton = true, hidePaymentForm = false, hideFieldsBlock = false, isSinglePageCheckout = false, stripePublishableKey, stripeAccountId, onStripeReady = _identity, onPaymentElementChange, enablePaymentPlan = true, }: IPaymentPage) => { const [reviewData, setReviewData] = useState(initialReviewValues) const [orderData, setOrderData] = useState(initialOrderValues) const [error, setError] = useState(null) const [paymentIsLoading, setPaymentIsLoading] = useState(false) const [paymentDataIsLoading, setPaymentDataIsLoading] = useState(true) const [currency, setCurrency] = useState('') const [showPaymentPlanSection, setShowPaymentPlanSection] = useState(false) const [paymentPlanIsAvailable, setPaymentPlanIsAvailable] = useState(false) const [paymentPlanConfig, setPaymentPlanConfig] = useState( initialPaymentPlanConfiguration ) const [paymentPlanUseSavedCard, setPaymentPlanUseSavedCard] = useState(true) const showFormTitle = Boolean(formTitle) const showErrorText = Boolean(errorText) const eventId = getQueryVariable('event_id') || _get(reviewData, 'cart[0].product_id') || '' const { hash, total } = checkoutData const isFreeTickets = useMemo( () => (!Number(total) && !Number(orderData.total)) || !Number(orderData.pay_now), [total, orderData] ) const pageUrl = isBrowser ? window.location.href.split('?')[0] : '' usePixel(eventId, { page: 'review', pageUrl }) useEffect(() => { const fetchPaymentData = async () => { try { const paymentDataResponse = await getPaymentData(hash) if (paymentDataResponse.success) { const attributes = paymentDataResponse?.data?.attributes setReviewData(attributes) const { cart, order_details } = attributes const { tickets: [ticket], } = order_details const orderDataArray = _map(order_details.tickets, item => ({ product_name: cart[0]?.product_name, ticketType: item?.name, quantity: item?.guest_count, price: item?.price, cost: item?.cost, id: item.id, count: item?.quantity, })) const orderData = { id: order_details?.id, product_name: cart[0]?.product_name, ticketType: ticket?.name, quantity: ticket?.quantity, price: ticket?.price, total: order_details?.total, currency: order_details?.currency, add_ons: order_details?.add_ons || [], pay_now: order_details?.pay_now || '', guest_count: order_details?.guest_count || '', debt: order_details?.debt || null, tableTypes: orderDataArray, cost: ticket?.cost, subtotal: order_details?.subtotal, fees: order_details?.fees, } setOrderData(orderData) setCurrency(order_details?.currency) onGetPaymentDataSuccess(paymentDataResponse.data) } } catch (e) { setError(_get(e, 'response.data.message', null)) onGetPaymentDataError((e as Record).response as AxiosError) } finally { setPaymentDataIsLoading(false) } } if (isSinglePageCheckout) { if (!orderData?.total) { setOrderData(current => ({ ...current, pay_now: 1, total: 1 })) setPaymentDataIsLoading(false) } } else { fetchPaymentData() } }, [ orderData, hash, isSinglePageCheckout, onGetPaymentDataError, onGetPaymentDataSuccess, ]) const showPaymentForm = () => { if (hidePaymentForm) { return false } let showPaymentForm = !isFreeTickets if ( paymentPlanIsAvailable && showPaymentPlanSection && !!paymentPlanConfig.saved_card?.stripe_payment_method_id ) { showPaymentForm = !paymentPlanUseSavedCard } return showPaymentForm } const getPublishableKey = () => stripePublishableKey || _get(reviewData, 'payment_method.stripe_publishable_key') const getStripePromise = useCallback(() => { const stripePublishableKey = getPublishableKey() const stripeAccount = stripeAccountId || _get(reviewData, 'payment_method.stripe_connected_account') const options: StripeConstructorOptions = {} if (stripeAccount) { options.stripeAccount = stripeAccount } return loadStripe(stripePublishableKey, options) }, [reviewData, stripePublishableKey]) const themeMui = createTheme(themeOptions) const hasTableTypes = Boolean(Number(orderData.guest_count)) const paymentFieldsData = hasTableTypes ? [ { label: 'Event', id: 'product_name', }, { label: '', id: 'tableTypes', }, { label: 'Add-ons', id: 'add_ons', }, { label: 'Total (incl. fees, card processing and taxes)', id: 'total', normalizer: (value: string, currency: string) => currencyNormalizerCreator( createFixedFloatNormalizer(2)(parseFloat(value)), currency ), }, { label: 'Pay Now', id: 'pay_now', normalizer: (value: string, currency: string) => currencyNormalizerCreator( createFixedFloatNormalizer(2)(parseFloat(value)), currency ), }, { label: 'Pay On Check-in', id: 'debt', normalizer: (value: string, currency: string) => currencyNormalizerCreator( createFixedFloatNormalizer(2)(parseFloat(value)), currency ), }, ] : paymentFields const isTable = orderData?.guest_count useEffect(() => { const paymentMethod = reviewData.payment_method || {} const paymentPlanAvailable = paymentMethod.stripe_payment_plan_enabled && enablePaymentPlan setPaymentPlanIsAvailable(paymentPlanAvailable) if (paymentPlanAvailable) { const paymentPlanConfig = paymentMethod.stripe_payment_plan_configuration || initialPaymentPlanConfiguration setPaymentPlanConfig(paymentPlanConfig) setPaymentPlanUseSavedCard(!!paymentPlanConfig.saved_card?.stripe_payment_method_id) if (isBrowser) { const sessionData = window.localStorage.getItem('paymentConfiguration') const session = sessionData ? JSON.parse(sessionData) : {} if (!!session && session?.orderId == reviewData.order_details?.id) { setPaymentPlanUseSavedCard(session.paymentPlanUseSavedCard ?? false) setShowPaymentPlanSection(session.showPaymentPlanSection ?? false) } } } }, [enablePaymentPlan, reviewData]) useEffect(() => { if (isBrowser && !!orderData?.id) { window.localStorage.setItem( 'paymentConfiguration', JSON.stringify({ paymentPlanUseSavedCard, showPaymentPlanSection, orderId: orderData?.id, }) ) } }, [showPaymentPlanSection, paymentPlanUseSavedCard, orderData?.id]) return (
{enableTimer && ( )} {isSinglePageCheckout ? null : error && ( {error} )} {paymentDataIsLoading && } {!paymentDataIsLoading && ( {showFormTitle &&

{isTable ? 'Get Your Tables' : formTitle}

}
{orderInfoLabel}
{!hideFieldsBlock && (
{_map(paymentFieldsData, field => { const { id, label, className = '', normalizer = _identity } = field let value = orderData[id as keyof IOrderData] || '' let component = null if (field.id === 'add_ons' && _isEmpty(value)) { return false } if ( field.id === 'total' && paymentPlanIsAvailable && showPaymentPlanSection ) { value = '' + paymentPlanConfig.total } if (field.id === 'tableTypes') { const valueArray = value as Array component = (
{_map(valueArray, tableTypeItem => (
Table Type
{tableTypeItem.ticketType}
Number of Tables
{tableTypeItem.count}
Guest Count
{tableTypeItem.quantity}
))}
) } return ( component || (
{label}
{typeof value === 'string' || typeof value === 'number' ? normalizer(value, currency, orderData) : _map(value, item => (
{item.quantity} {' x '} {item.groupName ? item.groupName + ' - ' : ''} {item.name} {' - '} {CONFIGS.FEES_STYLE === FEES_STYLES.TRADITIONAL && currencyNormalizerCreator( createFixedFloatNormalizer(2)( parseFloat(item.price) ), currency ) + ' (incl. fees)'} {CONFIGS.FEES_STYLE === FEES_STYLES.DISPLAY_BOTH && currencyNormalizerCreator( createFixedFloatNormalizer(2)( parseFloat(item.cost) ), currency )} {CONFIGS.FEES_STYLE === FEES_STYLES.FINAL_WITH_BREAKDOWN && currencyNormalizerCreator( createFixedFloatNormalizer(2)( parseFloat(item.price) ), currency )} {' each'}
{CONFIGS.FEES_STYLE === FEES_STYLES.DISPLAY_BOTH && (

{`(${currencyNormalizerCreator( createFixedFloatNormalizer(2)( parseFloat(String(item.price)) ), currency )} with fees)`}

)} {CONFIGS.FEES_STYLE === FEES_STYLES.FINAL_WITH_BREAKDOWN && parseFloat(item.price) - parseFloat(item.cost) > 0 && (

{`(${currencyNormalizerCreator(createFixedFloatNormalizer(2)(parseFloat(item.cost)), currency)} + ${currencyNormalizerCreator(createFixedFloatNormalizer(2)(parseFloat(item.price) - parseFloat(item.cost)), currency)} fee)`}

)}
))}
) ) })}
)} {!isFreeTickets && paymentPlanIsAvailable && (
Click to checkout using Payment Plan } required={true} onChange={() => { setShowPaymentPlanSection(!showPaymentPlanSection) }} checked={showPaymentPlanSection} />
)} {showPaymentPlanSection && ( )} {showPaymentForm() && !!getPublishableKey() ? (
{paymentInfoLabel}
{showErrorText &&

{errorText}

}
{elementsOptions && ( )}
) : displayPaymentButton ? (
) : null}
)}
) }