Android-inapp-billing-v3: Billing Library version 3 or newer.

Created on 16 Feb 2021  ·  32Comments  ·  Source: anjlab/android-inapp-billing-v3

Reminder: Starting on August 2, 2021, all new apps must use Billing Library version 3 or newer. By November 1, 2021, all updates to existing apps must use Billing Library version 3 or newer. Learn more.

Most helpful comment

IMO, this library isn't needed anymore. Google Play billing v2 offered a very low-level AIDL, but v3 offers a Kotlin/Java API. The AIDL API required a lot of boilerplate code for handling raw data and making requests. The Kotlin/Java API offered by v3 takes care of that, which is also one of the pain points that this library addressed for v2. Unlike v2, v3 also queries Play Store's cache on querying purchases. A major chunk of this library was dedicated to maintaining this cache.

The v3 library doesn't address verifying purchases. You can copy-paste the Security class for that. Your licenseKey is the public key for verifying purchases. productId, purchaseData and dataSignature are returned by Purchase#getSku(), Purchase#getOriginalJson() and Purchase#getSignature() respectively.

https://github.com/anjlab/android-inapp-billing-v3/blob/23b702219e7449e99ba94ace21cbe2cdb6eb6e8d/library/src/main/java/com/anjlab/android/iab/v3/BillingProcessor.java#L173-L177

https://github.com/anjlab/android-inapp-billing-v3/blob/23b702219e7449e99ba94ace21cbe2cdb6eb6e8d/library/src/main/java/com/anjlab/android/iab/v3/BillingProcessor.java#L1009-L1024

On a side note, you are now required to either acknowledge or consume all purchases. You also need to handle pending purchases that may transition to the purchased state while the user is away from your app.

All 32 comments

Joining the question!
Need release

Joining
Please upgrade

Joining
Please~~

please upgrade.

Same warning. Pls upgrade library

Joining please.

Please upgrade!

@serggl wake up bro

Please update v3 soon..

following

Me too following

+1

It will be great 👯‍♀️

Joining the question!
Need release

Pls upgrade library

Joining the question!
please update

+1

+1

I have made an Android Library to manage Google Play Billing, In App Purchases and Subscriptions with just a few lines of code.
Check it out and be sure to star the project if you liked my contribution :)

https://github.com/akshaaatt/Google-IAP

+1

+1

Please update +1

+1

IMO, this library isn't needed anymore. Google Play billing v2 offered a very low-level AIDL, but v3 offers a Kotlin/Java API. The AIDL API required a lot of boilerplate code for handling raw data and making requests. The Kotlin/Java API offered by v3 takes care of that, which is also one of the pain points that this library addressed for v2. Unlike v2, v3 also queries Play Store's cache on querying purchases. A major chunk of this library was dedicated to maintaining this cache.

The v3 library doesn't address verifying purchases. You can copy-paste the Security class for that. Your licenseKey is the public key for verifying purchases. productId, purchaseData and dataSignature are returned by Purchase#getSku(), Purchase#getOriginalJson() and Purchase#getSignature() respectively.

https://github.com/anjlab/android-inapp-billing-v3/blob/23b702219e7449e99ba94ace21cbe2cdb6eb6e8d/library/src/main/java/com/anjlab/android/iab/v3/BillingProcessor.java#L173-L177

https://github.com/anjlab/android-inapp-billing-v3/blob/23b702219e7449e99ba94ace21cbe2cdb6eb6e8d/library/src/main/java/com/anjlab/android/iab/v3/BillingProcessor.java#L1009-L1024

On a side note, you are now required to either acknowledge or consume all purchases. You also need to handle pending purchases that may transition to the purchased state while the user is away from your app.

After trying to make this project compatible with v3, I gave up and using only the v3 itself, It is not complicate to use, you can follow this sample https://medium.com/@programtown/how-to-make-in-app-purchase-in-android-using-google-play-billing-library-updated-library-3-0-1-72e0b1fd43d5 or more advance sample of Google ClassyTaxiJava https://github.com/android/play-billing-samples/blob/main/ClassyTaxiJava/app/src/main/java/com/sample/android/classytaxijava/billing/BillingClientLifecycle.java , change the BillingClient.SkuType.SUBS to BillingClient.SkuType.INAPP for use with non subscription

Please upgrade

thanks to @dzungpv i was able to update my library to 3.0.1, still testing, will comment after testing.

Billing Library v-3
Demo Application :- https://github.com/Mahadev-code/AndroidInAppPurchase

Source code also tested on Google Play store

Bump, Please update

Any idea what to do? It seems that there won't be any update.

class BillingProvider constructor(
val context: Context,
val license:String,
val productsList:List,
var purchaseStatusUpdate: ((purchases: List?,isRestore:Boolean) -> Unit)
) : PurchasesUpdatedListener {

init {
    initBillingClient()
}

//var purchaseStatusUpdate: ((productId:String,status:Boolean)->Unit)? = null

private var billingClient: BillingClient? = null


private fun initBillingClient() {
    billingClient = BillingClient.newBuilder(context)
        .enablePendingPurchases().setListener(this).build()
    billingClient?.startConnection(object : BillingClientStateListener {
        override fun onBillingSetupFinished(billingResult: BillingResult) {
            if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
                val queryPurchase = billingClient?.queryPurchases(BillingClient.SkuType.INAPP)
                val queryPurchases =
                    queryPurchase?.purchasesList
                if (queryPurchases != null && queryPurchases.size > 0) {
                    handlePurchases(queryPurchases,true)
                } else {
                    purchaseStatusUpdate(null,false)
                }
            }
            else
                purchaseStatusUpdate(null,false)
        }

        override fun onBillingServiceDisconnected() {
            purchaseStatusUpdate(null,false)
        }
    })
}


open fun handlePurchases(purchases: List<Purchase>,isRestore:Boolean= false) {
    val billingItems = ArrayList<BillingItem>()
    for (purchase in purchases) {
        //if item is purchased
        if (productsList.contains(purchase.sku) && purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
            if (!verifyValidSignature(purchase.originalJson, purchase.signature)) {
                // Invalid purchase
                // show error to user
                billingItems.add(BillingItem(purchase.sku, false))
                context.showMessege("Error : Invalid Purchase")
                return
            }
            // else purchase is valid
            //if item is purchased and not acknowledged
            if (!purchase.isAcknowledged) {
                billingItems.add(BillingItem(purchase.sku, true))
                val acknowledgePurchaseParams =
                    AcknowledgePurchaseParams.newBuilder()
                        .setPurchaseToken(purchase.purchaseToken)
                        .build()
                billingClient?.acknowledgePurchase(acknowledgePurchaseParams) {

// val result = (it.responseCode == BillingClient.BillingResponseCode.OK)
// purchaseStatusUpdate(listOf(BillingItem(purchase.sku, result)))
}
} else {

            }
            billingItems.add(BillingItem(purchase.sku, true))
        } else if (productsList.contains(purchase.sku) && purchase.purchaseState == Purchase.PurchaseState.PENDING) {
            context.showMessege("Purchase is Pending. Please complete Transaction")
            billingItems.add(BillingItem(purchase.sku, false))
        } else if (productsList.contains(purchase.sku) && purchase.purchaseState == Purchase.PurchaseState.UNSPECIFIED_STATE) {
            billingItems.add(BillingItem(purchase.sku, false))
        }

    }
    purchaseStatusUpdate(billingItems,isRestore)
}


private fun verifyValidSignature(
    signedData: String,
    signature: String
): Boolean {
    return try {
        // To get key go to Developer Console > Select your app > Development Tools > Services & APIs.
        Security.verifyPurchase(license, signedData, signature)
    } catch (e: IOException) {
        false
    }
}

override fun onPurchasesUpdated(
    billingResult: BillingResult,
    purchases: MutableList<Purchase>?
) {
    if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && purchases != null) {
        handlePurchases(purchases)
    } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
        val queryAlreadyPurchasesResult: Purchase.PurchasesResult? =
            billingClient?.queryPurchases(
                BillingClient.SkuType.INAPP
            )
        val alreadyPurchases: List<Purchase>? = queryAlreadyPurchasesResult?.getPurchasesList()
        alreadyPurchases?.let { handlePurchases(it) }
    } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
        context.showMessege("Purchase Canceled")
    } else {
        context.showMessege(
            "Error " + billingResult.getDebugMessage()
        )
    }
}

private fun initiatePurchase(activity: Activity, itemId: String) {
    val skuList = ArrayList<String>()
    skuList.add(itemId)
    val params = SkuDetailsParams.newBuilder()
    params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP)
    billingClient?.querySkuDetailsAsync(
        params.build()
    ) { billingResult, skuDetailsList ->
        if (billingResult.responseCode === BillingClient.BillingResponseCode.OK) {
            if (skuDetailsList != null && skuDetailsList.size > 0) {
                val flowParams = BillingFlowParams.newBuilder()
                    .setSkuDetails(skuDetailsList[0])
                    .build()
                billingClient?.launchBillingFlow(activity, flowParams)
            } else {
                //try to add item/product id "purchase" inside managed product in google play console
                context.showMessege(
                    "Purchase Item not Found"
                )
            }
        } else {
            context.showMessege(
                " Error " + billingResult.debugMessage
            )
        }
    }
}

 fun requsetPaymment(activity: Activity, itemId: String) {
    if (billingClient?.isReady==true) {
        initiatePurchase(activity,itemId)
    } else {
        billingClient =
            BillingClient.newBuilder(context).enablePendingPurchases().setListener(this).build()
        billingClient?.startConnection(object : BillingClientStateListener {
            override fun onBillingSetupFinished(billingResult: BillingResult) {
                if (billingResult.responseCode === BillingClient.BillingResponseCode.OK) {
                    initiatePurchase(activity,itemId)
                } else {
                    context.showMessege(
                        "Error " + billingResult.debugMessage)
                }
            }

            override fun onBillingServiceDisconnected() {

            }
        })
    }
}

}

data class BillingItem(val productId: String, val status: Boolean)

kindly use the above helper class like this:

private fun startBillingProcess() {
billinProvider = BillingProvider(this,BillingData.APP_LICENSE,BillingData.listProducts){itemsList,isRestore->
itemsList?.forEach {
if(it.productId==BillingData.REMOVE_ADS_ID && it.status)
preferenceManager.removeAds = true
}

    }
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

2math picture 2math  ·  8Comments

AdityaAnand1 picture AdityaAnand1  ·  6Comments

mahirozdin picture mahirozdin  ·  3Comments

RajatVaghani picture RajatVaghani  ·  8Comments

KRIPT4 picture KRIPT4  ·  3Comments