tf-checkout-react 1.6.6 → 1.7.1
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 +25 -1
- package/dist/components/common/DatePickerField.d.ts +7 -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 +6 -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 +11231 -9563
- 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 +11194 -9529
- 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/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 +827 -406
- package/src/components/billing-info-container/{utils.ts → utils.tsx} +119 -0
- package/src/components/common/CheckboxField/index.tsx +1 -1
- package/src/components/common/CustomField.tsx +38 -2
- package/src/components/common/DatePickerField.tsx +25 -10
- 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 +128 -118
- package/src/components/preRegistration/PreRegistrationInformations.tsx +21 -15
- package/src/components/preRegistration/constants.tsx +10 -4
- package/src/components/preRegistration/index.tsx +194 -174
- 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/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/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,21 +1,27 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1
2
|
/* eslint-disable react/no-unescaped-entities */
|
|
2
3
|
|
|
3
4
|
import { CircularProgress } from '@mui/material'
|
|
4
5
|
import { Form, Formik } from 'formik'
|
|
5
6
|
import _get from 'lodash/get'
|
|
6
7
|
import _identity from 'lodash/identity'
|
|
7
|
-
import
|
|
8
|
+
import _map from 'lodash/map'
|
|
9
|
+
import React, { useEffect, useMemo, useState } from 'react'
|
|
10
|
+
import { Tooltip } from 'react-tooltip'
|
|
8
11
|
|
|
9
12
|
import { getAddons, getCart, getCheckoutPageConfigs, postOnCheckout } from '../../api'
|
|
13
|
+
import { FEES_STYLES } from '../../constants'
|
|
10
14
|
import { currencyNormalizerCreator } from '../../normalizers'
|
|
11
15
|
import { ICheckoutPageConfigs } from '../../types'
|
|
12
16
|
import {
|
|
17
|
+
CONFIGS,
|
|
13
18
|
createCheckoutDataBodyWithDefaultHolder,
|
|
14
19
|
createMarkup,
|
|
15
20
|
getQueryVariable,
|
|
16
21
|
isBrowser,
|
|
17
22
|
} from '../../utils'
|
|
18
23
|
import { VerificationPendingModal } from '../idVerificationContainer/VerificationPendingModal'
|
|
24
|
+
import InfoIcon from '../ticketsContainer/InfoIcon'
|
|
19
25
|
import TimerWidget from '../timerWidget'
|
|
20
26
|
import { addonsWithGroupsAdapter, cartAdapter } from './adapters'
|
|
21
27
|
import AddonComponent from './AddonComponent'
|
|
@@ -40,6 +46,12 @@ export interface IAddonContainterProps {
|
|
|
40
46
|
onConfirmSelectionError?: (error: any) => void;
|
|
41
47
|
onCountdownFinish?: () => void;
|
|
42
48
|
onPendingVerification?: () => void;
|
|
49
|
+
samePage?: boolean;
|
|
50
|
+
descriptionTrigger?: 'click' | 'hover' | 'always';
|
|
51
|
+
addOnDataWithCustomFields: any;
|
|
52
|
+
configs: any;
|
|
53
|
+
|
|
54
|
+
onAddOnSelect?: (id: string, value: string, addon: any) => void;
|
|
43
55
|
}
|
|
44
56
|
|
|
45
57
|
export interface ObjectLiteral {
|
|
@@ -58,6 +70,11 @@ export const AddonsContainter = ({
|
|
|
58
70
|
onConfirmSelectionError = _identity,
|
|
59
71
|
onCountdownFinish = _identity,
|
|
60
72
|
onPendingVerification = _identity,
|
|
73
|
+
samePage,
|
|
74
|
+
descriptionTrigger = 'click',
|
|
75
|
+
addOnDataWithCustomFields,
|
|
76
|
+
configs,
|
|
77
|
+
onAddOnSelect = _identity,
|
|
61
78
|
}: IAddonContainterProps) => {
|
|
62
79
|
const eventId = getQueryVariable('event_id')
|
|
63
80
|
const [addons, setAddons] = useState<any>([])
|
|
@@ -69,7 +86,16 @@ export const AddonsContainter = ({
|
|
|
69
86
|
const [cartExpirationTime, setCartExpirationTime] = useState(0)
|
|
70
87
|
const [pendingVerificationMessage, setPendingVerificationMessage] = useState()
|
|
71
88
|
|
|
89
|
+
const [visibleDescription, setVisibleDescription] = useState<string | null>(null)
|
|
90
|
+
|
|
91
|
+
const handleDescriptionToggle = (ticketId: string) => {
|
|
92
|
+
setVisibleDescription(current => (current === ticketId ? null : ticketId))
|
|
93
|
+
}
|
|
94
|
+
|
|
72
95
|
useEffect(() => {
|
|
96
|
+
if (samePage) {
|
|
97
|
+
window.localStorage.removeItem('add_ons')
|
|
98
|
+
}
|
|
73
99
|
const getAddonsPageInfo = async () => {
|
|
74
100
|
try {
|
|
75
101
|
if (eventId) {
|
|
@@ -222,8 +248,8 @@ export const AddonsContainter = ({
|
|
|
222
248
|
total,
|
|
223
249
|
})
|
|
224
250
|
} catch (error) {
|
|
225
|
-
if (error.response?.data?.data?.hasUnverifiedOrder) {
|
|
226
|
-
setPendingVerificationMessage(error.response?.data?.message)
|
|
251
|
+
if ((error as any).response?.data?.data?.hasUnverifiedOrder) {
|
|
252
|
+
setPendingVerificationMessage((error as any).response?.data?.message)
|
|
227
253
|
} else {
|
|
228
254
|
onPostCheckoutError(error)
|
|
229
255
|
onConfirmSelectionError(error)
|
|
@@ -255,6 +281,22 @@ export const AddonsContainter = ({
|
|
|
255
281
|
window.localStorage.removeItem('add_ons')
|
|
256
282
|
}
|
|
257
283
|
|
|
284
|
+
const initialValues = useMemo(() => {
|
|
285
|
+
const addOnsData: any = {}
|
|
286
|
+
if (addons?.length > 0 && addOnDataWithCustomFields?.fields?.length > 0) {
|
|
287
|
+
_map(addons, addon => {
|
|
288
|
+
_map(addOnDataWithCustomFields.fields, field => {
|
|
289
|
+
const { id, groupItems } = field
|
|
290
|
+
_map(groupItems, item => {
|
|
291
|
+
addOnsData[`${addon.id}-${id}-${item.name}`] = item.value
|
|
292
|
+
})
|
|
293
|
+
})
|
|
294
|
+
})
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return addOnsData
|
|
298
|
+
}, [addons, addOnDataWithCustomFields])
|
|
299
|
+
|
|
258
300
|
if (loading) {
|
|
259
301
|
return (
|
|
260
302
|
<div className={`${classNamePrefix}_loader`}>
|
|
@@ -263,9 +305,17 @@ export const AddonsContainter = ({
|
|
|
263
305
|
)
|
|
264
306
|
}
|
|
265
307
|
|
|
308
|
+
if (addons?.length === 0) {
|
|
309
|
+
return null
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const params = new URL(`${window.location}`).searchParams
|
|
313
|
+
const addOnIsIncluded = params.get('include_add_on') === 'true'
|
|
314
|
+
const isResale = params.get('resale') === 'true'
|
|
315
|
+
|
|
266
316
|
return (
|
|
267
317
|
<>
|
|
268
|
-
{!!cartExpirationTime && enableTimer && (
|
|
318
|
+
{!!cartExpirationTime && enableTimer && !samePage && (
|
|
269
319
|
<TimerWidget
|
|
270
320
|
expires_at={cartExpirationTime}
|
|
271
321
|
onCountdownFinish={() => {
|
|
@@ -277,44 +327,72 @@ export const AddonsContainter = ({
|
|
|
277
327
|
<div className={`${classNamePrefix}_container`}>
|
|
278
328
|
<div className={`${classNamePrefix}_block`}>
|
|
279
329
|
<div className={`${classNamePrefix}_line_block`}>
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
className={`${classNamePrefix}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
330
|
+
{samePage ? null : (
|
|
331
|
+
<p className={`${classNamePrefix}_info_title`}>Get Your Tickets</p>
|
|
332
|
+
)}
|
|
333
|
+
{(!addons?.length || (isResale && addOnIsIncluded)) && samePage ? null : (
|
|
334
|
+
<div className={`${classNamePrefix}_title`}>UPGRADES & ADD-ONS</div>
|
|
335
|
+
)}
|
|
336
|
+
{samePage ? null : (
|
|
337
|
+
<button
|
|
338
|
+
type="button"
|
|
339
|
+
className={`${classNamePrefix}_skip`}
|
|
340
|
+
onClick={() => {
|
|
341
|
+
handleClearAddons()
|
|
342
|
+
handleConfirm({}, true)
|
|
343
|
+
}}
|
|
344
|
+
>
|
|
345
|
+
Skip
|
|
346
|
+
</button>
|
|
347
|
+
)}
|
|
295
348
|
</div>
|
|
349
|
+
{(!addons?.length || (isResale && addOnIsIncluded)) && samePage ? null : (
|
|
350
|
+
<div className={`${classNamePrefix}_subtitle`}>
|
|
351
|
+
PLEASE SELECT FROM THE OPTIONAL ADD-ONS BELOW
|
|
352
|
+
</div>
|
|
353
|
+
)}
|
|
296
354
|
<Formik
|
|
297
|
-
initialValues={
|
|
355
|
+
initialValues={initialValues}
|
|
298
356
|
onSubmit={values => {
|
|
299
357
|
handleConfirm(values)
|
|
300
358
|
}}
|
|
359
|
+
validate={
|
|
360
|
+
samePage
|
|
361
|
+
? values => {
|
|
362
|
+
if (isBrowser) {
|
|
363
|
+
window.localStorage.setItem('add_ons', JSON.stringify(values))
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
: undefined
|
|
367
|
+
}
|
|
301
368
|
>
|
|
302
|
-
{({ values }) => {
|
|
369
|
+
{({ values, errors, setFieldTouched }) => {
|
|
303
370
|
const isConfirmDisabled = !isAtLeastOneAddonSelected(values)
|
|
304
371
|
|
|
305
372
|
return (
|
|
306
373
|
<Form autoComplete="off" className="form_holder">
|
|
307
374
|
<>
|
|
308
|
-
{addons.map((addon: any) => {
|
|
375
|
+
{(isResale && addOnIsIncluded ? [] : addons).map((addon: any) => {
|
|
309
376
|
const price = addon.feeIncluded ? addon.price : addon.cost
|
|
310
|
-
const isAddonFree = Number(price) === 0
|
|
377
|
+
const isAddonFree = Number(addon?.price) === 0
|
|
378
|
+
|
|
379
|
+
const addOnNormalizedCost = isAddonFree
|
|
380
|
+
? 'FREE'
|
|
381
|
+
: currencyNormalizerCreator(
|
|
382
|
+
getNormalizedPrice(addon?.cost ?? 0),
|
|
383
|
+
addon.currency
|
|
384
|
+
)
|
|
311
385
|
|
|
312
386
|
const addonNormalizedPrice = isAddonFree
|
|
313
387
|
? 'FREE'
|
|
314
388
|
: currencyNormalizerCreator(
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
389
|
+
getNormalizedPrice(
|
|
390
|
+
CONFIGS.FEES_STYLE === FEES_STYLES.DISPLAY_BOTH
|
|
391
|
+
? addon?.price ?? price
|
|
392
|
+
: price
|
|
393
|
+
),
|
|
394
|
+
addon.currency
|
|
395
|
+
)
|
|
318
396
|
|
|
319
397
|
return (
|
|
320
398
|
<div
|
|
@@ -331,20 +409,69 @@ export const AddonsContainter = ({
|
|
|
331
409
|
<div className={`${classNamePrefix}_product_info_block`}>
|
|
332
410
|
<div className={`${classNamePrefix}_product_title`}>
|
|
333
411
|
{addon.name}
|
|
412
|
+
{addon.description && descriptionTrigger !== 'always' && (
|
|
413
|
+
<>
|
|
414
|
+
<span
|
|
415
|
+
aria-hidden
|
|
416
|
+
className="info-icon"
|
|
417
|
+
onClick={
|
|
418
|
+
descriptionTrigger === 'click'
|
|
419
|
+
? () => handleDescriptionToggle(addon.id)
|
|
420
|
+
: undefined
|
|
421
|
+
}
|
|
422
|
+
onMouseEnter={
|
|
423
|
+
descriptionTrigger === 'hover'
|
|
424
|
+
? () => setVisibleDescription(addon.id)
|
|
425
|
+
: undefined
|
|
426
|
+
}
|
|
427
|
+
onMouseLeave={
|
|
428
|
+
descriptionTrigger === 'hover'
|
|
429
|
+
? () => setVisibleDescription(null)
|
|
430
|
+
: undefined
|
|
431
|
+
}
|
|
432
|
+
style={{
|
|
433
|
+
marginLeft: 8,
|
|
434
|
+
cursor: 'pointer',
|
|
435
|
+
display: 'flex',
|
|
436
|
+
}}
|
|
437
|
+
data-tooltip-id={`tooltip-${addon.id}`}
|
|
438
|
+
data-tooltip-content="View Add-On info"
|
|
439
|
+
>
|
|
440
|
+
<InfoIcon size={14} />
|
|
441
|
+
</span>
|
|
442
|
+
<Tooltip id={`tooltip-${addon.id}`} place="top">
|
|
443
|
+
{addon.description || 'No description available'}
|
|
444
|
+
</Tooltip>
|
|
445
|
+
</>
|
|
446
|
+
)}
|
|
334
447
|
</div>
|
|
335
448
|
<div className={`${classNamePrefix}_product_price`}>
|
|
336
|
-
{
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
449
|
+
{CONFIGS.FEES_STYLE === FEES_STYLES.TRADITIONAL
|
|
450
|
+
? addonNormalizedPrice
|
|
451
|
+
: addOnNormalizedCost}
|
|
452
|
+
{!isAddonFree &&
|
|
453
|
+
CONFIGS.FEES_STYLE === FEES_STYLES.TRADITIONAL && (
|
|
454
|
+
<span className={`${classNamePrefix}_product_fee`}>
|
|
455
|
+
{addon.feeIncluded ? '(incl. Fees)' : '(excl. Fees)'}
|
|
456
|
+
</span>
|
|
457
|
+
)}
|
|
458
|
+
{!isAddonFree &&
|
|
459
|
+
CONFIGS.FEES_STYLE === FEES_STYLES.DISPLAY_BOTH && (
|
|
460
|
+
<>
|
|
461
|
+
<span className={`${classNamePrefix}_product_fee`}>
|
|
462
|
+
{`(${addonNormalizedPrice} with fees)`}
|
|
463
|
+
</span>
|
|
464
|
+
</>
|
|
465
|
+
)}
|
|
342
466
|
</div>
|
|
343
467
|
</div>
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
468
|
+
{(visibleDescription === addon.id ||
|
|
469
|
+
descriptionTrigger === 'always') && (
|
|
470
|
+
<div
|
|
471
|
+
className={`${classNamePrefix}_product_desc`}
|
|
472
|
+
dangerouslySetInnerHTML={createMarkup(addon.description)}
|
|
473
|
+
/>
|
|
474
|
+
)}
|
|
348
475
|
<div className={`${classNamePrefix}_product_select_container`}>
|
|
349
476
|
{addon.variants ? (
|
|
350
477
|
addon.variants.map((variant: any) => (
|
|
@@ -354,9 +481,14 @@ export const AddonsContainter = ({
|
|
|
354
481
|
data={variant}
|
|
355
482
|
selectOptions={addonsOptions[variant.id]}
|
|
356
483
|
classNamePrefix={classNamePrefix}
|
|
357
|
-
handleAddonChange={(id, value) =>
|
|
484
|
+
handleAddonChange={(id, value) => {
|
|
485
|
+
onAddOnSelect(id, value, addon)
|
|
358
486
|
onFieldChange(id, value, addon)
|
|
359
|
-
}
|
|
487
|
+
}}
|
|
488
|
+
addOnDataWithCustomFields={addOnDataWithCustomFields}
|
|
489
|
+
configs={configs}
|
|
490
|
+
values={values}
|
|
491
|
+
errors={errors}
|
|
360
492
|
/>
|
|
361
493
|
))
|
|
362
494
|
) : (
|
|
@@ -366,23 +498,42 @@ export const AddonsContainter = ({
|
|
|
366
498
|
data={addon}
|
|
367
499
|
selectOptions={addonsOptions[addon.id]}
|
|
368
500
|
classNamePrefix={classNamePrefix}
|
|
369
|
-
handleAddonChange={(id, value) =>
|
|
501
|
+
handleAddonChange={(id, value) => {
|
|
502
|
+
onAddOnSelect(id, value, addon)
|
|
370
503
|
onFieldChange(id, value, addon)
|
|
371
|
-
|
|
504
|
+
|
|
505
|
+
_map(addOnDataWithCustomFields.fields, fieldGroup => {
|
|
506
|
+
const { id, groupItems } = fieldGroup
|
|
507
|
+
_map(groupItems, field => {
|
|
508
|
+
setFieldTouched(
|
|
509
|
+
`${addon.id}-${id}-${field.name}`,
|
|
510
|
+
true,
|
|
511
|
+
true
|
|
512
|
+
)
|
|
513
|
+
})
|
|
514
|
+
})
|
|
515
|
+
}}
|
|
516
|
+
addOnDataWithCustomFields={addOnDataWithCustomFields}
|
|
517
|
+
configs={configs}
|
|
518
|
+
values={values}
|
|
519
|
+
errors={errors}
|
|
372
520
|
/>
|
|
373
521
|
)}
|
|
374
522
|
</div>
|
|
375
523
|
</div>
|
|
376
524
|
)
|
|
377
525
|
})}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
526
|
+
{samePage ? null : (
|
|
527
|
+
<button
|
|
528
|
+
type="submit"
|
|
529
|
+
className={`${
|
|
530
|
+
isConfirmDisabled ? `${classNamePrefix}_is_disabled` : ''
|
|
381
531
|
} ${classNamePrefix}_submit_button`}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
532
|
+
disabled={isConfirmDisabled}
|
|
533
|
+
>
|
|
534
|
+
CONFIRM SELECTION
|
|
535
|
+
</button>
|
|
536
|
+
)}
|
|
386
537
|
</>
|
|
387
538
|
</Form>
|
|
388
539
|
)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useCallback } from 'react'
|
|
2
|
+
|
|
3
|
+
export const usePaymentContext = () => {
|
|
4
|
+
const storePaymentContext = useCallback((context: any) => {
|
|
5
|
+
localStorage.setItem('stripe_payment_context', JSON.stringify(context))
|
|
6
|
+
}, [])
|
|
7
|
+
|
|
8
|
+
const getPaymentContext = useCallback(() => {
|
|
9
|
+
const storedContext = localStorage.getItem('stripe_payment_context')
|
|
10
|
+
return storedContext ? JSON.parse(storedContext) : null
|
|
11
|
+
}, [])
|
|
12
|
+
|
|
13
|
+
const clearPaymentContext = useCallback(() => {
|
|
14
|
+
localStorage.removeItem('stripe_payment_context')
|
|
15
|
+
}, [])
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
storePaymentContext,
|
|
19
|
+
getPaymentContext,
|
|
20
|
+
clearPaymentContext
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { Stripe } from '@stripe/stripe-js'
|
|
2
|
+
import { FormikHelpers } from 'formik'
|
|
3
|
+
import _identity from 'lodash/identity'
|
|
4
|
+
import { useEffect } from 'react'
|
|
5
|
+
|
|
6
|
+
import { handlePaymentMiddleWare } from '../../paymentContainer/handlePayment'
|
|
7
|
+
|
|
8
|
+
interface PaymentContext {
|
|
9
|
+
attributes: any
|
|
10
|
+
isFreeTickets: boolean
|
|
11
|
+
updatedOrderData: any
|
|
12
|
+
eventId: string
|
|
13
|
+
values: any
|
|
14
|
+
formikHelpers: FormikHelpers<any>
|
|
15
|
+
checkoutResponse: any
|
|
16
|
+
checkoutUpdateResponse: any
|
|
17
|
+
timestamp: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface UsePaymentRedirectProps {
|
|
21
|
+
stripeRef: React.MutableRefObject<Stripe | null>
|
|
22
|
+
setError: (error: string | null) => void
|
|
23
|
+
setLoading: (loading: boolean) => void
|
|
24
|
+
removeReferralKey: () => void
|
|
25
|
+
removeAdditionalConfigs: () => void
|
|
26
|
+
handleSubmit: (
|
|
27
|
+
values: any,
|
|
28
|
+
formikHelpers: FormikHelpers<any>,
|
|
29
|
+
eventId: any,
|
|
30
|
+
checkoutResponse: any,
|
|
31
|
+
checkoutUpdateResponse: any,
|
|
32
|
+
paymentResponse: any
|
|
33
|
+
) => void
|
|
34
|
+
isBrowser: boolean
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const usePaymentRedirect = ({
|
|
38
|
+
stripeRef,
|
|
39
|
+
setError,
|
|
40
|
+
setLoading,
|
|
41
|
+
removeReferralKey,
|
|
42
|
+
removeAdditionalConfigs,
|
|
43
|
+
handleSubmit,
|
|
44
|
+
isBrowser,
|
|
45
|
+
}: UsePaymentRedirectProps) => {
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
const handlePaymentReturn = async () => {
|
|
48
|
+
const urlParams = new URLSearchParams(window.location.search)
|
|
49
|
+
const isPaymentReturn = urlParams.get('payment_return')
|
|
50
|
+
const clientSecret = urlParams.get('payment_intent_client_secret')
|
|
51
|
+
|
|
52
|
+
if (isPaymentReturn && clientSecret && stripeRef.current) {
|
|
53
|
+
console.log('Detected payment redirect return, checking status')
|
|
54
|
+
setLoading(true)
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
// Get stored payment context
|
|
58
|
+
const storedContext = localStorage.getItem('stripe_payment_context')
|
|
59
|
+
if (!storedContext) {
|
|
60
|
+
setError('Payment context not found')
|
|
61
|
+
setLoading(false)
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const paymentContext: PaymentContext = JSON.parse(storedContext)
|
|
66
|
+
const { paymentIntent } = await stripeRef.current.retrievePaymentIntent(
|
|
67
|
+
clientSecret
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if (paymentIntent?.status === 'succeeded') {
|
|
71
|
+
console.log('Payment succeeded after redirect, continuing flow')
|
|
72
|
+
|
|
73
|
+
// Clean up stored context
|
|
74
|
+
localStorage.removeItem('stripe_payment_context')
|
|
75
|
+
|
|
76
|
+
// Continue with the success flow using stored context
|
|
77
|
+
let paymentResponse = null
|
|
78
|
+
await handlePaymentMiddleWare(
|
|
79
|
+
null,
|
|
80
|
+
{},
|
|
81
|
+
{
|
|
82
|
+
reviewData: paymentContext.attributes,
|
|
83
|
+
isFreeTickets: paymentContext.isFreeTickets,
|
|
84
|
+
paymentPlanIsAvailable: false,
|
|
85
|
+
showPaymentPlanSection: false,
|
|
86
|
+
handlePayment: (data: any) => {
|
|
87
|
+
paymentResponse = data
|
|
88
|
+
},
|
|
89
|
+
setPaymentIsLoading: _identity,
|
|
90
|
+
setError: _identity,
|
|
91
|
+
orderData: paymentContext.updatedOrderData,
|
|
92
|
+
eventId: paymentContext.eventId,
|
|
93
|
+
isBrowser,
|
|
94
|
+
onPaymentError: (error: any) => {
|
|
95
|
+
throw error
|
|
96
|
+
},
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
// Complete the flow with cleanup and handleSubmit
|
|
101
|
+
removeReferralKey()
|
|
102
|
+
removeAdditionalConfigs()
|
|
103
|
+
handleSubmit(
|
|
104
|
+
paymentContext.values,
|
|
105
|
+
paymentContext.formikHelpers,
|
|
106
|
+
paymentContext.eventId,
|
|
107
|
+
paymentContext.checkoutResponse,
|
|
108
|
+
paymentContext.checkoutUpdateResponse,
|
|
109
|
+
paymentResponse
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
// Clean URL parameters
|
|
113
|
+
window.history.replaceState({}, document.title, window.location.pathname)
|
|
114
|
+
} else if (paymentIntent?.status === 'requires_action') {
|
|
115
|
+
setError('Payment requires additional action')
|
|
116
|
+
// Clean up stored context and URL to prevent infinite loop
|
|
117
|
+
localStorage.removeItem('stripe_payment_context')
|
|
118
|
+
window.history.replaceState({}, document.title, window.location.pathname)
|
|
119
|
+
} else {
|
|
120
|
+
setError(`Payment failed or was cancelled.`)
|
|
121
|
+
// Clean up stored context and URL to prevent infinite loop
|
|
122
|
+
localStorage.removeItem('stripe_payment_context')
|
|
123
|
+
window.history.replaceState({}, document.title, window.location.pathname)
|
|
124
|
+
}
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error('Error handling payment return:', error)
|
|
127
|
+
setError('Error processing payment return')
|
|
128
|
+
// Clean up stored context and URL to prevent infinite loop
|
|
129
|
+
localStorage.removeItem('stripe_payment_context')
|
|
130
|
+
window.history.replaceState({}, document.title, window.location.pathname)
|
|
131
|
+
} finally {
|
|
132
|
+
setLoading(false)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
handlePaymentReturn()
|
|
138
|
+
}, [
|
|
139
|
+
stripeRef.current,
|
|
140
|
+
setError,
|
|
141
|
+
setLoading,
|
|
142
|
+
removeReferralKey,
|
|
143
|
+
removeAdditionalConfigs,
|
|
144
|
+
handleSubmit,
|
|
145
|
+
isBrowser,
|
|
146
|
+
])
|
|
147
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { FormikHelpers } from 'formik'
|
|
2
|
+
import _identity from 'lodash/identity'
|
|
3
|
+
|
|
4
|
+
import { handlePaymentMiddleWare } from '../../paymentContainer/handlePayment'
|
|
5
|
+
|
|
6
|
+
interface PaymentData {
|
|
7
|
+
attributes: any
|
|
8
|
+
isFreeTickets: boolean
|
|
9
|
+
updatedOrderData: any
|
|
10
|
+
eventId: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface UseStripePaymentProps {
|
|
14
|
+
stripeRef: any
|
|
15
|
+
elementsRef: React.MutableRefObject<any>
|
|
16
|
+
setError: (error: string | null) => void
|
|
17
|
+
isBrowser: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const useStripePayment = ({
|
|
21
|
+
stripeRef,
|
|
22
|
+
elementsRef,
|
|
23
|
+
setError,
|
|
24
|
+
isBrowser,
|
|
25
|
+
}: UseStripePaymentProps) => {
|
|
26
|
+
const processPayment = async (
|
|
27
|
+
paymentDataResponse: any,
|
|
28
|
+
values: any,
|
|
29
|
+
formikHelpers: FormikHelpers<any>,
|
|
30
|
+
checkoutResponse: any,
|
|
31
|
+
checkoutUpdateResponse: any,
|
|
32
|
+
paymentData: PaymentData
|
|
33
|
+
) => {
|
|
34
|
+
const { attributes, isFreeTickets, updatedOrderData, eventId } = paymentData
|
|
35
|
+
|
|
36
|
+
if (!isFreeTickets) {
|
|
37
|
+
if (!stripeRef.current) {
|
|
38
|
+
setError('Stripe is not ready')
|
|
39
|
+
console.log('Stripe is not ready in billing-info-container')
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
console.log('Stripe confirmPayment in billing-info-container')
|
|
44
|
+
|
|
45
|
+
// Step 1: Submit elements first
|
|
46
|
+
const { error: submitError } = await elementsRef.current.submit()
|
|
47
|
+
if (submitError) {
|
|
48
|
+
setError('' + submitError?.message)
|
|
49
|
+
console.log(
|
|
50
|
+
'Stripe elements.submit() error in billing-info-container',
|
|
51
|
+
submitError
|
|
52
|
+
)
|
|
53
|
+
return null
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Step 2: Store payment context before potential redirect
|
|
57
|
+
const paymentContext = {
|
|
58
|
+
attributes,
|
|
59
|
+
isFreeTickets,
|
|
60
|
+
updatedOrderData,
|
|
61
|
+
eventId,
|
|
62
|
+
values,
|
|
63
|
+
formikHelpers,
|
|
64
|
+
checkoutResponse,
|
|
65
|
+
checkoutUpdateResponse,
|
|
66
|
+
timestamp: Date.now(),
|
|
67
|
+
}
|
|
68
|
+
localStorage.setItem('stripe_payment_context', JSON.stringify(paymentContext))
|
|
69
|
+
|
|
70
|
+
// Step 3: Confirm payment with current page return URL
|
|
71
|
+
const { error: confirmError } = await stripeRef.current.confirmPayment({
|
|
72
|
+
clientSecret:
|
|
73
|
+
paymentDataResponse.data.attributes.payment_method.stripe_client_secret,
|
|
74
|
+
elements: elementsRef.current,
|
|
75
|
+
confirmParams: {
|
|
76
|
+
return_url:
|
|
77
|
+
window.location.href +
|
|
78
|
+
(window.location.href.includes('?') ? '&' : '?') +
|
|
79
|
+
'payment_return=true',
|
|
80
|
+
},
|
|
81
|
+
redirect: 'if_required',
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
if (confirmError) {
|
|
85
|
+
setError('' + confirmError?.message)
|
|
86
|
+
console.log('Stripe confirmPayment error in billing-info-container')
|
|
87
|
+
return null
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
console.log('Stripe confirmPayment success in billing-info-container')
|
|
92
|
+
|
|
93
|
+
// Handle payment middleware for non-redirect payments
|
|
94
|
+
let paymentResponse = null
|
|
95
|
+
await handlePaymentMiddleWare(
|
|
96
|
+
null,
|
|
97
|
+
{},
|
|
98
|
+
{
|
|
99
|
+
reviewData: attributes,
|
|
100
|
+
isFreeTickets,
|
|
101
|
+
paymentPlanIsAvailable: false,
|
|
102
|
+
showPaymentPlanSection: false,
|
|
103
|
+
handlePayment: (data: any) => {
|
|
104
|
+
paymentResponse = data
|
|
105
|
+
},
|
|
106
|
+
setPaymentIsLoading: _identity,
|
|
107
|
+
setError: _identity,
|
|
108
|
+
orderData: updatedOrderData,
|
|
109
|
+
eventId,
|
|
110
|
+
isBrowser,
|
|
111
|
+
onPaymentError: (error: any) => {
|
|
112
|
+
throw error
|
|
113
|
+
},
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
return paymentResponse
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return { processPayment }
|
|
121
|
+
}
|