@tagadapay/plugin-sdk 2.4.38 → 2.5.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/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/react/hooks/useCheckout.js +21 -33
- package/dist/react/hooks/useCheckoutSession.d.ts +19 -0
- package/dist/react/hooks/useCheckoutSession.js +108 -0
- package/dist/react/hooks/useCheckoutToken.d.ts +17 -0
- package/dist/react/hooks/useCheckoutToken.js +80 -0
- package/dist/react/hooks/useOrderBump.js +94 -29
- package/dist/react/hooks/useOrderBumpV2.d.ts +17 -0
- package/dist/react/hooks/useOrderBumpV2.js +95 -0
- package/dist/react/hooks/useOrderBumpV3.d.ts +23 -0
- package/dist/react/hooks/useOrderBumpV3.js +109 -0
- package/dist/react/hooks/usePayment.d.ts +1 -1
- package/dist/react/hooks/usePayment.js +2 -18
- package/dist/react/hooks/usePluginConfig.js +2 -13
- package/dist/react/hooks/usePostPurchases.js +11 -5
- package/dist/react/hooks/useProducts.js +2 -16
- package/dist/react/index.d.ts +9 -1
- package/dist/react/index.js +5 -1
- package/dist/react/providers/TagadaProvider.d.ts +0 -1
- package/dist/react/providers/TagadaProvider.js +16 -12
- package/dist/react/services/apiService.d.ts +1 -0
- package/dist/react/services/apiService.js +3 -0
- package/dist/v2/core/googleAutocomplete.d.ts +65 -0
- package/dist/v2/core/googleAutocomplete.js +94 -0
- package/dist/v2/core/index.d.ts +8 -0
- package/dist/v2/core/index.js +11 -0
- package/dist/v2/core/isoData.d.ts +50 -0
- package/dist/v2/core/isoData.js +103 -0
- package/dist/v2/core/resources/apiClient.d.ts +25 -0
- package/dist/v2/core/resources/apiClient.js +95 -0
- package/dist/v2/core/resources/checkout.d.ts +189 -0
- package/dist/v2/core/resources/checkout.js +119 -0
- package/dist/v2/core/resources/index.d.ts +13 -0
- package/dist/v2/core/resources/index.js +13 -0
- package/dist/v2/core/resources/offers.d.ts +98 -0
- package/dist/v2/core/resources/offers.js +115 -0
- package/dist/v2/core/resources/orders.d.ts +40 -0
- package/dist/v2/core/resources/orders.js +59 -0
- package/dist/v2/core/resources/payments.d.ts +140 -0
- package/dist/v2/core/resources/payments.js +126 -0
- package/dist/v2/core/resources/postPurchases.d.ts +182 -0
- package/dist/v2/core/resources/postPurchases.js +116 -0
- package/dist/v2/core/resources/products.d.ts +29 -0
- package/dist/v2/core/resources/products.js +49 -0
- package/dist/v2/core/resources/promotions.d.ts +45 -0
- package/dist/v2/core/resources/promotions.js +87 -0
- package/dist/v2/core/resources/threeds.d.ts +23 -0
- package/dist/v2/core/resources/threeds.js +15 -0
- package/dist/v2/core/utils/checkout.d.ts +24 -0
- package/dist/v2/core/utils/checkout.js +30 -0
- package/dist/v2/core/utils/currency.d.ts +28 -0
- package/dist/v2/core/utils/currency.js +272 -0
- package/dist/v2/core/utils/index.d.ts +12 -0
- package/dist/v2/core/utils/index.js +12 -0
- package/dist/v2/core/utils/order.d.ts +159 -0
- package/dist/v2/core/utils/order.js +42 -0
- package/dist/v2/core/utils/orderBump.d.ts +40 -0
- package/dist/v2/core/utils/orderBump.js +47 -0
- package/dist/v2/core/utils/pluginConfig.d.ts +43 -0
- package/dist/v2/core/utils/pluginConfig.js +155 -0
- package/dist/v2/core/utils/postPurchases.d.ts +32 -0
- package/dist/v2/core/utils/postPurchases.js +42 -0
- package/dist/v2/core/utils/products.d.ts +58 -0
- package/dist/v2/core/utils/products.js +64 -0
- package/dist/v2/core/utils/promotions.d.ts +24 -0
- package/dist/v2/core/utils/promotions.js +30 -0
- package/dist/v2/index.d.ts +19 -0
- package/dist/v2/index.js +15 -0
- package/dist/v2/react/components/DebugDrawer.d.ts +7 -0
- package/dist/v2/react/components/DebugDrawer.js +383 -0
- package/dist/v2/react/hooks/useApiQuery.d.ts +28 -0
- package/dist/v2/react/hooks/useApiQuery.js +84 -0
- package/dist/v2/react/hooks/useCheckoutQuery.d.ts +39 -0
- package/dist/v2/react/hooks/useCheckoutQuery.js +208 -0
- package/dist/v2/react/hooks/useCheckoutToken.d.ts +17 -0
- package/dist/v2/react/hooks/useCheckoutToken.js +80 -0
- package/dist/v2/react/hooks/useCurrency.d.ts +9 -0
- package/dist/v2/react/hooks/useCurrency.js +21 -0
- package/dist/v2/react/hooks/useGeoLocation.d.ts +138 -0
- package/dist/v2/react/hooks/useGeoLocation.js +126 -0
- package/dist/v2/react/hooks/useGoogleAutocomplete.d.ts +74 -0
- package/dist/v2/react/hooks/useGoogleAutocomplete.js +207 -0
- package/dist/v2/react/hooks/useISOData.d.ts +61 -0
- package/dist/v2/react/hooks/useISOData.js +176 -0
- package/dist/v2/react/hooks/useOffersQuery.d.ts +65 -0
- package/dist/v2/react/hooks/useOffersQuery.js +353 -0
- package/dist/v2/react/hooks/useOrderBumpQuery.d.ts +20 -0
- package/dist/v2/react/hooks/useOrderBumpQuery.js +88 -0
- package/dist/v2/react/hooks/useOrderQuery.d.ts +29 -0
- package/dist/v2/react/hooks/useOrderQuery.js +98 -0
- package/dist/v2/react/hooks/usePaymentPolling.d.ts +45 -0
- package/dist/v2/react/hooks/usePaymentPolling.js +153 -0
- package/dist/v2/react/hooks/usePaymentQuery.d.ts +19 -0
- package/dist/v2/react/hooks/usePaymentQuery.js +283 -0
- package/dist/v2/react/hooks/usePluginConfig.d.ts +16 -0
- package/dist/v2/react/hooks/usePluginConfig.js +36 -0
- package/dist/v2/react/hooks/usePostPurchasesQuery.d.ts +63 -0
- package/dist/v2/react/hooks/usePostPurchasesQuery.js +365 -0
- package/dist/v2/react/hooks/useProductsQuery.d.ts +31 -0
- package/dist/v2/react/hooks/useProductsQuery.js +102 -0
- package/dist/v2/react/hooks/usePromotionsQuery.d.ts +28 -0
- package/dist/v2/react/hooks/usePromotionsQuery.js +97 -0
- package/dist/v2/react/hooks/useThreeds.d.ts +36 -0
- package/dist/v2/react/hooks/useThreeds.js +166 -0
- package/dist/v2/react/hooks/useThreedsModal.d.ts +13 -0
- package/dist/v2/react/hooks/useThreedsModal.js +343 -0
- package/dist/v2/react/index.d.ts +38 -0
- package/dist/v2/react/index.js +27 -0
- package/dist/v2/react/providers/TagadaProvider.d.ts +63 -0
- package/dist/v2/react/providers/TagadaProvider.js +680 -0
- package/package.json +10 -3
package/dist/index.d.ts
CHANGED
|
@@ -14,3 +14,4 @@ export type { Currency, Customer, Environment, EnvironmentConfig, Locale, Order,
|
|
|
14
14
|
export { convertCurrency, formatMoney, formatMoneyWithoutSymbol, formatSimpleMoney, getCurrencyInfo, minorUnitsToMajorUnits, moneyStringOrNumberToMinorUnits, } from './react/utils/money';
|
|
15
15
|
export * from './data/iso3166';
|
|
16
16
|
export * from './data/languages';
|
|
17
|
+
export * from './v2';
|
package/dist/index.js
CHANGED
|
@@ -1,40 +1,11 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { useCurrency } from '../hooks/useCurrency';
|
|
3
|
-
import {
|
|
3
|
+
import { useTagadaContext } from '../providers/TagadaProvider';
|
|
4
4
|
import { collectTrackingData } from '../utils/trackingUtils';
|
|
5
5
|
import { usePluginConfig } from './usePluginConfig';
|
|
6
6
|
export function useCheckout(options = {}) {
|
|
7
|
-
const
|
|
7
|
+
const { apiService, updateCheckoutDebugData, refreshCoordinator, currency, isSessionInitialized } = useTagadaContext();
|
|
8
8
|
const { storeId } = usePluginConfig();
|
|
9
|
-
// If context is not ready yet, return loading state
|
|
10
|
-
if (!context) {
|
|
11
|
-
return {
|
|
12
|
-
checkout: null,
|
|
13
|
-
error: null,
|
|
14
|
-
isLoading: true,
|
|
15
|
-
isInitialized: false,
|
|
16
|
-
initialized: false,
|
|
17
|
-
init: async () => ({ checkoutUrl: '', checkoutSession: {}, checkoutToken: '' }),
|
|
18
|
-
getCheckout: async () => ({}),
|
|
19
|
-
refresh: async () => { },
|
|
20
|
-
updateAddress: async () => ({ success: false, shippingCountryChanged: false, billingCountryChanged: false }),
|
|
21
|
-
setCheckoutInfo: async () => ({ success: false }),
|
|
22
|
-
applyPromotionCode: async () => ({ success: false }),
|
|
23
|
-
removePromotion: async () => ({ success: false }),
|
|
24
|
-
getAppliedPromotions: async () => [],
|
|
25
|
-
updateLineItems: async () => ({ success: false }),
|
|
26
|
-
addLineItems: async () => ({ success: false }),
|
|
27
|
-
removeLineItems: async () => ({ success: false }),
|
|
28
|
-
setItemQuantity: async () => ({ success: false }),
|
|
29
|
-
toggleOrderBump: async () => ({ success: false }),
|
|
30
|
-
updateCustomer: async () => ({ success: false }),
|
|
31
|
-
updateCustomerAndSessionInfo: async () => ({ success: false }),
|
|
32
|
-
previewOrderSummary: async () => ({ savings: 0, savingsPct: 0, currency: 'USD' }),
|
|
33
|
-
previewCheckoutSession: async () => ({ success: false, preview: {} }),
|
|
34
|
-
clear: () => { },
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
const { apiService, updateCheckoutDebugData, refreshCoordinator, currency, isSessionInitialized } = context;
|
|
38
9
|
const { code: currentCurrency } = useCurrency();
|
|
39
10
|
const [checkout, setCheckout] = useState(null);
|
|
40
11
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -502,11 +473,25 @@ export function useCheckout(options = {}) {
|
|
|
502
473
|
}, []);
|
|
503
474
|
// Auto-load existing checkout session from provided token
|
|
504
475
|
useEffect(() => {
|
|
476
|
+
console.log('🔧 useCheckout: Auto-load effect triggered', {
|
|
477
|
+
autoLoadFromToken,
|
|
478
|
+
hasAutoLoaded: hasAutoLoadedRef.current,
|
|
479
|
+
isInitialized,
|
|
480
|
+
providedToken: providedToken ? providedToken.substring(0, 8) + '...' : null,
|
|
481
|
+
isLoading,
|
|
482
|
+
isSessionInitialized
|
|
483
|
+
});
|
|
505
484
|
if (!autoLoadFromToken || hasAutoLoadedRef.current || isInitialized || !providedToken) {
|
|
485
|
+
console.log('🔧 useCheckout: Auto-load skipped', {
|
|
486
|
+
reason: !autoLoadFromToken ? 'autoLoadFromToken false' :
|
|
487
|
+
hasAutoLoadedRef.current ? 'already loaded' :
|
|
488
|
+
isInitialized ? 'already initialized' :
|
|
489
|
+
!providedToken ? 'no token' : 'unknown'
|
|
490
|
+
});
|
|
506
491
|
return;
|
|
507
492
|
}
|
|
508
493
|
if (!isLoading) {
|
|
509
|
-
console.
|
|
494
|
+
console.log('🔧 useCheckout: Auto-loading from provided token:', {
|
|
510
495
|
tokenPreview: providedToken.substring(0, 8) + '...',
|
|
511
496
|
});
|
|
512
497
|
hasAutoLoadedRef.current = true;
|
|
@@ -516,7 +501,10 @@ export function useCheckout(options = {}) {
|
|
|
516
501
|
hasAutoLoadedRef.current = false; // Reset to allow retry
|
|
517
502
|
});
|
|
518
503
|
}
|
|
519
|
-
|
|
504
|
+
else {
|
|
505
|
+
console.log('🔧 useCheckout: Auto-load skipped - still loading');
|
|
506
|
+
}
|
|
507
|
+
}, [autoLoadFromToken, providedToken, isInitialized, isLoading, getCheckout, isSessionInitialized]);
|
|
520
508
|
return {
|
|
521
509
|
checkout,
|
|
522
510
|
isLoading,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { CheckoutData } from './useCheckout';
|
|
2
|
+
export interface UseCheckoutSessionOptions {
|
|
3
|
+
checkoutToken: string | null;
|
|
4
|
+
autoRefresh?: boolean;
|
|
5
|
+
refreshInterval?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface UseCheckoutSessionResult {
|
|
8
|
+
checkout: CheckoutData | null;
|
|
9
|
+
isLoading: boolean;
|
|
10
|
+
error: Error | null;
|
|
11
|
+
isInitialized: boolean;
|
|
12
|
+
refresh: () => Promise<void>;
|
|
13
|
+
getCheckout: (token: string) => Promise<CheckoutData>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Fetches and manages checkout session data for a specific token
|
|
17
|
+
* This hook is composable and can be used multiple times with different tokens
|
|
18
|
+
*/
|
|
19
|
+
export declare function useCheckoutSession(options: UseCheckoutSessionOptions): UseCheckoutSessionResult;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { useTagadaContext } from '../providers/TagadaProvider';
|
|
3
|
+
/**
|
|
4
|
+
* Fetches and manages checkout session data for a specific token
|
|
5
|
+
* This hook is composable and can be used multiple times with different tokens
|
|
6
|
+
*/
|
|
7
|
+
export function useCheckoutSession(options) {
|
|
8
|
+
const { apiService, isSessionInitialized } = useTagadaContext();
|
|
9
|
+
const { checkoutToken, autoRefresh = false, refreshInterval = 30000 } = options;
|
|
10
|
+
const [checkout, setCheckout] = useState(null);
|
|
11
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
12
|
+
const [error, setError] = useState(null);
|
|
13
|
+
const [isInitialized, setIsInitialized] = useState(false);
|
|
14
|
+
const refreshIntervalRef = useRef(null);
|
|
15
|
+
const isSessionInitializedRef = useRef(isSessionInitialized);
|
|
16
|
+
// Keep ref in sync with state
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
isSessionInitializedRef.current = isSessionInitialized;
|
|
19
|
+
}, [isSessionInitialized]);
|
|
20
|
+
const getCheckout = useCallback(async (token) => {
|
|
21
|
+
console.log('🔧 useCheckoutSession: Fetching checkout data', {
|
|
22
|
+
tokenPreview: token.substring(0, 8) + '...'
|
|
23
|
+
});
|
|
24
|
+
// Wait for session initialization if needed
|
|
25
|
+
if (!isSessionInitializedRef.current) {
|
|
26
|
+
console.log('🔧 useCheckoutSession: Waiting for session initialization...');
|
|
27
|
+
await new Promise((resolve) => {
|
|
28
|
+
const checkSession = () => {
|
|
29
|
+
if (isSessionInitializedRef.current) {
|
|
30
|
+
resolve();
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
setTimeout(checkSession, 100);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
checkSession();
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
const response = await apiService.fetch(`/api/v1/checkout-sessions/${token}`, {
|
|
40
|
+
method: 'GET',
|
|
41
|
+
});
|
|
42
|
+
console.log('🔧 useCheckoutSession: Checkout data fetched', {
|
|
43
|
+
sessionId: response.checkoutSession?.id,
|
|
44
|
+
itemsCount: response.summary?.items?.length || 0,
|
|
45
|
+
totalAmount: response.summary?.totalAmount
|
|
46
|
+
});
|
|
47
|
+
return response;
|
|
48
|
+
}, [apiService]);
|
|
49
|
+
const refresh = useCallback(async () => {
|
|
50
|
+
if (!checkoutToken) {
|
|
51
|
+
console.log('🔧 useCheckoutSession: No token available for refresh');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
setIsLoading(true);
|
|
56
|
+
setError(null);
|
|
57
|
+
const data = await getCheckout(checkoutToken);
|
|
58
|
+
setCheckout(data);
|
|
59
|
+
setIsInitialized(true);
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
const error = err instanceof Error ? err : new Error('Failed to refresh checkout');
|
|
63
|
+
setError(error);
|
|
64
|
+
console.error('🔧 useCheckoutSession: Refresh failed:', error);
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
setIsLoading(false);
|
|
68
|
+
}
|
|
69
|
+
}, [checkoutToken, getCheckout]);
|
|
70
|
+
// Auto-load when token becomes available
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (!checkoutToken || isInitialized) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
console.log('🔧 useCheckoutSession: Token available, loading checkout data', {
|
|
76
|
+
tokenPreview: checkoutToken.substring(0, 8) + '...'
|
|
77
|
+
});
|
|
78
|
+
refresh();
|
|
79
|
+
}, [checkoutToken, refresh, isInitialized]);
|
|
80
|
+
// Auto-refresh setup
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (!autoRefresh || !checkout?.checkoutSession?.id) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
console.log('🔧 useCheckoutSession: Setting up auto-refresh', {
|
|
86
|
+
interval: refreshInterval,
|
|
87
|
+
sessionId: checkout.checkoutSession.id
|
|
88
|
+
});
|
|
89
|
+
refreshIntervalRef.current = setInterval(() => {
|
|
90
|
+
console.log('🔧 useCheckoutSession: Auto-refreshing checkout data');
|
|
91
|
+
refresh();
|
|
92
|
+
}, refreshInterval);
|
|
93
|
+
return () => {
|
|
94
|
+
if (refreshIntervalRef.current) {
|
|
95
|
+
clearInterval(refreshIntervalRef.current);
|
|
96
|
+
refreshIntervalRef.current = null;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}, [autoRefresh, refreshInterval, checkout?.checkoutSession?.id, refresh]);
|
|
100
|
+
return {
|
|
101
|
+
checkout,
|
|
102
|
+
isLoading,
|
|
103
|
+
error,
|
|
104
|
+
isInitialized,
|
|
105
|
+
refresh,
|
|
106
|
+
getCheckout,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface UseCheckoutTokenOptions {
|
|
2
|
+
checkoutToken?: string;
|
|
3
|
+
autoLoadFromToken?: boolean;
|
|
4
|
+
}
|
|
5
|
+
export interface UseCheckoutTokenResult {
|
|
6
|
+
checkoutToken: string | null;
|
|
7
|
+
isLoading: boolean;
|
|
8
|
+
error: Error | null;
|
|
9
|
+
isInitialized: boolean;
|
|
10
|
+
setToken: (token: string | null) => void;
|
|
11
|
+
clearToken: () => void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Manages checkout token state and auto-loading behavior
|
|
15
|
+
* This hook handles token management without fetching checkout data
|
|
16
|
+
*/
|
|
17
|
+
export declare function useCheckoutToken(options?: UseCheckoutTokenOptions): UseCheckoutTokenResult;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { useTagadaContext } from '../providers/TagadaProvider';
|
|
3
|
+
/**
|
|
4
|
+
* Manages checkout token state and auto-loading behavior
|
|
5
|
+
* This hook handles token management without fetching checkout data
|
|
6
|
+
*/
|
|
7
|
+
export function useCheckoutToken(options = {}) {
|
|
8
|
+
const { isSessionInitialized } = useTagadaContext();
|
|
9
|
+
const { checkoutToken: providedToken, autoLoadFromToken = true } = options;
|
|
10
|
+
const [checkoutToken, setCheckoutToken] = useState(providedToken || null);
|
|
11
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
12
|
+
const [error, setError] = useState(null);
|
|
13
|
+
const [isInitialized, setIsInitialized] = useState(false);
|
|
14
|
+
const hasAutoLoadedRef = useRef(false);
|
|
15
|
+
const isSessionInitializedRef = useRef(isSessionInitialized);
|
|
16
|
+
// Keep ref in sync with state
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
isSessionInitializedRef.current = isSessionInitialized;
|
|
19
|
+
}, [isSessionInitialized]);
|
|
20
|
+
// Update token when provided token changes
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (providedToken && providedToken !== checkoutToken) {
|
|
23
|
+
console.log('🔧 useCheckoutToken: Token updated from props', {
|
|
24
|
+
oldToken: checkoutToken ? checkoutToken.substring(0, 8) + '...' : null,
|
|
25
|
+
newToken: providedToken.substring(0, 8) + '...'
|
|
26
|
+
});
|
|
27
|
+
setCheckoutToken(providedToken);
|
|
28
|
+
setIsInitialized(false);
|
|
29
|
+
hasAutoLoadedRef.current = false;
|
|
30
|
+
}
|
|
31
|
+
}, [providedToken, checkoutToken]);
|
|
32
|
+
// Auto-load token from URL if no token provided
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (!providedToken && autoLoadFromToken && !checkoutToken && !hasAutoLoadedRef.current) {
|
|
35
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
36
|
+
const urlToken = urlParams.get('checkoutToken') || urlParams.get('token');
|
|
37
|
+
if (urlToken) {
|
|
38
|
+
console.log('🔧 useCheckoutToken: Auto-loading token from URL', {
|
|
39
|
+
tokenPreview: urlToken.substring(0, 8) + '...'
|
|
40
|
+
});
|
|
41
|
+
setCheckoutToken(urlToken);
|
|
42
|
+
hasAutoLoadedRef.current = true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}, [providedToken, autoLoadFromToken, checkoutToken]);
|
|
46
|
+
// Wait for session initialization
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (!isSessionInitialized || !checkoutToken || isInitialized) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
console.log('🔧 useCheckoutToken: Session initialized, token ready', {
|
|
52
|
+
tokenPreview: checkoutToken.substring(0, 8) + '...',
|
|
53
|
+
isSessionInitialized
|
|
54
|
+
});
|
|
55
|
+
setIsInitialized(true);
|
|
56
|
+
}, [isSessionInitialized, checkoutToken, isInitialized]);
|
|
57
|
+
const setToken = useCallback((token) => {
|
|
58
|
+
console.log('🔧 useCheckoutToken: Setting token', {
|
|
59
|
+
tokenPreview: token ? token.substring(0, 8) + '...' : null
|
|
60
|
+
});
|
|
61
|
+
setCheckoutToken(token);
|
|
62
|
+
setIsInitialized(!!token);
|
|
63
|
+
setError(null);
|
|
64
|
+
}, []);
|
|
65
|
+
const clearToken = useCallback(() => {
|
|
66
|
+
console.log('🔧 useCheckoutToken: Clearing token');
|
|
67
|
+
setCheckoutToken(null);
|
|
68
|
+
setIsInitialized(false);
|
|
69
|
+
setError(null);
|
|
70
|
+
hasAutoLoadedRef.current = false;
|
|
71
|
+
}, []);
|
|
72
|
+
return {
|
|
73
|
+
checkoutToken,
|
|
74
|
+
isLoading,
|
|
75
|
+
error,
|
|
76
|
+
isInitialized,
|
|
77
|
+
setToken,
|
|
78
|
+
clearToken,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -1,44 +1,102 @@
|
|
|
1
1
|
import { useState, useCallback, useEffect } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { useTagadaContext } from '../providers/TagadaProvider';
|
|
3
|
+
import { useCheckout } from './useCheckout';
|
|
3
4
|
export function useOrderBump(options) {
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
5
|
+
const { apiService, refreshCoordinator } = useTagadaContext();
|
|
6
|
+
const { checkout } = useCheckout();
|
|
7
|
+
const { checkoutSessionId, offerId, orderBumpType = 'primary', autoPreview = true } = options;
|
|
8
|
+
// Debug logging for hook initialization
|
|
9
|
+
console.log('🔧 useOrderBump initialized:', {
|
|
10
|
+
offerId,
|
|
11
|
+
orderBumpType,
|
|
12
|
+
checkoutSessionId,
|
|
13
|
+
hasCheckout: !!checkout,
|
|
14
|
+
hasSession: !!checkout?.checkoutSession,
|
|
15
|
+
sessionLineItems: checkout?.checkoutSession?.sessionLineItems?.length || 0
|
|
16
|
+
});
|
|
17
|
+
// Initialize isSelected based on current checkout session if available
|
|
18
|
+
const getInitialSelection = () => {
|
|
19
|
+
if (!checkout?.checkoutSession?.sessionLineItems) {
|
|
20
|
+
console.log('🔧 getInitialSelection: No session line items available');
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
if (orderBumpType === 'vip') {
|
|
24
|
+
console.log('🔧 getInitialSelection: VIP order bump (placeholder)');
|
|
25
|
+
return false; // Placeholder for VIP logic
|
|
26
|
+
}
|
|
27
|
+
const isSelected = checkout.checkoutSession.sessionLineItems.some((item) => {
|
|
28
|
+
const matches = item.isOrderBump === true &&
|
|
29
|
+
(item.orderBumpOfferId === offerId || item.productId === offerId);
|
|
30
|
+
console.log('🔧 Checking line item:', {
|
|
31
|
+
itemId: item.id,
|
|
32
|
+
productId: item.productId,
|
|
33
|
+
isOrderBump: item.isOrderBump,
|
|
34
|
+
offerId,
|
|
35
|
+
matches
|
|
36
|
+
});
|
|
37
|
+
if (matches) {
|
|
38
|
+
console.log('🔧 getInitialSelection: Found matching order bump item:', item);
|
|
39
|
+
}
|
|
40
|
+
return matches;
|
|
41
|
+
});
|
|
42
|
+
console.log('🔧 getInitialSelection result:', isSelected);
|
|
43
|
+
return isSelected;
|
|
44
|
+
};
|
|
45
|
+
const [isSelected, setIsSelected] = useState(getInitialSelection);
|
|
21
46
|
const [preview, setPreview] = useState(null);
|
|
22
47
|
const [isLoading, setIsLoading] = useState(false);
|
|
23
48
|
const [isToggling, setIsToggling] = useState(false);
|
|
24
49
|
const [error, setError] = useState(null);
|
|
50
|
+
// Check if order bump is selected from checkout session line items
|
|
51
|
+
const checkOrderBumpSelection = useCallback(() => {
|
|
52
|
+
if (!checkout?.checkoutSession?.sessionLineItems)
|
|
53
|
+
return false;
|
|
54
|
+
// For VIP order bumps, we need to check if any VIP offers are selected
|
|
55
|
+
if (orderBumpType === 'vip') {
|
|
56
|
+
// VIP logic would go here - checking for VIP offers in line items
|
|
57
|
+
return false; // Placeholder for now
|
|
58
|
+
}
|
|
59
|
+
// For primary/secondary order bumps, check if the offer's product is in line items
|
|
60
|
+
// The offerId should match the order bump offer ID, not the product ID
|
|
61
|
+
return checkout.checkoutSession.sessionLineItems.some((item) => {
|
|
62
|
+
// Check if this is an order bump item and matches our offer
|
|
63
|
+
return item.isOrderBump === true &&
|
|
64
|
+
(item.orderBumpOfferId === offerId || item.productId === offerId);
|
|
65
|
+
});
|
|
66
|
+
}, [checkout?.checkoutSession?.sessionLineItems, offerId, orderBumpType]);
|
|
25
67
|
const refreshPreview = useCallback(async () => {
|
|
26
68
|
if (!checkoutSessionId)
|
|
27
69
|
return;
|
|
28
70
|
setIsLoading(true);
|
|
29
71
|
setError(null);
|
|
30
72
|
try {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
73
|
+
// Only use VIP preview for VIP order bumps
|
|
74
|
+
if (orderBumpType === 'vip') {
|
|
75
|
+
const response = await apiService.fetch(`/api/v1/checkout-sessions/${checkoutSessionId}/vip-preview`, {
|
|
76
|
+
method: 'POST',
|
|
77
|
+
body: {
|
|
78
|
+
orderBumpOfferIds: [offerId],
|
|
79
|
+
orderBumpType,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
setPreview(response);
|
|
83
|
+
// Update isSelected based on preview data for VIP
|
|
84
|
+
const offerSelected = response.selectedOffers?.some((offer) => offer.isSelected);
|
|
85
|
+
setIsSelected(offerSelected ?? false);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
// For primary/secondary order bumps, check checkout session directly
|
|
89
|
+
const isSelectedFromSession = checkOrderBumpSelection();
|
|
90
|
+
setIsSelected(isSelectedFromSession);
|
|
91
|
+
setPreview(null); // No preview for non-VIP order bumps
|
|
92
|
+
// Debug logging to help troubleshoot
|
|
93
|
+
console.log('Order bump selection check:', {
|
|
94
|
+
offerId,
|
|
35
95
|
orderBumpType,
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const offerSelected = response.selectedOffers?.some((offer) => offer.isSelected);
|
|
41
|
-
setIsSelected(offerSelected ?? false);
|
|
96
|
+
sessionLineItems: checkout?.checkoutSession?.sessionLineItems,
|
|
97
|
+
isSelectedFromSession
|
|
98
|
+
});
|
|
99
|
+
}
|
|
42
100
|
}
|
|
43
101
|
catch (err) {
|
|
44
102
|
const error = err instanceof Error ? err : new Error('Failed to fetch preview');
|
|
@@ -48,7 +106,7 @@ export function useOrderBump(options) {
|
|
|
48
106
|
finally {
|
|
49
107
|
setIsLoading(false);
|
|
50
108
|
}
|
|
51
|
-
}, [checkoutSessionId, offerId, orderBumpType, apiService]);
|
|
109
|
+
}, [checkoutSessionId, offerId, orderBumpType, apiService, checkOrderBumpSelection]);
|
|
52
110
|
const toggle = useCallback(async (selected) => {
|
|
53
111
|
if (!checkoutSessionId) {
|
|
54
112
|
throw new Error('No checkout session available');
|
|
@@ -90,6 +148,13 @@ export function useOrderBump(options) {
|
|
|
90
148
|
setIsToggling(false);
|
|
91
149
|
}
|
|
92
150
|
}, [checkoutSessionId, offerId, isSelected, apiService, refreshPreview, refreshCoordinator]);
|
|
151
|
+
// Initialize isSelected when checkout session first loads
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
if (checkout?.checkoutSession?.sessionLineItems) {
|
|
154
|
+
const isSelectedFromSession = checkOrderBumpSelection();
|
|
155
|
+
setIsSelected(isSelectedFromSession);
|
|
156
|
+
}
|
|
157
|
+
}, [checkOrderBumpSelection, checkout?.checkoutSession?.sessionLineItems]);
|
|
93
158
|
// Auto-fetch preview on mount and when dependencies change
|
|
94
159
|
useEffect(() => {
|
|
95
160
|
if (autoPreview && checkoutSessionId) {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface UseOrderBumpV2Options {
|
|
2
|
+
offerId: string;
|
|
3
|
+
productId?: string;
|
|
4
|
+
orderBumpType?: 'primary' | 'secondary';
|
|
5
|
+
}
|
|
6
|
+
export interface UseOrderBumpV2Result {
|
|
7
|
+
isSelected: boolean;
|
|
8
|
+
isLoading: boolean;
|
|
9
|
+
isToggling: boolean;
|
|
10
|
+
error: Error | null;
|
|
11
|
+
isReady: boolean;
|
|
12
|
+
toggle: (selected?: boolean) => Promise<{
|
|
13
|
+
success: boolean;
|
|
14
|
+
error?: any;
|
|
15
|
+
}>;
|
|
16
|
+
}
|
|
17
|
+
export declare function useOrderBumpV2(options: UseOrderBumpV2Options): UseOrderBumpV2Result;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
2
|
+
import { useTagadaContext } from '../providers/TagadaProvider';
|
|
3
|
+
import { useCheckout } from './useCheckout';
|
|
4
|
+
export function useOrderBumpV2(options) {
|
|
5
|
+
const { apiService, refreshCoordinator } = useTagadaContext();
|
|
6
|
+
const { checkout } = useCheckout();
|
|
7
|
+
const { offerId, productId, orderBumpType = 'primary' } = options;
|
|
8
|
+
// Check if order bump is selected from checkout session line items
|
|
9
|
+
const checkOrderBumpSelection = useCallback(() => {
|
|
10
|
+
console.log('🔧 useOrderBumpV2: Full checkout object:', checkout);
|
|
11
|
+
console.log('🔧 useOrderBumpV2: Checkout session:', checkout?.checkoutSession);
|
|
12
|
+
console.log('🔧 useOrderBumpV2: Session line items:', checkout?.checkoutSession?.sessionLineItems);
|
|
13
|
+
console.log('🔧 useOrderBumpV2: Checkout is null?', checkout === null);
|
|
14
|
+
console.log('🔧 useOrderBumpV2: Checkout session is null?', checkout?.checkoutSession === null);
|
|
15
|
+
console.log('🔧 useOrderBumpV2: Session line items is null?', checkout?.checkoutSession?.sessionLineItems === null);
|
|
16
|
+
if (!checkout?.checkoutSession?.sessionLineItems) {
|
|
17
|
+
console.log('🔧 useOrderBumpV2: No session line items available');
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const targetProductId = productId || offerId; // Fallback to offerId if productId not provided
|
|
21
|
+
console.log('🔧 useOrderBumpV2: Looking for product ID:', targetProductId);
|
|
22
|
+
const isSelected = checkout.checkoutSession.sessionLineItems.some((item) => {
|
|
23
|
+
const matches = item.isOrderBump === true && item.productId === targetProductId;
|
|
24
|
+
console.log('🔧 useOrderBumpV2: Checking item:', {
|
|
25
|
+
itemId: item.id,
|
|
26
|
+
productId: item.productId,
|
|
27
|
+
isOrderBump: item.isOrderBump,
|
|
28
|
+
targetProductId,
|
|
29
|
+
offerId,
|
|
30
|
+
matches
|
|
31
|
+
});
|
|
32
|
+
return matches;
|
|
33
|
+
});
|
|
34
|
+
console.log('🔧 useOrderBumpV2: Selection result:', isSelected);
|
|
35
|
+
return isSelected;
|
|
36
|
+
}, [checkout?.checkoutSession?.sessionLineItems, offerId, productId]);
|
|
37
|
+
// Initialize state based on current checkout session
|
|
38
|
+
const [isSelected, setIsSelected] = useState(() => checkOrderBumpSelection());
|
|
39
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
40
|
+
const [isToggling, setIsToggling] = useState(false);
|
|
41
|
+
const [error, setError] = useState(null);
|
|
42
|
+
// Update isSelected when checkout session changes
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
const isSelectedFromSession = checkOrderBumpSelection();
|
|
45
|
+
setIsSelected(isSelectedFromSession);
|
|
46
|
+
}, [checkOrderBumpSelection]);
|
|
47
|
+
const toggle = useCallback(async (selected) => {
|
|
48
|
+
if (!checkout?.checkoutSession?.id) {
|
|
49
|
+
console.warn('useOrderBumpV2: No checkout session available yet');
|
|
50
|
+
return { success: false, error: 'Checkout session not ready' };
|
|
51
|
+
}
|
|
52
|
+
const targetState = selected ?? !isSelected;
|
|
53
|
+
// Optimistic update
|
|
54
|
+
setIsSelected(targetState);
|
|
55
|
+
setIsToggling(true);
|
|
56
|
+
setError(null);
|
|
57
|
+
try {
|
|
58
|
+
const response = await apiService.fetch(`/api/v1/checkout-sessions/${checkout.checkoutSession.id}/toggle-order-bump`, {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
body: {
|
|
61
|
+
orderBumpOfferId: offerId,
|
|
62
|
+
selected: targetState,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
if (response.success) {
|
|
66
|
+
// Notify checkout hook that order bump data changed
|
|
67
|
+
await refreshCoordinator.notifyOrderBumpChanged();
|
|
68
|
+
return { success: true };
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// Revert optimistic update
|
|
72
|
+
setIsSelected(!targetState);
|
|
73
|
+
return { success: false, error: response.error };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
// Revert optimistic update
|
|
78
|
+
setIsSelected(!targetState);
|
|
79
|
+
const error = err instanceof Error ? err : new Error('Failed to toggle order bump');
|
|
80
|
+
setError(error);
|
|
81
|
+
return { success: false, error: error.message };
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
setIsToggling(false);
|
|
85
|
+
}
|
|
86
|
+
}, [checkout?.checkoutSession?.id, offerId, isSelected, apiService, refreshCoordinator]);
|
|
87
|
+
return {
|
|
88
|
+
isSelected,
|
|
89
|
+
isLoading,
|
|
90
|
+
isToggling,
|
|
91
|
+
error,
|
|
92
|
+
toggle,
|
|
93
|
+
isReady: !!checkout?.checkoutSession?.id,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface UseOrderBumpV3Options {
|
|
2
|
+
checkoutToken: string | null;
|
|
3
|
+
offerId: string;
|
|
4
|
+
productId?: string;
|
|
5
|
+
orderBumpType?: 'primary' | 'secondary';
|
|
6
|
+
}
|
|
7
|
+
export interface UseOrderBumpV3Result {
|
|
8
|
+
isSelected: boolean;
|
|
9
|
+
isLoading: boolean;
|
|
10
|
+
isToggling: boolean;
|
|
11
|
+
error: Error | null;
|
|
12
|
+
isReady: boolean;
|
|
13
|
+
toggle: (selected?: boolean) => Promise<{
|
|
14
|
+
success: boolean;
|
|
15
|
+
error?: any;
|
|
16
|
+
}>;
|
|
17
|
+
checkout: any;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Composable order bump hook that works with a specific checkout token
|
|
21
|
+
* This allows multiple order bumps to work with different checkout sessions
|
|
22
|
+
*/
|
|
23
|
+
export declare function useOrderBumpV3(options: UseOrderBumpV3Options): UseOrderBumpV3Result;
|