I'm trying to enable in-app purchases, and can't figure out why my skuDetails query always returns 0 items.
I have publicized the app to my internal testing and added my mail address to the license test(tried both my developer mail and a 2. mail-address), I then downloaded the app on my phone, waited a couple of hours, and tried to make a purchase. It accepts my connection but always returns 0 elements in my skuDetailsList when I query SKU details. help, please?
manifest:
<uses-permission android:name="com.android.vending.BILLING" />
build.gradle:
def billing_version = "3.0.0"
implementation "com.android.billingclient:billing-ktx:$billing_version"
querySkuDetails method:
suspend fun querySkuDetails() {
val skuList = ArrayList<String>()
skuList.add(PREMIUM_UPGRADE_ID)
val params = SkuDetailsParams.newBuilder()
params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP)
withContext(Dispatchers.IO) {
billingClient.querySkuDetailsAsync(params.build()) { billingResult, skuDetailsList ->
// Process the result.
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
Log.v(TAG, "queryInventoryAsync - skuDetailsList:")
if (!skuDetailsList.isNullOrEmpty()) {
for (item in skuDetailsList) {
Log.v(TAG, item.toString())
skuDetails = item
}
} else {
Log.v(TAG, "skuDetailsList - is empty")
}
return@querySkuDetailsAsync
}
Full class:
import android.util.Log
import android.widget.Toast
import com.alexco.habitrack.activities.main.MainActivity
import com.alexco.habitrack.utilities.Persistence
import com.android.billingclient.api.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class BillingCli(private val activity: MainActivity, private val database: Persistence) {
companion object {
private const val TAG = "InAppBilling"
private const val PREMIUM_UPGRADE_ID = "noaddsplease"
}
private var purchases: Purchase.PurchasesResult? = null
private var skuDetails: SkuDetails? = null
private val purchasesUpdateListener =
PurchasesUpdatedListener { billingResult, purchases ->
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.
}
}
private var billingClient: BillingClient = BillingClient.newBuilder(activity)
.setListener(purchasesUpdateListener)
.enablePendingPurchases()
.build()
init {
connect()
}
private fun handlePurchase(purchase: Purchase) {
if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
Log.v(TAG, "purchase approved")
database.setPremium(activity, true)
activity.newHabitHandler.openAddHabitActivity(activity.tutorial.enabled)
Toast.makeText(activity, "welcome to premium", Toast.LENGTH_LONG).show()
if (!purchase.isAcknowledged) {
val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
suspend {
val ackPurchaseResult = withContext(Dispatchers.IO) {
billingClient.acknowledgePurchase(
acknowledgePurchaseParams.build()
) {
Log.v(TAG, "purchase acknowledged")
}
}
}
}
}
if (purchase.purchaseState == Purchase.PurchaseState.PENDING) {//pending
Log.v(TAG, "purchased pending")
return
}
if (purchase.purchaseState == Purchase.PurchaseState.UNSPECIFIED_STATE) {//pending
Log.v(TAG, "purchase unspecified state")
return
}
}
private fun connect() {
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingSetupFinished(billingResult: BillingResult) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
Log.v(TAG, "service connected")
// The BillingClient is ready. You can query purchases here.
suspend {
querySkuDetails()
}
suspend {
purchases = billingClient.queryPurchases(BillingClient.SkuType.INAPP)
Log.v(TAG, "onBillingSetUpFinished, purchases: ${purchases.toString()}")
}
} else {
Log.v(TAG, "service did not connect")
}
}
override fun onBillingServiceDisconnected() {
Log.v(TAG, "service disconnected")
}
})
}
suspend fun querySkuDetails() {
val skuList = ArrayList<String>()
skuList.add(PREMIUM_UPGRADE_ID)
val params = SkuDetailsParams.newBuilder()
params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP)
withContext(Dispatchers.IO) {
billingClient.querySkuDetailsAsync(params.build()) { billingResult, skuDetailsList ->
// Process the result.
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
Log.v(TAG, "queryInventoryAsync - skuDetailsList:")
if (!skuDetailsList.isNullOrEmpty()) {
for (item in skuDetailsList) {
Log.v(TAG, item.toString())
skuDetails = item
}
} else {
Log.v(TAG, "skuDetailsList - is empty")
}
return@querySkuDetailsAsync
}
if (billingResult.responseCode == 1) {
//user cancel
return@querySkuDetailsAsync
}
if (billingResult.responseCode == 2) {
Toast.makeText(activity, "Internet required for purchase", Toast.LENGTH_LONG)
.show()
return@querySkuDetailsAsync
}
if (billingResult.responseCode == 3) {
Toast.makeText(
activity,
"Incompatible Google Play Billing Version",
Toast.LENGTH_LONG
).show()
return@querySkuDetailsAsync
}
if (billingResult.responseCode == 7) {
Toast.makeText(activity, "you already own Premium", Toast.LENGTH_LONG)
.show()
return@querySkuDetailsAsync
}
Toast.makeText(
activity,
"no skuDetails sorry",
Toast.LENGTH_LONG
)
.show()
Log.v(TAG, "responseCode: ${billingResult.responseCode}")
Log.v(TAG, "debug: ${billingResult.debugMessage}")
}
}
}
fun makePurchase() {
if (purchases != null) {
if (purchases!!.purchasesList != null) {
if (purchases!!.purchasesList!!.size > 0) {
Toast.makeText(
activity,
"you already owns premium\n-premium activated-",
Toast.LENGTH_LONG
).show()
database.setPremium(activity, true)
activity.newHabitHandler.openAddHabitActivity(activity.tutorial.enabled)
}
}
}
// Retrieve a value for "skuDetails" by calling querySkuDetailsAsync().
if (skuDetails != null) {
val flowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetails!!)
.build()
val responseCode = billingClient.launchBillingFlow(activity, flowParams).responseCode
Log.v(TAG, "makePurchase - responseCode:$responseCode")
} else {
Log.v(TAG, "makePurchase - no skuDetails")
Toast.makeText(activity, "Google didn't accept the purchase attempt", Toast.LENGTH_LONG)
.show()
}
}
I deleted the item in the google play console and got a new item with a new id, and it worked ¯_(ツ)_/¯