Skip to content

Commit

Permalink
Merge pull request #155 from AhmadKadour/improvements
Browse files Browse the repository at this point in the history
Adding onPurchaseFailed callback, Fixing onProductPurchased not called immediately.
  • Loading branch information
akshaaatt authored Jan 12, 2024
2 parents 7be9be3 + 6ef1486 commit cfa998a
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 2 deletions.
10 changes: 10 additions & 0 deletions app/src/main/java/com/limurse/iapsample/JavaSampleActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
Expand Down Expand Up @@ -71,6 +72,11 @@ public void onProductPurchased(@NonNull DataWrappers.PurchaseInfo purchaseInfo)
public void onProductRestored(@NonNull DataWrappers.PurchaseInfo purchaseInfo) {

}

@Override
public void onPurchaseFailed(@Nullable DataWrappers.PurchaseInfo purchaseInfo, @Nullable Integer billingResponseCode) {
Toast.makeText(getApplicationContext(), "Your purchase has been failed", Toast.LENGTH_SHORT).show();
}
});
iapConnector.addSubscriptionListener(new SubscriptionServiceListener() {
public void onSubscriptionRestored(@NonNull DataWrappers.PurchaseInfo purchaseInfo) {
Expand All @@ -85,6 +91,10 @@ public void onSubscriptionPurchased(@NonNull DataWrappers.PurchaseInfo purchaseI
public void onPricesUpdated(@NotNull Map iapKeyPrices) {

}

@Override
public void onPurchaseFailed(@Nullable DataWrappers.PurchaseInfo purchaseInfo, @Nullable Integer billingResponseCode) {
}
});

binding.btPurchaseCons.setOnClickListener(it ->
Expand Down
10 changes: 10 additions & 0 deletions app/src/main/java/com/limurse/iapsample/KotlinSampleActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.limurse.iapsample

import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.MutableLiveData
import com.limurse.iap.BillingClientConnectionListener
Expand Down Expand Up @@ -77,6 +78,11 @@ class KotlinSampleActivity : AppCompatActivity() {
override fun onProductRestored(purchaseInfo: DataWrappers.PurchaseInfo) {
// will be triggered fetching owned products using IapConnector;
}

override fun onPurchaseFailed(purchaseInfo: DataWrappers.PurchaseInfo?, billingResponseCode: Int?) {
// will be triggered whenever a product purchase is failed
Toast.makeText(applicationContext, "Your purchase has been failed", Toast.LENGTH_SHORT).show()
}
})

iapConnector.addSubscriptionListener(object : SubscriptionServiceListener {
Expand All @@ -99,6 +105,10 @@ class KotlinSampleActivity : AppCompatActivity() {
override fun onPricesUpdated(iapKeyPrices: Map<String, List<DataWrappers.ProductDetails>>) {
// list of available products will be received here, so you can update UI with prices if needed
}

override fun onPurchaseFailed(purchaseInfo: DataWrappers.PurchaseInfo?, billingResponseCode: Int?) {
// will be triggered whenever subscription purchase is failed
}
})

isBillingClientConnected.observe(this) {connected->
Expand Down
14 changes: 12 additions & 2 deletions iap/src/main/java/com/limurse/iap/BillingService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ class BillingService(
val responseCode = billingResult.responseCode
val debugMessage = billingResult.debugMessage
log("onPurchasesUpdated: responseCode:$responseCode debugMessage: $debugMessage")
if (!billingResult.isOk()){
updateFailedPurchases(purchases?.map { getPurchaseInfo(it) }, responseCode)
}
when (responseCode) {
BillingClient.BillingResponseCode.OK -> {
log("onPurchasesUpdated. purchase: $purchases")
Expand Down Expand Up @@ -184,16 +187,18 @@ class BillingService(
if (purchaseSuccess && purchase.products[0].isProductReady()) {
if (!isSignatureValid(purchase)) {
log("processPurchases. Signature is not valid for: $purchase")
updateFailedPurchase(getPurchaseInfo(purchase))
continue@purchases
}

// Grant entitlement to the user.
val productDetails = productDetails[purchase.products[0]]
val isProductConsumable = consumableKeys.contains(purchase.products[0])
when (productDetails?.productType) {
BillingClient.ProductType.INAPP -> {
// Consume the purchase
when {
consumableKeys.contains(purchase.products[0]) -> {
isProductConsumable && purchase.purchaseState == Purchase.PurchaseState.PURCHASED -> {
mBillingClient.consumeAsync(
ConsumeParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken).build()
Expand All @@ -207,6 +212,7 @@ class BillingService(
TAG,
"Handling consumables : Error during consumption attempt -> ${billingResult.debugMessage}"
)
updateFailedPurchase(getPurchaseInfo(purchase), billingResult.responseCode)
}
}
}
Expand All @@ -222,7 +228,7 @@ class BillingService(
}

// If the state is PURCHASED, acknowledge the purchase if it hasn't been acknowledged yet.
if (!purchase.isAcknowledged && purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
if (!purchase.isAcknowledged && !isProductConsumable && purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken).build()
mBillingClient.acknowledgePurchase(acknowledgePurchaseParams, this)
Expand All @@ -232,6 +238,7 @@ class BillingService(
TAG, "processPurchases failed. purchase: $purchase " +
"purchaseState: ${purchase.purchaseState} isSkuReady: ${purchase.products[0].isProductReady()}"
)
updateFailedPurchase(getPurchaseInfo(purchase))
}
}
} else {
Expand Down Expand Up @@ -383,6 +390,9 @@ class BillingService(

override fun onAcknowledgePurchaseResponse(billingResult: BillingResult) {
log("onAcknowledgePurchaseResponse: billingResult: $billingResult")
if(!billingResult.isOk()){
updateFailedPurchase(billingResponseCode = billingResult.responseCode)
}
}

override fun close() {
Expand Down
8 changes: 8 additions & 0 deletions iap/src/main/java/com/limurse/iap/BillingServiceListener.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,12 @@ interface BillingServiceListener {
* @param iapKeyPrices - a map with available products
*/
fun onPricesUpdated(iapKeyPrices: Map<String, List<DataWrappers.ProductDetails>>)

/**
* Callback will be triggered when a purchase was failed.
*
* @param purchaseInfo - specifier of purchase info if exists
* @param billingResponseCode - response code returned from the billing library if exists
*/
fun onPurchaseFailed(purchaseInfo: DataWrappers.PurchaseInfo?, billingResponseCode: Int?)
}
21 changes: 21 additions & 0 deletions iap/src/main/java/com/limurse/iap/IBillingService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,27 @@ abstract class IBillingService {
}
}

fun updateFailedPurchases(purchaseInfo: List<DataWrappers.PurchaseInfo>? = null, billingResponseCode: Int? = null) {
purchaseInfo?.forEach {
updateFailedPurchase(it, billingResponseCode)
} ?: updateFailedPurchase()
}

fun updateFailedPurchase(purchaseInfo: DataWrappers.PurchaseInfo? = null, billingResponseCode: Int? = null) {
findUiHandler().post {
updateFailedPurchasesInternal(purchaseInfo, billingResponseCode)
}
}

private fun updateFailedPurchasesInternal(purchaseInfo: DataWrappers.PurchaseInfo? = null, billingResponseCode: Int? = null) {
for (billingServiceListener in purchaseServiceListeners) {
billingServiceListener.onPurchaseFailed(purchaseInfo, billingResponseCode)
}
for (billingServiceListener in subscriptionServiceListeners) {
billingServiceListener.onPurchaseFailed(purchaseInfo, billingResponseCode)
}
}

abstract fun init(key: String?)
abstract fun buy(activity: Activity, sku: String, obfuscatedAccountId: String?, obfuscatedProfileId: String?)
abstract fun subscribe(activity: Activity, sku: String, obfuscatedAccountId: String?, obfuscatedProfileId: String?)
Expand Down

1 comment on commit cfa998a

@coderGtm
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you give situations when onPurchaseFailed occurs. in google play iap, we get different error codes for failure, like item already owned, user cancelled, etc.

So does this get called in those situations?

Please sign in to comment.