API Google Pay para Jetpack Compose no Android

1. Introdução

O que você vai criar

Ao concluir este codelab, você terá um app mínimo viável do Jetpack Compose com uma integração funcional do Google Pay para Android. Esse projeto recupera um token de pagamento que pode ser enviado a um provedor de serviços de pagamento para processamento.

O que você vai aprender

  • Como instalar e configurar a biblioteca do Google Pay Jetpack Compose
  • Como mostrar o botão do Google Pay e processar cliques
  • Como solicitar um token de pagamento do Google Pay

O que é necessário

  • Android Studio (versão estável mais recente) instalado.
  • SDK do Android e um emulador ou dispositivo configurado no Android Studio.
  • Para produção, você vai precisar de um Google Pay merchantId. Leva apenas um minuto para se registrar no console do Google Pay e da Carteira, então aproveite para fazer isso agora.

2. Criar o projeto do Jetpack Compose

Criar arquivos de projeto

  1. Crie um novo projeto do Jetpack Compose no Android Studio chamado gpay-compose:
    • Abra o Android Studio e selecione New Project.
    • Escolha o modelo Empty Activity (Jetpack Compose).
    • Nome: gpay-compose, nome do pacote: com.example.gpay.
    • Linguagem: Kotlin, SDK mínimo: API 21 ou mais recente.
    • Conclua para gerar o projeto.
  2. Adicione a dependência do botão do Google Pay Compose.No arquivo app/build.gradle(.kts), adicione:
    dependencies {
        implementation("com.google.pay.button:compose-pay-button:1.0.0")
    }
    
    DSL do Groovy:
    dependencies {
        implementation 'com.google.pay.button:compose-pay-button:1.0.0'
    }
    
  3. Adicione a dependência da Carteira do Google Play Services (para abrir a página do Google Pay). No arquivo app/build.gradle(.kts), adicione a seguinte dependência:
    dependencies {
        implementation("com.google.android.gms:play-services-wallet:19.3.0")
    }
    
    Se o projeto usar o DSL do Groovy, use:
    dependencies {
        implementation 'com.google.android.gms:play-services-wallet:19.3.0'
    }
    
  4. Abra MainActivity.kt no Android Studio e substitua o conteúdo pelo seguinte scaffold mínimo do app Compose (vamos conectar o botão em seguida):
    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. Configurar o Google Pay

Uma solicitação de pagamento do Google Pay exige um objeto de solicitação. O objeto definido aqui como baseGooglePayRequest contém as configurações comuns mínimas para todas as solicitações. Outras configurações serão adicionadas dependendo da solicitação feita, que vamos analisar neste codelab.

Adicione as constantes de configuração do Google Pay a MainActivity.kt (você vai reutilizá-las na próxima etapa):

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

4. Adicionar o botão do Google Pay

Use o botão de pagamento do Compose para renderizar um botão nativo do Google Pay e a API Wallet para abrir a página do Google Pay.

Substitua todo o conteúdo de MainActivity.kt pelo exemplo completo a seguir (inclui configuração + botão + fluxo de pagamento):

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. Fazer uma solicitação de pagamento

Quando o botão do Google Pay é pressionado, requestPayment(...) cria um PaymentDataRequest adicionando transactionInfo ao seu baseGooglePayRequest, abre a página do Google Pay e retorna um token de pagamento.

Pontos principais

  • Botão: PayButton renderiza o botão nativo do Google Pay.
  • Cliente: PaymentsClient é configurado com TEST ou PRODUCTION.
  • Inicialização: use loadPaymentData e resolva com um IntentSender quando necessário.
  • Token: analise PaymentData.toJson() para extrair paymentMethodData.tokenizationData.token e enviar ao PSP.

6. Conclusão

Parabéns por concluir este codelab. Você aprendeu a integrar a API Google Pay a um app do Jetpack Compose para Android.

Executar o projeto

Execute o projeto no Android Studio (Run > Run 'app') para iniciar o app.

O que fazer depois disso

Outros recursos