# Tap to Pay on iPhone

> Accept contactless card payments in person on iPhone with Tap to Pay and Moov's iOS SDK, MoovKit, from reader setup through authorization and transfer.

> AI agents: search and read all Moov docs with the docs MCP server at https://docs.moov.io/mcp (no API key). Setup: https://docs.moov.io/guides/developer-tools/mcp-ai/docs-mcp/index.md

Accept contactless cards, Apple Pay, and other digital wallets in person on an iPhone, with no extra hardware, by integrating Moov's iOS SDK, [MoovKit](https://moovfinancial.github.io/moov-ios/documentation/moovkit/getting-started/).

[*Video: ttpoi short*](https://docs.moov.io/guides/accept-payments/cards/tap-to-pay-iphone/images/ttpoi-short.mp4)

## Prerequisites

| Requirement       | Details                                                                                                                                                                                                                                               |
|-------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Device            | iPhone XS or newer. Call [isPaymentCardSupported()](https://moovfinancial.github.io/moov-ios/documentation/moovkit/moov/ispaymentcardsupported%28%29) to check hardware support; it doesn't check the iOS version or the entitlement.                 |
| OS                | iOS 17.0 or later; the latest iOS version is recommended                                                                                                                                                                                              |
| SDK               | MoovKit v0.27.0 or later, added with Swift Package Manager                                                                                                                                                                                            |
| Apple entitlement | Tap to Pay on iPhone entitlement (`com.apple.developer.proximity-reader.payment.acceptance`), requested from Apple. See the [SDK developer guidelines](https://moovfinancial.github.io/moov-ios/documentation/moovkit/developer-guidelines/).         |
| Moov capability   | The merchant needs `collect-funds.card-payments` and `wallet.balance`, the same as for online card payments ([capabilities](/guides/accounts/capabilities/index.md)). The buyer needs no capability; transfers use the `card-present-payment` payment method. |
| Moov registration | A terminal application registered with Moov, `enabled`, and linked to the merchant account (step 5 below)                                                                                                                                             |
| Test mode         | Works on the iPhone Simulator with simulated test cards; see [test mode](#test-mode)                                                                                                                                                                  |

Before processing in production:

- Prevent the screen or app from sleeping while a transaction is in flight. If it sleeps, processing may be interrupted or fail.
- Turn off **developer mode** on the device.

Setup has two phases: register your terminal application with Moov and link it to a merchant, then configure the merchant's app to accept payments. See the full [MoovKit documentation](https://moovfinancial.github.io/moov-ios/documentation/moovkit/getting-started/) for SDK reference.

## Integrate MoovKit

Moov's SDK to integrate Tap to Pay on iPhone functionality is available for [download](https://github.com/moovfinancial/moov-ios) and you can find more details in the [documentation](https://moovfinancial.github.io/moov-ios/documentation/moovkit/).

Add MoovKit as a package dependency to your project with Swift Package Manager.

```fallback
https://github.com/moovfinancial/moov-ios
```

Within the project you'll also need to embed the framework and link binary with libraries. See the [MoovKit](https://moovfinancial.github.io/moov-ios/documentation/moovkit/getting-started) documentation for complete details.

And finally, add MoovKit to your project.

```swift
import MoovKit
```

## Request entitlement from Apple

Your application must request an entitlement from Apple for Tap to Pay on iPhone. You can request from your [Apple Developer Account](https://developer.apple.com/contact/request/contactless-payments).

After receiving the entitlement, you should configure the entitlement in your project. View the full instructions on [developer.apple.com](https://developer.apple.com/documentation/proximityreader/setting-up-the-entitlement-for-tap-to-pay-on-iphone).

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.developer.proximity-reader.payment.acceptance</key>
    <true/>
</dict>
</plist>
```

## Review Apple's guidelines

Before submitting your application, review Apple’s [human interface guidelines](https://developer.apple.com/design/human-interface-guidelines/tap-to-pay-on-iphone) to ensure your implementation follows all requirements to avoid any delays in the approval process.

## Implement terminal configuration provider

Implement a terminal configuration provider to initialize MoovKit. For production use, fetch the configuration from your backend. Below is a sample implementation, assuming `PartnerClient` is implemented to interact with your backend API:

```swift
import Foundation
import MoovKit
import PartnerClient
import os

actor TerminalConfigProvider: TerminalConfigurationProvider {
  let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "main")
  
  var terminalConfiguration: String {
    get async throws {
        return try await fetch(terminalApplicationID: TerminalApplicationID("7922ce07-daf3-48f2-aab3-ed878c76a1a3"))
    }
  }

  let partnerClient: PartnerClient
  let merchantAccountID: MerchantAccountID

  public init(partnerClient: PartnerClient, merchantAccountID: MerchantAccountID) {
    self.partnerClient = partnerClient
    self.merchantAccountID = merchantAccountID
  }

  func fetch(terminalApplicationID: TerminalApplicationID) async throws -> String {
    do {            
      let terminalConfig = try await partnerClient.getTerminalConfiguration(
        accountID: merchantAccountID,
        terminalApplicationID: terminalApplicationID
      )
      return terminalConfig.configuration
    } catch {
      logger.error("\(error)")
      throw error
    }
  }
}

let terminalConfigProvider = TerminalConfigProvider(
  partnerClient: partnerClient,
  merchantAccountID: MerchantAccountID("merchant-account-id")
)
let moov = Moov(terminalConfigProvider: terminalConfigProvider)
```

View the [SDK](https://moovfinancial.github.io/moov-ios/documentation/moovkit/getting-started/#6-Configure-Terminal-Provider) documentation for more details and context.

## Register application

Applications must be registered with Moov to be authorized to use [MoovKit](https://moovfinancial.github.io/moov-ios/documentation/moovkit/getting-started/). The process for application registration is as follows:

1. Create a terminal application via the `POST` terminal application [endpoint](/api/sources/terminal-applications/create/index.md).

```zsh
curl -X POST "https://api.moov.io/terminal-applications" \
  -H "Authorization: Bearer {token}" \
  -H "x-moov-version: v2026.07.00" \
  --data '{
    "platform": "ios",
    "appBundleID": "com.example.app"
  }'
```

2. The application will move from `pending` to `enabled` once approved. You can subscribe to the `terminalAppRegistration.updated` webhook to be notified of the status updates.
3. Once the application is enabled, you must link the app's `terminalApplicationID` to a merchant account via the link `POST` terminal application [endpoint](/api/sources/terminal-applications/link/index.md).

```zsh
curl -X POST "https://api.moov.io/accounts/{accountID}/terminal-applications" \
  -H "Authorization: Bearer {token}" \
  -H "x-moov-version: v2026.07.00" \
  --data-raw '{
    "terminalApplicationID": "12345678-1234-1234-1234-123456789012"
  }'
```

## Accept Apple's terms and conditions

Merchants need to accept Apple's terms and conditions - once for each merchant identifier. Once the terms and conditions are accepted, any device using the same merchant identifier can use Tap to Pay on iPhone without accepting the terms and conditions again. The additional devices don’t need to use the same Apple account, or be signed in to Apple.

For the best user experience, it’s recommended to present the terms and conditions before the checkout experience.

View [Apple's documentation](https://developer.apple.com/documentation/ProximityReader/adding-support-for-tap-to-pay-on-iphone-to-your-app#Display-the-Terms-and-Conditions-interface-to-the-merchant) for more information on terms and conditions.

## Accept a payment

> **Note:** Tap to Pay on iPhone uses the built-in security and privacy features of iPhone to help protect your business and customer data. When a payment is processed, Apple doesn’t store card numbers on iPhone or on Apple servers.

1. Once terms and conditions have been accepted, initialize the reader hardware. MoovKit checks the Tap to Pay entitlement during `initReader()`. If the app is missing the entitlement, the call throws `CardReaderError.notAllowed`.

```swift
do {
  _ = try await moov.initReader()
} catch {
  print("Reader initialization failed")
}
```

2. Create a payment session by calling `createSession(invalidateTerminalConfiguration:)` as early as possible to configure the device.

> **Tip:** Each time your app launches, create a `PaymentCardReaderSession` to handle the reading of payment card information. You create this session using the `createSession(invalidateTerminalConfiguration:)` method of your Moov object. The initial configuration of the device can take up to two minutes, but subsequent requests typically take only a few seconds. Register an `eventHandler:` to receive an asynchronous stream of events that you can use to display a progress UI to the merchant.

There are two main paths to accept EMV payments:

- Directly create a `transfer` from the SDK
- Create an authorization via the SDK and create the `transfer` separately

**Directly create transfer**

This method accepts the EMV tap, authorizes the transaction with the card network, creates a transfer on the Moov platform, and returns the transfer ID.

```swift
import Foundation
import MoovKit

do {
  let accessToken = "access-token-from-your-backend"
  let paymentMethodId = PaymentMethodID("destination-payment-method-id")
  let tapAmount = TapAmount(Decimal(string: "10.00")!, currency: .USD)
  let transfer = try await moov.createTapTransfer(
    for: tapAmount,
    transactionType: .purchase, 
    destinationPaymentMethodId: paymentMethodId, 
    customerAccountId: nil, 
    description: nil, 
    using: accessToken
	)
} catch {
	print("Card read error")
}
```

**SDK authorization & transfer API**

This method accepts the EMV tap, authorizes the transaction with the card network, and returns a token which can be passed into the [create transfer](/api/money-movement/transfers/create/index.md) API.

```swift
import Foundation
import MoovKit

do {
  let tapAmount = TapAmount(Decimal(string: "1.23")!, currency: .USD)
  let authResponse = try await moov.createTapAuthorization(
    for: tapAmount,
    transactionType: .purchase,
    customerAccountId: nil
  )
    
// send authResponse.paymentToken to your backend to create a transfer using authorization as the source
} catch {
  print("Card read error")
}
```

The transfer's `source.cardDetails.status` moves to `confirmed` once the card network authorizes it. Subscribe to the `transfer.updated` webhook to track it to `completed`. See [card transfer statuses](/guides/accept-payments/cards/timing/index.md#card-transfer-statuses).

## Test mode

[Test mode](/guides/get-started/api-authentication/test-mode/index.md) allows you to explore and verify your integration with the MoovKit SDK before processing live payments. The terminal configuration fetched from the Moov API determines whether the SDK operates in test or production mode. In test mode, MoovKit provides access to features and methods specifically designed for testing and simulating various scenarios.

Select from predefined test cards to simulate different authorization outcomes, including approvals and declines, without requiring a physical card tap. Instead of requiring a physical card tap the SDK will present a screen to select a card to use. See the [Tap to Pay](/guides/get-started/api-authentication/test-mode/index.md#tap-to-pay-on-mobile) section of the test mode data reference for available test cards.

> **Tip:** Test mode is also supported on the iPhone Simulator, allowing you to develop and test your integration without a physical device.

Refer to the [iOS SDK](https://moovfinancial.github.io/moov-ios/documentation/moovkit/test-mode) documentation for complete details on how to use test-specific functionality.

## Tips and surcharges

Pass a `TapAmount` to include a tip or surcharge. The [iOS SDK](https://moovfinancial.github.io/moov-ios/documentation/moovkit/getting-started/#tip-and-surcharge) guide explains the amount breakdown, purchase-only restriction, and surcharge requirements.

## Tap to Share

[Tap to Share](https://moovfinancial.github.io/moov-ios/documentation/moovkit/tap-to-share/) lets a merchant share a cart or request customer details on the customer's iPhone. Tap to Share requires iOS 27 or later and an iPhone 12 or later.

For Apple's feature overview and availability details, see [Tap to Share](https://developer.apple.com/tap-to-pay/tap-to-share/).

## Error handling

MoovKit communicates errors with your application via exceptions. View the documentation for the following error types:

- [SDKError](https://moovfinancial.github.io/moov-ios/documentation/moovkit/sdkerror)
- [CardReaderError](https://moovfinancial.github.io/moov-ios/documentation/moovkit/cardreadererror)
- [CardReadError](https://moovfinancial.github.io/moov-ios/documentation/moovkit/cardreaderror)

If you encounter any issues, please create a [support ticket](https://portal.usepylon.com/moov/forms/problem) and attach the SDK logs. Be sure to include:

- The SDK version and OS/device details
- A description of the steps that led to the error
- The relevant portion of the logs (compressed if large)
- Ensure any sensitive information (API keys, card details, or PII) is redacted

* * *

Tap to Pay on iPhone requires a supported payment app and the latest version of iOS. Update to the latest version by going to Settings &gt; General &gt; Software Update. Tap Download and Install. Some contactless cards may not be accepted by your payment app. Transaction limits may apply. The Contactless Symbol is a trademark owned by and used with permission of EMVCo, LLC. Tap to Pay on iPhone is not available in all markets. For Tap to Pay on iPhone countries and regions, see [https://developer.apple.com/tap-to-pay/regions/](https://developer.apple.com/tap-to-pay/regions/).

