Skip to content

Commit c111ed4

Browse files
authored
Send pixel when existing account is restored during successful purchase (#5806)
Task/Issue URL: https://app.asana.com/0/1205648422731273/1209714124375337/f ### Description This PR adds a temporary pixel to measure how often existing account is restored when purchasing subscription. ### Steps to test this PR See task ### No UI changes
1 parent acc0073 commit c111ed4

File tree

4 files changed

+60
-2
lines changed

4 files changed

+60
-2
lines changed

subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/SubscriptionsManager.kt

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ class RealSubscriptionsManager @Inject constructor(
269269
private var purchaseStateJob: Job? = null
270270

271271
private var removeExpiredSubscriptionOnCancelledPurchase: Boolean = false
272+
private var purchaseFlowStartedUsingRestoredAccount: Boolean = false
272273

273274
override suspend fun isSignedIn(): Boolean {
274275
return isSignedInV1() || isSignedInV2()
@@ -469,6 +470,11 @@ class RealSubscriptionsManager @Inject constructor(
469470
}
470471
pixelSender.reportPurchaseSuccess()
471472
pixelSender.reportSubscriptionActivated()
473+
if (purchaseFlowStartedUsingRestoredAccount) {
474+
purchaseFlowStartedUsingRestoredAccount = false
475+
val hasEmail = !authRepository.getAccount()?.email.isNullOrBlank()
476+
pixelSender.reportPurchaseWithRestoredAccount(hasEmail)
477+
}
472478
emitEntitlementsValues()
473479
_currentPurchaseState.emit(CurrentPurchase.Success)
474480
} else {
@@ -484,6 +490,7 @@ class RealSubscriptionsManager @Inject constructor(
484490
}
485491

486492
private suspend fun handlePurchaseFailed() {
493+
purchaseFlowStartedUsingRestoredAccount = false
487494
authRepository.purchaseToWaitingStatus()
488495
pixelSender.reportPurchaseFailureBackend()
489496
_currentPurchaseState.emit(CurrentPurchase.Waiting)
@@ -819,16 +826,20 @@ class RealSubscriptionsManager @Inject constructor(
819826
isSignedInV1() -> fetchAndStoreAllData()
820827
}
821828

829+
var restoredAccount = false
830+
822831
if (!isSignedIn()) {
823832
recoverSubscriptionFromStore()
833+
restoredAccount = isSignedIn()
824834
} else {
825835
authRepository.getSubscription()?.run {
826836
if (status.isExpired() && platform == "google") {
827837
// re-authenticate in case previous subscription was bought using different google account
828838
val accountId = authRepository.getAccount()?.externalId
829839
recoverSubscriptionFromStore()
830-
removeExpiredSubscriptionOnCancelledPurchase =
831-
accountId != null && accountId != authRepository.getAccount()?.externalId
840+
val accountIdChanged = accountId != null && accountId != authRepository.getAccount()?.externalId
841+
removeExpiredSubscriptionOnCancelledPurchase = accountIdChanged
842+
restoredAccount = accountIdChanged
832843
}
833844
}
834845
}
@@ -849,6 +860,8 @@ class RealSubscriptionsManager @Inject constructor(
849860
}
850861
}
851862

863+
purchaseFlowStartedUsingRestoredAccount = restoredAccount
864+
852865
logcat(LogPriority.DEBUG) { "Subs: external id is ${authRepository.getAccount()!!.externalId}" }
853866
_currentPurchaseState.emit(CurrentPurchase.PreFlowFinished)
854867
playBillingManager.launchBillingFlow(
@@ -862,6 +875,7 @@ class RealSubscriptionsManager @Inject constructor(
862875
logcat(LogPriority.ERROR) { "Subs: $error" }
863876
pixelSender.reportPurchaseFailureOther()
864877
_currentPurchaseState.emit(CurrentPurchase.Failure(error))
878+
purchaseFlowStartedUsingRestoredAccount = false
865879
}
866880
}
867881

subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/pixels/SubscriptionPixel.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,11 @@ enum class SubscriptionPixel(
201201
type = Count,
202202
includedParameters = setOf(ATB, APP_VERSION),
203203
),
204+
SUBSCRIPTION_PURCHASE_WITH_RESTORED_ACCOUNT(
205+
baseName = "m_privacy-pro_app_purchase_with_restored_account",
206+
type = Count,
207+
includedParameters = setOf(APP_VERSION),
208+
),
204209
AUTH_V2_INVALID_REFRESH_TOKEN_DETECTED(
205210
baseName = "m_privacy-pro_auth_invalid_refresh_token_detected",
206211
types = setOf(Count, Daily()),

subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/pixels/SubscriptionPixelSender.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package com.duckduckgo.subscriptions.impl.pixels
1818

1919
import com.duckduckgo.app.statistics.pixels.Pixel
2020
import com.duckduckgo.appbuildconfig.api.AppBuildConfig
21+
import com.duckduckgo.common.utils.extensions.toBinaryString
2122
import com.duckduckgo.common.utils.extensions.toSanitizedLanguageTag
2223
import com.duckduckgo.di.scopes.AppScope
2324
import com.duckduckgo.subscriptions.impl.freetrial.FreeTrialPrivacyProPixelsPlugin
@@ -66,6 +67,7 @@ import com.duckduckgo.subscriptions.impl.pixels.SubscriptionPixel.SUBSCRIPTION_O
6667
import com.duckduckgo.subscriptions.impl.pixels.SubscriptionPixel.SUBSCRIPTION_PRICE_MONTHLY_CLICK
6768
import com.duckduckgo.subscriptions.impl.pixels.SubscriptionPixel.SUBSCRIPTION_PRICE_YEARLY_CLICK
6869
import com.duckduckgo.subscriptions.impl.pixels.SubscriptionPixel.SUBSCRIPTION_PRIVACY_PRO_REDIRECT
70+
import com.duckduckgo.subscriptions.impl.pixels.SubscriptionPixel.SUBSCRIPTION_PURCHASE_WITH_RESTORED_ACCOUNT
6971
import com.duckduckgo.subscriptions.impl.pixels.SubscriptionPixel.SUBSCRIPTION_SETTINGS_CHANGE_PLAN_OR_BILLING_CLICK
7072
import com.duckduckgo.subscriptions.impl.pixels.SubscriptionPixel.SUBSCRIPTION_SETTINGS_REMOVE_FROM_DEVICE_CLICK
7173
import com.duckduckgo.subscriptions.impl.pixels.SubscriptionPixel.SUBSCRIPTION_SETTINGS_SHOWN
@@ -107,6 +109,7 @@ interface SubscriptionPixelSender {
107109
fun reportOnboardingFaqClick()
108110
fun reportAddEmailSuccess()
109111
fun reportPrivacyProRedirect()
112+
fun reportPurchaseWithRestoredAccount(hasEmail: Boolean)
110113
fun reportAuthV2InvalidRefreshTokenDetected()
111114
fun reportAuthV2InvalidRefreshTokenSignedOut()
112115
fun reportAuthV2InvalidRefreshTokenRecovered()
@@ -239,6 +242,9 @@ class SubscriptionPixelSenderImpl @Inject constructor(
239242
override fun reportPrivacyProRedirect() =
240243
fire(SUBSCRIPTION_PRIVACY_PRO_REDIRECT)
241244

245+
override fun reportPurchaseWithRestoredAccount(hasEmail: Boolean) =
246+
fire(SUBSCRIPTION_PURCHASE_WITH_RESTORED_ACCOUNT, mapOf("hasEmail" to hasEmail.toBinaryString()))
247+
242248
override fun reportAuthV2InvalidRefreshTokenDetected() {
243249
fire(AUTH_V2_INVALID_REFRESH_TOKEN_DETECTED)
244250
}

subscriptions/subscriptions-impl/src/test/java/com/duckduckgo/subscriptions/impl/RealSubscriptionsManagerTest.kt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,6 +1183,39 @@ class RealSubscriptionsManagerTest(private val authApiV2Enabled: Boolean) {
11831183
}
11841184
}
11851185

1186+
@Test
1187+
fun whenPurchaseIsSuccessfulAndAccountWasRestoredThenPixelIsSent() = runTest {
1188+
givenUserIsNotSignedIn()
1189+
givenPurchaseStored()
1190+
givenStoreLoginSucceeds()
1191+
givenSubscriptionSucceedsWithoutEntitlements(status = EXPIRED.statusName)
1192+
givenConfirmPurchaseSucceeds()
1193+
givenAccessTokenSucceeds()
1194+
givenV2AccessTokenRefreshSucceeds()
1195+
1196+
val purchaseState = MutableSharedFlow<PurchaseState>()
1197+
whenever(playBillingManager.purchaseState).thenReturn(purchaseState)
1198+
1199+
subscriptionsManager.currentPurchaseState.test {
1200+
subscriptionsManager.purchase(mock(), planId = "", offerId = null)
1201+
assertTrue(awaitItem() is CurrentPurchase.PreFlowInProgress)
1202+
assertTrue(awaitItem() is CurrentPurchase.PreFlowFinished)
1203+
1204+
purchaseState.emit(PurchaseState.Purchased("any", "any"))
1205+
givenSubscriptionSucceedsWithEntitlements()
1206+
1207+
assertTrue(awaitItem() is CurrentPurchase.InProgress)
1208+
assertTrue(awaitItem() is CurrentPurchase.Success)
1209+
1210+
verify(pixelSender).reportPurchaseSuccess()
1211+
verify(pixelSender).reportPurchaseWithRestoredAccount(any())
1212+
verify(pixelSender).reportSubscriptionActivated()
1213+
verifyNoMoreInteractions(pixelSender)
1214+
1215+
cancelAndConsumeRemainingEvents()
1216+
}
1217+
}
1218+
11861219
@Test
11871220
fun whenPurchaseFailsThenPixelIsSent() = runTest {
11881221
givenUserIsSignedIn()

0 commit comments

Comments
 (0)