@tagadapay/plugin-sdk 2.7.16 → 2.7.20

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.
@@ -70,12 +70,22 @@ export interface FunnelNavigateRequest {
70
70
  * Automatically extracted from event.currentUrl or window.location.href
71
71
  */
72
72
  currentUrl?: string;
73
+ /**
74
+ * Funnel ID for session recovery when session expires
75
+ * If session is not found and funnelId is provided, a new session will be created
76
+ */
77
+ funnelId?: string;
73
78
  }
74
79
  export interface FunnelNavigateResponse {
75
80
  success: boolean;
76
81
  result?: {
77
82
  stepId: string;
78
83
  url?: string;
84
+ /**
85
+ * New session ID if session was recovered (expired/removed)
86
+ * SDK should update its sessionId if this is present
87
+ */
88
+ sessionId?: string;
79
89
  tracking?: {
80
90
  from: string;
81
91
  to: string;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Resolves a value from multiple possible environment sources (Node, Vite, CRA, Next, window)
3
+ * using a list of supported prefixes.
4
+ */
5
+ export declare function resolveEnvValue(key: string, prefixes?: string[]): string | undefined;
@@ -0,0 +1,20 @@
1
+ const DEFAULT_PREFIXES = ['', 'VITE_', 'REACT_APP_', 'NEXT_PUBLIC_'];
2
+ /**
3
+ * Resolves a value from multiple possible environment sources (Node, Vite, CRA, Next, window)
4
+ * using a list of supported prefixes.
5
+ */
6
+ export function resolveEnvValue(key, prefixes = DEFAULT_PREFIXES) {
7
+ for (const prefix of prefixes) {
8
+ const envKey = prefix ? `${prefix}${key}` : key;
9
+ if (typeof process !== 'undefined' && process?.env?.[envKey]) {
10
+ return process.env[envKey];
11
+ }
12
+ if (typeof import.meta !== 'undefined' && import.meta?.env?.[envKey]) {
13
+ return import.meta.env[envKey];
14
+ }
15
+ if (typeof window !== 'undefined' && window?.__TAGADA_ENV__?.[envKey]) {
16
+ return window.__TAGADA_ENV__[envKey];
17
+ }
18
+ }
19
+ return undefined;
20
+ }
@@ -32,26 +32,12 @@ export declare const loadPluginConfig: (configVariant?: string, rawConfig?: RawP
32
32
  * Returns the config object that can be passed to TagadaProvider
33
33
  */
34
34
  export declare function loadLocalConfig(configName?: string, defaultConfig?: any): Promise<Record<string, unknown> | null>;
35
- export interface CreateRawPluginConfigOptions {
36
- config?: any;
37
- }
38
35
  /**
39
36
  * Creates a RawPluginConfig object from provided options
40
37
  * @param options - Configuration options including storeId, accountId, basePath, configName, or a direct config object
41
38
  * @returns A RawPluginConfig object or undefined if required fields are missing
42
39
  */
43
- export declare function createRawPluginConfig(options?: CreateRawPluginConfigOptions): Promise<RawPluginConfig | undefined>;
44
- /**
45
- * Simple helper to get local plugin config with minimal boilerplate
46
- * Returns null if not in development or config not found
47
- *
48
- * @example
49
- * const config = await localConfigIfAny('blue');
50
- * if (config) {
51
- * console.log('Config loaded:', config);
52
- * }
53
- */
54
- export declare function localConfigIfAny(configName?: string): Promise<RawPluginConfig | null>;
40
+ export declare function createRawPluginConfig(): Promise<RawPluginConfig | undefined>;
55
41
  export declare class PluginConfigUtils {
56
42
  /**
57
43
  * Get plugin configuration from various sources
@@ -2,6 +2,7 @@
2
2
  * Plugin Configuration Utility Functions
3
3
  * Pure functions for plugin configuration management
4
4
  */
5
+ import { resolveEnvValue } from './env';
5
6
  /**
6
7
  * Load local development configuration
7
8
  */
@@ -138,7 +139,6 @@ const loadProductionConfig = async () => {
138
139
  export const loadPluginConfig = async (configVariant = 'default', rawConfig) => {
139
140
  // If raw config is provided, use it directly
140
141
  if (rawConfig) {
141
- console.log('🛠️ Using raw plugin config:', rawConfig);
142
142
  return {
143
143
  storeId: rawConfig.storeId,
144
144
  accountId: rawConfig.accountId,
@@ -146,6 +146,12 @@ export const loadPluginConfig = async (configVariant = 'default', rawConfig) =>
146
146
  config: rawConfig.config ?? {},
147
147
  };
148
148
  }
149
+ else {
150
+ const rawConfig = await createRawPluginConfig();
151
+ if (rawConfig) {
152
+ return rawConfig;
153
+ }
154
+ }
149
155
  // Try local development config
150
156
  const localConfig = await loadLocalDevConfig(configVariant);
151
157
  if (localConfig) {
@@ -202,33 +208,17 @@ export async function loadLocalConfig(configName = 'default', defaultConfig) {
202
208
  * @param options - Configuration options including storeId, accountId, basePath, configName, or a direct config object
203
209
  * @returns A RawPluginConfig object or undefined if required fields are missing
204
210
  */
205
- export async function createRawPluginConfig(options) {
211
+ export async function createRawPluginConfig() {
206
212
  try {
207
- const resolveEnv = (key) => {
208
- const prefixes = ['', 'VITE_', 'REACT_APP_', 'NEXT_PUBLIC_'];
209
- for (const prefix of prefixes) {
210
- const envKey = prefix ? `${prefix}${key}` : key;
211
- if (typeof process !== 'undefined' && process?.env?.[envKey]) {
212
- return process.env[envKey];
213
- }
214
- if (typeof import.meta !== 'undefined' && import.meta?.env?.[envKey]) {
215
- return import.meta.env[envKey];
216
- }
217
- if (typeof window !== 'undefined' && window?.__TAGADA_ENV__?.[envKey]) {
218
- return window.__TAGADA_ENV__[envKey];
219
- }
220
- }
221
- return undefined;
222
- };
223
- const storeId = resolveEnv('TAGADA_STORE_ID');
224
- const accountId = resolveEnv('TAGADA_ACCOUNT_ID');
225
- const basePath = resolveEnv('TAGADA_BASE_PATH');
226
- const configName = resolveEnv('TAGADA_CONFIG_NAME');
227
- if (!storeId) {
213
+ const storeId = resolveEnvValue('TAGADA_STORE_ID');
214
+ const accountId = resolveEnvValue('TAGADA_ACCOUNT_ID');
215
+ const basePath = resolveEnvValue('TAGADA_BASE_PATH');
216
+ const configName = resolveEnvValue('TAGADA_CONFIG_NAME');
217
+ if (!storeId || !accountId) {
228
218
  console.warn('[createRawPluginConfig] No storeId provided');
229
219
  return undefined;
230
220
  }
231
- const resolvedConfig = await loadLocalConfig(configName, options?.config);
221
+ const resolvedConfig = await loadLocalConfig(configName);
232
222
  if (!resolvedConfig) {
233
223
  console.warn('[createRawPluginConfig] No config found');
234
224
  return undefined;
@@ -252,34 +242,6 @@ export async function createRawPluginConfig(options) {
252
242
  return undefined;
253
243
  }
254
244
  }
255
- /**
256
- * Simple helper to get local plugin config with minimal boilerplate
257
- * Returns null if not in development or config not found
258
- *
259
- * @example
260
- * const config = await localConfigIfAny('blue');
261
- * if (config) {
262
- * console.log('Config loaded:', config);
263
- * }
264
- */
265
- export async function localConfigIfAny(configName) {
266
- try {
267
- // Only work in localhost
268
- const isLocalhost = typeof window !== 'undefined' &&
269
- (window.location.hostname === 'localhost' ||
270
- window.location.hostname.includes('.localhost') ||
271
- window.location.hostname.includes('127.0.0.1'));
272
- if (!isLocalhost) {
273
- return null;
274
- }
275
- const rawConfig = await createRawPluginConfig();
276
- return rawConfig || null;
277
- }
278
- catch (error) {
279
- console.warn('[localConfigIfAny] Failed to load config:', error);
280
- return null;
281
- }
282
- }
283
245
  export class PluginConfigUtils {
284
246
  /**
285
247
  * Get plugin configuration from various sources
@@ -22,7 +22,7 @@ export type { ApplyDiscountResponse, Discount, DiscountCodeValidation, RemoveDis
22
22
  export type { ToggleOrderBumpResponse, VipOffer, VipPreviewResponse } from './core/resources/vipOffers';
23
23
  export type { StoreConfig } from './core/resources/storeConfig';
24
24
  export type { FunnelContextUpdateRequest, FunnelContextUpdateResponse, FunnelEvent, FunnelInitializeRequest, FunnelInitializeResponse, FunnelNavigateRequest, FunnelNavigateResponse, FunnelNavigationAction, FunnelNavigationResult, SimpleFunnelContext } from './core/resources/funnel';
25
- export { ApplePayButton, ExpressPaymentMethodsProvider, formatMoney, getAvailableLanguages, GooglePayButton, queryKeys, TagadaProvider, useApiMutation, useApiQuery, useAuth, useCheckout, useCheckoutToken, useClubOffers, useCountryOptions, useCurrency, useCustomer, useCustomerInfos, useCustomerOrders, useCustomerSubscriptions, useDiscounts, useExpressPaymentMethods, useFunnel, useGeoLocation, useGoogleAutocomplete, useInvalidateQuery, useISOData, useLanguageImport, useLocalPluginConfig, useLogin, useOffers, useOrder, useOrderBump, usePayment, usePluginConfig, usePostPurchases, usePreloadQuery, useProducts, usePromotions, useRegionOptions, useRemappableParams, useShippingRates, useSimpleFunnel, useStoreConfig, useTagadaContext, useThreeds, useThreedsModal, useTranslation, useVipOffers } from './react';
25
+ export { ApplePayButton, ExpressPaymentMethodsProvider, formatMoney, getAvailableLanguages, GooglePayButton, queryKeys, TagadaProvider, useApiMutation, useApiQuery, useAuth, useCheckout, useCheckoutToken, useClubOffers, useCountryOptions, useCurrency, useCustomer, useCustomerInfos, useCustomerOrders, useCustomerSubscriptions, useDiscounts, useExpressPaymentMethods, useFunnel, useGeoLocation, useGoogleAutocomplete, useInvalidateQuery, useISOData, useLanguageImport, useLogin, useOffers, useOrder, useOrderBump, usePayment, usePluginConfig, usePostPurchases, usePreloadQuery, useProducts, usePromotions, useRegionOptions, useRemappableParams, useShippingRates, useSimpleFunnel, useStoreConfig, useTagadaContext, useThreeds, useThreedsModal, useTranslation, useVipOffers } from './react';
26
26
  export type { TranslateFunction, TranslationText, UseTranslationOptions, UseTranslationResult } from './react/hooks/useTranslation';
27
27
  export type { ClubOffer, ClubOfferItem, ClubOfferLineItem, ClubOfferSummary, UseClubOffersOptions, UseClubOffersResult } from './react/hooks/useClubOffers';
28
28
  export type { UseLoginOptions, UseLoginResult } from './react/hooks/useLogin';
package/dist/v2/index.js CHANGED
@@ -14,4 +14,4 @@ export * from './core/utils/products';
14
14
  // Path remapping helpers (framework-agnostic)
15
15
  export * from './core/pathRemapping';
16
16
  // React exports (hooks and components only, types are exported above)
17
- export { ApplePayButton, ExpressPaymentMethodsProvider, formatMoney, getAvailableLanguages, GooglePayButton, queryKeys, TagadaProvider, useApiMutation, useApiQuery, useAuth, useCheckout, useCheckoutToken, useClubOffers, useCountryOptions, useCurrency, useCustomer, useCustomerInfos, useCustomerOrders, useCustomerSubscriptions, useDiscounts, useExpressPaymentMethods, useFunnel, useGeoLocation, useGoogleAutocomplete, useInvalidateQuery, useISOData, useLanguageImport, useLocalPluginConfig, useLogin, useOffers, useOrder, useOrderBump, usePayment, usePluginConfig, usePostPurchases, usePreloadQuery, useProducts, usePromotions, useRegionOptions, useRemappableParams, useShippingRates, useSimpleFunnel, useStoreConfig, useTagadaContext, useThreeds, useThreedsModal, useTranslation, useVipOffers } from './react';
17
+ export { ApplePayButton, ExpressPaymentMethodsProvider, formatMoney, getAvailableLanguages, GooglePayButton, queryKeys, TagadaProvider, useApiMutation, useApiQuery, useAuth, useCheckout, useCheckoutToken, useClubOffers, useCountryOptions, useCurrency, useCustomer, useCustomerInfos, useCustomerOrders, useCustomerSubscriptions, useDiscounts, useExpressPaymentMethods, useFunnel, useGeoLocation, useGoogleAutocomplete, useInvalidateQuery, useISOData, useLanguageImport, useLogin, useOffers, useOrder, useOrderBump, usePayment, usePluginConfig, usePostPurchases, usePreloadQuery, useProducts, usePromotions, useRegionOptions, useRemappableParams, useShippingRates, useSimpleFunnel, useStoreConfig, useTagadaContext, useThreeds, useThreedsModal, useTranslation, useVipOffers } from './react';
@@ -156,7 +156,8 @@ export function useFunnel(options) {
156
156
  contextUpdates: {
157
157
  lastActivityAt: Date.now()
158
158
  },
159
- currentUrl // ✅ Send to backend for URL→Step mapping
159
+ currentUrl, // ✅ Send to backend for URL→Step mapping
160
+ funnelId: effectiveFunnelId || options.funnelId // ✅ Send for session recovery
160
161
  };
161
162
  const response = await funnelResource.navigate(requestBody);
162
163
  if (response.success && response.result) {
@@ -169,19 +170,32 @@ export function useFunnel(options) {
169
170
  onSuccess: (result) => {
170
171
  if (!context)
171
172
  return;
173
+ // 🔄 Handle session recovery (if backend created a new session)
174
+ let recoveredSessionId;
175
+ if (result.sessionId && result.sessionId !== context.sessionId) {
176
+ console.warn(`🔄 Funnel: Session recovered! Old: ${context.sessionId}, New: ${result.sessionId}`);
177
+ recoveredSessionId = result.sessionId;
178
+ }
172
179
  // Update local context
173
180
  const newContext = {
174
181
  ...context,
182
+ sessionId: recoveredSessionId || context.sessionId, // ✅ Use recovered session ID if provided
175
183
  currentStepId: result.stepId,
176
184
  previousStepId: context.currentStepId,
177
185
  lastActivityAt: Date.now(),
178
186
  metadata: {
179
187
  ...context.metadata,
180
188
  lastEvent: 'navigation',
181
- lastTransition: new Date().toISOString()
189
+ lastTransition: new Date().toISOString(),
190
+ ...(recoveredSessionId ? { recovered: true, oldSessionId: context.sessionId } : {})
182
191
  }
183
192
  };
184
193
  setContext(newContext);
194
+ // Update cookie with new session ID if recovered
195
+ if (recoveredSessionId) {
196
+ document.cookie = `funnelSessionId=${recoveredSessionId}; path=/; max-age=86400; SameSite=Lax`;
197
+ console.log(`🍪 Funnel: Updated cookie with recovered session ID: ${recoveredSessionId}`);
198
+ }
185
199
  // Create typed navigation result
186
200
  const navigationResult = {
187
201
  stepId: result.stepId,
@@ -18,7 +18,6 @@ export { useExpressPaymentMethods } from './hooks/useExpressPaymentMethods';
18
18
  export { useGeoLocation } from './hooks/useGeoLocation';
19
19
  export { useGoogleAutocomplete } from './hooks/useGoogleAutocomplete';
20
20
  export { getAvailableLanguages, useCountryOptions, useISOData, useLanguageImport, useRegionOptions } from './hooks/useISOData';
21
- export { useLocalPluginConfig } from './hooks/useLocalPluginConfig';
22
21
  export { useLogin } from './hooks/useLogin';
23
22
  export { usePluginConfig } from './hooks/usePluginConfig';
24
23
  export { useRemappableParams } from './hooks/useRemappableParams';
@@ -21,7 +21,6 @@ export { useExpressPaymentMethods } from './hooks/useExpressPaymentMethods';
21
21
  export { useGeoLocation } from './hooks/useGeoLocation';
22
22
  export { useGoogleAutocomplete } from './hooks/useGoogleAutocomplete';
23
23
  export { getAvailableLanguages, useCountryOptions, useISOData, useLanguageImport, useRegionOptions } from './hooks/useISOData';
24
- export { useLocalPluginConfig } from './hooks/useLocalPluginConfig';
25
24
  export { useLogin } from './hooks/useLogin';
26
25
  export { usePluginConfig } from './hooks/usePluginConfig';
27
26
  export { useRemappableParams } from './hooks/useRemappableParams';
@@ -12,6 +12,7 @@ import { decodeJWTClient, isTokenExpired } from '../../../react/utils/jwtDecoder
12
12
  import { convertCurrency, formatMoney, formatMoneyWithoutSymbol, formatSimpleMoney, getCurrencyInfo, minorUnitsToMajorUnits, moneyStringOrNumberToMinorUnits, } from '../../../react/utils/money';
13
13
  import { clearClientToken, getClientToken, setClientToken } from '../../../react/utils/tokenStorage';
14
14
  import { ApiClient } from '../../core/resources/apiClient';
15
+ import { resolveEnvValue } from '../../core/utils/env';
15
16
  import { loadPluginConfig } from '../../core/utils/pluginConfig';
16
17
  import { default as DebugDrawer } from '../components/DebugDrawer';
17
18
  import { setGlobalApiClient } from '../hooks/useApiQuery';
@@ -47,6 +48,15 @@ const InitializationLoader = () => (_jsxs("div", { style: {
47
48
  100% { transform: rotate(360deg); }
48
49
  }
49
50
  ` })] }));
51
+ const isEnvironment = (value) => value === 'production' || value === 'development' || value === 'local';
52
+ const resolveEnvironmentFromVariables = () => {
53
+ const envValue = resolveEnvValue('TAGADA_ENV') ?? resolveEnvValue('TAGADA_ENVIRONMENT');
54
+ if (!envValue) {
55
+ return undefined;
56
+ }
57
+ const normalized = envValue.trim().toLowerCase();
58
+ return isEnvironment(normalized) ? normalized : undefined;
59
+ };
50
60
  const TagadaContext = createContext(null);
51
61
  // Global instance tracking for TagadaProvider
52
62
  let globalTagadaInstance = null;
@@ -154,7 +164,6 @@ rawPluginConfig, }) {
154
164
  }, [configVariant, rawPluginConfig]);
155
165
  // Extract store/account IDs from plugin config (only source now)
156
166
  const storeId = pluginConfig.storeId;
157
- const _accountId = pluginConfig.accountId;
158
167
  const [isLoading, setIsLoading] = useState(true);
159
168
  const [isInitialized, setIsInitialized] = useState(false);
160
169
  const [token, setToken] = useState(null);
@@ -163,12 +172,16 @@ rawPluginConfig, }) {
163
172
  const isInitializing = useRef(false);
164
173
  // Initialize environment configuration
165
174
  const [environmentConfig, _setEnvironmentConfig] = useState(() => {
166
- const detectedEnv = environment || detectEnvironment();
175
+ const envFromVariables = resolveEnvironmentFromVariables();
176
+ const detectedEnv = environment || envFromVariables || detectEnvironment();
167
177
  const config = getEnvironmentConfig(detectedEnv);
168
178
  // Log environment detection for debugging
169
179
  if (environment) {
170
180
  console.log(`[TagadaSDK] Using explicit environment: ${environment}`);
171
181
  }
182
+ else if (envFromVariables) {
183
+ console.log(`[TagadaSDK] Using environment from env variables: ${envFromVariables}`);
184
+ }
172
185
  else {
173
186
  console.log(`[TagadaSDK] Auto-detected environment: ${detectedEnv} (${typeof window !== 'undefined' ? window.location.hostname : 'SSR'})`);
174
187
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tagadapay/plugin-sdk",
3
- "version": "2.7.16",
3
+ "version": "2.7.20",
4
4
  "description": "Modern React SDK for building Tagada Pay plugins",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,22 +0,0 @@
1
- import { RawPluginConfig } from '../../core/utils/pluginConfig';
2
- /**
3
- * Hook to load local plugin config with minimal boilerplate
4
- * Handles loading state and returns config when ready
5
- *
6
- * @example
7
- * function MyApp() {
8
- * const { config, isLoading } = useLocalPluginConfig();
9
- *
10
- * if (isLoading) return <div>Loading...</div>;
11
- *
12
- * return (
13
- * <TagadaProvider rawPluginConfig={config}>
14
- * <YourApp />
15
- * </TagadaProvider>
16
- * );
17
- * }
18
- */
19
- export declare function useLocalPluginConfig(): {
20
- config: RawPluginConfig | null;
21
- isLoading: boolean;
22
- };
@@ -1,30 +0,0 @@
1
- import { useEffect, useState } from 'react';
2
- import { localConfigIfAny } from '../../core/utils/pluginConfig';
3
- /**
4
- * Hook to load local plugin config with minimal boilerplate
5
- * Handles loading state and returns config when ready
6
- *
7
- * @example
8
- * function MyApp() {
9
- * const { config, isLoading } = useLocalPluginConfig();
10
- *
11
- * if (isLoading) return <div>Loading...</div>;
12
- *
13
- * return (
14
- * <TagadaProvider rawPluginConfig={config}>
15
- * <YourApp />
16
- * </TagadaProvider>
17
- * );
18
- * }
19
- */
20
- export function useLocalPluginConfig() {
21
- const [config, setConfig] = useState(null);
22
- const [isLoading, setIsLoading] = useState(true);
23
- useEffect(() => {
24
- localConfigIfAny().then((loadedConfig) => {
25
- setConfig(loadedConfig);
26
- setIsLoading(false);
27
- });
28
- }, []);
29
- return { config, isLoading };
30
- }