@wix/himalaya-cli 0.1362.0 → 0.1364.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +10 -8
- package/package.json +2 -2
- package/templates/beauty-salon/tier5-src/himi-branded-lite-plans/index.ts +23 -3
- package/templates/branded-lite/tier5-src/himi-branded-lite-plans/index.ts +23 -3
- package/templates/gym-fitness/tier5-src/himi-branded-lite-plans/index.ts +23 -3
- package/templates/nonprofit-cause/tier5-src/himi-branded-lite-plans/index.ts +23 -3
- package/templates/podcast-creator/tier5-src/himi-branded-lite-plans/index.ts +23 -3
- package/templates/yoga-studio/tier5-src/himi-branded-lite-plans/index.ts +23 -3
package/dist/cli.mjs
CHANGED
|
@@ -7650,6 +7650,7 @@ function buttonVariant(style) {
|
|
|
7650
7650
|
var SYSTEM_FONT_FAMILY, TONAL_ELEVATION_ALPHA, BUTTON_MIN_HIT_TARGET, nonNegative, nonEmpty, own, COLOR_TOKEN_PROPS, TYPOGRAPHY_TOKEN_PROPS, BUTTON_VARIANTS;
|
|
7651
7651
|
var init_tokens = __esm({
|
|
7652
7652
|
"../../core/ts/src/tokens.ts"() {
|
|
7653
|
+
"use strict";
|
|
7653
7654
|
SYSTEM_FONT_FAMILY = "$system";
|
|
7654
7655
|
TONAL_ELEVATION_ALPHA = [0, 0.05, 0.08, 0.11, 0.12, 0.14];
|
|
7655
7656
|
BUTTON_MIN_HIT_TARGET = 44;
|
|
@@ -7705,6 +7706,7 @@ function resolveTranslation(value, catalog3, sourceCatalog) {
|
|
|
7705
7706
|
var BINDING_SIGILS, BINDING_SIGIL_RE, TRANSLATION_PREFIX, TRANSLATION_KEY_RE;
|
|
7706
7707
|
var init_bindings = __esm({
|
|
7707
7708
|
"../../core/ts/src/bindings.ts"() {
|
|
7709
|
+
"use strict";
|
|
7708
7710
|
BINDING_SIGILS = ["service", "flag", "item", "t"];
|
|
7709
7711
|
BINDING_SIGIL_RE = new RegExp(
|
|
7710
7712
|
`^\\$(?:${BINDING_SIGILS.filter((s2) => s2 !== "t").join("|")})\\b|^\\$t:`
|
|
@@ -13544,12 +13546,10 @@ var init_player_host_manifest = __esm({
|
|
|
13544
13546
|
],
|
|
13545
13547
|
"unlinked": {
|
|
13546
13548
|
"ios": [
|
|
13547
|
-
"base44",
|
|
13548
13549
|
"godot"
|
|
13549
13550
|
],
|
|
13550
13551
|
"macos": [
|
|
13551
13552
|
"audio_record_background",
|
|
13552
|
-
"base44",
|
|
13553
13553
|
"browser_auth",
|
|
13554
13554
|
"godot",
|
|
13555
13555
|
"iap",
|
|
@@ -13560,7 +13560,6 @@ var init_player_host_manifest = __esm({
|
|
|
13560
13560
|
"wix_cashier"
|
|
13561
13561
|
],
|
|
13562
13562
|
"android": [
|
|
13563
|
-
"base44",
|
|
13564
13563
|
"godot"
|
|
13565
13564
|
],
|
|
13566
13565
|
"web": []
|
|
@@ -24884,7 +24883,7 @@ function assessPackage(header, host) {
|
|
|
24884
24883
|
if (caps.length) out2.push({ reason: "missing_capabilities", missing: caps });
|
|
24885
24884
|
const comps = missing(req.nativeComponents ?? [], host.nativeComponents);
|
|
24886
24885
|
if (comps.length) out2.push({ reason: "missing_components", missing: comps });
|
|
24887
|
-
const auth = missing(req.authProviders ?? [], host.authProviders);
|
|
24886
|
+
const auth = missing((req.authProviders ?? []).filter((id) => id !== "none"), host.authProviders);
|
|
24888
24887
|
if (auth.length) out2.push({ reason: "missing_auth_providers", missing: auth });
|
|
24889
24888
|
}
|
|
24890
24889
|
return out2;
|
|
@@ -28037,8 +28036,8 @@ var init_shell_capabilities_generated = __esm({
|
|
|
28037
28036
|
appDir: "apps/himi-player",
|
|
28038
28037
|
shared: true,
|
|
28039
28038
|
extensible: false,
|
|
28040
|
-
built: ["animation", "audio_record_background", "biometrics", "calendar", "calls", "camera", "clipboard", "contacts", "haptic", "iap", "location", "media_playback", "motion", "on_device_ai", "photos", "push", "speech"],
|
|
28041
|
-
serves: ["animation", "app_info", "audio_record", "audio_record_background", "biometrics", "browser_auth", "calendar", "calls", "camera", "clipboard", "contacts", "file_picker", "files", "haptic", "himi_apps", "iap", "keyboard", "local_database", "location", "media_playback", "motion", "on_device_ai", "open_url", "pdf", "photos", "presigned_upload", "push", "release_control", "secure_storage", "share", "speaker_diarization", "speech", "stream", "theme", "ui", "wix_api", "wix_cashier", "wix_platform"]
|
|
28039
|
+
built: ["animation", "audio_record_background", "base44", "biometrics", "calendar", "calls", "camera", "clipboard", "contacts", "haptic", "iap", "location", "media_playback", "motion", "on_device_ai", "photos", "push", "speech"],
|
|
28040
|
+
serves: ["animation", "app_info", "audio_record", "audio_record_background", "base44", "biometrics", "browser_auth", "calendar", "calls", "camera", "clipboard", "contacts", "file_picker", "files", "haptic", "himi_apps", "iap", "keyboard", "local_database", "location", "media_playback", "motion", "on_device_ai", "open_url", "pdf", "photos", "presigned_upload", "push", "release_control", "secure_storage", "share", "speaker_diarization", "speech", "stream", "theme", "ui", "wix_api", "wix_cashier", "wix_platform"]
|
|
28042
28041
|
}
|
|
28043
28042
|
};
|
|
28044
28043
|
}
|
|
@@ -137339,7 +137338,9 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
|
|
|
137339
137338
|
playerRequirements: {
|
|
137340
137339
|
capabilities: [...requiredCapabilities].sort(),
|
|
137341
137340
|
nativeComponents: [...requiredNativeComponents].sort(),
|
|
137342
|
-
|
|
137341
|
+
// `none` is the scaffold's spelling of "no auth" (`new-app --auth=none`), not a provider a
|
|
137342
|
+
// host must carry — recording it blocked every such app in the Player picker (#3695).
|
|
137343
|
+
authProviders: [config.auth.providerId].filter((id) => id && id !== "none").sort()
|
|
137343
137344
|
},
|
|
137344
137345
|
// #4091 — the app takes its Wix identity from a HOST (Spaces Lite) instead of signing in
|
|
137345
137346
|
// itself (#4088 `hostProvided` on its auth provider). A host that lends its identity only
|
|
@@ -169974,7 +169975,8 @@ function missingClaims(requirements, offered) {
|
|
|
169974
169975
|
const out2 = {};
|
|
169975
169976
|
for (const kind of HOST_CLAIM_KINDS) {
|
|
169976
169977
|
const has = new Set(offered[kind]);
|
|
169977
|
-
|
|
169978
|
+
const required3 = kind === "authProviders" ? requirements[kind].filter((id) => id !== "none") : requirements[kind];
|
|
169979
|
+
out2[kind] = [...new Set(required3.filter((id) => !has.has(id)))];
|
|
169978
169980
|
}
|
|
169979
169981
|
return out2;
|
|
169980
169982
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/himalaya-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1364.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
|
|
6
6
|
"keywords": [
|
|
@@ -71,5 +71,5 @@
|
|
|
71
71
|
"artifactId": "himalaya-cli"
|
|
72
72
|
}
|
|
73
73
|
},
|
|
74
|
-
"falconPackageHash": "
|
|
74
|
+
"falconPackageHash": "d3c28af7c96bcc51af2111ceb585221c63a75150cacb4a2e9ec7facc"
|
|
75
75
|
}
|
|
@@ -9,7 +9,7 @@ import { hostSessionFromGlobal, iapPriceFields, type IapCatalogItem } from "@him
|
|
|
9
9
|
import { createIapCommerce, pricingPlanOrders } from "@himalaya/iap-checkout";
|
|
10
10
|
import { iapPriceCopy, iapPurchaseCopy, resetUnhydratedKeysOnOlderRuntime } from "../_shared/iap-worker.js";
|
|
11
11
|
import { formatCurrency } from "../_shared/engine-intl.js";
|
|
12
|
-
import { isHostedWorker } from "../_shared/hosted-checkout.js";
|
|
12
|
+
import { isHostedWorker, hostedSitePageOpener } from "../_shared/hosted-checkout.js";
|
|
13
13
|
|
|
14
14
|
/** Wix Pricing Plans appDefId — the vertical that owns a plan order, so the `appId` its
|
|
15
15
|
* cashier order is paid under (mirrors AppDefId.PRICING_PLANS in Wix's RN apps). */
|
|
@@ -186,6 +186,8 @@ let started = false;
|
|
|
186
186
|
let loading = 0;
|
|
187
187
|
let contentReady = false;
|
|
188
188
|
const joining = new Set<string>();
|
|
189
|
+
/** A hosted paid plan's page is being prepared by the host (one ticket at a time). */
|
|
190
|
+
let hostedPlanOpenInFlight = false;
|
|
189
191
|
const memberSession = hostSessionFromGlobal();
|
|
190
192
|
let regularGeneration = -1;
|
|
191
193
|
let regularAccess = new Set<string>();
|
|
@@ -227,9 +229,27 @@ async function regularJoinMessage(row: PlanRow, reference: { kind: string; id: s
|
|
|
227
229
|
// Hosted inside a host shell (Spaces) a plan cannot be paid in-app: classic Pricing Plans is not on
|
|
228
230
|
// ecom, so the host's member checkout has no checkout to open, and the redirect session needs a
|
|
229
231
|
// headless OAuth client a hosted app does not have. An online order created here would only be
|
|
230
|
-
// left behind as a DRAFT, so none is — the member joins on the site
|
|
231
|
-
//
|
|
232
|
+
// left behind as a DRAFT, so none is — the member joins on the site: the HOST opens the site's own
|
|
233
|
+
// plans page signed in as the lent member (`himi.host.memberSitePage {page:"plans"}`), or this
|
|
234
|
+
// message stands when it cannot. A FREE plan has nothing to pay: its online order activates at
|
|
235
|
+
// once, so it still joins in-app.
|
|
232
236
|
if (isHostedWorker() && !row.free) {
|
|
237
|
+
// One host open at a time: the host keeps a single ticket, so a second plan's prepare would replace the
|
|
238
|
+
// first's and drop it. The checkout guard is re-read right before the host opens the page.
|
|
239
|
+
if (hostedPlanOpenInFlight) return "";
|
|
240
|
+
hostedPlanOpenInFlight = true;
|
|
241
|
+
let opened: boolean | "withdrawn";
|
|
242
|
+
try {
|
|
243
|
+
opened = await hostedSitePageOpener({ page: "plans", planId: row.id })(
|
|
244
|
+
async () => commerce.pricing.continueCheckoutGuard(reference).allowed);
|
|
245
|
+
} finally {
|
|
246
|
+
hostedPlanOpenInFlight = false;
|
|
247
|
+
}
|
|
248
|
+
if (generation !== memberSession.current().generation) return "";
|
|
249
|
+
if (opened === "withdrawn") return "";
|
|
250
|
+
if (opened === true) {
|
|
251
|
+
return copyTemplate("plans.continueOnSiteMessage", "Finish joining {name} on the site.", { name: row.name });
|
|
252
|
+
}
|
|
233
253
|
return copyTemplate("plans.joinOnSiteMessage", "Memberships are purchased on the website — visit the site to join {name}.", { name: row.name });
|
|
234
254
|
}
|
|
235
255
|
try {
|
|
@@ -9,7 +9,7 @@ import { hostSessionFromGlobal, iapPriceFields, type IapCatalogItem } from "@him
|
|
|
9
9
|
import { createIapCommerce, pricingPlanOrders } from "@himalaya/iap-checkout";
|
|
10
10
|
import { iapPriceCopy, iapPurchaseCopy, resetUnhydratedKeysOnOlderRuntime } from "../_shared/iap-worker.js";
|
|
11
11
|
import { formatCurrency } from "../_shared/engine-intl.js";
|
|
12
|
-
import { isHostedWorker } from "../_shared/hosted-checkout.js";
|
|
12
|
+
import { isHostedWorker, hostedSitePageOpener } from "../_shared/hosted-checkout.js";
|
|
13
13
|
|
|
14
14
|
/** Wix Pricing Plans appDefId — the vertical that owns a plan order, so the `appId` its
|
|
15
15
|
* cashier order is paid under (mirrors AppDefId.PRICING_PLANS in Wix's RN apps). */
|
|
@@ -186,6 +186,8 @@ let started = false;
|
|
|
186
186
|
let loading = 0;
|
|
187
187
|
let contentReady = false;
|
|
188
188
|
const joining = new Set<string>();
|
|
189
|
+
/** A hosted paid plan's page is being prepared by the host (one ticket at a time). */
|
|
190
|
+
let hostedPlanOpenInFlight = false;
|
|
189
191
|
const memberSession = hostSessionFromGlobal();
|
|
190
192
|
let regularGeneration = -1;
|
|
191
193
|
let regularAccess = new Set<string>();
|
|
@@ -227,9 +229,27 @@ async function regularJoinMessage(row: PlanRow, reference: { kind: string; id: s
|
|
|
227
229
|
// Hosted inside a host shell (Spaces) a plan cannot be paid in-app: classic Pricing Plans is not on
|
|
228
230
|
// ecom, so the host's member checkout has no checkout to open, and the redirect session needs a
|
|
229
231
|
// headless OAuth client a hosted app does not have. An online order created here would only be
|
|
230
|
-
// left behind as a DRAFT, so none is — the member joins on the site
|
|
231
|
-
//
|
|
232
|
+
// left behind as a DRAFT, so none is — the member joins on the site: the HOST opens the site's own
|
|
233
|
+
// plans page signed in as the lent member (`himi.host.memberSitePage {page:"plans"}`), or this
|
|
234
|
+
// message stands when it cannot. A FREE plan has nothing to pay: its online order activates at
|
|
235
|
+
// once, so it still joins in-app.
|
|
232
236
|
if (isHostedWorker() && !row.free) {
|
|
237
|
+
// One host open at a time: the host keeps a single ticket, so a second plan's prepare would replace the
|
|
238
|
+
// first's and drop it. The checkout guard is re-read right before the host opens the page.
|
|
239
|
+
if (hostedPlanOpenInFlight) return "";
|
|
240
|
+
hostedPlanOpenInFlight = true;
|
|
241
|
+
let opened: boolean | "withdrawn";
|
|
242
|
+
try {
|
|
243
|
+
opened = await hostedSitePageOpener({ page: "plans", planId: row.id })(
|
|
244
|
+
async () => commerce.pricing.continueCheckoutGuard(reference).allowed);
|
|
245
|
+
} finally {
|
|
246
|
+
hostedPlanOpenInFlight = false;
|
|
247
|
+
}
|
|
248
|
+
if (generation !== memberSession.current().generation) return "";
|
|
249
|
+
if (opened === "withdrawn") return "";
|
|
250
|
+
if (opened === true) {
|
|
251
|
+
return copyTemplate("plans.continueOnSiteMessage", "Finish joining {name} on the site.", { name: row.name });
|
|
252
|
+
}
|
|
233
253
|
return copyTemplate("plans.joinOnSiteMessage", "Memberships are purchased on the website — visit the site to join {name}.", { name: row.name });
|
|
234
254
|
}
|
|
235
255
|
try {
|
|
@@ -9,7 +9,7 @@ import { hostSessionFromGlobal, iapPriceFields, type IapCatalogItem } from "@him
|
|
|
9
9
|
import { createIapCommerce, pricingPlanOrders } from "@himalaya/iap-checkout";
|
|
10
10
|
import { iapPriceCopy, iapPurchaseCopy, resetUnhydratedKeysOnOlderRuntime } from "../_shared/iap-worker.js";
|
|
11
11
|
import { formatCurrency } from "../_shared/engine-intl.js";
|
|
12
|
-
import { isHostedWorker } from "../_shared/hosted-checkout.js";
|
|
12
|
+
import { isHostedWorker, hostedSitePageOpener } from "../_shared/hosted-checkout.js";
|
|
13
13
|
|
|
14
14
|
/** Wix Pricing Plans appDefId — the vertical that owns a plan order, so the `appId` its
|
|
15
15
|
* cashier order is paid under (mirrors AppDefId.PRICING_PLANS in Wix's RN apps). */
|
|
@@ -186,6 +186,8 @@ let started = false;
|
|
|
186
186
|
let loading = 0;
|
|
187
187
|
let contentReady = false;
|
|
188
188
|
const joining = new Set<string>();
|
|
189
|
+
/** A hosted paid plan's page is being prepared by the host (one ticket at a time). */
|
|
190
|
+
let hostedPlanOpenInFlight = false;
|
|
189
191
|
const memberSession = hostSessionFromGlobal();
|
|
190
192
|
let regularGeneration = -1;
|
|
191
193
|
let regularAccess = new Set<string>();
|
|
@@ -227,9 +229,27 @@ async function regularJoinMessage(row: PlanRow, reference: { kind: string; id: s
|
|
|
227
229
|
// Hosted inside a host shell (Spaces) a plan cannot be paid in-app: classic Pricing Plans is not on
|
|
228
230
|
// ecom, so the host's member checkout has no checkout to open, and the redirect session needs a
|
|
229
231
|
// headless OAuth client a hosted app does not have. An online order created here would only be
|
|
230
|
-
// left behind as a DRAFT, so none is — the member joins on the site
|
|
231
|
-
//
|
|
232
|
+
// left behind as a DRAFT, so none is — the member joins on the site: the HOST opens the site's own
|
|
233
|
+
// plans page signed in as the lent member (`himi.host.memberSitePage {page:"plans"}`), or this
|
|
234
|
+
// message stands when it cannot. A FREE plan has nothing to pay: its online order activates at
|
|
235
|
+
// once, so it still joins in-app.
|
|
232
236
|
if (isHostedWorker() && !row.free) {
|
|
237
|
+
// One host open at a time: the host keeps a single ticket, so a second plan's prepare would replace the
|
|
238
|
+
// first's and drop it. The checkout guard is re-read right before the host opens the page.
|
|
239
|
+
if (hostedPlanOpenInFlight) return "";
|
|
240
|
+
hostedPlanOpenInFlight = true;
|
|
241
|
+
let opened: boolean | "withdrawn";
|
|
242
|
+
try {
|
|
243
|
+
opened = await hostedSitePageOpener({ page: "plans", planId: row.id })(
|
|
244
|
+
async () => commerce.pricing.continueCheckoutGuard(reference).allowed);
|
|
245
|
+
} finally {
|
|
246
|
+
hostedPlanOpenInFlight = false;
|
|
247
|
+
}
|
|
248
|
+
if (generation !== memberSession.current().generation) return "";
|
|
249
|
+
if (opened === "withdrawn") return "";
|
|
250
|
+
if (opened === true) {
|
|
251
|
+
return copyTemplate("plans.continueOnSiteMessage", "Finish joining {name} on the site.", { name: row.name });
|
|
252
|
+
}
|
|
233
253
|
return copyTemplate("plans.joinOnSiteMessage", "Memberships are purchased on the website — visit the site to join {name}.", { name: row.name });
|
|
234
254
|
}
|
|
235
255
|
try {
|
|
@@ -9,7 +9,7 @@ import { hostSessionFromGlobal, iapPriceFields, type IapCatalogItem } from "@him
|
|
|
9
9
|
import { createIapCommerce, pricingPlanOrders } from "@himalaya/iap-checkout";
|
|
10
10
|
import { iapPriceCopy, iapPurchaseCopy, resetUnhydratedKeysOnOlderRuntime } from "../_shared/iap-worker.js";
|
|
11
11
|
import { formatCurrency } from "../_shared/engine-intl.js";
|
|
12
|
-
import { isHostedWorker } from "../_shared/hosted-checkout.js";
|
|
12
|
+
import { isHostedWorker, hostedSitePageOpener } from "../_shared/hosted-checkout.js";
|
|
13
13
|
|
|
14
14
|
/** Wix Pricing Plans appDefId — the vertical that owns a plan order, so the `appId` its
|
|
15
15
|
* cashier order is paid under (mirrors AppDefId.PRICING_PLANS in Wix's RN apps). */
|
|
@@ -186,6 +186,8 @@ let started = false;
|
|
|
186
186
|
let loading = 0;
|
|
187
187
|
let contentReady = false;
|
|
188
188
|
const joining = new Set<string>();
|
|
189
|
+
/** A hosted paid plan's page is being prepared by the host (one ticket at a time). */
|
|
190
|
+
let hostedPlanOpenInFlight = false;
|
|
189
191
|
const memberSession = hostSessionFromGlobal();
|
|
190
192
|
let regularGeneration = -1;
|
|
191
193
|
let regularAccess = new Set<string>();
|
|
@@ -227,9 +229,27 @@ async function regularJoinMessage(row: PlanRow, reference: { kind: string; id: s
|
|
|
227
229
|
// Hosted inside a host shell (Spaces) a plan cannot be paid in-app: classic Pricing Plans is not on
|
|
228
230
|
// ecom, so the host's member checkout has no checkout to open, and the redirect session needs a
|
|
229
231
|
// headless OAuth client a hosted app does not have. An online order created here would only be
|
|
230
|
-
// left behind as a DRAFT, so none is — the member joins on the site
|
|
231
|
-
//
|
|
232
|
+
// left behind as a DRAFT, so none is — the member joins on the site: the HOST opens the site's own
|
|
233
|
+
// plans page signed in as the lent member (`himi.host.memberSitePage {page:"plans"}`), or this
|
|
234
|
+
// message stands when it cannot. A FREE plan has nothing to pay: its online order activates at
|
|
235
|
+
// once, so it still joins in-app.
|
|
232
236
|
if (isHostedWorker() && !row.free) {
|
|
237
|
+
// One host open at a time: the host keeps a single ticket, so a second plan's prepare would replace the
|
|
238
|
+
// first's and drop it. The checkout guard is re-read right before the host opens the page.
|
|
239
|
+
if (hostedPlanOpenInFlight) return "";
|
|
240
|
+
hostedPlanOpenInFlight = true;
|
|
241
|
+
let opened: boolean | "withdrawn";
|
|
242
|
+
try {
|
|
243
|
+
opened = await hostedSitePageOpener({ page: "plans", planId: row.id })(
|
|
244
|
+
async () => commerce.pricing.continueCheckoutGuard(reference).allowed);
|
|
245
|
+
} finally {
|
|
246
|
+
hostedPlanOpenInFlight = false;
|
|
247
|
+
}
|
|
248
|
+
if (generation !== memberSession.current().generation) return "";
|
|
249
|
+
if (opened === "withdrawn") return "";
|
|
250
|
+
if (opened === true) {
|
|
251
|
+
return copyTemplate("plans.continueOnSiteMessage", "Finish joining {name} on the site.", { name: row.name });
|
|
252
|
+
}
|
|
233
253
|
return copyTemplate("plans.joinOnSiteMessage", "Memberships are purchased on the website — visit the site to join {name}.", { name: row.name });
|
|
234
254
|
}
|
|
235
255
|
try {
|
|
@@ -9,7 +9,7 @@ import { hostSessionFromGlobal, iapPriceFields, type IapCatalogItem } from "@him
|
|
|
9
9
|
import { createIapCommerce, pricingPlanOrders } from "@himalaya/iap-checkout";
|
|
10
10
|
import { iapPriceCopy, iapPurchaseCopy, resetUnhydratedKeysOnOlderRuntime } from "../_shared/iap-worker.js";
|
|
11
11
|
import { formatCurrency } from "../_shared/engine-intl.js";
|
|
12
|
-
import { isHostedWorker } from "../_shared/hosted-checkout.js";
|
|
12
|
+
import { isHostedWorker, hostedSitePageOpener } from "../_shared/hosted-checkout.js";
|
|
13
13
|
|
|
14
14
|
/** Wix Pricing Plans appDefId — the vertical that owns a plan order, so the `appId` its
|
|
15
15
|
* cashier order is paid under (mirrors AppDefId.PRICING_PLANS in Wix's RN apps). */
|
|
@@ -186,6 +186,8 @@ let started = false;
|
|
|
186
186
|
let loading = 0;
|
|
187
187
|
let contentReady = false;
|
|
188
188
|
const joining = new Set<string>();
|
|
189
|
+
/** A hosted paid plan's page is being prepared by the host (one ticket at a time). */
|
|
190
|
+
let hostedPlanOpenInFlight = false;
|
|
189
191
|
const memberSession = hostSessionFromGlobal();
|
|
190
192
|
let regularGeneration = -1;
|
|
191
193
|
let regularAccess = new Set<string>();
|
|
@@ -227,9 +229,27 @@ async function regularJoinMessage(row: PlanRow, reference: { kind: string; id: s
|
|
|
227
229
|
// Hosted inside a host shell (Spaces) a plan cannot be paid in-app: classic Pricing Plans is not on
|
|
228
230
|
// ecom, so the host's member checkout has no checkout to open, and the redirect session needs a
|
|
229
231
|
// headless OAuth client a hosted app does not have. An online order created here would only be
|
|
230
|
-
// left behind as a DRAFT, so none is — the member joins on the site
|
|
231
|
-
//
|
|
232
|
+
// left behind as a DRAFT, so none is — the member joins on the site: the HOST opens the site's own
|
|
233
|
+
// plans page signed in as the lent member (`himi.host.memberSitePage {page:"plans"}`), or this
|
|
234
|
+
// message stands when it cannot. A FREE plan has nothing to pay: its online order activates at
|
|
235
|
+
// once, so it still joins in-app.
|
|
232
236
|
if (isHostedWorker() && !row.free) {
|
|
237
|
+
// One host open at a time: the host keeps a single ticket, so a second plan's prepare would replace the
|
|
238
|
+
// first's and drop it. The checkout guard is re-read right before the host opens the page.
|
|
239
|
+
if (hostedPlanOpenInFlight) return "";
|
|
240
|
+
hostedPlanOpenInFlight = true;
|
|
241
|
+
let opened: boolean | "withdrawn";
|
|
242
|
+
try {
|
|
243
|
+
opened = await hostedSitePageOpener({ page: "plans", planId: row.id })(
|
|
244
|
+
async () => commerce.pricing.continueCheckoutGuard(reference).allowed);
|
|
245
|
+
} finally {
|
|
246
|
+
hostedPlanOpenInFlight = false;
|
|
247
|
+
}
|
|
248
|
+
if (generation !== memberSession.current().generation) return "";
|
|
249
|
+
if (opened === "withdrawn") return "";
|
|
250
|
+
if (opened === true) {
|
|
251
|
+
return copyTemplate("plans.continueOnSiteMessage", "Finish joining {name} on the site.", { name: row.name });
|
|
252
|
+
}
|
|
233
253
|
return copyTemplate("plans.joinOnSiteMessage", "Memberships are purchased on the website — visit the site to join {name}.", { name: row.name });
|
|
234
254
|
}
|
|
235
255
|
try {
|
|
@@ -9,7 +9,7 @@ import { hostSessionFromGlobal, iapPriceFields, type IapCatalogItem } from "@him
|
|
|
9
9
|
import { createIapCommerce, pricingPlanOrders } from "@himalaya/iap-checkout";
|
|
10
10
|
import { iapPriceCopy, iapPurchaseCopy, resetUnhydratedKeysOnOlderRuntime } from "../_shared/iap-worker.js";
|
|
11
11
|
import { formatCurrency } from "../_shared/engine-intl.js";
|
|
12
|
-
import { isHostedWorker } from "../_shared/hosted-checkout.js";
|
|
12
|
+
import { isHostedWorker, hostedSitePageOpener } from "../_shared/hosted-checkout.js";
|
|
13
13
|
|
|
14
14
|
/** Wix Pricing Plans appDefId — the vertical that owns a plan order, so the `appId` its
|
|
15
15
|
* cashier order is paid under (mirrors AppDefId.PRICING_PLANS in Wix's RN apps). */
|
|
@@ -186,6 +186,8 @@ let started = false;
|
|
|
186
186
|
let loading = 0;
|
|
187
187
|
let contentReady = false;
|
|
188
188
|
const joining = new Set<string>();
|
|
189
|
+
/** A hosted paid plan's page is being prepared by the host (one ticket at a time). */
|
|
190
|
+
let hostedPlanOpenInFlight = false;
|
|
189
191
|
const memberSession = hostSessionFromGlobal();
|
|
190
192
|
let regularGeneration = -1;
|
|
191
193
|
let regularAccess = new Set<string>();
|
|
@@ -227,9 +229,27 @@ async function regularJoinMessage(row: PlanRow, reference: { kind: string; id: s
|
|
|
227
229
|
// Hosted inside a host shell (Spaces) a plan cannot be paid in-app: classic Pricing Plans is not on
|
|
228
230
|
// ecom, so the host's member checkout has no checkout to open, and the redirect session needs a
|
|
229
231
|
// headless OAuth client a hosted app does not have. An online order created here would only be
|
|
230
|
-
// left behind as a DRAFT, so none is — the member joins on the site
|
|
231
|
-
//
|
|
232
|
+
// left behind as a DRAFT, so none is — the member joins on the site: the HOST opens the site's own
|
|
233
|
+
// plans page signed in as the lent member (`himi.host.memberSitePage {page:"plans"}`), or this
|
|
234
|
+
// message stands when it cannot. A FREE plan has nothing to pay: its online order activates at
|
|
235
|
+
// once, so it still joins in-app.
|
|
232
236
|
if (isHostedWorker() && !row.free) {
|
|
237
|
+
// One host open at a time: the host keeps a single ticket, so a second plan's prepare would replace the
|
|
238
|
+
// first's and drop it. The checkout guard is re-read right before the host opens the page.
|
|
239
|
+
if (hostedPlanOpenInFlight) return "";
|
|
240
|
+
hostedPlanOpenInFlight = true;
|
|
241
|
+
let opened: boolean | "withdrawn";
|
|
242
|
+
try {
|
|
243
|
+
opened = await hostedSitePageOpener({ page: "plans", planId: row.id })(
|
|
244
|
+
async () => commerce.pricing.continueCheckoutGuard(reference).allowed);
|
|
245
|
+
} finally {
|
|
246
|
+
hostedPlanOpenInFlight = false;
|
|
247
|
+
}
|
|
248
|
+
if (generation !== memberSession.current().generation) return "";
|
|
249
|
+
if (opened === "withdrawn") return "";
|
|
250
|
+
if (opened === true) {
|
|
251
|
+
return copyTemplate("plans.continueOnSiteMessage", "Finish joining {name} on the site.", { name: row.name });
|
|
252
|
+
}
|
|
233
253
|
return copyTemplate("plans.joinOnSiteMessage", "Memberships are purchased on the website — visit the site to join {name}.", { name: row.name });
|
|
234
254
|
}
|
|
235
255
|
try {
|