API Google Pay per Jetpack Compose su Android

1. Introduzione

Che cosa creerai

Al termine di questo codelab, avrai un'app Jetpack Compose minima e funzionante con un'integrazione di Google Pay per Android. Questo progetto recupera un token di pagamento che può essere inviato a un fornitore di servizi di pagamento per l'elaborazione.

Obiettivi didattici

  • Come installare e configurare la libreria Jetpack Compose di Google Pay
  • Come visualizzare il pulsante Google Pay e gestire i clic
  • Come richiedere un token di pagamento da Google Pay

Che cosa ti serve

  • Android Studio (ultima versione stabile) installato.
  • SDK Android e un emulatore o un dispositivo configurato in Android Studio.
  • Per la produzione, avrai bisogno di un Google Pay merchantId. La registrazione nella Console di Google Pay e Wallet richiede solo un minuto, quindi puoi occupartene subito.

2. Crea il progetto Jetpack Compose

Crea i file di progetto

  1. Crea un nuovo progetto Jetpack Compose in Android Studio denominato gpay-compose:
    • Apri Android Studio e seleziona New Project.
    • Scegli il modello Empty Activity (Jetpack Compose).
    • Nome: gpay-compose, nome del pacchetto: com.example.gpay.
    • Lingua: Kotlin, SDK minimo: API 21+.
    • Completa la generazione del progetto.
  2. Aggiungi la dipendenza del pulsante Google Pay Compose.Nel file app/build.gradle(.kts), aggiungi:
    dependencies {
        implementation("com.google.pay.button:compose-pay-button:1.0.0")
    }
    
    DSL Groovy:
    dependencies {
        implementation 'com.google.pay.button:compose-pay-button:1.0.0'
    }
    
  3. Aggiungi la dipendenza di Google Play Services Wallet (per aprire il foglio Google Pay):nel file app/build.gradle(.kts), aggiungi la seguente dipendenza:
    dependencies {
        implementation("com.google.android.gms:play-services-wallet:19.3.0")
    }
    
    Se il tuo progetto utilizza DSL Groovy, usa:
    dependencies {
        implementation 'com.google.android.gms:play-services-wallet:19.3.0'
    }
    
  4. Apri MainActivity.kt in Android Studio e sostituisci i contenuti con il seguente scaffold dell'app Compose minima (collegheremo il pulsante nel passaggio successivo):
    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 richiesta di pagamento di Google Pay richiede un oggetto della richiesta. L'oggetto definito qui come baseGooglePayRequest contiene le impostazioni comuni minime per tutte le richieste. Verranno aggiunte impostazioni aggiuntive a seconda della richiesta effettuata, che esamineremo in questo codelab.

Aggiungi le costanti di configurazione di Google Pay a MainActivity.kt (le riutilizzerai nel passaggio successivo):

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()

Risorse

  • Riferimento API: documentazione sugli oggetti della richiesta dell'API Google Pay
  • Riferimento API: consulta PaymentMethod per ulteriori informazioni sui metodi di autorizzazione consentiti, sulle reti di carte consentite e sulle specifiche di tokenizzazione, incluso il valore del gateway corretto.

4. Aggiungi il pulsante Google Pay

Utilizza il pulsante di pagamento Compose per eseguire il rendering di un pulsante Google Pay nativo e l'API Wallet per aprire il foglio Google Pay.

Sostituisci l'intero contenuto di MainActivity.kt con il seguente esempio completo (include configurazione, pulsante e flusso di 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. Effettua una richiesta di pagamento

Quando viene premuto il pulsante Google Pay, requestPayment(...) crea un PaymentDataRequest aggiungendo transactionInfo a baseGooglePayRequest, apre il foglio Google Pay e restituisce un token di pagamento.

Punti chiave

  • Pulsante: PayButton esegue il rendering del pulsante Google Pay nativo.
  • Client: PaymentsClient è configurato con TEST o PRODUCTION.
  • Avvio: utilizza loadPaymentData e risolvi con un IntentSender quando necessario.
  • Token: analizza PaymentData.toJson() per estrarre paymentMethodData.tokenizationData.token e inviarlo al tuo PSP.

6. Conclusione

Congratulazioni per aver completato questo codelab. Hai imparato a integrare l'API Google Pay in un'app Jetpack Compose per Android.

Esegui il progetto

Esegui il progetto da Android Studio (Run > Run 'app') per avviare l'app.

Come procedere

Risorse aggiuntive