Guía técnica · Android / Kotlin
Integrar el SDK Android de Tester Marketplace en 30 líneas
Actualizado:
Sin este paso, los testers pueden instalar tu app pero nosotros no podemos medir si la usan, y tú pagas sin pruebas. La integración completa son dos ficheros copiados y una Activity de 60 líneas.
Cómo funciona
- El tester se une a tu campaña en su panel web y pulsa «Generar código»: recibe un código de 8 caracteres (ABCD-EFGH) que vale 15 minutos y un solo uso, o toca «Abrir en la app» para pasárselo a tu app sin teclear.
- Tu app canjea el código en POST /track/pair y recibe un token de 90 días que solo sirve para enviar sesiones y eventos de esa asignación. No hay pantalla de login ni contraseñas de Tester Marketplace dentro de tu app.
- Cada vez que la app pasa a primer plano envías session/start y, al salir, session/end. Con eso el panel muestra sesiones, minutos y días activos por tester, y los pagos se liberan según las reglas de la campaña.
Pasos
Copia Tracker.kt y AssignmentProgress.kt del SDK a tu proyecto (sin dependencias externas: usa HttpURLConnection y coroutines) y sigue los tres pasos.
1. Manifest: acepta el deep link
<activity android:name=".MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="testermarketplace" android:host="pair" />
</intent-filter>
</activity>Con este intent-filter, el botón «Abrir en la app» de la asignación abre tu app con el código ya cargado. Si el tester está en otro dispositivo, siempre puede teclearlo: mantén el campo de texto.
2. Activity: emparejar y registrar sesiones
class MainActivity : AppCompatActivity() {
private var sessionStartedAt = 0L
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Tracker.apiBaseUrl = "https://testermarketplace.com/api-proxy"
Tracker.deviceId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
val prefs = getSharedPreferences("tm", MODE_PRIVATE)
Tracker.authToken = prefs.getString("tm_token", "") ?: ""
Tracker.assignmentId = prefs.getString("tm_assignment", "") ?: ""
findViewById<Button>(R.id.pairButton).setOnClickListener {
pair(findViewById<EditText>(R.id.code).text.toString())
}
Tracker.pairingCodeFromUri(intent?.dataString)?.let { pair(it) } // deep link
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
Tracker.pairingCodeFromUri(intent.dataString)?.let { pair(it) }
}
private fun pair(code: String) = Tracker.pair(code) { result ->
result.onSuccess { r ->
getSharedPreferences("tm", MODE_PRIVATE).edit()
.putString("tm_token", r.token).putString("tm_assignment", r.assignmentId).apply()
Tracker.startSession(); sessionStartedAt = System.currentTimeMillis()
}
}
override fun onResume() {
super.onResume()
if (Tracker.authToken.isNotBlank() && sessionStartedAt == 0L) {
Tracker.startSession(); sessionStartedAt = System.currentTimeMillis()
}
}
override fun onPause() {
super.onPause()
if (Tracker.authToken.isNotBlank() && sessionStartedAt > 0L) {
Tracker.endSession(((System.currentTimeMillis() - sessionStartedAt) / 1000).toInt())
sessionStartedAt = 0L
}
}
}Sustituye SharedPreferences por EncryptedSharedPreferences en producción y usa un deviceId estable propio (UUID persistido). El resto es literal.
3. Prueba de integración (10 minutos)
- Crea una campaña de validación de 1 tester y 1 día con el mismo packageName que la real.
- Únete con una cuenta tester tuya, genera el código y empareja tu build de pruebas.
- Abre y cierra la app: en 1 minuto la columna «Prueba» del panel muestra la sesión y la pantalla de activación deja de avisar en rojo.
- Activa la campaña real. Si activas sin haber recibido ninguna sesión, te pedimos confirmar el riesgo por escrito: los testers podrían usar la app sin que quede constancia y tendrías que pagar igualmente.
Eventos in-app y Play Integrity (opcional)
Si tu campaña exige eventos concretos (nivel completado, compra de prueba…), envíalos con Tracker.trackEvent("nombre_exacto") usando los mismos strings que pusiste al crear la campaña. Si marcaste Play Integrity, obtén el token con la API de Google y súbelo con Tracker.submitPlayIntegrityToken(): es la única prueba de que la app corrió en un móvil real, y en el panel ese tester aparece como «Play Integrity ✓».
Preguntas frecuentes
¿Necesito que el tester inicie sesión en mi app?
No. El código de emparejamiento sustituye al login: tu app nunca ve el email ni la contraseña del tester, y el token que recibe no da acceso a nada más de su cuenta.
¿Qué pasa si el tester reinstala la app o cambia de móvil?
Genera otro código desde su asignación y lo empareja de nuevo. El token anterior sigue siendo válido 90 días, pero cada asignación tiene un solo dispositivo emparejado visible en el panel.
¿Puedo integrarlo en una app híbrida (Flutter, React Native, Unity)?
Sí. Son tres llamadas HTTP JSON (pair, session/start, session/end) con cabecera Authorization: Bearer <token>. La referencia OpenAPI completa está en /docs.
¿Cómo sé que un tester ha usado la app de verdad y no solo la ha instalado?
En el panel de campaña cada tester muestra sesiones, minutos, días activos y la columna «Prueba» (Play Integrity ✓ o solo app). Además, antes de liberar cada tramo de pago recibes un email con el resumen y 48 horas para abrir disputa.