@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
@@ -150,6 +150,24 @@ export function usePostPurchasesQuery(options) {
150
150
  });
151
151
  // Fetch order summary with variant options
152
152
  await fetchOrderSummary(offerId, sessionId);
153
+ // Detect Whop payment method for this checkout session
154
+ try {
155
+ const paymentMethods = await postPurchasesResource.getPaymentMethods(sessionId);
156
+ const isWhop = paymentMethods.some((m) => m.type === 'whop');
157
+ if (isWhop) {
158
+ setCheckoutSessions(prev => ({
159
+ ...prev,
160
+ [offerId]: {
161
+ ...prev[offerId],
162
+ isWhop: true,
163
+ }
164
+ }));
165
+ }
166
+ }
167
+ catch (err) {
168
+ // Non-critical: if payment methods fetch fails, fall back to standard flow
169
+ console.warn(`[SDK] Failed to detect payment methods for offer ${offerId}:`, err);
170
+ }
153
171
  }
154
172
  finally {
155
173
  // Remove from initializing set
@@ -421,8 +439,22 @@ export function usePostPurchasesQuery(options) {
421
439
  if (!sessionState?.checkoutSessionId) {
422
440
  throw new Error('Checkout session not initialized for this offer');
423
441
  }
424
- // Use the enhanced payWithCheckoutSession with proper metadata
425
- await postPurchasesResource.payWithCheckoutSession(sessionState.checkoutSessionId, orderId);
442
+ if (sessionState.isWhop) {
443
+ // Whop post-purchase: charge using the stored payment method from the original order
444
+ const storeId = session?.storeId;
445
+ if (!storeId) {
446
+ throw new Error('Store ID not available for Whop payment');
447
+ }
448
+ await postPurchasesResource.chargeWhopPostPurchase({
449
+ checkoutSessionId: sessionState.checkoutSessionId,
450
+ orderId,
451
+ storeId,
452
+ });
453
+ }
454
+ else {
455
+ // Standard post-purchase: use the generic pay endpoint
456
+ await postPurchasesResource.payWithCheckoutSession(sessionState.checkoutSessionId, orderId);
457
+ }
426
458
  },
427
459
  };
428
460
  }
@@ -1,9 +1,6 @@
1
1
  /**
2
- * Hook to extract URL parameters that works with both remapped and non-remapped paths
3
- *
4
- * This hook automatically detects if the current path is remapped and extracts
5
- * parameters correctly in both cases, so your components don't need to know
6
- * about path remapping at all.
2
+ * Hook to extract URL parameters that works with both remapped and non-remapped paths.
3
+ * Router-agnostic: uses window.location + path-to-regexp directly (no react-router-dom needed).
7
4
  *
8
5
  * @param internalPath - The internal path pattern (e.g., "/hello-with-param/:myparam")
9
6
  * @returns Parameters object with extracted values
@@ -13,7 +10,6 @@
13
10
  * function HelloWithParamPage() {
14
11
  * // Works for both /hello-with-param/test AND /myremap/test
15
12
  * const { myparam } = useRemappableParams<{ myparam: string }>('/hello-with-param/:myparam');
16
- *
17
13
  * return <div>Parameter: {myparam}</div>;
18
14
  * }
19
15
  * ```
@@ -1,12 +1,22 @@
1
- import { useParams } from 'react-router-dom';
2
1
  import { getPathInfo } from '../../core/pathRemapping';
3
2
  import { match } from 'path-to-regexp';
4
3
  /**
5
- * Hook to extract URL parameters that works with both remapped and non-remapped paths
6
- *
7
- * This hook automatically detects if the current path is remapped and extracts
8
- * parameters correctly in both cases, so your components don't need to know
9
- * about path remapping at all.
4
+ * Extract params from a URL path using a pattern via path-to-regexp
5
+ */
6
+ function extractMatchParams(pattern, path) {
7
+ try {
8
+ const matchFn = match(pattern, { decode: decodeURIComponent });
9
+ const result = matchFn(path);
10
+ if (result && typeof result !== 'boolean') {
11
+ return result.params;
12
+ }
13
+ }
14
+ catch { /* ignore */ }
15
+ return null;
16
+ }
17
+ /**
18
+ * Hook to extract URL parameters that works with both remapped and non-remapped paths.
19
+ * Router-agnostic: uses window.location + path-to-regexp directly (no react-router-dom needed).
10
20
  *
11
21
  * @param internalPath - The internal path pattern (e.g., "/hello-with-param/:myparam")
12
22
  * @returns Parameters object with extracted values
@@ -16,21 +26,19 @@ import { match } from 'path-to-regexp';
16
26
  * function HelloWithParamPage() {
17
27
  * // Works for both /hello-with-param/test AND /myremap/test
18
28
  * const { myparam } = useRemappableParams<{ myparam: string }>('/hello-with-param/:myparam');
19
- *
20
29
  * return <div>Parameter: {myparam}</div>;
21
30
  * }
22
31
  * ```
23
32
  */
24
33
  export function useRemappableParams(internalPath) {
25
- const routerParams = useParams();
26
34
  const pathInfo = getPathInfo();
27
- // If not remapped, just return router params
35
+ const currentPath = typeof window !== 'undefined' ? window.location.pathname : '/';
36
+ // If not remapped, extract params from URL using the internal pattern directly
28
37
  if (!pathInfo.isRemapped) {
29
- return routerParams;
38
+ return extractMatchParams(internalPath, currentPath) ?? {};
30
39
  }
31
40
  // If remapped, extract params from the external URL
32
41
  try {
33
- // Get the external pattern from localStorage (for testing) or from meta tag
34
42
  let externalPattern = null;
35
43
  // Check localStorage for explicit remapping (development/testing)
36
44
  if (typeof localStorage !== 'undefined') {
@@ -54,17 +62,13 @@ export function useRemappableParams(internalPath) {
54
62
  externalPattern = metaTag.getAttribute('content');
55
63
  }
56
64
  }
57
- // If we have an external pattern, extract params from it
65
+ // If we have an external pattern, extract params and map to internal names
58
66
  if (externalPattern) {
59
67
  const matchFn = match(externalPattern, { decode: decodeURIComponent });
60
68
  const result = matchFn(pathInfo.externalPath);
61
69
  if (result && typeof result !== 'boolean') {
62
- // We have extracted params from external URL
63
- // Now we need to map them to internal param names
64
- // Extract param names from both patterns
65
70
  const externalParamNames = extractParamNames(externalPattern);
66
71
  const internalParamNames = extractParamNames(internalPath);
67
- // Map external params to internal params (by position)
68
72
  const mappedParams = {};
69
73
  externalParamNames.forEach((externalName, index) => {
70
74
  const internalName = internalParamNames[index];
@@ -76,17 +80,13 @@ export function useRemappableParams(internalPath) {
76
80
  }
77
81
  }
78
82
  // Fallback: try to extract from internal path directly
79
- const matchFn = match(internalPath, { decode: decodeURIComponent });
80
- const result = matchFn(pathInfo.externalPath);
81
- if (result && typeof result !== 'boolean') {
82
- return result.params;
83
- }
83
+ return extractMatchParams(internalPath, pathInfo.externalPath) ?? {};
84
84
  }
85
85
  catch (error) {
86
86
  console.error('[useRemappableParams] Failed to extract params:', error);
87
87
  }
88
- // Fallback to router params
89
- return routerParams;
88
+ // Final fallback: try matching current URL against internal pattern
89
+ return extractMatchParams(internalPath, currentPath) ?? {};
90
90
  }
91
91
  /**
92
92
  * Extract parameter names from a path pattern
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Hook for setting the payment method on a checkout session
3
+ */
4
+ import { CheckoutData } from '../../core/resources/checkout';
5
+ import type { PaymentMethodName } from '../../core/resources/checkout';
6
+ export interface UseSetPaymentMethodOptions {
7
+ sessionId?: string;
8
+ checkout?: CheckoutData;
9
+ onSuccess?: (paymentMethodName: PaymentMethodName) => void;
10
+ }
11
+ export interface UseSetPaymentMethodResult {
12
+ setPaymentMethod: (paymentMethodName: PaymentMethodName) => Promise<void>;
13
+ isPending: boolean;
14
+ error: Error | null;
15
+ }
16
+ export declare function useSetPaymentMethod(options?: UseSetPaymentMethodOptions): UseSetPaymentMethodResult;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Hook for setting the payment method on a checkout session
3
+ */
4
+ import { useMutation, useQueryClient } from '@tanstack/react-query';
5
+ import { useMemo } from 'react';
6
+ import { CheckoutResource } from '../../core/resources/checkout';
7
+ import { getGlobalApiClient } from './useApiQuery';
8
+ export function useSetPaymentMethod(options = {}) {
9
+ const { sessionId, checkout, onSuccess } = options;
10
+ const queryClient = useQueryClient();
11
+ const checkoutResource = useMemo(() => new CheckoutResource(getGlobalApiClient()), []);
12
+ const effectiveSessionId = sessionId ?? checkout?.checkoutSession?.id;
13
+ const mutation = useMutation({
14
+ mutationFn: (paymentMethodName) => {
15
+ if (!effectiveSessionId) {
16
+ throw new Error('No session ID available');
17
+ }
18
+ return checkoutResource.setPaymentMethod(effectiveSessionId, paymentMethodName);
19
+ },
20
+ onSuccess: async (_, paymentMethodName) => {
21
+ await queryClient.invalidateQueries({ queryKey: ['checkout'] });
22
+ onSuccess?.(paymentMethodName);
23
+ },
24
+ });
25
+ const setPaymentMethod = async (paymentMethodName) => {
26
+ await mutation.mutateAsync(paymentMethodName);
27
+ };
28
+ return {
29
+ setPaymentMethod,
30
+ isPending: mutation.isPending,
31
+ error: mutation.error,
32
+ };
33
+ }
@@ -29,7 +29,11 @@ export function useShippingRatesQuery(options = {}) {
29
29
  // Main shipping rates query
30
30
  const { data: shippingRatesData, isLoading: isFetching, error: fetchError, refetch: refetchRates, } = useQuery({
31
31
  queryKey: ['shipping-rates', effectiveSessionId],
32
- queryFn: () => shippingRatesResource.getShippingRates(effectiveSessionId),
32
+ queryFn: () => {
33
+ if (!enabled)
34
+ return Promise.resolve({ rates: [] });
35
+ return shippingRatesResource.getShippingRates(effectiveSessionId);
36
+ },
33
37
  enabled: enabled && !!effectiveSessionId,
34
38
  staleTime: 30000, // 30 seconds
35
39
  refetchOnWindowFocus: false,
@@ -76,8 +80,10 @@ export function useShippingRatesQuery(options = {}) {
76
80
  });
77
81
  // Select rate function
78
82
  const selectRate = useCallback(async (rateId) => {
83
+ if (!enabled)
84
+ return;
79
85
  await setShippingRateMutation.mutateAsync({ rateId });
80
- }, [setShippingRateMutation]);
86
+ }, [enabled, setShippingRateMutation]);
81
87
  // Auto-select cheapest rate when:
82
88
  // 1. Rates are loaded
83
89
  // 2. No rate is currently selected in checkout
@@ -117,8 +123,10 @@ export function useShippingRatesQuery(options = {}) {
117
123
  }, [shippingRates, checkoutSelectedRateId]);
118
124
  // Refetch function
119
125
  const refetch = useCallback(async () => {
126
+ if (!enabled)
127
+ return;
120
128
  await refetchRates();
121
- }, [refetchRates]);
129
+ }, [enabled, refetchRates]);
122
130
  // Clear error function
123
131
  const clearError = useCallback(() => {
124
132
  // TanStack Query doesn't provide a direct way to clear errors
@@ -127,7 +135,7 @@ export function useShippingRatesQuery(options = {}) {
127
135
  }, [refetch]);
128
136
  // Preview shipping rates for a country without updating session
129
137
  const previewRates = useCallback(async (countryCode, stateCode) => {
130
- if (!effectiveSessionId)
138
+ if (!enabled || !effectiveSessionId)
131
139
  return [];
132
140
  try {
133
141
  setIsPreviewLoading(true);
@@ -153,7 +161,7 @@ export function useShippingRatesQuery(options = {}) {
153
161
  finally {
154
162
  setIsPreviewLoading(false);
155
163
  }
156
- }, [effectiveSessionId, shippingRatesResource]);
164
+ }, [enabled, effectiveSessionId, shippingRatesResource]);
157
165
  return {
158
166
  shippingRates,
159
167
  selectedRate,
@@ -10,18 +10,18 @@
10
10
  *
11
11
  * Usage:
12
12
  * ```tsx
13
- * const { stepConfig, paymentFlowId, staticResources } = useStepConfig();
13
+ * const { stepConfig, paymentFlowId, resources } = useStepConfig();
14
14
  *
15
15
  * // Access payment flow override
16
16
  * if (paymentFlowId) {
17
17
  * console.log('Using custom payment flow:', paymentFlowId);
18
18
  * }
19
19
  *
20
- * // Access static resources (e.g., offer ID for current A/B variant)
21
- * const offerId = staticResources?.offer;
20
+ * // Access resource bindings (e.g., offer ID for current A/B variant)
21
+ * const offerId = resources?.offer;
22
22
  * ```
23
23
  */
24
- import { PixelsConfig, RuntimeStepConfig } from '../../core/funnelClient';
24
+ import { type PaymentSetupConfig, type PixelsConfig, type RuntimeStepConfig } from '../../core/funnelClient';
25
25
  export interface UseStepConfigResult {
26
26
  /**
27
27
  * Full step configuration object
@@ -34,10 +34,11 @@ export interface UseStepConfigResult {
34
34
  */
35
35
  paymentFlowId: string | undefined;
36
36
  /**
37
- * Static resources assigned to this step/variant
38
- * For A/B tests, this contains the resources for the specific variant the user landed on
37
+ * Resource bindings for this step/variant.
39
38
  * e.g., { offer: 'offer_xxx', product: 'product_xxx' }
40
39
  */
40
+ resources: Record<string, string> | undefined;
41
+ /** @deprecated Use `resources` instead */
41
42
  staticResources: Record<string, string> | undefined;
42
43
  /**
43
44
  * Get scripts for a specific injection position
@@ -46,6 +47,22 @@ export interface UseStepConfigResult {
46
47
  */
47
48
  getScripts: (position?: 'head-start' | 'head-end' | 'body-start' | 'body-end') => RuntimeStepConfig['scripts'];
48
49
  pixels: PixelsConfig | undefined;
50
+ /**
51
+ * Payment setup configuration for this step.
52
+ * Keys: "{method}" or "{method}:{provider}" (e.g. "card", "apple_pay:stripe")
53
+ * Use helpers: getEnabledMethods(), getExpressMethods(), findMethod()
54
+ */
55
+ paymentSetupConfig: PaymentSetupConfig | undefined;
56
+ /**
57
+ * Enabled order bump offer IDs for this step.
58
+ * undefined = inherit all store bumps, string[] = only these IDs.
59
+ */
60
+ orderBumpOfferIds: string[] | undefined;
61
+ /**
62
+ * Enabled upsell offer IDs for this step.
63
+ * undefined = inherit all store upsells, string[] = only these IDs.
64
+ */
65
+ upsellOfferIds: string[] | undefined;
49
66
  }
50
67
  /**
51
68
  * Hook to access runtime step configuration injected via HTML
@@ -10,19 +10,19 @@
10
10
  *
11
11
  * Usage:
12
12
  * ```tsx
13
- * const { stepConfig, paymentFlowId, staticResources } = useStepConfig();
13
+ * const { stepConfig, paymentFlowId, resources } = useStepConfig();
14
14
  *
15
15
  * // Access payment flow override
16
16
  * if (paymentFlowId) {
17
17
  * console.log('Using custom payment flow:', paymentFlowId);
18
18
  * }
19
19
  *
20
- * // Access static resources (e.g., offer ID for current A/B variant)
21
- * const offerId = staticResources?.offer;
20
+ * // Access resource bindings (e.g., offer ID for current A/B variant)
21
+ * const offerId = resources?.offer;
22
22
  * ```
23
23
  */
24
24
  import { useMemo } from 'react';
25
- import { getAssignedPaymentFlowId, getAssignedPixels, getAssignedScripts, getAssignedStaticResources, getAssignedStepConfig, } from '../../core/funnelClient';
25
+ import { getAssignedOrderBumpOfferIds, getAssignedPaymentFlowId, getAssignedPixels, getAssignedResources, getAssignedScripts, getAssignedStepConfig, getAssignedUpsellOfferIds, } from '../../core/funnelClient';
26
26
  /**
27
27
  * Hook to access runtime step configuration injected via HTML
28
28
  *
@@ -35,7 +35,7 @@ export function useStepConfig() {
35
35
  // Read once on mount - these values don't change during the page lifecycle
36
36
  const stepConfig = useMemo(() => getAssignedStepConfig(), []);
37
37
  const paymentFlowId = useMemo(() => getAssignedPaymentFlowId(), []);
38
- const staticResources = useMemo(() => getAssignedStaticResources(), []);
38
+ const resources = useMemo(() => getAssignedResources(), []);
39
39
  const pixels = useMemo(() => getAssignedPixels(), []);
40
40
  // Create a stable reference for getScripts
41
41
  const getScripts = useMemo(() => {
@@ -43,11 +43,18 @@ export function useStepConfig() {
43
43
  return getAssignedScripts(position);
44
44
  };
45
45
  }, []);
46
+ const paymentSetupConfig = useMemo(() => stepConfig?.paymentSetupConfig, [stepConfig]);
47
+ const orderBumpOfferIds = useMemo(() => getAssignedOrderBumpOfferIds(), []);
48
+ const upsellOfferIds = useMemo(() => getAssignedUpsellOfferIds(), []);
46
49
  return {
47
50
  stepConfig,
48
51
  paymentFlowId,
49
- staticResources,
52
+ resources,
53
+ staticResources: resources,
50
54
  getScripts,
51
- pixels
55
+ pixels,
56
+ paymentSetupConfig,
57
+ orderBumpOfferIds,
58
+ upsellOfferIds,
52
59
  };
53
60
  }
@@ -85,9 +85,24 @@ export const useTranslation = (options = {}) => {
85
85
  // Even without params, we need to unescape any escaped characters
86
86
  return str.replace(/\\([{}<>])/g, '$1');
87
87
  }
88
+ // Replace escaped characters with sentinels so they are not matched
89
+ // by tag/placeholder regexes (avoids lookbehind which breaks older Safari)
90
+ const ESC_LT = '\x00ELT\x00';
91
+ const ESC_GT = '\x00EGT\x00';
92
+ const ESC_LB = '\x00ELB\x00';
93
+ const ESC_RB = '\x00ERB\x00';
94
+ const restoreSentinels = (s) => s.replace(/\x00ELT\x00/g, '<')
95
+ .replace(/\x00EGT\x00/g, '>')
96
+ .replace(/\x00ELB\x00/g, '{')
97
+ .replace(/\x00ERB\x00/g, '}');
98
+ str = str
99
+ .replace(/\\</g, ESC_LT)
100
+ .replace(/\\>/g, ESC_GT)
101
+ .replace(/\\{/g, ESC_LB)
102
+ .replace(/\\}/g, ESC_RB);
88
103
  // First, process tag-based interpolation for functions
89
- // Match tags like <i>content</i> but not \<i>content</i>
90
- const tagRegex = /(?<!\\)<(\w+)>(.*?)(?<!\\)<\/\1>/g;
104
+ // Match tags like <i>content</i> (escaped tags are already replaced with sentinels)
105
+ const tagRegex = /<(\w+)>(.*?)<\/\1>/g;
91
106
  const hasTags = tagRegex.test(str);
92
107
  if (hasTags) {
93
108
  // Reset regex lastIndex for reuse
@@ -125,13 +140,13 @@ export const useTranslation = (options = {}) => {
125
140
  // Process {key} placeholders in string parts
126
141
  let processed = Object.entries(params).reduce((acc, [key, value]) => {
127
142
  if (typeof value !== 'function') {
128
- const regex = new RegExp(`(?<!\\\\)\\{${key}\\}`, 'g');
143
+ const regex = new RegExp(`\\{${key}\\}`, 'g');
129
144
  return acc.replace(regex, String(value));
130
145
  }
131
146
  return acc;
132
147
  }, part);
133
- // Unescape escaped characters
134
- processed = processed.replace(/\\([{}<>])/g, '$1');
148
+ // Restore sentinels to literal characters
149
+ processed = restoreSentinels(processed);
135
150
  return processed;
136
151
  }
137
152
  return part;
@@ -149,13 +164,13 @@ export const useTranslation = (options = {}) => {
149
164
  // Process {key} placeholders for string/number values
150
165
  let result = Object.entries(params).reduce((acc, [key, value]) => {
151
166
  if (typeof value !== 'function') {
152
- const regex = new RegExp(`(?<!\\\\)\\{${key}\\}`, 'g');
167
+ const regex = new RegExp(`\\{${key}\\}`, 'g');
153
168
  return acc.replace(regex, String(value));
154
169
  }
155
170
  return acc;
156
171
  }, str);
157
- // Remove escape characters from escaped characters (\{ -> {, \} -> }, \< -> <, \> -> >)
158
- result = result.replace(/\\([{}<>])/g, '$1');
172
+ // Restore sentinels to literal characters (\{ -> {, \} -> }, \< -> <, \> -> >)
173
+ result = restoreSentinels(result);
159
174
  return result;
160
175
  };
161
176
  if (!text)
@@ -0,0 +1,30 @@
1
+ export interface WhopPayment {
2
+ id: string;
3
+ status: 'succeeded' | 'declined' | 'pending';
4
+ requireAction: 'none' | 'redirect' | 'error';
5
+ orderId: string;
6
+ amount: number;
7
+ currency: string;
8
+ order?: {
9
+ id: string;
10
+ amount: number;
11
+ currency: string;
12
+ };
13
+ }
14
+ export interface UseWhopPaymentPollingOptions {
15
+ apiService: {
16
+ fetch: <T>(url: string, options: any) => Promise<T>;
17
+ };
18
+ checkoutSessionId: string;
19
+ maxAttempts?: number;
20
+ intervalMs?: number;
21
+ onSuccess?: (payment: WhopPayment) => void;
22
+ onMaxAttemptsReached?: () => void;
23
+ onError?: (error: Error) => void;
24
+ }
25
+ export interface UseWhopPaymentPollingReturn {
26
+ startPolling: (receiptId: string, orderId: string) => void;
27
+ stopPolling: () => void;
28
+ isPolling: boolean;
29
+ }
30
+ export declare const useWhopPaymentPolling: ({ apiService, checkoutSessionId, maxAttempts, intervalMs, onSuccess, onMaxAttemptsReached, onError, }: UseWhopPaymentPollingOptions) => UseWhopPaymentPollingReturn;
@@ -0,0 +1,61 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+ export const useWhopPaymentPolling = ({ apiService, checkoutSessionId, maxAttempts = 60, intervalMs = 2000, onSuccess, onMaxAttemptsReached, onError, }) => {
3
+ const pollIntervalRef = useRef(null);
4
+ const attemptsRef = useRef(0);
5
+ const currentParamsRef = useRef(null);
6
+ const [isPolling, setIsPolling] = useState(false);
7
+ const stopPolling = useCallback(() => {
8
+ if (pollIntervalRef.current) {
9
+ clearInterval(pollIntervalRef.current);
10
+ pollIntervalRef.current = null;
11
+ }
12
+ setIsPolling(false);
13
+ attemptsRef.current = 0;
14
+ currentParamsRef.current = null;
15
+ }, []);
16
+ const startPolling = useCallback((receiptId, orderId) => {
17
+ stopPolling();
18
+ attemptsRef.current = 0;
19
+ currentParamsRef.current = { receiptId, orderId };
20
+ setIsPolling(true);
21
+ pollIntervalRef.current = setInterval(async () => {
22
+ if (!currentParamsRef.current)
23
+ return;
24
+ try {
25
+ attemptsRef.current++;
26
+ console.log(`Whop payment polling attempt ${attemptsRef.current}/${maxAttempts}...`);
27
+ const data = await apiService.fetch(`/api/v1/checkout-sessions/${checkoutSessionId}/whop-payment-status`, {
28
+ method: 'POST',
29
+ body: JSON.stringify(currentParamsRef.current),
30
+ headers: { 'Content-Type': 'application/json' },
31
+ });
32
+ if (data && (data.status === 'succeeded' || data.status === 'declined')) {
33
+ stopPolling();
34
+ onSuccess?.(data);
35
+ }
36
+ else if (attemptsRef.current >= maxAttempts) {
37
+ stopPolling();
38
+ onMaxAttemptsReached?.();
39
+ }
40
+ }
41
+ catch (error) {
42
+ console.warn(`Whop polling attempt ${attemptsRef.current} failed:`, error);
43
+ if (attemptsRef.current >= maxAttempts) {
44
+ stopPolling();
45
+ onError?.(error instanceof Error ? error : new Error(String(error)));
46
+ }
47
+ // Otherwise keep polling through transient errors
48
+ }
49
+ }, intervalMs);
50
+ }, [apiService, checkoutSessionId, maxAttempts, intervalMs, onSuccess, onMaxAttemptsReached, onError, stopPolling]);
51
+ useEffect(() => {
52
+ return () => {
53
+ stopPolling();
54
+ };
55
+ }, [stopPolling]);
56
+ return {
57
+ startPolling,
58
+ stopPolling,
59
+ isPolling,
60
+ };
61
+ };
@@ -6,8 +6,10 @@ export { ExpressPaymentMethodsProvider } from './providers/ExpressPaymentMethods
6
6
  export { TagadaProvider, useTagadaContext } from './providers/TagadaProvider';
7
7
  export type { DebugScript } from './providers/TagadaProvider';
8
8
  export { ApplePayButton } from './components/ApplePayButton';
9
+ export { StripeExpressButton } from './components/StripeExpressButton';
9
10
  export { GooglePayButton } from './components/GooglePayButton';
10
11
  export { PreviewModeIndicator } from './components/PreviewModeIndicator';
12
+ export { WhopCheckout } from './components/WhopCheckout';
11
13
  export { useApplePayCheckout } from './hooks/useApplePayCheckout';
12
14
  export { useAuth } from './hooks/useAuth';
13
15
  export { useCheckoutToken } from './hooks/useCheckoutToken';
@@ -40,12 +42,14 @@ export { usePostPurchasesQuery as usePostPurchases } from './hooks/usePostPurcha
40
42
  export { usePreviewOffer } from './hooks/usePreviewOffer';
41
43
  export { useProductsQuery as useProducts } from './hooks/useProductsQuery';
42
44
  export { usePromotionsQuery as usePromotions } from './hooks/usePromotionsQuery';
45
+ export { useSetPaymentMethod } from './hooks/useSetPaymentMethod';
43
46
  export { useShippingRatesQuery as useShippingRates } from './hooks/useShippingRatesQuery';
44
47
  export { useStoreConfigQuery as useStoreConfig } from './hooks/useStoreConfigQuery';
45
48
  export { useThreeds } from './hooks/useThreeds';
46
49
  export { useThreedsModal } from './hooks/useThreedsModal';
47
50
  export { useTranslation } from './hooks/useTranslation';
48
51
  export { useVipOffersQuery as useVipOffers } from './hooks/useVipOffersQuery';
52
+ export { useWhopPaymentPolling } from './hooks/useWhopPaymentPolling';
49
53
  export { useFunnel } from './hooks/useFunnel';
50
54
  export { useStepConfig } from './hooks/useStepConfig';
51
55
  export { useFunnel as useFunnelLegacy, useSimpleFunnel } from './hooks/useFunnelLegacy';
@@ -61,19 +65,24 @@ export type { ExpressPaymentMethodsContextType, ExpressPaymentMethodsProviderPro
61
65
  export type { UseGooglePayCheckoutOptions } from './hooks/useGooglePayCheckout';
62
66
  export type { UseLoginOptions, UseLoginResult } from './hooks/useLogin';
63
67
  export type { ApplePayButtonProps } from './components/ApplePayButton';
68
+ export type { StripeExpressButtonProps } from './components/StripeExpressButton';
64
69
  export type { GooglePayButtonProps } from './components/GooglePayButton';
65
70
  export type { PreviewModeIndicatorProps } from './components/PreviewModeIndicator';
71
+ export type { WhopCheckoutHandle, WhopCheckoutProps } from './components/WhopCheckout';
72
+ export type { WhopPayment, UseWhopPaymentPollingOptions, UseWhopPaymentPollingReturn } from './hooks/useWhopPaymentPolling';
66
73
  export type { GeoLocationData, UseGeoLocationOptions, UseGeoLocationReturn } from './hooks/useGeoLocation';
67
74
  export type { ExtractedAddress, GooglePlaceDetails, GooglePrediction, UseGoogleAutocompleteOptions, UseGoogleAutocompleteResult } from './hooks/useGoogleAutocomplete';
68
75
  export type { ISOCountry, ISORegion, UseISODataResult } from './hooks/useISOData';
69
76
  export type { UsePluginConfigOptions, UsePluginConfigResult } from './hooks/usePluginConfig';
70
77
  export type { TranslateFunction, UseTranslationOptions, UseTranslationResult } from './hooks/useTranslation';
71
78
  export { FunnelActionType } from '../core/resources/funnel';
72
- export type { UseCheckoutQueryOptions as UseCheckoutOptions, UseCheckoutQueryResult as UseCheckoutResult } from './hooks/useCheckoutQuery';
79
+ export type { CheckoutMessages, UseCheckoutQueryOptions as UseCheckoutOptions, UseCheckoutQueryResult as UseCheckoutResult } from './hooks/useCheckoutQuery';
73
80
  export type { UseDiscountsQueryOptions as UseDiscountsOptions, UseDiscountsQueryResult as UseDiscountsResult } from './hooks/useDiscountsQuery';
74
81
  export type { FunnelAction, FunnelNavigationAction, FunnelNavigationResult, SimpleFunnelContext } from '../core/resources/funnel';
75
82
  export type { FunnelContextValue, StepConfigValue } from './hooks/useFunnel';
76
83
  export type { UseStepConfigResult } from './hooks/useStepConfig';
84
+ export type { RuntimeStepConfig, PaymentMethodConfig, PaymentSetupConfig, PaymentSetupMethod } from '../core/funnelClient';
85
+ export { getEnabledMethods, getExpressMethods, getExpressMethodsByProcessor, findMethod, isMethodEnabled } from '../core/funnelClient';
77
86
  export type { UseFunnelOptions as UseFunnelLegacyOptions, UseFunnelResult as UseFunnelLegacyResult } from './hooks/useFunnelLegacy';
78
87
  export type { AvailableVariant, LineItemSelection, OfferLineItem, OfferPreviewSummary, UseOfferQueryOptions as UseOfferOptions, UseOfferQueryResult as UseOfferResult } from './hooks/useOfferQuery';
79
88
  export type { UseOrderBumpQueryOptions as UseOrderBumpOptions, UseOrderBumpQueryResult as UseOrderBumpResult } from './hooks/useOrderBumpQuery';
@@ -84,9 +93,14 @@ export type { UsePostPurchasesQueryOptions as UsePostPurchasesOptions, UsePostPu
84
93
  export type { PreviewOfferSummary, UsePreviewOfferOptions, UsePreviewOfferResult } from './hooks/usePreviewOffer';
85
94
  export type { UseProductsQueryOptions as UseProductsOptions, UseProductsQueryResult as UseProductsResult } from './hooks/useProductsQuery';
86
95
  export type { UsePromotionsQueryOptions as UsePromotionsOptions, UsePromotionsQueryResult as UsePromotionsResult } from './hooks/usePromotionsQuery';
96
+ export type { UseSetPaymentMethodOptions, UseSetPaymentMethodResult } from './hooks/useSetPaymentMethod';
87
97
  export type { UseShippingRatesQueryOptions as UseShippingRatesOptions, UseShippingRatesQueryResult as UseShippingRatesResult } from './hooks/useShippingRatesQuery';
88
98
  export type { UseStoreConfigQueryOptions as UseStoreConfigOptions, UseStoreConfigQueryResult as UseStoreConfigResult } from './hooks/useStoreConfigQuery';
89
99
  export type { PaymentInstrument, ThreedsChallenge, ThreedsHook, ThreedsOptions, ThreedsProvider, ThreedsSession } from './hooks/useThreeds';
90
100
  export type { UseVipOffersQueryOptions as UseVipOffersOptions, UseVipOffersQueryResult as UseVipOffersResult } from './hooks/useVipOffersQuery';
91
101
  export { formatMoney } from '../../react/utils/money';
102
+ export { TagadaError, TagadaApiError, TagadaNetworkError, TagadaAuthError, TagadaValidationError, TagadaCircuitBreakerError, TagadaErrorCode, } from '../core/errors';
103
+ export type { TagadaErrorOptions, TagadaErrorCodeValue } from '../core/errors';
104
+ export type { PaymentMethodName } from '../core/resources/checkout';
105
+ export type { Order, OrderItem as CoreOrderItem, OrderAddress, OrderSummary, OrderWithRelations, PaymentSummary, PromotionSummary, Subscription, DeviceInfo, PromotionCode, Customer, Store, PickupPoint, } from '../core/types';
92
106
  export type OrderItem = import('../core/utils/order').OrderLineItem;
@@ -7,8 +7,10 @@ export { ExpressPaymentMethodsProvider } from './providers/ExpressPaymentMethods
7
7
  export { TagadaProvider, useTagadaContext } from './providers/TagadaProvider';
8
8
  // Components
9
9
  export { ApplePayButton } from './components/ApplePayButton';
10
+ export { StripeExpressButton } from './components/StripeExpressButton';
10
11
  export { GooglePayButton } from './components/GooglePayButton';
11
12
  export { PreviewModeIndicator } from './components/PreviewModeIndicator';
13
+ export { WhopCheckout } from './components/WhopCheckout';
12
14
  // Hooks
13
15
  export { useApplePayCheckout } from './hooks/useApplePayCheckout';
14
16
  export { useAuth } from './hooks/useAuth';
@@ -42,12 +44,14 @@ export { usePostPurchasesQuery as usePostPurchases } from './hooks/usePostPurcha
42
44
  export { usePreviewOffer } from './hooks/usePreviewOffer';
43
45
  export { useProductsQuery as useProducts } from './hooks/useProductsQuery';
44
46
  export { usePromotionsQuery as usePromotions } from './hooks/usePromotionsQuery';
47
+ export { useSetPaymentMethod } from './hooks/useSetPaymentMethod';
45
48
  export { useShippingRatesQuery as useShippingRates } from './hooks/useShippingRatesQuery';
46
49
  export { useStoreConfigQuery as useStoreConfig } from './hooks/useStoreConfigQuery';
47
50
  export { useThreeds } from './hooks/useThreeds';
48
51
  export { useThreedsModal } from './hooks/useThreedsModal';
49
52
  export { useTranslation } from './hooks/useTranslation';
50
53
  export { useVipOffersQuery as useVipOffers } from './hooks/useVipOffersQuery';
54
+ export { useWhopPaymentPolling } from './hooks/useWhopPaymentPolling';
51
55
  // Funnel hooks
52
56
  export { useFunnel } from './hooks/useFunnel';
53
57
  // Step config hook (access runtime configuration from HTML injection)
@@ -56,5 +60,8 @@ export { useStepConfig } from './hooks/useStepConfig';
56
60
  export { useFunnel as useFunnelLegacy, useSimpleFunnel } from './hooks/useFunnelLegacy';
57
61
  // TanStack Query types
58
62
  export { FunnelActionType } from '../core/resources/funnel';
63
+ export { getEnabledMethods, getExpressMethods, getExpressMethodsByProcessor, findMethod, isMethodEnabled } from '../core/funnelClient';
59
64
  // Re-export utilities from main react
60
65
  export { formatMoney } from '../../react/utils/money';
66
+ // Error types
67
+ export { TagadaError, TagadaApiError, TagadaNetworkError, TagadaAuthError, TagadaValidationError, TagadaCircuitBreakerError, TagadaErrorCode, } from '../core/errors';