Google Wallet API を Android に統合してパスをデジタル化する

1. 始める前に

Google Wallet API を使用すると、ポイントカード、クーポン、ギフトカード、イベント チケット、乗車券、搭乗券など、事前定義済みのさまざまなタイプのパスを通じてユーザーに働きかけることができます。これらのパスはすべて、ユースケースに固有のフィールドや機能を備えていますが、すべてのユースケースに適合するとは限りません。そのため、今回汎用パスタイプを作成しました。汎用パスタイプは、その名前が示すように、ユースケースが他の特殊なタイプに当てはまらない場合に使用します。汎用パスタイプのユースケースの例を次に示します。

  • 駐車パス
  • 図書館カード
  • ストアドバリュー型クーポン
  • ジムの会員カード
  • 保険証
  • さまざまな種類の予約

最大 3 行の情報からなるカードの形式でユーザーに提示可能な、あらゆるユースケースに汎用パスを使用できます。また、オプションとしてバーコードと詳細セクションを含めることができます。ただし、ユースケースが Google の利用規定を遵守していることが条件となります。

Google Wallet API を使用すると、以下を作成できます。

  • パスクラス。クラスは、プログラムやイベントに属するすべてのパスで共有される共通の情報を含むテンプレートと考えることができます。すべてのパス オブジェクトはクラスに属します。
  • パス オブジェクトは、ビジネスとしてのアクティビティやユーザーに関連する具体的な目的を果たすものです(駐車カード、ジムの会員カードなど)。これらのアイテムはそれぞれ、事前に定義されたクラスに関連付けられ、そのクラスから共通のプロパティを継承します。

この Codelab では、事前定義されたクラスを提供するとともに、パス オブジェクトの JSON を定義する方法について説明します。また、Android アプリに [Google ウォレットに追加] ボタンを追加して、ユーザーがパスを Google ウォレットに保存できるようにします。

Google Wallet API の詳細、または Android アプリに [Google ウォレットに追加] ボタンを追加する方法について詳しくは、Google ウォレットのデベロッパー向けドキュメントをご覧ください。

前提条件

  • Android Studio がインストールされているパソコン
  • Android Studio でプロジェクトを作成、実行できること
  • Git がインストールされていること

学習内容

  • Android アプリに Google ウォレット SDK を追加する方法
  • Wallet API が利用可能かどうかを確認する方法
  • ユーザーが Google ウォレットにパスを追加できるように UI を実装する方法

2. 設定する

一時的な発行者アカウントを作成する

ユーザーのパスを作成するには、まず発行者アカウントを作成し、Wallet API を有効にしてからクラスを作成します。これらすべてを、Google Pay Business Console で行うことができます。ただし、Google Pay Business Console へのアクセスは、承認プロセスが完了した後にのみ許可されます。そのため、この Codelab では、一時的な発行者アカウントとパスクラスの両方を作成します。

  1. 一時的な発行者アカウントとサンプルクラスを作成をクリックします。
  1. 発行者 ID とクラス ID をメモしておきます。これらは以降の手順で必要になります。

git リポジトリのクローンを作成する

次のコマンドを使用して、編集する Android プロジェクトが含まれる git リポジトリのクローンを作成します。

git clone -b wallet-lab git@github.com:google-pay/android-quickstart.git

Android Studio で開く

Android Studio を開き、ウェルカム画面で [Open] を選択します。その後、新しくクローンを作成したリポジトリの kotlin フォルダを選択し、プロジェクトとして開きます。

3. Google ウォレット SDK をアプリに追加する

Android プロジェクトには空のアクティビティが含まれていますが、これはこの後で編集します。ここではまず、Google ウォレット SDK を依存関係として追加します。Android Studio でモジュール レベルの「build.gradle」スクリプトを選択し、次の行を dependencies セクションに追加します。

dependencies {
    ...
    implementation 'com.google.android.gms:play-services-pay:16.0.3'
}

変更後、Android Studio の右上にある Gradle の [Sync Project] ボタンをクリックします。

4. [Google ウォレットに追加] ボタンを追加する

次に、アクティビティに [Google ウォレットに追加] ボタンを追加します。ボタンのアセットはすでにプロジェクトに追加されているため、レイアウト ファイルへの追加のみが必要です。そのためには、ボタンを含む個別のレイアウトを作成することをおすすめします。

add_to_google_wallet_button.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:clickable="true"
    android:focusable="true"
    android:layout_width="match_parent"
    android:layout_height="48sp"
    android:background="@drawable/add_to_google_wallet_button_background_shape"
    android:contentDescription="@string/add_to_google_wallet_button_content_description">
    <ImageView
        android:layout_width="227dp"
        android:layout_height="26dp"
        android:layout_gravity="center"
        android:duplicateParentState="true"
        android:src="@drawable/add_to_google_wallet_button_foreground"/>
</FrameLayout>

ボタンは次のようになります。

その後、これをレイアウト ファイルに含めます。

<include
    android:id="@+id/addToGoogleWalletButton"
    layout="@layout/add_to_google_wallet_button"
    android:layout_width="match_parent"
    android:layout_height="48dp"
    android:layout_marginTop="10dp"/>

5. API が利用可能かどうかを確認する

次に、コーディングを行います。Android Studio で、Google Wallet API に対して呼び出しを行うファイル(サンプルアプリを使用している場合は CheckoutActivity.kt ファイル)を開き、クラスのメンバーとして PayClient オブジェクトをインスタンス化します。このオブジェクトは、Google Wallet API の呼び出しに役立ちます。getClient メソッドは、インスタンス化元クラスのコンテキスト(この場合は呼び出しアクティビティ)を受け取ります。

private lateinit var walletClient: PayClient

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    walletClient = Pay.getClient(this)
    ...
}

次に、Google ウォレット SDK とアプリがデバイスで利用できることを確認します。クラス内に、Google Wallet API を利用できるかどうかを判断するための新しいメソッド(例: fetchCanUseGoogleWalletApi)を作成します。また、API が利用できる場合に、結果に反応して [Google ウォレットに追加] ボタンを表示するためのメソッドも作成します。

private fun fetchCanUseGoogleWalletApi() {
    walletClient
        .getPayApiAvailabilityStatus(PayClient.RequestType.SAVE_PASSES)
        .addOnSuccessListener { status ->
            if (status == PayApiAvailabilityStatus.AVAILABLE)
                layout.passContainer.visibility = View.VISIBLE
        }
        .addOnFailureListener {
             // Hide the button and optionally show an error message
        }
}

最後に、onCreate メソッドで fetchCanUseGoogleWalletApi メソッドを呼び出して、Google Wallet API が使用可能かどうかを判断します。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    ...
    fetchCanUseGoogleWalletApi()
}

アプリを実行すると、UI に [Google ウォレットに追加] ボタンが表示されます。

6. Google ウォレットにパスを追加する

Google Wallet API を利用できることを確認したので、次は Google ウォレットにパスを追加するようユーザーに促します。その前に、このワークショップで作成されたパスを確認します。

このレイアウトでは、汎用パスの柔軟性を活かし、ID バッジとして機能するオブジェクトを作成すると同時に、イベント中に参加者にポイントを集めるよう促すチャレンジもサポートできます。汎用オブジェクトは次のようになります。

{
    "id": "999999.d1fa-4cca1...",
    "classId": "999999.a92b-129f...",
    "genericType": "GENERIC_TYPE_UNSPECIFIED",
    "hexBackgroundColor": "#4285f4",
    "logo": { ... },
    "cardTitle": { ... },
    "subheader": { ... },
    "header": { ... },
    "barcode": { ... },
    "heroImage": { ... },
    "textModulesData": []
}

API はオブジェクトを挿入するにあたり、新しい要素をラップする無署名の JWT 本体を想定します。

{
    "iss": <owner-email-address>,
    "aud": "google",
    "typ": "savetowallet",
    "iat": <unix-time>,
    "origins": [],
    "payload": {
        "genericObjects": [],
        "genericClasses": [],
        ...
    }
}

一時的な発行者アカウントの作成時に取得した値を使用してすべてをまとめ、結果として得られるオブジェクトをクラスの変数に割り当てます。

private val issuerEmail = "<insert-your-issuer-email-address>"
private val issuerId = "<insert-your-issuer-id>"
private val passClass = "<insert-your-class-id>"
private val passId = UUID.randomUUID().toString()

private val newObjectJson = """
    {
      "iss": "$issuerEmail",
      "aud": "google",
      "typ": "savetowallet",
      "iat": ${Date().time / 1000L},
      "origins": [],
      "payload": {
        "genericObjects": [
          {
            "id": "$issuerId.$passId",
            "classId": "$passClass",
            "genericType": "GENERIC_TYPE_UNSPECIFIED",
            "hexBackgroundColor": "#4285f4",
            "logo": {
              "sourceUri": {
                "uri": "https://storage.googleapis.com/wallet-lab-tools-codelab-artifacts-public/pass_google_logo.jpg"
              }
            },
            "cardTitle": {
              "defaultValue": {
                "language": "en",
                "value": "Google I/O '22  [DEMO ONLY]"
              }
            },
            "subheader": {
              "defaultValue": {
                "language": "en",
                "value": "Attendee"
              }
            },
            "header": {
              "defaultValue": {
                "language": "en",
                "value": "Alex McJacobs"
              }
            },
            "barcode": {
              "type": "QR_CODE",
              "value": "$passId"
            },
            "heroImage": {
              "sourceUri": {
                "uri": "https://storage.googleapis.com/wallet-lab-tools-codelab-artifacts-public/google-io-hero-demo-only.jpg"
              }
            },
            "textModulesData": [
              {
                "header": "POINTS",
                "body": "${Random.nextInt(0, 9999)}",
                "id": "points"
              },
              {
                "header": "CONTACTS",
                "body": "${Random.nextInt(1, 99)}",
                "id": "contacts"
              }
            ]
          }
        ]
      }
    }
    """

最後に、onCreate メソッドに戻り、[Google Wallet に追加] ボタンにクリック ハンドラを追加し、クライアントで savePasses メソッドを使用してオペレーションを開始します。

private val addToGoogleWalletRequestCode = 1000

private lateinit var layout: ActivityCheckoutBinding
private lateinit var addToGoogleWalletButton: View

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // Use view binding to access the UI elements
    layout = ActivityCheckoutBinding.inflate(layoutInflater)
    setContentView(layout.root)

    addToGoogleWalletButton = layout.addToGoogleWalletButton.root
    addToGoogleWalletButton.setOnClickListener {
        walletClient.savePasses(newObjectJson, this, addToGoogleWalletRequestCode)
    }

    ...
}

これで、先ほど作成した発行者 ID とクラス ID を使用して、新しいオブジェクトの JSON を定義できました。ユーザーが [Google ウォレットに追加] ボタンをクリックすると、payClient.savePasses が呼び出され、新しいパス オブジェクトを Google ウォレットに追加するよう求められます。

7. 結果を処理する

完了まであと少しです。最後に、オペレーションの結果(成功または失敗)を処理します。次のコードを含めるように onActivityResult メソッドをオーバーライドします。

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)

    if (requestCode == addToGoogleWalletRequestCode) {
        when (resultCode) {
            RESULT_OK ->
                // Pass saved successfully. Consider informing the user.
            RESULT_CANCELED -> {
                // Save canceled
            }

            PayClient.SavePassesResult.SAVE_ERROR -> data?.let { intentData ->
                val errorMessage = intentData.getStringExtra(PayClient.EXTRA_API_ERROR_MESSAGE)
                // Handle error. Consider informing the user.
            }

            else -> {
                // Handle unexpected (non-API) exception
            }
        }
    }
}

このコードでは、パスが正常に追加された場合、ユーザーがアクションをキャンセルした場合、エラーが発生した場合を処理します。アプリを再度実行して、パスを追加し、結果を期待どおりに処理できることを確認します。

8. 完了

これで、Google Wallet API の Android への統合が完了しました。

詳細

詳細な例については、GitHub のサンプルアプリにある完全な統合をご覧ください。

発行者アカウントを登録する

本番環境で自分のパスを発行する準備が整ったら、Google Pay & Wallet Console に移動して Google Wallet API へのアクセスをリクエストし、Android アプリが API に対する呼び出しを発行することを許可します。詳しくは、こちらのドキュメントをご覧ください。