# Accept payments via Tap to Pay on iPhone - iOS

The **Revolut Tap to Pay SDK for iOS** allows merchants to accept contactless payments directly on iPhone using Apple's Tap to Pay technology. This guide covers the client-side integration: installing the SDK, implementing the token provider, initialising the SDK, and accepting payments.

The SDK wraps Apple's `ProximityReader` framework and Revolut's payment infrastructure into a single integration. It handles device pairing, order creation, payment processing, and UI presentation - your app provides the token provider and calls the payment methods.

![Tap to Pay on iPhone](/img/accept-payments/payment-methods/tap-to-pay/tap-to-pay.png 'Tap to Pay on iPhone')

:::note
Tap to Pay is not currently supported in the [sandbox environment](https://sandbox-business.revolut.com). Real transactions must be made to test your implementation in the **production environment**.
:::

## How it works

From an implementation perspective, the Tap to Pay SDK works with the following components:

1. **Server-side:** Your server holds the merchant's long-lived OAuth tokens and provides short-lived device tokens to the iPhone app. For setup details, see [Get started](/docs/guides/merchant/accept-payments/in-person-payments/tap-to-pay/get-started).
1. **Client-side:** The SDK (`RevolutTapToPayKit`) is initialised with a token provider that fetches short-lived device tokens from your backend. The SDK handles Apple ProximityReader pairing, Card Reader Token management, order creation, payment processing, and presents the payment UI.
1. **Endpoint for webhooks:** Set up an endpoint to receive webhook events from the Merchant API to track the payment lifecycle. Webhooks are the only reliable way to get the final, authoritative payment status - client-side callbacks are for UI updates only. For more information, see: [Use webhooks to keep track of the payment lifecycle](/docs/guides/merchant/monitor-and-observe/webhooks/using-webhooks).

The payment flow works as follows:

1. Your app calls the SDK to start a payment session with an amount and currency.
1. The SDK requests a short-lived device token from your backend via the token provider.
1. The SDK handles Apple `ProximityReader` pairing and Card Reader Token management.
1. The SDK creates an order and processes the payment through Revolut's infrastructure.
1. The customer taps their contactless card or digital wallet on the iPhone.
1. The SDK returns the payment result and an optional receipt to your app.
1. Your server receives webhook notifications to confirm the final payment state.

<!-- The following diagram shows how the components interact during a payment:

```mermaid
sequenceDiagram
    participant App as Your iOS app
    participant Backend as Your backend
    participant SDK as Revolut Tap to Pay SDK
    participant Apple as Apple ProximityReader
    participant Revolut as Revolut API

    App->>SDK: Start a payment session with amount and currency
    SDK->>App: Request a device token via `TokenProviderProtocol`
    App->>Backend: Request a short-lived device token
    Backend->>Revolut: `POST /api/oauth/token` with `grant_type=exchange_token`
    Revolut->>Backend: Return short-lived device token
    Backend->>App: Return device token
    App->>SDK: Return `SdkTokenApiDto` with the access token
    SDK->>Apple: Pair the device and request a Card Reader Token
    Apple->>SDK: Return Card Reader Token
    SDK->>Revolut: Create an order and initiate the payment
    Revolut->>SDK: Confirm order created
    SDK->>Apple: Present the payment UI on the iPhone
    Note over Apple: Customer taps their contactless card or digital wallet
    Apple->>SDK: Return the payment result
    SDK->>Revolut: Confirm the final payment status
    Revolut->>SDK: Return final payment state
    SDK->>App: Call `onDidCharge` with receipt and result (UI still on screen)
    SDK->>App: Call `completion` (UI dismissed, app can continue)
    Revolut->>Backend: Send webhook event with payment status
``` -->

The SDK offers two payment methods: **Tap to Pay** (contactless card or wallet) and **QR code**. In `performPayments` (multi-payment mode), the merchant chooses between them for each transaction. In `performPayment` (single payment), the SDK uses Tap to Pay and automatically falls back to QR code if contactless isn't available.

:::info
For more information about the order and payment lifecycle, see: [Order and payment lifecycle](/docs/guides/merchant/reference/order-lifecycle).
:::

### What the SDK handles

The SDK manages the following internally - you should not call these endpoints directly:

| Responsibility | Description |
|----------------|-------------|
| **Device pairing** | Tap to Pay device and reader pairing with Apple's `ProximityReader` framework. |
| **Card Reader Token** | Card Reader Token handling and lifecycle management. |
| **Payment flow** | Creating and managing the Revolut payment flow - order creation, processing, and status tracking. |
| **Payment UI** | Presenting the payment UI (the SDK presents its own view controllers - you don't need to provide a presenting controller). |
| **Payment result** | Tracking the payment status and returning the final result to your app. |
| **QR code payments** | QR code as a primary payment option in `performPayments`, and as an automatic fallback in `performPayment` when contactless isn't available. |

### What your app handles

Your app remains responsible for the user experience around the SDK:

| Responsibility | Description |
|----------------|-------------|
| **Token provider** | Implementing `TokenProviderProtocol` to request short-lived device tokens from your backend. |
| **SDK initialisation** | Initialising the SDK with the token provider and `onLogout` handler. |
| **Payment sessions** | Starting a payment session with amount, currency, and reference details. |
| **User feedback** | Displaying SDK prompts, errors, cancellations, and final payment outcomes to the merchant. |
| **State management** | Updating your own order, basket, or POS state after the SDK returns the result. |

### Implementation overview

1. [Set up the Apple entitlement](#1-set-up-the-apple-entitlement)
1. [Install the SDK](#2-install-the-sdk)
1. [Implement the token provider](#3-implement-the-token-provider)
1. [Initialise the SDK](#4-initialise-the-sdk)
1. [First-time setup (optional)](#5-first-time-setup-optional)
1. [Receive payments](#6-receive-payments)
1. [Handle payment results](#7-handle-payment-results)

### Before you begin

Before you start this tutorial, ensure you have completed the [Get started](/docs/guides/merchant/accept-payments/in-person-payments/tap-to-pay/get-started) guide and have the following:

- [ ] Your application is registered and submitted to production in the [Developer Portal](https://developer.revolut.com/portal)
- [ ] The merchant has `API & integrations` permission enabled on their Revolut Business account
- [ ] Your backend serves short-lived device tokens to the iOS app (`grant_type=exchange_token`)
- [ ] Your backend can refresh the merchant's OAuth tokens when they expire (`grant_type=refresh_token`)
- [ ] Apple `ProximityReader` entitlement requested and approved

## Integrate the Tap to Pay SDK

The following steps walk you through the client-side integration: setting up the Apple entitlement, installing the SDK, implementing the token provider that connects to your backend, initialising the SDK, and accepting payments.

### 1. Set up the Apple entitlement

Before the SDK can use [Apple's Tap to Pay on iPhone](https://developer.apple.com/tap-to-pay/) technology, you must set up the `ProximityReader` entitlement in your Apple Developer account and add it to your Xcode project.

1. Follow Apple's guide to [set up the entitlement for Tap to Pay on iPhone](https://developer.apple.com/documentation/proximityreader/setting-up-the-entitlement-for-tap-to-pay-on-iphone).
1. Add the entitlement to your app's capabilities in Xcode under **Signing & Capabilities**.

    ```xml [RevolutTapToPayApp.entitlements]
    com.apple.developer.proximity-reader.payment.acceptance
    ```

:::warning
Without this entitlement, the SDK cannot pair with the device's payment reader and all payment methods will fail.
:::

### 2. Install the SDK

:::info
The minimum supported iOS version is 16.4.
:::

We recommend using [CocoaPods](https://cocoapods.org/) to integrate the SDK into your Xcode project.

1. Ensure you have the latest version of [CocoaPods](https://guides.cocoapods.org/using/getting-started.html) installed.
1. If your project doesn't have a [`Podfile`](https://guides.cocoapods.org/syntax/podfile.html) yet, create one by running:

    ```bash
    pod init
    ```

1. Add the following line to your `Podfile`:

    ```ruby [Podfile]
    pod 'RevolutPayments/RevolutTapToPay'
    ```

1. At the end of your `Podfile`, add this configuration:

    ```ruby [Podfile]
    post_install do |installer|
        installer.generated_projects.each do |project|
            project.targets.each do |target|
                target.build_configurations.each do |config|
                    config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '16.4'
                end
            end
        end
    end
    ```

1. Install the dependencies:

    ```bash
    pod install
    ```

1. From now on, open your project using the `.xcworkspace` file rather than the `.xcodeproj` file.
1. To ensure the SDK uses the latest version, run:

    ```bash
    pod update RevolutPayments/RevolutTapToPay
    ```

    :::tip
    To update the SDK to a newer version re-run this command.
    :::

### 3. Implement the token provider

The SDK needs an access token to authenticate its API calls. Instead of receiving a static credential, the SDK calls a `TokenProviderProtocol` implementation whenever it needs a fresh token. Your implementation depends on which [integration architecture](/docs/guides/merchant/accept-payments/in-person-payments/tap-to-pay/get-started#integration-architecture) you chose:

- ![Backend-led]

    Your backend produces short-lived device tokens using `grant_type=exchange_token` (see [Get started: 3. Get a device token](/docs/guides/merchant/accept-payments/in-person-payments/tap-to-pay/get-started#3-get-a-device-token)). Your `TokenProviderProtocol` implementation calls your backend endpoint and passes the device token back to the SDK:

    ```swift [TokenProvider.swift]
    import RevolutTapToPay

    final class TokenProvider: TokenProviderProtocol {
        func fetchAccessToken() async throws -> SdkTokenApiDto {
            // Call your backend endpoint that returns a short-lived device token.
            // Your backend uses `grant_type=exchange_token` to obtain it
            // from the merchant's OAuth `access_token` (see Get started: Step 3).
            let response = try await backendClient.fetchDeviceToken()
            return SdkTokenApiDto(accessToken: response.accessToken)
        }
    }
    ```

    | Code | Description |
    |------|-------------|
    | `import RevolutTapToPay` | Imports the SDK module. |
    | `TokenProviderProtocol` | The protocol the SDK uses to request tokens. Your type must conform to it and implement `fetchAccessToken()`. |
    | `fetchAccessToken()` | The method the SDK calls when it needs a fresh device token - either on the first API call or after receiving an unauthorised error. |
    | `backendClient.fetchDeviceToken()` | Your backend call - replace with your actual networking code. Your backend should use `grant_type=exchange_token` to obtain a device token from the merchant's OAuth `access_token`. |
    | `SdkTokenApiDto` | The wrapper struct that holds the device token. The SDK reads `accessToken` from it for pairing and payment API calls. |
    | `response.accessToken` | The short-lived device token returned by your backend. |

- ![App-led]

    The app holds the access token obtained directly via the OAuth flow - no device token exchange or backend endpoint needed. Your `TokenProviderProtocol` implementation simply returns the stored access token:

    ```swift [TokenProvider.swift]
    import RevolutTapToPay

    final class TokenProvider: TokenProviderProtocol {
        func fetchAccessToken() async throws -> SdkTokenApiDto {
            // Return the access token obtained directly via OAuth in the app.
            // The app handles the OAuth PKCE flow and token refresh itself.
            return SdkTokenApiDto(accessToken: storedAccessToken)
        }
    }
    ```

    | Code | Description |
    |------|-------------|
    | `import RevolutTapToPay` | Imports the SDK module. |
    | `TokenProviderProtocol` | The protocol the SDK uses to request tokens. Your type must conform to it and implement `fetchAccessToken()`. |
    | `fetchAccessToken()` | The method the SDK calls when it needs a fresh token - either on the first API call or after receiving an unauthorised error. |
    | `storedAccessToken` | The access token your app obtained via the OAuth PKCE flow. Store it securely in the keychain and refresh it using the `refresh_token` when it expires. |
    | `SdkTokenApiDto` | The wrapper struct that holds the access token. The SDK reads `accessToken` from it for pairing and payment API calls. |

#### When the SDK requests a new token

The SDK calls `fetchAccessToken()` in two situations:

1. **First API call** - no token has been provided yet.
1. **Token expired** - the SDK received an unauthorised error, meaning the current device token is no longer valid.

:::note
If the SDK receives two unauthorised errors in a row, it calls the `onLogout` handler provided during [initialisation](#4-initialise-the-sdk). This means the merchant is no longer authorised - consent was revoked, not granted, or the app permission was removed in the Developer Portal. The SDK cannot continue operating with that merchant account. Stop SDK usage and prompt the merchant to re-authorise via the OAuth consent flow, or fall back to other payment methods.
:::

### 4. Initialise the SDK

Create an instance of `RevolutTapToPayKit` - the main entry point to all SDK flows. Create it where it's accessible to the code that triggers payments - for example, as a property on a view model or a shared service. You only need one instance per app session.

```swift [MenuViewModel.swift]
import RevolutTapToPay

final class MenuViewModel {
    private lazy var tapToPayKit = RevolutTapToPayKit(
        clientId: Config.clientId,
        environment: .production,
        tokenProvider: TokenProvider(),
        onLogout: {
            // The merchant is no longer authorised to use the SDK.
            // This means consent was revoked, not granted, or the app
            // permission was removed in the Developer Portal.
            // Stop SDK usage and prompt the merchant to re-authorise
            // via the OAuth consent flow, or fall back to other payment methods.
        }
    )
}
```

| Code | Description |
|------|-------------|
| `lazy var tapToPayKit = RevolutTapToPayKit(...)` | Creates the SDK instance stored as a property on your view model. Using `lazy` ensures it's initialised on first access. Store it wherever your payment-triggering code can reach it - you only need one instance per app session. |
| `clientId` | Identifier of your application provided by Revolut - the same client ID from [Get started: Step 1](/docs/guides/merchant/accept-payments/in-person-payments/tap-to-pay/get-started#1-register-your-application). Must match the environment you select. |
| `environment` | Determines which Revolut API endpoints the SDK calls. Only `.production` is currently available. |
| `TokenProvider()` | Your token provider implementation from [Step 3](#3-implement-the-token-provider) - connects the SDK to your backend's device token endpoint. |
| `onLogout` | Called when the merchant is no longer authorised - consent was revoked, not granted, or the app permission was removed in the Developer Portal. The SDK cannot continue operating with that merchant account. Stop SDK usage and prompt the merchant to re-authorise via the OAuth consent flow, or fall back to other payment methods. |

:::note
The SDK presents its own UI - you don't need to create any payment view controllers or provide a presenting controller. When you call a payment method, the SDK handles all UI presentation over your app.
:::

:::info
For the full parameter reference, see [SDK reference: `RevolutTapToPayKit` initialiser](/docs/sdks/merchant-ios-sdk/tap-to-pay-sdk/methods-parameters#revoluttaptopaykit-initialiser).
:::

### 5. First-time setup (optional)

You can optionally call `performFirstTimeSetup` to guide the merchant through the initial setup before accepting payments. This method:

1. Runs the steps necessary to set up Apple's `ProximityReader` framework for operation, including accepting terms and conditions.
1. Optionally presents an introduction screen giving a short description of Tap to Pay on iPhone capabilities.
1. Gives the merchant an opportunity to run a test payment. The test payment behaves exactly like `performPayment`, with a predefined amount of 1 currency unit (e.g., £0.01).

:::note
Invoking this method is **not required** - `performPayment` and `performPayments` automatically handle any necessary setup on first use, without failures. 

However, running `performFirstTimeSetup` provides a smoother first-time experience for the merchant by guiding them through the setup explicitly.
:::

```swift [MenuViewModel.swift]
tapToPayKit.performFirstTimeSetup(
    shouldShowIntro: true,
    currency: "GBP",
    merchantName: "City Taxi Co.",
    completion: { [self] receipt, result in
        switch result {
        case .success:
            // Setup completed. If a test payment was made, receipt is available.
            if let receipt {
                // receipt.url contains a URL for sharing the test payment receipt
                print("Test payment receipt URL: \(receipt.url)")
            }
        case .failure(let error):
            // Setup or test payment failed.
            print("Setup failed: \(error)")
        }
    }
)
```

| Code | Description |
|------|------------------------|
| `tapToPayKit.performFirstTimeSetup` | Starts the first-time setup flow on the `RevolutTapToPayKit` instance created in [Step 4](#4-initialise-the-sdk). The SDK presents the setup UI over your app. |
| `shouldShowIntro` | Whether to show an introduction screen explaining Tap to Pay capabilities. Only shown once - subsequent calls with `true` won't show it again. |
| `currency` | The currency for the optional test payment (3-letter [ISO 4217 code](https://en.wikipedia.org/wiki/ISO_4217)). The test amount is 1 unit of this currency (e.g., £0.01). |
| `merchantName` | Appears on the test payment receipt. |
| `completion` | Called when the setup flow finishes and the SDK's UI has been dismissed. Receives a `Receipt` (if a test payment was made) containing a `url: URL` for sharing, and a `Result` indicating success or failure. |

:::info
For the full parameter reference, see [SDK reference: `RevolutTapToPayKit.performFirstTimeSetup`](/docs/sdks/merchant-ios-sdk/tap-to-pay-sdk/methods-parameters#revoluttaptopaykitperformfirsttimesetup-method).
:::

### 6. Receive payments

The SDK provides two methods for accepting payments. Both present their own UI and return results via callbacks.

- ![Single payment]

    To receive a single payment for a predetermined amount, call `performPayment`:

    ```swift [MenuViewModel.swift]
    tapToPayKit.performPayment(
        amount: 10_00,
        currency: "GBP",
        description: "Taxi fare - Trip #1234",
        merchantName: "City Taxi Co.",
        onDidCharge: { [self] receipt, result in
            switch result {
            case .success:
                // Payment completed. Receipt contains a URL for sharing.
                if let receipt {
                    print("Receipt URL: \(receipt.url)")
                }
            case .failure(let error):
                // Payment failed. Handle the error.
                print("Payment failed: \(error)")
            }
        },
        completion: { [self] in
            // The SDK's UI has been dismissed.
            // Continue with any UI updates in your app.
            print("Payment flow completed")
        }
    )
    ```

    | Code | Description |
    |------|--------------------------|
    | `tapToPayKit.performPayment` | Starts a single payment for a fixed amount. The SDK presents the payment UI over your app. |
    | `amount` | The payment amount in minor units (e.g., `10_00` for £10.00). Cannot be zero. |
    | `currency` | The payment currency (3-letter [ISO 4217 code](https://en.wikipedia.org/wiki/ISO_4217)). |
    | `description` | Optional. Appears in the merchant's Revolut Business transaction history. |
    | `merchantName` | Optional. Appears on the receipt. |
    | `onDidCharge` | Called after the payment completes, while the SDK's UI is still on screen. Receives a `Receipt` (if available) containing a URL for sharing, and a `Result` indicating success or failure. |
    | `completion` | Called after the SDK's UI has been dismissed. Use this to continue your app's flow. |

    :::note
    If the SDK detects that Tap to Pay cannot accept the payment (for example, the customer's card doesn't support contactless), it automatically falls back to a QR code payment method. The QR code payment method cannot be invoked directly.
    :::

    :::info
    For the full parameter reference, see [SDK reference: `RevolutTapToPayKit.performPayment`](/docs/sdks/merchant-ios-sdk/tap-to-pay-sdk/methods-parameters#revoluttaptopaykitperformpayment-method).
    :::

- ![Series of payments]

    To receive a series of payments where the merchant enters the amount each time, call `performPayments`. This presents a UI for entering the amount, optionally specifying a tax rate and description.

    After the amount is entered, the SDK presents a UI allowing the merchant to choose between two payment methods:

    | Payment method | How it works |
    |----------------|-------------|
    | **:LogoAppleTapToPayFilled: Tap to Pay** | The customer taps their contactless card or digital wallet on the iPhone. |
    | **:Qr: QR code** | The SDK presents a QR code which the customer can scan with their device. |

    ```swift [MenuViewModel.swift]
    tapToPayKit.performPayments(
        currency: "GBP",
        merchantName: "City Taxi Co.",
        onDidCharge: { [self] receipt, result in
            switch result {
            case .success:
                // Each payment completed successfully.
                if let receipt {
                    print("Receipt URL: \(receipt.url)")
                }
            case .failure(let error):
                // A payment in the series failed.
                print("Payment failed: \(error)")
            }
        },
        completion: { [self] in
            // The payment series flow has been dismissed.
            print("Payments flow completed")
        }
    )
    ```

    | Code | Description |
    |------|--------------------------|
    | `tapToPayKit.performPayments` | Starts a series of payments where the merchant enters the amount each time via the SDK's amount entry UI. After entry, the SDK presents a choice between **:LogoAppleTapToPayFilled: Tap to Pay** and **:Qr: QR code** payment. |
    | `currency` | The payment currency (3-letter [ISO 4217 code](https://en.wikipedia.org/wiki/ISO_4217)). The same currency is used for all payments in the series. |
    | `merchantName` | Optional. Appears on the receipt. |
    | `onDidCharge` | Called after each payment completes, while the SDK's UI is still on screen. Receives a `Receipt` (if available) containing a URL for sharing, and a `Result` indicating success or failure. |
    | `completion` | Called after the entire payment series flow has been dismissed. Use this to continue your app's flow. |

    :::info
    For the full parameter reference, see [SDK reference: `RevolutTapToPayKit.performPayments`](/docs/sdks/merchant-ios-sdk/tap-to-pay-sdk/methods-parameters#revoluttaptopaykitperformpayments-method).
    :::

### 7. Handle payment results

Both `performPayment` and `performPayments` return results through two callbacks with different timing:

| Callback | When it's called | Use it for |
|----------|-----------------|------------|
| `onDidCharge` | Immediately after a payment completes, while the SDK's UI is still on screen. | Immediate feedback: updating your POS state, logging the result, or preparing the receipt for sharing. The `Receipt` object (if provided) contains a `url: URL` property for sharing. |
| `completion` | After the SDK's UI has been fully dismissed. | Continuing your app's flow - for example, returning to the home screen or presenting the next order. |

```swift [Payment result handling]
onDidCharge: { [self] receipt, result in
    switch result {
    case .success:
        // Payment successful - update your order/POS state
        if let receipt {
            // Present receipt sharing options to the merchant
            let activityVC = UIActivityViewController(
                activityItems: [receipt.url],
                applicationActivities: nil
            )
            present(activityVC, animated: true)
        }
    case .failure(let error):
        // Payment failed - inform the merchant
        showAlert(title: "Payment failed", message: error.localizedDescription)
    }
},
completion: { [self] in
    // SDK UI dismissed - continue app flow
    navigationController?.popToRootViewController(animated: true)
}
```

:::warning
The `onDidCharge` closure is for **UI updates only**. Its delivery is not guaranteed due to network conditions or other issues. You **must** rely on server-to-server [webhooks](/docs/guides/merchant/monitor-and-observe/webhooks/using-webhooks) to get the final, authoritative status of a payment before fulfilling the order.
:::

<!-- TODO: Add link to example project when available -->

## Implementation checklist

Use this checklist to verify that your Tap to Pay integration is correctly implemented before going live.

### SDK setup

- [ ] ProximityReader entitlement (`com.apple.developer.proximity-reader.payment.acceptance`) added to your Xcode project.
- [ ] SDK installed via CocoaPods with correct deployment target (iOS 16.4+).
- [ ] SDK initialised with the correct `clientId` and `.production` environment.
- [ ] `TokenProviderProtocol` implemented and calls your backend for device tokens.
- [ ] `onLogout` handler implemented (notifies backend or prompts merchant to re-authorise).
- [ ] `RevolutTapToPayKit` instance stored accessibly (e.g., view model property or shared service).

### Payment flow

- [ ] `performPayment` tested with a real test amount.
- [ ] `performPayments` tested with the amount entry UI.
- [ ] `performFirstTimeSetup` tested (optional, but recommended for first-time users).
- [ ] Payment result handling tested for success, failure, and cancellation scenarios.
- [ ] `onDidCharge` and `completion` callbacks handled with correct timing expectations.
- [ ] Receipt URL sharing tested.
- [ ] QR code payment fallback verified (test with a non-contactless card or by triggering a Tap to Pay unavailability scenario).
- [ ] Two consecutive unauthorised errors trigger `onLogout` (test with an expired or invalid token).

### Backend

- [ ] Device token endpoint implemented and serving tokens to the iOS app (`grant_type=exchange_token`).
- [ ] Access token refresh implemented (`grant_type=refresh_token`).
- [ ] Webhook endpoint receives and processes payment status events.

:::tip
**You've successfully integrated Tap to Pay on iPhone!** 

Since sandbox is not currently supported, test with small real transaction amounts and verify they appear in your Revolut Business merchant account.
:::

---

## What's next

- [:16/Code: SDK reference](/docs/sdks/merchant-ios-sdk/tap-to-pay-sdk/methods-parameters 'Full method and parameter reference for the Tap to Pay iOS SDK.')
- [:HelpChat: FAQs](/docs/guides/merchant/accept-payments/in-person-payments/tap-to-pay/faq 'Common questions about Tap to Pay on iPhone.')
- [:Webhook: Using webhooks](/docs/guides/merchant/monitor-and-observe/webhooks/using-webhooks 'Track the payment lifecycle with server-to-server webhooks.')
- [:ArrowExchange: Order and payment lifecycle](/docs/guides/merchant/reference/order-lifecycle 'Understand how orders and payments are processed.')