@umituz/web-polar-payment 1.0.14 → 1.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +20 -80
- package/dist/index.d.ts +20 -80
- package/dist/index.js +172 -170
- package/dist/index.mjs +177 -170
- package/package.json +1 -1
- package/src/domain/entities/cancellation.entity.ts +0 -5
- package/src/domain/entities/checkout.entity.ts +0 -6
- package/src/domain/entities/order.entity.ts +0 -5
- package/src/domain/entities/subscription.entity.ts +0 -6
- package/src/domain/entities/sync.entity.ts +0 -5
- package/src/domain/index.ts +0 -5
- package/src/domain/interfaces/polar-adapter.interface.ts +0 -5
- package/src/index.ts +9 -11
- package/src/infrastructure/constants/billing.constants.ts +0 -8
- package/src/infrastructure/index.ts +2 -5
- package/src/infrastructure/services/firebase-billing.service.ts +28 -64
- package/src/infrastructure/utils/firebase-helpers.util.ts +18 -0
- package/src/infrastructure/utils/normalization.util.ts +13 -23
- package/src/infrastructure/utils/validations.util.ts +17 -0
- package/src/presentation/components/PolarProvider.tsx +28 -35
- package/src/presentation/hooks/usePolarBilling.ts +0 -6
- package/src/presentation/index.ts +0 -5
package/dist/index.mjs
CHANGED
|
@@ -1,18 +1,164 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
// src/presentation/components/PolarProvider.tsx
|
|
2
|
+
import {
|
|
3
|
+
useEffect,
|
|
4
|
+
useState,
|
|
5
|
+
useCallback,
|
|
6
|
+
useRef,
|
|
7
|
+
useMemo
|
|
8
|
+
} from "react";
|
|
9
|
+
|
|
10
|
+
// src/presentation/hooks/usePolarBilling.ts
|
|
11
|
+
import { createContext, useContext } from "react";
|
|
12
|
+
var PolarContext = createContext(void 0);
|
|
13
|
+
function usePolarBilling() {
|
|
14
|
+
const ctx = useContext(PolarContext);
|
|
15
|
+
if (!ctx) throw new Error("usePolarBilling must be used within <PolarProvider>");
|
|
16
|
+
return ctx;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// src/infrastructure/utils/validations.util.ts
|
|
20
|
+
function normalizeUserId(userId) {
|
|
21
|
+
if (typeof userId !== "string") return void 0;
|
|
22
|
+
const trimmed = userId.trim();
|
|
23
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
24
|
+
}
|
|
25
|
+
function isValidProductId(productId) {
|
|
26
|
+
return typeof productId === "string" && productId.trim().length > 0;
|
|
27
|
+
}
|
|
28
|
+
function isValidCheckoutUrl(url) {
|
|
29
|
+
return url.startsWith("https://") || url.startsWith("http://");
|
|
30
|
+
}
|
|
31
|
+
function isProductionInsecureUrl(url) {
|
|
32
|
+
return url.startsWith("http://") && !url.includes("localhost") && !url.includes("127.0.0.1");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/presentation/components/PolarProvider.tsx
|
|
36
|
+
import { jsx } from "react/jsx-runtime";
|
|
37
|
+
var FREE_STATUS = {
|
|
38
|
+
plan: "free",
|
|
39
|
+
subscriptionStatus: "none"
|
|
13
40
|
};
|
|
41
|
+
function PolarProvider({ adapter, userId, children }) {
|
|
42
|
+
const [status, setStatus] = useState(FREE_STATUS);
|
|
43
|
+
const [loading, setLoading] = useState(true);
|
|
44
|
+
const adapterRef = useRef(adapter);
|
|
45
|
+
const statusRef = useRef({ setStatus, setLoading });
|
|
46
|
+
const userIdRef = useRef(normalizeUserId(userId));
|
|
47
|
+
const refreshMountedRef = useRef(true);
|
|
48
|
+
adapterRef.current = adapter;
|
|
49
|
+
statusRef.current = { setStatus, setLoading };
|
|
50
|
+
userIdRef.current = normalizeUserId(userId);
|
|
51
|
+
const refresh = useCallback(async () => {
|
|
52
|
+
const uid = userIdRef.current;
|
|
53
|
+
const { setStatus: setStatus2, setLoading: setLoading2 } = statusRef.current;
|
|
54
|
+
if (!uid) {
|
|
55
|
+
setStatus2(FREE_STATUS);
|
|
56
|
+
setLoading2(false);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
refreshMountedRef.current = true;
|
|
60
|
+
try {
|
|
61
|
+
setLoading2(true);
|
|
62
|
+
const s = await adapterRef.current.getStatus(uid);
|
|
63
|
+
if (refreshMountedRef.current) setStatus2(s);
|
|
64
|
+
} catch (err) {
|
|
65
|
+
if (refreshMountedRef.current) {
|
|
66
|
+
setStatus2(FREE_STATUS);
|
|
67
|
+
}
|
|
68
|
+
} finally {
|
|
69
|
+
if (refreshMountedRef.current) setLoading2(false);
|
|
70
|
+
}
|
|
71
|
+
}, []);
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
refreshMountedRef.current = true;
|
|
74
|
+
refresh();
|
|
75
|
+
return () => {
|
|
76
|
+
refreshMountedRef.current = false;
|
|
77
|
+
};
|
|
78
|
+
}, [userId]);
|
|
79
|
+
const startCheckout = useCallback(async (params) => {
|
|
80
|
+
if (!isValidProductId(params.productId)) {
|
|
81
|
+
throw new Error("[polar-billing] Invalid productId: must be a non-empty string");
|
|
82
|
+
}
|
|
83
|
+
const uid = userIdRef.current;
|
|
84
|
+
const result = await adapterRef.current.createCheckout({ ...params, userId: uid ?? void 0 });
|
|
85
|
+
if (!isValidCheckoutUrl(result.url)) {
|
|
86
|
+
throw new Error("[polar-billing] Invalid checkout URL returned: URL must start with https:// or http://");
|
|
87
|
+
}
|
|
88
|
+
if (isProductionInsecureUrl(result.url)) {
|
|
89
|
+
throw new Error("[polar-billing] ERROR: Using insecure http:// URL in production environment");
|
|
90
|
+
}
|
|
91
|
+
window.location.href = result.url;
|
|
92
|
+
}, []);
|
|
93
|
+
const syncSubscription = useCallback(async () => {
|
|
94
|
+
const uid = userIdRef.current;
|
|
95
|
+
if (!uid) return { synced: false };
|
|
96
|
+
const checkoutId = new URLSearchParams(window.location.search).get("checkout_id") ?? void 0;
|
|
97
|
+
const result = await adapterRef.current.syncSubscription(uid, checkoutId);
|
|
98
|
+
if (result.synced) await refresh();
|
|
99
|
+
return result;
|
|
100
|
+
}, [refresh]);
|
|
101
|
+
const getBillingHistory = useCallback(async () => {
|
|
102
|
+
const uid = userIdRef.current;
|
|
103
|
+
if (!uid) return [];
|
|
104
|
+
return adapterRef.current.getBillingHistory(uid);
|
|
105
|
+
}, []);
|
|
106
|
+
const cancelSubscription = useCallback(
|
|
107
|
+
async (reason) => {
|
|
108
|
+
const result = await adapterRef.current.cancelSubscription(reason);
|
|
109
|
+
if (result.success) await refresh();
|
|
110
|
+
return result;
|
|
111
|
+
},
|
|
112
|
+
[refresh]
|
|
113
|
+
);
|
|
114
|
+
const getPortalUrl = useCallback(async () => {
|
|
115
|
+
const uid = userIdRef.current;
|
|
116
|
+
if (!uid) throw new Error("[polar-billing] Cannot get portal URL: No authenticated user");
|
|
117
|
+
return adapterRef.current.getPortalUrl(uid);
|
|
118
|
+
}, []);
|
|
119
|
+
const value = useMemo(
|
|
120
|
+
() => ({
|
|
121
|
+
status,
|
|
122
|
+
loading,
|
|
123
|
+
refresh,
|
|
124
|
+
startCheckout,
|
|
125
|
+
syncSubscription,
|
|
126
|
+
getBillingHistory,
|
|
127
|
+
cancelSubscription,
|
|
128
|
+
getPortalUrl
|
|
129
|
+
}),
|
|
130
|
+
[status, loading, refresh, syncSubscription]
|
|
131
|
+
);
|
|
132
|
+
return /* @__PURE__ */ jsx(PolarContext.Provider, { value, children });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/infrastructure/constants/billing.constants.ts
|
|
136
|
+
var SUBSCRIPTION_STATUS = Object.freeze({
|
|
137
|
+
ACTIVE: "active",
|
|
138
|
+
CANCELED: "canceled",
|
|
139
|
+
REVOKED: "revoked",
|
|
140
|
+
TRIALING: "trialing",
|
|
141
|
+
PAST_DUE: "past_due",
|
|
142
|
+
INCOMPLETE: "incomplete",
|
|
143
|
+
INCOMPLETE_EXPIRED: "incomplete_expired",
|
|
144
|
+
UNPAID: "unpaid",
|
|
145
|
+
NONE: "none"
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// src/infrastructure/utils/normalization.util.ts
|
|
149
|
+
var STATUS_MAP = Object.freeze({
|
|
150
|
+
[SUBSCRIPTION_STATUS.ACTIVE]: SUBSCRIPTION_STATUS.ACTIVE,
|
|
151
|
+
[SUBSCRIPTION_STATUS.CANCELED]: SUBSCRIPTION_STATUS.CANCELED,
|
|
152
|
+
[SUBSCRIPTION_STATUS.REVOKED]: SUBSCRIPTION_STATUS.REVOKED,
|
|
153
|
+
[SUBSCRIPTION_STATUS.TRIALING]: SUBSCRIPTION_STATUS.TRIALING,
|
|
154
|
+
[SUBSCRIPTION_STATUS.PAST_DUE]: SUBSCRIPTION_STATUS.PAST_DUE,
|
|
155
|
+
[SUBSCRIPTION_STATUS.INCOMPLETE]: SUBSCRIPTION_STATUS.INCOMPLETE,
|
|
156
|
+
[SUBSCRIPTION_STATUS.INCOMPLETE_EXPIRED]: SUBSCRIPTION_STATUS.INCOMPLETE_EXPIRED,
|
|
157
|
+
[SUBSCRIPTION_STATUS.UNPAID]: SUBSCRIPTION_STATUS.UNPAID,
|
|
158
|
+
[SUBSCRIPTION_STATUS.NONE]: SUBSCRIPTION_STATUS.NONE
|
|
159
|
+
});
|
|
14
160
|
function normalizeStatus(raw) {
|
|
15
|
-
if (typeof raw !== "string") return "none";
|
|
161
|
+
if (raw == null || typeof raw !== "string") return "none";
|
|
16
162
|
return STATUS_MAP[raw.toLowerCase()] ?? "none";
|
|
17
163
|
}
|
|
18
164
|
function normalizeBillingCycle(interval) {
|
|
@@ -23,7 +169,7 @@ function normalizeBillingCycle(interval) {
|
|
|
23
169
|
return "monthly";
|
|
24
170
|
}
|
|
25
171
|
|
|
26
|
-
// src/infrastructure/
|
|
172
|
+
// src/infrastructure/utils/firebase-helpers.util.ts
|
|
27
173
|
function asString(value) {
|
|
28
174
|
if (typeof value === "string") return value;
|
|
29
175
|
return void 0;
|
|
@@ -33,8 +179,10 @@ function asBoolean(value) {
|
|
|
33
179
|
return void 0;
|
|
34
180
|
}
|
|
35
181
|
function isTimestamp(value) {
|
|
36
|
-
return typeof value === "object" && value !== null && "toDate" in value && typeof value.toDate
|
|
182
|
+
return typeof value === "object" && value !== null && "toDate" in value && "function" === typeof value.toDate;
|
|
37
183
|
}
|
|
184
|
+
|
|
185
|
+
// src/infrastructure/services/firebase-billing.service.ts
|
|
38
186
|
function createFirebaseAdapter(config) {
|
|
39
187
|
const functions = config.functions;
|
|
40
188
|
const firestore = config.firestore;
|
|
@@ -60,7 +208,7 @@ function createFirebaseAdapter(config) {
|
|
|
60
208
|
async function getHttpsCallable() {
|
|
61
209
|
if (!httpsCallableCache) {
|
|
62
210
|
const mod = await import("firebase/functions");
|
|
63
|
-
httpsCallableCache = mod.httpsCallable;
|
|
211
|
+
httpsCallableCache = mod.httpsCallable(functions);
|
|
64
212
|
}
|
|
65
213
|
return httpsCallableCache;
|
|
66
214
|
}
|
|
@@ -73,15 +221,17 @@ function createFirebaseAdapter(config) {
|
|
|
73
221
|
}
|
|
74
222
|
async function callable(name, data) {
|
|
75
223
|
const httpsCallable = await getHttpsCallable();
|
|
76
|
-
const fn = httpsCallable(
|
|
224
|
+
const fn = httpsCallable(name);
|
|
77
225
|
const result = await fn(data);
|
|
78
226
|
return result.data;
|
|
79
227
|
}
|
|
80
228
|
return {
|
|
81
229
|
async getStatus(userId) {
|
|
82
230
|
const { doc, getDoc } = await getFirestore();
|
|
83
|
-
const
|
|
84
|
-
|
|
231
|
+
const docRef = doc(firestore, db.collection, userId);
|
|
232
|
+
const snap = await getDoc(docRef);
|
|
233
|
+
const exists = snap.exists;
|
|
234
|
+
if (!exists) {
|
|
85
235
|
return { plan: "free", subscriptionStatus: "none" };
|
|
86
236
|
}
|
|
87
237
|
const d = snap.data();
|
|
@@ -107,174 +257,31 @@ function createFirebaseAdapter(config) {
|
|
|
107
257
|
async createCheckout(params) {
|
|
108
258
|
return callable(callables.createCheckout, params);
|
|
109
259
|
},
|
|
110
|
-
async syncSubscription(
|
|
111
|
-
return callable(callables.sync, {});
|
|
260
|
+
async syncSubscription(userId, checkoutId) {
|
|
261
|
+
return callable(callables.sync, { userId, checkoutId });
|
|
112
262
|
},
|
|
113
|
-
async getBillingHistory(
|
|
263
|
+
async getBillingHistory(userId) {
|
|
114
264
|
const result = await callable(
|
|
115
265
|
callables.billing,
|
|
116
|
-
{}
|
|
266
|
+
{ userId }
|
|
117
267
|
);
|
|
118
268
|
return result.orders ?? [];
|
|
119
269
|
},
|
|
120
270
|
async cancelSubscription(reason) {
|
|
121
271
|
return callable(callables.cancel, { reason });
|
|
122
272
|
},
|
|
123
|
-
async getPortalUrl(
|
|
273
|
+
async getPortalUrl(userId) {
|
|
124
274
|
const result = await callable(
|
|
125
275
|
callables.portal,
|
|
126
|
-
{}
|
|
276
|
+
{ userId }
|
|
127
277
|
);
|
|
128
|
-
|
|
129
|
-
if (!url) throw new Error("No portal URL returned from Cloud Function");
|
|
130
|
-
return url;
|
|
278
|
+
return result.url;
|
|
131
279
|
}
|
|
132
280
|
};
|
|
133
281
|
}
|
|
134
|
-
|
|
135
|
-
// src/infrastructure/constants/billing.constants.ts
|
|
136
|
-
var SUBSCRIPTION_STATUS = Object.freeze({
|
|
137
|
-
ACTIVE: "active",
|
|
138
|
-
CANCELED: "canceled",
|
|
139
|
-
REVOKED: "revoked",
|
|
140
|
-
TRIALING: "trialing",
|
|
141
|
-
PAST_DUE: "past_due",
|
|
142
|
-
INCOMPLETE: "incomplete",
|
|
143
|
-
INCOMPLETE_EXPIRED: "incomplete_expired",
|
|
144
|
-
UNPAID: "unpaid",
|
|
145
|
-
NONE: "none"
|
|
146
|
-
});
|
|
147
|
-
var FREE_PLAN = "free";
|
|
148
|
-
|
|
149
|
-
// src/presentation/components/PolarProvider.tsx
|
|
150
|
-
import {
|
|
151
|
-
useEffect,
|
|
152
|
-
useState,
|
|
153
|
-
useCallback,
|
|
154
|
-
useRef,
|
|
155
|
-
useMemo
|
|
156
|
-
} from "react";
|
|
157
|
-
|
|
158
|
-
// src/presentation/hooks/usePolarBilling.ts
|
|
159
|
-
import { createContext, useContext } from "react";
|
|
160
|
-
var PolarContext = createContext(void 0);
|
|
161
|
-
function usePolarBilling() {
|
|
162
|
-
const ctx = useContext(PolarContext);
|
|
163
|
-
if (!ctx) throw new Error("usePolarBilling must be used within <PolarProvider>");
|
|
164
|
-
return ctx;
|
|
165
|
-
}
|
|
166
|
-
var useSubscription = usePolarBilling;
|
|
167
|
-
|
|
168
|
-
// src/presentation/components/PolarProvider.tsx
|
|
169
|
-
import { jsx } from "react/jsx-runtime";
|
|
170
|
-
var FREE_STATUS = {
|
|
171
|
-
plan: "free",
|
|
172
|
-
subscriptionStatus: "none"
|
|
173
|
-
};
|
|
174
|
-
function normalizeUserId(userId) {
|
|
175
|
-
if (typeof userId !== "string") return void 0;
|
|
176
|
-
const trimmed = userId.trim();
|
|
177
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
178
|
-
}
|
|
179
|
-
function PolarProvider({ adapter, userId, children }) {
|
|
180
|
-
const [status, setStatus] = useState(FREE_STATUS);
|
|
181
|
-
const [loading, setLoading] = useState(true);
|
|
182
|
-
const adapterRef = useRef(adapter);
|
|
183
|
-
const statusRef = useRef({ setStatus, setLoading });
|
|
184
|
-
const userIdRef = useRef(normalizeUserId(userId));
|
|
185
|
-
const refreshAbortRef = useRef(null);
|
|
186
|
-
adapterRef.current = adapter;
|
|
187
|
-
statusRef.current = { setStatus, setLoading };
|
|
188
|
-
userIdRef.current = normalizeUserId(userId);
|
|
189
|
-
const refresh = useCallback(async () => {
|
|
190
|
-
const uid = userIdRef.current;
|
|
191
|
-
const { setStatus: setStatus2, setLoading: setLoading2 } = statusRef.current;
|
|
192
|
-
if (!uid) {
|
|
193
|
-
setStatus2(FREE_STATUS);
|
|
194
|
-
setLoading2(false);
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
refreshAbortRef.current?.abort();
|
|
198
|
-
const ctrl = new AbortController();
|
|
199
|
-
refreshAbortRef.current = ctrl;
|
|
200
|
-
try {
|
|
201
|
-
setLoading2(true);
|
|
202
|
-
const s = await adapterRef.current.getStatus(uid);
|
|
203
|
-
if (!ctrl.signal.aborted) setStatus2(s);
|
|
204
|
-
} catch (err) {
|
|
205
|
-
if (!ctrl.signal.aborted) {
|
|
206
|
-
console.error("[polar-billing] getStatus failed:", err);
|
|
207
|
-
setStatus2(FREE_STATUS);
|
|
208
|
-
}
|
|
209
|
-
} finally {
|
|
210
|
-
if (!ctrl.signal.aborted) setLoading2(false);
|
|
211
|
-
}
|
|
212
|
-
}, []);
|
|
213
|
-
useEffect(() => {
|
|
214
|
-
refresh();
|
|
215
|
-
return () => {
|
|
216
|
-
refreshAbortRef.current?.abort();
|
|
217
|
-
};
|
|
218
|
-
}, [refresh]);
|
|
219
|
-
const startCheckout = useCallback(async (params) => {
|
|
220
|
-
const uid = userIdRef.current;
|
|
221
|
-
const result = await adapterRef.current.createCheckout({ ...params, userId: uid ?? void 0 });
|
|
222
|
-
if (!result.url.startsWith("https://")) {
|
|
223
|
-
throw new Error("[polar-billing] Invalid checkout URL returned: URL must start with https://");
|
|
224
|
-
}
|
|
225
|
-
window.location.href = result.url;
|
|
226
|
-
}, []);
|
|
227
|
-
const syncSubscription = useCallback(async () => {
|
|
228
|
-
const uid = userIdRef.current;
|
|
229
|
-
if (!uid) return { synced: false };
|
|
230
|
-
const checkoutId = new URLSearchParams(window.location.search).get("checkout_id") ?? void 0;
|
|
231
|
-
const result = await adapterRef.current.syncSubscription(uid, checkoutId);
|
|
232
|
-
if (result.synced) await refresh();
|
|
233
|
-
return result;
|
|
234
|
-
}, [refresh]);
|
|
235
|
-
const getBillingHistory = useCallback(async () => {
|
|
236
|
-
const uid = userIdRef.current;
|
|
237
|
-
if (!uid) return [];
|
|
238
|
-
return adapterRef.current.getBillingHistory(uid);
|
|
239
|
-
}, []);
|
|
240
|
-
const cancelSubscription = useCallback(
|
|
241
|
-
async (reason) => {
|
|
242
|
-
const result = await adapterRef.current.cancelSubscription(reason);
|
|
243
|
-
if (result.success) await refresh();
|
|
244
|
-
return result;
|
|
245
|
-
},
|
|
246
|
-
[refresh]
|
|
247
|
-
// Only depends on stable refresh
|
|
248
|
-
);
|
|
249
|
-
const getPortalUrl = useCallback(async () => {
|
|
250
|
-
const uid = userIdRef.current;
|
|
251
|
-
if (!uid) throw new Error("[polar-billing] Cannot get portal URL: No authenticated user");
|
|
252
|
-
return adapterRef.current.getPortalUrl(uid);
|
|
253
|
-
}, []);
|
|
254
|
-
const value = useMemo(
|
|
255
|
-
() => ({
|
|
256
|
-
status,
|
|
257
|
-
loading,
|
|
258
|
-
refresh,
|
|
259
|
-
startCheckout,
|
|
260
|
-
syncSubscription,
|
|
261
|
-
getBillingHistory,
|
|
262
|
-
cancelSubscription,
|
|
263
|
-
getPortalUrl
|
|
264
|
-
}),
|
|
265
|
-
[status, loading, refresh, syncSubscription]
|
|
266
|
-
// startCheckout, getBillingHistory, cancelSubscription, getPortalUrl are stable
|
|
267
|
-
);
|
|
268
|
-
return /* @__PURE__ */ jsx(PolarContext.Provider, { value, children });
|
|
269
|
-
}
|
|
270
282
|
export {
|
|
271
|
-
FREE_PLAN,
|
|
272
|
-
PolarContext,
|
|
273
283
|
PolarProvider,
|
|
274
284
|
SUBSCRIPTION_STATUS,
|
|
275
285
|
createFirebaseAdapter,
|
|
276
|
-
|
|
277
|
-
normalizeStatus,
|
|
278
|
-
usePolarBilling,
|
|
279
|
-
useSubscription
|
|
286
|
+
usePolarBilling
|
|
280
287
|
};
|
package/package.json
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Checkout Entity
|
|
3
|
-
* @description Types for initiating and following process of checkouts
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
export interface CheckoutParams {
|
|
7
2
|
productId: string;
|
|
8
3
|
planKey?: string;
|
|
9
4
|
billingCycle?: 'monthly' | 'yearly';
|
|
10
5
|
successUrl?: string;
|
|
11
|
-
/** Injected automatically by PolarProvider — do not pass manually */
|
|
12
6
|
userId?: string;
|
|
13
7
|
}
|
|
14
8
|
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Subscription Entity
|
|
3
|
-
* @description Types for subscription status and billing cycles
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
export type SubscriptionStatusValue =
|
|
7
2
|
| 'active'
|
|
8
3
|
| 'canceled'
|
|
@@ -24,6 +19,5 @@ export interface SubscriptionStatus {
|
|
|
24
19
|
currentPeriodEnd?: string;
|
|
25
20
|
billingCycle?: BillingCycle;
|
|
26
21
|
polarCustomerId?: string;
|
|
27
|
-
/** Token balance (for token-based projects like Aria) */
|
|
28
22
|
tokens?: number;
|
|
29
23
|
}
|
package/src/domain/index.ts
CHANGED
|
@@ -8,14 +8,9 @@ import type {
|
|
|
8
8
|
SyncResult,
|
|
9
9
|
} from '../entities';
|
|
10
10
|
|
|
11
|
-
/**
|
|
12
|
-
* Backend-agnostic interface every adapter must implement.
|
|
13
|
-
* @description Contract for Polar billing adapters (Firebase, Supabase, etc.)
|
|
14
|
-
*/
|
|
15
11
|
export interface PolarAdapter {
|
|
16
12
|
getStatus(userId: string): Promise<SubscriptionStatus>;
|
|
17
13
|
createCheckout(params: CheckoutParams): Promise<CheckoutResult>;
|
|
18
|
-
/** checkoutId is read from URL by the context and passed explicitly */
|
|
19
14
|
syncSubscription(userId: string, checkoutId?: string): Promise<SyncResult>;
|
|
20
15
|
getBillingHistory(userId: string): Promise<OrderItem[]>;
|
|
21
16
|
cancelSubscription(reason?: CancellationReason): Promise<CancelResult>;
|
package/src/index.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export
|
|
10
|
-
export * from './infrastructure';
|
|
11
|
-
export * from './presentation';
|
|
1
|
+
export { PolarProvider } from './presentation/components/PolarProvider';
|
|
2
|
+
export { usePolarBilling } from './presentation/hooks/usePolarBilling';
|
|
3
|
+
export type { PolarContextValue } from './presentation/hooks/usePolarBilling';
|
|
4
|
+
export { createFirebaseAdapter } from './infrastructure/services/firebase-billing.service';
|
|
5
|
+
export type { FirebaseAdapterConfig } from './infrastructure/services/firebase-billing.service';
|
|
6
|
+
export { SUBSCRIPTION_STATUS } from './infrastructure/constants/billing.constants';
|
|
7
|
+
export type { SubscriptionStatusValue, BillingCycle } from './domain/entities/subscription.entity';
|
|
8
|
+
export type { SubscriptionStatus, CheckoutParams, CheckoutResult, OrderItem, CancellationReason, CancelResult, SyncResult } from './domain/entities';
|
|
9
|
+
export type { PolarAdapter } from './domain/interfaces';
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Billing Constants
|
|
3
|
-
* @description Standardized subscription states and plan names
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
// Object.freeze() prevents accidental mutations and enables V8 optimizations
|
|
7
1
|
export const SUBSCRIPTION_STATUS = Object.freeze({
|
|
8
2
|
ACTIVE: 'active' as const,
|
|
9
3
|
CANCELED: 'canceled' as const,
|
|
@@ -25,5 +19,3 @@ export const SUBSCRIPTION_STATUS = Object.freeze({
|
|
|
25
19
|
UNPAID: 'unpaid';
|
|
26
20
|
NONE: 'none';
|
|
27
21
|
}>;
|
|
28
|
-
|
|
29
|
-
export const FREE_PLAN = 'free';
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Infrastructure Layer
|
|
3
|
-
* Subpath: @umituz/web-polar-payment/infrastructure
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
export * from './services/firebase-billing.service';
|
|
7
2
|
export * from './constants/billing.constants';
|
|
8
3
|
export * from './utils/normalization.util';
|
|
4
|
+
export * from './utils/validations.util';
|
|
5
|
+
export * from './utils/firebase-helpers.util';
|