@wix/himalaya-cli 0.1572.0 → 0.1574.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.
Files changed (103) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/dev-server/src/app-module.d.ts +2 -0
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/message-format.d.ts +59 -0
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/checkout/src/index.d.ts +4 -0
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/apple-iap-contract.d.ts +1 -1
  5. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/commerce-contract.d.ts +2 -0
  6. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/screens/src/index.d.ts +1 -0
  7. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/screens/src/one-app-page.d.ts +61 -0
  8. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/social-feed/src/index.d.ts +34 -9
  9. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/checkout.mjs +28 -3
  10. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core.mjs +3 -3
  11. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/iap-checkout.mjs +2 -1
  12. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/l10n.mjs +8 -3
  13. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/screens.mjs +573 -73
  14. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/sdui.mjs +3 -3
  15. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-social-feed.mjs +24 -1
  16. package/dist/cli.mjs +2708 -1182
  17. package/dist/preview-assets.generated.bin +0 -0
  18. package/guides/himi-json-output.md +41 -2
  19. package/guides/himi-mcp.md +1 -1
  20. package/guides/himi-test.md +3 -2
  21. package/guides/iap-products.md +1 -1
  22. package/guides/importing-oneapp-translations.md +115 -0
  23. package/guides/index.json +6 -0
  24. package/guides/one-app-section-mappings.md +16 -4
  25. package/guides/one-app-to-himi-app.md +221 -10
  26. package/guides/qa-builds.md +21 -1
  27. package/guides/sdk-reference.json +28 -4
  28. package/guides/templates.md +3 -2
  29. package/guides/testing-himi-apps.md +1 -1
  30. package/guides/webview-page-sdk.md +68 -3
  31. package/package.json +3 -3
  32. package/templates/beauty-salon/dev/index.ts +22 -6
  33. package/templates/beauty-salon/tier5-src/_shared/iap-worker.ts +2 -0
  34. package/templates/beauty-salon/tier5-src/_shared/payment-setup.ts +6 -0
  35. package/templates/beauty-salon/tier5-src/himi-booking-time/index.ts +5 -2
  36. package/templates/beauty-salon/tier5-src/himi-branded-lite-cart/index.ts +5 -2
  37. package/templates/beauty-salon/tier5-src/himi-branded-lite-plans/index.ts +5 -2
  38. package/templates/beauty-salon-owner/dev/index.ts +22 -6
  39. package/templates/branded-lite/dev/index.ts +22 -6
  40. package/templates/branded-lite/tier5-src/_shared/iap-worker.ts +2 -0
  41. package/templates/branded-lite/tier5-src/_shared/payment-setup.ts +6 -0
  42. package/templates/branded-lite/tier5-src/himi-booking-time/index.ts +5 -2
  43. package/templates/branded-lite/tier5-src/himi-branded-lite-cart/index.ts +5 -2
  44. package/templates/branded-lite/tier5-src/himi-branded-lite-plans/index.ts +5 -2
  45. package/templates/branded-lite/tier5-src/himi-branded-lite-program/index.ts +5 -2
  46. package/templates/branded-lite/tier5-src/himi-events-detail/index.ts +9 -4
  47. package/templates/community-club/dev/index.ts +22 -6
  48. package/templates/community-club/tier5-src/_shared/iap-worker.ts +2 -0
  49. package/templates/community-club/tier5-src/_shared/payment-setup.ts +6 -0
  50. package/templates/community-club/tier5-src/himi-events-detail/index.ts +9 -4
  51. package/templates/community-club-owner/dev/index.ts +22 -6
  52. package/templates/events-meetups/dev/index.ts +22 -6
  53. package/templates/events-meetups/tier5-src/_shared/iap-worker.ts +2 -0
  54. package/templates/events-meetups/tier5-src/_shared/payment-setup.ts +6 -0
  55. package/templates/events-meetups/tier5-src/himi-events-detail/index.ts +9 -4
  56. package/templates/events-meetups-owner/dev/index.ts +22 -6
  57. package/templates/gym-fitness/dev/index.ts +22 -6
  58. package/templates/gym-fitness/tier5-src/_shared/iap-worker.ts +2 -0
  59. package/templates/gym-fitness/tier5-src/_shared/payment-setup.ts +6 -0
  60. package/templates/gym-fitness/tier5-src/himi-booking-time/index.ts +5 -2
  61. package/templates/gym-fitness/tier5-src/himi-branded-lite-cart/index.ts +5 -2
  62. package/templates/gym-fitness/tier5-src/himi-branded-lite-plans/index.ts +5 -2
  63. package/templates/gym-fitness/tier5-src/himi-events-detail/index.ts +9 -4
  64. package/templates/gym-fitness-owner/dev/index.ts +22 -6
  65. package/templates/himi-chat/dev/index.ts +22 -6
  66. package/templates/nonprofit-cause/dev/index.ts +22 -6
  67. package/templates/nonprofit-cause/tier5-src/_shared/iap-worker.ts +2 -0
  68. package/templates/nonprofit-cause/tier5-src/_shared/payment-setup.ts +6 -0
  69. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-plans/index.ts +5 -2
  70. package/templates/nonprofit-cause/tier5-src/himi-events-detail/index.ts +9 -4
  71. package/templates/nonprofit-cause-owner/dev/index.ts +22 -6
  72. package/templates/owner-lite/dev/index.ts +22 -6
  73. package/templates/podcast-creator/dev/index.ts +22 -6
  74. package/templates/podcast-creator/tier5-src/_shared/iap-worker.ts +2 -0
  75. package/templates/podcast-creator/tier5-src/_shared/payment-setup.ts +6 -0
  76. package/templates/podcast-creator/tier5-src/himi-branded-lite-plans/index.ts +5 -2
  77. package/templates/podcast-creator/tier5-src/himi-events-detail/index.ts +9 -4
  78. package/templates/podcast-creator-owner/dev/index.ts +22 -6
  79. package/templates/portfolio-creative/dev/index.ts +22 -6
  80. package/templates/portfolio-creative/tier5-src/_shared/iap-worker.ts +2 -0
  81. package/templates/portfolio-creative/tier5-src/_shared/payment-setup.ts +6 -0
  82. package/templates/portfolio-creative/tier5-src/himi-branded-lite-cart/index.ts +5 -2
  83. package/templates/portfolio-creative/tier5-src/himi-events-detail/index.ts +9 -4
  84. package/templates/portfolio-creative-owner/dev/index.ts +22 -6
  85. package/templates/restaurant/dev/index.ts +22 -6
  86. package/templates/restaurant/tier5-src/_shared/iap-worker.ts +2 -0
  87. package/templates/restaurant/tier5-src/_shared/payment-setup.ts +6 -0
  88. package/templates/restaurant/tier5-src/himi-events-detail/index.ts +9 -4
  89. package/templates/restaurant-owner/dev/index.ts +22 -6
  90. package/templates/retail-store/dev/index.ts +22 -6
  91. package/templates/retail-store/tier5-src/_shared/iap-worker.ts +2 -0
  92. package/templates/retail-store/tier5-src/_shared/payment-setup.ts +6 -0
  93. package/templates/retail-store/tier5-src/himi-branded-lite-cart/index.ts +5 -2
  94. package/templates/retail-store-owner/dev/index.ts +22 -6
  95. package/templates/surface-showcase/dev/index.ts +22 -6
  96. package/templates/yoga-studio/dev/index.ts +22 -6
  97. package/templates/yoga-studio/tier5-src/_shared/iap-worker.ts +2 -0
  98. package/templates/yoga-studio/tier5-src/_shared/payment-setup.ts +6 -0
  99. package/templates/yoga-studio/tier5-src/himi-booking-time/index.ts +5 -2
  100. package/templates/yoga-studio/tier5-src/himi-branded-lite-plans/index.ts +5 -2
  101. package/templates/yoga-studio/tier5-src/himi-branded-lite-program/index.ts +5 -2
  102. package/templates/yoga-studio/tier5-src/himi-events-detail/index.ts +9 -4
  103. package/templates/yoga-studio-owner/dev/index.ts +22 -6
@@ -1,3 +1,4 @@
1
+ import { paymentSetupMessage } from "../_shared/payment-setup.js";
1
2
  // Tier-5 bundle: himi-events — event detail.
2
3
  import { calendarDay, formatClock, formatCurrency, formatWeekdayDateTime, formatWeekdayMonthDay, formatTimeZoneLabel } from "../_shared/engine-intl.js";
3
4
  import { defineService as registerService } from "@himalaya/state";
@@ -18,7 +19,7 @@ import {
18
19
  type WixEvent,
19
20
  } from "@himalaya/wix-events";
20
21
  import { session as wixPlatformSession } from "@himalaya/wix-platform";
21
- import { payCashierOrder } from "@himalaya/checkout";
22
+ import { isPaymentSetupFailure, payCashierOrder } from "@himalaya/checkout";
22
23
  import { get, upsert, type DbEntity } from "@himalaya/local-db";
23
24
  import {
24
25
  currentInputId,
@@ -986,16 +987,18 @@ const svc: ReturnType<typeof defineService<S, DetailActions>> = defineService<S,
986
987
  if (pay.kind === "paid") result = { ...s, ticketSheetOpen: false, snackbarMessage: detailCopy("event.ticketIssued", "You're in — your ticket is confirmed.") };
987
988
  else if (pay.kind === "handoff") result = { ...s, ticketSheetOpen: false, snackbarMessage: detailCopy("event.checkoutOpened", "Complete checkout in the browser to get your tickets.") };
988
989
  else if (pay.kind === "cancelled") result = { ...s, snackbarMessage: detailCopy("event.ticketCancelled", "Payment cancelled — no ticket was issued.") };
990
+ else if (isPaymentSetupFailure(pay)) result = { ...s, snackbarMessage: paymentSetupMessage("event") };
989
991
  else throw new Error("Payment could not be completed");
990
992
  }
991
993
  }
992
994
  }
993
- } catch {
995
+ } catch (error) {
996
+ if (isPaymentSetupFailure(error)) result = { ...s, snackbarMessage: paymentSetupMessage("event") };
994
997
  try {
995
998
  // A public event page is not pinned to this selection or price. Only
996
999
  // continue in hosted checkout when a reservation already holds it.
997
1000
  const params = reservationId ? { reservationId } : undefined;
998
- if (params) {
1001
+ if (params && !result) {
999
1002
  const redirect = await createRedirectSession({ eventsCheckout: params });
1000
1003
  const url = redirect.redirectSession?.fullUrl;
1001
1004
  if (url) {
@@ -1003,7 +1006,9 @@ const svc: ReturnType<typeof defineService<S, DetailActions>> = defineService<S,
1003
1006
  result = { ...s, ticketSheetOpen: false, snackbarMessage: opened.opened ? detailCopy("event.checkoutOpened", "Complete checkout in the browser to get your tickets.") : detailCopy("event.registrationOpenFailed", "Could not open the registration page.") };
1004
1007
  }
1005
1008
  }
1006
- } catch { /* report below */ }
1009
+ } catch (error) {
1010
+ if (isPaymentSetupFailure(error)) result = { ...s, snackbarMessage: paymentSetupMessage("event") };
1011
+ }
1007
1012
  if (!result) result = { ...s, snackbarMessage: detailCopy("event.purchaseUnavailable", "Purchasing is temporarily unavailable.") };
1008
1013
  } finally { ticketCheckoutInFlight = false; }
1009
1014
  return derive(result!);
@@ -24821,15 +24821,31 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
24821
24821
 
24822
24822
  // Live seed-id enumeration: a published release serves only EXACT screens, so
24823
24823
  // `himi push` bakes each templated detail route's CURRENT catalog ids as exact
24824
- // screens via `seedIds`. Anonymous visitor rung against this app's own clientId;
24825
- // best-effort (offline build → no baked details, preview still live-resolves).
24824
+ // screens via `seedIds`. Anonymous visitor rung against this app's own clientId — or,
24825
+ // for an app bound through the One App visitor identity (`visitorSource: "one-app"`,
24826
+ // no client), that site's One App visitor instance. Best-effort (offline build → no
24827
+ // baked details, preview still live-resolves).
24828
+ async function liveSeedToken(WIX: string): Promise<string | undefined> {
24829
+ const auth = (config as { auth?: { config?: { clientId?: string; visitorSource?: string; metaSiteId?: string } } }).auth?.config;
24830
+ // A One App binding boots as the site's One App visitor, so the seed reads as that visitor too —
24831
+ // even beside a member-login clientId, and never repointed by a stray local WIX_CLIENT_ID.
24832
+ const oneApp = auth?.visitorSource === "one-app";
24833
+ const clientId = oneApp ? "" : process.env.WIX_CLIENT_ID ?? auth?.clientId ?? "";
24834
+ if (clientId && !clientId.startsWith("YOUR_")) {
24835
+ const mint = await fetch(WIX + "/oauth2/token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ clientId, grantType: "anonymous" }) });
24836
+ return ((await mint.json()) as { access_token?: string }).access_token;
24837
+ }
24838
+ const msid = oneApp ? auth?.metaSiteId ?? "" : "";
24839
+ if (!/^[0-9a-fA-F-]{10,64}$/.test(msid)) return undefined;
24840
+ const mint = await fetch(`https://apps.wix.com/_api/one-app-session-web/v3/businesses/${msid}/signed-instance?signed_instance_type=Visitor`, { headers: { Accept: "application/json" }, redirect: "error" });
24841
+ const visitor = (await mint.json()) as { signedInstance?: string; signedInstanceType?: string };
24842
+ return visitor.signedInstanceType === "Visitor" ? visitor.signedInstance : undefined;
24843
+ }
24844
+
24826
24845
  async function liveSeedIds(spec: LiveSeed): Promise<string[]> {
24827
24846
  try {
24828
24847
  const WIX = "https://www.wixapis.com";
24829
- const clientId = process.env.WIX_CLIENT_ID ?? (config as { auth?: { config?: { clientId?: string } } }).auth?.config?.clientId ?? "";
24830
- if (!clientId || clientId.startsWith("YOUR_")) return [];
24831
- const mint = await fetch(WIX + "/oauth2/token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ clientId, grantType: "anonymous" }) });
24832
- const token = ((await mint.json()) as { access_token?: string }).access_token;
24848
+ const token = await liveSeedToken(WIX);
24833
24849
  if (!token) return [];
24834
24850
  const res = await fetch(WIX + spec.path, { method: "POST", headers: { Authorization: token, "Content-Type": "application/json" }, body: JSON.stringify(spec.body) });
24835
24851
  const json = (await res.json()) as Record<string, unknown>;
@@ -4,6 +4,7 @@
4
4
  // SDK; this only supplies the copy and the hydration reset.
5
5
  import { hostSessionFromGlobal, type IapPriceCopy } from "@himalaya/iap";
6
6
  import type { IapCommerce, IapPurchaseCopy, IapRestoreResult } from "@himalaya/iap-checkout";
7
+ import { paymentSetupMessage } from "./payment-setup.js";
7
8
  import { copyParam, copyTemplate } from "./himi-copy.js";
8
9
 
9
10
  /** Price labels from the screen's copy pack (`<prefix>.priceLoadingText`, …). */
@@ -25,6 +26,7 @@ export function iapPurchaseCopy(prefix: string): IapPurchaseCopy {
25
26
  awaitingApproval: copyParam(`${prefix}.purchaseAwaitingApprovalText`, "Waiting for approval"),
26
27
  unavailable: copyParam(`${prefix}.purchaseTemporarilyUnavailableText`, "Purchasing is temporarily unavailable."),
27
28
  signInRequired: copyParam(`${prefix}.purchaseSignInText`, "Sign in to buy."),
29
+ sitePaymentUnavailable: paymentSetupMessage(prefix),
28
30
  retry: copyParam(`${prefix}.purchaseRetryText`, "Something went wrong. Try again."),
29
31
  priceChanged: copyParam(`${prefix}.purchasePriceChangedText`, "The price changed. Check it and try again."),
30
32
  support: copyParam(`${prefix}.purchaseSupportText`, "We couldn't finish this purchase. Contact support."),
@@ -0,0 +1,6 @@
1
+ import { copyParam } from "./himi-copy.js";
2
+
3
+ /** No provider or country guess: Wix only established that online payment is unavailable. */
4
+ export function paymentSetupMessage(prefix: string): string {
5
+ return copyParam(`${prefix}.paymentSetupUnavailable`, "This site is not accepting online payments. Go back or contact the business for other payment options.");
6
+ }
@@ -1,3 +1,4 @@
1
+ import { paymentSetupMessage } from "../_shared/payment-setup.js";
1
2
  // Tier-5 bundle: himi-events — event detail.
2
3
  import { calendarDay, formatClock, formatCurrency, formatWeekdayDateTime, formatWeekdayMonthDay, formatTimeZoneLabel } from "../_shared/engine-intl.js";
3
4
  import { defineService as registerService } from "@himalaya/state";
@@ -18,7 +19,7 @@ import {
18
19
  type WixEvent,
19
20
  } from "@himalaya/wix-events";
20
21
  import { session as wixPlatformSession } from "@himalaya/wix-platform";
21
- import { payCashierOrder } from "@himalaya/checkout";
22
+ import { isPaymentSetupFailure, payCashierOrder } from "@himalaya/checkout";
22
23
  import { get, upsert, type DbEntity } from "@himalaya/local-db";
23
24
  import {
24
25
  currentInputId,
@@ -986,16 +987,18 @@ const svc: ReturnType<typeof defineService<S, DetailActions>> = defineService<S,
986
987
  if (pay.kind === "paid") result = { ...s, ticketSheetOpen: false, snackbarMessage: detailCopy("event.ticketIssued", "You're in — your ticket is confirmed.") };
987
988
  else if (pay.kind === "handoff") result = { ...s, ticketSheetOpen: false, snackbarMessage: detailCopy("event.checkoutOpened", "Complete checkout in the browser to get your tickets.") };
988
989
  else if (pay.kind === "cancelled") result = { ...s, snackbarMessage: detailCopy("event.ticketCancelled", "Payment cancelled — no ticket was issued.") };
990
+ else if (isPaymentSetupFailure(pay)) result = { ...s, snackbarMessage: paymentSetupMessage("event") };
989
991
  else throw new Error("Payment could not be completed");
990
992
  }
991
993
  }
992
994
  }
993
- } catch {
995
+ } catch (error) {
996
+ if (isPaymentSetupFailure(error)) result = { ...s, snackbarMessage: paymentSetupMessage("event") };
994
997
  try {
995
998
  // A public event page is not pinned to this selection or price. Only
996
999
  // continue in hosted checkout when a reservation already holds it.
997
1000
  const params = reservationId ? { reservationId } : undefined;
998
- if (params) {
1001
+ if (params && !result) {
999
1002
  const redirect = await createRedirectSession({ eventsCheckout: params });
1000
1003
  const url = redirect.redirectSession?.fullUrl;
1001
1004
  if (url) {
@@ -1003,7 +1006,9 @@ const svc: ReturnType<typeof defineService<S, DetailActions>> = defineService<S,
1003
1006
  result = { ...s, ticketSheetOpen: false, snackbarMessage: opened.opened ? detailCopy("event.checkoutOpened", "Complete checkout in the browser to get your tickets.") : detailCopy("event.registrationOpenFailed", "Could not open the registration page.") };
1004
1007
  }
1005
1008
  }
1006
- } catch { /* report below */ }
1009
+ } catch (error) {
1010
+ if (isPaymentSetupFailure(error)) result = { ...s, snackbarMessage: paymentSetupMessage("event") };
1011
+ }
1007
1012
  if (!result) result = { ...s, snackbarMessage: detailCopy("event.purchaseUnavailable", "Purchasing is temporarily unavailable.") };
1008
1013
  } finally { ticketCheckoutInFlight = false; }
1009
1014
  return derive(result!);
@@ -85002,15 +85002,31 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
85002
85002
 
85003
85003
  // Live seed-id enumeration: a published release serves only EXACT screens, so
85004
85004
  // `himi push` bakes each templated detail route's CURRENT catalog ids as exact
85005
- // screens via `seedIds`. Anonymous visitor rung against this app's own clientId;
85006
- // best-effort (offline build → no baked details, preview still live-resolves).
85005
+ // screens via `seedIds`. Anonymous visitor rung against this app's own clientId — or,
85006
+ // for an app bound through the One App visitor identity (`visitorSource: "one-app"`,
85007
+ // no client), that site's One App visitor instance. Best-effort (offline build → no
85008
+ // baked details, preview still live-resolves).
85009
+ async function liveSeedToken(WIX: string): Promise<string | undefined> {
85010
+ const auth = (config as { auth?: { config?: { clientId?: string; visitorSource?: string; metaSiteId?: string } } }).auth?.config;
85011
+ // A One App binding boots as the site's One App visitor, so the seed reads as that visitor too —
85012
+ // even beside a member-login clientId, and never repointed by a stray local WIX_CLIENT_ID.
85013
+ const oneApp = auth?.visitorSource === "one-app";
85014
+ const clientId = oneApp ? "" : process.env.WIX_CLIENT_ID ?? auth?.clientId ?? "";
85015
+ if (clientId && !clientId.startsWith("YOUR_")) {
85016
+ const mint = await fetch(WIX + "/oauth2/token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ clientId, grantType: "anonymous" }) });
85017
+ return ((await mint.json()) as { access_token?: string }).access_token;
85018
+ }
85019
+ const msid = oneApp ? auth?.metaSiteId ?? "" : "";
85020
+ if (!/^[0-9a-fA-F-]{10,64}$/.test(msid)) return undefined;
85021
+ const mint = await fetch(`https://apps.wix.com/_api/one-app-session-web/v3/businesses/${msid}/signed-instance?signed_instance_type=Visitor`, { headers: { Accept: "application/json" }, redirect: "error" });
85022
+ const visitor = (await mint.json()) as { signedInstance?: string; signedInstanceType?: string };
85023
+ return visitor.signedInstanceType === "Visitor" ? visitor.signedInstance : undefined;
85024
+ }
85025
+
85007
85026
  async function liveSeedIds(spec: LiveSeed): Promise<string[]> {
85008
85027
  try {
85009
85028
  const WIX = "https://www.wixapis.com";
85010
- const clientId = process.env.WIX_CLIENT_ID ?? (config as { auth?: { config?: { clientId?: string } } }).auth?.config?.clientId ?? "";
85011
- if (!clientId || clientId.startsWith("YOUR_")) return [];
85012
- const mint = await fetch(WIX + "/oauth2/token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ clientId, grantType: "anonymous" }) });
85013
- const token = ((await mint.json()) as { access_token?: string }).access_token;
85029
+ const token = await liveSeedToken(WIX);
85014
85030
  if (!token) return [];
85015
85031
  const res = await fetch(WIX + spec.path, { method: "POST", headers: { Authorization: token, "Content-Type": "application/json" }, body: JSON.stringify(spec.body) });
85016
85032
  const json = (await res.json()) as Record<string, unknown>;
@@ -22326,15 +22326,31 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
22326
22326
 
22327
22327
  // Live seed-id enumeration: a published release serves only EXACT screens, so
22328
22328
  // `himi push` bakes each templated detail route's CURRENT catalog ids as exact
22329
- // screens via `seedIds`. Anonymous visitor rung against this app's own clientId;
22330
- // best-effort (offline build → no baked details, preview still live-resolves).
22329
+ // screens via `seedIds`. Anonymous visitor rung against this app's own clientId — or,
22330
+ // for an app bound through the One App visitor identity (`visitorSource: "one-app"`,
22331
+ // no client), that site's One App visitor instance. Best-effort (offline build → no
22332
+ // baked details, preview still live-resolves).
22333
+ async function liveSeedToken(WIX: string): Promise<string | undefined> {
22334
+ const auth = (config as { auth?: { config?: { clientId?: string; visitorSource?: string; metaSiteId?: string } } }).auth?.config;
22335
+ // A One App binding boots as the site's One App visitor, so the seed reads as that visitor too —
22336
+ // even beside a member-login clientId, and never repointed by a stray local WIX_CLIENT_ID.
22337
+ const oneApp = auth?.visitorSource === "one-app";
22338
+ const clientId = oneApp ? "" : process.env.WIX_CLIENT_ID ?? auth?.clientId ?? "";
22339
+ if (clientId && !clientId.startsWith("YOUR_")) {
22340
+ const mint = await fetch(WIX + "/oauth2/token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ clientId, grantType: "anonymous" }) });
22341
+ return ((await mint.json()) as { access_token?: string }).access_token;
22342
+ }
22343
+ const msid = oneApp ? auth?.metaSiteId ?? "" : "";
22344
+ if (!/^[0-9a-fA-F-]{10,64}$/.test(msid)) return undefined;
22345
+ const mint = await fetch(`https://apps.wix.com/_api/one-app-session-web/v3/businesses/${msid}/signed-instance?signed_instance_type=Visitor`, { headers: { Accept: "application/json" }, redirect: "error" });
22346
+ const visitor = (await mint.json()) as { signedInstance?: string; signedInstanceType?: string };
22347
+ return visitor.signedInstanceType === "Visitor" ? visitor.signedInstance : undefined;
22348
+ }
22349
+
22331
22350
  async function liveSeedIds(spec: LiveSeed): Promise<string[]> {
22332
22351
  try {
22333
22352
  const WIX = "https://www.wixapis.com";
22334
- const clientId = process.env.WIX_CLIENT_ID ?? (config as { auth?: { config?: { clientId?: string } } }).auth?.config?.clientId ?? "";
22335
- if (!clientId || clientId.startsWith("YOUR_")) return [];
22336
- const mint = await fetch(WIX + "/oauth2/token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ clientId, grantType: "anonymous" }) });
22337
- const token = ((await mint.json()) as { access_token?: string }).access_token;
22353
+ const token = await liveSeedToken(WIX);
22338
22354
  if (!token) return [];
22339
22355
  const res = await fetch(WIX + spec.path, { method: "POST", headers: { Authorization: token, "Content-Type": "application/json" }, body: JSON.stringify(spec.body) });
22340
22356
  const json = (await res.json()) as Record<string, unknown>;
@@ -4,6 +4,7 @@
4
4
  // SDK; this only supplies the copy and the hydration reset.
5
5
  import { hostSessionFromGlobal, type IapPriceCopy } from "@himalaya/iap";
6
6
  import type { IapCommerce, IapPurchaseCopy, IapRestoreResult } from "@himalaya/iap-checkout";
7
+ import { paymentSetupMessage } from "./payment-setup.js";
7
8
  import { copyParam, copyTemplate } from "./himi-copy.js";
8
9
 
9
10
  /** Price labels from the screen's copy pack (`<prefix>.priceLoadingText`, …). */
@@ -25,6 +26,7 @@ export function iapPurchaseCopy(prefix: string): IapPurchaseCopy {
25
26
  awaitingApproval: copyParam(`${prefix}.purchaseAwaitingApprovalText`, "Waiting for approval"),
26
27
  unavailable: copyParam(`${prefix}.purchaseTemporarilyUnavailableText`, "Purchasing is temporarily unavailable."),
27
28
  signInRequired: copyParam(`${prefix}.purchaseSignInText`, "Sign in to buy."),
29
+ sitePaymentUnavailable: paymentSetupMessage(prefix),
28
30
  retry: copyParam(`${prefix}.purchaseRetryText`, "Something went wrong. Try again."),
29
31
  priceChanged: copyParam(`${prefix}.purchasePriceChangedText`, "The price changed. Check it and try again."),
30
32
  support: copyParam(`${prefix}.purchaseSupportText`, "We couldn't finish this purchase. Contact support."),
@@ -0,0 +1,6 @@
1
+ import { copyParam } from "./himi-copy.js";
2
+
3
+ /** No provider or country guess: Wix only established that online payment is unavailable. */
4
+ export function paymentSetupMessage(prefix: string): string {
5
+ return copyParam(`${prefix}.paymentSetupUnavailable`, "This site is not accepting online payments. Go back or contact the business for other payment options.");
6
+ }
@@ -1,3 +1,4 @@
1
+ import { paymentSetupMessage } from "../_shared/payment-setup.js";
1
2
  // Tier-5 bundle: himi-events — event detail.
2
3
  import { calendarDay, formatClock, formatCurrency, formatWeekdayDateTime, formatWeekdayMonthDay, formatTimeZoneLabel } from "../_shared/engine-intl.js";
3
4
  import { defineService as registerService } from "@himalaya/state";
@@ -18,7 +19,7 @@ import {
18
19
  type WixEvent,
19
20
  } from "@himalaya/wix-events";
20
21
  import { session as wixPlatformSession } from "@himalaya/wix-platform";
21
- import { payCashierOrder } from "@himalaya/checkout";
22
+ import { isPaymentSetupFailure, payCashierOrder } from "@himalaya/checkout";
22
23
  import { get, upsert, type DbEntity } from "@himalaya/local-db";
23
24
  import {
24
25
  currentInputId,
@@ -986,16 +987,18 @@ const svc: ReturnType<typeof defineService<S, DetailActions>> = defineService<S,
986
987
  if (pay.kind === "paid") result = { ...s, ticketSheetOpen: false, snackbarMessage: detailCopy("event.ticketIssued", "You're in — your ticket is confirmed.") };
987
988
  else if (pay.kind === "handoff") result = { ...s, ticketSheetOpen: false, snackbarMessage: detailCopy("event.checkoutOpened", "Complete checkout in the browser to get your tickets.") };
988
989
  else if (pay.kind === "cancelled") result = { ...s, snackbarMessage: detailCopy("event.ticketCancelled", "Payment cancelled — no ticket was issued.") };
990
+ else if (isPaymentSetupFailure(pay)) result = { ...s, snackbarMessage: paymentSetupMessage("event") };
989
991
  else throw new Error("Payment could not be completed");
990
992
  }
991
993
  }
992
994
  }
993
- } catch {
995
+ } catch (error) {
996
+ if (isPaymentSetupFailure(error)) result = { ...s, snackbarMessage: paymentSetupMessage("event") };
994
997
  try {
995
998
  // A public event page is not pinned to this selection or price. Only
996
999
  // continue in hosted checkout when a reservation already holds it.
997
1000
  const params = reservationId ? { reservationId } : undefined;
998
- if (params) {
1001
+ if (params && !result) {
999
1002
  const redirect = await createRedirectSession({ eventsCheckout: params });
1000
1003
  const url = redirect.redirectSession?.fullUrl;
1001
1004
  if (url) {
@@ -1003,7 +1006,9 @@ const svc: ReturnType<typeof defineService<S, DetailActions>> = defineService<S,
1003
1006
  result = { ...s, ticketSheetOpen: false, snackbarMessage: opened.opened ? detailCopy("event.checkoutOpened", "Complete checkout in the browser to get your tickets.") : detailCopy("event.registrationOpenFailed", "Could not open the registration page.") };
1004
1007
  }
1005
1008
  }
1006
- } catch { /* report below */ }
1009
+ } catch (error) {
1010
+ if (isPaymentSetupFailure(error)) result = { ...s, snackbarMessage: paymentSetupMessage("event") };
1011
+ }
1007
1012
  if (!result) result = { ...s, snackbarMessage: detailCopy("event.purchaseUnavailable", "Purchasing is temporarily unavailable.") };
1008
1013
  } finally { ticketCheckoutInFlight = false; }
1009
1014
  return derive(result!);
@@ -76399,15 +76399,31 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
76399
76399
 
76400
76400
  // Live seed-id enumeration: a published release serves only EXACT screens, so
76401
76401
  // `himi push` bakes each templated detail route's CURRENT catalog ids as exact
76402
- // screens via `seedIds`. Anonymous visitor rung against this app's own clientId;
76403
- // best-effort (offline build → no baked details, preview still live-resolves).
76402
+ // screens via `seedIds`. Anonymous visitor rung against this app's own clientId — or,
76403
+ // for an app bound through the One App visitor identity (`visitorSource: "one-app"`,
76404
+ // no client), that site's One App visitor instance. Best-effort (offline build → no
76405
+ // baked details, preview still live-resolves).
76406
+ async function liveSeedToken(WIX: string): Promise<string | undefined> {
76407
+ const auth = (config as { auth?: { config?: { clientId?: string; visitorSource?: string; metaSiteId?: string } } }).auth?.config;
76408
+ // A One App binding boots as the site's One App visitor, so the seed reads as that visitor too —
76409
+ // even beside a member-login clientId, and never repointed by a stray local WIX_CLIENT_ID.
76410
+ const oneApp = auth?.visitorSource === "one-app";
76411
+ const clientId = oneApp ? "" : process.env.WIX_CLIENT_ID ?? auth?.clientId ?? "";
76412
+ if (clientId && !clientId.startsWith("YOUR_")) {
76413
+ const mint = await fetch(WIX + "/oauth2/token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ clientId, grantType: "anonymous" }) });
76414
+ return ((await mint.json()) as { access_token?: string }).access_token;
76415
+ }
76416
+ const msid = oneApp ? auth?.metaSiteId ?? "" : "";
76417
+ if (!/^[0-9a-fA-F-]{10,64}$/.test(msid)) return undefined;
76418
+ const mint = await fetch(`https://apps.wix.com/_api/one-app-session-web/v3/businesses/${msid}/signed-instance?signed_instance_type=Visitor`, { headers: { Accept: "application/json" }, redirect: "error" });
76419
+ const visitor = (await mint.json()) as { signedInstance?: string; signedInstanceType?: string };
76420
+ return visitor.signedInstanceType === "Visitor" ? visitor.signedInstance : undefined;
76421
+ }
76422
+
76404
76423
  async function liveSeedIds(spec: LiveSeed): Promise<string[]> {
76405
76424
  try {
76406
76425
  const WIX = "https://www.wixapis.com";
76407
- const clientId = process.env.WIX_CLIENT_ID ?? (config as { auth?: { config?: { clientId?: string } } }).auth?.config?.clientId ?? "";
76408
- if (!clientId || clientId.startsWith("YOUR_")) return [];
76409
- const mint = await fetch(WIX + "/oauth2/token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ clientId, grantType: "anonymous" }) });
76410
- const token = ((await mint.json()) as { access_token?: string }).access_token;
76426
+ const token = await liveSeedToken(WIX);
76411
76427
  if (!token) return [];
76412
76428
  const res = await fetch(WIX + spec.path, { method: "POST", headers: { Authorization: token, "Content-Type": "application/json" }, body: JSON.stringify(spec.body) });
76413
76429
  const json = (await res.json()) as Record<string, unknown>;
@@ -49075,15 +49075,31 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
49075
49075
 
49076
49076
  // Live seed-id enumeration: a published release serves only EXACT screens, so
49077
49077
  // `himi push` bakes each templated detail route's CURRENT catalog ids as exact
49078
- // screens via `seedIds`. Anonymous visitor rung against this app's own clientId;
49079
- // best-effort (offline build → no baked details, preview still live-resolves).
49078
+ // screens via `seedIds`. Anonymous visitor rung against this app's own clientId — or,
49079
+ // for an app bound through the One App visitor identity (`visitorSource: "one-app"`,
49080
+ // no client), that site's One App visitor instance. Best-effort (offline build → no
49081
+ // baked details, preview still live-resolves).
49082
+ async function liveSeedToken(WIX: string): Promise<string | undefined> {
49083
+ const auth = (config as { auth?: { config?: { clientId?: string; visitorSource?: string; metaSiteId?: string } } }).auth?.config;
49084
+ // A One App binding boots as the site's One App visitor, so the seed reads as that visitor too —
49085
+ // even beside a member-login clientId, and never repointed by a stray local WIX_CLIENT_ID.
49086
+ const oneApp = auth?.visitorSource === "one-app";
49087
+ const clientId = oneApp ? "" : process.env.WIX_CLIENT_ID ?? auth?.clientId ?? "";
49088
+ if (clientId && !clientId.startsWith("YOUR_")) {
49089
+ const mint = await fetch(WIX + "/oauth2/token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ clientId, grantType: "anonymous" }) });
49090
+ return ((await mint.json()) as { access_token?: string }).access_token;
49091
+ }
49092
+ const msid = oneApp ? auth?.metaSiteId ?? "" : "";
49093
+ if (!/^[0-9a-fA-F-]{10,64}$/.test(msid)) return undefined;
49094
+ const mint = await fetch(`https://apps.wix.com/_api/one-app-session-web/v3/businesses/${msid}/signed-instance?signed_instance_type=Visitor`, { headers: { Accept: "application/json" }, redirect: "error" });
49095
+ const visitor = (await mint.json()) as { signedInstance?: string; signedInstanceType?: string };
49096
+ return visitor.signedInstanceType === "Visitor" ? visitor.signedInstance : undefined;
49097
+ }
49098
+
49080
49099
  async function liveSeedIds(spec: LiveSeed): Promise<string[]> {
49081
49100
  try {
49082
49101
  const WIX = "https://www.wixapis.com";
49083
- const clientId = process.env.WIX_CLIENT_ID ?? (config as { auth?: { config?: { clientId?: string } } }).auth?.config?.clientId ?? "";
49084
- if (!clientId || clientId.startsWith("YOUR_")) return [];
49085
- const mint = await fetch(WIX + "/oauth2/token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ clientId, grantType: "anonymous" }) });
49086
- const token = ((await mint.json()) as { access_token?: string }).access_token;
49102
+ const token = await liveSeedToken(WIX);
49087
49103
  if (!token) return [];
49088
49104
  const res = await fetch(WIX + spec.path, { method: "POST", headers: { Authorization: token, "Content-Type": "application/json" }, body: JSON.stringify(spec.body) });
49089
49105
  const json = (await res.json()) as Record<string, unknown>;
@@ -4,6 +4,7 @@
4
4
  // SDK; this only supplies the copy and the hydration reset.
5
5
  import { hostSessionFromGlobal, type IapPriceCopy } from "@himalaya/iap";
6
6
  import type { IapCommerce, IapPurchaseCopy, IapRestoreResult } from "@himalaya/iap-checkout";
7
+ import { paymentSetupMessage } from "./payment-setup.js";
7
8
  import { copyParam, copyTemplate } from "./himi-copy.js";
8
9
 
9
10
  /** Price labels from the screen's copy pack (`<prefix>.priceLoadingText`, …). */
@@ -25,6 +26,7 @@ export function iapPurchaseCopy(prefix: string): IapPurchaseCopy {
25
26
  awaitingApproval: copyParam(`${prefix}.purchaseAwaitingApprovalText`, "Waiting for approval"),
26
27
  unavailable: copyParam(`${prefix}.purchaseTemporarilyUnavailableText`, "Purchasing is temporarily unavailable."),
27
28
  signInRequired: copyParam(`${prefix}.purchaseSignInText`, "Sign in to buy."),
29
+ sitePaymentUnavailable: paymentSetupMessage(prefix),
28
30
  retry: copyParam(`${prefix}.purchaseRetryText`, "Something went wrong. Try again."),
29
31
  priceChanged: copyParam(`${prefix}.purchasePriceChangedText`, "The price changed. Check it and try again."),
30
32
  support: copyParam(`${prefix}.purchaseSupportText`, "We couldn't finish this purchase. Contact support."),
@@ -0,0 +1,6 @@
1
+ import { copyParam } from "./himi-copy.js";
2
+
3
+ /** No provider or country guess: Wix only established that online payment is unavailable. */
4
+ export function paymentSetupMessage(prefix: string): string {
5
+ return copyParam(`${prefix}.paymentSetupUnavailable`, "This site is not accepting online payments. Go back or contact the business for other payment options.");
6
+ }
@@ -1,3 +1,4 @@
1
+ import { paymentSetupMessage } from "../_shared/payment-setup.js";
1
2
  import { withOnlineWrites, offlineWritesEnabled } from "../_shared/online-actions.js";
2
3
  // Tier-5 bundle: himi-booking — availability and member booking.
3
4
  import { defineService as registerService, navigate } from "@himalaya/state";
@@ -9,7 +10,7 @@ import { dispatch } from "@himalaya/dispatch";
9
10
  import { bookSchedule, bookSlot, getService, queryServiceAvailability, registerToWaitlist, type WixBookingSlotFull, type WixSelectedPaymentOption, type WixService } from "@himalaya/wix-bookings";
10
11
  import { createCheckout, createOrder } from "@himalaya/wix-ecom";
11
12
  import { createRedirectSession, type WixSlot } from "@himalaya/wix-redirects";
12
- import { payCashierOrder } from "@himalaya/checkout";
13
+ import { isPaymentSetupFailure, payCashierOrder } from "@himalaya/checkout";
13
14
  import { hostedCheckoutOpener, isHostedWorker } from "../_shared/hosted-checkout.js";
14
15
  import { session as wixPlatformSession } from "@himalaya/wix-platform";
15
16
  import { displayName, getMyData, type WixMyData } from "@himalaya/wix-users";
@@ -1587,10 +1588,12 @@ async function bookSelectedIn(s: S): Promise<S> {
1587
1588
  ? copyParam("time.coursePaymentCancelled", "Payment cancelled — your course booking wasn't confirmed. Try again.")
1588
1589
  : copyParam("time.paymentCancelled", "Payment cancelled — your booking wasn't confirmed. Pick a time to try again.") });
1589
1590
  }
1591
+ if (isPaymentSetupFailure(pay)) return derive({ ...s, bookingInFlight: false, message: paymentSetupMessage("time") });
1590
1592
  return derive({ ...s, bookingInFlight: false, message: s.isCourse
1591
1593
  ? copyParam("time.coursePaymentFailed", "Payment couldn't be completed. Try again.")
1592
1594
  : copyParam("time.paymentFailed", "Payment couldn't be completed. Pick another time or try again.") });
1593
- } catch {
1595
+ } catch (error) {
1596
+ if (isPaymentSetupFailure(error)) return derive({ ...s, bookingInFlight: false, message: paymentSetupMessage("time") });
1594
1597
  return derive({ ...s, bookingInFlight: false, message: s.isCourse
1595
1598
  ? copyParam("time.courseCheckoutFailed", "Checkout could not start. Try again.")
1596
1599
  : copyParam("time.checkoutFailed", "Checkout could not start. Pick another time or try again.") });
@@ -1,3 +1,4 @@
1
+ import { paymentSetupMessage } from "../_shared/payment-setup.js";
1
2
  import { withOnlineWrites } from "../_shared/online-actions.js";
2
3
  import { defineService as registerState } from "@himalaya/state";
3
4
  import { getStoreProduct } from "../_shared/himi-store.js";
@@ -7,7 +8,7 @@ import { withIapPresentation } from "../_shared/iap-presentation.js";
7
8
  import { type IapCommerce, classifyCart } from "@himalaya/iap-checkout";
8
9
  import { addCouponToCurrentCart, calculateCurrentCart, createCheckoutFromCurrentCart, getCurrentCart, removeCouponFromCurrentCart, removeLineItemsFromCurrentCart, updateLineItemsQuantity, type WixCartDetail, type WixCartLineItemDetail } from "@himalaya/wix-ecom";
9
10
  import { createRedirectSession } from "@himalaya/wix-redirects";
10
- import { payCashierOrder } from "@himalaya/checkout";
11
+ import { isPaymentSetupFailure, payCashierOrder } from "@himalaya/checkout";
11
12
  import { hostedCheckoutOpener, isHostedWorker } from "../_shared/hosted-checkout.js";
12
13
  import { formatCurrency } from "../_shared/engine-intl.js";
13
14
 
@@ -574,8 +575,10 @@ const svc = defineService<State, {
574
575
  // only confirmed once it lands, so the cart must survive a bail-out.
575
576
  return derive({ ...state, checkoutHint: copyParam("cart.checkoutOpenedMessage", "Checkout opened.") });
576
577
  }
578
+ if (isPaymentSetupFailure(pay)) return derive({ ...state, checkoutHint: paymentSetupMessage("cart") });
577
579
  return derive({ ...state, checkoutHint: copyParam("cart.checkoutFailedMessage", "Checkout could not start.") });
578
- } catch {
580
+ } catch (error) {
581
+ if (isPaymentSetupFailure(error)) return derive({ ...state, checkoutHint: paymentSetupMessage("cart") });
579
582
  return derive({ ...state, checkoutHint: copyParam("cart.checkoutFailedMessage", "Checkout could not start.") });
580
583
  }
581
584
  },
@@ -1,3 +1,4 @@
1
+ import { paymentSetupMessage } from "../_shared/payment-setup.js";
1
2
  import { offlineWriteMessage, offlineWritesEnabled, withOnlineWrites } from "../_shared/online-actions.js";
2
3
  import { connectivity } from "@himalaya/state";
3
4
  import { defineService as registerService } from "../_shared/collection-service.js";
@@ -6,7 +7,7 @@ import { createRedirectSession } from "@himalaya/wix-redirects";
6
7
  import { open as openUrl } from "@himalaya/open-url";
7
8
  import { session as wixPlatformSession } from "@himalaya/wix-platform";
8
9
  import { copyParam, copyTemplate, stashCopy } from "../_shared/himi-copy.js";
9
- import { activePlanIds, entitlement, payCashierOrder } from "@himalaya/checkout";
10
+ import { activePlanIds, entitlement, isPaymentSetupFailure, payCashierOrder } from "@himalaya/checkout";
10
11
  import { hostSessionFromGlobal, iapPriceFields, type IapCatalogItem } from "@himalaya/iap";
11
12
  import { createIapCommerce, pricingPlanOrders } from "@himalaya/iap-checkout";
12
13
  import { iapPriceCopy, iapPurchaseCopy, resetUnhydratedKeysOnOlderRuntime } from "../_shared/iap-worker.js";
@@ -290,7 +291,9 @@ async function regularJoinMessage(row: PlanRow, reference: { kind: string; id: s
290
291
  if (pay.kind === "cancelled") {
291
292
  return copyParam("plans.joinCancelledMessage", "Payment cancelled — you haven't joined.");
292
293
  }
293
- } catch {
294
+ if (isPaymentSetupFailure(pay)) return paymentSetupMessage("plans");
295
+ } catch (error) {
296
+ if (isPaymentSetupFailure(error)) return paymentSetupMessage("plans");
294
297
  }
295
298
  return copyTemplate("plans.joinOnSiteMessage", "Memberships are purchased on the website — visit the site to join {name}.", { name: row.name });
296
299
  }
@@ -1,3 +1,4 @@
1
+ import { paymentSetupMessage } from "../_shared/payment-setup.js";
1
2
  // Tier-5 bundle: himi-events — event detail.
2
3
  import { calendarDay, formatClock, formatCurrency, formatWeekdayDateTime, formatWeekdayMonthDay, formatTimeZoneLabel } from "../_shared/engine-intl.js";
3
4
  import { defineService as registerService } from "@himalaya/state";
@@ -18,7 +19,7 @@ import {
18
19
  type WixEvent,
19
20
  } from "@himalaya/wix-events";
20
21
  import { session as wixPlatformSession } from "@himalaya/wix-platform";
21
- import { payCashierOrder } from "@himalaya/checkout";
22
+ import { isPaymentSetupFailure, payCashierOrder } from "@himalaya/checkout";
22
23
  import { get, upsert, type DbEntity } from "@himalaya/local-db";
23
24
  import {
24
25
  currentInputId,
@@ -986,16 +987,18 @@ const svc: ReturnType<typeof defineService<S, DetailActions>> = defineService<S,
986
987
  if (pay.kind === "paid") result = { ...s, ticketSheetOpen: false, snackbarMessage: detailCopy("event.ticketIssued", "You're in — your ticket is confirmed.") };
987
988
  else if (pay.kind === "handoff") result = { ...s, ticketSheetOpen: false, snackbarMessage: detailCopy("event.checkoutOpened", "Complete checkout in the browser to get your tickets.") };
988
989
  else if (pay.kind === "cancelled") result = { ...s, snackbarMessage: detailCopy("event.ticketCancelled", "Payment cancelled — no ticket was issued.") };
990
+ else if (isPaymentSetupFailure(pay)) result = { ...s, snackbarMessage: paymentSetupMessage("event") };
989
991
  else throw new Error("Payment could not be completed");
990
992
  }
991
993
  }
992
994
  }
993
- } catch {
995
+ } catch (error) {
996
+ if (isPaymentSetupFailure(error)) result = { ...s, snackbarMessage: paymentSetupMessage("event") };
994
997
  try {
995
998
  // A public event page is not pinned to this selection or price. Only
996
999
  // continue in hosted checkout when a reservation already holds it.
997
1000
  const params = reservationId ? { reservationId } : undefined;
998
- if (params) {
1001
+ if (params && !result) {
999
1002
  const redirect = await createRedirectSession({ eventsCheckout: params });
1000
1003
  const url = redirect.redirectSession?.fullUrl;
1001
1004
  if (url) {
@@ -1003,7 +1006,9 @@ const svc: ReturnType<typeof defineService<S, DetailActions>> = defineService<S,
1003
1006
  result = { ...s, ticketSheetOpen: false, snackbarMessage: opened.opened ? detailCopy("event.checkoutOpened", "Complete checkout in the browser to get your tickets.") : detailCopy("event.registrationOpenFailed", "Could not open the registration page.") };
1004
1007
  }
1005
1008
  }
1006
- } catch { /* report below */ }
1009
+ } catch (error) {
1010
+ if (isPaymentSetupFailure(error)) result = { ...s, snackbarMessage: paymentSetupMessage("event") };
1011
+ }
1007
1012
  if (!result) result = { ...s, snackbarMessage: detailCopy("event.purchaseUnavailable", "Purchasing is temporarily unavailable.") };
1008
1013
  } finally { ticketCheckoutInFlight = false; }
1009
1014
  return derive(result!);
@@ -90716,15 +90716,31 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
90716
90716
 
90717
90717
  // Live seed-id enumeration: a published release serves only EXACT screens, so
90718
90718
  // `himi push` bakes each templated detail route's CURRENT catalog ids as exact
90719
- // screens via `seedIds`. Anonymous visitor rung against this app's own clientId;
90720
- // best-effort (offline build → no baked details, preview still live-resolves).
90719
+ // screens via `seedIds`. Anonymous visitor rung against this app's own clientId — or,
90720
+ // for an app bound through the One App visitor identity (`visitorSource: "one-app"`,
90721
+ // no client), that site's One App visitor instance. Best-effort (offline build → no
90722
+ // baked details, preview still live-resolves).
90723
+ async function liveSeedToken(WIX: string): Promise<string | undefined> {
90724
+ const auth = (config as { auth?: { config?: { clientId?: string; visitorSource?: string; metaSiteId?: string } } }).auth?.config;
90725
+ // A One App binding boots as the site's One App visitor, so the seed reads as that visitor too —
90726
+ // even beside a member-login clientId, and never repointed by a stray local WIX_CLIENT_ID.
90727
+ const oneApp = auth?.visitorSource === "one-app";
90728
+ const clientId = oneApp ? "" : process.env.WIX_CLIENT_ID ?? auth?.clientId ?? "";
90729
+ if (clientId && !clientId.startsWith("YOUR_")) {
90730
+ const mint = await fetch(WIX + "/oauth2/token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ clientId, grantType: "anonymous" }) });
90731
+ return ((await mint.json()) as { access_token?: string }).access_token;
90732
+ }
90733
+ const msid = oneApp ? auth?.metaSiteId ?? "" : "";
90734
+ if (!/^[0-9a-fA-F-]{10,64}$/.test(msid)) return undefined;
90735
+ const mint = await fetch(`https://apps.wix.com/_api/one-app-session-web/v3/businesses/${msid}/signed-instance?signed_instance_type=Visitor`, { headers: { Accept: "application/json" }, redirect: "error" });
90736
+ const visitor = (await mint.json()) as { signedInstance?: string; signedInstanceType?: string };
90737
+ return visitor.signedInstanceType === "Visitor" ? visitor.signedInstance : undefined;
90738
+ }
90739
+
90721
90740
  async function liveSeedIds(spec: LiveSeed): Promise<string[]> {
90722
90741
  try {
90723
90742
  const WIX = "https://www.wixapis.com";
90724
- const clientId = process.env.WIX_CLIENT_ID ?? (config as { auth?: { config?: { clientId?: string } } }).auth?.config?.clientId ?? "";
90725
- if (!clientId || clientId.startsWith("YOUR_")) return [];
90726
- const mint = await fetch(WIX + "/oauth2/token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ clientId, grantType: "anonymous" }) });
90727
- const token = ((await mint.json()) as { access_token?: string }).access_token;
90743
+ const token = await liveSeedToken(WIX);
90728
90744
  if (!token) return [];
90729
90745
  const res = await fetch(WIX + spec.path, { method: "POST", headers: { Authorization: token, "Content-Type": "application/json" }, body: JSON.stringify(spec.body) });
90730
90746
  const json = (await res.json()) as Record<string, unknown>;