Skip to main content

Troubleshooting Guide

This guide helps you diagnose and resolve common issues you might encounter when using the Paysight Widget SDK.

Common Issues

Widget Initialization

Widget Fails to Load

Symptoms:
  • Widget container remains empty
  • ERROR event with initialization error
  • Console errors related to script loading
Possible Causes:
  1. Invalid script source
  2. Network connectivity issues
  3. Invalid configuration
  4. DOM target element not found
Solutions:
  1. Check Script Loading
<!-- Ensure correct script URL -->
<script src="https://payment.paysight.io/widget-sdk.js"></script>

<!-- Add error handling -->
<script>
  const script = document.createElement('script');
  script.src = 'https://payment.paysight.io/widget-sdk.js';
  script.onerror = (error) => {
    console.error('Failed to load widget SDK:', error);
  };
  document.body.appendChild(script);
</script>
  1. Verify Configuration
// Ensure all required fields are present
const config = {
  productId: 'prod_123',    // Required
  sessionId: 'session_123', // Required
  amount: 2999,            // Required
  currency: 'USD'          // Required
};

// Add error handling
try {
  const widget = PaysightSDK.createWidget({
    targetId: 'widget-container',
    config,
    onError: (error) => {
      console.error('Widget initialization error:', error);
    }
  });
} catch (error) {
  console.error('Failed to create widget:', error);
}
  1. Check DOM Target
const targetElement = document.getElementById('widget-container');
if (!targetElement) {
  console.error('Widget container not found');
  return;
}

// Ensure container is visible and has dimensions
if (targetElement.offsetWidth === 0 || targetElement.offsetHeight === 0) {
  console.warn('Widget container has no dimensions');
}

Payment Processing

Payment Fails with 3DS Error

Symptoms:
  • Payment fails during 3DS verification
  • PAYMENT_3DS_ERROR event
  • User sees 3DS popup but verification fails
Solutions:
  1. Enable 3DS Debugging
const config = {
  threeDSRequired: true,
  debug: true,
  callbacks: {
    onMessage: (message) => {
      if (message.type.includes('3DS')) {
        console.log('3DS Event:', message);
      }
    }
  }
};
  1. Handle 3DS Events Properly
function handle3DSFlow(message) {
  switch (message.type) {
    case 'PAYMENT_3DS_START':
      showLoadingUI('Starting 3D Secure verification...');
      break;
      
    case 'PAYMENT_3DS_ERROR':
      const { code, message: errorMessage } = message.payload;
      
      if (code === '3DS_TIMEOUT') {
        retryPayment();
      } else if (code === '3DS_NOT_SUPPORTED') {
        fallbackToNon3DS();
      } else {
        showError(errorMessage);
      }
      break;
  }
}
Symptoms:
  • Intermittent payment failures
  • Network timeout errors
  • ERROR events
Solutions:
  1. Implement Retry Logic
class PaymentProcessor {
  constructor(widget) {
    this.widget = widget;
    this.retryCount = 0;
    this.maxRetries = 3;
  }

  async processPayment() {
    try {
      await this.widget.validate();
    } catch (error) {
      if (this.shouldRetry(error)) {
        await this.retryPayment();
      } else {
        throw error;
      }
    }
  }

  shouldRetry(error) {
    return (
      error.type === 'NETWORK_ERROR' &&
      this.retryCount < this.maxRetries
    );
  }

  async retryPayment() {
    this.retryCount++;
    console.log(\`Retrying payment (attempt \${this.retryCount})\`);
    
    await new Promise(resolve => setTimeout(resolve, 2000));
    return this.processPayment();
  }
}
  1. Check Network Status
function checkConnectivity() {
  if (!navigator.onLine) {
    showError('You are offline. Please check your internet connection.');
    return false;
  }
  return true;
}

// Add network status listeners
window.addEventListener('online', () => {
  hideError();
  retryFailedPayments();
});

window.addEventListener('offline', () => {
  showError('You are offline. Payment processing will resume when connected.');
});

Validation Issues

Form Validation Errors

Symptoms:
  • Fields show validation errors
  • ERROR events
  • Form submission blocked
Solutions:
  1. Debug Field Values
function debugFieldValues() {
  const state = widget.getState();
  
  Object.entries(state.fields).forEach(([fieldName, field]) => {
    console.log(\`Field: \${fieldName}\`, {
      value: field.value,
      valid: field.valid,
      error: field.error
    });
  });
}
  1. Custom Validation Rules
const config = {
  fields: {
    email: {
      validation: {
        pattern: '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$',
        message: 'Please enter a valid email address'
      }
    },
    phone: {
      validation: {
        pattern: '^\\+?[1-9]\\d{1,14}$',
        message: 'Please enter a valid phone number'
      }
    }
  }
};

Browser Compatibility

Widget Display Issues

Symptoms:
  • Visual glitches
  • Layout problems
  • Style inconsistencies
Solutions:
  1. Check Browser Support
function checkBrowserSupport() {
  const requirements = {
    flexbox: 'flex' in document.documentElement.style,
    grid: 'grid' in document.documentElement.style,
    customProperties: CSS.supports('(--custom-property: value)'),
    fetch: 'fetch' in window,
    promise: 'Promise' in window
  };

  const unsupported = Object.entries(requirements)
    .filter(([, supported]) => !supported)
    .map(([feature]) => feature);

  if (unsupported.length > 0) {
    console.warn('Browser missing required features:', unsupported);
    return false;
  }

  return true;
}
  1. Add Fallback Styles
const config = {
  theme: {
    css: {
      // Modern browsers
      '@supports (display: grid)': {
        '.widget-container': {
          display: 'grid',
          gridTemplateColumns: '1fr 1fr',
          gap: '16px'
        }
      },
      // Fallback for older browsers
      '@supports not (display: grid)': {
        '.widget-container': {
          display: 'flex',
          flexWrap: 'wrap'
        },
        '.field': {
          flex: '0 0 calc(50% - 8px)',
          marginRight: '16px'
        }
      }
    }
  }
};

Debugging Tools

Debug Mode

Enable debug mode to get detailed logging:
const widget = PaysightSDK.createWidget({
  config: {
    debug: true
  },
  onMessage: (message) => {
    console.log('Widget Event:', {
      type: message.type,
      payload: message.payload,
      timestamp: new Date(message.timestamp).toISOString()
    });
  }
});

Network Inspector

Monitor network requests:
class NetworkInspector {
  constructor() {
    this.requests = new Map();
  }

  startMonitoring() {
    const originalFetch = window.fetch;
    
    window.fetch = async (...args) => {
      const requestId = Math.random().toString(36).slice(2);
      const startTime = Date.now();
      
      this.requests.set(requestId, {
        url: args[0],
        startTime,
        status: 'pending'
      });
      
      try {
        const response = await originalFetch(...args);
        this.requests.set(requestId, {
          ...this.requests.get(requestId),
          status: 'complete',
          duration: Date.now() - startTime,
          responseStatus: response.status
        });
        return response;
      } catch (error) {
        this.requests.set(requestId, {
          ...this.requests.get(requestId),
          status: 'error',
          duration: Date.now() - startTime,
          error: error.message
        });
        throw error;
      }
    };
  }

  getRequestLog() {
    return Array.from(this.requests.values());
  }
}

State Inspector

Monitor widget state changes:
class StateInspector {
  constructor(widget) {
    this.widget = widget;
    this.stateHistory = [];
  }

  startMonitoring() {
    setInterval(() => {
      const currentState = this.widget.getState();
      this.stateHistory.push({
        timestamp: Date.now(),
        state: { ...currentState }
      });
    }, 1000);
  }

  getStateChanges() {
    return this.stateHistory;
  }

  getDiff(index) {
    if (index < 1) return null;
    
    const previous = this.stateHistory[index - 1].state;
    const current = this.stateHistory[index].state;
    
    return {
      timestamp: this.stateHistory[index].timestamp,
      changes: this.diffObjects(previous, current)
    };
  }

  diffObjects(obj1, obj2) {
    const changes = {};
    
    Object.keys({ ...obj1, ...obj2 }).forEach(key => {
      if (JSON.stringify(obj1[key]) !== JSON.stringify(obj2[key])) {
        changes[key] = {
          from: obj1[key],
          to: obj2[key]
        };
      }
    });
    
    return changes;
  }
}

Next Steps

I