# Tap to Share on iPhone

> Use Tap to Share with MoovKit to send a cart to a customer's iPhone, collect their details, and let them pay with Apple Pay on their own device.

> 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

Tap to Share extends [Tap to Pay on iPhone](/guides/accept-payments/cards/tap-to-pay-iphone/index.md) so a merchant's iPhone can connect to a customer's iPhone. The customer sees their cart, shares their contact or shipping details, and can pay with Apple Pay, all on their own device.

## Prerequisites

Tap to Share requires a newer merchant device than Tap to Pay on iPhone. What the customer can do depends on their device:

|                           | Merchant device                                                         | Customer on iPhone with iOS 27+ | Any other customer device                        |
|---------------------------|-------------------------------------------------------------------------|---------------------------------|--------------------------------------------------|
| Requirements              | iPhone 12 or newer, iOS 27+, Wi-Fi turned on (even when using cellular) | None                            | None                                             |
| Connection                | –                                                                       | Direct, local wireless          | Apple's web app fallback, through a server relay |
| View cart & share details | –                                                                       | ✓                               | ✓                                                |
| Apple Pay on their device | –                                                                       | ✓                               | –                                                |
| App needed                | Your app with MoovKit                                                   | None                            | None                                             |

You'll also need:

- An existing [Tap to Pay on iPhone](/guides/accept-payments/cards/tap-to-pay-iphone/index.md) integration, including the Tap to Pay entitlement, a registered and linked terminal application, and a `TerminalConfigurationProvider`
- Xcode 27 or newer, with the iOS 27 SDK
- The latest version of [MoovKit](https://github.com/moovfinancial/moov-ios)
- The **Tap to Share** capability in Xcode, plus the **Apple Pay** capability with a merchant identifier if you'll accept Apple Pay on the customer's device (step 1 below)

> **Tip:** You can keep your app's current deployment target. Every Tap to Share API is marked `@available(iOS 27.0, *)`, except `isCustomerSessionSupported()`. That method works on every iOS version MoovKit supports, and returns `false` on anything older than iOS 27.

## What Tap to Share does

With Tap to Pay on iPhone, the customer taps their card or phone on the merchant's iPhone. With Tap to Share, the merchant and customer bring the tops of their iPhones together. That opens a secure session between the two devices. From there, the merchant's app can:

- **Show the cart** – The customer sees each item, any discounts, and the total on their own screen, and it updates live as the merchant adds items.
- **Collect customer information** – Ask for an email address or phone number to find a customer account or send a receipt, collect a shipping or billing address, or sign the customer up for a membership with optional email and SMS marketing opt-ins.
- **Accept Apple Pay on the customer's device** – The customer approves the payment with Face ID or Touch ID on their own iPhone.
- **Show status screens** – Show the customer screens like **Payment completed** or **Checkout completed** once the transaction finishes.

The customer doesn't need to install your app. On an iPhone running iOS 27 or newer, iOS provides the whole customer experience. Other devices get Apple's web app fallback from the same tap, where customers can review their cart and share their details. Either way, the customer decides what to share for every request.

## When to use Tap to Share

Tap to Share is useful anywhere the checkout conversation happens face to face and the merchant is using an iPhone instead of a fixed point-of-sale screen:

- **No more typing on the merchant's phone** – Customers fill in emails and shipping addresses on their own device using AutoFill. This is faster and leads to fewer typos than reading details out loud to staff.
- **Customers can see their cart** – In line-busting, pop-up, or tableside settings, customers can check items and totals without leaning over the merchant's phone.
- **Easier sign-ups** – Capture loyalty or membership sign-ups, with email and SMS consent, as part of checkout.
- **More privacy** – The customer chooses exactly which fields to share, and their payment details stay on their own device.
- **Easy fallback** – If the customer's device can't use Tap to Share, or they'd rather not, the merchant can fall back to a regular Tap to Pay card tap in the same app.

## Add the Tap to Share capability

Tap to Share builds on your existing Tap to Pay on iPhone entitlement. In Xcode, select your app target, then in **Signing & Capabilities**, add the **Tap to Share** capability.

Adding the capability to your entitlements file isn't enough on its own. Xcode also needs a provisioning profile that grants the capability to your app's bundle ID.

If you plan to accept Apple Pay on the customer's device, also add the **Apple Pay** capability, with a merchant identifier.

Before you submit your app, review Apple's [Adding support for Tap to Share](https://developer.apple.com/documentation/proximityreader/adding-support-for-tap-to-share-to-your-app) documentation and [human interface guidelines](https://developer.apple.com/design/human-interface-guidelines/tap-to-pay-on-iphone).

## Check device support

Before you show a Tap to Share option to the merchant, check whether their device can support it:

```swift
guard moov.isCustomerSessionSupported() else {
  // Hide Tap to Share and use a Tap to Pay card tap instead.
  return
}
```

`isCustomerSessionSupported()` only checks the OS and reader. There's no API to detect the iPhone model, and the simulator always returns `true`. That means `createCustomerSession(configuration:)` can still throw `CustomerSessionError.notSupported`, so always handle that error and keep a fallback payment path.

## Open a session

First, initialize the reader with `initReader()`, as you do for Tap to Pay. Then, register a customer event handler **before** opening the session. That way, you'll catch the `connected` and `ready` events from the start.

```swift
await moov.setCustomerEventHandler { event in
  switch event {
  case .connected:    // The customer's iPhone connected.
  case .ready:        // Safe to share a cart or request information.
  case .disconnected: // The customer's iPhone went out of range.
  case .closed:       // The session ended.
  case .unsupported:  // An event added after this SDK release.
  }
}

let configuration = CustomerSessionConfiguration(
  currency: .USD,
  region: .unitedStates,
  privacyPolicyURL: URL(string: "https://example.com/privacy")!,
  storeName: "Oakbrook Mall",
  deviceName: "Emily"
)

try await moov.createCustomerSession(configuration: configuration)
```

The customer sees the store name, device name, and privacy policy link when they connect. Prompt the merchant to bring the tops of the two iPhones together. `createCustomerSession(configuration:)` returns once the devices connect. Wait for the `.ready` event before you send any requests. Any requests you send before that will throw `CustomerSessionError.notReady`.

MoovKit handles authentication for you. If you've already called `createSession(invalidateTerminalConfiguration:)` for Tap to Pay, MoovKit reuses that session. Otherwise, it fetches a card reader token from your `TerminalConfigurationProvider`.

## Share the cart

Send the cart to the customer's iPhone with `updateShoppingCart(_:)`. The customer's iPhone doesn't remember previous carts, so every call replaces the whole cart. Always send the complete cart, not just what changed.

- Enter amounts in whole currency units, like `15.00` for $15.00. They're formatted using the currency and region from your `CustomerSessionConfiguration` and the customer's locale.
- Give every item a unique `id`, and reuse it for the same product in later updates so the customer's screen can animate changes to quantity and price.
- To show a placeholder on the customer's screen, share an empty cart.

```swift
let cart = ShoppingCart(
  items: [
    ShoppingCart.Item(
      id: "HRSP-M-UN-11-001",
      name: "Hair Spray",
      quantity: 4,
      totalPrice: Decimal(string: "20.00")!,
      totalAdjustedPrice: Decimal(string: "15.00")!,
      descriptions: ["Unscented, 11 oz", "Buy 3 Get 1 Free Offer"],
      details: ["This item cannot be shipped."]
    ),
  ],
  summary: ShoppingCart.Summary(
    summaryLines: [
      ShoppingCart.Summary.LineItem(label: "Subtotal", value: Decimal(string: "15.00")!),
      ShoppingCart.Summary.LineItem(label: "Coupon Code: NY2OFF", value: Decimal(string: "-2.00")!),
      // Values that aren't amounts are displayed verbatim.
      ShoppingCart.Summary.LineItem(label: "Shipping", value: "Free"),
      ShoppingCart.Summary.LineItem(label: "Tax", value: "10%"),
    ],
    footer: "Thank you for shopping with us.",
    total: Decimal(string: "14.30")!
  )
)

let cartToken = try await moov.updateShoppingCart(cart)
```

Save the `ShoppingCartToken` from your most recent update. You'll need it to request Apple Pay.

## Request customer information

Each request opens a form on the customer's iPhone. The customer chooses what to share, so any field you ask for can come back as `nil`. If the customer declines the whole request, it throws `CustomerSessionError.userDeclined`. You can only show one form at a time. If you send a second request while a form is open, it throws `CustomerSessionError.sessionBusy`.

**Look up a customer**

Ask for contact details to find an existing customer account or send a receipt.

```swift
let info = try await moov.requestCustomerInfo(
  purpose: .checkIn,
  fields: [.emailAddress],
  message: "Look up your account"
)
// info.emailAddress, info.phoneNumber
```

**Collect an address**

Collect a shipping or billing address. The response always includes the customer's name and postal address. You can also request contact details.

```swift
let address = try await moov.requestAddress(
  purpose: .shipping,
  fields: [.emailAddress, .phoneNumber]
)
// address.name, address.street, address.city, address.state, address.postalCode, ...
```

**Sign up a customer**

Sign a customer up for a membership or loyalty program, with optional email and SMS marketing opt-ins and Markdown-formatted terms.

```swift
let signUp = try await moov.requestSignup(
  fields: [.emailAddress, .phoneNumber],
  emailConsent: .visible,
  smsConsent: .visible,
  termsAndConditions: "**Terms and Conditions**\n\nYour terms, in Markdown."
)
// signUp.emailOptInSelection, signUp.smsOptInSelection
```

The `purpose` explains to the customer why you're asking. Available purposes are `accountCreation`, `accountUpdate`, `billing`, `checkIn`, `membership`, `receipt`, and `shipping`. Consent options can be `hidden`, `visible`, or `preSelected`.

> **Note:** You can only request an email address or phone number. Apple also supports Wallet pass lookups and adding passes to Wallet, but MoovKit doesn't support those.

## Accept Apple Pay on the customer's device

Apple only shows the Apple Pay option for a cart you've already shared, so pass the `ShoppingCartToken` from your latest `updateShoppingCart(_:)` call. First, check `customerClientType`:

- `.localWireless` – The customer is on an iPhone with iOS 27 or newer, and you can offer Apple Pay.
- `.web` – The customer connected through Apple's web app fallback. Apple Pay isn't available, so take payment with a [Tap to Pay](/guides/accept-payments/cards/tap-to-pay-iphone/index.md#accept-a-payment) card tap.

```swift
guard await moov.customerClientType == .localWireless else {
  // Fall back to a Tap to Pay card tap on the merchant's iPhone.
  return
}

let paymentRequest = PKPaymentRequest()
paymentRequest.merchantIdentifier = "merchant.com.example.posapp"
paymentRequest.currencyCode = "USD"
paymentRequest.countryCode = "US"
paymentRequest.supportedNetworks = [.amex, .discover, .masterCard, .visa]
paymentRequest.merchantCapabilities = .threeDSecure
paymentRequest.paymentSummaryItems = paymentSummaryItems

let presented = try await moov.requestApplePayPayment(
  for: cartToken,
  using: paymentRequest,
  delegate: paymentAuthorizationControllerDelegate
)

if !presented {
  // The customer chose another option or hasn't set up Apple Pay.
  // Fall back to another payment method.
}
```

MoovKit shows the payment sheet and passes the authorization to your `PKPaymentAuthorizationControllerDelegate`. It doesn't process the resulting `PKPayment`. Your delegate sends the payment to your backend, and your backend completes it the same way as [Apple Pay on the web](/guides/accept-payments/cards/apple-pay/index.md):

1. **Link the token to the payer's account.** Call the [link Apple Pay token](/api/sources/apple-pay/token/index.md) endpoint with the `accountID` of the customer paying, and pass in the token from the `PKPayment`. Your access token needs the `/accounts/{accountID}/cards.write` scope for that account.
   
   ```zsh
   curl -X POST "https://api.moov.io/accounts/{payerAccountID}/apple-pay/tokens" \
     -H "Authorization: Bearer {token}" \
     -H "x-moov-version: v2026.07.00" \
     --data '{
       "token": {
         "paymentData": { ... },
         "paymentMethod": { ... },
         "transactionIdentifier": "string"
       }
     }'
   ```
   
   On API `v2026.04.00` and newer, the response is an array of payment methods. Use the `paymentMethodID` with a `paymentMethodType` of `apple-pay`.
2. **Create a transfer.** Call the [create transfer](/api/money-movement/transfers/create/index.md) endpoint with the `apple-pay` payment method as the `source` and the merchant's Moov wallet as the `destination`. Add the `X-Wait-For` [header](/guides/money-movement/events-and-statuses/index.md#create-transfer-responses) to get a synchronous response.
3. **Finish the payment sheet.** Call the delegate's completion handler with `.success` or `.failure`, based on the transfer result.

```swift
func paymentAuthorizationController(
  _ controller: PKPaymentAuthorizationController,
  didAuthorizePayment payment: PKPayment,
  handler completion: @escaping (PKPaymentAuthorizationResult) -> Void
) {
  Task {
    do {
      // Your backend links payment.token and creates the transfer.
      try await partnerClient.payWithApplePay(token: payment.token, amount: total)
      completion(PKPaymentAuthorizationResult(status: .success, errors: nil))
      try await moov.updateCustomerStatus(.paymentCompleted)
    } catch {
      completion(PKPaymentAuthorizationResult(status: .failure, errors: [error]))
    }
  }
}
```

If Apple Pay isn't available, the merchant can still take payment with a regular [Tap to Pay](/guides/accept-payments/cards/tap-to-pay-iphone/index.md#accept-a-payment) card tap on their own iPhone. Customer details collected through Tap to Share still work with that flow.

## Update status & close the session

After the payment goes through, show a status screen on the customer's iPhone. You can do this anytime a request form isn't open.

```swift
try await moov.updateCustomerStatus(.paymentCompleted)
```

Available statuses are `ready`, `paymentCompleted`, `paymentFailed`, and `checkoutCompleted`.

Close the session as soon as the checkout is done, so the customer's iPhone doesn't stay connected:

```swift
await moov.closeCustomerSession()
```

MoovKit returns customer details directly to your app. Your app is responsible for storing, using, and deleting that data in line with the privacy policy you linked in `CustomerSessionConfiguration`.

## Test mode

Tap to Share follows the same mode as your terminal configuration. In [test mode](/guides/get-started/api-authentication/test-mode/index.md), the session doesn't show anything on a customer device. Instead, it records what you shared and returns sample customer responses. That means you can test the whole flow in the iOS 27 Simulator without a second device. Requests sent before the session opens still throw `CustomerSessionError.notReady`, just like they would in production.

Moov doesn't support Apple Pay in test mode. In test mode, `requestApplePayPayment(for:using:delegate:)` always returns `false`, so your integration follows its normal fallback path.

|                                    | Simulator | Physical device          |
|------------------------------------|-----------|--------------------------|
| Test mode session                  | ✓         | ✓                        |
| Cart, status, and request flow     | ✓         | ✓                        |
| Real customer connection           | –         | Requires a second iPhone |
| Apple Pay on the customer's device | –         | Production only          |

You can't open a production session in the Simulator. There, `createCustomerSession(configuration:)` throws `CustomerSessionError.notSupported`. To test end to end, you need two physical iPhones, both on iOS 27 with Wi-Fi turned on. The merchant's iPhone needs to be an iPhone 12 or newer, with the entitlement and capability.

## Error handling

Session errors are reported as [CustomerSessionError](https://moovfinancial.github.io/moov-ios/documentation/moovkit/customersessionerror). Make sure to handle these errors:

| Error                               | Meaning                                                           | Suggested handling                                                              |
|-------------------------------------|-------------------------------------------------------------------|---------------------------------------------------------------------------------|
| `notSupported`                      | The device or OS can't run Tap to Share                           | Hide Tap to Share and use a Tap to Pay card tap instead                         |
| `wifiDisabled`                      | Wi-Fi is off                                                      | Ask the merchant to turn on Wi-Fi, even if they're using cellular               |
| `notReady`                          | A request was sent before the `.ready` event                      | Wait for `.ready` before sending requests                                       |
| `sessionBusy`                       | A form is already showing on the customer's device                | Wait for the current request to finish                                          |
| `userDeclined`                      | The customer declined to share                                    | Continue without the information, or ask the merchant to collect it another way |
| `incompatibleRequest`               | The customer's device or connection type can't handle the request | Check `customerClientType` and use a fallback                                   |
| `connectionFailed`, `pairingFailed` | The devices couldn't connect                                      | Ask the merchant to bring the iPhones together and try again                    |
| `paymentRequestCancelled`           | The customer dismissed the Apple Pay sheet                        | Offer another payment method                                                    |

If `initReader()` hasn't been called, `createCustomerSession(configuration:)` throws `SDKError.notInitialized`.

View the full [Tap to Share](https://moovfinancial.github.io/moov-ios/documentation/moovkit/tap-to-share) SDK documentation for more information. If you run into issues, create a [support ticket](https://portal.usepylon.com/moov/forms/problem) and attach the SDK logs, as described in the [Tap to Pay on iPhone](/guides/accept-payments/cards/tap-to-pay-iphone/index.md#error-handling) guide.

* * *

Tap to Share requires iPhone 12 or later and iOS 27 or later. Tap to Pay on iPhone requires a supported payment app and the latest version of iOS. Some contactless cards may not be accepted by your payment app. Transaction limits may apply. Tap to Pay on iPhone isn't 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/).

