@wix/himalaya-cli 0.1243.0 → 0.1244.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/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/catalog-adapters/online-programs.d.ts +6 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/catalog-price-fields.d.ts +4 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/catalog-pricing-contract.d.ts +87 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/catalog-pricing-core.d.ts +10 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/catalog-pricing.d.ts +6 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/index.d.ts +4 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/mapping-source.d.ts +5 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/checkout/src/index.d.ts +3 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/checkout.mjs +3 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/iap.mjs +192 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/screens.mjs +3 -0
- package/dist/cli.mjs +2 -0
- package/guides/getting-started.md +6 -0
- package/guides/himi-skill.md +6 -0
- package/guides/iap-pricing.md +162 -211
- package/guides/index.json +7 -6
- package/guides/sdk-reference.json +96 -0
- package/guides/tier-5-add-a-screen.md +9 -0
- package/guides/wix-api-usage.md +11 -0
- package/guides/wix-site-to-himi-app.md +1 -1
- package/package.json +2 -2
- package/templates/branded-lite/dev/index.ts +110 -3
- package/templates/branded-lite/tier5-src/_shared/iap-worker.ts +65 -0
- package/templates/branded-lite/tier5-src/himi-branded-lite-program/index.ts +179 -52
- package/templates/branded-lite/tier5-src/himi-branded-lite-programs/index.ts +158 -38
- package/templates/yoga-studio/dev/index.ts +110 -3
- package/templates/yoga-studio/tier5-src/_shared/iap-worker.ts +65 -0
- package/templates/yoga-studio/tier5-src/himi-branded-lite-program/index.ts +179 -52
- package/templates/yoga-studio/tier5-src/himi-branded-lite-programs/index.ts +158 -38
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { IapCatalogItem } from "../catalog-pricing-contract.js";
|
|
2
|
+
/** An Online Programs program as a shared IAP catalog item. A program without a Wix ID cannot be
|
|
3
|
+
* mapped (a title fallback is not an identity), so it has no item and its screen shows no price. */
|
|
4
|
+
export declare function onlineProgramItem(program: {
|
|
5
|
+
id?: string | null;
|
|
6
|
+
}, commerceDisplayPrice?: string): IapCatalogItem | undefined;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { IapItemView, IapPriceCopy, IapPriceFields } from "./catalog-pricing-contract.js";
|
|
2
|
+
/** Bindable display state for one item. A screen spreads these into its own state and binds
|
|
3
|
+
* them; the labels stay in the screen's copy pack. */
|
|
4
|
+
export declare function iapPriceFields(view: IapItemView, copy: IapPriceCopy): IapPriceFields;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { CatalogReference, PricingMappingProvider, PricingUnavailableReason } from "./pricing-contract.js";
|
|
2
|
+
/** The Wix entity types `himi store iap` writes into its Wix→Apple mapping, so a mapping
|
|
3
|
+
* backend can serve that export without translating kinds. */
|
|
4
|
+
export declare const IAP_CATALOG_KINDS: readonly ["onlineProgram", "storesProduct", "pricingPlan"];
|
|
5
|
+
export type IapCatalogKind = typeof IAP_CATALOG_KINDS[number];
|
|
6
|
+
export interface IapCatalogItem {
|
|
7
|
+
reference: CatalogReference;
|
|
8
|
+
/** The screen's own formatted commerce price; shown only on an explicit regular route. */
|
|
9
|
+
commerceDisplayPrice?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface IapCatalogScope {
|
|
12
|
+
appId: string;
|
|
13
|
+
bundleId: string;
|
|
14
|
+
/** Wix metaSiteId. Omit it to use the connected mapping source's bound site. */
|
|
15
|
+
siteId?: string;
|
|
16
|
+
contextRevision?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface IapCatalogInput {
|
|
19
|
+
scope: IapCatalogScope;
|
|
20
|
+
/** The host's own platform (from `@himalaya/app-info`); only "ios" resolves Apple prices. */
|
|
21
|
+
platform: string;
|
|
22
|
+
items: IapCatalogItem[];
|
|
23
|
+
}
|
|
24
|
+
/** `disabled` means no backend is connected: the screen's existing commerce behavior applies. */
|
|
25
|
+
export type IapPaymentRoute = "disabled" | "regular" | "appleIap" | "resolving" | "unavailable";
|
|
26
|
+
/** Serializable per-item state a screen binds. Carries no credential, transaction or entitlement. */
|
|
27
|
+
export interface IapItemView {
|
|
28
|
+
reference: CatalogReference;
|
|
29
|
+
route: IapPaymentRoute;
|
|
30
|
+
/** Apple: StoreKit's string verbatim. Regular: the caller's commerce price. Otherwise "". */
|
|
31
|
+
displayPrice: string;
|
|
32
|
+
reason?: PricingUnavailableReason;
|
|
33
|
+
retryable: boolean;
|
|
34
|
+
/** True only when the existing commerce checkout may run: disabled or an explicit regular route. */
|
|
35
|
+
canStartRegularCheckout: boolean;
|
|
36
|
+
/** Apple purchasing is not available until a backend verifies transactions. */
|
|
37
|
+
purchasable: false;
|
|
38
|
+
}
|
|
39
|
+
export interface IapCheckoutDecision {
|
|
40
|
+
allowed: boolean;
|
|
41
|
+
route: IapPaymentRoute;
|
|
42
|
+
}
|
|
43
|
+
/** A deployed Wix→Apple mapping backend. Never supplied by a screen. */
|
|
44
|
+
export interface IapMappingSource {
|
|
45
|
+
mapping: PricingMappingProvider;
|
|
46
|
+
/** The site this backend serves, used when the caller passes no site. */
|
|
47
|
+
siteId?: string;
|
|
48
|
+
}
|
|
49
|
+
export interface IapPollDirective {
|
|
50
|
+
id: string;
|
|
51
|
+
action: string;
|
|
52
|
+
intervalMs: number;
|
|
53
|
+
immediate: boolean;
|
|
54
|
+
}
|
|
55
|
+
export interface IapCatalogPricing {
|
|
56
|
+
readonly enabled: boolean;
|
|
57
|
+
/** Batched mapping + Apple lookup for the items a screen shows. */
|
|
58
|
+
load(input: IapCatalogInput): Promise<void>;
|
|
59
|
+
refresh(): Promise<void>;
|
|
60
|
+
view(reference: CatalogReference): IapItemView;
|
|
61
|
+
/** May a commerce flow START now? Re-read immediately before the first mutation: the route as it
|
|
62
|
+
* is now, so a refresh in flight (`resolving`) blocks. */
|
|
63
|
+
checkoutGuard(reference: CatalogReference): IapCheckoutDecision;
|
|
64
|
+
/** May a flow that already STARTED take its next commerce step (e.g. pay after a join)? While no
|
|
65
|
+
* new answer exists (a refresh in flight, or a mapping backend that did not answer) it uses the
|
|
66
|
+
* route the last settled lookup gave for the current scope, so a routine refresh cannot strand a
|
|
67
|
+
* regular checkout halfway; any new answer other than regular stops it. */
|
|
68
|
+
continueCheckoutGuard(reference: CatalogReference): IapCheckoutDecision;
|
|
69
|
+
/** The host-scheduled visible refresh; undefined while disabled. */
|
|
70
|
+
pollDirective(action: string): IapPollDirective | undefined;
|
|
71
|
+
dispose(): void;
|
|
72
|
+
}
|
|
73
|
+
/** Labels come from the screen's copy pack; the shared layer only chooses between them. */
|
|
74
|
+
export interface IapPriceCopy {
|
|
75
|
+
loading: string;
|
|
76
|
+
unavailable: string;
|
|
77
|
+
oneTimePurchase: string;
|
|
78
|
+
purchaseUnavailable: string;
|
|
79
|
+
}
|
|
80
|
+
export interface IapPriceFields {
|
|
81
|
+
priceText: string;
|
|
82
|
+
priceNote: string;
|
|
83
|
+
priceLoading: boolean;
|
|
84
|
+
hidePriceRetry: boolean;
|
|
85
|
+
actionDisabled: boolean;
|
|
86
|
+
actionNote: string;
|
|
87
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Product } from "./index.js";
|
|
2
|
+
import type { IapCatalogPricing, IapMappingSource } from "./catalog-pricing-contract.js";
|
|
3
|
+
/** The catalog layer with its dependencies explicit. Internal: the public factory supplies the
|
|
4
|
+
* connected mapping source, so a screen cannot pass its own. Tests use it directly. */
|
|
5
|
+
export interface IapCatalogPricingDependencies {
|
|
6
|
+
source: IapMappingSource | undefined;
|
|
7
|
+
getProducts(productIds: string[]): Promise<Product[]>;
|
|
8
|
+
onChange?(): void;
|
|
9
|
+
}
|
|
10
|
+
export declare function createIapCatalogPricingWith(deps: IapCatalogPricingDependencies): IapCatalogPricing;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { IapCatalogPricing } from "./catalog-pricing-contract.js";
|
|
2
|
+
/** One screen worker's catalog pricing. Activation comes only from the connected mapping
|
|
3
|
+
* source, so a screen cannot turn Apple prices on by supplying its own mapping. */
|
|
4
|
+
export declare function createIapCatalogPricing(options?: {
|
|
5
|
+
onChange?(): void;
|
|
6
|
+
}): IapCatalogPricing;
|
package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/index.d.ts
CHANGED
|
@@ -3,6 +3,10 @@ export * from "./delivery-contract.js";
|
|
|
3
3
|
export * from "./delivery.js";
|
|
4
4
|
export * from "./pricing-contract.js";
|
|
5
5
|
export * from "./pricing.js";
|
|
6
|
+
export * from "./catalog-pricing-contract.js";
|
|
7
|
+
export * from "./catalog-pricing.js";
|
|
8
|
+
export * from "./catalog-price-fields.js";
|
|
9
|
+
export * from "./catalog-adapters/online-programs.js";
|
|
6
10
|
/** The three store product categories. */
|
|
7
11
|
export type ProductType = "subscription" | "nonConsumable" | "consumable";
|
|
8
12
|
export interface Product {
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { IapMappingSource } from "./catalog-pricing-contract.js";
|
|
2
|
+
/** The single activation switch for catalog pricing. No Wix→Apple mapping backend is deployed,
|
|
3
|
+
* so every screen stays on its existing commerce flow. Only automated tests substitute this module
|
|
4
|
+
* (at bundle-build time); the backend adapter replaces it when it exists. */
|
|
5
|
+
export declare function iapMappingSource(): IapMappingSource | undefined;
|
|
@@ -66,6 +66,9 @@ export interface PayRequest {
|
|
|
66
66
|
currency?: string;
|
|
67
67
|
/** Mints the in-app cashier URL. Called ONLY when the wallet rail cannot settle. */
|
|
68
68
|
cashierUrl?: () => Promise<string | undefined>;
|
|
69
|
+
/** Re-read immediately before money moves (the wallet charge, the cashier opening). The caller's
|
|
70
|
+
* own route can change while a rail is being chosen; false stops the flow with nothing charged. */
|
|
71
|
+
mayCharge?: () => boolean;
|
|
69
72
|
}
|
|
70
73
|
/**
|
|
71
74
|
* Collect payment through the two sanctioned rails, in order.
|
|
@@ -122,12 +122,14 @@ async function entitlement() {
|
|
|
122
122
|
async function isEntitled() {
|
|
123
123
|
return (await entitlement()).entitled;
|
|
124
124
|
}
|
|
125
|
+
var WITHDRAWN = { kind: "failed", reason: "the caller withdrew the charge" };
|
|
125
126
|
async function payOrder(req) {
|
|
126
127
|
if (!req.snapshotId && !req.cashierUrl) {
|
|
127
128
|
return { kind: "failed", reason: "no order to settle" };
|
|
128
129
|
}
|
|
129
130
|
const availability = await isAvailable().catch(() => "unavailable");
|
|
130
131
|
if (availability === "full" && req.snapshotId) {
|
|
132
|
+
if (req.mayCharge?.() === false) return WITHDRAWN;
|
|
131
133
|
const r = await proceedToPayment({
|
|
132
134
|
appId: req.appId,
|
|
133
135
|
snapshotId: req.snapshotId,
|
|
@@ -148,6 +150,7 @@ async function payOrder(req) {
|
|
|
148
150
|
if (req.cashierUrl) {
|
|
149
151
|
const url = await req.cashierUrl().catch(() => void 0);
|
|
150
152
|
if (url) {
|
|
153
|
+
if (req.mayCharge?.() === false) return WITHDRAWN;
|
|
151
154
|
const opened = await open(url, "in_app_browser").catch(() => ({ opened: false }));
|
|
152
155
|
if (opened.opened) return { kind: "handoff" };
|
|
153
156
|
}
|
|
@@ -648,6 +648,194 @@ var isObject2 = (value) => value !== null && typeof value === "object" && !Array
|
|
|
648
648
|
var nonempty = (value) => typeof value === "string" && value.trim().length > 0;
|
|
649
649
|
var sameReference = (value, reference) => isObject2(value) && value.kind === reference.kind && value.id === reference.id;
|
|
650
650
|
|
|
651
|
+
// ../../core/sdk/iap/src/catalog-pricing-contract.ts
|
|
652
|
+
var IAP_CATALOG_KINDS = ["onlineProgram", "storesProduct", "pricingPlan"];
|
|
653
|
+
|
|
654
|
+
// ../../core/sdk/iap/src/mapping-source.ts
|
|
655
|
+
function iapMappingSource() {
|
|
656
|
+
return void 0;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// ../../core/sdk/iap/src/catalog-pricing-core.ts
|
|
660
|
+
var POLL_ID = "iapCatalogPricing";
|
|
661
|
+
var POLL_INTERVAL_MS = 6e4;
|
|
662
|
+
function createIapCatalogPricingWith(deps) {
|
|
663
|
+
return deps.source ? enabledPricing(deps.source, deps) : disabledPricing();
|
|
664
|
+
}
|
|
665
|
+
function disabledPricing() {
|
|
666
|
+
return {
|
|
667
|
+
enabled: false,
|
|
668
|
+
load: () => Promise.resolve(),
|
|
669
|
+
refresh: () => Promise.resolve(),
|
|
670
|
+
view: disabledView,
|
|
671
|
+
checkoutGuard: (reference) => decisionOf(disabledView(reference)),
|
|
672
|
+
continueCheckoutGuard: (reference) => decisionOf(disabledView(reference)),
|
|
673
|
+
pollDirective: () => void 0,
|
|
674
|
+
dispose: () => {
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
function enabledPricing(source, deps) {
|
|
679
|
+
let controller;
|
|
680
|
+
let disposed = false;
|
|
681
|
+
const settled = /* @__PURE__ */ new Map();
|
|
682
|
+
let settledScope = "";
|
|
683
|
+
const rememberSettled = (state) => {
|
|
684
|
+
const scopeKey = JSON.stringify(state.scope);
|
|
685
|
+
if (scopeKey !== settledScope) {
|
|
686
|
+
settled.clear();
|
|
687
|
+
settledScope = scopeKey;
|
|
688
|
+
}
|
|
689
|
+
for (const item of state.items) {
|
|
690
|
+
const answered = viewOf(item.reference, item);
|
|
691
|
+
if (!unanswered(answered)) settled.set(referenceKey(item.reference), answered);
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
let lookupRevision = 0;
|
|
695
|
+
let answeredRoutes;
|
|
696
|
+
const mapping = {
|
|
697
|
+
resolve: async (request) => {
|
|
698
|
+
const asked = lookupRevision;
|
|
699
|
+
const answer = await source.mapping.resolve(request);
|
|
700
|
+
if (asked === lookupRevision) answeredRoutes = routesAnswered(answer);
|
|
701
|
+
return answer;
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
const trackLookup = (state) => {
|
|
705
|
+
if (state.revision !== lookupRevision) {
|
|
706
|
+
lookupRevision = state.revision;
|
|
707
|
+
answeredRoutes = void 0;
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
const ensureController = () => controller ?? (controller = createPricingController({
|
|
711
|
+
mapping,
|
|
712
|
+
getProducts: deps.getProducts,
|
|
713
|
+
onState: (state) => {
|
|
714
|
+
trackLookup(state);
|
|
715
|
+
rememberSettled(state);
|
|
716
|
+
deps.onChange?.();
|
|
717
|
+
}
|
|
718
|
+
}));
|
|
719
|
+
const view = (reference) => disposed ? blockedView(reference, "unavailable") : viewOf(reference, controller && itemFor(controller.getState().items, reference));
|
|
720
|
+
const continueCheckoutGuard = (reference) => {
|
|
721
|
+
const current = view(reference);
|
|
722
|
+
if (!unanswered(current)) return decisionOf(current);
|
|
723
|
+
if (answeredRoutes) return routeDecision(answeredRoutes.get(referenceKey(reference)) ?? "unavailable");
|
|
724
|
+
return decisionOf(settled.get(referenceKey(reference)) ?? current);
|
|
725
|
+
};
|
|
726
|
+
return {
|
|
727
|
+
enabled: true,
|
|
728
|
+
load: (input) => disposed ? Promise.resolve() : ensureController().load(pricingInput(input, source)),
|
|
729
|
+
refresh: () => controller?.refresh() ?? Promise.resolve(),
|
|
730
|
+
view,
|
|
731
|
+
checkoutGuard: (reference) => decisionOf(view(reference)),
|
|
732
|
+
continueCheckoutGuard,
|
|
733
|
+
pollDirective: (action) => ({ id: POLL_ID, action, intervalMs: POLL_INTERVAL_MS, immediate: true }),
|
|
734
|
+
dispose: () => {
|
|
735
|
+
disposed = true;
|
|
736
|
+
settled.clear();
|
|
737
|
+
controller?.dispose();
|
|
738
|
+
}
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
function pricingInput(input, source) {
|
|
742
|
+
return { scope: pricingScope(input.scope, source.siteId), platform: input.platform, items: input.items };
|
|
743
|
+
}
|
|
744
|
+
function pricingScope(scope, boundSiteId) {
|
|
745
|
+
return {
|
|
746
|
+
appId: scope.appId,
|
|
747
|
+
bundleId: scope.bundleId,
|
|
748
|
+
siteId: siteIdFor(scope.siteId, boundSiteId),
|
|
749
|
+
...scope.contextRevision === void 0 ? {} : { contextRevision: scope.contextRevision }
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
function siteIdFor(callerSiteId, boundSiteId) {
|
|
753
|
+
if (callerSiteId && boundSiteId && callerSiteId !== boundSiteId) return "";
|
|
754
|
+
return callerSiteId || boundSiteId || "";
|
|
755
|
+
}
|
|
756
|
+
var referenceKey = (reference) => JSON.stringify([reference.kind, reference.id]);
|
|
757
|
+
function routesAnswered(answer) {
|
|
758
|
+
const routes = /* @__PURE__ */ new Map();
|
|
759
|
+
for (const entry of Array.isArray(answer) ? answer : []) {
|
|
760
|
+
const reference = entry?.reference;
|
|
761
|
+
if (typeof reference?.kind !== "string" || typeof reference.id !== "string") continue;
|
|
762
|
+
const key = referenceKey(reference);
|
|
763
|
+
const route = entry.route;
|
|
764
|
+
routes.set(key, routes.has(key) ? "unavailable" : route === "regular" || route === "appleIap" ? route : "unavailable");
|
|
765
|
+
}
|
|
766
|
+
return routes;
|
|
767
|
+
}
|
|
768
|
+
function routeDecision(route) {
|
|
769
|
+
return { allowed: route === "regular", route };
|
|
770
|
+
}
|
|
771
|
+
function unanswered(view) {
|
|
772
|
+
return view.route === "resolving" || view.reason === "mappingUnavailable";
|
|
773
|
+
}
|
|
774
|
+
function itemFor(items, reference) {
|
|
775
|
+
return items.find((item) => item.reference.kind === reference.kind && item.reference.id === reference.id);
|
|
776
|
+
}
|
|
777
|
+
function viewOf(reference, item) {
|
|
778
|
+
if (!item || item.status === "loading") return blockedView(reference, "resolving");
|
|
779
|
+
if (item.status === "ready") return readyView(reference, item);
|
|
780
|
+
return unavailableView(reference, item);
|
|
781
|
+
}
|
|
782
|
+
function readyView(reference, item) {
|
|
783
|
+
return item.source === "appleIap" ? { ...blockedView(reference, "appleIap"), displayPrice: item.displayPrice } : { ...regularView(reference), displayPrice: item.displayPrice };
|
|
784
|
+
}
|
|
785
|
+
function unavailableView(reference, item) {
|
|
786
|
+
if (item.reason === "commercePriceUnavailable") return regularView(reference);
|
|
787
|
+
return { ...blockedView(reference, "unavailable"), reason: item.reason, retryable: item.retryable };
|
|
788
|
+
}
|
|
789
|
+
function blockedView(reference, route) {
|
|
790
|
+
return { reference: { kind: reference.kind, id: reference.id }, route, displayPrice: "", retryable: false, canStartRegularCheckout: false, purchasable: false };
|
|
791
|
+
}
|
|
792
|
+
function regularView(reference) {
|
|
793
|
+
return { ...blockedView(reference, "regular"), canStartRegularCheckout: true };
|
|
794
|
+
}
|
|
795
|
+
function disabledView(reference) {
|
|
796
|
+
return { ...blockedView(reference, "disabled"), canStartRegularCheckout: true };
|
|
797
|
+
}
|
|
798
|
+
function decisionOf(view) {
|
|
799
|
+
return { allowed: view.canStartRegularCheckout, route: view.route };
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
// ../../core/sdk/iap/src/catalog-pricing.ts
|
|
803
|
+
function createIapCatalogPricing(options = {}) {
|
|
804
|
+
return createIapCatalogPricingWith({ source: iapMappingSource(), getProducts, onChange: options.onChange });
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// ../../core/sdk/iap/src/catalog-price-fields.ts
|
|
808
|
+
function iapPriceFields(view, copy) {
|
|
809
|
+
const fields = {
|
|
810
|
+
priceText: "",
|
|
811
|
+
priceNote: "",
|
|
812
|
+
priceLoading: false,
|
|
813
|
+
hidePriceRetry: true,
|
|
814
|
+
actionDisabled: !view.canStartRegularCheckout,
|
|
815
|
+
actionNote: ""
|
|
816
|
+
};
|
|
817
|
+
switch (view.route) {
|
|
818
|
+
case "resolving":
|
|
819
|
+
return { ...fields, priceText: copy.loading, priceLoading: true };
|
|
820
|
+
case "appleIap":
|
|
821
|
+
return { ...fields, priceText: view.displayPrice, priceNote: copy.oneTimePurchase, actionNote: copy.purchaseUnavailable };
|
|
822
|
+
case "unavailable":
|
|
823
|
+
return { ...fields, priceText: copy.unavailable, hidePriceRetry: !view.retryable };
|
|
824
|
+
case "regular":
|
|
825
|
+
return { ...fields, priceText: view.displayPrice };
|
|
826
|
+
case "disabled":
|
|
827
|
+
return fields;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// ../../core/sdk/iap/src/catalog-adapters/online-programs.ts
|
|
832
|
+
var ONLINE_PROGRAM = "onlineProgram";
|
|
833
|
+
function onlineProgramItem(program, commerceDisplayPrice) {
|
|
834
|
+
const id = program.id;
|
|
835
|
+
if (typeof id !== "string" || !id.trim()) return void 0;
|
|
836
|
+
return { reference: { kind: ONLINE_PROGRAM, id }, ...commerceDisplayPrice ? { commerceDisplayPrice } : {} };
|
|
837
|
+
}
|
|
838
|
+
|
|
651
839
|
// ../../core/sdk/iap/src/index.ts
|
|
652
840
|
async function getPaywallConfig() {
|
|
653
841
|
const r = await dispatch("capability.iap.getPaywallConfig");
|
|
@@ -742,10 +930,12 @@ function decodeTransaction(value) {
|
|
|
742
930
|
return { store, signedTransaction, transactionId, originalTransactionId, productId, appAccountToken, environment };
|
|
743
931
|
}
|
|
744
932
|
export {
|
|
933
|
+
IAP_CATALOG_KINDS,
|
|
745
934
|
MAX_AUTOMATIC_ATTEMPTS,
|
|
746
935
|
MAX_RETRY_DELAY_MS,
|
|
747
936
|
appleManagedStore,
|
|
748
937
|
consume,
|
|
938
|
+
createIapCatalogPricing,
|
|
749
939
|
createPricingController,
|
|
750
940
|
createPurchaseCoordinator,
|
|
751
941
|
entitlements,
|
|
@@ -753,6 +943,8 @@ export {
|
|
|
753
943
|
getPaywallConfig,
|
|
754
944
|
getProducts,
|
|
755
945
|
getUnfinishedTransactions,
|
|
946
|
+
iapPriceFields,
|
|
947
|
+
onlineProgramItem,
|
|
756
948
|
purchase,
|
|
757
949
|
purchaseForVerification,
|
|
758
950
|
restore,
|
|
@@ -220,12 +220,14 @@ async function entitlement() {
|
|
|
220
220
|
return { entitled: false, planName: "", planId: "" };
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
|
+
var WITHDRAWN = { kind: "failed", reason: "the caller withdrew the charge" };
|
|
223
224
|
async function payOrder(req) {
|
|
224
225
|
if (!req.snapshotId && !req.cashierUrl) {
|
|
225
226
|
return { kind: "failed", reason: "no order to settle" };
|
|
226
227
|
}
|
|
227
228
|
const availability = await isAvailable().catch(() => "unavailable");
|
|
228
229
|
if (availability === "full" && req.snapshotId) {
|
|
230
|
+
if (req.mayCharge?.() === false) return WITHDRAWN;
|
|
229
231
|
const r = await proceedToPayment({
|
|
230
232
|
appId: req.appId,
|
|
231
233
|
snapshotId: req.snapshotId,
|
|
@@ -246,6 +248,7 @@ async function payOrder(req) {
|
|
|
246
248
|
if (req.cashierUrl) {
|
|
247
249
|
const url = await req.cashierUrl().catch(() => void 0);
|
|
248
250
|
if (url) {
|
|
251
|
+
if (req.mayCharge?.() === false) return WITHDRAWN;
|
|
249
252
|
const opened = await open(url, "in_app_browser").catch(() => ({ opened: false }));
|
|
250
253
|
if (opened.opened) return { kind: "handoff" };
|
|
251
254
|
}
|
package/dist/cli.mjs
CHANGED
|
@@ -25748,6 +25748,7 @@ function shouldRevealError(args) {
|
|
|
25748
25748
|
var EMAIL_RE, URL_HOST_RE, INTEGER_RE, NUMBER_RE;
|
|
25749
25749
|
var init_validation = __esm({
|
|
25750
25750
|
"../../core/ts/src/validation.ts"() {
|
|
25751
|
+
"use strict";
|
|
25751
25752
|
EMAIL_RE = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
|
|
25752
25753
|
URL_HOST_RE = /^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(:[0-9]+)?$/;
|
|
25753
25754
|
INTEGER_RE = /^[+-]?[0-9]+$/;
|
|
@@ -26407,6 +26408,7 @@ function validateWorkerSubtree(raw, slotId, policy, actionCatalog) {
|
|
|
26407
26408
|
var WORKER_SUBTREE_CEILINGS, ID_PART, ACTION_ID;
|
|
26408
26409
|
var init_worker_subtree = __esm({
|
|
26409
26410
|
"../../core/ts/src/worker-subtree.ts"() {
|
|
26411
|
+
"use strict";
|
|
26410
26412
|
init_worker_subtree_safe_types_generated();
|
|
26411
26413
|
WORKER_SUBTREE_CEILINGS = Object.freeze({
|
|
26412
26414
|
maxEncodedBytes: 65536,
|
|
@@ -218,6 +218,12 @@ mapper writes the site's light colour into the dark arm too, so dark mode is whe
|
|
|
218
218
|
To create/publish/invoke the site's Velo HTTP functions, `himi docs wix-site-http-functions`
|
|
219
219
|
and `himi site function` (not analyze, and not `himi functions`).
|
|
220
220
|
|
|
221
|
+
**Selling digital items** (programs, products, plans the owner registered with `himi store iap`)?
|
|
222
|
+
Any screen that shows such an item's price — a template screen or one you write — gets its App
|
|
223
|
+
Store price and payment route from the shared `createIapCatalogPricing` in `@himalaya/iap`:
|
|
224
|
+
read `himi docs iap-pricing` before writing the worker. Starting from a template does not cover
|
|
225
|
+
your own screens; each screen opts in explicitly.
|
|
226
|
+
|
|
221
227
|
After `himi init … --template branded-lite` (or a vertical overlay), **point the app at your
|
|
222
228
|
Wix site** before expecting live catalog data:
|
|
223
229
|
|
package/guides/himi-skill.md
CHANGED
|
@@ -247,6 +247,12 @@ mapper writes the site's light colour into the dark arm too, so dark mode is whe
|
|
|
247
247
|
To create/publish/invoke the site's Velo HTTP functions, `himi docs wix-site-http-functions`
|
|
248
248
|
and `himi site function` (not analyze, and not `himi functions`).
|
|
249
249
|
|
|
250
|
+
**Selling digital items** (programs, products, plans the owner registered with `himi store iap`)?
|
|
251
|
+
Any screen that shows such an item's price — a template screen or one you write — gets its App
|
|
252
|
+
Store price and payment route from the shared `createIapCatalogPricing` in `@himalaya/iap`:
|
|
253
|
+
read `himi docs iap-pricing` before writing the worker. Starting from a template does not cover
|
|
254
|
+
your own screens; each screen opts in explicitly.
|
|
255
|
+
|
|
250
256
|
After `himi init … --template branded-lite` (or a vertical overlay), **point the app at your
|
|
251
257
|
Wix site** before expecting live catalog data:
|
|
252
258
|
|