> For the complete documentation index, see [llms.txt](https://docs.payments.thalescloud.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.payments.thalescloud.io/secure-card-display/implement-secure-card-display/display-card-details.md).

# Display Card Details

## Overview

This guide explains how to securely display full card details (PAN, expiry date, cardholder name, and CVV2/dCVV2) with the D1 SDK.

You have two approaches:

* Get the card details and render them yourself in the issuer application UI.
* Delegate the rendering to the D1 SDK to minimize data exposure.

{% hint style="warning" %}
Avoid logging, analytics, or crash reports that could capture PAN, expiry date, cardholder name, or CVV2/dCVV2. Always wipe sensitive data from memory as soon as it is no longer needed.
{% endhint %}

## User experience

<figure><img src="https://422423898-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FdtyHvT2CJEJbb6gkHON7%2Fuploads%2FepKW8GdSzX8mIKBllHan%2FUX%20-%20Display%20card%20details.png?alt=media&amp;token=6c4380e9-98d6-4b8b-9d47-1efaa5ac00f9" alt=""><figcaption></figcaption></figure>

## Option 1: get the card details

In this approach, the issuer application calls `getCardDetails()` to retrieve the full card information and is responsible for rendering it.

To protect confidentiality, the D1 SDK returns values as byte arrays rather than plain text strings. You can retrieve:

* PAN
* Expiry date
* Cardholder name (end user) — optional
* CVV2 or dCVV2

## Sequence diagram

#### Prerequisites:

* End user and cards are registered in D1.
* D1 SDK is properly initialised.
* Issuer Application called D1 SDK login API.

<figure><img src="https://422423898-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FdtyHvT2CJEJbb6gkHON7%2Fuploads%2FnF3UNnj6FOB0ULHiZ5v0%2FSequence%20Diagram%20-%20Display%20Card%20Details%20-%201.png?alt=media&amp;token=2c6ef9cb-984a-4e02-ad8a-87657cc2e482" alt=""><figcaption></figcaption></figure>

### D1 SDK Integration

{% tabs %}
{% tab title="Android >= 4.4.0" %}

```kotlin
fun getCardDetails(d1Task: D1Task, cardID: String, activity: FragmentActivity) {
    activity.lifecycleScope.launch {
        try {
            val cardDetails = d1Task.secureCardDisplayService.getCardDetails(cardID).await()

            // handle cardDetails object from server, convert byte array type to string
            val cardHolderName: String? =
                cardDetails.cardHolderName?.let { String(it, Charsets.UTF_8) }
            val pan = String(cardDetails.pan, Charsets.UTF_8)
            val expiryDate = String(cardDetails.expiryDate, Charsets.UTF_8)
            val cvv = String(cardDetails.cvv, Charsets.UTF_8)

            // wipe byte array variables of cardDetails object
            cardDetails.wipe()
        } catch (exception: D1Exception) {
            // Refer to D1 SDK Integration – Error Management section
        }
    }
}
```

{% endtab %}

{% tab title="Android < 4.4.0" %}

```kotlin
fun getCardDetails(d1Task: D1Task) {
            
  var cardID = "" // card id received from backend
  val callback = object : D1Task.Callback<CardDetails> {
    
    override fun onSuccess(cardDetails: CardDetails) {
      // Handles the cardDetails object from server, convert byte array type to string.
      val cardHolderName: String? = cardDetails.cardHolderName?.let { String(it,Charsets.UTF_8) }
      val pan = String(cardDetails.pan, Charsets.UTF_8)
      val expiryDate = String(cardDetails.expiryDate, Charsets.UTF_8)
      val cvv = String(cardDetails.cvv, Charsets.UTF_8)
      // Wipes the byte array variables of cardDetails object.
      cardDetails.wipe()
    }

    override fun onError(exception: D1Exception) {
      // Refer to D1 SDK Integration – Error Management section.
    }
  }

  d1Task.getCardDetails(cardID,callback)
}
```

{% endtab %}

{% tab title="iOS >= 4.4.0" %}

```swift
let cardID = "" // obtained e.g. from server
do {
    let service = try d1Task.secureCardDisplayService()
    var cardDetails = try await service.cardDetails(cardID)
    // Proceed with subsequent flows e.g. update UI
    print("Exp: \((String(data: cardDetails.expiryDate, encoding: .utf8) ?? ""))")
    print("CVV: \((String(data: cardDetails.cvv, encoding: .utf8) ?? ""))")
    print("Name: \(String(data: cardDetails.cardHolderName ?? Data(), encoding: .utf8) ?? "")")
    print("PAN: \(String(data: cardDetails.pan, encoding: .utf8) ?? ""))")
    
    // after usage, wipe byte array cardDetails
    cardDetails.wipe()
        
    
} catch let d1Error as D1Error {
    // Refer to D1 SDK Integration – Error Management section
} catch let error {
    // Handle other error
}
```

{% endtab %}

{% tab title="iOS < 4.4.0" %}

```swift
let cardID = "" // obtained e.g. from server
d1Task.cardDetails(cardID) { cardDetails, error in
    if let error = error {
        // Refer to D1 SDK Integration – Error Management section
    } else if var cardDetails = cardDetails {
        // Proceed with subsequent flows e.g. update UI
        print("Exp: \((String(data: cardDetails.expiryDate, encoding: .utf8) ?? ""))")
        print("CVV: \((String(data: cardDetails.cvv, encoding: .utf8) ?? ""))")
        print("Name: \(String(data: cardDetails.cardHolderName ?? Data(), encoding: .utf8) ?? "")")
        print("PAN: \(String(data: cardDetails.pan, encoding: .utf8) ?? ""))")

        // after usage, wipe byte array cardDetails
        cardDetails.wipe()
    }
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Best for issuers who need custom UI/UX and full control over the rendering lifecycle. Ensure you minimize in‑memory lifetimes and call `wipe()` immediately after use.
{% endhint %}

## Option 2: delegate display to the D1 SDK

To reduce PAN exposure, use `displayCardDetails`. Unlike `getCardDetails`, this API does not return card data. Instead, you pass UI components via `CardDetailsUI`, and the D1 SDK sets their text content securely. The issuer application retains full control of styling and layout.

{% hint style="warning" %}
This option supports Visa and Mastercard cards only. It does not support Amex cards because PAN length and rendering differ.
{% endhint %}

`CardDetailsUI` provides:

* `setPanMaskCharacter` to set the PAN mask character for `maskCardDetails` (default: `*`).
* `setPanSeparatorCharacter` to set the PAN separator every 4 digits (default: space).
* `setExpiryDateFormat` to set the expiry date format (default: `MM/YY`).
* `insecureCopyToClipboard` to copy the PAN to the system clipboard. Not recommended.

The following values can be displayed:

* PAN
* Expiry date
* Cardholder name (end user) — optional
* CVV2 or dCVV2

### Sequence diagram

#### Prerequisites:

* End user and cards are registered in D1.
* D1 SDK is properly initialised.
* Issuer application called D1 SDK login API.

<figure><img src="https://422423898-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FdtyHvT2CJEJbb6gkHON7%2Fuploads%2FBKSAecUgqYxXgWfrM07j%2FSequence%20Diagram%20-%20Display%20Card%20Details%20-%202.png?alt=media&amp;token=49ffb19c-3f35-45f2-af5a-503937787870" alt=""><figcaption><p>Option 2 — The D1 SDK renders card details in provided UI components.</p></figcaption></figure>

`CardDetailsUI` contains `DisplayTextView` (Android) and `D1DisplayTextField` (iOS), which subclass the native [TextView](https://developer.android.com/reference/android/widget/TextView) and [UITextField](https://developer.apple.com/documentation/uikit/uitextfield). To limit exposure, `getText()` is restricted.

Refer to the [Android security guidelines](/secure-card-display/integrate-d1-sdk/deployment/security-guidelines/android-security-guidelines.md) to obfuscate `DisplayTextView`.

### D1 SDK Integration

{% tabs %}
{% tab title="Android >= 4.4.0" %}

```kotlin
fun displayCardDetails(d1Task: D1Task, cardID: String, cardDetailsUI: CardDetailsUI, activity: FragmentActivity) {
    activity.lifecycleScope.launch(Dispatchers.Main) {
        try {
            d1Task.secureCardDisplayService.displayCardDetails(cardID, cardDetailsUI)
                .await()
            // UI is updated
        } catch (exception: D1Exception) {
            // Refer to D1 SDK Integration – Error Management section
        }
    }
    // UI is updated
}
```

{% endtab %}

{% tab title="Android < 4.4.0" %}

```kotlin
fun displayCardDetails (d1Task: D1Task) {

  var view: View? = null // Inflate your layout here.
  val panTextView: DisplayTextView? = null // view?.findViewById(R.id.panText)
  val expiryDateTextView: DisplayTextView? = null //view?.findViewById(R.id.expiryDateText)
  val cvvTextView: DisplayTextView? = null //view?.findViewById(R.id.cvvText)
  val cardHolderNameTextView: DisplayTextView? = null //view?.findViewById(R.id.cardHolderNameText)

  val cardDetailsUI = CardDetailsUI.getInstance(panTextView, expiryDateTextView,
  cvvTextView, cardHolderNameTextView)

  var cardID = "" // Card ID received from backend.
  val callback = object : D1Task.Callback<Void> {
      override fun onSuccess(ignore: Void?) {
        // UI is updated.
      }

      override fun onError(exception: D1Exception) {
        // Refer to D1 SDK Integration – Error Management section.
      }
    }

  d1Task.displayCardDetails(cardID, cardDetailsUI, callback)
}
```

{% endtab %}

{% tab title="iOS >= 4.4.0" %}

```swift
let panTextField = D1DisplayTextField()
let cvvTextField = D1DisplayTextField()
let expiryDateTextField = D1DisplayTextField()
let cardHolderNameTextField = D1DisplayTextField()

/* e.g. layout the textfields */

let cardDetailsUI = SecureCardDisplayService.CardDetailsUI(panTextField: panTextField,
                                                           expiryDateTextField: expiryDateTextField,
                                                           cvvTextField: cvvTextField,
                                                           cardHolderNameTextField: cardHolderNameTextField)

let cardID = "" // obtained e.g. from server
do {
    let service = try d1Task.secureCardDisplayService()
    try await service.displayCardDetails(cardID, cardDetailsUI: cardDetailsUI)
} catch let d1Error as D1Error {
    // Refer to D1 SDK Integration – Error Management section
} catch let error {
    // Handle other error
}
```

{% endtab %}

{% tab title="iOS < 4.4.0" %}

```swift
let panTextField = D1DisplayTextField()
let cvvTextField = D1DisplayTextField()
let expiryDateTextField = D1DisplayTextField()
let cardHolderNameTextField = D1DisplayTextField()

/* e.g. layout the textfields */

let cardDetailsUI = CardDetailsUI(panTextField: panTextField,
        expiryDateTextField: expiryDateTextField,
        cvvTextField: cvvTextField,
        cardHolderNameTextField: cardHolderNameTextField)

let cardID = "" // obtained e.g. from server
d1Task.displayCardDetails(cardID, cardDetailsUI: cardDetailsUI) { error in
    if let error = error {
        // Refer to D1 SDK Integration – Error Management section
    } else {
        // UI is updated
    }
}
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
`insecureCopyToClipboard` exposes PAN to the system‑wide clipboard and is not recommended.
{% endhint %}

### Mask and show card details

For convenience, `CardDetailsUI` lets you mask and show card details:

* `maskCardDetails` masks the details.
* `showCardDetails` shows the details. For security, this always refetches data from the server.

{% hint style="info" %}
Best practice: set a short timer (for example, 1 minute) to automatically re‑mask details by calling `maskCardDetails` and reduce exposure.
{% endhint %}

{% tabs %}
{% tab title="Android >= 4.4.0" %}

```kotlin
fun showCardDetails(cardDetailsUI: CardDetailsUI, activity: FragmentActivity) {
        activity.lifecycleScope.launch {
            try {
                // Mask the card details
                cardDetailsUI.maskCardDetails()

                // Show the card details again
                cardDetailsUI.showCardDetails().await()
                // UI is updated
            } catch (exception: D1Exception) {
                // Refer to D1 SDK Integration – Error Management section
            }
    }
}
```

{% endtab %}

{% tab title="Android < 4.4.0" %}

```kotlin
fun showCardDetails (cardDetailsUI: CardDetailsUI) {
  // Masks the card details.
  cardDetailsUI.maskCardDetails()

  val callback = object : D1Task.Callback<Void> {
    override fun onSuccess(ignore: Void?) {
      // UI is updated.
    }

    override fun onError(exception: D1Exception) {
      // Refer to D1 SDK Integration – Error Management section.
    }
  }

  // Shows the card details again.
  cardDetailsUI.showCardDetails(callback)
}
```

{% endtab %}

{% tab title="iOS >= 4.4.0" %}

```swift
// mask the card details
cardDetailsUI.maskCardDetails()

// show the card details again
do {
    try await cardDetailsUI.showCardDetails()
    // Proceed with subsequent flows
} catch let d1Error as D1Error {
    // Refer to D1 SDK Integration – Error Management section
} catch let error {
    // Handle other error
}
```

{% endtab %}

{% tab title="iOS < 4.4.0" %}

<pre class="language-swift"><code class="lang-swift"><strong>// mask the card details
</strong>cardDetailsUI.maskCardDetails()

// show the card details again
cardDetailsUI.showCardDetails() { error in
    if let error = error {
        // Refer to D1 SDK Integration – Error Management section
    } else {
        // UI is updated
    }
}
</code></pre>

{% endtab %}
{% endtabs %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.payments.thalescloud.io/secure-card-display/implement-secure-card-display/display-card-details.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
