API de Google Pay para Jetpack Compose en Android

1. Introducción

Qué compilarás

Cuando completes este codelab, tendrás una app de Jetpack Compose mínima viable con una integración de Google Pay para Android que funcione. Este proyecto recupera un token de pago que se puede enviar a un proveedor de servicios de pago para su procesamiento.

Qué aprenderás

  • Cómo instalar y configurar la biblioteca de Jetpack Compose de Google Pay
  • Cómo mostrar el botón de Google Pay y controlar los clics
  • Cómo solicitar un token de pago a Google Pay

Requisitos

  • Android Studio (la versión estable más reciente) instalado
  • SDK de Android y un emulador o dispositivo configurado en Android Studio
  • Para la producción, necesitarás un Google Pay merchantId. Solo toma un minuto registrarse en la Consola de Google Pay y la Billetera, así que puedes hacerlo ahora.

2. Crea el proyecto de Jetpack Compose

Crea archivos de proyecto

  1. Crea un nuevo proyecto de Jetpack Compose en Android Studio llamado gpay-compose:
    • Abre Android Studio y selecciona New Project.
    • Elige la plantilla Empty Activity (Jetpack Compose).
    • Nombre: gpay-compose, nombre del paquete: com.example.gpay.
    • Idioma: Kotlin, SDK mínimo: API 21+.
    • Haz clic en Finish para generar el proyecto.
  2. Agrega la dependencia del botón de Compose de Google Pay.En tu archivo app/build.gradle(.kts), agrega lo siguiente:
    dependencies {
        implementation("com.google.pay.button:compose-pay-button:1.0.0")
    }
    
    DSL de Groovy:
    dependencies {
        implementation 'com.google.pay.button:compose-pay-button:1.0.0'
    }
    
  3. Agrega la dependencia de la Billetera de los Servicios de Google Play (para abrir la hoja de Google Pay).En tu archivo app/build.gradle(.kts), agrega la siguiente dependencia:
    dependencies {
        implementation("com.google.android.gms:play-services-wallet:19.3.0")
    }
    
    Si tu proyecto usa DSL de Groovy, usa lo siguiente:
    dependencies {
        implementation 'com.google.android.gms:play-services-wallet:19.3.0'
    }
    
  4. Abre MainActivity.kt en Android Studio y reemplaza el contenido por el siguiente scaffold mínimo de la app de Compose (conectaremos el botón a continuación):
    package com.example.gpay
    
    import android.os.Bundle
    import androidx.activity.ComponentActivity
    import androidx.activity.compose.setContent
    import androidx.compose.foundation.layout.Box
    import androidx.compose.foundation.layout.fillMaxSize
    import androidx.compose.material3.MaterialTheme
    import androidx.compose.material3.Surface
    import androidx.compose.ui.Alignment
    import androidx.compose.ui.Modifier
    
    class MainActivity : ComponentActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContent {
                Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
                    Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
                        // Google Pay button added in the next section
                    }
                }
            }
        }
    }
    

3. Configura Google Pay

Una solicitud de pago de Google Pay requiere un objeto de solicitud. El objeto definido aquí como baseGooglePayRequest contiene la configuración común mínima para todas las solicitudes. Se agregarán parámetros de configuración adicionales según la solicitud realizada, que revisaremos en este codelab.

Agrega las constantes de configuración de Google Pay a MainActivity.kt (las volverás a usar en el siguiente paso):

private const val merchantId = "12345678901234567890"

// This is the base configuration for all Google Pay payment data requests.
private val baseGooglePayRequest = """
    {
      "apiVersion": 2,
      "apiVersionMinor": 0,
      "allowedPaymentMethods": [
        {
          "type": "CARD",
          "parameters": {
            "allowedAuthMethods": [
              "PAN_ONLY", "CRYPTOGRAM_3DS"
            ],
            "allowedCardNetworks": [
              "AMEX", "DISCOVER", "INTERAC", "JCB", "MASTERCARD", "VISA"
            ]
          },
          "tokenizationSpecification": {
            "type": "PAYMENT_GATEWAY",
            "parameters": {
              "gateway": "example",
              "gatewayMerchantId": "exampleGatewayMerchantId"
            }
          }
        }
      ],
      "merchantInfo": {
        "merchantId": "$merchantId"
      }
    }
""".trimIndent()

Recursos

  • Referencia de la API: Documentación de los objetos de solicitud de la API de Google Pay
  • Referencia de la API: Consulta PaymentMethod para obtener más información sobre los métodos de autorización permitidos, las redes de tarjetas permitidas y las especificaciones de tokenización, incluido el valor de puerta de enlace adecuado.

4. Agrega el botón de Google Pay

Usa el botón de pago de Compose para renderizar un botón nativo de Google Pay y la API de Wallet para abrir la hoja de Google Pay.

Reemplaza todo el contenido de MainActivity.kt por el siguiente ejemplo completo (incluye configuración + botón + flujo de pago):

package com.example.gpay

import android.os.Bundle
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import com.google.android.gms.common.api.CommonStatusCodes
import com.google.android.gms.tasks.Task
import com.google.android.gms.wallet.contract.TaskResultContracts.GetPaymentDataResult
import com.google.android.gms.wallet.*
import com.google.pay.button.ButtonTheme
import com.google.pay.button.ButtonType
import com.google.pay.button.PayButton
import org.json.JSONObject

private const val merchantId = "12345678901234567890"

// Base Google Pay request used for both the button and the Wallet request
private val baseGooglePayRequest = """
    {
      "apiVersion": 2,
      "apiVersionMinor": 0,
      "allowedPaymentMethods": [
        {
          "type": "CARD",
          "parameters": {
            "allowedAuthMethods": [
              "PAN_ONLY", "CRYPTOGRAM_3DS"
            ],
            "allowedCardNetworks": [
              "AMEX", "DISCOVER", "INTERAC", "JCB", "MASTERCARD", "VISA"
            ]
          },
          "tokenizationSpecification": {
            "type": "PAYMENT_GATEWAY",
            "parameters": {
              "gateway": "example",
              "gatewayMerchantId": "exampleGatewayMerchantId"
            }
          }
        }
      ],
      "merchantInfo": {
        "merchantId": "$merchantId"
      }
    }
""".trimIndent()

class MainActivity : ComponentActivity() {
  private val paymentDataLauncher = registerForActivityResult(GetPaymentDataResult()) { taskResult ->
    when (taskResult.status.statusCode) {
      CommonStatusCodes.SUCCESS -> {
        handlePaymentData(taskResult.result!!)
      }
      //CommonStatusCodes.CANCELED -> The user canceled
      //CommonStatusCodes.DEVELOPER_ERROR -> The API returned an error (it.status: Status)
      //else -> Handle internal and other unexpected errors
    }
  }
  private lateinit var paymentsClient: PaymentsClient

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

    // Create the PaymentsClient
    paymentsClient = Wallet.getPaymentsClient(
      this,
      Wallet.WalletOptions.Builder()
        .setEnvironment(WalletConstants.ENVIRONMENT_TEST) // Switch to PRODUCTION when ready
        .build()
    )

    // Derive allowedPaymentMethods for the button from baseGooglePayRequest
    val allowedPaymentMethods = JSONObject(baseGooglePayRequest)
      .getJSONArray("allowedPaymentMethods")
      .toString()

    setContent {
      Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
        PayButton(
          onClick = { requestPayment(paymentDataLauncher) },
          allowedPaymentMethods = allowedPaymentMethods,
          type = ButtonType.Pay,
          theme = ButtonTheme.Light
        )
      }
    }
  }

  private fun requestPayment(launcher: ActivityResultLauncher<Task<PaymentData>>) {
    // Build a PaymentDataRequest from the base request by adding transaction info
    val requestJson = JSONObject(baseGooglePayRequest).apply {
      put("transactionInfo", JSONObject().apply {
        put("totalPrice", "14.95")
        put("totalPriceStatus", "FINAL")
        put("countryCode", "US")
        put("currencyCode", "USD")
      })
    }
    val request = PaymentDataRequest.fromJson(requestJson.toString())
    val task = paymentsClient.loadPaymentData(request)
    task.addOnCompleteListener(paymentDataLauncher::launch)
  }

  private fun handlePaymentData(paymentData: PaymentData?) {
    val json = paymentData?.toJson() ?: return
    val paymentMethodData = JSONObject(json).getJSONObject("paymentMethodData")
    val tokenizationData = paymentMethodData.getJSONObject("tokenizationData")
    val token = tokenizationData.getString("token")
    // Send 'token' to your payment service provider (PSP)
    println("Payment token: $token")
  }
}

5. Realiza una solicitud de pago

Cuando se presiona el botón de Google Pay, requestPayment(...) compila un PaymentDataRequest agregando transactionInfo a tu baseGooglePayRequest, abre la hoja de Google Pay y muestra un token de pago.

Puntos clave

  • Botón: PayButton renderiza el botón nativo de Google Pay.
  • Cliente: PaymentsClient se configura con TEST o PRODUCTION.
  • Lanzamiento: Usa loadPaymentData y resuelve con un IntentSender cuando sea necesario.
  • Token: Analiza PaymentData.toJson() para extraer paymentMethodData.tokenizationData.token y enviarlo a tu PSP.

6. Conclusión

¡Felicitaciones por completar este codelab! Aprendiste a integrar la API de Google Pay en una app de Jetpack Compose para Android.

Cómo ejecutar el proyecto

Ejecuta el proyecto desde Android Studio (Run > Run 'app') para iniciar tu app.

Lo que vendrá

Recursos adicionales