Skip to content

Commit

Permalink
Merge pull request #255 from Automattic/update/free-trial-eligibility…
Browse files Browse the repository at this point in the history
…-using-purchase-history

Update/free trial eligibility based on purchase history
  • Loading branch information
geekygecko authored Aug 22, 2022
2 parents 5fd062e + 04f142c commit 0c746b7
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class CreateFrequencyFragment : BaseFragment() {
title = subscription.productDetails.title,
price = subscription.recurringPricingPhase.pricingPhase.priceAmountMicros * 1_000_000.0,
currency = subscription.recurringPricingPhase.pricingPhase.priceCurrencyCode,
isFreeTrial = subscriptionManager.isFreeTrialEnabled() && subscription is Subscription.WithTrial
isFreeTrial = subscription is Subscription.WithTrial
)
it.findNavController().navigate(R.id.action_createFrequencyFragment_to_createTOSFragment)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ class CreateAccountViewModel
onSuccess = { productDetailsState ->
if (productDetailsState is ProductDetailsState.Loaded) {
val subscriptions = productDetailsState.productDetails
.mapNotNull { Subscription.fromProductDetails(it) }
.mapNotNull {
Subscription.fromProductDetails(
productDetails = it,
isFreeTrialEligible = subscriptionManager.isFreeTrialEligible()
)
}
subscriptionManager.getDefaultSubscription(subscriptions)?.let { updateSubscription(it) }
createAccountState.postValue(CreateAccountState.ProductsLoaded(subscriptions))
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ class AccountDetailsViewModel
private val subscription = subscriptionManager.observeProductDetails().map { state ->
if (state is ProductDetailsState.Loaded) {
val subscriptions = state.productDetails
.mapNotNull { Subscription.fromProductDetails(it) }
.mapNotNull {
Subscription.fromProductDetails(
productDetails = it,
isFreeTrialEligible = subscriptionManager.isFreeTrialEligible()
)
}
Optional.of(subscriptionManager.getDefaultSubscription(subscriptions))
} else {
Optional.empty()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ class PlusSettingsFragment : BaseFragment() {
val subscriptions = when (productDetailsState) {
is ProductDetailsState.Error -> null
is ProductDetailsState.Loaded -> productDetailsState.productDetails.mapNotNull {
Subscription.fromProductDetails(it)
Subscription.fromProductDetails(
productDetails = it,
isFreeTrialEligible = subscriptionManager.isFreeTrialEligible()
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ class UpgradeAccountViewModel
private val productDetails = subscriptionManager.observeProductDetails().map { productDetailsState ->
if (productDetailsState is ProductDetailsState.Loaded) {
val subscriptions = productDetailsState.productDetails
.mapNotNull { Subscription.fromProductDetails(it) }
.mapNotNull {
Subscription.fromProductDetails(
productDetails = it,
isFreeTrialEligible = subscriptionManager.isFreeTrialEligible()
)
}
val productState = when (val subscription = subscriptionManager.getDefaultSubscription(subscriptions)) {
null -> null
is Subscription.WithTrial -> ProductState.ProductWithTrial(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ sealed interface Subscription {
}

companion object {
fun fromProductDetails(productDetails: ProductDetails): Subscription? {
fun fromProductDetails(productDetails: ProductDetails, isFreeTrialEligible: Boolean): Subscription? {

val recurringPhase = productDetails.recurringSubscriptionPricingPhase?.fromPricingPhase()
val trialPhase = productDetails.trialSubscriptionPricingPhase?.fromPricingPhase()
Expand All @@ -57,7 +57,7 @@ sealed interface Subscription {
LogBuffer.e(LogBuffer.TAG_SUBSCRIPTIONS, "unable to convert product details to a subscription")
null
}
trialPhase is TrialSubscriptionPricingPhase -> WithTrial(
trialPhase is TrialSubscriptionPricingPhase && isFreeTrialEligible -> WithTrial(
recurringPricingPhase = recurringPhase,
trialPricingPhase = trialPhase,
productDetails = productDetails
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ import io.reactivex.Single
interface SubscriptionManager {

companion object {
const val MONTHLY_PRODUCT_ID = "com.pocketcasts.plus.monthly"
const val YEARLY_PRODUCT_ID = "com.pocketcasts.plus.yearly"
const val TEST_FREE_TRIAL_PRODUCT_ID = "com.pocketcasts.plus.testfreetrialoffer"
const val PLUS_PRODUCT_BASE = "com.pocketcasts.plus"
const val MONTHLY_PRODUCT_ID = "$PLUS_PRODUCT_BASE.monthly"
const val YEARLY_PRODUCT_ID = "$PLUS_PRODUCT_BASE.yearly"
const val TEST_FREE_TRIAL_PRODUCT_ID = "$PLUS_PRODUCT_BASE.testfreetrialoffer"
}

fun signOut()
Expand All @@ -37,6 +38,7 @@ interface SubscriptionManager {
fun launchBillingFlow(activity: Activity, productDetails: ProductDetails): BillingResult?
fun getCachedStatus(): SubscriptionStatus?
fun clearCachedStatus()
fun isFreeTrialEnabled(): Boolean
fun isFreeTrialEligible(): Boolean
fun updateFreeTrialEligible(eligible: Boolean)
fun getDefaultSubscription(subscriptions: List<Subscription>): Subscription?
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import au.com.shiftyjelly.pocketcasts.models.type.SubscriptionType
import au.com.shiftyjelly.pocketcasts.preferences.Settings
import au.com.shiftyjelly.pocketcasts.repositories.BuildConfig
import au.com.shiftyjelly.pocketcasts.repositories.subscription.SubscriptionManager.Companion.MONTHLY_PRODUCT_ID
import au.com.shiftyjelly.pocketcasts.repositories.subscription.SubscriptionManager.Companion.PLUS_PRODUCT_BASE
import au.com.shiftyjelly.pocketcasts.repositories.subscription.SubscriptionManager.Companion.TEST_FREE_TRIAL_PRODUCT_ID
import au.com.shiftyjelly.pocketcasts.repositories.subscription.SubscriptionManager.Companion.YEARLY_PRODUCT_ID
import au.com.shiftyjelly.pocketcasts.servers.sync.SubscriptionPurchaseRequest
Expand All @@ -31,6 +32,7 @@ import com.android.billingclient.api.Purchase
import com.android.billingclient.api.PurchasesResult
import com.android.billingclient.api.PurchasesUpdatedListener
import com.android.billingclient.api.QueryProductDetailsParams
import com.android.billingclient.api.QueryPurchaseHistoryParams
import com.android.billingclient.api.QueryPurchasesParams
import com.android.billingclient.api.queryPurchasesAsync
import com.jakewharton.rxrelay2.BehaviorRelay
Expand Down Expand Up @@ -72,6 +74,7 @@ class SubscriptionManagerImpl @Inject constructor(private val syncServerManager:
private val productDetails = BehaviorRelay.create<ProductDetailsState>()
private val purchaseEvents = PublishRelay.create<PurchaseEvent>()
private val subscriptionChangedEvents = PublishRelay.create<SubscriptionChangedEvent>()
private var freeTrialEligible: Boolean = true

override fun signOut() {
clearCachedStatus()
Expand Down Expand Up @@ -156,7 +159,7 @@ class SubscriptionManagerImpl @Inject constructor(private val syncServerManager:
.setProductType(BillingClient.ProductType.SUBS)
.build(),
).apply {
if (isFreeTrialEnabled()) {
if (isFreeTrialEligible()) {
add(
QueryProductDetailsParams.Product.newBuilder()
.setProductId(TEST_FREE_TRIAL_PRODUCT_ID)
Expand Down Expand Up @@ -247,6 +250,7 @@ class SubscriptionManagerImpl @Inject constructor(private val syncServerManager:
.build()
billingClient.acknowledgePurchase(acknowledgePurchaseParams, this@SubscriptionManagerImpl)
}
updateFreeTrialEligible(false)
AnalyticsHelper.plusPurchased()
} catch (e: Exception) {
LogBuffer.e(LogBuffer.TAG_SUBSCRIPTIONS, e, "Could not send purchase info")
Expand All @@ -272,6 +276,8 @@ class SubscriptionManagerImpl @Inject constructor(private val syncServerManager:
override fun refreshPurchases() {
if (!billingClient.isReady) return

updateFreeTrialEligibilityIfPurchaseHistoryExists()

val queryPurchasesParams = QueryPurchasesParams.newBuilder()
.setProductType(BillingClient.ProductType.SUBS)
.build()
Expand All @@ -284,6 +290,18 @@ class SubscriptionManagerImpl @Inject constructor(private val syncServerManager:
}
}

private fun updateFreeTrialEligibilityIfPurchaseHistoryExists() {
val queryPurchaseHistoryParams = QueryPurchaseHistoryParams.newBuilder()
.setProductType(BillingClient.ProductType.SUBS)
.build()

billingClient.queryPurchaseHistoryAsync(queryPurchaseHistoryParams) { _, purchases ->
if (purchases?.any { it.products.toString().contains(PLUS_PRODUCT_BASE) } == true) {
updateFreeTrialEligible(false)
}
}
}

override suspend fun getPurchases(): PurchasesResult? {
if (!billingClient.isReady) return null

Expand Down Expand Up @@ -325,7 +343,11 @@ class SubscriptionManagerImpl @Inject constructor(private val syncServerManager:
subscriptionStatus.accept(Optional.empty())
}

override fun isFreeTrialEnabled() = BuildConfig.ENABLE_FREE_TRIAL
override fun isFreeTrialEligible() = freeTrialEligible && BuildConfig.ENABLE_FREE_TRIAL

override fun updateFreeTrialEligible(eligible: Boolean) {
freeTrialEligible = eligible
}

override fun getDefaultSubscription(subscriptions: List<Subscription>): Subscription? {
val trialsIfPresent = subscriptions
Expand Down

0 comments on commit 0c746b7

Please sign in to comment.