@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.
- package/build-cdn.js +397 -11
- package/dist/data/iso3166.d.ts +23 -33
- package/dist/data/iso3166.js +134 -198
- package/dist/data/languages.d.ts +5 -64
- package/dist/data/languages.js +23 -143
- package/dist/external-tracker.js +623 -3426
- package/dist/external-tracker.min.js +2 -25
- package/dist/external-tracker.min.js.map +4 -4
- package/dist/react/config/payment.d.ts +14 -4
- package/dist/react/config/payment.js +47 -9
- package/dist/react/hooks/useCheckout.d.ts +3 -0
- package/dist/react/hooks/useCheckout.js +4 -1
- package/dist/react/hooks/useISOData.js +1 -1
- package/dist/react/hooks/usePaymentPolling.d.ts +3 -3
- package/dist/react/hooks/usePluginConfig.js +9 -10
- package/dist/react/providers/TagadaProvider.js +1 -1
- package/dist/tagada-react-sdk-minimal.min.js +36 -0
- package/dist/tagada-react-sdk-minimal.min.js.map +7 -0
- package/dist/tagada-react-sdk.js +37821 -0
- package/dist/tagada-react-sdk.min.js +78 -0
- package/dist/tagada-react-sdk.min.js.map +7 -0
- package/dist/tagada-sdk.js +16044 -0
- package/dist/tagada-sdk.min.js +32 -0
- package/dist/tagada-sdk.min.js.map +7 -0
- package/dist/v2/cdn-react-minimal.d.ts +23 -0
- package/dist/v2/cdn-react-minimal.js +26 -0
- package/dist/v2/core/client.d.ts +4 -2
- package/dist/v2/core/client.js +5 -4
- package/dist/v2/core/config/environment.js +2 -1
- package/dist/v2/core/errors.d.ts +75 -0
- package/dist/v2/core/errors.js +104 -0
- package/dist/v2/core/funnelClient.d.ts +100 -10
- package/dist/v2/core/funnelClient.js +121 -27
- package/dist/v2/core/isoData.d.ts +4 -4
- package/dist/v2/core/isoData.js +7 -7
- package/dist/v2/core/pixelMapping.d.ts +49 -0
- package/dist/v2/core/pixelMapping.js +363 -0
- package/dist/v2/core/resources/apiClient.d.ts +2 -0
- package/dist/v2/core/resources/apiClient.js +52 -9
- package/dist/v2/core/resources/checkout.d.ts +99 -30
- package/dist/v2/core/resources/checkout.js +14 -0
- package/dist/v2/core/resources/customer.d.ts +20 -19
- package/dist/v2/core/resources/expressPaymentMethods.d.ts +1 -0
- package/dist/v2/core/resources/funnel.d.ts +17 -17
- package/dist/v2/core/resources/payments.d.ts +89 -13
- package/dist/v2/core/resources/payments.js +27 -9
- package/dist/v2/core/resources/postPurchases.d.ts +17 -0
- package/dist/v2/core/resources/postPurchases.js +20 -0
- package/dist/v2/core/types.d.ts +50 -12
- package/dist/v2/core/types.js +0 -3
- package/dist/v2/core/utils/checkout.d.ts +2 -2
- package/dist/v2/core/utils/checkout.js +7 -2
- package/dist/v2/core/utils/currency.d.ts +14 -0
- package/dist/v2/core/utils/currency.js +40 -0
- package/dist/v2/core/utils/deviceInfo.d.ts +0 -10
- package/dist/v2/core/utils/deviceInfo.js +152 -76
- package/dist/v2/core/utils/index.d.ts +1 -0
- package/dist/v2/core/utils/index.js +2 -0
- package/dist/v2/core/utils/order.d.ts +13 -9
- package/dist/v2/core/utils/pluginConfig.d.ts +8 -0
- package/dist/v2/core/utils/pluginConfig.js +36 -12
- package/dist/v2/index.d.ts +6 -3
- package/dist/v2/index.js +4 -2
- package/dist/v2/react/components/FunnelScriptInjector.js +166 -77
- package/dist/v2/react/components/StripeExpressButton.d.ts +13 -0
- package/dist/v2/react/components/StripeExpressButton.js +171 -0
- package/dist/v2/react/components/WhopCheckout.d.ts +24 -0
- package/dist/v2/react/components/WhopCheckout.js +237 -0
- package/dist/v2/react/hooks/__examples__/FunnelContextExample.js +1 -1
- package/dist/v2/react/hooks/payment-actions/useAirwallexRadarAction.d.ts +14 -0
- package/dist/v2/react/hooks/payment-actions/useAirwallexRadarAction.js +181 -0
- package/dist/v2/react/hooks/payment-actions/useErrorAction.d.ts +9 -0
- package/dist/v2/react/hooks/payment-actions/useErrorAction.js +21 -0
- package/dist/v2/react/hooks/payment-actions/useFinixRadarAction.d.ts +14 -0
- package/dist/v2/react/hooks/payment-actions/useFinixRadarAction.js +187 -0
- package/dist/v2/react/hooks/payment-actions/useKessPayAction.d.ts +11 -0
- package/dist/v2/react/hooks/payment-actions/useKessPayAction.js +91 -0
- package/dist/v2/react/hooks/payment-actions/useMasterCardAction.d.ts +24 -0
- package/dist/v2/react/hooks/payment-actions/useMasterCardAction.js +221 -0
- package/dist/v2/react/hooks/payment-actions/usePaymentActionHandler.d.ts +15 -0
- package/dist/v2/react/hooks/payment-actions/usePaymentActionHandler.js +142 -0
- package/dist/v2/react/hooks/payment-actions/useProcessorAuthAction.d.ts +3 -0
- package/dist/v2/react/hooks/payment-actions/useProcessorAuthAction.js +31 -0
- package/dist/v2/react/hooks/payment-actions/useRedirectAction.d.ts +10 -0
- package/dist/v2/react/hooks/payment-actions/useRedirectAction.js +35 -0
- package/dist/v2/react/hooks/payment-actions/useStripeRadarAction.d.ts +14 -0
- package/dist/v2/react/hooks/payment-actions/useStripeRadarAction.js +192 -0
- package/dist/v2/react/hooks/payment-actions/useThreedsAuthAction.d.ts +14 -0
- package/dist/v2/react/hooks/payment-actions/useThreedsAuthAction.js +81 -0
- package/dist/v2/react/hooks/payment-actions/useTrustFlowAction.d.ts +11 -0
- package/dist/v2/react/hooks/payment-actions/useTrustFlowAction.js +84 -0
- package/dist/v2/react/hooks/payment-processing/usePaymentInstruments.d.ts +14 -0
- package/dist/v2/react/hooks/payment-processing/usePaymentInstruments.js +36 -0
- package/dist/v2/react/hooks/payment-processing/usePaymentProcessors.d.ts +31 -0
- package/dist/v2/react/hooks/payment-processing/usePaymentProcessors.js +212 -0
- package/dist/v2/react/hooks/payment-redirect/useAirwallex3dsReturn.d.ts +14 -0
- package/dist/v2/react/hooks/payment-redirect/useAirwallex3dsReturn.js +207 -0
- package/dist/v2/react/hooks/payment-redirect/useGenericPaymentReturn.d.ts +12 -0
- package/dist/v2/react/hooks/payment-redirect/useGenericPaymentReturn.js +101 -0
- package/dist/v2/react/hooks/useApplePayCheckout.js +8 -8
- package/dist/v2/react/hooks/useCheckoutQuery.d.ts +16 -0
- package/dist/v2/react/hooks/useCheckoutQuery.js +63 -10
- package/dist/v2/react/hooks/useFunnel.d.ts +15 -4
- package/dist/v2/react/hooks/useFunnel.js +8 -4
- package/dist/v2/react/hooks/useGeoLocation.d.ts +2 -1
- package/dist/v2/react/hooks/useGeoLocation.js +4 -2
- package/dist/v2/react/hooks/useGoogleAutocomplete.d.ts +2 -0
- package/dist/v2/react/hooks/useGoogleAutocomplete.js +29 -15
- package/dist/v2/react/hooks/useISOData.d.ts +2 -5
- package/dist/v2/react/hooks/useISOData.js +26 -27
- package/dist/v2/react/hooks/usePaymentPolling.d.ts +3 -3
- package/dist/v2/react/hooks/usePaymentQuery.d.ts +18 -5
- package/dist/v2/react/hooks/usePaymentQuery.js +63 -1015
- package/dist/v2/react/hooks/usePaymentRetrieve.d.ts +3 -2
- package/dist/v2/react/hooks/usePaymentRetrieve.js +3 -1
- package/dist/v2/react/hooks/usePixelTracking.d.ts +5 -48
- package/dist/v2/react/hooks/usePixelTracking.js +283 -504
- package/dist/v2/react/hooks/usePostPurchasesQuery.js +34 -2
- package/dist/v2/react/hooks/useRemappableParams.d.ts +2 -6
- package/dist/v2/react/hooks/useRemappableParams.js +23 -23
- package/dist/v2/react/hooks/useSetPaymentMethod.d.ts +16 -0
- package/dist/v2/react/hooks/useSetPaymentMethod.js +33 -0
- package/dist/v2/react/hooks/useShippingRatesQuery.js +13 -5
- package/dist/v2/react/hooks/useStepConfig.d.ts +23 -6
- package/dist/v2/react/hooks/useStepConfig.js +14 -7
- package/dist/v2/react/hooks/useTranslation.js +23 -8
- package/dist/v2/react/hooks/useWhopPaymentPolling.d.ts +30 -0
- package/dist/v2/react/hooks/useWhopPaymentPolling.js +61 -0
- package/dist/v2/react/index.d.ts +15 -1
- package/dist/v2/react/index.js +7 -0
- package/dist/v2/react/providers/ExpressPaymentMethodsProvider.d.ts +3 -1
- package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +12 -2
- package/dist/v2/react/providers/TagadaProvider.js +74 -5
- package/dist/v2/standalone/external-tracker.d.ts +52 -46
- package/dist/v2/standalone/external-tracker.js +205 -98
- package/dist/v2/standalone/index.d.ts +40 -0
- package/dist/v2/standalone/index.js +148 -1
- package/dist/v2/standalone/payment-service.d.ts +134 -0
- package/dist/v2/standalone/payment-service.js +928 -0
- package/package.json +6 -4
- package/dist/react/utils/__tests__/urlUtils.test.d.ts +0 -1
- package/dist/react/utils/__tests__/urlUtils.test.js +0 -189
- package/dist/v2/core/__tests__/pathRemapping.test.d.ts +0 -11
- 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.
|
|
8
|
-
*
|
|
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 {
|
|
11
|
+
import { resolvePixelEvents, } from '../../core/pixelMapping';
|
|
13
12
|
import { useStepConfig } from './useStepConfig';
|
|
14
13
|
const PixelTrackingContext = createContext(null);
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
40
|
-
|
|
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
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (
|
|
140
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
102
|
+
// ---- Auto page-view ----
|
|
216
103
|
useEffect(() => {
|
|
217
104
|
if (!pixelsInitialized || !isMountedRef.current)
|
|
218
105
|
return;
|
|
219
|
-
|
|
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
|
-
|
|
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
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
274
|
-
|
|
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 =
|
|
278
|
-
s
|
|
279
|
-
|
|
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
|
-
|
|
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 (
|
|
293
|
-
return;
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
-
'
|
|
308
|
-
'
|
|
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 (
|
|
321
|
-
|
|
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.
|
|
329
|
-
const
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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 (
|
|
352
|
-
return;
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
-
|
|
368
|
-
const
|
|
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 =
|
|
373
|
-
u
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
snaptr
|
|
377
|
-
|
|
378
|
-
|
|
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 (
|
|
386
|
-
return;
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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
|
-
|
|
397
|
-
const s =
|
|
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 =
|
|
401
|
-
u
|
|
402
|
-
|
|
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
|
|
467
|
-
if (
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
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
|
|
478
|
-
|
|
479
|
-
|
|
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
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
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
|
-
|
|
527
|
-
|
|
528
|
-
|
|
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
|
-
|
|
537
|
-
|
|
538
|
-
|
|
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
|
-
|
|
581
|
-
|
|
582
|
-
|
|
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
|
}
|