1

I've been managing Google Play subscriptions from my Android app for a long time, using Google Play Billing Library version 4.0 since last year (it's not feasible to upgrade to 5.0 at this moment). Now I want to offer free trial periods in subscriptions, so I have created one offer in the Google Play console for each of my subscriptions, with the following configuration:

  • Offer's eligibility criteria: Developer determined (I don't want Google to decide which users may enjoy the offers, I want to be the one to decide it from my code, applying my own criteria)
  • A phase of "Free trial" type, 1 month duration.

When I need, for example, to get the details of a "my_monthly_plan" subscription from my app, my code is as follows:

val params = SkuDetailsParams.newBuilder().apply {
                setSkusList(listOf("my_monthly_plan"))
                setType(SkuType.SUBS)
            }
myBillingClient.querySkuDetailsAsync(params.build()) { billingResult, skuDetailsList ->
                if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
                    if (!skuDetailsList.isNullOrEmpty()) {

                        // A not empty products details list has been successfully retrieved.
                        ...
                    } else {

                        // Products details list is null or empty.
                        ...
                    }
                } else {

                    // The response code is not OK.
                    ...
                }
            }
        } 

If everything is ok, I receive a SkuDetails object containing something like this:

SkuDetails: {"productId":"my_monthly_plan", ... ,"price":"49,00 €", ... ,"freeTrialPeriod":"P1M"}

First problem: I always receive here an empty freeTrialPeriod object. All subscriptions and offers are correctly created on Google Play. Maybe I just have to wait a few more hours or days for them to be visible from my app?

When users choose which subscription (defined by skuDetails) they want to buy from my app, the following code is executed:

    val flowParams = BillingFlowParams.newBuilder()
                .setSkuDetails(skuDetails)
                .build()
    billingClient.launchBillingFlow(activity, flowParams)

Second problem: at this point I have no idea how to tell Google if the free trial should be applied to the purchase or not.

Let me explain with a couple of example scenarios:

Scenario 1

  1. I have a customer A who has never bought subscriptions in my app, and I want him to enjoy the free trial when he buys for the first time.
  2. The querySkuDetailsAsync call returns a plan with these SkuDetails: {"productId":"my_monthly_plan", ... ,"price":"€49.00", ... ,"SkuDetailsParams":"P1M"} (I am optimistic and I hope that my first problem will be solved and I get to receive a non-empty value in SkuDetailsParams)
  3. When customer A purchases the subscription, I use the SkuDetails obtained in the previous step to parameterize the launchBillingFlow call and launch the purchase process.
  4. I assume that Google is going to apply the free trial to my customer A for one month, and if the customer auto-renews the subscription after this free month will automatically be charged the price without offer, €49 each month. But, how can I be sure that the customer is not going to be charged €49 from this first purchase? Do I need to specify it somehow when calling to launchBillingFlow? Or maybe there is another API call for this?

Scenario 2

  1. I have another customer B who has never bought subscriptions in my app either, but for whatever reason I don't want him to enjoy the free trial when he buys for the first time (that's why I chose the "Developer determined" Offer's eligibility criteria).
  2. The querySkuDetailsAsync call returns the same SkuDetails as before.
  3. When customer B purchases the subscription, should I use the same SkuDetails to parameterize the launchBillingFlow call to launch the purchase process? How do I tell Google that the free trial does not apply to this customer B, that he has to be charged €49 from this first purchase?

Any idea to solve my two problems will be very welcome. Thanks in advance.

  • I've also been looking into this myself, and I have a feeling you need to include some sort of trial details that may only be available in Billing 5.0+. I'm going to check! – ConcernedHobbit Mar 13 '23 at 20:27

1 Answers1

0

I recently implemented the requirement mentioned in the article using Google Play Billing Library v5. Regarding the second question, I would like to mention the approach in v5, hoping it will be helpful to you.

Calling getSubscriptionOfferDetails() in v5 will return a List, and the contents of the List are all available offers for a single subscription. In my case, I have a subscription that includes one offer, and the eligibility criteria for the offer is determined by the developer. Therefore, the first item in the List returned by my getSubscriptionOfferDetails() is the offer scheme, and the second item is the regular scheme without any offer. Based on my needs, I can decide which scheme to provide to the user. Here is my code:

//Regular scheme
val productDetailsParamsList = listOf(
    productDetailsList2[0].getSubscriptionOfferDetails()?.get(1)?.let { offer ->
        BillingFlowParams.ProductDetailsParams.newBuilder()
            .setProductDetails(productDetailsList2[0])
            .setOfferToken(offer.offerToken)
            .build()
    }
)
//Offer scheme
val FreeproductDetailsParamsList = listOf(
    productDetailsList2[0].getSubscriptionOfferDetails()?.get(0)?.let { offer ->
        BillingFlowParams.ProductDetailsParams.newBuilder()
            .setProductDetails(productDetailsList2[0])
            .setOfferToken(offer.offerToken)
            .build()
    }
)

val billingFlowParams = BillingFlowParams.newBuilder()
    //You can choose to provide either the offer scheme or the regular scheme here.
    .setProductDetailsParamsList(productDetailsParamsList)
    .setIsOfferPersonalized(true)
    .setObfuscatedAccountId(auth.uid!!)
    .build()
val billingResult =
    billingClient.launchBillingFlow(this@MainActivity, billingFlowParams)
st920228
  • 31
  • 2