Skip to content

Card submit

Learn about how to configure the card submit component.

Basic usage

Minimal configuration

At minimum, the card submit component requires the following card input components to function:

import PxpCheckout from '@pxp-io/web-components-sdk';

const pxpCheckout = PxpCheckout.initialize({
  session: {
    sessionId: 'your-session-id',
    hmacKey: 'your-hmac-key',
    encryptionKey: 'your-encryption-key'
  },
  environment: 'production',
  transactionData: {
    amount: 1000,
    currency: 'USD',
    entryType: 'Ecom',
    intent: {
      card: 'Authorisation'
    },
    merchantTransactionId: 'unique-transaction-id',
    merchantTransactionDate: () => new Date().toISOString()
  },
  ownerType: 'MerchantGroup',
  ownerId: 'your-owner-id'
});

// Create card input components
const cardNumber = pxpCheckout.create('card-number', {
  label: "Card number"
});

const cardExpiry = pxpCheckout.create('card-expiry-date', {
  label: "Expiry date"
});

const cardCvc = pxpCheckout.create('card-cvc', {
  label: "CVC"
});

// Create card submit component
const cardSubmitComponent = pxpCheckout.create('card-submit', {
  cardNumberComponent: cardNumber,
  cardExpiryDateComponent: cardExpiry,
  cardCvcComponent: cardCvc
});

// Mount components
cardNumber.mount('card-number-container');
cardExpiry.mount('expiry-date-container');
cardCvc.mount('cvc-container');
cardSubmitComponent.mount('submit-container');
PropertyDescription
cardNumberComponent
CardNumberComponent
required
Card number input component. See Card number.
cardExpiryDateComponent
CardExpiryDateComponent
required
Card expiry date input component. See Card expiry date.
cardCvcComponent
CardCvcComponent
required
Card CVC input component. See Card CVC.

Advanced configuration

For more complex implementations, you can configure additional components and settings:

// Create additional components
const cardHolderName = pxpCheckout.create('card-holder-name', {
  label: "Cardholder name"
});

const cardConsent = pxpCheckout.create('card-consent', {
  consentText: "Save this card for future purchases"
});

const billingAddress = pxpCheckout.create('billing-address', {
  showFullForm: true
});

// Create card submit with advanced configuration
const cardSubmitComponent = pxpCheckout.create('card-submit', {
  // Required components
  cardNumberComponent: cardNumber,
  cardExpiryDateComponent: cardExpiry,
  cardCvcComponent: cardCvc,
  
  // Optional card components
  cardHolderNameComponent: cardHolderName,
  cardConsentComponent: cardConsent,
  
  // Basic configuration
  submitText: "Complete Payment",
  disableUntilValidated: true,
  hideSubmitButton: false,
  id: "card-submit-btn",
  class: "payment-submit-button",
  label: "Payment Submission",
  submitAriaLabel: "Complete your payment transaction",
  tabIndex: 1,
  
  // Card-on-file configuration
  useCardOnFile: false,
  
  // AVS configuration
  avsRequest: true,
  billingAddressComponents: {
    billingAddressComponent: billingAddress
  }
});
PropertyDescription
submitText
string
Text displayed on the submit button. Defaults to "Pay Now" for payments or "Withdraw" for payouts.
disableUntilValidated
boolean
Disables submit button until all fields are valid. Defaults to false.
hideSubmitButton
boolean
Hides the submit button completely. Defaults to false.
id
string
Unique identifier for the component element. Defaults to null.
class
string
CSS class name applied to the component. Defaults to null.
label
string
Label text displayed with the component. Defaults to null.
submitAriaLabel
string
Accessible label for the submit button. Defaults to "Submit button".
tabIndex
number
Tab order for keyboard navigation. Defaults to null.
cardHolderNameComponent
CardHolderNameComponent
Cardholder name input component. See Cardholder name. Defaults to null.
useCardOnFile
boolean
Uses saved card data instead of new card entry. Defaults to false.
avsRequest
boolean
Enables Address Verification System checks. Defaults to false.
newCardComponent
NewCardComponent
New card entry component. See New card. Defaults to null.
cardConsentComponent
CardConsentComponent
Card consent and terms component. Defaults to null.
cardOnFileComponent
CardOnFileComponent
Saved payment methods component. Defaults to null.
clickOnceStandaloneComponent
ClickOnceStandaloneComponent
One-click payment component. Defaults to null.
MITEnabled
boolean
Enables Merchant Initiated Transaction (MIT) processing. Defaults to null.
applyExemption
boolean
Applies Strong Customer Authentication (SCA) exemptions when applicable. Defaults to null.
useUnityAuthenticationStrategy
boolean
When true, uses Unity authentication strategy for 3DS authentication. Defaults to false.
threeDSStrategySetup
object
Advanced 3DS authentication strategy configuration for different SCA scenarios. Defaults to null.
billingAddressComponents.billingAddressComponent
BillingAddressComponent
Pre-built billing address component for address collection, compliance, or verification. See Billing address. Defaults to null.
billingAddressComponents.countrySelectionComponent
CountrySelectionComponent
Standalone country selector component. Defaults to null.
billingAddressComponents.postcodeComponent
PostcodeComponent
Standalone postcode input component. Defaults to null.
billingAddressComponents.addressComponent
AddressComponent
Standalone address input component. Defaults to null.

If both the pre-built billingAddressComponent and standalone components are provided, the pre-built component takes precedence. When avsRequest is enabled, billing address data will be used for Address Verification System checks during payment processing.

Styling

Default styling

The card submit component renders with these default styles:

const defaultCardSubmitStyles = {
  container: {
    display: 'block',
    width: '100%'
  },
  
  button: {
    backgroundColor: '#007bff',
    color: '#ffffff',
    border: '1px solid #007bff',
    borderRadius: '4px',
    padding: '12px 24px',
    fontSize: '16px',
    cursor: 'pointer',
    width: '100%'
  },
  
  'button:hover': {
    backgroundColor: '#0056b3',
    borderColor: '#0056b3'
  },
  
  'button:disabled': {
    backgroundColor: '#6c757d',
    borderColor: '#6c757d',
    cursor: 'not-allowed'
  }
};
PropertyDescription
id
string
Unique identifier for the component element. Defaults to null.
class
string
CSS class name applied to the component. Defaults to null.
label
string
Label text displayed with the component. Defaults to null.
submitAriaLabel
string
Accessible label for the submit button. Defaults to "Submit button".

Custom styling

You can override the default appearance by providing custom styles for any part of the component:

const cardSubmitComponent = pxpCheckout.create('card-submit', {
  cardNumberComponent: cardNumber,
  cardExpiryDateComponent: cardExpiry,
  cardCvcComponent: cardCvc,
  
  submitText: "Complete Payment",
  
  styles: {
    // Container styling
    container: {
      backgroundColor: '#ffffff',
      border: '1px solid #e0e0e0',
      borderRadius: '8px',
      padding: '16px',
      margin: '8px 0',
      boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
    },
    
    // Submit button styling
    button: {
      backgroundColor: '#007bff',
      color: '#ffffff',
      border: '1px solid #007bff',
      borderRadius: '4px',
      padding: '12px 24px',
      fontSize: '16px',
      fontWeight: '500',
      cursor: 'pointer',
      width: '100%',
      transition: 'all 0.2s ease',
      textAlign: 'center',
      textDecoration: 'none',
      display: 'inline-block',
      lineHeight: '1.5'
    },
    
    // Button hover state
    'button:hover': {
      backgroundColor: '#0056b3',
      borderColor: '#0056b3',
      transform: 'translateY(-1px)',
      boxShadow: '0 4px 8px rgba(0,0,0,0.15)'
    },
    
    // Button focus state
    'button:focus': {
      outline: '2px solid #007bff',
      outlineOffset: '2px'
    },
    
    // Button disabled state
    'button:disabled': {
      backgroundColor: '#6c757d',
      borderColor: '#6c757d',
      cursor: 'not-allowed',
      opacity: '0.6',
      transform: 'none',
      boxShadow: 'none'
    },
    
    // Button active/pressed state
    'button:active': {
      backgroundColor: '#004085',
      borderColor: '#004085',
      transform: 'translateY(0)'
    },
    
    // Label styling
    label: {
      color: '#333333',
      fontSize: '14px',
      fontWeight: '500',
      marginBottom: '8px',
      display: 'block'
    },
    
    // Error message styling
    errorMessage: {
      color: '#dc3545',
      fontSize: '12px',
      marginTop: '4px',
      display: 'block'
    },
    
    // Loading state styling
    loading: {
      opacity: '0.7',
      cursor: 'wait'
    },
    
    // Loading spinner
    loadingSpinner: {
      border: '2px solid #f3f3f3',
      borderTop: '2px solid #007bff',
      borderRadius: '50%',
      width: '16px',
      height: '16px',
      animation: 'spin 1s linear infinite',
      display: 'inline-block',
      marginRight: '8px'
    }
  }
});
PropertyDescription
styles.container
CSSStyleDeclaration
Styling for the main component container. Defaults to null.
styles.container.backgroundColor
string
The background colour of the container. Defaults to null.
styles.container.border
string
The border styling for the container. Defaults to null.
styles.container.borderRadius
string
The border radius for the container. Defaults to null.
styles.container.padding
string
The padding inside the container. Defaults to null.
styles.container.margin
string
The margin around the container. Defaults to null.
styles.container.boxShadow
string
The box shadow for the container. Defaults to null.
styles.button
CSSStyleDeclaration
Styling for the submit button in its default state. Defaults to null.
styles.button.backgroundColor
string
The background colour of the button. Defaults to null.
styles.button.color
string
The text colour of the button. Defaults to null.
styles.button.border
string
The border styling for the button. Defaults to null.
styles.button.borderRadius
string
The border radius for the button. Defaults to null.
styles.button.padding
string
The padding inside the button. Defaults to null.
styles.button.fontSize
string
The font size of the button text. Defaults to null.
styles.button.fontWeight
string
The font weight of the button text. Defaults to null.
styles.button.cursor
string
The cursor style when hovering over the button. Defaults to null.
styles.button.width
string
The width of the button. Defaults to null.
styles.button.transition
string
CSS transition effects for the button. Defaults to null.
styles.button.textAlign
string
The text alignment within the button. Defaults to null.
styles.button.textDecoration
string
Text decoration for the button. Defaults to null.
styles.button.display
string
The display type for the button. Defaults to null.
styles.button.lineHeight
string
The line height for the button text. Defaults to null.
styles['button:hover']
CSSStyleDeclaration
Styling for the submit button when hovered. Defaults to null.
styles['button:hover'].backgroundColor
string
The background colour when button is hovered. Defaults to null.
styles['button:hover'].borderColor
string
The border colour when the button is hovered. Defaults to null.
styles['button:hover'].transform
string
The transform effects when the button is hovered. Defaults to null.
styles['button:hover'].boxShadow
string
The box shadow when the button is hovered. Defaults to null.
styles['button:focus']
CSSStyleDeclaration
Styling for the submit button when focused. Defaults to null.
styles['button:focus'].outline
string
The outline styling when the button is focused. Defaults to null.
styles['button:focus'].outlineOffset
string
The outline offset when the button is focused. Defaults to null.
styles['button:disabled']
CSSStyleDeclaration
Styling for the submit button when disabled. Defaults to null.
styles['button:disabled'].backgroundColor
string
The background colour when the button is disabled. Defaults to null.
styles['button:disabled'].borderColor
string
The border colour when the button is disabled. Defaults to null.
styles['button:disabled'].cursor
string
The cursor style when the button is disabled. Defaults to null.
styles['button:disabled'].opacity
string
The opacity when the button is disabled. Defaults to null.
styles['button:disabled'].transform
string
The transform effects when the button is disabled. Defaults to null.
styles['button:disabled'].boxShadow
string
The box shadow when the button is disabled. Defaults to null.
styles['button:active']
CSSStyleDeclaration
Styling for the submit button when pressed/active. Defaults to null.
styles['button:active'].backgroundColor
string
The background colour when the button is active. Defaults to null.
styles['button:active'].borderColor
string
The border colour when the button is active. Defaults to null.
styles['button:active'].transform
string
The transform effects when the button is active. Defaults to null.
styles.label
CSSStyleDeclaration
Styling for any labels associated with the component. Defaults to null.
styles.label.color
string
The text colour of the label. Defaults to null.
styles.label.fontSize
string
The font size of the label. Defaults to null.
styles.label.fontWeight
string
The font weight of the label. Defaults to null.
styles.label.marginBottom
string
The bottom margin of the label. Defaults to null.
styles.label.display
string
The display type for the label. Defaults to null.
styles.errorMessage
CSSStyleDeclaration
Styling for validation or error messages. Defaults to null.
styles.errorMessage.color
string
The text colour of error messages. Defaults to null.
styles.errorMessage.fontSize
string
The font size of error messages. Defaults to null.
styles.errorMessage.marginTop
string
The top margin of error messages. Defaults to null.
styles.errorMessage.display
string
The display type for error messages. Defaults to null.
styles.loading
CSSStyleDeclaration
Styling applied to the component during loading states. Defaults to null.
styles.loading.opacity
string
The opacity during the loading state. Defaults to null.
styles.loading.cursor
string
The cursor style during the loading state. Defaults to null.
styles.loadingSpinner
CSSStyleDeclaration
Styling for loading spinner elements. Defaults to null.
styles.loadingSpinner.border
string
The border styling for the loading spinner. Defaults to null.
styles.loadingSpinner.borderTop
string
The top border styling for the loading spinner. Defaults to null.
styles.loadingSpinner.borderRadius
string
The border radius for the loading spinner. Defaults to null.
styles.loadingSpinner.width
string
The width of the loading spinner. Defaults to null.
styles.loadingSpinner.height
string
The height of the loading spinner. Defaults to null.
styles.loadingSpinner.animation
string
The animation for the loading spinner. Defaults to null.
styles.loadingSpinner.display
string
The display type for the loading spinner. Defaults to null.
styles.loadingSpinner.marginRight
string
The right margin for the loading spinner. Defaults to null.

Event handling

The card submit component provides event handlers to manage the complete payment flow from user interaction through final authorisation:

const cardSubmitComponent = pxpCheckout.create('card-submit', {
  cardNumberComponent: cardNumber,
  cardExpiryDateComponent: cardExpiry,
  cardCvcComponent: cardCvc,
  
  onClick: (event: Event) => {
    console.log("Submit button clicked");
  },
  
  onChange: (state: ComponentState) => {
    console.log("Component state changed:", state);
  },
  
  onValidation: (data: ValidationResult[]) => {
    console.log("Validation results:", data);
  },
  
  onCustomValidation: async () => {
    // Perform custom validation logic
    const termsAccepted = document.getElementById('terms-checkbox')?.checked;
    return termsAccepted === true;
  },
  
  onPreTokenisation: () => {
    console.log("About to tokenize card data");
    showLoadingSpinner();
    return true; // Return false to prevent tokenisation
  },
  
  onPostTokenisation: (data) => {
    console.log("Card tokenized:", data.token);
  },
  
  onPreInitiateAuthentication: () => {
    console.log("Preparing 3DS authentication");
    return null; // Return authentication data or null to skip
  },
  
  onPostInitiateAuthentication: (data) => {
    console.log("Authentication initiated:", data);
  },
  
  onPreAuthentication: async (data) => {
    console.log("Pre-authentication:", data);
    return null; // Return authentication data or null
  },
  
  onPostAuthentication: (data, threeDSData) => {
    console.log("Authentication completed:", data, threeDSData);
  },
  
  onPreAuthorisation: async (data, threeDSData) => {
    console.log("About to authorize payment:", data);
    return null; // Return transaction data or null
  },
  
  onPostAuthorisation: (submitResult) => {
    hideLoadingSpinner();
    
    if (submitResult.success) {
      console.log("Payment authorized successfully");
      window.location.href = '/thank-you';
    } else {
      console.error("Payment failed:", submitResult.errorMessage);
      showErrorMessage(submitResult.errorMessage);
    }
  },
  
  onPreRetrySoftDecline: (result) => {
    // Return true to retry, false to stop, or object with retry config
    return confirm("Payment was declined. Would you like to retry?");
  },
  
  onGetFingerprintResult: async () => {
    // Return device fingerprint data for fraud detection
    return await getDeviceFingerprint();
  },
  
  onCollectStart: (data) => {
    console.log("Starting data collection:", data);
  },
  
  onCollectEnd: (data) => {
    console.log("Data collection completed:", data);
  },
  
  onSubmitError: (error) => {
    hideLoadingSpinner();
    console.error("Submission error:", error);
    showErrorMessage(`Payment failed: ${error.message}`);
  }
});
CallbackDescription
onClick: (event: Event) => voidEvent handler for when the submit button is clicked.
onChange: (state: ComponentState) => voidEvent handler for when the component state changes.
onValidation: (data: ValidationResult[]) => voidEvent handler for when field validation occurs.
onCustomValidation: () => Promise<boolean>Event handler for custom validation before submission. Return true to proceed, false to prevent submission.
onPreTokenisation: () => booleanEvent handler called before tokenisation. Return false to prevent tokenisation.
onPostTokenisation: (data: CardTokenizationResult) => voidEvent handler called after successful tokenisation with the token data.
onPreInitiateAuthentication: () => PreInitiateIntegratedAuthenticationData | null | undefinedEvent handler to return authentication data to proceed, or null to skip.
onPostInitiateAuthentication: (data: AuthenticationResult) => voidEvent handler called after authentication initiation.
onPreAuthentication: (data: PreInitiateSuccessAuthenticationResult) => Promise<InitiateIntegratedAuthenticationData | null>Event handler to handle pre-authentication setup.
onPostAuthentication: (data: AuthenticationResult, threeDSAuthenticationData: ThreeDSAuthenticationData | null) => voidEvent handler called after authentication completion.
onPreAuthorisation: (data: PreAuthorizationData | null, threeDSAuthenticationData: ThreeDSAuthenticationData | null) => Promise<TransactionInitiationData | null>Event handler to handle pre-authorisation logic.
onPostAuthorisation: (submitResult: BaseSubmitResult) => voidEvent handler called after successful payment authorisation.
onPreRetrySoftDecline: (result: BaseSubmitResult) => boolean | { retry: boolean; updatedConfigs?: object }Event handler for handling soft declines. Return true or retry object to retry payment, false to stop.
onGetFingerprintResult: () => Promise<string>Event handler to provide device fingerprint data for fraud detection.
onCollectStart: (data: any) => voidEvent handler called when data collection begins.
onCollectEnd: (data: any) => voidEvent handler called when data collection completes.
onSubmitError: (baseSdkException: BaseSdkException) => voidEvent handler called when submission fails with error details.

For detailed information about event data structures and usage patterns, see Events.

Methods

The card submit component provides methods for form submission and lifecycle management:

submitAsync()

Programmatically triggers form submission and payment processing:

await cardSubmitComponent.submitAsync();

This method:

  1. Validates all required card fields.
  2. Tokenises the card data.
  3. Performs 3DS authentication (if required).
  4. Initiates payment authorisation.
  5. Executes all configured event handlers in sequence.

Returns a Promise<void> that resolves when the submission process completes.

mount(containerId)

Mounts the component to the DOM:

cardSubmitComponent.mount('submit-container');

Parameters:

  • containerId (string) - The ID of the container element

unmount()

Removes the component from the DOM and cleans up resources:

cardSubmitComponent.unmount();

The submitAsync() method is useful for custom submission flows, such as triggering payment from an external button, implementing multi-step forms, or adding custom pre-submission logic.

Examples

Basic payment form

A straightforward implementation with essential configuration and error handling:

// Create card input components
const cardNumber = pxpCheckout.create('card-number', {
  label: "Card number",
  formatCardNumber: true
});

const cardExpiry = pxpCheckout.create('card-expiry-date', {
  label: "Expiry date"
});

const cardCvc = pxpCheckout.create('card-cvc', {
  label: "CVC"
});

// Create submit component
const cardSubmitComponent = pxpCheckout.create('card-submit', {
  cardNumberComponent: cardNumber,
  cardExpiryDateComponent: cardExpiry,
  cardCvcComponent: cardCvc,
  
  submitText: "Pay now",
  disableUntilValidated: true,
  
  onPostAuthorisation: (result) => {
    if (result.success) {
      window.location.href = '/thank-you';
    } else {
      showErrorMessage('Payment was declined. Please try again.');
    }
  },
  
  onSubmitError: (error) => {
    console.error('Payment failed:', error);
    showErrorMessage(error.message);
  }
});

// Mount all components
cardNumber.mount('card-number-container');
cardExpiry.mount('expiry-date-container');
cardCvc.mount('cvc-container');
cardSubmitComponent.mount('submit-container');

Enterprise payment with full features

A comprehensive implementation with AVS, custom styling, and complete event handling:

// Create all card components
const cardNumber = pxpCheckout.create('card-number', {
  label: "Card number",
  formatCardNumber: true
});

const cardExpiry = pxpCheckout.create('card-expiry-date', {
  label: "Expiry date"
});

const cardCvc = pxpCheckout.create('card-cvc', {
  label: "CVC"
});

const cardHolderName = pxpCheckout.create('card-holder-name', {
  label: "Cardholder name"
});

const cardConsent = pxpCheckout.create('card-consent', {
  consentText: "Save card for future use"
});

const billingAddress = pxpCheckout.create('billing-address', {
  showFullForm: true
});

// Create submit with comprehensive configuration
const cardSubmitComponent = pxpCheckout.create('card-submit', {
  cardNumberComponent: cardNumber,
  cardExpiryDateComponent: cardExpiry,
  cardCvcComponent: cardCvc,
  cardHolderNameComponent: cardHolderName,
  cardConsentComponent: cardConsent,
  
  submitText: "Complete Payment",
  disableUntilValidated: true,
  id: "enterprise-submit-btn",
  class: "enterprise-payment-submit",
  
  avsRequest: true,
  billingAddressComponents: {
    billingAddressComponent: billingAddress
  },
  
  styles: {
    button: {
      backgroundColor: '#2563eb',
      color: '#ffffff',
      borderRadius: '6px',
      padding: '16px 32px',
      fontSize: '16px',
      fontWeight: '600'
    },
    'button:hover': {
      backgroundColor: '#1d4ed8'
    }
  },
  
  onClick: (event) => {
    if (!validateTermsAcceptance()) {
      event.preventDefault();
      showTermsModal();
    }
  },
  
  onValidation: (validationResults) => {
    handleValidationFeedback(validationResults);
  },
  
  onPreTokenisation: () => {
    showLoadingIndicator();
    return true;
  },
  
  onPostTokenisation: (tokenData) => {
    console.log('Tokenisation completed:', tokenData.token);
  },
  
  onPostAuthorisation: (submitResult) => {
    hideLoadingIndicator();
    
    if (submitResult.success) {
      showPaymentSuccessMessage();
      setTimeout(() => {
        window.location.href = `/thank-you?order=${submitResult.orderId}`;
      }, 2000);
    } else {
      handlePaymentFailure(submitResult);
    }
  },
  
  onSubmitError: (error) => {
    hideLoadingIndicator();
    
    switch (error.type) {
      case 'VALIDATION_ERROR':
        showValidationErrors(error.details);
        break;
      case 'NETWORK_ERROR':
        showNetworkError();
        break;
      default:
        showGenericError(error.message);
    }
  }
});

// Mount all components
cardNumber.mount('card-number-container');
cardExpiry.mount('expiry-date-container');
cardCvc.mount('cvc-container');
cardHolderName.mount('cardholder-name-container');
cardConsent.mount('consent-container');
billingAddress.mount('billing-address-container');
cardSubmitComponent.mount('submit-container');

Custom validation flow

const cardSubmitComponent = pxpCheckout.create('card-submit', {
  cardNumberComponent: cardNumber,
  cardExpiryDateComponent: cardExpiry,
  cardCvcComponent: cardCvc,
  
  submitText: "Process Payment",
  disableUntilValidated: true,
  
  onCustomValidation: async () => {
    // Check terms acceptance
    const termsAccepted = document.getElementById('terms-checkbox')?.checked;
    if (!termsAccepted) {
      showErrorMessage("Please accept the terms and conditions");
      return false;
    }
    
    // Verify email
    const email = document.getElementById('email-input')?.value;
    if (!email || !isValidEmail(email)) {
      showErrorMessage("Please enter a valid email address");
      return false;
    }
    
    // All custom validations passed
    return true;
  },
  
  onPostAuthorisation: (result) => {
    if (result.success) {
      // Send confirmation email
      sendConfirmationEmail(result.transactionId);
      window.location.href = '/success';
    }
  }
});

Programmatic submission

const cardSubmitComponent = pxpCheckout.create('card-submit', {
  cardNumberComponent: cardNumber,
  cardExpiryDateComponent: cardExpiry,
  cardCvcComponent: cardCvc,
  
  hideSubmitButton: true, // Hide the default button
  
  onPostAuthorisation: (result) => {
    if (result.success) {
      window.location.href = '/thank-you';
    }
  }
});

cardSubmitComponent.mount('submit-container');

// Custom submit button
document.getElementById('custom-pay-button')?.addEventListener('click', async () => {
  try {
    await cardSubmitComponent.submitAsync();
  } catch (error) {
    console.error('Payment failed:', error);
    showErrorMessage('Unable to process payment');
  }
});

Soft decline retry handling

const cardSubmitComponent = pxpCheckout.create('card-submit', {
  cardNumberComponent: cardNumber,
  cardExpiryDateComponent: cardExpiry,
  cardCvcComponent: cardCvc,
  
  submitText: "Pay Now",
  
  onPreRetrySoftDecline: (result) => {
    // Show custom retry dialog
    const shouldRetry = confirm(
      `Payment was declined: ${result.declineReason}\n\n` +
      `Would you like to try again?`
    );
    
    if (shouldRetry) {
      // Optionally update configuration for retry
      return {
        retry: true,
        updatedConfigs: {
          applyExemption: true // Try with SCA exemption
        }
      };
    }
    
    return false;
  },
  
  onPostAuthorisation: (result) => {
    if (result.success) {
      showSuccessMessage('Payment processed successfully');
    } else if (result.retryAttempted) {
      showErrorMessage('Payment declined after retry. Please use a different card.');
    }
  }
});

With 3DS authentication handling

const cardSubmitComponent = pxpCheckout.create('card-submit', {
  cardNumberComponent: cardNumber,
  cardExpiryDateComponent: cardExpiry,
  cardCvcComponent: cardCvc,
  
  submitText: "Secure Payment",
  
  onPreInitiateAuthentication: () => {
    console.log("Preparing 3DS authentication");
    showLoadingMessage("Preparing secure authentication...");
    return null;
  },
  
  onPostInitiateAuthentication: (data) => {
    console.log("3DS authentication initiated:", data);
    showLoadingMessage("Please complete authentication...");
  },
  
  onPostAuthentication: (data, threeDSData) => {
    if (data.authenticated) {
      console.log("Authentication successful");
      showLoadingMessage("Processing payment...");
    } else {
      console.log("Authentication failed or not required");
    }
  },
  
  onPostAuthorisation: (result) => {
    hideLoadingMessage();
    
    if (result.success) {
      showSuccessMessage("Payment completed successfully");
      window.location.href = '/confirmation';
    } else {
      showErrorMessage(result.errorMessage || "Payment failed");
    }
  },
  
  onSubmitError: (error) => {
    hideLoadingMessage();
    console.error("Payment error:", error);
    showErrorMessage(`Payment failed: ${error.message}`);
  }
});

For more information about integration and events, see Events, 3DS Authentication, and Data validation.