# Acepta pagos

El SDK tiene cuatro llamadas: `isSupported`, `prepare`, `acceptPayment` y `presentEducation`. La API es `@MainActor`. Llámala desde el actor principal, por ejemplo desde una vista de SwiftUI.

## Ejemplo[​](#example "Enlace directo al Ejemplo")

```
import MoneiTapToPay



// Al iniciarse, y otra vez después de cada renovación del token.

func startTapToPay() async {

  guard TapToPay.isSupported else { return } // Oculta Tap to Pay en el iPhone en tu interfaz.

  do {

    let token = try await myServer.fetchPosToken() // Solo el campo "token".

    try await TapToPay.prepare(token: token)

  } catch {

    // Consulta "Gestiona errores y resultados desconocidos".

  }

}



// Cuando el cliente paga.

func pay(amountInCents: Int, orderId: String) async {

  do {

    let result = try await TapToPay.acceptPayment(

      amount: amountInCents,

      orderId: orderId,

      callbackUrl: URL(string: "https://example.com/monei/webhook"))

    switch result.status {

    case .approved: showApproved(result)

    case .declined: showDeclined(result)

    @unknown default: showPending(orderId)

    }

  } catch TapToPayError.outcomeUnknown(let orderId) {

    // No vuelvas a intentarlo. Busca este orderId en MONEI.

    showPending(orderId)

  } catch {

    // Consulta "Gestiona errores y resultados desconocidos".

  }

}
```

## Comprueba la compatibilidad del dispositivo[​](#is-supported "Enlace directo al Comprueba la compatibilidad del dispositivo")

`TapToPay.isSupported` es `true` cuando el dispositivo es compatible con Tap to Pay en el iPhone: un iPhone XS o posterior con iOS 18.5 o posterior. Cuando es `false`, oculta Tap to Pay en el iPhone en tu interfaz. En un dispositivo no compatible, todas las llamadas lanzan `notSupported`.

## Prepara el lector[​](#prepare "Enlace directo al Prepara el lector")

Llama a `TapToPay.prepare(token:)` al iniciarse la app, y otra vez después de cada [renovación del token](https://docs.monei.com/es/monei-pay/in-app-tap-to-pay/backend.md#token-lifecycle).

* `prepare` guarda el token y prepara el lector en segundo plano, para que el primer pago empiece más rápido.
* El token se queda solo en memoria. Llama otra vez a `prepare` después de cada inicio.
* `prepare` no muestra los términos de Apple.
* `prepare` pide permiso de ubicación si el usuario todavía no ha respondido, y espera hasta que el usuario responde. No hay límite de tiempo para la respuesta. Si el usuario no permite el acceso a la ubicación, `prepare` lanza `locationDenied`. Si `Info.plist` no tiene `NSLocationWhenInUseUsageDescription`, `prepare` lanza `locationDenied` inmediatamente.
* El SDK necesita una ubicación para preparar el lector. El primer `prepare` o `acceptPayment` después del inicio espera hasta 15 segundos para obtenerla. Si el dispositivo no obtiene una ubicación en este tiempo, la llamada lanza `paymentFailed(code: .locationTimeout)`.

Cuando la app vuelve al primer plano, el SDK prepara otra vez el lector. No necesitas hacerlo tú.

## Cobra un pago[​](#accept-payment "Enlace directo al Cobra un pago")

Llama a `TapToPay.acceptPayment(amount:orderId:callbackUrl:)` cuando el cliente paga.

| Parámetro     | Tipo   | Descripción                                                                                                                                               |
| ------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `amount`      | Int    | El importe en céntimos de euro, mayor que 0. Por ejemplo, `1050` es 10,50 EUR.                                                                            |
| `orderId`     | String | Tu propia referencia del pedido. No puede estar vacía. Usa un `orderId` nuevo para cada pedido. MONEI lo guarda con el pago, y tú lo usas para conciliar. |
| `callbackUrl` | URL?   | La URL a la que MONEI envía el webhook firmado con el resultado del pago. Defínela siempre. Consulta [Confirma el pago con el webhook](#webhook).         |

Qué ocurre durante un pago:

1. Si la cuenta todavía no está vinculada, el primer `acceptPayment` en un dispositivo muestra los términos de Tap to Pay en el iPhone de Apple. El usuario debe aceptarlos.
2. Apple muestra la pantalla de pago. El cliente acerca la tarjeta.
3. MONEI procesa el pago.
4. `acceptPayment` devuelve un [`PaymentResult`](#result) o lanza un [`TapToPayError`](https://docs.monei.com/es/monei-pay/in-app-tap-to-pay/errors.md).

Solo puede ejecutarse un pago a la vez. Durante un pago, `acceptPayment` lanza `busy`, así que desactiva tu botón de pago.

El SDK nunca vuelve a intentar un pago

Si `acceptPayment` lanza `outcomeUnknown`, la tarjeta puede estar cobrada. No vuelvas a intentarlo, y no cobres otra vez con un `orderId` nuevo. Consulta [Resultado desconocido](https://docs.monei.com/es/monei-pay/in-app-tap-to-pay/errors.md#outcome-unknown).

## Lee el resultado[​](#result "Enlace directo al Lee el resultado")

`acceptPayment` devuelve un `PaymentResult`:

| Propiedad           | Tipo    | Descripción                                                                                                                                                    |
| ------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `paymentId`         | String  | El ID del pago en MONEI.                                                                                                                                       |
| `status`            | Status  | `.approved` o `.declined`.                                                                                                                                     |
| `cardBrand`         | String? | El nombre de la red de la tarjeta en minúsculas, por ejemplo `visa` o `amex`. `unknown` para una red que el SDK no conoce. `nil` si la red no está disponible. |
| `last4`             | String? | Los últimos 4 dígitos de la tarjeta, o `nil`.                                                                                                                  |
| `orderId`           | String  | El `orderId` del pago.                                                                                                                                         |
| `amount`            | Int     | El importe en céntimos.                                                                                                                                        |
| `currency`          | String  | La divisa como código ISO 4217, por ejemplo `EUR`.                                                                                                             |
| `statusCode`        | String? | El código de estado de MONEI, por ejemplo `E000`, o `nil`.                                                                                                     |
| `statusMessage`     | String? | El texto del código de estado de MONEI, o `nil`. Un resultado rechazado también tiene el motivo, por ejemplo fondos insuficientes.                             |
| `authorizationCode` | String? | El código de autorización de un resultado aprobado, o `nil`. Un resultado rechazado no tiene código de autorización.                                           |
| `cardType`          | String? | `credit`, `debit` o `prepaid`, o `nil`.                                                                                                                        |
| `cardCountry`       | String? | El país de la tarjeta como código ISO 3166-1 alfa-2, por ejemplo `ES`, o `nil`.                                                                                |

Solo `status` indica si el pago está aprobado. Usa los otros campos para mostrar el resultado en tu interfaz. El [webhook firmado](#webhook) sigue siendo el único resultado de pago fiable.

Un resultado con el estado `.declined` no es un error. La tarjeta se leyó y el emisor rechazó el pago. El pago está en MONEI con su `paymentId`.

### Muestra el motivo del rechazo[​](#decline-reason "Enlace directo al Muestra el motivo del rechazo")

Un resultado rechazado tiene el motivo en `statusMessage`. Muéstralo al cliente. No necesitas una llamada a MONEI para obtenerlo. `statusMessage` puede ser `nil`. En ese caso, muestra un texto general.

```
func showDeclined(_ result: PaymentResult) {

  // statusMessage puede ser nil. En ese caso, muestra un texto general.

  let reason = result.statusMessage ?? "La tarjeta se ha rechazado."

  showMessage("Pago rechazado: \(reason)")

}
```

## Confirma el pago con el webhook[​](#webhook "Enlace directo al Confirma el pago con el webhook")

Pasa siempre `callbackUrl`. MONEI envía a esta URL un webhook firmado con el resultado del pago. El webhook firmado es el único resultado de pago fiable. Usa el resultado de la app solo para la interfaz.

* Verifica la firma en tu servidor antes de completar el pedido. Consulta [Verificar la firma](https://docs.monei.com/es/guides/verify-signature.md).
* La `callbackUrl` debe usar `https`, tener 2048 caracteres o menos, y su host no puede ser una dirección IP privada. El SDK no comprueba la URL. MONEI la comprueba después de leer la tarjeta. Si la URL no es válida, MONEI no crea el pago, y `acceptPayment` puede lanzar `outcomeUnknown`. Asegúrate de que la URL es válida antes de aceptar pagos.
* Para saber cuándo envía MONEI este webhook, consulta [Callback del pago](https://docs.monei.com/es/guides/webhooks.md#payment-callback).

## Muestra las pantallas educativas de Apple[​](#education "Enlace directo al Muestra las pantallas educativas de Apple")

Llama a `TapToPay.presentEducation(from:)` para mostrar las pantallas de Apple que enseñan cómo acercar una tarjeta. Muéstralas, por ejemplo, en el primer uso y desde tu menú de ayuda.

```
try await TapToPay.presentEducation(from: viewController)
```

## Sentencias switch[​](#unknown-default "Enlace directo al Sentencias switch")

El SDK usa library evolution, y sus enums públicos no están congelados. Un `switch` sobre `PaymentResult.Status`, `TapToPayError` o `TapToPayErrorCode` necesita un caso `@unknown default`. Sin él, tu app no compila.

## Referencia de la API[​](#api-reference "Enlace directo al Referencia de la API")

| Símbolo                                                                                                 | Descripción                                                                                                                                                                                         |
| ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `TapToPay.isSupported: Bool`                                                                            | `true` cuando el dispositivo es compatible con Tap to Pay en el iPhone.                                                                                                                             |
| `TapToPay.prepare(token: String) async throws`                                                          | Guarda el token y prepara el lector en segundo plano. No muestra los términos de Apple. Pide permiso de ubicación si el usuario todavía no ha respondido.                                           |
| `TapToPay.acceptPayment(amount: Int, orderId: String, callbackUrl: URL?) async throws -> PaymentResult` | Cobra un pago con tarjeta. `amount` está en céntimos de euro y debe ser mayor que 0. `orderId` no puede estar vacío.                                                                                |
| `TapToPay.presentEducation(from: UIViewController) async throws`                                        | Muestra las pantallas de Apple que enseñan cómo acercar una tarjeta.                                                                                                                                |
| `PaymentResult`                                                                                         | `paymentId`, `status`, `cardBrand`, `last4`, `orderId`, `amount`, `currency`, `statusCode`, `statusMessage`, `authorizationCode`, `cardType` y `cardCountry`. Consulta [Lee el resultado](#result). |
| `TapToPayError`                                                                                         | El error que lanzan todas las llamadas. Consulta [Gestiona errores y resultados desconocidos](https://docs.monei.com/es/monei-pay/in-app-tap-to-pay/errors.md).                                     |

## Preguntas frecuentes[​](#common-questions "Enlace directo al Preguntas frecuentes")

### ¿Por qué tarda más el primer pago?[​](#first-payment "Enlace directo al ¿Por qué tarda más el primer pago?")

En el primer inicio, el SDK pide permiso de ubicación y espera la respuesta del usuario. Después necesita una ubicación antes de poder preparar el lector, y espera hasta 15 segundos para obtenerla. En el primer pago en un dispositivo, Apple también puede mostrar sus términos de Tap to Pay en el iPhone. Llama a `prepare` al iniciarse la app, para que el lector esté listo antes de que el cliente pague.

### ¿Debo llamar otra vez a prepare cuando la app vuelve del segundo plano?[​](#foreground "Enlace directo al ¿Debo llamar otra vez a prepare cuando la app vuelve del segundo plano?")

No. El SDK prepara otra vez el lector cuando la app vuelve al primer plano. Llama otra vez a `prepare` solo después de un inicio y después de una renovación del token.

### ¿Cada pedido necesita un orderId nuevo?[​](#new-order-id "Enlace directo al ¿Cada pedido necesita un orderId nuevo?")

Sí. Usa un `orderId` nuevo y no vacío para cada pedido. MONEI lo guarda con el pago, y tú lo usas para conciliar. Después de `outcomeUnknown`, no cobres otra vez con un `orderId` nuevo hasta que conozcas el resultado del primer pago. Consulta [Resultado desconocido](https://docs.monei.com/es/monei-pay/in-app-tap-to-pay/errors.md#outcome-unknown).
