通过 AIDL 使用 Google Play 结算服务

警告:AIDL 现已弃用并将在未来的版本中移除。要实现 Google Play 结算功能,请使用 Google Play 结算库

您可以使用 Android 接口定义语言 (AIDL) 接口来实现 Google Play 结算服务的一些功能。

购买商品

图 1. 购买请求的基本顺序。

Google Play Billing API 中的典型购买流程如下所示:

  1. 您的应用向 Google Play 发送 isBillingSupported 请求,以确定您当前使用的 Google Play Billing API 目标版本是否受支持。该请求还会验证 Google Play 是否支持在用户所在的国家/地区进行结算。
  2. 当您的应用启动或用户登录时,最好向 Google Play 进行查询,确定该用户拥有哪些商品。要查询用户购买的商品,请发送 getPurchases 请求。如果该请求成功,Google Play 会返回一个 Bundle,其中包含所购商品的商品 ID 列表,各项购买详情的列表以及购买签名的列表。
  3. 通常情况下,您需要将可供购买的商品通知用户。要查询您在 Google Play 中定义的应用内商品的详细信息,应用可以发送 getSkuDetails 请求。您必须在该查询请求中指定商品 ID 列表。如果该请求成功,Google Play 会返回一个 Bundle,其中包含商品详情(包括商品的价格、标题、说明和购买类型)。
  4. 如果该用户还未拥有某种应用内商品,您可以提示购买。为了发起购买请求,您的应用会发送 getBuyIntent 请求,指定要购买商品的商品 ID 以及其他参数。当您在 Google Play 管理中心内创建新的应用内商品时,应记录其商品 ID。
    1. Google Play 返回的 Bundle 中包含 PendingIntent,您的应用可用它来启动购买结算界面。
    2. 您的应用通过调用 startIntentSenderForResult 方法来启动待定 intent。
    3. 当结算流程结束后(即用户成功购买商品或取消购买),Google Play 会向您的 onActivityResult 方法发送响应 IntentonActivityResult 的结果代码中有一个代码将用于表明用户是完成了购买还是取消了购买。响应 Intent 中包含所购商品的相关信息,包括 Google Play 为了对此次购买交易进行唯一标识而生成的 purchaseToken 字符串。Intent 中还包含使用您的开发者私钥签署的购买签名。

要详细了解 Google Play Billing API 的调用和服务器响应,请参阅 Google Play 结算服务参考

消耗应用内商品

您可以使用消耗机制来跟踪用户对受管理商品的所有权。

所有受管理商品都在 Google Play Billing API 中进行管理。也就是说,用户对所购全部受管理商品的所有权均由 Google Play 进行维护,您的应用可以在需要时查询用户的购买信息。当用户成功购买受管理商品后,此次购买就会记录在 Google Play 中。受管理商品一经购买,就会被视为“被拥有”。处于“被拥有”状态的受管理商品无法再通过 Google Play 购买。您必须对“被拥有”的受管理商品发送消耗请求,然后 Google Play 才能再次将其设成可购买状态。消耗受管理商品会将其切换回“未被拥有”状态,并舍弃之前的购买数据。

图 2. 消耗请求的基本顺序。

为了检索用户所拥有商品的列表,您的应用会向 Google Play 发送 getPurchases 调用。您的应用可以通过发送 consumePurchase 调用提出消耗请求。在请求参数中,您必须指定受管理商品的唯一 purchaseToken 字符串,此字符串是在商品售出时由 Google Play 指定的。Google Play 会返回一个状态代码,指明此次消耗是否已成功记录。

非消耗型和消耗型受管理商品

您需要决定是要将受管理商品作为非消耗型商品还是消耗型商品进行处理。

非消耗型商品
通常情况下,对于在应用内购买一次就能永久使用的受管理商品,您无需实施消耗。这些商品在购买后将永久与用户的 Google 帐号相关联。高级版升级和关卡包就属于非消耗型受管理商品。
消耗型商品
相反,对于可多次购买的商品,您可以实施消耗。通常情况下,这类商品可提供一些临时效果。例如,用户在游戏中的角色可以从自己的库存中获得生命值或额外的金币。在您的应用中分配所购商品的收益或效果称之为“配置”受管理商品。您负责控制和跟踪如何向用户配置受管理商品。

重要提示:您必须先向 Google Play 发送消耗请求,在收到表明此次消耗已成功记录的响应之后,您才能在应用中配置消耗型受管理商品。

在应用中管理消耗型商品购买

下面是购买消耗型受管理商品的基本流程:

  1. 调用 getBuyIntent 方法以启动购买流程。
  2. 检查从 Google Play 返回的 Bundle,以确定购买是否成功完成。
  3. 如果购买成功,则通过调用 consumePurchase 方法消耗购买。
  4. 检查从 Google Play 接收的响应代码,以确定消耗是否成功完成。
  5. 如果消耗成功,则在您的应用中配置商品。

之后,当用户启动或登录到您的应用时,您应检查该用户是否拥有任何尚未消耗的消耗型应用内商品。如果有,请务必消耗并配置这些商品。如果您在应用中实现消耗型应用内商品,则可以采用以下推荐的应用启动流程:

  1. 发送 getPurchases 请求,查询该用户拥有的应用内商品。
  2. 如果有消耗型应用内商品,请通过调用 consumePurchase 消耗这些商品。必须执行这步操作,因为应用虽可能已完成该消耗型商品的采购订单,但在其发送消耗请求之前仍有可能已停止运行或断开连接。
  3. 检查从 Google Play 接收的响应代码,以确定消耗是否成功完成。
  4. 如果消耗成功,则在您的应用中配置商品。

配置奖励购买

使用 AIDL 处理奖励产品时,您应该在用户需要收取奖励之前缓存购买 `Intent`。您可以在后台线程上调用购买 Intent 并保存成功的响应 `Intent`,直到用户采取行动收取奖励。

列出并加载 SKU

在向用户提供奖励产品之前,请通过调用 getSkuDetails() 获取商品详情。为 SKU 列表中的每个奖励产品填充一个新的 JSON 字段“rewardToken”。

为了提供最佳用户体验,您应该确保在向用户提供奖励产品之前广告已加载并可用。为此,请在后台线程上调用 getBuyIntentExtraParams()。收到 BILLING_RESPONSE_RESULT_OK 的响应后,为用户启用奖励产品并保存返回的 PendingIntent 对象以供日后使用。以下代码段演示了加载与奖励产品相关联的广告的流程:

Kotlin

val rewardToken = skuDetailsJson.optString("rewardToken")
val extraParams = Bundle().putString("rewardToken", rewardToken)

// This call blocks the current thread, so do this in the background.
val buyIntentBundle : Bundle = mService.getBuyIntentExtraParams(9, packageName,
        sku, "inapp", "", extraParams)

val response = buyIntentBundle.getInt("RESPONSE_CODE")
if (response == BILLING_RESPONSE_RESULT_OK) {
    // Enable rewarded product.

    // Save this object for use later.
    val pendingIntentToSave = bundle.getParcelable(RESPONSE_BUY_INTENT)
} else {
    // Don't offer rewarded product.
}

Java

String rewardToken = skuDetailsJson.optString("rewardToken");
Bundle extraParams = new Bundle();
extraParams.putString("rewardToken", rewardToken);

// This call blocks the current thread, so do this in the background.
Bundle buyIntentBundle = mService.getBuyIntentExtraParams(9, getPackageName(),
        sku, "inapp", "", extraParams);

int response = buyIntentBundle.getInt("RESPONSE_CODE");
if (response == BILLING_RESPONSE_RESULT_OK) {
    // Enable rewarded product.

    // Save this object for use later.
    PendingIntent pendingIntentToSave = bundle.getParcelable(RESPONSE_BUY_INTENT);
} else {
    // Don't offer rewarded product.
}

声明适合相应年龄段的广告

为了协助履行与儿童和未成年用户相关的法律义务(包括《儿童在线隐私保护法》(COPPA)《一般数据保护条例》(GDPR)),您应该在自己的应用中声明哪些广告在美国应视为面向儿童的内容,以及哪些广告面向未满所在国家/地区规定年龄的用户。AdMob 帮助中心的文章阐明了在哪些情况下应将广告请求标记为面向儿童的内容,以及在哪些情况下应将其标记为适合未满规定年龄的用户的内容,并说明这样做的影响。

要指明奖励请求是针对儿童或未满规定年龄的用户,请添加 childDirectedunderAgeOfConsent 这两个额外的参数,如以下代码段中所示:

Kotlin

val rewardToken = skuDetailsJson.optString("rewardToken")
val extraParams = Bundle().putString("rewardToken", rewardToken)
        .putInt("childDirected", ChildDirected.CHILD_DIRECTED)
        .putInt("underAgeOfConsent", UnderAgeOfConsent.UNDER_AGE_OF_CONSENT)

// This call blocks the current thread, so do this in the background.
val buyIntentBundle : Bundle = mService.getBuyIntentExtraParams(9, packageName,
        sku, "inapp", "", extraParams)

Java

Bundle extraParams = new Bundle();
extraParams.putString("rewardToken", rewardToken);
extraParams.putInt("childDirected", ChildDirected.CHILD_DIRECTED);
extraParams.putInt("underAgeOfConsent", UnderAgeOfConsent.UNDER_AGE_OF_CONSENT);

// This call blocks the current thread, so do this in the background.
Bundle buyIntentBundle =
  mService.getBuyIntentExtraParams(
    9, getPackageName(), sku, "inapp", "", extraParams);

在奖励用户之前播放广告

在用户点击相应按钮开始观看广告后,您的应用就可以使用已保存的 PendingIntent 对象开始播放广告了。为此,请调用 startIntentSenderForResult()

Kotlin

startIntentSenderForResult(
    pendingIntentToSave,
    RC_BUY, Intent(),
    0,
    0,
    0
)

Java

startIntentSenderForResult(pendingIntentToSave, RC_BUY, new Intent(),
        0, 0, 0);

然后,在 onActivityResult() 中处理结算工作流的结果,如以下代码段中所示。处理广告播放流程时,Google Play 会使用与处理其他结算流程时使用的同一组服务器响应代码

Kotlin

fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent) {
    if (requestCode == RC_BUY) {
        int responseCode = data.getIntExtra(RESPONSE_CODE)
        String purchaseData = data.getStringExtra(RESPONSE_INAPP_PURCHASE_DATA)
        String signature = data.getStringExtra(RESPONSE_INAPP_SIGNATURE)

        // Handle reward purchase.
    }
}

Java

public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == RC_BUY) {
        int responseCode = data.getIntExtra(RESPONSE_CODE);
        String purchaseData = data.getStringExtra(RESPONSE_INAPP_PURCHASE_DATA);
        String signature = data.getStringExtra(RESPONSE_INAPP_SIGNATURE);

        // Handle reward purchase.
    }
}

本地缓存

由于 Google Play 客户端现在可以在设备上从本地缓存 Google Play 结算服务信息,因此您可以使用 Google Play Billing API 更频繁地查询这些信息。以下 Google Play Billing API 调用是在缓存中进行查找而不需要网络连接,这样显著加快了此 API 的响应速度:

  • getBuyIntent
  • getPurchases
  • isBillingSupported