Pases de problemas con el SDK de Android

Una vez que hayas creado un pase y lo hayas codificado en un JWT, estarás listo para emitirlo en tu app para Android. Para hacerlo, verifica que la API de la Billetera de Google esté disponible en el dispositivo del usuario, muéstrale el botón “Agregar a la Billetera de Google” y, luego, guarda el pase en la Billetera de Google cuando presione el botón.

Requisitos previos

Antes de intentar emitir un pase, asegúrate de haber completado lo siguiente:

1. Instala el SDK de Android de la Billetera de Google

Para usar el SDK de Android de la Billetera de Google, agrega com.google.android.gms:play-services-pay a la sección dependencies del archivo build.gradle a nivel de la app:

  implementation "com.google.android.gms:play-services-pay:16.5.0"

2. Comprueba la disponibilidad de la API de la Billetera de Google

Antes de guardar el nuevo objeto, asegúrate de que la API de la Billetera de Google esté disponible en el dispositivo de destino llamando al método getPayApiAvailabilityStatus en la clase PayClient.

Comienza por agregar una variable de miembro a la actividad en la que mostrarás el botón y crearás una instancia cuando se cree la actividad:

Kotlin

import com.google.android.gms.pay.PayClient

private lateinit var walletClient: PayClient

override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)

  walletClient = Pay.getClient(this)

  // Additional logic in your onCreate method
}

Java

import com.google.android.gms.pay.PayClient;

private final PayClient walletClient;

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  walletClient = Pay.getClient(application);

  // Additional logic in your onCreate method
}

Si usas otros patrones de diseño, considera ubicar la lógica empresarial específica del dominio de forma adecuada. Por ejemplo, si usas el patrón de MVVM, coloca la lógica empresarial relacionada con la IU en tu actividad o fragmento (p. ej.: elementos de la IU, resultado de la actividad) y lógica operativa en tu modelo de vista (p. ej., creación de instancias de clientes, activadores de llamadas de red).

A continuación, usa PayClient para verificar si la API está disponible:

Kotlin

import com.google.android.gms.pay.PayApiAvailabilityStatus

private fun fetchCanUseGoogleWalletApi() {
  walletClient
    .getPayApiAvailabilityStatus(PayClient.RequestType.SAVE_PASSES)
    .addOnSuccessListener { status ->
      if (status == PayApiAvailabilityStatus.AVAILABLE) {
        // The API is available, show the button in your UI
      } else {
        // The user or device is not eligible for using the Pay API
      }
    }
    .addOnFailureListener {
      // Hide the button and optionally show an error message
    }
}

Java

import com.google.android.gms.pay.PayApiAvailabilityStatus;

private void fetchCanAddPassesToGoogleWallet() {
  walletClient
    .getPayApiAvailabilityStatus(PayClient.RequestType.SAVE_PASSES)
    .addOnSuccessListener(status -> {
      if (status == PayApiAvailabilityStatus.AVAILABLE) {
        // The API is available, show the button in your UI
      } else {
        // The user or device is not eligible for using the Pay API
      };
    })
    .addOnFailureListener(exception -> {
      // Google Play Services is too old, or API availability not verified
      // Hide the button and optionally show an error message
    });
}

Por último, llama al método que acabas de definir en tu aplicación cuando necesites determinar la disponibilidad de la API.

Controla los casos en que la API no está disponible

Es posible que la API no esté disponible, por ejemplo, porque las versiones de los Servicios de Google Play o Android están desactualizadas, o el hecho de que la Billetera de Google no esté disponible en el país del usuario.

Si la API no está disponible, considera ocultar el botón y recurrir a una integración diferente (p.ej., con un vínculo de JWT). Ten en cuenta que el usuario podría cumplir con los requisitos para usar la API en el futuro.

3. Agrega el botón “Agregar a la Billetera de Google”

La Billetera de Google ofrece un botón conocido que puedes usar para activar el flujo Agregar a la Billetera de Google en tu aplicación. Los recursos vectoriales del botón están disponibles en los lineamientos de botones.

Puedes importar recursos vectoriales en Android Studio en File > New > Vector Asset. Selecciona "Archivo local" en el asistente y agrega un nombre (p. ej.: add_to_google_wallet_button.xml) y busca el archivo en tu unidad local para importarlo.

  • Botón Agregar a la Billetera de Google
  • Botón Agregar a la Billetera de Google condensado

Ahora, puedes usar el elemento de diseño importado para agregar el botón a tu interfaz de usuario:

    <ImageButton
        android:id="@+id/addToGoogleWalletButton"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:minWidth="200dp"
        android:clickable="true"
        android:src="@drawable/add_to_google_wallet_button" />

El botón tiene una layout_height de 48 dp y debe tener al menos 200 dp de ancho.

4. Agrega un pase a la Billetera de Google de un usuario

Se puede agregar OfferObject pasando un JWT sin firma al método savePasses. Puedes iniciar la operación de agregar si haces clic en el botón de la Billetera de Google.

Kotlin

import android.os.Bundle
import android.view.View
import com.google.android.gms.samples.wallet.databinding.ActivityCheckoutBinding

private val addToGoogleWalletRequestCode = 1000

private lateinit var layout: ActivityCheckoutBinding
private lateinit var addToGoogleWalletButton: View

override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)

  // Use view binding to access the UI elements
  layout = ActivityCheckoutBinding.inflate(layoutInflater)
  setContentView(layout.root)

  addToGoogleWalletButton = layout.addToGoogleWalletButton
  addToGoogleWalletButton.setOnClickListener {
    walletClient.savePasses(newObjectJson, this, addToGoogleWalletRequestCode)
  }

  // Additional logic in your onCreate method
}

Java

import android.os.Bundle;
import android.view.View;
import com.google.android.gms.samples.wallet.databinding.ActivityCheckoutBinding;

private static final int ADD_TO_GOOGLE_WALLET_REQUEST_CODE = 999;

private ActivityCheckoutBinding layout:
private View addToGoogleWalletButton;

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  // Use view binding to access the UI elements
  layout = ActivityCheckoutBinding.inflate(getLayoutInflater());
  setContentView(layout.getRoot());

  addToGoogleWalletButton = layout.addToGoogleWalletButton;
  addToGoogleWalletButton.setOnClickListener(v -> {
    walletClient.savePasses(newObjectJson, this, ADD_TO_GOOGLE_WALLET_REQUEST_CODE);
  });

  // Additional logic in your onCreate method
}

Manejo de resultados

El método savePasses activa el flujo de guardado y, luego, invoca el método onActivityResult una vez que este se completa. La implementación de onActivityResult debe ser similar a la siguiente:

Kotlin

import android.content.Intent

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
  super.onActivityResult(requestCode, resultCode, data)

  if (requestCode == addToGoogleWalletRequestCode) {
    when (resultCode) {
      RESULT_OK -> {
        // Pass saved successfully
      }

      RESULT_CANCELED -> {
        // Save operation canceled
      }

      PayClient.SavePassesResult.SAVE_ERROR -> data?.let { intentData ->
        val errorMessage = intentData.getStringExtra(PayClient.EXTRA_API_ERROR_MESSAGE)
        // Handle error
      }

      else -> {
          // Handle unexpected (non-API) exception
      }
    }
  }
}

Java

import android.content.Intent;

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
  super.onActivityResult(requestCode, resultCode, data);

  if (requestCode == ADD_TO_GOOGLE_WALLET_REQUEST_CODE) {
    switch (resultCode) {
      case RESULT_OK: {
        // Pass saved successfully
        break;
      }

      case RESULT_CANCELED: {
        // Save operation canceled
        break;
      }

      case PayClient.SavePassesResult.SAVE_ERROR: {
        if (data != null) {
          String apiErrorMessage = data.getStringExtra(PayClient.EXTRA_API_ERROR_MESSAGE);
          // Handle error
        }
        break;
      }

      default: {
        // Handle unexpected (non-API) exception
      }
    }
  }
}

Cuando el pase se agrega de forma correcta, resultCode contiene el valor de Activity.RESULT_OK.