tf-checkout-react 1.7.30 → 2.0.0-beta.0
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/LICENSE +1 -1
- package/README.md +141 -1033
- package/dist/checkout.css +17 -0
- package/dist/index.d.mts +3930 -0
- package/dist/index.d.ts +3930 -30
- package/dist/index.esm.js +9123 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +9277 -6
- package/dist/index.js.map +1 -0
- package/dist/styles/addons.css +75 -0
- package/dist/styles/auth.css +294 -0
- package/dist/styles/billing.css +98 -0
- package/dist/styles/confirmation.css +51 -0
- package/dist/styles/foundation.css +146 -0
- package/dist/styles/payment.css +179 -0
- package/dist/styles/primitives.css +356 -0
- package/dist/styles/seat-map.css +46 -0
- package/dist/styles/tickets.css +285 -0
- package/dist/styles/verification-confirmation.css +258 -0
- package/package.json +93 -78
- package/src/.d.ts +1 -1
- package/src/CardInput.tsx +374 -0
- package/src/EmbeddedCheckout.tsx +400 -0
- package/src/ResumedCheckout.tsx +132 -0
- package/src/auth/AuthModal.tsx +231 -0
- package/src/auth/ForgotPasswordForm.tsx +93 -0
- package/src/auth/LoginForm.tsx +104 -0
- package/src/auth/PasswordInput.tsx +33 -0
- package/src/auth/RegisterForm.tsx +333 -0
- package/src/auth/ResetPasswordForm.tsx +112 -0
- package/src/auth/index.ts +5 -0
- package/src/checkout.css +17 -0
- package/src/configure.ts +29 -0
- package/src/confirmer.ts +439 -0
- package/src/index.ts +41 -30
- package/src/instance.ts +21 -0
- package/src/sections/AddOns.tsx +241 -0
- package/src/sections/BillingInfo.tsx +743 -0
- package/src/sections/Confirmation.tsx +146 -0
- package/src/sections/IDVerification.tsx +76 -0
- package/src/sections/OrderConfirmation.tsx +277 -0
- package/src/sections/OrderIDVerification.tsx +117 -0
- package/src/sections/OrderSummary.tsx +84 -0
- package/src/sections/Payment.tsx +541 -0
- package/src/sections/SeatMap.tsx +512 -0
- package/src/sections/Tickets.tsx +573 -0
- package/src/sections/billing/BillingAddressFields.tsx +138 -0
- package/src/sections/billing/BuyerField.tsx +75 -0
- package/src/sections/billing/TicketHoldersCard.tsx +119 -0
- package/src/sections/billing/types.ts +10 -0
- package/src/sections/index.ts +15 -0
- package/src/sections/seatMap.utils.ts +105 -0
- package/src/styles/addons.css +75 -0
- package/src/styles/auth.css +294 -0
- package/src/styles/billing.css +98 -0
- package/src/styles/confirmation.css +51 -0
- package/src/styles/foundation.css +146 -0
- package/src/styles/payment.css +179 -0
- package/src/styles/primitives.css +356 -0
- package/src/styles/seat-map.css +46 -0
- package/src/styles/tickets.css +285 -0
- package/src/styles/verification-confirmation.css +258 -0
- package/src/typings.d.ts +2 -2
- package/src/ui/Button.tsx +38 -0
- package/src/ui/Card.tsx +54 -0
- package/src/ui/CheckoutProvider.tsx +172 -0
- package/src/ui/CustomFields.tsx +195 -0
- package/src/ui/Field.tsx +114 -0
- package/src/ui/I18nProvider.tsx +36 -0
- package/src/ui/InstallmentPicker.tsx +69 -0
- package/src/ui/Loader.tsx +12 -0
- package/src/ui/PaymentMethods.tsx +133 -0
- package/src/ui/PriceLine.tsx +24 -0
- package/src/ui/QuantitySelect.tsx +50 -0
- package/src/ui/Stepper.tsx +36 -0
- package/src/ui/Timer.tsx +34 -0
- package/src/ui/TimerBanner.tsx +55 -0
- package/src/ui/cx.ts +3 -0
- package/src/ui/index.ts +54 -0
- package/src/ui/theme.ts +83 -0
- package/src/useCheckout.ts +35 -0
- package/src/xendit.ts +438 -0
- package/dist/adapters/customFields.d.ts +0 -12
- package/dist/adapters/index.d.ts +0 -1
- package/dist/api/auth.d.ts +0 -27
- package/dist/api/cart.d.ts +0 -2
- package/dist/api/checkout.d.ts +0 -3
- package/dist/api/common.d.ts +0 -11
- package/dist/api/guestTicketDelegation.d.ts +0 -47
- package/dist/api/index.d.ts +0 -82
- package/dist/api/interceptors.d.ts +0 -1
- package/dist/api/orders.d.ts +0 -2
- package/dist/api/payment.d.ts +0 -5
- package/dist/api/preRegistrationComplete.d.ts +0 -3
- package/dist/api/publicRequest.d.ts +0 -9
- package/dist/api/resale.d.ts +0 -5
- package/dist/components/account-settings/index.d.ts +0 -3
- package/dist/components/addonsContainer/AddonComponent.d.ts +0 -18
- package/dist/components/addonsContainer/SimpleAddonsContainer.d.ts +0 -20
- package/dist/components/addonsContainer/adapters/index.d.ts +0 -12
- package/dist/components/addonsContainer/index.d.ts +0 -22
- package/dist/components/addonsContainer/normalizers/index.d.ts +0 -1
- package/dist/components/addonsContainer/useAddons.d.ts +0 -23
- package/dist/components/addonsContainer/utils/index.d.ts +0 -16
- package/dist/components/billing-info-container/hooks/index.d.ts +0 -3
- package/dist/components/billing-info-container/hooks/usePaymentContext.d.ts +0 -5
- package/dist/components/billing-info-container/hooks/usePaymentRedirect.d.ts +0 -14
- package/dist/components/billing-info-container/hooks/useStripePayment.d.ts +0 -18
- package/dist/components/billing-info-container/index.d.ts +0 -69
- package/dist/components/billing-info-container/utils.d.ts +0 -59
- package/dist/components/common/Checkbox.d.ts +0 -11
- package/dist/components/common/CheckboxField/index.d.ts +0 -9
- package/dist/components/common/CopyField.d.ts +0 -12
- package/dist/components/common/CopyMessageModal.d.ts +0 -7
- package/dist/components/common/CustomField.d.ts +0 -26
- package/dist/components/common/DatePickerField.d.ts +0 -20
- package/dist/components/common/FieldSection/index.d.ts +0 -19
- package/dist/components/common/FieldSection/utils/index.d.ts +0 -8
- package/dist/components/common/FormikPhoneNumberField.d.ts +0 -11
- package/dist/components/common/Loader.d.ts +0 -2
- package/dist/components/common/ModalComponent/index.d.ts +0 -16
- package/dist/components/common/NativeSelectFeild/index.d.ts +0 -21
- package/dist/components/common/PhoneNumberField.d.ts +0 -14
- package/dist/components/common/PoweredBy.d.ts +0 -2
- package/dist/components/common/RadioField.d.ts +0 -11
- package/dist/components/common/RadioGroupField/index.d.ts +0 -17
- package/dist/components/common/RedirectModal.d.ts +0 -7
- package/dist/components/common/SelectField/index.d.ts +0 -19
- package/dist/components/common/SnackbarAlert.d.ts +0 -13
- package/dist/components/common/index.d.ts +0 -11
- package/dist/components/common/socials.d.ts +0 -10
- package/dist/components/confirmModal/index.d.ts +0 -10
- package/dist/components/confirmationContainer/config.d.ts +0 -1
- package/dist/components/confirmationContainer/index.d.ts +0 -46
- package/dist/components/confirmationContainer/social-buttons.d.ts +0 -16
- package/dist/components/countdown/index.d.ts +0 -14
- package/dist/components/delegationsContainer/IssueComponent.d.ts +0 -10
- package/dist/components/delegationsContainer/IssueTicketForm.d.ts +0 -9
- package/dist/components/delegationsContainer/TicketsAssignedTable.d.ts +0 -11
- package/dist/components/delegationsContainer/TicketsAvailableTable.d.ts +0 -11
- package/dist/components/delegationsContainer/index.d.ts +0 -10
- package/dist/components/forgotPasswordModal/index.d.ts +0 -12
- package/dist/components/idVerificationContainer/VerificationPendingModal.d.ts +0 -10
- package/dist/components/idVerificationContainer/constants.d.ts +0 -21
- package/dist/components/idVerificationContainer/index.d.ts +0 -16
- package/dist/components/index.d.ts +0 -12
- package/dist/components/loginForm/index.d.ts +0 -31
- package/dist/components/loginModal/SignUpForm.d.ts +0 -10
- package/dist/components/loginModal/constants.d.ts +0 -39
- package/dist/components/loginModal/index.d.ts +0 -22
- package/dist/components/myTicketsContainer/index.d.ts +0 -16
- package/dist/components/myTicketsContainer/row.d.ts +0 -3
- package/dist/components/myTicketsContainer/tableConfig.d.ts +0 -5
- package/dist/components/orderDetailsContainer/CustomFieldsForm.d.ts +0 -12
- package/dist/components/orderDetailsContainer/TicketHolderCustomFields.d.ts +0 -10
- package/dist/components/orderDetailsContainer/index.d.ts +0 -66
- package/dist/components/orderDetailsContainer/ticketsTable.d.ts +0 -46
- package/dist/components/orderDetailsContainer/utils/index.d.ts +0 -6
- package/dist/components/paymentContainer/OrderDetails.d.ts +0 -9
- package/dist/components/paymentContainer/PaymentPlanSection.d.ts +0 -10
- package/dist/components/paymentContainer/handlePayment.d.ts +0 -15
- package/dist/components/paymentContainer/index.d.ts +0 -39
- package/dist/components/preRegistration/FieldsSection.d.ts +0 -19
- package/dist/components/preRegistration/Influancers.d.ts +0 -2
- package/dist/components/preRegistration/PreRegistrationComplete.d.ts +0 -13
- package/dist/components/preRegistration/PreRegistrationInformations.d.ts +0 -2
- package/dist/components/preRegistration/Prewards.d.ts +0 -2
- package/dist/components/preRegistration/ShareOptions.d.ts +0 -2
- package/dist/components/preRegistration/constants.d.ts +0 -2
- package/dist/components/preRegistration/index.d.ts +0 -23
- package/dist/components/preRegistration/utils.d.ts +0 -12
- package/dist/components/registerForm/adapters/index.d.ts +0 -4
- package/dist/components/registerForm/constants.d.ts +0 -1
- package/dist/components/registerForm/index.d.ts +0 -16
- package/dist/components/registerModal/index.d.ts +0 -12
- package/dist/components/resetPasswordContainer/index.d.ts +0 -10
- package/dist/components/rsvpContainer/index.d.ts +0 -7
- package/dist/components/seatMapContainer/SeatMapComponent.d.ts +0 -8
- package/dist/components/seatMapContainer/TicketsSection.d.ts +0 -9
- package/dist/components/seatMapContainer/addToCart.d.ts +0 -14
- package/dist/components/seatMapContainer/index.d.ts +0 -2
- package/dist/components/seatMapContainer/utils.d.ts +0 -6
- package/dist/components/signupModal/index.d.ts +0 -12
- package/dist/components/ticketResale/index.d.ts +0 -12
- package/dist/components/ticketResaleModal/index.d.ts +0 -19
- package/dist/components/ticketsContainer/AccessCodeSection.d.ts +0 -7
- package/dist/components/ticketsContainer/InfoIcon.d.ts +0 -5
- package/dist/components/ticketsContainer/PromoCodeSection.d.ts +0 -15
- package/dist/components/ticketsContainer/ReferralLogic.d.ts +0 -5
- package/dist/components/ticketsContainer/TicketRow.d.ts +0 -13
- package/dist/components/ticketsContainer/TicketsSection.d.ts +0 -20
- package/dist/components/ticketsContainer/TimeSlotTicketRow.d.ts +0 -19
- package/dist/components/ticketsContainer/TimeSlotsSection.d.ts +0 -25
- package/dist/components/ticketsContainer/index.d.ts +0 -93
- package/dist/components/ticketsContainer/utils.d.ts +0 -13
- package/dist/components/timerWidget/index.d.ts +0 -16
- package/dist/components/waitingList/index.d.ts +0 -9
- package/dist/constants/index.d.ts +0 -7
- package/dist/env.d.ts +0 -16
- package/dist/hoc/CustomFields/index.d.ts +0 -3
- package/dist/hoc/index.d.ts +0 -1
- package/dist/hooks/index.d.ts +0 -1
- package/dist/hooks/useCookieListener.d.ts +0 -1
- package/dist/hooks/useEventListener.d.ts +0 -1
- package/dist/hooks/useLocalStorageListener.d.ts +0 -1
- package/dist/hooks/useOnline.d.ts +0 -1
- package/dist/hooks/usePixel.d.ts +0 -6
- package/dist/images/Polygon.png +0 -0
- package/dist/images/cross.svg +0 -44
- package/dist/images/done.svg +0 -3
- package/dist/images/edit.svg +0 -10
- package/dist/images/email.svg +0 -1
- package/dist/images/instagram.svg +0 -5
- package/dist/images/logo-ttf.png +0 -0
- package/dist/images/spotify.svg +0 -6
- package/dist/images/user.svg +0 -1
- package/dist/images/xmark.svg +0 -92
- package/dist/normalizers/index.d.ts +0 -4
- package/dist/tf-checkout-react.cjs.development.js +0 -16149
- package/dist/tf-checkout-react.cjs.development.js.map +0 -1
- package/dist/tf-checkout-react.cjs.production.min.js +0 -2
- package/dist/tf-checkout-react.cjs.production.min.js.map +0 -1
- package/dist/tf-checkout-react.esm.js +0 -16116
- package/dist/tf-checkout-react.esm.js.map +0 -1
- package/dist/tf-checkout-styles.css +0 -1
- package/dist/types/add_on.d.ts +0 -8
- package/dist/types/billing-info-data.d.ts +0 -27
- package/dist/types/checkoutPageConfigs.d.ts +0 -13
- package/dist/types/index.d.ts +0 -6
- package/dist/types/order-data.d.ts +0 -15
- package/dist/types/payment-field.d.ts +0 -6
- package/dist/types/payment-plan-configuration.d.ts +0 -18
- package/dist/types/referral-promotion.d.ts +0 -6
- package/dist/types/verification.d.ts +0 -12
- package/dist/utils/adaptStripeErrors.d.ts +0 -8
- package/dist/utils/auth.d.ts +0 -21
- package/dist/utils/cookies.d.ts +0 -3
- package/dist/utils/createCheckoutDataBodyWithDefaultHolder.d.ts +0 -23
- package/dist/utils/createMarkup.d.ts +0 -3
- package/dist/utils/customFields.d.ts +0 -35
- package/dist/utils/downloadPDF.d.ts +0 -1
- package/dist/utils/form.d.ts +0 -3
- package/dist/utils/formikErrorFocus.d.ts +0 -2
- package/dist/utils/getDomain.d.ts +0 -1
- package/dist/utils/getImage.d.ts +0 -1
- package/dist/utils/getQueryVariable.d.ts +0 -1
- package/dist/utils/htmlNodeFromString.d.ts +0 -1
- package/dist/utils/index.d.ts +0 -15
- package/dist/utils/isBrowser.d.ts +0 -1
- package/dist/utils/isValidNumber.d.ts +0 -1
- package/dist/utils/jsonUtils.d.ts +0 -1
- package/dist/utils/replaceVarInString.d.ts +0 -1
- package/dist/utils/setConfigs.d.ts +0 -11
- package/dist/utils/showZero.d.ts +0 -1
- package/dist/validators/index.d.ts +0 -8
- package/src/adapters/customFields.ts +0 -85
- package/src/adapters/index.ts +0 -1
- package/src/api/auth.ts +0 -88
- package/src/api/cart.ts +0 -28
- package/src/api/checkout.ts +0 -28
- package/src/api/common.ts +0 -190
- package/src/api/guestTicketDelegation.ts +0 -79
- package/src/api/index.ts +0 -346
- package/src/api/interceptors.ts +0 -77
- package/src/api/orders.ts +0 -30
- package/src/api/payment.ts +0 -77
- package/src/api/preRegistrationComplete.ts +0 -37
- package/src/api/publicRequest.ts +0 -47
- package/src/api/resale.ts +0 -28
- package/src/assets/images/Polygon.png +0 -0
- package/src/assets/images/cross.svg +0 -44
- package/src/assets/images/done.svg +0 -3
- package/src/assets/images/edit.svg +0 -10
- package/src/assets/images/email.svg +0 -1
- package/src/assets/images/instagram.svg +0 -5
- package/src/assets/images/logo-ttf.png +0 -0
- package/src/assets/images/spotify.svg +0 -6
- package/src/assets/images/user.svg +0 -1
- package/src/assets/images/xmark.svg +0 -92
- package/src/components/account-settings/index.tsx +0 -161
- package/src/components/account-settings/style.css +0 -200
- package/src/components/addonsContainer/AddonComponent.tsx +0 -204
- package/src/components/addonsContainer/SimpleAddonsContainer.tsx +0 -459
- package/src/components/addonsContainer/adapters/index.tsx +0 -62
- package/src/components/addonsContainer/index.tsx +0 -353
- package/src/components/addonsContainer/normalizers/index.ts +0 -5
- package/src/components/addonsContainer/useAddons.ts +0 -248
- package/src/components/addonsContainer/utils/index.tsx +0 -231
- package/src/components/billing-info-container/hooks/index.ts +0 -3
- package/src/components/billing-info-container/hooks/usePaymentContext.ts +0 -22
- package/src/components/billing-info-container/hooks/usePaymentRedirect.ts +0 -145
- package/src/components/billing-info-container/hooks/useStripePayment.ts +0 -119
- package/src/components/billing-info-container/index.tsx +0 -1695
- package/src/components/billing-info-container/style.css +0 -150
- package/src/components/billing-info-container/utils.tsx +0 -413
- package/src/components/common/Checkbox.tsx +0 -51
- package/src/components/common/CheckboxField/index.tsx +0 -41
- package/src/components/common/CopyField.tsx +0 -53
- package/src/components/common/CopyMessageModal.tsx +0 -34
- package/src/components/common/CustomField.tsx +0 -168
- package/src/components/common/DatePickerField.tsx +0 -126
- package/src/components/common/FieldSection/index.tsx +0 -141
- package/src/components/common/FieldSection/utils/index.tsx +0 -92
- package/src/components/common/FormikPhoneNumberField.tsx +0 -51
- package/src/components/common/Loader.tsx +0 -8
- package/src/components/common/ModalComponent/index.tsx +0 -68
- package/src/components/common/NativeSelectFeild/index.tsx +0 -86
- package/src/components/common/PhoneNumberField.tsx +0 -117
- package/src/components/common/PoweredBy.tsx +0 -15
- package/src/components/common/RadioField.tsx +0 -36
- package/src/components/common/RadioGroupField/index.tsx +0 -76
- package/src/components/common/RedirectModal.tsx +0 -43
- package/src/components/common/SelectField/index.tsx +0 -104
- package/src/components/common/SnackbarAlert.tsx +0 -52
- package/src/components/common/index.tsx +0 -11
- package/src/components/common/socials.tsx +0 -33
- package/src/components/confirmModal/index.tsx +0 -60
- package/src/components/confirmationContainer/config.ts +0 -79
- package/src/components/confirmationContainer/index.tsx +0 -349
- package/src/components/confirmationContainer/social-buttons.tsx +0 -149
- package/src/components/confirmationContainer/style.css +0 -206
- package/src/components/countdown/index.tsx +0 -100
- package/src/components/countdown/style.css +0 -10
- package/src/components/delegationsContainer/IssueComponent.tsx +0 -156
- package/src/components/delegationsContainer/IssueTicketForm.tsx +0 -109
- package/src/components/delegationsContainer/TicketsAssignedTable.tsx +0 -55
- package/src/components/delegationsContainer/TicketsAvailableTable.tsx +0 -54
- package/src/components/delegationsContainer/index.tsx +0 -83
- package/src/components/forgotPasswordModal/index.tsx +0 -138
- package/src/components/forgotPasswordModal/style.css +0 -47
- package/src/components/idVerificationContainer/VerificationPendingModal.tsx +0 -47
- package/src/components/idVerificationContainer/constants.ts +0 -27
- package/src/components/idVerificationContainer/index.tsx +0 -323
- package/src/components/index.ts +0 -12
- package/src/components/loginForm/index.tsx +0 -202
- package/src/components/loginModal/SignUpForm.tsx +0 -329
- package/src/components/loginModal/constants.ts +0 -46
- package/src/components/loginModal/index.tsx +0 -263
- package/src/components/loginModal/style.css +0 -116
- package/src/components/myTicketsContainer/index.tsx +0 -228
- package/src/components/myTicketsContainer/row.tsx +0 -46
- package/src/components/myTicketsContainer/style.css +0 -40
- package/src/components/myTicketsContainer/tableConfig.tsx +0 -80
- package/src/components/myTicketsContainer/types.d.ts +0 -41
- package/src/components/orderDetailsContainer/CustomFieldsForm.tsx +0 -75
- package/src/components/orderDetailsContainer/TicketHolderCustomFields.tsx +0 -100
- package/src/components/orderDetailsContainer/index.tsx +0 -586
- package/src/components/orderDetailsContainer/style.css +0 -77
- package/src/components/orderDetailsContainer/ticketsTable.tsx +0 -246
- package/src/components/orderDetailsContainer/utils/index.tsx +0 -55
- package/src/components/paymentContainer/OrderDetails.tsx +0 -277
- package/src/components/paymentContainer/PaymentPlanSection.tsx +0 -169
- package/src/components/paymentContainer/handlePayment.ts +0 -86
- package/src/components/paymentContainer/index.tsx +0 -643
- package/src/components/paymentContainer/style.css +0 -196
- package/src/components/preRegistration/FieldsSection.tsx +0 -150
- package/src/components/preRegistration/Influancers.tsx +0 -34
- package/src/components/preRegistration/PreRegistrationComplete.tsx +0 -186
- package/src/components/preRegistration/PreRegistrationInformations.tsx +0 -60
- package/src/components/preRegistration/Prewards.tsx +0 -41
- package/src/components/preRegistration/ShareOptions.tsx +0 -77
- package/src/components/preRegistration/constants.tsx +0 -169
- package/src/components/preRegistration/index.tsx +0 -330
- package/src/components/preRegistration/style.css +0 -3
- package/src/components/preRegistration/utils.ts +0 -163
- package/src/components/registerForm/adapters/index.tsx +0 -10
- package/src/components/registerForm/constants.tsx +0 -98
- package/src/components/registerForm/index.tsx +0 -188
- package/src/components/registerModal/index.tsx +0 -154
- package/src/components/registerModal/style.css +0 -18
- package/src/components/resetPasswordContainer/index.tsx +0 -102
- package/src/components/resetPasswordContainer/style.css +0 -36
- package/src/components/rsvpContainer/index.tsx +0 -126
- package/src/components/seatMapContainer/SeatMapComponent.tsx +0 -77
- package/src/components/seatMapContainer/TicketsSection.tsx +0 -286
- package/src/components/seatMapContainer/addToCart.ts +0 -66
- package/src/components/seatMapContainer/index.tsx +0 -443
- package/src/components/seatMapContainer/utils.ts +0 -133
- package/src/components/signupModal/index.tsx +0 -178
- package/src/components/signupModal/style.css +0 -58
- package/src/components/ticketResale/index.tsx +0 -86
- package/src/components/ticketResaleModal/index.tsx +0 -224
- package/src/components/ticketResaleModal/style.css +0 -28
- package/src/components/ticketsContainer/AccessCodeSection.tsx +0 -50
- package/src/components/ticketsContainer/InfoIcon.tsx +0 -35
- package/src/components/ticketsContainer/PromoCodeSection.tsx +0 -116
- package/src/components/ticketsContainer/ReferralLogic.tsx +0 -33
- package/src/components/ticketsContainer/TicketRow.tsx +0 -107
- package/src/components/ticketsContainer/TicketsSection.tsx +0 -350
- package/src/components/ticketsContainer/TimeSlotTicketRow.tsx +0 -243
- package/src/components/ticketsContainer/TimeSlotsSection.tsx +0 -194
- package/src/components/ticketsContainer/index.tsx +0 -986
- package/src/components/ticketsContainer/style.css +0 -178
- package/src/components/ticketsContainer/utils.ts +0 -35
- package/src/components/timerWidget/index.tsx +0 -97
- package/src/components/timerWidget/style.css +0 -35
- package/src/components/waitingList/index.tsx +0 -190
- package/src/components/waitingList/style.css +0 -27
- package/src/constants/index.ts +0 -3
- package/src/env.ts +0 -29
- package/src/hoc/CustomFields/index.tsx +0 -85
- package/src/hoc/index.ts +0 -1
- package/src/hooks/index.ts +0 -1
- package/src/hooks/useCookieListener.ts +0 -32
- package/src/hooks/useEventListener.ts +0 -32
- package/src/hooks/useLocalStorageListener.ts +0 -32
- package/src/hooks/useOnline.ts +0 -21
- package/src/hooks/usePixel.ts +0 -88
- package/src/normalizers/index.ts +0 -49
- package/src/types/add_on.ts +0 -8
- package/src/types/api/auth.d.ts +0 -63
- package/src/types/api/axiosResponse.d.ts +0 -6
- package/src/types/api/cart.d.ts +0 -73
- package/src/types/api/checkout.d.ts +0 -92
- package/src/types/api/common.d.ts +0 -153
- package/src/types/api/guestTicketDelegation.d.ts +0 -18
- package/src/types/api/orders.d.ts +0 -116
- package/src/types/api/payment.d.ts +0 -180
- package/src/types/api/preRegistration.d.ts +0 -11
- package/src/types/api/preRegistrationComplete.d.ts +0 -95
- package/src/types/api/ticketResale.d.ts +0 -13
- package/src/types/billing-info-data.ts +0 -37
- package/src/types/checkoutPageConfigs.ts +0 -13
- package/src/types/formFields.d.ts +0 -29
- package/src/types/index.ts +0 -6
- package/src/types/order-data.ts +0 -16
- package/src/types/payment-field.ts +0 -7
- package/src/types/payment-plan-configuration.ts +0 -19
- package/src/types/pre-registration-complete.d.ts +0 -47
- package/src/types/referral-promotion.ts +0 -7
- package/src/types/seatMap.d.ts +0 -159
- package/src/types/verification.ts +0 -12
- package/src/utils/adaptStripeErrors.ts +0 -26
- package/src/utils/auth.ts +0 -45
- package/src/utils/cookies.ts +0 -73
- package/src/utils/createCheckoutDataBodyWithDefaultHolder.ts +0 -81
- package/src/utils/createMarkup.ts +0 -1
- package/src/utils/customFields.ts +0 -80
- package/src/utils/downloadPDF.tsx +0 -42
- package/src/utils/form.ts +0 -34
- package/src/utils/formikErrorFocus.ts +0 -24
- package/src/utils/getDomain.ts +0 -21
- package/src/utils/getImage.ts +0 -14
- package/src/utils/getQueryVariable.ts +0 -13
- package/src/utils/htmlNodeFromString.ts +0 -6
- package/src/utils/index.ts +0 -15
- package/src/utils/isBrowser.ts +0 -2
- package/src/utils/isValidNumber.ts +0 -8
- package/src/utils/jsonUtils.ts +0 -8
- package/src/utils/replaceVarInString.ts +0 -9
- package/src/utils/setConfigs.ts +0 -24
- package/src/utils/showZero.tsx +0 -10
- package/src/validators/index.ts +0 -61
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React, { type FC } from 'react'
|
|
2
|
+
|
|
3
|
+
import { hooks } from 'tf-checkout-shared'
|
|
4
|
+
|
|
5
|
+
import { Card } from '../ui/Card'
|
|
6
|
+
import { useCheckoutContext } from '../ui/CheckoutProvider'
|
|
7
|
+
|
|
8
|
+
export interface OrderSummaryProps {
|
|
9
|
+
eventId?: string
|
|
10
|
+
className?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Storefront-compatible order summary shown after the checkout controls. */
|
|
14
|
+
export const OrderSummary: FC<OrderSummaryProps> = ({ eventId, className }) => {
|
|
15
|
+
const checkout = useCheckoutContext()
|
|
16
|
+
const event = hooks.useEvent(eventId)
|
|
17
|
+
const breakdown = checkout.breakdown
|
|
18
|
+
const detailed = checkout.priceBreakdown
|
|
19
|
+
|
|
20
|
+
if (!breakdown || breakdown.tickets.length === 0) return null
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<Card className={['tf-checkout-order-summary', className].filter(Boolean).join(' ')}>
|
|
24
|
+
{event.data?.name && (
|
|
25
|
+
<div className="tf-checkout-order-summary__group">
|
|
26
|
+
<div className="tf-checkout-order-summary__label">Event</div>
|
|
27
|
+
<div className="tf-checkout-order-summary__event">{event.data.name}</div>
|
|
28
|
+
</div>
|
|
29
|
+
)}
|
|
30
|
+
|
|
31
|
+
<div className="tf-checkout-order-summary__group">
|
|
32
|
+
<div className="tf-checkout-order-summary__label">Your Tickets</div>
|
|
33
|
+
{breakdown.tickets.map((ticket, index) => {
|
|
34
|
+
const detail = detailed?.tickets[index]
|
|
35
|
+
return (
|
|
36
|
+
<div
|
|
37
|
+
key={`${ticket.ticketTypeId}-${index}`}
|
|
38
|
+
className="tf-checkout-order-summary__item"
|
|
39
|
+
>
|
|
40
|
+
<div className="tf-checkout-order-summary__ticket">
|
|
41
|
+
{ticket.quantity} x {ticket.name} - {detail?.unit_price ?? ticket.unit}{' '}
|
|
42
|
+
each
|
|
43
|
+
</div>
|
|
44
|
+
{detail?.line_total && detail.line_total !== detail.unit_price && (
|
|
45
|
+
<div className="tf-checkout-order-summary__fees">
|
|
46
|
+
({detail.line_total} with fees)
|
|
47
|
+
</div>
|
|
48
|
+
)}
|
|
49
|
+
</div>
|
|
50
|
+
)
|
|
51
|
+
})}
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
{(detailed?.add_ons.length ?? 0) > 0 && (
|
|
55
|
+
<div className="tf-checkout-order-summary__group">
|
|
56
|
+
<div className="tf-checkout-order-summary__label">
|
|
57
|
+
{detailed?.add_ons_title || 'Add-ons'}
|
|
58
|
+
</div>
|
|
59
|
+
{detailed?.add_ons.map((addOn, index) => (
|
|
60
|
+
<div
|
|
61
|
+
key={`${addOn.name}-${index}`}
|
|
62
|
+
className="tf-checkout-order-summary__item"
|
|
63
|
+
>
|
|
64
|
+
<div className="tf-checkout-order-summary__ticket">
|
|
65
|
+
{addOn.quantity} x {addOn.name}
|
|
66
|
+
{addOn.show_price ? ` - ${addOn.unit_price} each` : ''}
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
))}
|
|
70
|
+
</div>
|
|
71
|
+
)}
|
|
72
|
+
|
|
73
|
+
<div className="tf-checkout-order-summary__total">
|
|
74
|
+
<div className="tf-checkout-order-summary__label">Total</div>
|
|
75
|
+
<div className="tf-checkout-order-summary__amount">
|
|
76
|
+
{detailed?.total ??
|
|
77
|
+
`${breakdown.currencySymbol || `${breakdown.currencyCode} `}${Number(
|
|
78
|
+
breakdown.total
|
|
79
|
+
).toFixed(2)}`}
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</Card>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<Payment/>` — order review + payment-method selection + T&C, then place the
|
|
3
|
+
* order. Buyer details come from `<BillingInfo/>` (read back off the shared
|
|
4
|
+
* checkout). The method list is built from the event's gateway + `preOrderMethods()`
|
|
5
|
+
* + the priced snapshot, and each selection maps to the right `placeOrder` rail
|
|
6
|
+
* (Stripe card, Xendit card/e-wallet, PayPal, redirect rails, payment plan).
|
|
7
|
+
*/
|
|
8
|
+
import React, { useEffect, useMemo, useRef, useState, type FC } from 'react'
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
XENDIT_EWALLET_EXTRA_FIELD,
|
|
12
|
+
errorMessage,
|
|
13
|
+
missingCheckoutDetails,
|
|
14
|
+
paymentPlanTerms,
|
|
15
|
+
planConfirmLabel,
|
|
16
|
+
planTermsTitle,
|
|
17
|
+
ttf,
|
|
18
|
+
type EventCondition,
|
|
19
|
+
type PaymentRequest,
|
|
20
|
+
type PlaceOrderResult,
|
|
21
|
+
} from 'tf-checkout-shared'
|
|
22
|
+
|
|
23
|
+
import { CardInput, StripePaymentElement, XenditCardInput } from '../CardInput'
|
|
24
|
+
import { Button } from '../ui/Button'
|
|
25
|
+
import { Card, SectionHeader } from '../ui/Card'
|
|
26
|
+
import { useCheckoutContext, useCheckoutValidation } from '../ui/CheckoutProvider'
|
|
27
|
+
import { Checkbox } from '../ui/Field'
|
|
28
|
+
import { cx } from '../ui/cx'
|
|
29
|
+
import { Loader } from '../ui/Loader'
|
|
30
|
+
import { PaymentMethods, type PaymentMethodOption } from '../ui/PaymentMethods'
|
|
31
|
+
import { OrderSummary } from './OrderSummary'
|
|
32
|
+
|
|
33
|
+
type PreOrderMethods = Awaited<ReturnType<typeof ttf.cart.preOrderMethods>>
|
|
34
|
+
|
|
35
|
+
/** Server spellings → display labels for Stripe redirect rails. */
|
|
36
|
+
const REDIRECT_LABELS: Record<string, string> = {
|
|
37
|
+
klarna: 'Klarna',
|
|
38
|
+
afterpay_clearpay: 'Afterpay / Clearpay',
|
|
39
|
+
affirm: 'Affirm',
|
|
40
|
+
ideal: 'iDEAL',
|
|
41
|
+
bancontact: 'Bancontact',
|
|
42
|
+
eps: 'EPS',
|
|
43
|
+
giropay: 'giropay',
|
|
44
|
+
p24: 'Przelewy24',
|
|
45
|
+
alipay: 'Alipay',
|
|
46
|
+
grabpay: 'GrabPay',
|
|
47
|
+
cashapp: 'Cash App Pay',
|
|
48
|
+
paypal: 'PayPal',
|
|
49
|
+
}
|
|
50
|
+
const redirectLabel = (t: string) =>
|
|
51
|
+
REDIRECT_LABELS[t] ?? t.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase())
|
|
52
|
+
|
|
53
|
+
const isCardFieldError = (message: string) =>
|
|
54
|
+
/(?:card|expiry|expiration|security code|cvc).*(?:incomplete|required|invalid)|(?:incomplete|required|invalid).*(?:card|expiry|expiration|security code|cvc)/i.test(
|
|
55
|
+
message
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Anything the buyer would fix AT THE CARD FIELD — a bad number, a wrong CVC,
|
|
60
|
+
* an expired or declined card, missing details. These belong beside the card
|
|
61
|
+
* box; only order-level problems (conditions, billing, network) belong down by
|
|
62
|
+
* the pay button, where a card message is easy to miss entirely.
|
|
63
|
+
*/
|
|
64
|
+
const isCardAreaError = (message: string) =>
|
|
65
|
+
isCardFieldError(message) ||
|
|
66
|
+
/card details|security code|cvc|cvv|expired|declined|not accepted|balance|stolen|inactive|authentication failed|name on the card|different card/i.test(
|
|
67
|
+
message
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
export interface PaymentProps {
|
|
71
|
+
onComplete?: (order: PlaceOrderResult) => void
|
|
72
|
+
/** Optional event context adds the event name to the order review. */
|
|
73
|
+
eventId?: string
|
|
74
|
+
/** Must match the policy used by the paired `<BillingInfo/>`. */
|
|
75
|
+
requireEmailConfirmation?: boolean
|
|
76
|
+
/** Must match the holder policy used by the paired `<BillingInfo/>`. */
|
|
77
|
+
collectTicketHolders?: boolean
|
|
78
|
+
/** Must match the holder-contact policy used by the paired `<BillingInfo/>`. */
|
|
79
|
+
requireHolderContact?: boolean
|
|
80
|
+
/** Host-owned acknowledgements such as a brand privacy policy. */
|
|
81
|
+
additionalConditions?: EventCondition[]
|
|
82
|
+
/** Require the privacy checkbox managed by BillingInfo before payment. */
|
|
83
|
+
requirePrivacyPolicy?: boolean
|
|
84
|
+
/** Called when payment is attempted before required privacy consent. */
|
|
85
|
+
onPrivacyRequired?: () => void
|
|
86
|
+
/** Test card copy is useful in examples, but should never appear by default. */
|
|
87
|
+
showTestCardHint?: boolean
|
|
88
|
+
className?: string
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export const Payment: FC<PaymentProps> = ({
|
|
92
|
+
onComplete,
|
|
93
|
+
eventId,
|
|
94
|
+
requireEmailConfirmation = false,
|
|
95
|
+
collectTicketHolders = false,
|
|
96
|
+
requireHolderContact = false,
|
|
97
|
+
additionalConditions = [],
|
|
98
|
+
requirePrivacyPolicy = false,
|
|
99
|
+
onPrivacyRequired,
|
|
100
|
+
showTestCardHint = false,
|
|
101
|
+
className,
|
|
102
|
+
}) => {
|
|
103
|
+
const checkout = useCheckoutContext()
|
|
104
|
+
const validation = useCheckoutValidation()
|
|
105
|
+
const [error, setError] = useState('')
|
|
106
|
+
/** Field-level problem reported by `<XenditCardInput/>` (it renders none). */
|
|
107
|
+
const [cardFieldError, setCardFieldError] = useState<string | null>(null)
|
|
108
|
+
/** Card problem from the last pay attempt (declined, wrong CVC, expired…). */
|
|
109
|
+
const [payCardError, setPayCardError] = useState<string | null>(null)
|
|
110
|
+
/** True once the buyer edits the card after a failed attempt — their fix
|
|
111
|
+
* supersedes both our message and the engine's stale one. */
|
|
112
|
+
const [cardEditedSincePay, setCardEditedSincePay] = useState(true)
|
|
113
|
+
const [methods, setMethods] = useState<PreOrderMethods | null>(null)
|
|
114
|
+
const [selected, setSelected] = useState('')
|
|
115
|
+
const [extras, setExtras] = useState<
|
|
116
|
+
Record<string, { cashtag?: string; phoneNumber?: string }>
|
|
117
|
+
>({})
|
|
118
|
+
const [useSavedCard, setUseSavedCard] = useState(true)
|
|
119
|
+
const cardSectionRef = useRef<HTMLDivElement | null>(null)
|
|
120
|
+
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
let active = true
|
|
123
|
+
ttf.cart
|
|
124
|
+
.preOrderMethods()
|
|
125
|
+
.then((m) => active && setMethods(m))
|
|
126
|
+
.catch(() => active && setMethods(null))
|
|
127
|
+
return () => {
|
|
128
|
+
active = false
|
|
129
|
+
}
|
|
130
|
+
}, [])
|
|
131
|
+
|
|
132
|
+
const bd = checkout.breakdown
|
|
133
|
+
const isFree = bd != null && Number(bd.total) === 0
|
|
134
|
+
const isXendit = checkout.gateway === 'xendit'
|
|
135
|
+
|
|
136
|
+
// Build the selectable method rows from gateway + probe + snapshot.
|
|
137
|
+
const options = useMemo<PaymentMethodOption[]>(() => {
|
|
138
|
+
if (isFree || !methods) return []
|
|
139
|
+
const out: PaymentMethodOption[] = []
|
|
140
|
+
if (isXendit) {
|
|
141
|
+
if (methods.xenditCard)
|
|
142
|
+
out.push({ id: { kind: 'xenditCard' }, value: 'xenditCard', label: 'Card' })
|
|
143
|
+
for (const w of methods.xenditEWallets)
|
|
144
|
+
out.push({
|
|
145
|
+
id: { kind: 'xenditEWallet', walletKey: w.key },
|
|
146
|
+
value: `ew:${w.key}`,
|
|
147
|
+
label: w.label,
|
|
148
|
+
iconUrl: w.icon,
|
|
149
|
+
extra: (XENDIT_EWALLET_EXTRA_FIELD[w.key] as 'cashtag' | 'phoneNumber') ?? null,
|
|
150
|
+
})
|
|
151
|
+
} else if (checkout.gateway === 'razorpay') {
|
|
152
|
+
out.push({ id: { kind: 'razorpay' }, value: 'razorpay', label: 'Razorpay' })
|
|
153
|
+
} else {
|
|
154
|
+
// Stripe (or unknown → fail open to card).
|
|
155
|
+
out.push({ id: { kind: 'card' }, value: 'card', label: 'Card' })
|
|
156
|
+
for (const t of checkout.redirectPaymentMethods ?? []) {
|
|
157
|
+
if (t === 'paypal' && methods.paypal) continue // dedupe vs PayPal Standard
|
|
158
|
+
out.push({
|
|
159
|
+
id: { kind: 'redirect', methodType: t },
|
|
160
|
+
value: `rd:${t}`,
|
|
161
|
+
label: redirectLabel(t),
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
if (checkout.paymentPlan?.available)
|
|
165
|
+
out.push({
|
|
166
|
+
id: { kind: 'paymentPlan' },
|
|
167
|
+
value: 'paymentPlan',
|
|
168
|
+
label: 'Payment plan',
|
|
169
|
+
})
|
|
170
|
+
// OXXO cash voucher — Mexico only, and never listed in redirect methods.
|
|
171
|
+
if (checkout.breakdown?.currencyCode === 'MXN')
|
|
172
|
+
out.push({ id: { kind: 'oxxo' }, value: 'oxxo', label: 'OXXO (pay in cash)' })
|
|
173
|
+
}
|
|
174
|
+
if (methods.paypal)
|
|
175
|
+
out.push({ id: { kind: 'paypal' }, value: 'paypal', label: 'PayPal' })
|
|
176
|
+
return out
|
|
177
|
+
}, [
|
|
178
|
+
isFree,
|
|
179
|
+
methods,
|
|
180
|
+
isXendit,
|
|
181
|
+
checkout.gateway,
|
|
182
|
+
checkout.redirectPaymentMethods,
|
|
183
|
+
checkout.paymentPlan,
|
|
184
|
+
checkout.breakdown,
|
|
185
|
+
])
|
|
186
|
+
|
|
187
|
+
// Default the selection to the first row once options exist.
|
|
188
|
+
useEffect(() => {
|
|
189
|
+
if (options.length && !options.some((o) => o.value === selected))
|
|
190
|
+
setSelected(options[0].value)
|
|
191
|
+
}, [options, selected])
|
|
192
|
+
|
|
193
|
+
const cardValue = isXendit ? 'xenditCard' : 'card'
|
|
194
|
+
const selectedOption = options.find((o) => o.value === selected)
|
|
195
|
+
const isCardSelected = selected === cardValue
|
|
196
|
+
const isPlanSelected = selectedOption?.id.kind === 'paymentPlan'
|
|
197
|
+
const savedLast4 = checkout.paymentPlan?.savedCardLast4
|
|
198
|
+
/**
|
|
199
|
+
* "Reuse the card on file" only means something when there IS one. The raw
|
|
200
|
+
* `useSavedCard` toggle defaults to true (matching the hosted default-checked
|
|
201
|
+
* box), so reading it directly made a first-time buyer look like a returning
|
|
202
|
+
* one: `inputSlotFor` collapsed to undefined, the card field and the plan
|
|
203
|
+
* terms never mounted, and "Confirm Payment Plan" then failed in the
|
|
204
|
+
* confirmer with "No card element registered". Always gate on the card.
|
|
205
|
+
*/
|
|
206
|
+
const reuseSavedCard = Boolean(savedLast4) && useSavedCard
|
|
207
|
+
const usesCardDetails = !isFree && (isCardSelected || (isPlanSelected && !reuseSavedCard))
|
|
208
|
+
// Stripe's CardElement renders its own inline "Card number is invalid" text, so
|
|
209
|
+
// the pay handler can stay quiet and just scroll to it. Xendit's card form is
|
|
210
|
+
// three plain inputs with no error slot — there, the message MUST be shown or
|
|
211
|
+
// a declined/invalid card looks like the button simply did nothing.
|
|
212
|
+
const cardWidgetReportsItsOwnError = usesCardDetails && !isXendit
|
|
213
|
+
|
|
214
|
+
// Wallets the old-checkout way: when the event's `stripe_wallets` gate is on,
|
|
215
|
+
// render the Payment Element (card + Apple/Google Pay in one widget) instead of
|
|
216
|
+
// the bare card field. Held back for MXN so the CardElement-based MSI path
|
|
217
|
+
// (Mexican months-without-interest) keeps working — matching old, which didn't
|
|
218
|
+
// run MSI off its web Payment Element either.
|
|
219
|
+
const hasWallets = Boolean(
|
|
220
|
+
checkout.walletAvailability?.applePay || checkout.walletAvailability?.googlePay
|
|
221
|
+
)
|
|
222
|
+
// A payment plan NEVER rides the wallet element, matching the hosted flow's
|
|
223
|
+
// `wallets: planMode ? { applePay: 'never', googlePay: 'never' }`. Two
|
|
224
|
+
// reasons, both load-bearing:
|
|
225
|
+
// 1. A plan charges installments off-session, so it needs a REUSABLE card.
|
|
226
|
+
// Wallet tokens are typically single-use and can't be charged later.
|
|
227
|
+
// 2. Mechanically: the plan rail confirms via `confirmer.getCardElement()`,
|
|
228
|
+
// which only exists for `<CardInput/>`. The Payment Element registers
|
|
229
|
+
// itself through `registerPaymentElement` instead, so mounting it here
|
|
230
|
+
// made `confirmPaymentPlanSetup` throw "No card element registered" —
|
|
231
|
+
// the plan could never be completed on a wallet-enabled event.
|
|
232
|
+
const useWalletElement =
|
|
233
|
+
!isXendit &&
|
|
234
|
+
!isPlanSelected &&
|
|
235
|
+
hasWallets &&
|
|
236
|
+
checkout.breakdown?.currencyCode !== 'MXN' &&
|
|
237
|
+
Boolean((checkout.stripeElementsConfig as { mode?: unknown } | undefined)?.mode)
|
|
238
|
+
|
|
239
|
+
// When there's only ONE way to pay and it's a card, there's nothing to
|
|
240
|
+
// *choose* — the old checkout just showed the card form, no radio wrapper.
|
|
241
|
+
// Render the field directly; keep the radio list only when there's a choice.
|
|
242
|
+
const onlyOption = options.length === 1 ? options[0] : undefined
|
|
243
|
+
const singleCard =
|
|
244
|
+
onlyOption?.id.kind === 'card' || onlyOption?.id.kind === 'xenditCard'
|
|
245
|
+
|
|
246
|
+
// Gating: buyer + every configured holder/custom field complete, required
|
|
247
|
+
// conditions accepted, and payment-method extras filled.
|
|
248
|
+
const b = checkout.billing
|
|
249
|
+
const missingDetails = missingCheckoutDetails(
|
|
250
|
+
{
|
|
251
|
+
billing: b,
|
|
252
|
+
holders: checkout.holders,
|
|
253
|
+
selections: checkout.selections,
|
|
254
|
+
breakdown: checkout.breakdown,
|
|
255
|
+
holderNamesRequired: checkout.holderNamesRequired,
|
|
256
|
+
fieldGates: checkout.fieldGates,
|
|
257
|
+
customFields: checkout.customFields,
|
|
258
|
+
},
|
|
259
|
+
{ requireEmailConfirmation, collectTicketHolders, requireHolderContact }
|
|
260
|
+
)
|
|
261
|
+
const billingReady = missingDetails.length === 0
|
|
262
|
+
const conditions = [...(checkout.conditions ?? []), ...additionalConditions]
|
|
263
|
+
const requiredConditions = conditions.filter((c) => c.isRequired)
|
|
264
|
+
const conditionsOk = requiredConditions.every(
|
|
265
|
+
(c) => checkout.acceptedConditions?.[c.id]
|
|
266
|
+
)
|
|
267
|
+
const extra = selectedOption?.extra
|
|
268
|
+
const extrasOk =
|
|
269
|
+
!extra ||
|
|
270
|
+
(extra === 'cashtag'
|
|
271
|
+
? Boolean(extras[selected]?.cashtag?.trim())
|
|
272
|
+
: Boolean(extras[selected]?.phoneNumber?.trim()))
|
|
273
|
+
const privacyOk = !requirePrivacyPolicy || checkout.consent.ttfOptIn
|
|
274
|
+
// The plan CTA carries the brand's terminology ("Confirm Layaway"), and the
|
|
275
|
+
// terms sentence quotes this same label — they must agree.
|
|
276
|
+
const payLabel = isFree
|
|
277
|
+
? 'Complete Registration'
|
|
278
|
+
: isPlanSelected
|
|
279
|
+
? planConfirmLabel(checkout.paymentPlan?.configuration)
|
|
280
|
+
: 'Submit Payment'
|
|
281
|
+
const planTerms = paymentPlanTerms(checkout.paymentPlan?.configuration)
|
|
282
|
+
// 'placing' → order in flight; 'succeeded' → placed, host navigating away.
|
|
283
|
+
// Both keep the CTA busy so it can't be re-clicked during the handoff.
|
|
284
|
+
const finalizing = checkout.status === 'placing' || checkout.status === 'succeeded'
|
|
285
|
+
|
|
286
|
+
const pay = () => {
|
|
287
|
+
setError('')
|
|
288
|
+
setPayCardError(null)
|
|
289
|
+
setCardEditedSincePay(false)
|
|
290
|
+
if (!billingReady || !conditionsOk || !extrasOk || !privacyOk) {
|
|
291
|
+
if (!privacyOk) onPrivacyRequired?.()
|
|
292
|
+
validation.requestValidation()
|
|
293
|
+
return
|
|
294
|
+
}
|
|
295
|
+
// Backfill one holder per ticket if billing was set without holders.
|
|
296
|
+
if (checkout.holders.length === 0 && b) {
|
|
297
|
+
const n = checkout.selections.reduce((acc, s) => acc + s.quantity, 0) || 1
|
|
298
|
+
checkout.setHolders(
|
|
299
|
+
Array.from({ length: n }, () => ({
|
|
300
|
+
firstName: b.firstName,
|
|
301
|
+
lastName: b.lastName,
|
|
302
|
+
email: b.email,
|
|
303
|
+
}))
|
|
304
|
+
)
|
|
305
|
+
}
|
|
306
|
+
let req: PaymentRequest = { method: 'auto' }
|
|
307
|
+
const id = selectedOption?.id
|
|
308
|
+
if (isFree || !id) req = { method: 'auto' }
|
|
309
|
+
else if (id.kind === 'xenditEWallet')
|
|
310
|
+
req = { method: 'xenditEWallet', walletKey: id.walletKey, extra: extras[selected] }
|
|
311
|
+
else if (id.kind === 'redirect')
|
|
312
|
+
req = { method: 'redirect', methodType: id.methodType }
|
|
313
|
+
else if (id.kind === 'paymentPlan')
|
|
314
|
+
req = { method: 'paymentPlan', useSavedCard: reuseSavedCard }
|
|
315
|
+
else req = { method: id.kind } as PaymentRequest
|
|
316
|
+
// NOTE: called synchronously in the click handler — popup rails (e-wallet,
|
|
317
|
+
// redirect, razorpay, paypal) need the user gesture or the browser blocks them.
|
|
318
|
+
checkout
|
|
319
|
+
.placeOrder(req)
|
|
320
|
+
.then((order) => onComplete?.(order))
|
|
321
|
+
.catch((e) => {
|
|
322
|
+
const message = errorMessage(e)
|
|
323
|
+
if (usesCardDetails && isCardAreaError(message)) {
|
|
324
|
+
const behavior = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches
|
|
325
|
+
? 'auto'
|
|
326
|
+
: 'smooth'
|
|
327
|
+
cardSectionRef.current?.scrollIntoView({ behavior, block: 'center' })
|
|
328
|
+
// Stripe's CardElement prints the message inside its own widget, so
|
|
329
|
+
// scrolling to it is enough. Ours doesn't — show it AT the card box,
|
|
330
|
+
// not down by the button where it reads as an unrelated failure.
|
|
331
|
+
if (cardWidgetReportsItsOwnError) return
|
|
332
|
+
setPayCardError(message)
|
|
333
|
+
return
|
|
334
|
+
}
|
|
335
|
+
setError(message)
|
|
336
|
+
})
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// A card problem surfaced by the engine belongs at the box too, not under the
|
|
340
|
+
// pay button — same rule as the ones we catch in `pay()`.
|
|
341
|
+
// Whether the engine's error is a CARD problem at all. This must not depend on
|
|
342
|
+
// whether the buyer has since edited: the edit decides if we still SHOW it, it
|
|
343
|
+
// must never decide whether it may fall through to the button (it may not — a
|
|
344
|
+
// card error belongs at the card, always).
|
|
345
|
+
const engineErrorIsCardArea = Boolean(
|
|
346
|
+
isXendit && checkout.error && isCardAreaError(checkout.error)
|
|
347
|
+
)
|
|
348
|
+
const engineCardError =
|
|
349
|
+
engineErrorIsCardArea && !cardEditedSincePay ? checkout.error : ''
|
|
350
|
+
// The buyer's live field state wins over a stale decline from a past attempt.
|
|
351
|
+
const cardAreaError = cardFieldError || payCardError || engineCardError
|
|
352
|
+
const checkoutError =
|
|
353
|
+
checkout.error &&
|
|
354
|
+
!(cardWidgetReportsItsOwnError && isCardFieldError(checkout.error)) &&
|
|
355
|
+
!engineErrorIsCardArea
|
|
356
|
+
? checkout.error
|
|
357
|
+
: ''
|
|
358
|
+
// Single gate for the message under the pay button: whatever the source, a
|
|
359
|
+
// card problem is shown at the card and NEVER duplicated down here.
|
|
360
|
+
const bottomMessage = error || checkoutError
|
|
361
|
+
const bottomError =
|
|
362
|
+
bottomMessage && isXendit && isCardAreaError(bottomMessage) ? '' : bottomMessage
|
|
363
|
+
|
|
364
|
+
// The card/plan field + its note + plan terms. Rendered inside the selected
|
|
365
|
+
// method row when there's a choice, or directly when there's only a card.
|
|
366
|
+
const paymentInput = (
|
|
367
|
+
<>
|
|
368
|
+
{(isCardSelected || (isPlanSelected && !reuseSavedCard)) && (
|
|
369
|
+
<>
|
|
370
|
+
{useWalletElement ? (
|
|
371
|
+
// The Payment Element frames itself — don't box it again.
|
|
372
|
+
<div ref={cardSectionRef} className="tf-checkout-payment__element">
|
|
373
|
+
<StripePaymentElement
|
|
374
|
+
elementsConfig={checkout.stripeElementsConfig}
|
|
375
|
+
wallets={checkout.walletAvailability}
|
|
376
|
+
/>
|
|
377
|
+
</div>
|
|
378
|
+
) : (
|
|
379
|
+
<div
|
|
380
|
+
ref={cardSectionRef}
|
|
381
|
+
className={cx(
|
|
382
|
+
'tf-checkout-payment__cardbox',
|
|
383
|
+
isXendit && cardAreaError && 'tf-checkout-payment__cardbox--invalid'
|
|
384
|
+
)}
|
|
385
|
+
>
|
|
386
|
+
{isXendit ? (
|
|
387
|
+
<XenditCardInput
|
|
388
|
+
onValidationChange={(m) => {
|
|
389
|
+
setCardFieldError(m)
|
|
390
|
+
// Any edit supersedes the previous attempt's verdict — the
|
|
391
|
+
// buyer shouldn't be told a number is invalid while they're
|
|
392
|
+
// typing a valid one.
|
|
393
|
+
setPayCardError(null)
|
|
394
|
+
setCardEditedSincePay(true)
|
|
395
|
+
}}
|
|
396
|
+
/>
|
|
397
|
+
) : (
|
|
398
|
+
<CardInput />
|
|
399
|
+
)}
|
|
400
|
+
</div>
|
|
401
|
+
)}
|
|
402
|
+
{/* Below the box, not inside it — Stripe prints its own message within
|
|
403
|
+
its iframe, but the Xendit fields are ours to caption. */}
|
|
404
|
+
{isXendit && cardAreaError && (
|
|
405
|
+
<p className="tf-checkout-error-text">{cardAreaError}</p>
|
|
406
|
+
)}
|
|
407
|
+
{showTestCardHint && (
|
|
408
|
+
<p className="tf-checkout-note">
|
|
409
|
+
{isXendit
|
|
410
|
+
? 'Test card: 4000 0000 0000 0002 · any future date · any CVC'
|
|
411
|
+
: 'Test card: 4242 4242 4242 4242 · any future date · any CVC'}
|
|
412
|
+
</p>
|
|
413
|
+
)}
|
|
414
|
+
</>
|
|
415
|
+
)}
|
|
416
|
+
{/* The full hosted terms block. Composed in the SDK so web and native
|
|
417
|
+
state the identical schedule, admin fee and non-refundable clause —
|
|
418
|
+
these are disclosures, not summary copy. */}
|
|
419
|
+
{isPlanSelected && planTerms.length > 0 && (
|
|
420
|
+
<div className="tf-checkout-payment__plan">
|
|
421
|
+
<strong>{planTermsTitle(checkout.paymentPlan!.configuration!)}</strong>
|
|
422
|
+
{planTerms.map((part) => (
|
|
423
|
+
<p key={part.id} className="tf-checkout-note">
|
|
424
|
+
{part.text}
|
|
425
|
+
</p>
|
|
426
|
+
))}
|
|
427
|
+
</div>
|
|
428
|
+
)}
|
|
429
|
+
{/* Returning plan buyer with a reusable card on file. Ticked by default,
|
|
430
|
+
and when it stays ticked the card fields never mount — the plan is
|
|
431
|
+
created straight off the saved payment method, like the hosted flow. */}
|
|
432
|
+
{isPlanSelected && savedLast4 && (
|
|
433
|
+
<div className="tf-checkout-payment__plan-savedcard">
|
|
434
|
+
<p className="tf-checkout-note">
|
|
435
|
+
Your payment will proceed with the card ending in{' '}
|
|
436
|
+
<strong>**** {savedLast4}</strong>
|
|
437
|
+
</p>
|
|
438
|
+
<Checkbox
|
|
439
|
+
checked={useSavedCard}
|
|
440
|
+
onChange={(e) => setUseSavedCard(e.target.checked)}
|
|
441
|
+
label="Use this card"
|
|
442
|
+
/>
|
|
443
|
+
</div>
|
|
444
|
+
)}
|
|
445
|
+
</>
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
return (
|
|
449
|
+
<div className={['tf-checkout-payment-stack', className].filter(Boolean).join(' ')}>
|
|
450
|
+
<Card>
|
|
451
|
+
<SectionHeader title="Payment" />
|
|
452
|
+
|
|
453
|
+
{/* Payment method */}
|
|
454
|
+
{!isFree && (
|
|
455
|
+
<>
|
|
456
|
+
{!singleCard && (
|
|
457
|
+
<div className="tf-checkout-payment__label">Payment method</div>
|
|
458
|
+
)}
|
|
459
|
+
{!methods ? (
|
|
460
|
+
<div className="tf-checkout-payment__cardbox">
|
|
461
|
+
<Loader />
|
|
462
|
+
</div>
|
|
463
|
+
) : singleCard ? (
|
|
464
|
+
paymentInput
|
|
465
|
+
) : (
|
|
466
|
+
<PaymentMethods
|
|
467
|
+
options={options}
|
|
468
|
+
selected={selected}
|
|
469
|
+
onSelect={setSelected}
|
|
470
|
+
extraValues={extras}
|
|
471
|
+
onExtraChange={(v, patch) =>
|
|
472
|
+
setExtras((s) => ({ ...s, [v]: { ...s[v], ...patch } }))
|
|
473
|
+
}
|
|
474
|
+
showValidation={validation.attempt > 0}
|
|
475
|
+
/**
|
|
476
|
+
* The plan row ALWAYS owns the slot when it's selected. This
|
|
477
|
+
* used to be conditional on needing a card field, which meant a
|
|
478
|
+
* returning buyer reusing their saved card got `undefined` — and
|
|
479
|
+
* `undefined` renders the children nowhere, so the plan terms
|
|
480
|
+
* AND the "card ending in ****" block both vanished. The card
|
|
481
|
+
* field inside is separately gated on `!reuseSavedCard`, so the
|
|
482
|
+
* slot itself never needs to be.
|
|
483
|
+
*/
|
|
484
|
+
inputSlotFor={isPlanSelected ? 'paymentPlan' : cardValue}
|
|
485
|
+
>
|
|
486
|
+
{paymentInput}
|
|
487
|
+
</PaymentMethods>
|
|
488
|
+
)}
|
|
489
|
+
</>
|
|
490
|
+
)}
|
|
491
|
+
|
|
492
|
+
{/* Conditions */}
|
|
493
|
+
{conditions.map((c) => {
|
|
494
|
+
const invalid =
|
|
495
|
+
validation.attempt > 0 && c.isRequired && !checkout.acceptedConditions?.[c.id]
|
|
496
|
+
return (
|
|
497
|
+
<div
|
|
498
|
+
key={c.id}
|
|
499
|
+
className={invalid ? 'tf-checkout-consent--error' : undefined}
|
|
500
|
+
data-checkout-invalid={invalid || undefined}
|
|
501
|
+
>
|
|
502
|
+
<Checkbox
|
|
503
|
+
required={c.isRequired}
|
|
504
|
+
checked={Boolean(checkout.acceptedConditions?.[c.id])}
|
|
505
|
+
aria-invalid={invalid}
|
|
506
|
+
onChange={(e) => checkout.acceptCondition(c.id, e.target.checked)}
|
|
507
|
+
label={
|
|
508
|
+
<>
|
|
509
|
+
<span dangerouslySetInnerHTML={{ __html: c.content || c.name }} />
|
|
510
|
+
{c.isRequired && ' *'}
|
|
511
|
+
</>
|
|
512
|
+
}
|
|
513
|
+
/>
|
|
514
|
+
{invalid && (
|
|
515
|
+
<div className="tf-checkout-consent__error">
|
|
516
|
+
You must accept this condition to continue.
|
|
517
|
+
</div>
|
|
518
|
+
)}
|
|
519
|
+
</div>
|
|
520
|
+
)
|
|
521
|
+
})}
|
|
522
|
+
</Card>
|
|
523
|
+
|
|
524
|
+
<OrderSummary eventId={eventId} />
|
|
525
|
+
|
|
526
|
+
<div className="tf-checkout-payment__cta">
|
|
527
|
+
<Button
|
|
528
|
+
onClick={pay}
|
|
529
|
+
// Stay busy through 'succeeded' too: in redirect mode the checkout
|
|
530
|
+
// remains mounted while the host navigates away, so the button must
|
|
531
|
+
// not flicker back to a clickable state before the redirect lands.
|
|
532
|
+
loading={finalizing}
|
|
533
|
+
disabled={checkout.isBusy || finalizing || (!isFree && !methods)}
|
|
534
|
+
>
|
|
535
|
+
{finalizing ? 'Placing…' : payLabel}
|
|
536
|
+
</Button>
|
|
537
|
+
</div>
|
|
538
|
+
{bottomError && <p className="tf-checkout-error-text">{bottomError}</p>}
|
|
539
|
+
</div>
|
|
540
|
+
)
|
|
541
|
+
}
|