@solvapay/react 1.0.0-preview.19 → 1.0.0-preview.20
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/README.md +29 -29
- package/dist/index.cjs +170 -166
- package/dist/index.d.cts +166 -147
- package/dist/index.d.ts +166 -147
- package/dist/index.js +160 -156
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -23,57 +23,57 @@ __export(index_exports, {
|
|
|
23
23
|
PaymentForm: () => PaymentForm,
|
|
24
24
|
PlanBadge: () => PlanBadge,
|
|
25
25
|
PlanSelector: () => PlanSelector,
|
|
26
|
+
PurchaseGate: () => PurchaseGate,
|
|
26
27
|
SolvaPayProvider: () => SolvaPayProvider,
|
|
27
28
|
Spinner: () => Spinner,
|
|
28
29
|
StripePaymentFormWrapper: () => StripePaymentFormWrapper,
|
|
29
|
-
SubscriptionGate: () => SubscriptionGate,
|
|
30
30
|
defaultAuthAdapter: () => defaultAuthAdapter,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
filterPurchases: () => filterPurchases,
|
|
32
|
+
getActivePurchases: () => getActivePurchases,
|
|
33
|
+
getCancelledPurchasesWithEndDate: () => getCancelledPurchasesWithEndDate,
|
|
34
|
+
getMostRecentPurchase: () => getMostRecentPurchase,
|
|
35
|
+
getPrimaryPurchase: () => getPrimaryPurchase,
|
|
36
|
+
isPaidPurchase: () => isPaidPurchase,
|
|
37
37
|
useCheckout: () => useCheckout,
|
|
38
38
|
useCustomer: () => useCustomer,
|
|
39
39
|
usePlans: () => usePlans,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
usePurchase: () => usePurchase,
|
|
41
|
+
usePurchaseStatus: () => usePurchaseStatus,
|
|
42
|
+
useSolvaPay: () => useSolvaPay
|
|
43
43
|
});
|
|
44
44
|
module.exports = __toCommonJS(index_exports);
|
|
45
45
|
|
|
46
46
|
// src/SolvaPayProvider.tsx
|
|
47
47
|
var import_react = require("react");
|
|
48
48
|
|
|
49
|
-
// src/utils/
|
|
50
|
-
function
|
|
51
|
-
return
|
|
49
|
+
// src/utils/purchases.ts
|
|
50
|
+
function filterPurchases(purchases) {
|
|
51
|
+
return purchases.filter((purchase) => purchase.status === "active");
|
|
52
52
|
}
|
|
53
|
-
function
|
|
54
|
-
return
|
|
53
|
+
function getActivePurchases(purchases) {
|
|
54
|
+
return purchases.filter((purchase) => purchase.status === "active");
|
|
55
55
|
}
|
|
56
|
-
function
|
|
56
|
+
function getCancelledPurchasesWithEndDate(purchases) {
|
|
57
57
|
const now = /* @__PURE__ */ new Date();
|
|
58
|
-
return
|
|
59
|
-
return
|
|
58
|
+
return purchases.filter((purchase) => {
|
|
59
|
+
return purchase.status === "active" && purchase.cancelledAt && purchase.endDate && new Date(purchase.endDate) > now;
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
|
-
function
|
|
63
|
-
if (
|
|
64
|
-
return
|
|
62
|
+
function getMostRecentPurchase(purchases) {
|
|
63
|
+
if (purchases.length === 0) return null;
|
|
64
|
+
return purchases.reduce((latest, current) => {
|
|
65
65
|
return new Date(current.startDate) > new Date(latest.startDate) ? current : latest;
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
|
-
function
|
|
69
|
-
const filtered =
|
|
68
|
+
function getPrimaryPurchase(purchases) {
|
|
69
|
+
const filtered = filterPurchases(purchases);
|
|
70
70
|
if (filtered.length > 0) {
|
|
71
|
-
return
|
|
71
|
+
return getMostRecentPurchase(filtered);
|
|
72
72
|
}
|
|
73
73
|
return null;
|
|
74
74
|
}
|
|
75
|
-
function
|
|
76
|
-
return (
|
|
75
|
+
function isPaidPurchase(purchase) {
|
|
76
|
+
return (purchase.amount ?? 0) > 0;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
// src/adapters/auth.ts
|
|
@@ -159,12 +159,12 @@ function getAuthAdapter(config) {
|
|
|
159
159
|
var SolvaPayProvider = ({
|
|
160
160
|
config,
|
|
161
161
|
createPayment: customCreatePayment,
|
|
162
|
-
|
|
162
|
+
checkPurchase: customCheckPurchase,
|
|
163
163
|
processPayment: customProcessPayment,
|
|
164
164
|
children
|
|
165
165
|
}) => {
|
|
166
|
-
const [
|
|
167
|
-
|
|
166
|
+
const [purchaseData, setPurchaseData] = (0, import_react.useState)({
|
|
167
|
+
purchases: []
|
|
168
168
|
});
|
|
169
169
|
const [loading, setLoading] = (0, import_react.useState)(false);
|
|
170
170
|
const [internalCustomerRef, setInternalCustomerRef] = (0, import_react.useState)(void 0);
|
|
@@ -172,31 +172,31 @@ var SolvaPayProvider = ({
|
|
|
172
172
|
const [isAuthenticated, setIsAuthenticated] = (0, import_react.useState)(false);
|
|
173
173
|
const inFlightRef = (0, import_react.useRef)(null);
|
|
174
174
|
const lastFetchedRef = (0, import_react.useRef)(null);
|
|
175
|
-
const
|
|
175
|
+
const checkPurchaseRef = (0, import_react.useRef)(null);
|
|
176
176
|
const createPaymentRef = (0, import_react.useRef)(null);
|
|
177
177
|
const processPaymentRef = (0, import_react.useRef)(null);
|
|
178
178
|
const configRef = (0, import_react.useRef)(config);
|
|
179
|
-
const
|
|
179
|
+
const buildDefaultCheckPurchaseRef = (0, import_react.useRef)(
|
|
180
180
|
null
|
|
181
181
|
);
|
|
182
182
|
(0, import_react.useEffect)(() => {
|
|
183
183
|
configRef.current = config;
|
|
184
184
|
}, [config]);
|
|
185
185
|
(0, import_react.useEffect)(() => {
|
|
186
|
-
|
|
187
|
-
}, [
|
|
186
|
+
checkPurchaseRef.current = customCheckPurchase || null;
|
|
187
|
+
}, [customCheckPurchase]);
|
|
188
188
|
(0, import_react.useEffect)(() => {
|
|
189
189
|
createPaymentRef.current = customCreatePayment || null;
|
|
190
190
|
}, [customCreatePayment]);
|
|
191
191
|
(0, import_react.useEffect)(() => {
|
|
192
192
|
processPaymentRef.current = customProcessPayment || null;
|
|
193
193
|
}, [customProcessPayment]);
|
|
194
|
-
const
|
|
194
|
+
const buildDefaultCheckPurchase = (0, import_react.useCallback)(async () => {
|
|
195
195
|
const currentConfig = configRef.current;
|
|
196
196
|
const adapter = getAuthAdapter(currentConfig);
|
|
197
197
|
const token = await adapter.getToken();
|
|
198
198
|
const detectedUserId = await adapter.getUserId();
|
|
199
|
-
const route = currentConfig?.api?.
|
|
199
|
+
const route = currentConfig?.api?.checkPurchase || "/api/check-purchase";
|
|
200
200
|
const fetchFn = currentConfig?.fetch || fetch;
|
|
201
201
|
const cachedRef = getCachedCustomerRef(detectedUserId);
|
|
202
202
|
const headers = {
|
|
@@ -217,15 +217,15 @@ var SolvaPayProvider = ({
|
|
|
217
217
|
headers
|
|
218
218
|
});
|
|
219
219
|
if (!res.ok) {
|
|
220
|
-
const error = new Error(`Failed to check
|
|
221
|
-
currentConfig?.onError?.(error, "
|
|
220
|
+
const error = new Error(`Failed to check purchase: ${res.statusText}`);
|
|
221
|
+
currentConfig?.onError?.(error, "checkPurchase");
|
|
222
222
|
throw error;
|
|
223
223
|
}
|
|
224
224
|
return res.json();
|
|
225
225
|
}, []);
|
|
226
226
|
(0, import_react.useEffect)(() => {
|
|
227
|
-
|
|
228
|
-
}, [
|
|
227
|
+
buildDefaultCheckPurchaseRef.current = buildDefaultCheckPurchase;
|
|
228
|
+
}, [buildDefaultCheckPurchase]);
|
|
229
229
|
const buildDefaultCreatePayment = (0, import_react.useCallback)(
|
|
230
230
|
async (params) => {
|
|
231
231
|
const currentConfig = configRef.current;
|
|
@@ -249,8 +249,8 @@ var SolvaPayProvider = ({
|
|
|
249
249
|
Object.assign(headers, customHeaders);
|
|
250
250
|
}
|
|
251
251
|
const body = { planRef: params.planRef };
|
|
252
|
-
if (params.
|
|
253
|
-
body.
|
|
252
|
+
if (params.productRef) {
|
|
253
|
+
body.productRef = params.productRef;
|
|
254
254
|
}
|
|
255
255
|
const res = await fetchFn(route, {
|
|
256
256
|
method: "POST",
|
|
@@ -302,15 +302,15 @@ var SolvaPayProvider = ({
|
|
|
302
302
|
},
|
|
303
303
|
[]
|
|
304
304
|
);
|
|
305
|
-
const
|
|
306
|
-
if (
|
|
307
|
-
return
|
|
305
|
+
const _checkPurchase = (0, import_react.useCallback)(async () => {
|
|
306
|
+
if (checkPurchaseRef.current) {
|
|
307
|
+
return checkPurchaseRef.current();
|
|
308
308
|
}
|
|
309
|
-
if (
|
|
310
|
-
return
|
|
309
|
+
if (buildDefaultCheckPurchaseRef.current) {
|
|
310
|
+
return buildDefaultCheckPurchaseRef.current();
|
|
311
311
|
}
|
|
312
|
-
return
|
|
313
|
-
}, [
|
|
312
|
+
return buildDefaultCheckPurchase();
|
|
313
|
+
}, [buildDefaultCheckPurchase]);
|
|
314
314
|
const createPayment = (0, import_react.useCallback)(
|
|
315
315
|
async (params) => {
|
|
316
316
|
if (createPaymentRef.current) {
|
|
@@ -357,10 +357,10 @@ var SolvaPayProvider = ({
|
|
|
357
357
|
const interval = setInterval(detectAuth, 5e3);
|
|
358
358
|
return () => clearInterval(interval);
|
|
359
359
|
}, [userId]);
|
|
360
|
-
const
|
|
360
|
+
const fetchPurchase = (0, import_react.useCallback)(
|
|
361
361
|
async (force = false) => {
|
|
362
362
|
if (!isAuthenticated && !internalCustomerRef) {
|
|
363
|
-
|
|
363
|
+
setPurchaseData({ purchases: [] });
|
|
364
364
|
setLoading(false);
|
|
365
365
|
inFlightRef.current = null;
|
|
366
366
|
lastFetchedRef.current = null;
|
|
@@ -376,9 +376,9 @@ var SolvaPayProvider = ({
|
|
|
376
376
|
inFlightRef.current = cacheKey;
|
|
377
377
|
setLoading(true);
|
|
378
378
|
try {
|
|
379
|
-
const checkFn =
|
|
379
|
+
const checkFn = checkPurchaseRef.current || buildDefaultCheckPurchaseRef.current;
|
|
380
380
|
if (!checkFn) {
|
|
381
|
-
throw new Error("
|
|
381
|
+
throw new Error("checkPurchase function not available");
|
|
382
382
|
}
|
|
383
383
|
const data = await checkFn();
|
|
384
384
|
if (data.customerRef) {
|
|
@@ -390,16 +390,16 @@ var SolvaPayProvider = ({
|
|
|
390
390
|
if (inFlightRef.current === cacheKey) {
|
|
391
391
|
const filteredData = {
|
|
392
392
|
...data,
|
|
393
|
-
|
|
393
|
+
purchases: filterPurchases(data.purchases || [])
|
|
394
394
|
};
|
|
395
|
-
|
|
395
|
+
setPurchaseData(filteredData);
|
|
396
396
|
lastFetchedRef.current = cacheKey;
|
|
397
397
|
}
|
|
398
398
|
} catch (error) {
|
|
399
|
-
console.error("[SolvaPayProvider] Failed to fetch
|
|
399
|
+
console.error("[SolvaPayProvider] Failed to fetch purchase:", error);
|
|
400
400
|
if (inFlightRef.current === cacheKey) {
|
|
401
|
-
|
|
402
|
-
|
|
401
|
+
setPurchaseData({
|
|
402
|
+
purchases: []
|
|
403
403
|
});
|
|
404
404
|
lastFetchedRef.current = cacheKey;
|
|
405
405
|
}
|
|
@@ -412,23 +412,23 @@ var SolvaPayProvider = ({
|
|
|
412
412
|
},
|
|
413
413
|
[isAuthenticated, internalCustomerRef, userId]
|
|
414
414
|
);
|
|
415
|
-
const
|
|
415
|
+
const fetchPurchaseRef = (0, import_react.useRef)(fetchPurchase);
|
|
416
416
|
(0, import_react.useEffect)(() => {
|
|
417
|
-
|
|
418
|
-
}, [
|
|
419
|
-
const
|
|
417
|
+
fetchPurchaseRef.current = fetchPurchase;
|
|
418
|
+
}, [fetchPurchase]);
|
|
419
|
+
const refetchPurchase = (0, import_react.useCallback)(async () => {
|
|
420
420
|
lastFetchedRef.current = null;
|
|
421
421
|
inFlightRef.current = null;
|
|
422
|
-
|
|
423
|
-
await
|
|
422
|
+
setPurchaseData({ purchases: [] });
|
|
423
|
+
await fetchPurchaseRef.current(true);
|
|
424
424
|
}, []);
|
|
425
425
|
(0, import_react.useEffect)(() => {
|
|
426
426
|
lastFetchedRef.current = null;
|
|
427
427
|
inFlightRef.current = null;
|
|
428
428
|
if (isAuthenticated || internalCustomerRef) {
|
|
429
|
-
|
|
429
|
+
fetchPurchase();
|
|
430
430
|
} else {
|
|
431
|
-
|
|
431
|
+
setPurchaseData({ purchases: [] });
|
|
432
432
|
setLoading(false);
|
|
433
433
|
}
|
|
434
434
|
}, [isAuthenticated, internalCustomerRef, userId]);
|
|
@@ -436,49 +436,49 @@ var SolvaPayProvider = ({
|
|
|
436
436
|
(newCustomerRef) => {
|
|
437
437
|
setInternalCustomerRef(newCustomerRef);
|
|
438
438
|
setCachedCustomerRef(newCustomerRef, userId);
|
|
439
|
-
|
|
439
|
+
fetchPurchase(true);
|
|
440
440
|
},
|
|
441
|
-
[
|
|
441
|
+
[fetchPurchase, userId]
|
|
442
442
|
);
|
|
443
|
-
const
|
|
444
|
-
const
|
|
445
|
-
const
|
|
446
|
-
(
|
|
443
|
+
const purchase = (0, import_react.useMemo)(() => {
|
|
444
|
+
const activePurchase = getPrimaryPurchase(purchaseData.purchases);
|
|
445
|
+
const activePaidPurchases = purchaseData.purchases.filter(
|
|
446
|
+
(p) => p.status === "active" && isPaidPurchase(p)
|
|
447
447
|
);
|
|
448
|
-
const
|
|
448
|
+
const activePaidPurchase = activePaidPurchases.sort(
|
|
449
449
|
(a, b) => new Date(b.startDate).getTime() - new Date(a.startDate).getTime()
|
|
450
450
|
)[0] || null;
|
|
451
451
|
return {
|
|
452
452
|
loading,
|
|
453
|
-
customerRef:
|
|
454
|
-
email:
|
|
455
|
-
name:
|
|
456
|
-
|
|
453
|
+
customerRef: purchaseData.customerRef || internalCustomerRef,
|
|
454
|
+
email: purchaseData.email,
|
|
455
|
+
name: purchaseData.name,
|
|
456
|
+
purchases: purchaseData.purchases,
|
|
457
457
|
hasPlan: (planName) => {
|
|
458
|
-
return
|
|
459
|
-
(
|
|
458
|
+
return purchaseData.purchases.some(
|
|
459
|
+
(p) => p.planName.toLowerCase() === planName.toLowerCase() && p.status === "active"
|
|
460
460
|
);
|
|
461
461
|
},
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
462
|
+
activePurchase,
|
|
463
|
+
hasPaidPurchase: activePaidPurchases.length > 0,
|
|
464
|
+
activePaidPurchase
|
|
465
465
|
};
|
|
466
|
-
}, [loading,
|
|
466
|
+
}, [loading, purchaseData, internalCustomerRef]);
|
|
467
467
|
const contextValue = (0, import_react.useMemo)(
|
|
468
468
|
() => ({
|
|
469
|
-
|
|
470
|
-
|
|
469
|
+
purchase,
|
|
470
|
+
refetchPurchase,
|
|
471
471
|
createPayment,
|
|
472
472
|
processPayment,
|
|
473
|
-
customerRef:
|
|
473
|
+
customerRef: purchaseData.customerRef || internalCustomerRef,
|
|
474
474
|
updateCustomerRef
|
|
475
475
|
}),
|
|
476
476
|
[
|
|
477
|
-
|
|
478
|
-
|
|
477
|
+
purchase,
|
|
478
|
+
refetchPurchase,
|
|
479
479
|
createPayment,
|
|
480
480
|
processPayment,
|
|
481
|
-
|
|
481
|
+
purchaseData.customerRef,
|
|
482
482
|
internalCustomerRef,
|
|
483
483
|
updateCustomerRef
|
|
484
484
|
]
|
|
@@ -511,7 +511,8 @@ var stripePromiseCache = /* @__PURE__ */ new Map();
|
|
|
511
511
|
function getStripeCacheKey(publishableKey, accountId) {
|
|
512
512
|
return accountId ? `${publishableKey}:${accountId}` : publishableKey;
|
|
513
513
|
}
|
|
514
|
-
function useCheckout(
|
|
514
|
+
function useCheckout(options) {
|
|
515
|
+
const { planRef, productRef } = options;
|
|
515
516
|
const { createPayment, customerRef, updateCustomerRef } = useSolvaPay();
|
|
516
517
|
const [loading, setLoading] = (0, import_react3.useState)(false);
|
|
517
518
|
const [error, setError] = (0, import_react3.useState)(
|
|
@@ -532,7 +533,7 @@ function useCheckout(planRef, agentRef) {
|
|
|
532
533
|
setLoading(true);
|
|
533
534
|
setError(null);
|
|
534
535
|
try {
|
|
535
|
-
const result = await createPayment({ planRef,
|
|
536
|
+
const result = await createPayment({ planRef, productRef });
|
|
536
537
|
if (!result || typeof result !== "object") {
|
|
537
538
|
throw new Error("Invalid payment intent response from server");
|
|
538
539
|
}
|
|
@@ -561,7 +562,7 @@ function useCheckout(planRef, agentRef) {
|
|
|
561
562
|
setLoading(false);
|
|
562
563
|
isStartingRef.current = false;
|
|
563
564
|
}
|
|
564
|
-
}, [planRef,
|
|
565
|
+
}, [planRef, productRef, createPayment, updateCustomerRef, loading]);
|
|
565
566
|
const reset = (0, import_react3.useCallback)(() => {
|
|
566
567
|
isStartingRef.current = false;
|
|
567
568
|
setLoading(false);
|
|
@@ -579,12 +580,12 @@ function useCheckout(planRef, agentRef) {
|
|
|
579
580
|
};
|
|
580
581
|
}
|
|
581
582
|
|
|
582
|
-
// src/hooks/
|
|
583
|
-
function
|
|
584
|
-
const {
|
|
583
|
+
// src/hooks/usePurchase.ts
|
|
584
|
+
function usePurchase() {
|
|
585
|
+
const { purchase, refetchPurchase } = useSolvaPay();
|
|
585
586
|
return {
|
|
586
|
-
...
|
|
587
|
-
refetch:
|
|
587
|
+
...purchase,
|
|
588
|
+
refetch: refetchPurchase
|
|
588
589
|
};
|
|
589
590
|
}
|
|
590
591
|
|
|
@@ -629,12 +630,12 @@ var import_react_stripe_js = require("@stripe/react-stripe-js");
|
|
|
629
630
|
|
|
630
631
|
// src/hooks/useCustomer.ts
|
|
631
632
|
function useCustomer() {
|
|
632
|
-
const {
|
|
633
|
+
const { purchase, customerRef } = useSolvaPay();
|
|
633
634
|
return {
|
|
634
|
-
customerRef:
|
|
635
|
-
email:
|
|
636
|
-
name:
|
|
637
|
-
loading:
|
|
635
|
+
customerRef: purchase.customerRef || customerRef,
|
|
636
|
+
email: purchase.email,
|
|
637
|
+
name: purchase.name,
|
|
638
|
+
loading: purchase.loading
|
|
638
639
|
};
|
|
639
640
|
}
|
|
640
641
|
|
|
@@ -807,7 +808,7 @@ var StripePaymentFormWrapper = ({
|
|
|
807
808
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
808
809
|
var PaymentForm = ({
|
|
809
810
|
planRef,
|
|
810
|
-
|
|
811
|
+
productRef,
|
|
811
812
|
onSuccess,
|
|
812
813
|
onError,
|
|
813
814
|
returnUrl,
|
|
@@ -822,8 +823,8 @@ var PaymentForm = ({
|
|
|
822
823
|
clientSecret,
|
|
823
824
|
startCheckout,
|
|
824
825
|
stripePromise
|
|
825
|
-
} = useCheckout(validPlanRef,
|
|
826
|
-
const { refetch } =
|
|
826
|
+
} = useCheckout({ planRef: validPlanRef, productRef });
|
|
827
|
+
const { refetch } = usePurchase();
|
|
827
828
|
const { processPayment } = useSolvaPay();
|
|
828
829
|
const hasInitializedRef = (0, import_react5.useRef)(false);
|
|
829
830
|
(0, import_react5.useEffect)(() => {
|
|
@@ -842,11 +843,11 @@ var PaymentForm = ({
|
|
|
842
843
|
let processingTimeout = false;
|
|
843
844
|
let processingResult = null;
|
|
844
845
|
const paymentIntentAny = paymentIntent;
|
|
845
|
-
if (processPayment &&
|
|
846
|
+
if (processPayment && productRef) {
|
|
846
847
|
try {
|
|
847
848
|
const result = await processPayment({
|
|
848
849
|
paymentIntentId: paymentIntentAny.id,
|
|
849
|
-
|
|
850
|
+
productRef,
|
|
850
851
|
planRef
|
|
851
852
|
});
|
|
852
853
|
processingResult = result;
|
|
@@ -863,6 +864,7 @@ var PaymentForm = ({
|
|
|
863
864
|
...paymentIntentAny,
|
|
864
865
|
_processingTimeout: processingTimeout,
|
|
865
866
|
_processingResult: processingResult
|
|
867
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
866
868
|
});
|
|
867
869
|
}
|
|
868
870
|
throw new Error("Payment processing timed out");
|
|
@@ -876,6 +878,7 @@ var PaymentForm = ({
|
|
|
876
878
|
await onSuccess({
|
|
877
879
|
...paymentIntentAny,
|
|
878
880
|
_processingError: error
|
|
881
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
879
882
|
});
|
|
880
883
|
} catch {
|
|
881
884
|
}
|
|
@@ -890,10 +893,11 @@ var PaymentForm = ({
|
|
|
890
893
|
...paymentIntentAny,
|
|
891
894
|
_processingTimeout: processingTimeout,
|
|
892
895
|
_processingResult: processingResult
|
|
896
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
893
897
|
});
|
|
894
898
|
}
|
|
895
899
|
},
|
|
896
|
-
[processPayment,
|
|
900
|
+
[processPayment, productRef, planRef, refetch, onSuccess]
|
|
897
901
|
);
|
|
898
902
|
const handleError = (0, import_react5.useCallback)(
|
|
899
903
|
(err) => {
|
|
@@ -981,13 +985,13 @@ var PlanBadge = ({
|
|
|
981
985
|
as: Component = "div",
|
|
982
986
|
className
|
|
983
987
|
}) => {
|
|
984
|
-
const {
|
|
988
|
+
const { purchases, loading, hasPaidPurchase, activePurchase } = usePurchase();
|
|
985
989
|
const [displayPlan, setDisplayPlan] = (0, import_react6.useState)(null);
|
|
986
990
|
const [hasLoadedOnce, setHasLoadedOnce] = (0, import_react6.useState)(false);
|
|
987
991
|
const lastPlanRef = (0, import_react6.useRef)(null);
|
|
988
992
|
const lastLoadingRef = (0, import_react6.useRef)(true);
|
|
989
|
-
const
|
|
990
|
-
const currentPlanName =
|
|
993
|
+
const previousPurchasesRef = (0, import_react6.useRef)(purchases);
|
|
994
|
+
const currentPlanName = activePurchase?.planName || null;
|
|
991
995
|
const effectivePlanName = currentPlanName;
|
|
992
996
|
(0, import_react6.useEffect)(() => {
|
|
993
997
|
if (!loading && !hasLoadedOnce) {
|
|
@@ -996,16 +1000,16 @@ var PlanBadge = ({
|
|
|
996
1000
|
lastLoadingRef.current = loading;
|
|
997
1001
|
}, [loading, hasLoadedOnce]);
|
|
998
1002
|
(0, import_react6.useEffect)(() => {
|
|
999
|
-
const
|
|
1000
|
-
if (
|
|
1003
|
+
const previousPurchases = previousPurchasesRef.current;
|
|
1004
|
+
if (previousPurchases !== purchases) {
|
|
1001
1005
|
if (loading) {
|
|
1002
1006
|
setHasLoadedOnce(false);
|
|
1003
1007
|
}
|
|
1004
1008
|
setDisplayPlan(null);
|
|
1005
1009
|
lastPlanRef.current = null;
|
|
1006
|
-
|
|
1010
|
+
previousPurchasesRef.current = purchases;
|
|
1007
1011
|
}
|
|
1008
|
-
}, [
|
|
1012
|
+
}, [purchases, loading]);
|
|
1009
1013
|
(0, import_react6.useEffect)(() => {
|
|
1010
1014
|
const currentPlan = effectivePlanName;
|
|
1011
1015
|
const previousPlan = lastPlanRef.current;
|
|
@@ -1024,19 +1028,19 @@ var PlanBadge = ({
|
|
|
1024
1028
|
const shouldShow = effectivePlanName !== null && hasLoadedOnce;
|
|
1025
1029
|
const planToDisplay = displayPlan ?? effectivePlanName;
|
|
1026
1030
|
if (children) {
|
|
1027
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: children({
|
|
1031
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: children({ purchases, loading, displayPlan: planToDisplay, shouldShow }) });
|
|
1028
1032
|
}
|
|
1029
1033
|
if (!shouldShow) {
|
|
1030
1034
|
return null;
|
|
1031
1035
|
}
|
|
1032
|
-
const computedClassName = typeof className === "function" ? className({
|
|
1036
|
+
const computedClassName = typeof className === "function" ? className({ purchases }) : className;
|
|
1033
1037
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1034
1038
|
Component,
|
|
1035
1039
|
{
|
|
1036
1040
|
className: computedClassName,
|
|
1037
1041
|
"data-loading": loading,
|
|
1038
|
-
"data-has-
|
|
1039
|
-
"data-has-paid-
|
|
1042
|
+
"data-has-purchase": !!activePurchase,
|
|
1043
|
+
"data-has-paid-purchase": hasPaidPurchase,
|
|
1040
1044
|
role: "status",
|
|
1041
1045
|
"aria-live": "polite",
|
|
1042
1046
|
"aria-busy": loading,
|
|
@@ -1046,14 +1050,14 @@ var PlanBadge = ({
|
|
|
1046
1050
|
);
|
|
1047
1051
|
};
|
|
1048
1052
|
|
|
1049
|
-
// src/components/
|
|
1053
|
+
// src/components/PurchaseGate.tsx
|
|
1050
1054
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1051
|
-
var
|
|
1052
|
-
const {
|
|
1053
|
-
const hasAccess = requirePlan ? hasPlan(requirePlan) :
|
|
1055
|
+
var PurchaseGate = ({ requirePlan, requireProduct, children }) => {
|
|
1056
|
+
const { purchases, loading, hasPlan } = usePurchase();
|
|
1057
|
+
const hasAccess = requirePlan ? hasPlan(requirePlan) : requireProduct ? purchases.some((p) => p.status === "active" && (p.productName === requireProduct || p.productReference === requireProduct)) : purchases.some((p) => p.status === "active");
|
|
1054
1058
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: children({
|
|
1055
1059
|
hasAccess,
|
|
1056
|
-
|
|
1060
|
+
purchases,
|
|
1057
1061
|
loading
|
|
1058
1062
|
}) });
|
|
1059
1063
|
};
|
|
@@ -1066,7 +1070,7 @@ var import_react7 = require("react");
|
|
|
1066
1070
|
var plansCache = /* @__PURE__ */ new Map();
|
|
1067
1071
|
var CACHE_DURATION2 = 5 * 60 * 1e3;
|
|
1068
1072
|
function usePlans(options) {
|
|
1069
|
-
const { fetcher,
|
|
1073
|
+
const { fetcher, productRef, filter, sortBy, autoSelectFirstPaid = false } = options;
|
|
1070
1074
|
const [plans, setPlans] = (0, import_react7.useState)([]);
|
|
1071
1075
|
const [loading, setLoading] = (0, import_react7.useState)(true);
|
|
1072
1076
|
const [error, setError] = (0, import_react7.useState)(null);
|
|
@@ -1089,12 +1093,12 @@ function usePlans(options) {
|
|
|
1089
1093
|
}, [autoSelectFirstPaid]);
|
|
1090
1094
|
const fetchPlans = (0, import_react7.useCallback)(
|
|
1091
1095
|
async (force = false) => {
|
|
1092
|
-
if (!
|
|
1093
|
-
setError(new Error("
|
|
1096
|
+
if (!productRef) {
|
|
1097
|
+
setError(new Error("Product reference not configured"));
|
|
1094
1098
|
setLoading(false);
|
|
1095
1099
|
return;
|
|
1096
1100
|
}
|
|
1097
|
-
const cached = plansCache.get(
|
|
1101
|
+
const cached = plansCache.get(productRef);
|
|
1098
1102
|
const now = Date.now();
|
|
1099
1103
|
if (!force && cached && now - cached.timestamp < CACHE_DURATION2) {
|
|
1100
1104
|
const cachedPlans = cached.plans;
|
|
@@ -1135,14 +1139,14 @@ function usePlans(options) {
|
|
|
1135
1139
|
try {
|
|
1136
1140
|
setLoading(true);
|
|
1137
1141
|
setError(null);
|
|
1138
|
-
const fetchPromise = fetcherRef.current(
|
|
1139
|
-
plansCache.set(
|
|
1142
|
+
const fetchPromise = fetcherRef.current(productRef);
|
|
1143
|
+
plansCache.set(productRef, {
|
|
1140
1144
|
plans: [],
|
|
1141
1145
|
timestamp: now,
|
|
1142
1146
|
promise: fetchPromise
|
|
1143
1147
|
});
|
|
1144
1148
|
const fetchedPlans = await fetchPromise;
|
|
1145
|
-
plansCache.set(
|
|
1149
|
+
plansCache.set(productRef, {
|
|
1146
1150
|
plans: fetchedPlans,
|
|
1147
1151
|
timestamp: now,
|
|
1148
1152
|
promise: null
|
|
@@ -1157,13 +1161,13 @@ function usePlans(options) {
|
|
|
1157
1161
|
setSelectedPlanIndex(firstPaidIndex >= 0 ? firstPaidIndex : 0);
|
|
1158
1162
|
}
|
|
1159
1163
|
} catch (err) {
|
|
1160
|
-
plansCache.delete(
|
|
1164
|
+
plansCache.delete(productRef);
|
|
1161
1165
|
setError(err instanceof Error ? err : new Error("Failed to load plans"));
|
|
1162
1166
|
} finally {
|
|
1163
1167
|
setLoading(false);
|
|
1164
1168
|
}
|
|
1165
1169
|
},
|
|
1166
|
-
[
|
|
1170
|
+
[productRef]
|
|
1167
1171
|
);
|
|
1168
1172
|
(0, import_react7.useEffect)(() => {
|
|
1169
1173
|
fetchPlans();
|
|
@@ -1196,16 +1200,16 @@ function usePlans(options) {
|
|
|
1196
1200
|
// src/components/PlanSelector.tsx
|
|
1197
1201
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1198
1202
|
var PlanSelector = ({
|
|
1199
|
-
|
|
1203
|
+
productRef,
|
|
1200
1204
|
fetcher,
|
|
1201
1205
|
filter,
|
|
1202
1206
|
sortBy,
|
|
1203
1207
|
autoSelectFirstPaid,
|
|
1204
1208
|
children
|
|
1205
1209
|
}) => {
|
|
1206
|
-
const {
|
|
1210
|
+
const { purchases } = usePurchase();
|
|
1207
1211
|
const plansHook = usePlans({
|
|
1208
|
-
|
|
1212
|
+
productRef,
|
|
1209
1213
|
fetcher,
|
|
1210
1214
|
filter,
|
|
1211
1215
|
sortBy,
|
|
@@ -1219,46 +1223,46 @@ var PlanSelector = ({
|
|
|
1219
1223
|
},
|
|
1220
1224
|
[plans]
|
|
1221
1225
|
);
|
|
1222
|
-
const
|
|
1223
|
-
const
|
|
1224
|
-
return
|
|
1226
|
+
const activePurchase = (0, import_react8.useMemo)(() => {
|
|
1227
|
+
const activePurchases = purchases.filter((p) => p.status === "active");
|
|
1228
|
+
return activePurchases.sort(
|
|
1225
1229
|
(a, b) => new Date(b.startDate).getTime() - new Date(a.startDate).getTime()
|
|
1226
1230
|
)[0] || null;
|
|
1227
|
-
}, [
|
|
1231
|
+
}, [purchases]);
|
|
1228
1232
|
const isCurrentPlan = (0, import_react8.useCallback)(
|
|
1229
1233
|
(planName) => {
|
|
1230
|
-
return
|
|
1234
|
+
return activePurchase?.planName === planName;
|
|
1231
1235
|
},
|
|
1232
|
-
[
|
|
1236
|
+
[activePurchase]
|
|
1233
1237
|
);
|
|
1234
1238
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: children({
|
|
1235
1239
|
...plansHook,
|
|
1236
|
-
|
|
1240
|
+
purchases,
|
|
1237
1241
|
isPaidPlan,
|
|
1238
1242
|
isCurrentPlan
|
|
1239
1243
|
}) });
|
|
1240
1244
|
};
|
|
1241
1245
|
|
|
1242
|
-
// src/hooks/
|
|
1246
|
+
// src/hooks/usePurchaseStatus.ts
|
|
1243
1247
|
var import_react9 = require("react");
|
|
1244
|
-
function
|
|
1245
|
-
const {
|
|
1246
|
-
const
|
|
1247
|
-
return (
|
|
1248
|
+
function usePurchaseStatus() {
|
|
1249
|
+
const { purchases } = usePurchase();
|
|
1250
|
+
const isPaidPurchase2 = (0, import_react9.useCallback)((p) => {
|
|
1251
|
+
return (p.amount ?? 0) > 0;
|
|
1248
1252
|
}, []);
|
|
1249
|
-
const
|
|
1250
|
-
const
|
|
1251
|
-
return
|
|
1253
|
+
const purchaseData = (0, import_react9.useMemo)(() => {
|
|
1254
|
+
const cancelledPaidPurchases = purchases.filter((p) => {
|
|
1255
|
+
return p.status === "active" && p.cancelledAt && isPaidPurchase2(p);
|
|
1252
1256
|
});
|
|
1253
|
-
const
|
|
1257
|
+
const cancelledPurchase = cancelledPaidPurchases.sort(
|
|
1254
1258
|
(a, b) => new Date(b.startDate).getTime() - new Date(a.startDate).getTime()
|
|
1255
1259
|
)[0] || null;
|
|
1256
|
-
const shouldShowCancelledNotice = !!
|
|
1260
|
+
const shouldShowCancelledNotice = !!cancelledPurchase;
|
|
1257
1261
|
return {
|
|
1258
|
-
|
|
1262
|
+
cancelledPurchase,
|
|
1259
1263
|
shouldShowCancelledNotice
|
|
1260
1264
|
};
|
|
1261
|
-
}, [
|
|
1265
|
+
}, [purchases, isPaidPurchase2]);
|
|
1262
1266
|
const formatDate = (0, import_react9.useCallback)((dateString) => {
|
|
1263
1267
|
if (!dateString) return null;
|
|
1264
1268
|
return new Date(dateString).toLocaleDateString("en-US", {
|
|
@@ -1276,8 +1280,8 @@ function useSubscriptionStatus() {
|
|
|
1276
1280
|
return diffDays > 0 ? diffDays : 0;
|
|
1277
1281
|
}, []);
|
|
1278
1282
|
return {
|
|
1279
|
-
|
|
1280
|
-
shouldShowCancelledNotice:
|
|
1283
|
+
cancelledPurchase: purchaseData.cancelledPurchase,
|
|
1284
|
+
shouldShowCancelledNotice: purchaseData.shouldShowCancelledNotice,
|
|
1281
1285
|
formatDate,
|
|
1282
1286
|
getDaysUntilExpiration
|
|
1283
1287
|
};
|
|
@@ -1287,21 +1291,21 @@ function useSubscriptionStatus() {
|
|
|
1287
1291
|
PaymentForm,
|
|
1288
1292
|
PlanBadge,
|
|
1289
1293
|
PlanSelector,
|
|
1294
|
+
PurchaseGate,
|
|
1290
1295
|
SolvaPayProvider,
|
|
1291
1296
|
Spinner,
|
|
1292
1297
|
StripePaymentFormWrapper,
|
|
1293
|
-
SubscriptionGate,
|
|
1294
1298
|
defaultAuthAdapter,
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1299
|
+
filterPurchases,
|
|
1300
|
+
getActivePurchases,
|
|
1301
|
+
getCancelledPurchasesWithEndDate,
|
|
1302
|
+
getMostRecentPurchase,
|
|
1303
|
+
getPrimaryPurchase,
|
|
1304
|
+
isPaidPurchase,
|
|
1301
1305
|
useCheckout,
|
|
1302
1306
|
useCustomer,
|
|
1303
1307
|
usePlans,
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1308
|
+
usePurchase,
|
|
1309
|
+
usePurchaseStatus,
|
|
1310
|
+
useSolvaPay
|
|
1307
1311
|
});
|