共计 5473 个字符,预计需要花费 14 分钟才能阅读完成。
图片起源:https://unsplash.com/photos/r…
本文作者:zoulp
近年来中国移动利用出海势头良好。对于波及到 交易业务 的出海利用来说,Google 利用内领取是必不可少的领取渠道。不同于国内绝对欠缺的挪动领取体系,即便官网文档中对如何接入 Google 利用内领取做了根本论述,然而在接入的过程中,还是会遇到很多问题。本文将介绍交易的重点流程和核心技术要点,以及须要留神的问题。
接入过程
名词解释
首先解释三对概念来帮忙了解 Google 领取的逻辑。
一次性商品 vs 订阅型商品
一次性商品 是通过单次购买取得的商品。一次性商品又分为 消耗型商品 和非消耗型商品,消耗型商品顾名思义是能够被耗费的商品,例如 App 提供的金币或虚构货币,用户能够反复购买。非消耗型商品是通过一次购买能够失去的永恒权利,例如付费降级的内容。
订阅型商品 是指会定期产生购买行为的商品,如会员服务等,订阅会主动续期,直至勾销。
本文的探讨仅限于 消耗型商品,不波及其余类型的商品。
Consume vs Acknowledge
Consume 和 Acknowledge 均有实现领取后进行确认的含意,但两者并不完全相同。
Acknowledge 是实际意义上的确认操作,进行了 Acknowledge 会使得订单不被退款,Acknowledge 可由客户端 API acknowledge()) 执行,也可由 Google 服务端 API acknowledge() 实现。Google 会对已领取但未确认的订单在三天后进行主动退款解决。
Consume 是专门针对消耗型商品的操作,Consume 不仅蕴含确认的含意,并使得商品能够反复购买。Consume 能够看成是蕴含了 Acknowledge 操作,Consume 仅可由客户端 API consumeAsync() 实现,不能通过服务端 API 进行。
业务服务端 vs Google 服务端
本文中将屡次提到对于服务端的操作,别离应用 业务服务端 和 Google 服务端 来进行辨别,防止混同。业务服务端指的是 App 业务逻辑的服务端。Google 服务端在本文特指 Google 利用内领取的服务端,由 Google 提供。
交易流程概览
从业务的角度登程,一次交易流程能够大体用下图示意:
但在理论交易的过程中,充斥了不确定的因素,如用户的网络环境不稳固、误操作等等。因为交易业务的敏感性,既不能让用户多付钱,也不能少付、错付。因而须要全面思考各种可能呈现的状况,对可能导致订单和领取状态异样的因素做充沛的考量和处理。站在技术的视角,残缺的交易流程如下:
交易重点流程详解
创立订单
创立订单这里指的是创立业务服务端的订单。订单创立实现后,会将业务订单 ID 和 对应的 Google 商品 ID 传递到后续的步骤中。业务服务端会治理保护本身的商品以及订单,这与 Google 的商品和订单并不相同,且须要建设和保护它们之间的关联关系。
建设连贯
在进行 Google 领取前,须要与 Google Play 建设连贯,连贯的桥梁是 BillingClient。BillingClient 是 Google Play Billing Library 提供的重要工具,领取相干的操作都与之无关。假使因为网络等起因连贯断开,必须要重连能力持续后续操作。
查问商品
对于一个商品,须要提前在 Google 后盾实现创立。查问商品是查问 Google 后盾配置的商品信息,确认商品的信息无误,获得商品详情,为发动领取提供必要的数据。
发动领取
发动领取是通过 BillingClient 的 API launchBillingFlow()
调用 Google 的领取界面,此时对应的 Google 领取的服务端订单被创立。业务服务端订单和 Google 服务端订单需关联起来,惯例的形式是客户端在后续发动订单校验时,告知业务服务端对应 Google 订单 ID,事实中客户端可能因为某些起因没有收到领取后果,在由 Google 开发者实时告诉回调业务服务端的场景下,同样须要足够的信息来关联。在这里将业务服务端 ID 通过混同的形式传入,目标是使得业务服务端凭借混同 ID 将 Google 订单和业务服务端订单关联起来,实现后续的确认和履约。
订单确认
收到领取胜利的回调后,客户端被动调用 BillingClient 提供的 consumeAsync()
办法,确保订单已确认不被退款,并且可再次被购买。对于未确认的订单,Google 会在三天后主动退款。此外,须要被动向业务服务端发动订单的查看。
订单履约
依照流程,客户端发动订单的校验,服务端确认了订单的无效无误后须要进行履约,业务上通常体现为发放金币、余额减少等。服务端必须保障此操作的 幂等性,履约一次且仅且履约一次,这是订单弥补机制的前提。
订单弥补
一个领取的流程可能被打断,须要重点关注的是是用户领取实现然而没有收到权利的状况,此过程容易造成客诉,须要通过 订单弥补 作为兜底。
订单弥补分为两个场景,一个是业务服务端通过 Google 提供的实时开发者告诉,接管到 Pub/Sub 音讯,得悉订单领取状态发生变化,此时查看订单的状态,若是未履约态,会进行履约保障用户的权利,并且调用服务端 acknowledge API 确认订单,确保不呈现主动退款而造成资损的状况。
另一个场景是由客户端被动发动订单弥补机制,在适合的机会,获取已领取但未确认的订单进行后续的 Consume 和履约过程。被动弥补能够在启动 App、进入充值购买页面等多个机会进行,可依据业务场景自行决定。同时也会将第一种场景逻辑欠缺,第一个场景下服务端 Acknowledge 实现,权利失去发放,然而该商品却无奈再次购买,此时由客户端实现 Consume,使得商品可反复购买,造成整体逻辑的闭环。
技术实现
交易流程的一个重要的特点是事件驱动。在技术上体现为,须要在大量的回调办法中决定下一步的操作。依照面向过程的形式,代码会层层回调嵌套。这样的代码逻辑不够清晰、难以了解,有数的回调会导致异样解决简单,排查问题也比拟艰难。为了解决这个问题,能够将整个交易流程看成是一个 pipeline,将每一步形象成子模块。
Google Play Billing Library 间接提供的回调是无奈组成 pipeline 的,须要进行一层转换。Kotlin Coroutine 提供的 CallbackFlow 是一个 Flow 构建器,能够将基于回调的 API 转换成 Flow。
逻辑封装
整个交易的流程拆分层若干的子模块,使得子模块是可连接和可复用的。每个子模块有特定的输出和输入,上一个子模块的输入是下一个子模块的输出,例如查问商品子模块的输入是商品详情信息,而商品详情作为发动领取的输出,展现在输出面板上。并且,每个子模块需保障本身逻辑内聚,仅关怀以后流程须要实现的工作,不关怀接下来的流程。
咱们将拆分后的独立的子模块包装成 CallbackFlow。操作胜利后通过 offer 办法在协程外发送数据到后续的 Flow 中,呈现谬误能够终止 close() 或勾销 cancel() 以后 Flow。拆分的颗粒度由操作和回调独特决定,准则是模块性能繁多且内聚。此外,在整个操作过程中,都须要用到 BillingClient 的实例,输出时将传递该实例。
fun queryPurchasesFlow(client: BillingClient?): Flow<List<Purchase>> =
callbackFlow {
client?.queryPurchasesAsync(BillingClient.SkuType.INAPP) { p0, p1 ->
when (p0.responseCode) {
BillingClient.BillingResponseCode.OK -> {
// emit the value to the flow
offer(p1)
}
else -> {
// close the flow
close()}
}
}
awaitClose {// log & release resources}
}
pipeline 组建
在实现单个操作的封装后,须要将这些 Flow 组装起来。CallbackFlow 提供了操作符用于串联和转换 Flow,其中 flatMapConcat 是对上游的元素进行转换、拍扁并返回新的 Flow。flatMapConcat 是实用于以后场景的。一个串联的例子如下,通过建设 Google 连贯后,获取商品的信息,校验无误后调用领取面板。
startConnectionFlow(client).flatMapConcat {querySkuDetailFlow(client, request)
}.flatMapConcat {launchBillingFlow(activity, client, it, request)
}.catch { e ->
// catch exception
e.printStackTrace()}.collect {processNext(it)
}
此外,还能够灵便的编配 Flow,以实现不同的业务逻辑。订单弥补流程和失常的领取流程不完全相同,须要从新编排对应的 Flow,雷同的流程能够与领取逻辑复用,无需从新开发。
整体设计
Google 领取作为一个外围业务组件提供给 App 的其余模块应用,其特点是接入不便、接口简略。其组件的架构如下:
产品层:领取的调用方能够是不同状态的收银台,Web 收银台和 Native 收银台。
接口层:领取组件对外提供接口简略,领取发动和订单弥补。
管理层:控制器下有数据管理和连贯治理。数据管理是治理订单和商品相干的数据。连贯治理是治理 Google 提供的 BillingClient,在适合的机会断开连接。
核心层:核心层蕴含业务的外围逻辑,包含建设连贯、获取商品等。并且蕴含领取模块的日志与监控。
撑持层:依靠于 App 现有的底层根底性能。
踩坑总结
获取商品详情为空
billingClient.querySkuDetailsAsync(params) { p0, p1 ->
when (p0.responseCode) {
BillingClient.BillingResponseCode.OK -> {// p1 is empty}
}
}
在晚期调试的过程中,呈现通过 google sku id 应用 querySkuDetailsAsync() 获取到商品信息为空的状况,通过排查找到了几个影响点。
- 须要 公布内测版本,依据 Google Console 的要求公布一个内测版本,无需期待审核通过。
- 商品创立存在肯定的提早,在从未胜利调试领取的初期,会呈现短暂的提早,即创立商品的当下无奈获取,距离一段时间后胜利获取。这个状况后续未呈现。
- 包名、签名不匹配,须要应用上传 PlayStore 配置的包名和签名,不需应用提交的同一个包,然而包名和签名肯定要匹配。
无奈购买您要买的商品
呈现无奈购买您要买的商品提醒,可能的起因有:
- 查看测试用户是否在 内测用户 列表和 许可测试 列表。
- 须要登录内测的账号,点击 承受测试邀请链接,承受邀请。
必要条件
胜利实现一次领取,总结起来须要留神以下这些点。
- 测试机的 Google Services Framework 已装置。
- Google PlayStore 检测 IP 非国区。依赖于网络环境,也可在 Google PlayStore 设置国家(不是必选项)。
- 测试账号已增加到谷歌后盾,包含 内测用户 增加和 许可测试 增加,两者都须要增加。
- 承受测试邀请,找到内测链接,点击 承受内测邀请,这一步很重要,容易脱漏。
- 公布 内测版本,利用公布状态,无需期待审核通过。
- 安装包签名和包名 与提交到 Google 后盾的签名 统一。
- Google PlayStore 的 版本更新。
最初
交易业务的特殊性和重要性显而易见。无论是保障 App 的营收,还是精确执行用户的交易志愿,防止客诉,都要求交易业务尽可能把各种场景思考欠缺。对开发者来说,不仅要对整个下单、领取的业务流程了然于心,还要通过技术手段,将业务流程形象,将复杂多变的业务场景和逻辑分支,实现为灵便的编排,进步零碎整体的可维护性、健壮性。
本文首先对交易流程进行简明扼要的介绍,之后借助 Kotlin Coroutine 的 CallbackFlow 来实现业务链路的灵便编排,将层层嵌套的回调和事件告诉转为流式、线性的写法。但即便如此,还是不能笼罩到实在场景中的全副异常情况,除了订单弥补的机制外,还须要通过自动化、智能化的伎俩,剖析每一个交易失败的 case,并建设齐备的监控和报警机制,一直总结进步,这是后续须要继续投入的中央。
受限于篇幅,不能将每一个点进行详尽的介绍,欢送读者留言探讨。
参考链接
- Google Play’s billing system overview
- Integrate the Google Play Billing Library into your app
- play-billing-samples
- Is it possible acknowledge consumable products from server side?
- 图解 Monad
- Functors, Applicatives, And Monads In Pictures
- CompletableFuture
- ListenableFutureExplained
- Kotlin flows on Android – Convert callback-based APIs to flows
- Simplifying APIs with coroutines and Flow
本文公布自网易云音乐技术团队,文章未经受权禁止任何模式的转载。咱们长年招收各类技术岗位,如果你筹备换工作,又恰好喜爱云音乐,那就退出咱们 grp.music-fe(at)corp.netease.com!