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