@umituz/web-payment 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +349 -2
  3. package/dist/billing-adapter.interface-CDkeeowZ.d.mts +94 -0
  4. package/dist/billing-adapter.interface-m7yE50I0.d.ts +94 -0
  5. package/dist/billing-cycle.enum--hJRYlgP.d.mts +10 -0
  6. package/dist/billing-cycle.enum--hJRYlgP.d.ts +10 -0
  7. package/dist/billing-env-D4YjvA3M.d.mts +8 -0
  8. package/dist/billing-env-D4YjvA3M.d.ts +8 -0
  9. package/dist/checkout-metadata-D7Wp_DAD.d.mts +27 -0
  10. package/dist/checkout-metadata-D7Wp_DAD.d.ts +27 -0
  11. package/dist/chunk-2KV7JFYF.mjs +61 -0
  12. package/dist/chunk-2NH5DOLT.mjs +101 -0
  13. package/dist/chunk-3AXPZOCV.mjs +106 -0
  14. package/dist/chunk-4N3BASZ6.mjs +51 -0
  15. package/dist/chunk-NHKK3ENU.mjs +1128 -0
  16. package/dist/chunk-PIWNUAKW.mjs +30 -0
  17. package/dist/chunk-QFB7AOSQ.mjs +33 -0
  18. package/dist/chunk-RQTMBR74.mjs +30 -0
  19. package/dist/chunk-U45FS4FF.mjs +110 -0
  20. package/dist/chunk-X6R76FZT.mjs +0 -0
  21. package/dist/chunk-XQ2HJTTA.mjs +252 -0
  22. package/dist/core.d.mts +17 -0
  23. package/dist/core.d.ts +17 -0
  24. package/dist/core.js +425 -0
  25. package/dist/core.mjs +66 -0
  26. package/dist/http.d.mts +12 -0
  27. package/dist/http.d.ts +12 -0
  28. package/dist/http.js +115 -0
  29. package/dist/http.mjs +88 -0
  30. package/dist/index.d.mts +17 -0
  31. package/dist/index.d.ts +17 -0
  32. package/dist/index.js +1929 -0
  33. package/dist/index.mjs +153 -0
  34. package/dist/memory.d.mts +14 -0
  35. package/dist/memory.d.ts +14 -0
  36. package/dist/memory.js +130 -0
  37. package/dist/memory.mjs +6 -0
  38. package/dist/portal.d.mts +445 -0
  39. package/dist/portal.d.ts +445 -0
  40. package/dist/portal.js +2927 -0
  41. package/dist/portal.mjs +2737 -0
  42. package/dist/product.mapper-CnY1ISOp.d.ts +97 -0
  43. package/dist/product.mapper-noVl-sel.d.mts +97 -0
  44. package/dist/products.d.mts +64 -0
  45. package/dist/products.d.ts +64 -0
  46. package/dist/products.js +370 -0
  47. package/dist/products.mjs +21 -0
  48. package/dist/react.d.mts +182 -0
  49. package/dist/react.d.ts +182 -0
  50. package/dist/react.js +1874 -0
  51. package/dist/react.mjs +105 -0
  52. package/dist/server.d.mts +177 -0
  53. package/dist/server.d.ts +177 -0
  54. package/dist/server.js +622 -0
  55. package/dist/server.mjs +417 -0
  56. package/dist/visual-tokens-BCLVR85G.d.mts +94 -0
  57. package/dist/visual-tokens-BCLVR85G.d.ts +94 -0
  58. package/package.json +122 -4
  59. package/src/core.ts +81 -0
  60. package/src/domain/entities/cancellation.entity.ts +28 -0
  61. package/src/domain/entities/checkout.entity.ts +21 -0
  62. package/src/domain/entities/index.ts +10 -0
  63. package/src/domain/entities/order.entity.ts +15 -0
  64. package/src/domain/entities/product.entity.ts +16 -0
  65. package/src/domain/entities/subscription.entity.ts +24 -0
  66. package/src/domain/entities/sync.entity.ts +4 -0
  67. package/src/domain/enums/amount-type.enum.ts +10 -0
  68. package/src/domain/enums/billing-cycle.enum.ts +30 -0
  69. package/src/domain/enums/index.ts +5 -0
  70. package/src/domain/enums/payment-provider.enum.ts +14 -0
  71. package/src/domain/enums/subscription-status.enum.ts +32 -0
  72. package/src/domain/index.ts +5 -0
  73. package/src/domain/interfaces/billing-adapter.interface.ts +18 -0
  74. package/src/domain/interfaces/index.ts +1 -0
  75. package/src/domain/mappers/product.mapper.ts +176 -0
  76. package/src/domain/utils/minor-units.ts +20 -0
  77. package/src/domain/utils/percent-discount.ts +17 -0
  78. package/src/http.ts +3 -0
  79. package/src/index.ts +12 -0
  80. package/src/infrastructure/browser/hosted-checkout.ts +26 -0
  81. package/src/infrastructure/config/billing-env.ts +31 -0
  82. package/src/infrastructure/constants/billing.constants.ts +10 -0
  83. package/src/infrastructure/http/http-client.ts +83 -0
  84. package/src/infrastructure/pagination/page-iterator.ts +43 -0
  85. package/src/infrastructure/query/query-builder.ts +26 -0
  86. package/src/infrastructure/services/billing.client.ts +300 -0
  87. package/src/infrastructure/services/create-billing-client.ts +23 -0
  88. package/src/infrastructure/services/http/create-http-adapter.ts +123 -0
  89. package/src/infrastructure/services/memory/create-memory-adapter.ts +126 -0
  90. package/src/infrastructure/services/product.repository.ts +198 -0
  91. package/src/infrastructure/utils/checkout-metadata.ts +55 -0
  92. package/src/infrastructure/utils/date-renderer.ts +53 -0
  93. package/src/infrastructure/utils/money-formatter.ts +39 -0
  94. package/src/infrastructure/utils/normalization.util.ts +22 -0
  95. package/src/infrastructure/utils/pagination-clamp.ts +11 -0
  96. package/src/infrastructure/utils/receipt.util.ts +6 -0
  97. package/src/infrastructure/utils/security.util.ts +23 -0
  98. package/src/infrastructure/utils/type-guards.util.ts +23 -0
  99. package/src/infrastructure/utils/validation.util.ts +10 -0
  100. package/src/infrastructure/webhook/parse-event.ts +135 -0
  101. package/src/infrastructure/webhook/verify-webhook.ts +124 -0
  102. package/src/memory.ts +3 -0
  103. package/src/portal.ts +2 -0
  104. package/src/presentation/components/BillingHistoryList.tsx +172 -0
  105. package/src/presentation/components/BillingProvider.tsx +179 -0
  106. package/src/presentation/components/CancelSubscriptionButton.tsx +155 -0
  107. package/src/presentation/components/CheckoutButton.tsx +76 -0
  108. package/src/presentation/components/CustomerPortalLink.tsx +71 -0
  109. package/src/presentation/components/PricingTable.tsx +225 -0
  110. package/src/presentation/components/SubscriptionStatusBadge.tsx +102 -0
  111. package/src/presentation/context/billing-context.ts +30 -0
  112. package/src/presentation/hooks/useBilling.ts +3 -0
  113. package/src/presentation/hooks/useCancelSubscription.ts +47 -0
  114. package/src/presentation/hooks/useCheckoutButton.ts +49 -0
  115. package/src/presentation/hooks/useProducts.ts +76 -0
  116. package/src/presentation/navigation/navigation.strategy.ts +16 -0
  117. package/src/presentation/portal/TurnkeyBillingPage.tsx +144 -0
  118. package/src/presentation/portal/components/BillingLayout.tsx +163 -0
  119. package/src/presentation/portal/components/BillingPage.tsx +22 -0
  120. package/src/presentation/portal/components/BillingPortal.tsx +248 -0
  121. package/src/presentation/portal/components/InvoiceCard.tsx +275 -0
  122. package/src/presentation/portal/components/OverviewTab.tsx +137 -0
  123. package/src/presentation/portal/components/PaymentMethodsList.tsx +268 -0
  124. package/src/presentation/portal/components/PlanComparison.tsx +405 -0
  125. package/src/presentation/portal/components/PlanTab.tsx +197 -0
  126. package/src/presentation/portal/components/PortalButton.tsx +89 -0
  127. package/src/presentation/portal/components/PortalSpinner.tsx +30 -0
  128. package/src/presentation/portal/components/UsageCard.tsx +158 -0
  129. package/src/presentation/portal/components/index.ts +11 -0
  130. package/src/presentation/portal/constants.ts +44 -0
  131. package/src/presentation/portal/create-portal-client.ts +202 -0
  132. package/src/presentation/portal/hooks/index.ts +3 -0
  133. package/src/presentation/portal/hooks/useBilling.ts +240 -0
  134. package/src/presentation/portal/index.ts +97 -0
  135. package/src/presentation/portal/labels.tsx +218 -0
  136. package/src/presentation/portal/styles.ts +70 -0
  137. package/src/presentation/portal/types.ts +213 -0
  138. package/src/presentation/portal/utils/billing.ts +193 -0
  139. package/src/presentation/portal/utils/index.ts +27 -0
  140. package/src/presentation/portal/utils/plans.ts +71 -0
  141. package/src/presentation/theme/visual-tokens.ts +103 -0
  142. package/src/presentation/utils/cancellation-reason-catalog.ts +18 -0
  143. package/src/presentation/utils/checkout-button-visual.ts +50 -0
  144. package/src/presentation/utils/inline-error-visual.ts +21 -0
  145. package/src/presentation/utils/product-pricing.ts +33 -0
  146. package/src/presentation/utils/status-label.ts +23 -0
  147. package/src/presentation/utils/status-tone.ts +25 -0
  148. package/src/products.ts +20 -0
  149. package/src/react.tsx +63 -0
  150. package/src/server.ts +79 -0
@@ -0,0 +1,97 @@
1
+ import { b as BillingCycle } from './billing-cycle.enum--hJRYlgP.js';
2
+
3
+ declare const POLAR_AMOUNT_TYPE: Readonly<{
4
+ readonly FIXED: "fixed";
5
+ readonly CUSTOM: "custom";
6
+ readonly FREE: "free";
7
+ readonly SEAT_BASED: "seat_based";
8
+ readonly METERED_UNIT: "metered_unit";
9
+ }>;
10
+ type AmountType = (typeof POLAR_AMOUNT_TYPE)[keyof typeof POLAR_AMOUNT_TYPE];
11
+
12
+ interface ProductDto {
13
+ id: string;
14
+ name: string;
15
+ description: string | null;
16
+ is_recurring: boolean;
17
+ recurring_interval: string | null;
18
+ is_archived: boolean;
19
+ organization_id: string;
20
+ prices: ProductPriceDto[];
21
+ benefits: ProductBenefitDto[];
22
+ metadata: Record<string, unknown>;
23
+ created_at: string;
24
+ modified_at: string | null;
25
+ }
26
+ interface ProductPriceDto {
27
+ id: string;
28
+ type: string;
29
+ amount_type: string;
30
+ amount?: number;
31
+ currency: string;
32
+ recurring_interval?: string;
33
+ is_archived: boolean;
34
+ product_id: string;
35
+ legacy?: boolean;
36
+ }
37
+ interface ProductBenefitDto {
38
+ id: string;
39
+ type: string;
40
+ description: string | null;
41
+ properties: Record<string, unknown>;
42
+ }
43
+ interface ProductListResponseDto {
44
+ items: ProductDto[];
45
+ pagination: {
46
+ total_count: number;
47
+ max_page: number;
48
+ };
49
+ }
50
+ interface ProductPrice {
51
+ id: string;
52
+ type: AmountType;
53
+ amountType: AmountType;
54
+ amount?: number;
55
+ currency: string;
56
+ recurringInterval?: BillingCycle;
57
+ isArchived: boolean;
58
+ productId: string;
59
+ legacy?: boolean;
60
+ }
61
+ interface ProductBenefit {
62
+ id: string;
63
+ type: string;
64
+ description: string | null;
65
+ properties: Record<string, unknown>;
66
+ }
67
+ interface Product {
68
+ id: string;
69
+ name: string;
70
+ description: string | null;
71
+ isRecurring: boolean;
72
+ recurringInterval: BillingCycle | null;
73
+ isArchived: boolean;
74
+ organizationId: string;
75
+ prices: ProductPrice[];
76
+ benefits: ProductBenefit[];
77
+ metadata: Record<string, unknown>;
78
+ createdAt: string;
79
+ modifiedAt: string | null;
80
+ }
81
+ interface ProductListResponse {
82
+ items: Product[];
83
+ pagination: {
84
+ totalCount: number;
85
+ maxPage: number;
86
+ };
87
+ }
88
+ interface ProductListParams {
89
+ organizationId?: string;
90
+ isRecurring?: boolean;
91
+ page?: number;
92
+ limit?: number;
93
+ }
94
+ declare function mapProduct(dto: ProductDto | null | undefined): Product;
95
+ declare function mapProductList(dto: ProductListResponseDto | null | undefined): ProductListResponse;
96
+
97
+ export { type AmountType as A, POLAR_AMOUNT_TYPE as P, type Product as a, type ProductBenefit as b, type ProductBenefitDto as c, type ProductDto as d, type ProductListParams as e, type ProductListResponse as f, type ProductListResponseDto as g, type ProductPrice as h, type ProductPriceDto as i, mapProductList as j, mapProduct as m };
@@ -0,0 +1,97 @@
1
+ import { b as BillingCycle } from './billing-cycle.enum--hJRYlgP.mjs';
2
+
3
+ declare const POLAR_AMOUNT_TYPE: Readonly<{
4
+ readonly FIXED: "fixed";
5
+ readonly CUSTOM: "custom";
6
+ readonly FREE: "free";
7
+ readonly SEAT_BASED: "seat_based";
8
+ readonly METERED_UNIT: "metered_unit";
9
+ }>;
10
+ type AmountType = (typeof POLAR_AMOUNT_TYPE)[keyof typeof POLAR_AMOUNT_TYPE];
11
+
12
+ interface ProductDto {
13
+ id: string;
14
+ name: string;
15
+ description: string | null;
16
+ is_recurring: boolean;
17
+ recurring_interval: string | null;
18
+ is_archived: boolean;
19
+ organization_id: string;
20
+ prices: ProductPriceDto[];
21
+ benefits: ProductBenefitDto[];
22
+ metadata: Record<string, unknown>;
23
+ created_at: string;
24
+ modified_at: string | null;
25
+ }
26
+ interface ProductPriceDto {
27
+ id: string;
28
+ type: string;
29
+ amount_type: string;
30
+ amount?: number;
31
+ currency: string;
32
+ recurring_interval?: string;
33
+ is_archived: boolean;
34
+ product_id: string;
35
+ legacy?: boolean;
36
+ }
37
+ interface ProductBenefitDto {
38
+ id: string;
39
+ type: string;
40
+ description: string | null;
41
+ properties: Record<string, unknown>;
42
+ }
43
+ interface ProductListResponseDto {
44
+ items: ProductDto[];
45
+ pagination: {
46
+ total_count: number;
47
+ max_page: number;
48
+ };
49
+ }
50
+ interface ProductPrice {
51
+ id: string;
52
+ type: AmountType;
53
+ amountType: AmountType;
54
+ amount?: number;
55
+ currency: string;
56
+ recurringInterval?: BillingCycle;
57
+ isArchived: boolean;
58
+ productId: string;
59
+ legacy?: boolean;
60
+ }
61
+ interface ProductBenefit {
62
+ id: string;
63
+ type: string;
64
+ description: string | null;
65
+ properties: Record<string, unknown>;
66
+ }
67
+ interface Product {
68
+ id: string;
69
+ name: string;
70
+ description: string | null;
71
+ isRecurring: boolean;
72
+ recurringInterval: BillingCycle | null;
73
+ isArchived: boolean;
74
+ organizationId: string;
75
+ prices: ProductPrice[];
76
+ benefits: ProductBenefit[];
77
+ metadata: Record<string, unknown>;
78
+ createdAt: string;
79
+ modifiedAt: string | null;
80
+ }
81
+ interface ProductListResponse {
82
+ items: Product[];
83
+ pagination: {
84
+ totalCount: number;
85
+ maxPage: number;
86
+ };
87
+ }
88
+ interface ProductListParams {
89
+ organizationId?: string;
90
+ isRecurring?: boolean;
91
+ page?: number;
92
+ limit?: number;
93
+ }
94
+ declare function mapProduct(dto: ProductDto | null | undefined): Product;
95
+ declare function mapProductList(dto: ProductListResponseDto | null | undefined): ProductListResponse;
96
+
97
+ export { type AmountType as A, POLAR_AMOUNT_TYPE as P, type Product as a, type ProductBenefit as b, type ProductBenefitDto as c, type ProductDto as d, type ProductListParams as e, type ProductListResponse as f, type ProductListResponseDto as g, type ProductPrice as h, type ProductPriceDto as i, mapProductList as j, mapProduct as m };
@@ -0,0 +1,64 @@
1
+ import { e as ProductListParams, f as ProductListResponse, a as Product } from './product.mapper-noVl-sel.mjs';
2
+ import './billing-cycle.enum--hJRYlgP.mjs';
3
+
4
+ interface HttpRequest {
5
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
6
+ url: string;
7
+ headers?: Record<string, string>;
8
+ body?: unknown;
9
+ signal?: AbortSignal;
10
+ timeoutMs?: number;
11
+ }
12
+ interface HttpResponse<T> {
13
+ status: number;
14
+ ok: boolean;
15
+ data: T | null;
16
+ text(): Promise<string>;
17
+ }
18
+ interface HttpClient {
19
+ request<T>(request: HttpRequest): Promise<HttpResponse<T>>;
20
+ }
21
+ declare class FetchHttpClient implements HttpClient {
22
+ request<T>(request: HttpRequest): Promise<HttpResponse<T>>;
23
+ }
24
+
25
+ interface ProductApiConfig {
26
+ apiToken: string;
27
+ baseUrl?: string;
28
+ }
29
+ interface ProductRequestOptions {
30
+ signal?: AbortSignal;
31
+ timeoutMs?: number;
32
+ }
33
+ declare class BillingApiError extends Error {
34
+ readonly status: number;
35
+ readonly body?: string | undefined;
36
+ constructor(message: string, status: number, body?: string | undefined);
37
+ }
38
+ declare function isBillingApiError(error: unknown): error is BillingApiError;
39
+ declare class ProductRepository {
40
+ private readonly http;
41
+ private readonly apiToken;
42
+ private readonly baseUrl;
43
+ constructor(config: ProductApiConfig, http?: HttpClient);
44
+ list(params?: ProductListParams, options?: ProductRequestOptions): Promise<ProductListResponse>;
45
+ findById(productId: string, options?: ProductRequestOptions): Promise<Product | null>;
46
+ iterateAll(params?: Omit<ProductListParams, 'page' | 'limit'>, options?: {
47
+ limit?: number;
48
+ signal?: AbortSignal;
49
+ }): AsyncIterable<Product>;
50
+ listAll(params?: Omit<ProductListParams, 'page' | 'limit'>, options?: {
51
+ limit?: number;
52
+ signal?: AbortSignal;
53
+ }): Promise<Product[]>;
54
+ private authHeaders;
55
+ private clampLimit;
56
+ }
57
+ declare function getProducts(config: ProductApiConfig, params?: ProductListParams, options?: ProductRequestOptions): Promise<ProductListResponse>;
58
+ declare function getProductById(config: ProductApiConfig, productId: string, options?: ProductRequestOptions): Promise<Product | null>;
59
+ declare function getAllProducts(config: ProductApiConfig, params?: Omit<ProductListParams, 'page' | 'limit'>, options?: {
60
+ limit?: number;
61
+ signal?: AbortSignal;
62
+ }): Promise<Product[]>;
63
+
64
+ export { BillingApiError, FetchHttpClient, type HttpClient, type HttpRequest, type HttpResponse, type ProductApiConfig, ProductRepository, type ProductRequestOptions, getAllProducts, getProductById, getProducts, isBillingApiError };
@@ -0,0 +1,64 @@
1
+ import { e as ProductListParams, f as ProductListResponse, a as Product } from './product.mapper-CnY1ISOp.js';
2
+ import './billing-cycle.enum--hJRYlgP.js';
3
+
4
+ interface HttpRequest {
5
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
6
+ url: string;
7
+ headers?: Record<string, string>;
8
+ body?: unknown;
9
+ signal?: AbortSignal;
10
+ timeoutMs?: number;
11
+ }
12
+ interface HttpResponse<T> {
13
+ status: number;
14
+ ok: boolean;
15
+ data: T | null;
16
+ text(): Promise<string>;
17
+ }
18
+ interface HttpClient {
19
+ request<T>(request: HttpRequest): Promise<HttpResponse<T>>;
20
+ }
21
+ declare class FetchHttpClient implements HttpClient {
22
+ request<T>(request: HttpRequest): Promise<HttpResponse<T>>;
23
+ }
24
+
25
+ interface ProductApiConfig {
26
+ apiToken: string;
27
+ baseUrl?: string;
28
+ }
29
+ interface ProductRequestOptions {
30
+ signal?: AbortSignal;
31
+ timeoutMs?: number;
32
+ }
33
+ declare class BillingApiError extends Error {
34
+ readonly status: number;
35
+ readonly body?: string | undefined;
36
+ constructor(message: string, status: number, body?: string | undefined);
37
+ }
38
+ declare function isBillingApiError(error: unknown): error is BillingApiError;
39
+ declare class ProductRepository {
40
+ private readonly http;
41
+ private readonly apiToken;
42
+ private readonly baseUrl;
43
+ constructor(config: ProductApiConfig, http?: HttpClient);
44
+ list(params?: ProductListParams, options?: ProductRequestOptions): Promise<ProductListResponse>;
45
+ findById(productId: string, options?: ProductRequestOptions): Promise<Product | null>;
46
+ iterateAll(params?: Omit<ProductListParams, 'page' | 'limit'>, options?: {
47
+ limit?: number;
48
+ signal?: AbortSignal;
49
+ }): AsyncIterable<Product>;
50
+ listAll(params?: Omit<ProductListParams, 'page' | 'limit'>, options?: {
51
+ limit?: number;
52
+ signal?: AbortSignal;
53
+ }): Promise<Product[]>;
54
+ private authHeaders;
55
+ private clampLimit;
56
+ }
57
+ declare function getProducts(config: ProductApiConfig, params?: ProductListParams, options?: ProductRequestOptions): Promise<ProductListResponse>;
58
+ declare function getProductById(config: ProductApiConfig, productId: string, options?: ProductRequestOptions): Promise<Product | null>;
59
+ declare function getAllProducts(config: ProductApiConfig, params?: Omit<ProductListParams, 'page' | 'limit'>, options?: {
60
+ limit?: number;
61
+ signal?: AbortSignal;
62
+ }): Promise<Product[]>;
63
+
64
+ export { BillingApiError, FetchHttpClient, type HttpClient, type HttpRequest, type HttpResponse, type ProductApiConfig, ProductRepository, type ProductRequestOptions, getAllProducts, getProductById, getProducts, isBillingApiError };
@@ -0,0 +1,370 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/products.ts
21
+ var products_exports = {};
22
+ __export(products_exports, {
23
+ BillingApiError: () => BillingApiError,
24
+ FetchHttpClient: () => FetchHttpClient,
25
+ ProductRepository: () => ProductRepository,
26
+ getAllProducts: () => getAllProducts,
27
+ getProductById: () => getProductById,
28
+ getProducts: () => getProducts,
29
+ isBillingApiError: () => isBillingApiError
30
+ });
31
+ module.exports = __toCommonJS(products_exports);
32
+
33
+ // src/infrastructure/constants/billing.constants.ts
34
+ var API_BASE_URL = "https://api.polar.sh/v1";
35
+ var API_MAX_PAGE_SIZE = 100;
36
+
37
+ // src/infrastructure/http/http-client.ts
38
+ var DEFAULT_TIMEOUT_MS = 15e3;
39
+ var FetchHttpClient = class {
40
+ async request(request) {
41
+ const init = {
42
+ method: request.method,
43
+ headers: {
44
+ Accept: "application/json",
45
+ ...request.headers ?? {}
46
+ }
47
+ };
48
+ if (request.body !== void 0) {
49
+ init.body = JSON.stringify(request.body);
50
+ init.headers["Content-Type"] = "application/json";
51
+ }
52
+ const timeoutMs = request.timeoutMs ?? DEFAULT_TIMEOUT_MS;
53
+ const controller = new AbortController();
54
+ let timedOut = false;
55
+ const timer = setTimeout(() => {
56
+ timedOut = true;
57
+ controller.abort();
58
+ }, timeoutMs);
59
+ const forwardAbort = () => controller.abort();
60
+ if (request.signal) {
61
+ if (request.signal.aborted) controller.abort();
62
+ else request.signal.addEventListener("abort", forwardAbort);
63
+ }
64
+ init.signal = controller.signal;
65
+ let response;
66
+ try {
67
+ response = await fetch(request.url, init);
68
+ } catch (error) {
69
+ if (timedOut && error instanceof Error && error.name === "AbortError") {
70
+ throw new DOMException(`Request timed out after ${timeoutMs}ms`, "TimeoutError");
71
+ }
72
+ throw error;
73
+ } finally {
74
+ clearTimeout(timer);
75
+ request.signal?.removeEventListener("abort", forwardAbort);
76
+ }
77
+ const bodyText = await response.text();
78
+ let data = null;
79
+ if (bodyText.length > 0) {
80
+ try {
81
+ data = JSON.parse(bodyText);
82
+ } catch {
83
+ data = null;
84
+ }
85
+ }
86
+ return {
87
+ status: response.status,
88
+ ok: response.ok,
89
+ data,
90
+ text: async () => bodyText
91
+ };
92
+ }
93
+ };
94
+
95
+ // src/infrastructure/query/query-builder.ts
96
+ var QueryBuilder = class {
97
+ params;
98
+ constructor(initial) {
99
+ this.params = new URLSearchParams();
100
+ if (initial) this.setMany(initial);
101
+ }
102
+ set(key, value) {
103
+ if (value === void 0 || value === null) return this;
104
+ this.params.set(key, String(value));
105
+ return this;
106
+ }
107
+ setMany(values) {
108
+ for (const [key, value] of Object.entries(values)) {
109
+ this.set(key, value);
110
+ }
111
+ return this;
112
+ }
113
+ toString() {
114
+ return this.params.toString();
115
+ }
116
+ };
117
+
118
+ // src/infrastructure/pagination/page-iterator.ts
119
+ var PageIterator = class {
120
+ fetcher;
121
+ limit;
122
+ signal;
123
+ constructor(fetcher, options = {}) {
124
+ this.fetcher = fetcher;
125
+ this.limit = options.limit ?? 100;
126
+ this.signal = options.signal;
127
+ }
128
+ async *[Symbol.asyncIterator]() {
129
+ let page = 1;
130
+ while (true) {
131
+ if (this.signal?.aborted) return;
132
+ const current = await this.fetcher.fetchPage(page, this.limit);
133
+ for (const item of current.items) {
134
+ if (this.signal?.aborted) return;
135
+ yield item;
136
+ }
137
+ if (current.isLast || page >= current.maxPage) return;
138
+ page++;
139
+ }
140
+ }
141
+ };
142
+
143
+ // src/infrastructure/utils/pagination-clamp.ts
144
+ var MIN_PAGE_SIZE = 1;
145
+ var PaginationClamp = class {
146
+ static pageSize(raw, max = API_MAX_PAGE_SIZE) {
147
+ if (raw === void 0) return max;
148
+ return Math.min(Math.max(MIN_PAGE_SIZE, raw), max);
149
+ }
150
+ };
151
+
152
+ // src/domain/enums/billing-cycle.enum.ts
153
+ var BILLING_CYCLE = Object.freeze({
154
+ MONTHLY: "monthly",
155
+ YEARLY: "yearly"
156
+ });
157
+ var BILLING_CYCLE_VALUES = Object.freeze([
158
+ BILLING_CYCLE.MONTHLY,
159
+ BILLING_CYCLE.YEARLY
160
+ ]);
161
+ var CANONICAL_BILLING_CYCLES = new Set(BILLING_CYCLE_VALUES);
162
+ var BILLING_CYCLE_ALIASES = /* @__PURE__ */ new Map([
163
+ [BILLING_CYCLE.MONTHLY, BILLING_CYCLE.MONTHLY],
164
+ ["month", BILLING_CYCLE.MONTHLY],
165
+ [BILLING_CYCLE.YEARLY, BILLING_CYCLE.YEARLY],
166
+ ["year", BILLING_CYCLE.YEARLY]
167
+ ]);
168
+ function parseBillingCycle(value) {
169
+ if (typeof value !== "string") return null;
170
+ return BILLING_CYCLE_ALIASES.get(value.toLowerCase()) ?? null;
171
+ }
172
+
173
+ // src/domain/mappers/product.mapper.ts
174
+ var KNOWN_AMOUNT_TYPES = /* @__PURE__ */ new Set([
175
+ "fixed",
176
+ "custom",
177
+ "free",
178
+ "seat_based",
179
+ "metered_unit"
180
+ ]);
181
+ function toAmountType(raw) {
182
+ if (raw && KNOWN_AMOUNT_TYPES.has(raw)) return raw;
183
+ return "fixed";
184
+ }
185
+ function toBillingCycle(interval) {
186
+ return parseBillingCycle(interval);
187
+ }
188
+ function asRecord(value) {
189
+ return value && typeof value === "object" ? value : {};
190
+ }
191
+ function toPrice(dto) {
192
+ return {
193
+ id: String(dto.id),
194
+ type: toAmountType(dto.type),
195
+ amountType: toAmountType(dto.amount_type),
196
+ amount: typeof dto.amount === "number" ? dto.amount : void 0,
197
+ currency: String(dto.currency),
198
+ recurringInterval: toBillingCycle(dto.recurring_interval) ?? void 0,
199
+ isArchived: Boolean(dto.is_archived),
200
+ productId: String(dto.product_id),
201
+ legacy: Boolean(dto.legacy)
202
+ };
203
+ }
204
+ function toBenefit(dto) {
205
+ return {
206
+ id: String(dto.id),
207
+ type: String(dto.type),
208
+ description: dto.description ?? null,
209
+ properties: asRecord(dto.properties)
210
+ };
211
+ }
212
+ function mapProduct(dto) {
213
+ if (!dto || typeof dto !== "object") {
214
+ throw new TypeError("mapProduct: expected a product DTO object");
215
+ }
216
+ return {
217
+ id: String(dto.id),
218
+ name: String(dto.name),
219
+ description: dto.description ?? null,
220
+ isRecurring: Boolean(dto.is_recurring),
221
+ recurringInterval: toBillingCycle(dto.recurring_interval),
222
+ isArchived: Boolean(dto.is_archived),
223
+ organizationId: String(dto.organization_id),
224
+ prices: Array.isArray(dto.prices) ? dto.prices.map(toPrice) : [],
225
+ benefits: Array.isArray(dto.benefits) ? dto.benefits.map(toBenefit) : [],
226
+ metadata: asRecord(dto.metadata),
227
+ createdAt: String(dto.created_at),
228
+ modifiedAt: dto.modified_at ?? null
229
+ };
230
+ }
231
+ function mapProductList(dto) {
232
+ if (!dto || typeof dto !== "object") {
233
+ return { items: [], pagination: { totalCount: 0, maxPage: 1 } };
234
+ }
235
+ return {
236
+ items: Array.isArray(dto.items) ? dto.items.map(mapProduct) : [],
237
+ pagination: {
238
+ totalCount: Number(dto.pagination?.total_count ?? 0),
239
+ maxPage: Number(dto.pagination?.max_page ?? 1)
240
+ }
241
+ };
242
+ }
243
+
244
+ // src/infrastructure/services/product.repository.ts
245
+ var BillingApiError = class _BillingApiError extends Error {
246
+ constructor(message, status, body) {
247
+ super(message);
248
+ this.status = status;
249
+ this.body = body;
250
+ this.name = "BillingApiError";
251
+ Object.setPrototypeOf(this, _BillingApiError.prototype);
252
+ }
253
+ };
254
+ function isBillingApiError(error) {
255
+ return error instanceof BillingApiError;
256
+ }
257
+ var ProductRepository = class {
258
+ http;
259
+ apiToken;
260
+ baseUrl;
261
+ constructor(config, http = new FetchHttpClient()) {
262
+ this.http = http;
263
+ this.apiToken = config.apiToken;
264
+ this.baseUrl = config.baseUrl ?? API_BASE_URL;
265
+ }
266
+ async list(params, options) {
267
+ const query = new QueryBuilder({
268
+ organization_id: params?.organizationId,
269
+ is_recurring: params?.isRecurring,
270
+ page: params?.page,
271
+ limit: this.clampLimit(params?.limit)
272
+ });
273
+ const url = `${this.baseUrl}/products?${query.toString()}`;
274
+ const response = await this.http.request({
275
+ method: "GET",
276
+ url,
277
+ headers: this.authHeaders(),
278
+ signal: options?.signal,
279
+ timeoutMs: options?.timeoutMs
280
+ });
281
+ if (!response.ok) {
282
+ const body = await response.text();
283
+ throw new BillingApiError(
284
+ `Billing API error: ${response.status}`,
285
+ response.status,
286
+ body
287
+ );
288
+ }
289
+ return mapProductList(response.data);
290
+ }
291
+ async findById(productId, options) {
292
+ const url = `${this.baseUrl}/products/${encodeURIComponent(productId)}`;
293
+ const response = await this.http.request({
294
+ method: "GET",
295
+ url,
296
+ headers: this.authHeaders(),
297
+ signal: options?.signal,
298
+ timeoutMs: options?.timeoutMs
299
+ });
300
+ if (response.status === 404) return null;
301
+ if (!response.ok) {
302
+ const body = await response.text();
303
+ throw new BillingApiError(
304
+ `Billing API error: ${response.status}`,
305
+ response.status,
306
+ body
307
+ );
308
+ }
309
+ if (!response.data) {
310
+ throw new BillingApiError(
311
+ "Billing API returned an empty product body",
312
+ response.status
313
+ );
314
+ }
315
+ return mapProduct(response.data);
316
+ }
317
+ iterateAll(params, options = {}) {
318
+ const limit = this.clampLimit(options.limit);
319
+ const fetcher = {
320
+ fetchPage: async (page, pageLimit) => {
321
+ const response = await this.list(
322
+ { ...params, page, limit: pageLimit },
323
+ { signal: options.signal }
324
+ );
325
+ const pageData = {
326
+ items: response.items,
327
+ page,
328
+ totalCount: response.pagination.totalCount,
329
+ maxPage: response.pagination.maxPage,
330
+ isLast: page >= response.pagination.maxPage
331
+ };
332
+ return pageData;
333
+ }
334
+ };
335
+ return new PageIterator(fetcher, { limit, signal: options.signal });
336
+ }
337
+ async listAll(params, options = {}) {
338
+ const out = [];
339
+ for await (const product of this.iterateAll(params, options)) {
340
+ out.push(product);
341
+ }
342
+ return out;
343
+ }
344
+ authHeaders() {
345
+ return { Authorization: `Bearer ${this.apiToken}` };
346
+ }
347
+ clampLimit(limit) {
348
+ return PaginationClamp.pageSize(limit, API_MAX_PAGE_SIZE);
349
+ }
350
+ };
351
+ var defaultClient = new FetchHttpClient();
352
+ async function getProducts(config, params, options) {
353
+ return new ProductRepository(config, defaultClient).list(params, options);
354
+ }
355
+ async function getProductById(config, productId, options) {
356
+ return new ProductRepository(config, defaultClient).findById(productId, options);
357
+ }
358
+ async function getAllProducts(config, params, options = {}) {
359
+ return new ProductRepository(config, defaultClient).listAll(params, options);
360
+ }
361
+ // Annotate the CommonJS export names for ESM import in node:
362
+ 0 && (module.exports = {
363
+ BillingApiError,
364
+ FetchHttpClient,
365
+ ProductRepository,
366
+ getAllProducts,
367
+ getProductById,
368
+ getProducts,
369
+ isBillingApiError
370
+ });