@base44-preview/sdk 0.8.53-pr.331.bf2d5aa → 0.8.54-pr.316.81dc6f9

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 CHANGED
@@ -29,6 +29,7 @@ The SDK provides access to Base44's functionality through the following modules:
29
29
  - **[`entities`](https://docs.base44.com/developers/references/sdk/docs/interfaces/entities)**: Work with your app's data entities using CRUD operations.
30
30
  - **[`functions`](https://docs.base44.com/developers/references/sdk/docs/interfaces/functions)**: Execute backend functions.
31
31
  - **[`integrations`](https://docs.base44.com/developers/references/sdk/docs/type-aliases/integrations)**: Access pre-built and third-party integrations.
32
+ - **[`payments`](https://docs.base44.com/developers/references/sdk/docs/interfaces/payments)**: Embed checkouts, look up purchases, and handle refunds and subscriptions with the service role.
32
33
 
33
34
  ## Quickstarts
34
35
 
package/dist/client.js CHANGED
@@ -14,6 +14,7 @@ import { createAppModule } from "./modules/app.js";
14
14
  import { createUsersModule } from "./modules/users.js";
15
15
  import { RoomsSocket } from "./utils/socket-utils.js";
16
16
  import { createAnalyticsModule } from "./modules/analytics.js";
17
+ import { createPaymentsModule, createServiceRolePaymentsModule, } from "./modules/payments.js";
17
18
  import { createActorsModule, resolveActorsHost, } from "./modules/actors.js";
18
19
  /**
19
20
  * Creates a Base44 client.
@@ -202,6 +203,7 @@ export function createClient(config) {
202
203
  appLogs: createAppLogsModule(axiosClient, appId),
203
204
  app: createAppModule(axiosClient, appId),
204
205
  users: createUsersModule(axiosClient, appId),
206
+ payments: createPaymentsModule(axiosClient, appId),
205
207
  analytics: createAnalyticsModule({
206
208
  axiosClient,
207
209
  serverUrl,
@@ -252,6 +254,7 @@ export function createClient(config) {
252
254
  headers: aiGatewayHeaders,
253
255
  }),
254
256
  appLogs: createAppLogsModule(serviceRoleAxiosClient, appId),
257
+ payments: createServiceRolePaymentsModule(serviceRoleAxiosClient, appId),
255
258
  cleanup: () => {
256
259
  if (socket) {
257
260
  socket.disconnect();
@@ -2,6 +2,7 @@ import type { EntitiesModule } from "./modules/entities.types.js";
2
2
  import type { IntegrationsModule } from "./modules/integrations.types.js";
3
3
  import type { AuthModule } from "./modules/auth.types.js";
4
4
  import type { SsoModule } from "./modules/sso.types.js";
5
+ import type { PaymentsModule, ServiceRolePaymentsModule } from "./modules/payments.types.js";
5
6
  import type { ConnectorsModule, UserConnectorsModule } from "./modules/connectors.types.js";
6
7
  import type { FunctionsModule } from "./modules/functions.types.js";
7
8
  import type { AgentsModule } from "./modules/agents.types.js";
@@ -138,6 +139,8 @@ export interface Base44Client {
138
139
  functions: FunctionsModule;
139
140
  /** {@link IntegrationsModule | Integrations module} for calling pre-built integration endpoints. */
140
141
  integrations: IntegrationsModule;
142
+ /** {@link PaymentsModule | Payments module} for checkouts and purchases. */
143
+ payments: PaymentsModule;
141
144
  /** Cleanup function to disconnect WebSocket connections. Call when you're done with the client. */
142
145
  cleanup: () => void;
143
146
  /**
@@ -233,6 +236,8 @@ export interface Base44Client {
233
236
  functions: FunctionsModule;
234
237
  /** {@link IntegrationsModule | Integrations module} with elevated permissions. */
235
238
  integrations: IntegrationsModule;
239
+ /** {@link ServiceRolePaymentsModule | Payments module} for creating checkouts, refunds, and subscription changes. */
240
+ payments: ServiceRolePaymentsModule;
236
241
  /** {@link SsoModule | SSO module} for generating SSO tokens. */
237
242
  sso: SsoModule;
238
243
  /** Cleanup function to disconnect WebSocket connections. */
package/dist/index.d.ts CHANGED
@@ -14,6 +14,7 @@ export type { AppLogsModule } from "./modules/app-logs.types.js";
14
14
  export type { AppModule, AppPublicSettings, AppPublicSettingsResponse, } from "./modules/app.types.js";
15
15
  export type { ActorsModule, ActorClient, ActorRef, Connection, ActorSubscription, ActorConnectOptions, ActorNameRegistry, ActorRegistry, } from "./modules/actors.types.js";
16
16
  export type { SsoModule, SsoAccessTokenResponse } from "./modules/sso.types.js";
17
+ export type { PaymentsModule, ServiceRolePaymentsModule, PaymentsErrorCode, PaymentsRecurringFrequency, CheckoutBuyer, CheckoutFreeTrial, CheckoutItem, CheckoutItemRecurring, CheckoutPresentation, CheckoutElement, CheckoutMount, MountableCheckout, CreateCheckoutParams, CreateCheckoutResult, CompleteCheckoutResult, EmbeddedCheckout, PayPalEmbeddedCheckout, StripeEmbeddedCheckout, MountCheckoutOptions, ListPurchasesParams, ServiceRoleListPurchasesParams, ListPurchasesResult, Purchase, PurchaseItem, PurchaseStatus, RefundParams, RefundResult, CancelSubscriptionOptions, SubscriptionResult, } from "./modules/payments.types.js";
17
18
  export { Actor, type Conn } from "./actor.js";
18
19
  export type { ConnectorsModule, UserConnectorsModule, ConnectorApiRequest, ConnectorApiResponse, ConnectorApiResponsePhase, } from "./modules/connectors.types.js";
19
20
  export type { CustomIntegrationsModule, CustomIntegrationCallParams, CustomIntegrationCallResponse, } from "./modules/custom-integrations.types.js";
@@ -1,49 +1,66 @@
1
1
  /**
2
- * The app's access policy: whether the app is reachable without an account, and
3
- * who may sign in.
2
+ * The app's [visibility](/Setting-up-your-app/Managing-access#setting-your-app-visibility).
3
+ *
4
+ * You can set and manage the app's visibility in the app's dashboard or with the Base44 CLI [`visibility`](/developers/references/cli/commands/visibility) command.
5
+ *
6
+ * The following table shows the supported values and the visibility each one represents:
7
+ *
8
+ * | Value | Visibility |
9
+ * | --- | --- |
10
+ * | `private_with_login` | **Private**: Only [invited users](/Setting-up-your-app/Managing-access#inviting-users-to-your-app) |
11
+ * | `workspace_with_login` | **Workspace**: Users in your Base44 [workspace](/documentation/account-and-billing/about-workspaces) |
12
+ * | `public_with_login` | **Public (login required)**: Visitors must log in |
13
+ * | `public_without_login` | **Public (no login)**: Anyone |
14
+ *
15
+ * <Note>
16
+ * If your workspace [enforces SSO for apps](/Enterprise/Enterprise-SSO-and-app-visibility), apps set to `public_without_login` also require sign-in.
17
+ * </Note>
4
18
  */
5
19
  export type AppPublicSettings = "private_with_login" | "public_with_login" | "public_without_login" | "workspace_with_login" | string;
6
20
  /**
7
- * The app's public configuration, as returned by {@link AppModule.getPublicSettings}.
21
+ * The app's public settings.
8
22
  */
9
23
  export interface AppPublicSettingsResponse {
10
24
  /** The app's ID. */
11
25
  id: string;
12
- /** The app's access policy. */
26
+ /**
27
+ * The app's visibility.
28
+ * For supported values, see [`AppPublicSettings`](#apppublicsettings).
29
+ */
13
30
  public_settings: AppPublicSettings;
14
31
  }
15
32
  /**
16
- * App module for reading the app's own public configuration.
33
+ * App module for reading an app's public settings.
34
+ *
35
+ * Use it to read the app's visibility and determine whether the current visitor can access the app.
36
+ * If the app requires login, use `redirectToLogin()` from the {@link AuthModule | auth module} to send the visitor to the login page.
17
37
  *
18
- * Use it to discover how the app is gated before rendering it, so a private app
19
- * can send the visitor to login instead of rendering an empty shell.
38
+ * Learn more about [app visibility](/Setting-up-your-app/Managing-access#setting-your-app-visibility).
20
39
  *
21
- * ## Authentication Modes
40
+ * ## Authentication modes
22
41
  *
23
- * This module is available to use with a client in all authentication modes. The
24
- * client's token, when it has one, is sent with the request — a signed-in visitor
25
- * who has no access to the app is reported differently from an anonymous one.
42
+ * You can use this module with a client in any authentication mode. The client sends its token, if it has one, with the request.
26
43
  */
27
44
  export interface AppModule {
28
45
  /**
29
- * Get the app's public configuration.
46
+ * Retrieves the app's public settings.
30
47
  *
31
- * Rejects with a {@linkcode Base44Error} when the visitor may not open the app:
32
- * `status` is `403` and `data.extra_data.reason` says why — `"auth_required"`
33
- * when the visitor must sign in, `"user_not_registered"` when the signed-in
34
- * visitor has no access to this app.
48
+ * Rejects with a {@linkcode Base44Error} that has a `status` of `403` when the visitor can't access the app.
49
+ * Check `data.extra_data.reason` to find the cause:
50
+ * - `auth_required`: The visitor must sign in.
51
+ * - `user_not_registered`: The visitor is signed in but doesn't have access.
35
52
  *
36
- * @returns Promise resolving to the app's ID and access policy.
53
+ * @returns Promise resolving to the app's ID and visibility.
37
54
  *
38
55
  * @example
39
56
  * ```typescript
40
- * // Decide what to render before the app boots
57
+ * // Redirect a visitor who isn't signed in
41
58
  * try {
42
59
  * const { public_settings } = await base44.app.getPublicSettings();
43
- * console.log('App access policy:', public_settings);
60
+ * console.log('App visibility:', public_settings);
44
61
  * } catch (error) {
45
- * if (error.status === 403) {
46
- * console.log('Blocked because:', error.data?.extra_data?.reason);
62
+ * if (error.status === 403 && error.data?.extra_data?.reason === 'auth_required') {
63
+ * base44.auth.redirectToLogin('<URL to return to after login>');
47
64
  * }
48
65
  * }
49
66
  * ```
@@ -0,0 +1,31 @@
1
+ import type { CheckoutElement, CheckoutMount, CompleteCheckoutResult, MountableCheckout, MountCheckoutOptions } from "./payments.types.js";
2
+ /** @internal */
3
+ export type LoadScript = (src: string) => Promise<void>;
4
+ /**
5
+ * The response of `GET /payments/config`.
6
+ * @internal
7
+ */
8
+ export interface PaymentsCheckoutConfig {
9
+ paypal?: {
10
+ script_url?: string;
11
+ };
12
+ stripe?: {
13
+ script_url?: string;
14
+ };
15
+ }
16
+ /** @internal */
17
+ export interface MountCheckoutDeps {
18
+ completeCheckout: (checkoutId: string) => Promise<CompleteCheckoutResult>;
19
+ getConfig: () => Promise<PaymentsCheckoutConfig>;
20
+ loadScript: LoadScript;
21
+ }
22
+ /**
23
+ * Appends a script tag once per URL and resolves when it loads.
24
+ * @internal
25
+ */
26
+ export declare const loadScript: LoadScript;
27
+ /**
28
+ * Mounts the embedded checkout that createCheckout() returned.
29
+ * @internal
30
+ */
31
+ export declare function mountCheckout(element: CheckoutElement, checkout: MountableCheckout, options: MountCheckoutOptions, deps: MountCheckoutDeps): Promise<CheckoutMount>;
@@ -0,0 +1,235 @@
1
+ // The backend picks each script URL, but the script runs on the buyer's page, so only these hosts may load.
2
+ const ALLOWED_SCRIPT_HOSTS = {
3
+ paypal: ["www.paypal.com", "www.sandbox.paypal.com"],
4
+ stripe: ["js.stripe.com"],
5
+ };
6
+ async function scriptUrlFor(provider, deps) {
7
+ var _a, _b;
8
+ const value = (_b = (_a = (await deps.getConfig())) === null || _a === void 0 ? void 0 : _a[provider]) === null || _b === void 0 ? void 0 : _b.script_url;
9
+ let url;
10
+ try {
11
+ url = value ? new URL(value) : undefined;
12
+ }
13
+ catch (_c) {
14
+ url = undefined;
15
+ }
16
+ if (!url ||
17
+ url.protocol !== "https:" ||
18
+ url.port !== "" ||
19
+ url.username !== "" ||
20
+ url.password !== "" ||
21
+ !ALLOWED_SCRIPT_HOSTS[provider].includes(url.hostname)) {
22
+ const name = provider === "paypal" ? "PayPal" : "Stripe";
23
+ throw new Error(`The payments config has no valid ${name} script URL`);
24
+ }
25
+ return url.href;
26
+ }
27
+ function browserGlobals() {
28
+ return window;
29
+ }
30
+ const scriptLoads = new Map();
31
+ /**
32
+ * Appends a script tag once per URL and resolves when it loads.
33
+ * @internal
34
+ */
35
+ export const loadScript = (src) => {
36
+ let load = scriptLoads.get(src);
37
+ if (!load) {
38
+ load = new Promise((resolve, reject) => {
39
+ const script = document.createElement("script");
40
+ script.src = src;
41
+ script.async = true;
42
+ script.onload = () => resolve();
43
+ script.onerror = () => {
44
+ // Forget the failure so a later mount can retry.
45
+ scriptLoads.delete(src);
46
+ script.remove();
47
+ reject(new Error(`Failed to load ${src}`));
48
+ };
49
+ document.head.appendChild(script);
50
+ });
51
+ scriptLoads.set(src, load);
52
+ }
53
+ return load;
54
+ };
55
+ async function loadGlobal(src, read, load) {
56
+ if (!read()) {
57
+ await load(src);
58
+ }
59
+ const value = read();
60
+ if (!value) {
61
+ throw new Error(`${src} loaded but didn't define its global`);
62
+ }
63
+ return value;
64
+ }
65
+ // Stripe asks for a single Stripe instance per key.
66
+ const stripeInstances = new Map();
67
+ // Stripe allows one Embedded Checkout at a time, so mounts run in order and replace the last.
68
+ let stripeQueue = Promise.resolve();
69
+ let activeStripeCheckout = null;
70
+ function mountStripe(element, scriptUrl, embedded, deps) {
71
+ const mount = stripeQueue.then(async () => {
72
+ var _a;
73
+ const Stripe = await loadGlobal(scriptUrl, () => browserGlobals().Stripe, deps.loadScript);
74
+ let stripe = stripeInstances.get(embedded.publishable_key);
75
+ if (!stripe) {
76
+ stripe = Stripe(embedded.publishable_key);
77
+ stripeInstances.set(embedded.publishable_key, stripe);
78
+ }
79
+ // Try the pre-dahlia name first, since dahlia and later reject the ui_mode Base44 sessions use.
80
+ // A newer Stripe.js that the app loaded itself is still tried rather than refused.
81
+ const create = (_a = stripe.initEmbeddedCheckout) !== null && _a !== void 0 ? _a : stripe.createEmbeddedCheckoutPage;
82
+ if (typeof create !== "function") {
83
+ throw new Error("Stripe.js doesn't support Embedded Checkout");
84
+ }
85
+ activeStripeCheckout === null || activeStripeCheckout === void 0 ? void 0 : activeStripeCheckout.destroy();
86
+ const checkout = await create.call(stripe, {
87
+ fetchClientSecret: () => Promise.resolve(embedded.client_secret),
88
+ });
89
+ try {
90
+ checkout.mount(element);
91
+ }
92
+ catch (error) {
93
+ checkout.destroy();
94
+ throw error;
95
+ }
96
+ let destroyed = false;
97
+ const handle = {
98
+ destroy() {
99
+ if (destroyed)
100
+ return;
101
+ destroyed = true;
102
+ if (activeStripeCheckout === handle)
103
+ activeStripeCheckout = null;
104
+ checkout.destroy();
105
+ },
106
+ };
107
+ activeStripeCheckout = handle;
108
+ return handle;
109
+ });
110
+ stripeQueue = mount.catch(() => undefined);
111
+ return mount;
112
+ }
113
+ async function mountPayPal(element, scriptUrl, checkoutId, currency, embedded, options, deps) {
114
+ var _a;
115
+ const paypal = await loadGlobal(scriptUrl, () => browserGlobals().paypal, deps.loadScript);
116
+ if (typeof paypal.createInstance !== "function") {
117
+ throw new Error("Another PayPal JS SDK version is already loaded on this page. Remove it to use mountCheckout().");
118
+ }
119
+ const subscription = embedded.intent === "subscription";
120
+ const sdk = await paypal.createInstance({
121
+ clientId: embedded.client_id,
122
+ merchantId: embedded.merchant_id,
123
+ // Sent only when set, since the backend returns null when no attribution ID is configured.
124
+ ...(embedded.partner_attribution_id
125
+ ? { partnerAttributionId: embedded.partner_attribution_id }
126
+ : {}),
127
+ components: [subscription ? "paypal-subscriptions" : "paypal-payments"],
128
+ pageType: "checkout",
129
+ });
130
+ const methods = await sdk.findEligibleMethods({
131
+ currencyCode: currency.toUpperCase(),
132
+ ...(subscription ? { paymentFlow: "RECURRING_PAYMENT" } : {}),
133
+ });
134
+ if (!methods.isEligible("paypal")) {
135
+ throw new Error("PayPal isn't available for this checkout");
136
+ }
137
+ const createSession = subscription
138
+ ? (_a = sdk.createPayPalSubscriptionPaymentSession) !== null && _a !== void 0 ? _a : sdk.createPayPalSubscriptionSession
139
+ : sdk.createPayPalOneTimePaymentSession;
140
+ if (typeof createSession !== "function") {
141
+ throw new Error("The loaded PayPal JS SDK can't create this checkout");
142
+ }
143
+ let destroyed = false;
144
+ let completing = false;
145
+ // App callbacks run only while mounted, and their own errors never reach PayPal or onError.
146
+ const notify = (callback) => {
147
+ if (destroyed)
148
+ return;
149
+ try {
150
+ callback();
151
+ }
152
+ catch (error) {
153
+ console.error("[Base44 SDK] A mountCheckout() callback threw", error);
154
+ }
155
+ };
156
+ const button = document.createElement("paypal-button");
157
+ const session = createSession.call(sdk, {
158
+ // Errors are reported here, not rethrown, so PayPal doesn't report them a second time.
159
+ async onApprove() {
160
+ completing = true;
161
+ button.disabled = true;
162
+ let result;
163
+ try {
164
+ // Runs even after destroy(), so an approved payment is still captured.
165
+ result = await deps.completeCheckout(checkoutId);
166
+ }
167
+ catch (error) {
168
+ // Re-enabled only on failure, since a paid order can't be approved again.
169
+ completing = false;
170
+ button.disabled = false;
171
+ notify(() => { var _a; return (_a = options.onError) === null || _a === void 0 ? void 0 : _a.call(options, error); });
172
+ return;
173
+ }
174
+ completing = false;
175
+ notify(() => { var _a; return (_a = options.onComplete) === null || _a === void 0 ? void 0 : _a.call(options, result); });
176
+ },
177
+ onCancel() {
178
+ notify(() => { var _a; return (_a = options.onCancel) === null || _a === void 0 ? void 0 : _a.call(options); });
179
+ },
180
+ onError(error) {
181
+ notify(() => { var _a; return (_a = options.onError) === null || _a === void 0 ? void 0 : _a.call(options, error); });
182
+ },
183
+ });
184
+ button.setAttribute("type", subscription ? "subscribe" : "pay");
185
+ const onClick = () => {
186
+ if (completing)
187
+ return;
188
+ // Not awaited before start(), which must run inside the click to open PayPal's popup.
189
+ const id = Promise.resolve(subscription ? { subscriptionId: checkoutId } : { orderId: checkoutId });
190
+ const report = (error) => notify(() => { var _a; return (_a = options.onError) === null || _a === void 0 ? void 0 : _a.call(options, error); });
191
+ try {
192
+ Promise.resolve(session.start({ presentationMode: "auto" }, id)).catch(report);
193
+ }
194
+ catch (error) {
195
+ report(error);
196
+ }
197
+ };
198
+ button.addEventListener("click", onClick);
199
+ element.appendChild(button);
200
+ return {
201
+ destroy() {
202
+ var _a;
203
+ if (destroyed)
204
+ return;
205
+ destroyed = true;
206
+ button.removeEventListener("click", onClick);
207
+ button.remove();
208
+ (_a = session.destroy) === null || _a === void 0 ? void 0 : _a.call(session);
209
+ },
210
+ };
211
+ }
212
+ /**
213
+ * Mounts the embedded checkout that createCheckout() returned.
214
+ * @internal
215
+ */
216
+ export async function mountCheckout(element, checkout, options, deps) {
217
+ if (typeof window === "undefined" || typeof document === "undefined") {
218
+ throw new Error("mountCheckout() requires a browser environment");
219
+ }
220
+ if (!element) {
221
+ throw new Error("mountCheckout() requires an element to render in");
222
+ }
223
+ const embedded = checkout === null || checkout === void 0 ? void 0 : checkout.embedded;
224
+ if (!embedded) {
225
+ throw new Error("This checkout isn't embedded. Create it with presentation: 'embedded', or send the buyer to its redirect_url.");
226
+ }
227
+ switch (embedded.provider) {
228
+ case "stripe":
229
+ return mountStripe(element, await scriptUrlFor("stripe", deps), embedded, deps);
230
+ case "paypal":
231
+ return mountPayPal(element, await scriptUrlFor("paypal", deps), checkout.checkout_id, checkout.currency, embedded, options, deps);
232
+ default:
233
+ throw new Error(`Embedded checkout isn't supported for provider ${embedded.provider}`);
234
+ }
235
+ }
@@ -0,0 +1,20 @@
1
+ import { AxiosInstance } from "axios";
2
+ import { PaymentsModule, ServiceRolePaymentsModule } from "./payments.types.js";
3
+ /**
4
+ * Creates the payments module for anonymous and user authentication.
5
+ *
6
+ * @param axios - Axios instance
7
+ * @param appId - Application ID
8
+ * @returns Payments module for the browser
9
+ * @internal
10
+ */
11
+ export declare function createPaymentsModule(axios: AxiosInstance, appId: string): PaymentsModule;
12
+ /**
13
+ * Creates the payments module for service role authentication.
14
+ *
15
+ * @param axios - Axios instance (should be service role client)
16
+ * @param appId - Application ID
17
+ * @returns Payments module for backend functions
18
+ * @internal
19
+ */
20
+ export declare function createServiceRolePaymentsModule(axios: AxiosInstance, appId: string): ServiceRolePaymentsModule;
@@ -0,0 +1,79 @@
1
+ import { loadScript, mountCheckout, } from "./payments-checkout.js";
2
+ function requireId(value, name) {
3
+ if (!value || typeof value !== "string") {
4
+ throw new Error(`${name} is required and must be a string`);
5
+ }
6
+ return encodeURIComponent(value);
7
+ }
8
+ function createPaymentsRequests(axios, appId) {
9
+ const base = `/apps/${appId}/payments`;
10
+ return {
11
+ async completeCheckout(checkoutId) {
12
+ const id = requireId(checkoutId, "Checkout ID");
13
+ return axios.post(`${base}/checkout-sessions/${id}/complete`);
14
+ },
15
+ async listPurchases(params = {}) {
16
+ return axios.get(`${base}/purchases`, { params });
17
+ },
18
+ async createCheckout(params) {
19
+ return axios.post(`${base}/checkout-sessions`, params);
20
+ },
21
+ async refund(params) {
22
+ return axios.post(`${base}/refunds`, params);
23
+ },
24
+ async cancelSubscription(subscriptionId, options = {}) {
25
+ const id = requireId(subscriptionId, "Subscription ID");
26
+ return axios.post(`${base}/subscriptions/${id}/cancel`, options);
27
+ },
28
+ async pauseSubscription(subscriptionId) {
29
+ const id = requireId(subscriptionId, "Subscription ID");
30
+ return axios.post(`${base}/subscriptions/${id}/pause`);
31
+ },
32
+ async resumeSubscription(subscriptionId) {
33
+ const id = requireId(subscriptionId, "Subscription ID");
34
+ return axios.post(`${base}/subscriptions/${id}/resume`);
35
+ },
36
+ };
37
+ }
38
+ /**
39
+ * Creates the payments module for anonymous and user authentication.
40
+ *
41
+ * @param axios - Axios instance
42
+ * @param appId - Application ID
43
+ * @returns Payments module for the browser
44
+ * @internal
45
+ */
46
+ export function createPaymentsModule(axios, appId) {
47
+ const requests = createPaymentsRequests(axios, appId);
48
+ // One fetch per client, retried on the next mount if it fails.
49
+ let config;
50
+ const getConfig = () => {
51
+ config !== null && config !== void 0 ? config : (config = axios.get(`/apps/${appId}/payments/config`).catch((error) => {
52
+ config = undefined;
53
+ throw error;
54
+ }));
55
+ return config;
56
+ };
57
+ return {
58
+ completeCheckout: requests.completeCheckout,
59
+ listPurchases: requests.listPurchases,
60
+ mountCheckout(element, checkout, options = {}) {
61
+ return mountCheckout(element, checkout, options, {
62
+ completeCheckout: requests.completeCheckout,
63
+ getConfig,
64
+ loadScript,
65
+ });
66
+ },
67
+ };
68
+ }
69
+ /**
70
+ * Creates the payments module for service role authentication.
71
+ *
72
+ * @param axios - Axios instance (should be service role client)
73
+ * @param appId - Application ID
74
+ * @returns Payments module for backend functions
75
+ * @internal
76
+ */
77
+ export function createServiceRolePaymentsModule(axios, appId) {
78
+ return createPaymentsRequests(axios, appId);
79
+ }
@@ -0,0 +1,626 @@
1
+ /**
2
+ * How often a recurring item bills.
3
+ */
4
+ export type PaymentsRecurringFrequency = "day" | "week" | "month" | "year";
5
+ /**
6
+ * Free trial before the first charge of a recurring item.
7
+ */
8
+ export interface CheckoutFreeTrial {
9
+ /** Unit of the trial length. Stripe supports only `'day'` and `'week'`. */
10
+ frequency: PaymentsRecurringFrequency;
11
+ /** Number of `frequency` units the trial lasts, from 1 to 999. */
12
+ interval: number;
13
+ }
14
+ /**
15
+ * Billing terms that turn a checkout item into a subscription.
16
+ */
17
+ export interface CheckoutItemRecurring {
18
+ /** Unit of the billing period. */
19
+ frequency: PaymentsRecurringFrequency;
20
+ /** Number of `frequency` units between charges, from 1 to 3650. Defaults to `1`. */
21
+ interval?: number;
22
+ /** Whether the subscription renews automatically at the end of its billing cycles. Stripe doesn't support `false`. */
23
+ auto_renewal?: boolean;
24
+ /** Number of billing cycles before the subscription ends, at least 1. Omit for no limit. Not supported for Stripe. */
25
+ billing_cycles?: number;
26
+ /** Free trial before the first charge. Stripe applies one trial to the whole checkout, so every recurring item must request the same trial. */
27
+ free_trial?: CheckoutFreeTrial;
28
+ /** Subscription title shown to the buyer, up to 150 characters. */
29
+ title?: string;
30
+ /** Subscription description shown to the buyer, up to 500 characters. */
31
+ description?: string;
32
+ }
33
+ /**
34
+ * A line item in a checkout.
35
+ */
36
+ export interface CheckoutItem {
37
+ /** Item name shown to the buyer, 1 to 255 characters. */
38
+ name: string;
39
+ /** Unit price in the currency's minor units, such as cents. Must be greater than `0`. Providers may also reject a total below their minimum charge. */
40
+ amount: number;
41
+ /** Number of units, from 1 to 1,000,000. Defaults to `1`. */
42
+ quantity?: number;
43
+ /** Whether the item ships, so the provider collects a shipping address. */
44
+ shippable?: boolean;
45
+ /** Billing terms. Set this to sell the item as a subscription. PayPal and Wix Payments allow one recurring item per checkout, and PayPal allows no other items with it. */
46
+ recurring?: CheckoutItemRecurring;
47
+ }
48
+ /**
49
+ * Details about the buyer, used to prefill the checkout. Every field is optional.
50
+ */
51
+ export interface CheckoutBuyer {
52
+ /** Buyer's email address. */
53
+ email?: string;
54
+ /** Buyer's first name. */
55
+ first_name?: string;
56
+ /** Buyer's last name. */
57
+ last_name?: string;
58
+ /** Buyer's phone number. */
59
+ phone?: string;
60
+ }
61
+ /**
62
+ * How the buyer pays.
63
+ *
64
+ * - `'redirect'`: The buyer leaves the app for the provider's hosted checkout page at `redirect_url`.
65
+ * - `'embedded'`: The checkout renders inside the app with [`mountCheckout()`](/developers/references/sdk/docs/interfaces/payments#mountcheckout).
66
+ */
67
+ export type CheckoutPresentation = "redirect" | "embedded";
68
+ /**
69
+ * Parameters for [`createCheckout()`](/developers/references/sdk/docs/interfaces/service-role-payments#createcheckout).
70
+ */
71
+ export interface CreateCheckoutParams {
72
+ /** Items to sell, 1 to 100 entries. */
73
+ items: CheckoutItem[];
74
+ /** Three-letter ISO currency code, such as `'usd'`. Defaults to the payment account's currency. Required for PayPal. */
75
+ currency?: string;
76
+ /** App-relative path the buyer returns to after paying, such as `'/thank-you'`. Must start with `/`. */
77
+ success_path: string;
78
+ /** App-relative path the buyer returns to after cancelling. Must start with `/`. Not supported for Stripe embedded checkout. */
79
+ cancel_path?: string;
80
+ /** Details about the buyer, used to prefill the checkout. */
81
+ buyer?: CheckoutBuyer;
82
+ /** Your own key-value pairs, up to 50 keys. Returned in each purchase's `metadata` to service role callers only. Not supported for PayPal. */
83
+ metadata?: Record<string, string>;
84
+ /** Key that makes retries return the same checkout instead of creating another, 1 to 200 characters. Not supported for Wix Payments or PayPal subscriptions. */
85
+ idempotency_key?: string;
86
+ /** How the buyer pays. Defaults to `'redirect'`. PayPal apps must pass `'embedded'`, and Wix Payments supports only `'redirect'`. */
87
+ presentation?: CheckoutPresentation;
88
+ }
89
+ /**
90
+ * Data that Stripe Embedded Checkout needs to render.
91
+ */
92
+ export interface StripeEmbeddedCheckout {
93
+ /** Payment provider. */
94
+ provider: "stripe";
95
+ /** Stripe publishable key of the app's payment account. */
96
+ publishable_key: string;
97
+ /** Client secret of the Stripe Checkout Session. */
98
+ client_secret: string;
99
+ }
100
+ /**
101
+ * Data that the PayPal button needs to render.
102
+ */
103
+ export interface PayPalEmbeddedCheckout {
104
+ /** Payment provider. */
105
+ provider: "paypal";
106
+ /** PayPal client ID. */
107
+ client_id: string;
108
+ /** PayPal merchant ID of the app's seller account. */
109
+ merchant_id: string;
110
+ /** PayPal partner attribution ID, when set. */
111
+ partner_attribution_id?: string | null;
112
+ /** Whether the checkout is a one-time payment or a subscription. */
113
+ intent: "capture" | "subscription";
114
+ }
115
+ /**
116
+ * Data that an embedded checkout needs to render, by provider.
117
+ */
118
+ export type EmbeddedCheckout = StripeEmbeddedCheckout | PayPalEmbeddedCheckout;
119
+ /**
120
+ * Result of [`createCheckout()`](/developers/references/sdk/docs/interfaces/service-role-payments#createcheckout).
121
+ *
122
+ * Exactly one of `redirect_url` and `embedded` is set, depending on the checkout's `presentation`.
123
+ */
124
+ export interface CreateCheckoutResult {
125
+ /** ID of the checkout. For Stripe it's the Checkout Session ID, and for PayPal it's the PayPal order or subscription ID. */
126
+ checkout_id: string;
127
+ /** Three-letter ISO currency code of the checkout, in lowercase. */
128
+ currency: string;
129
+ /** Amount the buyer pays now, in minor units. It's `0` for a subscription that starts with a free trial. */
130
+ amount_total: number;
131
+ /** Absolute URL of the provider's hosted checkout page, for redirect checkouts. */
132
+ redirect_url: string | null;
133
+ /** Data for [`mountCheckout()`](/developers/references/sdk/docs/interfaces/payments#mountcheckout), for embedded checkouts. */
134
+ embedded: EmbeddedCheckout | null;
135
+ }
136
+ /**
137
+ * Result of [`completeCheckout()`](/developers/references/sdk/docs/interfaces/payments#completecheckout).
138
+ */
139
+ export interface CompleteCheckoutResult {
140
+ /** ID of the checkout. */
141
+ checkout_id: string;
142
+ /** Whether the payment went through or the provider is still processing it. */
143
+ status: "paid" | "pending";
144
+ }
145
+ /**
146
+ * Status of a purchase.
147
+ *
148
+ * - `'pending'`: The buyer hasn't finished paying, or the provider is still processing the payment.
149
+ * - `'paid'`: The payment went through.
150
+ * - `'refunded'`: The payment was refunded.
151
+ * - `'canceled'`: The checkout or subscription was canceled.
152
+ * - `'failed'`: The payment failed.
153
+ * - `'unknown'`: The provider reported a state that doesn't map to the others.
154
+ */
155
+ export type PurchaseStatus = "pending" | "paid" | "refunded" | "canceled" | "failed" | "unknown";
156
+ /**
157
+ * A line item of a purchase.
158
+ */
159
+ export interface PurchaseItem {
160
+ /** Item name. */
161
+ name: string;
162
+ /** Unit price in minor units, when the provider reports it. */
163
+ amount?: number | null;
164
+ /** Number of units, when the provider reports it. */
165
+ quantity?: number | null;
166
+ }
167
+ /**
168
+ * A completed or in-progress checkout.
169
+ */
170
+ export interface Purchase {
171
+ /** ID of the checkout the purchase came from. */
172
+ checkout_id: string;
173
+ /** Status of the purchase. */
174
+ status: PurchaseStatus;
175
+ /** Line items of the purchase. */
176
+ items: PurchaseItem[];
177
+ /** Total amount in minor units. */
178
+ amount_total?: number | null;
179
+ /** Three-letter ISO currency code, in lowercase. */
180
+ currency?: string | null;
181
+ /** Buyer's email address. */
182
+ buyer_email?: string | null;
183
+ /** The metadata passed to [`createCheckout()`](/developers/references/sdk/docs/interfaces/service-role-payments#createcheckout). Always `null` unless you call with the service role. */
184
+ metadata?: Record<string, string> | null;
185
+ /** ID of the subscription, for recurring purchases. */
186
+ subscription_id?: string | null;
187
+ /** ID of the payment to pass to [`refund()`](/developers/references/sdk/docs/interfaces/service-role-payments#refund). It's `null` where the provider doesn't name a single payment, such as PayPal subscriptions. */
188
+ payment_id?: string | null;
189
+ }
190
+ /**
191
+ * Parameters for [`listPurchases()`](/developers/references/sdk/docs/interfaces/payments#listpurchases).
192
+ */
193
+ export interface ListPurchasesParams {
194
+ /** Only return the purchase from this checkout. */
195
+ checkout_id?: string;
196
+ /** Only return purchases of this subscription. */
197
+ subscription_id?: string;
198
+ /** Maximum number of purchases to return, from 1 to 100. Defaults to `20`. */
199
+ limit?: number;
200
+ /** The `next_cursor` from the previous page. Send the same filters and `limit` as that call. */
201
+ cursor?: string;
202
+ }
203
+ /**
204
+ * Parameters for [`listPurchases()`](/developers/references/sdk/docs/interfaces/service-role-payments#listpurchases) with the service role.
205
+ */
206
+ export interface ServiceRoleListPurchasesParams extends ListPurchasesParams {
207
+ /** Only return purchases made with this email address. */
208
+ buyer_email?: string;
209
+ }
210
+ /**
211
+ * A page of purchases.
212
+ */
213
+ export interface ListPurchasesResult {
214
+ /** Purchases on this page. */
215
+ items: Purchase[];
216
+ /** Cursor for the next page, or `null` on the last page. */
217
+ next_cursor: string | null;
218
+ }
219
+ /**
220
+ * Parameters for [`refund()`](/developers/references/sdk/docs/interfaces/service-role-payments#refund).
221
+ */
222
+ export interface RefundParams {
223
+ /** The `payment_id` of the purchase to refund. */
224
+ payment_id: string;
225
+ /** Amount to refund in minor units. Must be greater than `0`. */
226
+ amount: number;
227
+ /** Key that makes retries return the original refund instead of refunding twice, 1 to 100 characters. */
228
+ idempotency_key: string;
229
+ }
230
+ /**
231
+ * Result of [`refund()`](/developers/references/sdk/docs/interfaces/service-role-payments#refund).
232
+ */
233
+ export interface RefundResult {
234
+ /** Provider's ID of the refund. */
235
+ refund_id: string;
236
+ /** ID of the refunded payment. */
237
+ payment_id: string;
238
+ /** Refunded amount in minor units. */
239
+ amount: number;
240
+ /** Three-letter ISO currency code, in lowercase. */
241
+ currency: string;
242
+ /** Whether the refund went through or the provider is still processing it. */
243
+ status: "succeeded" | "pending";
244
+ }
245
+ /**
246
+ * Options for [`cancelSubscription()`](/developers/references/sdk/docs/interfaces/service-role-payments#cancelsubscription).
247
+ */
248
+ export interface CancelSubscriptionOptions {
249
+ /** Cancel at the end of the current billing period instead of now. Defaults to `false`. Not supported for PayPal. */
250
+ at_period_end?: boolean;
251
+ }
252
+ /**
253
+ * State of a subscription after a change.
254
+ */
255
+ export interface SubscriptionResult {
256
+ /** ID of the subscription. */
257
+ subscription_id: string;
258
+ /** Current status of the subscription. */
259
+ status: "active" | "past_due" | "paused" | "canceled";
260
+ /** Whether the subscription is set to cancel at the end of the current billing period. */
261
+ cancel_at_period_end: boolean;
262
+ }
263
+ /**
264
+ * Error codes that payments methods reject with, in the `code` of a `Base44Error`.
265
+ *
266
+ * Some notable ones:
267
+ *
268
+ * - `payment_declined`: The buyer's payment method was declined when completing the checkout. The buyer can try again with another method.
269
+ * - `refund_failed`: The refund's outcome is unknown. Retry with the same `idempotency_key` and `amount`, which returns the original refund if one was made.
270
+ * - `refund_in_progress`: A refund with the same `idempotency_key` is still running.
271
+ * - `refund_amount_mismatch`: The `idempotency_key` was already used for a different amount.
272
+ * - `rate_limited`: Too many requests. Wait for the number of seconds in the `Retry-After` response header, available at `error.originalError.response.headers['retry-after']`.
273
+ * - `refund_not_supported`, `subscription_action_not_supported`, `complete_not_supported`, `presentation_not_supported`: The app's payment provider doesn't support the operation.
274
+ */
275
+ export type PaymentsErrorCode = "forbidden" | "unauthenticated" | "not_found" | "conflict" | "rate_limited" | "payments_not_connected" | "invalid_request" | "invalid_items" | "amount_below_minimum" | "currency_mismatch" | "currency_required" | "invalid_metadata" | "idempotency_not_supported" | "idempotency_key_reused" | "multiple_subscription_items" | "recurring_option_not_supported" | "invalid_cursor" | "presentation_not_supported" | "embedded_checkout_unavailable" | "checkout_not_found" | "checkout_not_approved" | "checkout_closed" | "payment_declined" | "complete_not_supported" | "provider_unavailable" | "payment_not_found" | "refund_rejected" | "refund_in_progress" | "refund_amount_mismatch" | "refund_temporarily_unavailable" | "refund_failed" | "refund_not_supported" | "subscription_not_found" | "subscription_not_active" | "subscription_action_not_supported";
276
+ /**
277
+ * Callbacks for [`mountCheckout()`](/developers/references/sdk/docs/interfaces/payments#mountcheckout).
278
+ */
279
+ export interface MountCheckoutOptions {
280
+ /**
281
+ * Called after the buyer approves a PayPal payment and the SDK completes the checkout.
282
+ *
283
+ * Use it to update the page, not to grant access. Browser callbacks can be faked, so unlock paid content only after your backend confirms the purchase, for example with [`listPurchases()`](/developers/references/sdk/docs/interfaces/service-role-payments#listpurchases) in a backend function.
284
+ *
285
+ * Stripe doesn't call this. After a Stripe payment, the buyer returns to the checkout's `success_path` with a `session_id` query parameter. That parameter is the `checkout_id`. Show the result there with [`listPurchases()`](/developers/references/sdk/docs/interfaces/payments#listpurchases), and grant access only after your backend confirms it.
286
+ */
287
+ onComplete?: (result: CompleteCheckoutResult) => void;
288
+ /** Called when the buyer closes the PayPal window without paying. The button stays mounted so the buyer can try again. */
289
+ onCancel?: () => void;
290
+ /**
291
+ * Called when a PayPal checkout fails after it's mounted, including a declined payment with the `payment_declined` code. The button stays mounted so the buyer can try again. If it keeps failing with `payment_declined`, create a new checkout and mount it. Stripe shows its own errors inside the checkout.
292
+ *
293
+ * The error is a `Base44Error` when completing the checkout fails, or PayPal's own error object when the PayPal window fails.
294
+ */
295
+ onError?: (error: unknown) => void;
296
+ }
297
+ /**
298
+ * An element on the page to render the checkout in, such as the result of `document.getElementById()`.
299
+ *
300
+ * Typed structurally so that the SDK's types don't require the DOM library in backend code.
301
+ */
302
+ export interface CheckoutElement {
303
+ /** Present on every DOM element. */
304
+ appendChild(node: never): unknown;
305
+ }
306
+ /**
307
+ * The fields of a {@linkcode CreateCheckoutResult} that [`mountCheckout()`](/developers/references/sdk/docs/interfaces/payments#mountcheckout) reads.
308
+ */
309
+ export interface MountableCheckout {
310
+ /** ID of the checkout. */
311
+ checkout_id: string;
312
+ /** Three-letter ISO currency code of the checkout. */
313
+ currency: string;
314
+ /** Data for the embedded checkout. Mounting rejects when it's `null`. */
315
+ embedded: EmbeddedCheckout | null;
316
+ }
317
+ /**
318
+ * A mounted embedded checkout.
319
+ */
320
+ export interface CheckoutMount {
321
+ /** Removes the checkout from the page. Call it when the component that holds it unmounts. For Stripe, mounting another checkout destroys this one for you. */
322
+ destroy: () => void;
323
+ }
324
+ /**
325
+ * Payments module for selling from the app through its connected payment provider.
326
+ *
327
+ * This module provides:
328
+ * - An embedded checkout that renders in any framework, with a PayPal button or Stripe Embedded Checkout
329
+ * - Completing PayPal checkouts
330
+ * - Lookup of the signed-in user's purchases
331
+ *
332
+ * Creating checkouts, refunds, and subscription changes are in {@linkcode ServiceRolePaymentsModule}.
333
+ *
334
+ * Each app connects one provider, which is Stripe, PayPal, or Wix Payments. Amounts are always integers in the currency's minor units, such as cents.
335
+ *
336
+ * | | Stripe | PayPal | Wix Payments |
337
+ * | :- | :- | :- | :- |
338
+ * | Redirect checkout | Yes | No | Yes |
339
+ * | Embedded checkout | Yes | Yes, the only mode | No |
340
+ * | Refunds | Yes | Yes | No |
341
+ * | Cancel a subscription | Yes, now or at period end | Yes, now only | No |
342
+ * | Pause and resume a subscription | Yes | Yes | No |
343
+ *
344
+ * ## Authentication modes
345
+ *
346
+ * - **Anonymous or user authentication** (`base44.payments`): Completes checkouts, mounts embedded checkouts, and lists the signed-in user's own purchases.
347
+ * - **Service role authentication** (`base44.asServiceRole.payments`): Creates checkouts, lists any purchase, refunds payments, and changes subscriptions. Use it in backend functions.
348
+ *
349
+ * Failed requests reject with a `Base44Error` whose `code` is one of the {@linkcode PaymentsErrorCode} values. Invalid arguments and [`mountCheckout()`](#mountcheckout) failures reject with a plain `Error`.
350
+ */
351
+ export interface PaymentsModule {
352
+ /**
353
+ * Completes a PayPal checkout after the buyer approves it.
354
+ *
355
+ * When you use {@linkcode mountCheckout | mountCheckout()}, it calls this for you. Call it yourself only if you render the PayPal button yourself. Calling it again for the same checkout returns the same result.
356
+ *
357
+ * Only PayPal checkouts need completing. Other providers reject with `complete_not_supported`.
358
+ *
359
+ * @param checkoutId - The `checkout_id` from [`createCheckout()`](/developers/references/sdk/docs/interfaces/service-role-payments#createcheckout).
360
+ * @returns Promise resolving to the checkout's payment status.
361
+ *
362
+ * @example
363
+ * ```typescript
364
+ * // Complete a PayPal checkout in your own onApprove handler
365
+ * const { status } = await base44.payments.completeCheckout(checkoutId);
366
+ * if (status === 'paid') {
367
+ * showThankYou();
368
+ * }
369
+ * ```
370
+ */
371
+ completeCheckout(checkoutId: string): Promise<CompleteCheckoutResult>;
372
+ /**
373
+ * Lists the signed-in user's purchases.
374
+ *
375
+ * Only purchases made with the user's own email address are returned. Rejects with `forbidden` when no user is signed in. PayPal apps must pass `checkout_id` or `subscription_id`, and get at most one purchase.
376
+ *
377
+ * @param params - Optional filters and pagination.
378
+ * @returns Promise resolving to a page of purchases.
379
+ *
380
+ * @example
381
+ * ```typescript
382
+ * // Check a purchase on the success page
383
+ * const { items } = await base44.payments.listPurchases({ checkout_id: checkoutId });
384
+ * const paid = items[0]?.status === 'paid';
385
+ * ```
386
+ *
387
+ * @example
388
+ * ```typescript
389
+ * // Page through the user's purchases
390
+ * let cursor: string | undefined;
391
+ * do {
392
+ * const page = await base44.payments.listPurchases({ limit: 50, cursor });
393
+ * page.items.forEach((purchase) => console.log(purchase.checkout_id, purchase.status));
394
+ * cursor = page.next_cursor ?? undefined;
395
+ * } while (cursor);
396
+ * ```
397
+ */
398
+ listPurchases(params?: ListPurchasesParams): Promise<ListPurchasesResult>;
399
+ /**
400
+ * Renders an embedded checkout inside an element on the page.
401
+ *
402
+ * Pass the result of a [`createCheckout()`](/developers/references/sdk/docs/interfaces/service-role-payments#createcheckout) call made with `presentation: 'embedded'`. Because only the service role can create checkouts, create it in a backend function and return the result to the page.
403
+ *
404
+ * The SDK loads the provider's script and renders the checkout for you:
405
+ * - PayPal: Renders a PayPal button. When the buyer approves the payment, the SDK calls {@linkcode completeCheckout | completeCheckout()} and then `onComplete`.
406
+ * - Stripe: Mounts Stripe Embedded Checkout. After paying, the buyer returns to the checkout's `success_path`. Stripe allows one embedded checkout per page, so mounting another Stripe checkout destroys the previous one.
407
+ *
408
+ * Requires a browser environment and can't be used in the backend. Rejects when the checkout isn't embedded, when the payments config can't be fetched or has no valid script URL for the provider, when the provider's script fails to load, or when PayPal isn't available for the checkout's currency.
409
+ *
410
+ * The checkout's callbacks run in the buyer's browser, where they can be faked. Grant access to what the buyer paid for only after your backend confirms the purchase, for example with [`listPurchases()`](/developers/references/sdk/docs/interfaces/service-role-payments#listpurchases) in a backend function.
411
+ *
412
+ * @param element - Element to render the checkout in. It should be empty.
413
+ * @param checkout - The result of [`createCheckout()`](/developers/references/sdk/docs/interfaces/service-role-payments#createcheckout).
414
+ * @param options - Callbacks for the checkout's outcome.
415
+ * @returns Promise resolving to the mounted checkout once it renders.
416
+ *
417
+ * @example
418
+ * ```typescript
419
+ * // Create the checkout in a backend function, then mount it
420
+ * const { data: checkout } = await base44.functions.invoke('createCheckout', { planId });
421
+ *
422
+ * const mount = await base44.payments.mountCheckout(
423
+ * document.getElementById('checkout')!,
424
+ * checkout,
425
+ * {
426
+ * onComplete: ({ status }) => navigate(status === 'paid' ? '/thank-you' : '/processing'),
427
+ * onError: () => setError('The payment failed. Please try again.'),
428
+ * }
429
+ * );
430
+ *
431
+ * // Later, when the page unmounts
432
+ * mount.destroy();
433
+ * ```
434
+ *
435
+ * @example
436
+ * ```tsx
437
+ * // React component
438
+ * function Checkout({ checkout }: { checkout: CreateCheckoutResult }) {
439
+ * const ref = useRef<HTMLDivElement>(null);
440
+ *
441
+ * useEffect(() => {
442
+ * let mount: CheckoutMount | undefined;
443
+ * let cancelled = false;
444
+ * base44.payments
445
+ * .mountCheckout(ref.current!, checkout, { onComplete: () => navigate('/thank-you') })
446
+ * .then((m) => (cancelled ? m.destroy() : (mount = m)))
447
+ * .catch(() => setError('The checkout could not load.'));
448
+ * return () => {
449
+ * cancelled = true;
450
+ * mount?.destroy();
451
+ * };
452
+ * }, [checkout]);
453
+ *
454
+ * return <div ref={ref} />;
455
+ * }
456
+ * ```
457
+ */
458
+ mountCheckout(element: CheckoutElement, checkout: MountableCheckout, options?: MountCheckoutOptions): Promise<CheckoutMount>;
459
+ }
460
+ /**
461
+ * Payments module with service role authentication, for backend functions.
462
+ *
463
+ * Creates checkouts, lists any purchase, refunds payments, and changes subscriptions. See {@linkcode PaymentsModule} for which providers support which operations.
464
+ *
465
+ * Refunds take a required `idempotency_key` and checkouts take an optional one, so retrying them doesn't charge or refund twice. Repeating a subscription change returns the subscription's current state.
466
+ */
467
+ export interface ServiceRolePaymentsModule {
468
+ /**
469
+ * Creates a checkout.
470
+ *
471
+ * For a redirect checkout, send the buyer to the returned `redirect_url`. For an embedded checkout, return the result to the page and pass it to [`mountCheckout()`](/developers/references/sdk/docs/interfaces/payments#mountcheckout).
472
+ *
473
+ * A checkout with a `recurring` item creates a subscription.
474
+ *
475
+ * @param params - Items, return paths, and options for the checkout.
476
+ * @returns Promise resolving to the checkout.
477
+ *
478
+ * @example
479
+ * ```typescript
480
+ * // Create a redirect checkout in a backend function
481
+ * import { createClientFromRequest } from 'npm:@base44/sdk';
482
+ *
483
+ * Deno.serve(async (req) => {
484
+ * const base44 = createClientFromRequest(req);
485
+ * const checkout = await base44.asServiceRole.payments.createCheckout({
486
+ * items: [{ name: 'T-shirt', amount: 2500, quantity: 2, shippable: true }],
487
+ * currency: 'usd',
488
+ * success_path: '/thank-you',
489
+ * cancel_path: '/cart',
490
+ * });
491
+ *
492
+ * return Response.json({ url: checkout.redirect_url });
493
+ * });
494
+ * ```
495
+ *
496
+ * @example
497
+ * ```typescript
498
+ * // Create an embedded subscription checkout for the signed-in user
499
+ * import { createClientFromRequest } from 'npm:@base44/sdk';
500
+ *
501
+ * Deno.serve(async (req) => {
502
+ * const base44 = createClientFromRequest(req);
503
+ * const user = await base44.auth.me();
504
+ * const checkout = await base44.asServiceRole.payments.createCheckout({
505
+ * items: [{
506
+ * name: 'Pro plan',
507
+ * amount: 1500,
508
+ * recurring: { frequency: 'month', free_trial: { frequency: 'day', interval: 14 } },
509
+ * }],
510
+ * currency: 'usd',
511
+ * success_path: '/welcome',
512
+ * buyer: { email: user.email },
513
+ * presentation: 'embedded',
514
+ * });
515
+ *
516
+ * // Pass this to base44.payments.mountCheckout() on the page
517
+ * return Response.json(checkout);
518
+ * });
519
+ * ```
520
+ */
521
+ createCheckout(params: CreateCheckoutParams): Promise<CreateCheckoutResult>;
522
+ /**
523
+ * Completes a PayPal checkout after the buyer approves it.
524
+ *
525
+ * See [`base44.payments.completeCheckout()`](/developers/references/sdk/docs/interfaces/payments#completecheckout).
526
+ *
527
+ * @param checkoutId - The `checkout_id` from {@linkcode createCheckout | createCheckout()}.
528
+ * @returns Promise resolving to the checkout's payment status.
529
+ *
530
+ * @example
531
+ * ```typescript
532
+ * // Complete a PayPal checkout from a backend function
533
+ * const { status } = await base44.asServiceRole.payments.completeCheckout(checkoutId);
534
+ * ```
535
+ */
536
+ completeCheckout(checkoutId: string): Promise<CompleteCheckoutResult>;
537
+ /**
538
+ * Lists the app's purchases.
539
+ *
540
+ * Unlike [`base44.payments.listPurchases()`](/developers/references/sdk/docs/interfaces/payments#listpurchases), it returns any buyer's purchases and includes their `metadata`. PayPal apps must pass `checkout_id` or `subscription_id`, and get at most one purchase.
541
+ *
542
+ * @param params - Optional filters and pagination.
543
+ * @returns Promise resolving to a page of purchases.
544
+ *
545
+ * @example
546
+ * ```typescript
547
+ * // Find a buyer's purchases
548
+ * const { items } = await base44.asServiceRole.payments.listPurchases({
549
+ * buyer_email: 'buyer@example.com',
550
+ * });
551
+ * ```
552
+ */
553
+ listPurchases(params?: ServiceRoleListPurchasesParams): Promise<ListPurchasesResult>;
554
+ /**
555
+ * Refunds all or part of a payment.
556
+ *
557
+ * Retrying with the same `idempotency_key` and `amount` returns the original refund instead of refunding twice. When the call rejects with `refund_failed`, the outcome is unknown, so retry it with the same values.
558
+ *
559
+ * Not supported for Wix Payments.
560
+ *
561
+ * @param params - The payment, amount, and idempotency key.
562
+ * @returns Promise resolving to the refund.
563
+ *
564
+ * @example
565
+ * ```typescript
566
+ * // Refund a purchase in full
567
+ * const { items } = await base44.asServiceRole.payments.listPurchases({ checkout_id: checkoutId });
568
+ * const purchase = items[0];
569
+ *
570
+ * const refund = await base44.asServiceRole.payments.refund({
571
+ * payment_id: purchase.payment_id!,
572
+ * amount: purchase.amount_total!,
573
+ * idempotency_key: `refund-${checkoutId}`,
574
+ * });
575
+ * ```
576
+ */
577
+ refund(params: RefundParams): Promise<RefundResult>;
578
+ /**
579
+ * Cancels a subscription, now or at the end of the current billing period.
580
+ *
581
+ * Not supported for Wix Payments. PayPal supports cancelling now only.
582
+ *
583
+ * @param subscriptionId - The `subscription_id` of the purchase.
584
+ * @param options - When to cancel.
585
+ * @returns Promise resolving to the subscription's state.
586
+ *
587
+ * @example
588
+ * ```typescript
589
+ * // Cancel at the end of the billing period
590
+ * await base44.asServiceRole.payments.cancelSubscription(subscriptionId, {
591
+ * at_period_end: true,
592
+ * });
593
+ * ```
594
+ */
595
+ cancelSubscription(subscriptionId: string, options?: CancelSubscriptionOptions): Promise<SubscriptionResult>;
596
+ /**
597
+ * Pauses a subscription's billing.
598
+ *
599
+ * Not supported for Wix Payments.
600
+ *
601
+ * @param subscriptionId - The `subscription_id` of the purchase.
602
+ * @returns Promise resolving to the subscription's state.
603
+ *
604
+ * @example
605
+ * ```typescript
606
+ * // Pause a subscription
607
+ * await base44.asServiceRole.payments.pauseSubscription(subscriptionId);
608
+ * ```
609
+ */
610
+ pauseSubscription(subscriptionId: string): Promise<SubscriptionResult>;
611
+ /**
612
+ * Resumes a paused subscription's billing.
613
+ *
614
+ * Not supported for Wix Payments.
615
+ *
616
+ * @param subscriptionId - The `subscription_id` of the purchase.
617
+ * @returns Promise resolving to the subscription's state.
618
+ *
619
+ * @example
620
+ * ```typescript
621
+ * // Resume a paused subscription
622
+ * await base44.asServiceRole.payments.resumeSubscription(subscriptionId);
623
+ * ```
624
+ */
625
+ resumeSubscription(subscriptionId: string): Promise<SubscriptionResult>;
626
+ }
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@base44-preview/sdk",
3
- "version": "0.8.53-pr.331.bf2d5aa",
3
+ "version": "0.8.54-pr.316.81dc6f9",
4
4
  "description": "JavaScript SDK for Base44 API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",