@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.
Files changed (76) hide show
  1. package/build-cdn.js +274 -6
  2. package/dist/external-tracker.js +236 -3906
  3. package/dist/external-tracker.min.js +2 -25
  4. package/dist/external-tracker.min.js.map +4 -4
  5. package/dist/react/config/payment.d.ts +14 -4
  6. package/dist/react/config/payment.js +47 -9
  7. package/dist/react/hooks/useCheckout.d.ts +3 -0
  8. package/dist/react/hooks/useCheckout.js +4 -1
  9. package/dist/react/hooks/usePluginConfig.js +9 -10
  10. package/dist/react/providers/TagadaProvider.js +1 -1
  11. package/dist/tagada-react-sdk-minimal.min.js +36 -0
  12. package/dist/tagada-react-sdk-minimal.min.js.map +7 -0
  13. package/dist/tagada-react-sdk.js +37821 -0
  14. package/dist/tagada-react-sdk.min.js +78 -0
  15. package/dist/tagada-react-sdk.min.js.map +7 -0
  16. package/dist/tagada-sdk.js +10309 -6331
  17. package/dist/tagada-sdk.min.js +4 -22
  18. package/dist/tagada-sdk.min.js.map +4 -4
  19. package/dist/v2/cdn-react-minimal.d.ts +23 -0
  20. package/dist/v2/cdn-react-minimal.js +26 -0
  21. package/dist/v2/core/client.js +1 -1
  22. package/dist/v2/core/config/environment.js +2 -1
  23. package/dist/v2/core/funnelClient.d.ts +98 -10
  24. package/dist/v2/core/funnelClient.js +121 -27
  25. package/dist/v2/core/index.d.ts +0 -1
  26. package/dist/v2/core/index.js +0 -2
  27. package/dist/v2/core/isoData.d.ts +4 -4
  28. package/dist/v2/core/isoData.js +7 -7
  29. package/dist/v2/core/pixelMapping.js +64 -26
  30. package/dist/v2/core/resources/checkout.d.ts +10 -0
  31. package/dist/v2/core/resources/checkout.js +6 -0
  32. package/dist/v2/core/resources/expressPaymentMethods.d.ts +1 -0
  33. package/dist/v2/core/resources/payments.d.ts +7 -2
  34. package/dist/v2/core/resources/payments.js +1 -0
  35. package/dist/v2/core/resources/postPurchases.d.ts +17 -0
  36. package/dist/v2/core/resources/postPurchases.js +20 -0
  37. package/dist/v2/core/utils/deviceInfo.d.ts +0 -10
  38. package/dist/v2/core/utils/deviceInfo.js +152 -76
  39. package/dist/v2/core/utils/order.d.ts +2 -0
  40. package/dist/v2/core/utils/pluginConfig.js +18 -22
  41. package/dist/v2/index.d.ts +4 -3
  42. package/dist/v2/index.js +4 -2
  43. package/dist/v2/react/components/FunnelScriptInjector.js +145 -77
  44. package/dist/v2/react/components/StripeExpressButton.d.ts +13 -0
  45. package/dist/v2/react/components/StripeExpressButton.js +171 -0
  46. package/dist/v2/react/components/WhopCheckout.js +7 -1
  47. package/dist/v2/react/hooks/payment-actions/useProcessorAuthAction.js +21 -3
  48. package/dist/v2/react/hooks/useApplePayCheckout.js +8 -8
  49. package/dist/v2/react/hooks/useCheckoutQuery.d.ts +10 -0
  50. package/dist/v2/react/hooks/useCheckoutQuery.js +21 -13
  51. package/dist/v2/react/hooks/useFunnel.d.ts +15 -4
  52. package/dist/v2/react/hooks/useFunnel.js +8 -4
  53. package/dist/v2/react/hooks/useGoogleAutocomplete.d.ts +2 -0
  54. package/dist/v2/react/hooks/useGoogleAutocomplete.js +29 -15
  55. package/dist/v2/react/hooks/useISOData.d.ts +2 -5
  56. package/dist/v2/react/hooks/useISOData.js +25 -26
  57. package/dist/v2/react/hooks/usePaymentPolling.d.ts +2 -2
  58. package/dist/v2/react/hooks/usePixelTracking.js +151 -70
  59. package/dist/v2/react/hooks/usePostPurchasesQuery.js +34 -2
  60. package/dist/v2/react/hooks/useRemappableParams.d.ts +2 -6
  61. package/dist/v2/react/hooks/useRemappableParams.js +23 -23
  62. package/dist/v2/react/hooks/useSetPaymentMethod.d.ts +16 -0
  63. package/dist/v2/react/hooks/useSetPaymentMethod.js +33 -0
  64. package/dist/v2/react/hooks/useStepConfig.d.ts +23 -6
  65. package/dist/v2/react/hooks/useStepConfig.js +14 -7
  66. package/dist/v2/react/hooks/useTranslation.js +23 -8
  67. package/dist/v2/react/index.d.ts +8 -1
  68. package/dist/v2/react/index.js +3 -0
  69. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.d.ts +1 -0
  70. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +9 -1
  71. package/dist/v2/react/providers/TagadaProvider.js +4 -4
  72. package/dist/v2/standalone/index.d.ts +21 -3
  73. package/dist/v2/standalone/index.js +25 -3
  74. package/dist/v2/standalone/payment-service.d.ts +134 -0
  75. package/dist/v2/standalone/payment-service.js +928 -0
  76. 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('Session initialization timeout. Please refresh the page and try again.'));
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('Failed to initialize checkout resource: ' + (error instanceof Error ? error.message : 'Unknown 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('No checkout session available');
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('No checkout session available');
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('No checkout session available');
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('No checkout session available');
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('No checkout session available');
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('No checkout session available');
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('No checkout session available');
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('No checkout session available');
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('No checkout session available');
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, static resources, etc.)
18
- * const offerId = stepConfig.staticResources?.offer;
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
- * Static resources assigned to this step/variant
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, static resources, etc.)
18
- * const offerId = stepConfig.staticResources?.offer;
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, getAssignedScripts, getAssignedStaticResources, getAssignedStepConfig } from '../../core/funnelClient';
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
- staticResources: getAssignedStaticResources(),
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
- // Inject Google Maps script
20
- useEffect(() => {
21
- if (!apiKey) {
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 with dynamic language loading
22
- * @param language - Language code (supports: en, ru, de, fr, es, zh, hi, pt, ja, ar, it, he)
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
- // Import the pre-built ISO data functions
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 with dynamic language loading
6
- * @param language - Language code (supports: en, ru, de, fr, es, zh, hi, pt, ja, ar, it, he)
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
- // Auto-import language if not registered and autoImport is true
10
+ // Fetch geodata from CDN (countries + regions) for the requested language
15
11
  useEffect(() => {
16
- if (!isLanguageRegistered(language) && autoImport && language !== 'en') {
17
- importLanguage(language)
18
- .then(() => {
12
+ let cancelled = false;
13
+ ensureGeoDataLoaded(language)
14
+ .then(() => {
15
+ if (!cancelled) {
19
16
  setIsLanguageLoaded(true);
20
17
  setRegisteredLanguages(getRegisteredLanguages());
21
- })
22
- .catch((error) => {
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 []; // Return empty array if no regions
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;