Skip to content
Open
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 @@ -60,5 +60,7 @@ internal data class TrackingEventParams(
@SerialName("button_type")
val buttonType: String? = null,
@SerialName("app_switch_enabled")
val appSwitchEnabled: Boolean = false
val appSwitchEnabled: Boolean = false,
@SerialName("button_session_id")
val buttonSessionId: String? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ internal class TrackingEventsAPI constructor(
tenantName = TENANT_NAME_PAYPAL,
orderId = event.orderId,
buttonType = event.buttonType,
appSwitchEnabled = event.appSwitchEnabled
appSwitchEnabled = event.appSwitchEnabled,
buttonSessionId = event.buttonSessionId
)

val events = TrackingEvents(eventParams = eventParams)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ internal data class AnalyticsEventData(
val timestamp: Long,
val orderId: String?,
val buttonType: String? = null,
val appSwitchEnabled: Boolean
val appSwitchEnabled: Boolean,
val buttonSessionId: String? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ class AnalyticsService internal constructor(
name: String,
orderId: String? = null,
buttonType: String? = null,
appSwitchEnabled: Boolean = false
appSwitchEnabled: Boolean = false,
buttonSessionId: String? = null
) {
// TODO: send analytics event using WorkManager (supports coroutines) to avoid lint error
// thrown because we don't use the Deferred result
Expand All @@ -58,7 +59,8 @@ class AnalyticsService internal constructor(
timestamp,
orderId = orderId,
buttonType = buttonType,
appSwitchEnabled = appSwitchEnabled
appSwitchEnabled = appSwitchEnabled,
buttonSessionId = buttonSessionId
)
val response = trackingEventsAPI.sendEvent(analyticsEventData, deviceData)
response.error?.message?.let { errorMessage ->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.paypal.android.corepayments.analytics

import androidx.annotation.RestrictTo
import java.util.UUID

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
object ButtonSessionStore {

@Volatile
var buttonSessionId: String = UUID.randomUUID().toString()
private set

fun resetSession() {
buttonSessionId = UUID.randomUUID().toString()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.paypal.android.corepayments.analytics

import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotEquals
import org.junit.Test

class ButtonSessionStoreUnitTest {

@Test
fun `buttonSessionId is initialized with a non-blank value`() {
assertFalse(ButtonSessionStore.buttonSessionId.isBlank())
}

@Test
fun `resetSession changes buttonSessionId`() {
val before = ButtonSessionStore.buttonSessionId
ButtonSessionStore.resetSession()
assertNotEquals(before, ButtonSessionStore.buttonSessionId)
}

@Test
fun `buttonSessionId is non-blank after resetSession`() {
ButtonSessionStore.resetSession()
assertFalse(ButtonSessionStore.buttonSessionId.isBlank())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.paypal.android.corepayments.Environment
import com.paypal.android.corepayments.ReturnToAppStrategy
import com.paypal.android.corepayments.UpdateClientConfigAPI
import com.paypal.android.corepayments.analytics.AnalyticsService
import com.paypal.android.corepayments.analytics.ButtonSessionStore
import com.paypal.android.corepayments.api.PatchCCOWithAppSwitchEligibility
import com.paypal.android.corepayments.common.DeviceInspector
import com.paypal.android.corepayments.model.APIResult
Expand Down Expand Up @@ -125,7 +126,7 @@ class PayPalWebCheckoutClient internal constructor(
): PayPalPresentAuthChallengeResult {
checkoutOrderId = request.orderId
appSwitchEnabled = false
analytics.notify(CheckoutEvent.STARTED, checkoutOrderId, appSwitchEnabled)
analytics.notify(CheckoutEvent.STARTED, checkoutOrderId, appSwitchEnabled, ButtonSessionStore.buttonSessionId)

val returnToAppStrategy = resolveReturnToAppStrategy(request.returnToAppStrategy)
?: return PayPalPresentAuthChallengeResult.Failure(PayPalWebCheckoutError.noReturnToAppStrategyError)
Expand All @@ -149,7 +150,8 @@ class PayPalWebCheckoutClient internal constructor(
analytics.notify(
CheckoutEvent.AUTH_CHALLENGE_PRESENTATION_SUCCEEDED,
checkoutOrderId,
appSwitchEnabled
appSwitchEnabled,
ButtonSessionStore.buttonSessionId
)

// update auth state value in session store
Expand All @@ -160,7 +162,8 @@ class PayPalWebCheckoutClient internal constructor(
analytics.notify(
CheckoutEvent.AUTH_CHALLENGE_PRESENTATION_FAILED,
checkoutOrderId,
appSwitchEnabled
appSwitchEnabled,
ButtonSessionStore.buttonSessionId
)
}
}
Expand All @@ -179,7 +182,7 @@ class PayPalWebCheckoutClient internal constructor(
): PayPalPresentAuthChallengeResult {

checkoutOrderId = request.orderId
analytics.notify(CheckoutEvent.STARTED, checkoutOrderId, appSwitchEnabled)
analytics.notify(CheckoutEvent.STARTED, checkoutOrderId, appSwitchEnabled, ButtonSessionStore.buttonSessionId)

val returnToAppStrategy = resolveReturnToAppStrategy(request.returnToAppStrategy)
?: return PayPalPresentAuthChallengeResult.Failure(PayPalWebCheckoutError.noReturnToAppStrategyError)
Expand Down Expand Up @@ -222,7 +225,8 @@ class PayPalWebCheckoutClient internal constructor(
analytics.notify(
CheckoutEvent.AUTH_CHALLENGE_PRESENTATION_SUCCEEDED,
checkoutOrderId,
appSwitchEnabled
appSwitchEnabled,
ButtonSessionStore.buttonSessionId
)

// update auth state value in session store
Expand All @@ -233,7 +237,8 @@ class PayPalWebCheckoutClient internal constructor(
analytics.notify(
CheckoutEvent.AUTH_CHALLENGE_PRESENTATION_FAILED,
checkoutOrderId,
appSwitchEnabled
appSwitchEnabled,
ButtonSessionStore.buttonSessionId
)
}
}
Expand Down Expand Up @@ -275,7 +280,7 @@ class PayPalWebCheckoutClient internal constructor(
request: PayPalWebVaultRequest
): PayPalPresentAuthChallengeResult {
vaultSetupTokenId = request.setupTokenId
analytics.notify(VaultEvent.STARTED, vaultSetupTokenId, appSwitchEnabled)
analytics.notify(VaultEvent.STARTED, vaultSetupTokenId, appSwitchEnabled, ButtonSessionStore.buttonSessionId)

val returnToAppStrategy = resolveReturnToAppStrategy(request.returnToAppStrategy)
?: return PayPalPresentAuthChallengeResult.Failure(PayPalWebCheckoutError.noReturnToAppStrategyError)
Expand All @@ -295,7 +300,8 @@ class PayPalWebCheckoutClient internal constructor(
analytics.notify(
VaultEvent.AUTH_CHALLENGE_PRESENTATION_SUCCEEDED,
vaultSetupTokenId,
appSwitchEnabled
appSwitchEnabled,
ButtonSessionStore.buttonSessionId
)

// update auth state value in session store
Expand All @@ -306,7 +312,8 @@ class PayPalWebCheckoutClient internal constructor(
analytics.notify(
VaultEvent.AUTH_CHALLENGE_PRESENTATION_FAILED,
vaultSetupTokenId,
appSwitchEnabled
appSwitchEnabled,
ButtonSessionStore.buttonSessionId
)
}
}
Expand All @@ -325,7 +332,7 @@ class PayPalWebCheckoutClient internal constructor(
request: PayPalWebVaultRequest
): PayPalPresentAuthChallengeResult {
vaultSetupTokenId = request.setupTokenId
analytics.notify(VaultEvent.STARTED, vaultSetupTokenId, appSwitchEnabled)
analytics.notify(VaultEvent.STARTED, vaultSetupTokenId, appSwitchEnabled, ButtonSessionStore.buttonSessionId)

val returnToAppStrategy = resolveReturnToAppStrategy(request.returnToAppStrategy)
?: return PayPalPresentAuthChallengeResult.Failure(PayPalWebCheckoutError.noReturnToAppStrategyError)
Expand All @@ -352,7 +359,8 @@ class PayPalWebCheckoutClient internal constructor(
analytics.notify(
VaultEvent.AUTH_CHALLENGE_PRESENTATION_SUCCEEDED,
vaultSetupTokenId,
appSwitchEnabled
appSwitchEnabled,
ButtonSessionStore.buttonSessionId
)

// update auth state value in session store
Expand All @@ -363,7 +371,8 @@ class PayPalWebCheckoutClient internal constructor(
analytics.notify(
VaultEvent.AUTH_CHALLENGE_PRESENTATION_FAILED,
vaultSetupTokenId,
appSwitchEnabled
appSwitchEnabled,
ButtonSessionStore.buttonSessionId
)
}
}
Expand Down Expand Up @@ -407,14 +416,35 @@ class PayPalWebCheckoutClient internal constructor(
fun finishStart(intent: Intent, authState: String): PayPalWebCheckoutFinishStartResult {
val result = payPalWebLauncher.completeCheckoutAuthRequest(intent, authState)
when (result) {
is PayPalWebCheckoutFinishStartResult.Success ->
analytics.notify(CheckoutEvent.SUCCEEDED, checkoutOrderId, appSwitchEnabled)
is PayPalWebCheckoutFinishStartResult.Success -> {
analytics.notify(
CheckoutEvent.SUCCEEDED,
checkoutOrderId,
appSwitchEnabled,
ButtonSessionStore.buttonSessionId
)
ButtonSessionStore.resetSession()
}

is PayPalWebCheckoutFinishStartResult.Canceled ->
analytics.notify(CheckoutEvent.CANCELED, checkoutOrderId, appSwitchEnabled)
is PayPalWebCheckoutFinishStartResult.Canceled -> {
analytics.notify(
CheckoutEvent.CANCELED,
checkoutOrderId,
appSwitchEnabled,
ButtonSessionStore.buttonSessionId
)
ButtonSessionStore.resetSession()
}

is PayPalWebCheckoutFinishStartResult.Failure ->
analytics.notify(CheckoutEvent.FAILED, checkoutOrderId, appSwitchEnabled)
is PayPalWebCheckoutFinishStartResult.Failure -> {
analytics.notify(
CheckoutEvent.FAILED,
checkoutOrderId,
appSwitchEnabled,
ButtonSessionStore.buttonSessionId
)
ButtonSessionStore.resetSession()
}

PayPalWebCheckoutFinishStartResult.NoResult -> {
// no analytics tracking required at the moment
Expand All @@ -439,27 +469,33 @@ class PayPalWebCheckoutClient internal constructor(
analytics.notify(
CheckoutEvent.SUCCEEDED,
checkoutOrderId,
appSwitchEnabled
appSwitchEnabled,
ButtonSessionStore.buttonSessionId
)
sessionStore.clear()
ButtonSessionStore.resetSession()
}

is PayPalWebCheckoutFinishStartResult.Canceled -> {
analytics.notify(
CheckoutEvent.CANCELED,
checkoutOrderId,
appSwitchEnabled
appSwitchEnabled,
ButtonSessionStore.buttonSessionId
)
sessionStore.clear()
ButtonSessionStore.resetSession()
}

is PayPalWebCheckoutFinishStartResult.Failure -> {
analytics.notify(
CheckoutEvent.FAILED,
checkoutOrderId,
appSwitchEnabled
appSwitchEnabled,
ButtonSessionStore.buttonSessionId
)
sessionStore.clear()
ButtonSessionStore.resetSession()
}

PayPalWebCheckoutFinishStartResult.NoResult -> {
Expand Down Expand Up @@ -489,13 +525,28 @@ class PayPalWebCheckoutClient internal constructor(
// TODO: see if we can get setup token id from somewhere for tracking
when (result) {
is PayPalWebCheckoutFinishVaultResult.Success ->
analytics.notify(VaultEvent.SUCCEEDED, vaultSetupTokenId, appSwitchEnabled)
analytics.notify(
VaultEvent.SUCCEEDED,
vaultSetupTokenId,
appSwitchEnabled,
ButtonSessionStore.buttonSessionId
)

is PayPalWebCheckoutFinishVaultResult.Failure ->
analytics.notify(VaultEvent.FAILED, vaultSetupTokenId, appSwitchEnabled)
analytics.notify(
VaultEvent.FAILED,
vaultSetupTokenId,
appSwitchEnabled,
ButtonSessionStore.buttonSessionId
)

PayPalWebCheckoutFinishVaultResult.Canceled ->
analytics.notify(VaultEvent.CANCELED, vaultSetupTokenId, appSwitchEnabled)
analytics.notify(
VaultEvent.CANCELED,
vaultSetupTokenId,
appSwitchEnabled,
ButtonSessionStore.buttonSessionId
)

PayPalWebCheckoutFinishVaultResult.NoResult -> {
// no analytics tracking required at the moment
Expand Down Expand Up @@ -580,17 +631,32 @@ class PayPalWebCheckoutClient internal constructor(
val result = payPalWebLauncher.completeVaultAuthRequest(intent, authState)
when (result) {
is PayPalWebCheckoutFinishVaultResult.Success -> {
analytics.notify(VaultEvent.SUCCEEDED, vaultSetupTokenId, appSwitchEnabled)
analytics.notify(
VaultEvent.SUCCEEDED,
vaultSetupTokenId,
appSwitchEnabled,
ButtonSessionStore.buttonSessionId
)
sessionStore.clear()
}

is PayPalWebCheckoutFinishVaultResult.Failure -> {
analytics.notify(VaultEvent.FAILED, vaultSetupTokenId, appSwitchEnabled)
analytics.notify(
VaultEvent.FAILED,
vaultSetupTokenId,
appSwitchEnabled,
ButtonSessionStore.buttonSessionId
)
sessionStore.clear()
}

PayPalWebCheckoutFinishVaultResult.Canceled -> {
analytics.notify(VaultEvent.CANCELED, vaultSetupTokenId, appSwitchEnabled)
analytics.notify(
VaultEvent.CANCELED,
vaultSetupTokenId,
appSwitchEnabled,
ButtonSessionStore.buttonSessionId
)
sessionStore.clear()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@ import com.paypal.android.corepayments.analytics.AnalyticsService

internal class PayPalWebAnalytics(private val analyticsService: AnalyticsService) {

fun notify(event: CheckoutEvent, orderId: String?, appSwitchEnabled: Boolean) {
fun notify(event: CheckoutEvent, orderId: String?, appSwitchEnabled: Boolean, buttonSessionId: String?) {
analyticsService.sendAnalyticsEvent(
name = event.value,
orderId = orderId,
appSwitchEnabled = appSwitchEnabled
appSwitchEnabled = appSwitchEnabled,
buttonSessionId = buttonSessionId
)
}

fun notify(event: VaultEvent, setupTokenId: String?, appSwitchEnabled: Boolean) {
fun notify(event: VaultEvent, setupTokenId: String?, appSwitchEnabled: Boolean, buttonSessionId: String?) {
analyticsService.sendAnalyticsEvent(
name = event.value,
orderId = setupTokenId,
appSwitchEnabled = appSwitchEnabled
appSwitchEnabled = appSwitchEnabled,
buttonSessionId = buttonSessionId
)
}
}
Loading
Loading