@tagadapay/plugin-sdk 3.1.22 → 3.1.25

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 (89) hide show
  1. package/build-cdn.js +274 -6
  2. package/dist/external-tracker.js +476 -6774
  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 +11 -3
  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 +37988 -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 +7847 -6420
  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 +2 -1
  22. package/dist/v2/core/config/environment.js +2 -1
  23. package/dist/v2/core/funnelClient.d.ts +106 -10
  24. package/dist/v2/core/funnelClient.js +122 -28
  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/apiClient.d.ts +18 -14
  31. package/dist/v2/core/resources/apiClient.js +151 -109
  32. package/dist/v2/core/resources/checkout.d.ts +10 -0
  33. package/dist/v2/core/resources/checkout.js +6 -0
  34. package/dist/v2/core/resources/expressPaymentMethods.d.ts +1 -0
  35. package/dist/v2/core/resources/index.d.ts +1 -1
  36. package/dist/v2/core/resources/index.js +1 -1
  37. package/dist/v2/core/resources/offers.js +4 -4
  38. package/dist/v2/core/resources/payments.d.ts +8 -2
  39. package/dist/v2/core/resources/payments.js +1 -0
  40. package/dist/v2/core/resources/postPurchases.d.ts +17 -0
  41. package/dist/v2/core/resources/postPurchases.js +20 -0
  42. package/dist/v2/core/utils/currency.d.ts +3 -0
  43. package/dist/v2/core/utils/currency.js +40 -2
  44. package/dist/v2/core/utils/deviceInfo.d.ts +1 -10
  45. package/dist/v2/core/utils/deviceInfo.js +153 -76
  46. package/dist/v2/core/utils/order.d.ts +2 -0
  47. package/dist/v2/core/utils/pluginConfig.js +18 -22
  48. package/dist/v2/core/utils/previewMode.js +12 -0
  49. package/dist/v2/index.d.ts +4 -3
  50. package/dist/v2/index.js +4 -2
  51. package/dist/v2/react/components/ApplePayButton.js +39 -16
  52. package/dist/v2/react/components/FunnelScriptInjector.js +145 -77
  53. package/dist/v2/react/components/StripeExpressButton.d.ts +13 -0
  54. package/dist/v2/react/components/StripeExpressButton.js +170 -0
  55. package/dist/v2/react/components/WhopCheckout.js +7 -1
  56. package/dist/v2/react/hooks/payment-actions/useAirwallexRadarAction.js +1 -0
  57. package/dist/v2/react/hooks/payment-actions/useProcessorAuthAction.js +21 -3
  58. package/dist/v2/react/hooks/useApiQuery.d.ts +1 -1
  59. package/dist/v2/react/hooks/useApiQuery.js +1 -1
  60. package/dist/v2/react/hooks/useApplePayCheckout.js +8 -8
  61. package/dist/v2/react/hooks/useCheckoutQuery.d.ts +10 -0
  62. package/dist/v2/react/hooks/useCheckoutQuery.js +27 -15
  63. package/dist/v2/react/hooks/useFunnel.d.ts +15 -4
  64. package/dist/v2/react/hooks/useFunnel.js +8 -4
  65. package/dist/v2/react/hooks/useGoogleAutocomplete.d.ts +2 -0
  66. package/dist/v2/react/hooks/useGoogleAutocomplete.js +29 -15
  67. package/dist/v2/react/hooks/useISOData.d.ts +2 -5
  68. package/dist/v2/react/hooks/useISOData.js +25 -26
  69. package/dist/v2/react/hooks/usePaymentPolling.d.ts +2 -2
  70. package/dist/v2/react/hooks/usePixelTracking.js +151 -70
  71. package/dist/v2/react/hooks/usePostPurchasesQuery.js +34 -2
  72. package/dist/v2/react/hooks/usePreviewOffer.js +1 -1
  73. package/dist/v2/react/hooks/useRemappableParams.d.ts +2 -6
  74. package/dist/v2/react/hooks/useRemappableParams.js +23 -23
  75. package/dist/v2/react/hooks/useSetPaymentMethod.d.ts +16 -0
  76. package/dist/v2/react/hooks/useSetPaymentMethod.js +33 -0
  77. package/dist/v2/react/hooks/useStepConfig.d.ts +23 -6
  78. package/dist/v2/react/hooks/useStepConfig.js +14 -7
  79. package/dist/v2/react/hooks/useTranslation.js +23 -8
  80. package/dist/v2/react/index.d.ts +8 -1
  81. package/dist/v2/react/index.js +3 -0
  82. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.d.ts +8 -0
  83. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +106 -10
  84. package/dist/v2/react/providers/TagadaProvider.js +5 -5
  85. package/dist/v2/standalone/index.d.ts +21 -3
  86. package/dist/v2/standalone/index.js +25 -3
  87. package/dist/v2/standalone/payment-service.d.ts +134 -0
  88. package/dist/v2/standalone/payment-service.js +929 -0
  89. 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
@@ -73,10 +80,14 @@ export function useCheckoutQuery(options = {}) {
73
80
  }, [providedToken, internalToken]);
74
81
  // Use provided token or internal token
75
82
  const checkoutToken = providedToken || internalToken;
83
+ // Only send currency to the backend when explicitly set via URL param or persisted storage.
84
+ // When not explicit, the backend uses the session's own selectedPresentmentCurrency --
85
+ // the frontend then reads the currency from the response, never guesses.
86
+ const explicitCurrency = currency.isExplicit ? currency.code : undefined;
76
87
  // Main checkout query
77
88
  const { data: checkout, isLoading, error, isSuccess, refetch, } = useQuery({
78
- queryKey: ['checkout', checkoutToken, currency.code],
79
- queryFn: () => checkoutResource.getCheckout(checkoutToken, currency.code),
89
+ queryKey: ['checkout', checkoutToken, explicitCurrency],
90
+ queryFn: () => checkoutResource.getCheckout(checkoutToken, explicitCurrency),
80
91
  enabled: enabled && !!checkoutToken && isSessionInitialized,
81
92
  staleTime: 30000, // 30 seconds
82
93
  refetchOnWindowFocus: false,
@@ -90,16 +101,17 @@ export function useCheckoutQuery(options = {}) {
90
101
  // Initialize checkout mutation (async mode for fast response)
91
102
  const initMutation = useMutation({
92
103
  mutationFn: async (params) => {
104
+ const enabledOrderBumpOfferIds = params.enabledOrderBumpOfferIds ?? getAssignedOrderBumpOfferIds();
93
105
  const requestBody = {
94
106
  ...params,
95
107
  storeId: params.storeId || storeId,
96
108
  returnUrl: params.returnUrl || window.location.origin,
97
- // Include customerId from session to prevent duplicate customer creation
98
109
  customerId: params.customerId || session?.customerId,
99
110
  customer: {
100
111
  ...params.customer,
101
112
  currency: params.customer?.currency ?? currency.code,
102
113
  },
114
+ ...(enabledOrderBumpOfferIds && { enabledOrderBumpOfferIds }),
103
115
  };
104
116
  // Use async mode for fast response (~50ms vs 2-5s)
105
117
  const asyncResponse = await checkoutResource.initCheckoutAsync(requestBody);
@@ -125,7 +137,7 @@ export function useCheckoutQuery(options = {}) {
125
137
  const replaceSessionLineItemsMutation = useMutation({
126
138
  mutationFn: ({ lineItems }) => {
127
139
  if (!checkout?.checkoutSession?.id) {
128
- throw new Error('No checkout session available');
140
+ throw new Error(messages.noCheckoutSession);
129
141
  }
130
142
  return checkoutResource.replaceSessionLineItems(checkout.checkoutSession.id, lineItems);
131
143
  },
@@ -139,7 +151,7 @@ export function useCheckoutQuery(options = {}) {
139
151
  const lineItemsMutation = useMutation({
140
152
  mutationFn: ({ lineItems }) => {
141
153
  if (!checkout?.checkoutSession?.id) {
142
- throw new Error('No checkout session available');
154
+ throw new Error(messages.noCheckoutSession);
143
155
  }
144
156
  return checkoutResource.updateLineItems(checkout.checkoutSession.id, lineItems);
145
157
  },
@@ -184,7 +196,7 @@ export function useCheckoutQuery(options = {}) {
184
196
  const addLineItemsMutation = useMutation({
185
197
  mutationFn: ({ lineItems }) => {
186
198
  if (!checkout?.checkoutSession?.id) {
187
- throw new Error('No checkout session available');
199
+ throw new Error(messages.noCheckoutSession);
188
200
  }
189
201
  return checkoutResource.addLineItems(checkout.checkoutSession.id, lineItems);
190
202
  },
@@ -198,7 +210,7 @@ export function useCheckoutQuery(options = {}) {
198
210
  const removeLineItemsMutation = useMutation({
199
211
  mutationFn: ({ lineItems }) => {
200
212
  if (!checkout?.checkoutSession?.id) {
201
- throw new Error('No checkout session available');
213
+ throw new Error(messages.noCheckoutSession);
202
214
  }
203
215
  return checkoutResource.removeLineItems(checkout.checkoutSession.id, lineItems);
204
216
  },
@@ -212,7 +224,7 @@ export function useCheckoutQuery(options = {}) {
212
224
  const quantityMutation = useMutation({
213
225
  mutationFn: ({ variantId, quantity, priceId }) => {
214
226
  if (!checkout?.checkoutSession?.id) {
215
- throw new Error('No checkout session available');
227
+ throw new Error(messages.noCheckoutSession);
216
228
  }
217
229
  return checkoutResource.setItemQuantity(checkout.checkoutSession.id, variantId, quantity, priceId);
218
230
  },
@@ -226,7 +238,7 @@ export function useCheckoutQuery(options = {}) {
226
238
  const customerMutation = useMutation({
227
239
  mutationFn: (data) => {
228
240
  if (!checkout?.checkoutSession?.id) {
229
- throw new Error('No checkout session available');
241
+ throw new Error(messages.noCheckoutSession);
230
242
  }
231
243
  return checkoutResource.updateCustomer(checkout.checkoutSession.id, data);
232
244
  },
@@ -240,7 +252,7 @@ export function useCheckoutQuery(options = {}) {
240
252
  const customerAndSessionMutation = useMutation({
241
253
  mutationFn: (data) => {
242
254
  if (!checkout?.checkoutSession?.id) {
243
- throw new Error('No checkout session available');
255
+ throw new Error(messages.noCheckoutSession);
244
256
  }
245
257
  return checkoutResource.updateCustomerAndSessionInfo(checkout.checkoutSession.id, data);
246
258
  },
@@ -254,7 +266,7 @@ export function useCheckoutQuery(options = {}) {
254
266
  const promotionMutation = useMutation({
255
267
  mutationFn: ({ code }) => {
256
268
  if (!checkout?.checkoutSession?.id) {
257
- throw new Error('No checkout session available');
269
+ throw new Error(messages.noCheckoutSession);
258
270
  }
259
271
  return checkoutResource.applyPromotionCode(checkout.checkoutSession.id, code);
260
272
  },
@@ -271,7 +283,7 @@ export function useCheckoutQuery(options = {}) {
271
283
  const removePromotionMutation = useMutation({
272
284
  mutationFn: ({ promotionId }) => {
273
285
  if (!checkout?.checkoutSession?.id) {
274
- throw new Error('No checkout session available');
286
+ throw new Error(messages.noCheckoutSession);
275
287
  }
276
288
  return checkoutResource.removePromotion(checkout.checkoutSession.id, promotionId);
277
289
  },
@@ -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;