作者 / Caren Chang, Android Engineer

Google Play Billing 系列内容是专门为中文开发者开辟的系列分享,着重讲解中国开发者对 Play Billing 最容易感到疑惑的地方。如果您有任何问题,也欢迎在留言区提出,我们会收集大家的反馈并在后续文章中做出解答。

销售数字内容是许多 Android 应用的主要营收渠道。具体形式包括销售应用内的特定商品 (如游戏金币) 以及订阅计划 (比如允许用户在限定时间内访问高级功能)。Google Play Billing 作为一个数字内容销售的工具和服务的集合,可以帮助开发者在 Android 应用中销售线上商品。

本文将从基础知识开始,带大家逐步深入,详细了解 Google Play Billing 3,及其用例和最佳实践。

首先,我们来熟悉一下 Google Play Billing 的一些关键组件。

  • Google Play 管理中心 (Google Play Console) - Google Play 管理中心既是 Android 应用发布平台,也可以用于设置应用中销售的各种内容。在 Play 管理中心可以配置待出售的商品,包括价格点,以及针对每个产品进行高级配置,如提供订阅的免费试用期;

  • Google Play Billing Library - 这是您集成到 Android 应用中的开发库。使用此库连接 Google Play 就可以执行各种与销售相关的任务,例如在用户购买商品时处理购买流程;

  • Google Play Developer API - 一组 REST API,可用于与 Google Play 通信。使用这些 API 可以查询和管理应用销售的商品。这些 API 还可以验证用户的购买中是否存在欺诈行为,或者检查订阅是否仍处于有效状态。

  • Google Play 管理中心

    https://developer.android.google.cn/distribute/console

  • Google Play Billing Library

    https://developer.android.google.cn/google/play/billing/billing_library_overview

  • Google Play Developer API

    https://developers.google.cn/android-publisher

了解了 Google Play Billing 的关键组件之后,我们将从头介绍如何设置环境并开始在 Android 应用中销售商品。

1. 设置 Android 应用以使用 Google Play Billing 开发库

第一步,也是最重要的一步,是设置 Android 应用以使用 Google Play Billing 开发库。

向 app/build.gradle 文件中添加以下依赖关系,在应用中实现 Google Play Billing:

implementation ‘com.android.billingclient:billing:3.0.0’

添加库依赖关系后,为应用构建一个发布版 APK,并将其上传到 Google Play 管理中心。

2. 添加应用内产品

上传 APK 后,可以使用 Google Play 管理中心开始添加要在应用中销售的应用内产品。在 "商店发布 (Store Presence)" 下,有一个设置应用内产品的部分。在这里可以设置两种类型的商品:

  • 托管产品 (或一次性购买)

  • 订阅

创建新的托管产品和订阅时,需要输入商品的产品 ID (Product ID) 或 SKU。这个产品 ID 后续将在应用代码中使用,我们稍后会讲到。在创建托管产品之前,应慎重规划产品 ID。产品 ID 在应用中必须唯一,并且在创建后无法更改或重复使用。

为了使测试更快、更简单,您可以将您的 Google 帐号添加到 Google Play 开发者帐号的 "许可测试 (License Testing)" 中。这样,只要软件包名称与 Play Store 中的 APK 匹配,就可以使用调试版本和调试签名进行测试。

  • 将 Google 帐号添加到 Google Play 开发者帐号的 "许可测试 (License Testing)"  中

    https://developer.android.google.cn/google/play/billing/billing_testing#testing-purchases

3. 检查设置是否成功

在 Play 管理中心中设置好产品后,您可以在应用中查询产品的详细信息来检查设置是否成功。

lateinit private var billingClient: BillingClientoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// Set up the billing clientbillingClient = BillingClient.newBuilder(this).enablePendingPurchases().setListener(this).build()billingClient.startConnection(object : BillingClientStateListener {override fun onBillingSetupFinished(billingResult: BillingResult) {if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {Log.i(TAG, "Billing client successfully set up")queryOneTimeProducts()}}override fun onBillingServiceDisconnected() {Log.i(TAG, "Billing service disconnected")}})
}private fun queryOneTimeProducts() {val skuListToQuery = ArrayList<String>()skuListToQuery.add("coins_5")// ‘coins_5’ is the product ID that was set in the Play Console.// Here is where we can add more product IDs to query for based on// what was set up in the Play Console.val params = SkuDetailsParams.newBuilder()params.setSkusList(skuListToQuery).setType(BillingClient.SkuType.INAPP)// SkuType.INAPP refers to 'managed products' or one time purchases.// To query for subscription products, you would use SkuType.SUBS.billingClient.querySkuDetailsAsync(params.build(),object : SkuDetailsResponseListener {override fun onSkuDetailsResponse(result: BillingResult?,skuDetails: MutableList<SkuDetails>?) {Log.i(TAG, "onSkuDetailsResponse ${result?.responseCode}")if (skuDetails != null) {for (skuDetail in skuDetails) {Log.i(TAG, skuDetail.toString())}} else {Log.i(TAG, "No skus found from query")}}})
}

如果一切顺利,您将会看到刚刚添加进 Play 管理中心的产品的详细信息!

4. 接入 Google Play Billing 开发库

下一步,便是如何在您的 Android 应用中接入 Google Play Billing 开发库。

本文将以一次性购买的生命周期为例,即在应用中销售数字商品及授予用户的过程。如果您在应用中提供了订阅功能,您也可以阅读往期文章《订阅取消后的那些事儿——恢复订阅和重新订阅》了解更复杂生命周期的订阅流程。

一次性产品可以是消耗品,也可以是非消耗品。消耗品意味着用户可以再次购买。例如,如果您的游戏允许用户购买金币,您可以将金币做成消耗品,让用户可以多次购买。非消耗品意味着用户只能购买一次,典型示例是包含额外应用内功能的升级包。

在 Google Play 管理中心配置应用内产品后,其销售过程如下:

让我们逐步分析这一过程。

1. 设置 BillingClient - BillingClient 类让您的应用可以与 Play Billing Library 进行通信。您的应用需要做的第一件事是调用 startConnection() 与 Google Play 建立连接。

  • startConnection()

    https://developer.android.google.cn/reference/com/android/billingclient/api/BillingClient#startconnection

在实际环境中连接是有可能中断的,所以您的应用还必须重写 onBillingServiceDisconnected() 回调来处理重新连接,确保应用在发出任何进一步请求之前已与 Google Play 连接。

// Set up the billing client
val billingClient = BillingClient.newBuilder(this).enablePendingPurchases().setListener(this).build()
billingClient.startConnection(object : BillingClientStateListener {override fun onBillingSetupFinished(billingResult: BillingResult) {if (billingResult.responseCode == OK) {Log.i(TAG, "Billing client successfully set up!")// Query for existing user purchases// Query for products for sale}}override fun onBillingServiceDisconnected() {Log.i(TAG, "Billing service disconnected")// Restart the connection with startConnection() so future requests don't fail.}
})
  • onBillingServiceDisconnected()

    https://developer.android.google.cn/reference/com/android/billingclient/api/BillingClientStateListener#onBillingServiceDisconnected()

2. 获取用户的既有购买记录 - 成功设置 BillingClient 后,您的应用现在可以调用 queryPurchases() 来查询用户先前的购买记录。

/*** Query Google Play Billing for existing purchases.** New purchases will be provided to PurchasesUpdatedListener.*/
fun queryPurchases() {if (!billingClient.isReady) {Log.e(TAG, "queryPurchases: BillingClient is not ready")}// Query for existing in app products that have been purchased. This does NOT include subscriptions.val result = billingClient.queryPurchases(BillingClient.SkuType.INAPP)if (result.purchasesList == null) {Log.i(TAG, "No existing in app purchases found.")} else {Log.i(TAG, "Existing purchases: ${result.purchasesList}")}
}
  • queryPurchases()

    https://developer.android.google.cn/reference/com/android/billingclient/api/BillingClient#querypurchases

3. 呈现待售产品 - 在本文的前半部分我们谈到了如何在 Google Play 管理中心中设置产品以及如何在应用中查询这些产品。在调用 querySkuDetailsAsync() 获取每个产品的 SkuDetails 后,即可使用这些信息设置对应的界面。

private fun queryOneTimeProducts() {val skuListToQuery = ArrayList<String>()// sku refers to the product ID that was set in the Play ConsoleskuListToQuery.add("small_pineapple_seed")val params = SkuDetailsParams.newBuilder()params.setSkusList(skuListToQuery).setType(BillingClient.SkuType.INAPP)// SkuType.INAPP refers to 'managed products' or one time purchases// To query for subscription products, you would use SkuType.SUBSbillingClient.querySkuDetailsAsync(params.build(),object : SkuDetailsResponseListener {override fun onSkuDetailsResponse(result: BillingResult,skuDetails: MutableList<SkuDetails>?) {if (skuDetails != null) {// Store sku and skuDetail to be used later} else {Log.i(TAG, "No sku found from query")}}})
}
  • querySkuDetailsAsync()

    https://developer.android.google.cn/reference/com/android/billingclient/api/BillingClient#queryskudetailsasync

  • SkuDetails

    https://developer.android.google.cn/reference/com/android/billingclient/api/SkuDetails

4. 启动购买流程 - 当用户点击产品进行购买时,您的应用需要带上产品 SkuDetails 来调用 launchBillingFlow(),从而向用户展示 Google Play 购买界面 (如下图所示)。

fun launchPurchaseFlow(skuDetails: SkuDetails) {val flowParams = BillingFlowParams.newBuilder().setSkuDetails(skuDetails).build()val responseCode = billingClient.launchBillingFlow(this, flowParams)Log.i(TAG, "launchPurchaseFlow result ${responseCode}")
}
  • launchBillingFlow()

    https://developer.android.google.cn/reference/com/android/billingclient/api/BillingClient#launchbillingflow

5. 处理购买结果 - 在用户退出 Google Play 购买界面时 (点击 "购买" 按钮完成购买,或者点击 "返回" 按钮取消购买),onPurchaseUpdated() 回调会将购买流程的结果发送回您的应用。然后,根据 BillingResult.responseCode 即可确定用户是否成功购买产品。如果 responseCode == OK,则表示购买已成功完成。

  • onPurchaseUpdated()

    https://developer.android.google.cn/reference/com/android/billingclient/api/PurchasesUpdatedListener#onPurchasesUpdated(com.android.billingclient.api.BillingResult,%20java.util.List%3Ccom.android.billingclient.api.Purchase%3E)

  • BillingResult.responseCode

    https://developer.android.google.cn/reference/com/android/billingclient/api/BillingClient.BillingResponseCode

onPurchaseUpdated() 会传回一个 Purchase 对象列表,其中包括用户通过应用进行的所有购买。每个 Purchase 对象都包含 sku、purchaseToken 和 isAcknowledged 以及其他很多字段。使用这些字段,您可以确定每个 Purchase 对象是需要处理的新购买还是不需要进一步处理的既有购买。

// Google Play calls this method to propogate the result of the purchase flow
override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase?>?) {if (billingResult.responseCode == OK && purchases != null) {for (purchase in purchases) {handlePurchase(purchase)}} else if (billingResult.responseCode == USER_CANCELED) {Log.i(TAG, "User cancelled purchase flow.")} else {Log.i(TAG, "onPurchaseUpdated error: ${billingResult?.responseCode}")}
}
  • Purchase

    https://developer.android.google.cn/reference/com/android/billingclient/api/Purchase

6. 验证和确认购买 - 使用 Play Billing Library 3.0 时,您的应用需要确认购买成功才能完成购买流程。如果您的应用未在 72 小时内确认购买,则用户会自动收到退款,并且 Google Play 会撤消该购买交易

如果您的应用包含验证服务器组件,您应在验证成功后再确认购买。我们强烈推荐开发者对所有的应用内购买进行验证。请查看本指南了解有关打击欺诈性购买的更多信息。

  • 指南: 打击欺诈和滥用行为

    https://developer.android.google.cn/google/play/billing/security#verify

在对购买进行验证之后,您还需要对其进行确认。

  • 非消耗品必须通过调用 acknowledgePurchase() 进行确认;

  • 消耗品必须通过调用 consumeAsync() 来标记为 "已消耗 (consumed)",使得用户可以再次购买。调用 consumeAsync() 还会将购买设置为已确认,因此只要调用了 consumeAsync(),就无需再对消耗品调用 acknowledgePurchase()。

fun handlePurchase(purchase: Purchase) {// If your app has a server component, first verify the purchase by checking that the// purchaseToken hasn't already been used.// If purchase was a consumable product (a product you want the user to be able to buy again)handleConsumableProduct(purchase)// If purchase was non-consumable producthandleNonConsumableProduct(purchase)
}fun handleConsumableProduct(purchase: Purchase) {val consumeParams =ConsumeParams.newBuilder().setPurchaseToken(purchase.getPurchaseToken()).build()billingClient.consumeAsync(consumeParams, { billingResult, purchaseToken ->if (billingResult.responseCode == BillingResponse.OK) {// Handle the success of the consume operation.}})
}fun handleNonConsumableProduct(purchase: Purchase) {if (purchase.purchaseState == PURCHASED) {if (!purchase.isAcknowledged) {val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder().setPurchaseToken(purchase.purchaseToken)billingClient.acknowledgePurchase(acknowledgePurchaseParams.build())}}
}
  • acknowledgePurchase()

    https://developer.android.google.cn/reference/com/android/billingclient/api/BillingClient#acknowledgepurchase

  • consumeAsync()

    https://developer.android.google.cn/reference/com/android/billingclient/api/BillingClient#consumeasync

7. 授予用户产品 - 完成上述步骤后,您的应用就可以向用户授予他们购买的应用内产品了!

如果您想查看 Google Play Billing 开发库的资源,可以在此处访问官方文档。我们还提供了一些示例,演示了实现 Billing 库的最佳实践。本文中的代码示例可以在 GitHub 上获取。

  • 官方文档: Google Play Billing 服务概览

    https://developer.android.google.cn/google/play/billing/billing_overview

  • Play Billing 开发库示例

    https://github.com/android/play-billing-samples

  • 本文中的代码示例

    http://github.com/calren

如果您的应用目前尚未使用 Play Billing Library 3,务必查看我们的迁移指南,将您的应用迁移到最新的 Play Billing Library。

  • 从 AIDL 迁移到 Google Play Billing 开发库的迁移指南

    https://developer.android.google.cn/google/play/billing/migrate


推荐阅读

 点击屏末  | 即刻通过 Google Play 开启成功出海之旅


简单 3 步配置 Google Play Billing | 系列分享相关推荐

  1. Google Play Billing 系列分享: 订阅取消后的那些事儿——恢复订阅和重新订阅

    作者/高寒蕊,Google 开发技术推广工程师 Google Play Billing 系列内容是专门为中文开发者开辟的系列分享,着重讲解中国开发者对 Play Billing 最容易感到疑惑的地方. ...

  2. Google play billing(Google play 内支付) 上篇

    写在前面: 最近Google貌似又被全面封杀了,幸好在此之前,把Google play billing弄完了,现在写篇 博客来做下记录.这篇博客一是自己做个记录,二是帮助其他有需要的人.因为现在基本登 ...

  3. Window ChromeDriver(简单4步完成)

    Window 下配置ChromeDriver(简单4步完成) 第一步:当然是下载最新的chrome浏览器版本 https://www.google.cn/chrome/ 第二步:下载最新的Chrome ...

  4. Google play billing(Google play 内支付)

    http://www.bubuko.com/infodetail-930440.html [html] view plaincopy 如billing开发文档所说,要在你的应用中实现In-app Bi ...

  5. python人脸识别程序如何嵌入到app_只用Python就能写安卓,简单几步实现人脸识别的App...

    最近闲来无事,研究研究在安卓上跑Python. 想起以前玩过的kivy技术,kivy[1]是一个跨平台的UI框架.当然对我们最有用的是,kivy可以把python代码打包成安卓App.但是由于安卓打包 ...

  6. 【RHCE】NFS服务器简介及简单共享目录配置

    目录 NFS服务器简介 NFS的使用 [手工挂载] 客户端使用autofs自动挂载 NFS服务器简介 配置文件置顶: 主配置文件:vim /etc/exports[文件可能不存在.正常的] NFS(N ...

  7. 简单三步,入驻行云管家管理您的云资源

    行云管家登录页面:https://www.cloudbility.com Step1:注册登录 登录行云管家,支持QQ.微信.微博.Google等第三方账号 Step2:创建团队 基于团队协同的工作模 ...

  8. Keycloak简单几步实现对Spring Boot应用的权限控制,程序员Java基础案例教程

    关注并星标 码农小胖哥,第一时间获取相关干货文章. 客户端 === 相信不少同学用过微信开放平台.蚂蚁开放平台.首先我们需要在这些开放平台上注册一个客户端以获取一套类似用户名和密码的凭证.有的叫app ...

  9. Google play billing(Google play 内支付) 下篇

    开篇: 如billing开发文档所说,要在你的应用中实现In-app Billing只需要完成以下几步就可以了. 第一,把你上篇下载的AIDL文件添加到你的工程里,第二,把<uses-permi ...

最新文章

  1. linux系统中指定端口连接数限制
  2. 测中策---我的Web自动化测试思路
  3. 计算机启动过程从电路,加电到启动系统的启动过程详解
  4. 《Adobe InDesign CS6中文版经典教程》—第1课1.8节练习
  5. mysql根据时间戳查询指定日期内数据
  6. 常见的正则表达式验证(更新中)
  7. html语言中div怎么起名,css如何命名?
  8. xshell使用指南
  9. PostgreSQL-4-DML数据操纵语言
  10. 零基础学python图文版-教到你会为止的Python入门课程即将开班
  11. 机器学习:matlab实现异常检测
  12. 冷峭的 渗透测试入门DVWA教程001:环境搭建
  13. 和平精英为什么服务器显示错误,和平精英为什么会出现错误代码5567?_和平精英错误代码5567解决步骤一览...
  14. 黑晓军 华中科技大学 博士 副教授
  15. KEIL平台下新建华大HC32F460单片机工程笔记
  16. 适合新手小白的苹果CMS安装与配置
  17. 百万用户同时在线游戏服务器架构实现.doc 基于epoll 通信模型
  18. Leetcode刷题笔记12:【20】有效的括号【155】最小栈【255】用队列实现栈(STL:stackC++ 单引号和双引号)
  19. Cadence OrCAD Capture按页码自动编号的方法图文教程及视频演示
  20. js绑定事件和解绑事件的方法

热门文章

  1. 如何下载并注册消费者联盟和如何拿2元新人现金
  2. WebContent vs webapp
  3. 丝雨学姐小灶班——Week 6
  4. @RequiredArgsConstructor 引发的循环依赖问题
  5. VIVADO仿真卡住(executing simulate step)
  6. 鸟笼山剿匪记胡戈又一新作
  7. 【SOA】从单体架构到分布式微服务架构
  8. 使用JSDelivr加速Github、博客文件
  9. TeamViewer——可以实现在手机上随时远程控制你的电脑
  10. 惊!钉钉CEO离职创业跨境出海领域;中欧班列跑出新高度;浙江数字化步伐再加速…|洞悉跨境