> ## Documentation Index
> Fetch the complete documentation index at: https://docs.paysight.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuration Guide

> How to configure the Paysight Widget for your specific needs, with up-to-date best practices.

## Widget Configuration

The Paysight Widget is highly configurable to meet your business needs. This guide covers **required parameters**, customer data handling, optional settings, and best practices for a robust integration.

## Customer object vs fields

| Option     | Role                                                                                                                        |
| ---------- | --------------------------------------------------------------------------------------------------------------------------- |
| `customer` | **Optional prefill** — seeds known values (email, name, address). Omit any field you want the shopper to type.              |
| `fields`   | **Required form layout** — controls which inputs render in the iframe. Your Paysight product defines minimum required data. |

<Callout type="warning">
  Prefilling `customer` does not skip validation. If required data is missing at submit, the widget emits `ERROR` with `VALIDATION_FAILED` and `missingFields`.
</Callout>

You can supply semi-required data via `customer`, via `fields`, or both — values from `customer` pre-populate matching fields when present.

### Required Parameters

<ParamField query="productId" type="number" required>
  Your Paysight product ID (numeric) from the dashboard.

  ```javascript theme={null}
  productId: YOUR_PRODUCT_ID
  ```
</ParamField>

<ParamField query="sessionId" type="string" required>
  A unique identifier for the payment session. This should be unique for each payment attempt.

  ```javascript theme={null}
  sessionId: 'unique-session-id'
  ```
</ParamField>

<ParamField query="amount" type="number" required>
  The payment amount in the selected currency. `1.00` = `$1.00` is the default value for testing or a specified amount for live testing (e.g. `14.99` = `$14.99`).

  ```javascript theme={null}
  amount: 1.00 // $1.00
  ```
</ParamField>

<ParamField query="environment" type="string" required>
  The environment to run the widget in: `'sandbox'` or `'production'`.

  ```javascript theme={null}
  environment: 'sandbox' // use 'production' for live payments
  ```
</ParamField>

### Semi-Required Customer Data

<Info>
  The following customer fields are semi-required. They can be provided either via the `customer` object or as fields in the `fields` array.
</Info>

<ParamField query="customer.firstName" type="string">
  Customer's first name. Can be provided via customer object or a name field.
</ParamField>

<ParamField query="customer.lastName" type="string">
  Customer's last name. Can be provided via customer object or a name field.
</ParamField>

<ParamField query="customer.state" type="string">
  Customer's state/province. Can be provided via customer object or a state field.
</ParamField>

<ParamField query="customer.country" type="string">
  Customer's country code (e.g., 'US'). Can be provided via customer object or a country field.

  <Note>
    This string is only required if the country is US.
  </Note>
</ParamField>

### Optional Parameters

<ParamField query="threeDSRequired" default="false" type="boolean">
  Enable 3D Secure authentication for the payment. Recommended for enhanced security.
</ParamField>

<ParamField query="failOnThreeDSChallenge" default="false" type="boolean">
  Do not display 3DS challenge if set to true. Proceed to payment without 3DS.
</ParamField>

<ParamField query="cancelOnThreeDSFailure" default="false" type="boolean">
  Cancel the payment if the 3DS not successfully completed.
</ParamField>

<ParamField query="ecom" default="false" type="boolean">
  Mark the payment as ecommerce. When `true`, the flag is sent to the Paysight API on card, wallet, and upsell payment paths.
</ParamField>

<ParamField query="locale" default="en-US" type="string">
  Set the language and region for the widget interface.
</ParamField>

<ParamField query="currency" default="USD" type="string">
  The currency code for the payment. Must be a valid ISO 4217 currency code.

  **Supported currencies:** `USD`, `EUR`, `GBP`, `JPY`, `AUD`, `CAD`, `CHF`, `CNY`, `HKD`, `NZD`, `SEK`, `KRW`, `SGD`, `NOK`, `MXN`, `INR`, `RUB`, `ZAR`, `TRY`, `BRL`, `TWD`, `DKK`, `PLN`, `THB`, `IDR`, `HUF`, `CZK`, `ILS`, `CLP`, `PHP`, `AED`, `COP`, `SAR`, `MYR`, `RON`
</ParamField>

<ParamField query="buttonText" default="Pay Now" type="string">
  Custom text for the payment button. Use this to match your brand voice or specific action.
</ParamField>

<ParamField query="theme" type="object">
  UI theme customization options to match your brand. Includes font, colors, and styling.
</ParamField>

<ParamField query="midOverride" type="string">
  Override the merchant ID for the payment. Requires special permissions.
</ParamField>

<ParamField query="paymentSuccess" type="object">
  Payment success message configuration. After successful payment, a payment complete message is shown with a title and description.

  <ParamField query="paymentSuccess.title" type="string" default="Payment Successful">
    Custom title for the payment success message. Overrides the default "Payment Successful" text.
  </ParamField>

  <ParamField query="paymentSuccess.description" type="string" default="Thank you for your business">
    Custom description for the payment success message. Overrides the default "Thank you for your business" text.
  </ParamField>

  ```javascript theme={null}
  paymentSuccess: {
    title: 'Order Complete!',
    description: 'Your order has been processed successfully.'
  }
  ```
</ParamField>

<ParamField query="applePayEnabled" type="boolean">
  Enable or disable Apple Pay for the widget.
</ParamField>

<ParamField query="applePayOptions" type="object">
  Apple Pay configuration options for enabling Apple Pay payments.

  <ParamField query="applePayOptions.applePayMerchantId" type="string" required>
    Apple Pay merchant identifier (per merchant account). This is the merchant ID registered with Apple Pay. Currently required, will be optional in v2.
  </ParamField>

  <ParamField query="applePayOptions.style" type="object">
    Optional styling for the Apple Pay button.

    <ParamField query="applePayOptions.style.buttonStyle" type="'black' | 'white' | 'white-outline'">
      Button style: `black`, `white`, or `white-outline`.
    </ParamField>

    <ParamField query="applePayOptions.style.buttonType" type="string">
      Button type: `pay`, `buy`, `donate`, etc.
    </ParamField>

    <ParamField query="applePayOptions.style.borderRadius" type="number">
      Border radius in pixels.
    </ParamField>

    <ParamField query="applePayOptions.style.size" type="object">
      Button size — width in px or '100%', height in px.

      <ParamField query="applePayOptions.style.size.width" type="number | string">
        Button width in pixels or '100%'.
      </ParamField>

      <ParamField query="applePayOptions.style.size.height" type="number | string">
        Button height in pixels.
      </ParamField>
    </ParamField>
  </ParamField>

  ```javascript theme={null}
  applePayEnabled: true,
  applePayOptions: {
    applePayMerchantId: 'merchant_xxxxxxxx',
    style: {
      buttonStyle: 'black',
      buttonType: 'pay',
      borderRadius: 8,
      size: {
        width: '100%',
        height: 48
      }
    }
  }
  ```
</ParamField>

<ParamField query="googlePayEnabled" type="boolean">
  Enable Google Pay. The Google Pay button is mounted on the **host page** above the iframe (parent-hosted). See [Google Pay via Widget](/widget-sdk/guides/google-pay-via-widget).
</ParamField>

<ParamField query="googlePayOptions" type="object">
  Google Pay configuration when `googlePayEnabled` is `true`.

  <ParamField query="googlePayOptions.googlePayMerchantId" type="string" required>
    Google Pay merchant identifier (from Paysight). Required when Google Pay is enabled.
  </ParamField>

  <ParamField query="googlePayOptions.style" type="object">
    Optional styling for the Google Pay button.

    <ParamField query="googlePayOptions.style.buttonStyle" type="'black' | 'white' | 'white-outline'">
      Button style for the hosted Google Pay button.
    </ParamField>

    <ParamField query="googlePayOptions.style.buttonType" type="string">
      Button type, e.g. `pay`, `checkout`.
    </ParamField>

    <ParamField query="googlePayOptions.style.borderRadius" type="number">
      Border radius in pixels.
    </ParamField>

    <ParamField query="googlePayOptions.style.locale" type="string">
      Optional locale for the Google Pay button.
    </ParamField>

    <ParamField query="googlePayOptions.style.size" type="object">
      <ParamField query="googlePayOptions.style.size.width" type="number | string">
        Button width in pixels or a percentage string.
      </ParamField>

      <ParamField query="googlePayOptions.style.size.height" type="number | string">
        Button height in pixels.
      </ParamField>
    </ParamField>
  </ParamField>

  ```javascript theme={null}
  googlePayEnabled: true,
  googlePayOptions: {
    googlePayMerchantId: 'merchant_xxx',
    style: {
      buttonStyle: 'black',
      buttonType: 'pay',
      borderRadius: 8,
      size: { width: '100%', height: 48 },
      locale: 'en',
    },
  }
  ```
</ParamField>

<ParamField query="showOnlyWalletMethods" type="boolean">
  When `true`, the checkout can show **only** Apple Pay and/or Google Pay (wallet buttons on the host page) and hide the card form, custom fields, and primary pay button inside the iframe—plus hide the horizontal “OR” divider—**if** at least one configured wallet is available. If no wallet is usable, readiness times out, or a **wallet payment** fails, the widget falls back to the full card UI. User cancellation of the wallet sheet does not by itself expand the card form.

  Requires `applePayEnabled` / `applePayOptions` and/or `googlePayEnabled` / `googlePayOptions` to be fully set when you want wallet-only mode.
</ParamField>

<Info>
  For a focused walkthrough (layout, fallbacks, code sample), see **[Wallet-only checkout](/widget-sdk/guides/wallet-only-checkout)**.
</Info>

<ParamField query="dev" type="boolean">
  Developer-only flag for extra diagnostics in some flows. Do not enable in production unless directed by Paysight.
</ParamField>

<ParamField query="disabledCardTypes" type="array">
  Disable specific card types from being used in the payment.

  Supported values:

  * `visa`
  * `mastercard`
  * `american-express`
  * `discover`
  * `diners`
  * `jcb`

  ```javascript theme={null}
  disabledCardTypes: ['american-express']
  ```
</ParamField>

<ParamField query="data" type="object">
  Additional custom data to include with the payment. Useful for tracking and analytics.

  ```javascript theme={null}
  data: {
    clickId: 'ad-campaign-123',
    // More properties to be supported soon
  }
  ```
</ParamField>

### Wallet buttons on the host page

<Info>
  Apple Pay and Google Pay render **outside** the iframe. When you enable either wallet in `config`, also supply container ids to `createWidget`:

  * **`applePayContainerId`** — default slot id `apple-pay-slot`
  * **`googlePayContainerId`** — default slot id `google-pay-slot`

  The vanilla SDK can create the wallet stack and slots if the elements are missing. See [Google Pay via Widget](/widget-sdk/guides/google-pay-via-widget) and [Apple Pay via Widget](/widget-sdk/guides/apple-pay-via-widget).
</Info>

### Fields Configuration

The `fields` parameter allows you to customize the form fields displayed in the widget. This is useful for collecting additional information from customers or customizing the checkout experience.

<ParamField query="fields" type="array">
  An array of field configuration objects that define custom form fields.

  ```javascript theme={null}
  fields: [
    {
      label: 'Email Address',
      placeholder: 'Enter your email',
      fieldType: 'email',
      position: 'above',
      size: 'full',
    },
    // Additional fields
    {
      label: 'Phone Number',
      placeholder: 'Enter your phone number',
      fieldType: 'phone',
      position: 'above',
      size: 'full'
    },
    {
      label: 'Company Name',
      placeholder: 'Enter your company name',
      fieldType: 'text',
      position: 'above',
      size: 'half'
    }
  ]
  ```
</ParamField>

**Each field object supports the following properties:**

<ParamField query="label" type="string" required>
  The label text displayed to the user.
</ParamField>

<ParamField query="placeholder" type="string">
  Placeholder text shown in the input field when empty.
</ParamField>

<ParamField query="fieldType" type="string" required>
  The type of field to display. Supported values:

  * `email` - Email address input with validation (required)
  * `name` - Name input field
  * `phone` - Phone number input with formatting
  * `address` - Street address input
  * `city` - City input
  * `state` - State/province input
  * `zip` - Postal/ZIP code input

  <Note>
    It is recommended to put a real ZIP code if you were to test live MIDs due to Address Verification Service (AVS) check, which is a fraud prevention measure used for processing credit card transactions.
  </Note>

  * `country` - Country selection dropdown
  * `text` - Generic text input
  * `divider` - Visual divider (not an input field)
</ParamField>

<ParamField query="position" default="above" type="string">
  Label position relative to the input field. Options: `above` or `below`.
</ParamField>

<ParamField query="size" default="full" type="string">
  Field width within the form. Options: `full`, `half`, or `third`.
</ParamField>

<ParamField query="required" default="false" type="boolean">
  Whether the field is required to be filled before submission.
</ParamField>

### Theme Configuration

The `theme` parameter allows you to customize the appearance of the widget to match your brand.

<ParamField query="theme" type="object">
  Object containing theme customization options.

  ```javascript theme={null}
  theme: {
    font: 'https://fonts.googleapis.com/css2?family=Inter',
    css: {
      '.ps-field-input' : {
        borderRadius: '8px',
        border: '1px solid #e5e7eb'
      },
      '.ps-submit' : {
        backgroundColor: '#5D4CFE',
        color: 'white',
        borderRadius: '8px'
      }
    }
  }
  ```
</ParamField>

<ParamField query="theme.font" type="string">
  URL to a custom font to use in the widget. Google Fonts URLs are supported.
</ParamField>

<ParamField query="theme.css" type="object">
  Custom CSS properties for different widget elements.

  <Info>
    See the [Styling Guide](/widget-sdk/guides/styling) for detailed theme customization options.
  </Info>
</ParamField>

### Additional Data

The `data` parameter allows you to include custom data with the payment transaction, which is useful for tracking and analytics.

<ParamField query="data" type="object">
  An object containing additional data to associate with the payment.

  ```javascript theme={null}
  data: {
    clickId: 'click_12345',
    campaignId: 'summer-promo-2023',
    affiliateId: 'partner-001',
    gclid: 'CjwKCAiAzc2tBhA4EiwA5gFXtqxZQn8H9XdF9jKm', // Google Click Identifier
    wbraid: 'CLjQ5oGVqvsCFQiNaAodgM4PqA', // Web BRAID for Google Ads
    gbraid: 'CLjQ5oGVqvsCFQiNaAodgM4PqA', // Google BRAID for app tracking
    // Additional custom properties
  }
  ```

  <Info>
    Google tracking parameters (`gclid`, `wbraid`, and `gbraid`) are automatically captured from URL parameters when available, but can also be manually specified.
  </Info>
</ParamField>

### Feature-Specific Configuration

<Tabs>
  <Tab title="Custom Button Text">
    You can customize the text displayed on the payment button to better match your brand voice or the specific action being taken.

    <Note>
      The custom button feature allows you to replace the default "Pay Now" text with your own text, such as "Complete Purchase", "Subscribe", or "Donate Now".
    </Note>

    #### Default Button Text

    By default, the payment button displays "Pay Now" or its localized equivalent based on the selected locale.

    You can customize the text displayed on the payment button using the `buttonText` property:

    ```javascript theme={null}
      const config = {
      // ... other fields
        buttonText: 'Complete Purchase' // Custom button text
      };
    ```

    <Note>
      This allows you to:

      * Match the button text to your specific use case (e.g., "Subscribe", "Donate", "Pay Now")
      * Maintain consistent language across your application
      * Improve conversion rates with action-oriented text
    </Note>

    <CodeGroup>
      ```javascript Subscription theme={null}
      const widget = PaySightSDK.createWidget({
        targetId: 'payment-form',
        config: {
          productId: 1234,
          sessionId: 'unique-session-id',
          amount: 29.99, // $29.99
          environment: 'production',
          fields: [
            {
              label: 'Email Address',
              placeholder: 'Enter your email',
              fieldType: 'email',
              position: 'above',
              size: 'full',
              required: true
            }
          ],
          buttonText: 'Subscribe Now' // Custom button text for subscription
        }
      });
      ```

      ```javascript Donation theme={null}
      const widget = PaySightSDK.createWidget({
        targetId: 'payment-form',
        config: {
          productId: 1234,
          sessionId: 'unique-session-id',
          amount: 50.00, // $50.00
          environment: 'production',
          fields: [
            {
              label: 'Email Address',
              placeholder: 'Enter your email',
              fieldType: 'email',
              position: 'above',
              size: 'full',
              required: true
            }
          ],
          buttonText: 'Donate $50' // Custom button text for donation
        }
      });
      ```
    </CodeGroup>

    <Info>
      The button text will automatically update when the payment is processing to indicate the current state.
    </Info>
  </Tab>

  <Tab title="Merchant ID Override">
    For advanced use cases, you can override the default merchant ID using the `midOverride` property:

    ```javascript theme={null}
      const config = {
        // ... other fields
        midOverride: 'MERCHANT_123' // Override merchant ID
      };
    ```

    <Note>
      This feature is useful for:

      * Multi-merchant platforms
      * White-label solutions
      * Testing specific merchant configurations
    </Note>

    <CodeGroup>
      ```javascript Basic Override theme={null}
      const widget = PaySightSDK.createWidget({
        targetId: 'payment-form',
        config: {
          productId: 1234,
          sessionId: 'unique-session-id',
          amount: 99.99, // $99.99
          environment: 'production',
            fields: [
              {
                label: 'Email Address',
                placeholder: 'Enter your email',
                fieldType: 'email',
                position: 'above',
                size: 'full',
                required: true
              }
            ],
            midOverride: 'MERCHANT_123' // Use specific merchant ID
        }
      });
      ```

      ```javascript Dynamic Override theme={null}
      // Function to get merchant ID based on user selection
      const getMerchantId = (selectedMerchant) => {
        const merchantMap = {
          'store1': 'MERCHANT_123',
          'store2': 'MERCHANT_456',
          'store3': 'MERCHANT_789'
        };
        return merchantMap[selectedMerchant] || defaultMerchantId;
      };
      // Use in widget configuration
      const widget = PaySightSDK.createWidget({
        targetId: 'payment-form',
        config: {
          productId: 1234,
          sessionId: 'unique-session-id',
          amount: 99.99, //$99.99
          environment: 'production',
          fields: [
            {
              label: 'Email Address',
              placeholder: 'Enter your email',
              fieldType: 'email',
              position: 'above',
              size: 'full',
              required: true
            }
          ],
          midOverride: getMerchantId(selectedMerchant)
        }
      });
      ```
    </CodeGroup>
  </Tab>
</Tabs>

## Complete Configuration Example

<Accordion title="Full HTML Example">
  ```html theme={null}
  <!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Paysight Widget Example</title>
    
    <!-- Add the Paysight Widget SDK -->
    <script src="https://payment.paysight.io/widget-sdk.js"></script>
    
    <style>
      .container {
        max-width: 600px;
        margin: 40px auto;
        padding: 20px;
      }
      .widget-container {
        border: 1px solid #e5e7eb;
        border-radius: 8px;
        padding: 20px;
      }
      .status {
        padding: 12px;
        border-radius: 6px;
        margin-bottom: 16px;
        display: none;
      }
      .error {
        background-color: #fee2e2;
        border: 1px solid #ef4444;
        color: #b91c1c;
      }
      .success {
        background-color: #dcfce7;
        border: 1px solid #22c55e;
        color: #15803d;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div id="success-message" class="status success"></div>
      <div id="error-message" class="status error"></div>
    <div id="apple-pay-slot"></div>
    <div id="google-pay-slot"></div>
    <div id="widget-container" class="widget-container"></div>
  </div>

  <script>
    const widget = PaySightSDK.createWidget({
      targetId: 'widget-container',
      applePayContainerId: 'apple-pay-slot',
      googlePayContainerId: 'google-pay-slot',
      config: {
          // Required parameters
          productId: YOUR_PRODUCT_ID,
          sessionId: new Date().getTime().toString(),
          environment: 'sandbox',
          amount: 1.00,
          
          // Pre-filled customer data
          customer: {
            email: 'customer@example.com',
            firstName: 'John',
            lastName: 'Doe',
            state: 'CA',
            country: 'US'
          },

          fields: [
            {
              label: 'Email Address',
              placeholder: 'Enter your email',
              fieldType: 'email',
              position: 'above',
              size: 'full',
              
            },
            {
              label: 'Full Name',
              placeholder: 'Enter your full name',
              fieldType: 'name',
              position: 'above',
              size: 'full'
            },
            {
              label: 'Country',
              placeholder: 'Select your country',
              fieldType: 'country',
              position: 'above',
              size: 'half'
            },
            {
              label: 'State/Province',
              placeholder: 'Enter your state',
              fieldType: 'state',
              position: 'above',
              size: 'half'
            }
          ],
          
          // Optional parameters
          threeDSRequired: false,
          failOnThreeDSChallenge: false,
          cancelOnThreeDSFailure: false,
          currency: 'USD',
          locale: 'en-US',
          buttonText: 'Pay Now',
          
          // Payment success message customization
          paymentSuccess: {
            title: 'Payment Complete!',
            description: 'Thank you for your purchase.'
          },
          
          // Apple Pay configuration
          applePayEnabled: true,
          applePayOptions: {
            applePayMerchantId: 'merchant_xxxxxxxx',
            style: {
              buttonStyle: 'black',
              buttonType: 'pay',
              borderRadius: 8,
              size: {
                width: '100%',
                height: 48
              }
            }
          },

          googlePayEnabled: true,
          googlePayOptions: {
            googlePayMerchantId: 'merchant_xxxxxxxx',
            style: {
              buttonStyle: 'black',
              buttonType: 'pay',
              borderRadius: 8,
              size: { width: '100%', height: 48 },
              locale: 'en',
            },
          },
          
          // Additional data for tracking
          data: {
            clickId: 'campaign-123',
            gclid: 'CjwKCAiAzc2tBhA4EiwA5gFXtqxZQn8H9XdF9jKm',
            wbraid: 'CLjQ5oGVqvsCFQiNaAodgM4PqA',
            gbraid: 'CLjQ5oGVqvsCFQiNaAodgM4PqA'
          }
        },
        onReady: () => {
          console.log('Widget is ready');
        },
        onError: (error) => {
          const errorElement = document.getElementById('error-message');
          errorElement.textContent = error.message;
          errorElement.style.display = 'block';
          console.error('Widget error:', error);
        },
        onMessage: (message) => {
          switch (message.type) {
            case 'PAYMENT_SUCCESS':
              const successElement = document.getElementById('success-message');
              successElement.textContent = `Payment successful! Transaction ID: ${message.payload.transactionId}`;
              successElement.style.display = 'block';
              break;
            case 'PAYMENT_ERROR':
              const errorElement = document.getElementById('error-message');
              errorElement.textContent = message.payload.message;
              errorElement.style.display = 'block';
              break;
          }
        }
      });
      
      // Example of updating customer data later
      function updateCustomerData() {
        widget.update({
          customer: {
            email: 'updated@example.com',
            firstName: 'Jane',
            lastName: 'Smith',
            phone: '+1234567890',
            address: '123 Main St',
            city: 'Anytown',
            state: 'NY',
            zip: '00000',
            country: 'US'
          }
        });
      }
    </script>
  </body>
  </html>
  ```
</Accordion>

## Configuration Best Practices

<Steps>
  <Step title="Validate Required Fields">
    Always ensure all required fields are provided with valid values.

    <Warning>
      Missing or invalid required fields will prevent the widget from initializing properly.
    </Warning>
  </Step>

  <Step title="Use Unique Session IDs">
    Generate a unique session ID for each payment attempt to prevent duplicate charges and ensure proper tracking.

    ```javascript theme={null}
      // Generate a unique session ID
      const sessionId = `session_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
    ```
  </Step>

  <Step title="Implement Error Handling">
    Always provide an `onError` handler to catch and respond to configuration errors.

    ```javascript theme={null}
    onError: (error) => {
      console.error('Configuration error:', error);
      showErrorMessage(`Payment form error: ${error.message}`);
    }
    ```
  </Step>

  <Step title="Test Configuration Changes">
    When updating configuration dynamically, test all possible combinations to ensure a smooth user experience.

    ```javascript theme={null}
      // Update configuration based on user selection
      document.getElementById('currency-selector').addEventListener('change', (e) => {
        const selectedCurrency = e.target.value;
          widget.update({ currency: selectedCurrency });
      });
    ```
  </Step>

  <Step title="Use test card numbers">
    In Sandbox mode, you can use specific test cards to simulate various real-life scenarios. These cards can be used with any valid expiry date and CVV.

    <Tabs>
      <Tab title="Basic Testing">
        <CardGroup cols={2}>
          <Card title="Successful Payment" icon="check">
            Card: 4242 4242 4242 4242
            Expiry: Any future date
            CVV: Any 3 digits
          </Card>

          <Card title="Failed Payment" icon="xmark">
            Card: 4000 0000 0000 0002
            Expiry: Any future date
            CVV: Any 3 digits
          </Card>
        </CardGroup>
      </Tab>

      <Tab title="NMI Test Cards PANs">
        <CardGroup cols={2}>
          <Card title="Visa (Credit)" icon="cc-visa">
            Card: 4532 1111 1111 1112
            Expiry: Any future date
            CVV: First 3 digits from the card (453)
          </Card>

          <Card title="Mastercard (Credit)" icon="cc-mastercard">
            Card: 5411 1111 1111 1115
            Expiry: Any future date
            CVV: First 3 digits from the card (453)
          </Card>
        </CardGroup>

        <Info>
          You can check NMI's full Test Cards [here](https://support.nmi.com/hc/en-gb/articles/115002375583-Test-Cards). Which includes more card schemes, AVS test cards and PAR test response.
        </Info>
      </Tab>

      <Tab title="3DS Challenge Flow">
        These cards will trigger a 3D-Secure challenge flow, requiring additional authentication:

        <CardGroup cols={3}>
          <Card title="Visa" icon="cc-visa">
            Any valid Visa card
            Expiry: Any future date
            CVV: Any 3 digits
          </Card>

          <Card title="Mastercard" icon="cc-mastercard">
            Any valid Mastercard
            Expiry: Any future date
            CVV: Any 3 digits
          </Card>

          <Card title="American Express" icon="cc-amex">
            Any valid Amex card
            Expiry: Any future date
            CVV: Any 4 digits
          </Card>
        </CardGroup>
      </Tab>

      <Tab title="3DS Successful Frictionless">
        These cards will trigger a successful 3D-Secure frictionless flow (no challenge):

        <CardGroup cols={3}>
          <Card title="Visa" icon="cc-visa">
            4111 1101 1663 8870
            Expiry: Any future date
            CVV: Any 3 digits
          </Card>

          <Card title="Mastercard" icon="cc-mastercard">
            5555 5501 3065 9057
            Expiry: Any future date
            CVV: Any 3 digits
          </Card>

          <Card title="American Express" icon="cc-amex">
            3782 8224 6310 005
            Expiry: Any future date
            CVV: Any 4 digits
          </Card>
        </CardGroup>
      </Tab>

      <Tab title="3DS Failed Frictionless">
        These cards will trigger a failed 3D-Secure frictionless flow:

        <CardGroup cols={3}>
          <Card title="Visa" icon="cc-visa">
            4111 1117 3897 3695
            Expiry: Any future date
            CVV: Any 3 digits
          </Card>

          <Card title="Mastercard" icon="cc-mastercard">
            5555 5504 8784 7545
            Expiry: Any future date
            CVV: Any 3 digits
          </Card>

          <Card title="American Express" icon="cc-amex">
            3782 8224 6310 013
            Expiry: Any future date
            CVV: Any 4 digits
          </Card>
        </CardGroup>
      </Tab>

      <Tab title="3DS Attempted Auth">
        These cards will trigger an attempted authentication flow:

        <CardGroup cols={3}>
          <Card title="Visa" icon="cc-visa">
            4111 1101 4848 6405
            Expiry: Any future date
            CVV: Any 3 digits
          </Card>

          <Card title="Mastercard" icon="cc-mastercard">
            5555 5588 2481 5604
            Expiry: Any future date
            CVV: Any 3 digits
          </Card>

          <Card title="American Express" icon="cc-amex">
            3782 8224 6310 021
            Expiry: Any future date
            CVV: Any 4 digits
          </Card>
        </CardGroup>
      </Tab>
    </Tabs>

    <Info>
      When prompted for 3DS authentication in test mode, use any value to complete the process.
    </Info>
  </Step>
</Steps>

## Next Steps

<CardGroup cols={2}>
  <Card title="Wallet-only checkout" icon="wallet" href="/widget-sdk/guides/wallet-only-checkout">
    Hide the card form and lead with Apple Pay and/or Google Pay.
  </Card>

  <Card title="Event Handling" icon="bolt" href="/widget-sdk/guides/events">
    Understand how to handle widget events for a complete integration.
  </Card>

  <Card title="Google Pay via Widget" icon="wallet" href="/widget-sdk/guides/google-pay-via-widget">
    Parent-hosted Google Pay setup and testing.
  </Card>

  <Card title="Styling Guide" icon="palette" href="/widget-sdk/guides/styling">
    Learn how to customize the widget appearance to match your brand.
  </Card>

  <Card title="Localization Guide" icon="globe" href="/widget-sdk/guides/localization">
    Implement multi-language support for global customers.
  </Card>
</CardGroup>
