Learn how to diagnose and fix common issues with individual Web components.
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.
Use this index to jump straight to the problem you are debugging:
| Scenario | Typical error codes |
|---|---|
| Connection error during payment | SDK0500 |
| Session expired mid-checkout | Session-related API messages |
| Session creation fails with invalid signature | 401/403 from PXP API |
| SDK initialisation fails | SDK01XX |
| Payment method not available | SDK0107–SDK0110, plus SDK0113, SDK0118 |
| Payment form not rendering | SDK0201, SDK0203 |
| Duplicate components on the page | — |
| Tokenisation fails at submit | SDK0304 |
| 3DS challenge iframe not loading | Browser / CSP |
| 3DS authentication rejected by the bank | SDK0503, SDK0505 |
| 3DS challenge abandoned or timed out | SDK0503 |
| API requests blocked by CORS or firewall | SDK0500 |
| Customer browser not supported | — |
Payment-method-specific problems are covered in separate troubleshooting guides:
| Scenario | Guide |
|---|---|
| Card validation or tokenisation failures | Card troubleshooting |
| Google Pay button missing or payment sheet fails | Google Pay troubleshooting |
| Apple Pay not available or merchant validation fails | Apple Pay troubleshooting |
| PayPal popup blocked or authentication fails | PayPal troubleshooting |
| OAuth flow fails or payout submission errors | PayPal payouts troubleshooting |
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 nameThe exception object has these properties:
| Property | Description |
|---|---|
ErrorCodestring | SDK error code in format SDK#### (e.g., 'SDK0500' for network error, 'SDK0201' for container not found). Use this for programmatic error handling. |
messagestring | Human-readable error message describing what went wrong. May include dynamic values. Inherited from Error class. |
namestring | Exception class name (e.g., 'NetworkSdkException', 'ContainerNotFoundException'). Inherited from Error class. |
detailsany (optional) | Optional additional error details for debugging. |
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);
}
}Symptoms:
- Payment fails with a connection or network error message.
onSubmitErroror componentonErrorreturnsSDK0500.- Requests to PXP APIs fail in the browser network tab.
- Intermittent failures that succeed on retry.
Common causes:
- Internet connection lost.
- Firewall or corporate proxy blocking requests.
- API endpoint unreachable.
- CORS misconfiguration (see API requests blocked by CORS or firewall).
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-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
sessionIdorhmacKeypassed toPxpCheckout.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.initializewith the newsessionand the sametransactionData,ownerId, and other config. - Recreate and
mountcomponents 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);
}
}
}
});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.
Configuration errors occur during SDK initialisation when required settings are missing or invalid.
Symptoms:
PxpCheckout.initialize()throws on page load.- Console shows
SDK01XXerror 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
environmentvalue (throws a plainError, not anSDK01XXcode).
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;
}
}Symptoms:
- A payment method button or form section is missing.
- SDK throws
SDK0107,SDK0108,SDK0109, orSDK0110when creating a card, PayPal, Apple Pay, or Google Pay component. Aeropay and Paze have their own funding-type codes (SDK0113,SDK0118). - Session
allowedFundingTypesdoes 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);
}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
useEffector Vuemountedfiring 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');
}
}
};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
useEffectre-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();
}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.
onSubmitErrorfires 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 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.');
}
}
};Symptoms:
- Customer completes the 3DS challenge but payment still fails.
- Error codes
SDK0503(challenge rejected or failed) orSDK0505(initiate authentication failed) inonSubmitError. - Message indicates authentication was rejected or failed.
onPostAuthenticationreceives aFailedAuthenticationResult.
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');
}
}
};Symptoms:
- Payment hangs after the 3DS challenge appears.
- Customer closes the challenge window without completing it.
- Error code
SDK0503after a long wait. onPostAuthenticationreceives aFailedAuthenticationResult.
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();
}
}
};Symptoms:
SDK0500network 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.
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}`);
}
}The following patterns apply across multiple scenarios above. Use them to build consistent error handling into your checkout integration.
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);
}
};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);
}These patterns complement the scenario fixes above and should be applied throughout your integration.
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 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);
}
}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);
}
});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
});
}
}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;
}Use these tools during development to reproduce and diagnose the scenarios above.
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');
}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' });
}
}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;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.
The SDK groups codes by the first two digits after SDK:
| Code range | Category | Description |
|---|---|---|
SDK00XX | Common errors | Unexpected errors and general failures |
SDK01XX | SDK errors | Configuration and initialisation errors |
SDK02XX | Component errors | Component creation and mounting errors |
SDK03XX | Token Vault errors | Token creation and management errors |
SDK04XX | 3D Secure errors | Authentication and 3DS flow errors |
SDK05XX | Transaction errors | Network and transaction processing errors |
SDK06XX | Apple Pay errors | Apple Pay specific errors |
SDK07XX | Google Pay errors | Google Pay specific errors |
SDK08XX | PayPal errors | PayPal specific errors |
SDK09XX | Venmo errors | Venmo payout specific errors |
SDK10XX | Payout errors | Common payout errors |
SDK11XX | Checkout Drop-in errors | Not covered in this guide. See Checkout Drop-in documentation. |
SDK12XX | Paze errors | Paze button component errors |
SDK13XX | Aeropay errors | Aeropay button component errors |
These codes cover unexpected failures and unimplemented paths:
| Error code | Exception | Description |
|---|---|---|
SDK0000 | UnexpectedSdkException | Unexpected error. |
SDK0001 | NotImplementedSdkException | Not implemented. |
These codes are thrown during PxpCheckout.initialize or create when configuration, intent, or funding types are missing:
| Error code | Exception | Description |
|---|---|---|
SDK0100 | MissingConfigException | SDK configuration object is missing. |
SDK0101 | MissingConfigEnvironmentException | Environment is missing from SDK configuration. |
SDK0102 | MissingConfigSessionException | Session data is missing from SDK configuration. |
SDK0103 | MissingConfigSessionIdException | Session ID is missing or empty. |
SDK0104 | MissingConfigSessionHmackeyException | Session HMAC key is missing or empty. |
SDK0105 | UnsupportedPaymentMethodSdkException | Unsupported payment method component type. |
SDK0106 | IntentTypeNotSupportComponentSdkException | Intent type does not support the requested component. |
SDK0107 | UnsupportedFundingTypeCardSdkException | Card isn't included in allowed funding types (session.allowedFundingTypes.cards is missing). |
SDK0108 | UnsupportedFundingTypePaypalSdkException | PayPal isn't included in allowed funding types (missing wallets.paypal.allowedFundingOptions). |
SDK0109 | UnsupportedFundingTypeApplePaySdkException | Apple Pay isn't included in allowed funding types (missing wallets.applePay.merchantId). |
SDK0110 | UnsupportedFundingTypeGooglePaySdkException | Google Pay isn't included in allowed funding types (missing wallets.googlePay.merchantId). |
SDK0111 | MissingIntentTypeCardSdkException | Card intent is required for the component you are creating. |
SDK0112 | MissingIntentTypePaypalSdkException | PayPal intent is required for the PayPal button. |
SDK0113 | UnsupportedFundingTypeAeropaySdkException | Aeropay isn't available for this session (missing payByBanks.aeropay.externalMerchantId or configurationId). |
SDK0114 | UnsupportedEntryTypeAeropaySdkException | Aeropay only supports the Ecom entry type. |
SDK0115 | MissingIntentTypeAeropaySdkException | Aeropay intent is required for the Aeropay button. |
SDK0116 | UnsupportedCurrencyAeropaySdkException | Aeropay only supports USD. |
SDK0118 | UnsupportedFundingTypePazeSdkException | Paze 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.
These codes are thrown when a component cannot be created, mounted, or a secured field fails to load:
| Error code | Exception | Description |
|---|---|---|
SDK0200 | MissingComponentNameException | Component name is missing or empty. |
SDK0201 | ContainerNotFoundException | The container element doesn't exist in the DOM. |
SDK0202 | NoPaymentMethodsAvailableException | No payment methods are available for this session. Raised by Checkout Drop-in, not by individual Web Components. |
SDK0203 | FieldLoadFailedException | Failed to load a secured card field. |
These codes are raised during card submit when tokenisation or BIN lookup fails:
| Error code | Exception | Description |
|---|---|---|
SDK0304 | TokenVaultException | Token creation or storage failed. |
SDK0305 | BinRangeLookupException | BIN range lookup returned no result while card restrictions with an allow list are set. That submit stops and onSubmitError fires. |
These codes are raised when pre-initiate 3DS request data is invalid:
| Error code | Exception | Description |
|---|---|---|
SDK0400 | CurrencyCodeInvalidException | Currency code must be 3 characters (ISO 4217). |
SDK0401 | FingerprintCallbackUrlEmptyException | Fingerprint callback URL is required for 3DS. |
SDK0402 | AcquirerProfileIdInvalidException | Acquirer profile ID must be 0 or 36 characters. |
SDK0403 | ProviderIdInvalidException | Provider ID must be 36 characters or less. |
SDK0404 | GatewayTokenIdInvalidException | Gateway token ID must be exactly 36 characters. |
SDK0405 | SchemeTokenIdInvalidException | Scheme token ID must be exactly 36 characters. |
SDK0406 | CardTokenIdRequiredException | Card token ID is required for authentication. |
SDK0407 | CurrencyNotRequireDecimalException | Currency does not accept decimal places (e.g., JPY). |
SDK0408 | CurrencyInvalidDecimalException | Incorrect decimal places for currency. |
These codes are raised when initiate-3DS request data is invalid or 3DS isn't enabled:
| Error code | Exception | Description |
|---|---|---|
SDK0409 | CountryNumericCodeInvalidException | Country numeric code is invalid (ISO 3166-1). |
SDK0410 | MerchantLegalNameInvalidException | Merchant legal name is invalid. |
SDK0411 | ChallengeWindowSizeInvalidException | Challenge window size is invalid (must be 1-5). |
SDK0412 | ChallengeCallbackUrlEmptyException | Challenge callback URL is required. |
SDK0413 | AcceptHeaderEmptyException | Accept header is required in request. |
SDK0414 | LanguageInvalidException | Invalid language format (must be IETF BCP 47). |
SDK0415 | ScreenHeightInvalidException | Screen height is invalid. |
SDK0416 | ScreenWidthInvalidException | Screen width is invalid. |
SDK0417 | EmailInvalidException | Email address format is invalid. |
SDK0418 | RecurringExpirationDateInvalidException | Recurring expiration date is invalid (ISO 8601). |
SDK0419 | RecurringFrequencyInDaysInvalidException | Recurring frequency in days is invalid. |
SDK0420 | IpAddressInvalidException | IP address format is invalid (IPv4/IPv6). |
SDK0421 | RequestorChallengeIndicatorInvalidException | Requestor challenge indicator is invalid (must be 01, 02, 03, 04, 05, or 10). |
SDK0422 | ThreeDSServiceNotEnabledException | 3D Secure service isn't enabled for this merchant. |
SDK0423 | AuthenticationConfigurationInvalidException | Authentication configuration is invalid. |
These codes cover network failures and 3DS or transaction authentication outcomes. The Scenario column links to the matching section above:
| Error code | Exception | Description | Scenario |
|---|---|---|---|
SDK0500 | NetworkSdkException | Network connectivity error during API request. | Connection error, CORS blocked |
SDK0501 | ValidationException | Field or configuration validation failed. | — |
SDK0502 | PreInitiateAuthenticationFailedException | Pre-initiate authentication failed. | — |
SDK0503 | TransactionAuthenticationRejectedException | Authentication rejected by the issuing bank, or the challenge failed or was abandoned. | 3DS rejected by bank, 3DS timed out |
SDK0504 | TransactionAuthenticationRequireScaExemptionException | SCA exemption type not provided when required. | 3DS rejected by bank |
SDK0505 | AuthenticationFailedException | Initiate authentication failed. | 3DS rejected by bank |
SDK0506 | TransactionAuthenticationInvalidException | Invalid SCA exemption value. | 3DS rejected by bank |
SDK0507 | AuthenticationRetrieveStrategyFailedException | Failed to retrieve authentication strategy. | — |
SDK0508 | AuthenticationRetrieveMerchantSetupFailedException | Merchant 3DS configuration unavailable or invalid. | — |
SDK0510 | AuthenticationRetrieveResultFailedException | 3DS authentication result could not be retrieved. | — |
For payment-method-specific scenarios, see:
- Card troubleshooting: Card validation errors or tokenisation failures
- Google Pay troubleshooting: Google Pay button missing or payment sheet fails
- Apple Pay troubleshooting: Apple Pay not available or merchant validation fails
- PayPal payments troubleshooting: PayPal popup blocked or authentication fails
- PayPal payouts troubleshooting: OAuth flow fails or payout submission errors
- Paze troubleshooting: Paze SDK load, configuration, or checkout eligibility fails
- Aeropay troubleshooting: Aeropay setup, verification, bank-linking, or transaction errors