@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
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core pixel event mapping, parameter transformation, and event gating.
|
|
3
|
+
*
|
|
4
|
+
* This module is framework-agnostic (no React, no DOM) so it can be shared
|
|
5
|
+
* between the React `PixelTrackingProvider` and the standalone vanilla SDK.
|
|
6
|
+
*
|
|
7
|
+
* Official event name references (verified Feb 2026 against vendor docs):
|
|
8
|
+
* Meta: fbq('track', 'Purchase', { value, currency }) — developers.facebook.com/docs/meta-pixel/reference
|
|
9
|
+
* TikTok: ttq.track('Purchase', { value, currency }) — ads.tiktok.com/help/article/standard-events-parameters (Sep 2025)
|
|
10
|
+
* Snapchat: snaptr('track', 'PURCHASE', { price, currency }) — developers.snap.com/api/marketing-api/Conversions-API/Parameters
|
|
11
|
+
* Pinterest: pintrk('track', 'checkout', { value, currency }) — help.pinterest.com/en/business/article/add-event-codes
|
|
12
|
+
* GTM/GA4: gtag('event', 'purchase', { value, currency, items })
|
|
13
|
+
*/
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Currency conversion helper (inline, zero-dep)
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
/**
|
|
18
|
+
* Convert minor units (cents) to major units using ISO 4217 digit counts.
|
|
19
|
+
* Falls back to ÷100 for unknown currencies.
|
|
20
|
+
*/
|
|
21
|
+
const ZERO_DECIMAL_CURRENCIES = new Set([
|
|
22
|
+
'BIF', 'CLP', 'DJF', 'GNF', 'ISK', 'JPY', 'KMF', 'KRW',
|
|
23
|
+
'PYG', 'RWF', 'UGX', 'VND', 'VUV', 'XAF', 'XOF', 'XPF',
|
|
24
|
+
]);
|
|
25
|
+
const THREE_DECIMAL_CURRENCIES = new Set(['BHD', 'IQD', 'JOD', 'KWD', 'LYD', 'OMR', 'TND']);
|
|
26
|
+
function minorToMajor(amount, currency) {
|
|
27
|
+
const code = currency.toUpperCase();
|
|
28
|
+
if (ZERO_DECIMAL_CURRENCIES.has(code))
|
|
29
|
+
return amount;
|
|
30
|
+
if (THREE_DECIMAL_CURRENCIES.has(code))
|
|
31
|
+
return amount / 1000;
|
|
32
|
+
return amount / 100;
|
|
33
|
+
}
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Event gating – respects per-event toggles
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
/**
|
|
38
|
+
* Returns `true` if the given event is enabled on the pixel config.
|
|
39
|
+
* If the pixel has no `events` map (e.g. MetaConversionTrackingConfig) we allow all.
|
|
40
|
+
*/
|
|
41
|
+
export function isEventEnabled(pixel, eventName) {
|
|
42
|
+
if (!('events' in pixel) || !pixel.events)
|
|
43
|
+
return true;
|
|
44
|
+
const events = pixel.events;
|
|
45
|
+
if (!(eventName in events))
|
|
46
|
+
return true; // unknown event → allow
|
|
47
|
+
return events[eventName] === true;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Filter an array of pixel configs to only those that are globally enabled
|
|
51
|
+
* AND have the specific event toggled on.
|
|
52
|
+
*/
|
|
53
|
+
export function getEligiblePixels(pixels, eventName) {
|
|
54
|
+
if (!pixels)
|
|
55
|
+
return [];
|
|
56
|
+
return pixels.filter((p) => p.enabled && isEventEnabled(p, eventName));
|
|
57
|
+
}
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// Shared parameter transforms
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
function ensureCurrencyUppercase(params) {
|
|
62
|
+
if (params.currency && typeof params.currency === 'string') {
|
|
63
|
+
return { ...params, currency: params.currency.toUpperCase() };
|
|
64
|
+
}
|
|
65
|
+
return { ...params };
|
|
66
|
+
}
|
|
67
|
+
function convertValueToMajor(params) {
|
|
68
|
+
if (params.currency && params.value != null) {
|
|
69
|
+
const currency = String(params.currency);
|
|
70
|
+
const major = minorToMajor(Number(params.value), currency);
|
|
71
|
+
return { ...params, value: major };
|
|
72
|
+
}
|
|
73
|
+
return params;
|
|
74
|
+
}
|
|
75
|
+
/** Convert top-level monetary fields (shipping, tax, subtotal, total_discount) from minor → major units. */
|
|
76
|
+
function convertMonetaryFieldsToMajor(params) {
|
|
77
|
+
const currency = params.currency ? String(params.currency) : null;
|
|
78
|
+
if (!currency)
|
|
79
|
+
return params;
|
|
80
|
+
const result = { ...params };
|
|
81
|
+
for (const field of ['shipping', 'tax', 'subtotal', 'total_discount']) {
|
|
82
|
+
if (result[field] != null) {
|
|
83
|
+
result[field] = minorToMajor(Number(result[field]), currency);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
/** Convert price fields inside contents[] items from minor → major units. */
|
|
89
|
+
function convertContentsPricesToMajor(params) {
|
|
90
|
+
if (!params.contents || !Array.isArray(params.contents))
|
|
91
|
+
return params;
|
|
92
|
+
const currency = params.currency ? String(params.currency) : null;
|
|
93
|
+
const result = { ...params };
|
|
94
|
+
result.contents = params.contents.map((item) => {
|
|
95
|
+
const itemCurrency = item.currency ? String(item.currency) : currency;
|
|
96
|
+
if (!itemCurrency)
|
|
97
|
+
return { ...item };
|
|
98
|
+
const converted = { ...item };
|
|
99
|
+
if (converted.price != null)
|
|
100
|
+
converted.price = minorToMajor(Number(converted.price), itemCurrency);
|
|
101
|
+
if (converted.original_price != null)
|
|
102
|
+
converted.original_price = minorToMajor(Number(converted.original_price), itemCurrency);
|
|
103
|
+
return converted;
|
|
104
|
+
});
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
function baseTransform(params) {
|
|
108
|
+
let p = ensureCurrencyUppercase(params);
|
|
109
|
+
p = convertValueToMajor(p);
|
|
110
|
+
p = convertMonetaryFieldsToMajor(p);
|
|
111
|
+
p = convertContentsPricesToMajor(p);
|
|
112
|
+
return p;
|
|
113
|
+
}
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
// Meta / Facebook
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
const META_EVENT_MAP = {
|
|
118
|
+
PageView: 'PageView',
|
|
119
|
+
ViewContent: 'ViewContent',
|
|
120
|
+
AddToCart: 'AddToCart',
|
|
121
|
+
AddToWishlist: 'AddToWishlist',
|
|
122
|
+
Search: 'Search',
|
|
123
|
+
InitiateCheckout: 'InitiateCheckout',
|
|
124
|
+
AddPaymentInfo: 'AddPaymentInfo',
|
|
125
|
+
Purchase: 'Purchase',
|
|
126
|
+
Lead: 'Lead',
|
|
127
|
+
CompleteRegistration: 'CompleteRegistration',
|
|
128
|
+
};
|
|
129
|
+
export function mapMetaEvent(eventName, parameters) {
|
|
130
|
+
const name = META_EVENT_MAP[eventName] ?? eventName;
|
|
131
|
+
const params = baseTransform(parameters);
|
|
132
|
+
if (params.contents && Array.isArray(params.contents)) {
|
|
133
|
+
params.content_ids = params.contents.map((i) => i.content_id).filter(Boolean);
|
|
134
|
+
params.content_type = params.content_type ?? 'product';
|
|
135
|
+
// Reshape contents to Meta's expected format (prices already converted by baseTransform)
|
|
136
|
+
params.contents = params.contents.map((item) => ({
|
|
137
|
+
id: item.content_id,
|
|
138
|
+
quantity: item.quantity != null ? Number(item.quantity) : undefined,
|
|
139
|
+
item_price: item.price != null ? Number(item.price) : undefined,
|
|
140
|
+
content_name: item.content_name,
|
|
141
|
+
content_category: item.content_category,
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
return { name, params };
|
|
145
|
+
}
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
// TikTok
|
|
148
|
+
// Official: ttq.track('Purchase', { value, currency })
|
|
149
|
+
// Source: ads.tiktok.com/help/article/standard-events-parameters (Sep 2025)
|
|
150
|
+
// Standard events use PascalCase. Note: TikTok has no standard PageView event
|
|
151
|
+
// (page views use ttq.page()), so we pass it as-is for custom tracking.
|
|
152
|
+
// TikTok has no Lead event; SubmitForm is the closest standard equivalent.
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
const TIKTOK_EVENT_MAP = {
|
|
155
|
+
PageView: 'Pageview',
|
|
156
|
+
ViewContent: 'ViewContent',
|
|
157
|
+
AddToCart: 'AddToCart',
|
|
158
|
+
AddToWishlist: 'AddToWishlist',
|
|
159
|
+
Search: 'Search',
|
|
160
|
+
InitiateCheckout: 'InitiateCheckout',
|
|
161
|
+
AddPaymentInfo: 'AddPaymentInfo',
|
|
162
|
+
Purchase: 'Purchase',
|
|
163
|
+
Lead: 'SubmitForm',
|
|
164
|
+
CompleteRegistration: 'CompleteRegistration',
|
|
165
|
+
};
|
|
166
|
+
export function mapTikTokEvent(eventName, parameters) {
|
|
167
|
+
const name = TIKTOK_EVENT_MAP[eventName] ?? eventName;
|
|
168
|
+
const params = baseTransform(parameters);
|
|
169
|
+
if (params.contents && Array.isArray(params.contents)) {
|
|
170
|
+
const ids = params.contents
|
|
171
|
+
.map((i) => i.content_id)
|
|
172
|
+
.filter(Boolean);
|
|
173
|
+
// TikTok requires content_id (singular) at the top level for VSA
|
|
174
|
+
if (!params.content_id && ids.length > 0) {
|
|
175
|
+
params.content_id = ids.length === 1 ? ids[0] : JSON.stringify(ids);
|
|
176
|
+
}
|
|
177
|
+
// Also keep content_ids for backwards compatibility
|
|
178
|
+
if (!params.content_ids) {
|
|
179
|
+
params.content_ids = ids;
|
|
180
|
+
}
|
|
181
|
+
// Reshape contents to TikTok's expected format (prices already converted by baseTransform)
|
|
182
|
+
params.contents = params.contents.map((item) => ({
|
|
183
|
+
content_id: item.content_id,
|
|
184
|
+
content_name: item.content_name,
|
|
185
|
+
content_type: item.content_type ?? 'product',
|
|
186
|
+
content_category: item.content_category,
|
|
187
|
+
price: item.price != null ? Number(item.price) : undefined,
|
|
188
|
+
original_price: item.original_price != null ? Number(item.original_price) : undefined,
|
|
189
|
+
quantity: item.quantity != null ? Number(item.quantity) : undefined,
|
|
190
|
+
description: item.description,
|
|
191
|
+
}));
|
|
192
|
+
}
|
|
193
|
+
// Set required fields for InitiateCheckout
|
|
194
|
+
if (eventName === 'InitiateCheckout') {
|
|
195
|
+
params.content_type = params.content_type ?? 'product';
|
|
196
|
+
if (!params.delivery_category) {
|
|
197
|
+
params.delivery_category = 'home_delivery';
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return { name, params };
|
|
201
|
+
}
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
// Snapchat
|
|
204
|
+
// Official: snaptr('track', 'PURCHASE', { price, currency })
|
|
205
|
+
// Source: developers.snap.com/api/marketing-api/Conversions-API/Parameters
|
|
206
|
+
// All event names are UPPER_SNAKE_CASE.
|
|
207
|
+
// Note: Snapchat has no LEAD standard event; we pass it as-is (custom event).
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
const SNAPCHAT_EVENT_MAP = {
|
|
210
|
+
PageView: 'PAGE_VIEW',
|
|
211
|
+
ViewContent: 'VIEW_CONTENT',
|
|
212
|
+
AddToCart: 'ADD_CART',
|
|
213
|
+
AddToWishlist: 'ADD_TO_WISHLIST',
|
|
214
|
+
Search: 'SEARCH',
|
|
215
|
+
InitiateCheckout: 'START_CHECKOUT',
|
|
216
|
+
AddPaymentInfo: 'ADD_BILLING',
|
|
217
|
+
Purchase: 'PURCHASE',
|
|
218
|
+
Lead: 'LEAD',
|
|
219
|
+
CompleteRegistration: 'SIGN_UP',
|
|
220
|
+
};
|
|
221
|
+
export function mapSnapchatEvent(eventName, parameters) {
|
|
222
|
+
const name = SNAPCHAT_EVENT_MAP[eventName] ?? eventName;
|
|
223
|
+
let params = baseTransform(parameters);
|
|
224
|
+
// Snapchat uses `price` not `value`
|
|
225
|
+
if ('value' in params) {
|
|
226
|
+
params = { ...params, price: params.value };
|
|
227
|
+
delete params.value;
|
|
228
|
+
}
|
|
229
|
+
// Snapchat uses `number_items` not `num_items`
|
|
230
|
+
if ('num_items' in params) {
|
|
231
|
+
params = { ...params, number_items: params.num_items };
|
|
232
|
+
delete params.num_items;
|
|
233
|
+
}
|
|
234
|
+
if (params.contents && Array.isArray(params.contents)) {
|
|
235
|
+
params.item_ids = params.contents.map((i) => i.content_id).filter(Boolean);
|
|
236
|
+
}
|
|
237
|
+
return { name, params };
|
|
238
|
+
}
|
|
239
|
+
// ---------------------------------------------------------------------------
|
|
240
|
+
// Pinterest
|
|
241
|
+
// Official: pintrk('track', 'checkout', { value, currency, line_items })
|
|
242
|
+
// Event names are all lowercase.
|
|
243
|
+
// ---------------------------------------------------------------------------
|
|
244
|
+
const PINTEREST_EVENT_MAP = {
|
|
245
|
+
PageView: 'pagevisit',
|
|
246
|
+
ViewContent: 'viewcontent',
|
|
247
|
+
AddToCart: 'addtocart',
|
|
248
|
+
AddToWishlist: 'addtowishlist',
|
|
249
|
+
Search: 'search',
|
|
250
|
+
InitiateCheckout: 'initiatecheckout',
|
|
251
|
+
AddPaymentInfo: 'addpaymentinfo',
|
|
252
|
+
Purchase: 'checkout',
|
|
253
|
+
Lead: 'lead',
|
|
254
|
+
CompleteRegistration: 'signup',
|
|
255
|
+
};
|
|
256
|
+
export function mapPinterestEvent(eventName, parameters) {
|
|
257
|
+
const name = PINTEREST_EVENT_MAP[eventName] ?? eventName.toLowerCase();
|
|
258
|
+
const params = baseTransform(parameters);
|
|
259
|
+
if (params.order_quantity !== undefined) {
|
|
260
|
+
params.order_quantity = Number(params.order_quantity);
|
|
261
|
+
}
|
|
262
|
+
if (params.contents && Array.isArray(params.contents)) {
|
|
263
|
+
// Reshape to Pinterest line_items format (prices already converted by baseTransform)
|
|
264
|
+
params.line_items = params.contents.map((item) => ({
|
|
265
|
+
product_id: item.content_id,
|
|
266
|
+
product_name: item.content_name,
|
|
267
|
+
product_category: item.content_category,
|
|
268
|
+
product_price: item.price != null ? Number(item.price) : undefined,
|
|
269
|
+
product_quantity: item.quantity != null ? Number(item.quantity) : undefined,
|
|
270
|
+
}));
|
|
271
|
+
}
|
|
272
|
+
return { name, params };
|
|
273
|
+
}
|
|
274
|
+
// ---------------------------------------------------------------------------
|
|
275
|
+
// GTM / GA4 / Google Ads
|
|
276
|
+
// Official: gtag('event', 'purchase', { value, currency, items })
|
|
277
|
+
// Event names are snake_case (GA4 convention).
|
|
278
|
+
// ---------------------------------------------------------------------------
|
|
279
|
+
const GTM_EVENT_MAP = {
|
|
280
|
+
PageView: 'page_view',
|
|
281
|
+
ViewContent: 'view_item',
|
|
282
|
+
AddToCart: 'add_to_cart',
|
|
283
|
+
AddToWishlist: 'add_to_wishlist',
|
|
284
|
+
Search: 'search',
|
|
285
|
+
InitiateCheckout: 'begin_checkout',
|
|
286
|
+
AddPaymentInfo: 'add_payment_info',
|
|
287
|
+
Purchase: 'purchase',
|
|
288
|
+
Lead: 'generate_lead',
|
|
289
|
+
CompleteRegistration: 'sign_up',
|
|
290
|
+
Conversion: 'conversion',
|
|
291
|
+
};
|
|
292
|
+
export function mapGTMEvent(eventName, parameters) {
|
|
293
|
+
const name = GTM_EVENT_MAP[eventName] ?? eventName.toLowerCase();
|
|
294
|
+
const params = baseTransform(parameters);
|
|
295
|
+
if (params.num_items !== undefined) {
|
|
296
|
+
params.num_items = Number(params.num_items);
|
|
297
|
+
}
|
|
298
|
+
if (params.contents && Array.isArray(params.contents)) {
|
|
299
|
+
// Reshape to GA4 items format (prices already converted by baseTransform)
|
|
300
|
+
params.items = params.contents.map((item) => ({
|
|
301
|
+
item_id: item.content_id,
|
|
302
|
+
item_name: item.content_name,
|
|
303
|
+
item_category: item.content_category,
|
|
304
|
+
price: item.price != null ? Number(item.price) : undefined,
|
|
305
|
+
quantity: item.quantity != null ? Number(item.quantity) : undefined,
|
|
306
|
+
}));
|
|
307
|
+
params.contents = params.contents.map((item) => ({
|
|
308
|
+
id: item.content_id,
|
|
309
|
+
name: item.content_name,
|
|
310
|
+
category: item.content_category,
|
|
311
|
+
price: item.price != null ? Number(item.price) : undefined,
|
|
312
|
+
quantity: item.quantity != null ? Number(item.quantity) : undefined,
|
|
313
|
+
}));
|
|
314
|
+
}
|
|
315
|
+
return { name, params };
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Append Google Ads `send_to` parameter when conversion tracking is configured.
|
|
319
|
+
*/
|
|
320
|
+
export function applyGoogleAdsConversion(params, eventName, pixel) {
|
|
321
|
+
if ((eventName === 'Purchase' || eventName === 'Conversion') &&
|
|
322
|
+
pixel.googleAdsConversionId &&
|
|
323
|
+
pixel.googleAdsConversionLabel) {
|
|
324
|
+
return { ...params, send_to: `${pixel.googleAdsConversionId}/${pixel.googleAdsConversionLabel}` };
|
|
325
|
+
}
|
|
326
|
+
return params;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Given a PixelsConfig and an event, return per-provider mapped events
|
|
330
|
+
* for every eligible (enabled + event-toggled-on) pixel.
|
|
331
|
+
*/
|
|
332
|
+
export function resolvePixelEvents(pixels, eventName, parameters) {
|
|
333
|
+
const results = [];
|
|
334
|
+
const fb = getEligiblePixels(pixels.facebook, eventName);
|
|
335
|
+
if (fb.length > 0) {
|
|
336
|
+
const mapped = mapMetaEvent(eventName, parameters);
|
|
337
|
+
fb.forEach((pixel) => results.push({ provider: 'facebook', mapped, pixel }));
|
|
338
|
+
}
|
|
339
|
+
const tt = getEligiblePixels(pixels.tiktok, eventName);
|
|
340
|
+
if (tt.length > 0) {
|
|
341
|
+
const mapped = mapTikTokEvent(eventName, parameters);
|
|
342
|
+
tt.forEach((pixel) => results.push({ provider: 'tiktok', mapped, pixel }));
|
|
343
|
+
}
|
|
344
|
+
const sc = getEligiblePixels(pixels.snapchat, eventName);
|
|
345
|
+
if (sc.length > 0) {
|
|
346
|
+
const mapped = mapSnapchatEvent(eventName, parameters);
|
|
347
|
+
sc.forEach((pixel) => results.push({ provider: 'snapchat', mapped, pixel }));
|
|
348
|
+
}
|
|
349
|
+
const pt = getEligiblePixels(pixels.pinterest, eventName);
|
|
350
|
+
if (pt.length > 0) {
|
|
351
|
+
const mapped = mapPinterestEvent(eventName, parameters);
|
|
352
|
+
pt.forEach((pixel) => results.push({ provider: 'pinterest', mapped, pixel }));
|
|
353
|
+
}
|
|
354
|
+
const gtm = getEligiblePixels(pixels.gtm, eventName);
|
|
355
|
+
if (gtm.length > 0) {
|
|
356
|
+
const mapped = mapGTMEvent(eventName, parameters);
|
|
357
|
+
gtm.forEach((pixel) => {
|
|
358
|
+
const finalParams = applyGoogleAdsConversion(mapped.params, eventName, pixel);
|
|
359
|
+
results.push({ provider: 'gtm', mapped: { name: mapped.name, params: finalParams }, pixel });
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
return results;
|
|
363
|
+
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Shared between all resource clients
|
|
4
4
|
*/
|
|
5
5
|
import axios from 'axios';
|
|
6
|
+
import { TagadaApiError, TagadaAuthError, TagadaNetworkError, TagadaCircuitBreakerError, TagadaError, TagadaErrorCode, } from '../errors';
|
|
6
7
|
export class ApiClient {
|
|
7
8
|
constructor(config) {
|
|
8
9
|
this.currentToken = null;
|
|
@@ -61,12 +62,15 @@ export class ApiClient {
|
|
|
61
62
|
console.error('[SDK] Request error:', error);
|
|
62
63
|
return Promise.reject(error instanceof Error ? error : new Error(String(error)));
|
|
63
64
|
});
|
|
64
|
-
// Response interceptor
|
|
65
|
-
this.axios.interceptors.response.use((response) => {
|
|
66
|
-
// console.log('[SDK] Response status:', response.status);
|
|
67
|
-
return response;
|
|
68
|
-
}, (error) => {
|
|
65
|
+
// Response interceptor — maps Axios errors to structured TagadaError subtypes
|
|
66
|
+
this.axios.interceptors.response.use((response) => response, (error) => {
|
|
69
67
|
console.error('[SDK] Response error:', error.message);
|
|
68
|
+
if (error instanceof TagadaError) {
|
|
69
|
+
return Promise.reject(error);
|
|
70
|
+
}
|
|
71
|
+
if (axios.isAxiosError(error)) {
|
|
72
|
+
return Promise.reject(this.toTagadaError(error));
|
|
73
|
+
}
|
|
70
74
|
return Promise.reject(error instanceof Error ? error : new Error(String(error)));
|
|
71
75
|
});
|
|
72
76
|
}
|
|
@@ -146,10 +150,7 @@ export class ApiClient {
|
|
|
146
150
|
}
|
|
147
151
|
history.count++;
|
|
148
152
|
if (history.count > this.MAX_REQUESTS) {
|
|
149
|
-
|
|
150
|
-
// Add a property to identify this as a circuit breaker error
|
|
151
|
-
error.isCircuitBreaker = true;
|
|
152
|
-
throw error;
|
|
153
|
+
throw new TagadaCircuitBreakerError(`Circuit Breaker: Too many requests to ${key} (${history.count} in ${this.WINDOW_MS}ms)`);
|
|
153
154
|
}
|
|
154
155
|
}
|
|
155
156
|
cleanupHistory() {
|
|
@@ -160,4 +161,46 @@ export class ApiClient {
|
|
|
160
161
|
}
|
|
161
162
|
}
|
|
162
163
|
}
|
|
164
|
+
/** Convert an AxiosError into the appropriate TagadaError subclass. */
|
|
165
|
+
toTagadaError(error) {
|
|
166
|
+
const status = error.response?.status;
|
|
167
|
+
const data = error.response?.data;
|
|
168
|
+
const serverMessage = data?.message ??
|
|
169
|
+
data?.error ??
|
|
170
|
+
error.message;
|
|
171
|
+
// No response at all → network-level failure
|
|
172
|
+
if (!error.response) {
|
|
173
|
+
if (error.code === 'ECONNABORTED') {
|
|
174
|
+
return new TagadaApiError('Request timed out', 0, {
|
|
175
|
+
code: TagadaErrorCode.TIMEOUT,
|
|
176
|
+
retryable: true,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
return new TagadaNetworkError(serverMessage);
|
|
180
|
+
}
|
|
181
|
+
// Auth failures
|
|
182
|
+
if (status === 401 || status === 403) {
|
|
183
|
+
return new TagadaAuthError(serverMessage, status);
|
|
184
|
+
}
|
|
185
|
+
// Rate-limited
|
|
186
|
+
if (status === 429) {
|
|
187
|
+
return new TagadaApiError(serverMessage, 429, {
|
|
188
|
+
code: TagadaErrorCode.RATE_LIMITED,
|
|
189
|
+
retryable: true,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
// Not found
|
|
193
|
+
if (status === 404) {
|
|
194
|
+
return new TagadaApiError(serverMessage, 404, {
|
|
195
|
+
code: TagadaErrorCode.NOT_FOUND,
|
|
196
|
+
retryable: false,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
// Generic API error
|
|
200
|
+
return new TagadaApiError(serverMessage, status ?? 0, {
|
|
201
|
+
code: data?.code ?? TagadaErrorCode.API_ERROR,
|
|
202
|
+
details: data,
|
|
203
|
+
retryable: (status ?? 0) >= 500,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
163
206
|
}
|