Skip to content

Commit 28df234

Browse files
committed
Implement CardAuthLauncher deep linking refactor.
1 parent 48f49cd commit 28df234

File tree

3 files changed

+71
-91
lines changed

3 files changed

+71
-91
lines changed

CardPayments/src/main/java/com/paypal/android/cardpayments/CardAuthLauncher.kt

Lines changed: 23 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ package com.paypal.android.cardpayments
33
import android.app.Activity
44
import android.content.Intent
55
import com.paypal.android.corepayments.BrowserSwitchRequestCodes
6-
import com.paypal.android.corepayments.PayPalSDKError
6+
import com.paypal.android.corepayments.CaptureDeepLinkResult
7+
import com.paypal.android.corepayments.CardApproveOrderComplete
8+
import com.paypal.android.corepayments.CardVaultComplete
79
import com.paypal.android.corepayments.browserswitch.BrowserSwitchClient
8-
import com.paypal.android.corepayments.browserswitch.BrowserSwitchFinishResult
910
import com.paypal.android.corepayments.browserswitch.BrowserSwitchOptions
1011
import com.paypal.android.corepayments.browserswitch.BrowserSwitchPendingState
1112
import com.paypal.android.corepayments.browserswitch.BrowserSwitchStartResult
@@ -69,46 +70,21 @@ internal class CardAuthLauncher(
6970
fun completeApproveOrderAuthRequest(
7071
intent: Intent,
7172
authState: String
72-
): CardFinishApproveOrderResult {
73-
val pendingState = BrowserSwitchPendingState.fromBase64(authState)
74-
return if (pendingState == null) {
75-
val invalidAuthStateError = PayPalSDKError(0, "Auth State Invalid.")
76-
CardFinishApproveOrderResult.Failure(invalidAuthStateError)
77-
} else {
78-
val requestCode = BrowserSwitchRequestCodes.CARD_APPROVE_ORDER
79-
when (val finalResult = pendingState.match(intent, requestCode)) {
80-
is BrowserSwitchFinishResult.Success -> parseApproveOrderSuccessResult(finalResult)
81-
is BrowserSwitchFinishResult.DeepLinkNotPresent,
82-
is BrowserSwitchFinishResult.DeepLinkDoesNotMatch,
83-
is BrowserSwitchFinishResult.RequestCodeDoesNotMatch -> CardFinishApproveOrderResult.NoResult
84-
}
85-
}
73+
) = when (val result = captureDeepLink<CardApproveOrderComplete>(intent, authState)) {
74+
is CaptureDeepLinkResult.Success -> parseApproveOrderSuccessResult(result.deepLink)
75+
else -> CardFinishApproveOrderResult.NoResult
8676
}
8777

88-
fun completeVaultAuthRequest(intent: Intent, authState: String): CardFinishVaultResult {
89-
val pendingState = BrowserSwitchPendingState.fromBase64(authState)
90-
return if (pendingState == null) {
91-
val invalidAuthStateError = PayPalSDKError(0, "Auth State Invalid.")
92-
CardFinishVaultResult.Failure(invalidAuthStateError)
93-
} else {
94-
when (captureDeepLink<CardVaultComplete>(intent, pendingState)) {
95-
96-
}
97-
val requestCode = BrowserSwitchRequestCodes.CARD_VAULT
98-
when (val finalResult = pendingState.match(intent, requestCode)) {
99-
is BrowserSwitchFinishResult.Success ->
100-
parseVaultSuccessResult(pendingState.originalOptions)
101-
102-
is BrowserSwitchFinishResult.DeepLinkNotPresent,
103-
is BrowserSwitchFinishResult.DeepLinkDoesNotMatch,
104-
is BrowserSwitchFinishResult.RequestCodeDoesNotMatch -> CardFinishVaultResult.NoResult
105-
}
78+
fun completeVaultAuthRequest(intent: Intent, authState: String) =
79+
when (val result = captureDeepLink<CardVaultComplete>(intent, authState)) {
80+
is CaptureDeepLinkResult.Success -> parseVaultSuccessResult(result.deepLink)
81+
else -> CardFinishVaultResult.NoResult
10682
}
107-
}
10883

10984
private fun parseVaultSuccessResult(
110-
originalOptions: BrowserSwitchOptions
85+
deepLink: CardVaultComplete
11186
): CardFinishVaultResult {
87+
val originalOptions = deepLink.originalOptions
11288
val setupTokenId = originalOptions.metadata?.optString(METADATA_KEY_SETUP_TOKEN_ID)
11389
return if (setupTokenId == null) {
11490
CardFinishVaultResult.Failure(CardError.unknownError)
@@ -124,19 +100,17 @@ internal class CardAuthLauncher(
124100
}
125101

126102
private fun parseApproveOrderSuccessResult(
127-
finalResult: BrowserSwitchFinishResult.Success
128-
): CardFinishApproveOrderResult =
129-
if (finalResult.requestCode == BrowserSwitchRequestCodes.CARD_APPROVE_ORDER) {
130-
val orderId = finalResult.requestMetadata?.optString(METADATA_KEY_ORDER_ID)
131-
if (orderId == null) {
132-
CardFinishApproveOrderResult.Failure(CardError.unknownError)
133-
} else {
134-
CardFinishApproveOrderResult.Success(
135-
orderId = orderId,
136-
didAttemptThreeDSecureAuthentication = true
137-
)
138-
}
103+
deepLink: CardApproveOrderComplete
104+
): CardFinishApproveOrderResult {
105+
val originalOptions = deepLink.originalOptions
106+
val orderId = originalOptions.metadata?.optString(METADATA_KEY_ORDER_ID)
107+
return if (orderId == null) {
108+
CardFinishApproveOrderResult.Failure(CardError.unknownError)
139109
} else {
140-
CardFinishApproveOrderResult.NoResult
110+
CardFinishApproveOrderResult.Success(
111+
orderId = orderId,
112+
didAttemptThreeDSecureAuthentication = true
113+
)
141114
}
115+
}
142116
}

CardPayments/src/test/java/com/paypal/android/cardpayments/CardAuthLauncherUnitTest.kt

Lines changed: 25 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ package com.paypal.android.cardpayments
22

33
import android.content.Intent
44
import android.net.Uri
5+
import androidx.core.net.toUri
56
import androidx.fragment.app.FragmentActivity
6-
import com.braintreepayments.api.BrowserSwitchClient
7-
import com.braintreepayments.api.BrowserSwitchFinalResult
8-
import com.braintreepayments.api.BrowserSwitchOptions
9-
import com.braintreepayments.api.BrowserSwitchStartResult
107
import com.paypal.android.corepayments.BrowserSwitchRequestCodes
118
import com.paypal.android.corepayments.browserswitch.BrowserSwitchClient
9+
import com.paypal.android.corepayments.browserswitch.BrowserSwitchOptions
10+
import com.paypal.android.corepayments.browserswitch.BrowserSwitchPendingState
1211
import com.paypal.android.corepayments.browserswitch.BrowserSwitchStartResult
1312
import io.mockk.every
1413
import io.mockk.mockk
@@ -66,7 +65,7 @@ class CardAuthLauncherUnitTest {
6665
@Test
6766
fun `presentAuthChallenge() browser switches to approve order auth challenge url`() {
6867
val slot = slot<BrowserSwitchOptions>()
69-
val browserSwitchResult = BrowserSwitchStartResult.Started("pending request")
68+
val browserSwitchResult = BrowserSwitchStartResult.Success
7069
every { browserSwitchClient.start(activity, capture(slot)) } returns browserSwitchResult
7170

7271
val returnUrl = "merchant.app://return.com/deep-link"
@@ -81,14 +80,14 @@ class CardAuthLauncherUnitTest {
8180
val metadata = browserSwitchOptions.metadata
8281
assertEquals("fake-order-id", metadata?.getString("order_id"))
8382
assertEquals("merchant.app", browserSwitchOptions.returnUrlScheme)
84-
assertEquals(Uri.parse("https://fake.com/destination"), browserSwitchOptions.url)
83+
assertEquals(Uri.parse("https://fake.com/destination"), browserSwitchOptions.targetUri)
8584
assertEquals(BrowserSwitchRequestCodes.CARD_APPROVE_ORDER, browserSwitchOptions.requestCode)
8685
}
8786

8887
@Test
8988
fun `presentAuthChallenge() browser switches to vault auth challenge url`() {
9089
val slot = slot<BrowserSwitchOptions>()
91-
val browserSwitchResult = BrowserSwitchStartResult.Started("pending request")
90+
val browserSwitchResult = BrowserSwitchStartResult.Success
9291
every { browserSwitchClient.start(activity, capture(slot)) } returns browserSwitchResult
9392

9493
val returnUrl = "merchant.app://return.com/deep-link"
@@ -103,7 +102,7 @@ class CardAuthLauncherUnitTest {
103102
val metadata = browserSwitchOptions.metadata
104103
assertEquals("fake-setup-token-id", metadata?.getString("setup_token_id"))
105104
assertEquals("merchant.app", browserSwitchOptions.returnUrlScheme)
106-
assertEquals(Uri.parse("https://fake.com/destination"), browserSwitchOptions.url)
105+
assertEquals(Uri.parse("https://fake.com/destination"), browserSwitchOptions.targetUri)
107106
assertEquals(BrowserSwitchRequestCodes.CARD_VAULT, browserSwitchOptions.requestCode)
108107
}
109108

@@ -115,17 +114,17 @@ class CardAuthLauncherUnitTest {
115114
val domain = "example.com"
116115
val successDeepLink =
117116
"$scheme://$domain/return_url?state=undefined&code=undefined&liability_shift=NO"
118-
119-
val finalResult = createBrowserSwitchSuccessFinalResult(
120-
BrowserSwitchRequestCodes.CARD_APPROVE_ORDER,
121-
approveOrderMetadata,
122-
Uri.parse(successDeepLink)
117+
val options = BrowserSwitchOptions(
118+
targetUri = "https://fake.com/destination".toUri(),
119+
requestCode = BrowserSwitchRequestCodes.CARD_APPROVE_ORDER,
120+
returnUrlScheme = scheme,
121+
appLinkUrl = null,
122+
metadata = approveOrderMetadata
123123
)
124-
every {
125-
browserSwitchClient.completeRequest(intent, "pending request")
126-
} returns finalResult
124+
val authState = BrowserSwitchPendingState(options).toBase64EncodedJSON()
125+
intent.data = successDeepLink.toUri()
127126

128-
val result = sut.completeApproveOrderAuthRequest(intent, "pending request")
127+
val result = sut.completeApproveOrderAuthRequest(intent, authState)
129128
as CardFinishApproveOrderResult.Success
130129

131130
assertEquals("fake-order-id", result.orderId)
@@ -140,32 +139,19 @@ class CardAuthLauncherUnitTest {
140139
val scheme = "com.paypal.android.demo"
141140
val domain = "example.com"
142141
val successDeepLink = "$scheme://$domain/success"
143-
144-
val finalResult = createBrowserSwitchSuccessFinalResult(
145-
BrowserSwitchRequestCodes.CARD_VAULT,
146-
vaultMetadata,
147-
Uri.parse(successDeepLink)
142+
val options = BrowserSwitchOptions(
143+
targetUri = "https://fake.com/destination".toUri(),
144+
requestCode = BrowserSwitchRequestCodes.CARD_VAULT,
145+
returnUrlScheme = scheme,
146+
appLinkUrl = null,
147+
metadata = vaultMetadata
148148
)
149-
every {
150-
browserSwitchClient.completeRequest(intent, "pending request")
151-
} returns finalResult
149+
val authState = BrowserSwitchPendingState(options).toBase64EncodedJSON()
150+
intent.data = successDeepLink.toUri()
152151

153152
val result =
154-
sut.completeVaultAuthRequest(intent, "pending request") as CardFinishVaultResult.Success
153+
sut.completeVaultAuthRequest(intent, authState) as CardFinishVaultResult.Success
155154
assertEquals("fake-setup-token-id", result.setupTokenId)
156155
assertNull(result.status)
157156
}
158-
159-
private fun createBrowserSwitchSuccessFinalResult(
160-
requestCode: Int,
161-
metadata: JSONObject,
162-
deepLinkUrl: Uri
163-
): BrowserSwitchFinalResult.Success {
164-
val finalResult = mockk<BrowserSwitchFinalResult.Success>(relaxed = true)
165-
every { finalResult.returnUrl } returns deepLinkUrl
166-
every { finalResult.requestMetadata } returns metadata
167-
every { finalResult.requestCode } returns requestCode
168-
every { finalResult.requestUrl } returns Uri.parse("https://example.com/url")
169-
return finalResult
170-
}
171157
}

CorePayments/src/main/java/com/paypal/android/corepayments/captureDeepLink.kt

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@ import androidx.annotation.RestrictTo
66
import androidx.core.net.toUri
77
import com.paypal.android.corepayments.browserswitch.BrowserSwitchOptions
88
import com.paypal.android.corepayments.browserswitch.BrowserSwitchPendingState
9-
import kotlin.text.equals
109

1110
sealed class DeepLink(val uri: Uri, val originalOptions: BrowserSwitchOptions)
1211

12+
class CardApproveOrderComplete(uri: Uri, originalOptions: BrowserSwitchOptions) :
13+
DeepLink(uri, originalOptions)
14+
15+
class CardVaultComplete(uri: Uri, originalOptions: BrowserSwitchOptions) :
16+
DeepLink(uri, originalOptions)
17+
1318
class PayPalVaultComplete(uri: Uri, originalOptions: BrowserSwitchOptions) :
1419
DeepLink(uri, originalOptions)
1520

@@ -27,6 +32,12 @@ sealed class CaptureDeepLinkResult<out T : DeepLink>() {
2732
}
2833

2934
// Ref: https://stackoverflow.com/a/33158859
35+
inline fun <reified T : DeepLink> isCardApproveOrder(klass: Class<T>) =
36+
klass.isAssignableFrom(CardApproveOrderComplete::class.java)
37+
38+
inline fun <reified T : DeepLink> isCardVault(klass: Class<T>) =
39+
klass.isAssignableFrom(CardVaultComplete::class.java)
40+
3041
inline fun <reified T : DeepLink> isPayPalCheckout(klass: Class<T>) =
3142
klass.isAssignableFrom(PayPalCheckoutComplete::class.java)
3243

@@ -57,7 +68,11 @@ inline fun <reified T : DeepLink> captureDeepLink(
5768

5869
val options = pendingState.originalOptions
5970
val klass = T::class.java
60-
val requestCode = if (isPayPalVault(klass)) {
71+
val requestCode = if (isCardApproveOrder(klass)) {
72+
BrowserSwitchRequestCodes.CARD_APPROVE_ORDER
73+
} else if (isCardVault(klass)) {
74+
BrowserSwitchRequestCodes.CARD_VAULT
75+
} else if (isPayPalVault(klass)) {
6176
BrowserSwitchRequestCodes.PAYPAL_VAULT
6277
} else if (isPayPalCheckout(klass)) {
6378
BrowserSwitchRequestCodes.PAYPAL_CHECKOUT
@@ -77,13 +92,18 @@ inline fun <reified T : DeepLink> captureDeepLink(
7792
val isMatchingDeepLink =
7893
isCustomSchemeMatch(options, deepLinkUri) || isAppLinkMatch(options, deepLinkUri)
7994
return if (isMatchingDeepLink) {
80-
val deepLink = if (isPayPalVault(klass)) {
95+
val deepLink = if (isCardApproveOrder(klass)) {
96+
CardApproveOrderComplete(deepLinkUri, options)
97+
} else if (isCardVault(klass)) {
98+
CardVaultComplete(deepLinkUri, options)
99+
} else if (isPayPalVault(klass)) {
81100
PayPalVaultComplete(deepLinkUri, options)
82101
} else if (isPayPalCheckout(klass)) {
83102
PayPalCheckoutComplete(deepLinkUri, options)
84103
} else {
85104
null
86105
}
106+
87107
if (deepLink is T) {
88108
CaptureDeepLinkResult.Success(deepLink)
89109
} else {

0 commit comments

Comments
 (0)