Android の Jetpack Compose 向け Google Pay API

1. はじめに

作成するアプリの概要

この Codelab を完了すると、Android 向けに Google Pay 統合が機能する最小限の Jetpack Compose アプリが完成します。このプロジェクトは、処理のために決済サービス プロバイダに送信される決済トークンを取得します。

学習内容

  • Google Pay Jetpack Compose ライブラリのインストールと構成方法
  • Google Pay ボタンを表示してクリックを処理する方法
  • Google Pay から支払いトークンをリクエストする方法

必要なもの

  • Android Studio(最新の安定版)がインストールされている。
  • Android Studio でセットアップされた Android SDK とエミュレータまたはデバイス。
  • 本番環境では、Google Pay merchantId が必要になります。Google Pay & Wallet Console での登録は 1 分で完了します。今すぐ登録しましょう。

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 として定義されたオブジェクトには、すべてのリクエストに共通する最小限の設定が含まれています。リクエストに応じて追加の設定が追加されます。この Codelab で確認します。

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(...)baseGooglePayRequesttransactionInfo を追加して PaymentDataRequest をビルドし、Google Pay シートを開いて、支払いトークンを返します。

要点

  • ボタン: PayButton はネイティブの Google Pay ボタンをレンダリングします。
  • クライアント: PaymentsClient が TEST または PRODUCTION で構成されています。
  • リリース: 必要に応じて loadPaymentData を使用し、IntentSender で解決します。
  • トークン: PaymentData.toJson() を解析して paymentMethodData.tokenizationData.token を抽出し、PSP に送信します。

6. まとめ

これでこの Codelab は終了です。Android 向け Jetpack Compose アプリに Google Pay API を統合する方法を学びました。

プロジェクトを実行する

Android Studio(Run > Run 'app')からプロジェクトを実行して、アプリを起動します。

次のステップ

参考情報