@tagadapay/plugin-sdk 3.1.11 → 3.1.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1129 -1129
- package/build-cdn.js +231 -228
- 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 +968 -102
- package/dist/external-tracker.min.js +2 -2
- package/dist/external-tracker.min.js.map +4 -4
- package/dist/react/hooks/useISOData.js +1 -1
- package/dist/react/hooks/usePaymentPolling.d.ts +3 -3
- package/dist/react/hooks/useShippingRates.d.ts +6 -0
- package/dist/react/hooks/useShippingRates.js +38 -0
- package/dist/react/providers/TagadaProvider.js +5 -5
- package/dist/react/services/apiService.d.ts +21 -0
- package/dist/react/services/apiService.js +10 -0
- package/dist/tagada-sdk.js +2079 -179
- package/dist/tagada-sdk.min.js +4 -2
- package/dist/tagada-sdk.min.js.map +4 -4
- package/dist/v2/core/client.d.ts +4 -2
- package/dist/v2/core/client.js +4 -3
- package/dist/v2/core/errors.d.ts +75 -0
- package/dist/v2/core/errors.js +104 -0
- package/dist/v2/core/funnelClient.d.ts +16 -15
- package/dist/v2/core/funnelClient.js +1 -1
- package/dist/v2/core/index.d.ts +1 -0
- package/dist/v2/core/index.js +2 -0
- package/dist/v2/core/pixelMapping.d.ts +49 -0
- package/dist/v2/core/pixelMapping.js +325 -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 +89 -30
- package/dist/v2/core/resources/checkout.js +8 -0
- package/dist/v2/core/resources/customer.d.ts +20 -19
- package/dist/v2/core/resources/funnel.d.ts +17 -17
- package/dist/v2/core/resources/payments.d.ts +84 -13
- package/dist/v2/core/resources/payments.js +26 -9
- package/dist/v2/core/resources/shippingRates.d.ts +15 -0
- package/dist/v2/core/resources/shippingRates.js +11 -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/order.d.ts +11 -9
- package/dist/v2/core/utils/previewModeIndicator.js +101 -101
- package/dist/v2/index.d.ts +4 -2
- package/dist/v2/index.js +1 -1
- package/dist/v2/react/components/ApplePayButton.js +13 -4
- package/dist/v2/react/components/FunnelScriptInjector.js +51 -30
- package/dist/v2/react/components/WhopCheckout.d.ts +24 -0
- package/dist/v2/react/components/WhopCheckout.js +231 -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 +13 -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/useCheckoutQuery.d.ts +6 -0
- package/dist/v2/react/hooks/useCheckoutQuery.js +45 -0
- package/dist/v2/react/hooks/useFunnel.d.ts +1 -2
- 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.js +82 -33
- package/dist/v2/react/hooks/useISOData.js +1 -1
- 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 -43
- package/dist/v2/react/hooks/usePixelTracking.js +213 -407
- package/dist/v2/react/hooks/useShippingRatesQuery.d.ts +6 -0
- package/dist/v2/react/hooks/useShippingRatesQuery.js +47 -4
- package/dist/v2/react/hooks/useStepConfig.d.ts +2 -8
- package/dist/v2/react/hooks/useStepConfig.js +1 -1
- 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 +7 -0
- package/dist/v2/react/index.js +4 -0
- package/dist/v2/react/providers/ExpressPaymentMethodsProvider.d.ts +2 -1
- package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +3 -1
- package/dist/v2/react/providers/TagadaProvider.js +76 -7
- 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 +22 -0
- package/dist/v2/standalone/index.js +125 -0
- package/package.json +112 -112
- 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,325 @@
|
|
|
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
|
+
function baseTransform(params) {
|
|
76
|
+
let p = ensureCurrencyUppercase(params);
|
|
77
|
+
p = convertValueToMajor(p);
|
|
78
|
+
return p;
|
|
79
|
+
}
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// Meta / Facebook
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
const META_EVENT_MAP = {
|
|
84
|
+
PageView: 'PageView',
|
|
85
|
+
ViewContent: 'ViewContent',
|
|
86
|
+
AddToCart: 'AddToCart',
|
|
87
|
+
AddToWishlist: 'AddToWishlist',
|
|
88
|
+
Search: 'Search',
|
|
89
|
+
InitiateCheckout: 'InitiateCheckout',
|
|
90
|
+
AddPaymentInfo: 'AddPaymentInfo',
|
|
91
|
+
Purchase: 'Purchase',
|
|
92
|
+
Lead: 'Lead',
|
|
93
|
+
CompleteRegistration: 'CompleteRegistration',
|
|
94
|
+
};
|
|
95
|
+
export function mapMetaEvent(eventName, parameters) {
|
|
96
|
+
const name = META_EVENT_MAP[eventName] ?? eventName;
|
|
97
|
+
const params = baseTransform(parameters);
|
|
98
|
+
if (params.contents && Array.isArray(params.contents)) {
|
|
99
|
+
params.content_ids = params.contents.map((i) => i.content_id).filter(Boolean);
|
|
100
|
+
params.content_type = params.content_type ?? 'product';
|
|
101
|
+
}
|
|
102
|
+
return { name, params };
|
|
103
|
+
}
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// TikTok
|
|
106
|
+
// Official: ttq.track('Purchase', { value, currency })
|
|
107
|
+
// Source: ads.tiktok.com/help/article/standard-events-parameters (Sep 2025)
|
|
108
|
+
// Standard events use PascalCase. Note: TikTok has no standard PageView event
|
|
109
|
+
// (page views use ttq.page()), so we pass it as-is for custom tracking.
|
|
110
|
+
// TikTok has no Lead event; SubmitForm is the closest standard equivalent.
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
const TIKTOK_EVENT_MAP = {
|
|
113
|
+
PageView: 'Pageview',
|
|
114
|
+
ViewContent: 'ViewContent',
|
|
115
|
+
AddToCart: 'AddToCart',
|
|
116
|
+
AddToWishlist: 'AddToWishlist',
|
|
117
|
+
Search: 'Search',
|
|
118
|
+
InitiateCheckout: 'InitiateCheckout',
|
|
119
|
+
AddPaymentInfo: 'AddPaymentInfo',
|
|
120
|
+
Purchase: 'Purchase',
|
|
121
|
+
Lead: 'SubmitForm',
|
|
122
|
+
CompleteRegistration: 'CompleteRegistration',
|
|
123
|
+
};
|
|
124
|
+
export function mapTikTokEvent(eventName, parameters) {
|
|
125
|
+
const name = TIKTOK_EVENT_MAP[eventName] ?? eventName;
|
|
126
|
+
const params = baseTransform(parameters);
|
|
127
|
+
// Convert additional monetary fields from minor to major units
|
|
128
|
+
const currency = params.currency ? String(params.currency) : null;
|
|
129
|
+
if (currency) {
|
|
130
|
+
for (const field of ['shipping', 'tax', 'subtotal', 'total_discount']) {
|
|
131
|
+
if (params[field] != null) {
|
|
132
|
+
params[field] = minorToMajor(Number(params[field]), currency);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (params.contents && Array.isArray(params.contents)) {
|
|
137
|
+
// Extract content_ids at top level for TikTok product matching
|
|
138
|
+
if (!params.content_ids) {
|
|
139
|
+
params.content_ids = params.contents
|
|
140
|
+
.map((i) => i.content_id)
|
|
141
|
+
.filter(Boolean);
|
|
142
|
+
}
|
|
143
|
+
params.contents = params.contents.map((item) => {
|
|
144
|
+
const itemCurrency = item.currency ? String(item.currency) : currency;
|
|
145
|
+
return {
|
|
146
|
+
content_id: item.content_id,
|
|
147
|
+
content_name: item.content_name,
|
|
148
|
+
content_type: item.content_type ?? 'product',
|
|
149
|
+
content_category: item.content_category,
|
|
150
|
+
price: item.price != null && itemCurrency ? minorToMajor(Number(item.price), itemCurrency) : item.price != null ? Number(item.price) : undefined,
|
|
151
|
+
original_price: item.original_price != null && itemCurrency ? minorToMajor(Number(item.original_price), itemCurrency) : item.original_price != null ? Number(item.original_price) : undefined,
|
|
152
|
+
quantity: item.quantity != null ? Number(item.quantity) : undefined,
|
|
153
|
+
description: item.description,
|
|
154
|
+
};
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
// Set required fields for InitiateCheckout
|
|
158
|
+
if (eventName === 'InitiateCheckout') {
|
|
159
|
+
params.content_type = params.content_type ?? 'product';
|
|
160
|
+
if (!params.delivery_category) {
|
|
161
|
+
params.delivery_category = 'home_delivery';
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return { name, params };
|
|
165
|
+
}
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
// Snapchat
|
|
168
|
+
// Official: snaptr('track', 'PURCHASE', { price, currency })
|
|
169
|
+
// Source: developers.snap.com/api/marketing-api/Conversions-API/Parameters
|
|
170
|
+
// All event names are UPPER_SNAKE_CASE.
|
|
171
|
+
// Note: Snapchat has no LEAD standard event; we pass it as-is (custom event).
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
const SNAPCHAT_EVENT_MAP = {
|
|
174
|
+
PageView: 'PAGE_VIEW',
|
|
175
|
+
ViewContent: 'VIEW_CONTENT',
|
|
176
|
+
AddToCart: 'ADD_CART',
|
|
177
|
+
AddToWishlist: 'ADD_TO_WISHLIST',
|
|
178
|
+
Search: 'SEARCH',
|
|
179
|
+
InitiateCheckout: 'START_CHECKOUT',
|
|
180
|
+
AddPaymentInfo: 'ADD_BILLING',
|
|
181
|
+
Purchase: 'PURCHASE',
|
|
182
|
+
Lead: 'LEAD',
|
|
183
|
+
CompleteRegistration: 'SIGN_UP',
|
|
184
|
+
};
|
|
185
|
+
export function mapSnapchatEvent(eventName, parameters) {
|
|
186
|
+
const name = SNAPCHAT_EVENT_MAP[eventName] ?? eventName;
|
|
187
|
+
let params = baseTransform(parameters);
|
|
188
|
+
// Snapchat uses `price` not `value`
|
|
189
|
+
if ('value' in params) {
|
|
190
|
+
params = { ...params, price: params.value };
|
|
191
|
+
delete params.value;
|
|
192
|
+
}
|
|
193
|
+
// Snapchat uses `number_items` not `num_items`
|
|
194
|
+
if ('num_items' in params) {
|
|
195
|
+
params = { ...params, number_items: params.num_items };
|
|
196
|
+
delete params.num_items;
|
|
197
|
+
}
|
|
198
|
+
if (params.contents && Array.isArray(params.contents)) {
|
|
199
|
+
params.item_ids = params.contents.map((i) => i.content_id).filter(Boolean);
|
|
200
|
+
}
|
|
201
|
+
return { name, params };
|
|
202
|
+
}
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
// Pinterest
|
|
205
|
+
// Official: pintrk('track', 'checkout', { value, currency, line_items })
|
|
206
|
+
// Event names are all lowercase.
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
const PINTEREST_EVENT_MAP = {
|
|
209
|
+
PageView: 'pagevisit',
|
|
210
|
+
ViewContent: 'viewcontent',
|
|
211
|
+
AddToCart: 'addtocart',
|
|
212
|
+
AddToWishlist: 'addtowishlist',
|
|
213
|
+
Search: 'search',
|
|
214
|
+
InitiateCheckout: 'initiatecheckout',
|
|
215
|
+
AddPaymentInfo: 'addpaymentinfo',
|
|
216
|
+
Purchase: 'checkout',
|
|
217
|
+
Lead: 'lead',
|
|
218
|
+
CompleteRegistration: 'signup',
|
|
219
|
+
};
|
|
220
|
+
export function mapPinterestEvent(eventName, parameters) {
|
|
221
|
+
const name = PINTEREST_EVENT_MAP[eventName] ?? eventName.toLowerCase();
|
|
222
|
+
const params = baseTransform(parameters);
|
|
223
|
+
if (params.order_quantity !== undefined) {
|
|
224
|
+
params.order_quantity = Number(params.order_quantity);
|
|
225
|
+
}
|
|
226
|
+
if (params.contents && Array.isArray(params.contents)) {
|
|
227
|
+
params.line_items = params.contents.map((item) => ({
|
|
228
|
+
product_id: item.content_id,
|
|
229
|
+
product_name: item.content_name,
|
|
230
|
+
product_category: item.content_category,
|
|
231
|
+
product_price: item.price != null ? Number(item.price) : undefined,
|
|
232
|
+
product_quantity: item.quantity != null ? Number(item.quantity) : undefined,
|
|
233
|
+
}));
|
|
234
|
+
}
|
|
235
|
+
return { name, params };
|
|
236
|
+
}
|
|
237
|
+
// ---------------------------------------------------------------------------
|
|
238
|
+
// GTM / GA4 / Google Ads
|
|
239
|
+
// Official: gtag('event', 'purchase', { value, currency, items })
|
|
240
|
+
// Event names are snake_case (GA4 convention).
|
|
241
|
+
// ---------------------------------------------------------------------------
|
|
242
|
+
const GTM_EVENT_MAP = {
|
|
243
|
+
PageView: 'page_view',
|
|
244
|
+
ViewContent: 'view_item',
|
|
245
|
+
AddToCart: 'add_to_cart',
|
|
246
|
+
AddToWishlist: 'add_to_wishlist',
|
|
247
|
+
Search: 'search',
|
|
248
|
+
InitiateCheckout: 'begin_checkout',
|
|
249
|
+
AddPaymentInfo: 'add_payment_info',
|
|
250
|
+
Purchase: 'purchase',
|
|
251
|
+
Lead: 'generate_lead',
|
|
252
|
+
CompleteRegistration: 'sign_up',
|
|
253
|
+
Conversion: 'conversion',
|
|
254
|
+
};
|
|
255
|
+
export function mapGTMEvent(eventName, parameters) {
|
|
256
|
+
const name = GTM_EVENT_MAP[eventName] ?? eventName.toLowerCase();
|
|
257
|
+
const params = baseTransform(parameters);
|
|
258
|
+
if (params.num_items !== undefined) {
|
|
259
|
+
params.num_items = Number(params.num_items);
|
|
260
|
+
}
|
|
261
|
+
if (params.contents && Array.isArray(params.contents)) {
|
|
262
|
+
params.items = params.contents.map((item) => ({
|
|
263
|
+
item_id: item.content_id,
|
|
264
|
+
item_name: item.content_name,
|
|
265
|
+
item_category: item.content_category,
|
|
266
|
+
price: item.price != null ? Number(item.price) : undefined,
|
|
267
|
+
quantity: item.quantity != null ? Number(item.quantity) : undefined,
|
|
268
|
+
}));
|
|
269
|
+
params.contents = params.contents.map((item) => ({
|
|
270
|
+
id: item.content_id,
|
|
271
|
+
name: item.content_name,
|
|
272
|
+
category: item.content_category,
|
|
273
|
+
price: item.price != null ? Number(item.price) : undefined,
|
|
274
|
+
quantity: item.quantity != null ? Number(item.quantity) : undefined,
|
|
275
|
+
}));
|
|
276
|
+
}
|
|
277
|
+
return { name, params };
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Append Google Ads `send_to` parameter when conversion tracking is configured.
|
|
281
|
+
*/
|
|
282
|
+
export function applyGoogleAdsConversion(params, eventName, pixel) {
|
|
283
|
+
if ((eventName === 'Purchase' || eventName === 'Conversion') &&
|
|
284
|
+
pixel.googleAdsConversionId &&
|
|
285
|
+
pixel.googleAdsConversionLabel) {
|
|
286
|
+
return { ...params, send_to: `${pixel.googleAdsConversionId}/${pixel.googleAdsConversionLabel}` };
|
|
287
|
+
}
|
|
288
|
+
return params;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Given a PixelsConfig and an event, return per-provider mapped events
|
|
292
|
+
* for every eligible (enabled + event-toggled-on) pixel.
|
|
293
|
+
*/
|
|
294
|
+
export function resolvePixelEvents(pixels, eventName, parameters) {
|
|
295
|
+
const results = [];
|
|
296
|
+
const fb = getEligiblePixels(pixels.facebook, eventName);
|
|
297
|
+
if (fb.length > 0) {
|
|
298
|
+
const mapped = mapMetaEvent(eventName, parameters);
|
|
299
|
+
fb.forEach((pixel) => results.push({ provider: 'facebook', mapped, pixel }));
|
|
300
|
+
}
|
|
301
|
+
const tt = getEligiblePixels(pixels.tiktok, eventName);
|
|
302
|
+
if (tt.length > 0) {
|
|
303
|
+
const mapped = mapTikTokEvent(eventName, parameters);
|
|
304
|
+
tt.forEach((pixel) => results.push({ provider: 'tiktok', mapped, pixel }));
|
|
305
|
+
}
|
|
306
|
+
const sc = getEligiblePixels(pixels.snapchat, eventName);
|
|
307
|
+
if (sc.length > 0) {
|
|
308
|
+
const mapped = mapSnapchatEvent(eventName, parameters);
|
|
309
|
+
sc.forEach((pixel) => results.push({ provider: 'snapchat', mapped, pixel }));
|
|
310
|
+
}
|
|
311
|
+
const pt = getEligiblePixels(pixels.pinterest, eventName);
|
|
312
|
+
if (pt.length > 0) {
|
|
313
|
+
const mapped = mapPinterestEvent(eventName, parameters);
|
|
314
|
+
pt.forEach((pixel) => results.push({ provider: 'pinterest', mapped, pixel }));
|
|
315
|
+
}
|
|
316
|
+
const gtm = getEligiblePixels(pixels.gtm, eventName);
|
|
317
|
+
if (gtm.length > 0) {
|
|
318
|
+
const mapped = mapGTMEvent(eventName, parameters);
|
|
319
|
+
gtm.forEach((pixel) => {
|
|
320
|
+
const finalParams = applyGoogleAdsConversion(mapped.params, eventName, pixel);
|
|
321
|
+
results.push({ provider: 'gtm', mapped: { name: mapped.name, params: finalParams }, pixel });
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
return results;
|
|
325
|
+
}
|
|
@@ -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
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Checkout Resource Client
|
|
3
3
|
* Axios-based API client for checkout endpoints
|
|
4
4
|
*/
|
|
5
|
+
import type { OrderAddress } from '../types';
|
|
5
6
|
import { ApiClient } from './apiClient';
|
|
6
7
|
export interface CheckoutLineItem {
|
|
7
8
|
externalProductId?: string | null;
|
|
@@ -91,7 +92,7 @@ export interface Store {
|
|
|
91
92
|
chargeCurrencies: string[];
|
|
92
93
|
upsells: Upsell[];
|
|
93
94
|
emailDomains: string[];
|
|
94
|
-
integrations:
|
|
95
|
+
integrations: unknown[];
|
|
95
96
|
}
|
|
96
97
|
export interface CheckoutSession {
|
|
97
98
|
id: string;
|
|
@@ -106,7 +107,56 @@ export interface CheckoutSession {
|
|
|
106
107
|
updatedAt: string;
|
|
107
108
|
lastActiveDate: string;
|
|
108
109
|
selectedPresentmentCurrency: string;
|
|
109
|
-
|
|
110
|
+
shippingRate?: {
|
|
111
|
+
id: string;
|
|
112
|
+
[key: string]: unknown;
|
|
113
|
+
} | null;
|
|
114
|
+
shippingRateId?: string;
|
|
115
|
+
sessionLineItems?: Array<{
|
|
116
|
+
isOrderBump?: boolean;
|
|
117
|
+
[key: string]: unknown;
|
|
118
|
+
}>;
|
|
119
|
+
customer?: {
|
|
120
|
+
email?: string;
|
|
121
|
+
firstName?: string;
|
|
122
|
+
lastName?: string;
|
|
123
|
+
currency?: string;
|
|
124
|
+
[key: string]: unknown;
|
|
125
|
+
};
|
|
126
|
+
shippingAddress?: {
|
|
127
|
+
firstName?: string;
|
|
128
|
+
lastName?: string;
|
|
129
|
+
address1?: string;
|
|
130
|
+
address2?: string;
|
|
131
|
+
city?: string;
|
|
132
|
+
state?: string;
|
|
133
|
+
postal?: string;
|
|
134
|
+
country?: string;
|
|
135
|
+
phone?: string;
|
|
136
|
+
[key: string]: unknown;
|
|
137
|
+
};
|
|
138
|
+
billingAddress?: {
|
|
139
|
+
firstName?: string;
|
|
140
|
+
lastName?: string;
|
|
141
|
+
address1?: string;
|
|
142
|
+
address2?: string;
|
|
143
|
+
city?: string;
|
|
144
|
+
state?: string;
|
|
145
|
+
postal?: string;
|
|
146
|
+
country?: string;
|
|
147
|
+
phone?: string;
|
|
148
|
+
[key: string]: unknown;
|
|
149
|
+
};
|
|
150
|
+
metadata?: {
|
|
151
|
+
cartCustomAttributes?: Array<{
|
|
152
|
+
name?: string;
|
|
153
|
+
value?: string;
|
|
154
|
+
[key: string]: unknown;
|
|
155
|
+
}>;
|
|
156
|
+
clubProductId?: string;
|
|
157
|
+
[key: string]: unknown;
|
|
158
|
+
};
|
|
159
|
+
[key: string]: unknown;
|
|
110
160
|
}
|
|
111
161
|
export interface CheckoutSummaryItem {
|
|
112
162
|
id: string;
|
|
@@ -129,7 +179,7 @@ export interface CheckoutSummaryItem {
|
|
|
129
179
|
amount: number;
|
|
130
180
|
adjustedAmount: number;
|
|
131
181
|
currency: string;
|
|
132
|
-
adjustments:
|
|
182
|
+
adjustments: CheckoutSessionPreviewAdjustment[];
|
|
133
183
|
recurring: boolean;
|
|
134
184
|
interval?: 'day' | 'week' | 'month' | 'year';
|
|
135
185
|
intervalCount?: number;
|
|
@@ -169,6 +219,7 @@ export interface CheckoutSummary {
|
|
|
169
219
|
}[];
|
|
170
220
|
}
|
|
171
221
|
export interface CheckoutSessionPreviewAdjustment {
|
|
222
|
+
id?: string;
|
|
172
223
|
type: 'Promotion' | 'Tax' | 'Shipping';
|
|
173
224
|
description: string;
|
|
174
225
|
level: 'Order' | 'LineItem';
|
|
@@ -205,7 +256,7 @@ export interface CheckoutSessionPreviewItem {
|
|
|
205
256
|
intervalCount?: number;
|
|
206
257
|
totalBillingCycles?: number;
|
|
207
258
|
unitAmountAfterFirstCycle?: number;
|
|
208
|
-
subscriptionSettings?:
|
|
259
|
+
subscriptionSettings?: Record<string, unknown>;
|
|
209
260
|
orderLineItemProduct: {
|
|
210
261
|
name: string | null;
|
|
211
262
|
} | null;
|
|
@@ -221,13 +272,22 @@ export interface CheckoutSessionPreview {
|
|
|
221
272
|
totalAdjustedAmount: number;
|
|
222
273
|
totalPromotionAmount: number;
|
|
223
274
|
}
|
|
275
|
+
export interface PromotionAction {
|
|
276
|
+
id: string;
|
|
277
|
+
adjustmentAmount?: Record<string, {
|
|
278
|
+
amount: number;
|
|
279
|
+
[key: string]: unknown;
|
|
280
|
+
}>;
|
|
281
|
+
adjustmentPercentage?: number;
|
|
282
|
+
[key: string]: unknown;
|
|
283
|
+
}
|
|
224
284
|
export interface Promotion {
|
|
225
285
|
id: string;
|
|
226
286
|
name: string;
|
|
227
287
|
description: string | null;
|
|
228
288
|
type: string;
|
|
229
|
-
rules:
|
|
230
|
-
actions:
|
|
289
|
+
rules: Record<string, unknown>[];
|
|
290
|
+
actions: PromotionAction[];
|
|
231
291
|
}
|
|
232
292
|
export interface CheckoutData {
|
|
233
293
|
checkoutSession: CheckoutSession;
|
|
@@ -236,14 +296,6 @@ export interface CheckoutData {
|
|
|
236
296
|
summary: CheckoutSummary;
|
|
237
297
|
availablePromotions: Promotion[];
|
|
238
298
|
}
|
|
239
|
-
export interface Promotion {
|
|
240
|
-
id: string;
|
|
241
|
-
name: string;
|
|
242
|
-
description: string | null;
|
|
243
|
-
type: string;
|
|
244
|
-
rules: any[];
|
|
245
|
-
actions: any[];
|
|
246
|
-
}
|
|
247
299
|
export declare class CheckoutResource {
|
|
248
300
|
private apiClient;
|
|
249
301
|
constructor(apiClient: ApiClient);
|
|
@@ -300,9 +352,9 @@ export declare class CheckoutResource {
|
|
|
300
352
|
funnelVariantId?: string;
|
|
301
353
|
navigationEvent?: string | {
|
|
302
354
|
type: string;
|
|
303
|
-
data?:
|
|
355
|
+
data?: Record<string, unknown>;
|
|
304
356
|
};
|
|
305
|
-
navigationOptions?:
|
|
357
|
+
navigationOptions?: Record<string, unknown>;
|
|
306
358
|
}, getFunnelSessionId?: () => string | null | undefined): Promise<{
|
|
307
359
|
checkoutToken: string;
|
|
308
360
|
customerId: string;
|
|
@@ -338,8 +390,8 @@ export declare class CheckoutResource {
|
|
|
338
390
|
* Update checkout address
|
|
339
391
|
*/
|
|
340
392
|
updateAddress(checkoutSessionId: string, data: {
|
|
341
|
-
shippingAddress:
|
|
342
|
-
billingAddress?:
|
|
393
|
+
shippingAddress: Partial<OrderAddress>;
|
|
394
|
+
billingAddress?: Partial<OrderAddress>;
|
|
343
395
|
}): Promise<{
|
|
344
396
|
success: boolean;
|
|
345
397
|
errors?: Record<string, {
|
|
@@ -353,8 +405,8 @@ export declare class CheckoutResource {
|
|
|
353
405
|
* Set checkout info
|
|
354
406
|
*/
|
|
355
407
|
setCheckoutInfo(checkoutSessionId: string, data: {
|
|
356
|
-
shippingAddress:
|
|
357
|
-
billingAddress?:
|
|
408
|
+
shippingAddress: Partial<OrderAddress>;
|
|
409
|
+
billingAddress?: Partial<OrderAddress>;
|
|
358
410
|
differentBillingAddress?: boolean;
|
|
359
411
|
}): Promise<{
|
|
360
412
|
success: boolean;
|
|
@@ -370,32 +422,39 @@ export declare class CheckoutResource {
|
|
|
370
422
|
*/
|
|
371
423
|
applyPromotionCode(checkoutSessionId: string, code: string): Promise<{
|
|
372
424
|
success: boolean;
|
|
373
|
-
error?:
|
|
425
|
+
error?: string;
|
|
374
426
|
}>;
|
|
375
427
|
/**
|
|
376
428
|
* Remove promotion
|
|
377
429
|
*/
|
|
378
430
|
removePromotion(checkoutSessionId: string, promotionId: string): Promise<{
|
|
379
431
|
success: boolean;
|
|
380
|
-
error?:
|
|
432
|
+
error?: string;
|
|
381
433
|
}>;
|
|
382
434
|
/**
|
|
383
435
|
* Get applied promotions
|
|
384
436
|
*/
|
|
385
437
|
getAppliedPromotions(checkoutSessionId: string): Promise<Promotion[]>;
|
|
438
|
+
/**
|
|
439
|
+
* Replace all session line items (full rewrite)
|
|
440
|
+
*/
|
|
441
|
+
replaceSessionLineItems(checkoutSessionId: string, lineItems: CheckoutLineItem[]): Promise<{
|
|
442
|
+
success: boolean;
|
|
443
|
+
error?: string;
|
|
444
|
+
}>;
|
|
386
445
|
/**
|
|
387
446
|
* Update line items
|
|
388
447
|
*/
|
|
389
448
|
updateLineItems(checkoutSessionId: string, lineItems: CheckoutLineItem[]): Promise<{
|
|
390
449
|
success: boolean;
|
|
391
|
-
error?:
|
|
450
|
+
error?: string;
|
|
392
451
|
}>;
|
|
393
452
|
/**
|
|
394
453
|
* Add line items
|
|
395
454
|
*/
|
|
396
455
|
addLineItems(checkoutSessionId: string, lineItems: CheckoutLineItem[]): Promise<{
|
|
397
456
|
success: boolean;
|
|
398
|
-
error?:
|
|
457
|
+
error?: string;
|
|
399
458
|
}>;
|
|
400
459
|
/**
|
|
401
460
|
* Remove line items
|
|
@@ -405,21 +464,21 @@ export declare class CheckoutResource {
|
|
|
405
464
|
quantity?: number;
|
|
406
465
|
}[]): Promise<{
|
|
407
466
|
success: boolean;
|
|
408
|
-
error?:
|
|
467
|
+
error?: string;
|
|
409
468
|
}>;
|
|
410
469
|
/**
|
|
411
470
|
* Set item quantity
|
|
412
471
|
*/
|
|
413
472
|
setItemQuantity(checkoutSessionId: string, variantId: string, quantity: number, priceId?: string): Promise<{
|
|
414
473
|
success: boolean;
|
|
415
|
-
error?:
|
|
474
|
+
error?: string;
|
|
416
475
|
}>;
|
|
417
476
|
/**
|
|
418
477
|
* Toggle order bump
|
|
419
478
|
*/
|
|
420
479
|
toggleOrderBump(checkoutSessionId: string, orderBumpOfferId: string, selected: boolean): Promise<{
|
|
421
480
|
success: boolean;
|
|
422
|
-
error?:
|
|
481
|
+
error?: string;
|
|
423
482
|
}>;
|
|
424
483
|
/**
|
|
425
484
|
* Update customer
|
|
@@ -429,7 +488,7 @@ export declare class CheckoutResource {
|
|
|
429
488
|
acceptsMarketing?: boolean;
|
|
430
489
|
}): Promise<{
|
|
431
490
|
success: boolean;
|
|
432
|
-
error?:
|
|
491
|
+
error?: string;
|
|
433
492
|
}>;
|
|
434
493
|
/**
|
|
435
494
|
* Update customer and session info
|
|
@@ -439,8 +498,8 @@ export declare class CheckoutResource {
|
|
|
439
498
|
email: string;
|
|
440
499
|
acceptsMarketing?: boolean;
|
|
441
500
|
};
|
|
442
|
-
shippingAddress:
|
|
443
|
-
billingAddress?:
|
|
501
|
+
shippingAddress: Partial<OrderAddress>;
|
|
502
|
+
billingAddress?: Partial<OrderAddress>;
|
|
444
503
|
differentBillingAddress?: boolean;
|
|
445
504
|
}): Promise<{
|
|
446
505
|
success: boolean;
|
|
@@ -147,6 +147,14 @@ export class CheckoutResource {
|
|
|
147
147
|
async getAppliedPromotions(checkoutSessionId) {
|
|
148
148
|
return this.apiClient.get(`/api/v1/checkout-sessions/${checkoutSessionId}/promotions`);
|
|
149
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* Replace all session line items (full rewrite)
|
|
152
|
+
*/
|
|
153
|
+
async replaceSessionLineItems(checkoutSessionId, lineItems) {
|
|
154
|
+
return this.apiClient.post(`/api/v1/checkout-sessions/${checkoutSessionId}/line-items`, {
|
|
155
|
+
lineItems,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
150
158
|
/**
|
|
151
159
|
* Update line items
|
|
152
160
|
*/
|