@tagadapay/plugin-sdk 3.1.2 → 3.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1129 -1129
- package/build-cdn.js +113 -113
- package/dist/external-tracker.js +1104 -491
- package/dist/external-tracker.min.js +2 -2
- package/dist/external-tracker.min.js.map +4 -4
- package/dist/react/hooks/useApplePay.js +25 -36
- package/dist/react/hooks/usePaymentPolling.d.ts +9 -3
- package/dist/react/providers/TagadaProvider.js +5 -5
- package/dist/react/utils/money.d.ts +4 -3
- package/dist/react/utils/money.js +39 -6
- package/dist/react/utils/trackingUtils.js +1 -0
- package/dist/v2/core/client.js +34 -2
- package/dist/v2/core/config/environment.js +9 -2
- package/dist/v2/core/funnelClient.d.ts +92 -1
- package/dist/v2/core/funnelClient.js +247 -3
- package/dist/v2/core/resources/apiClient.js +1 -1
- package/dist/v2/core/resources/checkout.d.ts +68 -0
- package/dist/v2/core/resources/funnel.d.ts +15 -0
- package/dist/v2/core/resources/payments.d.ts +50 -3
- package/dist/v2/core/resources/payments.js +38 -7
- package/dist/v2/core/utils/pluginConfig.js +40 -5
- package/dist/v2/core/utils/previewMode.d.ts +3 -0
- package/dist/v2/core/utils/previewMode.js +44 -14
- package/dist/v2/core/utils/previewModeIndicator.d.ts +19 -0
- package/dist/v2/core/utils/previewModeIndicator.js +414 -0
- package/dist/v2/core/utils/tokenStorage.d.ts +4 -0
- package/dist/v2/core/utils/tokenStorage.js +15 -1
- package/dist/v2/index.d.ts +6 -1
- package/dist/v2/index.js +6 -1
- package/dist/v2/react/components/ApplePayButton.d.ts +21 -121
- package/dist/v2/react/components/ApplePayButton.js +221 -290
- package/dist/v2/react/components/FunnelScriptInjector.d.ts +3 -1
- package/dist/v2/react/components/FunnelScriptInjector.js +128 -24
- package/dist/v2/react/components/PreviewModeIndicator.d.ts +46 -0
- package/dist/v2/react/components/PreviewModeIndicator.js +113 -0
- package/dist/v2/react/hooks/useApplePayCheckout.d.ts +16 -0
- package/dist/v2/react/hooks/useApplePayCheckout.js +193 -0
- package/dist/v2/react/hooks/useFunnel.d.ts +42 -6
- package/dist/v2/react/hooks/useFunnel.js +25 -5
- package/dist/v2/react/hooks/usePaymentPolling.d.ts +9 -3
- package/dist/v2/react/hooks/usePaymentPolling.js +31 -9
- package/dist/v2/react/hooks/usePaymentQuery.d.ts +32 -2
- package/dist/v2/react/hooks/usePaymentQuery.js +304 -7
- package/dist/v2/react/hooks/usePaymentRetrieve.d.ts +26 -0
- package/dist/v2/react/hooks/usePaymentRetrieve.js +175 -0
- package/dist/v2/react/hooks/useStepConfig.d.ts +62 -0
- package/dist/v2/react/hooks/useStepConfig.js +52 -0
- package/dist/v2/react/index.d.ts +9 -3
- package/dist/v2/react/index.js +5 -1
- package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +27 -19
- package/dist/v2/react/providers/TagadaProvider.js +7 -7
- package/dist/v2/standalone/external-tracker.d.ts +2 -0
- package/dist/v2/standalone/external-tracker.js +6 -3
- package/package.json +112 -112
- package/dist/v2/react/hooks/useApplePay.d.ts +0 -16
- package/dist/v2/react/hooks/useApplePay.js +0 -247
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payment Retrieve Hook using TanStack Query (V2)
|
|
3
|
+
* Handles payment status retrieval after external redirects
|
|
4
|
+
* This is useful for processors that require server-side status checks (e.g., some 3DS flows)
|
|
5
|
+
*/
|
|
6
|
+
import { useState, useCallback, useRef, useEffect, useMemo } from 'react';
|
|
7
|
+
import { PaymentsResource } from '../../core/resources/payments';
|
|
8
|
+
import { usePaymentPolling } from './usePaymentPolling';
|
|
9
|
+
import { getGlobalApiClient } from './useApiQuery';
|
|
10
|
+
export function usePaymentRetrieve() {
|
|
11
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
12
|
+
const [error, setError] = useState(null);
|
|
13
|
+
// Create payments resource client
|
|
14
|
+
const paymentsResource = useMemo(() => {
|
|
15
|
+
try {
|
|
16
|
+
return new PaymentsResource(getGlobalApiClient());
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
throw new Error('Failed to initialize payments resource: ' + (error instanceof Error ? error.message : 'Unknown error'));
|
|
20
|
+
}
|
|
21
|
+
}, []);
|
|
22
|
+
const { startPolling } = usePaymentPolling();
|
|
23
|
+
// Refs to prevent multiple simultaneous calls
|
|
24
|
+
const isPollingRef = useRef(false);
|
|
25
|
+
const pollIntervalRef = useRef(null);
|
|
26
|
+
const attemptsRef = useRef(0);
|
|
27
|
+
const isPageLoadedRef = useRef(false);
|
|
28
|
+
// Check if page is fully loaded
|
|
29
|
+
const checkPageLoaded = useCallback(() => {
|
|
30
|
+
return new Promise((resolve) => {
|
|
31
|
+
// If already loaded, resolve immediately
|
|
32
|
+
if (document.readyState === 'complete') {
|
|
33
|
+
isPageLoadedRef.current = true;
|
|
34
|
+
resolve();
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
// Wait for load event
|
|
38
|
+
const handleLoad = () => {
|
|
39
|
+
isPageLoadedRef.current = true;
|
|
40
|
+
window.removeEventListener('load', handleLoad);
|
|
41
|
+
resolve();
|
|
42
|
+
};
|
|
43
|
+
window.addEventListener('load', handleLoad);
|
|
44
|
+
// Fallback: resolve after a short delay if load event doesn't fire
|
|
45
|
+
setTimeout(() => {
|
|
46
|
+
if (!isPageLoadedRef.current) {
|
|
47
|
+
isPageLoadedRef.current = true;
|
|
48
|
+
window.removeEventListener('load', handleLoad);
|
|
49
|
+
resolve();
|
|
50
|
+
}
|
|
51
|
+
}, 1000);
|
|
52
|
+
});
|
|
53
|
+
}, []);
|
|
54
|
+
// Cleanup function
|
|
55
|
+
const cleanup = useCallback(() => {
|
|
56
|
+
if (pollIntervalRef.current) {
|
|
57
|
+
clearInterval(pollIntervalRef.current);
|
|
58
|
+
pollIntervalRef.current = null;
|
|
59
|
+
}
|
|
60
|
+
isPollingRef.current = false;
|
|
61
|
+
attemptsRef.current = 0;
|
|
62
|
+
setIsLoading(false);
|
|
63
|
+
// Clean up query parameters
|
|
64
|
+
if (typeof window !== 'undefined') {
|
|
65
|
+
const newUrl = window.location.pathname;
|
|
66
|
+
window.history.replaceState({}, document.title, newUrl);
|
|
67
|
+
}
|
|
68
|
+
}, []);
|
|
69
|
+
// Start retrieve polling
|
|
70
|
+
const startRetrievePolling = useCallback(async (paymentId) => {
|
|
71
|
+
// Prevent multiple simultaneous calls
|
|
72
|
+
if (isPollingRef.current) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
// Wait for page to be fully loaded
|
|
76
|
+
await checkPageLoaded();
|
|
77
|
+
// Double-check after page load
|
|
78
|
+
if (isPollingRef.current) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
isPollingRef.current = true;
|
|
82
|
+
setIsLoading(true);
|
|
83
|
+
attemptsRef.current = 0;
|
|
84
|
+
const maxAttempts = 10;
|
|
85
|
+
const pollInterval = 3000; // 3 seconds
|
|
86
|
+
const checkRetrieve = async () => {
|
|
87
|
+
try {
|
|
88
|
+
attemptsRef.current++;
|
|
89
|
+
const result = await paymentsResource.retrievePayment(paymentId);
|
|
90
|
+
const status = result?.retrieveResult?.status || result?.status;
|
|
91
|
+
// Fetch payment to check for requireAction
|
|
92
|
+
const payment = await paymentsResource.getPaymentStatus(paymentId);
|
|
93
|
+
// Check if payment requires action
|
|
94
|
+
if (payment?.requireAction !== 'none' && payment?.requireActionData && !payment?.requireActionData?.processed) {
|
|
95
|
+
cleanup();
|
|
96
|
+
// Payment requires new action - would need to be handled by parent component
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
// Check if payment is succeeded
|
|
100
|
+
if (result?.retrieveResult?.success && status === 'succeeded') {
|
|
101
|
+
cleanup();
|
|
102
|
+
// Start regular polling to handle UI updates
|
|
103
|
+
startPolling(paymentId, {
|
|
104
|
+
onSuccess: () => {
|
|
105
|
+
// Payment succeeded after retrieve
|
|
106
|
+
},
|
|
107
|
+
onFailure: (errorMsg) => {
|
|
108
|
+
setError(errorMsg);
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// Check if payment is declined or error
|
|
114
|
+
if (status === 'declined' || status === 'error') {
|
|
115
|
+
cleanup();
|
|
116
|
+
const errorMsg = result?.retrieveResult?.message || result?.message || 'Payment failed';
|
|
117
|
+
setError(errorMsg);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
// If still pending and haven't reached max attempts, continue polling
|
|
121
|
+
if (attemptsRef.current < maxAttempts && status === 'pending') {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
// Max attempts reached or unknown status
|
|
125
|
+
if (attemptsRef.current >= maxAttempts) {
|
|
126
|
+
cleanup();
|
|
127
|
+
const errorMsg = 'Payment status check timed out. Please check your payment status manually.';
|
|
128
|
+
setError(errorMsg);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
// On error, continue polling unless we've reached max attempts
|
|
133
|
+
if (attemptsRef.current >= maxAttempts) {
|
|
134
|
+
cleanup();
|
|
135
|
+
const errorMsg = error instanceof Error ? error.message : 'Failed to retrieve payment status';
|
|
136
|
+
setError(errorMsg);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
// Initial check
|
|
141
|
+
await checkRetrieve();
|
|
142
|
+
// Set up polling interval
|
|
143
|
+
pollIntervalRef.current = setInterval(async () => {
|
|
144
|
+
if (attemptsRef.current >= maxAttempts || !isPollingRef.current) {
|
|
145
|
+
cleanup();
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
await checkRetrieve();
|
|
149
|
+
}, pollInterval);
|
|
150
|
+
}, [paymentsResource, startPolling, cleanup, checkPageLoaded]);
|
|
151
|
+
// Cleanup on unmount
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
return () => {
|
|
154
|
+
cleanup();
|
|
155
|
+
};
|
|
156
|
+
}, [cleanup]);
|
|
157
|
+
// Handle retrieve mode from URL query parameters
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
if (typeof window === 'undefined')
|
|
160
|
+
return;
|
|
161
|
+
// Check for payment retrieve mode in URL
|
|
162
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
163
|
+
const paymentMode = urlParams.get('mode');
|
|
164
|
+
const paymentIdFromUrl = urlParams.get('paymentId');
|
|
165
|
+
if (paymentMode === 'retrieve' && paymentIdFromUrl) {
|
|
166
|
+
void startRetrievePolling(paymentIdFromUrl);
|
|
167
|
+
}
|
|
168
|
+
}, [startRetrievePolling]);
|
|
169
|
+
return {
|
|
170
|
+
startRetrievePolling,
|
|
171
|
+
isLoading,
|
|
172
|
+
error,
|
|
173
|
+
isPolling: isPollingRef.current,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useStepConfig Hook - Access runtime step configuration from HTML injection
|
|
3
|
+
*
|
|
4
|
+
* This hook provides access to step-specific configuration that is injected
|
|
5
|
+
* into the HTML by the CRM when the page is served. This includes:
|
|
6
|
+
* - Payment flow overrides
|
|
7
|
+
* - Static resources (offer, product IDs for A/B variants)
|
|
8
|
+
* - Custom scripts
|
|
9
|
+
* - Pixel tracking configuration
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* ```tsx
|
|
13
|
+
* const { stepConfig, paymentFlowId, staticResources } = useStepConfig();
|
|
14
|
+
*
|
|
15
|
+
* // Access payment flow override
|
|
16
|
+
* if (paymentFlowId) {
|
|
17
|
+
* console.log('Using custom payment flow:', paymentFlowId);
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* // Access static resources (e.g., offer ID for current A/B variant)
|
|
21
|
+
* const offerId = staticResources?.offer;
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
import { RuntimeStepConfig } from '../../core/funnelClient';
|
|
25
|
+
export interface UseStepConfigResult {
|
|
26
|
+
/**
|
|
27
|
+
* Full step configuration object
|
|
28
|
+
* Contains payment, staticResources, scripts, and pixels
|
|
29
|
+
*/
|
|
30
|
+
stepConfig: RuntimeStepConfig | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Payment flow ID override for this step
|
|
33
|
+
* If set, this payment flow should be used instead of the store default
|
|
34
|
+
*/
|
|
35
|
+
paymentFlowId: string | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Static resources assigned to this step/variant
|
|
38
|
+
* For A/B tests, this contains the resources for the specific variant the user landed on
|
|
39
|
+
* e.g., { offer: 'offer_xxx', product: 'product_xxx' }
|
|
40
|
+
*/
|
|
41
|
+
staticResources: Record<string, string> | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* Get scripts for a specific injection position
|
|
44
|
+
* Only returns enabled scripts
|
|
45
|
+
* @param position - Where the scripts should be injected
|
|
46
|
+
*/
|
|
47
|
+
getScripts: (position?: 'head-start' | 'head-end' | 'body-start' | 'body-end') => RuntimeStepConfig['scripts'];
|
|
48
|
+
/**
|
|
49
|
+
* Pixel tracking configuration
|
|
50
|
+
* e.g., { facebook: 'pixel_id', google: 'ga_id' }
|
|
51
|
+
*/
|
|
52
|
+
pixels: Record<string, string> | undefined;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Hook to access runtime step configuration injected via HTML
|
|
56
|
+
*
|
|
57
|
+
* The step config is read from window.__TGD_STEP_CONFIG__ or the x-step-config meta tag.
|
|
58
|
+
* Values are memoized and only re-computed on mount.
|
|
59
|
+
*
|
|
60
|
+
* @returns Step configuration values and helpers
|
|
61
|
+
*/
|
|
62
|
+
export declare function useStepConfig(): UseStepConfigResult;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useStepConfig Hook - Access runtime step configuration from HTML injection
|
|
3
|
+
*
|
|
4
|
+
* This hook provides access to step-specific configuration that is injected
|
|
5
|
+
* into the HTML by the CRM when the page is served. This includes:
|
|
6
|
+
* - Payment flow overrides
|
|
7
|
+
* - Static resources (offer, product IDs for A/B variants)
|
|
8
|
+
* - Custom scripts
|
|
9
|
+
* - Pixel tracking configuration
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* ```tsx
|
|
13
|
+
* const { stepConfig, paymentFlowId, staticResources } = useStepConfig();
|
|
14
|
+
*
|
|
15
|
+
* // Access payment flow override
|
|
16
|
+
* if (paymentFlowId) {
|
|
17
|
+
* console.log('Using custom payment flow:', paymentFlowId);
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* // Access static resources (e.g., offer ID for current A/B variant)
|
|
21
|
+
* const offerId = staticResources?.offer;
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
import { useMemo } from 'react';
|
|
25
|
+
import { getAssignedStepConfig, getAssignedPaymentFlowId, getAssignedStaticResources, getAssignedScripts, } from '../../core/funnelClient';
|
|
26
|
+
/**
|
|
27
|
+
* Hook to access runtime step configuration injected via HTML
|
|
28
|
+
*
|
|
29
|
+
* The step config is read from window.__TGD_STEP_CONFIG__ or the x-step-config meta tag.
|
|
30
|
+
* Values are memoized and only re-computed on mount.
|
|
31
|
+
*
|
|
32
|
+
* @returns Step configuration values and helpers
|
|
33
|
+
*/
|
|
34
|
+
export function useStepConfig() {
|
|
35
|
+
// Read once on mount - these values don't change during the page lifecycle
|
|
36
|
+
const stepConfig = useMemo(() => getAssignedStepConfig(), []);
|
|
37
|
+
const paymentFlowId = useMemo(() => getAssignedPaymentFlowId(), []);
|
|
38
|
+
const staticResources = useMemo(() => getAssignedStaticResources(), []);
|
|
39
|
+
// Create a stable reference for getScripts
|
|
40
|
+
const getScripts = useMemo(() => {
|
|
41
|
+
return (position) => {
|
|
42
|
+
return getAssignedScripts(position);
|
|
43
|
+
};
|
|
44
|
+
}, []);
|
|
45
|
+
return {
|
|
46
|
+
stepConfig,
|
|
47
|
+
paymentFlowId,
|
|
48
|
+
staticResources,
|
|
49
|
+
getScripts,
|
|
50
|
+
pixels: stepConfig?.pixels,
|
|
51
|
+
};
|
|
52
|
+
}
|
package/dist/v2/react/index.d.ts
CHANGED
|
@@ -7,7 +7,8 @@ export { TagadaProvider, useTagadaContext } from './providers/TagadaProvider';
|
|
|
7
7
|
export type { DebugScript } from './providers/TagadaProvider';
|
|
8
8
|
export { ApplePayButton } from './components/ApplePayButton';
|
|
9
9
|
export { GooglePayButton } from './components/GooglePayButton';
|
|
10
|
-
export {
|
|
10
|
+
export { PreviewModeIndicator } from './components/PreviewModeIndicator';
|
|
11
|
+
export { useApplePayCheckout } from './hooks/useApplePayCheckout';
|
|
11
12
|
export { useAuth } from './hooks/useAuth';
|
|
12
13
|
export { useCheckoutToken } from './hooks/useCheckoutToken';
|
|
13
14
|
export { useClubOffers } from './hooks/useClubOffers';
|
|
@@ -32,6 +33,7 @@ export { usePreviewOffer } from './hooks/usePreviewOffer';
|
|
|
32
33
|
export { useOrderBumpQuery as useOrderBump } from './hooks/useOrderBumpQuery';
|
|
33
34
|
export { useOrderQuery as useOrder } from './hooks/useOrderQuery';
|
|
34
35
|
export { usePaymentQuery as usePayment } from './hooks/usePaymentQuery';
|
|
36
|
+
export { usePaymentRetrieve } from './hooks/usePaymentRetrieve';
|
|
35
37
|
export { usePostPurchasesQuery as usePostPurchases } from './hooks/usePostPurchasesQuery';
|
|
36
38
|
export { useProductsQuery as useProducts } from './hooks/useProductsQuery';
|
|
37
39
|
export { usePromotionsQuery as usePromotions } from './hooks/usePromotionsQuery';
|
|
@@ -42,8 +44,9 @@ export { useThreedsModal } from './hooks/useThreedsModal';
|
|
|
42
44
|
export { useTranslation } from './hooks/useTranslation';
|
|
43
45
|
export { useVipOffersQuery as useVipOffers } from './hooks/useVipOffersQuery';
|
|
44
46
|
export { useFunnel } from './hooks/useFunnel';
|
|
47
|
+
export { useStepConfig } from './hooks/useStepConfig';
|
|
45
48
|
export { useFunnel as useFunnelLegacy, useSimpleFunnel } from './hooks/useFunnelLegacy';
|
|
46
|
-
export type {
|
|
49
|
+
export type { UseApplePayCheckoutOptions } from './hooks/useApplePayCheckout';
|
|
47
50
|
export type { UseCheckoutTokenOptions, UseCheckoutTokenResult } from './hooks/useCheckoutToken';
|
|
48
51
|
export type { ClubOffer, ClubOfferItem, ClubOfferLineItem, ClubOfferSummary, UseClubOffersOptions, UseClubOffersResult } from './hooks/useClubOffers';
|
|
49
52
|
export type { UseCreditsOptions, UseCreditsResult } from './hooks/useCredits';
|
|
@@ -55,6 +58,7 @@ export type { ExpressPaymentMethodsContextType, ExpressPaymentMethodsProviderPro
|
|
|
55
58
|
export type { UseLoginOptions, UseLoginResult } from './hooks/useLogin';
|
|
56
59
|
export type { ApplePayButtonProps } from './components/ApplePayButton';
|
|
57
60
|
export type { GooglePayButtonProps } from './components/GooglePayButton';
|
|
61
|
+
export type { PreviewModeIndicatorProps } from './components/PreviewModeIndicator';
|
|
58
62
|
export type { GeoLocationData, UseGeoLocationOptions, UseGeoLocationReturn } from './hooks/useGeoLocation';
|
|
59
63
|
export type { ExtractedAddress, GooglePlaceDetails, GooglePrediction, UseGoogleAutocompleteOptions, UseGoogleAutocompleteResult } from './hooks/useGoogleAutocomplete';
|
|
60
64
|
export type { ISOCountry, ISORegion, UseISODataResult } from './hooks/useISOData';
|
|
@@ -64,13 +68,15 @@ export { FunnelActionType } from '../core/resources/funnel';
|
|
|
64
68
|
export type { UseCheckoutQueryOptions as UseCheckoutOptions, UseCheckoutQueryResult as UseCheckoutResult } from './hooks/useCheckoutQuery';
|
|
65
69
|
export type { UseDiscountsQueryOptions as UseDiscountsOptions, UseDiscountsQueryResult as UseDiscountsResult } from './hooks/useDiscountsQuery';
|
|
66
70
|
export type { FunnelAction, FunnelNavigationAction, FunnelNavigationResult, SimpleFunnelContext } from '../core/resources/funnel';
|
|
67
|
-
export type { FunnelContextValue } from './hooks/useFunnel';
|
|
71
|
+
export type { FunnelContextValue, StepConfigValue } from './hooks/useFunnel';
|
|
72
|
+
export type { UseStepConfigResult } from './hooks/useStepConfig';
|
|
68
73
|
export type { UseFunnelOptions as UseFunnelLegacyOptions, UseFunnelResult as UseFunnelLegacyResult } from './hooks/useFunnelLegacy';
|
|
69
74
|
export type { AvailableVariant, LineItemSelection, OfferLineItem, OfferPreviewSummary, UseOfferQueryOptions as UseOfferOptions, UseOfferQueryResult as UseOfferResult } from './hooks/useOfferQuery';
|
|
70
75
|
export type { PreviewOfferSummary, UsePreviewOfferOptions, UsePreviewOfferResult } from './hooks/usePreviewOffer';
|
|
71
76
|
export type { UseOrderBumpQueryOptions as UseOrderBumpOptions, UseOrderBumpQueryResult as UseOrderBumpResult } from './hooks/useOrderBumpQuery';
|
|
72
77
|
export type { UseOrderQueryOptions as UseOrderOptions, UseOrderQueryResult as UseOrderResult } from './hooks/useOrderQuery';
|
|
73
78
|
export type { PaymentHook as UsePaymentResult } from './hooks/usePaymentQuery';
|
|
79
|
+
export type { PaymentRetrieveHook as UsePaymentRetrieveResult } from './hooks/usePaymentRetrieve';
|
|
74
80
|
export type { UsePostPurchasesQueryOptions as UsePostPurchasesOptions, UsePostPurchasesQueryResult as UsePostPurchasesResult } from './hooks/usePostPurchasesQuery';
|
|
75
81
|
export type { UseProductsQueryOptions as UseProductsOptions, UseProductsQueryResult as UseProductsResult } from './hooks/useProductsQuery';
|
|
76
82
|
export type { UsePromotionsQueryOptions as UsePromotionsOptions, UsePromotionsQueryResult as UsePromotionsResult } from './hooks/usePromotionsQuery';
|
package/dist/v2/react/index.js
CHANGED
|
@@ -8,8 +8,9 @@ export { TagadaProvider, useTagadaContext } from './providers/TagadaProvider';
|
|
|
8
8
|
// Components
|
|
9
9
|
export { ApplePayButton } from './components/ApplePayButton';
|
|
10
10
|
export { GooglePayButton } from './components/GooglePayButton';
|
|
11
|
+
export { PreviewModeIndicator } from './components/PreviewModeIndicator';
|
|
11
12
|
// Hooks
|
|
12
|
-
export {
|
|
13
|
+
export { useApplePayCheckout } from './hooks/useApplePayCheckout';
|
|
13
14
|
export { useAuth } from './hooks/useAuth';
|
|
14
15
|
export { useCheckoutToken } from './hooks/useCheckoutToken';
|
|
15
16
|
export { useClubOffers } from './hooks/useClubOffers';
|
|
@@ -35,6 +36,7 @@ export { usePreviewOffer } from './hooks/usePreviewOffer';
|
|
|
35
36
|
export { useOrderBumpQuery as useOrderBump } from './hooks/useOrderBumpQuery';
|
|
36
37
|
export { useOrderQuery as useOrder } from './hooks/useOrderQuery';
|
|
37
38
|
export { usePaymentQuery as usePayment } from './hooks/usePaymentQuery';
|
|
39
|
+
export { usePaymentRetrieve } from './hooks/usePaymentRetrieve';
|
|
38
40
|
export { usePostPurchasesQuery as usePostPurchases } from './hooks/usePostPurchasesQuery';
|
|
39
41
|
export { useProductsQuery as useProducts } from './hooks/useProductsQuery';
|
|
40
42
|
export { usePromotionsQuery as usePromotions } from './hooks/usePromotionsQuery';
|
|
@@ -46,6 +48,8 @@ export { useTranslation } from './hooks/useTranslation';
|
|
|
46
48
|
export { useVipOffersQuery as useVipOffers } from './hooks/useVipOffersQuery';
|
|
47
49
|
// Funnel hooks
|
|
48
50
|
export { useFunnel } from './hooks/useFunnel';
|
|
51
|
+
// Step config hook (access runtime configuration from HTML injection)
|
|
52
|
+
export { useStepConfig } from './hooks/useStepConfig';
|
|
49
53
|
// Legacy funnel hooks (deprecated - use TagadaProvider + useFunnel instead)
|
|
50
54
|
export { useFunnel as useFunnelLegacy, useSimpleFunnel } from './hooks/useFunnelLegacy';
|
|
51
55
|
// TanStack Query types
|
|
@@ -40,8 +40,11 @@ export const ExpressPaymentMethodsProvider = ({ children, customerId, checkout,
|
|
|
40
40
|
setAvailableExpressPaymentMethodIds((prev) => (prev.includes(id) ? prev : [...prev, id]));
|
|
41
41
|
}, []);
|
|
42
42
|
const minorUnitsToCurrencyString = useCallback((amountMinor, currency) => {
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
// Fail safely - don't allow invalid data to become '0.00'
|
|
44
|
+
if (amountMinor === undefined || amountMinor === null || !currency) {
|
|
45
|
+
throw new Error(`Invalid currency data: amountMinor=${amountMinor}, currency=${currency}`);
|
|
46
|
+
}
|
|
47
|
+
// 0 is a valid amount (e.g., free shipping)
|
|
45
48
|
return (amountMinor / 100).toFixed(2);
|
|
46
49
|
}, []);
|
|
47
50
|
// Convert shipping rates to express shipping methods
|
|
@@ -69,9 +72,11 @@ export const ExpressPaymentMethodsProvider = ({ children, customerId, checkout,
|
|
|
69
72
|
// Update checkout session address mutation
|
|
70
73
|
const updateAddressMutation = useMutation({
|
|
71
74
|
mutationFn: (data) => expressPaymentResource.updateCheckoutSessionAddress(checkoutSessionId, { data }),
|
|
72
|
-
onSuccess: () => {
|
|
73
|
-
// Invalidate checkout query to
|
|
74
|
-
|
|
75
|
+
onSuccess: async () => {
|
|
76
|
+
// Invalidate and wait for checkout query to refetch
|
|
77
|
+
await queryClient.invalidateQueries({ queryKey: ['checkout'] });
|
|
78
|
+
// Small delay to ensure parent's query has time to refetch
|
|
79
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
75
80
|
},
|
|
76
81
|
});
|
|
77
82
|
// Update customer email mutation
|
|
@@ -79,35 +84,38 @@ export const ExpressPaymentMethodsProvider = ({ children, customerId, checkout,
|
|
|
79
84
|
mutationFn: (data) => expressPaymentResource.updateCustomerEmail(customerId, { data }),
|
|
80
85
|
});
|
|
81
86
|
// Recompute order summary (refetch after updates)
|
|
87
|
+
// Uses current checkout prop and refetches shipping rates
|
|
82
88
|
const reComputeOrderSummary = useCallback(async () => {
|
|
83
89
|
try {
|
|
84
|
-
//
|
|
85
|
-
await queryClient.invalidateQueries({ queryKey: ['checkout'] });
|
|
90
|
+
// Only refetch shipping rates - checkout is managed by parent
|
|
86
91
|
await refetchRates();
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
// Use current checkout prop (will be updated by parent after address mutation)
|
|
93
|
+
const currentSummary = checkout?.summary;
|
|
94
|
+
if (!currentSummary || !shippingRates) {
|
|
95
|
+
console.error('[ExpressProvider] Missing data:', {
|
|
96
|
+
hasSummary: !!currentSummary,
|
|
97
|
+
hasShippingRates: !!shippingRates,
|
|
98
|
+
checkout,
|
|
99
|
+
});
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
94
102
|
const recomputedLineItems = [
|
|
95
103
|
{
|
|
96
104
|
label: 'Subtotal',
|
|
97
|
-
amount: minorUnitsToCurrencyString(
|
|
105
|
+
amount: minorUnitsToCurrencyString(currentSummary.subtotalAdjustedAmount, currentSummary.currency),
|
|
98
106
|
},
|
|
99
107
|
{
|
|
100
108
|
label: 'Shipping',
|
|
101
|
-
amount: minorUnitsToCurrencyString(
|
|
109
|
+
amount: minorUnitsToCurrencyString(currentSummary.shippingCost ?? 0, currentSummary.currency),
|
|
102
110
|
},
|
|
103
111
|
{
|
|
104
112
|
label: 'Tax',
|
|
105
|
-
amount: minorUnitsToCurrencyString(
|
|
113
|
+
amount: minorUnitsToCurrencyString(currentSummary.totalTaxAmount, currentSummary.currency),
|
|
106
114
|
},
|
|
107
115
|
];
|
|
108
116
|
const total = {
|
|
109
117
|
label: 'Order Total',
|
|
110
|
-
amount: minorUnitsToCurrencyString(
|
|
118
|
+
amount: minorUnitsToCurrencyString(currentSummary.totalAdjustedAmount, currentSummary.currency),
|
|
111
119
|
};
|
|
112
120
|
const recomputedShippingMethods = (shippingRates || []).map((rate) => ({
|
|
113
121
|
label: rate.shippingRateName,
|
|
@@ -125,7 +133,7 @@ export const ExpressPaymentMethodsProvider = ({ children, customerId, checkout,
|
|
|
125
133
|
console.error('Error recomputing order summary:', e);
|
|
126
134
|
return undefined;
|
|
127
135
|
}
|
|
128
|
-
}, [
|
|
136
|
+
}, [refetchRates, shippingRates, minorUnitsToCurrencyString, checkout]);
|
|
129
137
|
// Update checkout session values
|
|
130
138
|
const updateCheckoutSessionValues = useCallback(async (input) => {
|
|
131
139
|
await updateAddressMutation.mutateAsync(input.data);
|
|
@@ -37,11 +37,11 @@ const InitializationLoader = () => (_jsxs("div", { style: {
|
|
|
37
37
|
borderTop: '1.5px solid #9ca3af',
|
|
38
38
|
borderRadius: '50%',
|
|
39
39
|
animation: 'tagada-spin 1s linear infinite',
|
|
40
|
-
} }), _jsx("span", { children: "Loading..." }), _jsx("style", { children: `
|
|
41
|
-
@keyframes tagada-spin {
|
|
42
|
-
0% { transform: rotate(0deg); }
|
|
43
|
-
100% { transform: rotate(360deg); }
|
|
44
|
-
}
|
|
40
|
+
} }), _jsx("span", { children: "Loading..." }), _jsx("style", { children: `
|
|
41
|
+
@keyframes tagada-spin {
|
|
42
|
+
0% { transform: rotate(0deg); }
|
|
43
|
+
100% { transform: rotate(360deg); }
|
|
44
|
+
}
|
|
45
45
|
` })] }));
|
|
46
46
|
const TagadaContext = createContext(null);
|
|
47
47
|
export function TagadaProvider({ children, environment, customApiConfig, debugMode, localConfig, blockUntilSessionReady = false, rawPluginConfig, features, funnelId, autoInitializeFunnel = true, onNavigate, onFunnelError, debugScripts = [], }) {
|
|
@@ -248,8 +248,8 @@ export function TagadaProvider({ children, environment, customApiConfig, debugMo
|
|
|
248
248
|
window.location.href = result.url;
|
|
249
249
|
}
|
|
250
250
|
};
|
|
251
|
-
const next = async (event) => {
|
|
252
|
-
const result = await client.funnel.navigate(event);
|
|
251
|
+
const next = async (event, options) => {
|
|
252
|
+
const result = await client.funnel.navigate(event, options);
|
|
253
253
|
handleNavigationResult(result);
|
|
254
254
|
return result;
|
|
255
255
|
};
|
|
@@ -71,6 +71,8 @@ export interface NavigateOptions {
|
|
|
71
71
|
eventData?: Record<string, unknown>;
|
|
72
72
|
/** Override return URL */
|
|
73
73
|
returnUrl?: string;
|
|
74
|
+
/** Disable auto-redirect to control navigation manually (default: true) */
|
|
75
|
+
autoRedirect?: boolean;
|
|
74
76
|
}
|
|
75
77
|
declare class TagadaExternalTracker {
|
|
76
78
|
private config;
|
|
@@ -135,15 +135,18 @@ class TagadaExternalTracker {
|
|
|
135
135
|
throw new Error('Tracker not initialized. Call init() first.');
|
|
136
136
|
}
|
|
137
137
|
log(this.config.debug, '🚀 Navigating:', options);
|
|
138
|
+
// Determine if we should auto-redirect (default: true)
|
|
139
|
+
const shouldAutoRedirect = options.autoRedirect !== false;
|
|
138
140
|
try {
|
|
139
141
|
const result = await this.client.funnel.navigate({
|
|
140
142
|
type: options.eventType,
|
|
141
143
|
data: options.eventData || {},
|
|
142
|
-
}
|
|
144
|
+
}, { autoRedirect: false } // Always disable SDK auto-redirect, we handle it here
|
|
145
|
+
);
|
|
143
146
|
if (result?.url) {
|
|
144
147
|
log(this.config.debug, '✅ Navigation result:', result.url);
|
|
145
|
-
//
|
|
146
|
-
if (typeof window !== 'undefined') {
|
|
148
|
+
// Only redirect if autoRedirect is enabled
|
|
149
|
+
if (shouldAutoRedirect && typeof window !== 'undefined') {
|
|
147
150
|
window.location.href = options.returnUrl || result.url;
|
|
148
151
|
}
|
|
149
152
|
return { url: result.url };
|