Tap to Pay on iPhone

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.

Prerequisites

Requirement Details
Device iPhone XS or newer. Call isPaymentCardSupported() 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.
Moov capability The merchant needs collect-funds.card-payments and wallet.balance, the same as for online card payments (capabilities). 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

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 for SDK reference.

Integrate MoovKit

Moov's SDK to integrate Tap to Pay on iPhone functionality is available for download and you can find more details in the documentation.

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

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 documentation for complete details.

And finally, add MoovKit to your project.

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.

After receiving the entitlement, you should configure the entitlement in your project. View the full instructions on developer.apple.com.

<?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 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:

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 documentation for more details and context.

Register application

Applications must be registered with Moov to be authorized to use MoovKit. The process for application registration is as follows:

  1. Create a terminal application via the POST terminal application endpoint.
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"
  }'
  1. 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.

  2. Once the application is enabled, you must link the app's terminalApplicationID to a merchant account via the link POST terminal application endpoint.

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 for more information on terms and conditions.

Accept a payment

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.
do {
  _ = try await moov.initReader()
} catch {
  print("Reader initialization failed")
}
  1. Create a payment session by calling createSession(invalidateTerminalConfiguration:) as early as possible to configure the device.
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

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.

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")
}

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.

Test mode

Test mode 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 section of the test mode data reference for available test cards.

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 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 guide explains the amount breakdown, purchase-only restriction, and surcharge requirements.

Tap to Share

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.

Error handling

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

If you encounter any issues, please create a support ticket 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 > General > 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/.