Android 適用的 Jetpack Compose 版 Google Pay API

1. 簡介

建構項目

完成本程式碼研究室後,您將擁有最低可行的 Jetpack Compose 應用程式,並整合可運作的 Android 版 Google Pay。這個專案會擷取付款權杖,並傳送給付款服務供應商進行處理。

課程內容

  • 如何安裝及設定 Google Pay Jetpack Compose 程式庫
  • 如何顯示 Google Pay 按鈕及處理點擊事件
  • 如何向 Google Pay 申請付款權杖

軟硬體需求

  • 已安裝 Android Studio (最新穩定版)。
  • Android SDK,以及在 Android Studio 中設定的模擬器或裝置。
  • 如要使用正式版,您需要 Google Pay merchantId。在 Google Pay 和錢包主控台註冊只需一分鐘,不妨現在就完成註冊。

2. 建立 Jetpack Compose 專案

建立專案檔案

  1. 在 Android Studio 中建立名為 gpay-compose 的新 Jetpack Compose 專案:
    • 開啟 Android Studio,然後選取 New Project
    • 選擇 Empty Activity (Jetpack Compose) 範本。
    • 名稱:gpay-compose,套件名稱:com.example.gpay
    • 語言:Kotlin,最低 SDK:API 21 以上版本。
    • 完成後即可生成專案。
  2. 新增 Google Pay Compose 按鈕依附元件。在 app/build.gradle(.kts) 檔案中新增:
    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. 新增 Google Play 服務錢包依附元件 (開啟 Google Pay 結帳頁面):在 app/build.gradle(.kts) 檔案中新增下列依附元件:
    dependencies {
        implementation("com.google.android.gms:play-services-wallet:19.3.0")
    }
    
    如果專案使用 Groovy DSL,請使用:
    dependencies {
        implementation 'com.google.android.gms:play-services-wallet:19.3.0'
    }
    
  4. 在 Android Studio 中開啟 MainActivity.kt,並將內容替換為下列最簡單的 Compose 應用程式架構 (我們會在下一個步驟中連結按鈕):
    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. 設定 Google Pay

Google Pay 付款要求需要要求物件。這裡定義的物件 (baseGooglePayRequest) 包含所有要求的最低通用設定。我們會根據提出的要求新增其他設定,並在程式碼研究室中進行檢查。

將 Google Pay 設定常數新增至 MainActivity.kt (您會在下一個步驟中重複使用這些常數):

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

資源

  • API 參考資料:Google Pay API 要求物件說明文件
  • API 參考資料:如要進一步瞭解允許的授權方法、允許的卡片網路和代碼化規格 (包括適當的閘道值),請參閱 PaymentMethod

4. 新增 Google Pay 按鈕

使用 Compose Pay Button 算繪原生 Google Pay 按鈕,並使用 Wallet API 開啟 Google Pay 結帳頁面。

MainActivity.kt 的所有內容替換為下列完整範例 (包括設定 + 按鈕 + 付款流程):

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. 提出付款要求

按下 Google Pay 按鈕時,requestPayment(...) 會將 transactionInfo 新增至 baseGooglePayRequest,藉此建構 PaymentDataRequest、開啟 Google Pay 畫面,並傳回付款權杖。

重點

  • Button:PayButton 會算繪原生 Google Pay 按鈕。
  • 用戶端:PaymentsClient 已設定為 TEST 或 PRODUCTION。
  • 啟動:使用 loadPaymentData,並在必要時以 IntentSender 解決問題。
  • 權杖:剖析 PaymentData.toJson() 以擷取 paymentMethodData.tokenizationData.token,然後傳送給 PSP。

6. 結語

恭喜您完成本程式碼研究室!您已瞭解如何將 Google Pay API 整合至 Android 的 Jetpack Compose 應用程式。

執行專案

從 Android Studio (Run > Run 'app') 執行專案,啟動應用程式。

下一步該做什麼?

其他資源