购买场景中的应用内购买问题 - Kotlin
In-App Purchase Issue in Purchase Scenario - Kotlin
我正在我的 android 应用程序中实施应用内购买演示,以仔细深入地学习所有内容。
现在,为此,我在 google 游戏机帐户中添加了以下 三个产品 ID:
consumable_product_coins
nonconsumable_product_basic_videos
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(coins)是消耗品,所以实现了consumeAsync
方法。对于休息,实施 acknowledgePurchase
.
但是,问题是我第一次可以正常购买硬币产品。从第二次开始,它向我展示了:“你已经拥有了这个项目”
这两种产品的其余部分也发生了同样的事情。
可能是什么问题?
注意:5分钟后我可以再次购买!我没有实施订阅。
得到解决方案:
问题在于将 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()
方法没有被调用。
我正在我的 android 应用程序中实施应用内购买演示,以仔细深入地学习所有内容。
现在,为此,我在 google 游戏机帐户中添加了以下 三个产品 ID:
consumable_product_coins
nonconsumable_product_basic_videos
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(coins)是消耗品,所以实现了consumeAsync
方法。对于休息,实施 acknowledgePurchase
.
但是,问题是我第一次可以正常购买硬币产品。从第二次开始,它向我展示了:“你已经拥有了这个项目” 这两种产品的其余部分也发生了同样的事情。
可能是什么问题?
注意:5分钟后我可以再次购买!我没有实施订阅。
得到解决方案:
问题在于将 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()
方法没有被调用。