@tagadapay/plugin-sdk 3.1.11 → 3.1.22

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.
Files changed (116) hide show
  1. package/README.md +1129 -1129
  2. package/build-cdn.js +231 -228
  3. package/dist/data/iso3166.d.ts +23 -33
  4. package/dist/data/iso3166.js +134 -198
  5. package/dist/data/languages.d.ts +5 -64
  6. package/dist/data/languages.js +23 -143
  7. package/dist/external-tracker.js +968 -102
  8. package/dist/external-tracker.min.js +2 -2
  9. package/dist/external-tracker.min.js.map +4 -4
  10. package/dist/react/hooks/useISOData.js +1 -1
  11. package/dist/react/hooks/usePaymentPolling.d.ts +3 -3
  12. package/dist/react/hooks/useShippingRates.d.ts +6 -0
  13. package/dist/react/hooks/useShippingRates.js +38 -0
  14. package/dist/react/providers/TagadaProvider.js +5 -5
  15. package/dist/react/services/apiService.d.ts +21 -0
  16. package/dist/react/services/apiService.js +10 -0
  17. package/dist/tagada-sdk.js +2079 -179
  18. package/dist/tagada-sdk.min.js +4 -2
  19. package/dist/tagada-sdk.min.js.map +4 -4
  20. package/dist/v2/core/client.d.ts +4 -2
  21. package/dist/v2/core/client.js +4 -3
  22. package/dist/v2/core/errors.d.ts +75 -0
  23. package/dist/v2/core/errors.js +104 -0
  24. package/dist/v2/core/funnelClient.d.ts +16 -15
  25. package/dist/v2/core/funnelClient.js +1 -1
  26. package/dist/v2/core/index.d.ts +1 -0
  27. package/dist/v2/core/index.js +2 -0
  28. package/dist/v2/core/pixelMapping.d.ts +49 -0
  29. package/dist/v2/core/pixelMapping.js +325 -0
  30. package/dist/v2/core/resources/apiClient.d.ts +2 -0
  31. package/dist/v2/core/resources/apiClient.js +52 -9
  32. package/dist/v2/core/resources/checkout.d.ts +89 -30
  33. package/dist/v2/core/resources/checkout.js +8 -0
  34. package/dist/v2/core/resources/customer.d.ts +20 -19
  35. package/dist/v2/core/resources/funnel.d.ts +17 -17
  36. package/dist/v2/core/resources/payments.d.ts +84 -13
  37. package/dist/v2/core/resources/payments.js +26 -9
  38. package/dist/v2/core/resources/shippingRates.d.ts +15 -0
  39. package/dist/v2/core/resources/shippingRates.js +11 -0
  40. package/dist/v2/core/types.d.ts +50 -12
  41. package/dist/v2/core/types.js +0 -3
  42. package/dist/v2/core/utils/checkout.d.ts +2 -2
  43. package/dist/v2/core/utils/checkout.js +7 -2
  44. package/dist/v2/core/utils/order.d.ts +11 -9
  45. package/dist/v2/core/utils/previewModeIndicator.js +101 -101
  46. package/dist/v2/index.d.ts +4 -2
  47. package/dist/v2/index.js +1 -1
  48. package/dist/v2/react/components/ApplePayButton.js +13 -4
  49. package/dist/v2/react/components/FunnelScriptInjector.js +51 -30
  50. package/dist/v2/react/components/WhopCheckout.d.ts +24 -0
  51. package/dist/v2/react/components/WhopCheckout.js +231 -0
  52. package/dist/v2/react/hooks/__examples__/FunnelContextExample.js +1 -1
  53. package/dist/v2/react/hooks/payment-actions/useAirwallexRadarAction.d.ts +14 -0
  54. package/dist/v2/react/hooks/payment-actions/useAirwallexRadarAction.js +181 -0
  55. package/dist/v2/react/hooks/payment-actions/useErrorAction.d.ts +9 -0
  56. package/dist/v2/react/hooks/payment-actions/useErrorAction.js +21 -0
  57. package/dist/v2/react/hooks/payment-actions/useFinixRadarAction.d.ts +14 -0
  58. package/dist/v2/react/hooks/payment-actions/useFinixRadarAction.js +187 -0
  59. package/dist/v2/react/hooks/payment-actions/useKessPayAction.d.ts +11 -0
  60. package/dist/v2/react/hooks/payment-actions/useKessPayAction.js +91 -0
  61. package/dist/v2/react/hooks/payment-actions/useMasterCardAction.d.ts +24 -0
  62. package/dist/v2/react/hooks/payment-actions/useMasterCardAction.js +221 -0
  63. package/dist/v2/react/hooks/payment-actions/usePaymentActionHandler.d.ts +15 -0
  64. package/dist/v2/react/hooks/payment-actions/usePaymentActionHandler.js +142 -0
  65. package/dist/v2/react/hooks/payment-actions/useProcessorAuthAction.d.ts +3 -0
  66. package/dist/v2/react/hooks/payment-actions/useProcessorAuthAction.js +13 -0
  67. package/dist/v2/react/hooks/payment-actions/useRedirectAction.d.ts +10 -0
  68. package/dist/v2/react/hooks/payment-actions/useRedirectAction.js +35 -0
  69. package/dist/v2/react/hooks/payment-actions/useStripeRadarAction.d.ts +14 -0
  70. package/dist/v2/react/hooks/payment-actions/useStripeRadarAction.js +192 -0
  71. package/dist/v2/react/hooks/payment-actions/useThreedsAuthAction.d.ts +14 -0
  72. package/dist/v2/react/hooks/payment-actions/useThreedsAuthAction.js +81 -0
  73. package/dist/v2/react/hooks/payment-actions/useTrustFlowAction.d.ts +11 -0
  74. package/dist/v2/react/hooks/payment-actions/useTrustFlowAction.js +84 -0
  75. package/dist/v2/react/hooks/payment-processing/usePaymentInstruments.d.ts +14 -0
  76. package/dist/v2/react/hooks/payment-processing/usePaymentInstruments.js +36 -0
  77. package/dist/v2/react/hooks/payment-processing/usePaymentProcessors.d.ts +31 -0
  78. package/dist/v2/react/hooks/payment-processing/usePaymentProcessors.js +212 -0
  79. package/dist/v2/react/hooks/payment-redirect/useAirwallex3dsReturn.d.ts +14 -0
  80. package/dist/v2/react/hooks/payment-redirect/useAirwallex3dsReturn.js +207 -0
  81. package/dist/v2/react/hooks/payment-redirect/useGenericPaymentReturn.d.ts +12 -0
  82. package/dist/v2/react/hooks/payment-redirect/useGenericPaymentReturn.js +101 -0
  83. package/dist/v2/react/hooks/useCheckoutQuery.d.ts +6 -0
  84. package/dist/v2/react/hooks/useCheckoutQuery.js +45 -0
  85. package/dist/v2/react/hooks/useFunnel.d.ts +1 -2
  86. package/dist/v2/react/hooks/useGeoLocation.d.ts +2 -1
  87. package/dist/v2/react/hooks/useGeoLocation.js +4 -2
  88. package/dist/v2/react/hooks/useGoogleAutocomplete.js +82 -33
  89. package/dist/v2/react/hooks/useISOData.js +1 -1
  90. package/dist/v2/react/hooks/usePaymentPolling.d.ts +3 -3
  91. package/dist/v2/react/hooks/usePaymentQuery.d.ts +18 -5
  92. package/dist/v2/react/hooks/usePaymentQuery.js +63 -1015
  93. package/dist/v2/react/hooks/usePaymentRetrieve.d.ts +3 -2
  94. package/dist/v2/react/hooks/usePaymentRetrieve.js +3 -1
  95. package/dist/v2/react/hooks/usePixelTracking.d.ts +5 -43
  96. package/dist/v2/react/hooks/usePixelTracking.js +213 -407
  97. package/dist/v2/react/hooks/useShippingRatesQuery.d.ts +6 -0
  98. package/dist/v2/react/hooks/useShippingRatesQuery.js +47 -4
  99. package/dist/v2/react/hooks/useStepConfig.d.ts +2 -8
  100. package/dist/v2/react/hooks/useStepConfig.js +1 -1
  101. package/dist/v2/react/hooks/useWhopPaymentPolling.d.ts +30 -0
  102. package/dist/v2/react/hooks/useWhopPaymentPolling.js +61 -0
  103. package/dist/v2/react/index.d.ts +7 -0
  104. package/dist/v2/react/index.js +4 -0
  105. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.d.ts +2 -1
  106. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +3 -1
  107. package/dist/v2/react/providers/TagadaProvider.js +76 -7
  108. package/dist/v2/standalone/external-tracker.d.ts +52 -46
  109. package/dist/v2/standalone/external-tracker.js +205 -98
  110. package/dist/v2/standalone/index.d.ts +22 -0
  111. package/dist/v2/standalone/index.js +125 -0
  112. package/package.json +112 -112
  113. package/dist/react/utils/__tests__/urlUtils.test.d.ts +0 -1
  114. package/dist/react/utils/__tests__/urlUtils.test.js +0 -189
  115. package/dist/v2/core/__tests__/pathRemapping.test.d.ts +0 -11
  116. package/dist/v2/core/__tests__/pathRemapping.test.js +0 -776
@@ -4,18 +4,16 @@ import { jsx as _jsx } from "react/jsx-runtime";
4
4
  * usePixelTracking Hook & Provider
5
5
  *
6
6
  * SDK-level pixel tracking based on runtime stepConfig.pixels injected
7
- * by the CRM. This mirrors the CMS pixel context pattern but uses the
8
- * funnel step configuration as the source of truth instead of store
9
- * integrations.
7
+ * by the CRM. Uses core/pixelMapping for event mapping and gating,
8
+ * keeping browser-specific init/fire logic here.
10
9
  */
11
10
  import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from 'react';
12
- import { minorUnitsToMajorUnits } from '../../../react/utils/money';
11
+ import { resolvePixelEvents, } from '../../core/pixelMapping';
13
12
  import { useStepConfig } from './useStepConfig';
14
13
  const PixelTrackingContext = createContext(null);
15
- /**
16
- * Simple per-page duplicate guard (time-window based)
17
- * Avoids accidental double-fires during rerenders.
18
- */
14
+ // ---------------------------------------------------------------------------
15
+ // Duplicate guard (time-window based)
16
+ // ---------------------------------------------------------------------------
19
17
  function createDuplicateGuard(windowMs) {
20
18
  const lastEvents = new Map();
21
19
  return (eventName, parameters) => {
@@ -29,480 +27,288 @@ function createDuplicateGuard(windowMs) {
29
27
  return true;
30
28
  }
31
29
  catch {
32
- // If hashing fails for any reason, just allow the event
33
30
  return true;
34
31
  }
35
32
  };
36
33
  }
37
34
  const shouldTrackEvent = createDuplicateGuard(5000);
38
- /**
39
- * Provider that initializes pixels based on stepConfig.pixels
40
- * and exposes a simple track(event, params) API.
41
- */
35
+ // ---------------------------------------------------------------------------
36
+ // Provider
37
+ // ---------------------------------------------------------------------------
42
38
  export function PixelTrackingProvider({ children }) {
43
39
  const { pixels } = useStepConfig();
44
40
  const [pixelsInitialized, setPixelsInitialized] = useState(false);
45
41
  const isMountedRef = useRef(true);
46
42
  useEffect(() => {
47
43
  isMountedRef.current = true;
48
- return () => {
49
- isMountedRef.current = false;
50
- };
44
+ return () => { isMountedRef.current = false; };
51
45
  }, []);
52
- // Initialize pixels once when configuration is available
46
+ // ---- Initialize pixel scripts once ----
53
47
  useEffect(() => {
54
48
  if (!pixels || pixelsInitialized || !isMountedRef.current)
55
49
  return;
56
50
  try {
57
- // Facebook / Meta - support multiple pixels
58
- const facebookPixels = pixels.facebook; // Support both 'facebook' and legacy 'meta'
59
- if (facebookPixels) {
60
- if (Array.isArray(facebookPixels)) {
61
- facebookPixels.forEach((pixel) => {
62
- if (pixel.enabled && pixel.pixelId) {
63
- initMetaPixel(pixel.pixelId);
64
- }
65
- });
66
- }
67
- }
68
- // TikTok - support multiple pixels
69
- const tiktokPixels = pixels.tiktok;
70
- if (tiktokPixels) {
71
- if (Array.isArray(tiktokPixels)) {
72
- tiktokPixels.forEach((pixel) => {
73
- if (pixel.enabled && pixel.pixelId) {
74
- initTikTokPixel(pixel.pixelId);
75
- }
76
- });
77
- }
78
- }
79
- // Snapchat - support multiple pixels
80
- const snapchatPixels = pixels.snapchat;
81
- if (snapchatPixels) {
82
- if (Array.isArray(snapchatPixels)) {
83
- snapchatPixels.forEach((pixel) => {
84
- if (pixel.enabled && pixel.pixelId) {
85
- initSnapchatPixel(pixel.pixelId);
86
- }
87
- });
88
- }
89
- }
90
- // GTM - support multiple containers
91
- const gtmPixels = pixels.gtm;
92
- if (gtmPixels) {
93
- if (Array.isArray(gtmPixels)) {
94
- gtmPixels.forEach((pixel) => {
95
- if (pixel.enabled && pixel.containerId) {
96
- initGTM(pixel.containerId);
97
- }
98
- });
99
- }
100
- }
101
- if (isMountedRef.current) {
51
+ pixels.facebook?.forEach((px) => { if (px.enabled && px.pixelId)
52
+ initMetaPixel(px.pixelId); });
53
+ pixels.tiktok?.forEach((px) => { if (px.enabled && px.pixelId)
54
+ initTikTokPixel(px.pixelId); });
55
+ pixels.snapchat?.forEach((px) => { if (px.enabled && px.pixelId)
56
+ initSnapchatPixel(px.pixelId); });
57
+ pixels.pinterest?.forEach((px) => { if (px.enabled && px.pixelId)
58
+ initPinterestPixel(px.pixelId); });
59
+ pixels.gtm?.forEach((px) => { if (px.enabled && px.containerId)
60
+ initGTM(px.containerId); });
61
+ if (isMountedRef.current)
102
62
  setPixelsInitialized(true);
103
- }
104
63
  }
105
64
  catch (error) {
106
- // Fail-safe: never break the host page because of pixel issues
107
- if (typeof console !== 'undefined') {
108
- console.error('[SDK Pixels] Error during pixel initialization:', error);
109
- }
65
+ console.error('[SDK Pixels] Initialization error:', error);
110
66
  }
111
67
  }, [pixels, pixelsInitialized]);
68
+ // ---- Track function ----
112
69
  const track = useCallback((eventName, parameters = {}) => {
113
- if (!pixels || !pixelsInitialized || !isMountedRef.current) {
70
+ if (!pixels || !pixelsInitialized || !isMountedRef.current)
114
71
  return;
115
- }
116
- // Duplicate guard
117
- if (!shouldTrackEvent(eventName, parameters)) {
72
+ if (!shouldTrackEvent(eventName, parameters))
118
73
  return;
119
- }
120
74
  try {
121
- // Facebook / Meta - track to all enabled pixels
122
- const facebookPixels = pixels.facebook; // Support both 'facebook' and legacy 'meta'
123
- if (facebookPixels) {
124
- const pixelArray = Array.isArray(facebookPixels)
125
- ? facebookPixels
126
- : [facebookPixels];
127
- const enabledPixels = pixelArray.filter((p) => p.enabled);
128
- if (enabledPixels.length > 0) {
129
- const { name, params } = mapMetaEvent(eventName, parameters);
130
- // Track to all enabled Facebook pixels
131
- enabledPixels.forEach(() => {
132
- trackMetaEvent(name, params);
133
- });
134
- }
135
- }
136
- // TikTok - track to all enabled pixels
137
- const tiktokPixels = pixels.tiktok;
138
- if (tiktokPixels) {
139
- const pixelArray = Array.isArray(tiktokPixels)
140
- ? tiktokPixels
141
- : [tiktokPixels];
142
- const enabledPixels = pixelArray.filter((p) => p.enabled);
143
- if (enabledPixels.length > 0) {
144
- const { name, params } = mapTikTokEvent(eventName, parameters);
145
- // Track to all enabled TikTok pixels
146
- enabledPixels.forEach(() => {
147
- trackTikTokEvent(name, params);
148
- });
149
- }
150
- }
151
- // Snapchat - track to all enabled pixels
152
- const snapchatPixels = pixels.snapchat;
153
- if (snapchatPixels) {
154
- const pixelArray = Array.isArray(snapchatPixels)
155
- ? snapchatPixels
156
- : [snapchatPixels];
157
- const enabledPixels = pixelArray.filter((p) => p.enabled);
158
- if (enabledPixels.length > 0) {
159
- const { name, params } = mapSnapchatEvent(eventName, parameters);
160
- // Track to all enabled Snapchat pixels
161
- enabledPixels.forEach(() => {
162
- trackSnapchatEvent(name, params);
163
- });
164
- }
165
- }
166
- // GTM - track to all enabled containers
167
- const gtmPixels = pixels.gtm;
168
- if (gtmPixels) {
169
- const pixelArray = Array.isArray(gtmPixels)
170
- ? gtmPixels
171
- : [gtmPixels];
172
- const enabledPixels = pixelArray.filter((p) => p.enabled);
173
- if (enabledPixels.length > 0) {
174
- const { name, params } = mapGTMEvent(eventName, parameters);
175
- // Track to all enabled GTM containers
176
- enabledPixels.forEach(() => {
177
- console.log('trackGTMEvent', name, params);
178
- trackGTMEvent(name, params);
179
- });
180
- }
75
+ const events = resolvePixelEvents(pixels, eventName, parameters);
76
+ for (const { provider, mapped } of events) {
77
+ fire(provider, mapped.name, mapped.params);
181
78
  }
182
79
  }
183
80
  catch (error) {
184
- if (typeof console !== 'undefined') {
185
- console.error('[SDK Pixels] Error tracking pixel event:', error);
186
- }
81
+ console.error('[SDK Pixels] Tracking error:', error);
187
82
  }
188
83
  }, [pixels, pixelsInitialized]);
189
- // Track page views automatically when pixels are initialized
84
+ // ---- Auto page-view ----
190
85
  useEffect(() => {
191
86
  if (!pixelsInitialized || !isMountedRef.current)
192
87
  return;
193
- // Small delay to ensure we don't double fire during strict mode remounts
194
- const pageViewTimeoutId = setTimeout(() => {
88
+ const id = setTimeout(() => {
195
89
  if (isMountedRef.current) {
196
- track('PageView', {
197
- path: typeof window !== 'undefined' ? window.location.pathname : '',
198
- });
90
+ track('PageView', { path: typeof window !== 'undefined' ? window.location.pathname : '' });
199
91
  }
200
92
  }, 0);
201
- return () => {
202
- if (pageViewTimeoutId) {
203
- clearTimeout(pageViewTimeoutId);
204
- }
205
- };
93
+ return () => clearTimeout(id);
206
94
  }, [pixelsInitialized, track]);
207
- const value = useMemo(() => ({
208
- track,
209
- pixelsInitialized,
210
- }), [track, pixelsInitialized]);
95
+ const value = useMemo(() => ({ track, pixelsInitialized }), [track, pixelsInitialized]);
211
96
  return _jsx(PixelTrackingContext.Provider, { value: value, children: children });
212
97
  }
213
- /**
214
- * Hook to access SDK pixel tracking.
215
- * Must be used within TagadaProvider (which wraps PixelTrackingProvider).
216
- */
217
98
  export function usePixelTracking() {
218
99
  const context = useContext(PixelTrackingContext);
219
- if (!context) {
100
+ if (!context)
220
101
  throw new Error('usePixelTracking must be used within a PixelTrackingProvider');
221
- }
222
102
  return context;
223
103
  }
224
- function initMetaPixel(pixelId) {
104
+ // ---------------------------------------------------------------------------
105
+ // Browser-specific: fire an event to the correct global pixel function
106
+ // ---------------------------------------------------------------------------
107
+ /* eslint-disable @typescript-eslint/no-explicit-any */
108
+ function fire(provider, name, params) {
225
109
  if (typeof window === 'undefined')
226
110
  return;
227
- if (window.fbq)
228
- return;
229
- // Standard Facebook Pixel bootstrap
230
- (function (f, b, e) {
231
- if (f.fbq)
232
- return;
233
- const n = function (...args) {
234
- if (n.callMethod) {
235
- n.callMethod(...args);
111
+ const w = window;
112
+ switch (provider) {
113
+ case 'facebook':
114
+ w.fbq?.('track', name, params);
115
+ break;
116
+ case 'tiktok':
117
+ // TikTok handles page views via ttq.page(), not ttq.track('Pageview')
118
+ if (name === 'Pageview') {
119
+ w.ttq?.page?.();
236
120
  }
237
121
  else {
238
- n.queue.push(args);
122
+ w.ttq?.track?.(name, params);
239
123
  }
240
- };
241
- f.fbq = n;
242
- if (!f._fbq)
243
- f._fbq = n;
244
- n.queue = n.queue ?? [];
245
- n.loaded = true;
246
- n.version = '2.0';
247
- n.queue = [];
248
- const t = b.createElement(e);
249
- t.async = true;
250
- t.src = 'https://connect.facebook.net/en_US/fbevents.js';
251
- const s = b.getElementsByTagName(e)[0];
252
- s.parentNode?.insertBefore(t, s);
253
- })(window, document, 'script');
254
- const fbq = window.fbq;
255
- if (fbq) {
256
- fbq('init', pixelId);
124
+ break;
125
+ case 'snapchat':
126
+ w.snaptr?.('track', name, params);
127
+ break;
128
+ case 'pinterest':
129
+ // Pinterest handles page views via pintrk('page'), not pintrk('track', 'pagevisit')
130
+ if (name === 'pagevisit') {
131
+ w.pintrk?.('page');
132
+ }
133
+ else {
134
+ w.pintrk?.('track', name, params);
135
+ }
136
+ break;
137
+ case 'gtm':
138
+ fireGTM(name, params);
139
+ break;
140
+ }
141
+ }
142
+ function fireGTM(event, params) {
143
+ try {
144
+ const w = window;
145
+ if (w.gtag) {
146
+ w.gtag('event', event, params);
147
+ }
148
+ else if (w.dataLayer) {
149
+ w.dataLayer.push({ event, ...params });
150
+ }
151
+ }
152
+ catch (error) {
153
+ console.error('[SDK GTM] Error:', error);
257
154
  }
258
155
  }
259
- function trackMetaEvent(name, params) {
260
- if (typeof window === 'undefined' || !window.fbq)
156
+ /* eslint-enable @typescript-eslint/no-explicit-any */
157
+ // ===========================================================================
158
+ // Pixel initialization (browser-only)
159
+ // All pixel globals are accessed via `win` typed as `any` to avoid
160
+ // `declare global` augmentation issues across tsconfig scopes.
161
+ // ===========================================================================
162
+ /* eslint-disable @typescript-eslint/no-explicit-any */
163
+ const win = (typeof window !== 'undefined' ? window : undefined);
164
+ function initMetaPixel(pixelId) {
165
+ if (!win || win.fbq)
261
166
  return;
262
- const fbq = window.fbq;
263
- fbq?.('track', name, params);
167
+ const n = function (...args) {
168
+ if (n.callMethod)
169
+ n.callMethod(...args);
170
+ else
171
+ n.queue.push(args);
172
+ };
173
+ n.queue = [];
174
+ n.loaded = true;
175
+ n.version = '2.0';
176
+ win.fbq = n;
177
+ if (!win._fbq)
178
+ win._fbq = n;
179
+ const t = document.createElement('script');
180
+ t.async = true;
181
+ t.src = 'https://connect.facebook.net/en_US/fbevents.js';
182
+ const s = document.getElementsByTagName('script')[0];
183
+ s?.parentNode?.insertBefore(t, s);
184
+ win.fbq('init', pixelId);
264
185
  }
265
186
  function initTikTokPixel(pixelId) {
266
- if (typeof window === 'undefined')
267
- return;
268
- if (window.ttq)
187
+ if (!win)
269
188
  return;
270
- (function (w, d, t) {
271
- const ttq = w.ttq ||
272
- function (...args) {
273
- ttq.queue.push(args);
274
- };
275
- if (!ttq.queue) {
276
- ttq.queue = [];
277
- }
278
- w.ttq = ttq;
189
+ // Initialize TikTok base code once (mirrors official TikTok snippet)
190
+ if (!win.ttq) {
191
+ win.TiktokAnalyticsObject = 'ttq';
192
+ const ttq = (win.ttq = win.ttq || []);
279
193
  ttq.methods = [
280
- 'page',
281
- 'track',
282
- 'identify',
283
- 'instances',
284
- 'debug',
285
- 'on',
286
- 'off',
287
- 'once',
288
- 'ready',
289
- 'alias',
290
- 'group',
291
- 'enableCookie',
292
- 'disableCookie',
194
+ 'page', 'track', 'identify', 'instances', 'debug', 'on', 'off',
195
+ 'once', 'ready', 'alias', 'group', 'enableCookie', 'disableCookie',
196
+ 'holdConsent', 'revokeConsent', 'grantConsent',
293
197
  ];
294
- ttq.setAndDefer = function (target, method) {
295
- target[method] = function (...args) {
296
- target.queue.push([method, ...args]);
297
- };
198
+ ttq.setAndDefer = function (t, e) {
199
+ t[e] = function (...args) { t.push([e, ...args]); };
298
200
  };
299
201
  for (const method of ttq.methods) {
300
202
  ttq.setAndDefer(ttq, method);
301
203
  }
302
- ttq.load = function (e) {
303
- const n = 'https://analytics.tiktok.com/i18n/pixel/events.js';
304
- const a = d.createElement(t);
305
- a.type = 'text/javascript';
306
- a.async = true;
307
- a.src = n + '?sdkid=' + e + '&lib=ttq';
308
- const s = d.getElementsByTagName(t)[0];
309
- s.parentNode.insertBefore(a, s);
204
+ ttq.instance = function (t) {
205
+ const e = ttq._i[t] || [];
206
+ for (const method of ttq.methods) {
207
+ ttq.setAndDefer(e, method);
208
+ }
209
+ return e;
310
210
  };
311
- })(window, document, 'script');
312
- const ttqInstance = window.ttq;
313
- if (!ttqInstance)
314
- return;
315
- ttqInstance.load?.(pixelId);
316
- ttqInstance.page?.();
317
- }
318
- function trackTikTokEvent(name, params) {
319
- if (typeof window === 'undefined' || !window.ttq)
211
+ ttq.load = function (e, n) {
212
+ const r = 'https://analytics.tiktok.com/i18n/pixel/events.js';
213
+ ttq._i = ttq._i || {};
214
+ ttq._i[e] = [];
215
+ ttq._i[e]._u = r;
216
+ ttq._t = ttq._t || {};
217
+ ttq._t[e] = +new Date();
218
+ ttq._o = ttq._o || {};
219
+ ttq._o[e] = n || {};
220
+ const s = document.createElement('script');
221
+ s.type = 'text/javascript';
222
+ s.async = true;
223
+ s.src = r + '?sdkid=' + e + '&lib=ttq';
224
+ const p = document.getElementsByTagName('script')[0];
225
+ p?.parentNode?.insertBefore(s, p);
226
+ };
227
+ }
228
+ // Skip if this specific pixel ID is already loaded
229
+ if (win.ttq?._i?.[pixelId])
320
230
  return;
321
- const ttq = window.ttq;
322
- ttq?.track?.(name, params);
231
+ win.ttq.load(pixelId);
232
+ // Note: ttq.page() is NOT called here — the Provider's auto page-view
233
+ // effect fires track('PageView') which handles it. Calling both would
234
+ // double-count page views in TikTok Ads Manager.
323
235
  }
324
236
  function initSnapchatPixel(pixelId) {
325
- if (typeof window === 'undefined')
326
- return;
327
- if (window.snaptr)
237
+ if (!win || win.snaptr)
328
238
  return;
329
- (function (w, d, tagName) {
330
- if (w.snaptr)
331
- return;
332
- const a = (function (...args) {
333
- if (a.handleRequest) {
334
- a.handleRequest(...args);
335
- }
336
- else {
337
- a.queue.push(args);
338
- }
339
- });
340
- a.queue = [];
341
- w.snaptr = a;
342
- const s = 'script';
343
- const r = d.createElement(s);
344
- r.async = true;
345
- r.src = 'https://sc-static.net/scevent.min.js';
346
- const u = d.getElementsByTagName(tagName)[0];
347
- u.parentNode?.insertBefore(r, u);
348
- })(window, document, 'script');
349
- const snaptr = window.snaptr;
350
- snaptr?.('init', pixelId);
239
+ const a = function (...args) {
240
+ if (a.handleRequest)
241
+ a.handleRequest(...args);
242
+ else
243
+ a.queue.push(args);
244
+ };
245
+ a.queue = [];
246
+ win.snaptr = a;
247
+ const r = document.createElement('script');
248
+ r.async = true;
249
+ r.src = 'https://sc-static.net/scevent.min.js';
250
+ const u = document.getElementsByTagName('script')[0];
251
+ u?.parentNode?.insertBefore(r, u);
252
+ win.snaptr('init', pixelId);
351
253
  }
352
- function trackSnapchatEvent(name, params) {
353
- if (typeof window === 'undefined' || !window.snaptr)
254
+ function initPinterestPixel(pixelId) {
255
+ if (!win || win.pintrk)
354
256
  return;
355
- const snaptr = window.snaptr;
356
- snaptr?.('track', name, params);
257
+ const a = function (...args) { a.queue.push(args); };
258
+ a.queue = [];
259
+ win.pintrk = a;
260
+ const s = document.createElement('script');
261
+ s.async = true;
262
+ s.src = 'https://s.pinimg.com/ct/core.js';
263
+ const u = document.getElementsByTagName('script')[0];
264
+ u?.parentNode?.insertBefore(s, u);
265
+ win.pintrk('load', pixelId);
266
+ // Note: pintrk('page') is NOT called here — the Provider's auto page-view
267
+ // effect handles it via fire(). Calling both would double-count page views.
357
268
  }
358
269
  function initGTM(containerId) {
359
- if (typeof window === 'undefined' || typeof document === 'undefined')
270
+ if (!win || !containerId)
360
271
  return;
361
- if (!containerId)
272
+ const isGtmContainer = containerId.startsWith('GTM-');
273
+ const scriptUrlPart = isGtmContainer
274
+ ? `googletagmanager.com/gtm.js?id=${containerId}`
275
+ : `googletagmanager.com/gtag/js?id=${containerId}`;
276
+ if (document.querySelector(`script[src*="${scriptUrlPart}"]`))
362
277
  return;
363
- // Check if GTM script is already loaded for this container
364
- const existingScript = document.querySelector(`script[src*="googletagmanager.com/gtm.js?id=${containerId}"]`);
365
- if (existingScript) {
366
- return; // GTM already initialized for this container
367
- }
368
- // Initialize dataLayer before GTM script (Google's official pattern)
369
- window.dataLayer = window.dataLayer || [];
370
- // Push gtm.start event (must be done before script loads)
371
- window.dataLayer.push({
372
- 'gtm.start': new Date().getTime(),
373
- event: 'gtm.js',
374
- });
375
- // Create and inject GTM script (Google's official pattern)
376
- // This matches Google's exact implementation from their documentation
377
- (function (w, d, s, l, i) {
378
- const f = d.getElementsByTagName(s)[0];
379
- const j = d.createElement(s);
380
- const dl = l != 'dataLayer' ? '&l=' + l : '';
278
+ win.dataLayer = win.dataLayer || [];
279
+ if (isGtmContainer) {
280
+ win.dataLayer.push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' });
281
+ const f = document.getElementsByTagName('script')[0];
282
+ const j = document.createElement('script');
381
283
  j.async = true;
382
- j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl;
383
- // Insert before first script tag (typically in head)
384
- if (f && f.parentNode) {
284
+ j.src = 'https://www.googletagmanager.com/gtm.js?id=' + containerId;
285
+ if (f?.parentNode)
385
286
  f.parentNode.insertBefore(j, f);
386
- }
387
- else {
388
- // Fallback: append to head if no script tags found
389
- const head = d.head || d.getElementsByTagName('head')[0];
390
- if (head) {
391
- head.appendChild(j);
392
- }
393
- }
394
- })(window, document, 'script', 'dataLayer', containerId);
395
- // Also add noscript fallback in body (Google's official pattern)
396
- // This ensures GTM works even if JavaScript is disabled
397
- const noscript = document.createElement('noscript');
398
- const iframe = document.createElement('iframe');
399
- iframe.src = `https://www.googletagmanager.com/ns.html?id=${containerId}`;
400
- iframe.height = '0';
401
- iframe.width = '0';
402
- iframe.style.display = 'none';
403
- iframe.style.visibility = 'hidden';
404
- noscript.appendChild(iframe);
405
- // Insert noscript at the beginning of body
406
- const body = document.body || document.getElementsByTagName('body')[0];
407
- if (body) {
408
- body.insertBefore(noscript, body.firstChild);
409
- }
410
- }
411
- function trackGTMEvent(event, params = {}) {
412
- if (typeof window === 'undefined') {
413
- console.warn('[SDK GTM] Window not available, event not tracked:', event);
414
- return;
415
- }
416
- // Ensure dataLayer exists (should be initialized by initGTM, but double-check)
417
- if (!window.dataLayer) {
418
- console.warn('[SDK GTM] dataLayer not initialized, initializing now...');
419
- window.dataLayer = [];
287
+ else
288
+ (document.head || document.getElementsByTagName('head')[0])?.appendChild(j);
289
+ const noscript = document.createElement('noscript');
290
+ const iframe = document.createElement('iframe');
291
+ iframe.src = `https://www.googletagmanager.com/ns.html?id=${containerId}`;
292
+ iframe.height = '0';
293
+ iframe.width = '0';
294
+ iframe.style.display = 'none';
295
+ iframe.style.visibility = 'hidden';
296
+ noscript.appendChild(iframe);
297
+ (document.body || document.getElementsByTagName('body')[0])?.insertBefore(noscript, document.body?.firstChild ?? null);
420
298
  }
421
- try {
422
- const eventData = {
423
- event,
424
- ...params,
425
- };
426
- console.log('[SDK GTM] Pushing event to dataLayer:', eventData);
427
- window.dataLayer.push(eventData);
428
- console.log('[SDK GTM] Event pushed successfully. Current dataLayer length:', window.dataLayer.length);
429
- }
430
- catch (error) {
431
- if (typeof console !== 'undefined') {
432
- console.error('[SDK GTM] Error tracking event:', error);
433
- }
434
- }
435
- }
436
- // --- Basic event mapping (can be extended later if needed) ---
437
- function mapMetaEvent(eventName, parameters) {
438
- return { name: eventName, params: parameters };
439
- }
440
- function mapTikTokEvent(eventName, parameters) {
441
- // TikTok naming is usually aligned; adjust here if needed later
442
- return { name: eventName, params: parameters };
443
- }
444
- function mapSnapchatEvent(eventName, parameters) {
445
- return { name: eventName, params: parameters };
446
- }
447
- function mapGTMEvent(eventName, parameters) {
448
- // Map standard event names to GTM event names
449
- const gtmEventMap = {
450
- PageView: 'page_view',
451
- AddPaymentInfo: 'add_payment_info',
452
- AddToCart: 'add_to_cart',
453
- InitiateCheckout: 'begin_checkout',
454
- Purchase: 'purchase',
455
- ViewContent: 'view_item',
456
- CompleteRegistration: 'sign_up',
457
- };
458
- const gtmEventName = gtmEventMap[eventName] ?? eventName.toLowerCase();
459
- // Transform parameters for GTM
460
- const gtmParams = transformGTMParameters(eventName, parameters);
461
- return { name: gtmEventName, params: gtmParams };
462
- }
463
- function transformGTMParameters(eventName, parameters) {
464
- const gtmParameters = { ...parameters };
465
- // Ensure currency is uppercase
466
- if (gtmParameters.currency) {
467
- gtmParameters.currency = String(gtmParameters.currency).toUpperCase();
468
- }
469
- // Transform currency values to major units if needed
470
- if (gtmParameters.currency && gtmParameters.value) {
471
- try {
472
- gtmParameters.value = minorUnitsToMajorUnits(Number(gtmParameters.value), String(gtmParameters.currency));
473
- }
474
- catch (error) {
475
- // If conversion fails, try simple division by 100 as fallback
476
- if (typeof console !== 'undefined') {
477
- console.warn('[SDK GTM] Currency conversion failed, using fallback:', error);
478
- }
479
- gtmParameters.value = Number(gtmParameters.value) / 100;
299
+ else {
300
+ if (!win.gtag) {
301
+ win.gtag = function (..._args) { win.dataLayer.push(arguments); };
480
302
  }
303
+ win.gtag('js', new Date());
304
+ win.gtag('config', containerId);
305
+ const script = document.createElement('script');
306
+ script.async = true;
307
+ script.src = 'https://www.googletagmanager.com/gtag/js?id=' + containerId;
308
+ const firstScript = document.getElementsByTagName('script')[0];
309
+ if (firstScript?.parentNode)
310
+ firstScript.parentNode.insertBefore(script, firstScript);
311
+ else
312
+ (document.head || document.getElementsByTagName('head')[0])?.appendChild(script);
481
313
  }
482
- // Ensure numeric values
483
- if (gtmParameters.value !== undefined) {
484
- gtmParameters.value = Number(gtmParameters.value);
485
- }
486
- if (gtmParameters.num_items !== undefined) {
487
- gtmParameters.num_items = Number(gtmParameters.num_items);
488
- }
489
- // Transform contents to a format better suited for GTM
490
- if (gtmParameters.contents && Array.isArray(gtmParameters.contents)) {
491
- gtmParameters.items = gtmParameters.contents.map((item) => ({
492
- item_id: item.content_id,
493
- item_name: item.content_name,
494
- item_category: item.content_category,
495
- price: item.price ? Number(item.price) : undefined,
496
- quantity: item.quantity ? Number(item.quantity) : undefined,
497
- }));
498
- // Keep original contents for compatibility
499
- gtmParameters.contents = gtmParameters.contents.map((item) => ({
500
- id: item.content_id,
501
- name: item.content_name,
502
- category: item.content_category,
503
- price: item.price ? Number(item.price) : undefined,
504
- quantity: item.quantity ? Number(item.quantity) : undefined,
505
- }));
506
- }
507
- return gtmParameters;
508
314
  }