Skip to content

Remove temporary pixel to measure expired account restore while purchasing Privacy Pro #6159

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,6 @@ class RealSubscriptionsManager @Inject constructor(
private var purchaseStateJob: Job? = null

private var removeExpiredSubscriptionOnCancelledPurchase: Boolean = false
private var purchaseFlowStartedUsingRestoredAccount: Boolean = false

// Indicates whether the user is part of any FE experiment at the time of purchase
private var experimentAssigned: Experiment? = null
Expand Down Expand Up @@ -474,11 +473,6 @@ class RealSubscriptionsManager @Inject constructor(
}
pixelSender.reportPurchaseSuccess()
pixelSender.reportSubscriptionActivated()
if (purchaseFlowStartedUsingRestoredAccount) {
purchaseFlowStartedUsingRestoredAccount = false
val hasEmail = !authRepository.getAccount()?.email.isNullOrBlank()
pixelSender.reportPurchaseWithRestoredAccount(hasEmail)
}
emitEntitlementsValues()
_currentPurchaseState.emit(CurrentPurchase.Success)
} else {
Expand All @@ -494,7 +488,6 @@ class RealSubscriptionsManager @Inject constructor(
}

private suspend fun handlePurchaseFailed() {
purchaseFlowStartedUsingRestoredAccount = false
authRepository.purchaseToWaitingStatus()
pixelSender.reportPurchaseFailureBackend()
_currentPurchaseState.emit(CurrentPurchase.Waiting)
Expand Down Expand Up @@ -834,20 +827,16 @@ class RealSubscriptionsManager @Inject constructor(
isSignedInV1() -> fetchAndStoreAllData()
}

var restoredAccount = false

if (!isSignedIn()) {
recoverSubscriptionFromStore()
restoredAccount = isSignedIn()
} else {
authRepository.getSubscription()?.run {
if (status.isExpired() && platform == "google") {
// re-authenticate in case previous subscription was bought using different google account
val accountId = authRepository.getAccount()?.externalId
recoverSubscriptionFromStore()
val accountIdChanged = accountId != null && accountId != authRepository.getAccount()?.externalId
removeExpiredSubscriptionOnCancelledPurchase = accountIdChanged
restoredAccount = accountIdChanged
removeExpiredSubscriptionOnCancelledPurchase =
accountId != null && accountId != authRepository.getAccount()?.externalId
}
}
}
Expand All @@ -874,8 +863,6 @@ class RealSubscriptionsManager @Inject constructor(
Experiment(experimentName, experimentCohort)
}

purchaseFlowStartedUsingRestoredAccount = restoredAccount

logcat { "Subs: external id is ${authRepository.getAccount()!!.externalId}" }
_currentPurchaseState.emit(CurrentPurchase.PreFlowFinished)
playBillingManager.launchBillingFlow(
Expand All @@ -889,7 +876,6 @@ class RealSubscriptionsManager @Inject constructor(
logcat(ERROR) { "Subs: $error" }
pixelSender.reportPurchaseFailureOther()
_currentPurchaseState.emit(CurrentPurchase.Failure(error))
purchaseFlowStartedUsingRestoredAccount = false
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,11 +201,6 @@ enum class SubscriptionPixel(
type = Count,
includedParameters = setOf(ATB, APP_VERSION),
),
SUBSCRIPTION_PURCHASE_WITH_RESTORED_ACCOUNT(
baseName = "m_privacy-pro_app_purchase_with_restored_account",
type = Count,
includedParameters = setOf(APP_VERSION),
),
AUTH_V2_INVALID_REFRESH_TOKEN_DETECTED(
baseName = "m_privacy-pro_auth_invalid_refresh_token_detected",
types = setOf(Count, Daily()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package com.duckduckgo.subscriptions.impl.pixels

import com.duckduckgo.app.statistics.pixels.Pixel
import com.duckduckgo.appbuildconfig.api.AppBuildConfig
import com.duckduckgo.common.utils.extensions.toBinaryString
import com.duckduckgo.common.utils.extensions.toSanitizedLanguageTag
import com.duckduckgo.di.scopes.AppScope
import com.duckduckgo.subscriptions.impl.freetrial.FreeTrialPrivacyProPixelsPlugin
Expand Down Expand Up @@ -67,7 +66,6 @@ import com.duckduckgo.subscriptions.impl.pixels.SubscriptionPixel.SUBSCRIPTION_O
import com.duckduckgo.subscriptions.impl.pixels.SubscriptionPixel.SUBSCRIPTION_PRICE_MONTHLY_CLICK
import com.duckduckgo.subscriptions.impl.pixels.SubscriptionPixel.SUBSCRIPTION_PRICE_YEARLY_CLICK
import com.duckduckgo.subscriptions.impl.pixels.SubscriptionPixel.SUBSCRIPTION_PRIVACY_PRO_REDIRECT
import com.duckduckgo.subscriptions.impl.pixels.SubscriptionPixel.SUBSCRIPTION_PURCHASE_WITH_RESTORED_ACCOUNT
import com.duckduckgo.subscriptions.impl.pixels.SubscriptionPixel.SUBSCRIPTION_SETTINGS_CHANGE_PLAN_OR_BILLING_CLICK
import com.duckduckgo.subscriptions.impl.pixels.SubscriptionPixel.SUBSCRIPTION_SETTINGS_REMOVE_FROM_DEVICE_CLICK
import com.duckduckgo.subscriptions.impl.pixels.SubscriptionPixel.SUBSCRIPTION_SETTINGS_SHOWN
Expand Down Expand Up @@ -109,7 +107,6 @@ interface SubscriptionPixelSender {
fun reportOnboardingFaqClick()
fun reportAddEmailSuccess()
fun reportPrivacyProRedirect()
fun reportPurchaseWithRestoredAccount(hasEmail: Boolean)
fun reportAuthV2InvalidRefreshTokenDetected()
fun reportAuthV2InvalidRefreshTokenSignedOut()
fun reportAuthV2InvalidRefreshTokenRecovered()
Expand Down Expand Up @@ -242,9 +239,6 @@ class SubscriptionPixelSenderImpl @Inject constructor(
override fun reportPrivacyProRedirect() =
fire(SUBSCRIPTION_PRIVACY_PRO_REDIRECT)

override fun reportPurchaseWithRestoredAccount(hasEmail: Boolean) =
fire(SUBSCRIPTION_PURCHASE_WITH_RESTORED_ACCOUNT, mapOf("hasEmail" to hasEmail.toBinaryString()))

override fun reportAuthV2InvalidRefreshTokenDetected() {
fire(AUTH_V2_INVALID_REFRESH_TOKEN_DETECTED)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1184,39 +1184,6 @@ class RealSubscriptionsManagerTest(private val authApiV2Enabled: Boolean) {
}
}

@Test
fun whenPurchaseIsSuccessfulAndAccountWasRestoredThenPixelIsSent() = runTest {
givenUserIsNotSignedIn()
givenPurchaseStored()
givenStoreLoginSucceeds()
givenSubscriptionSucceedsWithoutEntitlements(status = EXPIRED.statusName)
givenConfirmPurchaseSucceeds()
givenAccessTokenSucceeds()
givenV2AccessTokenRefreshSucceeds()

val purchaseState = MutableSharedFlow<PurchaseState>()
whenever(playBillingManager.purchaseState).thenReturn(purchaseState)

subscriptionsManager.currentPurchaseState.test {
purchase()
assertTrue(awaitItem() is CurrentPurchase.PreFlowInProgress)
assertTrue(awaitItem() is CurrentPurchase.PreFlowFinished)

purchaseState.emit(PurchaseState.Purchased("any", "any"))
givenSubscriptionSucceedsWithEntitlements()

assertTrue(awaitItem() is CurrentPurchase.InProgress)
assertTrue(awaitItem() is CurrentPurchase.Success)

verify(pixelSender).reportPurchaseSuccess()
verify(pixelSender).reportPurchaseWithRestoredAccount(any())
verify(pixelSender).reportSubscriptionActivated()
verifyNoMoreInteractions(pixelSender)

cancelAndConsumeRemainingEvents()
}
}

@Test
fun whenPurchaseFailsThenPixelIsSent() = runTest {
givenUserIsSignedIn()
Expand Down
Loading