@tagadapay/plugin-sdk 3.1.22 → 3.1.24
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/build-cdn.js +274 -6
- package/dist/external-tracker.js +236 -3906
- package/dist/external-tracker.min.js +2 -25
- package/dist/external-tracker.min.js.map +4 -4
- package/dist/react/config/payment.d.ts +14 -4
- package/dist/react/config/payment.js +47 -9
- package/dist/react/hooks/useCheckout.d.ts +3 -0
- package/dist/react/hooks/useCheckout.js +4 -1
- package/dist/react/hooks/usePluginConfig.js +9 -10
- package/dist/react/providers/TagadaProvider.js +1 -1
- package/dist/tagada-react-sdk-minimal.min.js +36 -0
- package/dist/tagada-react-sdk-minimal.min.js.map +7 -0
- package/dist/tagada-react-sdk.js +37821 -0
- package/dist/tagada-react-sdk.min.js +78 -0
- package/dist/tagada-react-sdk.min.js.map +7 -0
- package/dist/tagada-sdk.js +10309 -6331
- package/dist/tagada-sdk.min.js +4 -22
- package/dist/tagada-sdk.min.js.map +4 -4
- package/dist/v2/cdn-react-minimal.d.ts +23 -0
- package/dist/v2/cdn-react-minimal.js +26 -0
- package/dist/v2/core/client.js +1 -1
- package/dist/v2/core/config/environment.js +2 -1
- package/dist/v2/core/funnelClient.d.ts +98 -10
- package/dist/v2/core/funnelClient.js +121 -27
- package/dist/v2/core/index.d.ts +0 -1
- package/dist/v2/core/index.js +0 -2
- package/dist/v2/core/isoData.d.ts +4 -4
- package/dist/v2/core/isoData.js +7 -7
- package/dist/v2/core/pixelMapping.js +64 -26
- package/dist/v2/core/resources/checkout.d.ts +10 -0
- package/dist/v2/core/resources/checkout.js +6 -0
- package/dist/v2/core/resources/expressPaymentMethods.d.ts +1 -0
- package/dist/v2/core/resources/payments.d.ts +7 -2
- package/dist/v2/core/resources/payments.js +1 -0
- package/dist/v2/core/resources/postPurchases.d.ts +17 -0
- package/dist/v2/core/resources/postPurchases.js +20 -0
- package/dist/v2/core/utils/deviceInfo.d.ts +0 -10
- package/dist/v2/core/utils/deviceInfo.js +152 -76
- package/dist/v2/core/utils/order.d.ts +2 -0
- package/dist/v2/core/utils/pluginConfig.js +18 -22
- package/dist/v2/index.d.ts +4 -3
- package/dist/v2/index.js +4 -2
- package/dist/v2/react/components/FunnelScriptInjector.js +145 -77
- package/dist/v2/react/components/StripeExpressButton.d.ts +13 -0
- package/dist/v2/react/components/StripeExpressButton.js +171 -0
- package/dist/v2/react/components/WhopCheckout.js +7 -1
- package/dist/v2/react/hooks/payment-actions/useProcessorAuthAction.js +21 -3
- package/dist/v2/react/hooks/useApplePayCheckout.js +8 -8
- package/dist/v2/react/hooks/useCheckoutQuery.d.ts +10 -0
- package/dist/v2/react/hooks/useCheckoutQuery.js +21 -13
- package/dist/v2/react/hooks/useFunnel.d.ts +15 -4
- package/dist/v2/react/hooks/useFunnel.js +8 -4
- package/dist/v2/react/hooks/useGoogleAutocomplete.d.ts +2 -0
- package/dist/v2/react/hooks/useGoogleAutocomplete.js +29 -15
- package/dist/v2/react/hooks/useISOData.d.ts +2 -5
- package/dist/v2/react/hooks/useISOData.js +25 -26
- package/dist/v2/react/hooks/usePaymentPolling.d.ts +2 -2
- package/dist/v2/react/hooks/usePixelTracking.js +151 -70
- package/dist/v2/react/hooks/usePostPurchasesQuery.js +34 -2
- package/dist/v2/react/hooks/useRemappableParams.d.ts +2 -6
- package/dist/v2/react/hooks/useRemappableParams.js +23 -23
- package/dist/v2/react/hooks/useSetPaymentMethod.d.ts +16 -0
- package/dist/v2/react/hooks/useSetPaymentMethod.js +33 -0
- package/dist/v2/react/hooks/useStepConfig.d.ts +23 -6
- package/dist/v2/react/hooks/useStepConfig.js +14 -7
- package/dist/v2/react/hooks/useTranslation.js +23 -8
- package/dist/v2/react/index.d.ts +8 -1
- package/dist/v2/react/index.js +3 -0
- package/dist/v2/react/providers/ExpressPaymentMethodsProvider.d.ts +1 -0
- package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +9 -1
- package/dist/v2/react/providers/TagadaProvider.js +4 -4
- package/dist/v2/standalone/index.d.ts +21 -3
- package/dist/v2/standalone/index.js +25 -3
- package/dist/v2/standalone/payment-service.d.ts +134 -0
- package/dist/v2/standalone/payment-service.js +928 -0
- package/package.json +4 -2
|
@@ -5,12 +5,19 @@
|
|
|
5
5
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
6
6
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
7
7
|
import { CheckoutResource } from '../../core/resources/checkout';
|
|
8
|
+
import { getAssignedOrderBumpOfferIds } from '../../core/funnelClient';
|
|
8
9
|
import { useTagadaContext } from '../providers/TagadaProvider';
|
|
9
10
|
import { getGlobalApiClient } from './useApiQuery';
|
|
10
11
|
import { useCurrency } from './useCurrency';
|
|
11
12
|
import { usePluginConfig } from './usePluginConfig';
|
|
13
|
+
const DEFAULT_MESSAGES = {
|
|
14
|
+
sessionTimeout: 'Session initialization timeout. Please refresh the page and try again.',
|
|
15
|
+
initFailed: 'Failed to initialize checkout resource',
|
|
16
|
+
noCheckoutSession: 'No checkout session available',
|
|
17
|
+
};
|
|
12
18
|
export function useCheckoutQuery(options = {}) {
|
|
13
|
-
const { checkoutToken: providedToken, enabled = true } = options;
|
|
19
|
+
const { checkoutToken: providedToken, enabled = true, messages: userMessages } = options;
|
|
20
|
+
const messages = { ...DEFAULT_MESSAGES, ...userMessages };
|
|
14
21
|
const { storeId } = usePluginConfig();
|
|
15
22
|
const currency = useCurrency();
|
|
16
23
|
const queryClient = useQueryClient();
|
|
@@ -48,7 +55,7 @@ export function useCheckoutQuery(options = {}) {
|
|
|
48
55
|
if (sessionResolvers.current.size === 0) {
|
|
49
56
|
pendingSessionPromise.current = null;
|
|
50
57
|
}
|
|
51
|
-
reject(new Error(
|
|
58
|
+
reject(new Error(messages.sessionTimeout));
|
|
52
59
|
}
|
|
53
60
|
}, 10000);
|
|
54
61
|
});
|
|
@@ -60,7 +67,7 @@ export function useCheckoutQuery(options = {}) {
|
|
|
60
67
|
return new CheckoutResource(getGlobalApiClient());
|
|
61
68
|
}
|
|
62
69
|
catch (error) {
|
|
63
|
-
throw new Error(
|
|
70
|
+
throw new Error(messages.initFailed + ': ' + (error instanceof Error ? error.message : 'Unknown error'));
|
|
64
71
|
}
|
|
65
72
|
}, []);
|
|
66
73
|
// Internal token state that can be updated after init
|
|
@@ -90,16 +97,17 @@ export function useCheckoutQuery(options = {}) {
|
|
|
90
97
|
// Initialize checkout mutation (async mode for fast response)
|
|
91
98
|
const initMutation = useMutation({
|
|
92
99
|
mutationFn: async (params) => {
|
|
100
|
+
const enabledOrderBumpOfferIds = params.enabledOrderBumpOfferIds ?? getAssignedOrderBumpOfferIds();
|
|
93
101
|
const requestBody = {
|
|
94
102
|
...params,
|
|
95
103
|
storeId: params.storeId || storeId,
|
|
96
104
|
returnUrl: params.returnUrl || window.location.origin,
|
|
97
|
-
// Include customerId from session to prevent duplicate customer creation
|
|
98
105
|
customerId: params.customerId || session?.customerId,
|
|
99
106
|
customer: {
|
|
100
107
|
...params.customer,
|
|
101
108
|
currency: params.customer?.currency ?? currency.code,
|
|
102
109
|
},
|
|
110
|
+
...(enabledOrderBumpOfferIds && { enabledOrderBumpOfferIds }),
|
|
103
111
|
};
|
|
104
112
|
// Use async mode for fast response (~50ms vs 2-5s)
|
|
105
113
|
const asyncResponse = await checkoutResource.initCheckoutAsync(requestBody);
|
|
@@ -125,7 +133,7 @@ export function useCheckoutQuery(options = {}) {
|
|
|
125
133
|
const replaceSessionLineItemsMutation = useMutation({
|
|
126
134
|
mutationFn: ({ lineItems }) => {
|
|
127
135
|
if (!checkout?.checkoutSession?.id) {
|
|
128
|
-
throw new Error(
|
|
136
|
+
throw new Error(messages.noCheckoutSession);
|
|
129
137
|
}
|
|
130
138
|
return checkoutResource.replaceSessionLineItems(checkout.checkoutSession.id, lineItems);
|
|
131
139
|
},
|
|
@@ -139,7 +147,7 @@ export function useCheckoutQuery(options = {}) {
|
|
|
139
147
|
const lineItemsMutation = useMutation({
|
|
140
148
|
mutationFn: ({ lineItems }) => {
|
|
141
149
|
if (!checkout?.checkoutSession?.id) {
|
|
142
|
-
throw new Error(
|
|
150
|
+
throw new Error(messages.noCheckoutSession);
|
|
143
151
|
}
|
|
144
152
|
return checkoutResource.updateLineItems(checkout.checkoutSession.id, lineItems);
|
|
145
153
|
},
|
|
@@ -184,7 +192,7 @@ export function useCheckoutQuery(options = {}) {
|
|
|
184
192
|
const addLineItemsMutation = useMutation({
|
|
185
193
|
mutationFn: ({ lineItems }) => {
|
|
186
194
|
if (!checkout?.checkoutSession?.id) {
|
|
187
|
-
throw new Error(
|
|
195
|
+
throw new Error(messages.noCheckoutSession);
|
|
188
196
|
}
|
|
189
197
|
return checkoutResource.addLineItems(checkout.checkoutSession.id, lineItems);
|
|
190
198
|
},
|
|
@@ -198,7 +206,7 @@ export function useCheckoutQuery(options = {}) {
|
|
|
198
206
|
const removeLineItemsMutation = useMutation({
|
|
199
207
|
mutationFn: ({ lineItems }) => {
|
|
200
208
|
if (!checkout?.checkoutSession?.id) {
|
|
201
|
-
throw new Error(
|
|
209
|
+
throw new Error(messages.noCheckoutSession);
|
|
202
210
|
}
|
|
203
211
|
return checkoutResource.removeLineItems(checkout.checkoutSession.id, lineItems);
|
|
204
212
|
},
|
|
@@ -212,7 +220,7 @@ export function useCheckoutQuery(options = {}) {
|
|
|
212
220
|
const quantityMutation = useMutation({
|
|
213
221
|
mutationFn: ({ variantId, quantity, priceId }) => {
|
|
214
222
|
if (!checkout?.checkoutSession?.id) {
|
|
215
|
-
throw new Error(
|
|
223
|
+
throw new Error(messages.noCheckoutSession);
|
|
216
224
|
}
|
|
217
225
|
return checkoutResource.setItemQuantity(checkout.checkoutSession.id, variantId, quantity, priceId);
|
|
218
226
|
},
|
|
@@ -226,7 +234,7 @@ export function useCheckoutQuery(options = {}) {
|
|
|
226
234
|
const customerMutation = useMutation({
|
|
227
235
|
mutationFn: (data) => {
|
|
228
236
|
if (!checkout?.checkoutSession?.id) {
|
|
229
|
-
throw new Error(
|
|
237
|
+
throw new Error(messages.noCheckoutSession);
|
|
230
238
|
}
|
|
231
239
|
return checkoutResource.updateCustomer(checkout.checkoutSession.id, data);
|
|
232
240
|
},
|
|
@@ -240,7 +248,7 @@ export function useCheckoutQuery(options = {}) {
|
|
|
240
248
|
const customerAndSessionMutation = useMutation({
|
|
241
249
|
mutationFn: (data) => {
|
|
242
250
|
if (!checkout?.checkoutSession?.id) {
|
|
243
|
-
throw new Error(
|
|
251
|
+
throw new Error(messages.noCheckoutSession);
|
|
244
252
|
}
|
|
245
253
|
return checkoutResource.updateCustomerAndSessionInfo(checkout.checkoutSession.id, data);
|
|
246
254
|
},
|
|
@@ -254,7 +262,7 @@ export function useCheckoutQuery(options = {}) {
|
|
|
254
262
|
const promotionMutation = useMutation({
|
|
255
263
|
mutationFn: ({ code }) => {
|
|
256
264
|
if (!checkout?.checkoutSession?.id) {
|
|
257
|
-
throw new Error(
|
|
265
|
+
throw new Error(messages.noCheckoutSession);
|
|
258
266
|
}
|
|
259
267
|
return checkoutResource.applyPromotionCode(checkout.checkoutSession.id, code);
|
|
260
268
|
},
|
|
@@ -271,7 +279,7 @@ export function useCheckoutQuery(options = {}) {
|
|
|
271
279
|
const removePromotionMutation = useMutation({
|
|
272
280
|
mutationFn: ({ promotionId }) => {
|
|
273
281
|
if (!checkout?.checkoutSession?.id) {
|
|
274
|
-
throw new Error(
|
|
282
|
+
throw new Error(messages.noCheckoutSession);
|
|
275
283
|
}
|
|
276
284
|
return checkoutResource.removePromotion(checkout.checkoutSession.id, promotionId);
|
|
277
285
|
},
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
* // In any child component:
|
|
15
15
|
* const { context, next, isLoading, stepConfig } = useFunnel();
|
|
16
16
|
*
|
|
17
|
-
* // Access step-specific config (payment flows,
|
|
18
|
-
* const offerId = stepConfig.
|
|
17
|
+
* // Access step-specific config (payment flows, resources, etc.)
|
|
18
|
+
* const offerId = stepConfig.resources?.offer;
|
|
19
19
|
* const paymentFlowId = stepConfig.paymentFlowId;
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
@@ -35,10 +35,11 @@ export interface StepConfigValue {
|
|
|
35
35
|
*/
|
|
36
36
|
paymentFlowId: string | undefined;
|
|
37
37
|
/**
|
|
38
|
-
*
|
|
39
|
-
* For A/B tests, this contains the resources for the specific variant
|
|
38
|
+
* Resource bindings for this step/variant.
|
|
40
39
|
* e.g., { offer: 'offer_xxx', product: 'product_xxx' }
|
|
41
40
|
*/
|
|
41
|
+
resources: Record<string, string> | undefined;
|
|
42
|
+
/** @deprecated Use `resources` instead */
|
|
42
43
|
staticResources: Record<string, string> | undefined;
|
|
43
44
|
/**
|
|
44
45
|
* Get scripts for a specific injection position
|
|
@@ -54,6 +55,16 @@ export interface StepConfigValue {
|
|
|
54
55
|
[TrackingProvider.SNAPCHAT]?: SnapchatTrackingConfig[];
|
|
55
56
|
[TrackingProvider.GTM]?: GTMTrackingConfig[];
|
|
56
57
|
};
|
|
58
|
+
/**
|
|
59
|
+
* Enabled order bump offer IDs for this step.
|
|
60
|
+
* undefined = inherit all store bumps, string[] = only these IDs.
|
|
61
|
+
*/
|
|
62
|
+
orderBumpOfferIds: string[] | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Enabled upsell offer IDs for this step.
|
|
65
|
+
* undefined = inherit all store upsells, string[] = only these IDs.
|
|
66
|
+
*/
|
|
67
|
+
upsellOfferIds: string[] | undefined;
|
|
57
68
|
}
|
|
58
69
|
export interface FunnelContextValue extends FunnelState {
|
|
59
70
|
currentStep: {
|
|
@@ -14,13 +14,13 @@
|
|
|
14
14
|
* // In any child component:
|
|
15
15
|
* const { context, next, isLoading, stepConfig } = useFunnel();
|
|
16
16
|
*
|
|
17
|
-
* // Access step-specific config (payment flows,
|
|
18
|
-
* const offerId = stepConfig.
|
|
17
|
+
* // Access step-specific config (payment flows, resources, etc.)
|
|
18
|
+
* const offerId = stepConfig.resources?.offer;
|
|
19
19
|
* const paymentFlowId = stepConfig.paymentFlowId;
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
22
|
import { useMemo } from 'react';
|
|
23
|
-
import { TrackingProvider, getAssignedPaymentFlowId, getAssignedPixels,
|
|
23
|
+
import { TrackingProvider, getAssignedOrderBumpOfferIds, getAssignedPaymentFlowId, getAssignedPixels, getAssignedResources, getAssignedScripts, getAssignedStepConfig, getAssignedUpsellOfferIds, } from '../../core/funnelClient';
|
|
24
24
|
import { useTagadaContext } from '../providers/TagadaProvider';
|
|
25
25
|
/**
|
|
26
26
|
* Hook to access funnel state and methods
|
|
@@ -35,12 +35,16 @@ export function useFunnel() {
|
|
|
35
35
|
// Compute step config from HTML injection (memoized, computed once on mount)
|
|
36
36
|
const stepConfig = useMemo(() => {
|
|
37
37
|
const raw = getAssignedStepConfig();
|
|
38
|
+
const resources = getAssignedResources();
|
|
38
39
|
return {
|
|
39
40
|
raw,
|
|
40
41
|
paymentFlowId: getAssignedPaymentFlowId(),
|
|
41
|
-
|
|
42
|
+
resources,
|
|
43
|
+
staticResources: resources,
|
|
42
44
|
pixels: getAssignedPixels(),
|
|
43
45
|
getScripts: (position) => getAssignedScripts(position),
|
|
46
|
+
orderBumpOfferIds: getAssignedOrderBumpOfferIds(),
|
|
47
|
+
upsellOfferIds: getAssignedUpsellOfferIds(),
|
|
44
48
|
};
|
|
45
49
|
}, []);
|
|
46
50
|
return {
|
|
@@ -68,6 +68,8 @@ export interface UseGoogleAutocompleteOptions {
|
|
|
68
68
|
version?: string;
|
|
69
69
|
language?: string;
|
|
70
70
|
region?: string;
|
|
71
|
+
/** When true, defer loading the Google Maps script until searchPlaces() is first called. Default: false (eager load). */
|
|
72
|
+
defer?: boolean;
|
|
71
73
|
}
|
|
72
74
|
export interface UseGoogleAutocompleteResult {
|
|
73
75
|
predictions: GooglePrediction[];
|
|
@@ -8,7 +8,7 @@ import { useCallback, useEffect, useRef, useState } from 'react';
|
|
|
8
8
|
* Automatically loads the Google Maps JavaScript API with Places library
|
|
9
9
|
*/
|
|
10
10
|
export function useGoogleAutocomplete(options) {
|
|
11
|
-
const { apiKey, libraries = ['places'], version = 'weekly', language, region } = options;
|
|
11
|
+
const { apiKey, libraries = ['places'], version = 'weekly', language, region, defer = false } = options;
|
|
12
12
|
const [predictions, setPredictions] = useState([]);
|
|
13
13
|
const [isLoading, setIsLoading] = useState(false);
|
|
14
14
|
const [isScriptLoaded, setIsScriptLoaded] = useState(false);
|
|
@@ -16,19 +16,16 @@ export function useGoogleAutocomplete(options) {
|
|
|
16
16
|
const placesServiceRef = useRef(null);
|
|
17
17
|
const scriptLoadedRef = useRef(false);
|
|
18
18
|
const debounceTimeoutRef = useRef(null);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
19
|
+
const pendingSearchRef = useRef(null);
|
|
20
|
+
// Shared script injection logic (used by both eager useEffect and lazy trigger)
|
|
21
|
+
const injectScript = useCallback(() => {
|
|
24
22
|
if (scriptLoadedRef.current || window.google?.maps) {
|
|
25
23
|
setIsScriptLoaded(true);
|
|
24
|
+
scriptLoadedRef.current = true;
|
|
26
25
|
return;
|
|
27
26
|
}
|
|
28
|
-
// Check if script is already being loaded
|
|
29
27
|
const existingScript = document.querySelector('script[src*="maps.googleapis.com"]');
|
|
30
28
|
if (existingScript) {
|
|
31
|
-
// Wait for existing script to load
|
|
32
29
|
const checkLoaded = () => {
|
|
33
30
|
if (window.google?.maps?.places?.AutocompleteService) {
|
|
34
31
|
setIsScriptLoaded(true);
|
|
@@ -41,7 +38,6 @@ export function useGoogleAutocomplete(options) {
|
|
|
41
38
|
checkLoaded();
|
|
42
39
|
return;
|
|
43
40
|
}
|
|
44
|
-
// Create and inject the script
|
|
45
41
|
const script = document.createElement('script');
|
|
46
42
|
const params = new URLSearchParams({
|
|
47
43
|
key: apiKey,
|
|
@@ -63,12 +59,23 @@ export function useGoogleAutocomplete(options) {
|
|
|
63
59
|
// Failed to load Google Maps API
|
|
64
60
|
};
|
|
65
61
|
document.head.appendChild(script);
|
|
66
|
-
// Cleanup function
|
|
67
|
-
return () => {
|
|
68
|
-
// Note: We don't remove the script as it might be used by other components
|
|
69
|
-
// The script will remain in the DOM for the session
|
|
70
|
-
};
|
|
71
62
|
}, [apiKey, libraries, version, language, region]);
|
|
63
|
+
// Inject Google Maps script eagerly (unless defer is true)
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
if (!apiKey || defer) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
injectScript();
|
|
69
|
+
}, [apiKey, defer, injectScript]);
|
|
70
|
+
// When script finishes loading in deferred mode, replay the pending search
|
|
71
|
+
const searchPlacesRef = useRef(null);
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (isScriptLoaded && pendingSearchRef.current && searchPlacesRef.current) {
|
|
74
|
+
const { input, countryRestriction } = pendingSearchRef.current;
|
|
75
|
+
pendingSearchRef.current = null;
|
|
76
|
+
searchPlacesRef.current(input, countryRestriction);
|
|
77
|
+
}
|
|
78
|
+
}, [isScriptLoaded]);
|
|
72
79
|
// Initialize Google Places services
|
|
73
80
|
const initializeServices = useCallback(() => {
|
|
74
81
|
if (typeof window === 'undefined')
|
|
@@ -89,6 +96,11 @@ export function useGoogleAutocomplete(options) {
|
|
|
89
96
|
// Search for place predictions
|
|
90
97
|
const searchPlaces = useCallback((input, countryRestriction) => {
|
|
91
98
|
if (!isScriptLoaded) {
|
|
99
|
+
// When deferred, trigger lazy load and queue the search for replay
|
|
100
|
+
if (defer && !scriptLoadedRef.current) {
|
|
101
|
+
pendingSearchRef.current = { input, countryRestriction };
|
|
102
|
+
injectScript();
|
|
103
|
+
}
|
|
92
104
|
return;
|
|
93
105
|
}
|
|
94
106
|
if (!initializeServices()) {
|
|
@@ -124,7 +136,9 @@ export function useGoogleAutocomplete(options) {
|
|
|
124
136
|
}
|
|
125
137
|
});
|
|
126
138
|
}, 300); // 300ms debounce
|
|
127
|
-
}, [initializeServices, isScriptLoaded]);
|
|
139
|
+
}, [initializeServices, isScriptLoaded, defer, injectScript]);
|
|
140
|
+
// Keep ref in sync so the deferred-replay useEffect can call searchPlaces
|
|
141
|
+
searchPlacesRef.current = searchPlaces;
|
|
128
142
|
// Get detailed place information
|
|
129
143
|
const getPlaceDetails = useCallback((placeId) => {
|
|
130
144
|
return new Promise((resolve) => {
|
|
@@ -18,11 +18,8 @@ export interface UseISODataResult {
|
|
|
18
18
|
registeredLanguages: SupportedLanguage[];
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
|
-
* React hook for accessing ISO3166 countries and regions data
|
|
22
|
-
*
|
|
23
|
-
* @param autoImport - Whether to automatically import the language if not registered (default: true)
|
|
24
|
-
* @param disputeSetting - Territorial dispute perspective (currently only UN is supported)
|
|
25
|
-
* @returns Object with countries data and helper functions
|
|
21
|
+
* React hook for accessing ISO3166 countries and regions data.
|
|
22
|
+
* Fetches slim JSON from CDN on first use, then caches in memory.
|
|
26
23
|
*/
|
|
27
24
|
export declare function useISOData(language?: SupportedLanguage, autoImport?: boolean, disputeSetting?: string): UseISODataResult;
|
|
28
25
|
/**
|
|
@@ -1,35 +1,40 @@
|
|
|
1
1
|
import { useMemo, useEffect, useState, useCallback } from 'react';
|
|
2
|
-
|
|
3
|
-
import { getCountries, getStatesForCountry, importLanguage, isLanguageRegistered, getRegisteredLanguages } from '../../../data/iso3166';
|
|
2
|
+
import { getCountries, getStatesForCountry, ensureGeoDataLoaded, importLanguage, isLanguageRegistered, getRegisteredLanguages } from '../../../data/iso3166';
|
|
4
3
|
/**
|
|
5
|
-
* React hook for accessing ISO3166 countries and regions data
|
|
6
|
-
*
|
|
7
|
-
* @param autoImport - Whether to automatically import the language if not registered (default: true)
|
|
8
|
-
* @param disputeSetting - Territorial dispute perspective (currently only UN is supported)
|
|
9
|
-
* @returns Object with countries data and helper functions
|
|
4
|
+
* React hook for accessing ISO3166 countries and regions data.
|
|
5
|
+
* Fetches slim JSON from CDN on first use, then caches in memory.
|
|
10
6
|
*/
|
|
11
7
|
export function useISOData(language = 'en', autoImport = true, disputeSetting = 'UN') {
|
|
12
8
|
const [isLanguageLoaded, setIsLanguageLoaded] = useState(isLanguageRegistered(language));
|
|
13
9
|
const [registeredLanguages, setRegisteredLanguages] = useState(getRegisteredLanguages);
|
|
14
|
-
//
|
|
10
|
+
// Fetch geodata from CDN (countries + regions) for the requested language
|
|
15
11
|
useEffect(() => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
let cancelled = false;
|
|
13
|
+
ensureGeoDataLoaded(language)
|
|
14
|
+
.then(() => {
|
|
15
|
+
if (!cancelled) {
|
|
19
16
|
setIsLanguageLoaded(true);
|
|
20
17
|
setRegisteredLanguages(getRegisteredLanguages());
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
.catch((err) => {
|
|
21
|
+
console.error('[SDK] Failed to load geodata from CDN:', err);
|
|
22
|
+
});
|
|
23
|
+
return () => { cancelled = true; };
|
|
25
24
|
}, [language, autoImport]);
|
|
26
25
|
const data = useMemo(() => {
|
|
26
|
+
if (!isLanguageLoaded) {
|
|
27
|
+
return {
|
|
28
|
+
countries: {},
|
|
29
|
+
getRegions: () => [],
|
|
30
|
+
findRegion: () => null,
|
|
31
|
+
mapGoogleToISO: () => null,
|
|
32
|
+
isLanguageLoaded: false,
|
|
33
|
+
registeredLanguages,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
27
36
|
try {
|
|
28
|
-
console.log('[SDK] Loading ISO data for language:', language, 'autoImport:', autoImport);
|
|
29
|
-
// Get countries from pre-built data with language support
|
|
30
37
|
const countriesArray = getCountries(language);
|
|
31
|
-
console.log('[SDK] Loaded countries count:', countriesArray.length);
|
|
32
|
-
// Transform to our expected format (Record<string, ISOCountry>)
|
|
33
38
|
const countries = {};
|
|
34
39
|
countriesArray.forEach((country) => {
|
|
35
40
|
countries[country.code] = {
|
|
@@ -39,7 +44,6 @@ export function useISOData(language = 'en', autoImport = true, disputeSetting =
|
|
|
39
44
|
name: country.name,
|
|
40
45
|
};
|
|
41
46
|
});
|
|
42
|
-
// Helper to load regions for a specific country
|
|
43
47
|
const getRegions = (countryCode) => {
|
|
44
48
|
try {
|
|
45
49
|
const states = getStatesForCountry(countryCode, language);
|
|
@@ -49,31 +53,26 @@ export function useISOData(language = 'en', autoImport = true, disputeSetting =
|
|
|
49
53
|
}));
|
|
50
54
|
}
|
|
51
55
|
catch {
|
|
52
|
-
return [];
|
|
56
|
+
return [];
|
|
53
57
|
}
|
|
54
58
|
};
|
|
55
|
-
// Find a specific region by ISO code
|
|
56
59
|
const findRegion = (countryCode, regionCode) => {
|
|
57
60
|
const regions = getRegions(countryCode);
|
|
58
61
|
return regions.find((region) => region.iso === regionCode) ?? null;
|
|
59
62
|
};
|
|
60
|
-
// Map Google Places state to ISO region (proven 100% success rate)
|
|
61
63
|
const mapGoogleToISO = (googleState, googleStateLong, countryCode) => {
|
|
62
64
|
const regions = getRegions(countryCode);
|
|
63
65
|
if (regions.length === 0)
|
|
64
66
|
return null;
|
|
65
|
-
// Strategy 1: Exact ISO code match (86% success rate)
|
|
66
67
|
let match = regions.find((r) => r.iso === googleState);
|
|
67
68
|
if (match)
|
|
68
69
|
return match;
|
|
69
|
-
// Strategy 2: Name matching (14% success rate)
|
|
70
70
|
match = regions.find((r) => r.name.toLowerCase() === googleState.toLowerCase());
|
|
71
71
|
if (match)
|
|
72
72
|
return match;
|
|
73
73
|
match = regions.find((r) => r.name.toLowerCase() === googleStateLong.toLowerCase());
|
|
74
74
|
if (match)
|
|
75
75
|
return match;
|
|
76
|
-
// Strategy 3: Partial name matching (fallback)
|
|
77
76
|
match = regions.find((r) => r.name.toLowerCase().includes(googleStateLong.toLowerCase()) ||
|
|
78
77
|
googleStateLong.toLowerCase().includes(r.name.toLowerCase()));
|
|
79
78
|
return match ?? null;
|
|
@@ -2,9 +2,9 @@ export interface Payment {
|
|
|
2
2
|
id: string;
|
|
3
3
|
status: string;
|
|
4
4
|
subStatus: string;
|
|
5
|
-
requireAction: 'none' | 'redirect' | 'redirect_to_payment' | 'error' | 'radar';
|
|
5
|
+
requireAction: 'none' | 'redirect' | 'redirect_to_payment' | 'error' | 'radar' | 'stripe_express_checkout';
|
|
6
6
|
requireActionData?: {
|
|
7
|
-
type: 'redirect' | 'redirect_to_payment' | 'threeds_auth' | 'processor_auth' | 'error' | 'stripe_radar' | 'finix_radar' | 'radar' | 'kesspay_auth' | 'trustflow_auth' | 'mastercard_auth';
|
|
7
|
+
type: 'redirect' | 'redirect_to_payment' | 'threeds_auth' | 'processor_auth' | 'error' | 'stripe_radar' | 'finix_radar' | 'radar' | 'kesspay_auth' | 'trustflow_auth' | 'mastercard_auth' | 'stripe_express_checkout';
|
|
8
8
|
url?: string;
|
|
9
9
|
processed: boolean;
|
|
10
10
|
processorId?: string;
|