@tagadapay/plugin-sdk 2.2.9 → 2.3.2
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.
- package/README.md +90 -0
- package/dist/react/hooks/useCheckout.js +3 -6
- package/dist/react/providers/TagadaProvider.d.ts +2 -1
- package/dist/react/providers/TagadaProvider.js +12 -4
- package/package.json +1 -1
- package/dist/react/utils/trackingUtils.d.ts +0 -24
- package/dist/react/utils/trackingUtils.js +0 -172
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ A comprehensive React SDK for building plugins on the TagadaPay platform. Create
|
|
|
15
15
|
### Plugin Development
|
|
16
16
|
|
|
17
17
|
- **[Plugin Configuration](./docs/PLUGIN_CONFIG.md)** - How to access store context, config, and branding
|
|
18
|
+
- **[Initialization Modes](#-initialization-modes)** - Choose between blocking and non-blocking initialization
|
|
18
19
|
- **[Google Autocomplete](./docs/README-google-autocomplete.md)** - Address autocomplete with Google Places API
|
|
19
20
|
- **[ISO Data](./docs/README-iso-data.md)** - Country and region data with Google integration
|
|
20
21
|
|
|
@@ -164,6 +165,95 @@ function App() {
|
|
|
164
165
|
export default App;
|
|
165
166
|
```
|
|
166
167
|
|
|
168
|
+
## 🚀 Initialization Modes
|
|
169
|
+
|
|
170
|
+
The TagadaProvider supports two initialization modes to give you control over when your components render:
|
|
171
|
+
|
|
172
|
+
### Non-Blocking Mode (Default - Recommended)
|
|
173
|
+
|
|
174
|
+
```tsx
|
|
175
|
+
<TagadaProvider>
|
|
176
|
+
{/* Children render immediately after config loads */}
|
|
177
|
+
<YourApp />
|
|
178
|
+
</TagadaProvider>
|
|
179
|
+
|
|
180
|
+
// OR explicitly
|
|
181
|
+
<TagadaProvider blockUntilSessionReady={false}>
|
|
182
|
+
<YourApp />
|
|
183
|
+
</TagadaProvider>
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**Flow:**
|
|
187
|
+
1. **Phase 1 & 2** ✅ Plugin config loads → Children render immediately
|
|
188
|
+
2. **Phase 3** 🔄 Session initialization runs in background
|
|
189
|
+
3. **API calls** 🔄 Hooks automatically wait for session to be ready
|
|
190
|
+
|
|
191
|
+
**Benefits:**
|
|
192
|
+
- ⚡ **Faster rendering** - UI appears immediately
|
|
193
|
+
- 🎯 **Better UX** - Show loading states while session initializes
|
|
194
|
+
- 🔄 **Automatic waiting** - Hooks handle session timing for you
|
|
195
|
+
|
|
196
|
+
### Blocking Mode (Legacy Behavior)
|
|
197
|
+
|
|
198
|
+
```tsx
|
|
199
|
+
<TagadaProvider blockUntilSessionReady={true}>
|
|
200
|
+
{/* Children render only after ALL initialization completes */}
|
|
201
|
+
<YourApp />
|
|
202
|
+
</TagadaProvider>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Flow:**
|
|
206
|
+
1. **All Phases** ⏳ Config + Session must complete before children render
|
|
207
|
+
2. **API calls** ✅ Work immediately (no waiting needed)
|
|
208
|
+
|
|
209
|
+
**Use when:**
|
|
210
|
+
- 🔄 **Migrating** from older SDK versions
|
|
211
|
+
- 🎯 **Simple apps** that don't need progressive loading
|
|
212
|
+
|
|
213
|
+
### Console Logs
|
|
214
|
+
|
|
215
|
+
The SDK logs help you understand which mode you're using:
|
|
216
|
+
|
|
217
|
+
**Non-blocking mode:**
|
|
218
|
+
```
|
|
219
|
+
✅ Phase 1 & 2 Complete - Plugin config loaded
|
|
220
|
+
🚀 Non-blocking mode: Children can now render - Phase 3 will continue in background
|
|
221
|
+
🔄 [useCheckout] Waiting for session initialization to complete...
|
|
222
|
+
✅ Phase 3 Complete - Session initialization completed successfully
|
|
223
|
+
✅ [useCheckout] Session initialized, proceeding with checkout init
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Blocking mode:**
|
|
227
|
+
```
|
|
228
|
+
✅ Phase 1 & 2 Complete - Plugin config loaded
|
|
229
|
+
⏳ Blocking mode: Children will render after Phase 3 completes
|
|
230
|
+
✅ Phase 3 Complete - Session initialization completed successfully
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### TagadaProvider API
|
|
234
|
+
|
|
235
|
+
```tsx
|
|
236
|
+
interface TagadaProviderProps {
|
|
237
|
+
children: ReactNode;
|
|
238
|
+
environment?: 'local' | 'development' | 'staging' | 'production';
|
|
239
|
+
customApiConfig?: Partial<EnvironmentConfig>;
|
|
240
|
+
debugMode?: boolean;
|
|
241
|
+
localConfig?: string; // LOCAL DEV ONLY: Override config variant
|
|
242
|
+
blockUntilSessionReady?: boolean; // Default: false
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
| Prop | Type | Default | Description |
|
|
247
|
+
|------|------|---------|-------------|
|
|
248
|
+
| `children` | `ReactNode` | - | Your plugin components |
|
|
249
|
+
| `environment` | `string` | auto-detect | Override environment detection |
|
|
250
|
+
| `customApiConfig` | `object` | - | Custom API configuration |
|
|
251
|
+
| `debugMode` | `boolean` | auto (false in prod) | Enable debug features |
|
|
252
|
+
| `localConfig` | `string` | `'default'` | Config variant for local dev |
|
|
253
|
+
| `blockUntilSessionReady` | `boolean` | `false` | Use legacy blocking behavior |
|
|
254
|
+
|
|
255
|
+
> **Version Compatibility:** The `blockUntilSessionReady` option was added in v2.3.0. For older versions, the blocking behavior was the default and only option.
|
|
256
|
+
|
|
167
257
|
### Development vs Production
|
|
168
258
|
|
|
169
259
|
| Environment | Store/Account ID | Deployment Config | How it Works |
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { useCurrency } from '../hooks/useCurrency';
|
|
3
3
|
import { useTagadaContext } from '../providers/TagadaProvider';
|
|
4
|
-
import { collectTrackingData } from '../utils/trackingUtils';
|
|
5
4
|
import { usePluginConfig } from './usePluginConfig';
|
|
6
5
|
export function useCheckout(options = {}) {
|
|
7
6
|
const { apiService, updateCheckoutDebugData, refreshCoordinator, currency, isSessionInitialized } = useTagadaContext();
|
|
@@ -58,6 +57,8 @@ export function useCheckout(options = {}) {
|
|
|
58
57
|
throw new Error('Cannot initialize new checkout when checkoutToken is already provided. The existing checkout will be auto-loaded.');
|
|
59
58
|
}
|
|
60
59
|
// Wait for CMS session to initialize before making API calls
|
|
60
|
+
// Note: If TagadaProvider has blockUntilSessionReady=true, this component won't render
|
|
61
|
+
// until session is ready, so this wait is mainly for the new non-blocking mode
|
|
61
62
|
if (!isSessionInitialized) {
|
|
62
63
|
console.log('🔄 [useCheckout] Waiting for session initialization to complete...');
|
|
63
64
|
await new Promise((resolve) => {
|
|
@@ -76,13 +77,9 @@ export function useCheckout(options = {}) {
|
|
|
76
77
|
setIsLoading(true);
|
|
77
78
|
setError(null);
|
|
78
79
|
try {
|
|
79
|
-
//
|
|
80
|
-
const trackingData = await collectTrackingData();
|
|
81
|
-
// Enhanced customerMetadata with tracking data
|
|
80
|
+
// Enhanced customerMetadata without tracking data
|
|
82
81
|
const enhancedCustomerMetadata = {
|
|
83
82
|
...params.customerMetadata,
|
|
84
|
-
localStorage: trackingData.localStorageData,
|
|
85
|
-
cookies: trackingData.trackingCookiesData,
|
|
86
83
|
};
|
|
87
84
|
const requestBody = {
|
|
88
85
|
...params,
|
|
@@ -53,8 +53,9 @@ interface TagadaProviderProps {
|
|
|
53
53
|
customApiConfig?: Partial<EnvironmentConfig>;
|
|
54
54
|
debugMode?: boolean;
|
|
55
55
|
localConfig?: string;
|
|
56
|
+
blockUntilSessionReady?: boolean;
|
|
56
57
|
}
|
|
57
58
|
export declare function TagadaProvider({ children, environment, customApiConfig, debugMode, // Remove default, will be set based on environment
|
|
58
|
-
localConfig, }: TagadaProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
59
|
+
localConfig, blockUntilSessionReady, }: TagadaProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
59
60
|
export declare function useTagadaContext(): TagadaContextValue;
|
|
60
61
|
export {};
|
|
@@ -46,7 +46,8 @@ const InitializationLoader = () => (_jsxs("div", { style: {
|
|
|
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
|
-
localConfig,
|
|
49
|
+
localConfig, blockUntilSessionReady = false, // Default to new non-blocking behavior
|
|
50
|
+
}) {
|
|
50
51
|
// LOCAL DEV ONLY: Use localConfig override if in local development, otherwise use default
|
|
51
52
|
const isLocalDev = typeof window !== 'undefined' &&
|
|
52
53
|
(window.location.hostname === 'localhost' ||
|
|
@@ -83,7 +84,12 @@ localConfig, }) {
|
|
|
83
84
|
basePath: config.basePath,
|
|
84
85
|
hasConfig: !!config.config,
|
|
85
86
|
});
|
|
86
|
-
|
|
87
|
+
if (blockUntilSessionReady) {
|
|
88
|
+
console.log('⏳ Blocking mode: Children will render after Phase 3 (session init) completes');
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
console.log('🚀 Non-blocking mode: Children can now render - Phase 3 (session init) will continue in background');
|
|
92
|
+
}
|
|
87
93
|
}
|
|
88
94
|
catch (error) {
|
|
89
95
|
console.error('❌ Failed to load plugin config in TagadaProvider:', error);
|
|
@@ -517,9 +523,11 @@ localConfig, }) {
|
|
|
517
523
|
]);
|
|
518
524
|
// Determine if we should show loading
|
|
519
525
|
// Phase 1 & 2 are mandatory: config loading and storeId availability
|
|
520
|
-
// Phase 3 (session initialization) is
|
|
526
|
+
// Phase 3 (session initialization) is optional/non-blocking by default
|
|
521
527
|
const shouldShowLoading = configLoading || (!storeId && configLoading);
|
|
522
|
-
const canRenderChildren =
|
|
528
|
+
const canRenderChildren = blockUntilSessionReady
|
|
529
|
+
? (!configLoading && storeId && isInitialized) // Old behavior: wait for all phases
|
|
530
|
+
: (!configLoading && storeId); // New behavior: render after phases 1 & 2
|
|
523
531
|
return (_jsxs(TagadaContext.Provider, { value: contextValue, children: [shouldShowLoading && _jsx(InitializationLoader, {}), finalDebugMode && canRenderChildren && (_jsxs(_Fragment, { children: [_jsx("button", { onClick: () => setIsDebugDrawerOpen(true), style: {
|
|
524
532
|
position: 'fixed',
|
|
525
533
|
bottom: '16px',
|
package/package.json
CHANGED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export interface TrackingData {
|
|
2
|
-
trackingCookiesData: Record<string, string>;
|
|
3
|
-
localStorageData: Record<string, string>;
|
|
4
|
-
}
|
|
5
|
-
/**
|
|
6
|
-
* Define pixel tracking cookie patterns for various platforms
|
|
7
|
-
*/
|
|
8
|
-
export declare const trackingCookiePatterns: RegExp[];
|
|
9
|
-
/**
|
|
10
|
-
* Function to get cookies with retry logic
|
|
11
|
-
*/
|
|
12
|
-
export declare const getCookiesWithRetry: (maxRetries?: number, delay?: number) => Promise<string[]>;
|
|
13
|
-
/**
|
|
14
|
-
* Collect localStorage data as dictionary
|
|
15
|
-
*/
|
|
16
|
-
export declare const getLocalStorageData: () => Record<string, string>;
|
|
17
|
-
/**
|
|
18
|
-
* Collect tracking cookies data based on defined patterns
|
|
19
|
-
*/
|
|
20
|
-
export declare const getTrackingCookiesData: () => Promise<Record<string, string>>;
|
|
21
|
-
/**
|
|
22
|
-
* Collect all tracking data (localStorage and cookies)
|
|
23
|
-
*/
|
|
24
|
-
export declare const collectTrackingData: () => Promise<TrackingData>;
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Define pixel tracking cookie patterns for various platforms
|
|
3
|
-
*/
|
|
4
|
-
export const trackingCookiePatterns = [
|
|
5
|
-
// Meta/Facebook pixels
|
|
6
|
-
/^_fbp/,
|
|
7
|
-
/^_fbc/,
|
|
8
|
-
/^fr$/,
|
|
9
|
-
/^_fbq/,
|
|
10
|
-
/^fbq/,
|
|
11
|
-
/^sb$/,
|
|
12
|
-
// Google Analytics & Ads
|
|
13
|
-
/^_ga/,
|
|
14
|
-
/^_gid/,
|
|
15
|
-
/^_gcl_au/,
|
|
16
|
-
/^_gac_/,
|
|
17
|
-
/^_gtag/,
|
|
18
|
-
/^_gat/,
|
|
19
|
-
/^_dc_gtm_/,
|
|
20
|
-
/^_gtm/,
|
|
21
|
-
// Google Ads
|
|
22
|
-
/^_gcl_/,
|
|
23
|
-
/^_gclid/,
|
|
24
|
-
/^_gclsrc/,
|
|
25
|
-
// Snapchat
|
|
26
|
-
/^_scid/,
|
|
27
|
-
/^_sctr/,
|
|
28
|
-
/^_schn/,
|
|
29
|
-
/^_scpx/,
|
|
30
|
-
// TikTok
|
|
31
|
-
/^_ttp/,
|
|
32
|
-
/^_tt_enable_cookie/,
|
|
33
|
-
/^_ttclid/,
|
|
34
|
-
/^_tta/,
|
|
35
|
-
// Pinterest
|
|
36
|
-
/^_pin/,
|
|
37
|
-
/^_pinterest_/,
|
|
38
|
-
/^_pinid/,
|
|
39
|
-
// Twitter/X
|
|
40
|
-
/^_twitter/,
|
|
41
|
-
/^_twid/,
|
|
42
|
-
/^muc_ads/,
|
|
43
|
-
// LinkedIn
|
|
44
|
-
/^_li/,
|
|
45
|
-
/^AnalyticsSyncHistory/,
|
|
46
|
-
/^bcookie/,
|
|
47
|
-
/^bscookie/,
|
|
48
|
-
// Microsoft/Bing
|
|
49
|
-
/^_uetsid/,
|
|
50
|
-
/^_uetvid/,
|
|
51
|
-
/^MUID/,
|
|
52
|
-
/^_msdcs/,
|
|
53
|
-
// Amazon
|
|
54
|
-
/^ad-id/,
|
|
55
|
-
/^ad-privacy/,
|
|
56
|
-
// CVG tracking
|
|
57
|
-
/^__cvg_uid/,
|
|
58
|
-
/^__cvg_sid/,
|
|
59
|
-
/^__cvg_session/,
|
|
60
|
-
// Shopify tracking
|
|
61
|
-
/^_shopify_y/,
|
|
62
|
-
/^_shopify_s/,
|
|
63
|
-
/^_shopify_ga/,
|
|
64
|
-
/^_shopify_ga_/,
|
|
65
|
-
/^_shopify_sa_p/,
|
|
66
|
-
/^_shopify_sa_t/,
|
|
67
|
-
// General tracking
|
|
68
|
-
/^_awc/,
|
|
69
|
-
/^_aw_/,
|
|
70
|
-
/^utm_/,
|
|
71
|
-
/^_clck/,
|
|
72
|
-
/^_clsk/,
|
|
73
|
-
];
|
|
74
|
-
/**
|
|
75
|
-
* Function to get cookies with retry logic
|
|
76
|
-
*/
|
|
77
|
-
export const getCookiesWithRetry = async (maxRetries = 3, delay = 100) => {
|
|
78
|
-
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
79
|
-
try {
|
|
80
|
-
const cookies = document.cookie.split('; ');
|
|
81
|
-
if (cookies.length > 0 && cookies[0] !== '') {
|
|
82
|
-
return cookies;
|
|
83
|
-
}
|
|
84
|
-
// If no cookies found, wait and retry
|
|
85
|
-
if (attempt < maxRetries) {
|
|
86
|
-
console.log(`Cookie collection attempt ${attempt} failed, retrying in ${delay}ms...`);
|
|
87
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
catch (error) {
|
|
91
|
-
console.warn(`Cookie collection attempt ${attempt} failed:`, error);
|
|
92
|
-
if (attempt < maxRetries) {
|
|
93
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return [];
|
|
98
|
-
};
|
|
99
|
-
/**
|
|
100
|
-
* Collect localStorage data as dictionary
|
|
101
|
-
*/
|
|
102
|
-
export const getLocalStorageData = () => {
|
|
103
|
-
const localStorageData = {};
|
|
104
|
-
try {
|
|
105
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
106
|
-
const key = localStorage.key(i);
|
|
107
|
-
if (key) {
|
|
108
|
-
localStorageData[key] = localStorage.getItem(key) || '';
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
catch (error) {
|
|
113
|
-
console.warn('Failed to read localStorage:', error);
|
|
114
|
-
}
|
|
115
|
-
return localStorageData;
|
|
116
|
-
};
|
|
117
|
-
/**
|
|
118
|
-
* Collect tracking cookies data based on defined patterns
|
|
119
|
-
*/
|
|
120
|
-
export const getTrackingCookiesData = async () => {
|
|
121
|
-
const trackingCookiesData = {};
|
|
122
|
-
try {
|
|
123
|
-
// Get cookies with retry logic
|
|
124
|
-
const cookies = await getCookiesWithRetry();
|
|
125
|
-
if (cookies.length === 0) {
|
|
126
|
-
console.warn('No cookies found after retry attempts');
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
console.log(`Successfully collected ${cookies.length} cookies`);
|
|
130
|
-
}
|
|
131
|
-
cookies.forEach((cookie) => {
|
|
132
|
-
const [key, ...valueParts] = cookie.split('=');
|
|
133
|
-
const value = valueParts.join('='); // Handle values that might contain =
|
|
134
|
-
if (key && trackingCookiePatterns.some((pattern) => pattern.test(key))) {
|
|
135
|
-
try {
|
|
136
|
-
trackingCookiesData[key] = decodeURIComponent(value || '');
|
|
137
|
-
}
|
|
138
|
-
catch (error) {
|
|
139
|
-
// If decoding fails, use raw value
|
|
140
|
-
trackingCookiesData[key] = value || '';
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
// Log specific cookies we're looking for
|
|
145
|
-
const importantCookies = ['_shopify_y', '__cvg_uid', '_fbp', '_ga'];
|
|
146
|
-
importantCookies.forEach((cookieName) => {
|
|
147
|
-
if (trackingCookiesData[cookieName]) {
|
|
148
|
-
console.log(`Found ${cookieName}:`, trackingCookiesData[cookieName]);
|
|
149
|
-
}
|
|
150
|
-
else {
|
|
151
|
-
console.log(`Missing ${cookieName} cookie`);
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
catch (error) {
|
|
156
|
-
console.warn('Failed to read tracking cookies:', error);
|
|
157
|
-
}
|
|
158
|
-
return trackingCookiesData;
|
|
159
|
-
};
|
|
160
|
-
/**
|
|
161
|
-
* Collect all tracking data (localStorage and cookies)
|
|
162
|
-
*/
|
|
163
|
-
export const collectTrackingData = async () => {
|
|
164
|
-
const [localStorageData, trackingCookiesData] = await Promise.all([
|
|
165
|
-
Promise.resolve(getLocalStorageData()),
|
|
166
|
-
getTrackingCookiesData(),
|
|
167
|
-
]);
|
|
168
|
-
return {
|
|
169
|
-
localStorageData,
|
|
170
|
-
trackingCookiesData,
|
|
171
|
-
};
|
|
172
|
-
};
|