Skip to content

Error handling

Learn how to diagnose and fix common issues with individual Web components.

Overview

To use this guide, start with the symptom you see, follow the diagnostic steps, then apply the fix.

This guide doesn't cover Checkout Drop-in, which is a separate, all-in-one integration path with its own API (CheckoutDropIn.initialize()) and error codes (SDK11XX). For payment-method-specific problems with individual components, see the links at the end of this page.

Scenario index

Use this index to jump straight to the problem you are debugging:

Payment-method-specific problems are covered in separate troubleshooting guides:

ScenarioGuide
Card validation or tokenisation failuresCard troubleshooting
Google Pay button missing or payment sheet failsGoogle Pay troubleshooting
Apple Pay not available or merchant validation failsApple Pay troubleshooting
PayPal popup blocked or authentication failsPayPal troubleshooting
OAuth flow fails or payout submission errorsPayPal payouts troubleshooting

Error structure

BaseSdkException class

All SDK errors extend BaseSdkException. Import it from the package root. The class exposes ErrorCode, optional details, and the inherited Error fields message and name:

import { BaseSdkException } from '@pxpio/web-components-sdk';

// Typical shape (do not copy this class into your app)
// ErrorCode: string            // Format: SDK####
// details?: any                // Optional additional error details
// message: string              // Inherited from Error
// name: string                 // Exception class name

Error object properties

The exception object has these properties:

PropertyDescription
ErrorCode
string
SDK error code in format SDK#### (e.g., 'SDK0500' for network error, 'SDK0201' for container not found). Use this for programmatic error handling.
message
string
Human-readable error message describing what went wrong. May include dynamic values. Inherited from Error class.
name
string
Exception class name (e.g., 'NetworkSdkException', 'ContainerNotFoundException'). Inherited from Error class.
details
any (optional)
Optional additional error details for debugging.

Basic error handling example

Wrap SDK initialisation and component mounting in a try-catch block to handle BaseSdkException errors by code:

import { PxpCheckout, BaseSdkException } from '@pxpio/web-components-sdk';

try {
  const pxpSdk = PxpCheckout.initialize({
    environment: 'test',
    session: sessionData,
    ownerId: 'MERCHANT-1',
    ownerType: 'MerchantGroup', // initialize always sets this value and mutates the config object
    transactionData: { /* ... */ }
  });
  
  const cardNumber = pxpSdk.create('card-number');
  cardNumber.mount('card-number-container');
  
} catch (error) {
  if (error instanceof BaseSdkException) {
    console.error('SDK error code:', error.ErrorCode);
    console.error('Error message:', error.message);
    
    // Handle specific error codes
    switch (error.ErrorCode) {
      case 'SDK0100':
        showError('SDK configuration is missing. Please contact support.');
        break;
      case 'SDK0201':
        showError('Container element not found. Please check your HTML.');
        break;
      case 'SDK0500':
        showError('Network error. Please check your connection and try again.');
        break;
      default:
        showError(`An error occurred: ${error.message}`);
    }
  } else {
    console.error('Unexpected error:', error);
  }
}

Common scenarios

Connection error during payment

Symptoms:

  • Payment fails with a connection or network error message.
  • onSubmitError or component onError returns SDK0500.
  • Requests to PXP APIs fail in the browser network tab.
  • Intermittent failures that succeed on retry.

Common causes:

Handle SDK0500 on card submit with onSubmitError. For wallet button components in the Web components guides, handle it with onError when that callback receives BaseSdkException. Retry transient failures with exponential backoff.

// Retry card submit on network errors. Configure onSubmitError when creating card-submit.
let networkRetryCount = 0;
const maxNetworkRetries = 3;

const cardSubmit = pxpSdk.create('card-submit', {
  cardNumberComponent: cardNumber,
  cardExpiryDateComponent: cardExpiry,
  cardCvcComponent: cardCvc,

  onSubmitError: (error) => {
    if (!(error instanceof BaseSdkException)) {
      console.error('Unexpected error:', error);
      return;
    }

    if (error.ErrorCode === 'SDK0500' && networkRetryCount < maxNetworkRetries) {
      networkRetryCount++;
      const delay = Math.min(1000 * Math.pow(2, networkRetryCount), 5000);
      setTimeout(() => cardSubmit.submitAsync(), delay);
      return;
    }

    if (error.ErrorCode === 'SDK0500') {
      showError('Connection error. Please check your internet connection and try again.');
      showRetryButton();
      trackError('network_error', { errorCode: error.ErrorCode });
    }
  }
});

pxpSdk.create('google-pay-button', {
  onError: (error) => {
    if (error instanceof BaseSdkException && error.ErrorCode === 'SDK0500') {
      showError('Connection error. Please check your internet connection and try again.');
    }
  }
});

Session expired mid-checkout

Session-related errors occur when session data is invalid, expired, or improperly configured. The SDK SessionData type includes sessionId, hmacKey, and encryptionKey — it doesn't include an expiry timestamp. Track session TTL on your backend and refresh before the session expires.

Symptoms:

  • Payments fail with session-related errors.
  • Components stop working after period of inactivity.
  • "Session expired" or "Session invalid" errors from API responses.

Common causes:

  • Session TTL elapsed on the server.
  • Stale sessionId or hmacKey passed to PxpCheckout.initialize().
  • Session refreshed on the backend but the SDK wasn't reinitialised.

Run this helper to verify the session fields the SDK requires:

function diagnoseSession() {
  console.group('🔍 Session Diagnostics');
  
  const session = pxpSdk.getConfig().session;
  
  console.log('Session ID:', session.sessionId ? 'Present' : 'Missing');
  console.log('HMAC Key:', session.hmacKey ? 'Present' : 'Missing');
  console.log('Encryption Key:', session.encryptionKey ? 'Present' : 'Missing');
  
  console.groupEnd();
}

Calling PxpCheckout.initialize with a new session does not update components that are already mounted. Those instances keep the previous session until you unmount and recreate them. After a successful session refresh:

  • Unmount all mounted components (component.unmount()).
  • Call PxpCheckout.initialize with the new session and the same transactionData, ownerId, and other config.
  • Recreate and mount components from the new instance.

Refresh the session on a schedule using the expiry time from your session API. After remount, ask the shopper to submit again, or call submitAsync() only on the new card-submit instance:

// Store expiry from your backend when creating the session
let sessionExpiresAt = null;

const checkoutMounts = [
  { type: 'card-number', containerId: 'card-number-container', config: cardNumberConfig },
  { type: 'card-expiry-date', containerId: 'card-expiry-container', config: cardExpiryConfig },
  { type: 'card-cvc', containerId: 'card-cvc-container', config: cardCvcConfig },
  { type: 'card-submit', containerId: 'card-submit-container', config: cardSubmitConfig }
];

async function createCheckoutSession() {
  const response = await fetch('/api/sessions', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' }
  }).then(r => r.json());

  sessionExpiresAt = response.expiresAt; // field from your session API
  return response.session; // { sessionId, hmacKey, encryptionKey, allowedFundingTypes }
}

function remountAllComponents(sdk) {
  checkoutMounts.forEach(({ type, containerId, config }) => {
    const component = sdk.create(type, config);
    component.mount(containerId);
    mountedComponents.set(`${type}-${containerId}`, component);
  });
}

async function refreshSessionAndRemount() {
  const newSession = await createCheckoutSession();
  const currentConfig = pxpSdk.getConfig();

  unmountAllComponents(); // same map pattern as Duplicate components

  pxpSdk = PxpCheckout.initialize({
    ...currentConfig,
    session: newSession
  });

  remountAllComponents(pxpSdk);
}

function monitorSessionExpiry(pxpSdkInstance, expiresAt) {
  if (!expiresAt) {
    console.warn('No session expiry time available from backend');
    return;
  }

  const expiresAtDate = new Date(expiresAt);
  const refreshTime = Math.max(expiresAtDate - Date.now() - (5 * 60 * 1000), 0);

  setTimeout(async () => {
    try {
      await refreshSessionAndRemount();
      monitorSessionExpiry(pxpSdk, sessionExpiresAt);
    } catch (error) {
      console.error('Failed to refresh session:', error);
      showError('Your session has expired. Please refresh the page.');
    }
  }, refreshTime);
}

const cardSubmit = pxpSdk.create('card-submit', {
  onSubmitError: async (error) => {
    if (!(error instanceof BaseSdkException)) {
      console.error('Unexpected error:', error);
      return;
    }

    if (error.message.toLowerCase().includes('session')) {
      try {
        await refreshSessionAndRemount();
        showMessage('Session refreshed. Please try your payment again.');
      } catch (refreshError) {
        showError('Session expired. Please refresh the page.');
        setTimeout(() => window.location.reload(), 3000);
      }
    }
  }
});

Session creation fails with invalid signature

Symptoms:

  • Authentication failures when creating session.
  • 401 or 403 errors from PXP API.
  • "Invalid signature" errors.

Common causes:

  • Incorrect HMAC key.
  • Wrong signing algorithm.
  • Clock skew between client and server.
  • Request body modified after signing.

The SDK uses the hmacKey from your session object to sign API requests. Session creation happens on your backend. Follow your PXP API integration guide for the correct signing format. Pass the session object from your backend to PxpCheckout.initialize() without changing sessionId, hmacKey, or encryptionKey:

// Pass the session object from your backend to the SDK without modification
async function initializeCheckout() {
  const { session } = await fetch('/api/sessions', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ amount, currency })
  }).then(r => r.json());

  return PxpCheckout.initialize({
    environment: 'test',
    ownerId: 'MERCHANT-1',
    ownerType: 'MerchantGroup',
    session,
    transactionData: { /* ... */ }
  });
}

PxpCheckout.initialize() always sets ownerType to 'MerchantGroup' and mutates the config object you pass in. 'Merchant' and 'Site' aren't used through this entry point. Pass 'MerchantGroup' (or omit ownerType) and set ownerId to your merchant group ID.

SDK initialisation fails

Configuration errors occur during SDK initialisation when required settings are missing or invalid.

Symptoms:

  • PxpCheckout.initialize() throws on page load.
  • Console shows SDK01XX error codes.
  • Payment form never appears.
  • Error message references missing configuration or session data.

Common causes:

  • Missing or incomplete session object (sessionId, hmacKey).
  • Missing environment (must be 'test' or 'live').
  • Invalid environment value (throws a plain Error, not an SDK01XX code).

PxpCheckout.initialize() validates environment and session id and HMAC only. It also overwrites ownerType to 'MerchantGroup' on the config object before constructing the SDK. encryptionKey and transactionData are required for card flows but are not checked inside initialize(). Card secured fields need encryptionKey. Creating card components such as card-number reads transactionData.intent.card and can throw at create time (e.g., SDK0111) if intent is missing, even when initialize() succeeded.

Validate those fields before calling PxpCheckout.initialize() and before mounting card UI. An invalid environment value throws a plain Error, not an SDK01XX code:

function validateSdkConfig(config) {
  const errors = [];

  if (!config?.environment) {
    errors.push('Environment is required');
  } else if (!['test', 'live'].includes(config.environment)) {
    errors.push('Environment must be "test" or "live"');
  }

  if (!config?.session) {
    errors.push('Session is required');
  } else {
    if (!config.session.sessionId?.trim()) {
      errors.push('Session ID is required');
    }
    if (!config.session.hmacKey?.trim()) {
      errors.push('Session HMAC key is required');
    }
    if (!config.session.encryptionKey?.trim()) {
      errors.push('Session encryption key is required for card secured fields');
    }
  }

  if (!config?.transactionData) {
    errors.push('transactionData is required');
  } else if (!config.transactionData.intent?.card) {
    errors.push('transactionData.intent.card is required for card components');
  }

  if (!config?.ownerId?.trim()) {
    errors.push('ownerId is required');
  }

  return { valid: errors.length === 0, errors };
}

function initializePaymentSDK(config) {
  const validation = validateSdkConfig(config);

  if (!validation.valid) {
    console.error('SDK configuration invalid:', validation.errors);
    showError('Payment system configuration error. Please contact support.');
    logConfigurationError(validation.errors);
    return null;
  }

  try {
    const pxpSdk = PxpCheckout.initialize(config);
    console.log('SDK initialized successfully');
    return pxpSdk;
  } catch (error) {
    console.error('SDK initialisation failed:', error);

    if (error instanceof BaseSdkException && error.ErrorCode?.startsWith('SDK01')) {
      showError('Payment system setup error. Please refresh the page.');
    } else if (error instanceof Error) {
      showError(error.message);
    }

    return null;
  }
}

Payment method not available

Symptoms:

  • A payment method button or form section is missing.
  • SDK throws SDK0107, SDK0108, SDK0109, or SDK0110 when creating a card, PayPal, Apple Pay, or Google Pay component. Aeropay and Paze have their own funding-type codes (SDK0113, SDK0118).
  • Session allowedFundingTypes does not include the requested method.
  • Customer cannot select Card, PayPal, Apple Pay, or Google Pay.

Common causes:

  • Payment method not enabled in the Unity Portal.
  • Session created without the funding type in allowedFundingTypes.
  • Attempting to create a component for a method not configured in the session.

Check payment method availability on load and hide unavailable options rather than failing the entire checkout:

// Progressive payment method availability
class PaymentMethodManager {
  constructor() {
    this.availableMethods = new Set();
    this.failedMethods = new Set();
  }
  
  async checkAvailability() {
    console.log('Checking payment method availability...');
    
    // Check card
    if (this.isCardAvailable()) {
      this.availableMethods.add('card');
      this.showPaymentMethod('card');
    } else {
      this.failedMethods.add('card');
      this.hidePaymentMethod('card');
    }
    
    // Check Google Pay
    try {
      await this.checkGooglePay();
      this.availableMethods.add('google-pay');
      this.showPaymentMethod('google-pay');
    } catch (error) {
      console.warn('Google Pay not available:', error);
      this.failedMethods.add('google-pay');
      this.hidePaymentMethod('google-pay');
    }
    
    // Check Apple Pay
    try {
      await this.checkApplePay();
      this.availableMethods.add('apple-pay');
      this.showPaymentMethod('apple-pay');
    } catch (error) {
      console.warn('Apple Pay not available:', error);
      this.failedMethods.add('apple-pay');
      this.hidePaymentMethod('apple-pay');
    }
    
    // Check PayPal
    if (this.isPayPalAvailable()) {
      this.availableMethods.add('paypal');
      this.showPaymentMethod('paypal');
    } else {
      this.failedMethods.add('paypal');
      this.hidePaymentMethod('paypal');
    }
    
    console.log('Available payment methods:', Array.from(this.availableMethods));
    
    if (this.availableMethods.size === 0) {
      this.showFallbackMessage();
    }
  }
  
  isCardAvailable() {
    const session = pxpSdk.getConfig().session;
    return !!session?.allowedFundingTypes?.cards;
  }
  
  async checkGooglePay() {
    if (!window.google?.payments?.api) {
      throw new Error('Google Pay API not loaded');
    }
    
    const session = pxpSdk.getConfig().session;
    if (!session?.allowedFundingTypes?.wallets?.googlePay?.merchantId) {
      throw new Error('Google Pay not configured');
    }
    
    const paymentsClient = new google.payments.api.PaymentsClient({
      environment: pxpSdk.getConfig().environment === 'live' ? 'PRODUCTION' : 'TEST'
    });
    
    const response = await paymentsClient.isReadyToPay({
      apiVersion: 2,
      apiVersionMinor: 0,
      allowedPaymentMethods: [{
        type: 'CARD',
        parameters: {
          allowedCardNetworks: ['VISA', 'MASTERCARD'],
          allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS']
        }
      }]
    });
    
    if (!response.result) {
      throw new Error('Google Pay not ready');
    }
  }
  
  async checkApplePay() {
    if (!window.ApplePaySession) {
      throw new Error('Apple Pay not supported');
    }
    
    const session = pxpSdk.getConfig().session;
    if (!session?.allowedFundingTypes?.wallets?.applePay?.merchantId) {
      throw new Error('Apple Pay not configured');
    }
    
    if (!ApplePaySession.canMakePayments()) {
      throw new Error('Apple Pay not available on device');
    }
  }
  
  isPayPalAvailable() {
    const session = pxpSdk.getConfig().session;
    return !!session?.allowedFundingTypes?.wallets?.paypal?.allowedFundingOptions;
  }
  
  showPaymentMethod(method) {
    const element = document.getElementById(`${method}-container`);
    if (element) element.style.display = 'block';
  }
  
  hidePaymentMethod(method) {
    const element = document.getElementById(`${method}-container`);
    if (element) element.style.display = 'none';
  }
  
  showFallbackMessage() {
    const message = document.createElement('div');
    message.className = 'payment-unavailable-message';
    message.innerHTML = `
      <p>Payment services are currently unavailable.</p>
      <p>Please try again later or contact support.</p>
    `;
    document.getElementById('payment-container').appendChild(message);
  }
}

const paymentManager = new PaymentMethodManager();
paymentManager.checkAvailability();

When creating a component for an unavailable method, catch the funding-type error and guide the customer to an alternative:

try {
  const googlePay = pxpSdk.create('google-pay-button', config);
  googlePay.mount('google-pay-container');
} catch (error) {
  if (error instanceof BaseSdkException) {
    if (error.ErrorCode === 'SDK0110') {
      console.warn('Google Pay not in allowed funding types');
      hidePaymentMethod('google-pay');
      showMessage('Google Pay is not available. Please use an alternative payment method.');
      return;
    }
    if (error.ErrorCode === 'SDK0201') {
      showError('Payment form configuration error. Please refresh the page.');
      return;
    }
  }
  console.error('Unexpected error:', error);
}

Payment form not rendering

Component lifecycle errors occur during component creation, mounting, or unmounting.

Symptoms:

  • Empty containers where components should appear.
  • No visible input fields or buttons.
  • Console errors about missing containers (SDK0201).
  • Card fields stay empty with no container error (SDK0203).

Common causes:

  • Component mounted before the DOM is ready.
  • Container element ID mismatch or element not yet in the DOM.
  • Container hidden with CSS (display: none, zero width or height).
  • SDK not initialised before the mount attempt.
  • Framework ref not ready (React useEffect or Vue mounted firing too early).
  • Secured card field iframe failed to load (CSP, network, or missing session encryptionKey).

If card fields stay empty and you didn't get SDK0201, handle SDK0203 on the field onLoadFailed callback. Standalone card fields use onLoadFailed on the field config, not onLoadIframeFailed (that name is for click-once and card-on-file). Check CSP, network blocking, and that the session includes encryptionKey.

Run this helper before creating a component to verify the DOM, container element, and SDK initialisation state:

function diagnoseComponentRendering(componentType, containerId) {
  console.group(`🔍 ${componentType} Component Diagnostics`);
  
  // Check 1: Verify DOM is ready
  if (document.readyState !== 'complete' && document.readyState !== 'interactive') {
    console.warn('⚠️ DOM not ready');
  } else {
    console.log('✅ DOM ready');
  }
  
  // Check 2: Verify container exists
  const container = document.getElementById(containerId);
  if (!container) {
    console.error('❌ Container not found:', containerId);
  } else {
    console.log('✅ Container exists');
    
    // Check container visibility
    const styles = window.getComputedStyle(container);
    console.log('Container styles:', {
      display: styles.display,
      visibility: styles.visibility,
      width: styles.width,
      height: styles.height
    });
    
    if (styles.display === 'none') {
      console.warn('⚠️ Container has display:none');
    }
    if (parseInt(styles.width) === 0) {
      console.warn('⚠️ Container has zero width');
    }
  }
  
  // Check 3: Verify SDK is initialized
  if (typeof pxpSdk === 'undefined') {
    console.error('❌ SDK not initialized');
  } else {
    console.log('✅ SDK initialized');
  }
  
  console.groupEnd();
}

// Use before component creation
diagnoseComponentRendering('card-number', 'card-number-container');

Defer component creation until the DOM is ready and use framework-specific mount patterns for React and Vue:

// Solution 1: Ensure proper timing
function createComponentSafely(componentType, containerId, config) {
  // Wait for DOM to be ready
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', () => {
      createComponentSafely(componentType, containerId, config);
    });
    return;
  }
  
  // Verify container exists
  const container = document.getElementById(containerId);
  if (!container) {
    console.error(`Container ${containerId} not found`);
    return;
  }
  
  // Verify SDK is initialized
  if (!pxpSdk) {
    console.error('SDK not initialized');
    return;
  }
  
  // Create and mount component
  try {
    const component = pxpSdk.create(componentType, config);
    component.mount(containerId);
    console.log(`${componentType} component mounted successfully`);
    return component;
  } catch (error) {
    console.error(`Failed to create ${componentType}:`, error);

    if (error instanceof BaseSdkException && error.ErrorCode === 'SDK0201') {
      showError('Payment form configuration error. Please refresh the page.');
      return;
    }
    console.error('Unexpected error:', error);
  }
}

// Solution 2: Framework-specific implementations

// React
function PaymentComponent() {
  const containerRef = useRef(null);
  const componentRef = useRef(null);
  
  useEffect(() => {
    if (!pxpSdk || !containerRef.current) return;
    
    try {
      const component = pxpSdk.create('card-number', config);
      component.mount('card-number-container');
      componentRef.current = component;
      
      console.log('Component mounted');
    } catch (error) {
      console.error('Component mount failed:', error);
    }
    
    // Cleanup
    return () => {
      if (componentRef.current) {
        componentRef.current.unmount();
        console.log('Component unmounted');
      }
    };
  }, [pxpSdk]);
  
  return <div ref={containerRef} id="card-number-container" />;
}

// Vue
export default {
  mounted() {
    if (!this.pxpSdk) {
      console.error('SDK not available');
      return;
    }
    
    try {
      this.component = this.pxpSdk.create('card-number', this.config);
      this.component.mount('card-number-container');
      console.log('Component mounted');
    } catch (error) {
      console.error('Component mount failed:', error);
    }
  },
  beforeUnmount() {
    if (this.component) {
      this.component.unmount();
      console.log('Component unmounted');
    }
  }
};

Duplicate components on the page

Symptoms:

  • Duplicate components in the same container.
  • Component appears multiple times.
  • Warnings about existing components.

Common causes:

  • Component mounted again without unmounting the previous instance.
  • React useEffect re-running on dependency changes without cleanup.
  • React Strict Mode double-mounting during development.
  • Route or tab navigation re-initialising checkout without tearing down components.
  • Multiple calls to mount() on the same container ID.

Track mounted components in a map and unmount them before remounting to prevent duplicates:

// Track mounted components
const mountedComponents = new Map();

function mountComponentOnce(componentType, containerId, config) {
  const key = `${componentType}-${containerId}`;
  
  // Check if already mounted
  if (mountedComponents.has(key)) {
    console.warn(`Component ${componentType} already mounted in ${containerId}`);
    return mountedComponents.get(key);
  }
  
  // Create and mount
  try {
    const component = pxpSdk.create(componentType, config);
    component.mount(containerId);
    
    // Track mounted component
    mountedComponents.set(key, component);
    
    console.log(`${componentType} mounted in ${containerId}`);
    return component;
  } catch (error) {
    console.error(`Failed to mount ${componentType}:`, error);
    return null;
  }
}

function unmountComponent(componentType, containerId) {
  const key = `${componentType}-${containerId}`;
  const component = mountedComponents.get(key);
  
  if (component) {
    component.unmount();
    mountedComponents.delete(key);
    console.log(`${componentType} unmounted from ${containerId}`);
  }
}

// Clean up all components
function unmountAllComponents() {
  mountedComponents.forEach((component, key) => {
    component.unmount();
    console.log(`Unmounted ${key}`);
  });
  mountedComponents.clear();
}

Tokenisation fails at submit

Token Vault errors occur during token creation, storage, or retrieval operations.

Symptoms:

  • Payment submit fails with SDK0304.
  • Customer sees "Unable to process payment" after entering card details.
  • onSubmitError fires but the card form appeared correctly.
  • Intermittent failures that may succeed on retry.

Common causes:

  • Network connectivity issue during token creation.
  • Expired or invalid session at submit time.
  • Token Vault service temporarily unavailable.

Handle token vault failures and BIN lookup failures in onSubmitError. SDK0305 only occurs when card restrictions with an allow list are set. It stops that submit:

const cardSubmit = pxpSdk.create('card-submit', {
  cardNumberComponent: cardNumber,
  cardExpiryDateComponent: cardExpiry,
  cardCvcComponent: cardCvc,
  
  onSubmitError: (error: BaseSdkException) => {
    if (!(error instanceof BaseSdkException)) {
      console.error('Unexpected error:', error);
      return;
    }

    if (error.ErrorCode === 'SDK0304') {
      console.error('Token vault error:', error.message);
      showError('Unable to process payment at this time. Please try again.');
      logError(error, 'token_vault_failure');
      enableRetryButton();
      
      trackError('token_vault_error', {
        errorCode: error.ErrorCode,
        timestamp: Date.now()
      });
    } else if (error.ErrorCode === 'SDK0305') {
      console.error('BIN range lookup failed:', error.message);
      showError('Unable to process payment at this time. Please try again.');
      logError(error, 'bin_range_lookup_failure');
      enableRetryButton();
    }
  }
});

SDK0305 is raised when a BIN range lookup returns no result while card restrictions are in force. That submit does not continue.

3DS challenge iframe not loading

3DS challenge UIs load in an iframe or popup. Loading failures are usually browser or page configuration issues (CSP, mixed content, blocked iframes) rather than SDK SDK04XX codes — those codes are raised for invalid 3DS configuration on the submit or auth request, not for a blank challenge frame.

Symptoms:

  • Authentication window doesn't appear.
  • Payment hangs during 3DS step.
  • White screen during authentication.
  • Console shows iframe loading errors or CSP violations.

Run this helper to check CSP settings, iframe support, and HTTPS requirements for 3DS:

function diagnose3DSSetup() {
  console.group('🔒 3DS Diagnostics');
  
  // Check CSP settings
  const cspMeta = document.querySelector('meta[http-equiv="Content-Security-Policy"]');
  if (cspMeta) {
    console.log('CSP found:', cspMeta.content);
    
    // Check if frame-src allows 3DS servers
    if (!cspMeta.content.includes('frame-src')) {
      console.warn('⚠️ No frame-src directive in CSP');
    }
  } else {
    console.log('No CSP meta tag found');
  }
  
  // Check if iframes are blocked
  try {
    const testIframe = document.createElement('iframe');
    testIframe.src = 'about:blank';
    document.body.appendChild(testIframe);
    console.log('✅ Iframes allowed');
    document.body.removeChild(testIframe);
  } catch (error) {
    console.error('❌ Iframes blocked:', error);
  }
  
  // Check HTTPS
  if (location.protocol !== 'https:') {
    console.warn('⚠️ 3DS requires HTTPS');
  }
  
  console.groupEnd();
}

Configure CSP for 3DS iframes and handle 3DS lifecycle callbacks on card-submit:

// Solution 1: Configure CSP for 3DS
// Add to HTML <head> or via HTTP header
const cspContent = `
  default-src 'self';
  frame-src 'self' https://*.3dsecure.io https://centinelapi.cardinalcommerce.com;
  script-src 'self' 'unsafe-inline' https://pay.google.com;
  style-src 'self' 'unsafe-inline';
`.trim().replace(/\s+/g, ' ');

// Solution 2: Handle 3DS authentication callbacks
function isFailedAuthentication(result) {
  return result && 'errorReason' in result;
}

const cardSubmitConfig = {
  onPostInitiateAuthentication: (result) => {
    if (isFailedAuthentication(result)) {
      console.error('3DS pre-initiate failed:', result.errorReason);
      showError('Card authentication setup failed. Please try again.');
      return;
    }
    showLoadingIndicator('Verifying your card...');
  },

  onPostAuthentication: (result) => {
    if (isFailedAuthentication(result)) {
      hideLoadingIndicator();
      showError('Authentication failed. Please try again or use a different card.');
      return;
    }
    showLoadingIndicator('Completing payment...');
  },

  onSubmitError: (error) => {
    // SDK04XX = invalid 3DS configuration on the auth/submit request (not iframe/CSP load failures)
    if (error instanceof BaseSdkException && error.ErrorCode?.startsWith('SDK04')) {
      console.error('3DS configuration error:', error);
      showError('Card authentication setup failed. Please try again or contact support.');
    }
  }
};

3DS authentication rejected by the bank

Symptoms:

  • Customer completes the 3DS challenge but payment still fails.
  • Error codes SDK0503 (challenge rejected or failed) or SDK0505 (initiate authentication failed) in onSubmitError.
  • Message indicates authentication was rejected or failed.
  • onPostAuthentication receives a FailedAuthenticationResult.

Common causes:

  • Issuing bank declined cardholder verification.
  • Incorrect 3DS merchant configuration.
  • SCA exemption required but not provided (SDK0504, SDK0506).

Handle authentication rejection in onPostAuthentication and onSubmitError:

function isFailedAuthentication(result) {
  return result && 'errorReason' in result;
}

const cardSubmitConfig = {
  onPostAuthentication: (result) => {
    if (isFailedAuthentication(result)) {
      showError('Your bank declined authentication. Please try a different card or contact your bank.');
      enableAlternativePaymentMethods();
      return;
    }
    showLoadingIndicator('Completing payment...');
  },

  onSubmitError: (error) => {
    if (!(error instanceof BaseSdkException)) {
      console.error('Unexpected error:', error);
      return;
    }

    if (error.ErrorCode === 'SDK0503') {
      showError('Authentication rejected by your bank. Please use a different card.');
    } else if (error.ErrorCode === 'SDK0505') {
      showError('Card authentication could not be started. Please try again or use a different card.');
    } else if (error.ErrorCode === 'SDK0504') {
      showError('Additional authentication required. Please contact support.');
      logError(error, 'sca_exemption_required');
    }
  }
};

3DS challenge abandoned or timed out

Symptoms:

  • Payment hangs after the 3DS challenge appears.
  • Customer closes the challenge window without completing it.
  • Error code SDK0503 after a long wait.
  • onPostAuthentication receives a FailedAuthenticationResult.

Common causes:

  • Customer closed the authentication popup or iframe.
  • Challenge timed out before the customer responded.
  • Browser blocked the challenge window.

Use onPreAuthentication to start a timeout and clear it in onPostAuthentication:

function isFailedAuthentication(result) {
  return result && 'errorReason' in result;
}

let authChallengeTimeout;

const submitConfig = {
  onPreAuthentication: async () => {
    showMessage('Please complete authentication when prompted');

    authChallengeTimeout = setTimeout(() => {
      console.warn('3DS challenge timeout');
      showError('Authentication timed out. Please try again.');
      enableRetryButton();
    }, 5 * 60 * 1000);

    return null; // or return your InitiateIntegratedAuthenticationData
  },

  onPostAuthentication: (result) => {
    if (authChallengeTimeout) {
      clearTimeout(authChallengeTimeout);
      authChallengeTimeout = null;
    }

    if (isFailedAuthentication(result)) {
      showError('Authentication was not completed. Please try again.');
    }
  },

  onSubmitError: (error) => {
    if (error instanceof BaseSdkException && error.ErrorCode === 'SDK0503') {
      showError('Authentication timed out or was cancelled. Please try again.');
      enableRetryButton();
    }
  }
};

API requests blocked by CORS or firewall

Symptoms:

  • SDK0500 network errors on every API call.
  • Browser console shows CORS policy errors.
  • Requests blocked in the network tab with no response body.
  • Works in Postman or server-side but fails in the browser.

Common causes:

  • PXP API domain not allowed in CORS configuration.
  • Corporate firewall or proxy blocking outbound requests.
  • Mixed content (HTTP page calling HTTPS API).

Use the browser console and Network tab to confirm CORS or network blocking:

async function diagnoseCorsAndNetwork(backendUrl = '/api/sessions') {
  console.group('🌐 CORS and network diagnostics');

  console.log('Browser online:', navigator.onLine ? '✅' : '❌');

  try {
    const response = await fetch(backendUrl, { method: 'HEAD' });
    const reachable = response.ok || response.status === 405;
    console.log('Backend reachable:', reachable ? '✅' : `❌ Status ${response.status}`);
  } catch (error) {
    console.error('❌ Backend request blocked:', error.message);
    if (error.message.includes('CORS') || error.message.includes('Failed to fetch')) {
      console.error('CORS or network error — allow this page origin on your backend');
    }
  }

  console.log('For SDK0500 on card fields or payments, check Network for blocked requests to PXP API hosts (e.g. api-services.pxp.io).');

  if (location.protocol !== 'https:' && location.hostname !== 'localhost') {
    console.warn('⚠️ Page served over HTTP — API calls may be blocked');
  }

  console.groupEnd();
}

Create sessions on your backend. The checkout SDK still calls PXP API hosts from the browser for tokenisation, payments, and wallets, which is why SDK0500 can appear on those hosts in the Network tab. Ensure those hosts are reachable and not blocked by CSP connect-src, proxies, or mixed content. If you proxy PXP API calls, configure CORS on your backend to allow your checkout domain.

Customer browser not supported

Symptoms:

  • Payment form fails to load for some customers but works for others.
  • SDK errors or blank components on older browsers.
  • Wallet buttons (Apple Pay, Google Pay) missing on unsupported devices.
  • Console warnings about missing browser APIs.

Common causes:

  • Older browser without the APIs the checkout page needs.
  • Page not served over HTTPS.
  • Browser missing Promise or Fetch API support.

Run compatibility checks before initialising the SDK. Confirm HTTPS, Promise, and Fetch. Wallet buttons also depend on device and browser support:

function checkBrowserCompatibility() {
  const issues = [];

  if (location.protocol !== 'https:' && location.hostname !== 'localhost') {
    issues.push({
      severity: 'critical',
      message: 'HTTPS required for payment processing',
      fix: 'Serve your site over HTTPS'
    });
  }

  if (typeof Promise === 'undefined') {
    issues.push({ severity: 'critical', message: 'Browser does not support Promises', fix: 'Use a modern browser' });
  }

  if (typeof fetch === 'undefined') {
    issues.push({ severity: 'critical', message: 'Browser does not support Fetch API', fix: 'Use a modern browser or add a polyfill' });
  }

  return {
    supported: issues.filter(i => i.severity === 'critical').length === 0,
    issues: issues
  };
}

const compatibility = checkBrowserCompatibility();
if (!compatibility.supported) {
  console.error('Browser compatibility issues:', compatibility.issues);
  const criticalIssues = compatibility.issues.filter(i => i.severity === 'critical');
  if (criticalIssues.length > 0) {
    showError(`Your browser is not supported. ${criticalIssues[0].fix}`);
  }
}

Implementation patterns

The following patterns apply across multiple scenarios above. Use them to build consistent error handling into your checkout integration.

Error monitoring and logging

Centralise error capture in a monitor class that forwards SDK errors to your logging and analytics services:

// Centralised error monitoring
class PaymentErrorMonitor {
  constructor() {
    this.errors = [];
  }
  
  captureError(error, context) {
    if (!(error instanceof BaseSdkException)) {
      console.error('Unexpected error:', error);
      return;
    }

    const errorData = {
      timestamp: new Date().toISOString(),
      errorCode: error.ErrorCode,
      errorName: error.name,
      message: error.message,
      stack: error.stack,
      context: context,
      userAgent: navigator.userAgent,
      url: location.href,
      sessionId: this.getSessionId()
    };
    
    this.errors.push(errorData);
    
    // Send to monitoring service
    this.sendToMonitoring(errorData);
    
    // Track in analytics
    this.trackInAnalytics(errorData);
    
    // Alert on critical errors
    if (this.isCritical(error)) {
      this.alertOpsTeam(errorData);
    }
  }
  
  isCritical(error) {
    if (!(error instanceof BaseSdkException)) return false;

    // Network errors
    if (error.ErrorCode === 'SDK0500') return true;
    
    // Configuration errors
    if (error.ErrorCode?.startsWith('SDK01')) return true;
    
    // Payment method load failures
    const loadFailures = ['SDK0600', 'SDK0704', 'SDK1200'];
    if (loadFailures.includes(error.ErrorCode)) return true;
    
    return false;
  }
  
  sendToMonitoring(data) {
    // Example: Sentry
    if (window.Sentry) {
      Sentry.captureException(new Error(data.message), {
        extra: data,
        tags: {
          errorCode: data.errorCode,
          component: data.context.component
        }
      });
    }
    
    // Example: Custom logging endpoint
    fetch('/api/errors', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    }).catch(err => console.error('Failed to log error:', err));
  }
  
  trackInAnalytics(data) {
    // Example: Google Analytics
    if (window.gtag) {
      gtag('event', 'payment_error', {
        error_code: data.errorCode,
        error_name: data.errorName,
        component: data.context.component
      });
    }
    
    // Example: Mixpanel
    if (window.mixpanel) {
      mixpanel.track('Payment Error', {
        'Error Code': data.errorCode,
        'Error Name': data.errorName,
        'Component': data.context.component
      });
    }
  }
  
  alertOpsTeam(data) {
    // Send critical errors to operations team
    fetch('/api/alerts/critical', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        type: 'payment_error',
        severity: 'critical',
        data: data
      })
    });
  }
  
  getSessionId() {
    return pxpSdk?.getConfig()?.session?.sessionId || 'unknown';
  }
  
  getErrorReport() {
    return {
      totalErrors: this.errors.length,
      errorsByCode: this.groupBy(this.errors, 'errorCode'),
      errorsByComponent: this.groupBy(this.errors, 'context.component'),
      recentErrors: this.errors.slice(-10)
    };
  }
  
  groupBy(array, key) {
    return array.reduce((acc, item) => {
      const value = key.split('.').reduce((obj, k) => obj?.[k], item);
      acc[value] = (acc[value] || 0) + 1;
      return acc;
    }, {});
  }
}

// Initialize global error monitor
const errorMonitor = new PaymentErrorMonitor();

// Use in component configurations
const cardSubmitConfig = {
  onSubmitError: (error) => {
    errorMonitor.captureError(error, { component: 'card-submit' });
    handleCardError(error);
  }
};

const googlePayConfig = {
  onError: (error) => {
    errorMonitor.captureError(error, { component: 'google-pay' });
    handleGooglePayError(error);
  }
};

User-friendly error messaging

Map error codes to user-facing messages instead of exposing raw SDK error text:

// Error message mapping
const errorMessages = {
  // Network errors
  'SDK0500': 'Connection error. Please check your internet connection and try again.',
  
  // Configuration errors
  'SDK0100': 'Payment system setup error. Please refresh the page.',
  'SDK0107': 'Card payments are not available. Please use an alternative payment method.',
  'SDK0108': 'PayPal is not available. Please use an alternative payment method.',
  'SDK0109': 'Apple Pay is not available. Please use an alternative payment method.',
  'SDK0110': 'Google Pay is not available. Please use an alternative payment method.',
  
  // Component errors
  'SDK0200': 'Invalid component configuration. Please refresh the page.',
  'SDK0201': 'Payment form not initialized. Please refresh the page.',
  'SDK0202': 'No payment methods available. Please use an alternative payment method.',

  // Transaction / 3DS authentication errors
  'SDK0503': 'Authentication was rejected, failed, or cancelled. Please try again or use a different card.',
  'SDK0504': 'Additional authentication required. Please contact support.',
  'SDK0505': 'Card authentication could not be started. Please try again or use a different card.',

  // Payment method specific
  'SDK0600': 'Unable to load Apple Pay. Please use an alternative payment method.',
  'SDK0704': 'Unable to load Google Pay. Please use an alternative payment method.',
  'SDK0801': 'Unable to load PayPal. Please use an alternative payment method.',
  
  // Generic fallback
  'default': 'An error occurred. Please try again or contact support.'
};

function getUserFriendlyMessage(error) {
  if (error instanceof BaseSdkException && error.ErrorCode && errorMessages[error.ErrorCode]) {
    return errorMessages[error.ErrorCode];
  }
  
  // Check for specific keywords in error message
  const message = (error?.message || '').toLowerCase();
  
  if (message.includes('session') && message.includes('expired')) {
    return 'Your session has expired. Please refresh the page.';
  }
  
  if (message.includes('network') || message.includes('connection')) {
    return 'Connection error. Please check your internet and try again.';
  }
  
  if (message.includes('declined')) {
    return 'Payment declined. Please try a different payment method.';
  }
  
  return errorMessages.default;
}

// Display error to user
function showError(error) {
  const message = typeof error === 'string' ? error : getUserFriendlyMessage(error);
  
  // Create error element
  const errorDiv = document.createElement('div');
  errorDiv.className = 'payment-error-message';
  errorDiv.innerHTML = `
    <div class="error-icon">⚠️</div>
    <div class="error-text">${message}</div>
    <button class="error-close" onclick="this.parentElement.remove()">×</button>
  `;
  
  // Add to page
  const container = document.getElementById('error-container') || document.body;
  container.appendChild(errorDiv);
  
  // Auto-dismiss after 10 seconds
  setTimeout(() => {
    errorDiv.remove();
  }, 10000);
}

Best practices

These patterns complement the scenario fixes above and should be applied throughout your integration.

Always catch exceptions

Wrap SDK operations in try-catch blocks to handle errors gracefully.

// ❌ Bad - unhandled error
const cardNumber = pxpSdk.create('card-number');
cardNumber.mount('card-number-container'); // May throw

// ✅ Good - error handling
try {
  const cardNumber = pxpSdk.create('card-number');
  cardNumber.mount('card-number-container');
} catch (error) {
  if (error instanceof BaseSdkException) {
    console.error('Component error:', error.ErrorCode, error.message);
    handleComponentError(error);
  } else {
    console.error('Unexpected error:', error);
  }
}

Use error codes for logic, messages for users

Use ErrorCode for programmatic handling, but show user-friendly messages based on message or custom mapping.

// ✅ Good - programmatic handling with user-friendly messages
if (error instanceof BaseSdkException) {
  // Use error code for logic
  if (error.ErrorCode === 'SDK0500') {
    // Network error - allow retry
    showError('Connection issue. Please check your internet and try again.');
    enableRetryButton();
  } else if (error.ErrorCode === 'SDK0201') {
    // Container not found - critical error
    showError('Payment form error. Please refresh the page.');
    logCriticalError(error);
  } else {
    // Use message for user feedback
    showError(error.message);
  }
}

Implement retry logic for transient errors

Limit retries to network errors and other transient failures. submitAsync() doesn't throw on failure; it invokes onSubmitError instead, so implement exponential backoff in that callback.

let networkRetryCount = 0;
const maxRetries = 3;

const cardSubmit = pxpSdk.create('card-submit', {
  cardNumberComponent: cardNumber,
  cardExpiryDateComponent: cardExpiry,
  cardCvcComponent: cardCvc,

  onPostAuthorisation: (result) => {
    networkRetryCount = 0;
    handlePaymentSuccess(result);
  },

  onSubmitError: (error) => {
    if (error instanceof BaseSdkException && error.ErrorCode === 'SDK0500' && networkRetryCount < maxRetries) {
      networkRetryCount++;
      console.log(`Network error. Retrying... (${networkRetryCount}/${maxRetries})`);
      const delay = Math.pow(2, networkRetryCount) * 1000;
      setTimeout(() => cardSubmit.submitAsync(), delay);
      return;
    }
    handleSubmitError(error);
  }
});

Log errors for monitoring

Always log errors to your monitoring service for diagnostics and alerting.

function logError(error: BaseSdkException, context: string) {
  // Send to error tracking service (Sentry example)
  if (window.Sentry) {
    Sentry.captureException(error, {
      tags: {
        errorCode: error.ErrorCode,
        context: context
      },
      extra: {
        errorMessage: error.message,
        errorName: error.name
      }
    });
  }
  
  // Log to console in development
  if (process.env.NODE_ENV === 'development') {
    console.error(`[${context}] ${error.ErrorCode}: ${error.message}`);
  }
  
  // Track in analytics
  if (window.gtag) {
    gtag('event', 'sdk_error', {
      error_code: error.ErrorCode,
      error_message: error.message,
      context: context,
      url: window.location.href
    });
  }
}

Handle session expiration gracefully

Implement session monitoring and refresh logic to prevent payment failures.

function isSessionError(error: BaseSdkException): boolean {
  return error.message.toLowerCase().includes('session') ||
         error.message.toLowerCase().includes('expired');
}

async function handleSessionError(error: BaseSdkException, currentSdk: PxpCheckout): Promise<PxpCheckout | null> {
  if (isSessionError(error)) {
    showMessage('Your session has expired. Creating new session...');
    
    try {
      // refreshSessionAndRemount is defined in Session expired mid-checkout
      await refreshSessionAndRemount();
      showMessage('Session refreshed. Please try your payment again.');
      return pxpSdk;
      
    } catch (refreshError) {
      console.error('Failed to refresh session:', refreshError);
      showError('Unable to refresh session. Please reload the page.');
      return null;
    }
  }
  return currentSdk;
}

Debugging tips

Use these tools during development to reproduce and diagnose the scenarios above.

Enable verbose logging

Set up comprehensive logging to catch all SDK errors during development.

// In development, log all errors including unhandled rejections
if (process.env.NODE_ENV === 'development') {
  window.addEventListener('unhandledrejection', (event) => {
    if (event.reason instanceof BaseSdkException) {
      console.group('🔴 Unhandled SDK Error');
      console.error('Code:', event.reason.ErrorCode);
      console.error('Message:', event.reason.message);
      console.error('Name:', event.reason.name);
      console.error('Stack:', event.reason.stack);
      console.groupEnd();
      
      // Prevent default handling
      event.preventDefault();
    }
  });
  
  // Log all SDK operations
  console.log('🔧 Debug mode enabled for PXP SDK');
}

Test error scenarios

Use query parameters or feature flags to simulate errors in development.

import { BaseSdkException, SdkErrorCodes } from '@pxpio/web-components-sdk';

// Test error scenarios in development
if (process.env.NODE_ENV === 'development') {
  const urlParams = new URLSearchParams(window.location.search);
  
  // Simulate network error: ?test=network-error
  if (urlParams.get('test') === 'network-error') {
    throw new BaseSdkException(SdkErrorCodes.NETWORK_ERROR);
  }
  
  // Simulate authentication failure: ?test=auth-failed
  if (urlParams.get('test') === 'auth-failed') {
    throw new BaseSdkException(SdkErrorCodes.TRANSACTION_AUTHENTICATION_FAILED);
  }
  
  // Simulate missing session id at initialize(): ?test=missing-session-id
  // This throws SDK0103. For mid-checkout expiry, see Session expired mid-checkout.
  if (urlParams.get('test') === 'missing-session-id') {
    throw new BaseSdkException(SdkErrorCodes.SDK_CONFIG_SESSION_ID_EMPTY);
  }
  
  // Simulate container not found: ?test=no-container
  if (urlParams.get('test') === 'no-container') {
    throw new BaseSdkException(SdkErrorCodes.COMPONENT_CONTAINER_NOT_FOUND, { id: 'card-number-container' });
  }
}

Debug helper function

Create a debug helper to inspect SDK state and diagnose issues.

function debugPaymentSDK() {
  console.group('🔍 PXP SDK Debug Info');
  
  // SDK initialisation
  if (typeof pxpSdk === 'undefined') {
    console.error('❌ SDK not initialized');
    console.groupEnd();
    return;
  }
  console.log('✅ SDK initialized');
  
  // Session info
  const config = pxpSdk.getConfig();
  const session = config.session;
  
  console.group('Session');
  console.log('Session ID:', session.sessionId ? '✅ Present' : '❌ Missing');
  console.log('HMAC Key:', session.hmacKey ? '✅ Present' : '❌ Missing');
  console.log('Encryption Key:', session.encryptionKey ? '✅ Present' : '❌ Missing');
  console.log('Environment:', config.environment || 'unknown');
  console.groupEnd();
  
  // Payment methods
  console.group('Payment methods');
  const fundingTypes = session.allowedFundingTypes || {};
  const wallets = fundingTypes.wallets || {};
  console.log('Card:', fundingTypes.cards ? '✅ Enabled' : '❌ Disabled');
  console.log('PayPal:', wallets.paypal?.allowedFundingOptions ? '✅ Enabled' : '❌ Disabled');
  console.log('Apple Pay:', wallets.applePay?.merchantId ? '✅ Enabled' : '❌ Disabled');
  console.log('Google Pay:', wallets.googlePay?.merchantId ? '✅ Enabled' : '❌ Disabled');
  console.groupEnd();
  
  // Browser environment
  console.group('Environment');
  console.log('HTTPS:', location.protocol === 'https:' ? '✅' : '❌');
  console.log('User agent:', navigator.userAgent);
  console.log('Viewport:', `${window.innerWidth}x${window.innerHeight}`);
  console.groupEnd();
  
  console.groupEnd();
}

// Call from browser console: debugPaymentSDK()
window.debugPaymentSDK = debugPaymentSDK;

Error code reference

Use this section to look up error codes returned by the SDK when integrating individual Web Components. For step-by-step fixes, start with the scenario index above.

Error codes in the SDK11XX range belong to Checkout Drop-in and aren't covered in this guide.

Error categories

The SDK groups codes by the first two digits after SDK:

Code rangeCategoryDescription
SDK00XXCommon errorsUnexpected errors and general failures
SDK01XXSDK errorsConfiguration and initialisation errors
SDK02XXComponent errorsComponent creation and mounting errors
SDK03XXToken Vault errorsToken creation and management errors
SDK04XX3D Secure errorsAuthentication and 3DS flow errors
SDK05XXTransaction errorsNetwork and transaction processing errors
SDK06XXApple Pay errorsApple Pay specific errors
SDK07XXGoogle Pay errorsGoogle Pay specific errors
SDK08XXPayPal errorsPayPal specific errors
SDK09XXVenmo errorsVenmo payout specific errors
SDK10XXPayout errorsCommon payout errors
SDK11XXCheckout Drop-in errorsNot covered in this guide. See Checkout Drop-in documentation.
SDK12XXPaze errorsPaze button component errors
SDK13XXAeropay errorsAeropay button component errors

Common errors (SDK00xx)

These codes cover unexpected failures and unimplemented paths:

Error codeExceptionDescription
SDK0000UnexpectedSdkExceptionUnexpected error.
SDK0001NotImplementedSdkExceptionNot implemented.

Configuration errors (SDK01xx)

These codes are thrown during PxpCheckout.initialize or create when configuration, intent, or funding types are missing:

Error codeExceptionDescription
SDK0100MissingConfigExceptionSDK configuration object is missing.
SDK0101MissingConfigEnvironmentExceptionEnvironment is missing from SDK configuration.
SDK0102MissingConfigSessionExceptionSession data is missing from SDK configuration.
SDK0103MissingConfigSessionIdExceptionSession ID is missing or empty.
SDK0104MissingConfigSessionHmackeyExceptionSession HMAC key is missing or empty.
SDK0105UnsupportedPaymentMethodSdkExceptionUnsupported payment method component type.
SDK0106IntentTypeNotSupportComponentSdkExceptionIntent type does not support the requested component.
SDK0107UnsupportedFundingTypeCardSdkExceptionCard isn't included in allowed funding types (session.allowedFundingTypes.cards is missing).
SDK0108UnsupportedFundingTypePaypalSdkExceptionPayPal isn't included in allowed funding types (missing wallets.paypal.allowedFundingOptions).
SDK0109UnsupportedFundingTypeApplePaySdkExceptionApple Pay isn't included in allowed funding types (missing wallets.applePay.merchantId).
SDK0110UnsupportedFundingTypeGooglePaySdkExceptionGoogle Pay isn't included in allowed funding types (missing wallets.googlePay.merchantId).
SDK0111MissingIntentTypeCardSdkExceptionCard intent is required for the component you are creating.
SDK0112MissingIntentTypePaypalSdkExceptionPayPal intent is required for the PayPal button.
SDK0113UnsupportedFundingTypeAeropaySdkExceptionAeropay isn't available for this session (missing payByBanks.aeropay.externalMerchantId or configurationId).
SDK0114UnsupportedEntryTypeAeropaySdkExceptionAeropay only supports the Ecom entry type.
SDK0115MissingIntentTypeAeropaySdkExceptionAeropay intent is required for the Aeropay button.
SDK0116UnsupportedCurrencyAeropaySdkExceptionAeropay only supports USD.
SDK0118UnsupportedFundingTypePazeSdkExceptionPaze isn't available for this session (missing wallets.paze.clientId).

An invalid environment value (not 'test' or 'live') throws a plain Error, not an SDK01XX code.

Component lifecycle errors (SDK02xx)

These codes are thrown when a component cannot be created, mounted, or a secured field fails to load:

Error codeExceptionDescription
SDK0200MissingComponentNameExceptionComponent name is missing or empty.
SDK0201ContainerNotFoundExceptionThe container element doesn't exist in the DOM.
SDK0202NoPaymentMethodsAvailableExceptionNo payment methods are available for this session. Raised by Checkout Drop-in, not by individual Web Components.
SDK0203FieldLoadFailedExceptionFailed to load a secured card field.

Token Vault errors (SDK03xx)

These codes are raised during card submit when tokenisation or BIN lookup fails:

Error codeExceptionDescription
SDK0304TokenVaultExceptionToken creation or storage failed.
SDK0305BinRangeLookupExceptionBIN range lookup returned no result while card restrictions with an allow list are set. That submit stops and onSubmitError fires.

3DS pre-initiate authentication errors (SDK0400-SDK0408)

These codes are raised when pre-initiate 3DS request data is invalid:

Error codeExceptionDescription
SDK0400CurrencyCodeInvalidExceptionCurrency code must be 3 characters (ISO 4217).
SDK0401FingerprintCallbackUrlEmptyExceptionFingerprint callback URL is required for 3DS.
SDK0402AcquirerProfileIdInvalidExceptionAcquirer profile ID must be 0 or 36 characters.
SDK0403ProviderIdInvalidExceptionProvider ID must be 36 characters or less.
SDK0404GatewayTokenIdInvalidExceptionGateway token ID must be exactly 36 characters.
SDK0405SchemeTokenIdInvalidExceptionScheme token ID must be exactly 36 characters.
SDK0406CardTokenIdRequiredExceptionCard token ID is required for authentication.
SDK0407CurrencyNotRequireDecimalExceptionCurrency does not accept decimal places (e.g., JPY).
SDK0408CurrencyInvalidDecimalExceptionIncorrect decimal places for currency.

3DS initiate authentication errors (SDK0409-SDK0423)

These codes are raised when initiate-3DS request data is invalid or 3DS isn't enabled:

Error codeExceptionDescription
SDK0409CountryNumericCodeInvalidExceptionCountry numeric code is invalid (ISO 3166-1).
SDK0410MerchantLegalNameInvalidExceptionMerchant legal name is invalid.
SDK0411ChallengeWindowSizeInvalidExceptionChallenge window size is invalid (must be 1-5).
SDK0412ChallengeCallbackUrlEmptyExceptionChallenge callback URL is required.
SDK0413AcceptHeaderEmptyExceptionAccept header is required in request.
SDK0414LanguageInvalidExceptionInvalid language format (must be IETF BCP 47).
SDK0415ScreenHeightInvalidExceptionScreen height is invalid.
SDK0416ScreenWidthInvalidExceptionScreen width is invalid.
SDK0417EmailInvalidExceptionEmail address format is invalid.
SDK0418RecurringExpirationDateInvalidExceptionRecurring expiration date is invalid (ISO 8601).
SDK0419RecurringFrequencyInDaysInvalidExceptionRecurring frequency in days is invalid.
SDK0420IpAddressInvalidExceptionIP address format is invalid (IPv4/IPv6).
SDK0421RequestorChallengeIndicatorInvalidExceptionRequestor challenge indicator is invalid (must be 01, 02, 03, 04, 05, or 10).
SDK0422ThreeDSServiceNotEnabledException3D Secure service isn't enabled for this merchant.
SDK0423AuthenticationConfigurationInvalidExceptionAuthentication configuration is invalid.

Network and transaction errors (SDK05xx)

These codes cover network failures and 3DS or transaction authentication outcomes. The Scenario column links to the matching section above:

Error codeExceptionDescriptionScenario
SDK0500NetworkSdkExceptionNetwork connectivity error during API request.Connection error, CORS blocked
SDK0501ValidationExceptionField or configuration validation failed.
SDK0502PreInitiateAuthenticationFailedExceptionPre-initiate authentication failed.
SDK0503TransactionAuthenticationRejectedExceptionAuthentication rejected by the issuing bank, or the challenge failed or was abandoned.3DS rejected by bank, 3DS timed out
SDK0504TransactionAuthenticationRequireScaExemptionExceptionSCA exemption type not provided when required.3DS rejected by bank
SDK0505AuthenticationFailedExceptionInitiate authentication failed.3DS rejected by bank
SDK0506TransactionAuthenticationInvalidExceptionInvalid SCA exemption value.3DS rejected by bank
SDK0507AuthenticationRetrieveStrategyFailedExceptionFailed to retrieve authentication strategy.
SDK0508AuthenticationRetrieveMerchantSetupFailedExceptionMerchant 3DS configuration unavailable or invalid.
SDK0510AuthenticationRetrieveResultFailedException3DS authentication result could not be retrieved.

What's next?

For payment-method-specific scenarios, see: