@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.
Files changed (112) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +2 -0
  3. package/dist/react/hooks/useCheckout.js +21 -33
  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 +94 -29
  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/usePayment.d.ts +1 -1
  14. package/dist/react/hooks/usePayment.js +2 -18
  15. package/dist/react/hooks/usePluginConfig.js +2 -13
  16. package/dist/react/hooks/usePostPurchases.js +11 -5
  17. package/dist/react/hooks/useProducts.js +2 -16
  18. package/dist/react/index.d.ts +9 -1
  19. package/dist/react/index.js +5 -1
  20. package/dist/react/providers/TagadaProvider.d.ts +0 -1
  21. package/dist/react/providers/TagadaProvider.js +16 -12
  22. package/dist/react/services/apiService.d.ts +1 -0
  23. package/dist/react/services/apiService.js +3 -0
  24. package/dist/v2/core/googleAutocomplete.d.ts +65 -0
  25. package/dist/v2/core/googleAutocomplete.js +94 -0
  26. package/dist/v2/core/index.d.ts +8 -0
  27. package/dist/v2/core/index.js +11 -0
  28. package/dist/v2/core/isoData.d.ts +50 -0
  29. package/dist/v2/core/isoData.js +103 -0
  30. package/dist/v2/core/resources/apiClient.d.ts +25 -0
  31. package/dist/v2/core/resources/apiClient.js +95 -0
  32. package/dist/v2/core/resources/checkout.d.ts +189 -0
  33. package/dist/v2/core/resources/checkout.js +119 -0
  34. package/dist/v2/core/resources/index.d.ts +13 -0
  35. package/dist/v2/core/resources/index.js +13 -0
  36. package/dist/v2/core/resources/offers.d.ts +98 -0
  37. package/dist/v2/core/resources/offers.js +115 -0
  38. package/dist/v2/core/resources/orders.d.ts +40 -0
  39. package/dist/v2/core/resources/orders.js +59 -0
  40. package/dist/v2/core/resources/payments.d.ts +140 -0
  41. package/dist/v2/core/resources/payments.js +126 -0
  42. package/dist/v2/core/resources/postPurchases.d.ts +182 -0
  43. package/dist/v2/core/resources/postPurchases.js +116 -0
  44. package/dist/v2/core/resources/products.d.ts +29 -0
  45. package/dist/v2/core/resources/products.js +49 -0
  46. package/dist/v2/core/resources/promotions.d.ts +45 -0
  47. package/dist/v2/core/resources/promotions.js +87 -0
  48. package/dist/v2/core/resources/threeds.d.ts +23 -0
  49. package/dist/v2/core/resources/threeds.js +15 -0
  50. package/dist/v2/core/utils/checkout.d.ts +24 -0
  51. package/dist/v2/core/utils/checkout.js +30 -0
  52. package/dist/v2/core/utils/currency.d.ts +28 -0
  53. package/dist/v2/core/utils/currency.js +272 -0
  54. package/dist/v2/core/utils/index.d.ts +12 -0
  55. package/dist/v2/core/utils/index.js +12 -0
  56. package/dist/v2/core/utils/order.d.ts +159 -0
  57. package/dist/v2/core/utils/order.js +42 -0
  58. package/dist/v2/core/utils/orderBump.d.ts +40 -0
  59. package/dist/v2/core/utils/orderBump.js +47 -0
  60. package/dist/v2/core/utils/pluginConfig.d.ts +43 -0
  61. package/dist/v2/core/utils/pluginConfig.js +155 -0
  62. package/dist/v2/core/utils/postPurchases.d.ts +32 -0
  63. package/dist/v2/core/utils/postPurchases.js +42 -0
  64. package/dist/v2/core/utils/products.d.ts +58 -0
  65. package/dist/v2/core/utils/products.js +64 -0
  66. package/dist/v2/core/utils/promotions.d.ts +24 -0
  67. package/dist/v2/core/utils/promotions.js +30 -0
  68. package/dist/v2/index.d.ts +19 -0
  69. package/dist/v2/index.js +15 -0
  70. package/dist/v2/react/components/DebugDrawer.d.ts +7 -0
  71. package/dist/v2/react/components/DebugDrawer.js +383 -0
  72. package/dist/v2/react/hooks/useApiQuery.d.ts +28 -0
  73. package/dist/v2/react/hooks/useApiQuery.js +84 -0
  74. package/dist/v2/react/hooks/useCheckoutQuery.d.ts +39 -0
  75. package/dist/v2/react/hooks/useCheckoutQuery.js +208 -0
  76. package/dist/v2/react/hooks/useCheckoutToken.d.ts +17 -0
  77. package/dist/v2/react/hooks/useCheckoutToken.js +80 -0
  78. package/dist/v2/react/hooks/useCurrency.d.ts +9 -0
  79. package/dist/v2/react/hooks/useCurrency.js +21 -0
  80. package/dist/v2/react/hooks/useGeoLocation.d.ts +138 -0
  81. package/dist/v2/react/hooks/useGeoLocation.js +126 -0
  82. package/dist/v2/react/hooks/useGoogleAutocomplete.d.ts +74 -0
  83. package/dist/v2/react/hooks/useGoogleAutocomplete.js +207 -0
  84. package/dist/v2/react/hooks/useISOData.d.ts +61 -0
  85. package/dist/v2/react/hooks/useISOData.js +176 -0
  86. package/dist/v2/react/hooks/useOffersQuery.d.ts +65 -0
  87. package/dist/v2/react/hooks/useOffersQuery.js +353 -0
  88. package/dist/v2/react/hooks/useOrderBumpQuery.d.ts +20 -0
  89. package/dist/v2/react/hooks/useOrderBumpQuery.js +88 -0
  90. package/dist/v2/react/hooks/useOrderQuery.d.ts +29 -0
  91. package/dist/v2/react/hooks/useOrderQuery.js +98 -0
  92. package/dist/v2/react/hooks/usePaymentPolling.d.ts +45 -0
  93. package/dist/v2/react/hooks/usePaymentPolling.js +153 -0
  94. package/dist/v2/react/hooks/usePaymentQuery.d.ts +19 -0
  95. package/dist/v2/react/hooks/usePaymentQuery.js +283 -0
  96. package/dist/v2/react/hooks/usePluginConfig.d.ts +16 -0
  97. package/dist/v2/react/hooks/usePluginConfig.js +36 -0
  98. package/dist/v2/react/hooks/usePostPurchasesQuery.d.ts +63 -0
  99. package/dist/v2/react/hooks/usePostPurchasesQuery.js +365 -0
  100. package/dist/v2/react/hooks/useProductsQuery.d.ts +31 -0
  101. package/dist/v2/react/hooks/useProductsQuery.js +102 -0
  102. package/dist/v2/react/hooks/usePromotionsQuery.d.ts +28 -0
  103. package/dist/v2/react/hooks/usePromotionsQuery.js +97 -0
  104. package/dist/v2/react/hooks/useThreeds.d.ts +36 -0
  105. package/dist/v2/react/hooks/useThreeds.js +166 -0
  106. package/dist/v2/react/hooks/useThreedsModal.d.ts +13 -0
  107. package/dist/v2/react/hooks/useThreedsModal.js +343 -0
  108. package/dist/v2/react/index.d.ts +38 -0
  109. package/dist/v2/react/index.js +27 -0
  110. package/dist/v2/react/providers/TagadaProvider.d.ts +63 -0
  111. package/dist/v2/react/providers/TagadaProvider.js +680 -0
  112. 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';
@@ -1,40 +1,11 @@
1
1
  import { useCallback, useEffect, useRef, useState } from 'react';
2
2
  import { useCurrency } from '../hooks/useCurrency';
3
- import { useTagadaContextSafe } from '../providers/TagadaProvider';
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 context = useTagadaContextSafe();
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.debug('[Checkout] Auto-loading from provided token:', {
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
- }, [autoLoadFromToken, providedToken, isInitialized, isLoading, getCheckout]);
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 { useTagadaContextSafe } from '../providers/TagadaProvider';
2
+ import { useTagadaContext } from '../providers/TagadaProvider';
3
+ import { useCheckout } from './useCheckout';
3
4
  export function useOrderBump(options) {
4
- const context = useTagadaContextSafe();
5
- // If context is not ready yet, return loading state
6
- if (!context) {
7
- return {
8
- isSelected: false,
9
- preview: null,
10
- savings: null,
11
- isLoading: false,
12
- isToggling: false,
13
- error: null,
14
- toggle: async () => ({ success: false }),
15
- refreshPreview: async () => { },
16
- };
17
- }
18
- const { apiService, refreshCoordinator } = context;
19
- const { checkoutSessionId, offerId, orderBumpType = 'vip', autoPreview = true } = options;
20
- const [isSelected, setIsSelected] = useState(false);
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
- const response = await apiService.fetch(`/api/v1/checkout-sessions/${checkoutSessionId}/vip-preview`, {
32
- method: 'POST',
33
- body: {
34
- 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,
35
95
  orderBumpType,
36
- },
37
- });
38
- setPreview(response);
39
- // Update isSelected based on preview data
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;