@tagadapay/plugin-sdk 3.1.12 → 3.1.24

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 (144) hide show
  1. package/build-cdn.js +397 -11
  2. package/dist/data/iso3166.d.ts +23 -33
  3. package/dist/data/iso3166.js +134 -198
  4. package/dist/data/languages.d.ts +5 -64
  5. package/dist/data/languages.js +23 -143
  6. package/dist/external-tracker.js +623 -3426
  7. package/dist/external-tracker.min.js +2 -25
  8. package/dist/external-tracker.min.js.map +4 -4
  9. package/dist/react/config/payment.d.ts +14 -4
  10. package/dist/react/config/payment.js +47 -9
  11. package/dist/react/hooks/useCheckout.d.ts +3 -0
  12. package/dist/react/hooks/useCheckout.js +4 -1
  13. package/dist/react/hooks/useISOData.js +1 -1
  14. package/dist/react/hooks/usePaymentPolling.d.ts +3 -3
  15. package/dist/react/hooks/usePluginConfig.js +9 -10
  16. package/dist/react/providers/TagadaProvider.js +1 -1
  17. package/dist/tagada-react-sdk-minimal.min.js +36 -0
  18. package/dist/tagada-react-sdk-minimal.min.js.map +7 -0
  19. package/dist/tagada-react-sdk.js +37821 -0
  20. package/dist/tagada-react-sdk.min.js +78 -0
  21. package/dist/tagada-react-sdk.min.js.map +7 -0
  22. package/dist/tagada-sdk.js +16044 -0
  23. package/dist/tagada-sdk.min.js +32 -0
  24. package/dist/tagada-sdk.min.js.map +7 -0
  25. package/dist/v2/cdn-react-minimal.d.ts +23 -0
  26. package/dist/v2/cdn-react-minimal.js +26 -0
  27. package/dist/v2/core/client.d.ts +4 -2
  28. package/dist/v2/core/client.js +5 -4
  29. package/dist/v2/core/config/environment.js +2 -1
  30. package/dist/v2/core/errors.d.ts +75 -0
  31. package/dist/v2/core/errors.js +104 -0
  32. package/dist/v2/core/funnelClient.d.ts +100 -10
  33. package/dist/v2/core/funnelClient.js +121 -27
  34. package/dist/v2/core/isoData.d.ts +4 -4
  35. package/dist/v2/core/isoData.js +7 -7
  36. package/dist/v2/core/pixelMapping.d.ts +49 -0
  37. package/dist/v2/core/pixelMapping.js +363 -0
  38. package/dist/v2/core/resources/apiClient.d.ts +2 -0
  39. package/dist/v2/core/resources/apiClient.js +52 -9
  40. package/dist/v2/core/resources/checkout.d.ts +99 -30
  41. package/dist/v2/core/resources/checkout.js +14 -0
  42. package/dist/v2/core/resources/customer.d.ts +20 -19
  43. package/dist/v2/core/resources/expressPaymentMethods.d.ts +1 -0
  44. package/dist/v2/core/resources/funnel.d.ts +17 -17
  45. package/dist/v2/core/resources/payments.d.ts +89 -13
  46. package/dist/v2/core/resources/payments.js +27 -9
  47. package/dist/v2/core/resources/postPurchases.d.ts +17 -0
  48. package/dist/v2/core/resources/postPurchases.js +20 -0
  49. package/dist/v2/core/types.d.ts +50 -12
  50. package/dist/v2/core/types.js +0 -3
  51. package/dist/v2/core/utils/checkout.d.ts +2 -2
  52. package/dist/v2/core/utils/checkout.js +7 -2
  53. package/dist/v2/core/utils/currency.d.ts +14 -0
  54. package/dist/v2/core/utils/currency.js +40 -0
  55. package/dist/v2/core/utils/deviceInfo.d.ts +0 -10
  56. package/dist/v2/core/utils/deviceInfo.js +152 -76
  57. package/dist/v2/core/utils/index.d.ts +1 -0
  58. package/dist/v2/core/utils/index.js +2 -0
  59. package/dist/v2/core/utils/order.d.ts +13 -9
  60. package/dist/v2/core/utils/pluginConfig.d.ts +8 -0
  61. package/dist/v2/core/utils/pluginConfig.js +36 -12
  62. package/dist/v2/index.d.ts +6 -3
  63. package/dist/v2/index.js +4 -2
  64. package/dist/v2/react/components/FunnelScriptInjector.js +166 -77
  65. package/dist/v2/react/components/StripeExpressButton.d.ts +13 -0
  66. package/dist/v2/react/components/StripeExpressButton.js +171 -0
  67. package/dist/v2/react/components/WhopCheckout.d.ts +24 -0
  68. package/dist/v2/react/components/WhopCheckout.js +237 -0
  69. package/dist/v2/react/hooks/__examples__/FunnelContextExample.js +1 -1
  70. package/dist/v2/react/hooks/payment-actions/useAirwallexRadarAction.d.ts +14 -0
  71. package/dist/v2/react/hooks/payment-actions/useAirwallexRadarAction.js +181 -0
  72. package/dist/v2/react/hooks/payment-actions/useErrorAction.d.ts +9 -0
  73. package/dist/v2/react/hooks/payment-actions/useErrorAction.js +21 -0
  74. package/dist/v2/react/hooks/payment-actions/useFinixRadarAction.d.ts +14 -0
  75. package/dist/v2/react/hooks/payment-actions/useFinixRadarAction.js +187 -0
  76. package/dist/v2/react/hooks/payment-actions/useKessPayAction.d.ts +11 -0
  77. package/dist/v2/react/hooks/payment-actions/useKessPayAction.js +91 -0
  78. package/dist/v2/react/hooks/payment-actions/useMasterCardAction.d.ts +24 -0
  79. package/dist/v2/react/hooks/payment-actions/useMasterCardAction.js +221 -0
  80. package/dist/v2/react/hooks/payment-actions/usePaymentActionHandler.d.ts +15 -0
  81. package/dist/v2/react/hooks/payment-actions/usePaymentActionHandler.js +142 -0
  82. package/dist/v2/react/hooks/payment-actions/useProcessorAuthAction.d.ts +3 -0
  83. package/dist/v2/react/hooks/payment-actions/useProcessorAuthAction.js +31 -0
  84. package/dist/v2/react/hooks/payment-actions/useRedirectAction.d.ts +10 -0
  85. package/dist/v2/react/hooks/payment-actions/useRedirectAction.js +35 -0
  86. package/dist/v2/react/hooks/payment-actions/useStripeRadarAction.d.ts +14 -0
  87. package/dist/v2/react/hooks/payment-actions/useStripeRadarAction.js +192 -0
  88. package/dist/v2/react/hooks/payment-actions/useThreedsAuthAction.d.ts +14 -0
  89. package/dist/v2/react/hooks/payment-actions/useThreedsAuthAction.js +81 -0
  90. package/dist/v2/react/hooks/payment-actions/useTrustFlowAction.d.ts +11 -0
  91. package/dist/v2/react/hooks/payment-actions/useTrustFlowAction.js +84 -0
  92. package/dist/v2/react/hooks/payment-processing/usePaymentInstruments.d.ts +14 -0
  93. package/dist/v2/react/hooks/payment-processing/usePaymentInstruments.js +36 -0
  94. package/dist/v2/react/hooks/payment-processing/usePaymentProcessors.d.ts +31 -0
  95. package/dist/v2/react/hooks/payment-processing/usePaymentProcessors.js +212 -0
  96. package/dist/v2/react/hooks/payment-redirect/useAirwallex3dsReturn.d.ts +14 -0
  97. package/dist/v2/react/hooks/payment-redirect/useAirwallex3dsReturn.js +207 -0
  98. package/dist/v2/react/hooks/payment-redirect/useGenericPaymentReturn.d.ts +12 -0
  99. package/dist/v2/react/hooks/payment-redirect/useGenericPaymentReturn.js +101 -0
  100. package/dist/v2/react/hooks/useApplePayCheckout.js +8 -8
  101. package/dist/v2/react/hooks/useCheckoutQuery.d.ts +16 -0
  102. package/dist/v2/react/hooks/useCheckoutQuery.js +63 -10
  103. package/dist/v2/react/hooks/useFunnel.d.ts +15 -4
  104. package/dist/v2/react/hooks/useFunnel.js +8 -4
  105. package/dist/v2/react/hooks/useGeoLocation.d.ts +2 -1
  106. package/dist/v2/react/hooks/useGeoLocation.js +4 -2
  107. package/dist/v2/react/hooks/useGoogleAutocomplete.d.ts +2 -0
  108. package/dist/v2/react/hooks/useGoogleAutocomplete.js +29 -15
  109. package/dist/v2/react/hooks/useISOData.d.ts +2 -5
  110. package/dist/v2/react/hooks/useISOData.js +26 -27
  111. package/dist/v2/react/hooks/usePaymentPolling.d.ts +3 -3
  112. package/dist/v2/react/hooks/usePaymentQuery.d.ts +18 -5
  113. package/dist/v2/react/hooks/usePaymentQuery.js +63 -1015
  114. package/dist/v2/react/hooks/usePaymentRetrieve.d.ts +3 -2
  115. package/dist/v2/react/hooks/usePaymentRetrieve.js +3 -1
  116. package/dist/v2/react/hooks/usePixelTracking.d.ts +5 -48
  117. package/dist/v2/react/hooks/usePixelTracking.js +283 -504
  118. package/dist/v2/react/hooks/usePostPurchasesQuery.js +34 -2
  119. package/dist/v2/react/hooks/useRemappableParams.d.ts +2 -6
  120. package/dist/v2/react/hooks/useRemappableParams.js +23 -23
  121. package/dist/v2/react/hooks/useSetPaymentMethod.d.ts +16 -0
  122. package/dist/v2/react/hooks/useSetPaymentMethod.js +33 -0
  123. package/dist/v2/react/hooks/useShippingRatesQuery.js +13 -5
  124. package/dist/v2/react/hooks/useStepConfig.d.ts +23 -6
  125. package/dist/v2/react/hooks/useStepConfig.js +14 -7
  126. package/dist/v2/react/hooks/useTranslation.js +23 -8
  127. package/dist/v2/react/hooks/useWhopPaymentPolling.d.ts +30 -0
  128. package/dist/v2/react/hooks/useWhopPaymentPolling.js +61 -0
  129. package/dist/v2/react/index.d.ts +15 -1
  130. package/dist/v2/react/index.js +7 -0
  131. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.d.ts +3 -1
  132. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +12 -2
  133. package/dist/v2/react/providers/TagadaProvider.js +74 -5
  134. package/dist/v2/standalone/external-tracker.d.ts +52 -46
  135. package/dist/v2/standalone/external-tracker.js +205 -98
  136. package/dist/v2/standalone/index.d.ts +40 -0
  137. package/dist/v2/standalone/index.js +148 -1
  138. package/dist/v2/standalone/payment-service.d.ts +134 -0
  139. package/dist/v2/standalone/payment-service.js +928 -0
  140. package/package.json +6 -4
  141. package/dist/react/utils/__tests__/urlUtils.test.d.ts +0 -1
  142. package/dist/react/utils/__tests__/urlUtils.test.js +0 -189
  143. package/dist/v2/core/__tests__/pathRemapping.test.d.ts +0 -11
  144. 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,588 +27,369 @@ 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 ----
47
+ // Wait for all external scripts to actually load before marking initialized,
48
+ // so that pixel helpers (TikTok, etc.) can intercept events properly.
53
49
  useEffect(() => {
54
50
  if (!pixels || pixelsInitialized || !isMountedRef.current)
55
51
  return;
52
+ const loadPromises = [];
56
53
  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
- // Pinterest - support multiple pixels
91
- const pinterestPixels = pixels.pinterest;
92
- if (pinterestPixels) {
93
- if (Array.isArray(pinterestPixels)) {
94
- pinterestPixels.forEach((pixel) => {
95
- if (pixel.enabled && pixel.pixelId) {
96
- initPinterestPixel(pixel.pixelId);
97
- }
98
- });
99
- }
100
- }
101
- // GTM - support multiple containers
102
- const gtmPixels = pixels.gtm;
103
- if (gtmPixels) {
104
- if (Array.isArray(gtmPixels)) {
105
- gtmPixels.forEach((pixel) => {
106
- if (pixel.enabled && pixel.containerId) {
107
- initGTM(pixel.containerId);
108
- }
109
- });
110
- }
111
- }
112
- if (isMountedRef.current) {
113
- setPixelsInitialized(true);
114
- }
54
+ pixels.facebook?.forEach((px) => { if (px.enabled && px.pixelId)
55
+ loadPromises.push(initMetaPixel(px.pixelId)); });
56
+ pixels.tiktok?.forEach((px) => { if (px.enabled && px.pixelId)
57
+ loadPromises.push(initTikTokPixel(px.pixelId)); });
58
+ pixels.snapchat?.forEach((px) => { if (px.enabled && px.pixelId)
59
+ loadPromises.push(initSnapchatPixel(px.pixelId)); });
60
+ pixels.pinterest?.forEach((px) => { if (px.enabled && px.pixelId)
61
+ loadPromises.push(initPinterestPixel(px.pixelId)); });
62
+ pixels.gtm?.forEach((px) => { if (px.enabled && px.containerId)
63
+ loadPromises.push(initGTM(px.containerId)); });
64
+ Promise.all(loadPromises).then(() => {
65
+ if (isMountedRef.current)
66
+ setPixelsInitialized(true);
67
+ });
115
68
  }
116
69
  catch (error) {
117
- // Fail-safe: never break the host page because of pixel issues
118
- if (typeof console !== 'undefined') {
119
- console.error('[SDK Pixels] Error during pixel initialization:', error);
120
- }
70
+ console.error('[SDK Pixels] Initialization error:', error);
121
71
  }
122
72
  }, [pixels, pixelsInitialized]);
73
+ // ---- Track function ----
123
74
  const track = useCallback((eventName, parameters = {}) => {
124
- if (!pixels || !pixelsInitialized || !isMountedRef.current) {
75
+ if (!pixels || !pixelsInitialized || !isMountedRef.current)
125
76
  return;
126
- }
127
- // Duplicate guard
128
- if (!shouldTrackEvent(eventName, parameters)) {
77
+ if (!shouldTrackEvent(eventName, parameters))
129
78
  return;
130
- }
131
79
  try {
132
- // Facebook / Meta - track to all enabled pixels
133
- const facebookPixels = pixels.facebook; // Support both 'facebook' and legacy 'meta'
134
- if (facebookPixels) {
135
- const pixelArray = Array.isArray(facebookPixels)
136
- ? facebookPixels
137
- : [facebookPixels];
138
- const enabledPixels = pixelArray.filter((p) => p.enabled);
139
- if (enabledPixels.length > 0) {
140
- const { name, params } = mapMetaEvent(eventName, parameters);
141
- // Track to all enabled Facebook pixels
142
- enabledPixels.forEach(() => {
143
- trackMetaEvent(name, params);
144
- });
145
- }
146
- }
147
- // TikTok - track to all enabled pixels
148
- const tiktokPixels = pixels.tiktok;
149
- if (tiktokPixels) {
150
- const pixelArray = Array.isArray(tiktokPixels)
151
- ? tiktokPixels
152
- : [tiktokPixels];
153
- const enabledPixels = pixelArray.filter((p) => p.enabled);
154
- if (enabledPixels.length > 0) {
155
- const { name, params } = mapTikTokEvent(eventName, parameters);
156
- // Track to all enabled TikTok pixels
157
- enabledPixels.forEach(() => {
158
- trackTikTokEvent(name, params);
159
- });
160
- }
161
- }
162
- // Snapchat - track to all enabled pixels
163
- const snapchatPixels = pixels.snapchat;
164
- if (snapchatPixels) {
165
- const pixelArray = Array.isArray(snapchatPixels)
166
- ? snapchatPixels
167
- : [snapchatPixels];
168
- const enabledPixels = pixelArray.filter((p) => p.enabled);
169
- if (enabledPixels.length > 0) {
170
- const { name, params } = mapSnapchatEvent(eventName, parameters);
171
- // Track to all enabled Snapchat pixels
172
- enabledPixels.forEach(() => {
173
- trackSnapchatEvent(name, params);
174
- });
175
- }
176
- }
177
- // Pinterest - track to all enabled pixels
178
- const pinterestPixels = pixels.pinterest;
179
- if (pinterestPixels) {
180
- const pixelArray = Array.isArray(pinterestPixels)
181
- ? pinterestPixels
182
- : [pinterestPixels];
183
- const enabledPixels = pixelArray.filter((p) => p.enabled);
184
- if (enabledPixels.length > 0) {
185
- const { name, params } = mapPinterestEvent(eventName, parameters);
186
- // Track to all enabled Pinterest pixels
187
- enabledPixels.forEach(() => {
188
- trackPinterestEvent(name, params);
189
- });
80
+ const events = resolvePixelEvents(pixels, eventName, parameters);
81
+ // Deduplicate by provider: Meta/Snapchat/Pinterest SDKs broadcast
82
+ // to all registered pixel IDs internally, so we only need to fire once per
83
+ // provider. GTM and TikTok need per-pixel firing (GTM for Google Ads
84
+ // send_to targeting, TikTok because ttq.instance() targets a specific pixel).
85
+ const firedProviders = new Set();
86
+ for (const { provider, mapped, pixel } of events) {
87
+ if (provider === 'gtm' || provider === 'tiktok') {
88
+ fire(provider, mapped.name, mapped.params, pixel);
190
89
  }
191
- }
192
- // GTM - track to all enabled containers
193
- const gtmPixels = pixels.gtm;
194
- if (gtmPixels) {
195
- const pixelArray = Array.isArray(gtmPixels)
196
- ? gtmPixels
197
- : [gtmPixels];
198
- const enabledPixels = pixelArray.filter((p) => p.enabled);
199
- if (enabledPixels.length > 0) {
200
- const { name, params } = mapGTMEvent(eventName, parameters);
201
- // Track to all enabled GTM containers
202
- enabledPixels.forEach(() => {
203
- console.log('trackGTMEvent', name, params);
204
- trackGTMEvent(name, params);
205
- });
90
+ else {
91
+ if (!firedProviders.has(provider)) {
92
+ fire(provider, mapped.name, mapped.params);
93
+ firedProviders.add(provider);
94
+ }
206
95
  }
207
96
  }
208
97
  }
209
98
  catch (error) {
210
- if (typeof console !== 'undefined') {
211
- console.error('[SDK Pixels] Error tracking pixel event:', error);
212
- }
99
+ console.error('[SDK Pixels] Tracking error:', error);
213
100
  }
214
101
  }, [pixels, pixelsInitialized]);
215
- // Track page views automatically when pixels are initialized
102
+ // ---- Auto page-view ----
216
103
  useEffect(() => {
217
104
  if (!pixelsInitialized || !isMountedRef.current)
218
105
  return;
219
- // Small delay to ensure we don't double fire during strict mode remounts
220
- const pageViewTimeoutId = setTimeout(() => {
106
+ const id = setTimeout(() => {
221
107
  if (isMountedRef.current) {
222
- track('PageView', {
223
- path: typeof window !== 'undefined' ? window.location.pathname : '',
224
- });
108
+ track('PageView', { path: typeof window !== 'undefined' ? window.location.pathname : '' });
225
109
  }
226
110
  }, 0);
227
- return () => {
228
- if (pageViewTimeoutId) {
229
- clearTimeout(pageViewTimeoutId);
230
- }
231
- };
111
+ return () => clearTimeout(id);
232
112
  }, [pixelsInitialized, track]);
233
- const value = useMemo(() => ({
234
- track,
235
- pixelsInitialized,
236
- }), [track, pixelsInitialized]);
113
+ const value = useMemo(() => ({ track, pixelsInitialized }), [track, pixelsInitialized]);
237
114
  return _jsx(PixelTrackingContext.Provider, { value: value, children: children });
238
115
  }
239
- /**
240
- * Hook to access SDK pixel tracking.
241
- * Must be used within TagadaProvider (which wraps PixelTrackingProvider).
242
- */
243
116
  export function usePixelTracking() {
244
117
  const context = useContext(PixelTrackingContext);
245
- if (!context) {
118
+ if (!context)
246
119
  throw new Error('usePixelTracking must be used within a PixelTrackingProvider');
247
- }
248
120
  return context;
249
121
  }
250
- function initMetaPixel(pixelId) {
122
+ // ---------------------------------------------------------------------------
123
+ // Browser-specific: fire an event to the correct global pixel function
124
+ // ---------------------------------------------------------------------------
125
+ /* eslint-disable @typescript-eslint/no-explicit-any */
126
+ function fire(provider, name, params, pixel) {
251
127
  if (typeof window === 'undefined')
252
128
  return;
253
- if (window.fbq)
254
- return;
255
- // Standard Facebook Pixel bootstrap
256
- (function (f, b, e) {
257
- if (f.fbq)
258
- return;
259
- const n = function (...args) {
260
- if (n.callMethod) {
261
- n.callMethod(...args);
129
+ const w = window;
130
+ switch (provider) {
131
+ case 'facebook':
132
+ w.fbq?.('track', name, params);
133
+ break;
134
+ case 'tiktok': {
135
+ // Use ttq.instance(pixelId) to target each pixel individually,
136
+ // since the global ttq.track() only fires for the sdkid pixel.
137
+ const pixelId = pixel && 'pixelId' in pixel ? pixel.pixelId : null;
138
+ const target = pixelId && w.ttq?.instance ? w.ttq.instance(pixelId) : w.ttq;
139
+ if (name === 'Pageview') {
140
+ target?.page?.();
262
141
  }
263
142
  else {
264
- n.queue.push(args);
143
+ target?.track?.(name, params);
144
+ }
145
+ break;
146
+ }
147
+ case 'snapchat':
148
+ w.snaptr?.('track', name, params);
149
+ break;
150
+ case 'pinterest':
151
+ // Pinterest handles page views via pintrk('page'), not pintrk('track', 'pagevisit')
152
+ if (name === 'pagevisit') {
153
+ w.pintrk?.('page');
265
154
  }
155
+ else {
156
+ w.pintrk?.('track', name, params);
157
+ }
158
+ break;
159
+ case 'gtm':
160
+ fireGTM(name, params);
161
+ break;
162
+ }
163
+ }
164
+ function fireGTM(event, params) {
165
+ try {
166
+ const w = window;
167
+ if (w.gtag) {
168
+ w.gtag('event', event, params);
169
+ }
170
+ else if (w.dataLayer) {
171
+ w.dataLayer.push({ event, ...params });
172
+ }
173
+ }
174
+ catch (error) {
175
+ console.error('[SDK GTM] Error:', error);
176
+ }
177
+ }
178
+ /* eslint-enable @typescript-eslint/no-explicit-any */
179
+ // ===========================================================================
180
+ // Pixel initialization (browser-only)
181
+ // All pixel globals are accessed via `win` typed as `any` to avoid
182
+ // `declare global` augmentation issues across tsconfig scopes.
183
+ // ===========================================================================
184
+ /* eslint-disable @typescript-eslint/no-explicit-any */
185
+ const win = (typeof window !== 'undefined' ? window : undefined);
186
+ const SCRIPT_LOAD_TIMEOUT_MS = 5000;
187
+ function waitForScriptLoad(script) {
188
+ // If the script has already loaded, resolve immediately
189
+ if (script.dataset.loaded)
190
+ return Promise.resolve();
191
+ return new Promise((resolve) => {
192
+ const done = () => { script.dataset.loaded = '1'; clearTimeout(timer); resolve(); };
193
+ const timer = setTimeout(done, SCRIPT_LOAD_TIMEOUT_MS);
194
+ script.addEventListener('load', done, { once: true });
195
+ script.addEventListener('error', done, { once: true });
196
+ });
197
+ }
198
+ let _metaScriptEl = null;
199
+ function initMetaPixel(pixelId) {
200
+ if (!win)
201
+ return Promise.resolve();
202
+ // Initialize Meta base code once
203
+ if (!win.fbq) {
204
+ const n = function (...args) {
205
+ if (n.callMethod)
206
+ n.callMethod(...args);
207
+ else
208
+ n.queue.push(args);
266
209
  };
267
- f.fbq = n;
268
- if (!f._fbq)
269
- f._fbq = n;
270
- n.queue = n.queue ?? [];
210
+ n.queue = [];
271
211
  n.loaded = true;
272
212
  n.version = '2.0';
273
- n.queue = [];
274
- const t = b.createElement(e);
213
+ win.fbq = n;
214
+ if (!win._fbq)
215
+ win._fbq = n;
216
+ const t = document.createElement('script');
275
217
  t.async = true;
276
218
  t.src = 'https://connect.facebook.net/en_US/fbevents.js';
277
- const s = b.getElementsByTagName(e)[0];
278
- s.parentNode?.insertBefore(t, s);
279
- })(window, document, 'script');
280
- const fbq = window.fbq;
281
- if (fbq) {
282
- fbq('init', pixelId);
219
+ const s = document.getElementsByTagName('script')[0];
220
+ s?.parentNode?.insertBefore(t, s);
221
+ _metaScriptEl = t;
283
222
  }
223
+ // Register each pixel ID (fbq supports multiple pixels via multiple init calls)
224
+ win.fbq('init', pixelId);
225
+ return _metaScriptEl ? waitForScriptLoad(_metaScriptEl) : Promise.resolve();
284
226
  }
285
- function trackMetaEvent(name, params) {
286
- if (typeof window === 'undefined' || !window.fbq)
287
- return;
288
- const fbq = window.fbq;
289
- fbq?.('track', name, params);
290
- }
227
+ let _tiktokBaseInitialized = false;
291
228
  function initTikTokPixel(pixelId) {
292
- if (typeof window === 'undefined')
293
- return;
294
- if (window.ttq)
295
- return;
296
- (function (w, d, t) {
297
- const ttq = w.ttq ||
298
- function (...args) {
299
- ttq.queue.push(args);
300
- };
301
- if (!ttq.queue) {
302
- ttq.queue = [];
303
- }
304
- w.ttq = ttq;
229
+ if (!win)
230
+ return Promise.resolve();
231
+ // Initialize TikTok base code once
232
+ if (!_tiktokBaseInitialized) {
233
+ _tiktokBaseInitialized = true;
234
+ win.TiktokAnalyticsObject = 'ttq';
235
+ const ttq = (win.ttq = win.ttq || []);
305
236
  ttq.methods = [
306
- 'page',
307
- 'track',
308
- 'identify',
309
- 'instances',
310
- 'debug',
311
- 'on',
312
- 'off',
313
- 'once',
314
- 'ready',
315
- 'alias',
316
- 'group',
317
- 'enableCookie',
318
- 'disableCookie',
237
+ 'page', 'track', 'identify', 'instances', 'debug', 'on', 'off',
238
+ 'once', 'ready', 'alias', 'group', 'enableCookie', 'disableCookie',
239
+ 'holdConsent', 'revokeConsent', 'grantConsent',
319
240
  ];
320
- ttq.setAndDefer = function (target, method) {
321
- target[method] = function (...args) {
322
- target.queue.push([method, ...args]);
323
- };
241
+ ttq.setAndDefer = function (t, e) {
242
+ t[e] = function (...args) { t.push([e, ...args]); };
324
243
  };
325
244
  for (const method of ttq.methods) {
326
245
  ttq.setAndDefer(ttq, method);
327
246
  }
328
- ttq.load = function (e) {
329
- const n = 'https://analytics.tiktok.com/i18n/pixel/events.js';
330
- const a = d.createElement(t);
331
- a.type = 'text/javascript';
332
- a.async = true;
333
- a.src = n + '?sdkid=' + e + '&lib=ttq';
334
- const s = d.getElementsByTagName(t)[0];
335
- s.parentNode.insertBefore(a, s);
247
+ ttq.instance = function (t) {
248
+ const e = ttq._i[t] || [];
249
+ for (const method of ttq.methods) {
250
+ ttq.setAndDefer(e, method);
251
+ }
252
+ return e;
336
253
  };
337
- })(window, document, 'script');
338
- const ttqInstance = window.ttq;
339
- if (!ttqInstance)
340
- return;
341
- ttqInstance.load?.(pixelId);
342
- ttqInstance.page?.();
343
- }
344
- function trackTikTokEvent(name, params) {
345
- if (typeof window === 'undefined' || !window.ttq)
346
- return;
347
- const ttq = window.ttq;
348
- ttq?.track?.(name, params);
254
+ ttq.load = function (e, n) {
255
+ const r = 'https://analytics.tiktok.com/i18n/pixel/events.js';
256
+ ttq._i = ttq._i || {};
257
+ ttq._i[e] = [];
258
+ ttq._i[e]._u = r;
259
+ ttq._t = ttq._t || {};
260
+ ttq._t[e] = +new Date();
261
+ ttq._o = ttq._o || {};
262
+ ttq._o[e] = n || {};
263
+ const s = document.createElement('script');
264
+ s.type = 'text/javascript';
265
+ s.async = true;
266
+ s.src = r + '?sdkid=' + e + '&lib=ttq';
267
+ const p = document.getElementsByTagName('script')[0];
268
+ p?.parentNode?.insertBefore(s, p);
269
+ };
270
+ }
271
+ // Skip if this specific pixel ID is already registered
272
+ if (win.ttq._i?.[pixelId])
273
+ return Promise.resolve();
274
+ // Register pixel and load its script
275
+ win.ttq.load(pixelId);
276
+ // Find the script we just created and wait for it
277
+ const scripts = document.querySelectorAll('script[src*="analytics.tiktok.com/i18n/pixel/events.js"]');
278
+ const lastScript = scripts[scripts.length - 1];
279
+ return lastScript ? waitForScriptLoad(lastScript) : Promise.resolve();
349
280
  }
281
+ let _snapchatScriptEl = null;
350
282
  function initSnapchatPixel(pixelId) {
351
- if (typeof window === 'undefined')
352
- return;
353
- if (window.snaptr)
354
- return;
355
- (function (w, d, tagName) {
356
- if (w.snaptr)
357
- return;
358
- const a = (function (...args) {
359
- if (a.handleRequest) {
283
+ if (!win)
284
+ return Promise.resolve();
285
+ // Initialize Snapchat base code once
286
+ if (!win.snaptr) {
287
+ const a = function (...args) {
288
+ if (a.handleRequest)
360
289
  a.handleRequest(...args);
361
- }
362
- else {
290
+ else
363
291
  a.queue.push(args);
364
- }
365
- });
292
+ };
366
293
  a.queue = [];
367
- w.snaptr = a;
368
- const s = 'script';
369
- const r = d.createElement(s);
294
+ win.snaptr = a;
295
+ const r = document.createElement('script');
370
296
  r.async = true;
371
297
  r.src = 'https://sc-static.net/scevent.min.js';
372
- const u = d.getElementsByTagName(tagName)[0];
373
- u.parentNode?.insertBefore(r, u);
374
- })(window, document, 'script');
375
- const snaptr = window.snaptr;
376
- snaptr?.('init', pixelId);
377
- }
378
- function trackSnapchatEvent(name, params) {
379
- if (typeof window === 'undefined' || !window.snaptr)
380
- return;
381
- const snaptr = window.snaptr;
382
- snaptr?.('track', name, params);
298
+ const u = document.getElementsByTagName('script')[0];
299
+ u?.parentNode?.insertBefore(r, u);
300
+ _snapchatScriptEl = r;
301
+ }
302
+ // Register each pixel ID (snaptr supports multiple pixels via multiple init calls)
303
+ win.snaptr('init', pixelId);
304
+ return _snapchatScriptEl ? waitForScriptLoad(_snapchatScriptEl) : Promise.resolve();
383
305
  }
306
+ let _pinterestScriptEl = null;
384
307
  function initPinterestPixel(pixelId) {
385
- if (typeof window === 'undefined')
386
- return;
387
- if (window.pintrk)
388
- return;
389
- (function (w, d, tagName) {
390
- if (w.pintrk)
391
- return;
392
- const a = (function (...args) {
393
- a.queue.push(args);
394
- });
308
+ if (!win)
309
+ return Promise.resolve();
310
+ // Initialize Pinterest base code once
311
+ if (!win.pintrk) {
312
+ const a = function (...args) { a.queue.push(args); };
395
313
  a.queue = [];
396
- w.pintrk = a;
397
- const s = d.createElement(tagName);
314
+ win.pintrk = a;
315
+ const s = document.createElement('script');
398
316
  s.async = true;
399
317
  s.src = 'https://s.pinimg.com/ct/core.js';
400
- const u = d.getElementsByTagName(tagName)[0];
401
- u.parentNode?.insertBefore(s, u);
402
- })(window, document, 'script');
403
- const pintrk = window.pintrk;
404
- pintrk?.('load', pixelId);
405
- pintrk?.('page');
406
- }
407
- function trackPinterestEvent(name, params) {
408
- if (typeof window === 'undefined' || !window.pintrk)
409
- return;
410
- const pintrk = window.pintrk;
411
- pintrk?.('track', name, params);
412
- }
413
- function initGTM(containerId) {
414
- if (typeof window === 'undefined' || typeof document === 'undefined')
415
- return;
416
- if (!containerId)
417
- return;
418
- // Check if GTM script is already loaded for this container
419
- const existingScript = document.querySelector(`script[src*="googletagmanager.com/gtm.js?id=${containerId}"]`);
420
- if (existingScript) {
421
- return; // GTM already initialized for this container
422
- }
423
- // Initialize dataLayer before GTM script (Google's official pattern)
424
- window.dataLayer = window.dataLayer || [];
425
- // Push gtm.start event (must be done before script loads)
426
- window.dataLayer.push({
427
- 'gtm.start': new Date().getTime(),
428
- event: 'gtm.js',
429
- });
430
- // Create and inject GTM script (Google's official pattern)
431
- // This matches Google's exact implementation from their documentation
432
- (function (w, d, s, l, i) {
433
- const f = d.getElementsByTagName(s)[0];
434
- const j = d.createElement(s);
435
- const dl = l != 'dataLayer' ? '&l=' + l : '';
436
- j.async = true;
437
- j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl;
438
- // Insert before first script tag (typically in head)
439
- if (f && f.parentNode) {
440
- f.parentNode.insertBefore(j, f);
441
- }
442
- else {
443
- // Fallback: append to head if no script tags found
444
- const head = d.head || d.getElementsByTagName('head')[0];
445
- if (head) {
446
- head.appendChild(j);
447
- }
448
- }
449
- })(window, document, 'script', 'dataLayer', containerId);
450
- // Also add noscript fallback in body (Google's official pattern)
451
- // This ensures GTM works even if JavaScript is disabled
452
- const noscript = document.createElement('noscript');
453
- const iframe = document.createElement('iframe');
454
- iframe.src = `https://www.googletagmanager.com/ns.html?id=${containerId}`;
455
- iframe.height = '0';
456
- iframe.width = '0';
457
- iframe.style.display = 'none';
458
- iframe.style.visibility = 'hidden';
459
- noscript.appendChild(iframe);
460
- // Insert noscript at the beginning of body
461
- const body = document.body || document.getElementsByTagName('body')[0];
462
- if (body) {
463
- body.insertBefore(noscript, body.firstChild);
318
+ const u = document.getElementsByTagName('script')[0];
319
+ u?.parentNode?.insertBefore(s, u);
320
+ _pinterestScriptEl = s;
464
321
  }
322
+ // Register each pixel ID (pintrk supports multiple pixels via multiple load calls)
323
+ win.pintrk('load', pixelId);
324
+ // Note: pintrk('page') is NOT called here — the Provider's auto page-view
325
+ // effect handles it via fire(). Calling both would double-count page views.
326
+ return _pinterestScriptEl ? waitForScriptLoad(_pinterestScriptEl) : Promise.resolve();
465
327
  }
466
- function trackGTMEvent(event, params = {}) {
467
- if (typeof window === 'undefined') {
468
- console.warn('[SDK GTM] Window not available, event not tracked:', event);
469
- return;
470
- }
471
- // Ensure dataLayer exists (should be initialized by initGTM, but double-check)
472
- if (!window.dataLayer) {
473
- console.warn('[SDK GTM] dataLayer not initialized, initializing now...');
474
- window.dataLayer = [];
475
- }
328
+ function initGTM(containerId) {
329
+ if (!win || !containerId)
330
+ return Promise.resolve();
331
+ const isGtmContainer = containerId.startsWith('GTM-');
332
+ const scriptUrlPart = isGtmContainer
333
+ ? `googletagmanager.com/gtm.js?id=${containerId}`
334
+ : `googletagmanager.com/gtag/js?id=${containerId}`;
335
+ if (document.querySelector(`script[src*="${scriptUrlPart}"]`))
336
+ return Promise.resolve();
337
+ win.dataLayer = win.dataLayer || [];
338
+ // Push referrer domain context into dataLayer before GTM loads, so tags
339
+ // inside the container can use it for cross-domain tracking configuration.
476
340
  try {
477
- const eventData = {
478
- event,
479
- ...params,
480
- };
481
- console.log('[SDK GTM] Pushing event to dataLayer:', eventData);
482
- window.dataLayer.push(eventData);
483
- console.log('[SDK GTM] Event pushed successfully. Current dataLayer length:', window.dataLayer.length);
484
- }
485
- catch (error) {
486
- if (typeof console !== 'undefined') {
487
- console.error('[SDK GTM] Error tracking event:', error);
341
+ const ref = document.referrer && new URL(document.referrer).hostname;
342
+ if (ref && ref !== window.location.hostname) {
343
+ win.dataLayer.push({ tagada_referrer_domain: ref });
488
344
  }
489
345
  }
490
- }
491
- // --- Basic event mapping (can be extended later if needed) ---
492
- function mapMetaEvent(eventName, parameters) {
493
- return { name: eventName, params: parameters };
494
- }
495
- function mapTikTokEvent(eventName, parameters) {
496
- // TikTok naming is usually aligned; adjust here if needed later
497
- return { name: eventName, params: parameters };
498
- }
499
- function mapSnapchatEvent(eventName, parameters) {
500
- return { name: eventName, params: parameters };
501
- }
502
- function mapPinterestEvent(eventName, parameters) {
503
- // Pinterest uses lowercase event names
504
- const pinterestEventMap = {
505
- PageView: 'pagevisit',
506
- ViewContent: 'viewcontent',
507
- AddToCart: 'addtocart',
508
- InitiateCheckout: 'initiatecheckout',
509
- AddPaymentInfo: 'addpaymentinfo',
510
- Purchase: 'checkout',
511
- Lead: 'lead',
512
- CompleteRegistration: 'signup',
513
- };
514
- const pinterestEventName = pinterestEventMap[eventName] ?? eventName.toLowerCase();
515
- // Transform parameters for Pinterest
516
- const pinterestParams = transformPinterestParameters(eventName, parameters);
517
- return { name: pinterestEventName, params: pinterestParams };
518
- }
519
- function transformPinterestParameters(eventName, parameters) {
520
- const pinParams = { ...parameters };
521
- // Pinterest uses 'value' and 'order_quantity' as primary event data
522
- // Ensure currency is uppercase
523
- if (pinParams.currency) {
524
- pinParams.currency = String(pinParams.currency).toUpperCase();
346
+ catch { /* ignore invalid referrer */ }
347
+ if (isGtmContainer) {
348
+ win.dataLayer.push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' });
349
+ const f = document.getElementsByTagName('script')[0];
350
+ const j = document.createElement('script');
351
+ j.async = true;
352
+ j.src = 'https://www.googletagmanager.com/gtm.js?id=' + containerId;
353
+ if (f?.parentNode)
354
+ f.parentNode.insertBefore(j, f);
355
+ else
356
+ (document.head || document.getElementsByTagName('head')[0])?.appendChild(j);
357
+ const noscript = document.createElement('noscript');
358
+ const iframe = document.createElement('iframe');
359
+ iframe.src = `https://www.googletagmanager.com/ns.html?id=${containerId}`;
360
+ iframe.height = '0';
361
+ iframe.width = '0';
362
+ iframe.style.display = 'none';
363
+ iframe.style.visibility = 'hidden';
364
+ noscript.appendChild(iframe);
365
+ (document.body || document.getElementsByTagName('body')[0])?.insertBefore(noscript, document.body?.firstChild ?? null);
366
+ return waitForScriptLoad(j);
525
367
  }
526
- // Convert to major units if currency is present
527
- if (pinParams.currency && pinParams.value) {
528
- try {
529
- pinParams.value = minorUnitsToMajorUnits(Number(pinParams.value), String(pinParams.currency));
530
- }
531
- catch {
532
- // Fallback to simple division by 100
533
- pinParams.value = Number(pinParams.value) / 100;
368
+ else {
369
+ if (!win.gtag) {
370
+ win.gtag = function (..._args) { win.dataLayer.push(arguments); };
534
371
  }
535
- }
536
- // Ensure numeric values
537
- if (pinParams.value !== undefined) {
538
- pinParams.value = Number(pinParams.value);
539
- }
540
- if (pinParams.order_quantity !== undefined) {
541
- pinParams.order_quantity = Number(pinParams.order_quantity);
542
- }
543
- // Transform contents to Pinterest format (product_id array)
544
- if (pinParams.contents && Array.isArray(pinParams.contents)) {
545
- pinParams.line_items = pinParams.contents.map((item) => ({
546
- product_id: item.content_id,
547
- product_name: item.content_name,
548
- product_category: item.content_category,
549
- product_price: item.price ? Number(item.price) : undefined,
550
- product_quantity: item.quantity ? Number(item.quantity) : undefined,
551
- }));
552
- }
553
- return pinParams;
554
- }
555
- function mapGTMEvent(eventName, parameters) {
556
- // Map standard event names to GTM event names
557
- const gtmEventMap = {
558
- PageView: 'page_view',
559
- AddPaymentInfo: 'add_payment_info',
560
- AddToCart: 'add_to_cart',
561
- InitiateCheckout: 'begin_checkout',
562
- Purchase: 'purchase',
563
- ViewContent: 'view_item',
564
- CompleteRegistration: 'sign_up',
565
- };
566
- const gtmEventName = gtmEventMap[eventName] ?? eventName.toLowerCase();
567
- // Transform parameters for GTM
568
- const gtmParams = transformGTMParameters(eventName, parameters);
569
- return { name: gtmEventName, params: gtmParams };
570
- }
571
- function transformGTMParameters(eventName, parameters) {
572
- const gtmParameters = { ...parameters };
573
- // Ensure currency is uppercase
574
- if (gtmParameters.currency) {
575
- gtmParameters.currency = String(gtmParameters.currency).toUpperCase();
576
- }
577
- // Transform currency values to major units if needed
578
- if (gtmParameters.currency && gtmParameters.value) {
372
+ win.gtag('js', new Date());
373
+ // Enable cross-domain tracking: accept incoming _gl parameter from referring
374
+ // domains (e.g. Shopify store → TagadaPay checkout) so Google preserves the
375
+ // client-id/session across the redirect.
376
+ const linkerConfig = { accept_incoming: true };
579
377
  try {
580
- gtmParameters.value = minorUnitsToMajorUnits(Number(gtmParameters.value), String(gtmParameters.currency));
581
- }
582
- catch (error) {
583
- // If conversion fails, try simple division by 100 as fallback
584
- if (typeof console !== 'undefined') {
585
- console.warn('[SDK GTM] Currency conversion failed, using fallback:', error);
378
+ const ref = document.referrer && new URL(document.referrer).hostname;
379
+ if (ref && ref !== window.location.hostname) {
380
+ linkerConfig.domains = [ref, window.location.hostname];
586
381
  }
587
- gtmParameters.value = Number(gtmParameters.value) / 100;
588
382
  }
383
+ catch { /* ignore invalid referrer */ }
384
+ win.gtag('config', containerId, { linker: linkerConfig });
385
+ const script = document.createElement('script');
386
+ script.async = true;
387
+ script.src = 'https://www.googletagmanager.com/gtag/js?id=' + containerId;
388
+ const firstScript = document.getElementsByTagName('script')[0];
389
+ if (firstScript?.parentNode)
390
+ firstScript.parentNode.insertBefore(script, firstScript);
391
+ else
392
+ (document.head || document.getElementsByTagName('head')[0])?.appendChild(script);
393
+ return waitForScriptLoad(script);
589
394
  }
590
- // Ensure numeric values
591
- if (gtmParameters.value !== undefined) {
592
- gtmParameters.value = Number(gtmParameters.value);
593
- }
594
- if (gtmParameters.num_items !== undefined) {
595
- gtmParameters.num_items = Number(gtmParameters.num_items);
596
- }
597
- // Transform contents to a format better suited for GTM
598
- if (gtmParameters.contents && Array.isArray(gtmParameters.contents)) {
599
- gtmParameters.items = gtmParameters.contents.map((item) => ({
600
- item_id: item.content_id,
601
- item_name: item.content_name,
602
- item_category: item.content_category,
603
- price: item.price ? Number(item.price) : undefined,
604
- quantity: item.quantity ? Number(item.quantity) : undefined,
605
- }));
606
- // Keep original contents for compatibility
607
- gtmParameters.contents = gtmParameters.contents.map((item) => ({
608
- id: item.content_id,
609
- name: item.content_name,
610
- category: item.content_category,
611
- price: item.price ? Number(item.price) : undefined,
612
- quantity: item.quantity ? Number(item.quantity) : undefined,
613
- }));
614
- }
615
- return gtmParameters;
616
395
  }