购买场景中的应用内购买问题 - Kotlin

时间:2021-01-11 11:10:48

标签: android in-app-purchase in-app-billing android-inapp-purchase acknowledgepurchase

我正在我的 android 应用程序中实施应用内购买演示,以仔细深入地学习所有内容。

现在,为此,我在 Google Play 控制台帐户中添加了以下三个产品 ID

  1. consumable_product_coins

  2. nonconsumable_product_basic_videos

  3. nonconsumable_product_prime_videos

现在,我的 xml 包含三个按钮,每个按钮用于购买以上三种产品,我将通过点击特定按钮启动购买流程。

在我的 Java 文件中,我做了如下操作:

class MainActivity : AppCompatActivity(), CoroutineScope, PurchasesUpdatedListener {
private lateinit var billingClient: BillingClient

private val prod_coin = "consumable_product_coin"
private val prod_basic_videos = "nonconsumable_product_basic_videos"
private val prod_prime_videos = "nonconsumable_product_prime_videos"
private var recent_purchase = ""

private var job: Job = Job()
private lateinit var mBtnBuyCoins: Button
private lateinit var mBtnBuyBasicVideos: Button
private lateinit var mBtnBuyPremiumVideos: Button

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    findViews()
    initializePurchaseUpdateListener()
    Toast.makeText(this@MainActivity, "No SKU Details found", Toast.LENGTH_SHORT).show()
}

private fun findViews() {
    mTvDescription = findViewById(R.id.tvDescription)
    mBtnBuyCoins = findViewById(R.id.btnBuyCoins)
    mBtnBuyBasicVideos = findViewById(R.id.btnBuyBasic)
    mBtnBuyPremiumVideos = findViewById(R.id.btnBuyPremium)

    mBtnBuyCoins.setOnClickListener {
        recent_purchase=prod_coin
        querySkuDetails(prod_coin)
    }

    mBtnBuyBasicVideos.setOnClickListener {         
        recent_purchase=prod_basic_videos
        querySkuDetails(prod_basic_videos)
    }

    mBtnBuyPremiumVideos.setOnClickListener {
        recent_purchase=prod_prime_videos
        querySkuDetails(prod_prime_videos)
    }
}

private fun initializePurchaseUpdateListener() {
    val purchasesUpdateListener =
        PurchasesUpdatedListener { billingResult, purchases ->
            // To be implemented in a later section.
        }

    initializeBillingClient(purchasesUpdateListener)
}

private fun initializeBillingClient(purchasesUpdateListener: PurchasesUpdatedListener) {
    billingClient = BillingClient.newBuilder(this@MainActivity)
        .setListener(purchasesUpdateListener)
        .enablePendingPurchases()
        .build()

    establishConnectionForBillingClient()
}

private fun establishConnectionForBillingClient() {
    billingClient.startConnection(object : BillingClientStateListener {
        override fun onBillingSetupFinished(billingResult: BillingResult) {
            if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
                // The BillingClient is ready. You can query purchases here.

            }
        }

        override fun onBillingServiceDisconnected() {
            // Try to restart the connection on the next request to
            // Google Play by calling the startConnection() method.
        }
    })
}

fun querySkuDetails(sku: String) {
    val skuList = ArrayList<String>()
    skuList.add(prod_coin)
    skuList.add(prod_basic_videos)
    skuList.add(prod_prime_videos)

    val params = SkuDetailsParams.newBuilder()
    params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP)
    launch {
        withContext(Dispatchers.IO) {
            billingClient.querySkuDetailsAsync(params.build()) { billingResult, skuDetailsList ->
                // Process the result.
                if (skuDetailsList?.size ?: 0 > 0) {
                    Toast.makeText(
                        this@MainActivity,
                        "TOTAL SKUS >> "+skuDetailsList?.size,
                        Toast.LENGTH_SHORT
                    ).show()


                    for (i in 0 until (skuDetailsList?.size ?: 0)) {
                        Log.e("INSIDE QUERY ", "" + skuDetailsList!![i])
                        if (skuDetailsList!![i].sku.equals(sku)) {
                            val billingFlowParams = BillingFlowParams.newBuilder()
                                .setSkuDetails(skuDetailsList!![i])
                                .build()
                            val responseCode = billingClient.launchBillingFlow(
                                this@MainActivity,
                                billingFlowParams
                            ).responseCode
                        }
                    }
                } else {
                    Toast.makeText(
                        this@MainActivity,
                        "No SKU Details found",
                        Toast.LENGTH_SHORT
                    ).show()
                }
            }
        }
    }
}

override val coroutineContext: CoroutineContext
    get() = Dispatchers.Main + job

override fun onDestroy() {
    super.onDestroy()
    job.cancel()
}

override fun onPurchasesUpdated(
    billingResult: BillingResult,
    purchases: MutableList<Purchase>?
) {
    if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && purchases != null)       {
        Log.e("On Purcahse Updated >> ", "> " + purchases!!.size)
        mTvDescription.text = "Size : " + purchases.size
        for (purchase in purchases) {
            handlePurchase(purchase)
        }

    } else if (billingResult.responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
        // Handle an error caused by a user cancelling the purchase flow.
        Log.e(">>>Purchase CANCELED", " >> P CAN")
    } else {
        // Handle any other error codes.
        Log.e(">>>ERROR > ", " Other Error Occured")
    }
}

fun handlePurchase(purchase: Purchase) {
    if(recent_purchase.equals(prod_coin))
    {
        //for consumable products
        Toast.makeText(this@MainActivity, "Inside handle purcahse", Toast.LENGTH_SHORT).show()
        val consumeParams =
            ConsumeParams.newBuilder()
                .setPurchaseToken(purchase.getPurchaseToken())
                .build()

        Toast.makeText(this@MainActivity,"Purchase Token : >>"+"\n"+purchase.purchaseToken.toString(),Toast.LENGTH_LONG).show()
        billingClient.consumeAsync(consumeParams, { billingResult, outToken ->
            if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
                // Handle the success of the consume operation.
                Toast.makeText(this@MainActivity,"Acknowledgement Successfull",Toast.LENGTH_LONG).show()
            }else{
                Toast.makeText(this@MainActivity,"Acknowledgement FAILS",Toast.LENGTH_LONG).show()
            }
        })
    }else{
        //for nonconsumable products
        if (purchase.purchaseState === Purchase.PurchaseState.PURCHASED) {
            if (!purchase.isAcknowledged) {
                val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.purchaseToken)
                launch{
                    val ackPurchaseResult = withContext(Dispatchers.IO) {
                        billingClient.acknowledgePurchase(acknowledgePurchaseParams.build())
                    }
                }
            }else{
                Toast.makeText(this@MainActivity,"You have already puchased this product",Toast.LENGTH_LONG).show()
            }
        }
    }
}

}

现在,从代码中可以看出product1(硬币)是消耗品,所以我实现了consumeAsync方法。休息一下,实现了acknowledgePurchase

但是,问题是我第一次可以正常购买硬币产品。从第二次开始它向我表明:“您已经拥有此物品” 其余两种产品也发生了同样的情况。

可能是什么问题?

注意:5分钟后我可以再次购买!我没有实施订阅。

1 个答案:

答案 0 :(得分:0)

得到解决方案:

问题在于将 purchaseUpdateListener 传递给 billingClient。

删除下面的代码:

private fun initializePurchaseUpdateListener() {
val purchasesUpdateListener =
    PurchasesUpdatedListener { billingResult, purchases ->
        // To be implemented in a later section.
    }
initializeBillingClient(purchasesUpdateListener)
}

private fun initializeBillingClient(purchasesUpdateListener: PurchasesUpdatedListener) {
    billingClient = BillingClient.newBuilder(this@MainActivity)
        .setListener(purchasesUpdateListener)
        .enablePendingPurchases()
        .build()

    establishConnectionForBillingClient()
}

由于我正在实施 PurchasesUpdatedListener,因此我必须更改以下几行:

billingClient = BillingClient.newBuilder(this@MainActivity)
        .setListener(purchasesUpdateListener)
        .enablePendingPurchases()
        .build()

billingClient = BillingClient.newBuilder(this@MainActivity)
        .setListener(this)
        .enablePendingPurchases()
        .build()

简而言之,我的 onPurchaseUpdate() 方法没有被调用。