# iOS App Integration

Accept NFC payments from your iOS app using the **MoneiPaySDK**. The SDK handles URL building, callback parsing, timeouts, and error handling.

**Source:** [github.com/MONEI/monei-pay-ios-sdk](https://github.com/MONEI/monei-pay-ios-sdk)

iOS Beta

MONEI Pay for iOS is in beta. Join TestFlight to install: <https://testflight.apple.com/join/kZU2j445>

No entitlements needed

Uses standard URL schemes — no special entitlements, certificates, or Apple approvals required.

## How It Works[​](#how-it-works "Direct link to How It Works")

1. Your app calls `MoneiPay.acceptPayment(...)` with a token and amount
2. The SDK opens MONEI Pay for the NFC tap-to-pay transaction
3. MONEI Pay redirects back to your app with the result
4. The SDK parses the callback and resolves a `PaymentResult`

## Prerequisites[​](#prerequisites "Direct link to Prerequisites")

* [MONEI account](https://dashboard.monei.com/register)
* iOS 15.0+, Swift 5.9+
* MONEI Pay installed on the device
* POS auth token from your backend (see [Getting Started](https://docs.monei.com/monei-pay/app-integration/getting-started/.md))

## Integration[​](#integration "Direct link to Integration")

### 1. Install the SDK[​](#1-install-the-sdk "Direct link to 1. Install the SDK")

* Swift Package Manager
* CocoaPods

In Xcode: **File → Add Package Dependencies** → enter `https://github.com/MONEI/monei-pay-ios-sdk` → **Up to Next Major Version** from `0.2.2`.

Or in `Package.swift`:

Package.swift

```
dependencies: [

    .package(url: "https://github.com/MONEI/monei-pay-ios-sdk", from: "0.2.2")

]
```

Podfile

```
pod 'MoneiPaySDK', :git => 'https://github.com/MONEI/monei-pay-ios-sdk.git', :tag => 'v0.2.2'
```

Run `pod install` and open the `.xcworkspace`.

### 2. Configure Info.plist[​](#2-configure-infoplist "Direct link to 2. Configure Info.plist")

Info.plist

```
<key>CFBundleURLTypes</key>

<array>

  <dict>

    <key>CFBundleURLSchemes</key>

    <array>

      <string>your-app</string>

    </array>

  </dict>

</array>



<key>LSApplicationQueriesSchemes</key>

<array>

  <string>monei-pay</string>

</array>
```

warning

Replace `your-app` with your app's unique URL scheme (e.g. your bundle ID). Each merchant app must use a different scheme.

### 3. Wire the Callback Handler[​](#3-wire-the-callback-handler "Direct link to 3. Wire the Callback Handler")

* SwiftUI
* UIKit

YourApp.swift

```
import SwiftUI

import MoneiPaySDK



@main

struct YourApp: App {

    var body: some Scene {

        WindowGroup {

            ContentView()

                .onOpenURL { url in

                    MoneiPay.handleCallback(url: url)

                }

        }

    }

}
```

AppDelegate.swift

```
import MoneiPaySDK



func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {

    return MoneiPay.handleCallback(url: url)

}
```

### 4. Accept a Payment[​](#4-accept-a-payment "Direct link to 4. Accept a Payment")

```
import MoneiPaySDK



func acceptPayment() async {

    do {

        let result = try await MoneiPay.acceptPayment(

            token: "eyJ...",

            amount: 1500,

            description: "Order #123",

            customerName: "John Doe",

            customerEmail: "john@example.com",

            callbackScheme: "your-app"

        )



        if result.success {

            print("Payment approved: \(result.transactionId)")

            print("Card: \(result.cardBrand ?? "") \(result.maskedCardNumber ?? "")")

        }

    } catch let error as MoneiPayError {

        switch error {

        case .moneiPayNotInstalled:

            break

        case .paymentCancelled:

            break

        case .paymentTimeout:

            break

        case .paymentFailed(let reason):

            print("Payment failed: \(reason ?? "unknown")")

        default:

            print("Error: \(error.localizedDescription)")

        }

    }

}
```

### SDK Reference[​](#sdk-reference "Direct link to SDK Reference")

#### `MoneiPay.acceptPayment(...)`[​](#moneipayacceptpayment "Direct link to moneipayacceptpayment")

| Parameter        | Type            | Required | Description                                       |
| ---------------- | --------------- | -------- | ------------------------------------------------- |
| `token`          | `String`        | Yes      | POS auth token (raw JWT, no "Bearer " prefix)     |
| `amount`         | `Int`           | Yes      | Payment amount in cents (e.g. `1500` = 15.00 EUR) |
| `description`    | `String?`       | No       | Payment description                               |
| `customerName`   | `String?`       | No       | Customer name                                     |
| `customerEmail`  | `String?`       | No       | Customer email                                    |
| `customerPhone`  | `String?`       | No       | Customer phone                                    |
| `callbackScheme` | `String`        | Yes      | Your app's registered URL scheme                  |
| `timeout`        | `TimeInterval?` | No       | Timeout in seconds (default: 60)                  |

#### `PaymentResult`[​](#paymentresult "Direct link to paymentresult")

| Property           | Type      | Description                            |
| ------------------ | --------- | -------------------------------------- |
| `transactionId`    | `String`  | MONEI transaction ID                   |
| `success`          | `Bool`    | Whether the payment was approved       |
| `amount`           | `Int?`    | Payment amount in cents                |
| `cardBrand`        | `String?` | Card brand (e.g. `visa`, `mastercard`) |
| `maskedCardNumber` | `String?` | Masked card number (e.g. `****1234`)   |

#### Error Types[​](#error-types "Direct link to Error Types")

| Error                    | Description                              |
| ------------------------ | ---------------------------------------- |
| `moneiPayNotInstalled`   | MONEI Pay is not installed               |
| `paymentInProgress`      | Another payment is already active        |
| `paymentTimeout`         | No response within the timeout           |
| `paymentCancelled`       | User cancelled                           |
| `paymentFailed(reason:)` | Payment declined or failed               |
| `invalidParameters(_:)`  | Invalid input (e.g. non-positive amount) |
| `failedToOpen`           | Could not open MONEI Pay                 |

***

## Manual Integration (URL Scheme)[​](#manual-integration-url-scheme "Direct link to Manual Integration (URL Scheme)")

If you prefer not to use the SDK, you can integrate directly via URL schemes.

Show manual URL scheme integration

### Open the Payment URL[​](#open-the-payment-url "Direct link to Open the Payment URL")

```
func acceptPayment(amountInCents: Int) {

    var components = URLComponents(string: "monei-pay://accept-payment")!

    components.queryItems = [

        URLQueryItem(name: "amount", value: String(amountInCents)),

        URLQueryItem(name: "auth_token", value: "eyJ..."),

        URLQueryItem(name: "callback", value: "your-app://payment-result")

    ]



    guard let url = components.url else { return }

    UIApplication.shared.open(url)

}
```

### Handle the Callback[​](#handle-the-callback "Direct link to Handle the Callback")

```
struct PaymentResult {

    let success: Bool

    let transactionId: String?

    let amount: String?

    let cardBrand: String?

    let maskedCardNumber: String?

    let error: String?



    init(from url: URL) {

        let params = URLComponents(url: url, resolvingAgainstBaseURL: false)?

            .queryItems?

            .reduce(into: [String: String]()) { $0[$1.name] = $1.value } ?? [:]



        self.success = params["success"] == "true"

        self.transactionId = params["transaction_id"]

        self.amount = params["amount"]

        self.cardBrand = params["card_brand"]

        self.maskedCardNumber = params["masked_card_number"]

        self.error = params["error"]

    }

}
```

### URL Parameters[​](#url-parameters "Direct link to URL Parameters")

#### Request Parameters[​](#request-parameters "Direct link to Request Parameters")

| Parameter        | Type    | Required | Description                                                                                       |
| ---------------- | ------- | -------- | ------------------------------------------------------------------------------------------------- |
| `amount`         | Integer | Yes      | Payment amount in cents (e.g. `1500` = 15.00 EUR)                                                 |
| `auth_token`     | String  | Yes      | POS auth token (raw JWT)                                                                          |
| `callback`       | String  | No       | Custom URL scheme where MONEI Pay sends the result. Must be a custom scheme (not `http`/`https`). |
| `description`    | String  | No       | Payment description                                                                               |
| `customer_name`  | String  | No       | Customer name                                                                                     |
| `customer_email` | String  | No       | Customer email                                                                                    |
| `customer_phone` | String  | No       | Customer phone                                                                                    |

#### Callback Parameters (Success)[​](#callback-parameters-success "Direct link to Callback Parameters (Success)")

| Parameter            | Type   | Description                            |
| -------------------- | ------ | -------------------------------------- |
| `success`            | String | `"true"`                               |
| `transaction_id`     | String | MONEI transaction ID                   |
| `amount`             | String | Payment amount in cents                |
| `card_brand`         | String | Card brand (e.g. `visa`, `mastercard`) |
| `masked_card_number` | String | Masked card number (e.g. `****1234`)   |

#### Callback Parameters (Error)[​](#callback-parameters-error "Direct link to Callback Parameters (Error)")

| Parameter | Type   | Description                                                               |
| --------- | ------ | ------------------------------------------------------------------------- |
| `success` | String | `"false"`                                                                 |
| `error`   | String | Error code (e.g. `PAYMENT_FAILED`, `INVALID_AMOUNT`, `NOT_AUTHENTICATED`) |

## Example App[​](#example-app "Direct link to Example App")

The [`examples/MerchantDemo`](https://github.com/MONEI/monei-pay-ios-sdk/tree/master/examples/MerchantDemo) directory contains a SwiftUI app demonstrating the full flow. Open `MerchantDemo.xcodeproj`, set your signing team, and run on a physical device with MONEI Pay installed. The project references the SDK as a local package, so SDK changes apply immediately.

## Troubleshooting[​](#troubleshooting "Direct link to Troubleshooting")

### `moneiPayNotInstalled`[​](#moneipaynotinstalled "Direct link to moneipaynotinstalled")

MONEI Pay must be installed on the device. During beta, install via TestFlight: <https://testflight.apple.com/join/kZU2j445>

### `failedToOpen`[​](#failedtoopen "Direct link to failedtoopen")

Verify `monei-pay` is listed in `LSApplicationQueriesSchemes` in your `Info.plist`. Without it, iOS blocks the `canOpenURL` check.

### `paymentTimeout`[​](#paymenttimeout "Direct link to paymenttimeout")

Default timeout is 60 seconds. Increase via the `timeout` parameter if needed. The timer uses wall-clock time — backgrounding your app does not pause it.

### Callback not received[​](#callback-not-received "Direct link to Callback not received")

Check that your URL scheme is registered under `CFBundleURLTypes` in `Info.plist`, and that `MoneiPay.handleCallback(url:)` is called in `onOpenURL` (SwiftUI) or `application(_:open:options:)` (UIKit). See [Wire the Callback Handler](#3-wire-the-callback-handler).

### Payment works in simulator but not on device[​](#payment-works-in-simulator-but-not-on-device "Direct link to Payment works in simulator but not on device")

NFC payments require a physical device with MONEI Pay installed. Simulators are not supported.
