How to identify the subscription duration (monthly or yearly) from a purchase in Google Play Billing Library?
I am using the Google Play Billing Library in my Kotlin app to allow users to purchase subscriptions. I have a single product with two subscription offers: one for a monthly duration and the other for a yearly duration.
After a user purchases a subscription, I can retrieve their purchase details using the BillingClient.queryPurchasesAsync() method, but it doesn't display the duration.
I want to determine whether the user selected the monthly or yearly subscription offer after the purchase has been made.
What I’ve Tried:
I queried the available ProductDetails using BillingClient.queryProductDetailsAsync() to retrieve the subscription offers (SubscriptionOfferDetails).
I attempted to compare the offerToken from the Purchase object with the offerToken in SubscriptionOfferDetails, but they are not the same.
How can I correctly determine the subscription duration (monthly or yearly) for a completed purchase when using the Google Play Billing Library? Is there a proper way to map the purchase details to the specific offer details?
Any guidance or examples would be greatly appreciated!
fun startBillingConnection() {
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingSetupFinished(billingResult: BillingResult) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
Log.d(TAG, "Billing response OK")
queryPurchases()
queryProductDetails()
_billingState.update { it.copy(isClientConnected = true) }
} else {
Log.e(TAG, billingResult.debugMessage)
handleBillingError(billingResult, ErrorContext.CONNECTION)
_billingState.update { it.copy(isClientConnected = false) }
}
}
override fun onBillingServiceDisconnected() {
Log.i(TAG, "Billing connection disconnected")
_billingState.update { it.copy(isClientConnected = false) }
}
})
}
private fun querySubscriptionProducts() {
val params = QueryProductDetailsParams.newBuilder()
val productList = SUBS_PRODUCTS.map { productId ->
QueryProductDetailsParams.Product.newBuilder()
.setProductId(productId)
.setProductType(BillingClient.ProductType.SUBS)
.build()
}
params.setProductList(productList).let { productDetailsParams ->
billingClient.queryProductDetailsAsync(productDetailsParams.build(), this)
}
}
override fun onProductDetailsResponse(
billingResult: BillingResult,
productDetailsList: MutableList<ProductDetails>
) {
when (billingResult.responseCode) {
BillingClient.BillingResponseCode.OK -> {
if (productDetailsList.isEmpty()) {
Log.e(TAG, "onProductDetailsResponse: Found null or empty ProductDetails.")
} else {
val newProducts = productDetailsList.associateBy { it.productId }
_billingState.update {
it.copy(products = it.products + newProducts)
}
}
}
else -> {
handleBillingError(billingResult, ErrorContext.PRODUCT_DETAILS)
}
}
}
Source: View source