Node.js

Conectarse con el backend

Integración de la interfaz de usuario con los puntos finales del backend

En el módulo anterior construimos la interfaz de usuario y una pequeña máquina de estados de interfaz de usuario (VerifyUiState). Ahora conectaremos esa UI a nuestro backend.

La idea principal es que la aplicación para Android nunca llamará a Vonage directamente. Solo llamará a tu backend:

  • POST /verification para iniciar el proceso de verificación

  • POST /check-code para validar un código (código SMS por ahora)

  • POST /next para forzar el uso del canal alternativo (SMS) de forma inmediata (y así no tener que esperar unos 20 segundos)

Empecemos por la aplicación.

Iniciar verificación (POST /verification)

Cuando el usuario pulsa Iniciar la verificación:

  1. La aplicación envía el número de teléfono a /verification

  2. El backend devuelve:

    • request_id (siempre)
    • check_url (opcional; se utiliza más adelante para la autenticación silenciosa)
  3. En esta sección, omitiremos la autenticación silenciosa y solicitaremos inmediatamente el retorno a SMS llamando a /next

  4. La interfaz de usuario pasa a la pantalla SMS (EnterSms(requestId))

private suspend fun startVerification(phone: String): Pair<String, String?> = withContext(Dispatchers.IO) {
    val client = OkHttpClient()

    val json = Gson().toJson(mapOf("phone" to phone))
    val requestBody = json.toRequestBody("application/json".toMediaType())

    val request = Request.Builder()
        .url("$BACKEND_URL/verification")
        .post(requestBody)
        .build()

    val response = client.newCall(request).execute()
    if (!response.isSuccessful) {
        val errorBody = response.body?.string() ?: "Unknown error"
        throw IOException("Start verification failed: HTTP ${response.code} - $errorBody")
    }

    val body = response.body?.string() ?: throw IOException("Empty response body")
    val jsonBody = Gson().fromJson(body, JsonObject::class.java)

    val requestId = jsonBody.get("request_id")?.asString ?: throw IOException("Missing request_id")
    val checkUrl = jsonBody.get("check_url")?.asString // may be null

    Pair(requestId, checkUrl)
}

Enviar código SMS (POST /check-code)

Cuando el usuario pulsa Enviar código:

  1. La aplicación envía { request_id, code } a /check-code
  2. El backend responde con { verified: true/false }
  3. La interfaz de usuario muestra «operación correcta» o «código no válido»
private suspend fun submitCode(requestId: String, code: String): CheckCodeResponse = withContext(Dispatchers.IO) {
    val client = OkHttpClient()

    val json = Gson().toJson(mapOf("request_id" to requestId, "code" to code))
    val requestBody = json.toRequestBody("application/json".toMediaType())

    val request = Request.Builder()
        .url("$BACKEND_URL/check-code")
        .post(requestBody)
        .build()

    val response = client.newCall(request).execute()
    if (!response.isSuccessful) {
        val errorBody = response.body?.string() ?: "Unknown error"
        throw IOException("Check code failed: HTTP ${response.code} - $errorBody")
    }

    val body = response.body?.string() ?: throw IOException("Empty response body")
    val jsonBody = Gson().fromJson(body, JsonObject::class.java)

    CheckCodeResponse(
        verified = jsonBody.get("verified")?.asBoolean ?: false,
        status = jsonBody.get("status")?.asString
    )
}

Forzar el plan alternativo rápidamente (POST /next)

Llamando a /next es opcional desde el punto de vista de la "corrección" (Vonage puede retroceder automáticamente), pero es muy útil para UX:

Si ya sabemos que no vamos a completar la autenticación silenciosa (o aún no la estamos implementando), llamar a /next evita la espera del tiempo de espera de la Autenticación Silenciosa.

private suspend fun requestNextWorkflow(requestId: String): Unit = withContext(Dispatchers.IO) {
    val client = OkHttpClient()

    val json = Gson().toJson(mapOf("requestId" to requestId))
    val requestBody = json.toRequestBody("application/json".toMediaType())

    val request = Request.Builder()
        .url("$BACKEND_URL/next")
        .post(requestBody)
        .build()

    val response = client.newCall(request).execute()
    if (!response.isSuccessful) {
        val errorBody = response.body?.string() ?: "Unknown error"
        throw IOException("Next workflow failed: HTTP ${response.code} - $errorBody")
    }
}