@tagadapay/plugin-sdk 3.1.12 → 3.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/build-cdn.js +129 -11
  2. package/dist/data/iso3166.d.ts +23 -33
  3. package/dist/data/iso3166.js +134 -198
  4. package/dist/data/languages.d.ts +5 -64
  5. package/dist/data/languages.js +23 -143
  6. package/dist/external-tracker.js +968 -101
  7. package/dist/external-tracker.min.js +2 -2
  8. package/dist/external-tracker.min.js.map +4 -4
  9. package/dist/react/hooks/useISOData.js +1 -1
  10. package/dist/react/hooks/usePaymentPolling.d.ts +3 -3
  11. package/dist/tagada-sdk.js +12066 -0
  12. package/dist/tagada-sdk.min.js +50 -0
  13. package/dist/tagada-sdk.min.js.map +7 -0
  14. package/dist/v2/core/client.d.ts +4 -2
  15. package/dist/v2/core/client.js +4 -3
  16. package/dist/v2/core/errors.d.ts +75 -0
  17. package/dist/v2/core/errors.js +104 -0
  18. package/dist/v2/core/funnelClient.d.ts +2 -0
  19. package/dist/v2/core/index.d.ts +1 -0
  20. package/dist/v2/core/index.js +2 -0
  21. package/dist/v2/core/pixelMapping.d.ts +49 -0
  22. package/dist/v2/core/pixelMapping.js +325 -0
  23. package/dist/v2/core/resources/apiClient.d.ts +2 -0
  24. package/dist/v2/core/resources/apiClient.js +52 -9
  25. package/dist/v2/core/resources/checkout.d.ts +89 -30
  26. package/dist/v2/core/resources/checkout.js +8 -0
  27. package/dist/v2/core/resources/customer.d.ts +20 -19
  28. package/dist/v2/core/resources/funnel.d.ts +17 -17
  29. package/dist/v2/core/resources/payments.d.ts +84 -13
  30. package/dist/v2/core/resources/payments.js +26 -9
  31. package/dist/v2/core/types.d.ts +50 -12
  32. package/dist/v2/core/types.js +0 -3
  33. package/dist/v2/core/utils/checkout.d.ts +2 -2
  34. package/dist/v2/core/utils/checkout.js +7 -2
  35. package/dist/v2/core/utils/currency.d.ts +14 -0
  36. package/dist/v2/core/utils/currency.js +40 -0
  37. package/dist/v2/core/utils/index.d.ts +1 -0
  38. package/dist/v2/core/utils/index.js +2 -0
  39. package/dist/v2/core/utils/order.d.ts +11 -9
  40. package/dist/v2/core/utils/pluginConfig.d.ts +8 -0
  41. package/dist/v2/core/utils/pluginConfig.js +28 -0
  42. package/dist/v2/index.d.ts +3 -1
  43. package/dist/v2/index.js +1 -1
  44. package/dist/v2/react/components/FunnelScriptInjector.js +21 -0
  45. package/dist/v2/react/components/WhopCheckout.d.ts +24 -0
  46. package/dist/v2/react/components/WhopCheckout.js +231 -0
  47. package/dist/v2/react/hooks/__examples__/FunnelContextExample.js +1 -1
  48. package/dist/v2/react/hooks/payment-actions/useAirwallexRadarAction.d.ts +14 -0
  49. package/dist/v2/react/hooks/payment-actions/useAirwallexRadarAction.js +181 -0
  50. package/dist/v2/react/hooks/payment-actions/useErrorAction.d.ts +9 -0
  51. package/dist/v2/react/hooks/payment-actions/useErrorAction.js +21 -0
  52. package/dist/v2/react/hooks/payment-actions/useFinixRadarAction.d.ts +14 -0
  53. package/dist/v2/react/hooks/payment-actions/useFinixRadarAction.js +187 -0
  54. package/dist/v2/react/hooks/payment-actions/useKessPayAction.d.ts +11 -0
  55. package/dist/v2/react/hooks/payment-actions/useKessPayAction.js +91 -0
  56. package/dist/v2/react/hooks/payment-actions/useMasterCardAction.d.ts +24 -0
  57. package/dist/v2/react/hooks/payment-actions/useMasterCardAction.js +221 -0
  58. package/dist/v2/react/hooks/payment-actions/usePaymentActionHandler.d.ts +15 -0
  59. package/dist/v2/react/hooks/payment-actions/usePaymentActionHandler.js +142 -0
  60. package/dist/v2/react/hooks/payment-actions/useProcessorAuthAction.d.ts +3 -0
  61. package/dist/v2/react/hooks/payment-actions/useProcessorAuthAction.js +13 -0
  62. package/dist/v2/react/hooks/payment-actions/useRedirectAction.d.ts +10 -0
  63. package/dist/v2/react/hooks/payment-actions/useRedirectAction.js +35 -0
  64. package/dist/v2/react/hooks/payment-actions/useStripeRadarAction.d.ts +14 -0
  65. package/dist/v2/react/hooks/payment-actions/useStripeRadarAction.js +192 -0
  66. package/dist/v2/react/hooks/payment-actions/useThreedsAuthAction.d.ts +14 -0
  67. package/dist/v2/react/hooks/payment-actions/useThreedsAuthAction.js +81 -0
  68. package/dist/v2/react/hooks/payment-actions/useTrustFlowAction.d.ts +11 -0
  69. package/dist/v2/react/hooks/payment-actions/useTrustFlowAction.js +84 -0
  70. package/dist/v2/react/hooks/payment-processing/usePaymentInstruments.d.ts +14 -0
  71. package/dist/v2/react/hooks/payment-processing/usePaymentInstruments.js +36 -0
  72. package/dist/v2/react/hooks/payment-processing/usePaymentProcessors.d.ts +31 -0
  73. package/dist/v2/react/hooks/payment-processing/usePaymentProcessors.js +212 -0
  74. package/dist/v2/react/hooks/payment-redirect/useAirwallex3dsReturn.d.ts +14 -0
  75. package/dist/v2/react/hooks/payment-redirect/useAirwallex3dsReturn.js +207 -0
  76. package/dist/v2/react/hooks/payment-redirect/useGenericPaymentReturn.d.ts +12 -0
  77. package/dist/v2/react/hooks/payment-redirect/useGenericPaymentReturn.js +101 -0
  78. package/dist/v2/react/hooks/useCheckoutQuery.d.ts +6 -0
  79. package/dist/v2/react/hooks/useCheckoutQuery.js +45 -0
  80. package/dist/v2/react/hooks/useGeoLocation.d.ts +2 -1
  81. package/dist/v2/react/hooks/useGeoLocation.js +4 -2
  82. package/dist/v2/react/hooks/useISOData.js +1 -1
  83. package/dist/v2/react/hooks/usePaymentPolling.d.ts +3 -3
  84. package/dist/v2/react/hooks/usePaymentQuery.d.ts +18 -5
  85. package/dist/v2/react/hooks/usePaymentQuery.js +63 -1015
  86. package/dist/v2/react/hooks/usePaymentRetrieve.d.ts +3 -2
  87. package/dist/v2/react/hooks/usePaymentRetrieve.js +3 -1
  88. package/dist/v2/react/hooks/usePixelTracking.d.ts +5 -48
  89. package/dist/v2/react/hooks/usePixelTracking.js +212 -514
  90. package/dist/v2/react/hooks/useShippingRatesQuery.js +13 -5
  91. package/dist/v2/react/hooks/useWhopPaymentPolling.d.ts +30 -0
  92. package/dist/v2/react/hooks/useWhopPaymentPolling.js +61 -0
  93. package/dist/v2/react/index.d.ts +7 -0
  94. package/dist/v2/react/index.js +4 -0
  95. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.d.ts +2 -1
  96. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +3 -1
  97. package/dist/v2/react/providers/TagadaProvider.js +71 -2
  98. package/dist/v2/standalone/external-tracker.d.ts +52 -46
  99. package/dist/v2/standalone/external-tracker.js +205 -98
  100. package/dist/v2/standalone/index.d.ts +22 -0
  101. package/dist/v2/standalone/index.js +126 -1
  102. package/package.json +3 -3
  103. package/dist/react/utils/__tests__/urlUtils.test.d.ts +0 -1
  104. package/dist/react/utils/__tests__/urlUtils.test.js +0 -189
  105. package/dist/v2/core/__tests__/pathRemapping.test.d.ts +0 -11
  106. package/dist/v2/core/__tests__/pathRemapping.test.js +0 -776
@@ -1,11 +1,11 @@
1
1
  import { FunnelClient } from './funnelClient';
2
2
  import { ApiClient } from './resources/apiClient';
3
- import { AuthState, Currency, Customer, Environment, EnvironmentConfig, Locale, Session, Store } from './types';
3
+ import { AuthState, Currency, Customer, DeepPartial, Environment, EnvironmentConfig, Locale, Session, Store } from './types';
4
4
  import { EventBus } from './utils/eventBus';
5
5
  import { PluginConfig, RawPluginConfig } from './utils/pluginConfig';
6
6
  export interface TagadaClientConfig {
7
7
  environment?: Environment;
8
- customApiConfig?: Partial<EnvironmentConfig>;
8
+ customApiConfig?: DeepPartial<EnvironmentConfig>;
9
9
  debugMode?: boolean;
10
10
  localConfig?: string;
11
11
  rawPluginConfig?: RawPluginConfig;
@@ -22,6 +22,8 @@ export interface TagadaClientConfig {
22
22
  */
23
23
  funnel?: boolean | {
24
24
  autoRedirect?: boolean;
25
+ /** Skip automatic funnel initialization (use when caller handles init explicitly) */
26
+ skipAutoInit?: boolean;
25
27
  };
26
28
  };
27
29
  }
@@ -620,9 +620,10 @@ export class TagadaClient {
620
620
  customer: response.customer ?? null,
621
621
  auth: authState
622
622
  });
623
- // Auto-initialize funnel if enabled
624
- // This runs after we have all required data: auth.session, store, accountId
625
- if (this.funnel && sessionData.customerId && response.store?.id) {
623
+ // Auto-initialize funnel if enabled (skip when caller handles init explicitly)
624
+ const funnelFeature = this.config.features?.funnel;
625
+ const skipAutoInit = typeof funnelFeature === 'object' && funnelFeature.skipAutoInit;
626
+ if (this.funnel && !skipAutoInit && sessionData.customerId && response.store?.id) {
626
627
  const accountId = response.store.accountId || this.state.pluginConfig?.accountId || sessionData.accountId || '';
627
628
  if (accountId) {
628
629
  // Get funnelId from URL or config
@@ -0,0 +1,75 @@
1
+ /**
2
+ * TagadaPay SDK Error Types
3
+ *
4
+ * Structured error hierarchy for programmatic error handling.
5
+ * All SDK errors extend TagadaError, which extends native Error.
6
+ */
7
+ export declare const TagadaErrorCode: {
8
+ readonly NETWORK_ERROR: "network_error";
9
+ readonly API_ERROR: "api_error";
10
+ readonly AUTH_REQUIRED: "auth_required";
11
+ readonly TOKEN_EXPIRED: "token_expired";
12
+ readonly NOT_FOUND: "not_found";
13
+ readonly VALIDATION_ERROR: "validation_error";
14
+ readonly CIRCUIT_BREAKER: "circuit_breaker";
15
+ readonly PAYMENT_FAILED: "payment_failed";
16
+ readonly CARD_DECLINED: "card_declined";
17
+ readonly SESSION_EXPIRED: "session_expired";
18
+ readonly RATE_LIMITED: "rate_limited";
19
+ readonly TIMEOUT: "timeout";
20
+ readonly UNKNOWN: "unknown";
21
+ };
22
+ export type TagadaErrorCodeValue = typeof TagadaErrorCode[keyof typeof TagadaErrorCode];
23
+ export interface TagadaErrorOptions {
24
+ code: TagadaErrorCodeValue | string;
25
+ statusCode?: number;
26
+ retryable?: boolean;
27
+ details?: Record<string, unknown>;
28
+ }
29
+ /**
30
+ * Base error class for all TagadaPay SDK errors.
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * try {
35
+ * await checkout.applyPromotionCode(sessionId, 'SAVE10');
36
+ * } catch (err) {
37
+ * if (err instanceof TagadaError) {
38
+ * console.log(err.code); // 'not_found'
39
+ * console.log(err.statusCode); // 404
40
+ * console.log(err.retryable); // false
41
+ * }
42
+ * }
43
+ * ```
44
+ */
45
+ export declare class TagadaError extends Error {
46
+ readonly code: string;
47
+ readonly statusCode?: number;
48
+ readonly retryable: boolean;
49
+ readonly details?: Record<string, unknown>;
50
+ constructor(message: string, options: TagadaErrorOptions);
51
+ }
52
+ /** API returned a non-2xx response with an error body. */
53
+ export declare class TagadaApiError extends TagadaError {
54
+ constructor(message: string, statusCode: number, options?: {
55
+ code?: string;
56
+ details?: Record<string, unknown>;
57
+ retryable?: boolean;
58
+ });
59
+ }
60
+ /** Network-level failure (no response received). */
61
+ export declare class TagadaNetworkError extends TagadaError {
62
+ constructor(message?: string);
63
+ }
64
+ /** 401/403 — authentication or authorization failure. */
65
+ export declare class TagadaAuthError extends TagadaError {
66
+ constructor(message?: string, statusCode?: number);
67
+ }
68
+ /** Input validation failed (client-side or 400 from server). */
69
+ export declare class TagadaValidationError extends TagadaError {
70
+ constructor(message: string, details?: Record<string, unknown>);
71
+ }
72
+ /** Circuit breaker tripped — too many requests in a short window. */
73
+ export declare class TagadaCircuitBreakerError extends TagadaError {
74
+ constructor(message: string);
75
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * TagadaPay SDK Error Types
3
+ *
4
+ * Structured error hierarchy for programmatic error handling.
5
+ * All SDK errors extend TagadaError, which extends native Error.
6
+ */
7
+ export const TagadaErrorCode = {
8
+ NETWORK_ERROR: 'network_error',
9
+ API_ERROR: 'api_error',
10
+ AUTH_REQUIRED: 'auth_required',
11
+ TOKEN_EXPIRED: 'token_expired',
12
+ NOT_FOUND: 'not_found',
13
+ VALIDATION_ERROR: 'validation_error',
14
+ CIRCUIT_BREAKER: 'circuit_breaker',
15
+ PAYMENT_FAILED: 'payment_failed',
16
+ CARD_DECLINED: 'card_declined',
17
+ SESSION_EXPIRED: 'session_expired',
18
+ RATE_LIMITED: 'rate_limited',
19
+ TIMEOUT: 'timeout',
20
+ UNKNOWN: 'unknown',
21
+ };
22
+ /**
23
+ * Base error class for all TagadaPay SDK errors.
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * try {
28
+ * await checkout.applyPromotionCode(sessionId, 'SAVE10');
29
+ * } catch (err) {
30
+ * if (err instanceof TagadaError) {
31
+ * console.log(err.code); // 'not_found'
32
+ * console.log(err.statusCode); // 404
33
+ * console.log(err.retryable); // false
34
+ * }
35
+ * }
36
+ * ```
37
+ */
38
+ export class TagadaError extends Error {
39
+ constructor(message, options) {
40
+ super(message);
41
+ this.name = 'TagadaError';
42
+ this.code = options.code;
43
+ this.statusCode = options.statusCode;
44
+ this.retryable = options.retryable ?? false;
45
+ this.details = options.details;
46
+ // Maintain proper prototype chain for instanceof checks
47
+ Object.setPrototypeOf(this, new.target.prototype);
48
+ }
49
+ }
50
+ /** API returned a non-2xx response with an error body. */
51
+ export class TagadaApiError extends TagadaError {
52
+ constructor(message, statusCode, options) {
53
+ super(message, {
54
+ code: options?.code ?? TagadaErrorCode.API_ERROR,
55
+ statusCode,
56
+ retryable: options?.retryable ?? (statusCode >= 500),
57
+ details: options?.details,
58
+ });
59
+ this.name = 'TagadaApiError';
60
+ }
61
+ }
62
+ /** Network-level failure (no response received). */
63
+ export class TagadaNetworkError extends TagadaError {
64
+ constructor(message = 'Network request failed') {
65
+ super(message, {
66
+ code: TagadaErrorCode.NETWORK_ERROR,
67
+ retryable: true,
68
+ });
69
+ this.name = 'TagadaNetworkError';
70
+ }
71
+ }
72
+ /** 401/403 — authentication or authorization failure. */
73
+ export class TagadaAuthError extends TagadaError {
74
+ constructor(message = 'Authentication required', statusCode = 401) {
75
+ super(message, {
76
+ code: TagadaErrorCode.AUTH_REQUIRED,
77
+ statusCode,
78
+ retryable: false,
79
+ });
80
+ this.name = 'TagadaAuthError';
81
+ }
82
+ }
83
+ /** Input validation failed (client-side or 400 from server). */
84
+ export class TagadaValidationError extends TagadaError {
85
+ constructor(message, details) {
86
+ super(message, {
87
+ code: TagadaErrorCode.VALIDATION_ERROR,
88
+ statusCode: 400,
89
+ retryable: false,
90
+ details,
91
+ });
92
+ this.name = 'TagadaValidationError';
93
+ }
94
+ }
95
+ /** Circuit breaker tripped — too many requests in a short window. */
96
+ export class TagadaCircuitBreakerError extends TagadaError {
97
+ constructor(message) {
98
+ super(message, {
99
+ code: TagadaErrorCode.CIRCUIT_BREAKER,
100
+ retryable: false,
101
+ });
102
+ this.name = 'TagadaCircuitBreakerError';
103
+ }
104
+ }
@@ -65,6 +65,8 @@ export interface GTMTrackingConfig extends BaseTrackingConfig {
65
65
  AddToCart: boolean;
66
66
  ViewContent: boolean;
67
67
  };
68
+ googleAdsConversionId?: string;
69
+ googleAdsConversionLabel?: string;
68
70
  }
69
71
  /**
70
72
  * Union of all tracking configs.
@@ -7,5 +7,6 @@ export * from './resources';
7
7
  export * from './client';
8
8
  export * from './funnelClient';
9
9
  export * from './pathRemapping';
10
+ export * from './pixelMapping';
10
11
  export * from './googleAutocomplete';
11
12
  export * from './isoData';
@@ -11,6 +11,8 @@ export * from './client';
11
11
  export * from './funnelClient';
12
12
  // Export path remapping helpers (framework-agnostic)
13
13
  export * from './pathRemapping';
14
+ // Export pixel mapping (framework-agnostic, shared by React + standalone)
15
+ export * from './pixelMapping';
14
16
  // Export legacy files that are still needed
15
17
  export * from './googleAutocomplete';
16
18
  export * from './isoData';
@@ -0,0 +1,49 @@
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
+ import type { GTMTrackingConfig, PixelConfig, PixelsConfig } from './funnelClient';
15
+ export type StandardPixelEvent = 'PageView' | 'ViewContent' | 'AddToCart' | 'AddToWishlist' | 'Search' | 'InitiateCheckout' | 'AddPaymentInfo' | 'Purchase' | 'Lead' | 'CompleteRegistration' | 'Conversion';
16
+ export type PixelProviderKey = 'facebook' | 'tiktok' | 'snapchat' | 'pinterest' | 'gtm';
17
+ export interface MappedEvent {
18
+ name: string;
19
+ params: Record<string, unknown>;
20
+ }
21
+ /**
22
+ * Returns `true` if the given event is enabled on the pixel config.
23
+ * If the pixel has no `events` map (e.g. MetaConversionTrackingConfig) we allow all.
24
+ */
25
+ export declare function isEventEnabled(pixel: PixelConfig, eventName: StandardPixelEvent): boolean;
26
+ /**
27
+ * Filter an array of pixel configs to only those that are globally enabled
28
+ * AND have the specific event toggled on.
29
+ */
30
+ export declare function getEligiblePixels<T extends PixelConfig>(pixels: T[] | undefined, eventName: StandardPixelEvent): T[];
31
+ export declare function mapMetaEvent(eventName: StandardPixelEvent, parameters: Record<string, unknown>): MappedEvent;
32
+ export declare function mapTikTokEvent(eventName: StandardPixelEvent, parameters: Record<string, unknown>): MappedEvent;
33
+ export declare function mapSnapchatEvent(eventName: StandardPixelEvent, parameters: Record<string, unknown>): MappedEvent;
34
+ export declare function mapPinterestEvent(eventName: StandardPixelEvent, parameters: Record<string, unknown>): MappedEvent;
35
+ export declare function mapGTMEvent(eventName: StandardPixelEvent, parameters: Record<string, unknown>): MappedEvent;
36
+ /**
37
+ * Append Google Ads `send_to` parameter when conversion tracking is configured.
38
+ */
39
+ export declare function applyGoogleAdsConversion(params: Record<string, unknown>, eventName: StandardPixelEvent, pixel: GTMTrackingConfig): Record<string, unknown>;
40
+ export interface ProviderEvent {
41
+ provider: PixelProviderKey;
42
+ mapped: MappedEvent;
43
+ pixel: PixelConfig;
44
+ }
45
+ /**
46
+ * Given a PixelsConfig and an event, return per-provider mapped events
47
+ * for every eligible (enabled + event-toggled-on) pixel.
48
+ */
49
+ export declare function resolvePixelEvents(pixels: PixelsConfig, eventName: StandardPixelEvent, parameters: Record<string, unknown>): ProviderEvent[];
@@ -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
+ }
@@ -35,4 +35,6 @@ export declare class ApiClient {
35
35
  updateConfig(config: Partial<ApiClientConfig>): void;
36
36
  private checkRequestLimit;
37
37
  private cleanupHistory;
38
+ /** Convert an AxiosError into the appropriate TagadaError subclass. */
39
+ private toTagadaError;
38
40
  }