集成 Google Wallet API 以在 Android 上数字化卡券

1. 准备工作

借助 Google Wallet API,您可以通过各种预定义的卡券类型与用户互动:会员卡、优惠券、礼品卡、活动门票、公交票卡和登机牌。这些卡券类型都具有特定于用例的字段和功能。但我们知道,这些类型可能并非在任何用例中都适用,因此我们创建了一个通用卡券类型。顾名思义,如果您的用例不属于任何其他专用类型,则应使用通用卡券类型。以下是通用卡券类型的一些用例:

  • 停车券
  • 图书馆会员卡
  • 储值代金券
  • 健身房会员卡
  • 保险卡
  • 各种预订

凡是能够以卡片(最多包含 3 行信息,可选择提供条形码和详细信息部分)形式向用户展示的用例,只要符合使用限制政策的要求,您都可以为其使用通用卡券。

借助 Google Wallet API,您可以创建:

  • 卡券类。您可以将类视为模板,其中包含属于某个项目或活动的所有卡券共享的通用信息。所有卡券对象都属于一个类。
  • 卡券对象服务于与您的商家活动和用户相关的具体用途(例如,停车券、健身房会员卡)。这些项都与之前定义的类相关联,并会继承类的通用属性。

此 Codelab 将为您提供一个预定义的类,并指导您为卡券对象定义 JSON,以及向您的 Android 应用添加“Add to Google Wallet”按钮,以允许用户将卡券保存到 Google 钱包。

如需详细了解 Google Wallet API 或如何向 Android 应用添加“Add to Google Wallet”按钮,请访问 Google 钱包开发者文档

前提条件

  • 一台安装了 Android Studio 的计算机。
  • 能够在 Android Studio 中创建和运行项目。
  • 已安装 Git。

学习内容

  • 如何将 Google Wallet SDK 添加到您的 Android 应用。
  • 如何检查 Wallet API 是否可用。
  • 如何实现界面,以允许用户将卡券添加到 Google 钱包。

2. 进行设置

创建临时发卡机构帐号

如需为用户创建卡券,您首先需要创建发卡机构帐号,启用 Wallet API,然后创建类。所有这些操作都可以通过 Google Pay 商家控制台完成。不过,只有在审批流程结束后,您才能获得访问权限。因此,在此 Codelab 中,我们将为您创建一个临时发卡机构帐号,以及一个卡券类。

  1. 点击创建临时发卡机构帐号和示例类
  1. 记下发卡机构 ID 和类 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 Wallet SDK 添加到您的应用

Android 项目包含一个空 activity,我们很快将修改此 activity,但首先,我们要将 Google Wallet SDK 添加为依赖项。在 Android Studio 中,选择模块级“build.gradle”脚本,并将以下代码行添加到 dependencies 部分:

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

更改后,点击 Android Studio 右上角的 Gradle“Sync Project”按钮。

4. 添加“Add to Google Wallet”按钮

接下来,您将向 activity 添加“Add to Google Wallet”按钮。该按钮的相关资源已为您添加到项目中,您只需将其添加到布局文件中即可。为此,我们建议您创建一个包含该按钮的单独布局:

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 方法会接收实例化类的上下文(在本例中,就是调用 activity 的上下文)。

private lateinit var walletClient: PayClient

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

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

接下来,确保 Google Wallet SDK 和应用在设备上可用。在类中创建一个新方法(例如:fetchCanUseGoogleWalletApi),以确定 Google Wallet API 是否可用;如果该 API 可用,则创建另一个方法,以通过显示“Add to Google Wallet”按钮来响应结果。

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

现在,当您运行应用时,应该会在界面中看到“Add to Google Wallet”按钮:

6. 向 Google 钱包添加卡券

现在您已经验证了 Google Wallet API 可用,接下来您可以提示用户将卡券添加到 Google 钱包。在此之前,请花点时间查看以下研讨会中创建的卡券:

该布局利用通用卡券的灵活性来创建可用作身份徽章的对象,同时还支持用于提醒参与者在活动期间收集积分的提示。该通用对象如下所示:

{
    "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 方法,向“Add to 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。当用户点击“Add to Google Wallet”按钮时,系统会调用 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. 恭喜

恭喜,您已成功在 Android 设备上集成 Google Wallet API!

了解详情

如需查看完整示例,请参阅 GitHub 中的示例应用中的完整集成。

注册发卡机构帐号

当您准备好在生产环境中发布自己的卡券时,请前往 Google Pay 和钱包控制台,请求对 Google Wallet API 的访问权限,并授权您的 Android 应用对该 API 发出调用。如需了解详情,请参阅此文档