# 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:


```typescript
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');
```

| Property | Description |
|  --- | --- |
| `cardNumberComponent`CardNumberComponent | Card number input component. See [Card number](/guides/checkout/components/web/card/card-number). |
| `cardExpiryDateComponent`CardExpiryDateComponent | Card expiry date input component. See [Card expiry date](/guides/checkout/components/web/card/card-expiry-date). |
| `cardCvcComponent`CardCvcComponent | Card CVC input component. See [Card CVC](/guides/checkout/components/web/card/card-cvc). |


### Advanced configuration

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


```typescript
// 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
  }
});
```

| Property | Description |
|  --- | --- |
| `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](/guides/checkout/components/web/card/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](/guides/checkout/components/web/card/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](/guides/checkout/components/web/card/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:


```typescript
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'
  }
};
```

| Property | Description |
|  --- | --- |
| `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:


```typescript
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'
    }
  }
});
```

| Property | Description |
|  --- | --- |
| `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:


```typescript
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 authorise payment:", data);
    return null; // Return transaction data or null
  },
  
  onPostAuthorisation: (submitResult) => {
    hideLoadingSpinner();
    
    if (submitResult.success) {
      console.log("Payment authorised 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}`);
  }
});
```

| Callback | Description |
|  --- | --- |
| `onClick: (event: Event) => void` | Event handler for when the submit button is clicked. |
| `onChange: (state: ComponentState) => void` | Event handler for when the component state changes. |
| `onValidation: (data: ValidationResult[]) => void` | Event 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: () => boolean` | Event handler called before tokenisation. Return `false` to prevent tokenisation. |
| `onPostTokenisation: (data: CardTokenizationResult) => void` | Event handler called after successful tokenisation with the token data. |
| `onPreInitiateAuthentication: () => PreInitiateIntegratedAuthenticationData | null | undefined` | Event handler to return authentication data to proceed, or null to skip. |
| `onPostInitiateAuthentication: (data: AuthenticationResult) => void` | Event handler called after authentication initiation. |
| `onPreAuthentication: (data: PreInitiateSuccessAuthenticationResult) => Promise<InitiateIntegratedAuthenticationData | null>` | Event handler to handle pre-authentication setup. |
| `onPostAuthentication: (data: AuthenticationResult, threeDSAuthenticationData: ThreeDSAuthenticationData | null) => void` | Event 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) => void` | Event 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) => void` | Event handler called when data collection begins. |
| `onCollectEnd: (data: any) => void` | Event handler called when data collection completes. |
| `onSubmitError: (baseSdkException: BaseSdkException) => void` | Event handler called when submission fails with error details. |


For detailed information about event data structures and usage patterns, see [Events](/guides/checkout/components/web/card/events).

## Methods

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

### `submitAsync()`

Programmatically triggers form submission and payment processing:


```typescript
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:


```typescript
cardSubmitComponent.mount('submit-container');
```

**Parameters:**

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


### `unmount()`

Removes the component from the DOM and cleans up resources:


```typescript
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:


```typescript
// 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:


```typescript
// 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


```typescript
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


```typescript
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


```typescript
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


```typescript
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](/guides/checkout/components/web/card/events), [3DS Authentication](/guides/checkout/components/web/card/3ds), and [Data validation](/guides/checkout/components/web/card/data-validation).