Skip to content

Troubleshooting

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

Common issues

IssueDescriptionNext 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 CodeExceptionDescriptionPrevention
SDK0200ComponentExceptionGeneric component error.Validate configuration before component creation.
SDK0201ComponentContainerNotFoundExceptionThe container element doesn't exist in the DOM.Ensure container exists before calling mount().
SDK0202ComponentNameEmptyExceptionComponent name is missing or empty.Always provide valid component type to create().
SDK0301TokenizeCardExceptionCard tokenisation failed.Verify API connectivity and session validity.
SDK0400ThreeDSecureException3D Secure authentication error.Check 3DS configuration and network access.
SDK0401PreInitiateAuthenticationExceptionPre-authentication request failed.Verify transaction data and session.
SDK0402InitiateAuthenticationExceptionAuthentication initiation failed.Check 3DS server connectivity.
SDK0403AuthenticationRequiredException3DS authentication required but not completed.Implement 3DS flow properly.
SDK0404AuthenticationChallengeExceptionChallenge presentation failed.Ensure iframe access and proper event handling.
SDK0405AuthenticationRejected ExceptionCard issuer rejected authentication.Inform customer to contact their bank.
SDK0500NetworkExceptionNetwork connectivity error.Implement retry logic and check internet connection.
SDK0501ValidationExceptionField 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

// 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:

// ❌ 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:

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:

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

// 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:

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:

// 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

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

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:

<!-- 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:

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:

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

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:

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:

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:

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:

/* 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:

// 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

// 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

// 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

// 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

// 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

// 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);
  }
};