@tagadapay/plugin-sdk 3.1.8 → 3.1.10

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 (50) hide show
  1. package/README.md +1129 -1129
  2. package/build-cdn.js +223 -113
  3. package/dist/external-tracker.js +135 -81
  4. package/dist/external-tracker.min.js +2 -2
  5. package/dist/external-tracker.min.js.map +4 -4
  6. package/dist/react/providers/TagadaProvider.js +5 -5
  7. package/dist/tagada-sdk.js +10164 -0
  8. package/dist/tagada-sdk.min.js +45 -0
  9. package/dist/tagada-sdk.min.js.map +7 -0
  10. package/dist/v2/core/funnelClient.d.ts +91 -4
  11. package/dist/v2/core/funnelClient.js +42 -3
  12. package/dist/v2/core/resources/funnel.d.ts +10 -0
  13. package/dist/v2/core/resources/payments.d.ts +21 -1
  14. package/dist/v2/core/resources/payments.js +34 -0
  15. package/dist/v2/core/utils/currency.d.ts +14 -0
  16. package/dist/v2/core/utils/currency.js +40 -0
  17. package/dist/v2/core/utils/index.d.ts +1 -0
  18. package/dist/v2/core/utils/index.js +2 -0
  19. package/dist/v2/core/utils/pluginConfig.d.ts +8 -0
  20. package/dist/v2/core/utils/pluginConfig.js +28 -0
  21. package/dist/v2/core/utils/previewMode.d.ts +4 -0
  22. package/dist/v2/core/utils/previewMode.js +28 -0
  23. package/dist/v2/core/utils/previewModeIndicator.js +101 -101
  24. package/dist/v2/index.d.ts +7 -6
  25. package/dist/v2/index.js +6 -6
  26. package/dist/v2/react/components/ApplePayButton.d.ts +1 -2
  27. package/dist/v2/react/components/ApplePayButton.js +57 -58
  28. package/dist/v2/react/components/FunnelScriptInjector.js +161 -172
  29. package/dist/v2/react/components/GooglePayButton.d.ts +2 -0
  30. package/dist/v2/react/components/GooglePayButton.js +80 -64
  31. package/dist/v2/react/hooks/useFunnel.d.ts +8 -2
  32. package/dist/v2/react/hooks/useFunnel.js +2 -2
  33. package/dist/v2/react/hooks/useGoogleAutocomplete.d.ts +10 -0
  34. package/dist/v2/react/hooks/useGoogleAutocomplete.js +48 -0
  35. package/dist/v2/react/hooks/useGooglePayCheckout.d.ts +21 -0
  36. package/dist/v2/react/hooks/useGooglePayCheckout.js +198 -0
  37. package/dist/v2/react/hooks/usePaymentPolling.d.ts +7 -1
  38. package/dist/v2/react/hooks/usePaymentQuery.d.ts +2 -0
  39. package/dist/v2/react/hooks/usePaymentQuery.js +435 -8
  40. package/dist/v2/react/hooks/usePixelTracking.d.ts +56 -0
  41. package/dist/v2/react/hooks/usePixelTracking.js +508 -0
  42. package/dist/v2/react/hooks/useStepConfig.d.ts +8 -6
  43. package/dist/v2/react/hooks/useStepConfig.js +3 -2
  44. package/dist/v2/react/index.d.ts +6 -2
  45. package/dist/v2/react/index.js +3 -1
  46. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.d.ts +1 -0
  47. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +33 -13
  48. package/dist/v2/react/providers/TagadaProvider.js +22 -21
  49. package/dist/v2/standalone/index.js +1 -1
  50. package/package.json +112 -112
@@ -19,8 +19,8 @@
19
19
  * const paymentFlowId = stepConfig.paymentFlowId;
20
20
  * ```
21
21
  */
22
+ import { FunnelState, GTMTrackingConfig, MetaConversionTrackingConfig, PixelTrackingConfig, RuntimeStepConfig, SnapchatTrackingConfig, TrackingProvider } from '../../core/funnelClient';
22
23
  import { FunnelAction, FunnelNavigationResult, SimpleFunnelContext } from '../../core/resources/funnel';
23
- import { FunnelState, RuntimeStepConfig } from '../../core/funnelClient';
24
24
  /**
25
25
  * Step configuration from HTML injection (for current step/variant)
26
26
  */
@@ -48,7 +48,13 @@ export interface StepConfigValue {
48
48
  /**
49
49
  * Pixel tracking configuration
50
50
  */
51
- pixels: Record<string, string> | undefined;
51
+ pixels?: {
52
+ [TrackingProvider.FACEBOOK]?: PixelTrackingConfig[];
53
+ [TrackingProvider.TIKTOK]?: PixelTrackingConfig[];
54
+ [TrackingProvider.SNAPCHAT]?: SnapchatTrackingConfig[];
55
+ [TrackingProvider.META_CONVERSION]?: MetaConversionTrackingConfig[];
56
+ [TrackingProvider.GTM]?: GTMTrackingConfig[];
57
+ };
52
58
  }
53
59
  export interface FunnelContextValue extends FunnelState {
54
60
  currentStep: {
@@ -20,8 +20,8 @@
20
20
  * ```
21
21
  */
22
22
  import { useMemo } from 'react';
23
+ import { TrackingProvider, getAssignedPaymentFlowId, getAssignedPixels, getAssignedScripts, getAssignedStaticResources, getAssignedStepConfig } from '../../core/funnelClient';
23
24
  import { useTagadaContext } from '../providers/TagadaProvider';
24
- import { getAssignedStepConfig, getAssignedPaymentFlowId, getAssignedStaticResources, getAssignedScripts, } from '../../core/funnelClient';
25
25
  /**
26
26
  * Hook to access funnel state and methods
27
27
  *
@@ -39,8 +39,8 @@ export function useFunnel() {
39
39
  raw,
40
40
  paymentFlowId: getAssignedPaymentFlowId(),
41
41
  staticResources: getAssignedStaticResources(),
42
+ pixels: getAssignedPixels(),
42
43
  getScripts: (position) => getAssignedScripts(position),
43
- pixels: raw?.pixels,
44
44
  };
45
45
  }, []);
46
46
  return {
@@ -43,6 +43,7 @@ export interface GooglePlaceDetails {
43
43
  };
44
44
  }
45
45
  export interface ExtractedAddress {
46
+ subpremise: string;
46
47
  streetNumber: string;
47
48
  route: string;
48
49
  locality: string;
@@ -52,6 +53,14 @@ export interface ExtractedAddress {
52
53
  administrativeAreaLevel2Long: string;
53
54
  country: string;
54
55
  postalCode: string;
56
+ fullStreetAddress: string;
57
+ }
58
+ export interface FormattedAddress {
59
+ address1: string;
60
+ city: string;
61
+ country: string;
62
+ state: string;
63
+ postal: string;
55
64
  }
56
65
  export interface UseGoogleAutocompleteOptions {
57
66
  apiKey: string;
@@ -67,6 +76,7 @@ export interface UseGoogleAutocompleteResult {
67
76
  searchPlaces: (input: string, countryRestriction?: string) => void;
68
77
  getPlaceDetails: (placeId: string) => Promise<GooglePlaceDetails | null>;
69
78
  extractAddressComponents: (place: GooglePlaceDetails) => ExtractedAddress;
79
+ extractFormattedAddress: (place: GooglePlaceDetails) => FormattedAddress;
70
80
  clearPredictions: () => void;
71
81
  }
72
82
  /**
@@ -153,6 +153,7 @@ export function useGoogleAutocomplete(options) {
153
153
  // Extract structured address components from Google place
154
154
  const extractAddressComponents = useCallback((place) => {
155
155
  const extracted = {
156
+ subpremise: '',
156
157
  streetNumber: '',
157
158
  route: '',
158
159
  locality: '',
@@ -162,9 +163,14 @@ export function useGoogleAutocomplete(options) {
162
163
  administrativeAreaLevel2Long: '',
163
164
  country: '',
164
165
  postalCode: '',
166
+ fullStreetAddress: '',
165
167
  };
166
168
  place.address_components?.forEach((component) => {
167
169
  const types = component.types;
170
+ if (types.includes('subpremise')) {
171
+ // Unit/Apartment number (e.g., "Unit 711", "711", "Apt 5B")
172
+ extracted.subpremise = component.long_name;
173
+ }
168
174
  if (types.includes('street_number')) {
169
175
  extracted.streetNumber = component.long_name;
170
176
  }
@@ -202,8 +208,49 @@ export function useGoogleAutocomplete(options) {
202
208
  extracted.administrativeAreaLevel1 = extracted.administrativeAreaLevel2Long || extracted.administrativeAreaLevel2;
203
209
  extracted.administrativeAreaLevel1Long = extracted.administrativeAreaLevel2Long;
204
210
  }
211
+ // Construct full street address
212
+ // Handle different formats:
213
+ // - "711/3 Network Place" (Australian unit format)
214
+ // - "3 Network Place" (house number only)
215
+ // - "Unit 711, 3 Network Place" (alternative format)
216
+ const streetParts = [];
217
+ if (extracted.subpremise) {
218
+ // Check if subpremise already contains formatting (e.g., "Unit 711")
219
+ const normalizedSubpremise = extracted.subpremise.trim();
220
+ streetParts.push(normalizedSubpremise);
221
+ }
222
+ if (extracted.streetNumber) {
223
+ streetParts.push(extracted.streetNumber);
224
+ }
225
+ if (extracted.route) {
226
+ streetParts.push(extracted.route);
227
+ }
228
+ // For Australian format, if we have both subpremise and streetNumber,
229
+ // format as "subpremise/streetNumber route" (e.g., "711/3 Network Place")
230
+ if (extracted.subpremise && extracted.streetNumber && extracted.route) {
231
+ // Remove "Unit", "Apt", etc. prefixes for cleaner format
232
+ const cleanSubpremise = extracted.subpremise
233
+ .replace(/^(Unit|Apt|Apartment|Suite|#)\s*/i, '')
234
+ .trim();
235
+ extracted.fullStreetAddress = `${cleanSubpremise}/${extracted.streetNumber} ${extracted.route}`;
236
+ }
237
+ else {
238
+ // Standard format: join all parts with spaces
239
+ extracted.fullStreetAddress = streetParts.join(' ');
240
+ }
205
241
  return extracted;
206
242
  }, []);
243
+ // Extract address in the format expected by shipping/billing address forms
244
+ const extractFormattedAddress = useCallback((place) => {
245
+ const extracted = extractAddressComponents(place);
246
+ return {
247
+ address1: extracted.fullStreetAddress || '',
248
+ city: extracted.locality || '',
249
+ country: extracted.country || '',
250
+ state: extracted.administrativeAreaLevel1 || '',
251
+ postal: extracted.postalCode || '',
252
+ };
253
+ }, [extractAddressComponents]);
207
254
  // Clear predictions
208
255
  const clearPredictions = useCallback(() => {
209
256
  setPredictions([]);
@@ -215,6 +262,7 @@ export function useGoogleAutocomplete(options) {
215
262
  searchPlaces,
216
263
  getPlaceDetails,
217
264
  extractAddressComponents,
265
+ extractFormattedAddress,
218
266
  clearPredictions,
219
267
  };
220
268
  }
@@ -0,0 +1,21 @@
1
+ import { CheckoutData } from '../../core/resources/checkout';
2
+ export interface UseGooglePayCheckoutOptions {
3
+ checkout: CheckoutData | undefined;
4
+ onSuccess?: (result: {
5
+ payment: any;
6
+ order?: any;
7
+ }) => void;
8
+ onError?: (error: string) => void;
9
+ onCancel?: () => void;
10
+ googlePayConfig?: {
11
+ merchantId?: string;
12
+ merchantName?: string;
13
+ sandboxed?: boolean;
14
+ };
15
+ }
16
+ export declare function useGooglePayCheckout({ checkout, onSuccess, onError, onCancel, googlePayConfig, }: UseGooglePayCheckoutOptions): {
17
+ handleGooglePayClick: () => void;
18
+ processingPayment: boolean;
19
+ error: string | null;
20
+ clearError: () => void;
21
+ };
@@ -0,0 +1,198 @@
1
+ import { useState, useCallback, useMemo, useEffect } from 'react';
2
+ import { usePaymentQuery } from './usePaymentQuery';
3
+ import { getBasisTheoryKeys } from '../../../config/basisTheory';
4
+ import { useTagadaContext } from '../providers/TagadaProvider';
5
+ export function useGooglePayCheckout({ checkout, onSuccess, onError, onCancel, googlePayConfig, }) {
6
+ const [processingPayment, setProcessingPayment] = useState(false);
7
+ const [error, setError] = useState(null);
8
+ const [paymentMethodConfig, setPaymentMethodConfig] = useState(null);
9
+ const { processGooglePayPayment } = usePaymentQuery();
10
+ const { apiService } = useTagadaContext();
11
+ // Determine Basis Theory keys based on sandboxed flag from payment method config
12
+ // When sandboxed is true (even on production hostname), use test keys
13
+ const { basistheoryPublicKey, basistheoryTenantId } = useMemo(() => {
14
+ const config = googlePayConfig || paymentMethodConfig;
15
+ // Use test keys if sandboxed is true, production keys only when explicitly sandboxed: false
16
+ const useProductionKeys = config?.sandboxed === false;
17
+ const keys = getBasisTheoryKeys(useProductionKeys);
18
+ console.log('[useGooglePayCheckout] Using Basis Theory keys:', {
19
+ sandboxed: config?.sandboxed,
20
+ useProductionKeys,
21
+ tenantId: keys.tenantId,
22
+ });
23
+ return {
24
+ basistheoryPublicKey: keys.apiKey,
25
+ basistheoryTenantId: keys.tenantId,
26
+ };
27
+ }, [googlePayConfig, paymentMethodConfig]);
28
+ // Fetch Google Pay payment method configuration
29
+ useEffect(() => {
30
+ if (!checkout?.checkoutSession?.id || googlePayConfig)
31
+ return;
32
+ const fetchPaymentMethods = async () => {
33
+ try {
34
+ const data = await apiService.fetch('/api/v1/payment-methods', {
35
+ method: 'GET',
36
+ params: { checkoutSessionId: checkout.checkoutSession.id },
37
+ });
38
+ const googlePayMethod = data?.find((method) => method.type === 'google_pay');
39
+ if (googlePayMethod) {
40
+ setPaymentMethodConfig(googlePayMethod.metadata);
41
+ }
42
+ console.log('googlePayMethod', googlePayMethod);
43
+ }
44
+ catch (err) {
45
+ console.error('Failed to fetch payment methods:', err);
46
+ }
47
+ };
48
+ void fetchPaymentMethods();
49
+ }, [checkout?.checkoutSession?.id, googlePayConfig, apiService]);
50
+ // Tokenize Google Pay payment using Basis Theory
51
+ const tokenizeGooglePay = useCallback(async (paymentData) => {
52
+ try {
53
+ // Extract the Google Pay token from the payment data
54
+ const googlePayTokenString = paymentData.paymentMethodData.tokenizationData.token;
55
+ const googlePayToken = JSON.parse(googlePayTokenString);
56
+ const response = await fetch('https://api.basistheory.com/google-pay', {
57
+ method: 'POST',
58
+ headers: {
59
+ 'Content-Type': 'application/json',
60
+ 'BT-API-KEY': basistheoryPublicKey,
61
+ },
62
+ body: JSON.stringify({
63
+ google_payment_data: googlePayToken,
64
+ }),
65
+ });
66
+ if (!response.ok) {
67
+ throw new Error(`HTTP error! Status: ${response.status}`);
68
+ }
69
+ const result = await response.json();
70
+ return result?.token_intent || result?.google_pay;
71
+ }
72
+ catch (err) {
73
+ console.error('Tokenization failed:', err);
74
+ throw err;
75
+ }
76
+ }, [basistheoryPublicKey]);
77
+ // Handle Google Pay payment click
78
+ const handleGooglePayClick = useCallback(() => {
79
+ // Don't proceed if checkout is not available
80
+ if (!checkout) {
81
+ console.error('Checkout data not available');
82
+ if (onError) {
83
+ onError('Checkout not ready');
84
+ }
85
+ return;
86
+ }
87
+ setProcessingPayment(true);
88
+ // Create Google Pay payment request (simpler than express - no shipping)
89
+ const paymentRequest = {
90
+ apiVersion: 2,
91
+ apiVersionMinor: 0,
92
+ allowedPaymentMethods: [
93
+ {
94
+ type: 'CARD',
95
+ parameters: {
96
+ allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'],
97
+ allowedCardNetworks: ['AMEX', 'DISCOVER', 'INTERAC', 'JCB', 'MASTERCARD', 'VISA'],
98
+ billingAddressRequired: false,
99
+ },
100
+ tokenizationSpecification: {
101
+ type: 'PAYMENT_GATEWAY',
102
+ parameters: {
103
+ gateway: 'basistheory',
104
+ gatewayMerchantId: basistheoryTenantId,
105
+ },
106
+ },
107
+ },
108
+ ],
109
+ transactionInfo: {
110
+ totalPriceStatus: 'FINAL',
111
+ totalPrice: (checkout.summary.totalAdjustedAmount / 100).toFixed(2),
112
+ currencyCode: checkout.summary.currency,
113
+ },
114
+ merchantInfo: {
115
+ merchantName: googlePayConfig?.merchantName || paymentMethodConfig?.merchantName || checkout.checkoutSession?.store?.name || 'Store',
116
+ merchantId: (() => {
117
+ const config = googlePayConfig || paymentMethodConfig;
118
+ if (!config)
119
+ return '12345678901234567890'; // Fallback to test
120
+ return config.sandboxed ? '12345678901234567890' : config.merchantId;
121
+ })(),
122
+ },
123
+ };
124
+ // Determine environment from configuration
125
+ const config = googlePayConfig || paymentMethodConfig;
126
+ const environment = config?.sandboxed !== false ? 'TEST' : 'PRODUCTION';
127
+ // Create Google Pay client
128
+ const paymentsClient = new google.payments.api.PaymentsClient({ environment });
129
+ // Load payment data
130
+ paymentsClient
131
+ .loadPaymentData(paymentRequest)
132
+ .then(async (paymentData) => {
133
+ try {
134
+ // Tokenize payment
135
+ const googlePayToken = await tokenizeGooglePay(paymentData);
136
+ // Process payment via SDK hook
137
+ const result = await processGooglePayPayment(checkout.checkoutSession.id, googlePayToken, {
138
+ onPaymentSuccess: (response) => {
139
+ // Keep processing state true during navigation
140
+ },
141
+ onPaymentFailed: (err) => {
142
+ setProcessingPayment(false);
143
+ setError(err.message);
144
+ if (onError) {
145
+ onError(err.message);
146
+ }
147
+ },
148
+ });
149
+ // Call success callback
150
+ if (onSuccess) {
151
+ onSuccess(result);
152
+ }
153
+ }
154
+ catch (error) {
155
+ console.error('Payment failed:', error);
156
+ setProcessingPayment(false);
157
+ const errorMsg = error instanceof Error ? error.message : 'Payment failed';
158
+ setError(errorMsg);
159
+ if (onError) {
160
+ onError(errorMsg);
161
+ }
162
+ }
163
+ })
164
+ .catch((error) => {
165
+ console.error('Google Pay error:', error);
166
+ setProcessingPayment(false);
167
+ // Check if user canceled
168
+ if (error.statusCode === 'CANCELED') {
169
+ if (onCancel) {
170
+ onCancel();
171
+ }
172
+ return;
173
+ }
174
+ const errorMsg = error.statusMessage || 'Google Pay failed';
175
+ setError(errorMsg);
176
+ if (onError) {
177
+ onError(errorMsg);
178
+ }
179
+ });
180
+ }, [
181
+ checkout,
182
+ basistheoryTenantId,
183
+ tokenizeGooglePay,
184
+ processGooglePayPayment,
185
+ onSuccess,
186
+ onError,
187
+ onCancel,
188
+ ]);
189
+ const clearError = useCallback(() => {
190
+ setError(null);
191
+ }, []);
192
+ return {
193
+ handleGooglePayClick,
194
+ processingPayment,
195
+ error,
196
+ clearError,
197
+ };
198
+ }
@@ -4,7 +4,7 @@ export interface Payment {
4
4
  subStatus: string;
5
5
  requireAction: 'none' | 'redirect' | 'error' | 'radar';
6
6
  requireActionData?: {
7
- type: 'redirect' | 'threeds_auth' | 'processor_auth' | 'error' | 'stripe_radar' | 'finix_radar';
7
+ type: 'redirect' | 'threeds_auth' | 'processor_auth' | 'error' | 'stripe_radar' | 'finix_radar' | 'radar';
8
8
  url?: string;
9
9
  processed: boolean;
10
10
  processorId?: string;
@@ -26,12 +26,18 @@ export interface Payment {
26
26
  orderId?: string;
27
27
  publishableKey?: string;
28
28
  };
29
+ provider?: string;
30
+ isTest?: boolean;
29
31
  };
30
32
  redirectUrl?: string;
31
33
  resumeToken?: string;
32
34
  message?: string;
33
35
  errorCode?: string;
34
36
  };
37
+ order?: {
38
+ id: string;
39
+ checkoutSessionId: string;
40
+ };
35
41
  }
36
42
  export interface PollingOptions {
37
43
  onRequireAction?: (payment: Payment, stop: () => void) => void;
@@ -37,9 +37,11 @@ export interface UsePaymentOptions {
37
37
  export interface PaymentHook {
38
38
  processCardPayment: (checkoutSessionId: string, cardData: CardPaymentMethod, options?: PaymentOptions) => Promise<PaymentResponse>;
39
39
  processApplePayPayment: (checkoutSessionId: string, applePayToken: ApplePayToken, options?: PaymentOptions) => Promise<PaymentResponse>;
40
+ processGooglePayPayment: (checkoutSessionId: string, googlePayToken: any, options?: PaymentOptions) => Promise<PaymentResponse>;
40
41
  processPaymentWithInstrument: (checkoutSessionId: string, paymentInstrumentId: string, options?: PaymentOptions) => Promise<PaymentResponse>;
41
42
  createCardPaymentInstrument: (cardData: CardPaymentMethod) => Promise<PaymentInstrumentResponse>;
42
43
  createApplePayPaymentInstrument: (applePayToken: ApplePayToken) => Promise<PaymentInstrumentResponse>;
44
+ createGooglePayPaymentInstrument: (googlePayToken: any) => Promise<PaymentInstrumentResponse>;
43
45
  getCardPaymentInstruments: () => Promise<PaymentInstrumentCustomerResponse>;
44
46
  isLoading: boolean;
45
47
  error: string | null;