tf-checkout-react 1.6.6 → 1.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +401 -59
- package/dist/adapters/customFields.d.ts +1 -0
- package/dist/api/checkout.d.ts +2 -0
- package/dist/api/common.d.ts +1 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/preRegistrationComplete.d.ts +1 -1
- package/dist/components/addonsContainer/AddonComponent.d.ts +6 -1
- package/dist/components/addonsContainer/SimpleAddonsContainer.d.ts +17 -0
- package/dist/components/addonsContainer/index.d.ts +6 -1
- package/dist/components/billing-info-container/hooks/index.d.ts +3 -0
- package/dist/components/billing-info-container/hooks/usePaymentContext.d.ts +5 -0
- package/dist/components/billing-info-container/hooks/usePaymentRedirect.d.ts +14 -0
- package/dist/components/billing-info-container/hooks/useStripePayment.d.ts +18 -0
- package/dist/components/billing-info-container/index.d.ts +13 -2
- package/dist/components/billing-info-container/utils.d.ts +26 -1
- package/dist/components/common/DatePickerField.d.ts +7 -1
- package/dist/components/common/PhoneNumberField.d.ts +1 -1
- package/dist/components/confirmationContainer/index.d.ts +4 -1
- package/dist/components/countdown/index.d.ts +1 -1
- package/dist/components/forgotPasswordModal/index.d.ts +2 -1
- package/dist/components/myTicketsContainer/index.d.ts +3 -2
- package/dist/components/orderDetailsContainer/index.d.ts +8 -1
- package/dist/components/paymentContainer/OrderDetails.d.ts +9 -0
- package/dist/components/paymentContainer/handlePayment.d.ts +15 -0
- package/dist/components/paymentContainer/index.d.ts +12 -6
- package/dist/components/preRegistration/FieldsSection.d.ts +7 -1
- package/dist/components/preRegistration/PreRegistrationComplete.d.ts +8 -0
- package/dist/components/preRegistration/constants.d.ts +2 -2
- package/dist/components/preRegistration/index.d.ts +6 -0
- package/dist/components/resetPasswordContainer/index.d.ts +2 -2
- package/dist/components/ticketsContainer/InfoIcon.d.ts +5 -0
- package/dist/components/ticketsContainer/TicketsSection.d.ts +3 -2
- package/dist/components/ticketsContainer/TimeSlotsSection.d.ts +25 -0
- package/dist/components/ticketsContainer/index.d.ts +29 -5
- package/dist/components/timerWidget/index.d.ts +2 -1
- package/dist/constants/index.d.ts +5 -0
- package/dist/index.d.ts +4 -1
- package/dist/tf-checkout-react.cjs.development.js +11284 -9565
- package/dist/tf-checkout-react.cjs.development.js.map +1 -1
- package/dist/tf-checkout-react.cjs.production.min.js +1 -1
- package/dist/tf-checkout-react.cjs.production.min.js.map +1 -1
- package/dist/tf-checkout-react.esm.js +11293 -9577
- package/dist/tf-checkout-react.esm.js.map +1 -1
- package/dist/tf-checkout-styles.css +1 -1
- package/dist/types/add_on.d.ts +1 -0
- package/dist/types/checkoutPageConfigs.d.ts +1 -1
- package/dist/types/order-data.d.ts +3 -1
- package/dist/utils/auth.d.ts +8 -0
- package/dist/utils/createCheckoutDataBodyWithDefaultHolder.d.ts +1 -0
- package/dist/utils/customFields.d.ts +11 -0
- package/dist/utils/getDomain.d.ts +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/setConfigs.d.ts +1 -0
- package/package.json +14 -8
- package/src/adapters/customFields.ts +7 -1
- package/src/api/auth.ts +2 -1
- package/src/api/checkout.ts +9 -4
- package/src/api/common.ts +49 -2
- package/src/api/index.ts +1 -0
- package/src/api/interceptors.ts +7 -23
- package/src/api/preRegistrationComplete.ts +1 -1
- package/src/api/publicRequest.ts +10 -0
- package/src/components/addonsContainer/AddonComponent.tsx +96 -11
- package/src/components/addonsContainer/SimpleAddonsContainer.tsx +420 -0
- package/src/components/addonsContainer/index.tsx +198 -47
- package/src/components/billing-info-container/hooks/index.ts +3 -0
- package/src/components/billing-info-container/hooks/usePaymentContext.ts +22 -0
- package/src/components/billing-info-container/hooks/usePaymentRedirect.ts +147 -0
- package/src/components/billing-info-container/hooks/useStripePayment.ts +121 -0
- package/src/components/billing-info-container/index.tsx +859 -418
- package/src/components/billing-info-container/{utils.ts → utils.tsx} +124 -1
- package/src/components/common/CheckboxField/index.tsx +1 -1
- package/src/components/common/CustomField.tsx +39 -3
- package/src/components/common/DatePickerField.tsx +25 -10
- package/src/components/common/PhoneNumberField.tsx +4 -2
- package/src/components/common/SnackbarAlert.tsx +32 -34
- package/src/components/confirmationContainer/config.ts +3 -3
- package/src/components/confirmationContainer/index.tsx +20 -1
- package/src/components/confirmationContainer/social-buttons.tsx +5 -3
- package/src/components/confirmationContainer/style.css +9 -5
- package/src/components/countdown/index.tsx +22 -22
- package/src/components/delegationsContainer/IssueComponent.tsx +2 -1
- package/src/components/forgotPasswordModal/index.tsx +44 -13
- package/src/components/loginForm/index.tsx +1 -1
- package/src/components/loginModal/index.tsx +19 -27
- package/src/components/loginModal/style.css +3 -1
- package/src/components/myTicketsContainer/index.tsx +13 -9
- package/src/components/orderDetailsContainer/index.tsx +206 -174
- package/src/components/paymentContainer/OrderDetails.tsx +257 -0
- package/src/components/paymentContainer/handlePayment.ts +86 -0
- package/src/components/paymentContainer/index.tsx +299 -259
- package/src/components/paymentContainer/style.css +141 -0
- package/src/components/preRegistration/FieldsSection.tsx +8 -0
- package/src/components/preRegistration/PreRegistrationComplete.tsx +138 -118
- package/src/components/preRegistration/PreRegistrationInformations.tsx +21 -15
- package/src/components/preRegistration/constants.tsx +10 -4
- package/src/components/preRegistration/index.tsx +233 -179
- package/src/components/preRegistration/style.css +3 -0
- package/src/components/registerForm/constants.tsx +3 -1
- package/src/components/registerForm/index.tsx +3 -3
- package/src/components/registerModal/index.tsx +47 -72
- package/src/components/resetPasswordContainer/index.tsx +20 -14
- package/src/components/seatMapContainer/TicketsSection.tsx +2 -2
- package/src/components/signupModal/index.tsx +13 -6
- package/src/components/ticketResale/index.tsx +7 -0
- package/src/components/ticketsContainer/InfoIcon.tsx +35 -0
- package/src/components/ticketsContainer/PromoCodeSection.tsx +34 -28
- package/src/components/ticketsContainer/TicketRow.tsx +1 -1
- package/src/components/ticketsContainer/TicketsSection.tsx +189 -57
- package/src/components/ticketsContainer/TimeSlotsSection.tsx +120 -0
- package/src/components/ticketsContainer/index.tsx +268 -106
- package/src/components/timerWidget/index.tsx +15 -3
- package/src/components/timerWidget/style.css +2 -1
- package/src/constants/index.ts +2 -0
- package/src/env.ts +14 -6
- package/src/hoc/CustomFields/index.tsx +9 -1
- package/src/index.ts +7 -2
- package/src/types/add_on.ts +1 -0
- package/src/types/api/cart.d.ts +8 -0
- package/src/types/api/checkout.d.ts +58 -7
- package/src/types/api/common.d.ts +30 -0
- package/src/types/api/orders.d.ts +19 -3
- package/src/types/api/payment.d.ts +6 -2
- package/src/types/api/preRegistrationComplete.d.ts +2 -2
- package/src/types/checkoutPageConfigs.ts +1 -1
- package/src/types/order-data.ts +3 -1
- package/src/types/pre-registration-complete.d.ts +6 -1
- package/src/utils/auth.ts +32 -0
- package/src/utils/cookies.ts +42 -11
- package/src/utils/createCheckoutDataBodyWithDefaultHolder.ts +3 -1
- package/src/utils/customFields.ts +22 -0
- package/src/utils/getDomain.ts +10 -4
- package/src/utils/index.ts +1 -1
- package/src/utils/setConfigs.ts +3 -1
- package/dist/components/stripePayment/index.d.ts +0 -24
- package/src/components/stripePayment/index.tsx +0 -281
- package/src/components/stripePayment/style.css +0 -60
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1
2
|
import './style.css'
|
|
2
3
|
|
|
3
4
|
import { CircularProgress, ThemeOptions } from '@mui/material'
|
|
@@ -5,6 +6,7 @@ import Backdrop from '@mui/material/Backdrop'
|
|
|
5
6
|
import Button from '@mui/material/Button'
|
|
6
7
|
import { createTheme, ThemeProvider } from '@mui/material/styles'
|
|
7
8
|
import { CSSProperties } from '@mui/styles'
|
|
9
|
+
import { Stripe, StripeElementsOptions } from '@stripe/stripe-js'
|
|
8
10
|
import axios, { AxiosError } from 'axios'
|
|
9
11
|
import { Field, Form, Formik, FormikHelpers, FormikProps, FormikValues } from 'formik'
|
|
10
12
|
import _find from 'lodash/find'
|
|
@@ -16,22 +18,24 @@ import _isEmpty from 'lodash/isEmpty'
|
|
|
16
18
|
import _isEqual from 'lodash/isEqual'
|
|
17
19
|
import _map from 'lodash/map'
|
|
18
20
|
import { nanoid } from 'nanoid'
|
|
19
|
-
import React, { FC, useEffect, useRef, useState } from 'react'
|
|
21
|
+
import React, { FC, useCallback, useEffect, useRef, useState } from 'react'
|
|
20
22
|
|
|
21
23
|
import {
|
|
22
24
|
AttributesConfig,
|
|
23
25
|
getCart,
|
|
24
26
|
getCheckoutPageConfigs,
|
|
25
27
|
getCountries,
|
|
28
|
+
getPaymentData,
|
|
26
29
|
getProfileData,
|
|
27
30
|
getStates,
|
|
28
31
|
postOnCheckout,
|
|
29
|
-
register,
|
|
30
32
|
setCustomHeader,
|
|
31
33
|
} from '../../api'
|
|
34
|
+
import { updateCheckout } from '../../api/checkout'
|
|
32
35
|
import { withCustomFields } from '../../hoc'
|
|
33
36
|
import { usePixel } from '../../hooks/usePixel'
|
|
34
37
|
import { IBillingInfoData } from '../../types'
|
|
38
|
+
import { ICheckoutResponseData } from '../../types/api/checkout'
|
|
35
39
|
import {
|
|
36
40
|
createCheckoutDataBodyWithDefaultHolder,
|
|
37
41
|
deleteCookieByName,
|
|
@@ -40,21 +44,26 @@ import {
|
|
|
40
44
|
setLoggedUserData,
|
|
41
45
|
} from '../../utils'
|
|
42
46
|
import { ErrorFocus } from '../../utils/formikErrorFocus'
|
|
47
|
+
import { AddonsContainter, IAddonContainterProps } from '../addonsContainer'
|
|
48
|
+
import SimpleAddonsContainer from '../addonsContainer/SimpleAddonsContainer'
|
|
43
49
|
import SnackbarAlert from '../common/SnackbarAlert'
|
|
44
50
|
import { ForgotPasswordModal } from '../forgotPasswordModal'
|
|
45
51
|
import { VerificationPendingModal } from '../idVerificationContainer/VerificationPendingModal'
|
|
46
52
|
import { LoginModal } from '../loginModal'
|
|
53
|
+
import { IPaymentPage, PaymentContainer } from '../paymentContainer'
|
|
47
54
|
import { SignupModal } from '../signupModal'
|
|
48
55
|
import TimerWidget from '../timerWidget'
|
|
56
|
+
import { usePaymentRedirect, useStripePayment } from './hooks'
|
|
49
57
|
import {
|
|
50
58
|
assingUniqueIds,
|
|
51
59
|
createCheckoutDataBody,
|
|
52
|
-
|
|
60
|
+
filterBillingInfoFields,
|
|
53
61
|
getFieldComponent,
|
|
54
62
|
getFieldLabel,
|
|
55
63
|
getInitialValues,
|
|
56
64
|
getValidateFunctions,
|
|
57
65
|
ICheckoutBody,
|
|
66
|
+
renderComponentWithProps,
|
|
58
67
|
} from './utils'
|
|
59
68
|
|
|
60
69
|
export interface IBillingInfoPage {
|
|
@@ -64,7 +73,9 @@ export interface IBillingInfoPage {
|
|
|
64
73
|
values: FormikValues,
|
|
65
74
|
formikHelpers: FormikHelpers<FormikValues>,
|
|
66
75
|
eventId: any,
|
|
67
|
-
res: any
|
|
76
|
+
res: any,
|
|
77
|
+
checkoutUpdateResponse?: any,
|
|
78
|
+
paymentResponse?: any
|
|
68
79
|
) => void;
|
|
69
80
|
onRegisterSuccess?: (value: any) => void;
|
|
70
81
|
onRegisterError?: (e: AxiosError, email: string) => void;
|
|
@@ -82,8 +93,11 @@ export interface IBillingInfoPage {
|
|
|
82
93
|
onLogin?: () => void;
|
|
83
94
|
onLoginSuccess?: () => void;
|
|
84
95
|
onErrorClose?: () => void;
|
|
96
|
+
onCheckoutUpdateSuccess?: (res: any) => void;
|
|
97
|
+
onCheckoutUpdateError?: (e: AxiosError) => void;
|
|
85
98
|
initialValues?: FormikValues;
|
|
86
99
|
buttonName?: string;
|
|
100
|
+
freeOrderButtonName?: string;
|
|
87
101
|
theme?: 'light' | 'dark';
|
|
88
102
|
isLoggedIn?: boolean;
|
|
89
103
|
accountInfoTitle?: string | JSX.Element;
|
|
@@ -110,11 +124,18 @@ export interface IBillingInfoPage {
|
|
|
110
124
|
customFieldsOrderKeys?: string[];
|
|
111
125
|
customFieldsTicketHolderKeys?: string[];
|
|
112
126
|
onPendingVerification?: () => void;
|
|
127
|
+
includeAddons?: boolean;
|
|
128
|
+
addonsProps?: IAddonContainterProps;
|
|
129
|
+
addOnDataWithCustomFields?: any;
|
|
130
|
+
isSinglePageCheckout?: boolean;
|
|
131
|
+
paymentProps?: Partial<IPaymentPage>;
|
|
132
|
+
paymentSectionAddon?: React.ReactNode;
|
|
113
133
|
}
|
|
114
134
|
|
|
115
135
|
const LogicRunner: FC<{
|
|
116
136
|
brandOptIn?: boolean;
|
|
117
137
|
values: any;
|
|
138
|
+
errors: any;
|
|
118
139
|
setStates: React.Dispatch<any>;
|
|
119
140
|
setFieldValue: any;
|
|
120
141
|
setValues: any;
|
|
@@ -133,72 +154,115 @@ const LogicRunner: FC<{
|
|
|
133
154
|
shouldFetchCountries,
|
|
134
155
|
brandOptIn,
|
|
135
156
|
}) => {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
157
|
+
const prevCountry = useRef(values.country)
|
|
158
|
+
const prevBuyerData = useRef({
|
|
159
|
+
firstName: '',
|
|
160
|
+
lastName: '',
|
|
161
|
+
email: '',
|
|
162
|
+
phone: '',
|
|
163
|
+
})
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
const fetchStates = async () => {
|
|
166
|
+
try {
|
|
167
|
+
const res = await getStates(values.country)
|
|
168
|
+
const mappedStates = _map(res.data, (item, key) => ({
|
|
169
|
+
label: item,
|
|
170
|
+
value: key,
|
|
171
|
+
}))
|
|
172
|
+
setStates(mappedStates)
|
|
173
|
+
if (prevCountry.current !== values.country) {
|
|
174
|
+
const stateExists = mappedStates.find(
|
|
175
|
+
state => state.value === values.state
|
|
176
|
+
)?.value
|
|
177
|
+
setFieldValue('state', stateExists ?? mappedStates[0]?.value ?? '')
|
|
178
|
+
prevCountry.current = values.country
|
|
179
|
+
}
|
|
180
|
+
onGetStatesSuccess(res.data)
|
|
181
|
+
} catch (e) {
|
|
182
|
+
if (axios.isAxiosError(e)) {
|
|
183
|
+
onGetStatesError(e)
|
|
158
184
|
}
|
|
159
185
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
useEffect(() => {
|
|
164
|
-
// set user data from local storage
|
|
165
|
-
const getStoredUserData = () => {
|
|
166
|
-
if (isBrowser) {
|
|
167
|
-
if (userDataEncoded) {
|
|
168
|
-
try {
|
|
169
|
-
const parsedData = JSON.parse(userDataEncoded)
|
|
170
|
-
const mappedValues = {
|
|
171
|
-
firstName: parsedData?.first_name || parsedData?.firstName || '',
|
|
172
|
-
lastName: parsedData?.last_name || parsedData?.lastName || '',
|
|
173
|
-
email: parsedData?.email || '',
|
|
174
|
-
phone: parsedData?.phone || '',
|
|
175
|
-
confirmEmail: parsedData?.email || '',
|
|
176
|
-
state: parsedData?.state || '',
|
|
177
|
-
street_address: parsedData?.street_address || '',
|
|
178
|
-
country: parsedData?.country || '1',
|
|
179
|
-
zip: parsedData?.zip || '',
|
|
180
|
-
brand_opt_in: brandOptIn ? brandOptIn : parsedData?.brand_opt_in || false,
|
|
181
|
-
city: parsedData?.city || '',
|
|
182
|
-
confirmPassword: '',
|
|
183
|
-
password: '',
|
|
184
|
-
'holderFirstName-0': parsedData?.first_name || parsedData?.firstName || '',
|
|
185
|
-
'holderLastName-0': parsedData?.last_name || parsedData?.lastName || '',
|
|
186
|
-
'holderEmail-0': parsedData?.email || '',
|
|
187
|
-
}
|
|
186
|
+
}
|
|
187
|
+
shouldFetchCountries && fetchStates()
|
|
188
|
+
}, [values.country, setStates, setFieldValue])
|
|
188
189
|
|
|
189
|
-
|
|
190
|
-
|
|
190
|
+
// Auto-fill first holder with buyer data
|
|
191
|
+
useEffect(() => {
|
|
192
|
+
const buyerFirstName = values.firstName || ''
|
|
193
|
+
const buyerLastName = values.lastName || ''
|
|
194
|
+
const buyerEmail = values.email || ''
|
|
195
|
+
const buyerPhone = values.phone || ''
|
|
191
196
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
197
|
+
// Check if any buyer data has changed
|
|
198
|
+
const firstNameChanged = prevBuyerData.current.firstName !== buyerFirstName
|
|
199
|
+
const lastNameChanged = prevBuyerData.current.lastName !== buyerLastName
|
|
200
|
+
const emailChanged = prevBuyerData.current.email !== buyerEmail
|
|
201
|
+
const phoneChanged = prevBuyerData.current.phone !== buyerPhone
|
|
202
|
+
|
|
203
|
+
// Update holder fields individually based on what changed
|
|
204
|
+
if (firstNameChanged) {
|
|
205
|
+
setFieldValue('holderFirstName-0', buyerFirstName, false)
|
|
206
|
+
prevBuyerData.current.firstName = buyerFirstName
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (lastNameChanged) {
|
|
210
|
+
setFieldValue('holderLastName-0', buyerLastName, false)
|
|
211
|
+
prevBuyerData.current.lastName = buyerLastName
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (emailChanged) {
|
|
215
|
+
setFieldValue('holderEmail-0', buyerEmail, false)
|
|
216
|
+
prevBuyerData.current.email = buyerEmail
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (phoneChanged) {
|
|
220
|
+
setFieldValue('holderPhone-0', buyerPhone, false)
|
|
221
|
+
prevBuyerData.current.phone = buyerPhone
|
|
222
|
+
}
|
|
223
|
+
}, [values.firstName, values.lastName, values.email, values.phone, setFieldValue])
|
|
224
|
+
|
|
225
|
+
const userDataEncoded = isBrowser ? window.localStorage.getItem('user_data') : ''
|
|
226
|
+
useEffect(() => {
|
|
227
|
+
// set user data from local storage
|
|
228
|
+
const getStoredUserData = () => {
|
|
229
|
+
if (isBrowser) {
|
|
230
|
+
if (userDataEncoded) {
|
|
231
|
+
try {
|
|
232
|
+
const parsedData = JSON.parse(userDataEncoded)
|
|
233
|
+
const mappedValues = {
|
|
234
|
+
firstName: parsedData?.first_name || parsedData?.firstName || '',
|
|
235
|
+
lastName: parsedData?.last_name || parsedData?.lastName || '',
|
|
236
|
+
email: parsedData?.email || '',
|
|
237
|
+
phone: parsedData?.phone || '',
|
|
238
|
+
confirmEmail: parsedData?.email || '',
|
|
239
|
+
state: parsedData?.state || '',
|
|
240
|
+
street_address: parsedData?.street_address || '',
|
|
241
|
+
country: parsedData?.country || '1',
|
|
242
|
+
zip: parsedData?.zip || '',
|
|
243
|
+
brand_opt_in: brandOptIn ? brandOptIn : parsedData?.brand_opt_in || false,
|
|
244
|
+
city: parsedData?.city || '',
|
|
245
|
+
confirmPassword: '',
|
|
246
|
+
password: '',
|
|
247
|
+
'holderFirstName-0': parsedData?.first_name || parsedData?.firstName || '',
|
|
248
|
+
'holderLastName-0': parsedData?.last_name || parsedData?.lastName || '',
|
|
249
|
+
'holderEmail-0': parsedData?.email || '',
|
|
250
|
+
'holderPhone-0': parsedData?.phone || '',
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const extraDataJSON = window.localStorage.getItem('extraData')
|
|
254
|
+
const extraData = extraDataJSON ? JSON.parse(extraDataJSON) : null
|
|
255
|
+
|
|
256
|
+
setValues({ ...values, ...mappedValues, ...(extraData ?? {}) })
|
|
257
|
+
setUserValues(mappedValues)
|
|
258
|
+
} catch (e) {}
|
|
196
259
|
}
|
|
197
260
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
261
|
+
}
|
|
262
|
+
getStoredUserData()
|
|
263
|
+
}, [userDataEncoded, setValues, setUserValues, brandOptIn])
|
|
264
|
+
return null
|
|
265
|
+
}
|
|
202
266
|
|
|
203
267
|
const BillingInfoContainer = React.memo(
|
|
204
268
|
({
|
|
@@ -209,6 +273,7 @@ const BillingInfoContainer = React.memo(
|
|
|
209
273
|
},
|
|
210
274
|
initialValues = {},
|
|
211
275
|
buttonName = 'Submit',
|
|
276
|
+
freeOrderButtonName = 'Complete Registration',
|
|
212
277
|
handleSubmit = _identity,
|
|
213
278
|
theme = 'light',
|
|
214
279
|
onRegisterSuccess = _identity,
|
|
@@ -224,6 +289,8 @@ const BillingInfoContainer = React.memo(
|
|
|
224
289
|
onGetProfileDataError = _identity,
|
|
225
290
|
onLogin,
|
|
226
291
|
onLoginSuccess = _identity,
|
|
292
|
+
onCheckoutUpdateSuccess = _identity,
|
|
293
|
+
onCheckoutUpdateError = _identity,
|
|
227
294
|
isLoggedIn: pIsLoggedIn = false,
|
|
228
295
|
accountInfoTitle = '',
|
|
229
296
|
hideLogo,
|
|
@@ -249,12 +316,20 @@ const BillingInfoContainer = React.memo(
|
|
|
249
316
|
onPendingVerification = _identity,
|
|
250
317
|
onGetCheckoutConfigsSuccess = _identity,
|
|
251
318
|
onGetCheckoutConfigsError = _identity,
|
|
319
|
+
includeAddons = false,
|
|
320
|
+
addonsProps,
|
|
321
|
+
addOnDataWithCustomFields,
|
|
322
|
+
isSinglePageCheckout = false,
|
|
323
|
+
paymentProps = {},
|
|
324
|
+
paymentSectionAddon,
|
|
252
325
|
}: IBillingInfoPage) => {
|
|
253
326
|
const [extraData, setExtraData] = useState(null)
|
|
254
327
|
const [isConfigLoading, setIsConfigLoading] = useState(true)
|
|
255
328
|
const [configs, setConfigs] = useState<null | AttributesConfig>(null)
|
|
256
|
-
const
|
|
329
|
+
const isNewUser = false
|
|
257
330
|
const themeMui = createTheme(themeOptions)
|
|
331
|
+
const elementsRef = useRef<any>(null)
|
|
332
|
+
const stripeRef = useRef<Stripe | null>(null)
|
|
258
333
|
|
|
259
334
|
useEffect(() => {
|
|
260
335
|
if (isBrowser) {
|
|
@@ -280,8 +355,15 @@ const BillingInfoContainer = React.memo(
|
|
|
280
355
|
const defaultCountry = isBrowser ? window.localStorage.getItem('eventCountry') : ''
|
|
281
356
|
const userData =
|
|
282
357
|
isBrowser && window.localStorage.getItem('user_data')
|
|
283
|
-
? JSON.parse(window.localStorage.getItem('user_data') || '')
|
|
358
|
+
? JSON.parse(window.localStorage.getItem('user_data') || '{}')
|
|
284
359
|
: {}
|
|
360
|
+
const additionalConfigs =
|
|
361
|
+
isBrowser && window.localStorage.getItem('checkoutAdditionalConfigs')
|
|
362
|
+
? JSON.parse(
|
|
363
|
+
window.localStorage.getItem('checkoutAdditionalConfigs') ||
|
|
364
|
+
'{"resale": false, "resaleWithAddons": false}'
|
|
365
|
+
)
|
|
366
|
+
: { resale: false, resaleWithAddons: false }
|
|
285
367
|
const [dataWithUniqueIds, setDataWithUniqueIds] = useState<IBillingInfoData[]>(
|
|
286
368
|
assingUniqueIds(data)
|
|
287
369
|
)
|
|
@@ -291,7 +373,7 @@ const BillingInfoContainer = React.memo(
|
|
|
291
373
|
const [countries, setCountries] = useState<any>([])
|
|
292
374
|
const [states, setStates] = useState<any>([])
|
|
293
375
|
const [showModalLogin, setShowModalLogin] = useState(false)
|
|
294
|
-
const
|
|
376
|
+
const alreadyHasUser= false
|
|
295
377
|
const [userExpired, setUserExpired] = useState(false)
|
|
296
378
|
const [showModalSignup, setShowModalSignup] = useState(false)
|
|
297
379
|
const [showModalForgotPassword, setShowModalForgotPassword] = useState(false)
|
|
@@ -314,13 +396,14 @@ const BillingInfoContainer = React.memo(
|
|
|
314
396
|
const [loading, setLoading] = useState(true)
|
|
315
397
|
const [cardLoading, setCardLoading] = useState(false)
|
|
316
398
|
const [isCountriesLoading, setIsCountriesLoading] = useState(true)
|
|
317
|
-
const [error, setError] = useState(null)
|
|
399
|
+
const [error, setError] = useState<string | null>(null)
|
|
318
400
|
const [phoneValidationIsLoading, setPhoneValidationIsLoading] = useState(false)
|
|
319
401
|
const emailLogged = _get(userData, 'email', '') || _get(userValues, 'email', '')
|
|
320
402
|
const firstNameLogged =
|
|
321
403
|
_get(userData, 'first_name', '') || _get(userValues, 'first_name', '')
|
|
322
404
|
const lastNameLogged =
|
|
323
405
|
_get(userData, 'last_name', '') || _get(userValues, 'last_name', '')
|
|
406
|
+
const phoneLogged = _get(userData, 'phone', '') || _get(userValues, 'phone', '')
|
|
324
407
|
const showDOB = configs?.age_required
|
|
325
408
|
const showTicketHolders = configs?.names_required
|
|
326
409
|
const eventId = configs?.event_id
|
|
@@ -349,18 +432,27 @@ const BillingInfoContainer = React.memo(
|
|
|
349
432
|
const hideInstagramField = !collectMandatoryInstagram && !collectOptionalInstagram
|
|
350
433
|
const collectMandatoryBusinessCategory = configs?.collect_mandatory_business_category
|
|
351
434
|
const collectOptionalBusinessCategory = configs?.collect_optional_business_category
|
|
435
|
+
const eventHasAddons = configs?.has_add_on
|
|
352
436
|
const hideBusinessCategoryField =
|
|
353
437
|
!collectMandatoryBusinessCategory && !collectOptionalBusinessCategory
|
|
354
438
|
|
|
355
|
-
const [pendingVerificationMessage, setPendingVerificationMessage] = useState()
|
|
356
|
-
|
|
357
|
-
|
|
439
|
+
const [pendingVerificationMessage, setPendingVerificationMessage] = useState<string>()
|
|
440
|
+
const [reviewData, setReviewData] = useState<any>({})
|
|
441
|
+
const [checkoutData, setCheckoutData] = useState<any>({})
|
|
442
|
+
const [checkoutUpdateData, setCheckoutUpdateData] =
|
|
443
|
+
useState<ICheckoutResponseData | null>(null)
|
|
358
444
|
const prevData = useRef(data)
|
|
359
445
|
|
|
360
|
-
const addAddOnsInAttributes = (checkoutBody: any) => {
|
|
446
|
+
const addAddOnsInAttributes = useCallback((checkoutBody: any) => {
|
|
361
447
|
const selectedAddOns = window.localStorage.getItem('add_ons') || '{}'
|
|
448
|
+
const addOnDataCapture = window.localStorage.getItem('add_on_data_capture') || '{}'
|
|
449
|
+
|
|
362
450
|
checkoutBody.attributes.add_ons = JSON.parse(selectedAddOns)
|
|
363
|
-
|
|
451
|
+
checkoutBody.attributes.add_on_data_capture = JSON.parse(addOnDataCapture)
|
|
452
|
+
}, [])
|
|
453
|
+
const [singleCheckoutAddons, setSingleCheckoutAddOns] = useState<any>({})
|
|
454
|
+
|
|
455
|
+
const orderIsFree = !Number(checkoutData?.total)
|
|
364
456
|
|
|
365
457
|
useEffect(() => {
|
|
366
458
|
const hasUniqueId = _get(dataWithUniqueIds, '[0].uniqueId')
|
|
@@ -373,13 +465,13 @@ const BillingInfoContainer = React.memo(
|
|
|
373
465
|
}
|
|
374
466
|
}, [dataWithUniqueIds, data])
|
|
375
467
|
|
|
376
|
-
const getQuantity = (cart: any = []) => {
|
|
468
|
+
const getQuantity = useCallback((cart: any = []) => {
|
|
377
469
|
let qty = 0
|
|
378
470
|
cart.forEach((item: any) => {
|
|
379
471
|
qty += +item.quantity
|
|
380
472
|
})
|
|
381
473
|
return qty
|
|
382
|
-
}
|
|
474
|
+
}, [])
|
|
383
475
|
|
|
384
476
|
useEffect(() => {
|
|
385
477
|
if (pIsLoggedIn !== isLoggedIn || xtfCookie) {
|
|
@@ -407,10 +499,22 @@ const BillingInfoContainer = React.memo(
|
|
|
407
499
|
shouldFetchCountries && fetchCountries()
|
|
408
500
|
fetchCart()
|
|
409
501
|
|
|
502
|
+
// Initialize checkout with event_id on first load
|
|
503
|
+
if (isSinglePageCheckout && eventId) {
|
|
504
|
+
updateCheckoutWithAddOns()
|
|
505
|
+
}
|
|
506
|
+
|
|
410
507
|
return () => {
|
|
411
508
|
isBrowser && localStorage.removeItem('selectedTicketsQuantity')
|
|
412
509
|
}
|
|
413
|
-
|
|
510
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
511
|
+
}, [
|
|
512
|
+
eventId,
|
|
513
|
+
isSinglePageCheckout,
|
|
514
|
+
shouldFetchCountries,
|
|
515
|
+
onGetCountriesSuccess,
|
|
516
|
+
onGetCountriesError,
|
|
517
|
+
])
|
|
414
518
|
|
|
415
519
|
// fetch cart data
|
|
416
520
|
const fetchCart = async () => {
|
|
@@ -459,6 +563,45 @@ const BillingInfoContainer = React.memo(
|
|
|
459
563
|
fetchCart()
|
|
460
564
|
}, [isLoggedIn])
|
|
461
565
|
|
|
566
|
+
useEffect(() => {
|
|
567
|
+
const fetchCheckoutUpdate = async () => {
|
|
568
|
+
if (!eventId) return
|
|
569
|
+
|
|
570
|
+
try {
|
|
571
|
+
const checkoutUpdateResponse = await updateCheckout({
|
|
572
|
+
attributes: {
|
|
573
|
+
event_id: eventId,
|
|
574
|
+
is_from_resale: additionalConfigs?.resale,
|
|
575
|
+
},
|
|
576
|
+
})
|
|
577
|
+
console.log('Stripe in [useEffect] fetchCheckoutUpdate', checkoutUpdateResponse)
|
|
578
|
+
|
|
579
|
+
if (checkoutUpdateResponse.success) {
|
|
580
|
+
const checkoutAttributes = checkoutUpdateResponse.data.attributes
|
|
581
|
+
const cartPriceBreakdown = _get(
|
|
582
|
+
checkoutAttributes,
|
|
583
|
+
'cart_price_breakdown',
|
|
584
|
+
{}
|
|
585
|
+
)
|
|
586
|
+
localStorage.setItem(
|
|
587
|
+
'checkoutData',
|
|
588
|
+
JSON.stringify({ hash: '', total: _get(cartPriceBreakdown, 'total', 0) })
|
|
589
|
+
)
|
|
590
|
+
console.log(
|
|
591
|
+
'Stripe in [useEffect] fetchCheckoutUpdate | checkoutAttributes',
|
|
592
|
+
checkoutAttributes
|
|
593
|
+
)
|
|
594
|
+
setCheckoutUpdateData(checkoutAttributes)
|
|
595
|
+
onCheckoutUpdateSuccess({ expires_at: expirationTime, ...cartPriceBreakdown })
|
|
596
|
+
}
|
|
597
|
+
} catch (error) {
|
|
598
|
+
console.error('Failed to fetch checkout update:', error)
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
fetchCheckoutUpdate()
|
|
603
|
+
}, [eventId, additionalConfigs?.resale])
|
|
604
|
+
|
|
462
605
|
useEffect(() => {
|
|
463
606
|
const collectPaymentData = async () => {
|
|
464
607
|
if (
|
|
@@ -481,12 +624,13 @@ const BillingInfoContainer = React.memo(
|
|
|
481
624
|
|
|
482
625
|
const checkoutResponse = await postOnCheckout(checkoutBody, flagFreeTicket)
|
|
483
626
|
removeReferralKey()
|
|
627
|
+
removeAdditionalConfigs()
|
|
484
628
|
onSkipBillingPage(checkoutResponse.data.attributes)
|
|
485
629
|
setLoading(false)
|
|
486
630
|
} catch (e) {
|
|
487
|
-
onSubmitError(e)
|
|
488
|
-
if (e.
|
|
489
|
-
setPendingVerificationMessage(e.
|
|
631
|
+
onSubmitError(e as AxiosError)
|
|
632
|
+
if (_get(e, 'response.data.data.hasUnverifiedOrder')) {
|
|
633
|
+
setPendingVerificationMessage(_get(e, 'response.data.message'))
|
|
490
634
|
}
|
|
491
635
|
}
|
|
492
636
|
} else {
|
|
@@ -496,91 +640,268 @@ const BillingInfoContainer = React.memo(
|
|
|
496
640
|
collectPaymentData()
|
|
497
641
|
}, [skipPage, ticketsQuantity])
|
|
498
642
|
|
|
499
|
-
const collectCheckoutBody = (
|
|
500
|
-
values: Record<string, any>,
|
|
501
|
-
|
|
502
|
-
): Record<string, any> => {
|
|
503
|
-
let checkoutBody = {} as ICheckoutBody
|
|
504
|
-
|
|
505
|
-
// Auto collect ticket holders name when it was skipped optionally
|
|
506
|
-
if (showDOB && !showTicketHolders && canSkipHolderNames) {
|
|
507
|
-
checkoutBody = createCheckoutDataBodyWithDefaultHolder(
|
|
508
|
-
ticketsQuantity.length,
|
|
509
|
-
values,
|
|
510
|
-
true,
|
|
511
|
-
{ emailLogged, firstNameLogged, lastNameLogged }
|
|
512
|
-
)
|
|
513
|
-
} else {
|
|
514
|
-
checkoutBody = createCheckoutDataBody(
|
|
515
|
-
ticketsQuantity.length,
|
|
516
|
-
values,
|
|
517
|
-
{
|
|
518
|
-
emailLogged: emailLogged || profileData.email,
|
|
519
|
-
firstNameLogged:
|
|
520
|
-
firstNameLogged || profileData.first_name || profileData.firstName,
|
|
521
|
-
lastNameLogged:
|
|
522
|
-
lastNameLogged || profileData.last_name || profileData.lastName,
|
|
523
|
-
},
|
|
524
|
-
showDOB
|
|
525
|
-
)
|
|
526
|
-
}
|
|
643
|
+
const collectCheckoutBody = useCallback(
|
|
644
|
+
(values: Record<string, any>, profileData?: any): Record<string, any> => {
|
|
645
|
+
let checkoutBody = {} as ICheckoutBody
|
|
527
646
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
647
|
+
// Auto collect ticket holders name when it was skipped optionally
|
|
648
|
+
if (showDOB && !showTicketHolders && canSkipHolderNames) {
|
|
649
|
+
checkoutBody = createCheckoutDataBodyWithDefaultHolder(
|
|
650
|
+
ticketsQuantity.length,
|
|
651
|
+
values,
|
|
652
|
+
true,
|
|
653
|
+
{ emailLogged, firstNameLogged, lastNameLogged, phoneLogged }
|
|
654
|
+
)
|
|
655
|
+
} else {
|
|
656
|
+
checkoutBody = createCheckoutDataBody(
|
|
657
|
+
ticketsQuantity.length,
|
|
658
|
+
values,
|
|
659
|
+
{
|
|
660
|
+
emailLogged: emailLogged || profileData.email,
|
|
661
|
+
firstNameLogged:
|
|
662
|
+
firstNameLogged || profileData.first_name || profileData.firstName,
|
|
663
|
+
lastNameLogged:
|
|
664
|
+
lastNameLogged || profileData.last_name || profileData.lastName,
|
|
665
|
+
phoneLogged: phoneLogged || profileData.phone,
|
|
666
|
+
},
|
|
667
|
+
showDOB
|
|
668
|
+
)
|
|
669
|
+
}
|
|
532
670
|
|
|
533
|
-
//
|
|
534
|
-
|
|
535
|
-
|
|
671
|
+
// Collect data_capture for Order Custom Fields
|
|
672
|
+
const data_capture: Record<string, any> = {}
|
|
673
|
+
_forEach(customFieldsOrderKeys, (key: string) => {
|
|
674
|
+
data_capture[key] = values[key]
|
|
536
675
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
'company',
|
|
541
|
-
'instagram',
|
|
542
|
-
'jobTitle',
|
|
543
|
-
'wallet_address',
|
|
544
|
-
]
|
|
676
|
+
// Delete from values list
|
|
677
|
+
delete checkoutBody.attributes[key]
|
|
678
|
+
})
|
|
545
679
|
|
|
546
|
-
|
|
547
|
-
const
|
|
548
|
-
|
|
680
|
+
// Temp solution for hardcoded data capture values, to be deleted in the future
|
|
681
|
+
const captureKeys = [
|
|
682
|
+
'businessCategory',
|
|
683
|
+
'company',
|
|
684
|
+
'instagram',
|
|
685
|
+
'jobTitle',
|
|
686
|
+
'wallet_address',
|
|
687
|
+
]
|
|
549
688
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
689
|
+
captureKeys.forEach(key => {
|
|
690
|
+
const path = `data_capture.${key}`
|
|
691
|
+
const value = _get(values, path, '')
|
|
553
692
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
693
|
+
if (value !== '') {
|
|
694
|
+
data_capture[key] = value
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
const attributeKey = `data_capture[${key}]`
|
|
698
|
+
delete checkoutBody.attributes?.[attributeKey]
|
|
699
|
+
})
|
|
700
|
+
|
|
701
|
+
// Collect data_capture for Ticket Holders Fields
|
|
702
|
+
_forEach(checkoutBody.attributes.ticket_holders, (holder, holderIndex) => {
|
|
703
|
+
const holderDataCapture: Record<string, any> = {}
|
|
704
|
+
|
|
705
|
+
_forEach(customFieldsTicketHolderKeys, customFieldKey => {
|
|
706
|
+
const customFieldHolderName = `${customFieldKey}-${holderIndex}`
|
|
707
|
+
const customFieldHolderKey = values[customFieldHolderName] || ''
|
|
708
|
+
holderDataCapture[customFieldKey] = customFieldHolderKey
|
|
709
|
+
|
|
710
|
+
// Delete holder specific value from values list
|
|
711
|
+
delete checkoutBody.attributes[customFieldHolderName]
|
|
712
|
+
})
|
|
713
|
+
|
|
714
|
+
// Assign Ticket Holder data_capture final value
|
|
715
|
+
if (!_isEmpty(holderDataCapture)) {
|
|
716
|
+
holder.ticket_data_capture = holderDataCapture
|
|
717
|
+
}
|
|
718
|
+
})
|
|
719
|
+
|
|
720
|
+
// Collect add_on_data_capture for Add-on Custom Fields
|
|
721
|
+
const add_on_data_capture: Record<string, any> = {}
|
|
722
|
+
_forEach(values, (val: any, key: string) => {
|
|
723
|
+
// Matches keys like: `${addonId}-${groupId}-${fieldKey}` created in AddonComponent
|
|
724
|
+
const match = key.match(/^(\d+)-\d+-(.+)$/)
|
|
725
|
+
if (match) {
|
|
726
|
+
const addonId = match[1]
|
|
727
|
+
const fieldKey = match[2]
|
|
557
728
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
729
|
+
// Initialize addon bucket
|
|
730
|
+
if (!add_on_data_capture[addonId]) {
|
|
731
|
+
add_on_data_capture[addonId] = {}
|
|
732
|
+
}
|
|
561
733
|
|
|
562
|
-
|
|
563
|
-
const customFieldHolderName = `${customFieldKey}-${holderIndex}`
|
|
564
|
-
const customFieldHolderKey = values[customFieldHolderName] || ''
|
|
565
|
-
holderDataCapture[customFieldKey] = customFieldHolderKey
|
|
734
|
+
add_on_data_capture[addonId][fieldKey] = val
|
|
566
735
|
|
|
567
|
-
|
|
568
|
-
|
|
736
|
+
// Remove the raw form key from attributes to avoid leaking internals
|
|
737
|
+
delete checkoutBody.attributes[key]
|
|
738
|
+
}
|
|
569
739
|
})
|
|
570
740
|
|
|
571
|
-
//
|
|
572
|
-
if (
|
|
573
|
-
|
|
741
|
+
// Also include add_on_data_capture from localStorage for single-page checkout
|
|
742
|
+
if (isBrowser) {
|
|
743
|
+
const storedAddOnDataCapture = localStorage.getItem('add_on_data_capture')
|
|
744
|
+
if (storedAddOnDataCapture) {
|
|
745
|
+
const parsed = JSON.parse(storedAddOnDataCapture)
|
|
746
|
+
Object.keys(parsed).forEach(addonId => {
|
|
747
|
+
if (!add_on_data_capture[addonId]) {
|
|
748
|
+
add_on_data_capture[addonId] = {}
|
|
749
|
+
}
|
|
750
|
+
Object.keys(parsed[addonId]).forEach(fieldKey => {
|
|
751
|
+
add_on_data_capture[addonId][fieldKey] = parsed[addonId][fieldKey]
|
|
752
|
+
})
|
|
753
|
+
})
|
|
754
|
+
}
|
|
574
755
|
}
|
|
575
|
-
})
|
|
576
756
|
|
|
577
|
-
|
|
578
|
-
|
|
757
|
+
const mergedAttributes: Record<string, any> = {
|
|
758
|
+
...checkoutBody.attributes,
|
|
759
|
+
data_capture,
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
if (!_isEmpty(add_on_data_capture)) {
|
|
763
|
+
mergedAttributes.add_on_data_capture = add_on_data_capture
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
return { attributes: mergedAttributes }
|
|
767
|
+
},
|
|
768
|
+
[
|
|
769
|
+
showDOB,
|
|
770
|
+
showTicketHolders,
|
|
771
|
+
canSkipHolderNames,
|
|
772
|
+
customFieldsOrderKeys,
|
|
773
|
+
ticketsQuantity.length,
|
|
774
|
+
emailLogged,
|
|
775
|
+
firstNameLogged,
|
|
776
|
+
lastNameLogged,
|
|
777
|
+
phoneLogged,
|
|
778
|
+
customFieldsTicketHolderKeys,
|
|
779
|
+
]
|
|
780
|
+
)
|
|
579
781
|
|
|
580
|
-
const removeReferralKey = () => {
|
|
782
|
+
const removeReferralKey = useCallback(() => {
|
|
581
783
|
localStorage.removeItem('referral_key')
|
|
582
|
-
}
|
|
784
|
+
}, [])
|
|
785
|
+
|
|
786
|
+
const removeAdditionalConfigs = useCallback(() => {
|
|
787
|
+
localStorage.removeItem('checkoutAdditionalConfigs')
|
|
788
|
+
}, [])
|
|
583
789
|
|
|
790
|
+
useEffect(() => {
|
|
791
|
+
onCheckoutUpdateSuccess({ expires_at: expirationTime, ...checkoutData })
|
|
792
|
+
}, [checkoutData, cartInfoData])
|
|
793
|
+
|
|
794
|
+
// Initialize payment hooks
|
|
795
|
+
usePaymentRedirect({
|
|
796
|
+
stripeRef,
|
|
797
|
+
setError,
|
|
798
|
+
setLoading,
|
|
799
|
+
removeReferralKey,
|
|
800
|
+
removeAdditionalConfigs,
|
|
801
|
+
handleSubmit,
|
|
802
|
+
isBrowser,
|
|
803
|
+
})
|
|
804
|
+
|
|
805
|
+
const { processPayment } = useStripePayment({
|
|
806
|
+
stripeRef,
|
|
807
|
+
elementsRef,
|
|
808
|
+
setError,
|
|
809
|
+
isBrowser,
|
|
810
|
+
})
|
|
811
|
+
|
|
812
|
+
const updateCheckoutWithAddOns = useCallback(
|
|
813
|
+
async (addOns: { [key: string]: number } = {}) => {
|
|
814
|
+
if (!isSinglePageCheckout) return
|
|
815
|
+
|
|
816
|
+
const mergedAddOns = { ...singleCheckoutAddons }
|
|
817
|
+
|
|
818
|
+
// Update existing entries and add new ones
|
|
819
|
+
Object.entries(addOns).forEach(([key, value]) => {
|
|
820
|
+
const amount = Number(value)
|
|
821
|
+
if (amount) {
|
|
822
|
+
mergedAddOns[key] = amount
|
|
823
|
+
} else {
|
|
824
|
+
delete mergedAddOns[key]
|
|
825
|
+
}
|
|
826
|
+
})
|
|
827
|
+
|
|
828
|
+
try {
|
|
829
|
+
const checkoutUpdateData = {
|
|
830
|
+
attributes: {
|
|
831
|
+
event_id: eventId,
|
|
832
|
+
add_ons: mergedAddOns,
|
|
833
|
+
is_from_resale: additionalConfigs?.resale,
|
|
834
|
+
},
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
const checkoutResponse = await updateCheckout(checkoutUpdateData)
|
|
838
|
+
|
|
839
|
+
if (checkoutResponse.success) {
|
|
840
|
+
const checkoutDataObj = _get(
|
|
841
|
+
checkoutResponse,
|
|
842
|
+
'data.attributes.cart_price_breakdown',
|
|
843
|
+
{}
|
|
844
|
+
)
|
|
845
|
+
setCheckoutData(checkoutDataObj)
|
|
846
|
+
setSingleCheckoutAddOns(mergedAddOns)
|
|
847
|
+
}
|
|
848
|
+
} catch (error) {
|
|
849
|
+
const errorMessage = _get(
|
|
850
|
+
error,
|
|
851
|
+
'response.data.message',
|
|
852
|
+
'Failed to update add-ons'
|
|
853
|
+
)
|
|
854
|
+
setError(errorMessage)
|
|
855
|
+
onCheckoutUpdateError(error as AxiosError)
|
|
856
|
+
}
|
|
857
|
+
},
|
|
858
|
+
[eventId, isSinglePageCheckout, onCheckoutUpdateError, onCheckoutUpdateSuccess]
|
|
859
|
+
)
|
|
860
|
+
console.log({ checkoutData })
|
|
861
|
+
|
|
862
|
+
const handleAddOnSelect = useCallback(
|
|
863
|
+
async (selectedAddOns: { [key: string]: number }) => {
|
|
864
|
+
await updateCheckoutWithAddOns(selectedAddOns)
|
|
865
|
+
},
|
|
866
|
+
[updateCheckoutWithAddOns]
|
|
867
|
+
)
|
|
868
|
+
|
|
869
|
+
const onAddOnSelect = useCallback(
|
|
870
|
+
(id: string, value: string, addon: any, fieldUpdates: Record<string, any> = {}) => {
|
|
871
|
+
const quantity = parseInt(value) || 0
|
|
872
|
+
const addonId = addon.id || id
|
|
873
|
+
|
|
874
|
+
// Get current add-ons and their custom fields
|
|
875
|
+
const currentAddOns = JSON.parse(localStorage.getItem('add_ons') || '{}')
|
|
876
|
+
const currentDataCapture = JSON.parse(
|
|
877
|
+
localStorage.getItem('add_on_data_capture') || '{}'
|
|
878
|
+
)
|
|
879
|
+
|
|
880
|
+
// Update quantities
|
|
881
|
+
const updatedAddOns = { ...currentAddOns }
|
|
882
|
+
updatedAddOns[addonId] = quantity
|
|
883
|
+
|
|
884
|
+
// Update custom fields if any
|
|
885
|
+
const updatedDataCapture = { ...currentDataCapture }
|
|
886
|
+
if (Object.keys(fieldUpdates).length > 0) {
|
|
887
|
+
updatedDataCapture[addonId] = {
|
|
888
|
+
...(updatedDataCapture[addonId] || {}),
|
|
889
|
+
...fieldUpdates,
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
if (quantity === 0 && updatedDataCapture[addonId]) {
|
|
894
|
+
delete updatedDataCapture[addonId]
|
|
895
|
+
}
|
|
896
|
+
// Save to localStorage
|
|
897
|
+
localStorage.setItem('add_ons', JSON.stringify(updatedAddOns))
|
|
898
|
+
localStorage.setItem('add_on_data_capture', JSON.stringify(updatedDataCapture))
|
|
899
|
+
|
|
900
|
+
// Call handleAddOnSelect with the updated addons object
|
|
901
|
+
handleAddOnSelect(updatedAddOns)
|
|
902
|
+
},
|
|
903
|
+
[handleAddOnSelect]
|
|
904
|
+
)
|
|
584
905
|
if (loading || (enableTimer && !expirationTime && isBrowser)) {
|
|
585
906
|
if (expirationTime === 0) {
|
|
586
907
|
// Redirect to homepage (countdown finished and browser reloaded case)
|
|
@@ -597,20 +918,38 @@ const BillingInfoContainer = React.memo(
|
|
|
597
918
|
if (isTable) {
|
|
598
919
|
dataWithUniqueIds[0].label = 'Get Your Tables'
|
|
599
920
|
}
|
|
921
|
+
|
|
922
|
+
const stripePublishableKey =
|
|
923
|
+
reviewData?.payment_method?.stripe_publishable_key ||
|
|
924
|
+
checkoutUpdateData?.additional_payment_information?.basic_config?.apiKey
|
|
925
|
+
const stripeAccountId =
|
|
926
|
+
reviewData?.payment_method?.stripe_connected_account ||
|
|
927
|
+
checkoutUpdateData?.additional_payment_information?.basic_config?.accountId
|
|
928
|
+
const addOnsIncludedOnInvitation =
|
|
929
|
+
additionalConfigs.resale && additionalConfigs.resaleWithAddons
|
|
930
|
+
|
|
931
|
+
const elementsOptions: StripeElementsOptions = {
|
|
932
|
+
...checkoutUpdateData?.additional_payment_information?.elements_config,
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
if (loading || cardLoading || isCountriesLoading || isConfigLoading || !eventId) {
|
|
936
|
+
return (
|
|
937
|
+
<Backdrop
|
|
938
|
+
sx={{ color: '#fff', backgroundColor: '#000000bd', zIndex: 1205 }}
|
|
939
|
+
open={true}
|
|
940
|
+
>
|
|
941
|
+
<CircularProgress color="inherit" />
|
|
942
|
+
</Backdrop>
|
|
943
|
+
)
|
|
944
|
+
}
|
|
945
|
+
|
|
600
946
|
return (
|
|
601
947
|
<ThemeProvider theme={themeMui}>
|
|
602
|
-
{(loading || cardLoading || isCountriesLoading || isConfigLoading) && (
|
|
603
|
-
<Backdrop
|
|
604
|
-
sx={{ color: '#fff', backgroundColor: '#000000bd', zIndex: 1205 }}
|
|
605
|
-
open={true}
|
|
606
|
-
>
|
|
607
|
-
<CircularProgress color="inherit" />
|
|
608
|
-
</Backdrop>
|
|
609
|
-
)}
|
|
610
948
|
{!!expirationTime && enableTimer && (
|
|
611
949
|
<TimerWidget
|
|
612
950
|
expires_at={expirationTime}
|
|
613
951
|
onCountdownFinish={onCountdownFinish}
|
|
952
|
+
container={isSinglePageCheckout ? 'body' : ''}
|
|
614
953
|
/>
|
|
615
954
|
)}
|
|
616
955
|
{!isCountriesLoading && !isConfigLoading && (
|
|
@@ -619,8 +958,9 @@ const BillingInfoContainer = React.memo(
|
|
|
619
958
|
dataWithUniqueIds,
|
|
620
959
|
{
|
|
621
960
|
country: initialCountry,
|
|
622
|
-
state:
|
|
623
|
-
|
|
961
|
+
state:
|
|
962
|
+
_get(userData, 'stateId', '') || _get(userData, 'state', '') || '1',
|
|
963
|
+
brand_opt_in: Boolean(optedInFieldValue),
|
|
624
964
|
ttf_opt_in: ttfOptIn,
|
|
625
965
|
data_capture: {
|
|
626
966
|
instagram: _get(extraData, 'data_capture.instagram', ''),
|
|
@@ -637,6 +977,36 @@ const BillingInfoContainer = React.memo(
|
|
|
637
977
|
enableReinitialize={false}
|
|
638
978
|
onSubmit={async (values, formikHelpers) => {
|
|
639
979
|
try {
|
|
980
|
+
// Validation: if phone is required for ticket holders, mark errors and stop submit
|
|
981
|
+
const flagRequirePhoneLocal = Boolean(configs?.phone_required)
|
|
982
|
+
if (flagRequirePhoneLocal) {
|
|
983
|
+
const holdersCount = ticketsQuantity.length
|
|
984
|
+
let hasHolderPhoneError = false
|
|
985
|
+
for (let i = 0; i < holdersCount; i++) {
|
|
986
|
+
const fieldName = `holderPhone-${i}`
|
|
987
|
+
const value = _get(values, fieldName, '')
|
|
988
|
+
if (!value) {
|
|
989
|
+
hasHolderPhoneError = true
|
|
990
|
+
formikHelpers.setFieldTouched(fieldName, true, false)
|
|
991
|
+
formikHelpers.setFieldError(fieldName, 'This field is required')
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
if (hasHolderPhoneError) {
|
|
995
|
+
// Do not proceed with submit; Formik will show errors and our PhoneNumberField now shows errors when submitCount > 0
|
|
996
|
+
return
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
if ((!elementsRef.current || !stripeRef.current) && !orderIsFree) {
|
|
1001
|
+
setError('Fill in the payment details')
|
|
1002
|
+
return
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
if (isSinglePageCheckout && !orderIsFree) {
|
|
1006
|
+
// For PaymentElement, we'll use confirmPayment directly
|
|
1007
|
+
// No need to create payment method separately
|
|
1008
|
+
}
|
|
1009
|
+
|
|
640
1010
|
if (isBrowser) {
|
|
641
1011
|
window.localStorage.setItem(
|
|
642
1012
|
'extraData',
|
|
@@ -654,124 +1024,164 @@ const BillingInfoContainer = React.memo(
|
|
|
654
1024
|
})
|
|
655
1025
|
)
|
|
656
1026
|
}
|
|
657
|
-
if (isLoggedIn) {
|
|
658
|
-
const checkoutBody = collectCheckoutBody(values, userData)
|
|
659
1027
|
|
|
660
|
-
|
|
661
|
-
|
|
1028
|
+
// Guest checkout: no need to register, just get profile if logged in
|
|
1029
|
+
let userDataObj = userData
|
|
1030
|
+
if (isLoggedIn) {
|
|
1031
|
+
try {
|
|
1032
|
+
const profileData = await getProfileData()
|
|
1033
|
+
const profileSpecifiedData = _get(profileData, 'data')
|
|
1034
|
+
userDataObj = setLoggedUserData(profileSpecifiedData)
|
|
1035
|
+
} catch (e) {
|
|
1036
|
+
// If profile fetch fails, use values from form
|
|
1037
|
+
userDataObj = {
|
|
1038
|
+
email: values.email,
|
|
1039
|
+
first_name: values.firstName,
|
|
1040
|
+
last_name: values.lastName,
|
|
1041
|
+
phone: values.phone,
|
|
1042
|
+
}
|
|
662
1043
|
}
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
if (isBrowser) {
|
|
671
|
-
const updatedUserData = await getProfileData()
|
|
672
|
-
const profileSpecifiedData = _get(updatedUserData, 'data')
|
|
673
|
-
const profileDataObj = setLoggedUserData(profileSpecifiedData)
|
|
674
|
-
window.localStorage.setItem(
|
|
675
|
-
'user_data',
|
|
676
|
-
JSON.stringify(profileDataObj)
|
|
677
|
-
)
|
|
1044
|
+
} else {
|
|
1045
|
+
// For guest checkout, use form values
|
|
1046
|
+
userDataObj = {
|
|
1047
|
+
email: values.email,
|
|
1048
|
+
first_name: values.firstName,
|
|
1049
|
+
last_name: values.lastName,
|
|
1050
|
+
phone: values.phone,
|
|
678
1051
|
}
|
|
679
|
-
|
|
680
|
-
handleSubmit(
|
|
681
|
-
values,
|
|
682
|
-
formikHelpers as FormikHelpers<any>,
|
|
683
|
-
eventId,
|
|
684
|
-
checkoutResponse
|
|
685
|
-
)
|
|
686
|
-
return
|
|
687
1052
|
}
|
|
688
|
-
const checkoutBodyForRegistration = createCheckoutDataBody(
|
|
689
|
-
ticketsQuantity.length,
|
|
690
|
-
values,
|
|
691
|
-
{ emailLogged, firstNameLogged, lastNameLogged },
|
|
692
|
-
showDOB
|
|
693
|
-
)
|
|
694
|
-
const bodyFormData = createRegisterFormData(
|
|
695
|
-
values,
|
|
696
|
-
checkoutBodyForRegistration,
|
|
697
|
-
flagFreeTicket
|
|
698
|
-
)
|
|
699
|
-
try {
|
|
700
|
-
setLoading(true)
|
|
701
|
-
const resRegister = await register(bodyFormData)
|
|
702
|
-
const xtfCookie = _get(resRegister, 'headers.x-tf-ecommerce')
|
|
703
|
-
const refreshToken = _get(resRegister, 'data.attributes.refresh_token')
|
|
704
|
-
const userProfile = _get(resRegister, 'data.attributes.user_profile')
|
|
705
|
-
|
|
706
|
-
setIsNewUser(true)
|
|
707
|
-
onRegisterSuccess({
|
|
708
|
-
xtfCookie,
|
|
709
|
-
refreshToken,
|
|
710
|
-
userProfile,
|
|
711
|
-
})
|
|
712
|
-
} catch (e) {
|
|
713
|
-
setLoading(false)
|
|
714
|
-
|
|
715
|
-
if (e.response?.data?.data?.hasUnverifiedOrder) {
|
|
716
|
-
setPendingVerificationMessage(e.response?.data?.message)
|
|
717
|
-
} else if (axios.isAxiosError(e)) {
|
|
718
|
-
const error = e?.response?.data?.message
|
|
719
|
-
if (_includes(error, 'You must be aged')) {
|
|
720
|
-
formikHelpers.setFieldError('holderAge', error)
|
|
721
|
-
}
|
|
722
|
-
if (error?.password) {
|
|
723
|
-
formikHelpers.setFieldError('password', error.password)
|
|
724
|
-
formikHelpers.setFieldError('confirmPassword', error.password)
|
|
725
|
-
}
|
|
726
|
-
if (error?.email && !onLogin) {
|
|
727
|
-
// False will stand for outside controll
|
|
728
|
-
setAlreadyHasUser(true)
|
|
729
|
-
setShowModalLogin(true)
|
|
730
|
-
}
|
|
731
1053
|
|
|
732
|
-
if (
|
|
733
|
-
_includes(error, 'The cart is expired') &&
|
|
734
|
-
!hideErrorsAlertSection
|
|
735
|
-
) {
|
|
736
|
-
setError(error)
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
onRegisterError(e, values.email)
|
|
740
|
-
}
|
|
741
|
-
return
|
|
742
|
-
}
|
|
743
|
-
const profileData = await getProfileData()
|
|
744
|
-
const profileSpecifiedData = _get(profileData, 'data')
|
|
745
|
-
const profileDataObj = setLoggedUserData(profileSpecifiedData)
|
|
746
1054
|
if (isBrowser) {
|
|
747
|
-
window.localStorage.setItem('user_data', JSON.stringify(
|
|
1055
|
+
window.localStorage.setItem('user_data', JSON.stringify(userDataObj))
|
|
748
1056
|
}
|
|
749
1057
|
|
|
750
|
-
const checkoutBody = collectCheckoutBody(values,
|
|
1058
|
+
const checkoutBody = collectCheckoutBody(values, userDataObj)
|
|
751
1059
|
|
|
752
|
-
if (isBrowser) {
|
|
1060
|
+
if (isBrowser && !isSinglePageCheckout) {
|
|
753
1061
|
addAddOnsInAttributes(checkoutBody)
|
|
754
1062
|
}
|
|
755
1063
|
|
|
1064
|
+
if (isSinglePageCheckout) {
|
|
1065
|
+
checkoutBody.attributes.add_ons = singleCheckoutAddons
|
|
1066
|
+
// Include add_on_data_capture for single-page checkout
|
|
1067
|
+
const storedAddOnDataCapture =
|
|
1068
|
+
localStorage.getItem('add_on_data_capture')
|
|
1069
|
+
if (storedAddOnDataCapture) {
|
|
1070
|
+
checkoutBody.attributes.add_on_data_capture =
|
|
1071
|
+
JSON.parse(storedAddOnDataCapture)
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
|
|
756
1075
|
const checkoutResponse = await postOnCheckout(
|
|
757
1076
|
checkoutBody,
|
|
758
1077
|
flagFreeTicket
|
|
759
1078
|
)
|
|
1079
|
+
|
|
1080
|
+
const checkoutUpdateResponse = await updateCheckout({
|
|
1081
|
+
attributes: {
|
|
1082
|
+
event_id: eventId,
|
|
1083
|
+
add_ons: checkoutBody?.attributes?.add_ons ?? [],
|
|
1084
|
+
is_from_resale: additionalConfigs?.resale,
|
|
1085
|
+
},
|
|
1086
|
+
})
|
|
1087
|
+
console.log(
|
|
1088
|
+
'Stripe checkoutUpdateResponse in billing-info-container',
|
|
1089
|
+
checkoutUpdateResponse
|
|
1090
|
+
)
|
|
1091
|
+
|
|
1092
|
+
setCheckoutUpdateData(checkoutUpdateResponse.data.attributes)
|
|
1093
|
+
|
|
1094
|
+
let paymentResponse = null
|
|
1095
|
+
|
|
1096
|
+
if (isSinglePageCheckout) {
|
|
1097
|
+
const { hash, total } = checkoutResponse.data.attributes
|
|
1098
|
+
localStorage.setItem('checkoutData', JSON.stringify({ hash, total }))
|
|
1099
|
+
|
|
1100
|
+
const paymentDataResponse = await getPaymentData(String(hash))
|
|
1101
|
+
|
|
1102
|
+
if (paymentDataResponse.success) {
|
|
1103
|
+
const { attributes } = paymentDataResponse.data
|
|
1104
|
+
console.log('Stripe confirmPayment success in billing-info-container')
|
|
1105
|
+
setReviewData(attributes)
|
|
1106
|
+
|
|
1107
|
+
const { order_details, cart } = attributes
|
|
1108
|
+
const {
|
|
1109
|
+
tickets: [ticket],
|
|
1110
|
+
} = order_details
|
|
1111
|
+
|
|
1112
|
+
const updatedOrderData = {
|
|
1113
|
+
add_ons: order_details.add_ons || [],
|
|
1114
|
+
total: order_details.total,
|
|
1115
|
+
subtotal: order_details.subtotal,
|
|
1116
|
+
fees: order_details.fees,
|
|
1117
|
+
pay_now: order_details.pay_now || '',
|
|
1118
|
+
|
|
1119
|
+
id: order_details?.id,
|
|
1120
|
+
product_name: cart[0]?.product_name,
|
|
1121
|
+
ticketType: ticket?.name,
|
|
1122
|
+
quantity: ticket?.quantity,
|
|
1123
|
+
price: ticket?.price,
|
|
1124
|
+
currency: order_details?.currency,
|
|
1125
|
+
guest_count: order_details?.guest_count || '',
|
|
1126
|
+
debt: order_details?.debt || null,
|
|
1127
|
+
cost: ticket?.cost,
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
const isFreeTickets =
|
|
1131
|
+
(!Number(total) && !Number(updatedOrderData.total)) ||
|
|
1132
|
+
!Number(updatedOrderData?.pay_now || 0)
|
|
1133
|
+
const paymentMethod = attributes.payment_method || {}
|
|
1134
|
+
const paymentPlanAvailable = paymentMethod.stripe_payment_plan_enabled
|
|
1135
|
+
console.log({ paymentPlanAvailable })
|
|
1136
|
+
// Process payment using the hook
|
|
1137
|
+
paymentResponse = await processPayment(
|
|
1138
|
+
paymentDataResponse,
|
|
1139
|
+
values,
|
|
1140
|
+
formikHelpers,
|
|
1141
|
+
checkoutResponse,
|
|
1142
|
+
checkoutUpdateResponse,
|
|
1143
|
+
{
|
|
1144
|
+
attributes,
|
|
1145
|
+
isFreeTickets,
|
|
1146
|
+
updatedOrderData,
|
|
1147
|
+
eventId,
|
|
1148
|
+
}
|
|
1149
|
+
)
|
|
1150
|
+
|
|
1151
|
+
if (!paymentResponse && !isFreeTickets) {
|
|
1152
|
+
// Payment failed or redirected, don't continue
|
|
1153
|
+
return
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
|
|
760
1158
|
removeReferralKey()
|
|
1159
|
+
removeAdditionalConfigs()
|
|
761
1160
|
handleSubmit(
|
|
762
1161
|
values,
|
|
763
1162
|
formikHelpers as FormikHelpers<any>,
|
|
764
1163
|
eventId,
|
|
765
|
-
checkoutResponse
|
|
1164
|
+
checkoutResponse,
|
|
1165
|
+
checkoutUpdateResponse,
|
|
1166
|
+
paymentResponse
|
|
766
1167
|
)
|
|
767
1168
|
} catch (e) {
|
|
768
1169
|
setLoading(false)
|
|
769
|
-
|
|
770
|
-
|
|
1170
|
+
onSubmitError(e as AxiosError)
|
|
1171
|
+
const hasUnverifiedOrder = _get(
|
|
1172
|
+
e,
|
|
1173
|
+
'response.data.data.hasUnverifiedOrder'
|
|
1174
|
+
)
|
|
1175
|
+
const message = _get(e, 'response.data.message', {}) as
|
|
1176
|
+
| { password?: string | null, email?: string | null }
|
|
1177
|
+
| string
|
|
1178
|
+
|
|
1179
|
+
if (hasUnverifiedOrder && typeof message === 'string') {
|
|
1180
|
+
setPendingVerificationMessage(message)
|
|
771
1181
|
} else if (axios.isAxiosError(e)) {
|
|
772
1182
|
if (
|
|
773
|
-
e.response?.status === 401 ||
|
|
774
|
-
e.
|
|
1183
|
+
(e as any).response?.status === 401 ||
|
|
1184
|
+
_get(e, 'response.data.error') === 'invalid_token'
|
|
775
1185
|
) {
|
|
776
1186
|
if (isBrowser) {
|
|
777
1187
|
window.localStorage.removeItem('user_data')
|
|
@@ -785,8 +1195,8 @@ const BillingInfoContainer = React.memo(
|
|
|
785
1195
|
window.document.dispatchEvent(event)
|
|
786
1196
|
}
|
|
787
1197
|
}
|
|
788
|
-
if (
|
|
789
|
-
setError(
|
|
1198
|
+
if (message && !hideErrorsAlertSection && typeof message === 'string') {
|
|
1199
|
+
setError(message)
|
|
790
1200
|
}
|
|
791
1201
|
onSubmitError(e)
|
|
792
1202
|
}
|
|
@@ -801,6 +1211,7 @@ const BillingInfoContainer = React.memo(
|
|
|
801
1211
|
<LogicRunner
|
|
802
1212
|
brandOptIn={brandOptIn}
|
|
803
1213
|
values={props.values}
|
|
1214
|
+
errors={props.errors}
|
|
804
1215
|
setStates={setStates}
|
|
805
1216
|
setFieldValue={props.setFieldValue}
|
|
806
1217
|
setValues={props.setValues}
|
|
@@ -810,16 +1221,18 @@ const BillingInfoContainer = React.memo(
|
|
|
810
1221
|
shouldFetchCountries={shouldFetchCountries}
|
|
811
1222
|
/>
|
|
812
1223
|
<div className={`billing-info-container ${theme}`}>
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
1224
|
+
{!!error && (
|
|
1225
|
+
<SnackbarAlert
|
|
1226
|
+
type="error"
|
|
1227
|
+
isOpen={!!error}
|
|
1228
|
+
message={error || ''}
|
|
1229
|
+
onClose={() => {
|
|
1230
|
+
setError(null)
|
|
1231
|
+
onErrorClose()
|
|
1232
|
+
}}
|
|
1233
|
+
/>
|
|
1234
|
+
)}
|
|
1235
|
+
{!isLoggedIn && !isSinglePageCheckout && (
|
|
823
1236
|
<div className="account-actions-block">
|
|
824
1237
|
<div className="action-item">
|
|
825
1238
|
<div>{accountInfoTitle}</div>
|
|
@@ -855,178 +1268,155 @@ const BillingInfoContainer = React.memo(
|
|
|
855
1268
|
</div>
|
|
856
1269
|
</div>
|
|
857
1270
|
)}
|
|
1271
|
+
{isSinglePageCheckout &&
|
|
1272
|
+
!addOnsIncludedOnInvitation &&
|
|
1273
|
+
eventHasAddons &&
|
|
1274
|
+
eventId ? (
|
|
1275
|
+
<SimpleAddonsContainer
|
|
1276
|
+
{...(addonsProps ?? {})}
|
|
1277
|
+
eventId={eventId}
|
|
1278
|
+
addOnDataWithCustomFields={addOnDataWithCustomFields}
|
|
1279
|
+
configs={configs}
|
|
1280
|
+
onAddOnSelect={onAddOnSelect}
|
|
1281
|
+
/>
|
|
1282
|
+
) : !addOnsIncludedOnInvitation &&
|
|
1283
|
+
includeAddons &&
|
|
1284
|
+
!isSinglePageCheckout ? (
|
|
1285
|
+
<AddonsContainter
|
|
1286
|
+
{...(addonsProps ?? {})}
|
|
1287
|
+
addOnDataWithCustomFields={addOnDataWithCustomFields}
|
|
1288
|
+
configs={configs}
|
|
1289
|
+
onAddOnSelect={onAddOnSelect}
|
|
1290
|
+
/>
|
|
1291
|
+
) : null}
|
|
858
1292
|
{!cardLoading &&
|
|
859
1293
|
_map(dataWithUniqueIds, item => {
|
|
860
1294
|
const { label, labelClassName, fields } = item
|
|
861
1295
|
return (
|
|
862
|
-
<
|
|
1296
|
+
<div key={item.uniqueId} className="billing-info-fields">
|
|
863
1297
|
<p className={labelClassName}>{label}</p>
|
|
864
1298
|
{_map(fields, group => {
|
|
865
1299
|
const { groupClassname, groupItems } = group
|
|
1300
|
+
const filteredGroupItems = filterBillingInfoFields(
|
|
1301
|
+
groupItems,
|
|
1302
|
+
{
|
|
1303
|
+
showDOB: Boolean(showDOB),
|
|
1304
|
+
hideTtfOptIn: Boolean(hideTtfOptIn),
|
|
1305
|
+
hidePhoneField: Boolean(hidePhoneField),
|
|
1306
|
+
flagRequirePhone: Boolean(flagRequirePhone),
|
|
1307
|
+
collectMandatoryWalletAddress: Boolean(
|
|
1308
|
+
collectMandatoryWalletAddress
|
|
1309
|
+
),
|
|
1310
|
+
collectMandatoryJobTitle: Boolean(
|
|
1311
|
+
collectMandatoryJobTitle
|
|
1312
|
+
),
|
|
1313
|
+
collectMandatoryBusinessCategory: Boolean(
|
|
1314
|
+
collectMandatoryBusinessCategory
|
|
1315
|
+
),
|
|
1316
|
+
collectMandatoryCompany: Boolean(collectMandatoryCompany),
|
|
1317
|
+
collectMandatoryInstagram: Boolean(
|
|
1318
|
+
collectMandatoryInstagram
|
|
1319
|
+
),
|
|
1320
|
+
flagFreeTicket: Boolean(flagFreeTicket),
|
|
1321
|
+
hideWalletAddressField: Boolean(hideWalletAddressField),
|
|
1322
|
+
hideJobTitleField: Boolean(hideJobTitleField),
|
|
1323
|
+
hideBusinessCategoryField: Boolean(
|
|
1324
|
+
hideBusinessCategoryField
|
|
1325
|
+
),
|
|
1326
|
+
hideCompanyField: Boolean(hideCompanyField),
|
|
1327
|
+
hideInstagramField,
|
|
1328
|
+
}
|
|
1329
|
+
)
|
|
866
1330
|
return (
|
|
867
1331
|
<React.Fragment key={group.uniqueId}>
|
|
868
1332
|
<div className={groupClassname}>
|
|
869
|
-
{_map(
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
if (el.name === 'data_capture[businessCategory]') {
|
|
895
|
-
if (collectMandatoryBusinessCategory) {
|
|
896
|
-
el.required = true
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
if (el.name === 'data_capture[company]') {
|
|
900
|
-
if (collectMandatoryCompany) {
|
|
901
|
-
el.required = true
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
if (el.name === 'data_capture[instagram]') {
|
|
905
|
-
if (collectMandatoryInstagram) {
|
|
906
|
-
el.required = true
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
if (
|
|
910
|
-
[
|
|
911
|
-
'street_address',
|
|
912
|
-
'country',
|
|
913
|
-
'state',
|
|
914
|
-
'city',
|
|
915
|
-
].includes(el.name)
|
|
916
|
-
) {
|
|
917
|
-
if (flagFreeTicket) {
|
|
918
|
-
el.required = false
|
|
919
|
-
return false
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
if (
|
|
923
|
-
hideWalletAddressField &&
|
|
924
|
-
(el.name === 'wallet-address-info' ||
|
|
925
|
-
el.name === 'data_capture[wallet_address]')
|
|
926
|
-
) {
|
|
927
|
-
return false
|
|
928
|
-
}
|
|
929
|
-
if (
|
|
930
|
-
hideJobTitleField &&
|
|
931
|
-
el.name === 'data_capture[jobTitle]'
|
|
932
|
-
) {
|
|
933
|
-
return false
|
|
934
|
-
}
|
|
935
|
-
if (
|
|
936
|
-
hideBusinessCategoryField &&
|
|
937
|
-
el.name === 'data_capture[businessCategory]'
|
|
938
|
-
) {
|
|
939
|
-
return false
|
|
940
|
-
}
|
|
941
|
-
if (
|
|
942
|
-
hideCompanyField &&
|
|
943
|
-
el.name === 'data_capture[company]'
|
|
944
|
-
) {
|
|
945
|
-
return false
|
|
946
|
-
}
|
|
947
|
-
if (
|
|
948
|
-
hideInstagramField &&
|
|
949
|
-
(el.name === 'data_capture[instagram]' ||
|
|
950
|
-
el.name === 'instagram-info')
|
|
951
|
-
) {
|
|
952
|
-
return false
|
|
953
|
-
}
|
|
954
|
-
return true
|
|
955
|
-
}),
|
|
956
|
-
element =>
|
|
957
|
-
[
|
|
958
|
-
'password',
|
|
959
|
-
'confirmPassword',
|
|
960
|
-
'password-info',
|
|
961
|
-
].includes(element.name) && isLoggedIn ? null : (
|
|
962
|
-
<React.Fragment key={element.uniqueId}>
|
|
963
|
-
<div
|
|
964
|
-
className={`${element.className} ${
|
|
965
|
-
props?.errors[element.name] || ''
|
|
966
|
-
}`}
|
|
967
|
-
>
|
|
968
|
-
{element.component ? (
|
|
969
|
-
element.component
|
|
1333
|
+
{_map(filteredGroupItems, element => {
|
|
1334
|
+
// Hide password fields in single-page checkout or when user is logged in
|
|
1335
|
+
const shouldHidePasswordFields =
|
|
1336
|
+
['password', 'confirmPassword', 'password-info'].includes(
|
|
1337
|
+
element.name
|
|
1338
|
+
) &&
|
|
1339
|
+
(isLoggedIn || isSinglePageCheckout)
|
|
1340
|
+
|
|
1341
|
+
return shouldHidePasswordFields ? null : (
|
|
1342
|
+
<React.Fragment key={element.uniqueId}>
|
|
1343
|
+
<div
|
|
1344
|
+
className={`${element.className} ${
|
|
1345
|
+
props?.errors[element.name] || ''
|
|
1346
|
+
}`}
|
|
1347
|
+
>
|
|
1348
|
+
{element.component ? (
|
|
1349
|
+
typeof element.component === 'function' ? (
|
|
1350
|
+
renderComponentWithProps(
|
|
1351
|
+
element.component as React.ComponentType<any>,
|
|
1352
|
+
element.name === 'payment_info'
|
|
1353
|
+
? { reviewData }
|
|
1354
|
+
: {}
|
|
1355
|
+
)
|
|
970
1356
|
) : (
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
element.type
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1357
|
+
element.component
|
|
1358
|
+
)
|
|
1359
|
+
) : (
|
|
1360
|
+
<Field
|
|
1361
|
+
{...element}
|
|
1362
|
+
type={
|
|
1363
|
+
element.type === 'radio' ||
|
|
1364
|
+
element.type === 'checkbox'
|
|
1365
|
+
? undefined
|
|
1366
|
+
: element.type
|
|
1367
|
+
}
|
|
1368
|
+
setPhoneValidationIsLoading={
|
|
1369
|
+
element.type === 'phone'
|
|
1370
|
+
? setPhoneValidationIsLoading
|
|
1371
|
+
: undefined
|
|
1372
|
+
}
|
|
1373
|
+
fill={element.type === 'phone' ? true : undefined}
|
|
1374
|
+
label={getFieldLabel(element, configs)}
|
|
1375
|
+
validate={getValidateFunctions(
|
|
1376
|
+
element,
|
|
1377
|
+
states,
|
|
1378
|
+
props.values,
|
|
1379
|
+
props.errors
|
|
1380
|
+
)}
|
|
1381
|
+
setFieldValue={props.setFieldValue}
|
|
1382
|
+
onBlur={props.handleBlur}
|
|
1383
|
+
component={getFieldComponent(element)}
|
|
1384
|
+
selectOptions={
|
|
1385
|
+
element.name === 'country'
|
|
1386
|
+
? _map(countries, item => ({
|
|
997
1387
|
value: item.id,
|
|
998
1388
|
label: item.name,
|
|
999
1389
|
}))
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
)}
|
|
1390
|
+
: element.name === 'state'
|
|
1391
|
+
? [
|
|
1392
|
+
{
|
|
1393
|
+
label: element.label,
|
|
1394
|
+
value: '',
|
|
1395
|
+
disabled: true,
|
|
1396
|
+
},
|
|
1397
|
+
...states,
|
|
1398
|
+
]
|
|
1399
|
+
: element.selectOptions || []
|
|
1400
|
+
}
|
|
1401
|
+
theme={theme}
|
|
1402
|
+
defaultCountry={
|
|
1403
|
+
defaultCountry || element.defaultCountry
|
|
1404
|
+
}
|
|
1405
|
+
dateFormat={element.format}
|
|
1406
|
+
isCountryCodeEditable={
|
|
1407
|
+
isCountryCodeEditable
|
|
1408
|
+
}
|
|
1409
|
+
/>
|
|
1410
|
+
)}
|
|
1411
|
+
</div>
|
|
1412
|
+
</React.Fragment>
|
|
1413
|
+
)
|
|
1414
|
+
})}
|
|
1025
1415
|
</div>
|
|
1026
1416
|
</React.Fragment>
|
|
1027
1417
|
)
|
|
1028
1418
|
})}
|
|
1029
|
-
</
|
|
1419
|
+
</div>
|
|
1030
1420
|
)
|
|
1031
1421
|
})}
|
|
1032
1422
|
{!_isEmpty(ticketHoldersFields.fields) && (
|
|
@@ -1046,7 +1436,7 @@ const BillingInfoContainer = React.memo(
|
|
|
1046
1436
|
{...element}
|
|
1047
1437
|
type={
|
|
1048
1438
|
element.type === 'radio' ||
|
|
1049
|
-
|
|
1439
|
+
element.type === 'checkbox'
|
|
1050
1440
|
? undefined
|
|
1051
1441
|
: element.type
|
|
1052
1442
|
}
|
|
@@ -1062,6 +1452,7 @@ const BillingInfoContainer = React.memo(
|
|
|
1062
1452
|
setPhoneValidationIsLoading={
|
|
1063
1453
|
setPhoneValidationIsLoading
|
|
1064
1454
|
}
|
|
1455
|
+
fill={element.type === 'phone' ? true : undefined}
|
|
1065
1456
|
defaultCountry={
|
|
1066
1457
|
defaultCountry || element.defaultCountry
|
|
1067
1458
|
}
|
|
@@ -1077,6 +1468,49 @@ const BillingInfoContainer = React.memo(
|
|
|
1077
1468
|
))}
|
|
1078
1469
|
</div>
|
|
1079
1470
|
)}
|
|
1471
|
+
<div className="payment-section">
|
|
1472
|
+
{isSinglePageCheckout && !orderIsFree && stripePublishableKey && (
|
|
1473
|
+
<PaymentContainer
|
|
1474
|
+
stripePublishableKey={stripePublishableKey}
|
|
1475
|
+
stripeAccountId={stripeAccountId}
|
|
1476
|
+
formTitle="Payment Information"
|
|
1477
|
+
orderInfoLabel=""
|
|
1478
|
+
enableTimer={false}
|
|
1479
|
+
checkoutData={checkoutData}
|
|
1480
|
+
elementsOptions={elementsOptions}
|
|
1481
|
+
paymentElementOptions={{
|
|
1482
|
+
wallets: {
|
|
1483
|
+
applePay:
|
|
1484
|
+
checkoutUpdateData?.additional_payment_information
|
|
1485
|
+
?.stripe_wallets?.applePay || 'never',
|
|
1486
|
+
googlePay:
|
|
1487
|
+
checkoutUpdateData?.additional_payment_information
|
|
1488
|
+
?.stripe_wallets?.googlePay || 'never',
|
|
1489
|
+
},
|
|
1490
|
+
}}
|
|
1491
|
+
onStripeReady={(stripe, elements) => {
|
|
1492
|
+
stripeRef.current = stripe
|
|
1493
|
+
elementsRef.current = elements
|
|
1494
|
+
}}
|
|
1495
|
+
paymentFields={paymentProps.paymentFields || []}
|
|
1496
|
+
onPaymentError={paymentProps.onPaymentError || _identity}
|
|
1497
|
+
handlePayment={paymentProps.handlePayment || _identity}
|
|
1498
|
+
onGetPaymentDataSuccess={
|
|
1499
|
+
paymentProps.onGetPaymentDataSuccess || _identity
|
|
1500
|
+
}
|
|
1501
|
+
onGetPaymentDataError={
|
|
1502
|
+
paymentProps.onGetPaymentDataError || _identity
|
|
1503
|
+
}
|
|
1504
|
+
themeOptions={themeOptions}
|
|
1505
|
+
paymentInfoLabel=""
|
|
1506
|
+
displayPaymentButton={false}
|
|
1507
|
+
hidePaymentForm={false}
|
|
1508
|
+
hideFieldsBlock={true}
|
|
1509
|
+
isSinglePageCheckout={true}
|
|
1510
|
+
/>
|
|
1511
|
+
)}
|
|
1512
|
+
{paymentSectionAddon}
|
|
1513
|
+
</div>
|
|
1080
1514
|
<div className="button-container">
|
|
1081
1515
|
<Button
|
|
1082
1516
|
type="submit"
|
|
@@ -1084,7 +1518,13 @@ const BillingInfoContainer = React.memo(
|
|
|
1084
1518
|
className="login-register-button"
|
|
1085
1519
|
disabled={props.isSubmitting || phoneValidationIsLoading}
|
|
1086
1520
|
>
|
|
1087
|
-
{props.isSubmitting ?
|
|
1521
|
+
{props.isSubmitting ? (
|
|
1522
|
+
<CircularProgress size={26} />
|
|
1523
|
+
) : orderIsFree ? (
|
|
1524
|
+
freeOrderButtonName
|
|
1525
|
+
) : (
|
|
1526
|
+
buttonName
|
|
1527
|
+
)}
|
|
1088
1528
|
</Button>
|
|
1089
1529
|
</div>
|
|
1090
1530
|
</div>
|
|
@@ -1149,6 +1589,7 @@ const BillingInfoContainer = React.memo(
|
|
|
1149
1589
|
onForgotPasswordSuccess={onForgotPasswordSuccess}
|
|
1150
1590
|
onForgotPasswordError={onForgotPasswordError}
|
|
1151
1591
|
showPoweredByImage={showPoweredByImage}
|
|
1592
|
+
displaySuccessMessage
|
|
1152
1593
|
/>
|
|
1153
1594
|
)}
|
|
1154
1595
|
<VerificationPendingModal
|