> 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/payment-passkeys-issuers/implement-payment-passkeys/create-a-payment-passkey.md).

# Create a payment passkey

### Overview

Allow the end user to create a payment passkey for the selected card and device.

The flow varies by payment network. Visa requires a payment passkey list check. Mastercard does not.

### User experience

{% hint style="info" %}
UI screens are for information purposes only; actual appearance may vary depending on device, OS, and configuration.
{% endhint %}

#### Visa

<figure><img src="https://1490598116-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHFkASoHUuSXO9TNwZIY8%2Fuploads%2FqTZCIhJELy6CSZQbRHD9%2FUX%20-%20Passkey%20Enrolment%20(Visa).png?alt=media&amp;token=f4f69a6e-b588-4dc6-b338-79991f1b4bea" alt=""><figcaption></figcaption></figure>

First, use the payment passkey list flow. Check whether a payment passkey exists for the card and current device.

Show **Create payment passkey** only when no payment passkey exists on the current device.

If the list flow ran within the last eight minutes, Visa skips the issuer-hosted web page. The selected card does not affect this timer.

#### Mastercard

<figure><img src="https://1490598116-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHFkASoHUuSXO9TNwZIY8%2Fuploads%2Fgit-blob-992ae49d88b9f413c97d8fafc6aa6f3442f3bb68%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

You cannot check for an existing payment passkey before starting the flow.

Allow the end user to start the creation flow each time.

Mastercard displays a different web page when a payment passkey already exists.

### Payment network requirements

For Mastercard, meet these requirements:

1. Inform the end user which personal data you share with Mastercard. Explain how you collected it.
2. Allow the end user to edit their shared personal data before submission.
3. Use an appropriate legal basis to share personal data with Mastercard.
4. When you host the consent screen:
   1. Link to the Mastercard Payment Passkey Terms of Use and Privacy Notice.
   2. Use Mastercard's latest consent layout and localized consent text.
5. Display **Mastercard payment passkey** during consent and on the completion screen.
6. Tell the end user when the flow fails.
7. Tell the end user that ID\&V may occur at checkout. This can apply to a new card or device.

{% hint style="info" %}
The issuer application must provide the end user's phone number and email address. The issuer is responsible for this data's accuracy.
{% endhint %}

### Flow

<figure><img src="https://1490598116-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHFkASoHUuSXO9TNwZIY8%2Fuploads%2FBAsx84JeCxOoXPg9Kxmz%2FScheme%20passkey%20creation%20from%20issuer%20app%20%E2%80%93%20Create%20passkey%20flow.png?alt=media&amp;token=9ed1181f-70c0-402d-9ac2-92ab8df30b42" alt=""><figcaption><p>Payment passkey creation flow for Visa and Mastercard.</p></figcaption></figure>

When the flow starts, the D1 SDK redirects the end user to the Visa or Mastercard page.

The payment network authenticates the end user. It then creates the payment passkey on the device.

After a successful flow, D1 sends a notification to the issuer backend.

### Sequence diagram

#### Prerequisites

* The issuer application integrates and initializes the D1 SDK.
* The issuer application is logged in to the D1 SDK.
* The end user and card are registered in the D1 platform.
* For Visa, first use the list payment passkey use case to determine whether a payment passkey can be created.

<figure><img src="https://1490598116-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHFkASoHUuSXO9TNwZIY8%2Fuploads%2Fgit-blob-7bab47da966ccfbc6e6ff605848fb0cdd4f14480%2FSequence%20Diagram%20-%20D1_passkeys_gitbook_create.png?alt=media" alt=""><figcaption></figcaption></figure>

### Integrate the D1 SDK

Use these implementation requirements:

* Integrate the D1 SDK as mentioned in this [section](/payment-passkeys-issuers/integrate-d1-sdk.md).
* **Android:** Call `handleResult()` directly from `onNewIntent()` on the main thread. Do not use a background thread or dispatcher.

#### Visa

{% tabs %}
{% tab title="Android Kotlin - Visa" %}
{% code lineNumbers="true" expandable="true" %}

```kotlin
class PaymentPasskeyActivity : AppCompatActivity() {

    private lateinit var d1Task: D1Task

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        d1Task = D1Task.Builder()
            /* Configure D1 Task */
            .build()

        val cardID = "your-card-id-here"
        val language = "en-US"
        val email = "john.doe@example.com"
        val consumerInfo = ConsumerInfo(email)

        lifecycleScope.launch(Dispatchers.Main) {
            try {
                // For Visa, first retrieve the passkey list
                val paymentPasskeyList = withContext(Dispatchers.IO) {
                    d1Task.paymentPasskeyService.getPaymentPasskeyList(
                        cardID,
                        language,
                        this@PaymentPasskeyActivity
                    ).await()
                }

                // Check if passkey already exists on current device
                val hasPasskeyOnCurrentDevice = paymentPasskeyList.any { it.isOnCurrentDevice == true }
                if (hasPasskeyOnCurrentDevice.not()) {
                    // Passkey list is empty or no passkey on current device, proceed to create
                    val paymentPasskey = withContext(Dispatchers.IO) {
                        d1Task.paymentPasskeyService.create(
                            cardID,
                            consumerInfo,
                            language,
                            this@PaymentPasskeyActivity
                        ).await()
                    }

                    // Handle the newly created payment passkey.
                    val paymentPasskeyCardID = paymentPasskey.cardID
                    val paymentPasskeyID = paymentPasskey.paymentPasskeyID
                    val creationTime = paymentPasskey.creationTime
                    val state = paymentPasskey.state
                    val deviceName = paymentPasskey.deviceName
                    val deviceNickname = paymentPasskey.deviceNickname
                    val isOnCurrentDevice = paymentPasskey.isOnCurrentDevice
                }
            } catch (exception: D1Exception) {
                // Refer to D1 SDK Integration – Error Management section
            }
        }
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)

        val appLink = "<your-domain>" // Refer to AndroidManifest.xml setup
        val customScheme = "myapp://passkey" // Refer to AndroidManifest.xml setup
        if (intent.data != null && (intent.toUri(Intent.URI_INTENT_SCHEME)
                .contains(appLink) || intent.toUri(Intent.URI_INTENT_SCHEME).contains(customScheme))
        ) {
            lifecycleScope.launch(Dispatchers.Main) {
                d1Task.paymentPasskeyService.handleResult(intent)
            }
        }
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="iOS - Visa" %}
{% code lineNumbers="true" expandable="true" %}

```swift
		// BEGIN
        let cardID = ""
        let window = UIWindow()
        let language = "en-US" // ISO 639-1 + ISO 3166-1 alpha-2 format
        
        // Consumer information - for Visa, email is required
        let consumerInfo = PaymentPasskeyService.ConsumerInfo(email: "user@example.com")
        
        do {
            let service = try d1Task.paymentPasskeyService()
            
            // For Visa, first retrieve the passkey list to initialize session
            let passkeys = try await service.paymentPasskeyList(cardID, language: language, window: window)
            
            // Check if passkey already exists on current device
            let hasPasskeyOnCurrentDevice = passkeys.contains { passkey in
                passkey.isOnCurrentDevice == true
            }
            
            if !hasPasskeyOnCurrentDevice {
                // Passkey list is empty or no passkey on current device, proceed to create
                let passkey = try await service.create(cardID,
                                                       consumerInfo: consumerInfo,
                                                       language: language,
                                                       window: window)
                
                // Passkey created successfully
                print("Card ID: \(passkey.cardID)")
                print("Passkey ID: \(passkey.paymentPasskeyID)")
                if let state = passkey.state {
                    print("State: \(state)")
                }
                if let deviceName = passkey.deviceName {
                    print("Device Name: \(deviceName)")
                }
                if let creationTime = passkey.creationTime {
                    print("Creation Time: \(creationTime)")
                }
            } else {
                print("Passkey already created on current device")
            }
        } catch {
            // Refer to D1 SDK Integration – Error Management section
        }
        // END
```

{% endcode %}
{% endtab %}
{% endtabs %}

#### Mastercard

For Mastercard, collect the end user's consent in the issuer application. Alternatively, delegate consent collection to Mastercard.

{% tabs %}
{% tab title="Android Mastercard with consent" %}
{% code lineNumbers="true" expandable="true" %}

```kotlin
class PaymentPasskeyActivity : AppCompatActivity() {

    private lateinit var d1Task: D1Task

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        d1Task = D1Task.Builder()
            /* Configure D1 Task */
            .build()

        val cardID = "your-card-id-here"
        val termsAndConditions: TermsAndConditions = TermsAndConditions().apply {
            complianceType = "TERMS_OF_USE_AND_PRIVACY_NOTICES"
            uri = "https://www.example.com".toUri()
            version = "1"
            datePublished = Date().toInstant()
            acceptedAt = Date().toInstant()
            locale = "en-US"
            callToActionText = "Create Passkey"
            companyName = "My Company"
            consentLanguageText = "Sample Language Text"
            consentLanguageTextVersion = "2"
            channel = "WEB"
        }

        // Using email
        val email = "john.doe@example.com" 
        var consumerInfo = ConsumerInfo(email)
        lifecycleScope.launch(Dispatchers.Main) {
            try {
                val paymentPasskey = withContext(Dispatchers.IO) {
                    d1Task.paymentPasskeyService.create(
                        cardID,
                        consumerInfo,
                        termsAndConditions,
                        this@PaymentPasskeyActivity
                    ).await()
                }

                // Handle the newly created payment passkey.
                val paymentPasskeyCardID = paymentPasskey.cardID
                val paymentPasskeyID = paymentPasskey.paymentPasskeyID
                val creationTime = paymentPasskey.creationTime
                val state = paymentPasskey.state
                val deviceName = paymentPasskey.deviceName
                val deviceNickname = paymentPasskey.deviceNickname
                val isOnCurrentDevice = paymentPasskey.isOnCurrentDevice
            } catch (exception: D1Exception) {
                // Refer to D1 SDK Integration – Error Management section
            }
        }

        // Using phone and country code
        val phoneNumber = "5551234567"
        val phoneNumberCountryCode = "+1"
        consumerInfo = ConsumerInfo(phoneNumber, phoneNumberCountryCode)
        lifecycleScope.launch(Dispatchers.Main) {
            try {
                val paymentPasskey = withContext(Dispatchers.IO) {
                    d1Task.paymentPasskeyService.create(
                        cardID,
                        consumerInfo,
                        termsAndConditions,
                        this@PaymentPasskeyActivity
                    ).await()
                }

                // Handle the newly created payment passkey.
                val paymentPasskeyCardID = paymentPasskey.cardID
                val paymentPasskeyID = paymentPasskey.paymentPasskeyID
                val creationTime = paymentPasskey.creationTime
                val state = paymentPasskey.state
                val deviceName = paymentPasskey.deviceName
                val deviceNickname = paymentPasskey.deviceNickname
                val isOnCurrentDevice = paymentPasskey.isOnCurrentDevice
            } catch (exception: D1Exception) {
                // Refer to D1 SDK Integration – Error Management section
            }
        }
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)

        val appLink = "<your-domain>" // Refer to AndroidManifest.xml setup
        val customScheme = "myapp://passkey" // Refer to AndroidManifest.xml setup
        if (intent.data != null && (intent.toUri(Intent.URI_INTENT_SCHEME)
                .contains(appLink) || intent.toUri(Intent.URI_INTENT_SCHEME).contains(customScheme))
        ) {
            lifecycleScope.launch(Dispatchers.Main) {
                d1Task.paymentPasskeyService.handleResult(intent)
            }
        }
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Android - Mastercard with language" %}
{% code lineNumbers="true" expandable="true" %}

```kotlin
class PaymentPasskeyActivity : AppCompatActivity() {

    private lateinit var d1Task: D1Task

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        d1Task = D1Task.Builder()
            /* Configure D1 Task */
            .build()

        val cardID = "your-card-id-here"
        val language = "en-US"

        // Using email
        val email = "john.doe@example.com"
        var consumerInfo = ConsumerInfo(email)
        lifecycleScope.launch(Dispatchers.Main) {
            try {
                val paymentPasskey = withContext(Dispatchers.IO) {
                    d1Task.paymentPasskeyService.create(
                        cardID,
                        consumerInfo,
                        language,
                        this@PaymentPasskeyActivity
                    ).await()
                }

                // Handle the newly created payment passkey.
                val paymentPasskeyCardID = paymentPasskey.cardID
                val paymentPasskeyID = paymentPasskey.paymentPasskeyID
                val creationTime = paymentPasskey.creationTime
                val state = paymentPasskey.state
                val deviceName = paymentPasskey.deviceName
                val deviceNickname = paymentPasskey.deviceNickname
                val isOnCurrentDevice = paymentPasskey.isOnCurrentDevice
            } catch (exception: D1Exception) {
                // Refer to D1 SDK Integration – Error Management section
            }
        }

        // Using phone and country code
        val phoneNumber = "5551234567"
        val phoneNumberCountryCode = "+1"
        consumerInfo = ConsumerInfo(phoneNumber, phoneNumberCountryCode)
        lifecycleScope.launch(Dispatchers.Main) {
            try {
                val paymentPasskey = withContext(Dispatchers.IO) {
                    d1Task.paymentPasskeyService.create(
                        cardID,
                        consumerInfo,
                        language,
                        this@PaymentPasskeyActivity
                    ).await()
                }

                // Handle the newly created payment passkey.
                val paymentPasskeyCardID = paymentPasskey.cardID
                val paymentPasskeyID = paymentPasskey.paymentPasskeyID
                val creationTime = paymentPasskey.creationTime
                val state = paymentPasskey.state
                val deviceName = paymentPasskey.deviceName
                val deviceNickname = paymentPasskey.deviceNickname
                val isOnCurrentDevice = paymentPasskey.isOnCurrentDevice
            } catch (exception: D1Exception) {
                // Refer to D1 SDK Integration – Error Management section
            }
        }
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)

        val appLink = "<your-domain>" // Refer to AndroidManifest.xml setup
        val customScheme = "myapp://passkey" // Refer to AndroidManifest.xml setup
        if (intent.data != null && (intent.toUri(Intent.URI_INTENT_SCHEME)
                .contains(appLink) || intent.toUri(Intent.URI_INTENT_SCHEME).contains(customScheme))
        ) {
            lifecycleScope.launch(Dispatchers.Main) {
                d1Task.paymentPasskeyService.handleResult(intent)
            }
        }
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="iOS - Mastercard with consent" %}
{% code lineNumbers="true" expandable="true" %}

```swift
		// BEGIN
        let cardID = ""
        let window = UIWindow() // use app window  
        // Consumer information - for Mastercard, provide either email or phone number
        let consumerWithEmail = PaymentPasskeyService.ConsumerInfo(email: "user@example.com")
        let consumerWithPhoneNumber = PaymentPasskeyService.ConsumerInfo(phoneNumber: "1234567890",
                                                                         phoneNumberCountryCode: "+1")
        
        // Terms and conditions (required for Mastercard)
        var termsAndConditions = PaymentPasskeyService.TermsAndConditions()
        termsAndConditions.acceptedAt = Date().timeIntervalSince1970
        termsAndConditions.locale = "en-US"
        termsAndConditions.callToActionText = "I agree to the terms and conditions"
        termsAndConditions.companyName = "Example Bank"
        termsAndConditions.complianceType = "TERMS_OF_USE_AND_PRIVACY_NOTICES"
        termsAndConditions.uri = URL(string: "https://example.com/terms")
        termsAndConditions.version = "1"
        termsAndConditions.datePublished = Date().timeIntervalSince1970
        termsAndConditions.channel = "WEB"
        termsAndConditions.languageText = "Terms and conditions text"
        termsAndConditions.languageTextVersion = "1.0"
        
        // Use email to register
        do {
            let service = try d1Task.paymentPasskeyService()
            let passkey = try await service.create(cardID,
                                                   consumerInfo: consumerWithEmail,
                                                   termsAndConditions: termsAndConditions,
                                                   window: window)
            
            // Passkey created successfully
            print("Card ID: \(passkey.cardID)")
            print("Passkey ID: \(passkey.paymentPasskeyID)")
            if let creationTime = passkey.creationTime {
                print("Creation Time: \(creationTime)")
            }
        } catch {
            // Refer to D1 SDK Integration – Error Management section
        }
        
        //OR use phone number to register
        do {
            let service = try d1Task.paymentPasskeyService()
            let passkey = try await service.create(cardID,
                                                   consumerInfo: consumerWithPhoneNumber,
                                                   termsAndConditions: termsAndConditions,
                                                   window: window)
            
            // Passkey created successfully
            print("Card ID: \(passkey.cardID)")
            print("Passkey ID: \(passkey.paymentPasskeyID)")
            if let creationTime = passkey.creationTime {
                print("Creation Time: \(creationTime)")
            }
        } catch {
            // Refer to D1 SDK Integration – Error Management section
        }
        // END
```

{% endcode %}
{% endtab %}

{% tab title="iOS - Mastercard with language" %}
{% code lineNumbers="true" expandable="true" %}

```swift
		// BEGIN
        let cardID = ""
        let window = UIWindow() // use app window 
        let language = "en-US" // ISO 639-1 + ISO 3166-1 alpha-2 format 
        // Consumer information - for Mastercard, provide either email or phone number
        let consumerWithEmail = PaymentPasskeyService.ConsumerInfo(email: "user@example.com")
        let consumerWithPhoneNumber = PaymentPasskeyService.ConsumerInfo(phoneNumber: "1234567890",
                                                                         phoneNumberCountryCode: "+1")
        // Use email to register
        do {
            let service = try d1Task.paymentPasskeyService()
            let passkey = try await service.create(cardID,
                                                   consumerInfo: consumerWithEmail,
                                                   language: language,
                                                   window: window)
            
            // Passkey created successfully
            print("Card ID: \(passkey.cardID)")
            print("Passkey ID: \(passkey.paymentPasskeyID)")
            if let creationTime = passkey.creationTime {
                print("Creation Time: \(creationTime)")
            }
        } catch {
            // Refer to D1 SDK Integration – Error Management section
        }
        
        // Or use phone number to register
        do {
            let service = try d1Task.paymentPasskeyService()
            let passkey = try await service.create(cardID,
                                                   consumerInfo: consumerWithPhoneNumber,
                                                   language: language,
                                                   window: window)
            
            // Passkey created successfully
            print("Card ID: \(passkey.cardID)")
            print("Passkey ID: \(passkey.paymentPasskeyID)")
            if let creationTime = passkey.creationTime {
                print("Creation Time: \(creationTime)")
            }
        } catch {
            // Refer to D1 SDK Integration – Error Management section
        }
        // END

```

{% endcode %}
{% 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/payment-passkeys-issuers/implement-payment-passkeys/create-a-payment-passkey.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.
