@tagadapay/plugin-sdk 2.6.16 → 2.6.18

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.
@@ -30,10 +30,14 @@ export interface RawPluginConfig<TConfig = Record<string, any>> {
30
30
  basePath?: string;
31
31
  config?: TConfig;
32
32
  }
33
+ export interface LocalDevConfig {
34
+ storeId: string;
35
+ accountId: string;
36
+ basePath: string;
37
+ }
33
38
  /**
34
39
  * Load plugin configuration (cached)
35
- * Tries raw config first, then production config
36
- * Note: Local dev config should be passed via rawConfig parameter
40
+ * Tries raw config first, then local dev config, then production config
37
41
  */
38
42
  export declare const loadPluginConfig: (configVariant?: string, rawConfig?: RawPluginConfig) => Promise<PluginConfig>;
39
43
  /**
@@ -22,6 +22,88 @@ import { useTagadaContext } from '../providers/TagadaProvider';
22
22
  // Simple cache for plugin configuration
23
23
  let cachedConfig = null;
24
24
  let configPromise = null;
25
+ /**
26
+ * Load local development configuration
27
+ * Combines .local.json + config/default.tgd.json (or specified variant)
28
+ */
29
+ const loadLocalDevConfig = async (configVariant = 'default') => {
30
+ try {
31
+ // Only try to load local config in development
32
+ // Use hostname-based detection for better Vite compatibility
33
+ const isLocalDev = typeof window !== 'undefined' &&
34
+ (window.location.hostname === 'localhost' ||
35
+ window.location.hostname.includes('ngrok-free.app') ||
36
+ window.location.hostname.includes('.localhost') ||
37
+ window.location.hostname.includes('127.0.0.1'));
38
+ if (!isLocalDev) {
39
+ return null;
40
+ }
41
+ // Load local store/account config
42
+ const localResponse = await fetch('/.local.json');
43
+ if (!localResponse.ok) {
44
+ return null;
45
+ }
46
+ const localConfig = await localResponse.json();
47
+ // Load deployment config (specified variant or fallback to default)
48
+ let config = {};
49
+ let configLoaded = false;
50
+ try {
51
+ // Try .tgd.json first (new format), then fallback to .json
52
+ let deploymentResponse = await fetch(`/config/${configVariant}.tgd.json`);
53
+ if (!deploymentResponse.ok) {
54
+ deploymentResponse = await fetch(`/config/${configVariant}.json`);
55
+ }
56
+ if (deploymentResponse.ok) {
57
+ config = await deploymentResponse.json();
58
+ configLoaded = true;
59
+ }
60
+ }
61
+ catch {
62
+ // Config fetch failed, will try fallback
63
+ }
64
+ // If config didn't load and it's not 'default', try fallback to default
65
+ if (!configLoaded && configVariant !== 'default') {
66
+ console.warn(`⚠️ Config variant '${configVariant}' not found, falling back to 'default'`);
67
+ try {
68
+ let defaultResponse = await fetch('/config/default.tgd.json');
69
+ if (!defaultResponse.ok) {
70
+ defaultResponse = await fetch('/config/default.json');
71
+ }
72
+ if (defaultResponse.ok) {
73
+ config = await defaultResponse.json();
74
+ configLoaded = true;
75
+ console.log(`✅ Fallback to 'default' config successful`);
76
+ }
77
+ }
78
+ catch {
79
+ // Default config also failed
80
+ }
81
+ }
82
+ // Final warning if no config was loaded
83
+ if (!configLoaded) {
84
+ if (configVariant === 'default') {
85
+ console.warn(`⚠️ No 'default' config found. Create /config/default.tgd.json`);
86
+ }
87
+ else {
88
+ console.warn(`⚠️ Neither '${configVariant}' nor 'default' config found. Create /config/default.tgd.json`);
89
+ }
90
+ }
91
+ console.log('🛠️ Using local development plugin config:', {
92
+ configName: config.configName || configVariant,
93
+ local: localConfig,
94
+ deployment: config
95
+ });
96
+ return {
97
+ storeId: localConfig.storeId,
98
+ accountId: localConfig.accountId,
99
+ basePath: localConfig.basePath,
100
+ config,
101
+ };
102
+ }
103
+ catch {
104
+ return null;
105
+ }
106
+ };
25
107
  /**
26
108
  * Load production config from headers and meta tags
27
109
  */
@@ -53,18 +135,12 @@ const loadProductionConfig = async () => {
53
135
  };
54
136
  /**
55
137
  * Load plugin configuration (cached)
56
- * Tries raw config first, then production config
57
- * Note: Local dev config should be passed via rawConfig parameter
138
+ * Tries raw config first, then local dev config, then production config
58
139
  */
59
140
  export const loadPluginConfig = async (configVariant = 'default', rawConfig) => {
60
141
  // If raw config is provided, use it directly
61
142
  if (rawConfig) {
62
- console.log('🛠️ Using raw plugin config:', {
63
- hasStoreId: !!rawConfig.storeId,
64
- hasAccountId: !!rawConfig.accountId,
65
- hasConfig: !!rawConfig.config,
66
- configKeys: rawConfig.config ? Object.keys(rawConfig.config) : [],
67
- });
143
+ console.log('🛠️ Using raw plugin config:', rawConfig);
68
144
  return {
69
145
  storeId: rawConfig.storeId,
70
146
  accountId: rawConfig.accountId,
@@ -72,7 +148,12 @@ export const loadPluginConfig = async (configVariant = 'default', rawConfig) =>
72
148
  config: rawConfig.config ?? {},
73
149
  };
74
150
  }
75
- // Fall back to production config (from headers/meta tags)
151
+ // Try local development config
152
+ const localConfig = await loadLocalDevConfig(configVariant);
153
+ if (localConfig) {
154
+ return localConfig;
155
+ }
156
+ // Fall back to production config
76
157
  return loadProductionConfig();
77
158
  };
78
159
  /**
@@ -20,6 +20,5 @@ export type { ShippingRate, ShippingRatesResponse } from './core/resources/shipp
20
20
  export type { ApplyDiscountResponse, Discount, DiscountCodeValidation, RemoveDiscountResponse } from './core/resources/discounts';
21
21
  export type { ToggleOrderBumpResponse, VipOffer, VipPreviewResponse } from './core/resources/vipOffers';
22
22
  export type { StoreConfig } from './core/resources/storeConfig';
23
- export type { FunnelContextUpdateRequest, FunnelContextUpdateResponse, FunnelEvent, FunnelInitializeRequest, FunnelInitializeResponse, FunnelNavigateRequest, FunnelNavigateResponse, FunnelNavigationAction, FunnelNavigationResult, SimpleFunnelContext } from './core/resources/funnel';
24
- export { ApplePayButton, ExpressPaymentMethodsProvider, formatMoney, getAvailableLanguages, GooglePayButton, queryKeys, TagadaProvider, useApiMutation, useApiQuery, useCheckout, useCheckoutToken, useCountryOptions, useCurrency, useDiscount, useDiscounts, useExpressPaymentMethods, useFunnel, useGeoLocation, useGoogleAutocomplete, useInvalidateQuery, useISOData, useLanguageImport, useOffers, useOrder, useOrderBump, usePayment, usePluginConfig, usePostPurchases, usePreloadQuery, useProducts, usePromotions, useRegionOptions, useShippingRates, useSimpleFunnel, useStoreConfig, useTagadaContext, useThreeds, useThreedsModal, useTranslation, useVipOffers } from './react';
25
- export type { StoreDiscount, TranslateFunction, TranslationText, UseTranslationOptions, UseTranslationResult } from './react';
23
+ export type { FunnelEvent, FunnelNavigationAction, FunnelNavigationResult, SimpleFunnelContext, FunnelInitializeRequest, FunnelInitializeResponse, FunnelNavigateRequest, FunnelNavigateResponse, FunnelContextUpdateRequest, FunnelContextUpdateResponse } from './core/resources/funnel';
24
+ export { ApplePayButton, ExpressPaymentMethodsProvider, formatMoney, getAvailableLanguages, GooglePayButton, queryKeys, TagadaProvider, useApiMutation, useApiQuery, useCheckout, useCheckoutToken, useCountryOptions, useCurrency, useDiscounts, useExpressPaymentMethods, useGeoLocation, useGoogleAutocomplete, useInvalidateQuery, useISOData, useLanguageImport, useOffers, useOrder, useOrderBump, usePayment, usePluginConfig, usePostPurchases, usePreloadQuery, useProducts, usePromotions, useRegionOptions, useShippingRates, useStoreConfig, useTagadaContext, useThreeds, useThreedsModal, useVipOffers, useFunnel, useSimpleFunnel } from './react';
package/dist/v2/index.js CHANGED
@@ -12,4 +12,4 @@ export * from './core/utils/currency';
12
12
  export * from './core/utils/pluginConfig';
13
13
  export * from './core/utils/products';
14
14
  // React exports (hooks and components only, types are exported above)
15
- export { ApplePayButton, ExpressPaymentMethodsProvider, formatMoney, getAvailableLanguages, GooglePayButton, queryKeys, TagadaProvider, useApiMutation, useApiQuery, useCheckout, useCheckoutToken, useCountryOptions, useCurrency, useDiscount, useDiscounts, useExpressPaymentMethods, useFunnel, useGeoLocation, useGoogleAutocomplete, useInvalidateQuery, useISOData, useLanguageImport, useOffers, useOrder, useOrderBump, usePayment, usePluginConfig, usePostPurchases, usePreloadQuery, useProducts, usePromotions, useRegionOptions, useShippingRates, useSimpleFunnel, useStoreConfig, useTagadaContext, useThreeds, useThreedsModal, useTranslation, useVipOffers } from './react';
15
+ export { ApplePayButton, ExpressPaymentMethodsProvider, formatMoney, getAvailableLanguages, GooglePayButton, queryKeys, TagadaProvider, useApiMutation, useApiQuery, useCheckout, useCheckoutToken, useCountryOptions, useCurrency, useDiscounts, useExpressPaymentMethods, useGeoLocation, useGoogleAutocomplete, useInvalidateQuery, useISOData, useLanguageImport, useOffers, useOrder, useOrderBump, usePayment, usePluginConfig, usePostPurchases, usePreloadQuery, useProducts, usePromotions, useRegionOptions, useShippingRates, useStoreConfig, useTagadaContext, useThreeds, useThreedsModal, useVipOffers, useFunnel, useSimpleFunnel } from './react';
@@ -12,11 +12,9 @@ export { useGeoLocation } from './hooks/useGeoLocation';
12
12
  export { useGoogleAutocomplete } from './hooks/useGoogleAutocomplete';
13
13
  export { getAvailableLanguages, useCountryOptions, useISOData, useLanguageImport, useRegionOptions } from './hooks/useISOData';
14
14
  export { usePluginConfig } from './hooks/usePluginConfig';
15
- export { useTranslation } from './hooks/useTranslation';
16
15
  export { queryKeys, useApiMutation, useApiQuery, useInvalidateQuery, usePreloadQuery } from './hooks/useApiQuery';
17
16
  export { useCheckoutQuery as useCheckout } from './hooks/useCheckoutQuery';
18
17
  export { useCurrency } from './hooks/useCurrency';
19
- export { useDiscountQuery as useDiscount } from './hooks/useDiscountQuery';
20
18
  export { useDiscountsQuery as useDiscounts } from './hooks/useDiscountsQuery';
21
19
  export { useOffersQuery as useOffers } from './hooks/useOffersQuery';
22
20
  export { useOrderBumpQuery as useOrderBump } from './hooks/useOrderBumpQuery';
@@ -39,11 +37,8 @@ export type { GeoLocationData, UseGeoLocationOptions, UseGeoLocationReturn } fro
39
37
  export type { ExtractedAddress, GooglePlaceDetails, GooglePrediction, UseGoogleAutocompleteOptions, UseGoogleAutocompleteResult } from './hooks/useGoogleAutocomplete';
40
38
  export type { ISOCountry, ISORegion, UseISODataResult } from './hooks/useISOData';
41
39
  export type { UsePluginConfigOptions, UsePluginConfigResult } from './hooks/usePluginConfig';
42
- export type { TranslateFunction, TranslationText, UseTranslationOptions, UseTranslationResult } from './hooks/useTranslation';
43
40
  export type { UseCheckoutQueryOptions as UseCheckoutOptions, UseCheckoutQueryResult as UseCheckoutResult } from './hooks/useCheckoutQuery';
44
- export type { StoreDiscount, UseDiscountQueryOptions as UseDiscountOptions, UseDiscountQueryResult as UseDiscountResult } from './hooks/useDiscountQuery';
45
41
  export type { UseDiscountsQueryOptions as UseDiscountsOptions, UseDiscountsQueryResult as UseDiscountsResult } from './hooks/useDiscountsQuery';
46
- export type { FunnelEvent, FunnelNavigationAction, FunnelNavigationResult, SimpleFunnelContext, UseFunnelOptions, UseFunnelResult } from './hooks/useFunnel';
47
42
  export type { UseOffersQueryOptions as UseOffersOptions, UseOffersQueryResult as UseOffersResult } from './hooks/useOffersQuery';
48
43
  export type { UseOrderBumpQueryOptions as UseOrderBumpOptions, UseOrderBumpQueryResult as UseOrderBumpResult } from './hooks/useOrderBumpQuery';
49
44
  export type { UseOrderQueryOptions as UseOrderOptions, UseOrderQueryResult as UseOrderResult } from './hooks/useOrderQuery';
@@ -55,5 +50,6 @@ export type { UseShippingRatesQueryOptions as UseShippingRatesOptions, UseShippi
55
50
  export type { UseStoreConfigQueryOptions as UseStoreConfigOptions, UseStoreConfigQueryResult as UseStoreConfigResult } from './hooks/useStoreConfigQuery';
56
51
  export type { PaymentInstrument, ThreedsChallenge, ThreedsHook, ThreedsOptions, ThreedsProvider, ThreedsSession } from './hooks/useThreeds';
57
52
  export type { UseVipOffersQueryOptions as UseVipOffersOptions, UseVipOffersQueryResult as UseVipOffersResult } from './hooks/useVipOffersQuery';
53
+ export type { UseFunnelOptions, UseFunnelResult, FunnelEvent, FunnelNavigationAction, FunnelNavigationResult, SimpleFunnelContext } from './hooks/useFunnel';
58
54
  export { formatMoney } from '../../react/utils/money';
59
55
  export type OrderItem = import('../core/utils/order').OrderLineItem;
@@ -15,12 +15,10 @@ export { useGeoLocation } from './hooks/useGeoLocation';
15
15
  export { useGoogleAutocomplete } from './hooks/useGoogleAutocomplete';
16
16
  export { getAvailableLanguages, useCountryOptions, useISOData, useLanguageImport, useRegionOptions } from './hooks/useISOData';
17
17
  export { usePluginConfig } from './hooks/usePluginConfig';
18
- export { useTranslation } from './hooks/useTranslation';
19
18
  // TanStack Query hooks (recommended)
20
19
  export { queryKeys, useApiMutation, useApiQuery, useInvalidateQuery, usePreloadQuery } from './hooks/useApiQuery';
21
20
  export { useCheckoutQuery as useCheckout } from './hooks/useCheckoutQuery';
22
21
  export { useCurrency } from './hooks/useCurrency';
23
- export { useDiscountQuery as useDiscount } from './hooks/useDiscountQuery';
24
22
  export { useDiscountsQuery as useDiscounts } from './hooks/useDiscountsQuery';
25
23
  export { useOffersQuery as useOffers } from './hooks/useOffersQuery';
26
24
  export { useOrderBumpQuery as useOrderBump } from './hooks/useOrderBumpQuery';
@@ -1,6 +1,3 @@
1
- /**
2
- * TagadaProvider - Main provider component for the Tagada Pay React SDK
3
- */
4
1
  import { ReactNode } from 'react';
5
2
  import { PluginConfig, RawPluginConfig } from '../../../react/hooks/usePluginConfig';
6
3
  import { ApiService } from '../../../react/services/apiService';
@@ -52,11 +49,12 @@ interface TagadaProviderProps {
52
49
  environment?: Environment;
53
50
  customApiConfig?: Partial<EnvironmentConfig>;
54
51
  debugMode?: boolean;
52
+ localConfig?: string;
55
53
  blockUntilSessionReady?: boolean;
56
54
  rawPluginConfig?: RawPluginConfig;
57
55
  }
58
56
  export declare function TagadaProvider({ children, environment, customApiConfig, debugMode, // Remove default, will be set based on environment
59
- blockUntilSessionReady, // Default to new non-blocking behavior
57
+ localConfig, blockUntilSessionReady, // Default to new non-blocking behavior
60
58
  rawPluginConfig, }: TagadaProviderProps): import("react/jsx-runtime").JSX.Element;
61
59
  export declare function useTagadaContext(): TagadaContextValue;
62
60
  export {};
@@ -3,9 +3,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  /**
4
4
  * TagadaProvider - Main provider component for the Tagada Pay React SDK
5
5
  */
6
- import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from 'react';
7
6
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
8
- import { default as DebugDrawer } from '../components/DebugDrawer';
7
+ import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from 'react';
9
8
  import { detectEnvironment, getEnvironmentConfig } from '../../../react/config/environment';
10
9
  import { ApiService } from '../../../react/services/apiService';
11
10
  import { collectDeviceInfo, getBrowserLocale, getUrlParams } from '../../../react/utils/deviceInfo';
@@ -13,8 +12,9 @@ import { decodeJWTClient, isTokenExpired } from '../../../react/utils/jwtDecoder
13
12
  import { convertCurrency, formatMoney, formatMoneyWithoutSymbol, formatSimpleMoney, getCurrencyInfo, minorUnitsToMajorUnits, moneyStringOrNumberToMinorUnits, } from '../../../react/utils/money';
14
13
  import { clearClientToken, getClientToken, setClientToken } from '../../../react/utils/tokenStorage';
15
14
  import { ApiClient } from '../../core/resources/apiClient';
16
- import { setGlobalApiClient, getGlobalApiClientOrNull } from '../hooks/useApiQuery';
17
15
  import { loadPluginConfig } from '../../core/utils/pluginConfig';
16
+ import { default as DebugDrawer } from '../components/DebugDrawer';
17
+ import { setGlobalApiClient } from '../hooks/useApiQuery';
18
18
  // Professional, subtle loading component for initialization
19
19
  const InitializationLoader = () => (_jsxs("div", { style: {
20
20
  position: 'fixed',
@@ -52,7 +52,7 @@ const TagadaContext = createContext(null);
52
52
  let globalTagadaInstance = null;
53
53
  let globalTagadaInitialized = false;
54
54
  export function TagadaProvider({ children, environment, customApiConfig, debugMode, // Remove default, will be set based on environment
55
- blockUntilSessionReady = false, // Default to new non-blocking behavior
55
+ localConfig, blockUntilSessionReady = false, // Default to new non-blocking behavior
56
56
  rawPluginConfig, }) {
57
57
  // Instance tracking
58
58
  const [instanceId] = useState(() => {
@@ -81,13 +81,20 @@ rawPluginConfig, }) {
81
81
  }
82
82
  };
83
83
  }, [instanceId]);
84
+ // LOCAL DEV ONLY: Use localConfig override if in local development, otherwise use default
85
+ const isLocalDev = typeof window !== 'undefined' &&
86
+ (window.location.hostname === 'localhost' ||
87
+ window.location.hostname.includes('.localhost') ||
88
+ window.location.hostname.includes('127.0.0.1'));
89
+ const configVariant = isLocalDev ? localConfig || 'default' : 'default';
84
90
  // Debug logging (only log once during initial render)
85
91
  const hasLoggedRef = useRef(false);
86
92
  if (!hasLoggedRef.current) {
87
- console.log(`🔍 [TagadaProvider] Instance ${instanceId} Initializing with:`, {
93
+ console.log(`🔍 [TagadaProvider] Instance ${instanceId} Config Debug:`, {
88
94
  hostname: typeof window !== 'undefined' ? window.location.hostname : 'SSR',
89
- hasRawConfig: !!rawPluginConfig,
90
- rawConfigStoreId: rawPluginConfig?.storeId,
95
+ isLocalDev,
96
+ localConfig,
97
+ configVariant,
91
98
  });
92
99
  hasLoggedRef.current = true;
93
100
  }
@@ -95,10 +102,6 @@ rawPluginConfig, }) {
95
102
  // Initialize with raw config if available to avoid empty config during loading
96
103
  const [pluginConfig, setPluginConfig] = useState(() => {
97
104
  if (rawPluginConfig) {
98
- console.log('🛠️ [TagadaProvider] Using raw plugin config immediately:', {
99
- storeId: rawPluginConfig.storeId,
100
- accountId: rawPluginConfig.accountId,
101
- });
102
105
  return {
103
106
  storeId: rawPluginConfig.storeId,
104
107
  accountId: rawPluginConfig.accountId,
@@ -106,11 +109,9 @@ rawPluginConfig, }) {
106
109
  config: rawPluginConfig.config ?? {},
107
110
  };
108
111
  }
109
- console.log('⏳ [TagadaProvider] No raw config, will load from headers/files');
110
112
  return { basePath: '/', config: {} };
111
113
  });
112
114
  const [configLoading, setConfigLoading] = useState(!rawPluginConfig);
113
- const [apiClientReady, setApiClientReady] = useState(false);
114
115
  // Load plugin config on mount with the specified variant
115
116
  useEffect(() => {
116
117
  // Prevent multiple config loads
@@ -120,8 +121,8 @@ rawPluginConfig, }) {
120
121
  }
121
122
  const loadConfig = async () => {
122
123
  try {
123
- // Use the v2 core loadPluginConfig function (only used when no rawPluginConfig)
124
- const config = await loadPluginConfig('default', rawPluginConfig);
124
+ // Use the v2 core loadPluginConfig function
125
+ const config = await loadPluginConfig(configVariant, rawPluginConfig);
125
126
  // Ensure we have required store ID before proceeding
126
127
  if (!config.storeId) {
127
128
  console.warn('⚠️ No store ID found in plugin config. This may cause hooks to fail.');
@@ -132,7 +133,7 @@ rawPluginConfig, }) {
132
133
  accountId: config.accountId,
133
134
  basePath: config.basePath,
134
135
  hasConfig: !!config.config,
135
- source: rawPluginConfig ? 'raw' : 'headers/files',
136
+ source: rawPluginConfig ? 'raw' : 'file',
136
137
  });
137
138
  if (blockUntilSessionReady) {
138
139
  console.log('⏳ Blocking mode: Children will render after Phase 3 (session init) completes');
@@ -150,7 +151,7 @@ rawPluginConfig, }) {
150
151
  }
151
152
  };
152
153
  void loadConfig();
153
- }, [rawPluginConfig]);
154
+ }, [configVariant, rawPluginConfig]);
154
155
  // Extract store/account IDs from plugin config (only source now)
155
156
  const storeId = pluginConfig.storeId;
156
157
  const _accountId = pluginConfig.accountId;
@@ -206,8 +207,9 @@ rawPluginConfig, }) {
206
207
  }
207
208
  return service;
208
209
  });
209
- // Initialize TanStack Query API client (global setup only)
210
- useEffect(() => {
210
+ // Initialize TanStack Query API client synchronously during provider initialization
211
+ // This ensures the global client is available immediately for hooks that use it in useMemo
212
+ const [apiClient] = useState(() => {
211
213
  const client = new ApiClient({
212
214
  baseURL: environmentConfig.apiConfig.baseUrl,
213
215
  headers: {
@@ -216,25 +218,40 @@ rawPluginConfig, }) {
216
218
  });
217
219
  // Set the global client for TanStack Query hooks
218
220
  setGlobalApiClient(client);
219
- setApiClientReady(true);
220
- console.log('[SDK] ApiClient initialized with baseURL:', environmentConfig.apiConfig.baseUrl);
221
- }, [environmentConfig]);
221
+ // Check for existing token and set it immediately
222
+ const existingToken = getClientToken();
223
+ if (existingToken && !isTokenExpired(existingToken)) {
224
+ client.updateToken(existingToken);
225
+ console.log('[SDK] ApiClient initialized with existing token:', existingToken.substring(0, 8) + '...');
226
+ }
227
+ else {
228
+ console.log('[SDK] ApiClient initialized with baseURL:', environmentConfig.apiConfig.baseUrl);
229
+ }
230
+ return client;
231
+ });
232
+ // Update API client when environment config changes
233
+ useEffect(() => {
234
+ apiClient.updateConfig({
235
+ baseURL: environmentConfig.apiConfig.baseUrl,
236
+ headers: {
237
+ 'Content-Type': 'application/json',
238
+ },
239
+ });
240
+ console.log('[SDK] ApiClient config updated with baseURL:', environmentConfig.apiConfig.baseUrl);
241
+ }, [environmentConfig, apiClient]);
222
242
  // Sync token updates between ApiService and ApiClient
223
243
  useEffect(() => {
224
- const client = getGlobalApiClientOrNull();
225
- if (client) {
226
- // Always use the token from ApiService as the source of truth
227
- const currentToken = apiService.getCurrentToken();
228
- if (currentToken && typeof currentToken === 'string') {
229
- client.updateToken(currentToken);
230
- console.log('[SDK] Token synced to ApiClient:', currentToken.substring(0, 8) + '...');
231
- }
232
- else {
233
- client.updateToken(null);
234
- console.log('[SDK] Token cleared from ApiClient');
235
- }
244
+ // Always use the token from ApiService as the source of truth
245
+ const currentToken = apiService.getCurrentToken();
246
+ if (currentToken && typeof currentToken === 'string') {
247
+ apiClient.updateToken(currentToken);
248
+ console.log('[SDK] Token synced to ApiClient:', currentToken.substring(0, 8) + '...');
236
249
  }
237
- }, [token, apiService]);
250
+ else {
251
+ apiClient.updateToken(null);
252
+ console.log('[SDK] Token cleared from ApiClient');
253
+ }
254
+ }, [token, apiService, apiClient]);
238
255
  // Update API service when config or token changes
239
256
  useEffect(() => {
240
257
  apiService.updateConfig(environmentConfig);
@@ -378,12 +395,6 @@ rawPluginConfig, }) {
378
395
  console.debug('[SDK][DEBUG] Auth state:', authState);
379
396
  }
380
397
  console.debug('[SDK] Session initialized successfully');
381
- // Update token to ApiClient immediately
382
- const client = getGlobalApiClientOrNull();
383
- if (client && token) {
384
- client.updateToken(token);
385
- console.log('[SDK] Token updated to ApiClient:', token.substring(0, 8) + '...');
386
- }
387
398
  setIsInitialized(true);
388
399
  setIsSessionInitialized(true); // Mark CMS session as ready
389
400
  setIsLoading(false);
@@ -425,6 +436,10 @@ rawPluginConfig, }) {
425
436
  setClientToken(response.token);
426
437
  // Update the API service with the new token
427
438
  apiService.updateToken(response.token);
439
+ // IMPORTANT: Immediately sync token to API client before marking session as ready
440
+ // This ensures any queries that become enabled after isSessionInitialized=true have the token
441
+ apiClient.updateToken(response.token);
442
+ console.log('[SDK] Token immediately synced to ApiClient:', response.token.substring(0, 8) + '...');
428
443
  // Decode the token to get session data
429
444
  const decodedSession = decodeJWTClient(response.token);
430
445
  if (decodedSession) {
@@ -441,12 +456,6 @@ rawPluginConfig, }) {
441
456
  setIsLoading(false);
442
457
  }
443
458
  console.log('[SDK] ✅ Phase 3 Complete - Session initialization completed successfully');
444
- // Update token to ApiClient immediately
445
- const client = getGlobalApiClientOrNull();
446
- if (client && token) {
447
- client.updateToken(token);
448
- console.log('[SDK] Token updated to ApiClient:', token.substring(0, 8) + '...');
449
- }
450
459
  setIsSessionInitialized(true); // Mark CMS session as ready
451
460
  }
452
461
  catch (error) {
@@ -508,6 +517,9 @@ rawPluginConfig, }) {
508
517
  setToken(tokenToUse);
509
518
  // Update the API service with the token
510
519
  apiService.updateToken(tokenToUse);
520
+ // IMPORTANT: Immediately sync token to API client
521
+ apiClient.updateToken(tokenToUse);
522
+ console.log('[SDK] Token immediately synced to ApiClient:', tokenToUse.substring(0, 8) + '...');
511
523
  // Decode token to get session data
512
524
  const decodedSession = decodeJWTClient(tokenToUse);
513
525
  if (decodedSession) {
@@ -669,16 +681,9 @@ rawPluginConfig, }) {
669
681
  // debugCheckout removed from deps to prevent unnecessary re-renders
670
682
  ]);
671
683
  // Determine if we should show loading
672
- // Always block until BOTH config is loaded AND API client is ready
673
- const shouldShowLoading = configLoading || !apiClientReady;
674
- const canRenderChildren = !configLoading && apiClientReady;
675
- if (!hasLoggedRef.current && canRenderChildren) {
676
- console.log('✅ [TagadaProvider] All initialization complete, ready to render children:', {
677
- configLoading,
678
- apiClientReady,
679
- hasStoreId: !!pluginConfig.storeId,
680
- });
681
- }
684
+ // Always block until config is loaded (even if empty)
685
+ const shouldShowLoading = configLoading;
686
+ const canRenderChildren = !configLoading;
682
687
  // Initialize TanStack Query client
683
688
  const [queryClient] = useState(() => new QueryClient({
684
689
  defaultOptions: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tagadapay/plugin-sdk",
3
- "version": "2.6.16",
3
+ "version": "2.6.18",
4
4
  "description": "Modern React SDK for building Tagada Pay plugins",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",