# Troubleshooting

Learn how to diagnose and fix common issues with card payment components.

## Common issues

| Issue | Description | Next steps |
|  --- | --- | --- |
| Component not rendering. | Card input fields don't appear in the container. | Verify that the container ID exists in the DOM.Check that the component is properly mounted after `pxpSdk.create()`.Ensure the SDK is fully initialised before creating components.Check browser console for JavaScript errors. |
| Validation errors. | Card number or other fields show validation errors. | Verify card number format (Luhn algorithm check).Check expiry date is in the future.Ensure CVV length matches card type (3 digits for most, 4 for Amex).Validate that all required fields are filled. |
| 3DS challenge not appearing. | 3D Secure authentication modal doesn't show. | Verify that 3DS is enabled in the Unity Portal.Check that the card supports 3DS.Ensure iframes are not blocked by browser/CSP.Verify network connectivity to authentication server. |
| Tokenisation fails. | Card tokenisation fails before transaction. | Check Unity API connectivity.Verify session data is valid and not expired.Ensure HMAC signature is correctly generated.Check that card data is properly formatted. |
| Styling issues. | Custom styles not applying to components. | Verify CSS selectors match component structure.Check that styles are loaded after component mounts.Use `customComponentCss` config for iframe-based fields.Ensure no conflicting global styles. |
| Submit button disabled. | Submit button remains disabled even with valid data. | Check that all required components are created and mounted.Verify that all fields pass validation.Ensure `onFieldValid` events are firing.Check for JavaScript errors preventing state updates. |


## Error codes and exceptions

### Component exceptions

The card components throw specific exceptions for different error scenarios:

| Error Code | Exception | Description | Prevention |
|  --- | --- | --- | --- |
| `SDK0200` | `ComponentException` | Generic component error. | Validate configuration before component creation. |
| `SDK0201` | `ComponentContainerNotFoundException` | The container element doesn't exist in the DOM. | Ensure container exists before calling `mount()`. |
| `SDK0202` | `ComponentNameEmptyException` | Component name is missing or empty. | Always provide valid component type to `create()`. |
| `SDK0301` | `TokenizeCardException` | Card tokenisation failed. | Verify API connectivity and session validity. |
| `SDK0400` | `ThreeDSecureException` | 3D Secure authentication error. | Check 3DS configuration and network access. |
| `SDK0401` | `PreInitiateAuthenticationException` | Pre-authentication request failed. | Verify transaction data and session. |
| `SDK0402` | `InitiateAuthenticationException` | Authentication initiation failed. | Check 3DS server connectivity. |
| `SDK0403` | `AuthenticationRequiredException` | 3DS authentication required but not completed. | Implement 3DS flow properly. |
| `SDK0404` | `AuthenticationChallengeException` | Challenge presentation failed. | Ensure iframe access and proper event handling. |
| `SDK0405` | `AuthenticationRejected Exception` | Card issuer rejected authentication. | Inform customer to contact their bank. |
| `SDK0500` | `NetworkException` | Network connectivity error. | Implement retry logic and check internet connection. |
| `SDK0501` | `ValidationException` | Field validation failed. | Display specific validation messages to user. |


## Troubleshooting component rendering

### Card components not appearing

**Symptoms:**

- Empty containers where card fields should be.
- No visible input fields for card number, expiry, CVV.
- Console errors about missing containers.


#### Diagnostic steps


```typescript
// Step 1: Verify DOM is ready
console.log('Checking DOM readiness...');
const container = document.getElementById('card-number-container');
if (!container) {
  console.error('Container not found in DOM');
} else {
  console.log('Container exists:', container);
}

// Step 2: Verify SDK initialisation
console.log('Checking SDK initialisation...');
if (typeof pxpSdk === 'undefined') {
  console.error('PxpCheckout SDK not initialised');
} else {
  console.log('SDK initialised successfully');
}

// Step 3: Check component creation
try {
  const cardNumber = pxpSdk.create('card-number');
  console.log('Card number component created:', cardNumber);
  
  // Try to mount
  cardNumber.mount('card-number-container');
  console.log('Card number component mounted successfully');
} catch (error) {
  console.error('Component creation/mount failed:', error);
}

// Step 4: Check for iframe creation
setTimeout(() => {
  const iframe = container.querySelector('iframe');
  if (iframe) {
    console.log('Iframe created successfully');
  } else {
    console.error('No iframe found - component may not have mounted');
  }
}, 500);
```

#### Solutions

**Ensure proper DOM timing:**


```typescript
// ❌ Bad - Mount before DOM is ready
const cardNumber = pxpSdk.create('card-number');
cardNumber.mount('card-number-container'); // May fail if DOM not ready

// ✅ Good - Wait for DOM
document.addEventListener('DOMContentLoaded', () => {
  const cardNumber = pxpSdk.create('card-number');
  cardNumber.mount('card-number-container');
});

// ✅ Better - Check container exists
function mountCardComponent() {
  const container = document.getElementById('card-number-container');
  
  if (!container) {
    console.error('Container not found, retrying...');
    setTimeout(mountCardComponent, 100);
    return;
  }
  
  const cardNumber = pxpSdk.create('card-number');
  cardNumber.mount('card-number-container');
}

// ✅ Best - Use framework lifecycle hooks
// React
useEffect(() => {
  if (containerRef.current && pxpSdk) {
    const cardNumber = pxpSdk.create('card-number');
    cardNumber.mount(containerRef.current);
    
    return () => cardNumber.unmount();
  }
}, [pxpSdk]);
```

**Handle SDK initialisation errors:**


```typescript
let pxpSdk;

try {
  pxpSdk = PxpCheckout.initialize({
    environment: 'production',
    session: sessionData,
    ownerId: 'MERCHANT-1',
    ownerType: 'MerchantGroup'
  });
  
  console.log('SDK initialised successfully');
} catch (error) {
  console.error('SDK initialisation failed:', error);
  
  // Show user-friendly error
  showError('Payment system unavailable. Please try again later.');
  
  // Log to monitoring service
  logError('SDK_INIT_FAILED', error);
  
  return; // Don't proceed with component creation
}

// Now safe to create components
const cardNumber = pxpSdk.create('card-number');
```

**Verify container visibility:**


```typescript
function diagnoseContainerVisibility() {
  const container = document.getElementById('card-number-container');
  
  if (!container) {
    console.error('Container not found');
    return false;
  }
  
  const styles = window.getComputedStyle(container);
  const diagnostics = {
    display: styles.display,
    visibility: styles.visibility,
    opacity: styles.opacity,
    width: styles.width,
    height: styles.height,
    position: styles.position,
    isInViewport: isElementInViewport(container)
  };
  
  console.log('Container diagnostics:', diagnostics);
  
  // Check for common issues
  if (styles.display === 'none') {
    console.warn('Container has display:none - component won\'t be visible');
  }
  
  if (parseInt(styles.width) === 0 || parseInt(styles.height) === 0) {
    console.warn('Container has zero dimensions');
  }
  
  return true;
}

function isElementInViewport(el) {
  const rect = el.getBoundingClientRect();
  return (
    rect.top >= 0 &&
    rect.left >= 0 &&
    rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
    rect.right <= (window.innerWidth || document.documentElement.clientWidth)
  );
}
```

## Troubleshooting validation

### Card number validation fails

**Symptoms:**

- Valid card numbers show as invalid.
- Error messages appear immediately on input.
- Unable to proceed with payment.


#### Diagnostic steps


```typescript
// Test card validation logic
function testCardValidation(cardNumber) {
  console.group('Card Validation Test');
  console.log('Testing card number:', cardNumber);
  
  // Check Luhn algorithm
  const isLuhnValid = validateLuhn(cardNumber);
  console.log('Luhn valid:', isLuhnValid);
  
  // Check card type
  const cardType = detectCardType(cardNumber);
  console.log('Detected card type:', cardType);
  
  // Check length
  const lengthValid = validateCardLength(cardNumber, cardType);
  console.log('Length valid:', lengthValid);
  
  console.groupEnd();
  
  return isLuhnValid && lengthValid;
}

function validateLuhn(cardNumber) {
  const digits = cardNumber.replace(/\D/g, '');
  let sum = 0;
  let isEven = false;
  
  for (let i = digits.length - 1; i >= 0; i--) {
    let digit = parseInt(digits[i]);
    
    if (isEven) {
      digit *= 2;
      if (digit > 9) {
        digit -= 9;
      }
    }
    
    sum += digit;
    isEven = !isEven;
  }
  
  return sum % 10 === 0;
}

function detectCardType(cardNumber) {
  const patterns = {
    visa: /^4/,
    mastercard: /^5[1-5]|^2[2-7]/,
    amex: /^3[47]/,
    discover: /^6(?:011|5)/,
    diners: /^3(?:0[0-5]|[68])/,
    jcb: /^35/
  };
  
  for (const [type, pattern] of Object.entries(patterns)) {
    if (pattern.test(cardNumber)) {
      return type;
    }
  }
  
  return 'unknown';
}

function validateCardLength(cardNumber, cardType) {
  const expectedLengths = {
    visa: [13, 16, 19],
    mastercard: [16],
    amex: [15],
    discover: [16, 19],
    diners: [14],
    jcb: [16, 19]
  };
  
  const digits = cardNumber.replace(/\D/g, '');
  const expected = expectedLengths[cardType] || [16];
  
  return expected.includes(digits.length);
}

// Test with various card numbers
testCardValidation('4111111111111111'); // Valid Visa
testCardValidation('5555555555554444'); // Valid Mastercard
testCardValidation('378282246310005');  // Valid Amex
```

#### Solutions

**Listen to validation events:**


```typescript
const cardNumberConfig = {
  onChange: (event) => {
    console.log('Card number changed:', {
      complete: event.complete,
      valid: event.valid,
      brand: event.brand,
      error: event.error
    });
    
    // Update UI based on validation
    if (event.error) {
      showFieldError('card-number', event.error.message);
    } else {
      clearFieldError('card-number');
    }
    
    // Update submit button state
    updateSubmitButton();
  },
  
  onFieldValid: () => {
    console.log('Card number is valid');
    highlightField('card-number', 'valid');
  },
  
  onFieldInvalid: (error) => {
    console.log('Card number is invalid:', error);
    highlightField('card-number', 'invalid');
  }
};

const cardNumber = pxpSdk.create('card-number', cardNumberConfig);
cardNumber.mount('card-number-container');

function showFieldError(fieldName, message) {
  const errorElement = document.getElementById(`${fieldName}-error`);
  if (errorElement) {
    errorElement.textContent = message;
    errorElement.style.display = 'block';
  }
}

function clearFieldError(fieldName) {
  const errorElement = document.getElementById(`${fieldName}-error`);
  if (errorElement) {
    errorElement.textContent = '';
    errorElement.style.display = 'none';
  }
}

function highlightField(fieldName, state) {
  const container = document.getElementById(`${fieldName}-container`);
  if (container) {
    container.classList.remove('valid', 'invalid');
    if (state !== 'neutral') {
      container.classList.add(state);
    }
  }
}
```

**Implement custom validation:**


```typescript
// Add business-specific validation rules
const cardSubmitConfig = {
  beforeSubmit: () => {
    // Custom validation before tokenisation
    const validationErrors = [];
    
    // Check if card is in excluded BIN list
    const cardNumber = getCardNumberValue();
    if (isExcludedBIN(cardNumber)) {
      validationErrors.push('This card type is not accepted');
    }
    
    // Check amount limits for card type
    const amount = getTransactionAmount();
    const cardType = detectCardType(cardNumber);
    if (amount > getMaxAmountForCard Type(cardType)) {
      validationErrors.push(`Amount exceeds limit for ${cardType} cards`);
    }
    
    // Show errors if any
    if (validationErrors.length > 0) {
      showValidationErrors(validationErrors);
      return false; // Prevent submission
    }
    
    return true; // Allow submission
  },
  
  onSubmitError: (error) => {
    if (error.ErrorCode === 'SDK0501') {
      // Validation error from SDK
      console.error('Validation error:', error.message);
      showError(error.message);
    }
  }
};

function isExcludedBIN(cardNumber) {
  const excludedBINs = ['123456', '654321']; // Example
  const bin = cardNumber.substring(0, 6);
  return excludedBINs.includes(bin);
}

function getMaxAmountForCardType(cardType) {
  const limits = {
    amex: 10000,
    visa: 15000,
    mastercard: 15000,
    discover: 10000
  };
  return limits[cardType] || 5000;
}
```

### Expiry date validation issues

**Symptoms:**

- Valid future dates show as invalid.
- Past dates are accepted.
- Format errors with MM/YY input.


#### Solutions


```typescript
const expiryConfig = {
  onChange: (event) => {
    if (!event.valid && event.complete) {
      // Card is expired
      showFieldError('expiry', 'Card has expired. Please use a different card.');
    } else if (event.error) {
      showFieldError('expiry', event.error.message);
    } else {
      clearFieldError('expiry');
    }
  },
  
  // Additional validation
  customValidation: (month, year) => {
    const now = new Date();
    const currentMonth = now.getMonth() + 1; // 0-indexed
    const currentYear = now.getFullYear() % 100; // Last 2 digits
    
    // Check if card expires within 30 days
    if (year === currentYear && month === currentMonth) {
      showFieldWarning('expiry', 'Card expires soon. Consider using a different card.');
    }
    
    // Check if expiry is too far in future (potential typo)
    if (year > currentYear + 10) {
      showFieldWarning('expiry', 'Please verify the expiry date.');
    }
  }
};

function showFieldWarning(fieldName, message) {
  const warningElement = document.getElementById(`${fieldName}-warning`);
  if (warningElement) {
    warningElement.textContent = message;
    warningElement.style.display = 'block';
  }
}
```

## Troubleshooting 3D Secure

### 3DS challenge iframe not loading

**Symptoms:**

- 3DS challenge window doesn't appear.
- Transaction fails with authentication error.
- Console shows iframe loading errors.


#### Diagnostic steps


```typescript
function diagnose3DSSetup() {
  console.group('3DS Diagnostic Check');
  
  // Check CSP settings
  const csp = document.querySelector('meta[http-equiv="Content-Security-Policy"]');
  console.log('CSP meta tag:', csp ? csp.content : 'Not found');
  
  // Check if iframes are blocked
  try {
    const testIframe = document.createElement('iframe');
    testIframe.src = 'about:blank';
    document.body.appendChild(testIframe);
    console.log('Iframe creation: Allowed');
    document.body.removeChild(testIframe);
  } catch (error) {
    console.error('Iframe creation: Blocked', error);
  }
  
  // Check network connectivity to 3DS servers
  checkThreeDSConnectivity();
  
  console.groupEnd();
}

async function checkThreeDSConnectivity() {
  const threeDSServers = [
    'https://threeds2.example.com',
    // Add your actual 3DS server URLs
  ];
  
  for (const server of threeDSServers) {
    try {
      const response = await fetch(server, { method: 'HEAD', mode: 'no-cors' });
      console.log(`3DS Server ${server}: Reachable`);
    } catch (error) {
      console.error(`3DS Server ${server}: Not reachable`, error);
    }
  }
}
```

#### Solutions

**Update Content Security Policy:**


```html
<!-- Add 3DS servers to CSP -->
<meta http-equiv="Content-Security-Policy" 
      content="frame-src 'self' https://threeds2.example.com https://*.3dsecure.io;">
```

**Handle 3DS events properly:**


```typescript
const cardSubmitConfig = {
  onSubmit: () => {
    console.log('Payment submission started');
    showLoadingIndicator('Processing payment...');
  },
  
  on3DSInitiated: () => {
    console.log('3DS authentication initiated');
    showLoadingIndicator('Verifying your card...');
  },
  
  on3DSChallengePresented: () => {
    console.log('3DS challenge presented to user');
    hideLoadingIndicator();
    showMessage('Please complete authentication in the popup');
  },
  
  on3DSComplete: (result) => {
    console.log('3DS authentication completed:', result);
    
    if (result.authenticated) {
      showLoadingIndicator('Completing payment...');
    } else {
      showError('Authentication failed. Please try again or use a different card.');
    }
  },
  
  onPostAuthorisation: (result) => {
    console.log('Payment authorised:', result);
    hideLoadingIndicator();
    
    // Redirect to success page
    window.location.href = `/success?id=${result.systemTransactionId}`;
  },
  
  onSubmitError: (error) => {
    console.error('Payment failed:', error);
    hideLoadingIndicator();
    
    if (error.ErrorCode === 'SDK0505') {
      showError('Card authentication failed. Please contact your bank or try a different card.');
    } else {
      showError(`Payment failed: ${error.message}`);
    }
  }
};

const cardSubmit = pxpSdk.create('card-submit', cardSubmitConfig);
```

**Implement 3DS timeout handling:**


```typescript
let threeDSTimeout;

const cardSubmitConfig = {
  on3DSChallengePresented: () => {
    // Set 5-minute timeout for 3DS challenge
    threeDSTimeout = setTimeout(() => {
      console.warn('3DS challenge timeout');
      showError('Authentication timed out. Please try again.');
      
      // Cancel the transaction
      cancelPayment();
    }, 5 * 60 * 1000);
  },
  
  on3DSComplete: () => {
    // Clear timeout when 3DS completes
    if (threeDSTimeout) {
      clearTimeout(threeDSTimeout);
      threeDSTimeout = null;
    }
  },
  
  onSubmitError: () => {
    // Clear timeout on error
    if (threeDSTimeout) {
      clearTimeout(threeDSTimeout);
      threeDSTimeout = null;
    }
  }
};
```

## Troubleshooting tokenisation

### Tokenisation failures

**Symptoms:**

- Payment fails before reaching transaction stage.
- Error: "Unable to tokenise card".
- Network errors in console.


#### Diagnostic steps


```typescript
async function diagnoseTokenisation() {
  console.group('Tokenisation Diagnostic');
  
  // Check session validity
  const sessionData = pxpSdk.getSession();
  console.log('Session data:', {
    sessionId: sessionData.sessionId ? 'Present' : 'Missing',
    expiresAt: sessionData.expiresAt,
    isExpired: sessionData.expiresAt ? new Date(sessionData.expiresAt) < new Date() : 'Unknown'
  });
  
  // Check HMAC signature
  const hmac = sessionData.hmacSignature;
  console.log('HMAC signature:', hmac ? 'Present' : 'Missing');
  
  // Check API connectivity
  try {
    const response = await fetch('https://api.pxp.io/health', { mode: 'no-cors' });
    console.log('API connectivity: OK');
  } catch (error) {
    console.error('API connectivity: Failed', error);
  }
  
  // Check token vault URL
  const tokenVaultUrl = pxpSdk.getConfig().tokenVaultUrl;
  console.log('Token Vault URL:', tokenVaultUrl);
  
  console.groupEnd();
}
```

#### Solutions

**Handle session expiration:**


```typescript
const cardSubmitConfig = {
  beforeSubmit: async () => {
    // Check if session is about to expire
    const session = pxpSdk.getSession();
    const expiresAt = new Date(session.expiresAt);
    const now = new Date();
    const minutesUntilExpiry = (expiresAt - now) / (1000 * 60);
    
    if (minutesUntilExpiry < 5) {
      console.log('Session expiring soon, refreshing...');
      
      try {
        // Request new session from backend
        const newSession = await fetch('/api/create-session').then(r => r.json());
        
        // Reinitialise SDK with new session
        pxpSdk.updateSession(newSession);
        
        console.log('Session refreshed successfully');
        return true;
      } catch (error) {
        console.error('Failed to refresh session:', error);
        showError('Your session has expired. Please refresh the page.');
        return false;
      }
    }
    
    return true;
  },
  
  onSubmitError: async (error) => {
    if (error.message.includes('session') || error.message.includes('expired')) {
      // Session expired during submission
      showError('Your session has expired. Refreshing...');
      
      // Refresh page after short delay
      setTimeout(() => {
        window.location.reload();
      }, 2000);
    }
  }
};
```

**Implement retry logic:**


```typescript
async function submitPaymentWithRetry(maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      console.log(`Payment attempt ${attempt}/${maxRetries}`);
      
      // Trigger card submit
      const result = await cardSubmit.submit();
      
      console.log('Payment successful:', result);
      return result;
      
    } catch (error) {
      console.error(`Attempt ${attempt} failed:`, error);
      
      // Check if error is retryable
      if (isRetryableError(error) && attempt < maxRetries) {
        const delay = Math.min(1000 * Math.pow(2, attempt), 5000);
        console.log(`Retrying in ${delay}ms...`);
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }
      
      // Non-retryable error or max retries reached
      throw error;
    }
  }
}

function isRetryableError(error) {
  const retryableCodes = ['SDK0500', 'SDK0301']; // Network, tokenisation
  const retryableMessages = ['network', 'timeout', 'temporarily unavailable'];
  
  return retryableCodes.includes(error.ErrorCode) ||
         retryableMessages.some(msg => error.message.toLowerCase().includes(msg));
}

// Use in submit button handler
document.getElementById('submit-button').addEventListener('click', async () => {
  try {
    showLoadingIndicator();
    const result = await submitPaymentWithRetry();
    handleSuccess(result);
  } catch (error) {
    handleError(error);
  } finally {
    hideLoadingIndicator();
  }
});
```

## Troubleshooting styling

### Custom styles not applying

**Symptoms:**

- Component styling doesn't match design.
- CSS changes have no effect.
- Inconsistent appearance across components.


#### Solutions

**Use customComponentCss for iframe fields:**


```typescript
const cardNumberConfig = {
  customComponentCss: {
    base: {
      fontSize: '16px',
      fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
      color: '#32325d',
      '::placeholder': {
        color: '#aab7c4'
      }
    },
    invalid: {
      color: '#fa755a',
      iconColor: '#fa755a'
    },
    valid: {
      color: '#32cd32'
    }
  }
};

const cardNumber = pxpSdk.create('card-number', cardNumberConfig);
cardNumber.mount('card-number-container');
```

**Style containers consistently:**


```css
/* Container styling (applies to all components) */
.card-field-container {
  margin-bottom: 16px;
  position: relative;
}

.card-field-container label {
  display: block;
  margin-bottom: 8px;
  font-weight: 600;
  color: #32325d;
  font-size: 14px;
}

.card-field-container .field-wrapper {
  border: 1px solid #e0e0e0;
  border-radius: 4px;
  padding: 12px;
  background: #fff;
  transition: all 0.2s ease;
}

.card-field-container .field-wrapper:focus-within {
  border-color: #5469d4;
  box-shadow: 0 0 0 3px rgba(84, 105, 212, 0.1);
}

.card-field-container.invalid .field-wrapper {
  border-color: #fa755a;
}

.card-field-container.valid .field-wrapper {
  border-color: #32cd32;
}

.card-field-container .error-message {
  color: #fa755a;
  font-size: 13px;
  margin-top: 4px;
  display: none;
}

.card-field-container.invalid .error-message {
  display: block;
}
```

**Apply consistent styling across all fields:**


```typescript
// Create shared styling config
const sharedFieldStyles = {
  customComponentCss: {
    base: {
      fontSize: '16px',
      fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
      color: '#32325d',
      lineHeight: '24px',
      '::placeholder': {
        color: '#aab7c4'
      },
      ':-webkit-autofill': {
        color: '#32325d'
      }
    },
    invalid: {
      color: '#fa755a',
      iconColor: '#fa755a'
    },
    valid: {
      color: '#32cd32'
    }
  }
};

// Apply to all card fields
const cardNumber = pxpSdk.create('card-number', sharedFieldStyles);
const cardExpiry = pxpSdk.create('card-expiry', sharedFieldStyles);
const cardCvv = pxpSdk.create('card-cvv', sharedFieldStyles);
const cardName = pxpSdk.create('card-holder-name', sharedFieldStyles);
```

## Debugging tools and techniques

### Enable verbose logging


```typescript
// Enable debug mode globally
window.pxpDebug = {
  enabled: true,
  logLevel: 'verbose',
  logComponents: true,
  logNetworkRequests: true
};

// Custom logger
const logger = {
  group(label) {
    if (window.pxpDebug?.enabled) {
      console.group(`🔍 ${label}`);
    }
  },
  
  groupEnd() {
    if (window.pxpDebug?.enabled) {
      console.groupEnd();
    }
  },
  
  log(message, data) {
    if (window.pxpDebug?.enabled) {
      console.log(`[PXP] ${message}`, data || '');
    }
  },
  
  error(message, error) {
    console.error(`[PXP ERROR] ${message}`, error);
  }
};

// Use in your code
logger.group('Card Component Setup');
logger.log('Creating card number component');
const cardNumber = pxpSdk.create('card-number');
logger.log('Mounting to container', 'card-number-container');
cardNumber.mount('card-number-container');
logger.groupEnd();
```

### Comprehensive event monitoring


```typescript
// Monitor all component events
function monitorComponentEvents(component, componentName) {
  const events = ['onChange', 'onFocus', 'onBlur', 'onFieldValid', 'onFieldInvalid'];
  
  events.forEach(eventName => {
    if (component[eventName]) {
      const originalHandler = component[eventName];
      component[eventName] = function(...args) {
        console.log(`[${componentName}] ${eventName}`, args);
        if (originalHandler) {
          return originalHandler.apply(this, args);
        }
      };
    }
  });
  
  return component;
}

// Use for debugging
const cardNumber = monitorComponentEvents(
  pxpSdk.create('card-number'),
  'CardNumber'
);
```

### Network request debugging


```typescript
// Intercept and log all API requests
const originalFetch = window.fetch;
window.fetch = async function(...args) {
  const [url, options] = args;
  
  // Log request
  console.group('🌐 Network Request');
  console.log('URL:', url);
  console.log('Method:', options?.method || 'GET');
  console.log('Headers:', options?.headers);
  console.groupEnd();
  
  try {
    const response = await originalFetch.apply(this, args);
    
    // Log response
    console.group('✅ Network Response');
    console.log('URL:', url);
    console.log('Status:', response.status);
    console.log('Status Text:', response.statusText);
    console.groupEnd();
    
    return response;
  } catch (error) {
    console.group('❌ Network Error');
    console.log('URL:', url);
    console.error('Error:', error);
    console.groupEnd();
    
    throw error;
  }
};
```

## Prevention and best practices

### Proactive error prevention


```typescript
// Pre-flight checks before initialisation
async function performPreflightChecks() {
  const checks = [
    {
      name: 'Session data validity',
      test: () => validateSessionData(),
      fix: 'Request new session from backend'
    },
    {
      name: 'DOM containers exist',
      test: () => checkContainersExist(),
      fix: 'Ensure containers are in the DOM before mounting'
    },
    {
      name: 'Network connectivity',
      test: async () => await checkNetworkConnectivity(),
      fix: 'Check internet connection'
    },
    {
      name: 'No conflicting scripts',
      test: () => checkForConflicts(),
      fix: 'Remove conflicting payment libraries'
    }
  ];
  
  const results = [];
  for (const check of checks) {
    try {
      const passed = await check.test();
      results.push({ ...check, passed });
      
      if (!passed) {
        console.warn(`❌ ${check.name}: ${check.fix}`);
      } else {
        console.log(`✅ ${check.name}`);
      }
    } catch (error) {
      console.error(`❌ ${check.name} failed:`, error);
      results.push({ ...check, passed: false, error });
    }
  }
  
  return results.every(r => r.passed);
}

function validateSessionData() {
  const sessionData = getSessionData();
  return sessionData && 
         sessionData.sessionId && 
         sessionData.hmacSignature &&
         new Date(sessionData.expiresAt) > new Date();
}

function checkContainersExist() {
  const requiredContainers = [
    'card-number-container',
    'card-expiry-container',
    'card-cvv-container',
    'card-submit-container'
  ];
  
  return requiredContainers.every(id => document.getElementById(id) !== null);
}

async function checkNetworkConnectivity() {
  try {
    await fetch('/api/health-check', { method: 'HEAD' });
    return true;
  } catch {
    return false;
  }
}

function checkForConflicts() {
  const conflictingScripts = ['stripe', 'braintree', 'square'];
  return !conflictingScripts.some(script => window[script]);
}

// Use before initialisation
async function initializePaymentForm() {
  const preflightPassed = await performPreflightChecks();
  
  if (!preflightPassed) {
    showError('Unable to initialise payment form. Please refresh the page.');
    return;
  }
  
  // Safe to proceed
  createCardComponents();
}
```

### Error monitoring and analytics


```typescript
// Comprehensive error tracking
const errorTracking = {
  capture(error, context) {
    const errorData = {
      message: error.message,
      code: error.ErrorCode,
      stack: error.stack,
      context: context,
      timestamp: new Date().toISOString(),
      userAgent: navigator.userAgent,
      url: location.href,
      sessionId: getSessionId(),
      userId: getUserId()
    };
    
    // Send to error monitoring service
    this.sendToMonitoring(errorData);
    
    // Track in analytics
    this.trackInAnalytics('payment_error', {
      error_code: error.ErrorCode,
      error_type: error.name,
      context: context
    });
  },
  
  sendToMonitoring(data) {
    // Example: Sentry, LogRocket, etc.
    if (window.Sentry) {
      Sentry.captureException(new Error(data.message), {
        extra: data
      });
    }
  },
  
  trackInAnalytics(event, properties) {
    // Example: Google Analytics, Mixpanel, etc.
    if (window.gtag) {
      gtag('event', event, properties);
    }
  }
};

// Use in component configuration
const cardSubmitConfig = {
  onSubmitError: (error) => {
    errorTracking.capture(error, 'card_submit');
    showUserFriendlyError(error);
  }
};
```