You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

迁移至Play Billing Library 6时setOfferToken触发空指针异常求助

迁移至Play Billing Library 6后订阅功能无法正常工作

我正在迁移至Play Billing Library 6,应用内购买功能已成功实现,但订阅功能无法正常工作。订阅功能必须使用setOfferToken参数,我的代码如下:

class AccessAllActivity: AppCompatActivity() {
    private lateinit var binding: ActivityAccessAllBinding
    private lateinit var billingClient: BillingClient
    private lateinit var productDetails: ProductDetails

    private val onBackPressedCallback: OnBackPressedCallback = object: OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            backPress()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
        binding = ActivityAccessAllBinding.inflate(layoutInflater)
        setContentView(binding.root)

        billingClient = BillingClient.newBuilder(this)
            .setListener(purchasesUpdatedListener)
            .enablePendingPurchases()
            .build()
        billingClient.startConnection(object: BillingClientStateListener {
            override fun onBillingSetupFinished(billingResult: BillingResult) {
                if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
                    Log.d(TAG, "Billing response OK")
                    queryPurchases()
                    queryAvailableProducts()
                } else {
                    Log.e(TAG, billingResult.debugMessage)
                }
            }
            override fun onBillingServiceDisconnected() {
                Log.i(TAG, "Billing connection disconnected")
            }
        })

        binding.btnClose.setOnClickListener {
            backPress()
        }

        binding.btnSubscribeNow.setOnClickListener {
            startSub()
        }
    }

    fun queryPurchases() {
        if (!billingClient.isReady) {
            Log.e(TAG, "queryPurchases: BillingClient is not ready")
        }
        billingClient.queryPurchasesAsync(
            QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.SUBS).build()
        ) { billingResult, _ ->
            if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
                /*if (purchaseList.isNotEmpty()) {
                    //_purchases.value = purchaseList
                } else {
                    //_purchases.value = emptyList()
                }*/
            } else {
                Log.e(TAG, billingResult.debugMessage)
            }
        }
    }

    private fun queryAvailableProducts() {
        val subProductList =
            listOf(
                Product.newBuilder()
                    .setProductId("subscription")
                    .setProductType(BillingClient.ProductType.SUBS)
                    .build(),
            )
        val subParams = QueryProductDetailsParams.newBuilder().setProductList(subProductList).build()
        billingClient.queryProductDetailsAsync(subParams) { billingResult, productDetailsList ->
            if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && productDetailsList.isNotEmpty()) {
                val appSubProduct = productDetailsList.find { p-> p.productId == "subscription" }
                if (appSubProduct != null) {
                    startSub()
                }
            }
        }
    }

    private fun startSub() {
        if (!productDetails.subscriptionOfferDetails.isNullOrEmpty()) {
            val productDetailsParamsList =
                listOf(
                    ProductDetailsParams.newBuilder()
                        .setProductDetails(productDetails)
                        .setOfferToken(productDetails.subscriptionOfferDetails!!.first().offerToken)
                        .build()
                )
            val billingFlowParams = BillingFlowParams.newBuilder()
                .setProductDetailsParamsList(productDetailsParamsList)
                .build()
            billingClient.launchBillingFlow(this, billingFlowParams)
        } else {
            println("Check productDetails, because is null")
        }
    }

    private val purchasesUpdatedListener = PurchasesUpdatedListener { billingResult, purchases ->
        if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
            for (purchase in purchases) {
                handlePurchase(purchase)
            }
        } else if (billingResult.responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
            Toast.makeText(this, string.payment_cancelled, Toast.LENGTH_LONG).show()
        } else {
            Toast.makeText(this, string.payment_error, Toast.LENGTH_LONG).show()
            Log.i(TAG, "Billing service error ${billingResult.responseCode}")
        }
    }

    private fun handlePurchase(purchase: Purchase) {
        if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
            val consumeParams = ConsumeParams.newBuilder()
                .setPurchaseToken(purchase.purchaseToken)
                .build()
            billingClient.consumeAsync(consumeParams) { _, _ ->
                finish()
            }
        } else if (purchase.purchaseState == Purchase.PurchaseState.PENDING) {
            Toast.makeText(this, string.payment_error, Toast.LENGTH_LONG).show()
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        billingClient.endConnection()
    }

    private fun backPress() {
        finish()
    }

    companion object {
        private const val TAG = "ActivityAccessAll"
    }
}

我已查阅官方文档及StackOverflow上的相关解答,但仍无法定位问题所在,恳请各位提供帮助。


内容的提问来源于stack exchange,提问作者Lanuwapangla

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 20:54:54