> 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/nfc-wallet-sdk-ios/implement-nfc-wallet/make-payments/implement-contactless-payment/4.-implement-payment.md).

# 4. Implement payment

You can now implement each contactless payment experience:

* **Double-click**: Start a contactless payment using the default payment card.
* **Field-detect**: Start a contactless payment when the POS reader is detected.
* **Manual mode**: Start a contactless payment after the end user selects a card.

For **field-detect**, you may need to prompt the end user to continue. Use either **double-click** or **manual mode**.

For **manual mode**, add UI that lets the end user select a card. Then start the payment with that card.

### Start payment <a href="#start-payment" id="start-payment"></a>

Start a contactless payment by calling `startPayment(withDigitalCardID:)`.

{% hint style="info" %}
`digitalCardID` is optional.

* Provide `digitalCardID` to pay with a specific card.\
  Use this for **manual mode**.
* Omit `digitalCardID` to let the SDK use the default payment card.\
  Use this for **double-click**.

To learn how the default payment card is selected, see [Set default payment card](/nfc-wallet-sdk-ios/implement-nfc-wallet/manage-digital-cards/set-default-payment-card.md).
{% endhint %}

```swift
// start payment with provided digitalCardID
// use case: manual mode (with User interaction)
await session.startPayment(withDigitalCardID: digitalCardID)

// start payment with the default card
// use case: double-click
await session.startPayment()
```

This call starts the payment session.

Your issuer application must then:

1. Listen to `ContactlessPaymentSession.eventStream`.
2. Handle each event.

{% hint style="info" %}
Your digital wallet application can also attach transaction data. See [Set wallet transaction data](/nfc-wallet-sdk-ios/additional-features/add-wallet-transaction-data.md).
{% endhint %}

### Handle contactless payment events

After you call `startPayment(...)`, iterate over `ContactlessPaymentSession.eventStream`. Each item is a `ContactlessPaymentSession.Event`.

Handle events until the flow ends with `.transactionCompleted` or `.errorEncountered`.

* `.authenticationRequired(let authentication)`: Requests end user authentication (for example, Touch ID or Face ID).
  * Call `authentication.proceed()` to prompt for authentication and continue the flow.
  * Call `authentication.cancel()` to stop the flow. You then receive `.errorEncountered` with `cancelled`.
* `.authenticationCompleted`: Confirms authentication succeeded. You typically start card emulation next.
* `.posConnected`: Indicates the device is in the RF field of the POS terminal.\
  On iOS 18, you must use a presentment intent assertion to receive this event.
* `.posDisconnected`: Indicates the device left the POS terminal field while the transaction is still in progress.
* `.transactionCompleted(let transactionContext)`: Indicates the transaction completed successfully. Use `transactionContext` to display status or a receipt.
* `.errorEncountered(let error)`: Indicates the flow ended due to an error. See [Handle errors.](#example-of-a-full-implementation)

```swift
func startContactlessPayment() async {
  let contactlessPaymentSession = ContactlessPaymentSession()
  contactlessPaymentSession.startPayment()
        
  for await state in contactlessPaymentSession.eventStream {
    switch state {
      case .authenticationRequired(let authentication):
        // proceed with authentication. Biometric authentication will be prompted
        authentication.proceed()
        //authentication.cancel()
      case .authenticationCompleted:
        // authentication is success.
        contactlessPaymentSession.startEmulation()
      case .posConnected: break
        // informative: application and show information to end user
      case .posDisconnected: break
        // informative: application and show information to end user
      case .transactionCompleted(let transactionContext): break
        // display UI to end user
      case .errorEncountered(let error): break
        // display UI to end user
    }
  }
}
```

### Start emulation

Call `startEmulation()` after you receive `ContactlessPaymentSession.Event.authenticationCompleted`.

This starts card emulation and presents the NFC modal UI to the end user:

<div align="left"><figure><img src="/files/QfwZUMvK64vehIphUZ8Z" alt=""><figcaption><p>NFC modal user interface shown after authentication.</p></figcaption></figure></div>

While the NFC modal UI is displayed, the SDK suppresses field-detect events.

The SDK also enables APDU (Application Protocol Data Unit) exchange with the POS terminal.

The end user has 60 seconds to complete the contactless payment.

If time expires, the SDK emits `.errorEncountered` with `maxSessionDurationReached`.

You can call `startEmulation()` multiple times.

```swift
contactlessPaymentSession.startEmulation()
```

### Change the payment card

Use `startPayment(withDigitalCardID:)` when the end user wants to pay with a non-default payment card.

This API:

* Sets the selected card as the default payment card (if the update succeeds).
* Continues the payment flow with that card.

{% hint style="warning" %}
This call change the default payment card **persistently**.\
If the SDK successfully updates the default payment card, it stays selected even if the payment fails or is cancelled.
{% endhint %}

Pick one of the following flows based on when you call `startEmulation()`.

#### Scenario A: Start emulation after authentication

Call `startEmulation()` when you receive `ContactlessPaymentSession.Event.authenticationCompleted`.

* The NFC modal UI appears immediately after authentication.
* To change the card, the end user must:
  1. Tap **Cancel** in the NFC modal UI.
  2. Select a different card in your UI.
  3. Call `startPayment(withDigitalCardID:)` again with the new `digitalCardID`.

#### Scenario B: Start emulation on POS terminal tap

Call `startEmulation()` when you receive `ContactlessPaymentSession.Event.posConnected`.

* The end user can switch cards in your UI before tapping the POS terminal.
* The NFC modal UI appears only after the device enters the RF field.

{% hint style="info" %}
Scenario B requires `NFCPresentmentIntentAssertion` to control reader detection and prevent the system from launching the default payment application.

* If your issuer application is **not** the default payment application (iOS 17.4 and iOS 18), you must use `NFCPresentmentIntentAssertion`. Otherwise, the default payment application launches and your payment flow cannot proceed.
* If your issuer application **is** the default payment application (iOS 18), you must use `NFCPresentmentIntentAssertion` to receive `ContactlessPaymentSession.Event.posConnected` from reader detection. Without it, reader detection is delivered via `NFCWindowSceneEvent.readerDetected` in your `SceneDelegate`.
  {% endhint %}

### Cancel contactless payment <a href="#cancel-contactless-payment" id="cancel-contactless-payment"></a>

Cancel the in-progress contactless payment by calling `cancel()`.

Your issuer application then receives `.errorEncountered` with `cancelled`. Use it to reset your UI state.

```swift
contactlessPaymentSession.cancel()
```

### Set alert message <a href="#set-alert-message" id="set-alert-message"></a>

Set the message shown in the NFC modal UI.

```swift
contactlessPaymentSession.setAlertMessage("POS Connected")
```

### Handle errors <a href="#example-of-a-full-implementation" id="example-of-a-full-implementation"></a>

When the flow ends with `ContactlessPaymentSession.Event.errorEncountered(let error)`, treat the payment as failed. The table below summarizes the list of possible errors:

| Error                              | Description                                                                                                                                                |
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `cancelled`                        | User cancels the transaction.                                                                                                                              |
| `maxSessionDurationReached`        | The session has expired because the maximum duration has reached.                                                                                          |
| `nfcPermissionNotAccepted`         | Permission is not granted for NFC connection.                                                                                                              |
| `systemEligibilityFailed`          | System is not eligible for contactless payment. Example: Apple ID or device location is not in EEA.                                                        |
| `keychainError`                    | An error is encountered in the keychain operation.                                                                                                         |
| `noDefaultCard`                    | No default card is set before the transaction.                                                                                                             |
| `noPaymentKeys`                    | There is no payment keys for the card. It is required to call for a replenishment.                                                                         |
| `apduFailure`                      | An error is encountered in the APDU exchange between the device and POS terminal.                                                                          |
| `transmissionError`                | General transmission error. You are allowed to retry the send operation if connection with NFC reader is still valid.                                      |
| `sessionInvalidated`               | The card session has been invalidated by the system. Example: The application is in the background.                                                        |
| `unknown`                          | An unknown error is encountered during the transaction.                                                                                                    |
| `deviceEnvironmentUnsafe`          | Device Environment Unsafe error.                                                                                                                           |
| `setDefaultCardFailure`            | An error is encountered when the digital card ID provided is set as the default card when the `startPayment(withDigitalCardID:)` API is called.            |
| `invalidDigitalCardID`             | An error is encountered when the Digital Card ID provided is invalid when `startPayment(withDigitalCardID:)`.                                              |
| `authenticationExpired` is called. | An error is encountered when a payment is made after the validity period of the authentication.                                                            |
| `posNotSupported`                  | An error is encountered when the POS terminal does not have the selected AID in the list.                                                                  |
| `cardNotSupported`                 | The card is not supported for contactless payment.                                                                                                         |
| `cardNotActive`                    | The card is not active.                                                                                                                                    |
| `authenticationKeyInvalidated`     | An error is encountered when the device passcode is turned off, causing the secure data to be wiped. Enable the passcode again and reinitialize the setup. |
| `biometricNotEnrolled`             | An error is encountered when the biometric data is not enrolled/supported.                                                                                 |
| `authenticationFailed`             | An error is encountered when the authentication is canceled, an app is interrupted, or a device passcode is missing.                                       |

{% hint style="warning" %}
NFC Wallet SDK automatically wipes stored credentials when `authenticationKeyInvalidated` occurs (for example, after a passcode reset or security change).

Your application should guide the end user through re-enrollment.
{% endhint %}


---

# 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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.payments.thalescloud.io/nfc-wallet-sdk-ios/implement-nfc-wallet/make-payments/implement-contactless-payment/4.-implement-payment.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
