@tagadapay/plugin-sdk 2.4.39 → 2.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +2 -0
  3. package/dist/react/hooks/useCheckout.js +19 -2
  4. package/dist/react/hooks/useCheckoutSession.d.ts +19 -0
  5. package/dist/react/hooks/useCheckoutSession.js +108 -0
  6. package/dist/react/hooks/useCheckoutToken.d.ts +17 -0
  7. package/dist/react/hooks/useCheckoutToken.js +80 -0
  8. package/dist/react/hooks/useOrderBump.js +92 -13
  9. package/dist/react/hooks/useOrderBumpV2.d.ts +17 -0
  10. package/dist/react/hooks/useOrderBumpV2.js +95 -0
  11. package/dist/react/hooks/useOrderBumpV3.d.ts +23 -0
  12. package/dist/react/hooks/useOrderBumpV3.js +109 -0
  13. package/dist/react/hooks/usePostPurchases.js +11 -5
  14. package/dist/react/index.d.ts +8 -0
  15. package/dist/react/index.js +4 -0
  16. package/dist/react/services/apiService.d.ts +1 -0
  17. package/dist/react/services/apiService.js +3 -0
  18. package/dist/v2/core/googleAutocomplete.d.ts +65 -0
  19. package/dist/v2/core/googleAutocomplete.js +94 -0
  20. package/dist/v2/core/index.d.ts +8 -0
  21. package/dist/v2/core/index.js +11 -0
  22. package/dist/v2/core/isoData.d.ts +50 -0
  23. package/dist/v2/core/isoData.js +103 -0
  24. package/dist/v2/core/resources/apiClient.d.ts +25 -0
  25. package/dist/v2/core/resources/apiClient.js +95 -0
  26. package/dist/v2/core/resources/checkout.d.ts +189 -0
  27. package/dist/v2/core/resources/checkout.js +119 -0
  28. package/dist/v2/core/resources/index.d.ts +13 -0
  29. package/dist/v2/core/resources/index.js +13 -0
  30. package/dist/v2/core/resources/offers.d.ts +98 -0
  31. package/dist/v2/core/resources/offers.js +115 -0
  32. package/dist/v2/core/resources/orders.d.ts +40 -0
  33. package/dist/v2/core/resources/orders.js +59 -0
  34. package/dist/v2/core/resources/payments.d.ts +140 -0
  35. package/dist/v2/core/resources/payments.js +126 -0
  36. package/dist/v2/core/resources/postPurchases.d.ts +182 -0
  37. package/dist/v2/core/resources/postPurchases.js +116 -0
  38. package/dist/v2/core/resources/products.d.ts +29 -0
  39. package/dist/v2/core/resources/products.js +49 -0
  40. package/dist/v2/core/resources/promotions.d.ts +45 -0
  41. package/dist/v2/core/resources/promotions.js +87 -0
  42. package/dist/v2/core/resources/threeds.d.ts +23 -0
  43. package/dist/v2/core/resources/threeds.js +15 -0
  44. package/dist/v2/core/utils/checkout.d.ts +24 -0
  45. package/dist/v2/core/utils/checkout.js +30 -0
  46. package/dist/v2/core/utils/currency.d.ts +28 -0
  47. package/dist/v2/core/utils/currency.js +272 -0
  48. package/dist/v2/core/utils/index.d.ts +12 -0
  49. package/dist/v2/core/utils/index.js +12 -0
  50. package/dist/v2/core/utils/order.d.ts +159 -0
  51. package/dist/v2/core/utils/order.js +42 -0
  52. package/dist/v2/core/utils/orderBump.d.ts +40 -0
  53. package/dist/v2/core/utils/orderBump.js +47 -0
  54. package/dist/v2/core/utils/pluginConfig.d.ts +43 -0
  55. package/dist/v2/core/utils/pluginConfig.js +155 -0
  56. package/dist/v2/core/utils/postPurchases.d.ts +32 -0
  57. package/dist/v2/core/utils/postPurchases.js +42 -0
  58. package/dist/v2/core/utils/products.d.ts +58 -0
  59. package/dist/v2/core/utils/products.js +64 -0
  60. package/dist/v2/core/utils/promotions.d.ts +24 -0
  61. package/dist/v2/core/utils/promotions.js +30 -0
  62. package/dist/v2/index.d.ts +19 -0
  63. package/dist/v2/index.js +15 -0
  64. package/dist/v2/react/components/DebugDrawer.d.ts +7 -0
  65. package/dist/v2/react/components/DebugDrawer.js +383 -0
  66. package/dist/v2/react/hooks/useApiQuery.d.ts +28 -0
  67. package/dist/v2/react/hooks/useApiQuery.js +84 -0
  68. package/dist/v2/react/hooks/useCheckoutQuery.d.ts +39 -0
  69. package/dist/v2/react/hooks/useCheckoutQuery.js +194 -0
  70. package/dist/v2/react/hooks/useCheckoutToken.d.ts +17 -0
  71. package/dist/v2/react/hooks/useCheckoutToken.js +65 -0
  72. package/dist/v2/react/hooks/useCurrency.d.ts +9 -0
  73. package/dist/v2/react/hooks/useCurrency.js +21 -0
  74. package/dist/v2/react/hooks/useGeoLocation.d.ts +138 -0
  75. package/dist/v2/react/hooks/useGeoLocation.js +123 -0
  76. package/dist/v2/react/hooks/useGoogleAutocomplete.d.ts +74 -0
  77. package/dist/v2/react/hooks/useGoogleAutocomplete.js +196 -0
  78. package/dist/v2/react/hooks/useISOData.d.ts +61 -0
  79. package/dist/v2/react/hooks/useISOData.js +175 -0
  80. package/dist/v2/react/hooks/useOffersQuery.d.ts +65 -0
  81. package/dist/v2/react/hooks/useOffersQuery.js +342 -0
  82. package/dist/v2/react/hooks/useOrderBumpQuery.d.ts +20 -0
  83. package/dist/v2/react/hooks/useOrderBumpQuery.js +92 -0
  84. package/dist/v2/react/hooks/useOrderQuery.d.ts +29 -0
  85. package/dist/v2/react/hooks/useOrderQuery.js +98 -0
  86. package/dist/v2/react/hooks/usePaymentPolling.d.ts +45 -0
  87. package/dist/v2/react/hooks/usePaymentPolling.js +140 -0
  88. package/dist/v2/react/hooks/usePaymentQuery.d.ts +19 -0
  89. package/dist/v2/react/hooks/usePaymentQuery.js +272 -0
  90. package/dist/v2/react/hooks/usePluginConfig.d.ts +16 -0
  91. package/dist/v2/react/hooks/usePluginConfig.js +35 -0
  92. package/dist/v2/react/hooks/usePostPurchasesQuery.d.ts +63 -0
  93. package/dist/v2/react/hooks/usePostPurchasesQuery.js +343 -0
  94. package/dist/v2/react/hooks/useProductsQuery.d.ts +31 -0
  95. package/dist/v2/react/hooks/useProductsQuery.js +102 -0
  96. package/dist/v2/react/hooks/usePromotionsQuery.d.ts +28 -0
  97. package/dist/v2/react/hooks/usePromotionsQuery.js +97 -0
  98. package/dist/v2/react/hooks/useThreeds.d.ts +36 -0
  99. package/dist/v2/react/hooks/useThreeds.js +150 -0
  100. package/dist/v2/react/hooks/useThreedsModal.d.ts +13 -0
  101. package/dist/v2/react/hooks/useThreedsModal.js +343 -0
  102. package/dist/v2/react/index.d.ts +38 -0
  103. package/dist/v2/react/index.js +27 -0
  104. package/dist/v2/react/providers/TagadaProvider.d.ts +63 -0
  105. package/dist/v2/react/providers/TagadaProvider.js +680 -0
  106. 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
@@ -15,3 +15,5 @@ export { convertCurrency, formatMoney, formatMoneyWithoutSymbol, formatSimpleMon
15
15
  // Export data utilities
16
16
  export * from './data/iso3166';
17
17
  export * from './data/languages';
18
+ // V2 exports - new clean architecture
19
+ export * from './v2';
@@ -473,11 +473,25 @@ export function useCheckout(options = {}) {
473
473
  }, []);
474
474
  // Auto-load existing checkout session from provided token
475
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
+ });
476
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
+ });
477
491
  return;
478
492
  }
479
493
  if (!isLoading) {
480
- console.debug('[Checkout] Auto-loading from provided token:', {
494
+ console.log('🔧 useCheckout: Auto-loading from provided token:', {
481
495
  tokenPreview: providedToken.substring(0, 8) + '...',
482
496
  });
483
497
  hasAutoLoadedRef.current = true;
@@ -487,7 +501,10 @@ export function useCheckout(options = {}) {
487
501
  hasAutoLoadedRef.current = false; // Reset to allow retry
488
502
  });
489
503
  }
490
- }, [autoLoadFromToken, providedToken, isInitialized, isLoading, getCheckout]);
504
+ else {
505
+ console.log('🔧 useCheckout: Auto-load skipped - still loading');
506
+ }
507
+ }, [autoLoadFromToken, providedToken, isInitialized, isLoading, getCheckout, isSessionInitialized]);
491
508
  return {
492
509
  checkout,
493
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,30 +1,102 @@
1
1
  import { useState, useCallback, useEffect } from 'react';
2
2
  import { useTagadaContext } from '../providers/TagadaProvider';
3
+ import { useCheckout } from './useCheckout';
3
4
  export function useOrderBump(options) {
4
5
  const { apiService, refreshCoordinator } = useTagadaContext();
5
- const { checkoutSessionId, offerId, orderBumpType = 'vip', autoPreview = true } = options;
6
- const [isSelected, setIsSelected] = useState(false);
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);
7
46
  const [preview, setPreview] = useState(null);
8
47
  const [isLoading, setIsLoading] = useState(false);
9
48
  const [isToggling, setIsToggling] = useState(false);
10
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]);
11
67
  const refreshPreview = useCallback(async () => {
12
68
  if (!checkoutSessionId)
13
69
  return;
14
70
  setIsLoading(true);
15
71
  setError(null);
16
72
  try {
17
- const response = await apiService.fetch(`/api/v1/checkout-sessions/${checkoutSessionId}/vip-preview`, {
18
- method: 'POST',
19
- body: {
20
- orderBumpOfferIds: [offerId],
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,
21
95
  orderBumpType,
22
- },
23
- });
24
- setPreview(response);
25
- // Update isSelected based on preview data
26
- const offerSelected = response.selectedOffers?.some((offer) => offer.isSelected);
27
- setIsSelected(offerSelected ?? false);
96
+ sessionLineItems: checkout?.checkoutSession?.sessionLineItems,
97
+ isSelectedFromSession
98
+ });
99
+ }
28
100
  }
29
101
  catch (err) {
30
102
  const error = err instanceof Error ? err : new Error('Failed to fetch preview');
@@ -34,7 +106,7 @@ export function useOrderBump(options) {
34
106
  finally {
35
107
  setIsLoading(false);
36
108
  }
37
- }, [checkoutSessionId, offerId, orderBumpType, apiService]);
109
+ }, [checkoutSessionId, offerId, orderBumpType, apiService, checkOrderBumpSelection]);
38
110
  const toggle = useCallback(async (selected) => {
39
111
  if (!checkoutSessionId) {
40
112
  throw new Error('No checkout session available');
@@ -76,6 +148,13 @@ export function useOrderBump(options) {
76
148
  setIsToggling(false);
77
149
  }
78
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]);
79
158
  // Auto-fetch preview on mount and when dependencies change
80
159
  useEffect(() => {
81
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;
@@ -0,0 +1,109 @@
1
+ import { useState, useCallback, useEffect } from 'react';
2
+ import { useTagadaContext } from '../providers/TagadaProvider';
3
+ import { useCheckoutSession } from './useCheckoutSession';
4
+ /**
5
+ * Composable order bump hook that works with a specific checkout token
6
+ * This allows multiple order bumps to work with different checkout sessions
7
+ */
8
+ export function useOrderBumpV3(options) {
9
+ const { apiService, refreshCoordinator } = useTagadaContext();
10
+ const { checkoutToken, offerId, productId, orderBumpType = 'primary' } = options;
11
+ // Use the composable checkout session hook
12
+ const { checkout, isLoading: checkoutLoading, error: checkoutError, refresh: refreshCheckout } = useCheckoutSession({
13
+ checkoutToken,
14
+ autoRefresh: false, // We'll handle refreshes manually
15
+ });
16
+ // Check if order bump is selected from checkout session line items
17
+ const checkOrderBumpSelection = useCallback(() => {
18
+ console.log('🔧 useOrderBumpV3: Checking order bump selection', {
19
+ checkoutToken: checkoutToken ? checkoutToken.substring(0, 8) + '...' : null,
20
+ offerId,
21
+ productId,
22
+ hasCheckout: !!checkout,
23
+ hasSession: !!checkout?.checkoutSession,
24
+ hasLineItems: !!checkout?.checkoutSession?.sessionLineItems
25
+ });
26
+ if (!checkout?.checkoutSession?.sessionLineItems) {
27
+ console.log('🔧 useOrderBumpV3: No session line items available');
28
+ return false;
29
+ }
30
+ const targetProductId = productId || offerId; // Fallback to offerId if productId not provided
31
+ console.log('🔧 useOrderBumpV3: Looking for product ID:', targetProductId);
32
+ const isSelected = checkout.checkoutSession.sessionLineItems.some((item) => {
33
+ const matches = item.isOrderBump === true && item.productId === targetProductId;
34
+ console.log('🔧 useOrderBumpV3: Checking item:', {
35
+ itemId: item.id,
36
+ productId: item.productId,
37
+ isOrderBump: item.isOrderBump,
38
+ targetProductId,
39
+ offerId,
40
+ matches
41
+ });
42
+ return matches;
43
+ });
44
+ console.log('🔧 useOrderBumpV3: Selection result:', isSelected);
45
+ return isSelected;
46
+ }, [checkout?.checkoutSession?.sessionLineItems, offerId, productId]);
47
+ // Initialize state based on current checkout session
48
+ const [isSelected, setIsSelected] = useState(() => checkOrderBumpSelection());
49
+ const [isToggling, setIsToggling] = useState(false);
50
+ const [error, setError] = useState(null);
51
+ // Update isSelected when checkout session changes
52
+ useEffect(() => {
53
+ const isSelectedFromSession = checkOrderBumpSelection();
54
+ setIsSelected(isSelectedFromSession);
55
+ }, [checkOrderBumpSelection]);
56
+ const toggle = useCallback(async (selected) => {
57
+ if (!checkout?.checkoutSession?.id) {
58
+ console.warn('useOrderBumpV3: No checkout session available yet');
59
+ return { success: false, error: 'Checkout session not ready' };
60
+ }
61
+ const targetState = selected ?? !isSelected;
62
+ // Optimistic update
63
+ setIsSelected(targetState);
64
+ setIsToggling(true);
65
+ setError(null);
66
+ try {
67
+ const response = await apiService.fetch(`/api/v1/checkout-sessions/${checkout.checkoutSession.id}/toggle-order-bump`, {
68
+ method: 'POST',
69
+ body: {
70
+ orderBumpOfferId: offerId,
71
+ selected: targetState,
72
+ },
73
+ });
74
+ if (response.success) {
75
+ // Refresh the checkout session to get updated data
76
+ await refreshCheckout();
77
+ // Notify other hooks that order bump data changed
78
+ await refreshCoordinator.notifyOrderBumpChanged();
79
+ // Also notify checkout hooks that data changed
80
+ await refreshCoordinator.notifyCheckoutChanged();
81
+ return { success: true };
82
+ }
83
+ else {
84
+ // Revert optimistic update
85
+ setIsSelected(!targetState);
86
+ return { success: false, error: response.error };
87
+ }
88
+ }
89
+ catch (err) {
90
+ // Revert optimistic update
91
+ setIsSelected(!targetState);
92
+ const error = err instanceof Error ? err : new Error('Failed to toggle order bump');
93
+ setError(error);
94
+ return { success: false, error: error.message };
95
+ }
96
+ finally {
97
+ setIsToggling(false);
98
+ }
99
+ }, [checkout?.checkoutSession?.id, offerId, isSelected, apiService, refreshCoordinator, refreshCheckout]);
100
+ return {
101
+ isSelected,
102
+ isLoading: checkoutLoading,
103
+ isToggling,
104
+ error: error || checkoutError,
105
+ toggle,
106
+ isReady: !!checkout?.checkoutSession?.id,
107
+ checkout,
108
+ };
109
+ }