# Accept payments via Revolut Pay - iOS

Welcome to the implementation guide for Revolut Pay on iOS! This page provides a comprehensive walkthrough for integrating Revolut Pay into your iOS application, allowing your customers to pay with their card or Revolut account for a streamlined checkout experience.

![Revolut Pay - Mobile](/img/accept-payments/payment-methods/revolut-pay/revolut-pay-mobile.png 'Revolut Pay - Mobile')

## How it works

From an implementation perspective, the Revolut Pay iOS SDK works with the following components:

1. **Server-side:** A server-side endpoint is required to securely communicate with the Merchant API to [create orders](/docs/api/merchant#create-order).
1. **Client-side:** The SDK is configured in your app with your public API key. When the customer taps the Revolut Pay button, the SDK uses the `token` obtained from your server to initate and process the payment. The SDK handles the entire payment flow, including presenting native sheets, handling biometric authentication, and redirecting to the Revolut app if necessary.
1. **Endpoint for webhooks:** Your server should listen for webhook events to reliably track the payment lifecycle and handle critical backend processes like updating order status, managing inventory, or initiating shipping. For more information, see: [Use webhooks to keep track of the payment lifecycle](/docs/guides/merchant/monitor-and-observe/webhooks/using-webhooks).

### Available versions

The Revolut Pay iOS SDK is available in two variants:

| SDK | Description |
|-----|-------------|
| **Revolut Pay Native (recommended)** | Initialises Revolut Pay UI within your app, offering a seamless experience regardless of whether the Revolut retail app is installed. Integrating this version will add approximately 45 MB to your app's total size. |
| **Revolut Pay Lite** | Triggers a redirect to the Revolut retail app to make the payment. If the Revolut retail app is not installed, the SDK falls back to a WebView within your app. All `2.x.x` versions are now deprecated in favour of `RevolutPayLite`. |

:::note
Both Revolut Pay Native and Revolut Pay Lite support `savePaymentMethodForMerchant` for Merchant Initiated Transactions (MIT).
:::

The payment flow for each variant:

- ![Revolut Pay Native]

  With Revolut Pay Native, the entire payment experience, including login and payment authorisation, occurs within a native interface inside your app. This provides the most seamless experience for the user, regardless of whether they have the Revolut app installed.
  1. The customer taps the **Revolut Pay** button in your app.
  1. Your app obtains an order `token` from your server and passes to the SDK for initialisation.
  1. The SDK presents a native payment sheet where the customer reviews and authorises the payment, potentially using biometrics or passkeys.
  1. The SDK processes the payment and returns the result to your app via a completion handler. You should use this result to update your UI (e.g., show a success or failure message).
  1. Your server receives webhook notifications to confirm the payment's final state and securely fulfill the order.

- ![Revolut Pay Lite]

  With Revolut Pay Lite, the customer is redirected to the Revolut app to make the payment. If the Revolut retail app is not installed, the SDK opens a secure WebView within your app.
  1. The customer taps the **Revolut Pay** button.
  1. Your app obtains an order `token` from your server and passes to the SDK for initialisation.
  1. The SDK checks for the Revolut app:
     - **If installed:** The customer is redirected to the Revolut app to authorise the payment, then redirected back to your app.
     - **If not installed:** The SDK opens a secure WebView within your app for the customer to complete the payment.

  1. The SDK returns the result to your app via a completion handler and URL handling.
  1. Your server receives webhook notifications to confirm the payment's final state and securely fulfill the order.

### Implementation overview

Check the following high-level overview on how to implement Revolut Pay in your app:

1. [Set up an endpoint for creating orders](#1-set-up-an-endpoint-for-creating-orders)
1. [Configure your Xcode project](#2-configure-your-xcode-project)
1. [Initialise the SDK](#3-initialise-the-sdk)
1. [Add the Revolut Pay button](#4-add-the-revolut-pay-button)
1. [Handle payment result](#5-handle-payment-result)

### Before you begin

Before you start this tutorial, ensure that you have completed the following steps:

- [Get started: 1. Apply for a Merchant account](/docs/guides/merchant/get-started)
- [Get started: 2. Generate the API key](/docs/guides/merchant/get-started)

## Implement Revolut Pay on iOS

This section walks you through the server- and client-side implementation step by step.

### 1. Set up an endpoint for creating orders

Before the Revolut Pay button can be displayed in your app, your client-side code needs to fetch a unique, single-use `token` that represents an order. This `token` can only be created on your server by making a secure call to the Revolut Merchant API.

**This** server-side endpoint is a mandatory **security requirement**. Your secret API key must never be exposed in your iOS application.

When a customer taps the Revolut Pay button in your app, your app will call this endpoint. Your endpoint is then responsible for:

1. Receive the checkout details (e.g., `amount`, `currency`) from your client-side request.
1. Securely call the [Merchant API: Create an order endpoint](/docs/api/merchant#create-order) with the checkout details.
1. Receive the order object, including the public `token`, in the API response.
1. Return the `token` from the response to your app.

Later, in the client-side configuration, the `createOrder` callback function will call this endpoint to fetch the `token`, which is required to initialise the SDK and display the Revolut Pay button.

Below is an example of the JSON response your endpoint will receive from the Merchant API after successfully creating an order. The crucial field to extract and return to your app is the `token`.

```json {3}
{
  "id": "6516e61c-d279-a454-a837-bc52ce55ed49",
  "token": "0adc0e3c-ab44-4f33-bcc0-534ded7354ce",
  "type": "payment",
  "state": "pending",
  "created_at": "2023-09-29T14:58:36.079398Z",
  "updated_at": "2023-09-29T14:58:36.079398Z",
  "amount": 1000,
  "currency": "GBP",
  "outstanding_amount": 1000,
  "capture_mode": "automatic",
  "checkout_url": "https://checkout.revolut.com/payment-link/0adc0e3c-ab44-4f33-bcc0-534ded7354ce",
  "enforce_challenge": "automatic"
}
```

### 2. Configure your Xcode project

This section covers the one-time setup tasks required to prepare your Xcode project for the SDK.

#### 2.1 Install the SDK

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

#### CocoaPods

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

:::info
Check the [available versions section](#available-versions) to determine which SDK variant is right for your needs.
:::

- ![Revolut Pay Native (recommended)]
  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
     pod 'RevolutPayments/RevolutPay'
     ```

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

     ```ruby
     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'] = '15.0'
                 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/RevolutPay
     ```

     :::info
     Run this command to update the SDK to a newer version in the future.
     :::

- ![Revolut Pay Lite]
  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
     pod 'RevolutPayments/RevolutPayLite'
     ```

  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/RevolutPayLite
     ```

     :::info
     Run this command to update the SDK to a newer version in the future.
     :::

#### 2.2 Configure project capabilities

To allow your app to open the Revolut retail app, you must declare the `revolut` URL scheme in your `Info.plist` file.

```xml
<key>LSApplicationQueriesSchemes</key>
<array>
    <string>revolut</string>
</array>
```

#### 2.3 Enable biometrics and passkeys support

:::info
This step is **only required** when implementing **Revolut Pay Native**.
:::

To offer users a seamless and secure login experience with Face ID, Touch ID, and passkeys, you must associate your app with our web service.

1.  **Configure your App ID for Passkeys:** To configure passkeys, you need your **App ID**, which is a combination of your **App ID Prefix** and your **Bundle ID**.

    It's crucial to get the correct **App ID Prefix**. This may be different from the **Team ID** shown in your main Apple Developer account page or in Xcode, especially for older developer accounts or apps that have been transferred between accounts.

    To find the correct **App ID Prefix** and **Bundle ID**:
    - Log in to your developer account at [developer.apple.com](https://developer.apple.com/) > `Certificates, IDs & Profiles` > `Identifiers`.
    - Select your app from the list.
    - Here you will find the **App ID Prefix** (e.g., `ABCDE12345`) and your app's **Bundle ID** (e.g., `com.yourcompany.yourapp`).

    Your final **App ID** will be in the format: `<App ID Prefix>.<Bundle ID>`, based on our example: `ABCDE12345.com.yourcompany.yourapp`.

1.  **Register your App ID with Revolut:** Email your verified **App ID** to [merchant-integration@revolut.com](mailto:merchant-integration@revolut.com).

1.  **Add the Associated Domain to your project:** Once Revolut confirms your App ID registration, you must link your app to the Revolut domain.
    - In your Xcode project, go to **Signing & Capabilities**.
    - Click **+ Capability** and add the **Associated Domains** capability.
    - In the domains field, add the following entry: `webcredentials:revolut.com`.

    :::warning
    **Important Update for SDK 3.9+**

    Starting with SDK version **3.9**, you must use `webcredentials:revolut.com`.

    The previous domain, `webcredentials:sso.revolut.com`, is now deprecated. Please ensure your Associated Domains configuration is updated.
    :::

    :::details[Migrating from 3.9>]

    If you are updating from a version of the SDK prior to 3.9.0, you will need to update your Associated Domains configuration.
    1. In your Xcode project, navigate to **Signing & Capabilities**.
    2. Find the **Associated Domains** section.
    3. Replace the old entry `webcredentials:sso.revolut.com` with the new entry `webcredentials:revolut.com`.

    :::

#### 2.4 Add the URL handler

When the user is redirected from the Revolut app back to yours, you must pass the incoming URL to the SDK.

Your implementation depends on your app's lifecycle management. Use the `UISceneDelegate` method for modern, scene-based apps (the default since iOS 13), which support features like multiple windows on iPad. Use the `AppDelegate` method for older apps or if you have explicitly opted out of the scene-based lifecycle.

- ![SceneDelegate]

  If you are using `UISceneDelegate`, add this to your `SceneDelegate`:

  ```swift
  func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
      if let url = URLContexts.first?.url {
          RevolutPayKit.handle(url: url)
      }
  }
  ```

- ![AppDelegate]

  If you are using the older `AppDelegate` lifecycle, add this to your `AppDelegate`:

  ```swift
  func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
      RevolutPayKit.handle(url: url)
      return true
  }
  ```

### 3. Initialise the SDK

First, import the `RevolutPayments` module. Then, on app launch, typically in your `AppDelegate`, configure the SDK with your **public API key** and the environment.

```swift
import RevolutPayments

// In your AppDelegate's didFinishLaunchingWithOptions method
RevolutPaymentsSDK.configure(with: .init(
    merchantPublicKey: "<yourPublicApiKey>",
    environment: .production // or .sandbox for testing
))
```

### 4. Add the Revolut Pay button

On your checkout screen, create and display the Revolut Pay button.

#### 4.1 Create the button

Instantiate `RevolutPayKit` and call the `button` method. This is where you connect your server-side endpoint and define what happens after the payment attempt.

```swift
let revolutPayKit = RevolutPayKit()

let button = revolutPayKit.button(
    style: .init(size: .large),
    returnURL: "myapp://revolut-pay", // Your app's custom URL scheme
    createOrder: { createOrderHandler in
        // 1. Call your server to create the order
        createOrderOnBackEnd { orderToken in
            // 2. Pass the token to the SDK
            createOrderHandler.set(orderToken: orderToken)
        }
    },
    completion: { result in
        switch result {
            case .success:
                // Handle successful payment
            case .failure(let error):
                // Handle payment error
            case .userAbandonedPayment:
                // Handle abandoned payment
        }
    }
)
```

:::info
For more information about the parameters and button styling, see: [Parameters: iOS](/docs/sdks/merchant-ios-sdk/revolut-pay-ios-sdk/methods-parameters#revolutpaykitbutton-method) and [Revolut Pay button guidelines](/docs/resources/revolut-pay-button-guidelines).
:::

#### 4.2 Display the button

Add the created button to your view hierarchy.

```swift
view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    button.bottomAnchor.constraint(equalTo: view.bottomAnchor),
    button.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor),
    button.trailingAnchor.constraint(lessThanOrEqualTo: view.trailingAnchor),
    button.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
```

#### 4.3 Advanced features

You can enable additional features like saving payment methods or Fast Checkout within the button initaliser.

- **Merchant Initiated Transactions (MIT):** To save a customer's payment method for future use (e.g., subscriptions), pass `savePaymentMethodForMerchant: true`. See our guide on [charging a saved payment method](/docs/guides/merchant/optimise-checkout/save-payment-methods/charge-saved-payment-method) for details.
- **Fast checkout:** To collect shipping details via Revolut Pay, pass `shouldRequestShipping: true`. This requires your backend to support the Fast checkout flow. See the [Fast Checkout](/docs/guides/merchant/optimise-checkout/fast-checkout) guide for details.

:::info
Fast checkout and MIT are mutually exclusive. You can enable one or the other, but not both in the same transaction.
:::

::::details [Add a promotional banner]

You can use the promotional banner widget to offer rewards to customers who create a new Revolut account after checkout.

:::tip[Boost Your Conversions!]
We recommend implementing the promotional banner. Analysis has shown that having the upsell widget **can increase conversion to payment by ~5%**.
:::

First, configure the banner by calling `revolutPayKit.promotionalBanner` method:

```swift
let promotionalBanner = revolutPayKit.promotionalBanner(
    transactionId: "transaction-id", // Unique ID of the payment corresponding to the promotional offer
    amount: 10_00,
    currency: .EUR,
    customer: .init()
)
```

Then, add the banner to your view hierarchy, for example on your order confirmation screen:

```swift
view.addSubview(promotionalBanner)

promotionalBanner.translatesAutoresizingMaskIntoConstraints = false

NSLayoutConstraint.activate([
    promotionalBanner.topAnchor.constraint(
        equalTo: view.safeAreaLayoutGuide.topAnchor,
        constant: 16
    ),
    promotionalBanner.trailingAnchor.constraint(
        equalTo: view.safeAreaLayoutGuide.trailingAnchor,
        constant: -16
    ),
    promotionalBanner.leadingAnchor.constraint(
        equalTo: view.safeAreaLayoutGuide.leadingAnchor,
        constant: 16
    )
])
```

:::info
For more information about the possible parameters of the banner, see: [Revolut Pay iOS SDK: `RevolutPayKit.promotionalBanner`](/docs/sdks/merchant-ios-sdk/revolut-pay-ios-sdk/methods-parameters#revolutpaykitpromotionalbanner-method).
:::

::::

### 5. Handle payment result

:::warning
The client-side completion handler 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.
:::

The `completion` block passed to the `button` method provides a `Result` type that tells you if the payment succeeded, failed, or was cancelled. Use this to show an appropriate message to the user.

:::tip
If all the steps have been followed correctly, you have successfully implemented Revolut Pay! 🎉
:::

## Example

For a complete, working implementation, please refer to the example app included in our [public SDK repository](https://bitbucket.org/revolut/revolut-payments-ios.git).

To run the example app:

1. Clone the repository.
1. Navigate to the `ExampleApp` directory within the latest release.
1. Run `make project` to install dependencies.
1. Open `RevolutPayApp.xcworkspace` in Xcode and run the project.

## Preparing for App Store submission

To comply with [Apple's App Store requirements](https://developer.apple.com/support/third-party-SDK-requirements/), you must include our privacy manifest in your app. This file, named [`PrivacyInfo.xcprivacy`](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files), details the data collection practices of the Revolut Pay SDK.

:::warning
Because the Revolut Pay SDK is distributed as a static library, you must manually merge our `PrivacyInfo.xcprivacy` file with your app's primary privacy manifest.
:::

These steps are required to prepare your iOS application for Apple's review policies and ensure that your Revolut Pay integration adheres to their privacy standards.

- ![Revolut Pay Native]

  For merchants using Revolut Pay Native, privacy manifests are available from version `3.2.1`:
  1. The privacy manifest file is located within the `RevolutPayments.zip` archive under `Frameworks/RevolutPay/RevolutPayNativeSDK.bundle/PrivacyInfo.xcprivacy`.
  1. Download the archive from the latest [Release](https://bitbucket.org/revolut/revolut-payments-ios/src/master/Releases/) and extract the privacy manifest.
  1. Add the privacy manifest to your app's resources:
     - If your app does not currently use a privacy manifest, simply add the SDK's file to your app's resources.
     - If you already have an existing privacy manifest, merge the SDK's manifest with your app's existing privacy document to ensure compliance with Apple's new requirements. Address any conflicts by integrating data handling practices from both the SDK and your app into a unified document.

- ![Revolut Pay Lite]

  For merchants using Revolut Pay Lite, privacy manifests are available from version `3.9.0`:
  1. The privacy manifest file is located within the `RevolutPayments.zip` archive under `Frameworks/RevolutPayLite/RevolutPayLiteSDK.bundle/PrivacyInfo.xcprivacy`.
  1. Download the archive from the latest [Release](https://bitbucket.org/revolut/revolut-payments-ios/src/master/Releases/) and extract the privacy manifest.
  1. Add the privacy manifest to your app's resources:
     - If your app does not currently use a privacy manifest, simply add the SDK's file to your app's resources.
     - If you already have an existing privacy manifest, merge the SDK's manifest with your app's existing privacy document to ensure compliance with Apple's new requirements. Address any conflicts by integrating data handling practices from both the SDK and your app into a unified document.

## Implementation checklist

:::info
The [Sandbox environment](https://sandbox-business.revolut.com/) is designed to replicate the production environment's behaviour, with the key difference being the absence of app redirection due to the lack of a sandbox version of the Revolut retail app.

For more information about Revolut Pay payment flows in Sandbox, see: [Test flows](/docs/guides/merchant/test-and-go-live/testing/test-flows#revolut-pay).
:::

Before going live, use this checklist to test your integration in both the `.sandbox` and `.production` environments.

#### Project setup

- [ ] SDK is installed via CocoaPods.

- [ ] `Info.plist` contains the `revolut` URL scheme.

- [ ] Associated Domain `webcredentials:revolut.com` is configured for passkeys.

  :::warning
  Double-check your Associated Domain, if you used an SDK version before 3.9.
  :::

- [ ] The SDK is initialised with the correct public API key and environment.

- [ ] The Privacy Manifest is correctly merged into the app's resources.

#### Payment flow

- [ ] The Revolut Pay button appears correctly on the checkout screen.
- [ ] Tapping the button successfully calls your server to create an order and receives a `token`.
- [ ] **Test case 1 (Revolut app not installed):** The payment flow completes successfully within the in-app flow (Revolut Pay Native) or in-app WebView (Revolut Pay Lite fallback).
- [ ] **Test case 2 (Revolut app installed):** The payment flow completes via the native in-app experience (Revolut Pay Native) or via redirect to the Revolut app and back (Revolut Pay Lite).
- [ ] **Test case 3 (Biometrics):** Passkey/Face ID/Touch ID login works as expected (requires a real device).
- [ ] A successful payment shows a success message in the UI.
- [ ] A failed payment shows an appropriate error message in the UI.
- [ ] The user cancelling the payment flow is handled gracefully.

#### Backend verification

- [ ] Your server correctly receives and processes webhook events for different payment states (e.g., `ORDER_COMPLETED`, `ORDER_AUTHORISED` for success; `ORDER_PAYMENT_FAILED`, `ORDER_CANCELLED` for failure or cancellation).
- [ ] Order fulfillment logic is only triggered after receiving a successful webhook, not from the client-side callback.

---

## What's next

- [SDK reference](/docs/sdks/merchant-ios-sdk/revolut-pay-ios-sdk/methods-parameters)
- [Sandbox test flows](/docs/guides/merchant/test-and-go-live/testing/test-flows)
- [Using webhooks](/docs/guides/merchant/monitor-and-observe/webhooks/using-webhooks)
- [Revolut Pay button guidelines](/docs/resources/revolut-pay-button-guidelines)
- [Revolut Pay marketing assets and guidelines](/docs/resources/marketing-guidelines)