@tagadapay/plugin-sdk 2.4.34 → 2.4.36

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.
@@ -2,10 +2,23 @@
2
2
  * Plugin Configuration Hook
3
3
  *
4
4
  * Professional SDK approach to access plugin configuration:
5
+ * - Raw Config: Direct configuration passed via TagadaProvider (highest priority)
5
6
  * - Store ID, Account ID, Base Path: from .local.json (dev) or headers (production)
6
7
  * - Deployment Config: from config/*.json (dev) or meta tags (production)
8
+ *
9
+ * Usage:
10
+ * 1. Raw config (recommended for programmatic usage):
11
+ * <TagadaProvider rawPluginConfig={{ storeId: 'store_123', config: {...} }}>
12
+ *
13
+ * 2. File-based config (for development):
14
+ * - .local.json for store/account info
15
+ * - config/default.tgd.json for deployment config
16
+ *
17
+ * 3. Production config:
18
+ * - Headers for store/account info
19
+ * - Meta tags for deployment config
7
20
  */
8
- import { useTagadaContext } from '../providers/TagadaProvider';
21
+ import { useTagadaContextSafe } from '../providers/TagadaProvider';
9
22
  // Simple cache for plugin configuration
10
23
  let cachedConfig = null;
11
24
  let configPromise = null;
@@ -122,10 +135,20 @@ const loadProductionConfig = async () => {
122
135
  };
123
136
  /**
124
137
  * Load plugin configuration (cached)
125
- * Tries local dev config first, then production config
138
+ * Tries raw config first, then local dev config, then production config
126
139
  */
127
- export const loadPluginConfig = async (configVariant = 'default') => {
128
- // Try local development config first
140
+ export const loadPluginConfig = async (configVariant = 'default', rawConfig) => {
141
+ // If raw config is provided, use it directly
142
+ if (rawConfig) {
143
+ console.log('🛠️ Using raw plugin config:', rawConfig);
144
+ return {
145
+ storeId: rawConfig.storeId,
146
+ accountId: rawConfig.accountId,
147
+ basePath: rawConfig.basePath ?? '/',
148
+ config: rawConfig.config ?? {},
149
+ };
150
+ }
151
+ // Try local development config
129
152
  const localConfig = await loadLocalDevConfig(configVariant);
130
153
  if (localConfig) {
131
154
  return localConfig;
@@ -138,8 +161,26 @@ export const loadPluginConfig = async (configVariant = 'default') => {
138
161
  * Gets config from TagadaProvider context (no parameters needed)
139
162
  */
140
163
  export const usePluginConfig = () => {
141
- const context = useTagadaContext();
164
+ const context = useTagadaContextSafe();
165
+ // Log to confirm we're using the updated SDK
166
+ console.log('🔧 [SDK DEBUG] usePluginConfig called - using updated SDK version with safe context handling');
167
+ // If context is not ready yet, return loading state
168
+ if (!context) {
169
+ console.log('🔧 [SDK DEBUG] Context not ready yet, returning loading state');
170
+ return {
171
+ storeId: undefined,
172
+ accountId: undefined,
173
+ basePath: '/',
174
+ config: {},
175
+ loading: true,
176
+ };
177
+ }
142
178
  const { pluginConfig, pluginConfigLoading } = context;
179
+ console.log('🔧 [SDK DEBUG] Context ready, returning plugin config:', {
180
+ storeId: pluginConfig.storeId,
181
+ loading: pluginConfigLoading,
182
+ hasConfig: !!pluginConfig.config
183
+ });
143
184
  return {
144
185
  storeId: pluginConfig.storeId,
145
186
  accountId: pluginConfig.accountId,
@@ -158,14 +199,14 @@ export const useBasePath = () => {
158
199
  /**
159
200
  * Get cached config directly (for non-React usage)
160
201
  */
161
- export const getPluginConfig = async (configVariant = 'default') => {
202
+ export const getPluginConfig = async (configVariant = 'default', rawConfig) => {
162
203
  if (cachedConfig) {
163
204
  return cachedConfig;
164
205
  }
165
206
  if (configPromise) {
166
207
  return configPromise;
167
208
  }
168
- configPromise = loadPluginConfig(configVariant);
209
+ configPromise = loadPluginConfig(configVariant, rawConfig);
169
210
  const result = await configPromise;
170
211
  cachedConfig = result;
171
212
  configPromise = null;
@@ -181,7 +222,7 @@ export const clearPluginConfigCache = () => {
181
222
  /**
182
223
  * Development helper to log current configuration
183
224
  */
184
- export const debugPluginConfig = async (configVariant = 'default') => {
225
+ export const debugPluginConfig = async (configVariant = 'default', rawConfig) => {
185
226
  // Use hostname-based detection for better Vite compatibility
186
227
  const isLocalDev = typeof window !== 'undefined' &&
187
228
  (window.location.hostname === 'localhost' ||
@@ -191,7 +232,7 @@ export const debugPluginConfig = async (configVariant = 'default') => {
191
232
  if (!isLocalDev) {
192
233
  return;
193
234
  }
194
- const config = await getPluginConfig(configVariant);
235
+ const config = await getPluginConfig(configVariant, rawConfig);
195
236
  console.group('🔧 Plugin Configuration Debug');
196
237
  console.log('Store ID:', config.storeId || 'Not available');
197
238
  console.log('Account ID:', config.accountId || 'Not available');
@@ -7,9 +7,6 @@ export { useCheckout } from './hooks/useCheckout';
7
7
  export { useClubOffers } from './hooks/useClubOffers';
8
8
  export { useCurrency } from './hooks/useCurrency';
9
9
  export { useCustomer } from './hooks/useCustomer';
10
- export { useCustomerInfos } from './hooks/useCustomerInfos';
11
- export { useCustomerOrders } from './hooks/useCustomerOrders';
12
- export { useCustomerSubscriptions } from './hooks/useCustomerSubscriptions';
13
10
  export { useDiscounts } from './hooks/useDiscounts';
14
11
  export { useEnvironment } from './hooks/useEnvironment';
15
12
  export { useGeoLocation } from './hooks/useGeoLocation';
@@ -25,10 +22,10 @@ export { useShippingRates } from './hooks/useShippingRates';
25
22
  export type { UseShippingRatesOptions, UseShippingRatesResult } from './hooks/useShippingRates';
26
23
  export { useTranslations } from './hooks/useTranslations';
27
24
  export { useVipOffers } from './hooks/useVipOffers';
28
- export { useTagadaContext } from './providers/TagadaProvider';
25
+ export { useTagadaContext, useTagadaContextSafe } from './providers/TagadaProvider';
29
26
  export { clearPluginConfigCache, debugPluginConfig, getPluginConfig, useBasePath, usePluginConfig } from './hooks/usePluginConfig';
30
27
  export type { PluginConfig } from './hooks/usePluginConfig';
31
- export { getAvailableLanguages, useCountryOptions, useISOData, useRegionOptions } from './hooks/useISOData';
28
+ export { getAvailableLanguages, useCountryOptions, useISOData, useRegionOptions, useLanguageImport } from './hooks/useISOData';
32
29
  export type { ISOCountry, ISORegion, UseISODataResult } from './hooks/useISOData';
33
30
  export type { GeoLocationData, UseGeoLocationOptions, UseGeoLocationReturn } from './hooks/useGeoLocation';
34
31
  export type { ExtractedAddress, GoogleAddressComponent, GooglePlaceDetails, GooglePrediction, UseGoogleAutocompleteOptions, UseGoogleAutocompleteResult } from './hooks/useGoogleAutocomplete';
@@ -40,7 +37,7 @@ export { useThreeds } from './hooks/useThreeds';
40
37
  export { useThreedsModal } from './hooks/useThreedsModal';
41
38
  export { useApplePay } from './hooks/useApplePay';
42
39
  export { ExpressPaymentProvider, useExpressPayment } from './hooks/useExpressPayment';
43
- export type { AuthState, Currency, Customer, CustomerInfos, Environment, EnvironmentConfig, Locale, Order, OrderAddress, OrderItem, OrderSummary, PickupPoint, Session, Store } from './types';
40
+ export type { AuthState, Currency, Customer, Environment, EnvironmentConfig, Locale, Order, OrderAddress, OrderItem, OrderSummary, PickupPoint, Session, Store } from './types';
44
41
  export type { CheckoutData, CheckoutInitParams, CheckoutLineItem, CheckoutSession, CheckoutSessionPreview, Promotion, UseCheckoutOptions, UseCheckoutResult } from './hooks/useCheckout';
45
42
  export type { Discount, DiscountCodeValidation, UseDiscountsOptions, UseDiscountsResult } from './hooks/useDiscounts';
46
43
  export type { OrderBumpPreview, UseOrderBumpOptions, UseOrderBumpResult } from './hooks/useOrderBump';
@@ -10,9 +10,6 @@ export { useCheckout } from './hooks/useCheckout';
10
10
  export { useClubOffers } from './hooks/useClubOffers';
11
11
  export { useCurrency } from './hooks/useCurrency';
12
12
  export { useCustomer } from './hooks/useCustomer';
13
- export { useCustomerInfos } from './hooks/useCustomerInfos';
14
- export { useCustomerOrders } from './hooks/useCustomerOrders';
15
- export { useCustomerSubscriptions } from './hooks/useCustomerSubscriptions';
16
13
  export { useDiscounts } from './hooks/useDiscounts';
17
14
  export { useEnvironment } from './hooks/useEnvironment';
18
15
  export { useGeoLocation } from './hooks/useGeoLocation';
@@ -27,11 +24,11 @@ export { useSession } from './hooks/useSession';
27
24
  export { useShippingRates } from './hooks/useShippingRates';
28
25
  export { useTranslations } from './hooks/useTranslations';
29
26
  export { useVipOffers } from './hooks/useVipOffers';
30
- export { useTagadaContext } from './providers/TagadaProvider';
27
+ export { useTagadaContext, useTagadaContextSafe } from './providers/TagadaProvider';
31
28
  // Plugin configuration hooks
32
29
  export { clearPluginConfigCache, debugPluginConfig, getPluginConfig, useBasePath, usePluginConfig } from './hooks/usePluginConfig';
33
30
  // ISO Data hooks
34
- export { getAvailableLanguages, useCountryOptions, useISOData, useRegionOptions } from './hooks/useISOData';
31
+ export { getAvailableLanguages, useCountryOptions, useISOData, useRegionOptions, useLanguageImport } from './hooks/useISOData';
35
32
  // Order hook exports
36
33
  export { useOrder } from './hooks/useOrder';
37
34
  // Payment hooks exports
@@ -2,7 +2,7 @@
2
2
  * TagadaProvider - Main provider component for the Tagada Pay React SDK
3
3
  */
4
4
  import { ReactNode } from 'react';
5
- import { PluginConfig } from '../hooks/usePluginConfig';
5
+ import { PluginConfig, RawPluginConfig } from '../hooks/usePluginConfig';
6
6
  import { ApiService } from '../services/apiService';
7
7
  import { AuthState, Currency, Customer, Environment, EnvironmentConfig, Locale, Session, Store } from '../types';
8
8
  import { convertCurrency, formatMoney, formatMoneyWithoutSymbol, formatSimpleMoney, getCurrencyInfo, minorUnitsToMajorUnits, moneyStringOrNumberToMinorUnits } from '../utils/money';
@@ -54,8 +54,11 @@ interface TagadaProviderProps {
54
54
  debugMode?: boolean;
55
55
  localConfig?: string;
56
56
  blockUntilSessionReady?: boolean;
57
+ rawPluginConfig?: RawPluginConfig;
57
58
  }
58
59
  export declare function TagadaProvider({ children, environment, customApiConfig, debugMode, // Remove default, will be set based on environment
59
- localConfig, blockUntilSessionReady, }: TagadaProviderProps): import("react/jsx-runtime").JSX.Element;
60
+ localConfig, blockUntilSessionReady, // Default to new non-blocking behavior
61
+ rawPluginConfig, }: TagadaProviderProps): import("react/jsx-runtime").JSX.Element;
60
62
  export declare function useTagadaContext(): TagadaContextValue;
63
+ export declare function useTagadaContextSafe(): TagadaContextValue | null;
61
64
  export {};
@@ -38,16 +38,16 @@ const InitializationLoader = () => (_jsxs("div", { style: {
38
38
  borderTop: '1.5px solid #9ca3af',
39
39
  borderRadius: '50%',
40
40
  animation: 'tagada-spin 1s linear infinite',
41
- } }), _jsx("span", { children: "Loading..." }), _jsx("style", { children: `
42
- @keyframes tagada-spin {
43
- 0% { transform: rotate(0deg); }
44
- 100% { transform: rotate(360deg); }
45
- }
41
+ } }), _jsx("span", { children: "Loading..." }), _jsx("style", { children: `
42
+ @keyframes tagada-spin {
43
+ 0% { transform: rotate(0deg); }
44
+ 100% { transform: rotate(360deg); }
45
+ }
46
46
  ` })] }));
47
47
  const TagadaContext = createContext(null);
48
48
  export function TagadaProvider({ children, environment, customApiConfig, debugMode, // Remove default, will be set based on environment
49
49
  localConfig, blockUntilSessionReady = false, // Default to new non-blocking behavior
50
- }) {
50
+ rawPluginConfig, }) {
51
51
  // LOCAL DEV ONLY: Use localConfig override if in local development, otherwise use default
52
52
  const isLocalDev = typeof window !== 'undefined' &&
53
53
  (window.location.hostname === 'localhost' ||
@@ -72,7 +72,7 @@ localConfig, blockUntilSessionReady = false, // Default to new non-blocking beha
72
72
  useEffect(() => {
73
73
  const loadConfig = async () => {
74
74
  try {
75
- const config = await loadPluginConfig(configVariant);
75
+ const config = await loadPluginConfig(configVariant, rawPluginConfig);
76
76
  // Ensure we have required store ID before proceeding
77
77
  if (!config.storeId) {
78
78
  console.warn('⚠️ No store ID found in plugin config. This may cause hooks to fail.');
@@ -83,6 +83,7 @@ localConfig, blockUntilSessionReady = false, // Default to new non-blocking beha
83
83
  accountId: config.accountId,
84
84
  basePath: config.basePath,
85
85
  hasConfig: !!config.config,
86
+ source: rawPluginConfig ? 'raw' : 'file',
86
87
  });
87
88
  if (blockUntilSessionReady) {
88
89
  console.log('⏳ Blocking mode: Children will render after Phase 3 (session init) completes');
@@ -100,7 +101,7 @@ localConfig, blockUntilSessionReady = false, // Default to new non-blocking beha
100
101
  }
101
102
  };
102
103
  void loadConfig();
103
- }, [configVariant]);
104
+ }, [configVariant, rawPluginConfig]);
104
105
  // Extract store/account IDs from plugin config (only source now)
105
106
  const storeId = pluginConfig.storeId;
106
107
  const accountId = pluginConfig.accountId;
@@ -350,58 +351,6 @@ localConfig, blockUntilSessionReady = false, // Default to new non-blocking beha
350
351
  setIsLoading(false);
351
352
  }
352
353
  }, [apiService, hasAttemptedAnonymousToken, initializeSession, finalDebugMode]);
353
- // Initialize token from storage or create anonymous token (extracted to stable callback)
354
- const initializeToken = useCallback(async () => {
355
- try {
356
- console.debug('[SDK] Initializing token...');
357
- setIsLoading(true);
358
- // Check for existing token
359
- const existingToken = getClientToken();
360
- let tokenToUse = null;
361
- // Check URL params for token
362
- const urlParams = new URLSearchParams(window.location.search);
363
- const queryToken = urlParams.get('token');
364
- if (queryToken) {
365
- console.debug('[SDK] Found token in URL params');
366
- tokenToUse = queryToken;
367
- setClientToken(queryToken);
368
- }
369
- else if (existingToken && !isTokenExpired(existingToken)) {
370
- console.debug('[SDK] Using existing token from storage');
371
- tokenToUse = existingToken;
372
- }
373
- else {
374
- console.debug('[SDK] No valid token found');
375
- // Determine storeId for anonymous token
376
- const targetStoreId = storeId || 'default-store';
377
- await createAnonymousToken(targetStoreId);
378
- return;
379
- }
380
- if (tokenToUse) {
381
- setToken(tokenToUse);
382
- // Update the API service with the token
383
- apiService.updateToken(tokenToUse);
384
- // Decode token to get session data
385
- const decodedSession = decodeJWTClient(tokenToUse);
386
- if (decodedSession) {
387
- setSession(decodedSession);
388
- // Initialize session with API call
389
- await initializeSession(decodedSession);
390
- }
391
- else {
392
- console.error('[SDK] Failed to decode token');
393
- setIsInitialized(true);
394
- setIsSessionInitialized(false); // Session failed to initialize
395
- setIsLoading(false);
396
- }
397
- }
398
- }
399
- catch (error) {
400
- console.error('[SDK] Error initializing token:', error);
401
- setIsInitialized(true);
402
- setIsLoading(false);
403
- }
404
- }, [apiService, storeId, createAnonymousToken, initializeSession]);
405
354
  // Initialize token from storage or create anonymous token
406
355
  // This runs in the background after phases 1 & 2 complete, but doesn't block rendering
407
356
  useEffect(() => {
@@ -413,19 +362,59 @@ localConfig, blockUntilSessionReady = false, // Default to new non-blocking beha
413
362
  return;
414
363
  }
415
364
  isInitializing.current = true;
416
- void initializeToken();
417
- }, [storeId, configLoading, initializeToken]);
418
- useEffect(() => {
419
- function onStorage() {
420
- // Re-run initialization when token may have changed in another tab
421
- isInitializing.current = false;
422
- void initializeToken();
423
- }
424
- window.addEventListener('storage', onStorage);
425
- return () => {
426
- window.removeEventListener('storage', onStorage);
365
+ const initializeToken = async () => {
366
+ try {
367
+ console.debug('[SDK] Initializing token...');
368
+ setIsLoading(true);
369
+ // Check for existing token
370
+ const existingToken = getClientToken();
371
+ let tokenToUse = null;
372
+ // Check URL params for token
373
+ const urlParams = new URLSearchParams(window.location.search);
374
+ const queryToken = urlParams.get('token');
375
+ if (queryToken) {
376
+ console.debug('[SDK] Found token in URL params');
377
+ tokenToUse = queryToken;
378
+ setClientToken(queryToken);
379
+ }
380
+ else if (existingToken && !isTokenExpired(existingToken)) {
381
+ console.debug('[SDK] Using existing token from storage');
382
+ tokenToUse = existingToken;
383
+ }
384
+ else {
385
+ console.debug('[SDK] No valid token found');
386
+ // Determine storeId for anonymous token
387
+ const targetStoreId = storeId || 'default-store';
388
+ await createAnonymousToken(targetStoreId);
389
+ return;
390
+ }
391
+ if (tokenToUse) {
392
+ setToken(tokenToUse);
393
+ // Update the API service with the token
394
+ apiService.updateToken(tokenToUse);
395
+ // Decode token to get session data
396
+ const decodedSession = decodeJWTClient(tokenToUse);
397
+ if (decodedSession) {
398
+ setSession(decodedSession);
399
+ // Initialize session with API call
400
+ await initializeSession(decodedSession);
401
+ }
402
+ else {
403
+ console.error('[SDK] Failed to decode token');
404
+ setIsInitialized(true);
405
+ setIsSessionInitialized(false); // Session failed to initialize
406
+ setIsLoading(false);
407
+ }
408
+ }
409
+ }
410
+ catch (error) {
411
+ console.error('[SDK] Error initializing token:', error);
412
+ setIsInitialized(true);
413
+ setIsLoading(false);
414
+ }
427
415
  };
428
- }, [initializeToken]);
416
+ void initializeToken();
417
+ }, [storeId, createAnonymousToken, initializeSession, configLoading]);
429
418
  // Update auth state when customer/session changes
430
419
  useEffect(() => {
431
420
  setAuth({
@@ -571,6 +560,10 @@ export function useTagadaContext() {
571
560
  }
572
561
  return context;
573
562
  }
563
+ // Safe version that returns null instead of throwing
564
+ export function useTagadaContextSafe() {
565
+ return useContext(TagadaContext);
566
+ }
574
567
  // Helper functions
575
568
  function getCurrencySymbol(code) {
576
569
  const symbols = {
@@ -150,112 +150,3 @@ export interface Order {
150
150
  };
151
151
  relatedOrders?: Order[];
152
152
  }
153
- export interface PaymentSummary {
154
- id: string;
155
- status: string;
156
- amount: number;
157
- currency: string;
158
- createdAt: string;
159
- updatedAt?: string;
160
- provider?: string;
161
- metadata?: Record<string, any>;
162
- }
163
- export interface PromotionSummary {
164
- id: string;
165
- code?: string | null;
166
- type?: string;
167
- amount?: number;
168
- description?: string | null;
169
- }
170
- export interface OrderAdjustmentSummary {
171
- type: string;
172
- amount: number;
173
- description: string;
174
- }
175
- export interface OrderWithRelations extends Order {
176
- customer?: Customer;
177
- store?: Store;
178
- account?: {
179
- id: string;
180
- name?: string;
181
- } | undefined;
182
- items: OrderItem[];
183
- payments?: PaymentSummary[];
184
- summaries: OrderSummary[];
185
- checkoutSession?: {
186
- id?: string;
187
- returnUrl?: string;
188
- [key: string]: any;
189
- };
190
- promotions?: PromotionSummary[];
191
- subscriptions?: any[];
192
- adjustments: OrderAdjustmentSummary[];
193
- }
194
- export interface CustomerAddress {
195
- company?: string;
196
- firstName: string;
197
- lastName: string;
198
- address1: string;
199
- city: string;
200
- country: string;
201
- state: string;
202
- postal: string;
203
- phone?: string;
204
- email?: string;
205
- }
206
- export interface CustomerOrderSummary {
207
- id: string;
208
- storeId: string;
209
- accountId: string;
210
- createdAt: string;
211
- updatedAt: string;
212
- status: string;
213
- cancelledAt: string | null;
214
- cancelledReason: string | null;
215
- paidAt: string | null;
216
- paidAmount: number | null;
217
- openAt: string | null;
218
- abandonedAt: string | null;
219
- currency: string;
220
- externalCustomerType: string | null;
221
- externalCustomerId: string | null;
222
- externalOrderId: string | null;
223
- billingAddress: CustomerAddress;
224
- shippingAddress: Omit<CustomerAddress, 'email'>;
225
- pickupAddress: any | null;
226
- taxesIncluded: boolean;
227
- draft: boolean;
228
- checkoutSessionId: string | null;
229
- sessionHash: string | null;
230
- customerId: string;
231
- createdFrom: string | null;
232
- paymentInstrumentId: string | null;
233
- refundedAt: string | null;
234
- refundedAmount: number | null;
235
- metadata?: Record<string, any>;
236
- }
237
- export interface CustomerInfos {
238
- customer: {
239
- id: string;
240
- email: string | null;
241
- firstName: string | null;
242
- lastName: string | null;
243
- externalCustomerId: string | null;
244
- lastOrderId: string | null;
245
- accountId: string;
246
- storeId: string;
247
- billingAddress: CustomerAddress | null;
248
- shippingAddress: Omit<CustomerAddress, 'email'> | null;
249
- currency: string | null;
250
- locale: string | null;
251
- draft: boolean;
252
- acceptsMarketing: boolean;
253
- createdAt: string;
254
- updatedAt: string;
255
- metadata: Record<string, any>;
256
- device: any | null;
257
- orders: CustomerOrderSummary[];
258
- subscriptions: any[];
259
- };
260
- promotionCodes: any[];
261
- }
@@ -10,7 +10,6 @@ export function setClientToken(token) {
10
10
  if (typeof window !== 'undefined') {
11
11
  try {
12
12
  localStorage.setItem(TOKEN_KEY, token);
13
- window.dispatchEvent(new Event('storage'));
14
13
  }
15
14
  catch (error) {
16
15
  console.error('Failed to save token to localStorage:', error);