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