前言
之前閱讀過一篇 通過AIDL方式集成Google支付的文檔绑青,想看的适室,可以去閱讀一下。但是呢?這個(gè)文檔只能適用于API3.0以前的版本,不適用于新版的API叽奥,所以還是不推薦去閱讀。新的API已經(jīng)不再支持AIDL的方式痛侍。所以現(xiàn)在來說說現(xiàn)在新的集成方式朝氓,準(zhǔn)備工作這邊就不做介紹,有需要的可以參考一下官方Google支付文檔主届,該文檔主要講解的是Google 支付的訂閱功能赵哲。
準(zhǔn)備工作
1.準(zhǔn)備Google Console 官方開發(fā)者賬號(hào)
2.準(zhǔn)備支持Google 服務(wù),帶Google Play商店的手機(jī)
首先
準(zhǔn)備工作完成后君丁,這時(shí)候需要在Google Play Console 官方配置一下商品信息誓竿,同時(shí)在Google Play console頁面添加一下沙盒測(cè)試,推薦先使用沙盒測(cè)試谈截,然后再通過真實(shí)環(huán)境測(cè)試筷屡。
注:
* 商品信息配置的時(shí)候,一旦商品生效后簸喂,將無法被刪除毙死,但是可以修改
* 從未上傳Google 的App,上傳內(nèi)部測(cè)試時(shí)間幾乎和上正式版的時(shí)間差不多
* 添加測(cè)試喻鳄,在應(yīng)用外扼倘,"許可測(cè)試"里需要添加人員和在內(nèi)部測(cè)試?yán)锾砑訙y(cè)試人員
支付流程
引入Google Play支付的包
implementation "com.android.billingclient:billing-ktx:3.0.1"
1.初始化 BillingClient
初始化一下支付方法,在初始化里添加一個(gè)監(jiān)聽,主要是用于接收應(yīng)用中所有交易的更新以及當(dāng)用戶支付完成后再菊,刷新一個(gè)購買信息狀態(tài)
private fun initBillingClient() {
Log.d(TAG, "startDataSourceConnections")
if (billingClient == null) {
billingClient = BillingClient.newBuilder(context)
.enablePendingPurchases()
.setListener { billingResult, purchases ->
onPurchasesUpdated(billingResult, purchases)
}
.build()
}
connectToPlayBillingService()
}
2.與 Google Play 建立連接
連接到 Google Play 的過程是異步操作爪喘,所以是需要使用BillingClientStateListener監(jiān)聽,確保能夠成功的連接到Google Play
private fun connectToPlayBillingService(): Boolean {
Log.d(TAG, "connectToPlayBillingService")
if (billingClient?.isReady != true) {
billingClient?.startConnection(object : BillingClientStateListener {
override fun onBillingServiceDisconnected() {
Log.d(TAG, "onBillingServiceDisconnected")
connectToPlayBillingService()
}
override fun onBillingSetupFinished(billingResult: BillingResult) {
billingSetup(billingResult)
}
})
return true
}
return false
}
3.與Google Play連接結(jié)束
連接結(jié)束后纠拔,Google Play將會(huì)返回BillingResponseCode 官方文檔秉剑。連接成功后,展示當(dāng)前可以購買的商品(即商品信息)稠诲,消耗掉上一次購買操作未消耗的商品(也叫確認(rèn)交易)侦鹏。
注:連接成功后,必須要核銷一下商品臀叙,否則會(huì)導(dǎo)致商品購買不成功
fun billingSetup(billingResult: BillingResult) {
when (billingResult.responseCode) {
BillingClient.BillingResponseCode.OK -> {
Log.d(TAG, "onBillingSetupFinished successfully")
querySkuDetailsAsync(BillingClient.SkuType.SUBS, skuS)
queryPurchasesHistory()
}
BillingClient.BillingResponseCode.BILLING_UNAVAILABLE -> {
Log.d(TAG, billingResult.debugMessage)
}
else -> {
Log.d(TAG, billingResult.debugMessage)
}
}
}
4.展示可供購買的商品
根據(jù)SkuType 官方文檔類型來查詢對(duì)應(yīng)的商品詳情略水,查詢的結(jié)果將會(huì)返回本地化商品信息(即將查詢的信息賦值到本地的List)。
請(qǐng)求成功后劝萤,調(diào)用一下invoke方法渊涝,它是kotlin的中一個(gè)函數(shù),可能換個(gè)寫法床嫌,這個(gè)函數(shù)可能看起來比較懵驶赏,那就換個(gè)寫法,它就相當(dāng)于onSkuDetails(skuDetailsList)
注:在配置應(yīng)用內(nèi)商品時(shí)創(chuàng)建的唯一商品 ID 將用于向 Google Play 異步查詢應(yīng)用內(nèi)商品詳情
private fun querySkuDetailsAsync(
@BillingClient.SkuType skuType: String,
skuList: List<String>
) {
val params = SkuDetailsParams.newBuilder()
.setSkusList(skuList)
.setType(skuType)
.build()
Log.d(TAG, "querySkuDetailsAsync for $skuType")
billingClient?.querySkuDetailsAsync(params) { billingResult, skuDetailsList ->
when (billingResult.responseCode) {
BillingClient.BillingResponseCode.OK -> {
if (skuDetailsList.orEmpty().isNotEmpty()) {
if (skuDetailsList != null) {
onSkuDetails?.invoke(skuDetailsList)
}
}
}
else -> {
Log.d(TAG, billingResult.debugMessage)
}
}
}
}
5.啟動(dòng)購買流程
從支付按鈕發(fā)起請(qǐng)求既鞠,調(diào)用成功后,頁面將會(huì)顯示Google Play支付頁面盖文,即如圖G1:
無法調(diào)起以下頁面原因:
1.該手機(jī)不支持Google服務(wù)
2.該手機(jī)未安裝Google服務(wù)三件套
3.Google Play商店“后臺(tái)彈出界面”的權(quán)限被關(guān)閉
4.Google Play商店獲取手機(jī)信息權(quán)限被關(guān)閉了
區(qū)別:
這里和舊的API是不一樣的嘱蛋,舊的API,在這里是使用AIDL的方法五续,將userId綁定到商品信息里洒敏。
新API是可以直接通過setObfuscatedAccountId(String)的方法,將userId綁定到對(duì)應(yīng)的商品信息內(nèi)疙驾。
fun launchBillingFlow(activity: Activity, skuDetails: SkuDetails, userId: String) {
val purchaseParams = BillingFlowParams
.newBuilder()
.setObfuscatedAccountId(userId)
//skuDetails對(duì)應(yīng)的商品
.setSkuDetails(skuDetails)
.build()
Log.d(TAG, "launchBillingFlow ${skuDetails.originalJson}")
billingClient?.launchBillingFlow(activity, purchaseParams)
}
6.確認(rèn)購買,支付
Google Play在支付完成后會(huì)再調(diào)用onPurchasesUpdated()凶伙,然后會(huì)將結(jié)果通過接口返回,用戶支付成功后它碎,Google Play將會(huì)生成購買令牌(唯一標(biāo)識(shí))函荣,同時(shí)用戶將會(huì)收到對(duì)應(yīng)訂單的郵件,訂單ID(退款需要)扳肛,以及扣款通知傻挂,同時(shí)也可以在Google Play商店看到對(duì)應(yīng)的訂單。BillingResponseCode 官方文檔
private fun onPurchasesUpdated(
billingResult: BillingResult,
purchases: MutableList<Purchase>?
) {
Log.d(TAG, "onPurchasesUpdated ${billingResult.responseCode} ${billingResult.debugMessage}")
when (billingResult.responseCode) {
BillingClient.BillingResponseCode.OK -> {
purchases?.apply {
for (purchase in purchases) {
handlePurchase(purchase)
}
}
}
BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED -> {
//購買失敗挖息,因?yàn)樵撐锲芬驯粨碛薪鹁埽撋唐沸枰幌? Log.d(TAG, billingResult.debugMessage)
queryPurchasesHistory()
}
BillingClient.BillingResponseCode.SERVICE_DISCONNECTED -> {
//購買失敗,Google服務(wù)斷開套腹,可能網(wǎng)絡(luò)原因
connectToPlayBillingService()
}
else -> {
Log.d(TAG, billingResult.debugMessage)
}
}
}
做到這一步绪抛,你是不是感覺已經(jīng)流程已經(jīng)結(jié)束了资铡,這邊可以準(zhǔn)確告訴你,它還沒有結(jié)束幢码,它還需要調(diào)用一個(gè)消耗交易操作(即確認(rèn)交易)笤休,如果沒有這一步,正常用戶會(huì)在3天內(nèi)退款蛤育,并且取消這一筆訂單宛官,測(cè)試用戶會(huì)在5分鐘內(nèi)取消訂單。
7.處理購買交易(消耗瓦糕,確認(rèn)交易)
在確認(rèn)交易之前要判斷一下PurchaseState 官方文檔底洗,當(dāng)購買交易只有處于PURCHASED的狀態(tài)下,才可以確認(rèn)交易咕娄,不在此狀態(tài)下的交易亥揖,是沒有辦法進(jìn)行確認(rèn)操作的
處理交易方式:
1.驗(yàn)證當(dāng)前購買交易
2.提供內(nèi)容給用戶,并且將"確認(rèn)內(nèi)容"傳送給用戶,并且標(biāo)志成已消費(fèi)
注:如果您在三天內(nèi)未確認(rèn)購買交易圣勒,用戶會(huì)自動(dòng)收到退款费变,并且 Google Play 會(huì)撤消該購買交易
Api2.0以后都是需要確認(rèn)交易,2.0以前是不需要消耗的
private fun handlePurchase(purchase: Purchase) {
Log.d(TAG, "handlePurchase ${purchase} isAcknowledged: ${purchase.isAcknowledged}")
if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
GlobalScope.launch(Dispatchers.Main) {
if (!purchase.isAcknowledged) {
val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
withContext(Dispatchers.IO) {
billingClient?.acknowledgePurchase(acknowledgePurchaseParams.build()) {}
}
}
}
}
}
8.提取購買交易(查詢購買交易圣贸,也可以叫恢復(fù)購買)
當(dāng)用戶進(jìn)入App的時(shí)候挚歧,建議在在onResume()和onCreate()調(diào)用此方法,主要用于處理用戶在App以外購買成功吁峻,為什么這么說滑负?因?yàn)橛嗛営脩羰强梢栽贕oogle Play商店里,進(jìn)行續(xù)訂用含,購買等操作的矮慕,但是購買成功后,同事也會(huì)提示用戶需要重新回到App啄骇,進(jìn)行確認(rèn)交易的操作步驟痴鳄。
存在漏單多種原因:
1.在購買過程中出現(xiàn)網(wǎng)絡(luò)問題,收到購買交易的通知之前失去了網(wǎng)絡(luò)連接
2.多臺(tái)設(shè)備:用戶在一臺(tái)設(shè)備上購買了一件商品,然后在切換設(shè)備時(shí)看到該商品
3.處理在您的應(yīng)用外進(jìn)行的購買交易(在App外交易)
private fun queryPurchasesHistory() {
val result = billingClient?.queryPurchases(BillingClient.SkuType.SUBS)
result?.purchasesList?.apply {
forEach {
handlePurchaseHistory(it)
}
}
Log.d(TAG,"queryPurchasesHistory results:${result?.purchasesList} size: ${result?.purchasesList?.size} "
)
}
9.調(diào)用消耗方法
調(diào)用方法之前缸夹,先判斷一下該訂單是否處于可被消耗的狀態(tài)痪寻,在該訂單消耗之前,將purchaseToken發(fā)送給后端虽惭。
private fun handlePurchaseHistory(purchase: Purchase) {
Log.d(
TAG,
"handlePurchaseHistory $purchase handlePurchase.purchaseState: ${purchase.purchaseState} isAcknowledged: ${purchase.isAcknowledged}"
)
if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
GlobalScope.launch(Dispatchers.Main) {
if (!purchase.isAcknowledged) {
// 向后端發(fā)送purchaseToken
·····
withContext(Dispatchers.IO) {
val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
billingClient?.acknowledgePurchase(acknowledgePurchaseParams.build()) {}
}
}
}
}
}
總結(jié)
Google 支付看似流程簡(jiǎn)單槽华,但是實(shí)際上細(xì)節(jié)方面比較稍微有一點(diǎn)難處理,但是如果一點(diǎn)點(diǎn)捋下來的話s 趟妥,發(fā)現(xiàn)邏輯其實(shí)也不是很復(fù)雜猫态,但是比較折磨人。