Android Kotlin订阅测试失败求助:点击按钮提示商品未找到
订阅配置测试失败:点击按钮返回“Purchase Item Not Found”
我查过大量类似问题都没能解决,现在测试订阅配置完全无法正常工作。点击按钮后没有任何反应,添加调试信息后发现,点击按钮时InitiatePurchase方法返回“Purchase Item Not Found”。
我已完成以下操作:
- 在app模块的build.gradle中添加依赖库:
implementation 'com.android.billingclient:billing:6.0.1'
- 在AndroidManifest中启用计费权限:
<uses-permission android:name="com.android.vending.BILLING" />
- 按照官方文档在Fragment中实现订阅逻辑:
class PremiumFeaturesFragment : Fragment(), PurchasesUpdatedListener { var purchaseStatus: TextView? = null var purchaseButton: Button? = null private var billingClient: BillingClient? = null override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val rootView = inflater.inflate(R.layout.fragment_premium_features, container, false) purchaseStatus = rootView.findViewById<View>(R.id.purchase_status) as TextView purchaseButton = rootView.findViewById<View>(R.id.purchase_button) as Button purchaseButton!!.setOnClickListener { purchase(rootView) } billingClient = BillingClient.newBuilder(requireContext()) .enablePendingPurchases().setListener(this).build() billingClient!!.startConnection(object : BillingClientStateListener { override fun onBillingSetupFinished(billingResult: BillingResult) { if (billingResult.responseCode == BillingResponseCode.OK) { billingClient!!.queryPurchasesAsync(INAPP) { queryResult, purchases -> if (queryResult.responseCode == BillingResponseCode.OK && purchases != null) { if (purchases.isNotEmpty()) { handlePurchases(purchases) } else { savePurchaseValueToPref(false) } } } } } override fun onBillingServiceDisconnected() {} }) //item Purchased if (purchaseValueFromPref) { purchaseButton!!.visibility = View.GONE purchaseStatus!!.text = "Purchase Status : Purchased" } //item not Purchased else { purchaseButton!!.visibility = View.VISIBLE purchaseStatus!!.text = "Purchase Status : Not Purchased" } return rootView } private val preferenceObject: SharedPreferences get() = requireContext().getSharedPreferences(PREF_FILE, 0) private val preferenceEditObject: Editor get() { val pref: SharedPreferences = requireContext().getSharedPreferences(PREF_FILE, 0) return pref.edit() } private val purchaseValueFromPref: Boolean get() = preferenceObject.getBoolean(SUBSCRIBE_KEY, false) private fun savePurchaseValueToPref(value: Boolean) { preferenceEditObject.putBoolean(SUBSCRIBE_KEY, value).commit() } //initiate purchase on button click fun purchase(view: View?) { //check if service is already connected if (billingClient!!.isReady) { initiatePurchase() } //else reconnect service else { billingClient = BillingClient.newBuilder(requireContext()).enablePendingPurchases().setListener(this).build() billingClient!!.startConnection(object : BillingClientStateListener { override fun onBillingSetupFinished(billingResult: BillingResult) { if (billingResult.responseCode == BillingResponseCode.OK) { initiatePurchase() } else { Toast.makeText(requireContext(), "Error " + billingResult.debugMessage, Toast.LENGTH_SHORT).show() } } override fun onBillingServiceDisconnected() {} }) } } private fun initiatePurchase() { println("initating purchase entered") val skuList: MutableList<String> = ArrayList() skuList.add(PRODUCT_ID) val params = SkuDetailsParams.newBuilder() params.setSkusList(skuList).setType(INAPP) billingClient!!.querySkuDetailsAsync(params.build()) { billingResult, skuDetailsList -> if (billingResult.responseCode == BillingResponseCode.OK) { println("initating purchase entered 2") if (skuDetailsList != null && skuDetailsList.size > 0) { println("initating purchase entered 3") val flowParams = BillingFlowParams.newBuilder() .setSkuDetails(skuDetailsList[0]) .build() billingClient!!.launchBillingFlow(requireActivity(), flowParams) } else { println("initating purchase entered 4") //try to add item/product id "purchase" inside managed product in google play console Toast.makeText(requireContext(), "Purchase Item not Found", Toast.LENGTH_SHORT).show() } } else { println("initating purchase entered 5") Toast.makeText(requireContext(), " Error " + billingResult.debugMessage, Toast.LENGTH_SHORT).show() } } } override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) { // If item newly purchased if (billingResult.responseCode == BillingResponseCode.OK && purchases != null) { handlePurchases(purchases) } //if item already purchased then check and reflect changes else if (billingResult.responseCode == BillingResponseCode.ITEM_ALREADY_OWNED) { billingClient!!.queryPurchasesAsync(INAPP) { _, alreadyPurchases -> if (alreadyPurchases != null) { handlePurchases(alreadyPurchases) } } } //if purchase cancelled else if (billingResult.responseCode == BillingResponseCode.USER_CANCELED) { Toast.makeText(requireContext(), "Purchase Canceled", Toast.LENGTH_SHORT).show() } // Handle any other error msgs else { Toast.makeText(requireContext(), "Error " + billingResult.debugMessage, Toast.LENGTH_SHORT).show() } } fun handlePurchases(purchases: List<Purchase>) { for (purchase in purchases) { //if item is purchased if ((purchase.skus.contains(PRODUCT_ID)) && purchase.purchaseState == PurchaseState.PURCHASED) { if (!verifyValidSignature(purchase.originalJson, purchase.signature)) { // Invalid purchase // show error to user Toast.makeText(requireContext(), "Error : Invalid Purchase", Toast.LENGTH_SHORT).show() return } // else purchase is valid //if item is purchased and not acknowledged if (!purchase.isAcknowledged) { val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder() .setPurchaseToken(purchase.purchaseToken) .build() billingClient!!.acknowledgePurchase(acknowledgePurchaseParams, ackPurchase) } //else item is purchased and also acknowledged else { // Grant entitlement to the user on item purchase // restart activity if (!purchaseValueFromPref) { savePurchaseValueToPref(true) Toast.makeText(requireContext(), "Item Purchased", Toast.LENGTH_SHORT).show() requireActivity().recreate() } } } //if purchase is pending else if ((purchase.skus.contains(PRODUCT_ID)) && purchase.purchaseState == PurchaseState.PENDING) { Toast.makeText(requireContext(), "Purchase is Pending. Please complete Transaction", Toast.LENGTH_SHORT).show() } //if purchase is refunded or unknown else if ((purchase.skus.contains(PRODUCT_ID)) && purchase.purchaseState == PurchaseState.UNSPECIFIED_STATE) { savePurchaseValueToPref(false) purchaseStatus!!.text = "Purchase Status : Not Purchased" purchaseButton!!.visibility = View.VISIBLE Toast.makeText(requireContext(), "Purchase Status Unknown", Toast.LENGTH_SHORT).show() } } } var ackPurchase = AcknowledgePurchaseResponseListener { billingResult -> if (billingResult.responseCode == BillingResponseCode.OK) { //if purchase is acknowledged // Grant entitlement to the user. and restart activity savePurchaseValueToPref(true) Toast.makeText(requireContext(), "Item Purchased", Toast.LENGTH_SHORT).show() requireActivity().recreate() } } /** * Verifies that the purchase was signed correctly for this developer's public key. * * Note: It's strongly recommended to perform such check on your backend since hackers can * replace this method with "constant true" if they decompile/rebuild your app. * */ private fun verifyValidSignature(signedData: String, signature: String): Boolean { return try { // To get key go to Developer Console > Select your app > Development Tools > Services & APIs. val base64Key = "LICENSE KEY FROM GOOGLE PLAY CONSOLE IS PUT HERE (ACTUAL KEY IS HERE IN LIVE CODE)" Security.verifyPurchase(base64Key, signedData, signature) } catch (e: IOException) { false } } override fun onDestroy() { super.onDestroy() if (billingClient != null) { billingClient!!.endConnection() } } companion object { const val PREF_FILE = "MyPref" const val SUBSCRIBE_KEY = "subscribe" const val PRODUCT_ID = "com.ManageIt.manageit.premium" }
- 已在Google Play商店中添加订阅商品:

我已经花费数周时间解决此问题,请问还需要提供哪些信息以获得帮助?
内容的提问来源于stack exchange,提问作者DSTEP
相关产品推荐
相关产品推荐

