@wix/pricing-plans-site-api 0.0.0-stage → 1.0.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/README.md +60 -2
- package/dist/index.d.ts +125 -0
- package/dist/index.js +300 -0
- package/dist/index.js.map +1 -0
- package/package.json +63 -3
package/README.md
CHANGED
|
@@ -1,3 +1,61 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @wix/pricing-plans-site-api
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Lets other verticals send site visitors to Pricing Plans pages from their own React components. It replaces the legacy Pricing Plans public API (`getPricingPlansPublicAPI` from `@wix/pricing-plans-tpa-api`).
|
|
4
|
+
|
|
5
|
+
The site can run either the legacy Pricing Plans app or the new Builder app. The hook works out which one is installed and calls that app's navigation, so the vertical's code is the same for both.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { usePricingPlansSiteApi } from '@wix/pricing-plans-site-api';
|
|
11
|
+
|
|
12
|
+
function BuyPlanButton({ planId }: { planId: string }) {
|
|
13
|
+
const { navigateToPlanPage } = usePricingPlansSiteApi();
|
|
14
|
+
|
|
15
|
+
return <button onClick={() => navigateToPlanPage({ planId })}>Buy plan</button>;
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Use the hook inside a site component or context provider, and add the site services it uses to the extension's `serviceDependencies`:
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
resources: {
|
|
23
|
+
client: {
|
|
24
|
+
componentUrl,
|
|
25
|
+
dependencies: {
|
|
26
|
+
serviceDependencies: [
|
|
27
|
+
'@wix/site-service-pages',
|
|
28
|
+
'@wix/site-service-url',
|
|
29
|
+
'@wix/site-service-router',
|
|
30
|
+
'@wix/viewer-service-router',
|
|
31
|
+
'@wix/site-service-public-api-caller',
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The hook navigates with `@wix/site-service-router`. Live sites on Thunderbolt releases from before 2026-10-08 don't provide that service yet, so the hook falls back to the older `@wix/viewer-service-router`. List both until the new one has rolled out everywhere.
|
|
39
|
+
|
|
40
|
+
## API
|
|
41
|
+
|
|
42
|
+
`usePricingPlansSiteApi()` returns:
|
|
43
|
+
|
|
44
|
+
- `navigateToPricingPage(options?)` opens the Pricing Plans page. `options` take the same fields as the legacy `navigateToPricingPage`: `planIds`, `pricingVariantsByPlanId`, `title`, `subtitle`, `biOptions` and `checkout`.
|
|
45
|
+
- `getPricingPageUrl(options?)` returns the URL of the page `navigateToPricingPage` opens, without navigating. It takes the same `options`.
|
|
46
|
+
- `navigateToPlanPage(options)` starts the purchase of one plan. `options` take the same fields as the legacy `navigateToPlanPage`: `planId`, `pricingVariantId`, `biOptions`, `checkoutFlowId`, `widgetType` and the checkout fields.
|
|
47
|
+
|
|
48
|
+
Each method returns a promise. It rejects when the site doesn't have the page, and the navigation methods also reject when the router doesn't navigate, such as when the site visitor is already on the page.
|
|
49
|
+
|
|
50
|
+
The hook works on any site, even one without Pricing Plans. There, every method rejects with `Pricing Plans is not installed on the site.` and doesn't navigate, so catch the error if your component can render on such sites.
|
|
51
|
+
|
|
52
|
+
## Builder app support
|
|
53
|
+
|
|
54
|
+
On the legacy app, every option works as before. The Builder app reads fewer options:
|
|
55
|
+
|
|
56
|
+
- `planIds` and `pricingVariantsByPlanId` choose the plans the Plans page shows.
|
|
57
|
+
- `checkout.minStartDate` and `checkout.maxStartDate` limit the start date on the plan customization page. A plan that goes straight to checkout doesn't get them.
|
|
58
|
+
- `checkout.thankYouPage.navigation` sets where the thank you page's continue button goes, and where the plan customization page goes after adding the plan to the cart. A target that isn't on the site is left out.
|
|
59
|
+
- `title`, `subtitle`, `biOptions`, `checkoutFlowId`, `widgetType` and `checkout.thankYouPage.content` aren't supported, and are left out.
|
|
60
|
+
|
|
61
|
+
`navigateToPlanPage` rejects a plan that isn't public, isn't buyable or has no visible pricing variant. On the Builder app it loads the plan and creates the eCommerce cart as the vertical's app, so the app needs permission to read Pricing Plans plans and to manage eCommerce carts.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import "@wix/react-component-utils";
|
|
2
|
+
//#region src/types.d.ts
|
|
3
|
+
type BiOptions = {
|
|
4
|
+
referralInfo: string;
|
|
5
|
+
referralId?: string;
|
|
6
|
+
};
|
|
7
|
+
type NavigateToSectionOptions = {
|
|
8
|
+
appDefinitionId: string;
|
|
9
|
+
sectionId: string;
|
|
10
|
+
queryParams?: object;
|
|
11
|
+
state?: string;
|
|
12
|
+
discardRouterPageSuffix?: boolean;
|
|
13
|
+
};
|
|
14
|
+
type NavigationPolicy = {
|
|
15
|
+
type: 'url';
|
|
16
|
+
url: string;
|
|
17
|
+
} | {
|
|
18
|
+
type: 'section';
|
|
19
|
+
options: NavigateToSectionOptions;
|
|
20
|
+
} | {
|
|
21
|
+
type: 'page';
|
|
22
|
+
pageId: string;
|
|
23
|
+
};
|
|
24
|
+
type ThankYouPageOptions = {
|
|
25
|
+
content?: {
|
|
26
|
+
title?: string;
|
|
27
|
+
message?: string;
|
|
28
|
+
cta?: string;
|
|
29
|
+
};
|
|
30
|
+
navigation?: NavigationPolicy;
|
|
31
|
+
};
|
|
32
|
+
type CheckoutOptions = {
|
|
33
|
+
minStartDate?: string;
|
|
34
|
+
maxStartDate?: string;
|
|
35
|
+
thankYouPage?: ThankYouPageOptions;
|
|
36
|
+
};
|
|
37
|
+
type NavigateToPricingPageOptions = {
|
|
38
|
+
planIds?: string[];
|
|
39
|
+
pricingVariantsByPlanId?: Record<string, string[]>;
|
|
40
|
+
title?: string;
|
|
41
|
+
subtitle?: string;
|
|
42
|
+
biOptions?: BiOptions;
|
|
43
|
+
checkout?: CheckoutOptions;
|
|
44
|
+
};
|
|
45
|
+
type NavigateToPlanPageOptions = {
|
|
46
|
+
planId: string;
|
|
47
|
+
pricingVariantId?: string;
|
|
48
|
+
biOptions?: BiOptions;
|
|
49
|
+
checkoutFlowId?: string;
|
|
50
|
+
widgetType?: string;
|
|
51
|
+
} & CheckoutOptions;
|
|
52
|
+
type PricingPlansSiteApi = {
|
|
53
|
+
/**
|
|
54
|
+
* Opens the Pricing Plans page, where site visitors pick a plan.
|
|
55
|
+
*
|
|
56
|
+
* On the legacy app, it accepts every option of the legacy `navigateToPricingPage`. The Builder
|
|
57
|
+
* app only reads `planIds`, `pricingVariantsByPlanId`, `checkout.minStartDate`,
|
|
58
|
+
* `checkout.maxStartDate` and `checkout.thankYouPage.navigation`; it leaves out the rest.
|
|
59
|
+
*
|
|
60
|
+
* @param options Plans to show and checkout settings. Shows the page's own plans when omitted.
|
|
61
|
+
* @returns Resolves once the site visitor is on the pricing page.
|
|
62
|
+
* @throws `Pricing Plans is not installed on the site.` when Pricing Plans isn't installed on the
|
|
63
|
+
* site.
|
|
64
|
+
* @throws When the site has Pricing Plans but no page to show the plans on.
|
|
65
|
+
* @throws When the router doesn't navigate, such as when the site visitor is already on the page.
|
|
66
|
+
*/
|
|
67
|
+
navigateToPricingPage(options?: NavigateToPricingPageOptions): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Builds the URL of the Pricing Plans page, the page {@link navigateToPricingPage} opens, without
|
|
70
|
+
* navigating to it. Use it for links, like a paywall's "See plans" button.
|
|
71
|
+
*
|
|
72
|
+
* Reads the same options as {@link navigateToPricingPage}, on both the legacy and the Builder
|
|
73
|
+
* app.
|
|
74
|
+
*
|
|
75
|
+
* @param options Plans to show and checkout settings. Shows the page's own plans when omitted.
|
|
76
|
+
* @returns The absolute URL of the pricing page.
|
|
77
|
+
* @throws `Pricing Plans is not installed on the site.` when Pricing Plans isn't installed on the
|
|
78
|
+
* site.
|
|
79
|
+
* @throws When the site has Pricing Plans but no page to show the plans on.
|
|
80
|
+
*/
|
|
81
|
+
getPricingPageUrl(options?: NavigateToPricingPageOptions): Promise<string>;
|
|
82
|
+
/**
|
|
83
|
+
* Starts the purchase of one plan, like the legacy `navigateToPlanPage`: goes to the plan
|
|
84
|
+
* customization page when the plan has a form or a start date to choose, and to checkout
|
|
85
|
+
* otherwise.
|
|
86
|
+
*
|
|
87
|
+
* Uses the given pricing variant, or the plan's first one when `pricingVariantId` is omitted.
|
|
88
|
+
* The Builder app leaves out `biOptions`, `checkoutFlowId`, `widgetType` and
|
|
89
|
+
* `thankYouPage.content`.
|
|
90
|
+
*
|
|
91
|
+
* @param options Plan to buy and checkout settings.
|
|
92
|
+
* @returns Resolves once the site visitor is on the plan customization page or checkout.
|
|
93
|
+
* @throws `Pricing Plans is not installed on the site.` when Pricing Plans isn't installed on the
|
|
94
|
+
* site.
|
|
95
|
+
* @throws When the plan can't be bought online, or when the site has no page to buy it on.
|
|
96
|
+
* @throws When the router doesn't navigate.
|
|
97
|
+
*/
|
|
98
|
+
navigateToPlanPage(options: NavigateToPlanPageOptions): Promise<void>;
|
|
99
|
+
};
|
|
100
|
+
//#endregion
|
|
101
|
+
//#region src/use-pricing-plans-site-api.d.ts
|
|
102
|
+
/**
|
|
103
|
+
* Navigation to Pricing Plans pages that works with both versions of the Pricing Plans app: the
|
|
104
|
+
* legacy app (OOI) and the Builder app. The hook detects which one the site runs and uses its
|
|
105
|
+
* pages, so the calls are the same for both.
|
|
106
|
+
*
|
|
107
|
+
* Calling the hook is safe on any site. When Pricing Plans isn't installed on the site, every method
|
|
108
|
+
* it returns rejects with the error `Pricing Plans is not installed on the site.` and doesn't
|
|
109
|
+
* navigate. The hook treats Pricing Plans as installed when the site has at least one Pricing Plans
|
|
110
|
+
* page.
|
|
111
|
+
*
|
|
112
|
+
* Use it inside a site component or context provider, and list these services in the extension's
|
|
113
|
+
* `serviceDependencies`: `@wix/site-service-pages`, `@wix/site-service-url`,
|
|
114
|
+
* `@wix/site-service-router`, `@wix/viewer-service-router` and
|
|
115
|
+
* `@wix/site-service-public-api-caller`.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* const { navigateToPlanPage } = usePricingPlansSiteApi();
|
|
119
|
+
*
|
|
120
|
+
* <button onClick={() => navigateToPlanPage({ planId })}>Buy plan</button>
|
|
121
|
+
*/
|
|
122
|
+
declare function usePricingPlansSiteApi(): PricingPlansSiteApi;
|
|
123
|
+
//#endregion
|
|
124
|
+
export { type BiOptions, type CheckoutOptions, type NavigateToPlanPageOptions, type NavigateToPricingPageOptions, type NavigateToSectionOptions, type NavigationPolicy, type PricingPlansSiteApi, type ThankYouPageOptions, usePricingPlansSiteApi };
|
|
125
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { usePages } from "@wix/react-component-utils";
|
|
2
|
+
import { useGetService, useService } from "@wix/services-manager-react";
|
|
3
|
+
import { UrlDefinition } from "@wix/site-service-url/definition";
|
|
4
|
+
import { useMemo } from "react";
|
|
5
|
+
import { cartV2 } from "@wix/ecom";
|
|
6
|
+
import { plansV3 } from "@wix/pricing-plans";
|
|
7
|
+
import { defineService } from "@wix/services-definitions";
|
|
8
|
+
import { RouterDefinition } from "@wix/site-service-router/definition";
|
|
9
|
+
//#region src/constants.ts
|
|
10
|
+
const PRICING_PLANS_APP_ID = "1522827f-c56c-a5c9-2ac9-00f9e6ae12d3";
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/builder/checkout.ts
|
|
13
|
+
async function loadPublicPlan(planId) {
|
|
14
|
+
const { items } = await plansV3.queryPlans().eq("_id", planId).eq("visibility", "PUBLIC").limit(1).find();
|
|
15
|
+
return items[0];
|
|
16
|
+
}
|
|
17
|
+
async function createCheckoutUrl(planId, pricingVariantId) {
|
|
18
|
+
const cart = await cartV2.createCart({
|
|
19
|
+
cart: { source: { channelType: "WEB" } },
|
|
20
|
+
catalogItems: [{
|
|
21
|
+
quantity: 1,
|
|
22
|
+
catalogReference: {
|
|
23
|
+
appId: PRICING_PLANS_APP_ID,
|
|
24
|
+
catalogItemId: planId,
|
|
25
|
+
options: {
|
|
26
|
+
type: "PLAN",
|
|
27
|
+
planOptions: {
|
|
28
|
+
pricingVariantId,
|
|
29
|
+
membersOnly: false
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}]
|
|
34
|
+
});
|
|
35
|
+
if (!cart?._id) throw new Error("Cart creation did not return an ID");
|
|
36
|
+
const { checkoutUrl } = await cartV2.getCheckoutUrl(cart._id);
|
|
37
|
+
if (!checkoutUrl) throw new Error("Checkout URL was not returned");
|
|
38
|
+
return checkoutUrl;
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/builder/plan-purchase.ts
|
|
42
|
+
function planPurchase(plan, pricingVariantId) {
|
|
43
|
+
if (!plan || plan.buyable !== true) return;
|
|
44
|
+
const variants = (plan.pricingVariants ?? []).filter((variant) => Boolean(variant._id) && variant.visible !== false);
|
|
45
|
+
const variant = variants.find(({ _id }) => _id === pricingVariantId) ?? variants[0];
|
|
46
|
+
if (!variant) return;
|
|
47
|
+
const requiresStartDate = plan.startDateSettings ? plan.startDateSettings.type === "DATE_RANGE" : variant.billingTerms?.startType === "CUSTOM";
|
|
48
|
+
return {
|
|
49
|
+
pricingVariantId: variant._id,
|
|
50
|
+
requiresCustomization: Boolean(plan.formId) || requiresStartDate
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region src/url.ts
|
|
55
|
+
function siteHref(siteUrl, path) {
|
|
56
|
+
return `${siteUrl.replace(/\/$/, "")}/${path.replace(/^\//, "")}`;
|
|
57
|
+
}
|
|
58
|
+
function withQuery(url, query) {
|
|
59
|
+
const search = query.toString();
|
|
60
|
+
return search ? `${url}?${search}` : url;
|
|
61
|
+
}
|
|
62
|
+
function queryOf(entries) {
|
|
63
|
+
return new URLSearchParams(entries.filter((entry) => Boolean(entry[1])));
|
|
64
|
+
}
|
|
65
|
+
function encodePricingVariants(pricingVariantsByPlanId) {
|
|
66
|
+
return Object.entries(pricingVariantsByPlanId).filter(([, pricingVariantIds]) => pricingVariantIds.length).map(([planId, pricingVariantIds]) => [planId, ...pricingVariantIds].join(":")).join(",");
|
|
67
|
+
}
|
|
68
|
+
//#endregion
|
|
69
|
+
//#region src/app-pages.ts
|
|
70
|
+
function findAppPage(pages, appPageId) {
|
|
71
|
+
const candidates = Object.values(pages).filter((page) => !page.popup && page.appPageId === appPageId);
|
|
72
|
+
const unowned = candidates.filter((page) => !page.appDefinitionId);
|
|
73
|
+
return candidates.find((page) => page.appDefinitionId === "1522827f-c56c-a5c9-2ac9-00f9e6ae12d3") ?? (unowned.length === 1 ? unowned[0] : void 0);
|
|
74
|
+
}
|
|
75
|
+
function appPageUrl(context, appPageId) {
|
|
76
|
+
const path = findAppPage(context.pages, appPageId) ? context.sectionUrl(PRICING_PLANS_APP_ID, appPageId) : void 0;
|
|
77
|
+
return path ? siteHref(context.siteUrl, path) : void 0;
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/builder/return-url.ts
|
|
81
|
+
function returnUrl({ pages, siteUrl, sectionUrl }, navigation) {
|
|
82
|
+
if (!navigation) return;
|
|
83
|
+
switch (navigation.type) {
|
|
84
|
+
case "url": return onSiteUrl(siteUrl, navigation.url);
|
|
85
|
+
case "page": {
|
|
86
|
+
const page = pages[navigation.pageId];
|
|
87
|
+
return page && siteHref(siteUrl, page.path);
|
|
88
|
+
}
|
|
89
|
+
case "section": {
|
|
90
|
+
const { appDefinitionId, sectionId, state, queryParams, discardRouterPageSuffix } = navigation.options;
|
|
91
|
+
const path = sectionUrl(appDefinitionId, sectionId, discardRouterPageSuffix);
|
|
92
|
+
if (!path) return;
|
|
93
|
+
const url = siteHref(siteUrl, state ? `${path.replace(/\/$/, "")}/${state}` : path);
|
|
94
|
+
return queryParams ? withQuery(url, new URLSearchParams({ appSectionParams: JSON.stringify(queryParams) })) : url;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function onSiteUrl(siteUrl, url) {
|
|
99
|
+
try {
|
|
100
|
+
const site = new URL(siteUrl.endsWith("/") ? siteUrl : `${siteUrl}/`);
|
|
101
|
+
const target = new URL(url.startsWith("/") ? siteHref(siteUrl, url) : url, site);
|
|
102
|
+
const basePath = site.pathname.replace(/\/$/, "");
|
|
103
|
+
if (target.origin === site.origin && !target.username && !target.password && (target.protocol === "https:" || target.protocol === "http:") && (target.pathname === basePath || target.pathname.startsWith(`${basePath}/`))) return target.href;
|
|
104
|
+
} catch {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
//#region src/builder/checkout-query.ts
|
|
110
|
+
function checkoutQueryEntries(context, checkout = {}) {
|
|
111
|
+
return [
|
|
112
|
+
["minStartDate", checkout.minStartDate],
|
|
113
|
+
["maxStartDate", checkout.maxStartDate],
|
|
114
|
+
["returnUrl", returnUrl(context, checkout.thankYouPage?.navigation)]
|
|
115
|
+
];
|
|
116
|
+
}
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/builder/plan-page-url.ts
|
|
119
|
+
const BUILDER_PLAN_PAGE_ID = "plan-customization-page";
|
|
120
|
+
function builderPlanPageUrl(context, { planId, pricingVariantId, ...checkout }) {
|
|
121
|
+
const url = appPageUrl(context, BUILDER_PLAN_PAGE_ID);
|
|
122
|
+
if (!url) return;
|
|
123
|
+
return withQuery(url, queryOf([
|
|
124
|
+
["planId", planId],
|
|
125
|
+
["pricingVariantId", pricingVariantId],
|
|
126
|
+
...checkoutQueryEntries(context, checkout)
|
|
127
|
+
]));
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
//#region src/builder/pricing-page-url.ts
|
|
131
|
+
const BUILDER_PRICING_PAGE_ID = "plans-page";
|
|
132
|
+
function builderPricingPageUrl(context, { planIds, pricingVariantsByPlanId, checkout }) {
|
|
133
|
+
const url = appPageUrl(context, BUILDER_PRICING_PAGE_ID);
|
|
134
|
+
if (!url) return;
|
|
135
|
+
const pricingVariants = pricingVariantsByPlanId && encodePricingVariants(pricingVariantsByPlanId);
|
|
136
|
+
return withQuery(url, queryOf([pricingVariants ? ["pricingVariants", pricingVariants] : ["planIds", planIds?.join(",")], ...checkoutQueryEntries(context, checkout)]));
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
139
|
+
//#region src/builder/success-url.ts
|
|
140
|
+
const BUILDER_THANK_YOU_PAGE_ID = "thank-you-page";
|
|
141
|
+
function withSuccessUrl(context, checkoutUrl, navigation) {
|
|
142
|
+
const thankYouPage = appPageUrl(context, BUILDER_THANK_YOU_PAGE_ID);
|
|
143
|
+
const target = returnUrl(context, navigation);
|
|
144
|
+
if (!thankYouPage || !target) return checkoutUrl;
|
|
145
|
+
const successUrl = new URL(thankYouPage);
|
|
146
|
+
const url = new URL(checkoutUrl);
|
|
147
|
+
successUrl.searchParams.set("returnUrl", target);
|
|
148
|
+
url.searchParams.set("successUrl", successUrl.href);
|
|
149
|
+
return url.href;
|
|
150
|
+
}
|
|
151
|
+
//#endregion
|
|
152
|
+
//#region src/adapters/navigate-to-page.ts
|
|
153
|
+
async function navigateToPage(navigateTo, href, pageName) {
|
|
154
|
+
if (!href) throw new Error(`Cannot navigate to ${pageName}, because it is not available on the site.`);
|
|
155
|
+
if (!await navigateTo(href)) throw new Error(`Navigation to ${pageName} failed.`);
|
|
156
|
+
}
|
|
157
|
+
async function getPageUrl(href, pageName) {
|
|
158
|
+
if (!href) throw new Error(`Cannot get ${pageName} URL, because it is not available on the site.`);
|
|
159
|
+
return href;
|
|
160
|
+
}
|
|
161
|
+
//#endregion
|
|
162
|
+
//#region src/adapters/builder.ts
|
|
163
|
+
function createBuilderSiteApi({ navigateTo, ...context }) {
|
|
164
|
+
return {
|
|
165
|
+
navigateToPricingPage: (options = {}) => navigateToPage(navigateTo, builderPricingPageUrl(context, options), "pricing page"),
|
|
166
|
+
getPricingPageUrl: (options = {}) => getPageUrl(builderPricingPageUrl(context, options), "pricing page"),
|
|
167
|
+
navigateToPlanPage: async (options) => {
|
|
168
|
+
const purchase = planPurchase(await loadPublicPlan(options.planId), options.pricingVariantId);
|
|
169
|
+
if (!purchase) throw new Error("Plan is not available for online purchase.");
|
|
170
|
+
if (purchase.requiresCustomization) return navigateToPage(navigateTo, builderPlanPageUrl(context, {
|
|
171
|
+
...options,
|
|
172
|
+
pricingVariantId: purchase.pricingVariantId
|
|
173
|
+
}), "plan page");
|
|
174
|
+
return navigateToPage(navigateTo, withSuccessUrl(context, await createCheckoutUrl(options.planId, purchase.pricingVariantId), options.thankYouPage?.navigation), "checkout");
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
//#endregion
|
|
179
|
+
//#region src/legacy/pages.ts
|
|
180
|
+
const LEGACY_PRICING_PAGE_ID = "membership_plan_picker_tpa";
|
|
181
|
+
function findLegacyPricingPage(pages) {
|
|
182
|
+
return findAppPage(pages, LEGACY_PRICING_PAGE_ID);
|
|
183
|
+
}
|
|
184
|
+
//#endregion
|
|
185
|
+
//#region src/legacy/pricing-page-url.ts
|
|
186
|
+
function legacyPricingPageUrl(context, options) {
|
|
187
|
+
const url = appPageUrl(context, LEGACY_PRICING_PAGE_ID);
|
|
188
|
+
return url && withQuery(url, pricingPageQuery(options));
|
|
189
|
+
}
|
|
190
|
+
function pricingPageQuery({ planIds, pricingVariantsByPlanId, title, subtitle, biOptions, checkout }) {
|
|
191
|
+
const { content, navigation } = checkout?.thankYouPage ?? {};
|
|
192
|
+
const pricingVariants = pricingVariantsByPlanId && encodePricingVariants(pricingVariantsByPlanId);
|
|
193
|
+
return queryOf([
|
|
194
|
+
["destination", navigation?.type === "url" ? navigation.url : void 0],
|
|
195
|
+
["page", navigation?.type === "page" ? navigation.pageId : void 0],
|
|
196
|
+
["section", navigation?.type === "section" ? encodeBase64Url(navigation.options) : void 0],
|
|
197
|
+
["referral", biOptions?.referralInfo],
|
|
198
|
+
["referralId", biOptions?.referralId],
|
|
199
|
+
["maxStartDate", checkout?.maxStartDate],
|
|
200
|
+
["minStartDate", checkout?.minStartDate],
|
|
201
|
+
["thankYouTitle", content?.title],
|
|
202
|
+
["thankYouMessage", content?.message],
|
|
203
|
+
["thankYouCTA", content?.cta],
|
|
204
|
+
pricingVariants ? ["pricingVariants", pricingVariants] : ["planIds", planIds?.join(",")],
|
|
205
|
+
["title", title],
|
|
206
|
+
["subtitle", subtitle]
|
|
207
|
+
]);
|
|
208
|
+
}
|
|
209
|
+
function encodeBase64Url(input) {
|
|
210
|
+
const bytes = encodeURIComponent(JSON.stringify(input)).replace(/%[0-9A-F]{2}/g, (percent) => String.fromCharCode(parseInt(percent.slice(1), 16)));
|
|
211
|
+
return btoa(bytes).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
212
|
+
}
|
|
213
|
+
//#endregion
|
|
214
|
+
//#region src/adapters/legacy-ooi.ts
|
|
215
|
+
function createLegacyOoiSiteApi({ navigateTo, callPublicApi, ...context }) {
|
|
216
|
+
return {
|
|
217
|
+
navigateToPricingPage: (options = {}) => navigateToPage(navigateTo, legacyPricingPageUrl(context, options), "pricing page"),
|
|
218
|
+
getPricingPageUrl: (options = {}) => getPageUrl(legacyPricingPageUrl(context, options), "pricing page"),
|
|
219
|
+
navigateToPlanPage: async (options) => {
|
|
220
|
+
await callPublicApi("navigateToPlanPage", [options]);
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
//#endregion
|
|
225
|
+
//#region src/adapters/not-installed.ts
|
|
226
|
+
const notInstalled = () => Promise.reject(/* @__PURE__ */ new Error("Pricing Plans is not installed on the site."));
|
|
227
|
+
function createNotInstalledSiteApi() {
|
|
228
|
+
return {
|
|
229
|
+
navigateToPricingPage: notInstalled,
|
|
230
|
+
getPricingPageUrl: notInstalled,
|
|
231
|
+
navigateToPlanPage: notInstalled
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
//#endregion
|
|
235
|
+
//#region src/create-site-api.ts
|
|
236
|
+
const BUILDER_PAGE_IDS = [
|
|
237
|
+
BUILDER_PRICING_PAGE_ID,
|
|
238
|
+
BUILDER_PLAN_PAGE_ID,
|
|
239
|
+
BUILDER_THANK_YOU_PAGE_ID
|
|
240
|
+
];
|
|
241
|
+
function createSiteApi(context) {
|
|
242
|
+
if (findLegacyPricingPage(context.pages)) return createLegacyOoiSiteApi(context);
|
|
243
|
+
if (Object.values(context.pages).some((page) => page.appDefinitionId === "1522827f-c56c-a5c9-2ac9-00f9e6ae12d3") || BUILDER_PAGE_IDS.some((appPageId) => findAppPage(context.pages, appPageId))) return createBuilderSiteApi(context);
|
|
244
|
+
return createNotInstalledSiteApi();
|
|
245
|
+
}
|
|
246
|
+
//#endregion
|
|
247
|
+
//#region src/public-api-caller.ts
|
|
248
|
+
const PublicApiCallerDefinition = defineService("@wix/site-service-public-api-caller");
|
|
249
|
+
//#endregion
|
|
250
|
+
//#region src/router.ts
|
|
251
|
+
const LegacyRouterDefinition = defineService("@wix/viewer-service-router");
|
|
252
|
+
function getRouter(getService) {
|
|
253
|
+
try {
|
|
254
|
+
return getService(RouterDefinition);
|
|
255
|
+
} catch {
|
|
256
|
+
return getService(LegacyRouterDefinition);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
//#endregion
|
|
260
|
+
//#region src/use-pricing-plans-site-api.ts
|
|
261
|
+
/**
|
|
262
|
+
* Navigation to Pricing Plans pages that works with both versions of the Pricing Plans app: the
|
|
263
|
+
* legacy app (OOI) and the Builder app. The hook detects which one the site runs and uses its
|
|
264
|
+
* pages, so the calls are the same for both.
|
|
265
|
+
*
|
|
266
|
+
* Calling the hook is safe on any site. When Pricing Plans isn't installed on the site, every method
|
|
267
|
+
* it returns rejects with the error `Pricing Plans is not installed on the site.` and doesn't
|
|
268
|
+
* navigate. The hook treats Pricing Plans as installed when the site has at least one Pricing Plans
|
|
269
|
+
* page.
|
|
270
|
+
*
|
|
271
|
+
* Use it inside a site component or context provider, and list these services in the extension's
|
|
272
|
+
* `serviceDependencies`: `@wix/site-service-pages`, `@wix/site-service-url`,
|
|
273
|
+
* `@wix/site-service-router`, `@wix/viewer-service-router` and
|
|
274
|
+
* `@wix/site-service-public-api-caller`.
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* const { navigateToPlanPage } = usePricingPlansSiteApi();
|
|
278
|
+
*
|
|
279
|
+
* <button onClick={() => navigateToPlanPage({ planId })}>Buy plan</button>
|
|
280
|
+
*/
|
|
281
|
+
function usePricingPlansSiteApi() {
|
|
282
|
+
const pages = usePages();
|
|
283
|
+
const url = useService(UrlDefinition);
|
|
284
|
+
const getService = useGetService();
|
|
285
|
+
return useMemo(() => createSiteApi({
|
|
286
|
+
pages,
|
|
287
|
+
siteUrl: url.siteUrl,
|
|
288
|
+
sectionUrl: (appDefinitionId, sectionId, discardRouterPageSuffix) => url.appSectionRelativeUrl(appDefinitionId, sectionId, discardRouterPageSuffix),
|
|
289
|
+
navigateTo: (href) => getRouter(getService).navigateTo({ href }),
|
|
290
|
+
callPublicApi: (methodPath, args) => getService(PublicApiCallerDefinition).callPublicApi(PRICING_PLANS_APP_ID, methodPath, args)
|
|
291
|
+
}), [
|
|
292
|
+
pages,
|
|
293
|
+
url,
|
|
294
|
+
getService
|
|
295
|
+
]);
|
|
296
|
+
}
|
|
297
|
+
//#endregion
|
|
298
|
+
export { usePricingPlansSiteApi };
|
|
299
|
+
|
|
300
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/constants.ts","../src/builder/checkout.ts","../src/builder/plan-purchase.ts","../src/url.ts","../src/app-pages.ts","../src/builder/return-url.ts","../src/builder/checkout-query.ts","../src/builder/plan-page-url.ts","../src/builder/pricing-page-url.ts","../src/builder/success-url.ts","../src/adapters/navigate-to-page.ts","../src/adapters/builder.ts","../src/legacy/pages.ts","../src/legacy/pricing-page-url.ts","../src/adapters/legacy-ooi.ts","../src/adapters/not-installed.ts","../src/create-site-api.ts","../src/public-api-caller.ts","../src/router.ts","../src/use-pricing-plans-site-api.ts"],"sourcesContent":["export const PRICING_PLANS_APP_ID = '1522827f-c56c-a5c9-2ac9-00f9e6ae12d3';\n","import { cartV2 } from '@wix/ecom';\nimport { plansV3 } from '@wix/pricing-plans';\nimport { PRICING_PLANS_APP_ID } from '../constants';\n\nexport async function loadPublicPlan(planId: string): Promise<plansV3.Plan | undefined> {\n const { items } = await plansV3\n .queryPlans()\n .eq('_id', planId)\n .eq('visibility', 'PUBLIC')\n .limit(1)\n .find();\n\n return items[0];\n}\n\nexport async function createCheckoutUrl(planId: string, pricingVariantId: string): Promise<string> {\n const cart = await cartV2.createCart({\n cart: { source: { channelType: 'WEB' } },\n catalogItems: [\n {\n quantity: 1,\n catalogReference: {\n appId: PRICING_PLANS_APP_ID,\n catalogItemId: planId,\n options: { type: 'PLAN', planOptions: { pricingVariantId, membersOnly: false } },\n },\n },\n ],\n });\n\n if (!cart?._id) {\n throw new Error('Cart creation did not return an ID');\n }\n\n const { checkoutUrl } = await cartV2.getCheckoutUrl(cart._id);\n\n if (!checkoutUrl) {\n throw new Error('Checkout URL was not returned');\n }\n\n return checkoutUrl;\n}\n","import type { plansV3 } from '@wix/pricing-plans';\n\nexport type PlanPurchase = {\n pricingVariantId: string;\n requiresCustomization: boolean;\n};\n\nexport function planPurchase(\n plan: plansV3.Plan | undefined,\n pricingVariantId?: string,\n): PlanPurchase | undefined {\n if (!plan || plan.buyable !== true) return;\n\n const variants = (plan.pricingVariants ?? []).filter(\n (variant): variant is plansV3.PricingVariant & { _id: string } =>\n Boolean(variant._id) && variant.visible !== false,\n );\n // Like the legacy app, a hidden or unknown pricingVariantId falls back to the first visible variant.\n const variant = variants.find(({ _id }) => _id === pricingVariantId) ?? variants[0];\n\n if (!variant) return;\n\n const requiresStartDate = plan.startDateSettings\n ? plan.startDateSettings.type === 'DATE_RANGE'\n : variant.billingTerms?.startType === 'CUSTOM';\n\n return {\n pricingVariantId: variant._id,\n requiresCustomization: Boolean(plan.formId) || requiresStartDate,\n };\n}\n","export function siteHref(siteUrl: string, path: string): string {\n return `${siteUrl.replace(/\\/$/, '')}/${path.replace(/^\\//, '')}`;\n}\n\nexport function withQuery(url: string, query: URLSearchParams): string {\n const search = query.toString();\n\n return search ? `${url}?${search}` : url;\n}\n\nexport function queryOf(entries: Array<[string, string | undefined]>): URLSearchParams {\n return new URLSearchParams(\n entries.filter((entry): entry is [string, string] => Boolean(entry[1])),\n );\n}\n\nexport function encodePricingVariants(pricingVariantsByPlanId: Record<string, string[]>): string {\n return Object.entries(pricingVariantsByPlanId)\n .filter(([, pricingVariantIds]) => pricingVariantIds.length)\n .map(([planId, pricingVariantIds]) => [planId, ...pricingVariantIds].join(':'))\n .join(',');\n}\n","import { PRICING_PLANS_APP_ID } from './constants';\nimport type { Page, Pages, UrlContext } from './types';\nimport { siteHref } from './url';\n\nexport function findAppPage(pages: Pages, appPageId: string): Page | undefined {\n const candidates = Object.values(pages).filter(\n (page) => !page.popup && page.appPageId === appPageId,\n );\n const unowned = candidates.filter((page) => !page.appDefinitionId);\n\n return (\n candidates.find((page) => page.appDefinitionId === PRICING_PLANS_APP_ID) ??\n (unowned.length === 1 ? unowned[0] : undefined)\n );\n}\n\nexport function appPageUrl(context: UrlContext, appPageId: string): string | undefined {\n const path = findAppPage(context.pages, appPageId)\n ? context.sectionUrl(PRICING_PLANS_APP_ID, appPageId)\n : undefined;\n\n return path ? siteHref(context.siteUrl, path) : undefined;\n}\n","import type { NavigationPolicy, UrlContext } from '../types';\nimport { siteHref, withQuery } from '../url';\n\nexport function returnUrl(\n { pages, siteUrl, sectionUrl }: UrlContext,\n navigation: NavigationPolicy | undefined,\n): string | undefined {\n if (!navigation) return;\n\n switch (navigation.type) {\n case 'url':\n return onSiteUrl(siteUrl, navigation.url);\n case 'page': {\n const page = pages[navigation.pageId];\n\n return page && siteHref(siteUrl, page.path);\n }\n case 'section': {\n const { appDefinitionId, sectionId, state, queryParams, discardRouterPageSuffix } =\n navigation.options;\n const path = sectionUrl(appDefinitionId, sectionId, discardRouterPageSuffix);\n\n if (!path) return;\n\n const url = siteHref(siteUrl, state ? `${path.replace(/\\/$/, '')}/${state}` : path);\n\n return queryParams\n ? withQuery(url, new URLSearchParams({ appSectionParams: JSON.stringify(queryParams) }))\n : url;\n }\n }\n}\n\n// Matches the targets the Thank You page accepts as `returnUrl`; it sends any other to the homepage.\nfunction onSiteUrl(siteUrl: string, url: string): string | undefined {\n try {\n const site = new URL(siteUrl.endsWith('/') ? siteUrl : `${siteUrl}/`);\n const target = new URL(url.startsWith('/') ? siteHref(siteUrl, url) : url, site);\n const basePath = site.pathname.replace(/\\/$/, '');\n\n if (\n target.origin === site.origin &&\n !target.username &&\n !target.password &&\n (target.protocol === 'https:' || target.protocol === 'http:') &&\n (target.pathname === basePath || target.pathname.startsWith(`${basePath}/`))\n ) {\n return target.href;\n }\n } catch {\n return;\n }\n}\n","import type { CheckoutOptions, UrlContext } from '../types';\nimport { returnUrl } from './return-url';\n\nexport function checkoutQueryEntries(\n context: UrlContext,\n checkout: CheckoutOptions = {},\n): Array<[string, string | undefined]> {\n return [\n ['minStartDate', checkout.minStartDate],\n ['maxStartDate', checkout.maxStartDate],\n ['returnUrl', returnUrl(context, checkout.thankYouPage?.navigation)],\n ];\n}\n","import { appPageUrl } from '../app-pages';\nimport type { NavigateToPlanPageOptions, UrlContext } from '../types';\nimport { queryOf, withQuery } from '../url';\nimport { checkoutQueryEntries } from './checkout-query';\n\nexport const BUILDER_PLAN_PAGE_ID = 'plan-customization-page';\n\nexport function builderPlanPageUrl(\n context: UrlContext,\n { planId, pricingVariantId, ...checkout }: NavigateToPlanPageOptions,\n): string | undefined {\n const url = appPageUrl(context, BUILDER_PLAN_PAGE_ID);\n\n if (!url) return;\n\n return withQuery(\n url,\n queryOf([\n ['planId', planId],\n ['pricingVariantId', pricingVariantId],\n ...checkoutQueryEntries(context, checkout),\n ]),\n );\n}\n","import { appPageUrl } from '../app-pages';\nimport type { NavigateToPricingPageOptions, UrlContext } from '../types';\nimport { encodePricingVariants, queryOf, withQuery } from '../url';\nimport { checkoutQueryEntries } from './checkout-query';\n\nexport const BUILDER_PRICING_PAGE_ID = 'plans-page';\n\nexport function builderPricingPageUrl(\n context: UrlContext,\n { planIds, pricingVariantsByPlanId, checkout }: NavigateToPricingPageOptions,\n): string | undefined {\n const url = appPageUrl(context, BUILDER_PRICING_PAGE_ID);\n\n if (!url) return;\n\n const pricingVariants = pricingVariantsByPlanId && encodePricingVariants(pricingVariantsByPlanId);\n\n return withQuery(\n url,\n queryOf([\n pricingVariants ? ['pricingVariants', pricingVariants] : ['planIds', planIds?.join(',')],\n ...checkoutQueryEntries(context, checkout),\n ]),\n );\n}\n","import { appPageUrl } from '../app-pages';\nimport type { NavigationPolicy, UrlContext } from '../types';\nimport { returnUrl } from './return-url';\n\nexport const BUILDER_THANK_YOU_PAGE_ID = 'thank-you-page';\n\nexport function withSuccessUrl(\n context: UrlContext,\n checkoutUrl: string,\n navigation: NavigationPolicy | undefined,\n): string {\n const thankYouPage = appPageUrl(context, BUILDER_THANK_YOU_PAGE_ID);\n const target = returnUrl(context, navigation);\n\n if (!thankYouPage || !target) {\n return checkoutUrl;\n }\n\n const successUrl = new URL(thankYouPage);\n const url = new URL(checkoutUrl);\n\n successUrl.searchParams.set('returnUrl', target);\n url.searchParams.set('successUrl', successUrl.href);\n\n return url.href;\n}\n","import type { SiteApiContext } from '../types';\n\nexport async function navigateToPage(\n navigateTo: SiteApiContext['navigateTo'],\n href: string | undefined,\n pageName: string,\n): Promise<void> {\n if (!href) {\n throw new Error(`Cannot navigate to ${pageName}, because it is not available on the site.`);\n }\n\n if (!(await navigateTo(href))) {\n throw new Error(`Navigation to ${pageName} failed.`);\n }\n}\n\nexport async function getPageUrl(href: string | undefined, pageName: string): Promise<string> {\n if (!href) {\n throw new Error(`Cannot get ${pageName} URL, because it is not available on the site.`);\n }\n\n return href;\n}\n","import { createCheckoutUrl, loadPublicPlan } from '../builder/checkout';\nimport { planPurchase } from '../builder/plan-purchase';\nimport { builderPlanPageUrl } from '../builder/plan-page-url';\nimport { builderPricingPageUrl } from '../builder/pricing-page-url';\nimport { withSuccessUrl } from '../builder/success-url';\nimport type { SiteApiContext, PricingPlansSiteApi } from '../types';\nimport { getPageUrl, navigateToPage } from './navigate-to-page';\n\nexport function createBuilderSiteApi({\n navigateTo,\n ...context\n}: SiteApiContext): PricingPlansSiteApi {\n return {\n navigateToPricingPage: (options = {}) =>\n navigateToPage(navigateTo, builderPricingPageUrl(context, options), 'pricing page'),\n getPricingPageUrl: (options = {}) =>\n getPageUrl(builderPricingPageUrl(context, options), 'pricing page'),\n navigateToPlanPage: async (options) => {\n const purchase = planPurchase(await loadPublicPlan(options.planId), options.pricingVariantId);\n\n if (!purchase) {\n throw new Error('Plan is not available for online purchase.');\n }\n\n if (purchase.requiresCustomization) {\n return navigateToPage(\n navigateTo,\n builderPlanPageUrl(context, { ...options, pricingVariantId: purchase.pricingVariantId }),\n 'plan page',\n );\n }\n\n const checkoutUrl = await createCheckoutUrl(options.planId, purchase.pricingVariantId);\n\n return navigateToPage(\n navigateTo,\n withSuccessUrl(context, checkoutUrl, options.thankYouPage?.navigation),\n 'checkout',\n );\n },\n };\n}\n","import { findAppPage } from '../app-pages';\nimport type { Page, Pages } from '../types';\n\nexport const LEGACY_PRICING_PAGE_ID = 'membership_plan_picker_tpa';\n\nexport function findLegacyPricingPage(pages: Pages): Page | undefined {\n return findAppPage(pages, LEGACY_PRICING_PAGE_ID);\n}\n","import { appPageUrl } from '../app-pages';\nimport type { NavigateToPricingPageOptions, UrlContext } from '../types';\nimport { encodePricingVariants, queryOf, withQuery } from '../url';\nimport { LEGACY_PRICING_PAGE_ID } from './pages';\n\nexport function legacyPricingPageUrl(\n context: UrlContext,\n options: NavigateToPricingPageOptions,\n): string | undefined {\n const url = appPageUrl(context, LEGACY_PRICING_PAGE_ID);\n\n return url && withQuery(url, pricingPageQuery(options));\n}\n\nfunction pricingPageQuery({\n planIds,\n pricingVariantsByPlanId,\n title,\n subtitle,\n biOptions,\n checkout,\n}: NavigateToPricingPageOptions): URLSearchParams {\n const { content, navigation } = checkout?.thankYouPage ?? {};\n const pricingVariants = pricingVariantsByPlanId && encodePricingVariants(pricingVariantsByPlanId);\n\n return queryOf([\n ['destination', navigation?.type === 'url' ? navigation.url : undefined],\n ['page', navigation?.type === 'page' ? navigation.pageId : undefined],\n ['section', navigation?.type === 'section' ? encodeBase64Url(navigation.options) : undefined],\n ['referral', biOptions?.referralInfo],\n ['referralId', biOptions?.referralId],\n ['maxStartDate', checkout?.maxStartDate],\n ['minStartDate', checkout?.minStartDate],\n ['thankYouTitle', content?.title],\n ['thankYouMessage', content?.message],\n ['thankYouCTA', content?.cta],\n pricingVariants ? ['pricingVariants', pricingVariants] : ['planIds', planIds?.join(',')],\n ['title', title],\n ['subtitle', subtitle],\n ]);\n}\n\nfunction encodeBase64Url(input: unknown): string {\n const bytes = encodeURIComponent(JSON.stringify(input)).replace(/%[0-9A-F]{2}/g, (percent) =>\n String.fromCharCode(parseInt(percent.slice(1), 16)),\n );\n\n return btoa(bytes).replace(/=/g, '').replace(/\\+/g, '-').replace(/\\//g, '_');\n}\n","import { legacyPricingPageUrl } from '../legacy/pricing-page-url';\nimport type { SiteApiContext, PricingPlansSiteApi } from '../types';\nimport { getPageUrl, navigateToPage } from './navigate-to-page';\n\nexport function createLegacyOoiSiteApi({\n navigateTo,\n callPublicApi,\n ...context\n}: SiteApiContext): PricingPlansSiteApi {\n return {\n navigateToPricingPage: (options = {}) =>\n navigateToPage(navigateTo, legacyPricingPageUrl(context, options), 'pricing page'),\n getPricingPageUrl: (options = {}) =>\n getPageUrl(legacyPricingPageUrl(context, options), 'pricing page'),\n navigateToPlanPage: async (options) => {\n await callPublicApi('navigateToPlanPage', [options]);\n },\n };\n}\n","import type { PricingPlansSiteApi } from '../types';\n\nconst notInstalled = () => Promise.reject(new Error('Pricing Plans is not installed on the site.'));\n\nexport function createNotInstalledSiteApi(): PricingPlansSiteApi {\n return {\n navigateToPricingPage: notInstalled,\n getPricingPageUrl: notInstalled,\n navigateToPlanPage: notInstalled,\n };\n}\n","import { createBuilderSiteApi } from './adapters/builder';\nimport { createLegacyOoiSiteApi } from './adapters/legacy-ooi';\nimport { createNotInstalledSiteApi } from './adapters/not-installed';\nimport { findAppPage } from './app-pages';\nimport { BUILDER_PLAN_PAGE_ID } from './builder/plan-page-url';\nimport { BUILDER_PRICING_PAGE_ID } from './builder/pricing-page-url';\nimport { BUILDER_THANK_YOU_PAGE_ID } from './builder/success-url';\nimport { PRICING_PLANS_APP_ID } from './constants';\nimport { findLegacyPricingPage } from './legacy/pages';\nimport type { SiteApiContext, PricingPlansSiteApi } from './types';\n\nconst BUILDER_PAGE_IDS = [BUILDER_PRICING_PAGE_ID, BUILDER_PLAN_PAGE_ID, BUILDER_THANK_YOU_PAGE_ID];\n\nexport function createSiteApi(context: SiteApiContext): PricingPlansSiteApi {\n if (findLegacyPricingPage(context.pages)) {\n return createLegacyOoiSiteApi(context);\n }\n\n if (\n Object.values(context.pages).some((page) => page.appDefinitionId === PRICING_PLANS_APP_ID) ||\n BUILDER_PAGE_IDS.some((appPageId) => findAppPage(context.pages, appPageId))\n ) {\n return createBuilderSiteApi(context);\n }\n\n return createNotInstalledSiteApi();\n}\n","import { defineService } from '@wix/services-definitions';\n\ntype PublicApiCaller = {\n callPublicApi(\n appDefinitionId: string,\n methodPath: string | string[],\n args?: unknown[],\n ): Promise<unknown>;\n};\n\nexport const PublicApiCallerDefinition = defineService<PublicApiCaller>(\n '@wix/site-service-public-api-caller',\n);\n","import { defineService } from '@wix/services-definitions';\nimport type { GetService } from '@wix/services-definitions';\nimport { RouterDefinition } from '@wix/site-service-router/definition';\nimport type { IRouterService } from '@wix/site-service-router/definition';\n\nconst LegacyRouterDefinition = defineService<IRouterService>('@wix/viewer-service-router');\n\nexport function getRouter(getService: GetService): IRouterService {\n try {\n return getService(RouterDefinition);\n } catch {\n return getService(LegacyRouterDefinition);\n }\n}\n","import { usePages } from '@wix/react-component-utils';\nimport { useGetService, useService } from '@wix/services-manager-react';\nimport { UrlDefinition } from '@wix/site-service-url/definition';\nimport { useMemo } from 'react';\nimport { PRICING_PLANS_APP_ID } from './constants';\nimport { createSiteApi } from './create-site-api';\nimport { PublicApiCallerDefinition } from './public-api-caller';\nimport { getRouter } from './router';\nimport type { PricingPlansSiteApi } from './types';\n\n/**\n * Navigation to Pricing Plans pages that works with both versions of the Pricing Plans app: the\n * legacy app (OOI) and the Builder app. The hook detects which one the site runs and uses its\n * pages, so the calls are the same for both.\n *\n * Calling the hook is safe on any site. When Pricing Plans isn't installed on the site, every method\n * it returns rejects with the error `Pricing Plans is not installed on the site.` and doesn't\n * navigate. The hook treats Pricing Plans as installed when the site has at least one Pricing Plans\n * page.\n *\n * Use it inside a site component or context provider, and list these services in the extension's\n * `serviceDependencies`: `@wix/site-service-pages`, `@wix/site-service-url`,\n * `@wix/site-service-router`, `@wix/viewer-service-router` and\n * `@wix/site-service-public-api-caller`.\n *\n * @example\n * const { navigateToPlanPage } = usePricingPlansSiteApi();\n *\n * <button onClick={() => navigateToPlanPage({ planId })}>Buy plan</button>\n */\nexport function usePricingPlansSiteApi(): PricingPlansSiteApi {\n const pages = usePages();\n const url = useService(UrlDefinition);\n const getService = useGetService();\n\n return useMemo(\n () =>\n createSiteApi({\n pages,\n siteUrl: url.siteUrl,\n sectionUrl: (appDefinitionId, sectionId, discardRouterPageSuffix) =>\n url.appSectionRelativeUrl(appDefinitionId, sectionId, discardRouterPageSuffix),\n navigateTo: (href) => getRouter(getService).navigateTo({ href }),\n callPublicApi: (methodPath, args) =>\n getService(PublicApiCallerDefinition).callPublicApi(\n PRICING_PLANS_APP_ID,\n methodPath,\n args,\n ),\n }),\n [pages, url, getService],\n );\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,66 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/pricing-plans-site-api",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"files": [
|
|
5
|
+
"dist"
|
|
6
|
+
],
|
|
7
|
+
"type": "module",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"registry": "https://registry.npmjs.org/",
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"lint": "oxlint",
|
|
21
|
+
"lint:fix": "oxlint --fix",
|
|
22
|
+
"fmt": "oxfmt",
|
|
23
|
+
"fmt:check": "oxfmt --check",
|
|
24
|
+
"build": "yarn typecheck && rolldown -c",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"test": "vitest"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@wix/ecom": "^1.0.2514",
|
|
30
|
+
"@wix/pricing-plans": "^1.0.403",
|
|
31
|
+
"@wix/services-definitions": "^1.0.5",
|
|
32
|
+
"@wix/site-service-router": "^1.0.1",
|
|
33
|
+
"@wix/site-service-url": "^1.0.2"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/react": "^18.3.31",
|
|
37
|
+
"@wix/fed-cli-oxlint": "^1.0.6",
|
|
38
|
+
"@wix/react-component-utils": "^1.13.0",
|
|
39
|
+
"@wix/services-manager-react": "^1.0.8",
|
|
40
|
+
"oxfmt": "^0.70.0",
|
|
41
|
+
"oxlint": "^1.85.0",
|
|
42
|
+
"react": "^18.3.1",
|
|
43
|
+
"rolldown": "^1.2.9",
|
|
44
|
+
"rolldown-plugin-dts": "^0.27.14",
|
|
45
|
+
"typescript": "^6.0.3",
|
|
46
|
+
"vitest": "^4.1.11"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"@wix/react-component-utils": "^1.13.0",
|
|
50
|
+
"@wix/services-manager-react": "^1.0.8",
|
|
51
|
+
"react": ">=18"
|
|
52
|
+
},
|
|
53
|
+
"wix": {
|
|
54
|
+
"artifact": {
|
|
55
|
+
"groupId": "com.wixpress.npm",
|
|
56
|
+
"artifactId": "pricing-plans-site-api"
|
|
57
|
+
},
|
|
58
|
+
"validations": {
|
|
59
|
+
"sourceOnly": [
|
|
60
|
+
"lint",
|
|
61
|
+
"fmt:check"
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"falconPackageHash": "9ad85277a4de2094e20da4400fed6f4e67a85c5dc4b70ea75c1f141a"
|
|
6
66
|
}
|