@itzsudhan/creem-expo 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +45 -0
  2. package/app.plugin.js +202 -0
  3. package/dist/chunk-BF74I2QD.mjs +857 -0
  4. package/dist/express-BAx2zfw7.d.mts +84 -0
  5. package/dist/express-jb3wXcce.d.ts +84 -0
  6. package/dist/index.d.mts +164 -0
  7. package/dist/index.d.ts +164 -0
  8. package/dist/index.js +1132 -0
  9. package/dist/index.mjs +1078 -0
  10. package/dist/server/express.d.mts +5 -0
  11. package/dist/server/express.d.ts +5 -0
  12. package/dist/server/express.js +888 -0
  13. package/dist/server/express.mjs +10 -0
  14. package/dist/server/index.d.mts +15 -0
  15. package/dist/server/index.d.ts +15 -0
  16. package/dist/server/index.js +898 -0
  17. package/dist/server/index.mjs +25 -0
  18. package/dist/types-NcFyNrWp.d.mts +271 -0
  19. package/dist/types-NcFyNrWp.d.ts +271 -0
  20. package/package.json +106 -0
  21. package/src/client/apiClient.ts +195 -0
  22. package/src/client/components/CreemCheckoutButton.tsx +91 -0
  23. package/src/client/components/CreemCheckoutModal.tsx +81 -0
  24. package/src/client/components/CreemManageSubscriptionButton.tsx +58 -0
  25. package/src/client/context.tsx +57 -0
  26. package/src/client/hooks/useCreemCheckout.ts +478 -0
  27. package/src/client/hooks/useCreemSubscription.ts +194 -0
  28. package/src/client/utils/checkoutState.ts +99 -0
  29. package/src/client/utils/linking.ts +232 -0
  30. package/src/errors.ts +61 -0
  31. package/src/index.ts +19 -0
  32. package/src/server/core.ts +815 -0
  33. package/src/server/createCreemClient.ts +16 -0
  34. package/src/server/express.ts +187 -0
  35. package/src/server/fetchHandlers.ts +191 -0
  36. package/src/server/index.ts +6 -0
  37. package/src/server/json.ts +44 -0
  38. package/src/server/signatures.ts +18 -0
  39. package/src/types.ts +402 -0
@@ -0,0 +1,478 @@
1
+ import { useEffect, useReducer, useRef, useState } from "react";
2
+ import * as Linking from "expo-linking";
3
+ import * as WebBrowser from "expo-web-browser";
4
+ import type { WebViewNavigation } from "react-native-webview/lib/WebViewTypes";
5
+ import { CreemError } from "../../errors";
6
+ import type {
7
+ CheckoutPresentation,
8
+ CheckoutVerificationResult,
9
+ CheckoutSessionResult,
10
+ CreateCheckoutInput,
11
+ LaunchCheckoutInput,
12
+ ResourceStatus,
13
+ StartCheckoutOptions,
14
+ VerifyCheckoutInput,
15
+ VerifyCheckoutOptions,
16
+ } from "../../types";
17
+ import { useCreemContext } from "../context";
18
+ import {
19
+ appendMetadataToUrl,
20
+ buildCheckoutUrlFromSessionId,
21
+ buildReturnUrl,
22
+ buildSuccessResult,
23
+ detectCheckoutStatusFromUrl,
24
+ } from "../utils/linking";
25
+ import {
26
+ checkoutReducer,
27
+ initialCheckoutState,
28
+ } from "../utils/checkoutState";
29
+
30
+ type ActiveRedirects = {
31
+ successUrl: string;
32
+ cancelUrl?: string;
33
+ };
34
+
35
+ const DEFAULT_VERIFY_TIMEOUT_MS = 12_000;
36
+ const DEFAULT_VERIFY_POLL_INTERVAL_MS = 1_200;
37
+
38
+ const createDirectSession = (
39
+ input: LaunchCheckoutInput,
40
+ checkoutUrl: string,
41
+ successUrl: string
42
+ ): CheckoutSessionResult => {
43
+ const checkoutUrlObject = new URL(checkoutUrl);
44
+ const checkoutId =
45
+ input.sessionId ??
46
+ checkoutUrlObject.searchParams.get("session_id") ??
47
+ checkoutUrlObject.searchParams.get("checkout_id") ??
48
+ "direct_checkout";
49
+
50
+ return {
51
+ checkoutId,
52
+ checkoutUrl,
53
+ requestId: input.sessionId,
54
+ successUrl,
55
+ status: "pending",
56
+ };
57
+ };
58
+
59
+ const resolveDirectCheckoutUrl = (input: LaunchCheckoutInput) => {
60
+ if (input.checkoutUrl) {
61
+ return input.checkoutUrl;
62
+ }
63
+
64
+ if (input.sessionId) {
65
+ return buildCheckoutUrlFromSessionId(input.sessionId);
66
+ }
67
+
68
+ throw new CreemError(
69
+ "INVALID_CHECKOUT_OPTIONS",
70
+ "Provide either `checkoutUrl` or `sessionId` when launching an existing Creem checkout."
71
+ );
72
+ };
73
+
74
+ const normalizeCheckoutError = (
75
+ error: unknown,
76
+ code: "CHECKOUT_LAUNCH_FAILED" | "INVALID_CHECKOUT_OPTIONS",
77
+ fallbackMessage: string
78
+ ) => {
79
+ if (error instanceof CreemError) {
80
+ return error;
81
+ }
82
+
83
+ if (error instanceof Error) {
84
+ return new CreemError(code, error.message || fallbackMessage, {
85
+ cause: error,
86
+ });
87
+ }
88
+
89
+ return new CreemError(code, fallbackMessage, {
90
+ cause: error,
91
+ });
92
+ };
93
+
94
+ const normalizeVerificationError = (error: unknown, fallbackMessage: string) => {
95
+ if (error instanceof CreemError) {
96
+ return error;
97
+ }
98
+
99
+ if (error instanceof Error) {
100
+ return new CreemError("CHECKOUT_VERIFICATION_FAILED", error.message || fallbackMessage, {
101
+ cause: error,
102
+ });
103
+ }
104
+
105
+ return new CreemError("CHECKOUT_VERIFICATION_FAILED", fallbackMessage, {
106
+ cause: error,
107
+ });
108
+ };
109
+
110
+ const resolveVerifyOptions = (
111
+ value: boolean | VerifyCheckoutOptions | undefined
112
+ ): { enabled: boolean; pollIntervalMs: number; timeoutMs: number } => {
113
+ if (!value) {
114
+ return {
115
+ enabled: false,
116
+ pollIntervalMs: DEFAULT_VERIFY_POLL_INTERVAL_MS,
117
+ timeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
118
+ };
119
+ }
120
+
121
+ if (value === true) {
122
+ return {
123
+ enabled: true,
124
+ pollIntervalMs: DEFAULT_VERIFY_POLL_INTERVAL_MS,
125
+ timeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
126
+ };
127
+ }
128
+
129
+ return {
130
+ enabled: true,
131
+ pollIntervalMs: value.pollIntervalMs ?? DEFAULT_VERIFY_POLL_INTERVAL_MS,
132
+ timeoutMs: value.timeoutMs ?? DEFAULT_VERIFY_TIMEOUT_MS,
133
+ };
134
+ };
135
+
136
+ const wait = (timeMs: number) =>
137
+ new Promise<void>((resolve) => {
138
+ setTimeout(resolve, timeMs);
139
+ });
140
+
141
+ export const __internal = {
142
+ createDirectSession,
143
+ resolveDirectCheckoutUrl,
144
+ normalizeCheckoutError,
145
+ normalizeVerificationError,
146
+ resolveVerifyOptions,
147
+ wait,
148
+ };
149
+
150
+ export const useCreemCheckout = () => {
151
+ const {
152
+ apiClient,
153
+ defaultPresentation,
154
+ returnPath,
155
+ defaultVerifyOnSuccess,
156
+ logger,
157
+ } = useCreemContext();
158
+ const [state, dispatch] = useReducer(checkoutReducer, initialCheckoutState);
159
+ const activeRedirectsRef = useRef<ActiveRedirects | null>(null);
160
+ const activeVerifyOptionsRef = useRef<boolean | VerifyCheckoutOptions | undefined>(
161
+ defaultVerifyOnSuccess
162
+ );
163
+ const [verification, setVerification] = useState<CheckoutVerificationResult | null>(null);
164
+ const [verificationStatus, setVerificationStatus] =
165
+ useState<ResourceStatus>("idle");
166
+ const [verificationError, setVerificationError] = useState<Error | null>(null);
167
+
168
+ useEffect(() => {
169
+ WebBrowser.maybeCompleteAuthSession();
170
+ }, []);
171
+
172
+ const clearActiveCheckoutContext = () => {
173
+ activeRedirectsRef.current = null;
174
+ activeVerifyOptionsRef.current = defaultVerifyOnSuccess;
175
+ };
176
+
177
+ const clearVerification = () => {
178
+ setVerification(null);
179
+ setVerificationStatus("idle");
180
+ setVerificationError(null);
181
+ };
182
+
183
+ const setActiveRedirects = (
184
+ successUrl: string,
185
+ cancelUrl: string | undefined,
186
+ verifyOptions: boolean | VerifyCheckoutOptions | undefined
187
+ ) => {
188
+ activeRedirectsRef.current = {
189
+ successUrl,
190
+ cancelUrl,
191
+ };
192
+ activeVerifyOptionsRef.current = verifyOptions;
193
+ };
194
+
195
+ const runCheckoutVerification = async (
196
+ checkoutId: string,
197
+ options?: VerifyCheckoutOptions
198
+ ) => {
199
+ setVerificationStatus("loading");
200
+ setVerificationError(null);
201
+
202
+ const pollIntervalMs = options?.pollIntervalMs ?? DEFAULT_VERIFY_POLL_INTERVAL_MS;
203
+ const timeoutMs = options?.timeoutMs ?? DEFAULT_VERIFY_TIMEOUT_MS;
204
+ const startedAt = Date.now();
205
+
206
+ try {
207
+ while (true) {
208
+ const nextVerification = await apiClient.verifyCheckout({
209
+ checkoutId,
210
+ });
211
+ setVerification(nextVerification);
212
+
213
+ if (nextVerification.verified || Date.now() - startedAt >= timeoutMs) {
214
+ setVerificationStatus("ready");
215
+ return nextVerification;
216
+ }
217
+
218
+ await wait(pollIntervalMs);
219
+ }
220
+ } catch (error) {
221
+ const normalizedError = normalizeVerificationError(
222
+ error,
223
+ "Failed to verify the Creem checkout after redirect."
224
+ );
225
+ setVerificationError(normalizedError);
226
+ setVerificationStatus("error");
227
+ logger?.error?.("creem checkout verification failed", normalizedError);
228
+ throw normalizedError;
229
+ }
230
+ };
231
+
232
+ const completeFromUrl = async (session: CheckoutSessionResult, url: string) => {
233
+ const verifyOptions = resolveVerifyOptions(activeVerifyOptionsRef.current);
234
+ clearActiveCheckoutContext();
235
+
236
+ let verificationResult: CheckoutVerificationResult | null = null;
237
+
238
+ if (verifyOptions.enabled) {
239
+ try {
240
+ verificationResult = await runCheckoutVerification(session.checkoutId, verifyOptions);
241
+ } catch {
242
+ verificationResult = null;
243
+ }
244
+ }
245
+
246
+ const result = buildSuccessResult(session, url, verificationResult);
247
+ dispatch({ type: "success", result });
248
+ logger?.info?.("creem checkout completed", result);
249
+ return result;
250
+ };
251
+
252
+ const cancelCheckout = (session?: CheckoutSessionResult, url?: string) => {
253
+ clearActiveCheckoutContext();
254
+ dispatch({ type: "cancelled", session });
255
+ logger?.info?.("creem checkout cancelled", {
256
+ url,
257
+ checkoutId: session?.checkoutId,
258
+ });
259
+ return session;
260
+ };
261
+
262
+ const detectReturnStatus = (url: string) => {
263
+ const activeRedirects = activeRedirectsRef.current;
264
+
265
+ if (!activeRedirects) {
266
+ return undefined;
267
+ }
268
+
269
+ return detectCheckoutStatusFromUrl(
270
+ url,
271
+ activeRedirects.successUrl,
272
+ activeRedirects.cancelUrl
273
+ );
274
+ };
275
+
276
+ const presentCheckout = async (
277
+ session: CheckoutSessionResult,
278
+ redirects: ActiveRedirects,
279
+ presentation: CheckoutPresentation,
280
+ verifyOptions: boolean | VerifyCheckoutOptions | undefined
281
+ ) => {
282
+ setActiveRedirects(redirects.successUrl, redirects.cancelUrl, verifyOptions);
283
+
284
+ if (presentation === "webview") {
285
+ dispatch({
286
+ type: "present",
287
+ session,
288
+ returnUrl: redirects.successUrl,
289
+ });
290
+ return session;
291
+ }
292
+
293
+ dispatch({ type: "browser-session", session });
294
+ const browserResult = await WebBrowser.openAuthSessionAsync(
295
+ session.checkoutUrl,
296
+ redirects.successUrl
297
+ );
298
+
299
+ if (browserResult.type === "success" && "url" in browserResult && browserResult.url) {
300
+ const detectedStatus = detectReturnStatus(browserResult.url);
301
+
302
+ if (detectedStatus === "cancelled") {
303
+ return cancelCheckout(session, browserResult.url);
304
+ }
305
+
306
+ return completeFromUrl(session, browserResult.url);
307
+ }
308
+
309
+ if (browserResult.type === "cancel" || browserResult.type === "dismiss") {
310
+ cancelCheckout(session);
311
+ return session;
312
+ }
313
+
314
+ return session;
315
+ };
316
+
317
+ const startCheckout = async (
318
+ input: CreateCheckoutInput,
319
+ options?: StartCheckoutOptions
320
+ ) => {
321
+ clearVerification();
322
+ dispatch({ type: "start" });
323
+
324
+ try {
325
+ const resolvedReturnUrl =
326
+ input.returnUrl ??
327
+ buildReturnUrl(options?.returnPath ?? returnPath, Linking.createURL);
328
+ const session = await apiClient.createCheckoutSession({
329
+ ...input,
330
+ returnUrl: resolvedReturnUrl,
331
+ });
332
+
333
+ return await presentCheckout(
334
+ session,
335
+ {
336
+ successUrl: resolvedReturnUrl,
337
+ },
338
+ options?.presentation ?? defaultPresentation,
339
+ options?.verifyOnSuccess ?? defaultVerifyOnSuccess
340
+ );
341
+ } catch (error) {
342
+ clearActiveCheckoutContext();
343
+ const normalizedError = normalizeCheckoutError(
344
+ error,
345
+ "CHECKOUT_LAUNCH_FAILED",
346
+ "Failed to start the Creem checkout."
347
+ );
348
+ logger?.error?.("creem checkout failed", normalizedError);
349
+ dispatch({ type: "error", error: normalizedError });
350
+ throw normalizedError;
351
+ }
352
+ };
353
+
354
+ const launchCheckout = async (
355
+ input: LaunchCheckoutInput,
356
+ options?: StartCheckoutOptions
357
+ ) => {
358
+ clearVerification();
359
+ dispatch({ type: "start" });
360
+
361
+ try {
362
+ const successUrl =
363
+ input.returnUrl ??
364
+ buildReturnUrl(options?.returnPath ?? returnPath, Linking.createURL);
365
+ const cancelUrl =
366
+ input.cancelUrl ??
367
+ buildReturnUrl(options?.cancelPath ?? "creem/cancel", Linking.createURL);
368
+ const checkoutUrl = appendMetadataToUrl(
369
+ resolveDirectCheckoutUrl(input),
370
+ input.metadata
371
+ );
372
+ const session = createDirectSession(input, checkoutUrl, successUrl);
373
+
374
+ return await presentCheckout(
375
+ session,
376
+ {
377
+ successUrl,
378
+ cancelUrl,
379
+ },
380
+ options?.presentation ?? defaultPresentation,
381
+ options?.verifyOnSuccess ?? defaultVerifyOnSuccess
382
+ );
383
+ } catch (error) {
384
+ clearActiveCheckoutContext();
385
+ const normalizedError = normalizeCheckoutError(
386
+ error,
387
+ error instanceof CreemError &&
388
+ error.code === "INVALID_CHECKOUT_OPTIONS"
389
+ ? "INVALID_CHECKOUT_OPTIONS"
390
+ : "CHECKOUT_LAUNCH_FAILED",
391
+ "Failed to launch the Creem checkout."
392
+ );
393
+ logger?.error?.("creem direct checkout failed", normalizedError);
394
+ dispatch({ type: "error", error: normalizedError });
395
+ throw normalizedError;
396
+ }
397
+ };
398
+
399
+ const handleReturnUrl = (url: string) => {
400
+ if (!state.session) {
401
+ return false;
402
+ }
403
+
404
+ const detectedStatus = detectReturnStatus(url);
405
+
406
+ if (!detectedStatus) {
407
+ return false;
408
+ }
409
+
410
+ if (detectedStatus === "success") {
411
+ completeFromUrl(state.session, url);
412
+ return true;
413
+ }
414
+
415
+ cancelCheckout(state.session, url);
416
+ return true;
417
+ };
418
+
419
+ const handleWebViewNavigation = (navigation: Pick<WebViewNavigation, "url">) =>
420
+ handleReturnUrl(navigation.url);
421
+
422
+ const verifyCheckout = async (
423
+ input?: VerifyCheckoutInput,
424
+ options?: VerifyCheckoutOptions
425
+ ) => {
426
+ const checkoutId = input?.checkoutId ?? state.session?.checkoutId;
427
+
428
+ if (!checkoutId) {
429
+ throw new CreemError(
430
+ "CHECKOUT_VERIFICATION_FAILED",
431
+ "No Creem checkout is available to verify yet."
432
+ );
433
+ }
434
+
435
+ return runCheckoutVerification(checkoutId, options);
436
+ };
437
+
438
+ const dismiss = () => {
439
+ cancelCheckout(state.session ?? undefined);
440
+ };
441
+
442
+ const reset = () => {
443
+ clearActiveCheckoutContext();
444
+ clearVerification();
445
+ dispatch({ type: "reset" });
446
+ };
447
+
448
+ return {
449
+ status: state.status,
450
+ loading: state.status === "creating" || verificationStatus === "loading",
451
+ verifying: verificationStatus === "loading",
452
+ error: state.error,
453
+ session: state.session,
454
+ result: state.result,
455
+ verification,
456
+ verificationStatus,
457
+ verificationError,
458
+ webViewState: {
459
+ visible: state.webViewVisible,
460
+ checkoutUrl: state.session?.checkoutUrl ?? null,
461
+ returnUrl: state.webViewReturnUrl,
462
+ },
463
+ modalVisible: state.webViewVisible,
464
+ modalProps: {
465
+ visible: state.webViewVisible,
466
+ checkoutUrl: state.session?.checkoutUrl ?? null,
467
+ onRequestClose: dismiss,
468
+ onReturnUrl: handleReturnUrl,
469
+ },
470
+ startCheckout,
471
+ launchCheckout,
472
+ verifyCheckout,
473
+ handleReturnUrl,
474
+ handleWebViewNavigation,
475
+ dismiss,
476
+ reset,
477
+ };
478
+ };
@@ -0,0 +1,194 @@
1
+ import { useEffect, useState } from "react";
2
+ import * as WebBrowser from "expo-web-browser";
3
+ import { CreemError } from "../../errors";
4
+ import { useCreemContext } from "../context";
5
+ import type {
6
+ CancelSubscriptionInput,
7
+ CustomerPortalResult,
8
+ PortalStatus,
9
+ SubscriptionActionName,
10
+ SubscriptionMutationStatus,
11
+ ResourceStatus,
12
+ PauseSubscriptionInput,
13
+ ResumeSubscriptionInput,
14
+ SubscriptionSnapshot,
15
+ UseCreemSubscriptionOptions,
16
+ UpgradeSubscriptionInput,
17
+ } from "../../types";
18
+
19
+ export const useCreemSubscription = (
20
+ options?: UseCreemSubscriptionOptions
21
+ ) => {
22
+ const { apiClient, logger } = useCreemContext();
23
+ const [status, setStatus] = useState<ResourceStatus>("idle");
24
+ const [actionStatus, setActionStatus] =
25
+ useState<SubscriptionMutationStatus>("idle");
26
+ const [portalStatus, setPortalStatus] = useState<PortalStatus>("idle");
27
+ const [subscription, setSubscription] = useState<SubscriptionSnapshot | null>(null);
28
+ const [error, setError] = useState<Error | null>(null);
29
+ const [portal, setPortal] = useState<CustomerPortalResult | null>(null);
30
+ const [lastAction, setLastAction] = useState<SubscriptionActionName | null>(null);
31
+
32
+ const normalizeError = (
33
+ value: unknown,
34
+ code:
35
+ | "PORTAL_OPEN_FAILED"
36
+ | "SUBSCRIPTION_FETCH_FAILED"
37
+ | "SUBSCRIPTION_MUTATION_FAILED",
38
+ fallback: string
39
+ ) => {
40
+ if (value instanceof CreemError) {
41
+ return value;
42
+ }
43
+
44
+ if (value instanceof Error) {
45
+ return new CreemError(code, value.message || fallback, {
46
+ cause: value,
47
+ });
48
+ }
49
+
50
+ return new CreemError(code, fallback, {
51
+ cause: value,
52
+ });
53
+ };
54
+
55
+ const refresh = async () => {
56
+ try {
57
+ setStatus("loading");
58
+ setError(null);
59
+ setLastAction("refresh");
60
+ const nextSubscription = await apiClient.getSubscription();
61
+ setSubscription(nextSubscription);
62
+ setStatus("ready");
63
+ return nextSubscription;
64
+ } catch (refreshError) {
65
+ const normalizedError = normalizeError(
66
+ refreshError,
67
+ "SUBSCRIPTION_FETCH_FAILED",
68
+ "Failed to load Creem subscription."
69
+ );
70
+ setStatus("error");
71
+ setError(normalizedError);
72
+ logger?.error?.("creem subscription refresh failed", normalizedError);
73
+ throw normalizedError;
74
+ }
75
+ };
76
+
77
+ const runMutation = async <TInput>(
78
+ action: SubscriptionActionName,
79
+ operation: (input: TInput) => Promise<SubscriptionSnapshot>,
80
+ input: TInput,
81
+ fallbackMessage: string
82
+ ) => {
83
+ try {
84
+ setActionStatus("loading");
85
+ setError(null);
86
+ setLastAction(action);
87
+ const nextSubscription = await operation(input);
88
+ setSubscription(nextSubscription);
89
+ setStatus("ready");
90
+ setActionStatus("ready");
91
+ return nextSubscription;
92
+ } catch (mutationError) {
93
+ const typedError = normalizeError(
94
+ mutationError,
95
+ "SUBSCRIPTION_MUTATION_FAILED",
96
+ fallbackMessage
97
+ );
98
+ setActionStatus("error");
99
+ setError(typedError);
100
+ logger?.error?.(`creem subscription ${action} failed`, typedError);
101
+ throw typedError;
102
+ }
103
+ };
104
+
105
+ useEffect(() => {
106
+ if (options?.enabled === false || options?.autoFetch === false) {
107
+ return;
108
+ }
109
+
110
+ refresh().catch(() => {
111
+ // The hook already stores the error state.
112
+ });
113
+ }, [options?.autoFetch, options?.enabled]);
114
+
115
+ const openPortal = async () => {
116
+ try {
117
+ setPortalStatus("opening");
118
+ const nextPortal = await apiClient.createCustomerPortal();
119
+ setPortal(nextPortal);
120
+ const browserResult = await WebBrowser.openBrowserAsync(
121
+ nextPortal.customerPortalLink
122
+ );
123
+
124
+ if (browserResult.type === "cancel" || browserResult.type === "dismiss") {
125
+ setPortalStatus("cancelled");
126
+ return nextPortal;
127
+ }
128
+
129
+ setPortalStatus("opened");
130
+ return nextPortal;
131
+ } catch (portalError) {
132
+ const normalizedError = normalizeError(
133
+ portalError,
134
+ "PORTAL_OPEN_FAILED",
135
+ "Failed to open Creem customer portal."
136
+ );
137
+ setPortalStatus("error");
138
+ setError(normalizedError);
139
+ logger?.error?.("creem portal open failed", normalizedError);
140
+ throw normalizedError;
141
+ }
142
+ };
143
+
144
+ const cancel = (input: CancelSubscriptionInput = {}) =>
145
+ runMutation(
146
+ "cancel",
147
+ apiClient.cancelSubscription,
148
+ input,
149
+ "Failed to cancel the Creem subscription."
150
+ );
151
+
152
+ const pause = (input: PauseSubscriptionInput = {}) =>
153
+ runMutation(
154
+ "pause",
155
+ apiClient.pauseSubscription,
156
+ input,
157
+ "Failed to pause the Creem subscription."
158
+ );
159
+
160
+ const resume = (input: ResumeSubscriptionInput = {}) =>
161
+ runMutation(
162
+ "resume",
163
+ apiClient.resumeSubscription,
164
+ input,
165
+ "Failed to resume the Creem subscription."
166
+ );
167
+
168
+ const upgrade = (input: UpgradeSubscriptionInput) =>
169
+ runMutation(
170
+ "upgrade",
171
+ apiClient.upgradeSubscription,
172
+ input,
173
+ "Failed to upgrade the Creem subscription."
174
+ );
175
+
176
+ return {
177
+ status,
178
+ loading: status === "loading",
179
+ actionStatus,
180
+ portalStatus,
181
+ lastAction,
182
+ subscription,
183
+ portal,
184
+ error,
185
+ refresh,
186
+ getSubscription: refresh,
187
+ cancel,
188
+ cancelSubscription: cancel,
189
+ pause,
190
+ resume,
191
+ upgrade,
192
+ openPortal,
193
+ };
194
+ };