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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 01:17:03