@umituz/web-polar-payment 1.0.18 → 1.0.20

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
@@ -76,6 +76,89 @@ interface PolarContextValue {
76
76
  }
77
77
  declare function usePolarBilling(): PolarContextValue;
78
78
 
79
+ /**
80
+ * Product Entity
81
+ *
82
+ * Represents a Polar.sh product with pricing information
83
+ */
84
+ type RecurringInterval = 'day' | 'week' | 'month' | 'year';
85
+ type PriceType = 'fixed' | 'custom' | 'free' | 'seat_based' | 'metered_unit';
86
+ interface PolarPrice {
87
+ id: string;
88
+ type: PriceType;
89
+ amount_type: string;
90
+ amount?: number;
91
+ currency: string;
92
+ recurring_interval?: RecurringInterval;
93
+ is_archived: boolean;
94
+ product_id: string;
95
+ legacy?: boolean;
96
+ }
97
+ interface PolarProduct {
98
+ id: string;
99
+ name: string;
100
+ description: string | null;
101
+ is_recurring: boolean;
102
+ recurring_interval: RecurringInterval | null;
103
+ is_archived: boolean;
104
+ organization_id: string;
105
+ prices: PolarPrice[];
106
+ benefits: Array<{
107
+ id: string;
108
+ type: string;
109
+ description: string | null;
110
+ properties: Record<string, unknown>;
111
+ }>;
112
+ metadata: Record<string, unknown>;
113
+ created_at: string;
114
+ modified_at: string | null;
115
+ }
116
+ interface ProductListResponse {
117
+ items: PolarProduct[];
118
+ pagination: {
119
+ total_count: number;
120
+ max_page: number;
121
+ };
122
+ }
123
+ interface ProductListParams {
124
+ organizationId?: string;
125
+ isRecurring?: boolean;
126
+ page?: number;
127
+ limit?: number;
128
+ }
129
+
130
+ /**
131
+ * usePolarProducts Hook
132
+ *
133
+ * Fetch and manage products from Polar.sh API
134
+ */
135
+
136
+ interface UsePolarProductsOptions {
137
+ apiToken: string;
138
+ organizationId?: string;
139
+ isRecurring?: boolean;
140
+ enabled?: boolean;
141
+ }
142
+ interface UsePolarProductsResult {
143
+ products: PolarProduct[];
144
+ loading: boolean;
145
+ error: string | null;
146
+ refetch: () => Promise<void>;
147
+ }
148
+ /**
149
+ * Fetch products from Polar.sh API
150
+ *
151
+ * @example
152
+ * ```tsx
153
+ * const { products, loading, error } = usePolarProducts({
154
+ * apiToken: polarApiToken,
155
+ * organizationId: polarOrgId,
156
+ * isRecurring: true,
157
+ * });
158
+ * ```
159
+ */
160
+ declare function usePolarProducts(options: UsePolarProductsOptions): UsePolarProductsResult;
161
+
79
162
  interface FirebaseAdapterConfig {
80
163
  functions: unknown;
81
164
  firestore: unknown;
@@ -111,4 +194,54 @@ declare const SUBSCRIPTION_STATUS: Readonly<{
111
194
  NONE: "none";
112
195
  }>;
113
196
 
114
- export { type BillingCycle, type CancelResult, type CancellationReason, type CheckoutParams, type CheckoutResult, type FirebaseAdapterConfig, type OrderItem, type PolarAdapter, type PolarContextValue, PolarProvider, SUBSCRIPTION_STATUS, type SubscriptionStatus, type SubscriptionStatusValue, type SyncResult, createFirebaseAdapter, usePolarBilling };
197
+ /**
198
+ * Polar API Service
199
+ *
200
+ * Direct client-side calls to Polar.sh API using OAT authentication
201
+ * Note: OAT tokens should only be used server-side in production
202
+ * This service is intended for server-side or edge function usage
203
+ */
204
+
205
+ interface PolarApiConfig {
206
+ apiToken: string;
207
+ baseUrl?: string;
208
+ }
209
+ /**
210
+ * Fetch products from Polar.sh API
211
+ *
212
+ * @param config - Polar API configuration with OAT token
213
+ * @param params - Query parameters for filtering products
214
+ * @returns Paginated list of products
215
+ *
216
+ * @example
217
+ * ```ts
218
+ * const products = await getProducts({
219
+ * apiToken: 'polar_oat_xxx',
220
+ * baseUrl: 'https://api.polar.sh/v1'
221
+ * }, {
222
+ * organizationId: 'org-123',
223
+ * isRecurring: true,
224
+ * page: 1,
225
+ * limit: 100
226
+ * });
227
+ * ```
228
+ */
229
+ declare function getProducts(config: PolarApiConfig, params?: ProductListParams): Promise<ProductListResponse>;
230
+ /**
231
+ * Fetch a single product by ID
232
+ *
233
+ * @param config - Polar API configuration
234
+ * @param productId - Product ID to fetch
235
+ * @returns Product details or null if not found
236
+ */
237
+ declare function getProductById(config: PolarApiConfig, productId: string): Promise<PolarProduct | null>;
238
+ /**
239
+ * Fetch all products with automatic pagination
240
+ *
241
+ * @param config - Polar API configuration
242
+ * @param params - Query parameters for filtering
243
+ * @returns All products matching the criteria
244
+ */
245
+ declare function getAllProducts(config: PolarApiConfig, params?: Omit<ProductListParams, 'page' | 'limit'>): Promise<PolarProduct[]>;
246
+
247
+ export { type BillingCycle, type CancelResult, type CancellationReason, type CheckoutParams, type CheckoutResult, type FirebaseAdapterConfig, type OrderItem, type PolarAdapter, type PolarApiConfig, type PolarContextValue, type PolarPrice, type PolarProduct, PolarProvider, type PriceType, type ProductListParams, type ProductListResponse, type RecurringInterval, SUBSCRIPTION_STATUS, type SubscriptionStatus, type SubscriptionStatusValue, type SyncResult, type UsePolarProductsOptions, type UsePolarProductsResult, createFirebaseAdapter, getAllProducts, getProductById, getProducts, usePolarBilling, usePolarProducts };
package/dist/index.d.ts CHANGED
@@ -76,6 +76,89 @@ interface PolarContextValue {
76
76
  }
77
77
  declare function usePolarBilling(): PolarContextValue;
78
78
 
79
+ /**
80
+ * Product Entity
81
+ *
82
+ * Represents a Polar.sh product with pricing information
83
+ */
84
+ type RecurringInterval = 'day' | 'week' | 'month' | 'year';
85
+ type PriceType = 'fixed' | 'custom' | 'free' | 'seat_based' | 'metered_unit';
86
+ interface PolarPrice {
87
+ id: string;
88
+ type: PriceType;
89
+ amount_type: string;
90
+ amount?: number;
91
+ currency: string;
92
+ recurring_interval?: RecurringInterval;
93
+ is_archived: boolean;
94
+ product_id: string;
95
+ legacy?: boolean;
96
+ }
97
+ interface PolarProduct {
98
+ id: string;
99
+ name: string;
100
+ description: string | null;
101
+ is_recurring: boolean;
102
+ recurring_interval: RecurringInterval | null;
103
+ is_archived: boolean;
104
+ organization_id: string;
105
+ prices: PolarPrice[];
106
+ benefits: Array<{
107
+ id: string;
108
+ type: string;
109
+ description: string | null;
110
+ properties: Record<string, unknown>;
111
+ }>;
112
+ metadata: Record<string, unknown>;
113
+ created_at: string;
114
+ modified_at: string | null;
115
+ }
116
+ interface ProductListResponse {
117
+ items: PolarProduct[];
118
+ pagination: {
119
+ total_count: number;
120
+ max_page: number;
121
+ };
122
+ }
123
+ interface ProductListParams {
124
+ organizationId?: string;
125
+ isRecurring?: boolean;
126
+ page?: number;
127
+ limit?: number;
128
+ }
129
+
130
+ /**
131
+ * usePolarProducts Hook
132
+ *
133
+ * Fetch and manage products from Polar.sh API
134
+ */
135
+
136
+ interface UsePolarProductsOptions {
137
+ apiToken: string;
138
+ organizationId?: string;
139
+ isRecurring?: boolean;
140
+ enabled?: boolean;
141
+ }
142
+ interface UsePolarProductsResult {
143
+ products: PolarProduct[];
144
+ loading: boolean;
145
+ error: string | null;
146
+ refetch: () => Promise<void>;
147
+ }
148
+ /**
149
+ * Fetch products from Polar.sh API
150
+ *
151
+ * @example
152
+ * ```tsx
153
+ * const { products, loading, error } = usePolarProducts({
154
+ * apiToken: polarApiToken,
155
+ * organizationId: polarOrgId,
156
+ * isRecurring: true,
157
+ * });
158
+ * ```
159
+ */
160
+ declare function usePolarProducts(options: UsePolarProductsOptions): UsePolarProductsResult;
161
+
79
162
  interface FirebaseAdapterConfig {
80
163
  functions: unknown;
81
164
  firestore: unknown;
@@ -111,4 +194,54 @@ declare const SUBSCRIPTION_STATUS: Readonly<{
111
194
  NONE: "none";
112
195
  }>;
113
196
 
114
- export { type BillingCycle, type CancelResult, type CancellationReason, type CheckoutParams, type CheckoutResult, type FirebaseAdapterConfig, type OrderItem, type PolarAdapter, type PolarContextValue, PolarProvider, SUBSCRIPTION_STATUS, type SubscriptionStatus, type SubscriptionStatusValue, type SyncResult, createFirebaseAdapter, usePolarBilling };
197
+ /**
198
+ * Polar API Service
199
+ *
200
+ * Direct client-side calls to Polar.sh API using OAT authentication
201
+ * Note: OAT tokens should only be used server-side in production
202
+ * This service is intended for server-side or edge function usage
203
+ */
204
+
205
+ interface PolarApiConfig {
206
+ apiToken: string;
207
+ baseUrl?: string;
208
+ }
209
+ /**
210
+ * Fetch products from Polar.sh API
211
+ *
212
+ * @param config - Polar API configuration with OAT token
213
+ * @param params - Query parameters for filtering products
214
+ * @returns Paginated list of products
215
+ *
216
+ * @example
217
+ * ```ts
218
+ * const products = await getProducts({
219
+ * apiToken: 'polar_oat_xxx',
220
+ * baseUrl: 'https://api.polar.sh/v1'
221
+ * }, {
222
+ * organizationId: 'org-123',
223
+ * isRecurring: true,
224
+ * page: 1,
225
+ * limit: 100
226
+ * });
227
+ * ```
228
+ */
229
+ declare function getProducts(config: PolarApiConfig, params?: ProductListParams): Promise<ProductListResponse>;
230
+ /**
231
+ * Fetch a single product by ID
232
+ *
233
+ * @param config - Polar API configuration
234
+ * @param productId - Product ID to fetch
235
+ * @returns Product details or null if not found
236
+ */
237
+ declare function getProductById(config: PolarApiConfig, productId: string): Promise<PolarProduct | null>;
238
+ /**
239
+ * Fetch all products with automatic pagination
240
+ *
241
+ * @param config - Polar API configuration
242
+ * @param params - Query parameters for filtering
243
+ * @returns All products matching the criteria
244
+ */
245
+ declare function getAllProducts(config: PolarApiConfig, params?: Omit<ProductListParams, 'page' | 'limit'>): Promise<PolarProduct[]>;
246
+
247
+ export { type BillingCycle, type CancelResult, type CancellationReason, type CheckoutParams, type CheckoutResult, type FirebaseAdapterConfig, type OrderItem, type PolarAdapter, type PolarApiConfig, type PolarContextValue, type PolarPrice, type PolarProduct, PolarProvider, type PriceType, type ProductListParams, type ProductListResponse, type RecurringInterval, SUBSCRIPTION_STATUS, type SubscriptionStatus, type SubscriptionStatusValue, type SyncResult, type UsePolarProductsOptions, type UsePolarProductsResult, createFirebaseAdapter, getAllProducts, getProductById, getProducts, usePolarBilling, usePolarProducts };
package/dist/index.js CHANGED
@@ -33,7 +33,11 @@ __export(index_exports, {
33
33
  PolarProvider: () => PolarProvider,
34
34
  SUBSCRIPTION_STATUS: () => SUBSCRIPTION_STATUS,
35
35
  createFirebaseAdapter: () => createFirebaseAdapter,
36
- usePolarBilling: () => usePolarBilling
36
+ getAllProducts: () => getAllProducts,
37
+ getProductById: () => getProductById,
38
+ getProducts: () => getProducts,
39
+ usePolarBilling: () => usePolarBilling,
40
+ usePolarProducts: () => usePolarProducts
37
41
  });
38
42
  module.exports = __toCommonJS(index_exports);
39
43
 
@@ -165,6 +169,123 @@ function PolarProvider({ adapter, userId, children }) {
165
169
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PolarContext.Provider, { value, children });
166
170
  }
167
171
 
172
+ // src/presentation/hooks/usePolarProducts.ts
173
+ var import_react3 = require("react");
174
+
175
+ // src/infrastructure/services/polar-api.service.ts
176
+ async function getProducts(config, params) {
177
+ const baseUrl = config.baseUrl || "https://api.polar.sh/v1";
178
+ const url = new URL(`${baseUrl}/products`);
179
+ if (params?.organizationId) {
180
+ url.searchParams.set("organization_id", params.organizationId);
181
+ }
182
+ if (params?.isRecurring !== void 0) {
183
+ url.searchParams.set("is_recurring", String(params.isRecurring));
184
+ }
185
+ if (params?.page) {
186
+ url.searchParams.set("page", String(params.page));
187
+ }
188
+ if (params?.limit) {
189
+ url.searchParams.set("limit", String(Math.min(params.limit, 100)));
190
+ }
191
+ const response = await fetch(url.toString(), {
192
+ method: "GET",
193
+ headers: {
194
+ "Authorization": `Bearer ${config.apiToken}`,
195
+ "Accept": "application/json"
196
+ }
197
+ });
198
+ if (!response.ok) {
199
+ const error = await response.text();
200
+ throw new Error(
201
+ `Polar API error: ${response.status} ${response.statusText}
202
+ ${error}`
203
+ );
204
+ }
205
+ const data = await response.json();
206
+ return data;
207
+ }
208
+ async function getProductById(config, productId) {
209
+ const baseUrl = config.baseUrl || "https://api.polar.sh/v1";
210
+ const url = `${baseUrl}/products/${productId}`;
211
+ const response = await fetch(url, {
212
+ method: "GET",
213
+ headers: {
214
+ "Authorization": `Bearer ${config.apiToken}`,
215
+ "Accept": "application/json"
216
+ }
217
+ });
218
+ if (response.status === 404) {
219
+ return null;
220
+ }
221
+ if (!response.ok) {
222
+ const error = await response.text();
223
+ throw new Error(
224
+ `Polar API error: ${response.status} ${response.statusText}
225
+ ${error}`
226
+ );
227
+ }
228
+ const data = await response.json();
229
+ return data;
230
+ }
231
+ async function getAllProducts(config, params) {
232
+ const allProducts = [];
233
+ let page = 1;
234
+ let maxPage = 1;
235
+ do {
236
+ const response = await getProducts(config, {
237
+ ...params,
238
+ page,
239
+ limit: 100
240
+ });
241
+ allProducts.push(...response.items);
242
+ maxPage = response.pagination.max_page;
243
+ page++;
244
+ } while (page <= maxPage);
245
+ return allProducts;
246
+ }
247
+
248
+ // src/presentation/hooks/usePolarProducts.ts
249
+ function usePolarProducts(options) {
250
+ const { apiToken, organizationId, isRecurring = true, enabled = true } = options;
251
+ const [products, setProducts] = (0, import_react3.useState)([]);
252
+ const [loading, setLoading] = (0, import_react3.useState)(true);
253
+ const [error, setError] = (0, import_react3.useState)(null);
254
+ const fetchProducts = async () => {
255
+ if (!enabled) {
256
+ setLoading(false);
257
+ return;
258
+ }
259
+ try {
260
+ setLoading(true);
261
+ setError(null);
262
+ const params = {
263
+ isRecurring
264
+ };
265
+ if (organizationId) {
266
+ params.organizationId = organizationId;
267
+ }
268
+ const data = await getAllProducts({ apiToken }, params);
269
+ setProducts(data);
270
+ } catch (err) {
271
+ const message = err instanceof Error ? err.message : "Failed to fetch products";
272
+ setError(message);
273
+ console.error("usePolarProducts error:", err);
274
+ } finally {
275
+ setLoading(false);
276
+ }
277
+ };
278
+ (0, import_react3.useEffect)(() => {
279
+ fetchProducts();
280
+ }, [apiToken, organizationId, isRecurring, enabled]);
281
+ return {
282
+ products,
283
+ loading,
284
+ error,
285
+ refetch: fetchProducts
286
+ };
287
+ }
288
+
168
289
  // src/infrastructure/constants/billing.constants.ts
169
290
  var SUBSCRIPTION_STATUS = Object.freeze({
170
291
  ACTIVE: "active",
@@ -317,5 +438,9 @@ function createFirebaseAdapter(config) {
317
438
  PolarProvider,
318
439
  SUBSCRIPTION_STATUS,
319
440
  createFirebaseAdapter,
320
- usePolarBilling
441
+ getAllProducts,
442
+ getProductById,
443
+ getProducts,
444
+ usePolarBilling,
445
+ usePolarProducts
321
446
  });
package/dist/index.mjs CHANGED
@@ -132,6 +132,123 @@ function PolarProvider({ adapter, userId, children }) {
132
132
  return /* @__PURE__ */ jsx(PolarContext.Provider, { value, children });
133
133
  }
134
134
 
135
+ // src/presentation/hooks/usePolarProducts.ts
136
+ import { useState as useState2, useEffect as useEffect2 } from "react";
137
+
138
+ // src/infrastructure/services/polar-api.service.ts
139
+ async function getProducts(config, params) {
140
+ const baseUrl = config.baseUrl || "https://api.polar.sh/v1";
141
+ const url = new URL(`${baseUrl}/products`);
142
+ if (params?.organizationId) {
143
+ url.searchParams.set("organization_id", params.organizationId);
144
+ }
145
+ if (params?.isRecurring !== void 0) {
146
+ url.searchParams.set("is_recurring", String(params.isRecurring));
147
+ }
148
+ if (params?.page) {
149
+ url.searchParams.set("page", String(params.page));
150
+ }
151
+ if (params?.limit) {
152
+ url.searchParams.set("limit", String(Math.min(params.limit, 100)));
153
+ }
154
+ const response = await fetch(url.toString(), {
155
+ method: "GET",
156
+ headers: {
157
+ "Authorization": `Bearer ${config.apiToken}`,
158
+ "Accept": "application/json"
159
+ }
160
+ });
161
+ if (!response.ok) {
162
+ const error = await response.text();
163
+ throw new Error(
164
+ `Polar API error: ${response.status} ${response.statusText}
165
+ ${error}`
166
+ );
167
+ }
168
+ const data = await response.json();
169
+ return data;
170
+ }
171
+ async function getProductById(config, productId) {
172
+ const baseUrl = config.baseUrl || "https://api.polar.sh/v1";
173
+ const url = `${baseUrl}/products/${productId}`;
174
+ const response = await fetch(url, {
175
+ method: "GET",
176
+ headers: {
177
+ "Authorization": `Bearer ${config.apiToken}`,
178
+ "Accept": "application/json"
179
+ }
180
+ });
181
+ if (response.status === 404) {
182
+ return null;
183
+ }
184
+ if (!response.ok) {
185
+ const error = await response.text();
186
+ throw new Error(
187
+ `Polar API error: ${response.status} ${response.statusText}
188
+ ${error}`
189
+ );
190
+ }
191
+ const data = await response.json();
192
+ return data;
193
+ }
194
+ async function getAllProducts(config, params) {
195
+ const allProducts = [];
196
+ let page = 1;
197
+ let maxPage = 1;
198
+ do {
199
+ const response = await getProducts(config, {
200
+ ...params,
201
+ page,
202
+ limit: 100
203
+ });
204
+ allProducts.push(...response.items);
205
+ maxPage = response.pagination.max_page;
206
+ page++;
207
+ } while (page <= maxPage);
208
+ return allProducts;
209
+ }
210
+
211
+ // src/presentation/hooks/usePolarProducts.ts
212
+ function usePolarProducts(options) {
213
+ const { apiToken, organizationId, isRecurring = true, enabled = true } = options;
214
+ const [products, setProducts] = useState2([]);
215
+ const [loading, setLoading] = useState2(true);
216
+ const [error, setError] = useState2(null);
217
+ const fetchProducts = async () => {
218
+ if (!enabled) {
219
+ setLoading(false);
220
+ return;
221
+ }
222
+ try {
223
+ setLoading(true);
224
+ setError(null);
225
+ const params = {
226
+ isRecurring
227
+ };
228
+ if (organizationId) {
229
+ params.organizationId = organizationId;
230
+ }
231
+ const data = await getAllProducts({ apiToken }, params);
232
+ setProducts(data);
233
+ } catch (err) {
234
+ const message = err instanceof Error ? err.message : "Failed to fetch products";
235
+ setError(message);
236
+ console.error("usePolarProducts error:", err);
237
+ } finally {
238
+ setLoading(false);
239
+ }
240
+ };
241
+ useEffect2(() => {
242
+ fetchProducts();
243
+ }, [apiToken, organizationId, isRecurring, enabled]);
244
+ return {
245
+ products,
246
+ loading,
247
+ error,
248
+ refetch: fetchProducts
249
+ };
250
+ }
251
+
135
252
  // src/infrastructure/constants/billing.constants.ts
136
253
  var SUBSCRIPTION_STATUS = Object.freeze({
137
254
  ACTIVE: "active",
@@ -283,5 +400,9 @@ export {
283
400
  PolarProvider,
284
401
  SUBSCRIPTION_STATUS,
285
402
  createFirebaseAdapter,
286
- usePolarBilling
403
+ getAllProducts,
404
+ getProductById,
405
+ getProducts,
406
+ usePolarBilling,
407
+ usePolarProducts
287
408
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/web-polar-payment",
3
- "version": "1.0.18",
3
+ "version": "1.0.20",
4
4
  "description": "Universal Polar.sh subscription billing — Firebase adapter",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Product Entity
3
+ *
4
+ * Represents a Polar.sh product with pricing information
5
+ */
6
+
7
+ export type RecurringInterval = 'day' | 'week' | 'month' | 'year';
8
+
9
+ export type PriceType = 'fixed' | 'custom' | 'free' | 'seat_based' | 'metered_unit';
10
+
11
+ export interface PolarPrice {
12
+ id: string;
13
+ type: PriceType;
14
+ amount_type: string;
15
+ amount?: number;
16
+ currency: string;
17
+ recurring_interval?: RecurringInterval;
18
+ is_archived: boolean;
19
+ product_id: string;
20
+ legacy?: boolean;
21
+ }
22
+
23
+ export interface PolarProduct {
24
+ id: string;
25
+ name: string;
26
+ description: string | null;
27
+ is_recurring: boolean;
28
+ recurring_interval: RecurringInterval | null;
29
+ is_archived: boolean;
30
+ organization_id: string;
31
+ prices: PolarPrice[];
32
+ benefits: Array<{
33
+ id: string;
34
+ type: string;
35
+ description: string | null;
36
+ properties: Record<string, unknown>;
37
+ }>;
38
+ metadata: Record<string, unknown>;
39
+ created_at: string;
40
+ modified_at: string | null;
41
+ }
42
+
43
+ export interface ProductListResponse {
44
+ items: PolarProduct[];
45
+ pagination: {
46
+ total_count: number;
47
+ max_page: number;
48
+ };
49
+ }
50
+
51
+ export interface ProductListParams {
52
+ organizationId?: string;
53
+ isRecurring?: boolean;
54
+ page?: number;
55
+ limit?: number;
56
+ }
package/src/index.ts CHANGED
@@ -1,9 +1,16 @@
1
1
  export { PolarProvider } from './presentation/components/PolarProvider';
2
2
  export { usePolarBilling } from './presentation/hooks/usePolarBilling';
3
+ export { usePolarProducts } from './presentation/hooks/usePolarProducts';
3
4
  export type { PolarContextValue } from './presentation/hooks/usePolarBilling';
5
+ export type { UsePolarProductsOptions, UsePolarProductsResult } from './presentation/hooks/usePolarProducts';
4
6
  export { createFirebaseAdapter } from './infrastructure/services/firebase-billing.service';
5
7
  export type { FirebaseAdapterConfig } from './infrastructure/services/firebase-billing.service';
6
8
  export { SUBSCRIPTION_STATUS } from './infrastructure/constants/billing.constants';
7
9
  export type { SubscriptionStatusValue, BillingCycle } from './domain/entities/subscription.entity';
8
10
  export type { SubscriptionStatus, CheckoutParams, CheckoutResult, OrderItem, CancellationReason, CancelResult, SyncResult } from './domain/entities';
9
11
  export type { PolarAdapter } from './domain/interfaces';
12
+
13
+ // Product API
14
+ export { getProducts, getProductById, getAllProducts } from './infrastructure/services/polar-api.service';
15
+ export type { PolarApiConfig } from './infrastructure/services/polar-api.service';
16
+ export type { PolarProduct, PolarPrice, ProductListResponse, ProductListParams, RecurringInterval, PriceType } from './domain/entities/product.entity';
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Polar API Service
3
+ *
4
+ * Direct client-side calls to Polar.sh API using OAT authentication
5
+ * Note: OAT tokens should only be used server-side in production
6
+ * This service is intended for server-side or edge function usage
7
+ */
8
+
9
+ import type {
10
+ PolarProduct,
11
+ ProductListResponse,
12
+ ProductListParams
13
+ } from '../../domain/entities/product.entity';
14
+
15
+ export interface PolarApiConfig {
16
+ apiToken: string;
17
+ baseUrl?: string;
18
+ }
19
+
20
+ /**
21
+ * Fetch products from Polar.sh API
22
+ *
23
+ * @param config - Polar API configuration with OAT token
24
+ * @param params - Query parameters for filtering products
25
+ * @returns Paginated list of products
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * const products = await getProducts({
30
+ * apiToken: 'polar_oat_xxx',
31
+ * baseUrl: 'https://api.polar.sh/v1'
32
+ * }, {
33
+ * organizationId: 'org-123',
34
+ * isRecurring: true,
35
+ * page: 1,
36
+ * limit: 100
37
+ * });
38
+ * ```
39
+ */
40
+ export async function getProducts(
41
+ config: PolarApiConfig,
42
+ params?: ProductListParams
43
+ ): Promise<ProductListResponse> {
44
+ const baseUrl = config.baseUrl || 'https://api.polar.sh/v1';
45
+ const url = new URL(`${baseUrl}/products`);
46
+
47
+ if (params?.organizationId) {
48
+ url.searchParams.set('organization_id', params.organizationId);
49
+ }
50
+
51
+ if (params?.isRecurring !== undefined) {
52
+ url.searchParams.set('is_recurring', String(params.isRecurring));
53
+ }
54
+
55
+ if (params?.page) {
56
+ url.searchParams.set('page', String(params.page));
57
+ }
58
+
59
+ if (params?.limit) {
60
+ url.searchParams.set('limit', String(Math.min(params.limit, 100)));
61
+ }
62
+
63
+ const response = await fetch(url.toString(), {
64
+ method: 'GET',
65
+ headers: {
66
+ 'Authorization': `Bearer ${config.apiToken}`,
67
+ 'Accept': 'application/json',
68
+ },
69
+ });
70
+
71
+ if (!response.ok) {
72
+ const error = await response.text();
73
+ throw new Error(
74
+ `Polar API error: ${response.status} ${response.statusText}\n${error}`
75
+ );
76
+ }
77
+
78
+ const data = await response.json();
79
+ return data as ProductListResponse;
80
+ }
81
+
82
+ /**
83
+ * Fetch a single product by ID
84
+ *
85
+ * @param config - Polar API configuration
86
+ * @param productId - Product ID to fetch
87
+ * @returns Product details or null if not found
88
+ */
89
+ export async function getProductById(
90
+ config: PolarApiConfig,
91
+ productId: string
92
+ ): Promise<PolarProduct | null> {
93
+ const baseUrl = config.baseUrl || 'https://api.polar.sh/v1';
94
+ const url = `${baseUrl}/products/${productId}`;
95
+
96
+ const response = await fetch(url, {
97
+ method: 'GET',
98
+ headers: {
99
+ 'Authorization': `Bearer ${config.apiToken}`,
100
+ 'Accept': 'application/json',
101
+ },
102
+ });
103
+
104
+ if (response.status === 404) {
105
+ return null;
106
+ }
107
+
108
+ if (!response.ok) {
109
+ const error = await response.text();
110
+ throw new Error(
111
+ `Polar API error: ${response.status} ${response.statusText}\n${error}`
112
+ );
113
+ }
114
+
115
+ const data = await response.json();
116
+ return data as PolarProduct;
117
+ }
118
+
119
+ /**
120
+ * Fetch all products with automatic pagination
121
+ *
122
+ * @param config - Polar API configuration
123
+ * @param params - Query parameters for filtering
124
+ * @returns All products matching the criteria
125
+ */
126
+ export async function getAllProducts(
127
+ config: PolarApiConfig,
128
+ params?: Omit<ProductListParams, 'page' | 'limit'>
129
+ ): Promise<PolarProduct[]> {
130
+ const allProducts: PolarProduct[] = [];
131
+ let page = 1;
132
+ let maxPage = 1;
133
+
134
+ do {
135
+ const response = await getProducts(config, {
136
+ ...params,
137
+ page,
138
+ limit: 100,
139
+ });
140
+
141
+ allProducts.push(...response.items);
142
+ maxPage = response.pagination.max_page;
143
+ page++;
144
+ } while (page <= maxPage);
145
+
146
+ return allProducts;
147
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * usePolarProducts Hook
3
+ *
4
+ * Fetch and manage products from Polar.sh API
5
+ */
6
+
7
+ import { useState, useEffect } from 'react';
8
+ import { getAllProducts } from '../../infrastructure/services/polar-api.service';
9
+ import type { PolarProduct, ProductListParams } from '../../domain/entities/product.entity';
10
+
11
+ export interface UsePolarProductsOptions {
12
+ apiToken: string;
13
+ organizationId?: string;
14
+ isRecurring?: boolean;
15
+ enabled?: boolean;
16
+ }
17
+
18
+ export interface UsePolarProductsResult {
19
+ products: PolarProduct[];
20
+ loading: boolean;
21
+ error: string | null;
22
+ refetch: () => Promise<void>;
23
+ }
24
+
25
+ /**
26
+ * Fetch products from Polar.sh API
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * const { products, loading, error } = usePolarProducts({
31
+ * apiToken: polarApiToken,
32
+ * organizationId: polarOrgId,
33
+ * isRecurring: true,
34
+ * });
35
+ * ```
36
+ */
37
+ export function usePolarProducts(options: UsePolarProductsOptions): UsePolarProductsResult {
38
+ const { apiToken, organizationId, isRecurring = true, enabled = true } = options;
39
+
40
+ const [products, setProducts] = useState<PolarProduct[]>([]);
41
+ const [loading, setLoading] = useState(true);
42
+ const [error, setError] = useState<string | null>(null);
43
+
44
+ const fetchProducts = async () => {
45
+ if (!enabled) {
46
+ setLoading(false);
47
+ return;
48
+ }
49
+
50
+ try {
51
+ setLoading(true);
52
+ setError(null);
53
+
54
+ const params: ProductListParams = {
55
+ isRecurring,
56
+ };
57
+
58
+ if (organizationId) {
59
+ params.organizationId = organizationId;
60
+ }
61
+
62
+ const data = await getAllProducts({ apiToken }, params);
63
+ setProducts(data);
64
+ } catch (err) {
65
+ const message = err instanceof Error ? err.message : 'Failed to fetch products';
66
+ setError(message);
67
+ console.error('usePolarProducts error:', err);
68
+ } finally {
69
+ setLoading(false);
70
+ }
71
+ };
72
+
73
+ useEffect(() => {
74
+ fetchProducts();
75
+ }, [apiToken, organizationId, isRecurring, enabled]);
76
+
77
+ return {
78
+ products,
79
+ loading,
80
+ error,
81
+ refetch: fetchProducts,
82
+ };
83
+ }