@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
@@ -0,0 +1,61 @@
1
+ // src/domain/enums/payment-provider.enum.ts
2
+ var PAYMENT_PROVIDER = {
3
+ POLAR: "polar",
4
+ STRIPE: "stripe"
5
+ };
6
+ var PAYMENT_PROVIDER_VALUES = Object.values(PAYMENT_PROVIDER);
7
+ var PAYMENT_PROVIDER_SET = new Set(PAYMENT_PROVIDER_VALUES);
8
+ function isKnownPaymentProvider(value) {
9
+ return typeof value === "string" && PAYMENT_PROVIDER_SET.has(value);
10
+ }
11
+
12
+ // src/infrastructure/utils/receipt.util.ts
13
+ function formatReceiptNumber(id, prefix = "RCP") {
14
+ if (!id || typeof id !== "string") return `${prefix}-00000000`;
15
+ const clean = id.replace(/[^a-zA-Z0-9]/g, "");
16
+ const tail = clean.length >= 8 ? clean.slice(-8) : clean.padStart(8, "0");
17
+ return `${prefix}-${tail.toUpperCase()}`;
18
+ }
19
+
20
+ // src/infrastructure/utils/checkout-metadata.ts
21
+ function asNonEmptyString(value) {
22
+ if (typeof value !== "string" || value.length === 0) return void 0;
23
+ return value;
24
+ }
25
+ function buildCheckoutMetadata(input) {
26
+ const meta = {
27
+ userId: input.userId,
28
+ itemType: input.itemType,
29
+ itemId: input.itemId
30
+ };
31
+ if (input.couponCode && input.couponCode.trim().length > 0) {
32
+ meta.couponCode = input.couponCode.trim();
33
+ }
34
+ if (input.extra) {
35
+ for (const [key, value] of Object.entries(input.extra)) {
36
+ if (typeof value === "string" && value.length > 0) {
37
+ meta[key] = value;
38
+ }
39
+ }
40
+ }
41
+ return meta;
42
+ }
43
+ function readCheckoutMetadata(metadata, allowedItemTypes) {
44
+ if (metadata === void 0) return null;
45
+ const userId = asNonEmptyString(metadata.userId);
46
+ const itemType = asNonEmptyString(metadata.itemType);
47
+ const itemId = asNonEmptyString(metadata.itemId);
48
+ if (!userId || !itemType || !itemId) return null;
49
+ if (!allowedItemTypes.includes(itemType)) return null;
50
+ const couponCode = asNonEmptyString(metadata.couponCode);
51
+ return { userId, itemType, itemId, ...couponCode ? { couponCode } : {} };
52
+ }
53
+
54
+ export {
55
+ PAYMENT_PROVIDER,
56
+ PAYMENT_PROVIDER_VALUES,
57
+ isKnownPaymentProvider,
58
+ formatReceiptNumber,
59
+ buildCheckoutMetadata,
60
+ readCheckoutMetadata
61
+ };
@@ -0,0 +1,101 @@
1
+ // src/presentation/navigation/navigation.strategy.ts
2
+ var WindowNavigationStrategy = class {
3
+ redirect(url) {
4
+ window.location.href = url;
5
+ }
6
+ };
7
+ var NoopNavigationStrategy = class {
8
+ redirect(_url) {
9
+ }
10
+ };
11
+
12
+ // src/presentation/theme/visual-tokens.ts
13
+ var VisualTokens = Object.freeze({
14
+ spacing: Object.freeze({
15
+ none: 0,
16
+ xs: 4,
17
+ sm: 6,
18
+ md: 8,
19
+ lg: 12,
20
+ xl: 16,
21
+ xxl: 20,
22
+ section: 24,
23
+ page: 32
24
+ }),
25
+ radius: Object.freeze({
26
+ sm: 6,
27
+ md: 8,
28
+ lg: 12,
29
+ pill: 999
30
+ }),
31
+ fontSize: Object.freeze({
32
+ caption: 12,
33
+ body: 14,
34
+ title: 18,
35
+ display: 24
36
+ }),
37
+ fontWeight: Object.freeze({
38
+ regular: 400,
39
+ medium: 500,
40
+ strong: 600
41
+ }),
42
+ lineHeight: Object.freeze({
43
+ tight: 1.25,
44
+ normal: 1.5
45
+ }),
46
+ opacity: Object.freeze({
47
+ muted: 0.7,
48
+ disabled: 0.6,
49
+ hover: 0.85
50
+ }),
51
+ color: Object.freeze({
52
+ textPrimary: "#111827",
53
+ textMuted: "#6b7280",
54
+ textOnPrimary: "#ffffff",
55
+ textInverse: "#ffffff",
56
+ textError: "#b91c1c",
57
+ textOnError: "#ffffff",
58
+ surfacePrimary: "#ffffff",
59
+ surfaceMuted: "#f9fafb",
60
+ surfaceSubtle: "#f3f4f6",
61
+ surfaceWarning: "#fef3c7",
62
+ surfaceDanger: "#fee2e2",
63
+ surfaceDangerMuted: "#fef2f2",
64
+ borderSubtle: "#e5e7eb",
65
+ borderStrong: "#d1d5db",
66
+ borderWarning: "#fecaca",
67
+ brandPrimary: "#2563eb",
68
+ brandPrimaryHover: "#1d4ed8",
69
+ brandDanger: "#dc2626",
70
+ brandDangerHover: "#b91c1c",
71
+ brandDangerText: "#991b1b",
72
+ badgeNeutralBg: "#e5e7eb",
73
+ badgeNeutralFg: "#111827",
74
+ badgeActiveBg: "#dcfce7",
75
+ badgeActiveFg: "#166534",
76
+ badgeWarningBg: "#fef3c7",
77
+ badgeWarningFg: "#92400e",
78
+ badgeDangerBg: "#fee2e2",
79
+ badgeDangerFg: "#991b1b"
80
+ }),
81
+ transition: Object.freeze({
82
+ instant: "120ms ease"
83
+ }),
84
+ layout: Object.freeze({
85
+ cardMinWidth: 260,
86
+ benefitListMax: 5
87
+ })
88
+ });
89
+ var TONE_PAIRS = Object.freeze({
90
+ neutral: { bg: VisualTokens.color.badgeNeutralBg, fg: VisualTokens.color.badgeNeutralFg },
91
+ active: { bg: VisualTokens.color.badgeActiveBg, fg: VisualTokens.color.badgeActiveFg },
92
+ warning: { bg: VisualTokens.color.badgeWarningBg, fg: VisualTokens.color.badgeWarningFg },
93
+ danger: { bg: VisualTokens.color.badgeDangerBg, fg: VisualTokens.color.badgeDangerFg }
94
+ });
95
+
96
+ export {
97
+ WindowNavigationStrategy,
98
+ NoopNavigationStrategy,
99
+ VisualTokens,
100
+ TONE_PAIRS
101
+ };
@@ -0,0 +1,106 @@
1
+ // src/domain/entities/subscription.entity.ts
2
+ var FREE_SUBSCRIPTION_STATUS = Object.freeze({
3
+ plan: "free",
4
+ subscriptionStatus: "none"
5
+ });
6
+ function isActiveSubscription(status) {
7
+ return status.subscriptionStatus === "active" || status.subscriptionStatus === "trialing";
8
+ }
9
+
10
+ // src/infrastructure/services/memory/create-memory-adapter.ts
11
+ function createMemoryBillingAdapter(config = {}) {
12
+ let status = {
13
+ plan: "free",
14
+ subscriptionStatus: "none",
15
+ billingCycle: "monthly",
16
+ cancelAtPeriodEnd: false,
17
+ ...config.initialStatus
18
+ };
19
+ const history = config.initialHistory ? [...config.initialHistory] : [];
20
+ const pending = /* @__PURE__ */ new Map();
21
+ let sequence = 0;
22
+ function checkoutUrl(checkout) {
23
+ if (checkout.params.successUrl) {
24
+ return checkout.params.successUrl.replace("{CHECKOUT_ID}", checkout.id);
25
+ }
26
+ const base = config.checkoutUrlBase ?? "https://checkout.example";
27
+ return `${base.replace(/\/$/, "")}/${checkout.id}`;
28
+ }
29
+ function fulfill(checkout) {
30
+ const planName = checkout.params.planKey ?? checkout.params.productId ?? checkout.params.productKey ?? "default";
31
+ return {
32
+ ...status,
33
+ plan: planName,
34
+ subscriptionStatus: "active",
35
+ subscriptionId: `sub_${checkout.id.slice(4)}`,
36
+ billingCycle: checkout.params.billingCycle ?? status.billingCycle,
37
+ cancelAtPeriodEnd: false,
38
+ currentPeriodEnd: new Date(Date.now() + 30 * 24 * 60 * 60 * 1e3).toISOString()
39
+ };
40
+ }
41
+ return {
42
+ async getStatus(_userId) {
43
+ return { ...status };
44
+ },
45
+ async createCheckout(params) {
46
+ config.onCheckout?.(params);
47
+ sequence += 1;
48
+ const checkout = {
49
+ id: `chk_${Date.now()}_${sequence}`,
50
+ params
51
+ };
52
+ pending.set(checkout.id, checkout);
53
+ return {
54
+ id: checkout.id,
55
+ url: checkoutUrl(checkout)
56
+ };
57
+ },
58
+ async syncSubscription(_userId, checkoutId) {
59
+ const checkout = checkoutId ? pending.get(checkoutId) : void 0;
60
+ const last = checkout ? void 0 : [...pending.values()].pop();
61
+ const target = checkout ?? last;
62
+ if (!target) {
63
+ return { synced: false, plan: status.plan };
64
+ }
65
+ status = fulfill(target);
66
+ pending.delete(target.id);
67
+ history.unshift({
68
+ id: `ord_${target.id}`,
69
+ amount: 0,
70
+ currency: "usd",
71
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
72
+ productName: target.params.planKey ?? target.params.productId,
73
+ status: "paid",
74
+ paid: true
75
+ });
76
+ return { synced: true, plan: status.plan };
77
+ },
78
+ async getBillingHistory(_userId) {
79
+ return [...history];
80
+ },
81
+ async cancelSubscription(reason) {
82
+ config.onCancel?.(reason);
83
+ if (!isActiveSubscription(status)) {
84
+ return { success: false };
85
+ }
86
+ status = {
87
+ ...status,
88
+ subscriptionStatus: "canceled",
89
+ cancelAtPeriodEnd: true
90
+ };
91
+ return {
92
+ success: true,
93
+ endsAt: status.currentPeriodEnd ?? (/* @__PURE__ */ new Date()).toISOString()
94
+ };
95
+ },
96
+ async getPortalUrl(_userId) {
97
+ return config.portalUrl ?? "https://portal.example/billing";
98
+ }
99
+ };
100
+ }
101
+
102
+ export {
103
+ FREE_SUBSCRIPTION_STATUS,
104
+ isActiveSubscription,
105
+ createMemoryBillingAdapter
106
+ };
@@ -0,0 +1,51 @@
1
+ // src/domain/enums/amount-type.enum.ts
2
+ var POLAR_AMOUNT_TYPE = Object.freeze({
3
+ FIXED: "fixed",
4
+ CUSTOM: "custom",
5
+ FREE: "free",
6
+ SEAT_BASED: "seat_based",
7
+ METERED_UNIT: "metered_unit"
8
+ });
9
+
10
+ // src/domain/entities/cancellation.entity.ts
11
+ var CANCELLATION_REASON = Object.freeze({
12
+ TOO_EXPENSIVE: "too_expensive",
13
+ MISSING_FEATURES: "missing_features",
14
+ SWITCHED_SERVICE: "switched_service",
15
+ UNUSED: "unused",
16
+ CUSTOMER_SERVICE: "customer_service",
17
+ LOW_QUALITY: "low_quality",
18
+ TOO_COMPLEX: "too_complex",
19
+ OTHER: "other"
20
+ });
21
+ var CANCELLATION_REASON_VALUES = Object.freeze([
22
+ CANCELLATION_REASON.TOO_EXPENSIVE,
23
+ CANCELLATION_REASON.MISSING_FEATURES,
24
+ CANCELLATION_REASON.SWITCHED_SERVICE,
25
+ CANCELLATION_REASON.UNUSED,
26
+ CANCELLATION_REASON.CUSTOMER_SERVICE,
27
+ CANCELLATION_REASON.LOW_QUALITY,
28
+ CANCELLATION_REASON.TOO_COMPLEX,
29
+ CANCELLATION_REASON.OTHER
30
+ ]);
31
+
32
+ // src/domain/utils/percent-discount.ts
33
+ function normalizeCouponCode(code) {
34
+ if (typeof code !== "string") return null;
35
+ const normalized = code.trim().toUpperCase();
36
+ return normalized.length > 0 ? normalized : null;
37
+ }
38
+ function applyPercentDiscount(baseAmount, percent, minimumAmount) {
39
+ if (!Number.isFinite(baseAmount) || baseAmount <= 0) return 0;
40
+ if (!Number.isFinite(percent) || percent <= 0) return Math.round(baseAmount);
41
+ const discounted = Math.round(baseAmount * (1 - percent / 100));
42
+ return Math.max(minimumAmount, discounted);
43
+ }
44
+
45
+ export {
46
+ POLAR_AMOUNT_TYPE,
47
+ CANCELLATION_REASON,
48
+ CANCELLATION_REASON_VALUES,
49
+ normalizeCouponCode,
50
+ applyPercentDiscount
51
+ };