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
Utiliza intents estándar de Android — no se necesitan firmas especiales, permisos ni aprobaciones de plataforma.
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
Requisitos previos
- Cuenta MONEI
- Android 8.0+ (API 26), dispositivo compatible con NFC
- Token de autenticación POS desde tu backend — consulta Primeros pasos
- Modo directo: CloudCommerce instalado
- Modo a través de MONEI Pay: MONEI Pay instalado
Integración
1. Añade la dependencia
- GitHub Packages (recomendado)
- JitPack (sin autenticación)
Requiere un PAT con read:packages (o GITHUB_TOKEN en CI):
gpr.user=YOUR_GITHUB_USERNAME
gpr.key=YOUR_GITHUB_TOKEN
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()
}
}
}
}
dependencies {
implementation("com.monei:monei-pay-sdk:1.0.0")
}
2. Configura AndroidManifest
<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
- 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}")
}
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 para el registro. Consulta Entrega de resultados.
Referencia del SDK
MoneiPay.acceptPayment(...)
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. |
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
| 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
| 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)
Para integración sin el SDK usando intents de Android directamente.
Mostrar integración manual con intent
Configura tu Manifest
<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
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
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
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)
| 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)
| 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)
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 para el flujo completo desde el navegador.
Mostrar integración manual con deep link
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á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á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ámetro | Tipo | Descripción |
|---|---|---|
success | String | "false" |
error | String | Código de error — consulta lista de códigos de error |
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 para confirmar el resultado real antes de completar un pedido. Consulta Entrega de resultados.
App de ejemplo
El directorio 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
CloudCommerceNotInstalled
CloudCommerce es una app independiente de MONEI Pay. Instálala desde Google Play y verifica que tu manifest incluya el <package> y el esquema cloud_payment:// en <queries>.
MoneiPayNotInstalled
Instala MONEI Pay desde Google Play. Verifica que tu manifest incluya la acción com.monei.pay.ACCEPT_PAYMENT en <queries>.
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+
Android 11+ requiere <queries> para la visibilidad de paquetes. Asegúrate de que tu manifest tiene el bloque completo de queries de Configurar AndroidManifest.
La resolución de dependencias falla
- JitPack: Añade
google(),mavenCentral()yhttps://jitpack.ioasettings.gradle.ktsbajodependencyResolutionManagement. - GitHub Packages: Comprueba
gpr.user/gpr.keyengradle.propertiesy el bloque de repositoriomaven.pkg.github.com/MONEI/monei-pay-android-sdk.