Saltar al contenido principal

Tap to Pay

Esta guía explica cómo integrar tu app Android con la app de Sipay Softpos (SoftPOS) mediante Android Intents, de forma App‑to‑App. En la práctica, tu app “lanza” Sipay Softpos con un Intent y un JSON que describe la operación (venta, reembolso, anulación, etc.). Sipay Softpos procesa el pago de forma segura en el dispositivo y te devuelve el resultado. Por qué te interesa:

  • Integración rápida: sin SDK pesado ni manejo de datos de tarjeta en tu app.
  • Menor coste de cumplimiento: la app de Sipay Softpos se encarga del cumplimiento EMV/PCI.
  • Mejor experiencia: cobros contactless directamente en el móvil del comercio.
aviso

Recuerda que debes permitir en el dispositivo Android donde instale Sipay Sofpos los permisos de geolocalización precisa y habilitar NFC.

¿Qué es un Android Intent App‑to‑App?

Es el mecanismo nativo de Android para pedir a otra app que realice una acción con unos datos. Tú defines una “acción” y envías un “payload” (JSON en String) a Sipay Softpos. Sipay Softpos muestra su UI, realiza el cobro y te devuelve el control con el resultado.

Ventajas

  • Seguridad: tu app no toca PAN ni PIN; lo gestiona Sipay Softpos.
  • Aislamiento: actualizaciones de Sipay Softpos no rompen tu app si mantienes el contrato del Intent.

Requisitos previos

Dispositivo Android con NFC y compatible con SoftPOS. App de Sipay Softpos instalada en el dispositivo. Manifest: declara la app de Sipay Softpos para que Android permita descubrirla:

<queries>
<package android:name="es.sipay.softpos" />
</queries>
nota

Sustituye com.Sipay Softpos.softpos.app por el package real de Sipay Softpos que uses en pruebas/producción.

  • Convenciones de formato:
    • Cantidades en el payload como String con punto y dos decimales (ej.: "10.00"). Aunque en España usamos coma decimal, el JSON debe usar punto.
    • Códigos de idioma de 2 letras (ej.: "es", "en").

Dispositivo

  • Dispositivo Android con NFC habilitado.
  • Almacén de claves respaldado por hardware, es decir, TEE en un SoC (Sistema en Chip).
  • Servicios de Google Mobile instalados.

Android OS

  • Android 8.1 o superior en producción.
  • La versión de Android debe tener soporte activo y parches de seguridad en los últimos seis meses.
  • No debe estar rooteado ni marcado como manipulado.

Sipay Softpos

  • Diseñado para funcionar solo en línea, es decir, con conexión a internet.
  • Las opciones de desarrollador deben estar siempre desactivadas.
  • Debe instalarse desde Google PlayStore.

App‑to‑App: Lanzar Sipay Softpos desde tu app Android

Paso 1. Construye el Intent

// Recomendado: usa el package de TU app para componer la acción y la clave del extra
// es.sipay.softpos
val myPackage = applicationContext.packageName
val intent = Intent("$myPackage")

// configJsonString: JSON en String con la operación y sus datos (ver ejemplos más abajo)
intent.putExtra("$myPackage.CONFIGURATION", configJsonString)

// Verifica que existe una app que pueda manejar este Intent (Sipay Softpos)
if (intent.resolveActivity(packageManager) != null) {
startActivityForResult(intent, PHOS_REQUEST_CODE) // API tradicional
// Alternativa moderna: registerForActivityResult con un ActivityResultContract personalizado
} else {
// Manejo de error: Sipay Softpos no instalado
}

Paso 2. Estructura general del payload (JSON en String)

{
"operation": "sale | refund | void | sso_login | history | receipt | language",
"data": { /* campos específicos */ }
}

Paso 3. Operaciones soportadas y ejemplos

Ejemplo de venta

{
"operation": "sale",
"data": {
"amount": "10.00",
"tip": "2.00",
"order_reference": "REF-123",
"show_result": "true",
"extras": {
"channel": "INSTORE",
"correlation_id": "c0ffee-123"
}
}
}

Ejemplo de venta con desglose de pagos

{
"operation": "sale",
"data": {
"amount": "10.00",
"tip": "2.00",
"order_reference": "1769096164229",
"show_result": true,
"extras": {
"breakdown": "[{\"id\":\"transaction_1\",\"amount\":0.99,\"description\":\"some description for transaction 1\"},{\"id\":\"transaction_2\",\"amount\":9.0,\"description\":\"some description for transaction 2\"}]"
}
}
}

Ejemplo de venta con conciliación bancaria

{
"operation": "sale",
"data": {
"amount": "10.00",
"tip": "2.00",
"order_reference": "1769096545437",
"show_result": true,
"extras": {
"reference": "1234ABCD"
}
}
}

Ejemplo de venta con desglose de pagos y conciliación bancaria

{
"operation": "sale",
"data": {
"amount": "10.00",
"tip": "2.00",
"order_reference": "1769096696462",
"show_result": true,
"extras": {
"reference": "1234ABCD",
"breakdown": "[{\"id\":\"transaction_1\",\"amount\":0.99,\"description\":\"some description for transaction 1\"},{\"id\":\"transaction_2\",\"amount\":9.0,\"description\":\"some description for transaction 2\"}]"
}
}
}

Ejemplo de devolución

Recuerda haber almacenado el transaction_key recibido en el JSON de respuesta de la venta.

{
"operation": "refund",
"data": {
"transaction_id": "TX-20240801-0001",
"amount": "5.00",
"extras": {
"reason": "Devolución parcial"
}
}
}

Ejemplo de anulación

Recuerda haber almacenado el transaction_key recibido en el JSON de respuesta de la venta.

{
"operation": "void",
"data": {
"transaction_id": "TX-20240801-0001",
"extras": {
"user": "seller-42"
}
}
}

Paso 4. Recibir el resultado (onActivityResult)

  • App‑to‑App devuelve control a tu Activity.
  • Comportamiento típico:
  • resultCode: RESULT_OK | RESULT_CANCELED
  • data: Intent con extras (p. ej., status, response_code, transaction_id, message)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == PHOS_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK && data != null) {
val status = data.getStringExtra("status") // por confirmar clave exacta
val responseCode = data.getStringExtra("code") // por confirmar
val transactionId = data.getStringExtra("tx_id") // por confirmar
// TODO: mapear estados, persistir, actualizar UI
} else {
// Cancelación por usuario o error
}
}
}

Ejemplo del resultado:

{
"resultCode": 0,
"responseBody": {
"transaction": {
"isTransaction3D": false,
"status": 1,
"operation": "sale",
"add date": "2026-01-20T09:45:1OUTC",
"amount": 1,
"tip_amount": 0,
"currency": "EUR",
"voidable": true,
"refundable_amount": 1,
"sca_type": 1,
"retrieval_reference_number": "95828e143bc80437555e328ec6d0ad4d",
"surcharge_amount": 0,
"payment_method": 0,
"instalments_number": 0,
"order_reference": "1768898601100",
"transaction_key": "95828e143bc80437555e328ec6d0ad4d",
"stan": "000561",
"auth_code": "889822",
"application_id": "A0000000031010",
"card": "416598XXXXXX8985",
"card_type": "VISA",
"action_code": 0,
"message": ""
},
"mid": "399",
"tid": "02385",
"extras": {
"order_reference": "1768898601100"
}
}
}

Códigos de respuesta resultCode

CódigoDescripción
0Transaction success
3000Transaction cancelled
3001Transaction declined
3002Card read failed
3003Connectivity failed
3004Unknown result

Códigos de respuesta status

CódigoDescripción
0Pending
1Successful
-1Failed
999Unknown

Códigos de respuesta action_code

Este código de respuesta viene en el contenido del campo action_code del JSON de respuesta de la venta. El código 0 es transacción correcta, cualquier otro código es un error y debe ser revisado con los valores de esta tabla.

CódigoTexto en Español
01Consulte al emisor (Refer to card issuer)
02Consulte al emisor (Refer to card issuer)
03Número de comercio inválido
04Retener tarjeta (Pick-up)
05Denegada / No honrar
06Error del sistema
07Retener tarjeta
08Consulte al emisor
09Por favor, espere
10Aprobación parcial
12Transacción inválida
13Importe inválido
14Tarjeta inválida
15No se puede enrutar al emisor
21No se ha tomado ninguna acción
25No se puede localizar el registro
28El archivo no es accesible
30Error del sistema
31Error de formato
33Tarjeta caducada
34Retener tarjeta
36Tarjeta restringida. Retener tarjeta
37Retener tarjeta
38Intentos de entrada de PIN excedidos
40Función no soportada
41Tarjeta reportada como perdida. Retener tarjeta
43Tarjeta reportada como robada. Retener tarjeta
51Fondos insuficientes
52Sin cuenta corriente
53Sin cuenta de ahorro
54Tarjeta caducada
55PIN incorrecto
56Tarjeta desconocida
57Transacción original no encontrada
58Terminal desconocido
60Se requiere PIN
61Límite de retiro excedido
62Tarjeta restringida
63Violación de seguridad
64Importe superior a la transacción original
65Reintentar en modo contacto (insertar tarjeta)
66Retener tarjeta
67Retener tarjeta
68Respuesta retrasada
75Intentos de entrada de PIN excedidos
76PIN incorrecto
77El emisor no soporta el servicio
78Cliente no elegible para POS
79Error del sistema
80Error de red
81Error criptográfico de PIN
82Tiempo de espera de la transacción agotado
83Fallo de comunicación
85Fallo verificación número de cuenta, dirección o CVV2
86Validación de PIN no posible
87Importe de reembolso (cashback) rechazado
88Fallo criptográfico
89Fallo de autenticación
91Emisor temporalmente no disponible
92Tipo de tarjeta inválido
94Transacción duplicada
95Error de conciliación
96Error del sistema
97Error del sistema
98Error del sistema
99Error del sistema
11008Error de procesamiento
11009PIN Pad no inicializado
11010Reintentar en modo contacto

Funcionalidades adicionales

Conciliación bancaria

Si nececesitas enviar un identificador para la conciliación bancaria, puedes hacerlo mediante el campo TREF en el payload de extras.

"extras" : {
"reference": "1234ABCD"
}

Recuerda respetar las reglas de validación de la entidad bancaria para el campo P37. Más información.

Desglose de pagos

Si necesitas enviar un desglose de pagos, puedes hacerlo mediante el campo breakdown en el payload de extras.

"extras" : {
"breakdown": "[{\"id\":\"transaction_1\",\"amount\":0.99,\"description\":\"some description for transaction 1\"},{\"id\":\"transaction_2\",\"amount\":9.0,\"description\":\"some description for transaction 2\"}]"
}

breakdown es un array de objetos con los siguientes campos:

  • id: identificador de la transacción.
  • amount: importe de la transacción.
  • description: descripción de la transacción.

El campo description es opcional y puede contener hasta 255 caracteres alfanuméricos.

Buenas prácticas de integración

  • Verifica la disponibilidad de Sipay Softpos antes de lanzar el Intent; guía al usuario a instalarla si falta.
  • No registres datos sensibles en logs (emails/teléfonos de recibos, etc.).
  • Establece timeouts y reintentos idempotentes; usa order_reference y un correlation_id en extras.
  • Si usas SSO: tokens con TTL corto, firma asimétrica (p. ej. RS256), rotación de claves.
  • Internacionalización: cambia el idioma de Sipay Softpos vía operación language cuando tu app cambie de idioma.

Pruebas recomendadas

  • Ventas aprobadas/denegadas (fondos insuficientes, tarjeta expirada, PIN incorrecto).
  • Propina: con y sin tip; reglas de redondeo.
  • Void inmediato vs refund diferido.
  • Envío de recibos vía email/SMS.
  • Web‑to‑App: valida notificaciones backend y estados en tiempo real.
  • Rendimiento: tiempos de autorización y de UI.

Checklist rápido:

  • App de Sipay Softpos instalada y discoverable (resolveActivity).
  • JSON válido con punto decimal en importes.
  • Manejo de RESULT_OK/RESULT_CANCELED.
  • Persistencia de transaction_id y response_code.
  • Reintentos idempotentes con order_reference.

FAQ

  • ¿Necesito permisos especiales? No, salvo declarar el package de Sipay Softpos en <queries> del manifest.
  • ¿Mi app verá datos de tarjeta? No. Sipay Softpos gestiona EMV/PCI.
  • ¿Puedo usar coma decimal? En pantalla sí, pero en el JSON usa punto y dos decimales.
  • ¿Hay callback en Web‑to‑App? No. Usa notificaciones de backend.

Preguntas para terminar tu integración con éxito

Compártenos esta información para adaptar la guía a tu caso y validar en preproducción/producción:

  1. Identificadores técnicos
  • PackageName de tu app Android (prod y preprod).
  • PackageName exacto de Sipay Softpos que instalarás (prod y preprod).
  • ¿Confirmas uso de action "<tuPackage>" y extra "<tuPackage>.CONFIGURATION"?
  1. Contrato de respuesta
  • ¿Qué claves de respuesta necesitas que validemos en onActivityResult? (transaction_id, status, response_code, message).
  • ¿Quieres un esquema de errores y ejemplos JSON en la doc?
  1. Autenticación y SSO
  • ¿Usarás SSO desde el inicio? issuer, formato y caducidad del token.
  • ¿Endpoint/backend para emitir y validar tokens listo?
  1. Reglas de negocio
  • ¿Habrá propina? Reglas de redondeo e importe máximo.
  • ¿Permitirás refund parcial? ¿Ventanas temporales para void vs refund?
  • Moneda: ¿solo EUR o multi‑moneda?
  1. Recibos y marca
  • ¿Envío de recibos lo hará Sipay Softpos directamente o quieres pasar por tu pasarela?
  • ¿Branding (logo/textos) personalizable?
  1. Web‑to‑App
  • ¿Usarás Web‑to‑App? Confirma el flujo de notificaciones servidor‑a‑servidor.
  1. Operativa y soporte
  • Datos de contacto de soporte técnico, horarios, y acuerdos de nivel de servicio (SLA).