Skip to content

Commit a01ed11

Browse files
authored
Fix app switch vault analytics (#1271)
* Refactor PayPalPaymentResource to expose boolean if we should be in a app-switch flow * modify logic on how isAppSwitch is calculated * Add comments and CHANGELOG * fix compilation issues
1 parent af50938 commit a01ed11

File tree

10 files changed

+127
-37
lines changed

10 files changed

+127
-37
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.braintreepayments.api.core
2+
3+
/**
4+
* Repository to hold the state of the app switch flow.
5+
*/
6+
class AppSwitchRepository {
7+
var isAppSwitchFlow = false
8+
9+
companion object {
10+
val instance by lazy { AppSwitchRepository() }
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.braintreepayments.api.core
2+
3+
class GetAppSwitchUseCase(private val appSwitchRepository: AppSwitchRepository) {
4+
operator fun invoke(): Boolean {
5+
return appSwitchRepository.isAppSwitchFlow
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.braintreepayments.api.core
2+
3+
class SetAppSwitchUseCase(private val appSwitchRepository: AppSwitchRepository) {
4+
5+
operator fun invoke(appSwitchFlow: Boolean) {
6+
appSwitchRepository.isAppSwitchFlow = appSwitchFlow
7+
}
8+
}

Diff for: CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* Require `PayPalCheckoutRequest.userAuthenticationEmail` for App Switch support
99
* **Note:** This feature is currently in beta and may change or be removed in future releases.
1010
* Send `payPalContextId` in `PayPalLauncher.handleReturnToApp` related events.
11+
* Fix a bug to correctly log app switch flow analytics.
1112

1213
## 5.6.0 (2025-02-05)
1314

Diff for: PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalClient.kt

+5-2
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import android.net.Uri
55
import android.text.TextUtils
66
import com.braintreepayments.api.BrowserSwitchOptions
77
import com.braintreepayments.api.core.AnalyticsEventParams
8+
import com.braintreepayments.api.core.AppSwitchRepository
89
import com.braintreepayments.api.core.BraintreeClient
910
import com.braintreepayments.api.core.BraintreeException
1011
import com.braintreepayments.api.core.BraintreeRequestCodes
1112
import com.braintreepayments.api.core.Configuration
1213
import com.braintreepayments.api.core.ExperimentalBetaApi
14+
import com.braintreepayments.api.core.GetAppSwitchUseCase
1315
import com.braintreepayments.api.core.GetReturnLinkUseCase
1416
import com.braintreepayments.api.core.LinkType
1517
import com.braintreepayments.api.core.MerchantRepository
@@ -26,7 +28,8 @@ class PayPalClient internal constructor(
2628
private val braintreeClient: BraintreeClient,
2729
private val internalPayPalClient: PayPalInternalClient = PayPalInternalClient(braintreeClient),
2830
private val merchantRepository: MerchantRepository = MerchantRepository.instance,
29-
private val getReturnLinkUseCase: GetReturnLinkUseCase = GetReturnLinkUseCase(merchantRepository)
31+
private val getReturnLinkUseCase: GetReturnLinkUseCase = GetReturnLinkUseCase(merchantRepository),
32+
private val getAppSwitchUseCase: GetAppSwitchUseCase = GetAppSwitchUseCase(AppSwitchRepository.instance)
3033
) {
3134
/**
3235
* Used for linking events from the client to server side request
@@ -125,7 +128,7 @@ class PayPalClient internal constructor(
125128
error: Exception? ->
126129
if (payPalResponse != null) {
127130
payPalContextId = payPalResponse.paypalContextId
128-
val isAppSwitchFlow = internalPayPalClient.isAppSwitchEnabled(payPalRequest) &&
131+
val isAppSwitchFlow = getAppSwitchUseCase() && internalPayPalClient.isAppSwitchEnabled(payPalRequest) &&
129132
internalPayPalClient.isPayPalInstalled(context)
130133
linkType = if (isAppSwitchFlow) LinkType.APP_SWITCH else LinkType.APP_LINK
131134

Diff for: PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalInternalClient.kt

+5-5
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ package com.braintreepayments.api.paypal
33
import android.content.Context
44
import android.net.Uri
55
import com.braintreepayments.api.core.ApiClient
6+
import com.braintreepayments.api.core.AppSwitchRepository
67
import com.braintreepayments.api.core.BraintreeClient
78
import com.braintreepayments.api.core.BraintreeException
89
import com.braintreepayments.api.core.Configuration
910
import com.braintreepayments.api.core.DeviceInspector
1011
import com.braintreepayments.api.core.GetReturnLinkUseCase
1112
import com.braintreepayments.api.core.MerchantRepository
13+
import com.braintreepayments.api.core.SetAppSwitchUseCase
1214
import com.braintreepayments.api.datacollector.DataCollector
1315
import com.braintreepayments.api.datacollector.DataCollectorInternalRequest
1416
import org.json.JSONException
@@ -21,6 +23,7 @@ internal class PayPalInternalClient(
2123
private val deviceInspector: DeviceInspector = DeviceInspector(),
2224
private val merchantRepository: MerchantRepository = MerchantRepository.instance,
2325
private val getReturnLinkUseCase: GetReturnLinkUseCase = GetReturnLinkUseCase(merchantRepository),
26+
private val setAppSwitchUseCase: SetAppSwitchUseCase = SetAppSwitchUseCase(AppSwitchRepository.instance),
2427
private val payPalTokenResponseRepository: PayPalTokenResponseRepository = PayPalTokenResponseRepository.instance,
2528
private val payPalSetPaymentTokenUseCase: PayPalSetPaymentTokenUseCase = PayPalSetPaymentTokenUseCase(
2629
payPalTokenResponseRepository
@@ -125,6 +128,7 @@ internal class PayPalInternalClient(
125128
try {
126129
val paypalPaymentResource = PayPalPaymentResource.fromJson(responseBody)
127130
val parsedRedirectUri = Uri.parse(paypalPaymentResource.redirectUrl)
131+
setAppSwitchUseCase(paypalPaymentResource.isAppSwitchFlow)
128132
val paypalContextId = extractPayPalContextId(parsedRedirectUri)
129133
payPalSetPaymentTokenUseCase.setPaymentToken(paypalContextId)
130134
val clientMetadataId = payPalRequest.riskCorrelationId ?: run {
@@ -134,11 +138,7 @@ internal class PayPalInternalClient(
134138
applicationGuid = dataCollector.getPayPalInstallationGUID(context)
135139
clientMetadataId = paypalContextId
136140
}
137-
dataCollector.getClientMetadataId(
138-
context = context,
139-
request = dataCollectorRequest,
140-
configuration = configuration
141-
)
141+
dataCollector.getClientMetadataId(context, dataCollectorRequest, configuration)
142142
}
143143
val returnLink: String = when (val returnLinkResult = getReturnLinkUseCase()) {
144144
is GetReturnLinkUseCase.ReturnLinkResult.AppLink -> returnLinkResult.appLinkReturnUri.toString()

Diff for: PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalPaymentResource.kt

+15-9
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import org.json.JSONObject
1111
* containing an EC token
1212
*/
1313
internal data class PayPalPaymentResource(
14-
val redirectUrl: String
14+
val redirectUrl: String,
15+
val isAppSwitchFlow: Boolean = false
1516
) {
1617

1718
companion object {
@@ -20,6 +21,7 @@ internal data class PayPalPaymentResource(
2021
private const val AGREEMENT_SETUP_KEY = "agreementSetup"
2122
private const val APPROVAL_URL_KEY = "approvalUrl"
2223
private const val PAYPAL_APP_APPROVAL_URL_KEY = "paypalAppApprovalUrl"
24+
private const val LAUNCH_PAYPAL_APP_KEY = "launchPayPalApp"
2325

2426
/**
2527
* Create a PayPalPaymentResource from a jsonString. Checks for keys associated with Single
@@ -34,20 +36,24 @@ internal data class PayPalPaymentResource(
3436
fun fromJson(jsonString: String): PayPalPaymentResource {
3537
val json = JSONObject(jsonString)
3638
val paymentResource = json.optJSONObject(PAYMENT_RESOURCE_KEY)
37-
val redirectUrl = if (paymentResource != null) {
38-
Json.optString(paymentResource, REDIRECT_URL_KEY, "")
39+
return if (paymentResource != null) {
40+
val redirectUrl = Json.optString(paymentResource, REDIRECT_URL_KEY, "")
41+
val isAppSwitchFlow = Json.optBoolean(paymentResource, LAUNCH_PAYPAL_APP_KEY, false)
42+
PayPalPaymentResource(redirectUrl, isAppSwitchFlow)
3943
} else {
4044
val redirectJson = json.optJSONObject(AGREEMENT_SETUP_KEY)
45+
var isAppSwitchFlow = true
4146
val payPalApprovalURL = Json.optString(redirectJson, PAYPAL_APP_APPROVAL_URL_KEY, "")
47+
var redirectUrl = payPalApprovalURL
48+
// Presence of `payPalApprovalURL` indicates that this is an app-switch flow.
49+
// In case `payPalApprovalURL` is empty and we end up using `approvalUrl` instead,
50+
// one wouldn't go through the app-switch flow.
4251
payPalApprovalURL.ifEmpty {
43-
Json.optString(
44-
redirectJson,
45-
APPROVAL_URL_KEY,
46-
""
47-
)
52+
isAppSwitchFlow = false
53+
redirectUrl = Json.optString(redirectJson, APPROVAL_URL_KEY, "")
4854
}
55+
PayPalPaymentResource(redirectUrl = redirectUrl, isAppSwitchFlow = isAppSwitchFlow)
4956
}
50-
return PayPalPaymentResource(redirectUrl)
5157
}
5258
}
5359
}

0 commit comments

Comments
 (0)