# Integración de app iOS

Acepta pagos NFC desde tu app iOS usando el **MoneiPaySDK**. El SDK gestiona la construcción de la URL, el análisis de la redirección de finalización, los tiempos de espera y el manejo de errores.

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

No se requieren permisos especiales

Utiliza esquemas de URL estándar — no se necesitan permisos especiales, certificados ni aprobaciones de Apple.

## Cómo funciona[​](#cómo-funciona "Enlace directo al Cómo funciona")

1. Tu app llama a `MoneiPay.acceptPayment(...)` con un token y un importe
2. El SDK abre MONEI Pay para la transacción NFC por contacto
3. MONEI Pay redirige de vuelta a tu app con el resultado
4. El SDK analiza la redirección de finalización y resuelve un `PaymentResult`

## Requisitos previos[​](#requisitos-previos "Enlace directo al Requisitos previos")

* [Cuenta MONEI](https://dashboard.monei.com/register)
* iOS 15.0+, Swift 5.9+
* MONEI Pay instalado en el dispositivo
* Token de autenticación POS desde tu backend (consulta [Primeros pasos](https://docs.monei.com/es/es/monei-pay/app-integration/getting-started/.md))

## Integración[​](#integración "Enlace directo al Integración")

### 1. Instala el SDK[​](#1-instala-el-sdk "Enlace directo al 1. Instala el SDK")

* Swift Package Manager
* CocoaPods

En Xcode: **File → Add Package Dependencies** → introduce `https://github.com/MONEI/monei-pay-ios-sdk` → **Up to Next Major Version** desde `1.0.0`.

O en `Package.swift`:

Package.swift

```
dependencies: [

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

]
```

Podfile

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

Ejecuta `pod install` y abre el `.xcworkspace`.

### 2. Configura Info.plist[​](#2-configura-infoplist "Enlace directo al 2. Configura 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>
```

aviso

Reemplaza `your-app` con el esquema de URL único de tu app (p. ej. tu bundle ID). Cada app de comercio debe usar un esquema diferente.

### 3. Conecta el gestor de finalización[​](#3-conecta-el-gestor-de-finalización "Enlace directo al 3. Conecta el gestor de finalización")

* SwiftUI
* UIKit

YourApp.swift

```
import SwiftUI

import MoneiPaySDK



@main

struct YourApp: App {

    var body: some Scene {

        WindowGroup {

            ContentView()

                .onOpenURL { url in

                    MoneiPay.handleCompleteRedirect(url: url)

                }

        }

    }

}
```

AppDelegate.swift

```
import MoneiPaySDK



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

    return MoneiPay.handleCompleteRedirect(url: url)

}
```

### 4. Acepta un pago[​](#4-acepta-un-pago "Enlace directo al 4. Acepta un pago")

```
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",

            callbackUrl: "https://merchant.com/webhook/monei",

            completeScheme: "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 .tokenExpired, .invalidToken:

            break

        case .notAuthenticated, .accountNotConfigured:

            break

        case .paymentFailed(let reason):

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

        default:

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

        }

    }

}
```

Modelo de confianza

`callbackUrl` es un **webhook firmado de confianza** entregado de servidor a servidor — úsalo para completar pedidos. `completeScheme` (y la `complete_url` resultante) es **solo una redirección del lado del cliente** — devuelve al usuario a tu app pero no está firmada. Consulta [Entrega de resultados](https://docs.monei.com/es/es/monei-pay/app-integration/getting-started/.md#entrega-de-resultados) para más detalles.

### Referencia del SDK[​](#referencia-del-sdk "Enlace directo al Referencia del SDK")

#### `MoneiPay.acceptPayment(...)`[​](#moneipayacceptpayment "Enlace directo al moneipayacceptpayment")

| Parámetro         | Tipo            | Obligatorio | Descripción                                                                                                                                                                                                                   |
| ----------------- | --------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `token`           | `String`        | Sí          | Token de autenticación POS (JWT en bruto, sin prefijo "Bearer ")                                                                                                                                                              |
| `amount`          | `Int`           | Sí          | Importe del pago en céntimos (p. ej. `1500` = 15,00 EUR)                                                                                                                                                                      |
| `description`     | `String?`       | No          | Descripción del pago                                                                                                                                                                                                          |
| `customerName`    | `String?`       | No          | Nombre del cliente                                                                                                                                                                                                            |
| `customerEmail`   | `String?`       | No          | Correo electrónico del cliente                                                                                                                                                                                                |
| `customerPhone`   | `String?`       | No          | Teléfono del cliente                                                                                                                                                                                                          |
| `callbackUrl`     | `String?`       | No          | URL del webhook firmado de confianza. Debe ser `https://` estricto, máx. 2048 caracteres. Consulta [Entrega de resultados](https://docs.monei.com/es/es/monei-pay/app-integration/getting-started/.md#entrega-de-resultados). |
| `orderId`         | `String?`       | No          | Referencia de pedido del comercio. Configúralo para que aparezca en el webhook de callback para la conciliación con tu pedido POS. Máx. 2048 caracteres. Si se omite, MONEI genera uno.                                       |
| `transactionType` | `String?`       | No          | Tipo de transacción opcional: `SALE` (por defecto), `AUTH`, `REFUND`, `CAPTURE`, `CANCEL`, `PAYOUT`, `VERIF`. Validado en el servidor; los valores no válidos son rechazados.                                                 |
| `completeScheme`  | `String`        | Sí          | Esquema de URL registrado de tu app — usado para redirigir al usuario de vuelta tras completar el pago                                                                                                                        |
| `timeout`         | `TimeInterval?` | No          | Tiempo de espera en segundos (por defecto: 60)                                                                                                                                                                                |

#### `PaymentResult`[​](#paymentresult "Enlace directo al paymentresult")

| Propiedad          | Tipo      | Descripción                                       |
| ------------------ | --------- | ------------------------------------------------- |
| `transactionId`    | `String`  | ID de transacción de MONEI                        |
| `success`          | `Bool`    | Indica si el pago fue aprobado                    |
| `amount`           | `Int?`    | Importe del pago en céntimos                      |
| `cardBrand`        | `String?` | Marca de la tarjeta (p. ej. `visa`, `mastercard`) |
| `maskedCardNumber` | `String?` | Número de tarjeta enmascarado (p. ej. `****1234`) |

#### Tipos de error[​](#tipos-de-error "Enlace directo al Tipos de error")

| Error                    | Descripción                                                                                 |
| ------------------------ | ------------------------------------------------------------------------------------------- |
| `moneiPayNotInstalled`   | MONEI Pay no está instalado                                                                 |
| `paymentInProgress`      | Otro pago ya está activo                                                                    |
| `paymentTimeout`         | Sin respuesta dentro del tiempo de espera                                                   |
| `paymentCancelled`       | El usuario canceló                                                                          |
| `paymentFailed(reason:)` | Pago rechazado o fallido                                                                    |
| `invalidParameters(_:)`  | Entrada no válida (p. ej. importe no positivo, `callbackUrl` o `complete_url` mal formados) |
| `tokenExpired`           | El token de autenticación POS ha expirado (>24h)                                            |
| `invalidToken`           | El token de autenticación POS está mal formado o firmado con la clave incorrecta            |
| `notAuthenticated`       | No hay token POS y el usuario no ha iniciado sesión en MONEI Pay                            |
| `accountNotConfigured`   | La cuenta de MONEI Pay no tiene la configuración POS requerida                              |
| `failedToOpen`           | No se pudo abrir MONEI Pay                                                                  |

***

## Integración manual (esquema de URL)[​](#integración-manual-esquema-de-url "Enlace directo al Integración manual (esquema de URL)")

Si prefieres no usar el SDK, puedes integrarte directamente mediante esquemas de URL.

Mostrar integración manual con esquema de URL

### Abre la URL de pago[​](#abre-la-url-de-pago "Enlace directo al Abre la URL de pago")

```
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_url", value: "https://merchant.com/webhook/monei"),

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

    ]



    guard let url = components.url else { return }

    UIApplication.shared.open(url)

}
```

### Gestiona la redirección de finalización[​](#gestiona-la-redirección-de-finalización "Enlace directo al Gestiona la redirección de finalización")

```
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"]

    }

}
```

Modelo de confianza

Los parámetros de consulta en la redirección de `complete_url` **no están firmados** — pueden suplantarse. Usa `callback_url` (webhook firmado) o [Obtener pago](https://docs.monei.com/es/es/apis/rest/payments-get/.md) para confirmar el resultado real antes de completar un pedido. Consulta [Entrega de resultados](https://docs.monei.com/es/es/monei-pay/app-integration/getting-started/.md#entrega-de-resultados).

### Parámetros de la URL[​](#parámetros-de-la-url "Enlace directo al Parámetros de la URL")

#### Parámetros de la solicitud[​](#parámetros-de-la-solicitud "Enlace directo al Parámetros de la solicitud")

| Parámetro          | Tipo    | Obligatorio | Descripción                                                                                                                                                       |
| ------------------ | ------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `amount`           | Integer | Sí          | Importe del pago en céntimos (p. ej. `1500` = 15,00 EUR)                                                                                                          |
| `auth_token`       | String  | Sí          | Token de autenticación POS (JWT en bruto)                                                                                                                         |
| `callback_url`     | String  | No          | URL del webhook firmado de confianza. Debe ser `https://` estricto, máx. 2048 caracteres. Recibe un POST firmado con HMAC `MONEI-Signature` al completar.         |
| `complete_url`     | String  | No          | URL que se abre en el dispositivo tras completarse el pago. Acepta `https://`, esquemas de URL personalizados, Universal Links. Máx. 2048 caracteres. No firmada. |
| `description`      | String  | No          | Descripción del pago                                                                                                                                              |
| `customer_name`    | String  | No          | Nombre del cliente                                                                                                                                                |
| `customer_email`   | String  | No          | Correo electrónico del cliente                                                                                                                                    |
| `customer_phone`   | String  | No          | Teléfono del cliente                                                                                                                                              |
| `order_id`         | String  | No          | Referencia de pedido del comercio. Aparece en el webhook de callback para la conciliación. Máx. 2048 caracteres. Si se omite, MONEI genera uno.                   |
| `transaction_type` | String  | No          | Tipo de transacción opcional: `SALE`, `AUTH`, `REFUND`, `CAPTURE`, `CANCEL`, `PAYOUT`, `VERIF`. Validado en el servidor.                                          |

#### Parámetros de redirección de `complete_url` (éxito)[​](#parámetros-de-redirección-de-complete_url-éxito "Enlace directo al parámetros-de-redirección-de-complete_url-éxito")

| Parámetro            | Tipo   | Descripción                                       |
| -------------------- | ------ | ------------------------------------------------- |
| `success`            | String | `"true"`                                          |
| `transaction_id`     | String | ID de transacción de MONEI                        |
| `amount`             | String | Importe del pago en céntimos                      |
| `card_brand`         | String | Marca de la tarjeta (p. ej. `visa`, `mastercard`) |
| `masked_card_number` | String | Número de tarjeta enmascarado (p. ej. `****1234`) |

#### Parámetros de redirección de `complete_url` (error)[​](#parámetros-de-redirección-de-complete_url-error "Enlace directo al parámetros-de-redirección-de-complete_url-error")

| Parámetro | Tipo   | Descripción                                                                                                                                              |
| --------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `success` | String | `"false"`                                                                                                                                                |
| `error`   | String | Código de error — consulta [lista de códigos de error](https://docs.monei.com/es/es/monei-pay/app-integration/getting-started/.md#entrega-de-resultados) |

## App de ejemplo[​](#app-de-ejemplo "Enlace directo al App de ejemplo")

El directorio [`examples/MerchantDemo`](https://github.com/MONEI/monei-pay-ios-sdk/tree/master/examples/MerchantDemo) contiene una app SwiftUI que demuestra el flujo completo. Abre `MerchantDemo.xcodeproj`, configura tu equipo de firma y ejecútala en un dispositivo físico con MONEI Pay instalado. El proyecto referencia el SDK como paquete local, por lo que los cambios en el SDK se aplican de inmediato.

## Resolución de problemas[​](#resolución-de-problemas "Enlace directo al Resolución de problemas")

### `moneiPayNotInstalled`[​](#moneipaynotinstalled "Enlace directo al moneipaynotinstalled")

MONEI Pay debe estar instalado en el dispositivo. Instálalo desde el [App Store](https://apps.apple.com/app/monei-pay/id6478438783).

### `failedToOpen`[​](#failedtoopen "Enlace directo al failedtoopen")

Verifica que `monei-pay` aparece en `LSApplicationQueriesSchemes` en tu `Info.plist`. Sin ello, iOS bloquea la comprobación `canOpenURL`.

### `paymentTimeout`[​](#paymenttimeout "Enlace directo al paymenttimeout")

El tiempo de espera por defecto es de 60 segundos. Auméntalo mediante el parámetro `timeout` si es necesario. El temporizador usa tiempo de reloj real — poner tu app en segundo plano no lo pausa.

### La redirección de finalización no se recibe[​](#la-redirección-de-finalización-no-se-recibe "Enlace directo al La redirección de finalización no se recibe")

Comprueba que tu esquema de URL está registrado en `CFBundleURLTypes` en `Info.plist` y que `MoneiPay.handleCompleteRedirect(url:)` se llama en `onOpenURL` (SwiftUI) o en `application(_:open:options:)` (UIKit). Consulta [Conecta el gestor de finalización](#3-conecta-el-gestor-de-finalizaci%C3%B3n).

### El pago funciona en el simulador pero no en el dispositivo[​](#el-pago-funciona-en-el-simulador-pero-no-en-el-dispositivo "Enlace directo al El pago funciona en el simulador pero no en el dispositivo")

Los pagos NFC requieren un dispositivo físico con MONEI Pay instalado. Los simuladores no son compatibles.
