@tagadapay/plugin-sdk 1.0.8 → 1.0.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.
@@ -18,6 +18,12 @@ export declare const PAYMENT_CONFIGS: Record<string, PaymentConfig>;
18
18
  export declare function getPaymentConfig(environment?: string): PaymentConfig;
19
19
  /**
20
20
  * Get BasisTheory API key for current environment
21
- * Falls back to environment variable if available
21
+ *
22
+ * Behavior:
23
+ * - LOCAL/DEVELOPMENT: Always uses embedded test key (key_test_us_pub_VExdfbFQARn821iqP8zNaq)
24
+ * Environment variables are ignored to prevent accidental production key usage
25
+ *
26
+ * - PRODUCTION: Uses environment variable if set, falls back to embedded production key
27
+ * This allows deployment-time configuration while having safe defaults
22
28
  */
23
29
  export declare function getBasisTheoryApiKey(environment?: string): string;
@@ -15,14 +15,22 @@ export const PAYMENT_CONFIGS = {
15
15
  },
16
16
  development: {
17
17
  basisTheory: {
18
- // TODO: Replace with actual development BasisTheory public API key
18
+ // Embedded test API key for development
19
19
  publicApiKey: 'key_test_us_pub_VExdfbFQARn821iqP8zNaq',
20
20
  environment: 'sandbox',
21
21
  },
22
22
  },
23
23
  local: {
24
24
  basisTheory: {
25
- // Development/testing API key (sandbox)
25
+ // Embedded test API key for local development
26
+ publicApiKey: 'key_test_us_pub_VExdfbFQARn821iqP8zNaq',
27
+ environment: 'sandbox',
28
+ },
29
+ },
30
+ // Default fallback configuration
31
+ default: {
32
+ basisTheory: {
33
+ // Embedded test API key as fallback
26
34
  publicApiKey: 'key_test_us_pub_VExdfbFQARn821iqP8zNaq',
27
35
  environment: 'sandbox',
28
36
  },
@@ -32,21 +40,30 @@ export const PAYMENT_CONFIGS = {
32
40
  * Get payment configuration based on environment
33
41
  */
34
42
  export function getPaymentConfig(environment = 'local') {
35
- return PAYMENT_CONFIGS[environment] || PAYMENT_CONFIGS.local;
43
+ return PAYMENT_CONFIGS[environment] || PAYMENT_CONFIGS.default || PAYMENT_CONFIGS.local;
36
44
  }
37
45
  /**
38
46
  * Get BasisTheory API key for current environment
39
- * Falls back to environment variable if available
47
+ *
48
+ * Behavior:
49
+ * - LOCAL/DEVELOPMENT: Always uses embedded test key (key_test_us_pub_VExdfbFQARn821iqP8zNaq)
50
+ * Environment variables are ignored to prevent accidental production key usage
51
+ *
52
+ * - PRODUCTION: Uses environment variable if set, falls back to embedded production key
53
+ * This allows deployment-time configuration while having safe defaults
40
54
  */
41
55
  export function getBasisTheoryApiKey(environment = 'local') {
42
- // Check environment variable first (allows override)
43
- if (typeof process !== 'undefined' && process.env?.NEXT_PUBLIC_BASIS_THEORY_PUBLIC_API_KEY) {
44
- return process.env.NEXT_PUBLIC_BASIS_THEORY_PUBLIC_API_KEY;
45
- }
46
- if (typeof process !== 'undefined' && process.env?.VITE_BASIS_THEORY_PUBLIC_API_KEY) {
47
- return process.env.VITE_BASIS_THEORY_PUBLIC_API_KEY;
56
+ // For production environment, allow environment variable override
57
+ if (environment === 'production') {
58
+ if (typeof process !== 'undefined' && process.env?.NEXT_PUBLIC_BASIS_THEORY_PUBLIC_API_KEY) {
59
+ return process.env.NEXT_PUBLIC_BASIS_THEORY_PUBLIC_API_KEY;
60
+ }
61
+ if (typeof process !== 'undefined' && process.env?.VITE_BASIS_THEORY_PUBLIC_API_KEY) {
62
+ return process.env.VITE_BASIS_THEORY_PUBLIC_API_KEY;
63
+ }
48
64
  }
49
- // Fall back to embedded configuration
65
+ // For local/development, always use embedded test key to prevent accidental production key usage
66
+ // Fall back to embedded configuration based on environment
50
67
  const config = getPaymentConfig(environment);
51
68
  return config.basisTheory.publicApiKey;
52
69
  }
@@ -2,6 +2,7 @@ export interface CheckoutLineItem {
2
2
  externalProductId?: string | null;
3
3
  externalVariantId?: string | null;
4
4
  variantId?: string | null;
5
+ priceId?: string | null;
5
6
  quantity: number;
6
7
  }
7
8
  export interface CheckoutInitParams {
@@ -229,6 +230,21 @@ export interface UseCheckoutResult {
229
230
  success: boolean;
230
231
  error?: any;
231
232
  }>;
233
+ addLineItems: (lineItems: CheckoutLineItem[]) => Promise<{
234
+ success: boolean;
235
+ error?: any;
236
+ }>;
237
+ removeLineItems: (lineItems: {
238
+ variantId: string;
239
+ quantity?: number;
240
+ }[]) => Promise<{
241
+ success: boolean;
242
+ error?: any;
243
+ }>;
244
+ setItemQuantity: (variantId: string, quantity: number, priceId?: string) => Promise<{
245
+ success: boolean;
246
+ error?: any;
247
+ }>;
232
248
  toggleOrderBump: (orderBumpOfferId: string, selected: boolean) => Promise<{
233
249
  success: boolean;
234
250
  error?: any;
@@ -213,6 +213,63 @@ export function useCheckout(options = {}) {
213
213
  throw error;
214
214
  }
215
215
  }, [apiService, checkout?.checkoutSession.id, refresh]);
216
+ const addLineItems = useCallback(async (lineItems) => {
217
+ if (!checkout?.checkoutSession.id) {
218
+ throw new Error('No checkout session available');
219
+ }
220
+ try {
221
+ const response = await apiService.fetch(`/api/v1/checkout-sessions/${checkout.checkoutSession.id}/line-items/add`, {
222
+ method: 'POST',
223
+ body: { lineItems },
224
+ });
225
+ if (response.success) {
226
+ await refresh();
227
+ }
228
+ return response;
229
+ }
230
+ catch (err) {
231
+ const error = err instanceof Error ? err : new Error('Failed to add line items');
232
+ throw error;
233
+ }
234
+ }, [apiService, checkout?.checkoutSession.id, refresh]);
235
+ const removeLineItems = useCallback(async (lineItems) => {
236
+ if (!checkout?.checkoutSession.id) {
237
+ throw new Error('No checkout session available');
238
+ }
239
+ try {
240
+ const response = await apiService.fetch(`/api/v1/checkout-sessions/${checkout.checkoutSession.id}/line-items/remove`, {
241
+ method: 'POST',
242
+ body: { lineItems },
243
+ });
244
+ if (response.success) {
245
+ await refresh();
246
+ }
247
+ return response;
248
+ }
249
+ catch (err) {
250
+ const error = err instanceof Error ? err : new Error('Failed to remove line items');
251
+ throw error;
252
+ }
253
+ }, [apiService, checkout?.checkoutSession.id, refresh]);
254
+ const setItemQuantity = useCallback(async (variantId, quantity, priceId) => {
255
+ if (!checkout?.checkoutSession.id) {
256
+ throw new Error('No checkout session available');
257
+ }
258
+ try {
259
+ const response = await apiService.fetch(`/api/v1/checkout-sessions/${checkout.checkoutSession.id}/line-items/set-quantity`, {
260
+ method: 'POST',
261
+ body: { variantId, quantity, priceId },
262
+ });
263
+ if (response.success) {
264
+ await refresh();
265
+ }
266
+ return response;
267
+ }
268
+ catch (err) {
269
+ const error = err instanceof Error ? err : new Error('Failed to set item quantity');
270
+ throw error;
271
+ }
272
+ }, [apiService, checkout?.checkoutSession.id, refresh]);
216
273
  const toggleOrderBump = useCallback(async (orderBumpOfferId, selected) => {
217
274
  if (!checkout?.checkoutSession.id) {
218
275
  throw new Error('No checkout session available');
@@ -317,6 +374,9 @@ export function useCheckout(options = {}) {
317
374
  removePromotion,
318
375
  getAppliedPromotions,
319
376
  updateLineItems,
377
+ addLineItems,
378
+ removeLineItems,
379
+ setItemQuantity,
320
380
  toggleOrderBump,
321
381
  updateCustomer,
322
382
  updateCustomerAndSessionInfo,
@@ -1,4 +1,5 @@
1
- import { useState, useCallback, useEffect, useRef } from 'react';
1
+ import { useState, useCallback, useEffect, useRef, useMemo } from 'react';
2
+ import { useBasisTheory } from '@basis-theory/basis-theory-react';
2
3
  import { useTagadaContext } from '../providers/TagadaProvider';
3
4
  import { getBasisTheoryApiKey } from '../config/payment';
4
5
  import { usePaymentPolling } from './usePaymentPolling';
@@ -23,39 +24,25 @@ export function usePayment() {
23
24
  const { createSession, startChallenge } = useThreeds();
24
25
  // Track challenge in progress to prevent multiple challenges
25
26
  const challengeInProgressRef = useRef(false);
26
- // Initialize BasisTheory dynamically
27
- const [basisTheory, setBasisTheory] = useState(null);
27
+ // Stabilize environment value to prevent re-renders
28
+ const currentEnvironment = useMemo(() => environment?.environment || 'local', [environment?.environment]);
29
+ // Get API key from embedded configuration with proper environment detection
30
+ const apiKey = useMemo(() => getBasisTheoryApiKey(currentEnvironment), [currentEnvironment]);
31
+ // Initialize BasisTheory using React wrapper
32
+ const { bt: basisTheory, error: btError } = useBasisTheory(apiKey, {
33
+ elements: false,
34
+ });
35
+ // Handle BasisTheory initialization errors (only log once when state changes)
28
36
  useEffect(() => {
29
- let isMounted = true;
30
- const loadBasisTheory = async () => {
31
- try {
32
- // Get API key from embedded configuration (with env override support)
33
- const apiKey = getBasisTheoryApiKey(environment?.environment || 'local');
34
- if (!apiKey) {
35
- console.warn('BasisTheory API key not configured');
36
- return;
37
- }
38
- const { BasisTheory } = await import('@basis-theory/basis-theory-js');
39
- if (isMounted) {
40
- const bt = await new BasisTheory().init(apiKey, {
41
- elements: false,
42
- });
43
- setBasisTheory(bt);
44
- console.log('✅ BasisTheory initialized successfully');
45
- }
46
- }
47
- catch (err) {
48
- console.error('Failed to load BasisTheory:', err);
49
- if (isMounted) {
50
- setError('Failed to initialize payment processor');
51
- }
52
- }
53
- };
54
- void loadBasisTheory();
55
- return () => {
56
- isMounted = false;
57
- };
58
- }, [environment]);
37
+ if (btError) {
38
+ console.error('BasisTheory initialization error:', btError);
39
+ setError('Failed to initialize payment processor: ' + btError.message);
40
+ }
41
+ else if (basisTheory && !error) {
42
+ console.log('✅ BasisTheory initialized successfully');
43
+ setError(null); // Clear any previous errors
44
+ }
45
+ }, [basisTheory, btError]); // Removed error from dependency to prevent loops
59
46
  // Clean up polling when component unmounts
60
47
  useEffect(() => {
61
48
  return () => {
@@ -187,6 +174,9 @@ export function usePayment() {
187
174
  }, [basisTheory, apiService]);
188
175
  // Create Apple Pay payment instrument
189
176
  const createApplePayPaymentInstrument = useCallback(async (applePayToken) => {
177
+ if (!basisTheory) {
178
+ throw new Error('Payment processor not initialized');
179
+ }
190
180
  if (!applePayToken.id) {
191
181
  throw new Error('Apple Pay token is missing');
192
182
  }
@@ -213,7 +203,7 @@ export function usePayment() {
213
203
  console.error('Error creating Apple Pay payment instrument:', error);
214
204
  throw error;
215
205
  }
216
- }, [apiService]);
206
+ }, [basisTheory, apiService]);
217
207
  // Process payment directly with checkout session
218
208
  const processPaymentDirect = useCallback(async (checkoutSessionId, paymentInstrumentId, threedsSessionId, options = {}) => {
219
209
  try {
@@ -335,7 +325,7 @@ export function usePayment() {
335
325
  processPaymentWithInstrument,
336
326
  createCardPaymentInstrument,
337
327
  createApplePayPaymentInstrument,
338
- isLoading,
328
+ isLoading: isLoading || !basisTheory, // Indicate loading if BasisTheory is not initialized
339
329
  error,
340
330
  clearError,
341
331
  currentPaymentId,
@@ -7,16 +7,12 @@ export interface PaymentInstrument {
7
7
  maskedCardNumber?: string;
8
8
  expirationMonth?: number;
9
9
  expirationYear?: number;
10
- brand?: string;
11
- bin?: string;
12
- last4?: string;
13
- } | null;
10
+ };
14
11
  }
15
12
  export interface ThreedsSession {
16
13
  id: string;
17
- provider: ThreedsProvider;
18
- status: string;
19
- sessionData: any;
14
+ sessionId: string;
15
+ provider: string;
20
16
  }
21
17
  export interface ThreedsChallenge {
22
18
  sessionId: string;
@@ -33,6 +29,8 @@ export interface ThreedsHook {
33
29
  isLoading: boolean;
34
30
  error: Error | null;
35
31
  }
36
- export declare function useThreeds(options?: {
32
+ interface UseThreedsOptions {
37
33
  defaultProvider?: ThreedsProvider;
38
- }): ThreedsHook;
34
+ }
35
+ export declare function useThreeds(options?: UseThreedsOptions): ThreedsHook;
36
+ export {};
@@ -4,22 +4,17 @@ import { getBasisTheoryApiKey } from '../config/payment';
4
4
  import { useThreedsModal } from './useThreedsModal';
5
5
  export function useThreeds(options = {}) {
6
6
  const { defaultProvider = 'basis_theory' } = options;
7
- const { apiService, environment } = useTagadaContext();
7
+ const { apiService } = useTagadaContext();
8
8
  const [isLoading, setIsLoading] = useState(false);
9
9
  const [error, setError] = useState(null);
10
10
  const { createThreedsModal, closeThreedsModal } = useThreedsModal();
11
11
  const [basisTheory3ds, setBasisTheory3ds] = useState(null);
12
+ const [currentChallenge, setCurrentChallenge] = useState(null);
12
13
  // Dynamically import BasisTheory3ds on the client side only
13
14
  useEffect(() => {
14
15
  let isMounted = true;
15
16
  const loadBasisTheory = async () => {
16
17
  try {
17
- // Get API key from embedded configuration (with env override support)
18
- const apiKey = getBasisTheoryApiKey(environment?.environment || 'local');
19
- if (!apiKey) {
20
- console.warn('BasisTheory API key not configured');
21
- return;
22
- }
23
18
  // Dynamically import the library only on the client side
24
19
  const { BasisTheory3ds } = await import('@basis-theory/web-threeds');
25
20
  if (isMounted) {
@@ -38,20 +33,17 @@ export function useThreeds(options = {}) {
38
33
  return () => {
39
34
  isMounted = false;
40
35
  };
41
- }, [environment]);
36
+ }, []);
42
37
  // Create a 3DS session with BasisTheory
43
38
  const createBasisTheorySession = useCallback(async (paymentInstrument) => {
44
39
  try {
45
40
  if (!basisTheory3ds) {
46
41
  throw new Error('BasisTheory3ds not loaded yet');
47
42
  }
48
- // Get API key from embedded configuration
49
- const apiKey = getBasisTheoryApiKey(environment?.environment || 'local');
50
- if (!apiKey) {
51
- throw new Error('BasisTheory API key not configured');
52
- }
43
+ // Use the same API key approach as the working CMS version
44
+ const apiKey = getBasisTheoryApiKey('production'); // Use production config for now
53
45
  const bt3ds = basisTheory3ds(apiKey);
54
- console.log('Creating BasisTheory 3DS session for payment instrument:', paymentInstrument.id);
46
+ console.log('paymentInstrument paymentInstrument', paymentInstrument?.token);
55
47
  const session = await bt3ds.createSession({
56
48
  tokenId: paymentInstrument.token,
57
49
  });
@@ -70,7 +62,7 @@ export function useThreeds(options = {}) {
70
62
  console.error('Error creating BasisTheory 3DS session:', error);
71
63
  throw error;
72
64
  }
73
- }, [apiService, basisTheory3ds, environment]);
65
+ }, [apiService, basisTheory3ds]);
74
66
  // Generic createSession method that supports multiple providers
75
67
  const createSession = useCallback(async (paymentInstrument, options) => {
76
68
  const provider = options?.provider || defaultProvider;
@@ -96,31 +88,37 @@ export function useThreeds(options = {}) {
96
88
  }
97
89
  }, [defaultProvider, createBasisTheorySession]);
98
90
  // Start a 3DS challenge with BasisTheory
99
- const startBasisTheoryChallenge = useCallback(async (challengeData) => {
91
+ const startBasisTheoryChallenge = useCallback(async (sessionId, acsChallengeUrl, acsTransactionId, threeDSVersion) => {
100
92
  try {
101
93
  if (!basisTheory3ds) {
102
94
  throw new Error('BasisTheory3ds not loaded yet');
103
95
  }
104
- // Get API key from embedded configuration
105
- const apiKey = getBasisTheoryApiKey(environment?.environment || 'local');
96
+ // Use the same API key approach as the working CMS version
97
+ const apiKey = getBasisTheoryApiKey('production'); // Use production config for now
106
98
  if (!apiKey) {
107
- throw new Error('BasisTheory API key is not configured');
99
+ throw new Error('BasisTheory API key is not set');
108
100
  }
109
101
  const modal = createThreedsModal({
110
102
  onClose: () => {
103
+ // Throw error when user closes the modal
111
104
  throw new Error('Authentication was cancelled by the user');
112
105
  },
113
106
  });
114
107
  const bt3ds = basisTheory3ds(apiKey);
115
- console.log('Starting BasisTheory 3DS challenge:', challengeData);
108
+ console.log('bt3ds starting challenge with params', {
109
+ sessionId,
110
+ acsChallengeUrl,
111
+ acsTransactionId,
112
+ threeDSVersion,
113
+ });
116
114
  const challengeCompletion = await bt3ds.startChallenge({
117
- sessionId: challengeData.sessionId,
118
- acsChallengeUrl: challengeData.acsChallengeUrl,
119
- acsTransactionId: challengeData.acsTransactionId,
120
- threeDSVersion: challengeData.threeDSVersion,
115
+ sessionId,
116
+ acsChallengeUrl,
117
+ acsTransactionId,
118
+ threeDSVersion: threeDSVersion,
121
119
  containerId: modal.containerId + '-content',
122
120
  mode: 'iframe',
123
- timeout: 60000 * 3, // 3 minutes
121
+ timeout: 60000 * 3,
124
122
  });
125
123
  closeThreedsModal();
126
124
  return challengeCompletion;
@@ -130,7 +128,7 @@ export function useThreeds(options = {}) {
130
128
  closeThreedsModal();
131
129
  throw error;
132
130
  }
133
- }, [basisTheory3ds, createThreedsModal, closeThreedsModal, environment]);
131
+ }, [basisTheory3ds, createThreedsModal, closeThreedsModal]);
134
132
  // Generic startChallenge method that supports multiple providers
135
133
  const startChallenge = useCallback(async (challengeData, options) => {
136
134
  const provider = options?.provider || defaultProvider;
@@ -138,7 +136,7 @@ export function useThreeds(options = {}) {
138
136
  setError(null);
139
137
  try {
140
138
  if (provider === 'basis_theory') {
141
- return await startBasisTheoryChallenge(challengeData);
139
+ return await startBasisTheoryChallenge(challengeData.sessionId, challengeData.acsChallengeUrl, challengeData.acsTransactionId, challengeData.threeDSVersion);
142
140
  }
143
141
  else {
144
142
  throw new Error(`Unsupported 3DS provider: ${String(provider)}`);
@@ -1,16 +1,13 @@
1
- export interface ThreedsModalOptions {
2
- containerId?: string;
3
- mode?: 'fixed' | 'auto-fit';
4
- onClose?: () => void;
5
- }
6
- export interface ThreedsModalInstance {
7
- containerId: string;
8
- getContentElement: () => HTMLElement | null;
9
- updateModalSize: () => void;
10
- cleanup: () => void;
11
- }
12
- export interface ThreedsModalHook {
13
- createThreedsModal: (options?: ThreedsModalOptions) => ThreedsModalInstance;
1
+ export declare function useThreedsModal(): {
2
+ createThreedsModal: (options?: {
3
+ containerId?: string;
4
+ mode?: "fixed" | "auto-fit";
5
+ onClose?: () => void;
6
+ }) => {
7
+ containerId: string;
8
+ getContentElement: () => HTMLElement | null;
9
+ updateModalSize: () => void;
10
+ cleanup: () => void;
11
+ };
14
12
  closeThreedsModal: (containerId?: string) => void;
15
- }
16
- export declare function useThreedsModal(): ThreedsModalHook;
13
+ };
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  import { useCallback } from 'react';
2
3
  export function useThreedsModal() {
3
4
  // Close the 3DS modal
@@ -44,9 +45,9 @@ export function useThreedsModal() {
44
45
  zIndex: '9998',
45
46
  display: 'flex',
46
47
  justifyContent: 'center',
47
- alignItems: 'center',
48
+ alignItems: 'center', // Default to center alignment
48
49
  transition: 'opacity 0.3s ease',
49
- opacity: '0',
50
+ opacity: '0', // Start transparent
50
51
  });
51
52
  // Create modal container
52
53
  const container = document.createElement('div');
@@ -57,7 +58,7 @@ export function useThreedsModal() {
57
58
  const baseStyles = {
58
59
  position: 'relative',
59
60
  backgroundColor: 'white',
60
- borderRadius: '12px',
61
+ borderRadius: '12px', // Default border radius
61
62
  boxShadow: '0 10px 25px rgba(0, 0, 0, 0.2)',
62
63
  maxWidth: '100%',
63
64
  overflow: 'hidden',
@@ -69,10 +70,12 @@ export function useThreedsModal() {
69
70
  // Function to apply styles based on screen size and mode
70
71
  const applyResponsiveStyles = () => {
71
72
  isMobile = window.innerWidth < 768;
73
+ // Always ensure backdrop is properly centered
72
74
  backdrop.style.alignItems = 'center';
73
75
  backdrop.style.justifyContent = 'center';
74
76
  if (mode === 'fixed') {
75
77
  if (isMobile) {
78
+ // Full screen modal for mobile
76
79
  Object.assign(container.style, {
77
80
  ...baseStyles,
78
81
  position: 'fixed',
@@ -82,7 +85,7 @@ export function useThreedsModal() {
82
85
  bottom: '0',
83
86
  width: '100%',
84
87
  height: '100%',
85
- borderRadius: '0',
88
+ borderRadius: '0', // No border radius for full screen
86
89
  margin: '0',
87
90
  backgroundColor: 'white',
88
91
  transform: 'scale(0.95)',
@@ -91,6 +94,7 @@ export function useThreedsModal() {
91
94
  });
92
95
  }
93
96
  else {
97
+ // Desktop styles
94
98
  Object.assign(container.style, {
95
99
  ...baseStyles,
96
100
  width: '550px',
@@ -108,6 +112,7 @@ export function useThreedsModal() {
108
112
  else {
109
113
  // auto-fit mode
110
114
  if (isMobile) {
115
+ // Full screen modal for mobile
111
116
  Object.assign(container.style, {
112
117
  ...baseStyles,
113
118
  position: 'fixed',
@@ -117,7 +122,7 @@ export function useThreedsModal() {
117
122
  bottom: '0',
118
123
  width: '100%',
119
124
  height: '100%',
120
- borderRadius: '0',
125
+ borderRadius: '0', // No border radius for full screen
121
126
  margin: '0',
122
127
  backgroundColor: 'white',
123
128
  transform: 'scale(0.95)',
@@ -126,6 +131,7 @@ export function useThreedsModal() {
126
131
  });
127
132
  }
128
133
  else {
134
+ // Desktop styles
129
135
  Object.assign(container.style, {
130
136
  ...baseStyles,
131
137
  width: '550px',
@@ -249,6 +255,7 @@ export function useThreedsModal() {
249
255
  if (!content)
250
256
  return;
251
257
  if (isMobile) {
258
+ // On mobile, we're using full screen so just ensure content scrolls
252
259
  content.style.height = 'calc(100% - 60px)';
253
260
  content.style.overflowY = 'auto';
254
261
  }
@@ -258,10 +265,12 @@ export function useThreedsModal() {
258
265
  const headerHeight = header.offsetHeight;
259
266
  const maxModalHeight = viewportHeight * 0.85;
260
267
  if (contentHeight + headerHeight < maxModalHeight) {
268
+ // Content fits, let it determine the height
261
269
  container.style.height = 'auto';
262
270
  content.style.overflowY = 'visible';
263
271
  }
264
272
  else {
273
+ // Content is too large, cap the height
265
274
  container.style.height = `${maxModalHeight}px`;
266
275
  content.style.height = `${maxModalHeight - headerHeight}px`;
267
276
  content.style.overflowY = 'auto';
@@ -270,27 +279,33 @@ export function useThreedsModal() {
270
279
  };
271
280
  // Handle window resize
272
281
  const handleResize = () => {
282
+ // Store current opacity values before applying new styles
273
283
  const currentOpacity = container.style.opacity;
274
284
  const currentTransform = container.style.transform;
275
285
  applyResponsiveStyles();
276
286
  updateContentStyles();
287
+ // Restore opacity and transform to maintain visibility
277
288
  container.style.opacity = currentOpacity;
278
289
  container.style.transform = currentTransform;
290
+ // For auto-fit mode, also adjust the height
279
291
  if (mode === 'auto-fit') {
280
292
  adjustAutoFitHeight();
281
293
  }
282
294
  };
283
295
  // Set up mutation observer to detect content changes
284
- const contentObserver = new MutationObserver(() => {
296
+ const contentObserver = new MutationObserver((mutations) => {
297
+ // When content changes, adjust the height
285
298
  if (mode === 'auto-fit') {
299
+ // Small delay to ensure content has rendered
286
300
  setTimeout(adjustAutoFitHeight, 50);
287
301
  }
288
302
  });
303
+ // Configure and start the mutation observer
289
304
  contentObserver.observe(content, {
290
- childList: true,
291
- subtree: true,
292
- characterData: true,
293
- attributes: true,
305
+ childList: true, // Watch for changes to child elements
306
+ subtree: true, // Watch the entire subtree
307
+ characterData: true, // Watch for changes to text content
308
+ attributes: true, // Watch for changes to attributes
294
309
  });
295
310
  // For auto-fit mode, set up resize observer for content element
296
311
  let resizeObserver = null;
@@ -311,7 +326,7 @@ export function useThreedsModal() {
311
326
  return {
312
327
  containerId,
313
328
  getContentElement: () => document.getElementById(`${containerId}-content`),
314
- updateModalSize,
329
+ updateModalSize, // Expose function to manually update size
315
330
  cleanup: () => {
316
331
  window.removeEventListener('resize', handleResize);
317
332
  if (resizeObserver) {
@@ -21,6 +21,5 @@ export type { Customer, Session, AuthState, Locale, Currency, Store, Environment
21
21
  export type { UseCheckoutOptions, UseCheckoutResult, CheckoutInitParams, CheckoutLineItem, CheckoutSession, CheckoutData, Promotion, } from './hooks/useCheckout';
22
22
  export type { Payment, PollingOptions, PaymentPollingHook } from './hooks/usePaymentPolling';
23
23
  export type { PaymentInstrument, ThreedsSession, ThreedsChallenge, ThreedsOptions, ThreedsHook, ThreedsProvider, } from './hooks/useThreeds';
24
- export type { ThreedsModalOptions, ThreedsModalInstance, ThreedsModalHook } from './hooks/useThreedsModal';
25
24
  export type { ApplePayToken, CardPaymentMethod, PaymentResponse, PaymentOptions, PaymentInstrumentResponse, PaymentHook, } from './hooks/usePayment';
26
25
  export { formatMoney, formatMoneyWithoutSymbol, getCurrencyInfo, minorUnitsToMajorUnits, moneyStringOrNumberToMinorUnits, convertCurrency, formatSimpleMoney, } from './utils/money';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tagadapay/plugin-sdk",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "Modern React SDK for building Tagada Pay plugins",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -34,8 +34,8 @@
34
34
  "license": "MIT",
35
35
  "dependencies": {
36
36
  "@basis-theory/apple-pay-js": "^2.0.2",
37
- "@basis-theory/basis-theory-js": "^4.28.2",
38
- "@basis-theory/basis-theory-react": "^1.32.4",
37
+ "@basis-theory/basis-theory-js": "^4.30.0",
38
+ "@basis-theory/basis-theory-react": "^1.32.5",
39
39
  "@basis-theory/web-threeds": "^1.0.1",
40
40
  "axios": "^1.6.0",
41
41
  "react-intl": "^7.1.11"