Tap to Pay on iPhone beta

To process transactions with Tap to Pay on iPhone, integrate Moov's iOS SDK, MoovKit.

Accept all types of in-person, contactless payments right on your iPhone with no extra terminals or hardware needed. Accept physical debit and credit cards, Apple Pay, and other digital wallets. Tap to Pay on iPhone uses the built-in security and privacy features of iPhone to help protect your business and customer data.

This guide outlines how to set up Tap to Pay on iPhone. For the best results, merchants should implement code that prevents the screen or application from sleeping. If the screen or application sleeps while a transaction is in-flight, processing may be interrupted or fail. Additionally, ensure developer mode on the device is turned off before starting any transaction in production mode.

To use Tap to Pay on iPhone, you'll need to integrate with Moov's SDKs. Integration consists of two main phases:

  1. Onboard Terminal Applications – Register terminal applications then link them with a merchant
  2. Accept and Process Payments – Configure the merchant’s mobile application to process contactless payments

View Moov's full iOS SDK (MoovKit) documentation for more information.

Prerequisites & supported devices

Ensure the app is running on a compatible device and iOS version. You can use isPaymentCardSupported() to verify device compatibility.

  • iPhone XS or newer
  • iOS 16.0+ (US)
  • Latest iOS version recommended for optimal performance

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 via via Swift Package Manager.

1
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.

1
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.

1
2
3
4
5
6
7
8
<?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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import Foundation
import PartnerClient
import os

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

  let partnerClient: PartnerClient
  let partnerAccountID: UUID
  let merchantAccountID: UUID

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

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

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 TerminalApplication via the create terminal application endpoint
1
2
3
4
5
curl -X POST "/terminal-applications" \
  -H "Authorization: Bearer {token}" \
  --data '{
    "platform": "android"
  }'\
  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 TerminalApplication to a merchant account via the link terminal application endpoint

1
2
3
4
5
curl -X POST "/accounts/{accountID}/terminal-applications" \
  -H "Authorization: Bearer {token}" \
  --data '{
    "terminalApplicationID": "UUID"
  }'\

Terms & condition acceptance

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.

Create transfer

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.
1
2
3
4
5
6
do {
  let identifier = try await moov.initReader()
  logger.debug("identifier \\(String(describing: identifier))")
} catch {
  logger.error("Failed to fetch identifier")
}
  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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
do {
  let accessToken = // source from Moov backend
  let paymentMethodId = // source from Moov backend
  let transfer = try await moov.createTapTransfer(
    for: amount, 
    currencyCode: CurrencyCode.USD, 
    transactionType: .purchase, 
    destinationPaymentMethodId: paymentMethodId, 
    customerAccountId: nil, 
    description: nil, 
    using: accessToken
	)
} catch {
	logger.error("Card read error: \(error)")
}

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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
do {
  let amt = Decimal(1.23)
  let authResponse = try await moov.createTapAuthorization(
    for: amount,
    currencyCode: CurrencyCode.USD,
    transactionType: .purchase,
    customerAccountID: UUID()
  )
    
// send authResponse.paymentToken to your backend to create a transfer using authorization as the source
} catch {
  logger.error("Card read error: \(error)")
}

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/.

Summary Beta