PAYATHON 2026

android application 中的 Google wallet integration 问题

支付小周

结论

这些问题涉及早期的 Google Wallet Instant Buy for Android。该服务及旧版 SDK 均已停用,无法再按照旧文档申请或集成。新项目应改用 Google Pay API。旧示例中的 Interest Form、Instant Buy Client ID 和付款接收配置不适用于当前接口。

维护历史项目时,可以按以下方式理解:

  1. Instant Buy 的 Interest Form 是商户准入申请,并非普通的 SDK 配置步骤。
  2. 商户能否进入 Sandbox,取决于 Google 是否为该商户和应用开通权限。仅在 Developer Console 中创建 Client ID,不代表已经获得 Instant Buy 的使用权限。
  3. Google 账号所属国家通常不是唯一的判断条件,还要看商户主体、经营地区、收款能力和服务覆盖范围。印度账号即使能够提交表单,也不代表一定符合当时的商户准入要求。
  4. 付款接收方不能通过在 Android 示例中填写收款账号来配置。客户端只负责请求支付凭证,实际扣款和结算由支付网关及商户服务器处理。
  5. Developer Console 中创建的 Android Client ID 通常通过包名和签名证书与应用关联,不应随意以字符串形式写入客户端代码。如果接口要求填写 Web Client ID 或 Merchant ID,必须使用对应的标识,不能混用。

Interest Form 是否是 Sandbox 测试的必要条件

在旧版 Instant Buy 模式下,Interest Form 用于提交商户资料和申请接口权限。它不是用于创建测试账号的普通注册表单,而是产品准入流程的一部分。

能否在未提交表单的情况下运行示例,需要分情况判断:

  • 示例界面、普通 Android 代码以及不访问受限服务的部分,通常可以在本地运行。
  • 真正调用 Instant Buy、加载 Wallet、获取支付凭证或完成测试交易,往往要求商户和应用已经获得相应权限。

应用能够编译、安装,甚至正常显示购买按钮,并不代表 Sandbox 支付链路已经可用。缺少准入权限时,可能出现鉴权失败、商户未授权或 Wallet 请求无法完成等问题。

Instant Buy 已经退役,现在也无法通过补交旧表单恢复该能力。

印度 Google 账号能否提交

账号地区与商户服务的可用地区不是同一概念。

表单能否提交,可能只与账号是否可以访问页面有关。最终能否获批,还会受到以下条件影响:

  • 商户注册国家或地区;
  • 结算账户所在地;
  • 支付处理商是否受支持;
  • 销售的商品类型;
  • Google 当时开放的服务范围;
  • 商户审核和合规要求。

因此,仅凭 “Indian Google account” 无法判断服务一定可用或不可用。对于历史上的 Instant Buy,应以当时的区域支持政策和审核结果为准。新项目则应直接查看 Google Pay API 当前支持的国家、地区和支付网关。

付款接收方信息应该配置在哪里

不要将银行卡号、银行账户或私钥等付款接收方信息写入 Android 客户端。APK 很容易被反编译,写在其中的敏感收款配置可能泄露。

现代 Google Pay 集成通常分为三个部分:

  • Android 客户端声明商户名称、运行环境和支付方式;
  • 支付网关参数用于标识商户在网关侧的账户;
  • 商户服务器接收支付凭证,再调用支付网关完成扣款。

例如,Google Pay 的网关令牌化配置可以采用以下结构:

{
  "type": "PAYMENT_GATEWAY",
  "parameters": {
    "gateway": "example",
    "gatewayMerchantId": "exampleGatewayMerchantId"
  }
}

需要注意:

  • gateway 必须填写 Google Pay 支持的支付网关标识;
  • gatewayMerchantId 由支付网关提供,不是 Google Cloud Client ID;
  • 示例值不能用于真实交易;
  • API 密钥、网关密钥和私钥应保存在服务器端。

完整的客户端支付请求可以按以下结构组织:

val tokenizationSpecification = JSONObject().apply {
    put("type", "PAYMENT_GATEWAY")
    put("parameters", JSONObject().apply {
        put("gateway", "your_gateway")
        put("gatewayMerchantId", "your_gateway_merchant_id")
    })
}

val merchantInfo = JSONObject().apply {
    put("merchantName", "Your Store Name")
    // 获得生产环境 Merchant ID 后,再按当前文档要求设置。
    // put("merchantId", "01234567890123456789")
}

val paymentDataRequest = JSONObject().apply {
    put("apiVersion", 2)
    put("apiVersionMinor", 0)
    put("merchantInfo", merchantInfo)
    put("allowedPaymentMethods", JSONArray().put(
        JSONObject().apply {
            put("type", "CARD")
            put("tokenizationSpecification", tokenizationSpecification)
            put("parameters", JSONObject().apply {
                put(
                    "allowedAuthMethods",
                    JSONArray(listOf("PAN_ONLY", "CRYPTOGRAM_3DS"))
                )
                put(
                    "allowedCardNetworks",
                    JSONArray(listOf("VISA", "MASTERCARD"))
                )
            })
        }
    ))
    put("transactionInfo", JSONObject().apply {
        put("totalPriceStatus", "FINAL")
        put("totalPrice", "10.00")
        put("currencyCode", "USD")
    })
}

币种、网关、卡组织和 Merchant ID 都必须根据实际业务及当前官方要求填写,不能直接照搬示例。

Client ID 应该写在什么位置

首先要确认创建的是哪一种标识。

Android OAuth Client ID

Android OAuth Client ID 通常绑定以下信息:

  • Android package name;
  • 应用签名证书的 SHA-1 指纹。

这类 Client ID 主要用于识别应用身份,通常不需要直接写入 Google Pay 的支付请求。包名或签名不匹配时,即使 Client ID 本身正确,也可能导致认证失败。

调试版和正式版通常使用不同的证书,因此需要分别登记对应的 SHA-1:

keytool -list -v -keystore your-release-key.jks -alias your-key-alias

不要将 keystore 密码或私钥提交到代码仓库。

Web OAuth Client ID

Web Client ID 常用于服务器端 OAuth、ID Token 验证或特定的 Google 身份服务。只有相关 API 明确要求时,才应将其传入对应的身份认证配置。它不能替代 Google Pay Merchant ID,也不能替代支付网关的商户编号。

Google Pay Merchant ID

Google Pay Merchant ID 是生产环境中的商户标识,与 OAuth Client ID、Cloud project number 和支付网关 Merchant ID 都不是同一个值。

测试环境通常采用以下配置:

val walletOptions = Wallet.WalletOptions.Builder()
    .setEnvironment(WalletConstants.ENVIRONMENT_TEST)
    .build()

val paymentsClient = Wallet.getPaymentsClient(context, walletOptions)

生产环境则使用:

val walletOptions = Wallet.WalletOptions.Builder()
    .setEnvironment(WalletConstants.ENVIRONMENT_PRODUCTION)
    .build()

切换到生产环境不能只修改这一行代码。还需要完成 Google Pay 商户注册、应用审核、域名或应用信息配置,以及支付网关生产账户的设置。

推荐的迁移方式

如果现有示例包含 Instant Buy、FullWalletRequest、MaskedWalletRequest 或旧版 Wallet SDK,不建议继续在原有代码上修改。可以按以下步骤迁移:

  1. 在 Google Cloud 项目中启用当前所需的服务。
  2. 使用最新版 Google Play services Wallet 依赖。
  3. 通过 PaymentsClient 检查 Google Pay 是否可用。
  4. 使用 PaymentDataRequest 发起支付请求。
  5. 配置受支持的支付网关及其 gatewayMerchantId。
  6. 将返回的支付令牌发送到自己的服务器。
  7. 由服务器调用支付网关,完成授权、扣款和订单确认。
  8. 完成测试后,再申请并切换到生产环境。

不要将旧 Instant Buy 的 Client ID、Interest Form 或示例收款字段直接映射到 Google Pay API。两者属于不同阶段的产品流程,各类标识符的用途也不相同。

备注:内容仅供参考。