# Integración de app Android

Acepta pagos NFC desde tu app Android usando el **SDK de Android de MONEI Pay**. El SDK gestiona el lanzamiento de intents, el análisis de resultados y el manejo de errores.

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

No se requiere firma especial

Utiliza intents estándar de Android — no se necesitan firmas especiales, permisos ni aprobaciones de plataforma.

## Modos de pago[​](#modos-de-pago "Enlace directo al Modos de pago")

El SDK admite dos modos:

* **Directo** (`PaymentMode.DIRECT`) — Lanza CloudCommerce directamente. No se requiere MONEI Pay.
* **A través de MONEI Pay** (`PaymentMode.VIA_MONEI_PAY`) — Lanza MONEI Pay, que gestiona el pago NFC.

|                            | Directo                            | A través de MONEI Pay                 |
| -------------------------- | ---------------------------------- | ------------------------------------- |
| Token de autenticación POS | Obligatorio                        | Obligatorio                           |
| App MONEI Pay              | No necesaria                       | Obligatoria                           |
| App CloudCommerce          | Obligatoria                        | No obligatoria                        |
| Ideal para                 | Configuraciones solo CloudCommerce | Flujo completo del comercio MONEI Pay |

## Descarga las apps necesarias[​](#descarga-las-apps-necesarias "Enlace directo al Descarga las apps necesarias")

**MONEI Pay** (modo a través de MONEI Pay)

[![Descarga MONEI Pay en Google Play](/img/google-play-icon.svg)](https://play.google.com/store/apps/details?id=com.monei.pay)

**CloudCommerce** (modo directo)

[![Descarga CloudCommerce en Google Play](/img/google-play-icon.svg)](https://play.google.com/store/apps/details?id=com.mastercard.cpos)

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

* [Cuenta MONEI](https://dashboard.monei.com/register)
* Android 8.0+ (API 26), dispositivo compatible con NFC
* Token de autenticación POS desde tu backend — consulta [Primeros pasos](https://docs.monei.com/es/es/monei-pay/app-integration/getting-started/.md)
* **Modo directo:** [CloudCommerce](https://play.google.com/store/apps/details?id=com.mastercard.cpos) instalado
* **Modo a través de MONEI Pay:** [MONEI Pay](https://play.google.com/store/apps/details?id=com.monei.pay) instalado

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

### 1. Añade la dependencia[​](#1-añade-la-dependencia "Enlace directo al 1. Añade la dependencia")

* GitHub Packages (recomendado)
* JitPack (sin autenticación)

Requiere un PAT con `read:packages` (o `GITHUB_TOKEN` en CI):

gradle.properties

```
gpr.user=YOUR_GITHUB_USERNAME

gpr.key=YOUR_GITHUB_TOKEN
```

settings.gradle.kts

```
dependencyResolutionManagement {

    repositories {

        google()

        mavenCentral()

        maven {

            url = uri("https://maven.pkg.github.com/MONEI/monei-pay-android-sdk")

            credentials {

                username = providers.gradleProperty("gpr.user")

                    .orElse(providers.environmentVariable("GITHUB_ACTOR"))

                    .get()

                password = providers.gradleProperty("gpr.key")

                    .orElse(providers.environmentVariable("GITHUB_TOKEN"))

                    .get()

            }

        }

    }

}
```

app/build.gradle.kts

```
dependencies {

    implementation("com.monei:monei-pay-sdk:1.0.0")

}
```

settings.gradle.kts

```
dependencyResolutionManagement {

    repositories {

        google()

        mavenCentral()

        maven { url = uri("https://jitpack.io") }

    }

}
```

app/build.gradle.kts

```
dependencies {

    implementation("com.github.MONEI.monei-pay-android-sdk:sdk:v1.0.0")

}
```

[![](https://jitpack.io/v/MONEI/monei-pay-android-sdk.svg)](https://jitpack.io/#MONEI/monei-pay-android-sdk)

### 2. Configura AndroidManifest[​](#2-configura-androidmanifest "Enlace directo al 2. Configura AndroidManifest")

AndroidManifest.xml

```
<manifest xmlns:android="http://schemas.android.com/apk/res/android">



    <queries>

        <intent>

            <action android:name="com.monei.pay.ACCEPT_PAYMENT" />

        </intent>

        <package android:name="com.mastercard.cpos" />

        <intent>

            <action android:name="android.intent.action.VIEW" />

            <data android:scheme="cloud_payment" />

        </intent>

    </queries>



</manifest>
```

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

* Modo directo
* A través de MONEI Pay

```
import com.monei.pay.sdk.MoneiPay

import com.monei.pay.sdk.PaymentMode

import com.monei.pay.sdk.MoneiPayException



try {

    val result = MoneiPay.acceptPayment(

        context = this,

        token = "eyJ...",

        amount = 1500,

        description = "Order #123",

        customerName = "John Doe",

        customerEmail = "john@example.com",

        customerPhone = "+34600000000",

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

        mode = PaymentMode.DIRECT

    )



    if (result.success) {

        println("Payment approved: ${result.transactionId}")

        println("Card: ${result.cardBrand} ${result.maskedCardNumber}")

    }

} catch (e: MoneiPayException.CloudCommerceNotInstalled) {

    // Prompt user to install CloudCommerce

} catch (e: MoneiPayException.PaymentCancelled) {

    // User cancelled

} catch (e: MoneiPayException.InvalidToken) {

    // Token expired or invalid

} catch (e: MoneiPayException.PaymentFailed) {

    println("Payment failed: ${e.reason}")

} catch (e: MoneiPayException) {

    println("Error: ${e.message}")

}
```

```
import com.monei.pay.sdk.MoneiPay

import com.monei.pay.sdk.PaymentMode

import com.monei.pay.sdk.MoneiPayException



try {

    val result = MoneiPay.acceptPayment(

        context = this,

        token = "eyJ...",

        amount = 1500,

        description = "Order #123",

        customerName = "John Doe",

        customerEmail = "john@example.com",

        customerPhone = "+34600000000",

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

        mode = PaymentMode.VIA_MONEI_PAY

    )



    if (result.success) {

        println("Payment approved: ${result.transactionId}")

        println("Card: ${result.cardBrand} ${result.maskedCardNumber}")

    }

} catch (e: MoneiPayException.MoneiPayNotInstalled) {

    // Prompt user to install MONEI Pay

} catch (e: MoneiPayException.PaymentCancelled) {

    // User cancelled

} catch (e: MoneiPayException.InvalidToken) {

    // Token expired or invalid

} catch (e: MoneiPayException) {

    println("Payment failed: ${e.message}")

}
```

Modelo de confianza

`callbackUrl` es un **webhook firmado de confianza** entregado de servidor a servidor — úsalo para completar pedidos. El `PaymentResult` síncrono del SDK también es de confianza porque vuelve a través del sistema de intents de Android desde la app de pago de origen, pero igualmente debe conciliarse mediante el webhook o [Obtener pago](https://docs.monei.com/es/es/apis/rest/payments-get/.md) para el registro. Consulta [Entrega de resultados](https://docs.monei.com/es/es/monei-pay/app-integration/getting-started/.md#entrega-de-resultados).

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

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

Función suspendida — llámala desde un ámbito de corrutina.

| Parámetro         | Tipo          | Obligatorio | Descripción                                                                                                                                                                                                                   |
| ----------------- | ------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `context`         | `Context`     | Sí          | Contexto de Activity o Application                                                                                                                                                                                            |
| `token`           | `String`      | Sí          | Token JWT de autenticación en bruto (sin prefijo `"Bearer "`)                                                                                                                                                                 |
| `amount`          | `Int`         | Sí          | Importe en céntimos                                                                                                                                                                                                           |
| `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. Aparece en el webhook de callback para la conciliación. Máx. 2048 caracteres. Si se omite, el SDK genera uno.                                                                              |
| `transactionType` | `String?`     | No          | Tipo de transacción opcional: `SALE` (por defecto), `AUTH`, `REFUND`, `CAPTURE`, `CANCEL`, `PAYOUT`, `VERIF`. Validado en el servidor.                                                                                        |
| `mode`            | `PaymentMode` | No          | `DIRECT` (por defecto) o `VIA_MONEI_PAY`                                                                                                                                                                                      |

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

| Propiedad          | Tipo      | Descripción                                       |
| ------------------ | --------- | ------------------------------------------------- |
| `transactionId`    | `String`  | ID de transacción de MONEI                        |
| `success`          | `Boolean` | 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")

| Excepción                   | Descripción                                        |
| --------------------------- | -------------------------------------------------- |
| `MoneiPayNotInstalled`      | MONEI Pay no está instalado (modo VIA\_MONEI\_PAY) |
| `CloudCommerceNotInstalled` | CloudCommerce no está instalado (modo DIRECT)      |
| `PaymentInProgress`         | Otro pago ya está activo                           |
| `PaymentCancelled`          | El usuario canceló                                 |
| `PaymentFailed`             | Pago rechazado o fallido (tiene `reason`)          |
| `InvalidParameters`         | Entrada no válida (p. ej. importe no positivo)     |
| `InvalidToken`              | Token de autenticación no válido o expirado        |

***

## Integración manual (Intent)[​](#integración-manual-intent "Enlace directo al Integración manual (Intent)")

Para integración sin el SDK usando intents de Android directamente.

Mostrar integración manual con intent

### Configura tu Manifest[​](#configura-tu-manifest "Enlace directo al Configura tu Manifest")

AndroidManifest.xml

```
<manifest xmlns:android="http://schemas.android.com/apk/res/android">



    <uses-permission android:name="com.monei.pay.permission.ACCEPT_PAYMENT" />



    <queries>

        <intent>

            <action android:name="com.monei.pay.ACCEPT_PAYMENT" />

        </intent>

    </queries>



</manifest>
```

### Lanza el intent de pago[​](#lanza-el-intent-de-pago "Enlace directo al Lanza el intent de pago")

```
class MainActivity : AppCompatActivity() {



    private val paymentLauncher = registerForActivityResult(

        ActivityResultContracts.StartActivityForResult()

    ) { result ->

        when (result.resultCode) {

            RESULT_OK -> handleSuccess(result.data)

            RESULT_CANCELED -> handleError(result.data)

        }

    }



    fun acceptPayment(amountInCents: Int) {

        val intent = Intent("com.monei.pay.ACCEPT_PAYMENT").apply {

            setPackage("com.monei.moneibusiness")

            putExtra("amount_cents", amountInCents)

            putExtra("auth_token", "eyJ...")

        }



        if (intent.resolveActivity(packageManager) == null) {

            return

        }



        paymentLauncher.launch(intent)

    }

}
```

### Gestiona el resultado[​](#gestiona-el-resultado "Enlace directo al Gestiona el resultado")

```
private fun handleSuccess(data: Intent?) {

    val success = data?.getBooleanExtra("success", false) ?: false

    val transactionId = data?.getStringExtra("transaction_id")

    val amount = data?.getIntExtra("amount", 0)

    val cardBrand = data?.getStringExtra("card_brand")

    val maskedCard = data?.getStringExtra("masked_card_number")

}



private fun handleError(data: Intent?) {

    val errorCode = data?.getStringExtra("error_code") ?: "UNKNOWN"

    val errorMessage = data?.getStringExtra("error_message") ?: "Payment failed"

}
```

### Parámetros del intent[​](#parámetros-del-intent "Enlace directo al Parámetros del intent")

#### Extras de la solicitud[​](#extras-de-la-solicitud "Enlace directo al Extras de la solicitud")

| Extra            | Tipo   | Obligatorio | Descripción                                              |
| ---------------- | ------ | ----------- | -------------------------------------------------------- |
| `amount_cents`   | Int    | 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)                |
| `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                                     |

#### Extras del resultado (RESULT\_OK)[​](#extras-del-resultado-result_ok "Enlace directo al Extras del resultado (RESULT_OK)")

| Extra                | Tipo    | Descripción                                       |
| -------------------- | ------- | ------------------------------------------------- |
| `transaction_id`     | String  | ID de transacción de MONEI                        |
| `success`            | Boolean | `true` si aprobado, `false` si rechazado          |
| `amount`             | Int     | 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`) |

#### Extras del resultado (RESULT\_CANCELED)[​](#extras-del-resultado-result_canceled "Enlace directo al Extras del resultado (RESULT_CANCELED)")

| Extra           | Tipo   | Descripción                                                    |
| --------------- | ------ | -------------------------------------------------------------- |
| `error_code`    | String | Código de error (p. ej. `PAYMENT_FAILED`, `NOT_AUTHENTICATED`) |
| `error_message` | String | Descripción del error legible por personas                     |

## Integración manual (Deep Link)[​](#integración-manual-deep-link "Enlace directo al Integración manual (Deep Link)")

Para llamadores que no son Android — incluidas **apps web** (terminales de comercio basadas en navegador) — MONEI Pay acepta un deep link con la misma estructura que el esquema de URL de iOS. Consulta [Integración de app web](https://docs.monei.com/es/es/monei-pay/app-integration/web/.md) para el flujo completo desde el navegador.

Mostrar integración manual con deep link

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

```
<a

  href="monei-pay://accept-payment

  ?amount=1500

  &auth_token=eyJ...

  &callback_url=https://merchant.com/webhook/monei

  &complete_url=https://merchant.com/checkout/done"

>

  Pay with MONEI Pay

</a>
```

O de forma programática:

```
window.location.href =

  'monei-pay://accept-payment' +

  '?amount=1500' +

  '&auth_token=' +

  encodeURIComponent(token) +

  '&callback_url=' +

  encodeURIComponent('https://merchant.com/webhook/monei') +

  '&complete_url=' +

  encodeURIComponent('https://merchant.com/checkout/done');
```

Android resuelve `monei-pay://` a través de la app MONEI Pay instalada. El usuario pulsa el enlace (o el navegador lo abre), MONEI Pay gestiona el pago NFC y luego redirige a `complete_url` con el resultado.

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

| 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, App 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) |

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).

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

El directorio [`examples/merchant-demo/`](https://github.com/MONEI/monei-pay-android-sdk/tree/master/examples/merchant-demo) contiene una app mínima que demuestra el flujo completo. Ejecuta `./gradlew assembleDebug` e instálala en un dispositivo compatible con NFC. El proyecto usa `includeBuild` para referenciar el SDK localmente, 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")

### `CloudCommerceNotInstalled`[​](#cloudcommercenotinstalled "Enlace directo al cloudcommercenotinstalled")

CloudCommerce es una app independiente de MONEI Pay. Instálala desde [Google Play](https://play.google.com/store/apps/details?id=com.mastercard.cpos) y verifica que tu manifest incluya el `<package>` y el esquema `cloud_payment://` en `<queries>`.

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

Instala MONEI Pay desde [Google Play](https://play.google.com/store/apps/details?id=com.monei.pay). Verifica que tu manifest incluya la acción `com.monei.pay.ACCEPT_PAYMENT` en `<queries>`.

### `InvalidToken`[​](#invalidtoken "Enlace directo al invalidtoken")

El token de autenticación POS tiene una validez de 24h. Genera un token nuevo desde tu backend. Pasa la cadena JWT en bruto — no `"Bearer ..."`.

### El pago falla en API 30+[​](#el-pago-falla-en-api-30 "Enlace directo al El pago falla en API 30+")

Android 11+ requiere `<queries>` para la visibilidad de paquetes. Asegúrate de que tu manifest tiene el bloque completo de queries de [Configurar AndroidManifest](#2-configura-androidmanifest).

### La resolución de dependencias falla[​](#la-resolución-de-dependencias-falla "Enlace directo al La resolución de dependencias falla")

* **JitPack:** Añade `google()`, `mavenCentral()` y `https://jitpack.io` a `settings.gradle.kts` bajo `dependencyResolutionManagement`.
* **GitHub Packages:** Comprueba `gpr.user` / `gpr.key` en `gradle.properties` y el bloque de repositorio `maven.pkg.github.com/MONEI/monei-pay-android-sdk`.
