Google Pay API dla Jetpack Compose na Androidzie

1. Wprowadzenie

Co utworzysz

Po ukończeniu tego modułu będziesz mieć minimalną działającą aplikację Jetpack Compose z działającą integracją Google Pay na Androida. Ten projekt pobiera token płatności, który może zostać wysłany do dostawcy usług płatniczych w celu przetworzenia.

Czego się nauczysz

  • Instalowanie i konfigurowanie biblioteki Google Pay Jetpack Compose
  • Wyświetlanie przycisku Google Pay i obsługa kliknięć
  • Jak poprosić o token płatności w Google Pay

Czego potrzebujesz

  • Zainstalowane Android Studio (najnowsza stabilna wersja).
  • pakiet SDK Androida oraz emulator lub urządzenie skonfigurowane w Android Studio;
  • W przypadku środowiska produkcyjnego potrzebujesz merchantId Google Pay. Rejestracja w Konsoli usług Google Pay i Portfela Google zajmuje tylko minutę, więc warto to zrobić od razu.

2. Tworzenie projektu Jetpack Compose

Tworzenie plików projektu

  1. Utwórz w Android Studio nowy projekt Jetpack Compose o nazwie gpay-compose:
    • Otwórz Android Studio i kliknij New Project.
    • Wybierz szablon Empty Activity (Jetpack Compose).
    • Nazwa: gpay-compose, nazwa pakietu: com.example.gpay.
    • Język: Kotlin, minimalna wersja SDK: API 21 lub nowsza.
    • Zakończ, aby wygenerować projekt.
  2. Dodaj zależność przycisku Google Pay Compose.W pliku app/build.gradle(.kts) dodaj:
    dependencies {
        implementation("com.google.pay.button:compose-pay-button:1.0.0")
    }
    
    Groovy DSL:
    dependencies {
        implementation 'com.google.pay.button:compose-pay-button:1.0.0'
    }
    
  3. Dodaj zależność Portfela w Usługach Google Play (aby otworzyć arkusz Google Pay):w pliku app/build.gradle(.kts) dodaj tę zależność:
    dependencies {
        implementation("com.google.android.gms:play-services-wallet:19.3.0")
    }
    
    Jeśli Twój projekt korzysta z Groovy DSL, użyj:
    dependencies {
        implementation 'com.google.android.gms:play-services-wallet:19.3.0'
    }
    
  4. Otwórz plik MainActivity.kt w Android Studio i zastąp jego zawartość tym minimalnym szkieletem aplikacji Compose (przycisk połączymy później):
    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. Konfigurowanie Google Pay

Żądanie płatności Google Pay wymaga obiektu żądania. Obiekt zdefiniowany tutaj jako baseGooglePayRequest zawiera minimalne wspólne ustawienia wszystkich żądań. W zależności od zgłoszenia zostaną dodane dodatkowe ustawienia, które omówimy w tym laboratorium.

Dodaj do MainActivity.kt stałe konfiguracji Google Pay (użyjesz ich w następnym kroku):

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

Zasoby

  • Dokumentacja API: dokumentacja obiektów żądań interfejsu Google Pay API
  • Dokumentacja interfejsu API: więcej informacji o dozwolonych metodach autoryzacji, dozwolonych sieciach kart i specyfikacjach tokenizacji, w tym o prawidłowej wartości bramy, znajdziesz w PaymentMethod.

4. Dodawanie przycisku Google Pay

Użyj komponentu Compose Pay Button, aby wyrenderować natywny przycisk Google Pay, oraz interfejsu Wallet API, aby otworzyć arkusz Google Pay.

Zastąp całą zawartość pliku MainActivity.kt tym kompletnym przykładem (obejmuje konfigurację, przycisk i proces płatności):

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. Wysyłanie prośby o płatność

Gdy użytkownik naciśnie przycisk Google Pay, requestPayment(...) utworzy PaymentDataRequest, dodając transactionInfo do baseGooglePayRequest, otworzy arkusz Google Pay i zwróci token płatności.

Najważniejsze kwestie

  • Przycisk: PayButton renderuje natywny przycisk Google Pay.
  • Klient: PaymentsClient jest skonfigurowany jako TEST lub PRODUCTION.
  • Uruchomienie: użyj loadPaymentData i w razie potrzeby rozwiąż problem za pomocą IntentSender.
  • Token: przeanalizuj PaymentData.toJson(), aby wyodrębnić paymentMethodData.tokenizationData.token i wysłać do dostawcy usług płatniczych.

6. Podsumowanie

Gratulujemy ukończenia tego ćwiczenia! Wiesz już, jak zintegrować Google Pay API z aplikacją na Androida napisaną w Jetpack Compose.

Uruchamianie projektu

Uruchom projekt w Android Studio Run > Run 'app', aby rozpocząć korzystanie z aplikacji.

Co dalej

Dodatkowe materiały