@tagadapay/plugin-sdk 3.0.12 → 3.0.14

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.
@@ -343,16 +343,25 @@ export interface FunnelNavigationAction {
343
343
  data?: any;
344
344
  }
345
345
  export interface FunnelNavigationResult {
346
- stepId: string;
346
+ stepId?: string;
347
347
  url?: string;
348
- action: FunnelNavigationAction;
349
- context: SimpleFunnelContext;
348
+ action?: FunnelNavigationAction;
349
+ context?: SimpleFunnelContext;
350
350
  tracking?: {
351
351
  from: string;
352
352
  to: string;
353
353
  event: string;
354
354
  timestamp: string;
355
355
  };
356
+ /**
357
+ * Fire-and-forget response: indicates navigation was queued
358
+ * When true, stepId, url, action, and context will be undefined
359
+ */
360
+ queued?: boolean;
361
+ /**
362
+ * Session ID (may be different if session was recovered)
363
+ */
364
+ sessionId?: string;
356
365
  }
357
366
  /**
358
367
  * Funnel context available to plugins
@@ -480,11 +489,37 @@ export interface FunnelNavigateRequest {
480
489
  * If session is not found and funnelId is provided, a new session will be created
481
490
  */
482
491
  funnelId?: string;
492
+ /**
493
+ * Funnel step ID (from SDK injection/URL params)
494
+ * Used to sync session state before navigation
495
+ */
496
+ funnelStepId?: string;
497
+ /**
498
+ * Funnel variant ID (from SDK injection/URL params for A/B testing)
499
+ * Used to sync session state before navigation
500
+ */
501
+ funnelVariantId?: string;
502
+ /**
503
+ * Fire and forget mode - queues navigation to QStash and returns immediately
504
+ * No response data needed, just acknowledgment that request was queued
505
+ * When true, result will only contain queued status and sessionId (no URL or stepId)
506
+ */
507
+ fireAndForget?: boolean;
508
+ /**
509
+ * ✅ Customer tags to set (merged with existing customer tags)
510
+ * @example ['segment:vip', 'cart_value:high']
511
+ */
512
+ customerTags?: string[];
513
+ /**
514
+ * ✅ Device ID for geo/device tag enrichment (optional)
515
+ * @example 'dev_abc123xyz'
516
+ */
517
+ deviceId?: string;
483
518
  }
484
519
  export interface FunnelNavigateResponse {
485
520
  success: boolean;
486
521
  result?: {
487
- stepId: string;
522
+ stepId?: string;
488
523
  url?: string;
489
524
  /**
490
525
  * New session ID if session was recovered (expired/removed)
@@ -497,6 +532,11 @@ export interface FunnelNavigateResponse {
497
532
  event: string;
498
533
  timestamp: string;
499
534
  };
535
+ /**
536
+ * Fire-and-forget response: indicates navigation was queued
537
+ * When present, stepId and url will be undefined
538
+ */
539
+ queued?: boolean;
500
540
  };
501
541
  error?: string;
502
542
  }
@@ -303,6 +303,32 @@ export declare class OffersResource {
303
303
  checkoutSessionId?: string;
304
304
  customerId?: string;
305
305
  }>;
306
+ /**
307
+ * Transform offer to checkout session (async mode) ⚡
308
+ * Response time: ~50ms (20-50x faster!)
309
+ *
310
+ * Returns checkoutToken immediately, background job completes processing.
311
+ * Use getCheckout() to fetch full session data - it auto-waits for completion.
312
+ *
313
+ * @example
314
+ * // Fast transform
315
+ * const { checkoutToken } = await offers.toCheckoutAsync(offerId, currency, lineItems, returnUrl, mainOrderId);
316
+ *
317
+ * // Redirect user immediately
318
+ * window.location.href = `/checkout/${checkoutToken}/op`;
319
+ *
320
+ * // By the time page loads, background processing is usually complete
321
+ */
322
+ toCheckoutAsync(offerId: string, currency?: string, lineItems?: Array<{
323
+ lineItemId?: string;
324
+ productId?: string;
325
+ variantId: string;
326
+ quantity: number;
327
+ }>, returnUrl?: string, mainOrderId?: string): Promise<{
328
+ checkoutToken: string;
329
+ customerId: string;
330
+ status: 'processing';
331
+ }>;
306
332
  /**
307
333
  * @deprecated Use transformToCheckoutSession instead
308
334
  * Transform offer to checkout session with dynamic variant selection
@@ -202,6 +202,43 @@ export class OffersResource {
202
202
  customerId: response.customerId,
203
203
  };
204
204
  }
205
+ /**
206
+ * Transform offer to checkout session (async mode) ⚡
207
+ * Response time: ~50ms (20-50x faster!)
208
+ *
209
+ * Returns checkoutToken immediately, background job completes processing.
210
+ * Use getCheckout() to fetch full session data - it auto-waits for completion.
211
+ *
212
+ * @example
213
+ * // Fast transform
214
+ * const { checkoutToken } = await offers.toCheckoutAsync(offerId, currency, lineItems, returnUrl, mainOrderId);
215
+ *
216
+ * // Redirect user immediately
217
+ * window.location.href = `/checkout/${checkoutToken}/op`;
218
+ *
219
+ * // By the time page loads, background processing is usually complete
220
+ */
221
+ async toCheckoutAsync(offerId, currency = 'USD', lineItems, returnUrl, mainOrderId) {
222
+ console.log('🛒 [OffersResource] Calling to-checkout-async API:', {
223
+ offerId,
224
+ currency,
225
+ lineItems,
226
+ returnUrl,
227
+ mainOrderId,
228
+ endpoint: `/api/v1/offers/${offerId}/to-checkout-async`,
229
+ });
230
+ const response = await this.apiClient.post(`/api/v1/offers/${offerId}/to-checkout-async`, {
231
+ offerId,
232
+ lineItems: lineItems?.map(item => ({
233
+ variantId: item.variantId,
234
+ quantity: item.quantity,
235
+ })) || [],
236
+ returnUrl: returnUrl || (typeof window !== 'undefined' ? window.location.href : ''),
237
+ mainOrderId: mainOrderId || '',
238
+ });
239
+ console.log('📥 [OffersResource] To-checkout-async API response:', response);
240
+ return response;
241
+ }
205
242
  /**
206
243
  * @deprecated Use transformToCheckoutSession instead
207
244
  * Transform offer to checkout session with dynamic variant selection
@@ -7,7 +7,9 @@ export interface ApiConfig {
7
7
  endpoints: {
8
8
  checkout: {
9
9
  sessionInit: string;
10
+ sessionInitAsync: string;
10
11
  sessionStatus: string;
12
+ asyncStatus: string;
11
13
  };
12
14
  customer: {
13
15
  profile: string;
@@ -262,7 +264,7 @@ export interface CustomerInfos {
262
264
  }
263
265
  export interface SessionInitResponse {
264
266
  store: Store;
265
- locale: string;
267
+ locale?: string;
266
268
  messages?: Record<string, string>;
267
269
  customer?: Customer;
268
270
  session?: Session;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Cross-Domain Auth Handoff Utilities
3
+ *
4
+ * Handles automatic resolution of authCode query parameters for seamless
5
+ * cross-domain authentication.
6
+ *
7
+ * Flow:
8
+ * 1. Check URL for authCode parameter
9
+ * 2. If present, resolve it with backend
10
+ * 3. Store the returned token (overrides any existing token)
11
+ * 4. Clean the URL (remove authCode)
12
+ * 5. Return resolved customer and context
13
+ */
14
+ export interface AuthHandoffResolveResponse {
15
+ sessionId: string;
16
+ token: string;
17
+ customer: {
18
+ id: string;
19
+ email?: string;
20
+ firstName?: string;
21
+ lastName?: string;
22
+ role: 'authenticated' | 'anonymous';
23
+ };
24
+ context: Record<string, unknown>;
25
+ }
26
+ /**
27
+ * Check if authCode is present in URL
28
+ */
29
+ export declare function hasAuthCode(): boolean;
30
+ /**
31
+ * Get authCode from URL
32
+ */
33
+ export declare function getAuthCode(): string | null;
34
+ /**
35
+ * Check if a code has already been resolved
36
+ */
37
+ export declare function isCodeAlreadyResolved(code: string): boolean;
38
+ /**
39
+ * Resolve auth handoff and return token + customer info
40
+ *
41
+ * This function:
42
+ * 1. Calls POST /api/v1/cms/auth/resolve-handoff
43
+ * 2. Stores the returned token (overrides existing)
44
+ * 3. Cleans the URL (removes authCode)
45
+ * 4. Returns customer and context data
46
+ *
47
+ * 🔒 Deduplication: Multiple calls with the same code will return the same promise
48
+ * to prevent duplicate API requests (e.g., React StrictMode double-mounting)
49
+ */
50
+ export declare function resolveAuthHandoff(authCode: string, storeId: string, apiBaseUrl: string, debugMode?: boolean): Promise<AuthHandoffResolveResponse>;
51
+ /**
52
+ * Remove authCode from URL without page reload
53
+ * Uses history.replaceState to update URL cleanly
54
+ */
55
+ export declare function cleanAuthCodeFromUrl(debugMode?: boolean): void;
56
+ /**
57
+ * Check if we should resolve authCode
58
+ * Returns true if authCode is present and valid format
59
+ */
60
+ export declare function shouldResolveAuthCode(): boolean;
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Cross-Domain Auth Handoff Utilities
3
+ *
4
+ * Handles automatic resolution of authCode query parameters for seamless
5
+ * cross-domain authentication.
6
+ *
7
+ * Flow:
8
+ * 1. Check URL for authCode parameter
9
+ * 2. If present, resolve it with backend
10
+ * 3. Store the returned token (overrides any existing token)
11
+ * 4. Clean the URL (remove authCode)
12
+ * 5. Return resolved customer and context
13
+ */
14
+ import { setClientToken } from './tokenStorage';
15
+ // Track in-flight and completed resolutions to prevent duplicates
16
+ const resolutionCache = new Map();
17
+ const resolvedCodes = new Set();
18
+ /**
19
+ * Check if authCode is present in URL
20
+ */
21
+ export function hasAuthCode() {
22
+ if (typeof window === 'undefined')
23
+ return false;
24
+ const urlParams = new URLSearchParams(window.location.search);
25
+ return urlParams.has('authCode');
26
+ }
27
+ /**
28
+ * Get authCode from URL
29
+ */
30
+ export function getAuthCode() {
31
+ if (typeof window === 'undefined')
32
+ return null;
33
+ const urlParams = new URLSearchParams(window.location.search);
34
+ return urlParams.get('authCode');
35
+ }
36
+ /**
37
+ * Check if a code has already been resolved
38
+ */
39
+ export function isCodeAlreadyResolved(code) {
40
+ return resolvedCodes.has(code);
41
+ }
42
+ /**
43
+ * Resolve auth handoff and return token + customer info
44
+ *
45
+ * This function:
46
+ * 1. Calls POST /api/v1/cms/auth/resolve-handoff
47
+ * 2. Stores the returned token (overrides existing)
48
+ * 3. Cleans the URL (removes authCode)
49
+ * 4. Returns customer and context data
50
+ *
51
+ * 🔒 Deduplication: Multiple calls with the same code will return the same promise
52
+ * to prevent duplicate API requests (e.g., React StrictMode double-mounting)
53
+ */
54
+ export async function resolveAuthHandoff(authCode, storeId, apiBaseUrl, debugMode = false) {
55
+ // Check if already resolved
56
+ if (resolvedCodes.has(authCode)) {
57
+ if (debugMode) {
58
+ console.log('[AuthHandoff] Code already resolved, skipping duplicate request');
59
+ }
60
+ throw new Error('Auth code already resolved');
61
+ }
62
+ // Check if resolution is in-flight
63
+ const inFlightResolution = resolutionCache.get(authCode);
64
+ if (inFlightResolution) {
65
+ if (debugMode) {
66
+ console.log('[AuthHandoff] Resolution already in progress, waiting for existing request');
67
+ }
68
+ return inFlightResolution;
69
+ }
70
+ if (debugMode) {
71
+ console.log('[AuthHandoff] Resolving authCode:', authCode.substring(0, 15) + '...');
72
+ }
73
+ // Create resolution promise
74
+ const resolutionPromise = (async () => {
75
+ try {
76
+ // Call resolve endpoint (no authentication required)
77
+ const response = await fetch(`${apiBaseUrl}/api/v1/cms/auth/resolve-handoff`, {
78
+ method: 'POST',
79
+ headers: {
80
+ 'Content-Type': 'application/json',
81
+ },
82
+ body: JSON.stringify({
83
+ code: authCode,
84
+ storeId,
85
+ }),
86
+ });
87
+ if (!response.ok) {
88
+ const errorData = await response.json().catch(() => ({ message: 'Unknown error' }));
89
+ throw new Error(errorData.message || `Failed to resolve auth handoff: ${response.status}`);
90
+ }
91
+ const data = await response.json();
92
+ if (debugMode) {
93
+ console.log('[AuthHandoff] ✅ Resolved successfully:', {
94
+ customerId: data.customer.id,
95
+ role: data.customer.role,
96
+ hasContext: Object.keys(data.context).length > 0,
97
+ });
98
+ }
99
+ // Store token (overrides any existing token)
100
+ if (debugMode) {
101
+ console.log('[AuthHandoff] Storing new token (overriding existing)');
102
+ }
103
+ setClientToken(data.token);
104
+ // Clean URL (remove authCode parameter)
105
+ cleanAuthCodeFromUrl(debugMode);
106
+ // Mark as resolved
107
+ resolvedCodes.add(authCode);
108
+ return data;
109
+ }
110
+ catch (error) {
111
+ console.error('[AuthHandoff] ❌ Failed to resolve:', error);
112
+ throw error;
113
+ }
114
+ finally {
115
+ // Remove from in-flight cache after completion (success or failure)
116
+ resolutionCache.delete(authCode);
117
+ }
118
+ })();
119
+ // Cache the in-flight promise
120
+ resolutionCache.set(authCode, resolutionPromise);
121
+ return resolutionPromise;
122
+ }
123
+ /**
124
+ * Remove authCode from URL without page reload
125
+ * Uses history.replaceState to update URL cleanly
126
+ */
127
+ export function cleanAuthCodeFromUrl(debugMode = false) {
128
+ if (typeof window === 'undefined')
129
+ return;
130
+ const url = new URL(window.location.href);
131
+ if (url.searchParams.has('authCode')) {
132
+ url.searchParams.delete('authCode');
133
+ // Use replaceState to update URL without reload
134
+ window.history.replaceState({}, '', url.pathname + url.search + url.hash);
135
+ if (debugMode) {
136
+ console.log('[AuthHandoff] Cleaned authCode from URL');
137
+ }
138
+ }
139
+ }
140
+ /**
141
+ * Check if we should resolve authCode
142
+ * Returns true if authCode is present and valid format
143
+ */
144
+ export function shouldResolveAuthCode() {
145
+ const authCode = getAuthCode();
146
+ if (!authCode || !authCode.startsWith('ah_')) {
147
+ return false;
148
+ }
149
+ // Don't resolve if already resolved
150
+ if (resolvedCodes.has(authCode)) {
151
+ return false;
152
+ }
153
+ return true;
154
+ }
@@ -1,16 +1,27 @@
1
1
  export interface DeviceInfo {
2
2
  userAgent: {
3
+ name: string;
3
4
  browser: {
5
+ major: string;
4
6
  name: string;
5
7
  version: string;
8
+ type?: string;
6
9
  };
7
10
  os: {
8
11
  name: string;
9
12
  version: string;
10
13
  };
11
14
  device?: {
12
- type: string;
13
- model: string;
15
+ model?: string;
16
+ type?: string;
17
+ vendor?: string;
18
+ };
19
+ engine: {
20
+ name: string;
21
+ version: string;
22
+ };
23
+ cpu: {
24
+ architecture: string;
14
25
  };
15
26
  };
16
27
  screenResolution: {
@@ -18,13 +29,19 @@ export interface DeviceInfo {
18
29
  height: number;
19
30
  };
20
31
  timeZone: string;
32
+ flags?: {
33
+ isBot: boolean;
34
+ isChromeFamily: boolean;
35
+ isStandalonePWA: boolean;
36
+ isAppleSilicon: boolean;
37
+ };
21
38
  }
22
39
  /**
23
40
  * Get browser locale
24
41
  */
25
42
  export declare function getBrowserLocale(): string;
26
43
  /**
27
- * Collect all device information
44
+ * Collect all device information using UAParser
28
45
  */
29
46
  export declare function collectDeviceInfo(): DeviceInfo;
30
47
  /**
@@ -1,91 +1,5 @@
1
- /**
2
- * Get basic browser information from user agent
3
- */
4
- function getBrowserInfo() {
5
- const userAgent = navigator.userAgent;
6
- // Chrome
7
- if (userAgent.includes('Chrome')) {
8
- const match = /Chrome\/(\d+)/.exec(userAgent);
9
- return { name: 'Chrome', version: match ? match[1] : 'unknown' };
10
- }
11
- // Firefox
12
- if (userAgent.includes('Firefox')) {
13
- const match = /Firefox\/(\d+)/.exec(userAgent);
14
- return { name: 'Firefox', version: match ? match[1] : 'unknown' };
15
- }
16
- // Safari
17
- if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) {
18
- const match = /Version\/(\d+)/.exec(userAgent);
19
- return { name: 'Safari', version: match ? match[1] : 'unknown' };
20
- }
21
- // Edge
22
- if (userAgent.includes('Edge')) {
23
- const match = /Edge\/(\d+)/.exec(userAgent);
24
- return { name: 'Edge', version: match ? match[1] : 'unknown' };
25
- }
26
- return { name: 'unknown', version: 'unknown' };
27
- }
28
- /**
29
- * Get basic OS information from user agent
30
- */
31
- function getOSInfo() {
32
- const userAgent = navigator.userAgent;
33
- // Windows
34
- if (userAgent.includes('Windows')) {
35
- if (userAgent.includes('Windows NT 10.0'))
36
- return { name: 'Windows', version: '10' };
37
- if (userAgent.includes('Windows NT 6.3'))
38
- return { name: 'Windows', version: '8.1' };
39
- if (userAgent.includes('Windows NT 6.2'))
40
- return { name: 'Windows', version: '8' };
41
- if (userAgent.includes('Windows NT 6.1'))
42
- return { name: 'Windows', version: '7' };
43
- return { name: 'Windows', version: 'unknown' };
44
- }
45
- // macOS
46
- if (userAgent.includes('Mac OS X')) {
47
- const match = /Mac OS X (\d+[._]\d+)/.exec(userAgent);
48
- return { name: 'macOS', version: match ? match[1].replace('_', '.') : 'unknown' };
49
- }
50
- // iOS
51
- if (userAgent.includes('iPhone') || userAgent.includes('iPad')) {
52
- const match = /OS (\d+[._]\d+)/.exec(userAgent);
53
- return { name: 'iOS', version: match ? match[1].replace('_', '.') : 'unknown' };
54
- }
55
- // Android
56
- if (userAgent.includes('Android')) {
57
- const match = /Android (\d+[.\d]*)/.exec(userAgent);
58
- return { name: 'Android', version: match ? match[1] : 'unknown' };
59
- }
60
- // Linux
61
- if (userAgent.includes('Linux')) {
62
- return { name: 'Linux', version: 'unknown' };
63
- }
64
- return { name: 'unknown', version: 'unknown' };
65
- }
66
- /**
67
- * Get device information
68
- */
69
- function getDeviceInfo() {
70
- const userAgent = navigator.userAgent;
71
- // Mobile devices
72
- if (userAgent.includes('iPhone')) {
73
- return { type: 'mobile', model: 'iPhone' };
74
- }
75
- if (userAgent.includes('iPad')) {
76
- return { type: 'tablet', model: 'iPad' };
77
- }
78
- if (userAgent.includes('Android')) {
79
- if (userAgent.includes('Mobile')) {
80
- return { type: 'mobile', model: 'Android' };
81
- }
82
- else {
83
- return { type: 'tablet', model: 'Android' };
84
- }
85
- }
86
- // Desktop (no specific device info)
87
- return undefined;
88
- }
1
+ import { UAParser } from '@ua-parser-js/pro-enterprise';
2
+ import { isBot, isChromeFamily, isStandalonePWA, isAppleSilicon, } from '@ua-parser-js/pro-enterprise/helpers';
89
3
  /**
90
4
  * Get screen resolution
91
5
  */
@@ -120,28 +34,82 @@ export function getBrowserLocale() {
120
34
  }
121
35
  }
122
36
  /**
123
- * Collect all device information
37
+ * Collect all device information using UAParser
124
38
  */
125
39
  export function collectDeviceInfo() {
126
40
  if (typeof window === 'undefined') {
127
41
  // Server-side fallback
128
42
  return {
129
43
  userAgent: {
130
- browser: { name: 'unknown', version: 'unknown' },
131
- os: { name: 'unknown', version: 'unknown' },
44
+ name: '',
45
+ browser: { major: '', name: '', version: '' },
46
+ os: { name: '', version: '' },
47
+ device: undefined,
48
+ engine: { name: '', version: '' },
49
+ cpu: { architecture: '' },
132
50
  },
133
51
  screenResolution: { width: 0, height: 0 },
134
52
  timeZone: 'UTC',
53
+ flags: {
54
+ isBot: false,
55
+ isChromeFamily: false,
56
+ isStandalonePWA: false,
57
+ isAppleSilicon: false,
58
+ },
59
+ };
60
+ }
61
+ const parser = new UAParser();
62
+ const result = parser.getResult();
63
+ // Enhanced detection using UAParser official helpers
64
+ let flags;
65
+ try {
66
+ flags = {
67
+ isBot: isBot(result),
68
+ isChromeFamily: isChromeFamily(result),
69
+ isStandalonePWA: isStandalonePWA(),
70
+ isAppleSilicon: isAppleSilicon(result),
71
+ };
72
+ }
73
+ catch (error) {
74
+ console.error('Failed to compute device flags:', error);
75
+ flags = {
76
+ isBot: false,
77
+ isChromeFamily: false,
78
+ isStandalonePWA: false,
79
+ isAppleSilicon: false,
135
80
  };
136
81
  }
137
82
  return {
138
83
  userAgent: {
139
- browser: getBrowserInfo(),
140
- os: getOSInfo(),
141
- device: getDeviceInfo(),
84
+ name: result.ua,
85
+ browser: {
86
+ major: result.browser.major || '',
87
+ name: result.browser.name || '',
88
+ version: result.browser.version || '',
89
+ type: result.browser.type,
90
+ },
91
+ os: {
92
+ name: result.os.name || '',
93
+ version: result.os.version || '',
94
+ },
95
+ device: result.device.model || result.device.type || result.device.vendor
96
+ ? {
97
+ model: result.device.model,
98
+ type: result.device.type,
99
+ vendor: result.device.vendor,
100
+ }
101
+ : undefined,
102
+ engine: {
103
+ name: result.engine.name || '',
104
+ version: result.engine.version || '',
105
+ },
106
+ cpu: {
107
+ architecture: result.cpu.architecture || '',
108
+ },
142
109
  },
143
110
  screenResolution: getScreenResolution(),
144
111
  timeZone: getTimeZone(),
112
+ flags,
145
113
  };
146
114
  }
147
115
  /**
@@ -13,6 +13,9 @@
13
13
  * - token: Authentication token (URL > localStorage)
14
14
  * - funnelSessionId: Active funnel session (URL > cookie)
15
15
  *
16
+ * ⚠️ Note: authCode is NOT handled here - it has highest priority and is handled
17
+ * separately in client.ts before all other initialization logic.
18
+ *
16
19
  * Usage examples:
17
20
  * - Force production API: ?tagadaClientEnv=production
18
21
  * - Force development API: ?tagadaClientEnv=development
@@ -20,6 +23,7 @@
20
23
  * - Custom API URL: ?tagadaClientBaseUrl=https://tagada.loclx.io
21
24
  * - Combined: ?tagadaClientEnv=local&tagadaClientBaseUrl=https://tagada.loclx.io
22
25
  * - Hard reset + production: ?forceReset=true&tagadaClientEnv=production
26
+ * - Cross-domain auth: ?authCode=ah_... (automatically handled, highest priority)
23
27
  */
24
28
  /**
25
29
  * SDK Override Parameters - centralized across all SDK functions
@@ -13,6 +13,9 @@
13
13
  * - token: Authentication token (URL > localStorage)
14
14
  * - funnelSessionId: Active funnel session (URL > cookie)
15
15
  *
16
+ * ⚠️ Note: authCode is NOT handled here - it has highest priority and is handled
17
+ * separately in client.ts before all other initialization logic.
18
+ *
16
19
  * Usage examples:
17
20
  * - Force production API: ?tagadaClientEnv=production
18
21
  * - Force development API: ?tagadaClientEnv=development
@@ -20,6 +23,7 @@
20
23
  * - Custom API URL: ?tagadaClientBaseUrl=https://tagada.loclx.io
21
24
  * - Combined: ?tagadaClientEnv=local&tagadaClientBaseUrl=https://tagada.loclx.io
22
25
  * - Hard reset + production: ?forceReset=true&tagadaClientEnv=production
26
+ * - Cross-domain auth: ?authCode=ah_... (automatically handled, highest priority)
23
27
  */
24
28
  import { clearClientToken, setClientToken, getClientToken } from './tokenStorage';
25
29
  import { clearFunnelSessionCookie } from './sessionStorage';
@@ -13,7 +13,6 @@ export interface UseCheckoutQueryResult {
13
13
  error: Error | null;
14
14
  isSuccess: boolean;
15
15
  init: (params: CheckoutInitParams) => Promise<{
16
- checkoutUrl: string;
17
16
  checkoutSession: any;
18
17
  checkoutToken: string;
19
18
  }>;