Skip to content

Galaxy Store Free Trial Eligibility Detection#2947

Merged
fire-at-will merged 11 commits into
samsung-devfrom
promotion-eligibility
Dec 18, 2025
Merged

Galaxy Store Free Trial Eligibility Detection#2947
fire-at-will merged 11 commits into
samsung-devfrom
promotion-eligibility

Conversation

@fire-at-will

@fire-at-will fire-at-will commented Dec 17, 2025

Copy link
Copy Markdown
Contributor

Description

This PR adds support for including free trials for Galaxy Store products in the pricingPhases property of the GalaxySubscriptionOption. It does so by creating one (and only one) SubscriptionOption for each Galaxy subscription product, and, when the user is eligible for a free trial, it includes the free trial in that SubscriptionOption's pricingPhases.

As a part of this exercise, we also needed to modify GalaxyPurchasingData to keep a reference to just the product ID and product type, instead of the entire StoreProduct to avoid a circular reference between GalaxyStoreProduct and GalaxySubscriptionOption.

Out of Scope

This PR intentionally does not add introductory subscription pricing phases when a user is eligible for one. Support for those will come in the future :)

@fire-at-will fire-at-will added the pr:feat A new feature label Dec 17, 2025
@RevenueCat-Danger-Bot

RevenueCat-Danger-Bot commented Dec 17, 2025

Copy link
Copy Markdown
1 Warning
⚠️ This PR increases the size of the repo by more than 100.00 KB (increased by 146.84 KB).

Generated by 🚫 Danger

private const val ELIGIBILITY_PRICING_TIERED_PRICE = "TieredPrice"

internal fun ProductVo.toStoreProduct(
promotionEligibilities: List<PromotionEligibilityVo>? = null,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may be wondering why ProductVo.storeProduct now takes in a List<PromotionEligibilityVo>? instead of a single PromotionEligibilityVo?. This is because the IapHelper.getPromotionEligibility()'s listener provides us with an ArrayList of PromotionEligibilityVo? objects:

public interface OnGetPromotionEligibilityListener {
    void onGetPromotionEligibility(@NotNull ErrorVo var1, @NotNull ArrayList<PromotionEligibilityVo> var2);
}

And thus, it's allowed by the API to receive multiple PromotionEligibilityVos for the same product ID. I haven't seen this happen in my testing, but since it's technically allowed, it's a case we need to handle.

?.filter { it.itemId == this.itemId }
?.mapTo(eligibilityPricings) { it.pricing }

if (

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still haven't seen this case appear in our testing, but since it's possible through the API, it's probably worth handling in the code :)

@fire-at-will fire-at-will marked this pull request as ready for review December 17, 2025 21:43
@fire-at-will fire-at-will requested a review from a team as a code owner December 17, 2025 21:43
@fire-at-will fire-at-will requested review from MarkVillacampa, ajpallares and tonidero and removed request for ajpallares December 17, 2025 21:43
@fire-at-will fire-at-will changed the title [WIP]: Galaxy Store Promotion eligibility [WIP]: Galaxy Store Free Trial Eligibility Detection Dec 17, 2025
@fire-at-will fire-at-will changed the title [WIP]: Galaxy Store Free Trial Eligibility Detection Galaxy Store Free Trial Eligibility Detection Dec 17, 2025

@tonidero tonidero left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Just some comments but nothing big.

recurrenceMode = RecurrenceMode.NON_RECURRING,
billingCycleCount = null,
price = Price(
formatted = "%s%.2f".format(currencyUnit, 0.0),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, I think Google returns Free as a string in this case... But that's problematic for us since we would need to localize that string... So I'm ok just putting 0.0 for now, and we can reassess later

ReplaceWith("presentedOfferingContext.offeringIdentifier"),
)
override val presentedOfferingIdentifier: String?
get() = presentedOfferingContext?.offeringIdentifier

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this populated later at some point? for google, we add a copyWithPresentedOfferingContext method that creates a copy of subscription options with the context added. Seems we're missing that here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great callout, it looks like this happens at the StoreProduct level. I'll add that logic to GalaxyStoreProduct.copyWithPresentedOfferingContext()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to do this in cd237ec 👍

I'm going to go ahead and merge this so I can start working on including intro offers :)

fire-at-will and others added 2 commits December 18, 2025 08:48
@fire-at-will fire-at-will merged commit 2638828 into samsung-dev Dec 18, 2025
0 of 2 checks passed
@fire-at-will fire-at-will deleted the promotion-eligibility branch December 18, 2025 18:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:feat A new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants