@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.
- package/LICENSE +21 -0
- package/README.md +349 -2
- package/dist/billing-adapter.interface-CDkeeowZ.d.mts +94 -0
- package/dist/billing-adapter.interface-m7yE50I0.d.ts +94 -0
- package/dist/billing-cycle.enum--hJRYlgP.d.mts +10 -0
- package/dist/billing-cycle.enum--hJRYlgP.d.ts +10 -0
- package/dist/billing-env-D4YjvA3M.d.mts +8 -0
- package/dist/billing-env-D4YjvA3M.d.ts +8 -0
- package/dist/checkout-metadata-D7Wp_DAD.d.mts +27 -0
- package/dist/checkout-metadata-D7Wp_DAD.d.ts +27 -0
- package/dist/chunk-2KV7JFYF.mjs +61 -0
- package/dist/chunk-2NH5DOLT.mjs +101 -0
- package/dist/chunk-3AXPZOCV.mjs +106 -0
- package/dist/chunk-4N3BASZ6.mjs +51 -0
- package/dist/chunk-NHKK3ENU.mjs +1128 -0
- package/dist/chunk-PIWNUAKW.mjs +30 -0
- package/dist/chunk-QFB7AOSQ.mjs +33 -0
- package/dist/chunk-RQTMBR74.mjs +30 -0
- package/dist/chunk-U45FS4FF.mjs +110 -0
- package/dist/chunk-X6R76FZT.mjs +0 -0
- package/dist/chunk-XQ2HJTTA.mjs +252 -0
- package/dist/core.d.mts +17 -0
- package/dist/core.d.ts +17 -0
- package/dist/core.js +425 -0
- package/dist/core.mjs +66 -0
- package/dist/http.d.mts +12 -0
- package/dist/http.d.ts +12 -0
- package/dist/http.js +115 -0
- package/dist/http.mjs +88 -0
- package/dist/index.d.mts +17 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +1929 -0
- package/dist/index.mjs +153 -0
- package/dist/memory.d.mts +14 -0
- package/dist/memory.d.ts +14 -0
- package/dist/memory.js +130 -0
- package/dist/memory.mjs +6 -0
- package/dist/portal.d.mts +445 -0
- package/dist/portal.d.ts +445 -0
- package/dist/portal.js +2927 -0
- package/dist/portal.mjs +2737 -0
- package/dist/product.mapper-CnY1ISOp.d.ts +97 -0
- package/dist/product.mapper-noVl-sel.d.mts +97 -0
- package/dist/products.d.mts +64 -0
- package/dist/products.d.ts +64 -0
- package/dist/products.js +370 -0
- package/dist/products.mjs +21 -0
- package/dist/react.d.mts +182 -0
- package/dist/react.d.ts +182 -0
- package/dist/react.js +1874 -0
- package/dist/react.mjs +105 -0
- package/dist/server.d.mts +177 -0
- package/dist/server.d.ts +177 -0
- package/dist/server.js +622 -0
- package/dist/server.mjs +417 -0
- package/dist/visual-tokens-BCLVR85G.d.mts +94 -0
- package/dist/visual-tokens-BCLVR85G.d.ts +94 -0
- package/package.json +122 -4
- package/src/core.ts +81 -0
- package/src/domain/entities/cancellation.entity.ts +28 -0
- package/src/domain/entities/checkout.entity.ts +21 -0
- package/src/domain/entities/index.ts +10 -0
- package/src/domain/entities/order.entity.ts +15 -0
- package/src/domain/entities/product.entity.ts +16 -0
- package/src/domain/entities/subscription.entity.ts +24 -0
- package/src/domain/entities/sync.entity.ts +4 -0
- package/src/domain/enums/amount-type.enum.ts +10 -0
- package/src/domain/enums/billing-cycle.enum.ts +30 -0
- package/src/domain/enums/index.ts +5 -0
- package/src/domain/enums/payment-provider.enum.ts +14 -0
- package/src/domain/enums/subscription-status.enum.ts +32 -0
- package/src/domain/index.ts +5 -0
- package/src/domain/interfaces/billing-adapter.interface.ts +18 -0
- package/src/domain/interfaces/index.ts +1 -0
- package/src/domain/mappers/product.mapper.ts +176 -0
- package/src/domain/utils/minor-units.ts +20 -0
- package/src/domain/utils/percent-discount.ts +17 -0
- package/src/http.ts +3 -0
- package/src/index.ts +12 -0
- package/src/infrastructure/browser/hosted-checkout.ts +26 -0
- package/src/infrastructure/config/billing-env.ts +31 -0
- package/src/infrastructure/constants/billing.constants.ts +10 -0
- package/src/infrastructure/http/http-client.ts +83 -0
- package/src/infrastructure/pagination/page-iterator.ts +43 -0
- package/src/infrastructure/query/query-builder.ts +26 -0
- package/src/infrastructure/services/billing.client.ts +300 -0
- package/src/infrastructure/services/create-billing-client.ts +23 -0
- package/src/infrastructure/services/http/create-http-adapter.ts +123 -0
- package/src/infrastructure/services/memory/create-memory-adapter.ts +126 -0
- package/src/infrastructure/services/product.repository.ts +198 -0
- package/src/infrastructure/utils/checkout-metadata.ts +55 -0
- package/src/infrastructure/utils/date-renderer.ts +53 -0
- package/src/infrastructure/utils/money-formatter.ts +39 -0
- package/src/infrastructure/utils/normalization.util.ts +22 -0
- package/src/infrastructure/utils/pagination-clamp.ts +11 -0
- package/src/infrastructure/utils/receipt.util.ts +6 -0
- package/src/infrastructure/utils/security.util.ts +23 -0
- package/src/infrastructure/utils/type-guards.util.ts +23 -0
- package/src/infrastructure/utils/validation.util.ts +10 -0
- package/src/infrastructure/webhook/parse-event.ts +135 -0
- package/src/infrastructure/webhook/verify-webhook.ts +124 -0
- package/src/memory.ts +3 -0
- package/src/portal.ts +2 -0
- package/src/presentation/components/BillingHistoryList.tsx +172 -0
- package/src/presentation/components/BillingProvider.tsx +179 -0
- package/src/presentation/components/CancelSubscriptionButton.tsx +155 -0
- package/src/presentation/components/CheckoutButton.tsx +76 -0
- package/src/presentation/components/CustomerPortalLink.tsx +71 -0
- package/src/presentation/components/PricingTable.tsx +225 -0
- package/src/presentation/components/SubscriptionStatusBadge.tsx +102 -0
- package/src/presentation/context/billing-context.ts +30 -0
- package/src/presentation/hooks/useBilling.ts +3 -0
- package/src/presentation/hooks/useCancelSubscription.ts +47 -0
- package/src/presentation/hooks/useCheckoutButton.ts +49 -0
- package/src/presentation/hooks/useProducts.ts +76 -0
- package/src/presentation/navigation/navigation.strategy.ts +16 -0
- package/src/presentation/portal/TurnkeyBillingPage.tsx +144 -0
- package/src/presentation/portal/components/BillingLayout.tsx +163 -0
- package/src/presentation/portal/components/BillingPage.tsx +22 -0
- package/src/presentation/portal/components/BillingPortal.tsx +248 -0
- package/src/presentation/portal/components/InvoiceCard.tsx +275 -0
- package/src/presentation/portal/components/OverviewTab.tsx +137 -0
- package/src/presentation/portal/components/PaymentMethodsList.tsx +268 -0
- package/src/presentation/portal/components/PlanComparison.tsx +405 -0
- package/src/presentation/portal/components/PlanTab.tsx +197 -0
- package/src/presentation/portal/components/PortalButton.tsx +89 -0
- package/src/presentation/portal/components/PortalSpinner.tsx +30 -0
- package/src/presentation/portal/components/UsageCard.tsx +158 -0
- package/src/presentation/portal/components/index.ts +11 -0
- package/src/presentation/portal/constants.ts +44 -0
- package/src/presentation/portal/create-portal-client.ts +202 -0
- package/src/presentation/portal/hooks/index.ts +3 -0
- package/src/presentation/portal/hooks/useBilling.ts +240 -0
- package/src/presentation/portal/index.ts +97 -0
- package/src/presentation/portal/labels.tsx +218 -0
- package/src/presentation/portal/styles.ts +70 -0
- package/src/presentation/portal/types.ts +213 -0
- package/src/presentation/portal/utils/billing.ts +193 -0
- package/src/presentation/portal/utils/index.ts +27 -0
- package/src/presentation/portal/utils/plans.ts +71 -0
- package/src/presentation/theme/visual-tokens.ts +103 -0
- package/src/presentation/utils/cancellation-reason-catalog.ts +18 -0
- package/src/presentation/utils/checkout-button-visual.ts +50 -0
- package/src/presentation/utils/inline-error-visual.ts +21 -0
- package/src/presentation/utils/product-pricing.ts +33 -0
- package/src/presentation/utils/status-label.ts +23 -0
- package/src/presentation/utils/status-tone.ts +25 -0
- package/src/products.ts +20 -0
- package/src/react.tsx +63 -0
- 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
|
+
};
|
package/dist/http.d.mts
ADDED
|
@@ -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
|
+
});
|