**Last updated**: 30 October 2025 | [**Change log**](/access/products/checkout/android/changelog/)

# Card validation

Validate your customer's card details before processing them.

Warning
The validation does not check if your customer's card details are correct.
The validation only checks the format of the entered details.

## Get started

The key components for this integration are:

- A reference to your `AccessCheckoutEditText`'s for PAN, expiry date and CVC
- An `AccessCheckoutCardValidationListener` designed to receive validation events
- An instance of `CardValidationConfig` containing all the information required for the initialization of the validation flow, including references to the view components to enable validation for
- An `AccessCheckoutClient` responsible for initializing the validation flow


Full sample integration
You can see an example of the card validation integration [here](https://github.com/Worldpay/access-checkout-android/tree/master/demo-app/src/main/java/com/worldpay/access/checkout/sample).

## Reference your views

You must reference your views for PAN, expiry date, and CVC, as these are required for card validation.
Instructions can be found [here](/access/products/checkout/android/card-only#reference-your-views).

## Implement the validation listener

To receive validation events and card brand updates as your customer enters their card details,
you are required to create your own implementation of the `AccessCheckoutCardValidationListener` interface.
Each function of this interface is optional, giving you the flexibility to listen only to the events that are relevant
to your application.

Kotlin

```java
package com.worldpay.access.checkout.sample.code

import com.worldpay.access.checkout.client.validation.listener.AccessCheckoutCardValidationListener
import com.worldpay.access.checkout.client.validation.model.CardBrand

class CardValidationListener : AccessCheckoutCardValidationListener {

    // This event listener is notified when the card brand detected by the SDK has changed
    override fun onCardBrandsChanged(cardBrands: List<CardBrand>) {
        // The cardBrands list may contain one brand, or multiple brands for a co-branded card
        // CardBrand objects contains a name and optionally a list of of image objects
        // with a URL and a type (either image/png or image/svg+xml)

        // You might want to display the brand name of the first card brand returned
        // or in the case of a co-branded card, you may want to prompt shoppers with a choice
        // of brands to pay with

        // Don't forget to use the UI thread to update your views
    }

    // This event listener is notified when the PAN becomes valid or invalid
    override fun onPanValidated(isValid: Boolean) {
        // You might want to change the text color
        val textColor = if (isValid) Color.GREEN else Color.RED

        panAccessCheckoutView.setTextColor(textColor)

        // You might want to disable a submit button which would normally be clicked on when all fields are valid
        if (!isValid) submitBtn.isEnabled = false
    }

    // This event listener is notified when the expiry date becomes valid or invalid
    override fun onExpiryDateValidated(isValid: Boolean) {
        // You might want to change the text color
        val textColor = if (isValid) Color.GREEN else Color.RED

        expiryDateAccessCheckoutView.setTextColor(textColor)

        // You might want to disable a submit button which would normally be clicked on when all fields are valid
        if (!isValid) submitBtn.isEnabled = false
    }

    // This event listener is notified when the CVC becomes valid or invalid
    override fun onCvcValidated(isValid: Boolean) {
        // You might want to change the text color
        val textColor = if (isValid) Color.GREEN else Color.RED

        cvcAccessCheckoutView.setTextColor(textColor)

        // You might want to disable a submit button which would normally be clicked on when all fields are valid
        if (!isValid) submitBtn.isEnabled = false
    }

    // This event listener is notified when the PAN, expiry date and CVC are all valid
    override fun onValidationSuccess() {
        // You might want to enable a submit button when all fields are valid
        submitButton.isEnabled = true
    }
}
```

Java

```java
package com.worldpay.access.checkout.sample.code

import com.worldpay.access.checkout.client.validation.listener.AccessCheckoutCardValidationListener;
import com.worldpay.access.checkout.client.validation.model.CardBrand;

public class CardValidationListener implements AccessCheckoutCardValidationListener {

    // This event listener is notified when the card brand detected by the SDK has changed
    @Override
    public void onCardBrandsChanged(List<CardBrand> cardBrands) {
        // The cardBrands list may contain one brand, or multiple brands for a co-branded card
        // CardBrand objects contains a name and optionally a list of of image objects
        // with a URL and a type (either image/png or image/svg+xml)

        // You might want to display the brand name of the first card brand returned
        // or in the case of a co-branded card, you may want to prompt shoppers with a choice
        // of brands to pay with

        // Don't forget to use the UI thread to update your views
    }

    // This event listener is notified when the PAN becomes valid or invalid
    @Override
    public void onPanValidated(Boolean isValid) {
        // You might want to change the text color
        var textColor = isValid ? Color.GREEN : Color.RED;

        panAccessCheckoutView.setTextColor(textColor);

        // You might want to disable a submit button which would normally be clicked on when all fields are valid
        if (!isValid) submitBtn.isEnabled = false;
    }

    // This event listener is notified when the expiry date becomes valid or invalid
    @Override
    public void onExpiryDateValidated(Boolean isValid) {
        // You might want to change the text color
        var textColor = isValid ? Color.GREEN : Color.RED;

        expiryDateAccessCheckoutView.setTextColor(textColor);

        // You might want to disable a submit button which would normally be clicked on when all fields are valid
        if (!isValid) submitBtn.isEnabled = false;
    }

    // This event listener is notified when the CVC becomes valid or invalid
    @Override
    public void onCvcValidated(Boolean isValid) {
        // You might want to change the text color
        var textColor = isValid ? Color.GREEN : Color.RED;

        cvcAccessCheckoutView.setTextColor(textColor);

        // You might want to disable a submit button which would normally be clicked on when all fields are valid
        if (!isValid) submitBtn.isEnabled = false;
    }

    // This event listener is notified when the PAN, expiry date and CVC are all valid
    @Override
    public void onValidationSuccess() {
        // You might want to enable a submit button when all fields are valid
        submitButton.isEnabled = true;
    }
}
```

### Function and parameter descriptions

| Method | Description |
|  --- | --- |
| `onCardBrandsChanged` | This method is called with card brand(s) detected from the card number your customer is entering. The list contains the identified card brand(s), starting with the global brand, followed by the domestic brand if a co-branded card is in use.  If the card brand(s) can't be identified, the method is called with an empty list. Use this method to:• display the appropriate card brand logo using the [hosted images](https://access.worldpay-bsh.securedataplatform.com/access-checkout/cardTypes.json) - PNG/SVG formats available (only global brand contains image resources).• detect co-branded cards and prompt customers to select their preferred brand for payment (see our [co-branded cards](/access/products/checkout/android/co-branded-cards) documentation)• comply with the [European Union (EU) Interchange Fee Regulation (IFR) 2015/751](https://eur-lex.europa.eu/eli/reg/2015/751) |
| `onPanValidated` | This method is called with the validity of the pan field. `isValid` indicates whether the field is in a valid or invalid state. |
| `onExpiryDateValidated` | This method is called with the validity of the expiry date field. `isValid` indicates whether the field is in a valid or invalid state. |
| `onCvcValidated` | This method is called with the validity of the CVC field. `isValid` indicates whether the field is in a valid or invalid state. |
| `onValidationSuccess` | This method is called when all fields are in a valid state. You typically use this to enable the submit button. |


## Initialize the `AccessCheckoutClient` and validation

You must first initialize the `AccessCheckoutClient` using the `AccessCheckoutClientBuilder`,
providing your `baseUrl` and `checkoutId` and other parameters.
Instructions can be found [here](/access/products/checkout/android/card-only#initialize-the-accesscheckoutclient).

After implementing the `AccessCheckoutCardValidationListener`,
initialize validation for your views by creating a `CardValidationConfig`
using the builder and passing it to the `initialiseValidation` method of `AccessCheckoutClient`.

Kotlin

```java
package com.worldpay.access.checkout.sample.code

// android library imports omitted

import com.worldpay.access.checkout.client.AccessCheckoutClient;
import com.worldpay.access.checkout.client.AccessCheckoutClientBuilder;
import com.worldpay.access.checkout.client.validation.config.CardValidationConfig;
import com.worldpay.access.checkout.client.validation.listener.AccessCheckoutCardValidationListener;

class MainActivity : AppCompatActivity() {

    private val baseUrl = "TARGET_BASE_URL"
    private val checkoutId = "YOUR_CHECKOUT_ID"

    // other fields omitted

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Reference the AccessCheckoutEditText views
        val panAccessCheckoutView: AccessCheckoutEditText = findViewById<AccessCheckoutEditText>(R.id.card_flow_text_pan)
        val expiryDateAccessCheckoutView: AccessCheckoutEditText = findViewById<AccessCheckoutEditText>(R.id.card_flow_text_expiryDate)
        val cvcAccessCheckoutView: AccessCheckoutEditText = findViewById<AccessCheckoutEditText>(R.id.card_flow_text_cvc)

        // other view references omitted

        // Initialize the AccessCheckoutClient
        val sessionResponseListener = MySessionResponseListener()

        val accessCheckoutClient = AccessCheckoutClientBuilder()
            .baseUrl(baseUrl)
            .checkoutId(checkoutId)
            .context(this)
            .sessionResponseListener(sessionResponseListener)
            .lifecycleOwner(this)
            .build()

        // Create the validation configuration
        val cardValidationListener = CardValidationListener()

        val cardValidationConfig = CardValidationConfig.Builder()
            .pan(panAccessCheckoutView)
            .expiryDate(expiryDateAccessCheckoutView)
            .cvc(cvcAccessCheckoutView)
            .validationListener(cardValidationListener)
            .build()

        // Initialize validation using the client
        accessCheckoutClient.initialiseValidation(cardValidationConfig)

    }

}
```

Java

```java
package com.worldpay.access.checkout.sample.code

// android library imports omitted

import com.worldpay.access.checkout.client.AccessCheckoutClient;
import com.worldpay.access.checkout.client.AccessCheckoutClientBuilder;
import com.worldpay.access.checkout.client.validation.config.CardValidationConfig;
import com.worldpay.access.checkout.client.validation.listener.AccessCheckoutCardValidationListener;

public class MainActivity extends AppCompatActivity {

    private String baseUrl = "TARGET_BASE_URL";
    private String checkoutId = "YOUR_CHECKOUT_ID";

    // other fields omitted

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Reference the AccessCheckoutEditText views
        AccessCheckoutEditText panAccessCheckoutView = findViewById<AccessCheckoutEditText>(R.id.card_flow_text_pan);
        AccessCheckoutEditText expiryDateAccessCheckoutView = findViewById<AccessCheckoutEditText>(R.id.card_flow_text_expiryDate);
        AccessCheckoutEditText cvcAccessCheckoutView = findViewById<AccessCheckoutEditText>(R.id.card_flow_text_cvc);

        // other view references omitted

        // Initialize the AccessCheckoutClient
        SessionResponseListener sessionResponseListener = new MySessionResponseListener();

        final AccessCheckoutClient accessCheckoutClient = new AccessCheckoutClientBuilder()
                .baseUrl(baseUrl)
                .checkoutId(checkoutId)
                .context(this)
                .sessionResponseListener(sessionResponseListener)
                .lifecycleOwner(this)
                .build();

        // Create the validation configuration
        AccessCheckoutCardValidationListener cardValidationListener = CardValidationListener();

        CardValidationConfig cardValidationConfig = CardValidationConfig.Builder()
                .pan(panAccessCheckoutView)
                .expiryDate(expiryDateAccessCheckoutView)
                .cvc(cvcAccessCheckoutView)
                .validationListener(cardValidationListener)
                .build();

        // Initialize validation using the client
        accessCheckoutClient.initialiseValidation(cardValidationConfig);

    }

}
```

### Card validation rules

The validation logic in the SDK, is based off a set of default rules for any card type. Specific brand rules are fetched
from a `CardTypes` JSON file, which holds the validation rules and card brand logos for each brand. The icons are
available in both `SVG` and `PNG`.

#### Validation rules

The table below shows the rules that our SDK uses to validate your customer's card details.

| Card name | BIN range | PAN length | CVC length |
|  --- | --- | --- | --- |
| Amex | 34, 37 | 15 | 4 |
| Diners | 300-305, 3095, 36, 38, 39 | 14, 16, 19 | 3 |
| Discover | 6011, 644 - 649, 65 | 16, 19 | 3 |
| JCB | 2131, 1800, 3088 - 3094, 3096 - 3102, 3112 - 3120, 3158 - 3159, 3337 - 3349, 3528 - 3589 | 16, 17, 18, 19 | 3 |
| Maestro | 493698, 500000 - 506698, 506779 - 508999, 56 - 59, 63, 67, 6 | 12, 13, 14, 15, 16, 17, 18, 19 | 3 |
| MasterCard | 51 - 55, 2221 - 2229, 223 - 229, 23 - 26, 270 - 271, 2720: optimized using 22 - 27 | 16 | 3 |
| Visa | 4 | 13, 16, 18, 19 | 3 |


## Additional configuration options

### Card brand restriction

The SDK enables you to optionally provide a list of card brands that you support.
This means that if you do not support a certain card brand,
the SDK notifies your code of an invalid PAN if an unsupported brand is recognized.

By default, the SDK allows cards from any brand. If you do not wish to restrict the card brands that you accept then you
do not need to pass any configuration.

#### Example configuration

To restrict the card brands that you accept, simply pass in an array of the brands that you **do** wish to accept when
initializing the SDK.

The following validation configuration restricts the SDK to accept only American Express, Visa or Mastercard BIN ranges.

Kotlin

```java
val cardValidationConfig = CardValidationConfig.Builder()
    ...
    .acceptedCardBrands(arrayOf("visa", "mastercard","amex"))
    ...
    .build();
```

Java

```java
CardValidationConfig cardValidationConfig = new CardValidationConfig.Builder()
    ...
    .acceptedCardBrands(new String[] {"visa", "mastercard", "amex"})
    ...
    .build();
```

#### Currently supported card brands

The table below shows the card brands recognized by our SDK.

| Brand | Code |
|  --- | --- |
| American Express | `"amex"` |
| Diners | `"diners"` |
| Discover | `"discover"` |
| JCB | `"jcb"` |
| Maestro | `"maestro"` |
| Mastercard | `"mastercard"` |
| Visa | `"visa"` |


Note
If the SDK does not recognize a PAN as one of the above brands, it will be permitted as long as it meets the usual
criteria for a valid PAN.

### PAN formatting

The SDK allows PAN formatting as the customer types. This feature is disabled by default.

The PAN is formatted in the following way:

| Card type | Formatting |
|  --- | --- |
| Visa, Mastercard, JCB, Discover, Diners, Maestro | XXXX XXXX XXXX XXXX |
| Amex | XXXX XXXXXX XXXXX |


#### Enabling PAN formatting

To enable the PAN formatting behavior, simply call the `enablePanFormatting` method on the `CardValidationConfig` builder.

Kotlin

```java
val cardValidationConfig = CardValidationConfig.Builder()
    ...
    .enablePanFormatting()
    ...
    .build();
```

Java

```java
CardValidationConfig cardValidationConfig = new CardValidationConfig.Builder()
    ...
    .enablePanFormatting()
    ...
    .build();
```

**Next steps**

[Create a session to pay with a card](/access/products/checkout/android/card-only) or 
[Create sessions to pay with a card and CVC](/access/products/checkout/android/card-and-cvc)