tf-checkout-react 2.0.0-beta.0 → 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.
package/dist/index.d.mts CHANGED
@@ -152,6 +152,14 @@ interface TfProfile {
152
152
  hasDashboardAccess: boolean;
153
153
  ticketHolders: TfTicketHolder[];
154
154
  linkedCryptoWallets: string[];
155
+ /**
156
+ * Calendar date as `YYYY-MM-DD`, or null when none is on file. The profile
157
+ * endpoint has always returned this; it simply wasn't declared here, so the
158
+ * checkout couldn't prefill it. Only populated for buyers who have been
159
+ * through an age-gated checkout, RN registration or SSO, so treat it as
160
+ * best-effort.
161
+ */
162
+ dateOfBirth?: string | null;
155
163
  }
156
164
  interface TfCountry {
157
165
  id: string | number;
@@ -1916,6 +1924,14 @@ interface CheckoutEngine {
1916
1924
  /** Run ID verification for a NEED_ID_VERIFICATION order. */
1917
1925
  runIdVerification: (orderHash: string, options?: RunIdVerificationOptions) => Promise<IdVerificationStatus | undefined>;
1918
1926
  reset: () => void;
1927
+ /**
1928
+ * Move the cart to the terminal 'expired' state. Until now this only happened
1929
+ * reactively, when the backend rejected a call with an expired-cart 422 — so a
1930
+ * reservation that simply ran out on screen left the buyer editing a form that
1931
+ * could no longer be paid for. Consumers driving the countdown call this when
1932
+ * it reaches zero.
1933
+ */
1934
+ expireCart: () => void;
1919
1935
  }
1920
1936
  /**
1921
1937
  * Binds the orchestration to a store + a platform's ports. In React, wrap this
package/dist/index.d.ts CHANGED
@@ -152,6 +152,14 @@ interface TfProfile {
152
152
  hasDashboardAccess: boolean;
153
153
  ticketHolders: TfTicketHolder[];
154
154
  linkedCryptoWallets: string[];
155
+ /**
156
+ * Calendar date as `YYYY-MM-DD`, or null when none is on file. The profile
157
+ * endpoint has always returned this; it simply wasn't declared here, so the
158
+ * checkout couldn't prefill it. Only populated for buyers who have been
159
+ * through an age-gated checkout, RN registration or SSO, so treat it as
160
+ * best-effort.
161
+ */
162
+ dateOfBirth?: string | null;
155
163
  }
156
164
  interface TfCountry {
157
165
  id: string | number;
@@ -1916,6 +1924,14 @@ interface CheckoutEngine {
1916
1924
  /** Run ID verification for a NEED_ID_VERIFICATION order. */
1917
1925
  runIdVerification: (orderHash: string, options?: RunIdVerificationOptions) => Promise<IdVerificationStatus | undefined>;
1918
1926
  reset: () => void;
1927
+ /**
1928
+ * Move the cart to the terminal 'expired' state. Until now this only happened
1929
+ * reactively, when the backend rejected a call with an expired-cart 422 — so a
1930
+ * reservation that simply ran out on screen left the buyer editing a form that
1931
+ * could no longer be paid for. Consumers driving the countdown call this when
1932
+ * it reaches zero.
1933
+ */
1934
+ expireCart: () => void;
1919
1935
  }
1920
1936
  /**
1921
1937
  * Binds the orchestration to a store + a platform's ports. In React, wrap this
package/dist/index.esm.js CHANGED
@@ -2461,22 +2461,29 @@ var getOrderReview = async (orderHash) => {
2461
2461
  };
2462
2462
  };
2463
2463
  var mapProfileToBilling = (profile) => {
2464
- var _a, _b, _c;
2464
+ var _a, _b, _c, _d;
2465
2465
  const str = (v) => v == null || v === "" ? void 0 : String(v);
2466
2466
  const idStr = (v) => {
2467
2467
  const s = str(v);
2468
2468
  return s === void 0 || s === "0" || s === "-1" ? void 0 : s;
2469
2469
  };
2470
+ const dobParts = /^(\d{4})-(\d{2})-(\d{2})/.exec((_a = profile.dateOfBirth) != null ? _a : "");
2471
+ const dob = dobParts && Number(dobParts[1]) > 1900 ? {
2472
+ dobYear: Number(dobParts[1]),
2473
+ dobMonth: Number(dobParts[2]),
2474
+ dobDay: Number(dobParts[3])
2475
+ } : {};
2470
2476
  return {
2471
- firstName: (_a = profile.firstName) != null ? _a : "",
2472
- lastName: (_b = profile.lastName) != null ? _b : "",
2473
- email: (_c = profile.email) != null ? _c : "",
2477
+ firstName: (_b = profile.firstName) != null ? _b : "",
2478
+ lastName: (_c = profile.lastName) != null ? _c : "",
2479
+ email: (_d = profile.email) != null ? _d : "",
2474
2480
  phone: str(profile.phone),
2475
2481
  streetAddress: str(profile.streetAddress),
2476
2482
  city: str(profile.city),
2477
2483
  zip: str(profile.zipCode),
2478
2484
  countryId: idStr(profile.countryId),
2479
- stateId: idStr(profile.stateId)
2485
+ stateId: idStr(profile.stateId),
2486
+ ...dob
2480
2487
  };
2481
2488
  };
2482
2489
  var completeFreeRegistration = async (orderHash) => {
@@ -3401,6 +3408,10 @@ var createEngine = (config) => {
3401
3408
  reset: () => {
3402
3409
  clearCartGuestToken();
3403
3410
  store.reset();
3411
+ },
3412
+ expireCart: () => {
3413
+ clearCartGuestToken();
3414
+ store.expireCart();
3404
3415
  }
3405
3416
  };
3406
3417
  };
@@ -3907,6 +3918,12 @@ var useCheckout = (config) => {
3907
3918
  return () => clearInterval(t);
3908
3919
  }, [snapshot.expiresAt]);
3909
3920
  const secondsLeft = snapshot.expiresAt != null ? Math.max(0, Math.round((snapshot.expiresAt - now) / 1e3)) : void 0;
3921
+ useEffect(() => {
3922
+ if (secondsLeft !== 0) return;
3923
+ if (snapshot.status === "placing" || snapshot.status === "succeeded") return;
3924
+ if (snapshot.status === "expired") return;
3925
+ engine.expireCart();
3926
+ }, [engine, secondsLeft, snapshot.status]);
3910
3927
  const missingRequiredAddonNames = missingRequiredAddOns(
3911
3928
  snapshot.requiredAddOns,
3912
3929
  snapshot.addOns
@@ -6173,6 +6190,8 @@ var AuthModal = ({
6173
6190
  const previouslyFocused = useRef4(null);
6174
6191
  const submittingRef = useRef4(auth.isSubmitting);
6175
6192
  submittingRef.current = auth.isSubmitting;
6193
+ const onCloseRef = useRef4(onClose);
6194
+ onCloseRef.current = onClose;
6176
6195
  useEffect7(() => {
6177
6196
  var _a, _b, _c;
6178
6197
  if (!open) return;
@@ -6193,7 +6212,7 @@ var AuthModal = ({
6193
6212
  initialFocus == null ? void 0 : initialFocus.focus();
6194
6213
  const onKeyDown = (event) => {
6195
6214
  var _a2;
6196
- if (event.key === "Escape" && !submittingRef.current) onClose();
6215
+ if (event.key === "Escape" && !submittingRef.current) onCloseRef.current();
6197
6216
  if (event.key !== "Tab") return;
6198
6217
  const elements = focusable();
6199
6218
  if (elements.length === 0) {
@@ -6218,7 +6237,7 @@ var AuthModal = ({
6218
6237
  document.body.style.overflow = previousOverflow;
6219
6238
  (_a2 = previouslyFocused.current) == null ? void 0 : _a2.focus();
6220
6239
  };
6221
- }, [onClose, open]);
6240
+ }, [open]);
6222
6241
  if (!open) return null;
6223
6242
  const closeFromBackdrop = (event) => {
6224
6243
  if (event.target === event.currentTarget && !auth.isSubmitting) onClose();
@@ -7389,6 +7408,19 @@ var BuyerField = ({
7389
7408
  ));
7390
7409
  };
7391
7410
 
7411
+ // src/sections/billing/fieldRules.ts
7412
+ var isBlankPhone = (phone) => !phone || /^\+?\d{0,4}$/.test(phone.trim());
7413
+ var DOB_MIN = "1900-01-01";
7414
+ var todayISO = () => {
7415
+ const now = /* @__PURE__ */ new Date();
7416
+ return [
7417
+ now.getFullYear(),
7418
+ String(now.getMonth() + 1).padStart(2, "0"),
7419
+ String(now.getDate()).padStart(2, "0")
7420
+ ].join("-");
7421
+ };
7422
+ var toDateInputValue = (year, month, day) => year && month && day ? `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}` : void 0;
7423
+
7392
7424
  // src/sections/billing/TicketHoldersCard.tsx
7393
7425
  import React27 from "react";
7394
7426
  import { PhoneInput as PhoneInput2 } from "react-international-phone";
@@ -7471,6 +7503,8 @@ var TicketHoldersCard = ({
7471
7503
  className: "tf-checkout-phone",
7472
7504
  inputClassName: `tf-checkout-input tf-checkout-phone__input${fieldError(phoneKey) ? " tf-checkout-input--invalid" : ""}`,
7473
7505
  defaultCountry: phoneCountry,
7506
+ disableDialCodeAndPrefix: true,
7507
+ showDisabledDialCodeAndPrefix: true,
7474
7508
  disableDialCodePrefill: true,
7475
7509
  value: holder.phone,
7476
7510
  required: true,
@@ -7593,7 +7627,7 @@ var BillingInfo = ({
7593
7627
  lastName: prev.lastName || profileBilling.lastName || "",
7594
7628
  email: prev.email || profileBilling.email || "",
7595
7629
  confirmEmail: prev.confirmEmail || (checkout.isLoggedIn ? profileBilling.email : "") || "",
7596
- phone: prev.phone || profileBilling.phone,
7630
+ phone: (isBlankPhone(prev.phone) ? void 0 : prev.phone) || profileBilling.phone,
7597
7631
  streetAddress: prev.streetAddress || profileBilling.streetAddress,
7598
7632
  city: prev.city || profileBilling.city,
7599
7633
  zip: prev.zip || profileBilling.zip,
@@ -7603,6 +7637,12 @@ var BillingInfo = ({
7603
7637
  const keys = Object.keys(next);
7604
7638
  return keys.every((k) => next[k] === prev[k]) ? prev : next;
7605
7639
  });
7640
+ setDob((prev) => {
7641
+ var _a2;
7642
+ if (prev) return prev;
7643
+ const { dobYear: dobYear2, dobMonth: dobMonth2, dobDay: dobDay2 } = profileBilling;
7644
+ return (_a2 = toDateInputValue(dobYear2, dobMonth2, dobDay2)) != null ? _a2 : prev;
7645
+ });
7606
7646
  }, [profile.data, checkout.isLoggedIn]);
7607
7647
  useEffect11(() => {
7608
7648
  setHolders(
@@ -7684,6 +7724,7 @@ var BillingInfo = ({
7684
7724
  countryDefaultApplied.current = true;
7685
7725
  setBilling((current2) => {
7686
7726
  var _a3;
7727
+ if (current2.countryId) return current2;
7687
7728
  return {
7688
7729
  ...current2,
7689
7730
  countryId: String(preferred.id),
@@ -7724,24 +7765,30 @@ var BillingInfo = ({
7724
7765
  }, [billing.stateId, billing.stateCode, states]);
7725
7766
  const setB = (k) => (e) => setBilling((b) => ({ ...b, [k]: e.target.value }));
7726
7767
  const patchHolder = (i, patch2) => setHolders((hs) => hs.map((h, j) => j === i ? { ...h, ...patch2 } : h));
7768
+ const dobMax = useMemo5(todayISO, []);
7727
7769
  const [dobYear, dobMonth, dobDay] = dob ? dob.split("-").map(Number) : [];
7770
+ const buyerPhone = isBlankPhone(billing.phone) ? void 0 : billing.phone;
7728
7771
  const committedBilling = {
7729
7772
  ...checkout.billing,
7730
7773
  ...billing,
7774
+ phone: buyerPhone,
7731
7775
  stateRequired: gates.showAddress && Boolean(billing.countryId) && states.length > 0,
7732
7776
  dataCapture,
7733
7777
  dobDay,
7734
7778
  dobMonth,
7735
7779
  dobYear
7736
7780
  };
7737
- const committedHolders = holders.map((holder) => ({
7738
- firstName: holderNamesRequired ? holder.firstName : holder.firstName || billing.firstName,
7739
- lastName: holderNamesRequired ? holder.lastName : holder.lastName || billing.lastName,
7740
- email: showHolders && collectHolderContact ? holder.email : holder.email || billing.email,
7741
- phone: showHolders && collectHolderContact ? holder.phone : holder.phone || billing.phone,
7742
- ticketTypeId: holder.ticketTypeId,
7743
- ticketDataCapture: holder.ticketDataCapture
7744
- }));
7781
+ const committedHolders = holders.map((holder) => {
7782
+ const holderPhone = isBlankPhone(holder.phone) ? "" : holder.phone;
7783
+ return {
7784
+ firstName: holderNamesRequired ? holder.firstName : holder.firstName || billing.firstName,
7785
+ lastName: holderNamesRequired ? holder.lastName : holder.lastName || billing.lastName,
7786
+ email: showHolders && collectHolderContact ? holder.email : holder.email || billing.email,
7787
+ phone: showHolders && collectHolderContact ? holderPhone : holderPhone || buyerPhone || "",
7788
+ ticketTypeId: holder.ticketTypeId,
7789
+ ticketDataCapture: holder.ticketDataCapture
7790
+ };
7791
+ });
7745
7792
  const missingDetails = missingCheckoutDetails(
7746
7793
  {
7747
7794
  billing: committedBilling,
@@ -7910,6 +7957,8 @@ var BillingInfo = ({
7910
7957
  className: "tf-checkout-phone",
7911
7958
  inputClassName: `tf-checkout-input tf-checkout-phone__input${fieldError("Valid phone number") ? " tf-checkout-input--invalid" : ""}`,
7912
7959
  defaultCountry: phoneCountry,
7960
+ disableDialCodeAndPrefix: true,
7961
+ showDisabledDialCodeAndPrefix: true,
7913
7962
  disableDialCodePrefill: true,
7914
7963
  value: (_D = billing.phone) != null ? _D : "",
7915
7964
  required: gates.phoneRequired,
@@ -7935,6 +7984,8 @@ var BillingInfo = ({
7935
7984
  type: "date",
7936
7985
  value: dob,
7937
7986
  required: true,
7987
+ min: DOB_MIN,
7988
+ max: dobMax,
7938
7989
  invalid: Boolean(dobError),
7939
7990
  "aria-invalid": Boolean(dobError),
7940
7991
  onChange: (e) => setDob(e.target.value)
@@ -8101,7 +8152,7 @@ var Payment = ({
8101
8152
  showTestCardHint = false,
8102
8153
  className
8103
8154
  }) => {
8104
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
8155
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
8105
8156
  const checkout = useCheckoutContext();
8106
8157
  const validation = useCheckoutValidation();
8107
8158
  const [error, setError] = useState16("");
@@ -8123,8 +8174,10 @@ var Payment = ({
8123
8174
  const bd = checkout.breakdown;
8124
8175
  const isFree = bd != null && Number(bd.total) === 0;
8125
8176
  const isXendit = checkout.gateway === "xendit";
8177
+ const serverMethodTypes = (_a = checkout.stripeElementsConfig) == null ? void 0 : _a.paymentMethodTypes;
8178
+ const offersOxxo = Array.isArray(serverMethodTypes) && serverMethodTypes.includes("oxxo");
8126
8179
  const options = useMemo6(() => {
8127
- var _a2, _b2, _c2, _d2;
8180
+ var _a2, _b2, _c2;
8128
8181
  if (isFree || !methods) return [];
8129
8182
  const out = [];
8130
8183
  if (isXendit) {
@@ -8156,7 +8209,7 @@ var Payment = ({
8156
8209
  value: "paymentPlan",
8157
8210
  label: "Payment plan"
8158
8211
  });
8159
- if (((_d2 = checkout.breakdown) == null ? void 0 : _d2.currencyCode) === "MXN")
8212
+ if (offersOxxo)
8160
8213
  out.push({ id: { kind: "oxxo" }, value: "oxxo", label: "OXXO (pay in cash)" });
8161
8214
  }
8162
8215
  if (methods.paypal)
@@ -8169,7 +8222,7 @@ var Payment = ({
8169
8222
  checkout.gateway,
8170
8223
  checkout.redirectPaymentMethods,
8171
8224
  checkout.paymentPlan,
8172
- checkout.breakdown
8225
+ offersOxxo
8173
8226
  ]);
8174
8227
  useEffect12(() => {
8175
8228
  if (options.length && !options.some((o) => o.value === selected))
@@ -8179,14 +8232,14 @@ var Payment = ({
8179
8232
  const selectedOption = options.find((o) => o.value === selected);
8180
8233
  const isCardSelected = selected === cardValue;
8181
8234
  const isPlanSelected = (selectedOption == null ? void 0 : selectedOption.id.kind) === "paymentPlan";
8182
- const savedLast4 = (_a = checkout.paymentPlan) == null ? void 0 : _a.savedCardLast4;
8235
+ const savedLast4 = (_b = checkout.paymentPlan) == null ? void 0 : _b.savedCardLast4;
8183
8236
  const reuseSavedCard = Boolean(savedLast4) && useSavedCard;
8184
8237
  const usesCardDetails = !isFree && (isCardSelected || isPlanSelected && !reuseSavedCard);
8185
8238
  const cardWidgetReportsItsOwnError = usesCardDetails && !isXendit;
8186
8239
  const hasWallets = Boolean(
8187
- ((_b = checkout.walletAvailability) == null ? void 0 : _b.applePay) || ((_c = checkout.walletAvailability) == null ? void 0 : _c.googlePay)
8240
+ ((_c = checkout.walletAvailability) == null ? void 0 : _c.applePay) || ((_d = checkout.walletAvailability) == null ? void 0 : _d.googlePay)
8188
8241
  );
8189
- const useWalletElement = !isXendit && !isPlanSelected && hasWallets && ((_d = checkout.breakdown) == null ? void 0 : _d.currencyCode) !== "MXN" && Boolean((_e = checkout.stripeElementsConfig) == null ? void 0 : _e.mode);
8242
+ const useWalletElement = !isXendit && !isPlanSelected && hasWallets && ((_e = checkout.breakdown) == null ? void 0 : _e.currencyCode) !== "MXN" && Boolean((_f = checkout.stripeElementsConfig) == null ? void 0 : _f.mode);
8190
8243
  const onlyOption = options.length === 1 ? options[0] : void 0;
8191
8244
  const singleCard = (onlyOption == null ? void 0 : onlyOption.id.kind) === "card" || (onlyOption == null ? void 0 : onlyOption.id.kind) === "xenditCard";
8192
8245
  const b = checkout.billing;
@@ -8203,7 +8256,7 @@ var Payment = ({
8203
8256
  { requireEmailConfirmation, collectTicketHolders, requireHolderContact }
8204
8257
  );
8205
8258
  const billingReady = missingDetails.length === 0;
8206
- const conditions = [...(_f = checkout.conditions) != null ? _f : [], ...additionalConditions];
8259
+ const conditions = [...(_g = checkout.conditions) != null ? _g : [], ...additionalConditions];
8207
8260
  const requiredConditions = conditions.filter((c) => c.isRequired);
8208
8261
  const conditionsOk = requiredConditions.every(
8209
8262
  (c) => {
@@ -8212,10 +8265,10 @@ var Payment = ({
8212
8265
  }
8213
8266
  );
8214
8267
  const extra = selectedOption == null ? void 0 : selectedOption.extra;
8215
- const extrasOk = !extra || (extra === "cashtag" ? Boolean((_h = (_g = extras[selected]) == null ? void 0 : _g.cashtag) == null ? void 0 : _h.trim()) : Boolean((_j = (_i = extras[selected]) == null ? void 0 : _i.phoneNumber) == null ? void 0 : _j.trim()));
8268
+ const extrasOk = !extra || (extra === "cashtag" ? Boolean((_i = (_h = extras[selected]) == null ? void 0 : _h.cashtag) == null ? void 0 : _i.trim()) : Boolean((_k = (_j = extras[selected]) == null ? void 0 : _j.phoneNumber) == null ? void 0 : _k.trim()));
8216
8269
  const privacyOk = !requirePrivacyPolicy || checkout.consent.ttfOptIn;
8217
- const payLabel = isFree ? "Complete Registration" : isPlanSelected ? planConfirmLabel((_k = checkout.paymentPlan) == null ? void 0 : _k.configuration) : "Submit Payment";
8218
- const planTerms = paymentPlanTerms((_l = checkout.paymentPlan) == null ? void 0 : _l.configuration);
8270
+ const payLabel = isFree ? "Complete Registration" : isPlanSelected ? planConfirmLabel((_l = checkout.paymentPlan) == null ? void 0 : _l.configuration) : "Submit Payment";
8271
+ const planTerms = paymentPlanTerms((_m = checkout.paymentPlan) == null ? void 0 : _m.configuration);
8219
8272
  const finalizing = checkout.status === "placing" || checkout.status === "succeeded";
8220
8273
  const pay = () => {
8221
8274
  setError("");