Google In App Purchase Integration Working Example on 2020 May - android

This is not a question actually. I spent some time on this, so I am putting here the steps involved to make an in-app purchase.
added dependencies
//billing-client
implementation 'com.android.billingclient:billing:2.2.1'
implementation 'com.android.billingclient:billing-ktx:2.2.1'
added permission in manifest
Created application in Play Console.
Play Console -> settings -> Pricing Template :: added pricing template.
Select Application Form Console -> Store Presence -> In-App Products -> Managed
Products -> Create a Managed Product.
For Testing, first of all, you need to release a build as Alpha (with some testing mail id registered in closed track.)
After Successful publication you can use the same build (maybe needed the same version code and version number) (Both debug and Release works) for test the purchase.
Ensure the testing mail IDs are google ids (#gmail.com)
Here are the code snippets that I have used for billing.
implement PurchasesUpdatedListener, on fragment or activity
Setup BIlling Client
private fun setupBillingClient() {
billingClient = BillingClient.newBuilder(mainActivity)
.enablePendingPurchases()
.setListener(this)
.build()
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingSetupFinished(billingResult: BillingResult) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
// The BillingClient is setup
log("Setup Billing Done")
loadAllSKUs()
}
}
override fun onBillingServiceDisconnected() {
log("Failed")
}
})
}
Load All Skus (Ids of your purchasing templates (use arraylist))
private fun loadAllSKUs() = if (billingClient.isReady) {
val params = SkuDetailsParams
.newBuilder()
.setSkusList(skuList)
.setType(BillingClient.SkuType.INAPP)
.build()
billingClient.querySkuDetailsAsync(params) { billingResult, skuDetailsList ->
// Process the result.
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && skuDetailsList.isNotEmpty()) {
for (skuDetails in skuDetailsList) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && skuDetailsList.isNotEmpty()) {
this.skuDetailsList = skuDetailsList
}
}
}
}
} else {
log("Billing Client not ready")
}
Make Google Payment
private fun invokeGooglePayPayment(skuDetails: SkuDetails) {
val billingFlowParams = BillingFlowParams
.newBuilder()
.setSkuDetails(skuDetails)
.build()
billingClient.launchBillingFlow(mainActivity, billingFlowParams)
}
If you need to purchase again on the same in-app purchase item you can use this code,
override fun onPurchasesUpdated(
billingResult: BillingResult?,
purchases: MutableList<Purchase>?
) {
//todo remove this function only for token log
if (!purchases.isNullOrEmpty()) {
for (purchase in purchases) {
Log.e("Purchase Token" + purchase.purchaseToken, "OK")
}
}
if (billingResult?.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
for (purchase in purchases) {
acknowledgePurchase(purchase) // do after purchase
}
} else if (billingResult?.responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
//todo
} else if (billingResult?.responseCode == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
val consumeParams = ConsumeParams.newBuilder()
.setPurchaseToken("inapp:${BuildConfig.APPLICATION_ID}:${selectedSkuDetails?.sku}")
.build()
runBlocking {
CoroutineScope(Dispatchers.IO).launch {
billingClient.consumePurchase(consumeParams)
}
MainScope().launch {
invokeGooglePayPayment(selectedSkuDetails!!)
}
}
}
}

Related

Why is the subscription not updating correctly, Android?

I have two subscriptions in the application, each of them individually works correctly. But when I try to do an Update/Downgrade, problems arise:
I have two subscriptions in the application, each of them individually works correctly. But when I try to do an Update/Downgrade, problems arise:
When I try to change one subscription to another, I get the correct window with changing the subscription and shows that the new subscription was purchased successfully, but when I check on Google Play, it shows only the one I bought first, after a while I try again, this time it does not appear replacement window, only the standard purchase window, the second one is bought without problems and both are visible in the Store. Maybe I have some wrong approach to updating, everything seems to be in its place otherwise, the window with the replacement would not be displayed correctly.
My setSuccessfulScreen() method, in onPurchasesUpdated(), works correctly on the first purchase, but it crashes with an error when I try to update the subscription:
Fatal Exception: java.lang.RuntimeException: Error receiving broadcast Intent { act=com.android.vending.billing.PURCHASES_UPDATED
I thought maybe the case in Binding, I replaced with findViewById(), it does not help, without this method there is no crash.
My subscription code:
private fun buySubscription() {
val skuList = ArrayList<String>()
skuList.add("")
skuList.add("")
val params = SkuDetailsParams.newBuilder()
params.setSkusList(skuList).setType(BillingClient.SkuType.SUBS)
billingClient!!.querySkuDetailsAsync(params.build()) { billingResult, skuDetailsList ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && skuDetailsList != null) {
when (subsFlag) {
MONTH -> {
if (!ProviderPreferences.isSubscribed()) {
billingClient!!.launchBillingFlow(requireActivity(), BillingFlowParams.newBuilder().setSkuDetails(skuDetailsList[1]!!).build())
} else {
if (ProviderPreferences.getSubscribedType() == MONTH) {
billingClient!!.launchBillingFlow(requireActivity(), BillingFlowParams.newBuilder().setSkuDetails(skuDetailsList[1]!!).build())
} else {
val updateParams = BillingFlowParams.SubscriptionUpdateParams.newBuilder().setOldSkuPurchaseToken(ProviderPreferences.getPurchaseToken()!!).setReplaceSkusProrationMode(BillingFlowParams.ProrationMode.DEFERRED).build()
billingClient!!.launchBillingFlow(requireActivity(), BillingFlowParams.newBuilder().setSkuDetails(skuDetailsList[1]!!).setSubscriptionUpdateParams(updateParams).build())
}
}
}
YEAR -> {
if (!ProviderPreferences.isSubscribed()) {
billingClient!!.launchBillingFlow(requireActivity(), BillingFlowParams.newBuilder().setSkuDetails(skuDetailsList[0]!!).build())
} else {
if (ProviderPreferences.getSubscribedType() == YEAR) {
billingClient!!.launchBillingFlow(requireActivity(), BillingFlowParams.newBuilder().setSkuDetails(skuDetailsList[0]!!).build())
} else {
val updateParams = BillingFlowParams.SubscriptionUpdateParams.newBuilder().setOldSkuPurchaseToken(ProviderPreferences.getPurchaseToken()!!).setReplaceSkusProrationMode(BillingFlowParams.ProrationMode.DEFERRED).build()
billingClient!!.launchBillingFlow(requireActivity(), BillingFlowParams.newBuilder().setSkuDetails(skuDetailsList[0]!!).setSubscriptionUpdateParams(updateParams).build())
}
}
}
}
}
}
}
private fun purchases() {
val acknowledgePurchaseResponseListener = AcknowledgePurchaseResponseListener { billingResult -> }
billingClient = BillingClient.newBuilder(App.getInstance()).enablePendingPurchases().setListener { billingResult: BillingResult, list: List<Purchase>? ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
val purchase = list!![0]
handlePurchases(acknowledgePurchaseResponseListener, purchase)
ProviderPreferences.saveSubscriptionType(subsFlag)
ProviderPreferences.savePurchaseToken(list[0].purchaseToken)
setSuccessfulScreen()
}
}.build()
connectToGooglePlayBilling()
}
private fun handlePurchases(acknowledgePurchaseResponseListener: AcknowledgePurchaseResponseListener?, purchase: Purchase) {
if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
ProviderPreferences.saveSubscription(true)
if (!purchase.isAcknowledged) {
val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder().setPurchaseToken(purchase.purchaseToken).build()
billingClient!!.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener!!)
}
}
}
private fun connectToGooglePlayBilling() {
billingClient!!.startConnection(object : BillingClientStateListener {
override fun onBillingSetupFinished(billingResult: BillingResult) {
billingClient!!.queryPurchasesAsync(BillingClient.SkuType.SUBS) { billingResult, list ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && list != null) {
}
}
}
override fun onBillingServiceDisconnected() {
connectToGooglePlayBilling()
}
})
}
private fun setSuccessfulScreen() {
group!!.visibility = View.GONE
group2!!.visibility = View.VISIBLE
subscribeBtn!!.visibility = View.GONE
lottieAnimation!!.playAnimation()
}
I am using the billing v4 library.
Please tell me what could be the reason, maybe something is wrong with the subscription change code, I have not worked with subscription renewal before. And what could be the reason for the crash.
Thank you in advance.

Why google play billing subscription instantly gets canceled?

I have integrated the google play billing library inside my application to make a subscription purchase. It works well. The user receives purchase confirmation mail from google.
confirmation mail from google
The active subscription is also visible in the play store subscriptions screen.
Active subscription showing in play store
In the play console, the order detail said the user was successfully charged for a subscription. However, It gets canceled instantly.
play console order history
I also check purchase acknowledgment, it returns true on every purchase. Initially, I thought there would be a mistake in my code. So, I tried various billing libraries from Github. The problem persists. In the end, I replaced the entire google billing library with Revenue cat. Followed every step described on Revenue cat documents. Still, getting the same issue.
Is there anything that I am missing to implement or done incorrectly? please help me out. Thank you
code for fetching available products:
private fun fetchOffering(){
Purchases.sharedInstance.getOfferingsWith({ error ->
// An error occurred
handleBillingError(requireActivity(), error)
}) { offerings ->
offerings.current?.availablePackages?.takeUnless { it.isNullOrEmpty() }?.let {
// All packages from current offering
if (it.isNotEmpty()){
it.forEach { p: Package ->
offeringPackages.add(p)
}
isProductsAvailable = true
}
Log.d("RevenueCat", "fetchOffering: success: ${it.size}")
}
}
}
code for making purchase:
private fun makePurchase(pack:Package){
Purchases.sharedInstance.purchasePackageWith(
requireActivity(),
packageToPurchase = pack /* package from the fetched Offering*/,
onError = {error, userCancelled ->
Log.d("RevenueCat", "makePurchase: userCancelled: $userCancelled")
handleBillingError(requireActivity(), error)
},
onSuccess = { product, purchaserInfo ->
if (purchaserInfo.entitlements[REVENUE_ENTITLEMENT_ID_PRO]?.isActive == true) {
Log.d("RevenueCat", "makePurchase: success: ${product.originalJson} ")
afterPurchaseSuccessSetup()
}
})
}
I don't know where you are lagging, you should follow these steps to make subscribe purchase.
private lateinit var billingClient: BillingClient
private val skuListsubscribe = listOf("subscription_1", "subscription_2")
initialize billing client.
private fun setupBillingClient() {
billingClient = BillingClient.newBuilder(BaseActivity.mContext!!)
.enablePendingPurchases()
.setListener(this)
.build()
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingSetupFinished(billingResult: BillingResult) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
// The BillingClient is ready. You can query purchases here.
loadAllSKUsSubscription()
}
}
override fun onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
Log.e("TAG", "onBillingServiceDisconnected: ")
}
})
}
private fun loadAllSKUsSubscription() = if (billingClient.isReady) {
val params = SkuDetailsParams
.newBuilder()
.setSkusList(skuListsubscribe)
.setType(BillingClient.SkuType.SUBS)
.build()
billingClient.querySkuDetailsAsync(params) { billingResult, skuDetailsList ->
// Process the result.
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && skuDetailsList!!.isNotEmpty()) {
for (skuDetails in skuDetailsList) {
if (skuDetails.sku == subscribtionId) {
tv_full_category_price.text = skuDetails.price
realAmountSubscription = skuDetails.price
rl_bestvalue_plan.setOnClickListener {
isInAppPurchase = false
isSubscribtion = true
val billingFlowParams = BillingFlowParams
.newBuilder()
.setSkuDetails(skuDetails)
.build()
billingClient.launchBillingFlow(requireActivity(), billingFlowParams)
}
}
}
}
}
} else {
println("Billing Client not ready")
}
override purchase update method
override fun onPurchasesUpdated(
billingResult: BillingResult,
purchases: MutableList<Purchase>?,
) {
if (billingResult?.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
for (purchase in purchases) {
acknowledgePurchase(purchase)
}
} else if (billingResult?.responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
//logger("User Cancelled")
//logger(billingResult?.debugMessage.toString())
} else {
//logger(billingResult?.debugMessage.toString())
}
}
private fun acknowledgePurchase(purchase: Purchase) {
if (isSubscribtion) {
handleNonConsumableProduct(purchase = purchase)
}
}
fun handleConsumableProduct(purchase: Purchase) {
val consumeParams =
ConsumeParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
.build()
billingClient.consumeAsync(consumeParams) { billingResult, purchaseToken ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
if (isVisible && isAdded)
Snackbar.MakeInternetSnackbar(BaseActivity.mContext!!,
subscription_fragment_indicator_layout,
"Purchased Successfully")
InAppPaymentApi(realAmount.toString(), audio_id!!, purchase.purchaseToken)
}
}
}

onBillingSetupFinished never called

I have always worked with Java but this time I am trying to develop in Kotlin, I have an app that will have a button to pay to remove the ads (with Google Play's billing system) but I can't get it to work.
I have already uploaded my signed apk to Play Console and create the product that has the ID remove_ads
This is what I have in the AndroidManifest.xml file "android.permission.INTERNET" and "com.android.vending.BILLING"
In build.gradle:
implementation("com.android.billingclient:billing:4.0.0") implementation("com.android.billingclient:billing-ktx:4.0.0")
In my Kotlin Class "Settings":
OnCreate:
btnRemoveAds.setOnClickListener {
Toast.makeText(this#Settings, "Click Button", Toast.LENGTH_SHORT).show()
billingClient = BillingClient.newBuilder(this)
.setListener(purchasesUpdatedListener)
.enablePendingPurchases()
.build()
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingSetupFinished(billingResult: BillingResult) {
Toast.makeText(this#Settings, "SetupFinished", Toast.LENGTH_SHORT).show()
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
Toast.makeText(this#Settings, "OK", Toast.LENGTH_SHORT).show()
// The BillingClient is ready. You can query purchases here.
billingClient.queryPurchasesAsync(
SkuType.INAPP
) { billingResult, list ->
if (list.size != 0){
if (list.get(0).purchaseState == Purchase.PurchaseState.PURCHASED){
setPlus(true)
setResult(RESULT_OK)
} else{
queryAvaliableProducts()
}
} else{
queryAvaliableProducts()
}
}
} else{
Toast.makeText(this#Settings, "NO", Toast.LENGTH_SHORT).show()
}
}
override fun onBillingServiceDisconnected() {
Toast.makeText(this#Settings, "Disconnected", Toast.LENGTH_SHORT).show()
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
}
})
}
The only Toast I can see when pressing the button is "Click Button"
These are my methods:
private fun queryAvaliableProducts() {
val skuList: MutableList<String> = ArrayList()
skuList.add("remove_ads")
val params = SkuDetailsParams.newBuilder()
params.setSkusList(skuList).setType(INAPP)
billingClient.querySkuDetailsAsync(
params.build()
) { billingResult, skuDetailsList -> // Process the result.
if (skuDetailsList!!.size != 0) {
val billingFlowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetailsList[0])
.build()
val responseCode = billingClient.launchBillingFlow(
this#Settings,
billingFlowParams
).responseCode
}
}
}
private val purchasesUpdatedListener =
PurchasesUpdatedListener { billingResult, purchases ->
// To be implemented in a later section.
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK
&& purchases != null
) {
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.
} else {
// Handle any other error codes.
}
}
fun handlePurchase(purchase: Purchase) {
if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
if (!purchase.isAcknowledged) {
val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
.build()
billingClient.acknowledgePurchase(
acknowledgePurchaseParams,
acknowledgePurchaseResponseListener
)
}
}
}
var acknowledgePurchaseResponseListener =
AcknowledgePurchaseResponseListener { billingResult ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
setPlus(true)
setResult(RESULT_OK)
finish()
}
}
I also implemented a button to check connection status after billingClient.startConnection
btnConnectionState.setOnClickListener {
Toast.makeText(this#Settings, billingClient.connectionState.toString(), Toast.LENGTH_SHORT).show()
}
I am getting 2 (Connected) as a result so it seems weird to me that onBillingSetupFinished and onBillingServiceDisconnected never get executed, are there any errors in my code?
There are no errors in your code afaik except the Toast itself. onBillingServiceDisconnected isn't called on the main thread. If you put a try catch around it like so:
try {
Toast.makeText(this#Settings, "NO", Toast.LENGTH_SHORT).show()
} catch (ex: Exception) {
Log.e("MyApp", "Error", ex)
}
It will print an error like this
06-05 00:00:38.713 8595 9215 E MyApp :
java.lang.NullPointerException: Can't toast on a thread that has not
called Looper.prepare()
You can also print the thread the callback is called from:
Log.e("MyApp", "Running on ${Thread.currentThread().name}")
and it will tell you
Running on PlayBillingLibrary-1
So switch back to the main thread for the Toast (there are ways) or use a simple Log statement to verify that the method is called.

Kotlin can't get purchaseId with rxBilling

I'am trying to use rxBilling for in app purchases on my android app. library found here : https://github.com/betterme-dev/RxBilling
i manage to implemente it like this :
private fun launchBilling(subscription: Boolean) {
val sku = "com.app.subscription"
disposable.add(rxBilling.launchFlow(
this, BillingFlowParams.newBuilder()
.setSku(sku)
.setType(BillingClient.SkuType.SUBS)
.build()
)
.subscribe({
}, {
it.message?.let { msg -> unknownError(0, msg) }
})
)
}`
This is working perfectly, i pushed my app in internal test and i tried to subscribe, it's worked and i verified in my google play console that my subscription was taken into account.
The problem is that i want to get information of the purchase like date and transaction id. So i followed the guideline and implemented a disposable in the on start method :
override fun onStart() {
super.onStart()
println("onstrat")
disposable.add(
rxBilling.observeUpdates()
.subscribe({
println("here")
it.purchases.forEach { item ->
println("rxbillingstart")
println(item.toString())
println(item)
println(item.sku)
purchase(item)
}
}, {
togglePurchaseProgress(false)
it.message?.let { msg -> unknownError(0, msg) }
})
)
}
And for the implementation of the rxbilling and disposable in the on create method :
private lateinit var rxBilling: RxBilling
private lateinit var rxBillingFlow: RxBillingFlow
private val disposable = CompositeDisposable()
rxBilling = RxBillingImpl(BillingClientFactory(applicationContext))
rxBillingFlow = RxBillingFlow(applicationContext, BillingServiceFactory(this))
lifecycle.addObserver(BillingConnectionManager(rxBilling))
lifecycle.addObserver(BillingConnectionManager(rxBillingFlow))
The purchase methode is juste here to print item.orderId date ... but it's never called. Also my first print "onstrat" in the onStart method is called but all other print in the observable.subscribe are never called. Do I miss something? i don't get why i can't get subsciption informations
I found out that RxBilling is no longer maintained, so i used standard android billing client found here : https://developer.android.com/google/play/billing/integrate .But don't forget to add to your manifest :
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="com.android.vending.BILLING"/>
Then implemente those interface in your activity : PurchasesUpdatedListener, SkuDetailsResponseListener and set up the billing client variable : private lateinit var billingClient : BillingClient . Also it was unclear to me at first but sku is the product id you set up on googleplay console.
private fun buy(){
billingClient = BillingClient.newBuilder(this)
.setListener(this)
.enablePendingPurchases()
.build()
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingSetupFinished(billingResult: BillingResult) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
// The BillingClient is ready. You can query purchases here.
val skuList = ArrayList<String>()
skuList.add("premium_upgrade")
skuList.add("com.yourappname.subscription")
val params = SkuDetailsParams.newBuilder()
params.setSkusList(skuList).setType(BillingClient.SkuType.SUBS)
billingClient.querySkuDetailsAsync(params.build(), this#DetailsActivity)
}
}
override fun onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
}
})
}
Set up the skudetails you are searching for like this, then get the response here :
override fun onSkuDetailsResponse(responseCode: BillingResult, skuDetailsList: MutableList<SkuDetails>?) {
if (responseCode.responseCode != BillingClient.BillingResponseCode.OK) {
Log.w("LOG_TAG", "SkuDetails query failed with response: $responseCode")
} else {
Log.d("LOG_TAG", "SkuDetails query responded with success. List: $skuDetailsList")
if (skuDetailsList != null) {
for (skuDetails in skuDetailsList) {
var sku = skuDetails.getSku ()
var price = skuDetails.getPrice ()
var params = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetails)
.build();
if (sku == "com.yourappname.subscription") {
billingClient.launchBillingFlow(this, params);
}
}
}
}
}
This mean that if i found the right sku i lauchBillingflow -> i start the process of paying. Then you get the result on the payement here :
override fun onPurchasesUpdated(billingResult: BillingResult, purchases: MutableList<Purchase>?) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
for (purchase in purchases) {
purchase(1, purchase)
}
} else if (billingResult.responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
// Handle an error caused by a user cancelling the purchase flow.
} else {
// Handle any other error codes.
}
}
You can here get the result on your purchase such as purchaseTime, orderId, purchaseToken, and then my function purchase send the purchaseToken to an internal server to verify the transaction.
Hope it's will help some of you.

Verifying android in app purchase using developer payload

billing lib.: 'com.android.billingclient:billing:2.0.3'
starting a flow after a successful startConnection ,
val skuList = ArrayList<String>()
skuList.add("buy4")
val params = SkuDetailsParams.newBuilder()
params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP)
//billingClient is declared and initialized earlier
billingClient.querySkuDetailsAsync(params.build())
{ billingResult, skuDetailsList ->
val flowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetailsList.first())
.build()
val responseCode = billingClient.launchBillingFlow(this, flowParams)
println(responseCode.responseCode) //prints 0 ... OK
}
the MainActivity implements PurchasesUpdatedListener
override fun onPurchasesUpdated(billingResult: BillingResult?, purchases: MutableList<Purchase>?) {
if (billingResult?.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
for (purchase in purchases) {
acknowledgePurchase(purchase)
}
}
}
private fun acknowledgePurchase(purchase: Purchase) {
if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
// Grant entitlement to the user.
// Acknowledge the purchase
val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
.setDeveloperPayload("PayloadString")
.build()
billingClient.acknowledgePurchase(
acknowledgePurchaseParams,
object : AcknowledgePurchaseResponseListener {
override fun onAcknowledgePurchaseResponse(billingResult: BillingResult?) {
println("payload =${purchase.developerPayload}") // prints "payload="
}
})
}
}
the developer payload is empty, despite being set at the AcknowledgePurchaseParams , I also saved the purchase after acknowledging it, and tried calling purchase.developerPayload after a while, and still it's blank , what are the best practices for using developer payload to verify in app purchases ?
N.B I'm using an internal testing track on play console
In onAcknowledgePurchaseResponse you will need to refresh your purchase object from the cache. The cache is updated by the time onAcknowledgePurchaseResponse is called so you do this by calling https://developer.android.com/reference/com/android/billingclient/api/BillingClient.html#querypurchases. We will consider adding the update purchase to the listener for a future library release to make this more convenient.

Categories

Resources