4

This is the current test setup in Google Play console:

  • Test product 1
    • Subscription base plan 1: duration 1 month
    • Subscription base plan 2: duration 6 months
      • Offer 1: 1 week free trial period

The result of queryProductDetailsAsync are 3 offers:

  • Offer 1
    • Pricing phase 1: P1M
  • Offer 2
    • Pricing phase 1: P1W (free)
    • Pricing phase 2: P6M
  • Offer 3
    • Pricing phase 1: P6M

The app dynamically presents all available offers to the user, so that offers can be managed remotely via the Google Play console.

The issue is that the offer 2 appears in addition to offer 3. If a base plan has an offer and the user is eligible for it, I want to hide the base plan (offer 3), as it's kind of redundant because less attractive than P1W (free) + P6M (offer 2).

On the other hand, if a user already subscribed to P1W (free) + P6M and cancelled within the free trial period, they should not get the P1W (free) + P6M but only the base plan P6M. I assume that is already managed by the Google Billing, as queryProductDetailsAsync should only return offers that are available to the user.

In the queryProductDetailsAsync response I see no way to know that offer 2 is using offer 3 as a base plan. They appear as two unrelated offers, which makes it difficult to add any logic in the app to hide the base offer. The only solution I can think of is using offerTags, add a unique ID to the base plan to be able to associate then with one another.

What is the best approach to hide offer 3 if offer 2 is also available?

Manuel
  • 14,274
  • 6
  • 57
  • 130

3 Answers3

1

It seems that tags is what Google intended to be used, according to docs:

A tag is an optional label of up to 20 characters that you can use to mark or group base plans and offers and identify them in the API. Tags can be used to determine which offer to show when the user is eligible for more than one. You can add up to 20 tags. Users cannot see tags.

Tip: We recommend using tags to identify the offers created with developer-determined eligibility to help differentiate between them when showing the collection of offers available to the user.

It seems to be the only way to identify an offer as part of a base plan. That is surprising because Google Billing already knows that the offer is part of a base plan, why do we have to mess around with custom tags to get that information? Even more surprisingly, the Google Billing API 5.0 has only recently (May 2022) been redesigned but is lacking such a basic feature.

Manuel
  • 14,274
  • 6
  • 57
  • 130
1

I see two solutions:

  1. Use the Tags to identify offers provided by the billing API as mentioned in the currently accepted answer and documented here (search for "Tags").

  2. Use the Offer ID, the one set in Google Play Console for any discounted offer. Base plans don't have it as documented here:

    Note: This field is only set for a discounted offer. Returns null for a regular base plan.

    Example implementation (based on Billing Client 5.1.0):

    • Obtain a list of subscription offers (SubscriptionOfferDetails) via ProductDetails.getSubscriptionOfferDetails()
    • One of these offers represents the subscription base plan, that instance of SubscriptionOfferDetails contains the null value under the offerId/getOfferId() method.
    • TLDR: To filter out the base plan, I recommend to check if the list of returned subscription offers is larger than one, and if yes, remove the base plan offer from it (remove the one where offerId == null).

The answer refers to the redesigned subscriptions, introduced by Google in May 2022, more details here.

  • I'm not sure this addresses the fundamental issue of the missing explicit relation between offers and base plans. There is likely a good reason why the use of tags is suggested in the docs. Could you still infer that relation by filtering for `null` if there are multiple base plans and offers? – Manuel Feb 18 '23 at 22:31
0

I don't see why tags should be needed in your case.

basePlanId and offerId in the subscriptionOfferDetails objects returned by queryProductDetails completely identify each base plan / base plan with offer (as Jarosław said, offerId is null for base plans without offer).

In your example, you see that "offer 2 is using offer 3 as a base plan" as it has the same basePlanId.

So just check for each basePlanId if a subscriptionOfferDetails object with offerId != null is present, and in this case, hide the one with offerId = null

As indicated in the docs, tags should only be needed for offers with developer-determined eligibility.

jake n
  • 342
  • 3
  • 15