@umituz/web-payment 0.0.0-stage → 1.0.1

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
package/dist/core.js ADDED
@@ -0,0 +1,425 @@
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/core.ts
21
+ var core_exports = {};
22
+ __export(core_exports, {
23
+ BILLING_CYCLE: () => BILLING_CYCLE,
24
+ BILLING_CYCLE_VALUES: () => BILLING_CYCLE_VALUES,
25
+ CANCELLATION_REASON: () => CANCELLATION_REASON,
26
+ CANCELLATION_REASON_VALUES: () => CANCELLATION_REASON_VALUES,
27
+ FREE_SUBSCRIPTION_STATUS: () => FREE_SUBSCRIPTION_STATUS,
28
+ PAYMENT_PROVIDER: () => PAYMENT_PROVIDER,
29
+ PAYMENT_PROVIDER_VALUES: () => PAYMENT_PROVIDER_VALUES,
30
+ POLAR_AMOUNT_TYPE: () => POLAR_AMOUNT_TYPE,
31
+ SUBSCRIPTION_STATUS: () => SUBSCRIPTION_STATUS,
32
+ SUBSCRIPTION_STATUS_VALUES: () => SUBSCRIPTION_STATUS_VALUES,
33
+ applyPercentDiscount: () => applyPercentDiscount,
34
+ buildCheckoutMetadata: () => buildCheckoutMetadata,
35
+ createMemoryBillingAdapter: () => createMemoryBillingAdapter,
36
+ formatReceiptNumber: () => formatReceiptNumber,
37
+ isActiveSubscription: () => isActiveSubscription,
38
+ isKnownBillingCycle: () => isKnownBillingCycle,
39
+ isKnownPaymentProvider: () => isKnownPaymentProvider,
40
+ isKnownSubscriptionStatus: () => isKnownSubscriptionStatus,
41
+ mapProduct: () => mapProduct,
42
+ mapProductList: () => mapProductList,
43
+ minorUnitDivisor: () => minorUnitDivisor,
44
+ normalizeCouponCode: () => normalizeCouponCode,
45
+ parseBillingCycle: () => parseBillingCycle,
46
+ readCheckoutMetadata: () => readCheckoutMetadata,
47
+ toMajorUnits: () => toMajorUnits,
48
+ toMinorUnits: () => toMinorUnits
49
+ });
50
+ module.exports = __toCommonJS(core_exports);
51
+
52
+ // src/domain/enums/payment-provider.enum.ts
53
+ var PAYMENT_PROVIDER = {
54
+ POLAR: "polar",
55
+ STRIPE: "stripe"
56
+ };
57
+ var PAYMENT_PROVIDER_VALUES = Object.values(PAYMENT_PROVIDER);
58
+ var PAYMENT_PROVIDER_SET = new Set(PAYMENT_PROVIDER_VALUES);
59
+ function isKnownPaymentProvider(value) {
60
+ return typeof value === "string" && PAYMENT_PROVIDER_SET.has(value);
61
+ }
62
+
63
+ // src/domain/enums/subscription-status.enum.ts
64
+ var SUBSCRIPTION_STATUS = {
65
+ ACTIVE: "active",
66
+ CANCELED: "canceled",
67
+ REVOKED: "revoked",
68
+ TRIALING: "trialing",
69
+ PAST_DUE: "past_due",
70
+ INCOMPLETE: "incomplete",
71
+ INCOMPLETE_EXPIRED: "incomplete_expired",
72
+ UNPAID: "unpaid",
73
+ NONE: "none"
74
+ };
75
+ var SUBSCRIPTION_STATUS_VALUES = Object.freeze([
76
+ SUBSCRIPTION_STATUS.ACTIVE,
77
+ SUBSCRIPTION_STATUS.CANCELED,
78
+ SUBSCRIPTION_STATUS.REVOKED,
79
+ SUBSCRIPTION_STATUS.TRIALING,
80
+ SUBSCRIPTION_STATUS.PAST_DUE,
81
+ SUBSCRIPTION_STATUS.INCOMPLETE,
82
+ SUBSCRIPTION_STATUS.INCOMPLETE_EXPIRED,
83
+ SUBSCRIPTION_STATUS.UNPAID,
84
+ SUBSCRIPTION_STATUS.NONE
85
+ ]);
86
+ var SUBSCRIPTION_STATUS_SET = new Set(SUBSCRIPTION_STATUS_VALUES);
87
+ function isKnownSubscriptionStatus(value) {
88
+ return typeof value === "string" && SUBSCRIPTION_STATUS_SET.has(value.toLowerCase());
89
+ }
90
+
91
+ // src/domain/enums/billing-cycle.enum.ts
92
+ var BILLING_CYCLE = Object.freeze({
93
+ MONTHLY: "monthly",
94
+ YEARLY: "yearly"
95
+ });
96
+ var BILLING_CYCLE_VALUES = Object.freeze([
97
+ BILLING_CYCLE.MONTHLY,
98
+ BILLING_CYCLE.YEARLY
99
+ ]);
100
+ var CANONICAL_BILLING_CYCLES = new Set(BILLING_CYCLE_VALUES);
101
+ var BILLING_CYCLE_ALIASES = /* @__PURE__ */ new Map([
102
+ [BILLING_CYCLE.MONTHLY, BILLING_CYCLE.MONTHLY],
103
+ ["month", BILLING_CYCLE.MONTHLY],
104
+ [BILLING_CYCLE.YEARLY, BILLING_CYCLE.YEARLY],
105
+ ["year", BILLING_CYCLE.YEARLY]
106
+ ]);
107
+ function isKnownBillingCycle(value) {
108
+ return typeof value === "string" && CANONICAL_BILLING_CYCLES.has(value.toLowerCase());
109
+ }
110
+ function parseBillingCycle(value) {
111
+ if (typeof value !== "string") return null;
112
+ return BILLING_CYCLE_ALIASES.get(value.toLowerCase()) ?? null;
113
+ }
114
+
115
+ // src/domain/enums/amount-type.enum.ts
116
+ var POLAR_AMOUNT_TYPE = Object.freeze({
117
+ FIXED: "fixed",
118
+ CUSTOM: "custom",
119
+ FREE: "free",
120
+ SEAT_BASED: "seat_based",
121
+ METERED_UNIT: "metered_unit"
122
+ });
123
+
124
+ // src/domain/entities/cancellation.entity.ts
125
+ var CANCELLATION_REASON = Object.freeze({
126
+ TOO_EXPENSIVE: "too_expensive",
127
+ MISSING_FEATURES: "missing_features",
128
+ SWITCHED_SERVICE: "switched_service",
129
+ UNUSED: "unused",
130
+ CUSTOMER_SERVICE: "customer_service",
131
+ LOW_QUALITY: "low_quality",
132
+ TOO_COMPLEX: "too_complex",
133
+ OTHER: "other"
134
+ });
135
+ var CANCELLATION_REASON_VALUES = Object.freeze([
136
+ CANCELLATION_REASON.TOO_EXPENSIVE,
137
+ CANCELLATION_REASON.MISSING_FEATURES,
138
+ CANCELLATION_REASON.SWITCHED_SERVICE,
139
+ CANCELLATION_REASON.UNUSED,
140
+ CANCELLATION_REASON.CUSTOMER_SERVICE,
141
+ CANCELLATION_REASON.LOW_QUALITY,
142
+ CANCELLATION_REASON.TOO_COMPLEX,
143
+ CANCELLATION_REASON.OTHER
144
+ ]);
145
+
146
+ // src/domain/entities/subscription.entity.ts
147
+ var FREE_SUBSCRIPTION_STATUS = Object.freeze({
148
+ plan: "free",
149
+ subscriptionStatus: "none"
150
+ });
151
+ function isActiveSubscription(status) {
152
+ return status.subscriptionStatus === "active" || status.subscriptionStatus === "trialing";
153
+ }
154
+
155
+ // src/infrastructure/services/memory/create-memory-adapter.ts
156
+ function createMemoryBillingAdapter(config = {}) {
157
+ let status = {
158
+ plan: "free",
159
+ subscriptionStatus: "none",
160
+ billingCycle: "monthly",
161
+ cancelAtPeriodEnd: false,
162
+ ...config.initialStatus
163
+ };
164
+ const history = config.initialHistory ? [...config.initialHistory] : [];
165
+ const pending = /* @__PURE__ */ new Map();
166
+ let sequence = 0;
167
+ function checkoutUrl(checkout) {
168
+ if (checkout.params.successUrl) {
169
+ return checkout.params.successUrl.replace("{CHECKOUT_ID}", checkout.id);
170
+ }
171
+ const base = config.checkoutUrlBase ?? "https://checkout.example";
172
+ return `${base.replace(/\/$/, "")}/${checkout.id}`;
173
+ }
174
+ function fulfill(checkout) {
175
+ const planName = checkout.params.planKey ?? checkout.params.productId ?? checkout.params.productKey ?? "default";
176
+ return {
177
+ ...status,
178
+ plan: planName,
179
+ subscriptionStatus: "active",
180
+ subscriptionId: `sub_${checkout.id.slice(4)}`,
181
+ billingCycle: checkout.params.billingCycle ?? status.billingCycle,
182
+ cancelAtPeriodEnd: false,
183
+ currentPeriodEnd: new Date(Date.now() + 30 * 24 * 60 * 60 * 1e3).toISOString()
184
+ };
185
+ }
186
+ return {
187
+ async getStatus(_userId) {
188
+ return { ...status };
189
+ },
190
+ async createCheckout(params) {
191
+ config.onCheckout?.(params);
192
+ sequence += 1;
193
+ const checkout = {
194
+ id: `chk_${Date.now()}_${sequence}`,
195
+ params
196
+ };
197
+ pending.set(checkout.id, checkout);
198
+ return {
199
+ id: checkout.id,
200
+ url: checkoutUrl(checkout)
201
+ };
202
+ },
203
+ async syncSubscription(_userId, checkoutId) {
204
+ const checkout = checkoutId ? pending.get(checkoutId) : void 0;
205
+ const last = checkout ? void 0 : [...pending.values()].pop();
206
+ const target = checkout ?? last;
207
+ if (!target) {
208
+ return { synced: false, plan: status.plan };
209
+ }
210
+ status = fulfill(target);
211
+ pending.delete(target.id);
212
+ history.unshift({
213
+ id: `ord_${target.id}`,
214
+ amount: 0,
215
+ currency: "usd",
216
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
217
+ productName: target.params.planKey ?? target.params.productId,
218
+ status: "paid",
219
+ paid: true
220
+ });
221
+ return { synced: true, plan: status.plan };
222
+ },
223
+ async getBillingHistory(_userId) {
224
+ return [...history];
225
+ },
226
+ async cancelSubscription(reason) {
227
+ config.onCancel?.(reason);
228
+ if (!isActiveSubscription(status)) {
229
+ return { success: false };
230
+ }
231
+ status = {
232
+ ...status,
233
+ subscriptionStatus: "canceled",
234
+ cancelAtPeriodEnd: true
235
+ };
236
+ return {
237
+ success: true,
238
+ endsAt: status.currentPeriodEnd ?? (/* @__PURE__ */ new Date()).toISOString()
239
+ };
240
+ },
241
+ async getPortalUrl(_userId) {
242
+ return config.portalUrl ?? "https://portal.example/billing";
243
+ }
244
+ };
245
+ }
246
+
247
+ // src/domain/mappers/product.mapper.ts
248
+ var KNOWN_AMOUNT_TYPES = /* @__PURE__ */ new Set([
249
+ "fixed",
250
+ "custom",
251
+ "free",
252
+ "seat_based",
253
+ "metered_unit"
254
+ ]);
255
+ function toAmountType(raw) {
256
+ if (raw && KNOWN_AMOUNT_TYPES.has(raw)) return raw;
257
+ return "fixed";
258
+ }
259
+ function toBillingCycle(interval) {
260
+ return parseBillingCycle(interval);
261
+ }
262
+ function asRecord(value) {
263
+ return value && typeof value === "object" ? value : {};
264
+ }
265
+ function toPrice(dto) {
266
+ return {
267
+ id: String(dto.id),
268
+ type: toAmountType(dto.type),
269
+ amountType: toAmountType(dto.amount_type),
270
+ amount: typeof dto.amount === "number" ? dto.amount : void 0,
271
+ currency: String(dto.currency),
272
+ recurringInterval: toBillingCycle(dto.recurring_interval) ?? void 0,
273
+ isArchived: Boolean(dto.is_archived),
274
+ productId: String(dto.product_id),
275
+ legacy: Boolean(dto.legacy)
276
+ };
277
+ }
278
+ function toBenefit(dto) {
279
+ return {
280
+ id: String(dto.id),
281
+ type: String(dto.type),
282
+ description: dto.description ?? null,
283
+ properties: asRecord(dto.properties)
284
+ };
285
+ }
286
+ function mapProduct(dto) {
287
+ if (!dto || typeof dto !== "object") {
288
+ throw new TypeError("mapProduct: expected a product DTO object");
289
+ }
290
+ return {
291
+ id: String(dto.id),
292
+ name: String(dto.name),
293
+ description: dto.description ?? null,
294
+ isRecurring: Boolean(dto.is_recurring),
295
+ recurringInterval: toBillingCycle(dto.recurring_interval),
296
+ isArchived: Boolean(dto.is_archived),
297
+ organizationId: String(dto.organization_id),
298
+ prices: Array.isArray(dto.prices) ? dto.prices.map(toPrice) : [],
299
+ benefits: Array.isArray(dto.benefits) ? dto.benefits.map(toBenefit) : [],
300
+ metadata: asRecord(dto.metadata),
301
+ createdAt: String(dto.created_at),
302
+ modifiedAt: dto.modified_at ?? null
303
+ };
304
+ }
305
+ function mapProductList(dto) {
306
+ if (!dto || typeof dto !== "object") {
307
+ return { items: [], pagination: { totalCount: 0, maxPage: 1 } };
308
+ }
309
+ return {
310
+ items: Array.isArray(dto.items) ? dto.items.map(mapProduct) : [],
311
+ pagination: {
312
+ totalCount: Number(dto.pagination?.total_count ?? 0),
313
+ maxPage: Number(dto.pagination?.max_page ?? 1)
314
+ }
315
+ };
316
+ }
317
+
318
+ // src/domain/utils/minor-units.ts
319
+ var MINOR_UNIT_DIVISOR = 100;
320
+ var ZERO_DECIMAL_CURRENCIES = /* @__PURE__ */ new Set([
321
+ "JPY",
322
+ "KRW",
323
+ "VND",
324
+ "CLP",
325
+ "PYG",
326
+ "XAF",
327
+ "XOF",
328
+ "BIF",
329
+ "DJF",
330
+ "GNF",
331
+ "XPF"
332
+ ]);
333
+ function minorUnitDivisor(currency) {
334
+ return ZERO_DECIMAL_CURRENCIES.has(currency.toUpperCase()) ? 1 : MINOR_UNIT_DIVISOR;
335
+ }
336
+ function toMajorUnits(amount, currency) {
337
+ return amount / minorUnitDivisor(currency);
338
+ }
339
+ function toMinorUnits(amount, currency) {
340
+ return Math.round(amount * minorUnitDivisor(currency));
341
+ }
342
+
343
+ // src/domain/utils/percent-discount.ts
344
+ function normalizeCouponCode(code) {
345
+ if (typeof code !== "string") return null;
346
+ const normalized = code.trim().toUpperCase();
347
+ return normalized.length > 0 ? normalized : null;
348
+ }
349
+ function applyPercentDiscount(baseAmount, percent, minimumAmount) {
350
+ if (!Number.isFinite(baseAmount) || baseAmount <= 0) return 0;
351
+ if (!Number.isFinite(percent) || percent <= 0) return Math.round(baseAmount);
352
+ const discounted = Math.round(baseAmount * (1 - percent / 100));
353
+ return Math.max(minimumAmount, discounted);
354
+ }
355
+
356
+ // src/infrastructure/utils/receipt.util.ts
357
+ function formatReceiptNumber(id, prefix = "RCP") {
358
+ if (!id || typeof id !== "string") return `${prefix}-00000000`;
359
+ const clean = id.replace(/[^a-zA-Z0-9]/g, "");
360
+ const tail = clean.length >= 8 ? clean.slice(-8) : clean.padStart(8, "0");
361
+ return `${prefix}-${tail.toUpperCase()}`;
362
+ }
363
+
364
+ // src/infrastructure/utils/checkout-metadata.ts
365
+ function asNonEmptyString(value) {
366
+ if (typeof value !== "string" || value.length === 0) return void 0;
367
+ return value;
368
+ }
369
+ function buildCheckoutMetadata(input) {
370
+ const meta = {
371
+ userId: input.userId,
372
+ itemType: input.itemType,
373
+ itemId: input.itemId
374
+ };
375
+ if (input.couponCode && input.couponCode.trim().length > 0) {
376
+ meta.couponCode = input.couponCode.trim();
377
+ }
378
+ if (input.extra) {
379
+ for (const [key, value] of Object.entries(input.extra)) {
380
+ if (typeof value === "string" && value.length > 0) {
381
+ meta[key] = value;
382
+ }
383
+ }
384
+ }
385
+ return meta;
386
+ }
387
+ function readCheckoutMetadata(metadata, allowedItemTypes) {
388
+ if (metadata === void 0) return null;
389
+ const userId = asNonEmptyString(metadata.userId);
390
+ const itemType = asNonEmptyString(metadata.itemType);
391
+ const itemId = asNonEmptyString(metadata.itemId);
392
+ if (!userId || !itemType || !itemId) return null;
393
+ if (!allowedItemTypes.includes(itemType)) return null;
394
+ const couponCode = asNonEmptyString(metadata.couponCode);
395
+ return { userId, itemType, itemId, ...couponCode ? { couponCode } : {} };
396
+ }
397
+ // Annotate the CommonJS export names for ESM import in node:
398
+ 0 && (module.exports = {
399
+ BILLING_CYCLE,
400
+ BILLING_CYCLE_VALUES,
401
+ CANCELLATION_REASON,
402
+ CANCELLATION_REASON_VALUES,
403
+ FREE_SUBSCRIPTION_STATUS,
404
+ PAYMENT_PROVIDER,
405
+ PAYMENT_PROVIDER_VALUES,
406
+ POLAR_AMOUNT_TYPE,
407
+ SUBSCRIPTION_STATUS,
408
+ SUBSCRIPTION_STATUS_VALUES,
409
+ applyPercentDiscount,
410
+ buildCheckoutMetadata,
411
+ createMemoryBillingAdapter,
412
+ formatReceiptNumber,
413
+ isActiveSubscription,
414
+ isKnownBillingCycle,
415
+ isKnownPaymentProvider,
416
+ isKnownSubscriptionStatus,
417
+ mapProduct,
418
+ mapProductList,
419
+ minorUnitDivisor,
420
+ normalizeCouponCode,
421
+ parseBillingCycle,
422
+ readCheckoutMetadata,
423
+ toMajorUnits,
424
+ toMinorUnits
425
+ });
package/dist/core.mjs ADDED
@@ -0,0 +1,66 @@
1
+ import {
2
+ CANCELLATION_REASON,
3
+ CANCELLATION_REASON_VALUES,
4
+ POLAR_AMOUNT_TYPE,
5
+ applyPercentDiscount,
6
+ normalizeCouponCode
7
+ } from "./chunk-4N3BASZ6.mjs";
8
+ import {
9
+ FREE_SUBSCRIPTION_STATUS,
10
+ createMemoryBillingAdapter,
11
+ isActiveSubscription
12
+ } from "./chunk-3AXPZOCV.mjs";
13
+ import {
14
+ PAYMENT_PROVIDER,
15
+ PAYMENT_PROVIDER_VALUES,
16
+ buildCheckoutMetadata,
17
+ formatReceiptNumber,
18
+ isKnownPaymentProvider,
19
+ readCheckoutMetadata
20
+ } from "./chunk-2KV7JFYF.mjs";
21
+ import {
22
+ minorUnitDivisor,
23
+ toMajorUnits,
24
+ toMinorUnits
25
+ } from "./chunk-PIWNUAKW.mjs";
26
+ import {
27
+ SUBSCRIPTION_STATUS,
28
+ SUBSCRIPTION_STATUS_VALUES,
29
+ isKnownSubscriptionStatus,
30
+ mapProduct,
31
+ mapProductList
32
+ } from "./chunk-U45FS4FF.mjs";
33
+ import {
34
+ BILLING_CYCLE,
35
+ BILLING_CYCLE_VALUES,
36
+ isKnownBillingCycle,
37
+ parseBillingCycle
38
+ } from "./chunk-RQTMBR74.mjs";
39
+ export {
40
+ BILLING_CYCLE,
41
+ BILLING_CYCLE_VALUES,
42
+ CANCELLATION_REASON,
43
+ CANCELLATION_REASON_VALUES,
44
+ FREE_SUBSCRIPTION_STATUS,
45
+ PAYMENT_PROVIDER,
46
+ PAYMENT_PROVIDER_VALUES,
47
+ POLAR_AMOUNT_TYPE,
48
+ SUBSCRIPTION_STATUS,
49
+ SUBSCRIPTION_STATUS_VALUES,
50
+ applyPercentDiscount,
51
+ buildCheckoutMetadata,
52
+ createMemoryBillingAdapter,
53
+ formatReceiptNumber,
54
+ isActiveSubscription,
55
+ isKnownBillingCycle,
56
+ isKnownPaymentProvider,
57
+ isKnownSubscriptionStatus,
58
+ mapProduct,
59
+ mapProductList,
60
+ minorUnitDivisor,
61
+ normalizeCouponCode,
62
+ parseBillingCycle,
63
+ readCheckoutMetadata,
64
+ toMajorUnits,
65
+ toMinorUnits
66
+ };
@@ -0,0 +1,12 @@
1
+ import { B as BillingAdapter } from './billing-adapter.interface-CDkeeowZ.mjs';
2
+ import './billing-cycle.enum--hJRYlgP.mjs';
3
+
4
+ interface HttpAdapterConfig {
5
+ baseUrl?: string;
6
+ fetchImpl?: typeof fetch;
7
+ getUserId?: () => string | undefined | Promise<string | undefined>;
8
+ getHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
9
+ }
10
+ declare function createHttpAdapter(config?: HttpAdapterConfig): BillingAdapter;
11
+
12
+ export { type HttpAdapterConfig, createHttpAdapter };
package/dist/http.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ import { B as BillingAdapter } from './billing-adapter.interface-m7yE50I0.js';
2
+ import './billing-cycle.enum--hJRYlgP.js';
3
+
4
+ interface HttpAdapterConfig {
5
+ baseUrl?: string;
6
+ fetchImpl?: typeof fetch;
7
+ getUserId?: () => string | undefined | Promise<string | undefined>;
8
+ getHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
9
+ }
10
+ declare function createHttpAdapter(config?: HttpAdapterConfig): BillingAdapter;
11
+
12
+ export { type HttpAdapterConfig, createHttpAdapter };
package/dist/http.js ADDED
@@ -0,0 +1,115 @@
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/http.ts
21
+ var http_exports = {};
22
+ __export(http_exports, {
23
+ createHttpAdapter: () => createHttpAdapter
24
+ });
25
+ module.exports = __toCommonJS(http_exports);
26
+
27
+ // src/infrastructure/services/http/create-http-adapter.ts
28
+ function errorMessage(status, body) {
29
+ const envelope = body ?? {};
30
+ const errorDetail = typeof envelope.error === "string" ? envelope.error : envelope.error?.message;
31
+ const detail = errorDetail ?? envelope.message ?? "Billing request failed";
32
+ return `${detail} (HTTP ${status})`;
33
+ }
34
+ function createHttpAdapter(config = {}) {
35
+ const baseUrl = (config.baseUrl ?? "/api/billing").replace(/\/$/, "");
36
+ const doFetch = config.fetchImpl ?? globalThis.fetch.bind(globalThis);
37
+ async function request(method, path, body, explicitUserId) {
38
+ const userId = explicitUserId || (config.getUserId ? await config.getUserId() : void 0);
39
+ const headers = {
40
+ Accept: "application/json",
41
+ ...config.getHeaders ? await config.getHeaders() : {}
42
+ };
43
+ const withUser = { ...userId ? { userId } : {}, ...body ?? {} };
44
+ if (Object.keys(withUser).length > 0) headers["Content-Type"] = "application/json";
45
+ const query = userId && method === "GET" ? `?userId=${encodeURIComponent(userId)}` : "";
46
+ const response = await doFetch(`${baseUrl}${path}${query}`, {
47
+ method,
48
+ headers,
49
+ ...method === "POST" && Object.keys(withUser).length > 0 ? { body: JSON.stringify(withUser) } : {}
50
+ });
51
+ let data = null;
52
+ const text = await response.text();
53
+ if (text.length > 0) {
54
+ try {
55
+ data = JSON.parse(text);
56
+ } catch {
57
+ data = null;
58
+ }
59
+ }
60
+ if (!response.ok) {
61
+ throw new Error(errorMessage(response.status, data));
62
+ }
63
+ return data;
64
+ }
65
+ return {
66
+ getStatus(userId) {
67
+ return request("GET", "/status", void 0, userId);
68
+ },
69
+ createCheckout(params) {
70
+ return request(
71
+ "POST",
72
+ "/checkout",
73
+ {
74
+ ...params.productId ? { productId: params.productId } : {},
75
+ ...params.productKey ? { productKey: params.productKey } : {},
76
+ ...params.itemId ? { itemId: params.itemId } : {},
77
+ ...params.couponCode ? { couponCode: params.couponCode } : {},
78
+ ...params.billingCycle ? { billingCycle: params.billingCycle } : {},
79
+ ...params.successUrl ? { successUrl: params.successUrl } : {},
80
+ ...params.planKey ? { planKey: params.planKey } : {},
81
+ ...params.customerEmail ? { customerEmail: params.customerEmail } : {},
82
+ ...params.seats !== void 0 ? { seats: params.seats } : {},
83
+ ...params.metadata ? { metadata: params.metadata } : {}
84
+ },
85
+ params.userId
86
+ );
87
+ },
88
+ syncSubscription(userId, checkoutId) {
89
+ return request("POST", "/sync", checkoutId ? { checkoutId } : void 0, userId);
90
+ },
91
+ async getBillingHistory(userId) {
92
+ const result = await request(
93
+ "GET",
94
+ "/history",
95
+ void 0,
96
+ userId
97
+ );
98
+ if (Array.isArray(result)) return result;
99
+ return result.orders ?? result.items ?? [];
100
+ },
101
+ cancelSubscription(reason) {
102
+ return request("POST", "/cancel", {
103
+ ...reason ? { reason } : {}
104
+ });
105
+ },
106
+ async getPortalUrl(userId) {
107
+ const result = await request("GET", "/portal", void 0, userId);
108
+ return typeof result === "string" ? result : result.url;
109
+ }
110
+ };
111
+ }
112
+ // Annotate the CommonJS export names for ESM import in node:
113
+ 0 && (module.exports = {
114
+ createHttpAdapter
115
+ });