Skip to content

Testing

Learn how to test your Paze integration effectively before going live.

Overview

Thorough testing is essential to ensure your Paze integration works correctly across presentment modes, checkout flows, and error scenarios. This guide covers test environments, configuration, test scenarios, and debugging techniques.

Always test your integration in the UAT environment before deploying to production. Use test credentials and the Paze UAT SDK to avoid processing real payments during development.

Test environments

Development environment

Initialise the SDK with test environment settings, USD transaction data, and a test shopper callback:

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

const pxpSdk = PxpCheckout.initialize({
  environment: 'test', // Use 'test' for UAT
  session: sessionData, // From your backend
  ownerId: 'your-owner-id',
  ownerType: 'MerchantGroup',
  clientName: 'Your Merchant',
  siteName: 'Your Store',
  kountDisabled: true, // Disable Kount during initial testing
  transactionData: {
    currency: 'USD',
    amount: 10.00,
    merchantTransactionId: crypto.randomUUID(),
    merchantTransactionDate: () => new Date().toISOString(),
    entryType: 'Ecom',
    intent: {
      card: IntentType.Authorisation
    }
  },
  onGetShopper: () => Promise.resolve({
    id: 'test-shopper-1',
    email: 'test@example.com',
    firstName: 'Test',
    lastName: 'Shopper'
  })
});

Environment options

EnvironmentPaze SDK URLPurpose
testcheckout.wallet.uat.earlywarning.ioDevelopment and UAT testing
livecheckout.paze.comProduction payments

Never use production Paze credentials in development. UAT and live environments use separate client IDs and SDK endpoints.

Paze UAT wallets and eligibility

Dynamic presentment (buttonRenderMode: "dynamic") calls Paze canCheckout() with the shopper's emailAddress and phoneNumber. The button stays hidden when Paze reports no eligible wallet (onPresentmentResolved(false)).

To test dynamic mode in UAT:

  1. Use UAT credentials end-to-end: Portal Paze client ID on your site, environment: 'test' in the SDK, and the UAT Paze SDK URL (loaded automatically when environment is test).
  2. Register test shoppers with Paze: Use email and phone combinations that Paze has provisioned for your UAT merchant account. Paze provides eligible test identities during merchant onboarding — contact your Paze integration contact if you do not have UAT shopper credentials.
  3. Match identity at checkout: If both email and phone are set, only phoneNumber is sent to Paze checkout(). Use the same phone number Paze registered for the test wallet.
  4. Start with static mode: Use buttonRenderMode: 'static' while validating session, decrypt, and transaction flows before testing eligibility gating.

Static mode does not call canCheckout(). It shows the button after successful SDK initialisation, which is useful for testing checkout and payment flows before you have UAT-eligible shopper identities.

Test configuration

Basic test setup

Create a Paze button with logging callbacks on each stage of the payment flow:

const TEST_CONFIG = {
  environment: 'test' as const,
  emailAddress: 'test.shopper@example.com',
  phoneNumber: '15551234567',
  amount: 10.00,
  currency: 'USD'
};

const pazeButton = pxpSdk.create('paze-button', {
  buttonRenderMode: 'static',
  emailAddress: TEST_CONFIG.emailAddress,
  phoneNumber: TEST_CONFIG.phoneNumber,
  style: {
    color: 'pazeblue',
    shape: 'pill',
    label: 'check out with'
  },

  onInit: () => console.log('TEST: Paze initialised'),
  onPresentmentResolved: (isVisible) => console.log('TEST: Button visible:', isVisible),
  onPazeButtonClicked: () => console.log('TEST: Button clicked'),
  onCheckoutComplete: async (result) => {
    console.log('TEST: Checkout complete:', result);
    return true; // Approve to continue
  },
  onComplete: async (result) => {
    // Fires after Paze complete() decodes the response, before decryption
    console.log('TEST: Complete result:', result.completeDecodedResponse?.payloadId);
  },
  onPostDecryption: async (payload) => {
    console.log('TEST: Decrypted token:', payload.fundingData?.cardNetwork);
  },
  onPostAuthorisation: async (data) => {
    console.log('TEST: Authorisation result:', data.systemTransactionId);
  },
  onError: (error) => {
    console.error('TEST: Error:', error.ErrorCode, error.message);
  }
});

pazeButton.mount('paze-button-container');

Test scenarios

Presentment testing

Static presentment

In static mode, the button renders after successful initialisation and onPresentmentResolved(true) fires. This does not call canCheckout() to verify wallet eligibility:

const pazeButton = pxpSdk.create('paze-button', {
  buttonRenderMode: 'static',
  onPresentmentResolved: (isVisible) => {
    console.log('Static presentment resolved:', isVisible);
    // true after successful init and render; not called when initialisation fails (check onError)
  }
});

pazeButton.mount('paze-button-container');

Dynamic presentment

In dynamic mode, the SDK calls canCheckout() and hides the button when the shopper has no eligible Paze wallet:

const pazeButton = pxpSdk.create('paze-button', {
  buttonRenderMode: 'dynamic',
  emailAddress: 'eligible.shopper@example.com',
  phoneNumber: '15551234567',
  onPresentmentResolved: (isVisible) => {
    console.log('Dynamic presentment result:', isVisible);
    // isVisible is false when canCheckout() reports no eligible wallet
  }
});

pazeButton.mount('paze-button-container');

Checkout flow testing

Approve checkout

Return true from onCheckoutComplete to exercise the full happy path through complete(), decryption, and authorisation:

onCheckoutComplete: async (result) => {
  console.log('Checkout data:', {
    sessionId: result.checkoutDecodedResponse?.sessionId,
    consumerEmail: result.checkoutDecodedResponse?.consumer?.emailAddress,
    cardNetwork: result.checkoutDecodedResponse?.maskedCard?.paymentCardNetwork,
    panLastFour: result.checkoutDecodedResponse?.maskedCard?.panLastFour
  });
  return true;
}

Reject checkout

Return false from onCheckoutComplete to confirm the SDK treats checkout as incomplete:

onCheckoutComplete: async (result) => {
  console.log('Rejecting checkout for testing');
  return false;
},
onCheckoutIncomplete: async (result) => {
  console.log('Expected incomplete result:', result.reason);
  // Merchant rejection: "Merchant validation rejected completion."
}

Decryption testing

SDK-managed decryption

Return true from onPreDecryption (or omit the callback) to test the default SDK decryption path:

onPreDecryption: async () => {
  console.log('SDK will decrypt automatically');
  return true; // or omit callback entirely
},
onPostDecryption: async (payload) => {
  console.log('Decrypted network token present:', !!payload.fundingData?.networkToken);
  console.log('Card network:', payload.fundingData?.cardNetwork);
}

Manual decryption

Return false from onPreDecryption and handle securedPayload in onComplete. Your test backend should call the PXP decrypt-token API — see Backend decryption:

onPreDecryption: async () => false,
onComplete: async (result) => {
  const securedPayload = result.completeDecodedResponse?.securedPayload;
  console.log('Secured payload received:', !!securedPayload);

  // Send to your test backend
  const response = await fetch('/api/paze/decrypt', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ securedPayload })
  });
  console.log('Backend decryption result:', await response.json());
}

Error scenario testing

Mount or initialise components to trigger validation errors and confirm the expected SDK error codes:

// Test invalid currency (fails when the component initialises)
const invalidSdk = PxpCheckout.initialize({
  ...config,
  transactionData: { ...config.transactionData, currency: 'GBP' }
});
const invalidCurrencyButton = invalidSdk.create('paze-button', { buttonRenderMode: 'static' });
invalidCurrencyButton.mount('paze-button-container');
// Expected: SDK1213 via onError

// Test missing identity for dynamic mode
const dynamicButton = pxpSdk.create('paze-button', {
  buttonRenderMode: 'dynamic',
  emailAddress: 'test@example.com'
  // Missing phoneNumber — expected: SDK1208 on mount
});
dynamicButton.mount('paze-button-container');

// Test invalid phone format
const invalidPhone = pxpSdk.create('paze-button', {
  buttonRenderMode: 'static',
  phoneNumber: '+15551234567' // Expected: SDK1212 on mount
});
invalidPhone.mount('paze-button-container');

Debugging tools

Callback logging

Wrap component callbacks to trace the payment flow during manual or automated testing:

const callbacks = [
  'onInit', 'onPresentmentResolved', 'onCustomValidation', 'onPazeButtonClicked',
  'onCheckoutComplete', 'onCheckoutIncomplete', 'onComplete',
  'onPreDecryption', 'onPostDecryption',
  'onPreAuthorisation', 'onPostAuthorisation', 'onSubmitError', 'onError'
];

const logConfig = Object.fromEntries(
  callbacks.map(name => [name, (...args: unknown[]) => {
    console.log(`[Paze] ${name}:`, ...args);
  }])
);

const pazeButton = pxpSdk.create('paze-button', {
  ...logConfig,
  onCheckoutComplete: async (result) => {
    console.log('[Paze] onCheckoutComplete:', result);
    return true;
  }
});

pazeButton.mount('paze-button-container');

Analytics event tracking

Log analytics events from PxpCheckout.initialize() to verify the payment funnel. For the full event list, see Analytics:

const pxpSdk = PxpCheckout.initialize({
  ...config,
  analyticsEvent: (event) => {
    console.log('Analytics:', event.eventName, event);
  }
});

Key events to verify during testing:

EventWhen it fires
PazeButtonRenderedThe Paze button became visible
PazeButtonClickedThe shopper clicked the button
PazePopupLaunchedThe Paze checkout popup opened
PazeCheckoutCompletedCheckout returned COMPLETE
PazeCheckoutAbandonedCheckout incomplete, popup closed, or merchant rejected checkout
PazeDecryptionInitiatedSDK decryption started
PazeDecryptionCompletedSDK decryption succeeded
PazeDecryptionFailedSDK decryption failed
PazeTransactionInitiatedTransaction submission to PXP started (componentType: 'PazeButton')
PreAuthorisationonPreAuthorisation is configured and the flow proceeds (componentType: 'Paze')
PostAuthorisationonPostAuthorisation is configured and submission succeeds (componentType: 'Paze')
PazeAuthorisationFailedTransaction submission failed

Wallet visibility is not an analytics event. Track it with onPresentmentResolved instead.

Environment diagnostics

Run these checks in the browser console when the button does not appear:

function diagnosePaze() {
  const config = pxpSdk.getConfig();
  const session = config.session;

  console.group('Paze Diagnostics');
  console.log('Environment:', config.environment);
  console.log('Currency:', config.transactionData.currency);
  console.log('Amount:', config.transactionData.amount);
  console.log('Client ID:', session.allowedFundingTypes?.wallets?.paze?.clientId ? 'Present' : 'Missing');
  console.log('HTTPS:', location.protocol === 'https:' || location.hostname === 'localhost');
  console.log('Paze SDK loaded:', !!window.DIGITAL_WALLET_SDK);
  console.groupEnd();
}

Test checklist

Before going live, verify each scenario:

Configuration

  • Session includes allowedFundingTypes.wallets.paze.clientId
  • Currency is USD
  • Amount is greater than 0
  • onGetShopper returns valid shopper data
  • HTTPS is enabled (or localhost for development)

Presentment

  • Static mode renders the button after successful initialisation
  • Dynamic mode shows the button for eligible shoppers
  • Dynamic mode hides the button when canCheckout() reports no wallet
  • onPresentmentResolved fires with the expected visibility

Checkout

  • Button click opens the Paze checkout popup
  • Successful checkout triggers onCheckoutComplete
  • Returning false from onCheckoutComplete triggers onCheckoutIncomplete with "Merchant validation rejected completion."
  • Shopper closing the popup triggers onCheckoutIncomplete

Decryption and authorisation

  • SDK-managed decryption returns token data in onPostDecryption
  • Manual decryption receives securedPayload in onComplete (before onPreDecryption runs)
  • onPreAuthorisation returning false cancels authorisation
  • onPostAuthorisation is configured and fires for successful HTTP responses (including Declined states)
  • HTTP submission failures fire onSubmitError

Error handling

  • Invalid configuration surfaces errors via onError
  • Network errors are handled gracefully
  • Session expiry is handled with user guidance

Pre-launch checklist

Before going live with Paze, ensure your implementation meets these requirements:

Technical setup

  • HTTPS enabled on all checkout pages (localhost exempt for development).
  • Paze client ID configured in Unity Portal for the correct site.
  • environment: 'live' with production Paze credentials.
  • Transaction currency set to USD.
  • Tested on Chrome, Safari, Firefox, and Edge.
  • Comprehensive error handling for all payment scenarios.

Button and branding

  • Uses official <paze-button> element with approved style values.
  • Paze button has equal prominence with other payment methods.
  • Button renders correctly on desktop and mobile viewports.
  • Dynamic presentment hides the button when shopper is ineligible.

Security

  • Valid SSL certificate from a recognised authority.
  • No storage of sensitive payment data in browser storage.
  • Backend verification of all successful transactions.
  • HMAC credentials stored securely on the server only.
  • Privacy policy covers payment and identity data usage.

User experience

  • Clear error messages when Paze is unavailable.
  • Graceful fallback to other payment methods.
  • Loading indicators during checkout and processing.
  • Session refresh handling for expired sessions.

Go-live testing

  • End-to-end payment testing in UAT environment.
  • Tested static and dynamic presentment modes.
  • Tested checkout approval and rejection flows.
  • Tested SDK-managed and manual decryption paths.
  • Tested transaction failure and retry scenarios.