@tagadapay/plugin-sdk 3.1.12 → 3.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/build-cdn.js +397 -11
  2. package/dist/data/iso3166.d.ts +23 -33
  3. package/dist/data/iso3166.js +134 -198
  4. package/dist/data/languages.d.ts +5 -64
  5. package/dist/data/languages.js +23 -143
  6. package/dist/external-tracker.js +623 -3426
  7. package/dist/external-tracker.min.js +2 -25
  8. package/dist/external-tracker.min.js.map +4 -4
  9. package/dist/react/config/payment.d.ts +14 -4
  10. package/dist/react/config/payment.js +47 -9
  11. package/dist/react/hooks/useCheckout.d.ts +3 -0
  12. package/dist/react/hooks/useCheckout.js +4 -1
  13. package/dist/react/hooks/useISOData.js +1 -1
  14. package/dist/react/hooks/usePaymentPolling.d.ts +3 -3
  15. package/dist/react/hooks/usePluginConfig.js +9 -10
  16. package/dist/react/providers/TagadaProvider.js +1 -1
  17. package/dist/tagada-react-sdk-minimal.min.js +36 -0
  18. package/dist/tagada-react-sdk-minimal.min.js.map +7 -0
  19. package/dist/tagada-react-sdk.js +37821 -0
  20. package/dist/tagada-react-sdk.min.js +78 -0
  21. package/dist/tagada-react-sdk.min.js.map +7 -0
  22. package/dist/tagada-sdk.js +16044 -0
  23. package/dist/tagada-sdk.min.js +32 -0
  24. package/dist/tagada-sdk.min.js.map +7 -0
  25. package/dist/v2/cdn-react-minimal.d.ts +23 -0
  26. package/dist/v2/cdn-react-minimal.js +26 -0
  27. package/dist/v2/core/client.d.ts +4 -2
  28. package/dist/v2/core/client.js +5 -4
  29. package/dist/v2/core/config/environment.js +2 -1
  30. package/dist/v2/core/errors.d.ts +75 -0
  31. package/dist/v2/core/errors.js +104 -0
  32. package/dist/v2/core/funnelClient.d.ts +100 -10
  33. package/dist/v2/core/funnelClient.js +121 -27
  34. package/dist/v2/core/isoData.d.ts +4 -4
  35. package/dist/v2/core/isoData.js +7 -7
  36. package/dist/v2/core/pixelMapping.d.ts +49 -0
  37. package/dist/v2/core/pixelMapping.js +363 -0
  38. package/dist/v2/core/resources/apiClient.d.ts +2 -0
  39. package/dist/v2/core/resources/apiClient.js +52 -9
  40. package/dist/v2/core/resources/checkout.d.ts +99 -30
  41. package/dist/v2/core/resources/checkout.js +14 -0
  42. package/dist/v2/core/resources/customer.d.ts +20 -19
  43. package/dist/v2/core/resources/expressPaymentMethods.d.ts +1 -0
  44. package/dist/v2/core/resources/funnel.d.ts +17 -17
  45. package/dist/v2/core/resources/payments.d.ts +89 -13
  46. package/dist/v2/core/resources/payments.js +27 -9
  47. package/dist/v2/core/resources/postPurchases.d.ts +17 -0
  48. package/dist/v2/core/resources/postPurchases.js +20 -0
  49. package/dist/v2/core/types.d.ts +50 -12
  50. package/dist/v2/core/types.js +0 -3
  51. package/dist/v2/core/utils/checkout.d.ts +2 -2
  52. package/dist/v2/core/utils/checkout.js +7 -2
  53. package/dist/v2/core/utils/currency.d.ts +14 -0
  54. package/dist/v2/core/utils/currency.js +40 -0
  55. package/dist/v2/core/utils/deviceInfo.d.ts +0 -10
  56. package/dist/v2/core/utils/deviceInfo.js +152 -76
  57. package/dist/v2/core/utils/index.d.ts +1 -0
  58. package/dist/v2/core/utils/index.js +2 -0
  59. package/dist/v2/core/utils/order.d.ts +13 -9
  60. package/dist/v2/core/utils/pluginConfig.d.ts +8 -0
  61. package/dist/v2/core/utils/pluginConfig.js +36 -12
  62. package/dist/v2/index.d.ts +6 -3
  63. package/dist/v2/index.js +4 -2
  64. package/dist/v2/react/components/FunnelScriptInjector.js +166 -77
  65. package/dist/v2/react/components/StripeExpressButton.d.ts +13 -0
  66. package/dist/v2/react/components/StripeExpressButton.js +171 -0
  67. package/dist/v2/react/components/WhopCheckout.d.ts +24 -0
  68. package/dist/v2/react/components/WhopCheckout.js +237 -0
  69. package/dist/v2/react/hooks/__examples__/FunnelContextExample.js +1 -1
  70. package/dist/v2/react/hooks/payment-actions/useAirwallexRadarAction.d.ts +14 -0
  71. package/dist/v2/react/hooks/payment-actions/useAirwallexRadarAction.js +181 -0
  72. package/dist/v2/react/hooks/payment-actions/useErrorAction.d.ts +9 -0
  73. package/dist/v2/react/hooks/payment-actions/useErrorAction.js +21 -0
  74. package/dist/v2/react/hooks/payment-actions/useFinixRadarAction.d.ts +14 -0
  75. package/dist/v2/react/hooks/payment-actions/useFinixRadarAction.js +187 -0
  76. package/dist/v2/react/hooks/payment-actions/useKessPayAction.d.ts +11 -0
  77. package/dist/v2/react/hooks/payment-actions/useKessPayAction.js +91 -0
  78. package/dist/v2/react/hooks/payment-actions/useMasterCardAction.d.ts +24 -0
  79. package/dist/v2/react/hooks/payment-actions/useMasterCardAction.js +221 -0
  80. package/dist/v2/react/hooks/payment-actions/usePaymentActionHandler.d.ts +15 -0
  81. package/dist/v2/react/hooks/payment-actions/usePaymentActionHandler.js +142 -0
  82. package/dist/v2/react/hooks/payment-actions/useProcessorAuthAction.d.ts +3 -0
  83. package/dist/v2/react/hooks/payment-actions/useProcessorAuthAction.js +31 -0
  84. package/dist/v2/react/hooks/payment-actions/useRedirectAction.d.ts +10 -0
  85. package/dist/v2/react/hooks/payment-actions/useRedirectAction.js +35 -0
  86. package/dist/v2/react/hooks/payment-actions/useStripeRadarAction.d.ts +14 -0
  87. package/dist/v2/react/hooks/payment-actions/useStripeRadarAction.js +192 -0
  88. package/dist/v2/react/hooks/payment-actions/useThreedsAuthAction.d.ts +14 -0
  89. package/dist/v2/react/hooks/payment-actions/useThreedsAuthAction.js +81 -0
  90. package/dist/v2/react/hooks/payment-actions/useTrustFlowAction.d.ts +11 -0
  91. package/dist/v2/react/hooks/payment-actions/useTrustFlowAction.js +84 -0
  92. package/dist/v2/react/hooks/payment-processing/usePaymentInstruments.d.ts +14 -0
  93. package/dist/v2/react/hooks/payment-processing/usePaymentInstruments.js +36 -0
  94. package/dist/v2/react/hooks/payment-processing/usePaymentProcessors.d.ts +31 -0
  95. package/dist/v2/react/hooks/payment-processing/usePaymentProcessors.js +212 -0
  96. package/dist/v2/react/hooks/payment-redirect/useAirwallex3dsReturn.d.ts +14 -0
  97. package/dist/v2/react/hooks/payment-redirect/useAirwallex3dsReturn.js +207 -0
  98. package/dist/v2/react/hooks/payment-redirect/useGenericPaymentReturn.d.ts +12 -0
  99. package/dist/v2/react/hooks/payment-redirect/useGenericPaymentReturn.js +101 -0
  100. package/dist/v2/react/hooks/useApplePayCheckout.js +8 -8
  101. package/dist/v2/react/hooks/useCheckoutQuery.d.ts +16 -0
  102. package/dist/v2/react/hooks/useCheckoutQuery.js +63 -10
  103. package/dist/v2/react/hooks/useFunnel.d.ts +15 -4
  104. package/dist/v2/react/hooks/useFunnel.js +8 -4
  105. package/dist/v2/react/hooks/useGeoLocation.d.ts +2 -1
  106. package/dist/v2/react/hooks/useGeoLocation.js +4 -2
  107. package/dist/v2/react/hooks/useGoogleAutocomplete.d.ts +2 -0
  108. package/dist/v2/react/hooks/useGoogleAutocomplete.js +29 -15
  109. package/dist/v2/react/hooks/useISOData.d.ts +2 -5
  110. package/dist/v2/react/hooks/useISOData.js +26 -27
  111. package/dist/v2/react/hooks/usePaymentPolling.d.ts +3 -3
  112. package/dist/v2/react/hooks/usePaymentQuery.d.ts +18 -5
  113. package/dist/v2/react/hooks/usePaymentQuery.js +63 -1015
  114. package/dist/v2/react/hooks/usePaymentRetrieve.d.ts +3 -2
  115. package/dist/v2/react/hooks/usePaymentRetrieve.js +3 -1
  116. package/dist/v2/react/hooks/usePixelTracking.d.ts +5 -48
  117. package/dist/v2/react/hooks/usePixelTracking.js +283 -504
  118. package/dist/v2/react/hooks/usePostPurchasesQuery.js +34 -2
  119. package/dist/v2/react/hooks/useRemappableParams.d.ts +2 -6
  120. package/dist/v2/react/hooks/useRemappableParams.js +23 -23
  121. package/dist/v2/react/hooks/useSetPaymentMethod.d.ts +16 -0
  122. package/dist/v2/react/hooks/useSetPaymentMethod.js +33 -0
  123. package/dist/v2/react/hooks/useShippingRatesQuery.js +13 -5
  124. package/dist/v2/react/hooks/useStepConfig.d.ts +23 -6
  125. package/dist/v2/react/hooks/useStepConfig.js +14 -7
  126. package/dist/v2/react/hooks/useTranslation.js +23 -8
  127. package/dist/v2/react/hooks/useWhopPaymentPolling.d.ts +30 -0
  128. package/dist/v2/react/hooks/useWhopPaymentPolling.js +61 -0
  129. package/dist/v2/react/index.d.ts +15 -1
  130. package/dist/v2/react/index.js +7 -0
  131. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.d.ts +3 -1
  132. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +12 -2
  133. package/dist/v2/react/providers/TagadaProvider.js +74 -5
  134. package/dist/v2/standalone/external-tracker.d.ts +52 -46
  135. package/dist/v2/standalone/external-tracker.js +205 -98
  136. package/dist/v2/standalone/index.d.ts +40 -0
  137. package/dist/v2/standalone/index.js +148 -1
  138. package/dist/v2/standalone/payment-service.d.ts +134 -0
  139. package/dist/v2/standalone/payment-service.js +928 -0
  140. package/package.json +6 -4
  141. package/dist/react/utils/__tests__/urlUtils.test.d.ts +0 -1
  142. package/dist/react/utils/__tests__/urlUtils.test.js +0 -189
  143. package/dist/v2/core/__tests__/pathRemapping.test.d.ts +0 -11
  144. package/dist/v2/core/__tests__/pathRemapping.test.js +0 -776
@@ -3,9 +3,19 @@
3
3
  * Replaces the coordinator pattern with automatic cache invalidation
4
4
  */
5
5
  import { CheckoutData, CheckoutInitParams, CheckoutLineItem, CheckoutSessionPreview } from '../../core/resources/checkout';
6
+ export interface CheckoutMessages {
7
+ /** Error shown when CMS session takes too long to initialize. Default: 'Session initialization timeout. Please refresh the page and try again.' */
8
+ sessionTimeout?: string;
9
+ /** Error shown when checkout resource fails to initialize. Default: 'Failed to initialize checkout resource' */
10
+ initFailed?: string;
11
+ /** Error shown when an operation is attempted without an active checkout session. Default: 'No checkout session available' */
12
+ noCheckoutSession?: string;
13
+ }
6
14
  export interface UseCheckoutQueryOptions {
7
15
  checkoutToken?: string;
8
16
  enabled?: boolean;
17
+ /** Override default error messages (e.g. for i18n). Use with useTranslation's t() to pass translated strings. */
18
+ messages?: CheckoutMessages;
9
19
  }
10
20
  export interface UseCheckoutQueryResult {
11
21
  checkout: CheckoutData | undefined;
@@ -17,8 +27,14 @@ export interface UseCheckoutQueryResult {
17
27
  checkoutToken: string;
18
28
  }>;
19
29
  refresh: () => Promise<void>;
30
+ replaceSessionLineItems: (lineItems: CheckoutLineItem[]) => Promise<any>;
20
31
  updateLineItems: (lineItems: CheckoutLineItem[]) => Promise<any>;
21
32
  updateLineItemsOptimistic: (lineItems: CheckoutLineItem[]) => void;
33
+ addLineItems: (lineItems: CheckoutLineItem[]) => Promise<any>;
34
+ removeLineItems: (lineItems: {
35
+ variantId: string;
36
+ quantity?: number;
37
+ }[]) => Promise<any>;
22
38
  setItemQuantity: (variantId: string, quantity: number, priceId?: string) => Promise<any>;
23
39
  updateCustomer: (data: {
24
40
  email: string;
@@ -5,12 +5,19 @@
5
5
  import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
6
6
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
7
7
  import { CheckoutResource } from '../../core/resources/checkout';
8
+ import { getAssignedOrderBumpOfferIds } from '../../core/funnelClient';
8
9
  import { useTagadaContext } from '../providers/TagadaProvider';
9
10
  import { getGlobalApiClient } from './useApiQuery';
10
11
  import { useCurrency } from './useCurrency';
11
12
  import { usePluginConfig } from './usePluginConfig';
13
+ const DEFAULT_MESSAGES = {
14
+ sessionTimeout: 'Session initialization timeout. Please refresh the page and try again.',
15
+ initFailed: 'Failed to initialize checkout resource',
16
+ noCheckoutSession: 'No checkout session available',
17
+ };
12
18
  export function useCheckoutQuery(options = {}) {
13
- const { checkoutToken: providedToken, enabled = true } = options;
19
+ const { checkoutToken: providedToken, enabled = true, messages: userMessages } = options;
20
+ const messages = { ...DEFAULT_MESSAGES, ...userMessages };
14
21
  const { storeId } = usePluginConfig();
15
22
  const currency = useCurrency();
16
23
  const queryClient = useQueryClient();
@@ -48,7 +55,7 @@ export function useCheckoutQuery(options = {}) {
48
55
  if (sessionResolvers.current.size === 0) {
49
56
  pendingSessionPromise.current = null;
50
57
  }
51
- reject(new Error('Session initialization timeout. Please refresh the page and try again.'));
58
+ reject(new Error(messages.sessionTimeout));
52
59
  }
53
60
  }, 10000);
54
61
  });
@@ -60,7 +67,7 @@ export function useCheckoutQuery(options = {}) {
60
67
  return new CheckoutResource(getGlobalApiClient());
61
68
  }
62
69
  catch (error) {
63
- throw new Error('Failed to initialize checkout resource: ' + (error instanceof Error ? error.message : 'Unknown error'));
70
+ throw new Error(messages.initFailed + ': ' + (error instanceof Error ? error.message : 'Unknown error'));
64
71
  }
65
72
  }, []);
66
73
  // Internal token state that can be updated after init
@@ -90,16 +97,17 @@ export function useCheckoutQuery(options = {}) {
90
97
  // Initialize checkout mutation (async mode for fast response)
91
98
  const initMutation = useMutation({
92
99
  mutationFn: async (params) => {
100
+ const enabledOrderBumpOfferIds = params.enabledOrderBumpOfferIds ?? getAssignedOrderBumpOfferIds();
93
101
  const requestBody = {
94
102
  ...params,
95
103
  storeId: params.storeId || storeId,
96
104
  returnUrl: params.returnUrl || window.location.origin,
97
- // Include customerId from session to prevent duplicate customer creation
98
105
  customerId: params.customerId || session?.customerId,
99
106
  customer: {
100
107
  ...params.customer,
101
108
  currency: params.customer?.currency ?? currency.code,
102
109
  },
110
+ ...(enabledOrderBumpOfferIds && { enabledOrderBumpOfferIds }),
103
111
  };
104
112
  // Use async mode for fast response (~50ms vs 2-5s)
105
113
  const asyncResponse = await checkoutResource.initCheckoutAsync(requestBody);
@@ -121,11 +129,25 @@ export function useCheckoutQuery(options = {}) {
121
129
  },
122
130
  });
123
131
  // Order bump functionality removed - use useOrderBumpQuery instead
132
+ // Replace session line items mutation (full rewrite)
133
+ const replaceSessionLineItemsMutation = useMutation({
134
+ mutationFn: ({ lineItems }) => {
135
+ if (!checkout?.checkoutSession?.id) {
136
+ throw new Error(messages.noCheckoutSession);
137
+ }
138
+ return checkoutResource.replaceSessionLineItems(checkout.checkoutSession.id, lineItems);
139
+ },
140
+ onSuccess: () => {
141
+ if (checkoutToken) {
142
+ void queryClient.invalidateQueries({ queryKey: ['checkout', checkoutToken] });
143
+ }
144
+ },
145
+ });
124
146
  // Line items mutation with optimistic updates
125
147
  const lineItemsMutation = useMutation({
126
148
  mutationFn: ({ lineItems }) => {
127
149
  if (!checkout?.checkoutSession?.id) {
128
- throw new Error('No checkout session available');
150
+ throw new Error(messages.noCheckoutSession);
129
151
  }
130
152
  return checkoutResource.updateLineItems(checkout.checkoutSession.id, lineItems);
131
153
  },
@@ -166,11 +188,39 @@ export function useCheckoutQuery(options = {}) {
166
188
  }
167
189
  },
168
190
  });
191
+ // Add line items mutation
192
+ const addLineItemsMutation = useMutation({
193
+ mutationFn: ({ lineItems }) => {
194
+ if (!checkout?.checkoutSession?.id) {
195
+ throw new Error(messages.noCheckoutSession);
196
+ }
197
+ return checkoutResource.addLineItems(checkout.checkoutSession.id, lineItems);
198
+ },
199
+ onSuccess: () => {
200
+ if (checkoutToken) {
201
+ void queryClient.invalidateQueries({ queryKey: ['checkout', checkoutToken] });
202
+ }
203
+ },
204
+ });
205
+ // Remove line items mutation
206
+ const removeLineItemsMutation = useMutation({
207
+ mutationFn: ({ lineItems }) => {
208
+ if (!checkout?.checkoutSession?.id) {
209
+ throw new Error(messages.noCheckoutSession);
210
+ }
211
+ return checkoutResource.removeLineItems(checkout.checkoutSession.id, lineItems);
212
+ },
213
+ onSuccess: () => {
214
+ if (checkoutToken) {
215
+ void queryClient.invalidateQueries({ queryKey: ['checkout', checkoutToken] });
216
+ }
217
+ },
218
+ });
169
219
  // Item quantity mutation
170
220
  const quantityMutation = useMutation({
171
221
  mutationFn: ({ variantId, quantity, priceId }) => {
172
222
  if (!checkout?.checkoutSession?.id) {
173
- throw new Error('No checkout session available');
223
+ throw new Error(messages.noCheckoutSession);
174
224
  }
175
225
  return checkoutResource.setItemQuantity(checkout.checkoutSession.id, variantId, quantity, priceId);
176
226
  },
@@ -184,7 +234,7 @@ export function useCheckoutQuery(options = {}) {
184
234
  const customerMutation = useMutation({
185
235
  mutationFn: (data) => {
186
236
  if (!checkout?.checkoutSession?.id) {
187
- throw new Error('No checkout session available');
237
+ throw new Error(messages.noCheckoutSession);
188
238
  }
189
239
  return checkoutResource.updateCustomer(checkout.checkoutSession.id, data);
190
240
  },
@@ -198,7 +248,7 @@ export function useCheckoutQuery(options = {}) {
198
248
  const customerAndSessionMutation = useMutation({
199
249
  mutationFn: (data) => {
200
250
  if (!checkout?.checkoutSession?.id) {
201
- throw new Error('No checkout session available');
251
+ throw new Error(messages.noCheckoutSession);
202
252
  }
203
253
  return checkoutResource.updateCustomerAndSessionInfo(checkout.checkoutSession.id, data);
204
254
  },
@@ -212,7 +262,7 @@ export function useCheckoutQuery(options = {}) {
212
262
  const promotionMutation = useMutation({
213
263
  mutationFn: ({ code }) => {
214
264
  if (!checkout?.checkoutSession?.id) {
215
- throw new Error('No checkout session available');
265
+ throw new Error(messages.noCheckoutSession);
216
266
  }
217
267
  return checkoutResource.applyPromotionCode(checkout.checkoutSession.id, code);
218
268
  },
@@ -229,7 +279,7 @@ export function useCheckoutQuery(options = {}) {
229
279
  const removePromotionMutation = useMutation({
230
280
  mutationFn: ({ promotionId }) => {
231
281
  if (!checkout?.checkoutSession?.id) {
232
- throw new Error('No checkout session available');
282
+ throw new Error(messages.noCheckoutSession);
233
283
  }
234
284
  return checkoutResource.removePromotion(checkout.checkoutSession.id, promotionId);
235
285
  },
@@ -274,8 +324,11 @@ export function useCheckoutQuery(options = {}) {
274
324
  },
275
325
  refresh,
276
326
  // Checkout operations
327
+ replaceSessionLineItems: (lineItems) => replaceSessionLineItemsMutation.mutateAsync({ lineItems }),
277
328
  updateLineItems: (lineItems) => lineItemsMutation.mutateAsync({ lineItems }),
278
329
  updateLineItemsOptimistic: (lineItems) => lineItemsMutation.mutate({ lineItems }),
330
+ addLineItems: (lineItems) => addLineItemsMutation.mutateAsync({ lineItems }),
331
+ removeLineItems: (lineItems) => removeLineItemsMutation.mutateAsync({ lineItems }),
279
332
  setItemQuantity: (variantId, quantity, priceId) => quantityMutation.mutateAsync({ variantId, quantity, priceId }),
280
333
  updateCustomer: (data) => customerMutation.mutateAsync(data),
281
334
  updateCustomerAndSessionInfo: (data) => customerAndSessionMutation.mutateAsync(data),
@@ -14,8 +14,8 @@
14
14
  * // In any child component:
15
15
  * const { context, next, isLoading, stepConfig } = useFunnel();
16
16
  *
17
- * // Access step-specific config (payment flows, static resources, etc.)
18
- * const offerId = stepConfig.staticResources?.offer;
17
+ * // Access step-specific config (payment flows, resources, etc.)
18
+ * const offerId = stepConfig.resources?.offer;
19
19
  * const paymentFlowId = stepConfig.paymentFlowId;
20
20
  * ```
21
21
  */
@@ -35,10 +35,11 @@ export interface StepConfigValue {
35
35
  */
36
36
  paymentFlowId: string | undefined;
37
37
  /**
38
- * Static resources assigned to this step/variant
39
- * For A/B tests, this contains the resources for the specific variant
38
+ * Resource bindings for this step/variant.
40
39
  * e.g., { offer: 'offer_xxx', product: 'product_xxx' }
41
40
  */
41
+ resources: Record<string, string> | undefined;
42
+ /** @deprecated Use `resources` instead */
42
43
  staticResources: Record<string, string> | undefined;
43
44
  /**
44
45
  * Get scripts for a specific injection position
@@ -54,6 +55,16 @@ export interface StepConfigValue {
54
55
  [TrackingProvider.SNAPCHAT]?: SnapchatTrackingConfig[];
55
56
  [TrackingProvider.GTM]?: GTMTrackingConfig[];
56
57
  };
58
+ /**
59
+ * Enabled order bump offer IDs for this step.
60
+ * undefined = inherit all store bumps, string[] = only these IDs.
61
+ */
62
+ orderBumpOfferIds: string[] | undefined;
63
+ /**
64
+ * Enabled upsell offer IDs for this step.
65
+ * undefined = inherit all store upsells, string[] = only these IDs.
66
+ */
67
+ upsellOfferIds: string[] | undefined;
57
68
  }
58
69
  export interface FunnelContextValue extends FunnelState {
59
70
  currentStep: {
@@ -14,13 +14,13 @@
14
14
  * // In any child component:
15
15
  * const { context, next, isLoading, stepConfig } = useFunnel();
16
16
  *
17
- * // Access step-specific config (payment flows, static resources, etc.)
18
- * const offerId = stepConfig.staticResources?.offer;
17
+ * // Access step-specific config (payment flows, resources, etc.)
18
+ * const offerId = stepConfig.resources?.offer;
19
19
  * const paymentFlowId = stepConfig.paymentFlowId;
20
20
  * ```
21
21
  */
22
22
  import { useMemo } from 'react';
23
- import { TrackingProvider, getAssignedPaymentFlowId, getAssignedPixels, getAssignedScripts, getAssignedStaticResources, getAssignedStepConfig } from '../../core/funnelClient';
23
+ import { TrackingProvider, getAssignedOrderBumpOfferIds, getAssignedPaymentFlowId, getAssignedPixels, getAssignedResources, getAssignedScripts, getAssignedStepConfig, getAssignedUpsellOfferIds, } from '../../core/funnelClient';
24
24
  import { useTagadaContext } from '../providers/TagadaProvider';
25
25
  /**
26
26
  * Hook to access funnel state and methods
@@ -35,12 +35,16 @@ export function useFunnel() {
35
35
  // Compute step config from HTML injection (memoized, computed once on mount)
36
36
  const stepConfig = useMemo(() => {
37
37
  const raw = getAssignedStepConfig();
38
+ const resources = getAssignedResources();
38
39
  return {
39
40
  raw,
40
41
  paymentFlowId: getAssignedPaymentFlowId(),
41
- staticResources: getAssignedStaticResources(),
42
+ resources,
43
+ staticResources: resources,
42
44
  pixels: getAssignedPixels(),
43
45
  getScripts: (position) => getAssignedScripts(position),
46
+ orderBumpOfferIds: getAssignedOrderBumpOfferIds(),
47
+ upsellOfferIds: getAssignedUpsellOfferIds(),
44
48
  };
45
49
  }, []);
46
50
  return {
@@ -1,3 +1,4 @@
1
+ import { TagadaError } from '../../core/errors';
1
2
  export interface GeoLocationData {
2
3
  ip_address?: string | null;
3
4
  city?: string | null;
@@ -82,7 +83,7 @@ export interface UseGeoLocationReturn {
82
83
  /**
83
84
  * Any error that occurred during the request
84
85
  */
85
- error: string | null;
86
+ error: TagadaError | null;
86
87
  /**
87
88
  * Function to manually fetch geolocation data
88
89
  */
@@ -1,6 +1,7 @@
1
1
  'use client';
2
- import { useCallback, useEffect, useState } from 'react';
2
+ import { useCallback, useEffect, useMemo, useState } from 'react';
3
3
  import { useTagadaContext } from '../providers/TagadaProvider';
4
+ import { TagadaError, TagadaErrorCode } from '../../core/errors';
4
5
  /**
5
6
  * Hook to fetch and manage geolocation data - V2 Implementation
6
7
  * Compatible with V1 interface while using V2 provider architecture
@@ -111,10 +112,11 @@ export function useGeoLocation(options = {}) {
111
112
  void fetchGeoData();
112
113
  }
113
114
  }, [refetchOnMount, apiService, fetchGeoData]);
115
+ const tagadaError = useMemo(() => error ? new TagadaError(error, { code: TagadaErrorCode.NETWORK_ERROR }) : null, [error]);
114
116
  return {
115
117
  data,
116
118
  isLoading,
117
- error,
119
+ error: tagadaError,
118
120
  fetchGeoData,
119
121
  clearData,
120
122
  isLocalhost: isLocalhost(data?.ip_address),
@@ -68,6 +68,8 @@ export interface UseGoogleAutocompleteOptions {
68
68
  version?: string;
69
69
  language?: string;
70
70
  region?: string;
71
+ /** When true, defer loading the Google Maps script until searchPlaces() is first called. Default: false (eager load). */
72
+ defer?: boolean;
71
73
  }
72
74
  export interface UseGoogleAutocompleteResult {
73
75
  predictions: GooglePrediction[];
@@ -8,7 +8,7 @@ import { useCallback, useEffect, useRef, useState } from 'react';
8
8
  * Automatically loads the Google Maps JavaScript API with Places library
9
9
  */
10
10
  export function useGoogleAutocomplete(options) {
11
- const { apiKey, libraries = ['places'], version = 'weekly', language, region } = options;
11
+ const { apiKey, libraries = ['places'], version = 'weekly', language, region, defer = false } = options;
12
12
  const [predictions, setPredictions] = useState([]);
13
13
  const [isLoading, setIsLoading] = useState(false);
14
14
  const [isScriptLoaded, setIsScriptLoaded] = useState(false);
@@ -16,19 +16,16 @@ export function useGoogleAutocomplete(options) {
16
16
  const placesServiceRef = useRef(null);
17
17
  const scriptLoadedRef = useRef(false);
18
18
  const debounceTimeoutRef = useRef(null);
19
- // Inject Google Maps script
20
- useEffect(() => {
21
- if (!apiKey) {
22
- return;
23
- }
19
+ const pendingSearchRef = useRef(null);
20
+ // Shared script injection logic (used by both eager useEffect and lazy trigger)
21
+ const injectScript = useCallback(() => {
24
22
  if (scriptLoadedRef.current || window.google?.maps) {
25
23
  setIsScriptLoaded(true);
24
+ scriptLoadedRef.current = true;
26
25
  return;
27
26
  }
28
- // Check if script is already being loaded
29
27
  const existingScript = document.querySelector('script[src*="maps.googleapis.com"]');
30
28
  if (existingScript) {
31
- // Wait for existing script to load
32
29
  const checkLoaded = () => {
33
30
  if (window.google?.maps?.places?.AutocompleteService) {
34
31
  setIsScriptLoaded(true);
@@ -41,7 +38,6 @@ export function useGoogleAutocomplete(options) {
41
38
  checkLoaded();
42
39
  return;
43
40
  }
44
- // Create and inject the script
45
41
  const script = document.createElement('script');
46
42
  const params = new URLSearchParams({
47
43
  key: apiKey,
@@ -63,12 +59,23 @@ export function useGoogleAutocomplete(options) {
63
59
  // Failed to load Google Maps API
64
60
  };
65
61
  document.head.appendChild(script);
66
- // Cleanup function
67
- return () => {
68
- // Note: We don't remove the script as it might be used by other components
69
- // The script will remain in the DOM for the session
70
- };
71
62
  }, [apiKey, libraries, version, language, region]);
63
+ // Inject Google Maps script eagerly (unless defer is true)
64
+ useEffect(() => {
65
+ if (!apiKey || defer) {
66
+ return;
67
+ }
68
+ injectScript();
69
+ }, [apiKey, defer, injectScript]);
70
+ // When script finishes loading in deferred mode, replay the pending search
71
+ const searchPlacesRef = useRef(null);
72
+ useEffect(() => {
73
+ if (isScriptLoaded && pendingSearchRef.current && searchPlacesRef.current) {
74
+ const { input, countryRestriction } = pendingSearchRef.current;
75
+ pendingSearchRef.current = null;
76
+ searchPlacesRef.current(input, countryRestriction);
77
+ }
78
+ }, [isScriptLoaded]);
72
79
  // Initialize Google Places services
73
80
  const initializeServices = useCallback(() => {
74
81
  if (typeof window === 'undefined')
@@ -89,6 +96,11 @@ export function useGoogleAutocomplete(options) {
89
96
  // Search for place predictions
90
97
  const searchPlaces = useCallback((input, countryRestriction) => {
91
98
  if (!isScriptLoaded) {
99
+ // When deferred, trigger lazy load and queue the search for replay
100
+ if (defer && !scriptLoadedRef.current) {
101
+ pendingSearchRef.current = { input, countryRestriction };
102
+ injectScript();
103
+ }
92
104
  return;
93
105
  }
94
106
  if (!initializeServices()) {
@@ -124,7 +136,9 @@ export function useGoogleAutocomplete(options) {
124
136
  }
125
137
  });
126
138
  }, 300); // 300ms debounce
127
- }, [initializeServices, isScriptLoaded]);
139
+ }, [initializeServices, isScriptLoaded, defer, injectScript]);
140
+ // Keep ref in sync so the deferred-replay useEffect can call searchPlaces
141
+ searchPlacesRef.current = searchPlaces;
128
142
  // Get detailed place information
129
143
  const getPlaceDetails = useCallback((placeId) => {
130
144
  return new Promise((resolve) => {
@@ -18,11 +18,8 @@ export interface UseISODataResult {
18
18
  registeredLanguages: SupportedLanguage[];
19
19
  }
20
20
  /**
21
- * React hook for accessing ISO3166 countries and regions data with dynamic language loading
22
- * @param language - Language code (supports: en, ru, de, fr, es, zh, hi, pt, ja, ar, it, he)
23
- * @param autoImport - Whether to automatically import the language if not registered (default: true)
24
- * @param disputeSetting - Territorial dispute perspective (currently only UN is supported)
25
- * @returns Object with countries data and helper functions
21
+ * React hook for accessing ISO3166 countries and regions data.
22
+ * Fetches slim JSON from CDN on first use, then caches in memory.
26
23
  */
27
24
  export declare function useISOData(language?: SupportedLanguage, autoImport?: boolean, disputeSetting?: string): UseISODataResult;
28
25
  /**
@@ -1,35 +1,40 @@
1
1
  import { useMemo, useEffect, useState, useCallback } from 'react';
2
- // Import the pre-built ISO data functions
3
- import { getCountries, getStatesForCountry, importLanguage, isLanguageRegistered, getRegisteredLanguages } from '../../../data/iso3166';
2
+ import { getCountries, getStatesForCountry, ensureGeoDataLoaded, importLanguage, isLanguageRegistered, getRegisteredLanguages } from '../../../data/iso3166';
4
3
  /**
5
- * React hook for accessing ISO3166 countries and regions data with dynamic language loading
6
- * @param language - Language code (supports: en, ru, de, fr, es, zh, hi, pt, ja, ar, it, he)
7
- * @param autoImport - Whether to automatically import the language if not registered (default: true)
8
- * @param disputeSetting - Territorial dispute perspective (currently only UN is supported)
9
- * @returns Object with countries data and helper functions
4
+ * React hook for accessing ISO3166 countries and regions data.
5
+ * Fetches slim JSON from CDN on first use, then caches in memory.
10
6
  */
11
7
  export function useISOData(language = 'en', autoImport = true, disputeSetting = 'UN') {
12
8
  const [isLanguageLoaded, setIsLanguageLoaded] = useState(isLanguageRegistered(language));
13
9
  const [registeredLanguages, setRegisteredLanguages] = useState(getRegisteredLanguages);
14
- // Auto-import language if not registered and autoImport is true
10
+ // Fetch geodata from CDN (countries + regions) for the requested language
15
11
  useEffect(() => {
16
- if (!isLanguageRegistered(language) && autoImport && language !== 'en') {
17
- importLanguage(language)
18
- .then(() => {
12
+ let cancelled = false;
13
+ ensureGeoDataLoaded(language)
14
+ .then(() => {
15
+ if (!cancelled) {
19
16
  setIsLanguageLoaded(true);
20
17
  setRegisteredLanguages(getRegisteredLanguages());
21
- })
22
- .catch((error) => {
23
- });
24
- }
18
+ }
19
+ })
20
+ .catch((err) => {
21
+ console.error('[SDK] Failed to load geodata from CDN:', err);
22
+ });
23
+ return () => { cancelled = true; };
25
24
  }, [language, autoImport]);
26
25
  const data = useMemo(() => {
26
+ if (!isLanguageLoaded) {
27
+ return {
28
+ countries: {},
29
+ getRegions: () => [],
30
+ findRegion: () => null,
31
+ mapGoogleToISO: () => null,
32
+ isLanguageLoaded: false,
33
+ registeredLanguages,
34
+ };
35
+ }
27
36
  try {
28
- console.log('[SDK] Loading ISO data for language:', language, 'autoImport:', autoImport);
29
- // Get countries from pre-built data with language support
30
37
  const countriesArray = getCountries(language);
31
- console.log('[SDK] Loaded countries count:', countriesArray.length);
32
- // Transform to our expected format (Record<string, ISOCountry>)
33
38
  const countries = {};
34
39
  countriesArray.forEach((country) => {
35
40
  countries[country.code] = {
@@ -39,7 +44,6 @@ export function useISOData(language = 'en', autoImport = true, disputeSetting =
39
44
  name: country.name,
40
45
  };
41
46
  });
42
- // Helper to load regions for a specific country
43
47
  const getRegions = (countryCode) => {
44
48
  try {
45
49
  const states = getStatesForCountry(countryCode, language);
@@ -49,31 +53,26 @@ export function useISOData(language = 'en', autoImport = true, disputeSetting =
49
53
  }));
50
54
  }
51
55
  catch {
52
- return []; // Return empty array if no regions
56
+ return [];
53
57
  }
54
58
  };
55
- // Find a specific region by ISO code
56
59
  const findRegion = (countryCode, regionCode) => {
57
60
  const regions = getRegions(countryCode);
58
61
  return regions.find((region) => region.iso === regionCode) ?? null;
59
62
  };
60
- // Map Google Places state to ISO region (proven 100% success rate)
61
63
  const mapGoogleToISO = (googleState, googleStateLong, countryCode) => {
62
64
  const regions = getRegions(countryCode);
63
65
  if (regions.length === 0)
64
66
  return null;
65
- // Strategy 1: Exact ISO code match (86% success rate)
66
67
  let match = regions.find((r) => r.iso === googleState);
67
68
  if (match)
68
69
  return match;
69
- // Strategy 2: Name matching (14% success rate)
70
70
  match = regions.find((r) => r.name.toLowerCase() === googleState.toLowerCase());
71
71
  if (match)
72
72
  return match;
73
73
  match = regions.find((r) => r.name.toLowerCase() === googleStateLong.toLowerCase());
74
74
  if (match)
75
75
  return match;
76
- // Strategy 3: Partial name matching (fallback)
77
76
  match = regions.find((r) => r.name.toLowerCase().includes(googleStateLong.toLowerCase()) ||
78
77
  googleStateLong.toLowerCase().includes(r.name.toLowerCase()));
79
78
  return match ?? null;
@@ -106,7 +105,7 @@ export function useISOData(language = 'en', autoImport = true, disputeSetting =
106
105
  */
107
106
  export function getAvailableLanguages() {
108
107
  // Return all available languages (not just registered ones)
109
- return ['en', 'ru', 'de', 'fr', 'es', 'zh', 'hi', 'pt', 'ja', 'ar', 'it', 'he'];
108
+ return ['en', 'ru', 'de', 'da', 'fr', 'es', 'zh', 'hi', 'pt', 'ja', 'ar', 'it', 'he'];
110
109
  }
111
110
  /**
112
111
  * Hook to manually import and register a language
@@ -2,14 +2,14 @@ export interface Payment {
2
2
  id: string;
3
3
  status: string;
4
4
  subStatus: string;
5
- requireAction: 'none' | 'redirect' | 'error' | 'radar';
5
+ requireAction: 'none' | 'redirect' | 'redirect_to_payment' | 'error' | 'radar' | 'stripe_express_checkout';
6
6
  requireActionData?: {
7
- type: 'redirect' | 'threeds_auth' | 'processor_auth' | 'error' | 'stripe_radar' | 'finix_radar' | 'radar';
7
+ type: 'redirect' | 'redirect_to_payment' | 'threeds_auth' | 'processor_auth' | 'error' | 'stripe_radar' | 'finix_radar' | 'radar' | 'kesspay_auth' | 'trustflow_auth' | 'mastercard_auth' | 'stripe_express_checkout';
8
8
  url?: string;
9
9
  processed: boolean;
10
10
  processorId?: string;
11
11
  metadata?: {
12
- type: 'redirect' | 'stripe_radar' | 'finix_radar';
12
+ type: 'redirect' | 'redirect_to_payment' | 'stripe_radar' | 'finix_radar';
13
13
  redirect?: {
14
14
  redirectUrl: string;
15
15
  returnUrl: string;
@@ -2,7 +2,8 @@
2
2
  * Payment Hook using TanStack Query (V2)
3
3
  * Matches the old usePayment.ts implementation exactly for easy migration
4
4
  */
5
- import type { Payment, PaymentResponse, PaymentOptions, CardPaymentMethod, ApplePayToken, PaymentInstrumentResponse, PaymentInstrumentCustomerResponse } from '../../core/resources/payments';
5
+ import type { Payment, PaymentResponse, PaymentOptions, CardPaymentMethod, ApplePayToken, GooglePayToken, PaymentInstrumentResponse, PaymentInstrumentCustomerResponse } from '../../core/resources/payments';
6
+ import { TagadaError } from '../../core/errors';
6
7
  export type { Payment as PaymentType, PaymentResponse, PaymentOptions, CardPaymentMethod, ApplePayToken, PaymentInstrumentResponse, PaymentInstrumentCustomerResponse, PaymentInstrumentCustomer } from '../../core/resources/payments';
7
8
  /**
8
9
  * Metadata provided with payment callbacks
@@ -11,7 +12,12 @@ export interface PaymentCompletionMetadata {
11
12
  /** True if payment completed after external redirect (3DS, PayPal, etc.) */
12
13
  isRedirectReturn: boolean;
13
14
  /** Order associated with the payment (if available) */
14
- order?: any;
15
+ order?: {
16
+ id: string;
17
+ amount?: number;
18
+ currency?: string;
19
+ [key: string]: unknown;
20
+ };
15
21
  /** Checkout session ID (if available) */
16
22
  checkoutSessionId?: string;
17
23
  }
@@ -37,14 +43,21 @@ export interface UsePaymentOptions {
37
43
  export interface PaymentHook {
38
44
  processCardPayment: (checkoutSessionId: string, cardData: CardPaymentMethod, options?: PaymentOptions) => Promise<PaymentResponse>;
39
45
  processApplePayPayment: (checkoutSessionId: string, applePayToken: ApplePayToken, options?: PaymentOptions) => Promise<PaymentResponse>;
40
- processGooglePayPayment: (checkoutSessionId: string, googlePayToken: any, options?: PaymentOptions) => Promise<PaymentResponse>;
46
+ processGooglePayPayment: (checkoutSessionId: string, googlePayToken: GooglePayToken, options?: PaymentOptions) => Promise<PaymentResponse>;
41
47
  processPaymentWithInstrument: (checkoutSessionId: string, paymentInstrumentId: string, options?: PaymentOptions) => Promise<PaymentResponse>;
48
+ processApmPayment: (checkoutSessionId: string, options: {
49
+ processorId: string;
50
+ paymentMethod: string;
51
+ initiatedBy?: 'customer' | 'merchant';
52
+ source?: 'upsell' | 'checkout' | 'offer' | 'missing_club' | 'forced';
53
+ paymentFlowId?: string;
54
+ } & Pick<PaymentOptions, 'onFailure' | 'onPaymentFailed' | 'onSuccess' | 'onPaymentSuccess'>) => Promise<PaymentResponse>;
42
55
  createCardPaymentInstrument: (cardData: CardPaymentMethod) => Promise<PaymentInstrumentResponse>;
43
56
  createApplePayPaymentInstrument: (applePayToken: ApplePayToken) => Promise<PaymentInstrumentResponse>;
44
- createGooglePayPaymentInstrument: (googlePayToken: any) => Promise<PaymentInstrumentResponse>;
57
+ createGooglePayPaymentInstrument: (googlePayToken: GooglePayToken) => Promise<PaymentInstrumentResponse>;
45
58
  getCardPaymentInstruments: () => Promise<PaymentInstrumentCustomerResponse>;
46
59
  isLoading: boolean;
47
- error: string | null;
60
+ error: TagadaError | null;
48
61
  clearError: () => void;
49
62
  currentPaymentId: string | null;
50
63
  }