@umituz/web-polar-payment 1.0.13 → 1.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -2,10 +2,6 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as react from 'react';
3
3
  import { ReactNode } from 'react';
4
4
 
5
- /**
6
- * Subscription Entity
7
- * @description Types for subscription status and billing cycles
8
- */
9
5
  type SubscriptionStatusValue = 'active' | 'canceled' | 'revoked' | 'trialing' | 'past_due' | 'incomplete' | 'incomplete_expired' | 'unpaid' | 'none';
10
6
  type BillingCycle = 'monthly' | 'yearly';
11
7
  interface SubscriptionStatus {
@@ -16,14 +12,9 @@ interface SubscriptionStatus {
16
12
  currentPeriodEnd?: string;
17
13
  billingCycle?: BillingCycle;
18
14
  polarCustomerId?: string;
19
- /** Token balance (for token-based projects like Aria) */
20
15
  tokens?: number;
21
16
  }
22
17
 
23
- /**
24
- * Order Entity
25
- * @description Types for billing history items
26
- */
27
18
  interface OrderItem {
28
19
  id: string;
29
20
  createdAt: string;
@@ -35,16 +26,11 @@ interface OrderItem {
35
26
  invoiceUrl?: string;
36
27
  }
37
28
 
38
- /**
39
- * Checkout Entity
40
- * @description Types for initiating and following process of checkouts
41
- */
42
29
  interface CheckoutParams {
43
30
  productId: string;
44
31
  planKey?: string;
45
32
  billingCycle?: 'monthly' | 'yearly';
46
33
  successUrl?: string;
47
- /** Injected automatically by PolarProvider — do not pass manually */
48
34
  userId?: string;
49
35
  }
50
36
  interface CheckoutResult {
@@ -52,20 +38,12 @@ interface CheckoutResult {
52
38
  id: string;
53
39
  }
54
40
 
55
- /**
56
- * Cancellation Entity
57
- * @description Types for subscription cancellation reasons and outcomes
58
- */
59
41
  type CancellationReason = 'too_expensive' | 'missing_features' | 'switched_service' | 'unused' | 'customer_service' | 'low_quality' | 'too_complex' | 'other';
60
42
  interface CancelResult {
61
43
  success: boolean;
62
44
  endsAt?: string;
63
45
  }
64
46
 
65
- /**
66
- * Sync Entity
67
- * @description Type for subscription synchronization results
68
- */
69
47
  interface SyncResult {
70
48
  synced: boolean;
71
49
  plan?: string;
@@ -108,17 +86,9 @@ interface FirebaseAdapterConfig {
108
86
  currentPeriodEndField?: string;
109
87
  };
110
88
  }
111
- /**
112
- * Firebase Billing Service
113
- * @description Implementation of PolarAdapter for Firebase Functions and Firestore.
114
- */
115
89
  declare function createFirebaseAdapter(config: FirebaseAdapterConfig): PolarAdapter;
116
90
 
117
- /**
118
- * Billing Constants
119
- * @description Standardized subscription states and plan names
120
- */
121
- declare const SUBSCRIPTION_STATUS: {
91
+ declare const SUBSCRIPTION_STATUS: Readonly<{
122
92
  ACTIVE: "active";
123
93
  CANCELED: "canceled";
124
94
  REVOKED: "revoked";
@@ -128,24 +98,23 @@ declare const SUBSCRIPTION_STATUS: {
128
98
  INCOMPLETE_EXPIRED: "incomplete_expired";
129
99
  UNPAID: "unpaid";
130
100
  NONE: "none";
131
- };
101
+ }>;
132
102
  declare const FREE_PLAN = "free";
133
103
 
134
- /**
135
- * Normalize a raw Polar status string to a known value.
136
- * @description Defaults to 'none' for unknown statuses or non-string input.
137
- */
138
104
  declare function normalizeStatus(raw: string): SubscriptionStatusValue;
139
- /**
140
- * Normalize billing interval
141
- * @description Maps 'month'/'year' to 'monthly'/'yearly'. Defaults to 'monthly' for unknown values or non-string input.
142
- */
143
105
  declare function normalizeBillingCycle(interval: string): BillingCycle;
144
106
 
145
- /**
146
- * PolarProvider Component
147
- * @description Context provider for Polar billing management.
148
- */
107
+ declare function normalizeUserId(userId: string | undefined): string | undefined;
108
+ declare function isValidProductId(productId: unknown): productId is string;
109
+ declare function isValidCheckoutUrl(url: string): boolean;
110
+ declare function isProductionInsecureUrl(url: string): boolean;
111
+
112
+ declare function asString(value: unknown): string | undefined;
113
+ declare function asBoolean(value: unknown): boolean | undefined;
114
+ declare function isTimestamp(value: unknown): value is {
115
+ toDate(): Date;
116
+ };
117
+
149
118
  interface PolarProviderProps {
150
119
  adapter: PolarAdapter;
151
120
  userId?: string;
@@ -164,11 +133,7 @@ interface PolarContextValue {
164
133
  getPortalUrl: () => Promise<string>;
165
134
  }
166
135
  declare const PolarContext: react.Context<PolarContextValue | undefined>;
167
- /**
168
- * usePolarBilling Hook
169
- * @description Hook to access Polar billing context
170
- */
171
136
  declare function usePolarBilling(): PolarContextValue;
172
137
  declare const useSubscription: typeof usePolarBilling;
173
138
 
174
- export { type BillingCycle, type CancelResult, type CancellationReason, type CheckoutParams, type CheckoutResult, FREE_PLAN, type FirebaseAdapterConfig, type OrderItem, type PolarAdapter, PolarContext, type PolarContextValue, PolarProvider, SUBSCRIPTION_STATUS, type SubscriptionStatus, type SubscriptionStatusValue, type SyncResult, createFirebaseAdapter, normalizeBillingCycle, normalizeStatus, usePolarBilling, useSubscription };
139
+ export { type BillingCycle, type CancelResult, type CancellationReason, type CheckoutParams, type CheckoutResult, FREE_PLAN, type FirebaseAdapterConfig, type OrderItem, type PolarAdapter, PolarContext, type PolarContextValue, PolarProvider, SUBSCRIPTION_STATUS, type SubscriptionStatus, type SubscriptionStatusValue, type SyncResult, asBoolean, asString, createFirebaseAdapter, isProductionInsecureUrl, isTimestamp, isValidCheckoutUrl, isValidProductId, normalizeBillingCycle, normalizeStatus, normalizeUserId, usePolarBilling, useSubscription };
package/dist/index.d.ts CHANGED
@@ -2,10 +2,6 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as react from 'react';
3
3
  import { ReactNode } from 'react';
4
4
 
5
- /**
6
- * Subscription Entity
7
- * @description Types for subscription status and billing cycles
8
- */
9
5
  type SubscriptionStatusValue = 'active' | 'canceled' | 'revoked' | 'trialing' | 'past_due' | 'incomplete' | 'incomplete_expired' | 'unpaid' | 'none';
10
6
  type BillingCycle = 'monthly' | 'yearly';
11
7
  interface SubscriptionStatus {
@@ -16,14 +12,9 @@ interface SubscriptionStatus {
16
12
  currentPeriodEnd?: string;
17
13
  billingCycle?: BillingCycle;
18
14
  polarCustomerId?: string;
19
- /** Token balance (for token-based projects like Aria) */
20
15
  tokens?: number;
21
16
  }
22
17
 
23
- /**
24
- * Order Entity
25
- * @description Types for billing history items
26
- */
27
18
  interface OrderItem {
28
19
  id: string;
29
20
  createdAt: string;
@@ -35,16 +26,11 @@ interface OrderItem {
35
26
  invoiceUrl?: string;
36
27
  }
37
28
 
38
- /**
39
- * Checkout Entity
40
- * @description Types for initiating and following process of checkouts
41
- */
42
29
  interface CheckoutParams {
43
30
  productId: string;
44
31
  planKey?: string;
45
32
  billingCycle?: 'monthly' | 'yearly';
46
33
  successUrl?: string;
47
- /** Injected automatically by PolarProvider — do not pass manually */
48
34
  userId?: string;
49
35
  }
50
36
  interface CheckoutResult {
@@ -52,20 +38,12 @@ interface CheckoutResult {
52
38
  id: string;
53
39
  }
54
40
 
55
- /**
56
- * Cancellation Entity
57
- * @description Types for subscription cancellation reasons and outcomes
58
- */
59
41
  type CancellationReason = 'too_expensive' | 'missing_features' | 'switched_service' | 'unused' | 'customer_service' | 'low_quality' | 'too_complex' | 'other';
60
42
  interface CancelResult {
61
43
  success: boolean;
62
44
  endsAt?: string;
63
45
  }
64
46
 
65
- /**
66
- * Sync Entity
67
- * @description Type for subscription synchronization results
68
- */
69
47
  interface SyncResult {
70
48
  synced: boolean;
71
49
  plan?: string;
@@ -108,17 +86,9 @@ interface FirebaseAdapterConfig {
108
86
  currentPeriodEndField?: string;
109
87
  };
110
88
  }
111
- /**
112
- * Firebase Billing Service
113
- * @description Implementation of PolarAdapter for Firebase Functions and Firestore.
114
- */
115
89
  declare function createFirebaseAdapter(config: FirebaseAdapterConfig): PolarAdapter;
116
90
 
117
- /**
118
- * Billing Constants
119
- * @description Standardized subscription states and plan names
120
- */
121
- declare const SUBSCRIPTION_STATUS: {
91
+ declare const SUBSCRIPTION_STATUS: Readonly<{
122
92
  ACTIVE: "active";
123
93
  CANCELED: "canceled";
124
94
  REVOKED: "revoked";
@@ -128,24 +98,23 @@ declare const SUBSCRIPTION_STATUS: {
128
98
  INCOMPLETE_EXPIRED: "incomplete_expired";
129
99
  UNPAID: "unpaid";
130
100
  NONE: "none";
131
- };
101
+ }>;
132
102
  declare const FREE_PLAN = "free";
133
103
 
134
- /**
135
- * Normalize a raw Polar status string to a known value.
136
- * @description Defaults to 'none' for unknown statuses or non-string input.
137
- */
138
104
  declare function normalizeStatus(raw: string): SubscriptionStatusValue;
139
- /**
140
- * Normalize billing interval
141
- * @description Maps 'month'/'year' to 'monthly'/'yearly'. Defaults to 'monthly' for unknown values or non-string input.
142
- */
143
105
  declare function normalizeBillingCycle(interval: string): BillingCycle;
144
106
 
145
- /**
146
- * PolarProvider Component
147
- * @description Context provider for Polar billing management.
148
- */
107
+ declare function normalizeUserId(userId: string | undefined): string | undefined;
108
+ declare function isValidProductId(productId: unknown): productId is string;
109
+ declare function isValidCheckoutUrl(url: string): boolean;
110
+ declare function isProductionInsecureUrl(url: string): boolean;
111
+
112
+ declare function asString(value: unknown): string | undefined;
113
+ declare function asBoolean(value: unknown): boolean | undefined;
114
+ declare function isTimestamp(value: unknown): value is {
115
+ toDate(): Date;
116
+ };
117
+
149
118
  interface PolarProviderProps {
150
119
  adapter: PolarAdapter;
151
120
  userId?: string;
@@ -164,11 +133,7 @@ interface PolarContextValue {
164
133
  getPortalUrl: () => Promise<string>;
165
134
  }
166
135
  declare const PolarContext: react.Context<PolarContextValue | undefined>;
167
- /**
168
- * usePolarBilling Hook
169
- * @description Hook to access Polar billing context
170
- */
171
136
  declare function usePolarBilling(): PolarContextValue;
172
137
  declare const useSubscription: typeof usePolarBilling;
173
138
 
174
- export { type BillingCycle, type CancelResult, type CancellationReason, type CheckoutParams, type CheckoutResult, FREE_PLAN, type FirebaseAdapterConfig, type OrderItem, type PolarAdapter, PolarContext, type PolarContextValue, PolarProvider, SUBSCRIPTION_STATUS, type SubscriptionStatus, type SubscriptionStatusValue, type SyncResult, createFirebaseAdapter, normalizeBillingCycle, normalizeStatus, usePolarBilling, useSubscription };
139
+ export { type BillingCycle, type CancelResult, type CancellationReason, type CheckoutParams, type CheckoutResult, FREE_PLAN, type FirebaseAdapterConfig, type OrderItem, type PolarAdapter, PolarContext, type PolarContextValue, PolarProvider, SUBSCRIPTION_STATUS, type SubscriptionStatus, type SubscriptionStatusValue, type SyncResult, asBoolean, asString, createFirebaseAdapter, isProductionInsecureUrl, isTimestamp, isValidCheckoutUrl, isValidProductId, normalizeBillingCycle, normalizeStatus, normalizeUserId, usePolarBilling, useSubscription };
package/dist/index.js CHANGED
@@ -34,30 +34,50 @@ __export(index_exports, {
34
34
  PolarContext: () => PolarContext,
35
35
  PolarProvider: () => PolarProvider,
36
36
  SUBSCRIPTION_STATUS: () => SUBSCRIPTION_STATUS,
37
+ asBoolean: () => asBoolean,
38
+ asString: () => asString,
37
39
  createFirebaseAdapter: () => createFirebaseAdapter,
40
+ isProductionInsecureUrl: () => isProductionInsecureUrl,
41
+ isTimestamp: () => isTimestamp,
42
+ isValidCheckoutUrl: () => isValidCheckoutUrl,
43
+ isValidProductId: () => isValidProductId,
38
44
  normalizeBillingCycle: () => normalizeBillingCycle,
39
45
  normalizeStatus: () => normalizeStatus,
46
+ normalizeUserId: () => normalizeUserId,
40
47
  usePolarBilling: () => usePolarBilling,
41
48
  useSubscription: () => useSubscription
42
49
  });
43
50
  module.exports = __toCommonJS(index_exports);
44
51
 
52
+ // src/infrastructure/constants/billing.constants.ts
53
+ var SUBSCRIPTION_STATUS = Object.freeze({
54
+ ACTIVE: "active",
55
+ CANCELED: "canceled",
56
+ REVOKED: "revoked",
57
+ TRIALING: "trialing",
58
+ PAST_DUE: "past_due",
59
+ INCOMPLETE: "incomplete",
60
+ INCOMPLETE_EXPIRED: "incomplete_expired",
61
+ UNPAID: "unpaid",
62
+ NONE: "none"
63
+ });
64
+ var FREE_PLAN = "free";
65
+
45
66
  // src/infrastructure/utils/normalization.util.ts
67
+ var STATUS_MAP = Object.freeze({
68
+ [SUBSCRIPTION_STATUS.ACTIVE]: SUBSCRIPTION_STATUS.ACTIVE,
69
+ [SUBSCRIPTION_STATUS.CANCELED]: SUBSCRIPTION_STATUS.CANCELED,
70
+ [SUBSCRIPTION_STATUS.REVOKED]: SUBSCRIPTION_STATUS.REVOKED,
71
+ [SUBSCRIPTION_STATUS.TRIALING]: SUBSCRIPTION_STATUS.TRIALING,
72
+ [SUBSCRIPTION_STATUS.PAST_DUE]: SUBSCRIPTION_STATUS.PAST_DUE,
73
+ [SUBSCRIPTION_STATUS.INCOMPLETE]: SUBSCRIPTION_STATUS.INCOMPLETE,
74
+ [SUBSCRIPTION_STATUS.INCOMPLETE_EXPIRED]: SUBSCRIPTION_STATUS.INCOMPLETE_EXPIRED,
75
+ [SUBSCRIPTION_STATUS.UNPAID]: SUBSCRIPTION_STATUS.UNPAID,
76
+ [SUBSCRIPTION_STATUS.NONE]: SUBSCRIPTION_STATUS.NONE
77
+ });
46
78
  function normalizeStatus(raw) {
47
- const map = {
48
- active: "active",
49
- trialing: "trialing",
50
- past_due: "past_due",
51
- incomplete: "incomplete",
52
- incomplete_expired: "incomplete_expired",
53
- unpaid: "unpaid",
54
- canceled: "canceled",
55
- cancelled: "canceled",
56
- revoked: "revoked",
57
- none: "none"
58
- };
59
- if (typeof raw !== "string") return "none";
60
- return map[raw.toLowerCase()] ?? "none";
79
+ if (raw == null || typeof raw !== "string") return "none";
80
+ return STATUS_MAP[raw.toLowerCase()] ?? "none";
61
81
  }
62
82
  function normalizeBillingCycle(interval) {
63
83
  if (typeof interval !== "string") return "monthly";
@@ -67,7 +87,7 @@ function normalizeBillingCycle(interval) {
67
87
  return "monthly";
68
88
  }
69
89
 
70
- // src/infrastructure/services/firebase-billing.service.ts
90
+ // src/infrastructure/utils/firebase-helpers.util.ts
71
91
  function asString(value) {
72
92
  if (typeof value === "string") return value;
73
93
  return void 0;
@@ -79,6 +99,8 @@ function asBoolean(value) {
79
99
  function isTimestamp(value) {
80
100
  return typeof value === "object" && value !== null && "toDate" in value && typeof value.toDate === "function";
81
101
  }
102
+
103
+ // src/infrastructure/services/firebase-billing.service.ts
82
104
  function createFirebaseAdapter(config) {
83
105
  const functions = config.functions;
84
106
  const firestore = config.firestore;
@@ -99,15 +121,31 @@ function createFirebaseAdapter(config) {
99
121
  cancelAtPeriodEnd: config.db?.cancelAtPeriodEndField ?? "cancelAtPeriodEnd",
100
122
  currentPeriodEnd: config.db?.currentPeriodEndField ?? "currentPeriodEnd"
101
123
  };
124
+ let httpsCallableCache = null;
125
+ let firestoreCache = null;
126
+ async function getHttpsCallable() {
127
+ if (!httpsCallableCache) {
128
+ const mod = await import("firebase/functions");
129
+ httpsCallableCache = mod.httpsCallable;
130
+ }
131
+ return httpsCallableCache;
132
+ }
133
+ async function getFirestore() {
134
+ if (!firestoreCache) {
135
+ const mod = await import("firebase/firestore");
136
+ firestoreCache = { doc: mod.doc, getDoc: mod.getDoc };
137
+ }
138
+ return firestoreCache;
139
+ }
102
140
  async function callable(name, data) {
103
- const { httpsCallable } = await import("firebase/functions");
141
+ const httpsCallable = await getHttpsCallable();
104
142
  const fn = httpsCallable(functions, name);
105
143
  const result = await fn(data);
106
144
  return result.data;
107
145
  }
108
146
  return {
109
147
  async getStatus(userId) {
110
- const { doc, getDoc } = await import("firebase/firestore");
148
+ const { doc, getDoc } = await getFirestore();
111
149
  const snap = await getDoc(doc(firestore, db.collection, userId));
112
150
  if (!snap.exists()) {
113
151
  return { plan: "free", subscriptionStatus: "none" };
@@ -153,26 +191,26 @@ function createFirebaseAdapter(config) {
153
191
  callables.portal,
154
192
  {}
155
193
  );
156
- const url = result.url ?? result.customerPortalUrl;
157
- if (!url) throw new Error("No portal URL returned from Cloud Function");
158
- return url;
194
+ return result.url;
159
195
  }
160
196
  };
161
197
  }
162
198
 
163
- // src/infrastructure/constants/billing.constants.ts
164
- var SUBSCRIPTION_STATUS = {
165
- ACTIVE: "active",
166
- CANCELED: "canceled",
167
- REVOKED: "revoked",
168
- TRIALING: "trialing",
169
- PAST_DUE: "past_due",
170
- INCOMPLETE: "incomplete",
171
- INCOMPLETE_EXPIRED: "incomplete_expired",
172
- UNPAID: "unpaid",
173
- NONE: "none"
174
- };
175
- var FREE_PLAN = "free";
199
+ // src/infrastructure/utils/validations.util.ts
200
+ function normalizeUserId(userId) {
201
+ if (typeof userId !== "string") return void 0;
202
+ const trimmed = userId.trim();
203
+ return trimmed.length > 0 ? trimmed : void 0;
204
+ }
205
+ function isValidProductId(productId) {
206
+ return typeof productId === "string" && productId.trim().length > 0;
207
+ }
208
+ function isValidCheckoutUrl(url) {
209
+ return url.startsWith("https://") || url.startsWith("http://");
210
+ }
211
+ function isProductionInsecureUrl(url) {
212
+ return url.startsWith("http://") && !url.includes("localhost") && !url.includes("127.0.0.1");
213
+ }
176
214
 
177
215
  // src/presentation/components/PolarProvider.tsx
178
216
  var import_react2 = require("react");
@@ -193,67 +231,72 @@ var FREE_STATUS = {
193
231
  plan: "free",
194
232
  subscriptionStatus: "none"
195
233
  };
196
- function normalizeUserId(userId) {
197
- if (typeof userId !== "string") return void 0;
198
- const trimmed = userId.trim();
199
- return trimmed.length > 0 ? trimmed : void 0;
200
- }
201
234
  function PolarProvider({ adapter, userId, children }) {
202
235
  const [status, setStatus] = (0, import_react2.useState)(FREE_STATUS);
203
236
  const [loading, setLoading] = (0, import_react2.useState)(true);
204
237
  const adapterRef = (0, import_react2.useRef)(adapter);
238
+ const statusRef = (0, import_react2.useRef)({ setStatus, setLoading });
239
+ const userIdRef = (0, import_react2.useRef)(normalizeUserId(userId));
240
+ const refreshMountedRef = (0, import_react2.useRef)(true);
205
241
  adapterRef.current = adapter;
206
- const refreshAbortRef = (0, import_react2.useRef)(null);
242
+ statusRef.current = { setStatus, setLoading };
243
+ userIdRef.current = normalizeUserId(userId);
207
244
  const refresh = (0, import_react2.useCallback)(async () => {
208
- const uid = normalizeUserId(userId);
245
+ const uid = userIdRef.current;
246
+ const { setStatus: setStatus2, setLoading: setLoading2 } = statusRef.current;
209
247
  if (!uid) {
210
- setStatus(FREE_STATUS);
211
- setLoading(false);
248
+ setStatus2(FREE_STATUS);
249
+ setLoading2(false);
212
250
  return;
213
251
  }
214
- refreshAbortRef.current?.abort();
215
- const ctrl = new AbortController();
216
- refreshAbortRef.current = ctrl;
252
+ refreshMountedRef.current = true;
217
253
  try {
218
- setLoading(true);
254
+ setLoading2(true);
219
255
  const s = await adapterRef.current.getStatus(uid);
220
- if (!ctrl.signal.aborted) setStatus(s);
256
+ if (refreshMountedRef.current) setStatus2(s);
221
257
  } catch (err) {
222
- if (!ctrl.signal.aborted) {
258
+ if (refreshMountedRef.current) {
223
259
  console.error("[polar-billing] getStatus failed:", err);
224
- setStatus(FREE_STATUS);
260
+ setStatus2(FREE_STATUS);
225
261
  }
226
262
  } finally {
227
- if (!ctrl.signal.aborted) setLoading(false);
263
+ if (refreshMountedRef.current) setLoading2(false);
228
264
  }
229
- }, [userId]);
265
+ }, []);
230
266
  (0, import_react2.useEffect)(() => {
267
+ refreshMountedRef.current = true;
231
268
  refresh();
232
269
  return () => {
233
- refreshAbortRef.current?.abort();
270
+ refreshMountedRef.current = false;
234
271
  };
235
- }, [refresh]);
272
+ }, [userId]);
236
273
  const startCheckout = (0, import_react2.useCallback)(async (params) => {
237
- const uid = normalizeUserId(userId);
238
- const result = await adapterRef.current.createCheckout({ ...params, userId: uid });
239
- if (!result.url.startsWith("https://")) {
240
- throw new Error("[polar-billing] Invalid checkout URL returned: URL must start with https://");
274
+ if (!isValidProductId(params.productId)) {
275
+ throw new Error("[polar-billing] Invalid productId: must be a non-empty string");
276
+ }
277
+ const uid = userIdRef.current;
278
+ const result = await adapterRef.current.createCheckout({ ...params, userId: uid ?? void 0 });
279
+ if (!isValidCheckoutUrl(result.url)) {
280
+ throw new Error("[polar-billing] Invalid checkout URL returned: URL must start with https:// or http://");
281
+ }
282
+ if (isProductionInsecureUrl(result.url)) {
283
+ console.warn("[polar-billing] WARNING: Using insecure http:// URL in production environment");
241
284
  }
242
285
  window.location.href = result.url;
243
- }, [userId]);
286
+ }, []);
244
287
  const syncSubscription = (0, import_react2.useCallback)(async () => {
245
- const uid = normalizeUserId(userId);
288
+ const uid = userIdRef.current;
246
289
  if (!uid) return { synced: false };
247
290
  const checkoutId = new URLSearchParams(window.location.search).get("checkout_id") ?? void 0;
248
291
  const result = await adapterRef.current.syncSubscription(uid, checkoutId);
249
292
  if (result.synced) await refresh();
250
293
  return result;
251
- }, [userId, refresh]);
294
+ }, [refresh]);
252
295
  const getBillingHistory = (0, import_react2.useCallback)(async () => {
253
- const uid = normalizeUserId(userId);
296
+ const uid = userIdRef.current;
254
297
  if (!uid) return [];
255
298
  return adapterRef.current.getBillingHistory(uid);
256
- }, [userId]);
299
+ }, []);
257
300
  const cancelSubscription = (0, import_react2.useCallback)(
258
301
  async (reason) => {
259
302
  const result = await adapterRef.current.cancelSubscription(reason);
@@ -263,10 +306,10 @@ function PolarProvider({ adapter, userId, children }) {
263
306
  [refresh]
264
307
  );
265
308
  const getPortalUrl = (0, import_react2.useCallback)(async () => {
266
- const uid = normalizeUserId(userId);
309
+ const uid = userIdRef.current;
267
310
  if (!uid) throw new Error("[polar-billing] Cannot get portal URL: No authenticated user");
268
311
  return adapterRef.current.getPortalUrl(uid);
269
- }, [userId]);
312
+ }, []);
270
313
  const value = (0, import_react2.useMemo)(
271
314
  () => ({
272
315
  status,
@@ -278,7 +321,7 @@ function PolarProvider({ adapter, userId, children }) {
278
321
  cancelSubscription,
279
322
  getPortalUrl
280
323
  }),
281
- [status, loading, refresh, startCheckout, syncSubscription, getBillingHistory, cancelSubscription, getPortalUrl]
324
+ [status, loading, refresh, syncSubscription]
282
325
  );
283
326
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PolarContext.Provider, { value, children });
284
327
  }
@@ -288,9 +331,16 @@ function PolarProvider({ adapter, userId, children }) {
288
331
  PolarContext,
289
332
  PolarProvider,
290
333
  SUBSCRIPTION_STATUS,
334
+ asBoolean,
335
+ asString,
291
336
  createFirebaseAdapter,
337
+ isProductionInsecureUrl,
338
+ isTimestamp,
339
+ isValidCheckoutUrl,
340
+ isValidProductId,
292
341
  normalizeBillingCycle,
293
342
  normalizeStatus,
343
+ normalizeUserId,
294
344
  usePolarBilling,
295
345
  useSubscription
296
346
  });