From 80c920a20d7ddf0f02ff8b78b591acd568d04f04 Mon Sep 17 00:00:00 2001 From: Karthik Gangineni Date: Wed, 5 Nov 2025 16:54:48 -0600 Subject: [PATCH 01/10] * Deprecate url scheme passed in PayPalWebCheckoutClient * introduce appLinkUrl in PayPalWebVaultRequest and PayPalWebCheckoutRequest --- Demo/src/main/AndroidManifest.xml | 11 ++++ .../java/com/paypal/android/DemoConstants.kt | 2 +- .../java/com/paypal/android/MainActivity.kt | 7 +++ .../ui/paypalweb/PayPalCheckoutViewModel.kt | 5 +- .../ui/paypalwebvault/PayPalVaultViewModel.kt | 6 +- .../ui/vaultcard/VaultCardViewModel.kt | 5 +- .../PayPalWebCheckoutClient.kt | 62 ++++++++++++------- .../PayPalWebCheckoutRequest.kt | 4 +- .../paypalwebpayments/PayPalWebLauncher.kt | 8 ++- .../PayPalWebVaultRequest.kt | 2 + 10 files changed, 80 insertions(+), 32 deletions(-) diff --git a/Demo/src/main/AndroidManifest.xml b/Demo/src/main/AndroidManifest.xml index a85818050..1b436849a 100644 --- a/Demo/src/main/AndroidManifest.xml +++ b/Demo/src/main/AndroidManifest.xml @@ -28,6 +28,17 @@ + + + + + + + + diff --git a/Demo/src/main/java/com/paypal/android/DemoConstants.kt b/Demo/src/main/java/com/paypal/android/DemoConstants.kt index 6b7d41da4..615e2c3c4 100644 --- a/Demo/src/main/java/com/paypal/android/DemoConstants.kt +++ b/Demo/src/main/java/com/paypal/android/DemoConstants.kt @@ -1,7 +1,7 @@ package com.paypal.android object DemoConstants { - const val APP_URL = "com.paypal.android.demo://" + const val APP_URL = "https://ppcp-mobile-demo-sandbox-87bbd7f0a27f.herokuapp.com/" const val SUCCESS_URL = "${APP_URL}success" const val CANCEL_URL = "${APP_URL}cancel" const val VAULT_SUCCESS_URL = "${APP_URL}vault/success" diff --git a/Demo/src/main/java/com/paypal/android/MainActivity.kt b/Demo/src/main/java/com/paypal/android/MainActivity.kt index 92b10f46f..9dbf2bf32 100644 --- a/Demo/src/main/java/com/paypal/android/MainActivity.kt +++ b/Demo/src/main/java/com/paypal/android/MainActivity.kt @@ -1,5 +1,6 @@ package com.paypal.android +import android.content.Intent import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent @@ -21,4 +22,10 @@ class MainActivity : ComponentActivity() { DemoApp() } } + + override fun onNewIntent(intent: Intent?) { + println("MainActivity: $intent") + println("MainActivity: ${intent?.data}") + super.onNewIntent(intent) + } } diff --git a/Demo/src/main/java/com/paypal/android/ui/paypalweb/PayPalCheckoutViewModel.kt b/Demo/src/main/java/com/paypal/android/ui/paypalweb/PayPalCheckoutViewModel.kt index 98cd29489..2fc0ba70b 100644 --- a/Demo/src/main/java/com/paypal/android/ui/paypalweb/PayPalCheckoutViewModel.kt +++ b/Demo/src/main/java/com/paypal/android/ui/paypalweb/PayPalCheckoutViewModel.kt @@ -6,6 +6,7 @@ import android.util.Log import androidx.activity.ComponentActivity import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.paypal.android.DemoConstants.APP_URL import com.paypal.android.api.model.Order import com.paypal.android.api.model.OrderIntent import com.paypal.android.api.services.SDKSampleServerAPI @@ -43,7 +44,7 @@ class PayPalCheckoutViewModel @Inject constructor( private val coreConfig = CoreConfig(SDKSampleServerAPI.clientId) private val payPalDataCollector = PayPalDataCollector(coreConfig) private val paypalClient = - PayPalWebCheckoutClient(applicationContext, coreConfig, "com.paypal.android.demo") + PayPalWebCheckoutClient(applicationContext, coreConfig) private val _uiState = MutableStateFlow(PayPalUiState()) val uiState = _uiState.asStateFlow() @@ -116,7 +117,7 @@ class PayPalCheckoutViewModel @Inject constructor( payPalWebCheckoutState = ActionState.Loading val checkoutRequest = - PayPalWebCheckoutRequest(orderId, fundingSource, appSwitchWhenEligible) + PayPalWebCheckoutRequest(orderId, fundingSource, appSwitchWhenEligible, APP_URL) when (val startResult = paypalClient.start(activity, checkoutRequest)) { is PayPalPresentAuthChallengeResult.Success -> { // do nothing; wait for user to authenticate PayPal checkout in Chrome Custom Tab diff --git a/Demo/src/main/java/com/paypal/android/ui/paypalwebvault/PayPalVaultViewModel.kt b/Demo/src/main/java/com/paypal/android/ui/paypalwebvault/PayPalVaultViewModel.kt index 0591bd419..86bd06913 100644 --- a/Demo/src/main/java/com/paypal/android/ui/paypalwebvault/PayPalVaultViewModel.kt +++ b/Demo/src/main/java/com/paypal/android/ui/paypalwebvault/PayPalVaultViewModel.kt @@ -5,6 +5,7 @@ import android.content.Intent import androidx.activity.ComponentActivity import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.paypal.android.DemoConstants.APP_URL import com.paypal.android.api.model.PayPalSetupToken import com.paypal.android.api.services.SDKSampleServerAPI import com.paypal.android.corepayments.CoreConfig @@ -35,7 +36,7 @@ class PayPalVaultViewModel @Inject constructor( } private val coreConfig = CoreConfig(SDKSampleServerAPI.clientId) - private val paypalClient = PayPalWebCheckoutClient(applicationContext, coreConfig, URL_SCHEME) + private val paypalClient = PayPalWebCheckoutClient(applicationContext, coreConfig) private val _uiState = MutableStateFlow(PayPalVaultUiState()) val uiState = _uiState.asStateFlow() @@ -83,7 +84,8 @@ class PayPalVaultViewModel @Inject constructor( viewModelScope.launch { val request = PayPalWebVaultRequest( setupTokenId, - appSwitchWhenEligible + appSwitchWhenEligible, + APP_URL ) vaultSetupTokenWithRequest(activity, request) } diff --git a/Demo/src/main/java/com/paypal/android/ui/vaultcard/VaultCardViewModel.kt b/Demo/src/main/java/com/paypal/android/ui/vaultcard/VaultCardViewModel.kt index 309e51aad..71bd58264 100644 --- a/Demo/src/main/java/com/paypal/android/ui/vaultcard/VaultCardViewModel.kt +++ b/Demo/src/main/java/com/paypal/android/ui/vaultcard/VaultCardViewModel.kt @@ -5,7 +5,7 @@ import android.content.Intent import androidx.activity.ComponentActivity import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.paypal.android.DemoConstants +import com.paypal.android.DemoConstants.RETURN_URL import com.paypal.android.api.model.CardSetupToken import com.paypal.android.api.services.SDKSampleServerAPI import com.paypal.android.cardpayments.Card @@ -123,8 +123,7 @@ class VaultCardViewModel @Inject constructor( private fun updateSetupTokenWithId(activity: ComponentActivity, setupTokenId: String) { updateSetupTokenState = ActionState.Loading val card = parseCard(_uiState.value) - val returnUrl = DemoConstants.RETURN_URL - val cardVaultRequest = CardVaultRequest(setupTokenId, card, returnUrl) + val cardVaultRequest = CardVaultRequest(setupTokenId, card, RETURN_URL) cardClient.vault(cardVaultRequest) { result -> when (result) { is CardVaultResult.Success -> { diff --git a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt index aab82bd95..0a0d2295a 100644 --- a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt +++ b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt @@ -4,7 +4,6 @@ import android.app.Activity import android.content.Context import android.content.Intent import android.net.Uri -import android.util.Log import androidx.activity.ComponentActivity import androidx.core.net.toUri import com.paypal.android.corepayments.CoreConfig @@ -37,7 +36,7 @@ class PayPalWebCheckoutClient internal constructor( private val sessionStore: PayPalWebCheckoutSessionStore, private val deviceInspector: DeviceInspector, private val coreConfig: CoreConfig, - private val urlScheme: String, + private val urlScheme: String?, private val updateClientConfigAPI: UpdateClientConfigAPI, private val patchCCOWithAppSwitchEligibility: PatchCCOWithAppSwitchEligibility, @@ -55,7 +54,15 @@ class PayPalWebCheckoutClient internal constructor( * @param configuration a [CoreConfig] object * @param urlScheme the custom URl scheme used to return to your app from a browser switch flow */ - constructor(context: Context, configuration: CoreConfig, urlScheme: String) : this( + @Deprecated( + message = "Use PayPalWebCheckoutClient(context, configuration) instead.", + replaceWith = ReplaceWith("PayPalWebCheckoutClient(context, configuration)") + ) + constructor( + context: Context, + configuration: CoreConfig, + urlScheme: String + ) : this( analytics = PayPalWebAnalytics(AnalyticsService(context.applicationContext, configuration)), payPalWebLauncher = PayPalWebLauncher(), sessionStore = PayPalWebCheckoutSessionStore(), @@ -66,6 +73,20 @@ class PayPalWebCheckoutClient internal constructor( updateClientConfigAPI = UpdateClientConfigAPI(context, configuration), ) + constructor( + context: Context, + configuration: CoreConfig + ) : this( + analytics = PayPalWebAnalytics(AnalyticsService(context.applicationContext, configuration)), + payPalWebLauncher = PayPalWebLauncher(), + sessionStore = PayPalWebCheckoutSessionStore(), + deviceInspector = DeviceInspector(context), + coreConfig = configuration, + urlScheme = null, + patchCCOWithAppSwitchEligibility = PatchCCOWithAppSwitchEligibility(configuration), + updateClientConfigAPI = UpdateClientConfigAPI(context, configuration), + ) + /** * Capture instance state for later restoration. This can be useful for recovery during a * process kill. @@ -89,6 +110,7 @@ class PayPalWebCheckoutClient internal constructor( activity: Activity, request: PayPalWebCheckoutRequest ): PayPalPresentAuthChallengeResult { + checkoutOrderId = request.orderId analytics.notify(CheckoutEvent.STARTED, checkoutOrderId) @@ -105,7 +127,11 @@ class PayPalWebCheckoutClient internal constructor( token = request.orderId, tokenType = TokenType.ORDER_ID, appSwitchWhenEligible = request.appSwitchWhenEligible, - fallbackUri = buildPayPalCheckoutUri(request.orderId, request.fundingSource) + fallbackUri = buildPayPalCheckoutUri( + orderId = request.orderId, + funding = request.fundingSource, + request.appLinkUrl ?: redirectUriPayPalCheckout + ) ) } @@ -118,7 +144,8 @@ class PayPalWebCheckoutClient internal constructor( uri = launchUri, token = request.orderId, tokenType = TokenType.ORDER_ID, - returnUrlScheme = urlScheme + returnUrlScheme = urlScheme, + appLinkUrl = request.appLinkUrl ) when (result) { @@ -190,7 +217,8 @@ class PayPalWebCheckoutClient internal constructor( uri = launchUri, token = request.setupTokenId, tokenType = TokenType.VAULT_ID, - returnUrlScheme = urlScheme + returnUrlScheme = urlScheme, + appLinkUrl = request.appLinkUrl ) when (result) { @@ -336,17 +364,19 @@ class PayPalWebCheckoutClient internal constructor( return result } - private val redirectUriPayPalCheckout = "$urlScheme://x-callback-url/paypal-sdk/paypal-checkout" + private val redirectUriPayPalCheckout + get() = urlScheme?.let { "$urlScheme://x-callback-url/paypal-sdk/paypal-checkout" } private fun buildPayPalCheckoutUri( orderId: String?, - funding: PayPalWebCheckoutFundingSource + funding: PayPalWebCheckoutFundingSource, + redirectUrl: String? ): Uri { return baseUrl.toUri() .buildUpon() .appendPath("checkoutnow") .appendQueryParameter("token", orderId) - .appendQueryParameter("redirect_uri", redirectUriPayPalCheckout) + .appendQueryParameter("redirect_uri", redirectUrl) .appendQueryParameter("native_xo", "1") .appendQueryParameter("fundingSource", funding.value) .appendQueryParameter("integration_artifact", UpdateClientConfigAPI.Defaults.INTEGRATION_ARTIFACT) @@ -386,18 +416,8 @@ class PayPalWebCheckoutClient internal constructor( paypalNativeAppInstalled = true ) when (patchCcoResult) { - is APIResult.Success -> { - Log.d("PayPalWebCheckoutClient", "App switch response: ${patchCcoResult.data}") - patchCcoResult.data.launchUrl?.toUri() ?: fallbackUri - } - - is APIResult.Failure -> { - Log.e( - "PayPalWebCheckoutClient", - "App switch eligibility check failed: ${patchCcoResult.error}" - ) - fallbackUri - } + is APIResult.Success -> patchCcoResult.data.launchUrl?.toUri() ?: fallbackUri + is APIResult.Failure -> fallbackUri } } else { fallbackUri diff --git a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest.kt b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest.kt index f16c014df..1460be988 100644 --- a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest.kt +++ b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest.kt @@ -6,9 +6,11 @@ package com.paypal.android.paypalwebpayments * @param orderId The ID of the order to be approved. * @param fundingSource specify funding (credit, paylater or default) * @param appSwitchWhenEligible whether to switch to the PayPal app when eligible + * @param appLinkUrl The app link URL to use for browser switch, or null to use default. */ data class PayPalWebCheckoutRequest @JvmOverloads constructor( val orderId: String, val fundingSource: PayPalWebCheckoutFundingSource = PayPalWebCheckoutFundingSource.PAYPAL, - val appSwitchWhenEligible: Boolean = false + val appSwitchWhenEligible: Boolean = false, + val appLinkUrl: String? = null ) diff --git a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebLauncher.kt b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebLauncher.kt index 76d63473f..8b6001f25 100644 --- a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebLauncher.kt +++ b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebLauncher.kt @@ -3,6 +3,7 @@ package com.paypal.android.paypalwebpayments import android.app.Activity import android.content.Intent import android.net.Uri +import androidx.core.net.toUri import com.braintreepayments.api.BrowserSwitchClient import com.braintreepayments.api.BrowserSwitchFinalResult import com.braintreepayments.api.BrowserSwitchOptions @@ -30,13 +31,16 @@ internal class PayPalWebLauncher( uri: Uri, token: String, tokenType: TokenType, - returnUrlScheme: String + returnUrlScheme: String? = null, + appLinkUrl: String? = null ): PayPalPresentAuthChallengeResult { + val urlScheme = if (appLinkUrl != null) null else returnUrlScheme val metadata = getMetadata(token, tokenType) val options = BrowserSwitchOptions() .url(uri) .requestCode(getRequestCode(tokenType)) - .returnUrlScheme(returnUrlScheme) + .returnUrlScheme(urlScheme) + .appLinkUri(appLinkUrl?.toUri()) .metadata(metadata) return launchBrowserSwitch(activity, options) } diff --git a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt index 5d7548d19..2faaea06f 100644 --- a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt +++ b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt @@ -6,11 +6,13 @@ package com.paypal.android.paypalwebpayments * @property [setupTokenId] ID for the setup token associated with the vault approval * @property [approveVaultHref] URL for the approval web page * @property [appSwitchWhenEligible] whether to switch to the PayPal app when eligible + * @property [appLinkUrl] The app link URL to use for returning to app */ data class PayPalWebVaultRequest @Deprecated("Use PayPalWebVaultRequest(setupTokenId) instead.") constructor( val setupTokenId: String, val appSwitchWhenEligible: Boolean = false, + val appLinkUrl: String? = null, @Deprecated("The approveVaultHref property is no longer required and will be ignored.") val approveVaultHref: String? = null // NEXT_MAJOR_VERSION: - Remove this property ) { From eb137f88db5a8019432885b385aebe0f5a37d9d4 Mon Sep 17 00:00:00 2001 From: Karthik Gangineni Date: Thu, 6 Nov 2025 11:13:31 -0600 Subject: [PATCH 02/10] * update CHANGELOG.md --- CHANGELOG.md | 11 +- .../PayPalWebCheckoutClient.kt | 2 +- .../PayPalWebVaultRequest.kt | 6 +- .../PayPalWebCheckoutClientUnitTest.kt | 278 +++++++++++++++--- .../PayPalWebLauncherUnitTest.kt | 177 ++++++++--- 5 files changed, 390 insertions(+), 84 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c499acdf..204d2fcd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,15 @@ * Add overloaded functions with callbacks for `start` and `vault` methods in `PayPalWebCheckoutClient` -* Modify `PayPalWebCheckoutRequest` to include `appSwitchWhenEligible` property to control app +* Adds new property `appSwitchWhenEligible`in `PayPalWebCheckoutRequest` to include to control app switch behavior -* Breaking Changes - * Make start and vault functions in `PayPalWebCheckoutClient` suspend functions +* Adds new property `appSwitchWhenEligible`in `PayPalWebVaultRequest` to include to control app + switch behavior +* Adds new property `appLinkUrl` in `PayPalWebCheckoutRequest` to specify app link url that will be + used to recognize app after approving order +* Adds new property `appLinkUrl` in `PayPalWebVaultRequest` to specify app link url that will be + used to recognize app after vaulting +* Deprecates `urlScheme` property in `PayPalWebCheckoutClient` ## 2.3.0 (2025-11-03) * PayPalWebPayments diff --git a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt index 0a0d2295a..ef30a8e4a 100644 --- a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt +++ b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt @@ -36,9 +36,9 @@ class PayPalWebCheckoutClient internal constructor( private val sessionStore: PayPalWebCheckoutSessionStore, private val deviceInspector: DeviceInspector, private val coreConfig: CoreConfig, - private val urlScheme: String?, private val updateClientConfigAPI: UpdateClientConfigAPI, private val patchCCOWithAppSwitchEligibility: PatchCCOWithAppSwitchEligibility, + private val urlScheme: String? = null, ) { private val applicationScope = CoroutineScope(SupervisorJob()) diff --git a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt index 2faaea06f..61e5ad588 100644 --- a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt +++ b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt @@ -22,5 +22,9 @@ constructor( * * @property [setupTokenId] ID for the setup token associated with the vault approval */ - constructor(setupTokenId: String) : this(setupTokenId, false) + constructor( + setupTokenId: String, + appSwitchWhenEligible: Boolean = false, + appLinkUrl: String? = null + ) : this(setupTokenId, appSwitchWhenEligible, appLinkUrl, null) } diff --git a/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClientUnitTest.kt b/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClientUnitTest.kt index 11fed8f91..1abc19bfe 100644 --- a/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClientUnitTest.kt +++ b/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClientUnitTest.kt @@ -42,6 +42,7 @@ class PayPalWebCheckoutClientUnitTest { private val urlScheme = "com.example.app" private val fakeAppSwitchUrl = "https://paypal.com/vault-app-switch" private val updateClientConfigAPI = mockk(relaxed = true) + private val appLinkUrl = "https://example.com/" private val intent = Intent() @@ -60,8 +61,7 @@ class PayPalWebCheckoutClientUnitTest { updateClientConfigAPI = updateClientConfigAPI, patchCCOWithAppSwitchEligibility = patchCCOWithAppSwitchEligibility, deviceInspector = deviceInspector, - coreConfig = coreConfig, - urlScheme = urlScheme + coreConfig = coreConfig ) } @@ -74,11 +74,12 @@ class PayPalWebCheckoutClientUnitTest { any(), any(), any(), + any(), any() ) } returns launchResult - val request = PayPalWebCheckoutRequest("fake-order-id") + val request = PayPalWebCheckoutRequest("fake-order-id", appLinkUrl = appLinkUrl) sut.start(activity, request) // Verify launchWithUrl is called with correct parameters @@ -88,7 +89,7 @@ class PayPalWebCheckoutClientUnitTest { uri = any(), token = "fake-order-id", tokenType = TokenType.ORDER_ID, - returnUrlScheme = urlScheme + appLinkUrl = appLinkUrl ) } } @@ -103,11 +104,13 @@ class PayPalWebCheckoutClientUnitTest { any(), any(), any(), + any(), any() ) } returns launchResult - val request = PayPalWebCheckoutRequest("fake-order-id") + val appLinkUrl = "https://example.com/return" + val request = PayPalWebCheckoutRequest("fake-order-id", appLinkUrl = appLinkUrl) val result = sut.start(activity, request) assertSame(launchResult, result) } @@ -125,7 +128,8 @@ class PayPalWebCheckoutClientUnitTest { ) } returns launchResult - val request = PayPalWebVaultRequest("fake-setup-token-id") + val appLinkUrl = "https://example.com/vault/return" + val request = PayPalWebVaultRequest("fake-setup-token-id", appLinkUrl = appLinkUrl) sut.vault(activity, request) verify(exactly = 1) { payPalWebLauncher.launchWithUrl( @@ -133,7 +137,7 @@ class PayPalWebCheckoutClientUnitTest { uri = any(), token = "fake-setup-token-id", tokenType = TokenType.VAULT_ID, - returnUrlScheme = urlScheme + appLinkUrl = appLinkUrl ) } } @@ -148,11 +152,13 @@ class PayPalWebCheckoutClientUnitTest { any(), any(), any(), + any(), any() ) } returns launchResult - val request = PayPalWebVaultRequest("fake-setup-token-id") + val appLinkUrl = "https://example.com/vault/return" + val request = PayPalWebVaultRequest("fake-setup-token-id", appLinkUrl = appLinkUrl) val result = sut.vault(activity, request) as PayPalPresentAuthChallengeResult.Failure assertSame(sdkError, result.error) @@ -499,7 +505,12 @@ class PayPalWebCheckoutClientUnitTest { fun `start() uses app switch when enabled and app switch URL is available`() = runTest { // Given every { deviceInspector.isPayPalInstalled } returns true - val request = PayPalWebCheckoutRequest("fake-order-id", appSwitchWhenEligible = true) + val appLinkUrl = "https://example.com/return" + val request = PayPalWebCheckoutRequest( + "fake-order-id", + appSwitchWhenEligible = true, + appLinkUrl = appLinkUrl + ) val appSwitchResponse = createAppSwitchEligibilityResponse(fakeAppSwitchUrl) val launchResult = PayPalPresentAuthChallengeResult.Success("auth state") @@ -519,7 +530,7 @@ class PayPalWebCheckoutClientUnitTest { uri = any(), token = any(), tokenType = any(), - returnUrlScheme = urlScheme + appLinkUrl = any() ) } returns launchResult @@ -542,7 +553,7 @@ class PayPalWebCheckoutClientUnitTest { uri = any(), token = "fake-order-id", tokenType = TokenType.ORDER_ID, - returnUrlScheme = urlScheme + appLinkUrl = appLinkUrl ) } assertSame(launchResult, result) @@ -574,8 +585,7 @@ class PayPalWebCheckoutClientUnitTest { activity = activity, uri = any(), token = "fake-order-id", - tokenType = TokenType.ORDER_ID, - returnUrlScheme = urlScheme + tokenType = TokenType.ORDER_ID ) } assertSame(launchResult, result) @@ -608,7 +618,6 @@ class PayPalWebCheckoutClientUnitTest { uri = any(), token = "fake-order-id", tokenType = TokenType.ORDER_ID, - returnUrlScheme = urlScheme ) } assertSame(launchResult, result) @@ -618,7 +627,11 @@ class PayPalWebCheckoutClientUnitTest { fun `start() falls back to web checkout when app switch request fails`() = runTest { // Given every { deviceInspector.isPayPalInstalled } returns true - val request = PayPalWebCheckoutRequest("fake-order-id", appSwitchWhenEligible = true) + val request = PayPalWebCheckoutRequest( + orderId = "fake-order-id", + appSwitchWhenEligible = true, + appLinkUrl = appLinkUrl + ) val launchResult = PayPalPresentAuthChallengeResult.Success("auth state") coEvery { @@ -631,7 +644,8 @@ class PayPalWebCheckoutClientUnitTest { any(), any(), any(), - any() + any(), + appLinkUrl ) } returns launchResult @@ -646,7 +660,8 @@ class PayPalWebCheckoutClientUnitTest { uri = any(), token = "fake-order-id", tokenType = TokenType.ORDER_ID, - returnUrlScheme = urlScheme + returnUrlScheme = null, + appLinkUrl = any() ) } assertSame(launchResult, result) @@ -664,6 +679,7 @@ class PayPalWebCheckoutClientUnitTest { any(), any(), any(), + any(), any() ) } returns launchResult @@ -681,7 +697,6 @@ class PayPalWebCheckoutClientUnitTest { uri = any(), token = "fake-order-id", tokenType = TokenType.ORDER_ID, - returnUrlScheme = urlScheme ) } assertSame(launchResult, result) @@ -723,7 +738,11 @@ class PayPalWebCheckoutClientUnitTest { fun `vault() uses app switch when enabled and app switch URL is available`() = runTest { // Given every { deviceInspector.isPayPalInstalled } returns true - val request = PayPalWebVaultRequest("fake-setup-token-id", appSwitchWhenEligible = true) + val request = PayPalWebVaultRequest( + "fake-setup-token-id", + appSwitchWhenEligible = true, + appLinkUrl = appLinkUrl + ) val appSwitchResponse = createAppSwitchEligibilityResponse(fakeAppSwitchUrl) val launchResult = PayPalPresentAuthChallengeResult.Success("auth state") @@ -744,7 +763,8 @@ class PayPalWebCheckoutClientUnitTest { uri = fakeAppSwitchUrl.toUri(), token = "fake-setup-token-id", tokenType = TokenType.VAULT_ID, - returnUrlScheme = urlScheme + returnUrlScheme = null, + appLinkUrl = any() ) } returns launchResult @@ -767,7 +787,8 @@ class PayPalWebCheckoutClientUnitTest { uri = any(), token = "fake-setup-token-id", tokenType = TokenType.VAULT_ID, - returnUrlScheme = urlScheme + returnUrlScheme = null, + appLinkUrl = appLinkUrl ) } assertSame(launchResult, result) @@ -786,7 +807,7 @@ class PayPalWebCheckoutClientUnitTest { } returns APIResult.Success(appSwitchResponse) every { - payPalWebLauncher.launchWithUrl(any(), any(), any(), any(), any()) + payPalWebLauncher.launchWithUrl(any(), any(), any(), any(), any(), any()) } returns launchResult // When @@ -807,8 +828,7 @@ class PayPalWebCheckoutClientUnitTest { activity = activity, uri = any(), token = "fake-setup-token-id", - tokenType = TokenType.VAULT_ID, - returnUrlScheme = urlScheme + tokenType = TokenType.VAULT_ID ) } assertSame(launchResult, result) @@ -840,8 +860,7 @@ class PayPalWebCheckoutClientUnitTest { activity = activity, uri = any(), token = "fake-setup-token-id", - tokenType = TokenType.VAULT_ID, - returnUrlScheme = urlScheme + tokenType = TokenType.VAULT_ID ) } assertSame(launchResult, result) @@ -864,6 +883,7 @@ class PayPalWebCheckoutClientUnitTest { any(), any(), any(), + any(), any() ) } returns launchResult @@ -877,8 +897,7 @@ class PayPalWebCheckoutClientUnitTest { activity = activity, uri = any(), token = "fake-setup-token-id", - tokenType = TokenType.VAULT_ID, - returnUrlScheme = urlScheme + tokenType = TokenType.VAULT_ID ) } assertSame(launchResult, result) @@ -912,8 +931,7 @@ class PayPalWebCheckoutClientUnitTest { activity = activity, uri = any(), token = "fake-setup-token-id", - tokenType = TokenType.VAULT_ID, - returnUrlScheme = urlScheme + tokenType = TokenType.VAULT_ID ) } assertSame(launchResult, result) @@ -958,7 +976,7 @@ class PayPalWebCheckoutClientUnitTest { val launchResult = PayPalPresentAuthChallengeResult.Success("auth state") every { - payPalWebLauncher.launchWithUrl(any(), any(), any(), any(), any()) + payPalWebLauncher.launchWithUrl(any(), any(), any(), any(), any(), any()) } returns launchResult // When @@ -973,8 +991,7 @@ class PayPalWebCheckoutClientUnitTest { activity = activity, uri = any(), token = "fake-order-id", - tokenType = TokenType.ORDER_ID, - returnUrlScheme = urlScheme + tokenType = TokenType.ORDER_ID ) } assertSame(launchResult, result) @@ -1004,8 +1021,7 @@ class PayPalWebCheckoutClientUnitTest { activity = activity, uri = any(), token = "fake-setup-token-id", - tokenType = TokenType.VAULT_ID, - returnUrlScheme = urlScheme + tokenType = TokenType.VAULT_ID ) } assertSame(launchResult, result) @@ -1035,8 +1051,7 @@ class PayPalWebCheckoutClientUnitTest { activity = activity, uri = any(), token = "fake-order-id", - tokenType = TokenType.ORDER_ID, - returnUrlScheme = urlScheme + tokenType = TokenType.ORDER_ID ) } assertSame(launchResult, result) @@ -1066,8 +1081,7 @@ class PayPalWebCheckoutClientUnitTest { activity = activity, uri = any(), token = "fake-setup-token-id", - tokenType = TokenType.VAULT_ID, - returnUrlScheme = urlScheme + tokenType = TokenType.VAULT_ID ) } assertSame(launchResult, result) @@ -1090,7 +1104,6 @@ class PayPalWebCheckoutClientUnitTest { updateClientConfigAPI = updateClientConfigAPI, deviceInspector = deviceInspector, coreConfig = coreConfig, - urlScheme = urlScheme, patchCCOWithAppSwitchEligibility = patchCCOWithAppSwitchEligibility ) @@ -1129,7 +1142,6 @@ class PayPalWebCheckoutClientUnitTest { updateClientConfigAPI = updateClientConfigAPI, deviceInspector = deviceInspector, coreConfig = coreConfig, - urlScheme = urlScheme, patchCCOWithAppSwitchEligibility = patchCCOWithAppSwitchEligibility ) @@ -1326,6 +1338,190 @@ class PayPalWebCheckoutClientUnitTest { assertNull(sut.finishVault(intent)) } + @Test + @Suppress("DEPRECATION") + fun `start() with deprecated urlScheme constructor passes urlScheme to launcher`() = runTest { + val mockLauncher = mockk(relaxed = true) + val clientWithUrlScheme = PayPalWebCheckoutClient( + analytics = analytics, + payPalWebLauncher = mockLauncher, + sessionStore = PayPalWebCheckoutSessionStore(), + updateClientConfigAPI = updateClientConfigAPI, + patchCCOWithAppSwitchEligibility = patchCCOWithAppSwitchEligibility, + deviceInspector = deviceInspector, + coreConfig = coreConfig, + urlScheme = urlScheme + ) + + val request = PayPalWebCheckoutRequest("fake-order-id") + val launchResult = PayPalPresentAuthChallengeResult.Success("auth state") + + every { + mockLauncher.launchWithUrl( + any(), + any(), + any(), + any(), + any(), + any() + ) + } returns launchResult + + // When + clientWithUrlScheme.start(activity, request) + + // Then + verify { + mockLauncher.launchWithUrl( + activity = activity, + uri = any(), + token = "fake-order-id", + tokenType = TokenType.ORDER_ID, + returnUrlScheme = urlScheme, + appLinkUrl = null + ) + } + } + + @Test + @Suppress("DEPRECATION") + fun `vault() with deprecated urlScheme constructor passes urlScheme to launcher`() = runTest { + val mockLauncher = mockk(relaxed = true) + val clientWithUrlScheme = PayPalWebCheckoutClient( + analytics = analytics, + payPalWebLauncher = mockLauncher, + sessionStore = PayPalWebCheckoutSessionStore(), + updateClientConfigAPI = updateClientConfigAPI, + patchCCOWithAppSwitchEligibility = patchCCOWithAppSwitchEligibility, + deviceInspector = deviceInspector, + coreConfig = coreConfig, + urlScheme = urlScheme + ) + + val request = PayPalWebVaultRequest("fake-setup-token-id") + val launchResult = PayPalPresentAuthChallengeResult.Success("auth state") + + every { + mockLauncher.launchWithUrl( + any(), + any(), + any(), + any(), + any(), + any() + ) + } returns launchResult + + // When + clientWithUrlScheme.vault(activity, request) + + // Then + verify { + mockLauncher.launchWithUrl( + activity = activity, + uri = any(), + token = "fake-setup-token-id", + tokenType = TokenType.VAULT_ID, + returnUrlScheme = urlScheme, + appLinkUrl = null + ) + } + } + + @Test + @Suppress("DEPRECATION") + fun `start() with deprecated urlScheme constructor and appLinkUrl in request prioritizes appLinkUrl`() = + runTest { + val mockLauncher = mockk(relaxed = true) + val clientWithUrlScheme = PayPalWebCheckoutClient( + analytics = analytics, + payPalWebLauncher = mockLauncher, + sessionStore = PayPalWebCheckoutSessionStore(), + updateClientConfigAPI = updateClientConfigAPI, + patchCCOWithAppSwitchEligibility = patchCCOWithAppSwitchEligibility, + deviceInspector = deviceInspector, + coreConfig = coreConfig, + urlScheme = urlScheme + ) + + val appLinkUrl = "https://example.com/return" + val request = PayPalWebCheckoutRequest("fake-order-id", appLinkUrl = appLinkUrl) + val launchResult = PayPalPresentAuthChallengeResult.Success("auth state") + + every { + mockLauncher.launchWithUrl( + any(), + any(), + any(), + any(), + any(), + any() + ) + } returns launchResult + + // When + clientWithUrlScheme.start(activity, request) + + // Then + verify { + mockLauncher.launchWithUrl( + activity = activity, + uri = any(), + token = "fake-order-id", + tokenType = TokenType.ORDER_ID, + returnUrlScheme = urlScheme, + appLinkUrl = appLinkUrl + ) + } + } + + @Test + @Suppress("DEPRECATION") + fun `vault() with deprecated urlScheme constructor and appLinkUrl in request prioritizes appLinkUrl`() = + runTest { + val mockLauncher = mockk(relaxed = true) + val clientWithUrlScheme = PayPalWebCheckoutClient( + analytics = analytics, + payPalWebLauncher = mockLauncher, + sessionStore = PayPalWebCheckoutSessionStore(), + updateClientConfigAPI = updateClientConfigAPI, + patchCCOWithAppSwitchEligibility = patchCCOWithAppSwitchEligibility, + deviceInspector = deviceInspector, + coreConfig = coreConfig, + urlScheme = urlScheme + ) + + val appLinkUrl = "https://example.com/vault/return" + val request = PayPalWebVaultRequest("fake-setup-token-id", appLinkUrl = appLinkUrl) + val launchResult = PayPalPresentAuthChallengeResult.Success("auth state") + + every { + mockLauncher.launchWithUrl( + any(), + any(), + any(), + any(), + any(), + any() + ) + } returns launchResult + + // When + clientWithUrlScheme.vault(activity, request) + + // Then + verify { + mockLauncher.launchWithUrl( + activity = activity, + uri = any(), + token = "fake-setup-token-id", + tokenType = TokenType.VAULT_ID, + returnUrlScheme = urlScheme, + appLinkUrl = appLinkUrl + ) + } + } + fun createAppSwithEligibility(launchUrl: String?) = AppSwitchEligibilityData( appSwitchEligible = !launchUrl.isNullOrEmpty(), redirectURL = launchUrl, diff --git a/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebLauncherUnitTest.kt b/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebLauncherUnitTest.kt index 30ae75d9c..7f3f7b488 100644 --- a/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebLauncherUnitTest.kt +++ b/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebLauncherUnitTest.kt @@ -8,7 +8,8 @@ import com.braintreepayments.api.BrowserSwitchClient import com.braintreepayments.api.BrowserSwitchFinalResult import com.braintreepayments.api.BrowserSwitchOptions import com.braintreepayments.api.BrowserSwitchStartResult -import com.paypal.android.corepayments.BrowserSwitchRequestCodes +import com.paypal.android.corepayments.BrowserSwitchRequestCodes.PAYPAL_CHECKOUT +import com.paypal.android.corepayments.BrowserSwitchRequestCodes.PAYPAL_VAULT import com.paypal.android.corepayments.model.TokenType import io.mockk.every import io.mockk.mockk @@ -61,7 +62,7 @@ class PayPalWebLauncherUnitTest { get { metadata?.get("order_id") }.isEqualTo("fake-order-id") get { returnUrlScheme }.isEqualTo("com.example.app") get { url }.isEqualTo(Uri.parse("https://www.sandbox.paypal.com/checkoutnow")) - get { requestCode }.isEqualTo(BrowserSwitchRequestCodes.PAYPAL_CHECKOUT) + get { requestCode }.isEqualTo(PAYPAL_CHECKOUT) } } @@ -87,7 +88,7 @@ class PayPalWebLauncherUnitTest { get { metadata?.get("order_id") }.isEqualTo("fake-order-id") get { returnUrlScheme }.isEqualTo("com.example.app") get { url }.isEqualTo(Uri.parse("https://www.paypal.com/checkoutnow")) - get { requestCode }.isEqualTo(BrowserSwitchRequestCodes.PAYPAL_CHECKOUT) + get { requestCode }.isEqualTo(PAYPAL_CHECKOUT) } } @@ -113,7 +114,7 @@ class PayPalWebLauncherUnitTest { get { metadata?.get("order_id") }.isEqualTo("fake-order-id") get { returnUrlScheme }.isEqualTo("com.example.app") get { url }.isEqualTo(Uri.parse("https://www.paypal.com/checkoutnow")) - get { requestCode }.isEqualTo(BrowserSwitchRequestCodes.PAYPAL_CHECKOUT) + get { requestCode }.isEqualTo(PAYPAL_CHECKOUT) } } @@ -139,7 +140,7 @@ class PayPalWebLauncherUnitTest { get { metadata?.get("order_id") }.isEqualTo("fake-order-id") get { returnUrlScheme }.isEqualTo("com.example.app") get { url }.isEqualTo(Uri.parse("https://www.paypal.com/checkoutnow")) - get { requestCode }.isEqualTo(BrowserSwitchRequestCodes.PAYPAL_CHECKOUT) + get { requestCode }.isEqualTo(PAYPAL_CHECKOUT) } } @@ -185,7 +186,7 @@ class PayPalWebLauncherUnitTest { get { metadata?.get("setup_token_id") }.isEqualTo("fake-setup-token") get { returnUrlScheme }.isEqualTo("com.example.app") get { url }.isEqualTo(Uri.parse("https://sandbox.paypal.com/agreements/approve")) - get { requestCode }.isEqualTo(BrowserSwitchRequestCodes.PAYPAL_VAULT) + get { requestCode }.isEqualTo(PAYPAL_VAULT) } } @@ -211,7 +212,7 @@ class PayPalWebLauncherUnitTest { get { metadata?.get("setup_token_id") }.isEqualTo("fake-setup-token") get { returnUrlScheme }.isEqualTo("com.example.app") get { url }.isEqualTo(Uri.parse("https://paypal.com/agreements/approve")) - get { requestCode }.isEqualTo(BrowserSwitchRequestCodes.PAYPAL_VAULT) + get { requestCode }.isEqualTo(PAYPAL_VAULT) } } @@ -238,7 +239,7 @@ class PayPalWebLauncherUnitTest { @Test fun `completeCheckoutAuthRequest() parses successful checkout result`() { val browserSwitchResult = createCheckoutSuccessBrowserSwitchResult( - requestCode = BrowserSwitchRequestCodes.PAYPAL_CHECKOUT, + requestCode = PAYPAL_CHECKOUT, orderId = "fake-order-id", payerId = "fake-payer-id" ) @@ -258,7 +259,7 @@ class PayPalWebLauncherUnitTest { @Test fun `completeCheckoutAuthRequest() parses checkout failure when Payer Id is blank`() { val browserSwitchResult = createCheckoutSuccessBrowserSwitchResult( - requestCode = BrowserSwitchRequestCodes.PAYPAL_CHECKOUT, + requestCode = PAYPAL_CHECKOUT, orderId = "fake-order-id", payerId = "" ) @@ -277,7 +278,7 @@ class PayPalWebLauncherUnitTest { @Test fun `completeCheckoutAuthRequest() parses checkout failure when Order Id is blank`() { val browserSwitchResult = createCheckoutSuccessBrowserSwitchResult( - requestCode = BrowserSwitchRequestCodes.PAYPAL_CHECKOUT, + requestCode = PAYPAL_CHECKOUT, orderId = "", payerId = "fake-payer-id" ) @@ -296,7 +297,7 @@ class PayPalWebLauncherUnitTest { @Test fun `completeCheckoutAuthRequest() parses checkout failure when metadata is null`() { val browserSwitchResult = createCheckoutSuccessBrowserSwitchResult( - requestCode = BrowserSwitchRequestCodes.PAYPAL_CHECKOUT, + requestCode = PAYPAL_CHECKOUT, payerId = "fake-payer-id", metadata = null ) @@ -314,10 +315,7 @@ class PayPalWebLauncherUnitTest { @Test fun `completeCheckoutAuthRequest() parses checkout cancellation deep link url indicates failure`() { - val browserSwitchResult = createCheckoutCancellationBrowserSwitchResult( - requestCode = BrowserSwitchRequestCodes.PAYPAL_CHECKOUT, - orderId = "fake-order-id" - ) + val browserSwitchResult = createCheckoutCancellationBrowserSwitchResult() every { browserSwitchClient.completeRequest(intent, "pending request") @@ -332,7 +330,7 @@ class PayPalWebLauncherUnitTest { @Test fun `completeVaultAuthRequest() parses successful vault result`() { val browserSwitchResult = createVaultSuccessBrowserSwitchResult( - requestCode = BrowserSwitchRequestCodes.PAYPAL_VAULT, + requestCode = PAYPAL_VAULT, setupTokenId = "fake-setup-token-id", approvalSessionId = "fake-approval-session-id", ) @@ -348,11 +346,7 @@ class PayPalWebLauncherUnitTest { @Test fun `completeVaultAuthRequest() parses cancellation vault result when deep link path contains the word cancel`() { - val browserSwitchResult = createVaultCancellationBrowserSwitchResult( - requestCode = BrowserSwitchRequestCodes.PAYPAL_VAULT, - setupTokenId = "fake-setup-token-id", - approvalSessionId = "fake-approval-session-id", - ) + val browserSwitchResult = createVaultCancellationBrowserSwitchResult() every { browserSwitchClient.completeRequest(intent, "pending request") } returns browserSwitchResult @@ -365,7 +359,7 @@ class PayPalWebLauncherUnitTest { @Test fun `completeVaultAuthRequest() parses vault failure when approval session id is blank`() { val browserSwitchResult = createVaultSuccessBrowserSwitchResult( - requestCode = BrowserSwitchRequestCodes.PAYPAL_VAULT, + requestCode = PAYPAL_VAULT, setupTokenId = "fake-setup-token-id", approvalSessionId = "", ) @@ -395,13 +389,10 @@ class PayPalWebLauncherUnitTest { deepLinkUrl: Uri = createCheckoutDeepLinkUrl(payerId!!) ) = createBrowserSwitchSuccessFinalResult(requestCode, metadata, deepLinkUrl) - private fun createCheckoutCancellationBrowserSwitchResult( - requestCode: Int, - orderId: String, - ): BrowserSwitchFinalResult.Success { + private fun createCheckoutCancellationBrowserSwitchResult(): BrowserSwitchFinalResult.Success { val deepLinkUrl = "http://testurl.com/checkout?opType=cancel".toUri() - val metadata = createCheckoutMetadata(orderId) - return createBrowserSwitchSuccessFinalResult(requestCode, metadata, deepLinkUrl) + val metadata = createCheckoutMetadata("fake-order-id") + return createBrowserSwitchSuccessFinalResult(PAYPAL_CHECKOUT, metadata, deepLinkUrl) } private fun createVaultMetadata(setupTokenId: String) = JSONObject() @@ -418,15 +409,11 @@ class PayPalWebLauncherUnitTest { deepLinkUrl: Uri = createVaultDeepLinkUrl(approvalSessionId!!) ) = createBrowserSwitchSuccessFinalResult(requestCode, metadata, deepLinkUrl) - private fun createVaultCancellationBrowserSwitchResult( - requestCode: Int, - setupTokenId: String, - approvalSessionId: String - ): BrowserSwitchFinalResult.Success { - val metadata = createVaultMetadata(setupTokenId) + private fun createVaultCancellationBrowserSwitchResult(): BrowserSwitchFinalResult.Success { + val metadata = createVaultMetadata("fake-setup-token-id") val deepLinkUrl = - "http://testurl.com/checkout/cancel?approval_session_id=$approvalSessionId".toUri() - return createBrowserSwitchSuccessFinalResult(requestCode, metadata, deepLinkUrl) + "http://testurl.com/checkout/cancel?approval_session_id=fake-approval-session-id".toUri() + return createBrowserSwitchSuccessFinalResult(PAYPAL_VAULT, metadata, deepLinkUrl) } private fun createBrowserSwitchSuccessFinalResult( @@ -466,7 +453,7 @@ class PayPalWebLauncherUnitTest { get { metadata?.get("order_id") }.isEqualTo("order-123") get { returnUrlScheme }.isEqualTo("custom_url_scheme") get { url }.isEqualTo(Uri.parse("https://paypal.com/app-switch")) - get { requestCode }.isEqualTo(BrowserSwitchRequestCodes.PAYPAL_CHECKOUT) + get { requestCode }.isEqualTo(PAYPAL_CHECKOUT) } } @@ -492,7 +479,7 @@ class PayPalWebLauncherUnitTest { get { metadata?.get("setup_token_id") }.isEqualTo("setup-456") get { returnUrlScheme }.isEqualTo("custom_url_scheme") get { url }.isEqualTo(Uri.parse("https://paypal.com/vault-switch")) - get { requestCode }.isEqualTo(BrowserSwitchRequestCodes.PAYPAL_VAULT) + get { requestCode }.isEqualTo(PAYPAL_VAULT) } } @@ -516,4 +503,118 @@ class PayPalWebLauncherUnitTest { as PayPalPresentAuthChallengeResult.Failure assertEquals("error message from browser switch", result.error.errorDescription) } + + @Test + fun `launchWithUrl() with appLinkUrl sets appLinkUri and nullifies returnUrlScheme`() { + sut = PayPalWebLauncher(browserSwitchClient) + + val slot = slot() + every { + browserSwitchClient.start(activity, capture(slot)) + } returns BrowserSwitchStartResult.Started("pending request") + + val appLinkUrl = "https://example.com/return" + sut.launchWithUrl( + activity, + Uri.parse("https://paypal.com/checkout"), + "order-123", + TokenType.ORDER_ID, + "custom_url_scheme", + appLinkUrl + ) + + val browserSwitchOptions = slot.captured + expectThat(browserSwitchOptions) { + get { metadata?.get("order_id") }.isEqualTo("order-123") + get { returnUrlScheme }.isEqualTo(null) // Should be null when appLinkUrl is provided + get { url }.isEqualTo(Uri.parse("https://paypal.com/checkout")) + get { requestCode }.isEqualTo(PAYPAL_CHECKOUT) + get { appLinkUri }.isEqualTo(appLinkUrl.toUri()) + } + } + + @Test + fun `launchWithUrl() with null appLinkUrl uses returnUrlScheme`() { + sut = PayPalWebLauncher(browserSwitchClient) + + val slot = slot() + every { + browserSwitchClient.start(activity, capture(slot)) + } returns BrowserSwitchStartResult.Started("pending request") + + sut.launchWithUrl( + activity, + Uri.parse("https://paypal.com/checkout"), + "order-123", + TokenType.ORDER_ID, + "custom_url_scheme", + null + ) + + val browserSwitchOptions = slot.captured + expectThat(browserSwitchOptions) { + get { metadata?.get("order_id") }.isEqualTo("order-123") + get { returnUrlScheme }.isEqualTo("custom_url_scheme") + get { url }.isEqualTo(Uri.parse("https://paypal.com/checkout")) + get { requestCode }.isEqualTo(PAYPAL_CHECKOUT) + get { appLinkUri }.isEqualTo(null) + } + } + + @Test + fun `launchWithUrl() with vault token and appLinkUrl works correctly`() { + sut = PayPalWebLauncher(browserSwitchClient) + + val slot = slot() + every { + browserSwitchClient.start(activity, capture(slot)) + } returns BrowserSwitchStartResult.Started("pending request") + + val appLinkUrl = "https://example.com/vault/return" + sut.launchWithUrl( + activity, + Uri.parse("https://paypal.com/vault"), + "setup-456", + TokenType.VAULT_ID, + "custom_url_scheme", + appLinkUrl + ) + + val browserSwitchOptions = slot.captured + expectThat(browserSwitchOptions) { + get { metadata?.get("setup_token_id") }.isEqualTo("setup-456") + get { returnUrlScheme }.isEqualTo(null) // Should be null when appLinkUrl is provided + get { url }.isEqualTo(Uri.parse("https://paypal.com/vault")) + get { requestCode }.isEqualTo(PAYPAL_VAULT) + get { appLinkUri }.isEqualTo(appLinkUrl.toUri()) + } + } + + @Test + fun `launchWithUrl() with default parameters maintains backward compatibility`() { + sut = PayPalWebLauncher(browserSwitchClient) + + val slot = slot() + every { + browserSwitchClient.start(activity, capture(slot)) + } returns BrowserSwitchStartResult.Started("pending request") + + // Call with old signature (should default appLinkUrl to null) + sut.launchWithUrl( + activity, + Uri.parse("https://paypal.com/checkout"), + "order-123", + TokenType.ORDER_ID, + "custom_url_scheme" + ) + + val browserSwitchOptions = slot.captured + expectThat(browserSwitchOptions) { + get { metadata?.get("order_id") }.isEqualTo("order-123") + get { returnUrlScheme }.isEqualTo("custom_url_scheme") + get { url }.isEqualTo(Uri.parse("https://paypal.com/checkout")) + get { requestCode }.isEqualTo(PAYPAL_CHECKOUT) + get { appLinkUri }.isEqualTo(null) + } + } } From 9c6eb56f9ee8645e66ec1d6fbb622d667e6a4fd5 Mon Sep 17 00:00:00 2001 From: Karthik Gangineni Date: Thu, 6 Nov 2025 15:23:32 -0600 Subject: [PATCH 03/10] * restore deprecated start function * deprecate vault function without app switch feature --- .../ui/paypalweb/PayPalCheckoutViewModel.kt | 2 +- .../ui/paypalwebvault/PayPalVaultViewModel.kt | 2 +- .../PayPalWebCheckoutClient.kt | 121 ++++++++++++++++-- .../PayPalWebVaultRequest.kt | 4 +- .../PayPalWebCheckoutClientUnitTest.kt | 84 ++++++------ 5 files changed, 157 insertions(+), 56 deletions(-) diff --git a/Demo/src/main/java/com/paypal/android/ui/paypalweb/PayPalCheckoutViewModel.kt b/Demo/src/main/java/com/paypal/android/ui/paypalweb/PayPalCheckoutViewModel.kt index 2fc0ba70b..5a0f2e320 100644 --- a/Demo/src/main/java/com/paypal/android/ui/paypalweb/PayPalCheckoutViewModel.kt +++ b/Demo/src/main/java/com/paypal/android/ui/paypalweb/PayPalCheckoutViewModel.kt @@ -118,7 +118,7 @@ class PayPalCheckoutViewModel @Inject constructor( val checkoutRequest = PayPalWebCheckoutRequest(orderId, fundingSource, appSwitchWhenEligible, APP_URL) - when (val startResult = paypalClient.start(activity, checkoutRequest)) { + when (val startResult = paypalClient.startTemp(activity, checkoutRequest)) { is PayPalPresentAuthChallengeResult.Success -> { // do nothing; wait for user to authenticate PayPal checkout in Chrome Custom Tab } diff --git a/Demo/src/main/java/com/paypal/android/ui/paypalwebvault/PayPalVaultViewModel.kt b/Demo/src/main/java/com/paypal/android/ui/paypalwebvault/PayPalVaultViewModel.kt index 86bd06913..344de9117 100644 --- a/Demo/src/main/java/com/paypal/android/ui/paypalwebvault/PayPalVaultViewModel.kt +++ b/Demo/src/main/java/com/paypal/android/ui/paypalwebvault/PayPalVaultViewModel.kt @@ -99,7 +99,7 @@ class PayPalVaultViewModel @Inject constructor( vaultPayPalState = ActionState.Loading viewModelScope.launch { - when (val result = paypalClient.vault(activity, request)) { + when (val result = paypalClient.vaultTemp(activity, request)) { is PayPalPresentAuthChallengeResult.Success -> { // do nothing; wait for user to authenticate PayPal vault in Chrome Custom Tab } diff --git a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt index ef30a8e4a..b5d063e9f 100644 --- a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt +++ b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt @@ -106,7 +106,59 @@ class PayPalWebCheckoutClient internal constructor( * * @param request [PayPalWebCheckoutRequest] for requesting an order approval */ - suspend fun start( + @Deprecated( + message = "Use start(activity, request, callback) instead.", + replaceWith = ReplaceWith("start(activity, request, callback)") + ) + fun start( + activity: Activity, + request: PayPalWebCheckoutRequest + ): PayPalPresentAuthChallengeResult { + checkoutOrderId = request.orderId + analytics.notify(CheckoutEvent.STARTED, checkoutOrderId) + + val launchUri = buildPayPalCheckoutUri( + orderId = request.orderId, + funding = request.fundingSource, + redirectUrl = request.appLinkUrl ?: redirectUriPayPalCheckout + ) + + val result = payPalWebLauncher.launchWithUrl( + activity = activity, + uri = launchUri, + token = request.orderId, + tokenType = TokenType.ORDER_ID, + returnUrlScheme = urlScheme, + appLinkUrl = request.appLinkUrl + ) + + when (result) { + is PayPalPresentAuthChallengeResult.Success -> { + analytics.notify( + CheckoutEvent.AUTH_CHALLENGE_PRESENTATION_SUCCEEDED, + checkoutOrderId + ) + + // update auth state value in session store + sessionStore.authState = result.authState + } + + is PayPalPresentAuthChallengeResult.Failure -> { + analytics.notify( + CheckoutEvent.AUTH_CHALLENGE_PRESENTATION_FAILED, + checkoutOrderId + ) + } + } + return result + } + + /** + * Confirm PayPal payment source for an order. + * + * @param request [PayPalWebCheckoutRequest] for requesting an order approval + */ + suspend fun startTemp( activity: Activity, request: PayPalWebCheckoutRequest ): PayPalPresentAuthChallengeResult { @@ -183,7 +235,7 @@ class PayPalWebCheckoutClient internal constructor( callback: PayPalWebStartCallback ) { applicationScope.launch { - val result = start(activity, request) + val result = startTemp(activity, request) withContext(Dispatchers.Main) { callback.onPayPalWebStartResult(result) } @@ -195,7 +247,56 @@ class PayPalWebCheckoutClient internal constructor( * * @param request [PayPalWebVaultRequest] for vaulting PayPal as a payment method */ - suspend fun vault( + @Deprecated( + message = "Use vault(activity, request, callback) instead.", + replaceWith = ReplaceWith("vault(activity, request, callback)") + ) + fun vault( + activity: Activity, + request: PayPalWebVaultRequest + ): PayPalPresentAuthChallengeResult { + checkoutOrderId = request.setupTokenId + analytics.notify(VaultEvent.STARTED, vaultSetupTokenId) + + val launchUri = buildPayPalVaultUri(request.setupTokenId) + + val result = payPalWebLauncher.launchWithUrl( + activity = activity, + uri = launchUri, + token = request.setupTokenId, + tokenType = TokenType.VAULT_ID, + returnUrlScheme = urlScheme, + appLinkUrl = request.appLinkUrl + ) + + when (result) { + is PayPalPresentAuthChallengeResult.Success -> { + analytics.notify( + VaultEvent.AUTH_CHALLENGE_PRESENTATION_SUCCEEDED, + vaultSetupTokenId + ) + + // update auth state value in session store + sessionStore.authState = result.authState + } + + is PayPalPresentAuthChallengeResult.Failure -> { + analytics.notify( + VaultEvent.AUTH_CHALLENGE_PRESENTATION_FAILED, + vaultSetupTokenId + ) + } + } + + return result + } + + /** + * Vault PayPal as a payment method. + * + * @param request [PayPalWebVaultRequest] for vaulting PayPal as a payment method + */ + suspend fun vaultTemp( activity: Activity, request: PayPalWebVaultRequest ): PayPalPresentAuthChallengeResult { @@ -257,19 +358,19 @@ class PayPalWebCheckoutClient internal constructor( callback: PayPalWebVaultCallback ) { CoroutineScope(Dispatchers.Main).launch { - callback.onPayPalWebVaultResult(vault(activity, request)) + callback.onPayPalWebVaultResult(vaultTemp(activity, request)) } } /** * After a merchant app has re-entered the foreground following an auth challenge - * (@see [PayPalWebCheckoutClient.start]), call this method to see if a user has + * (@see [PayPalWebCheckoutClient.startTemp]), call this method to see if a user has * successfully authorized a PayPal account as a payment source. * * @param [intent] An Android intent that holds the deep link put the merchant app * back into the foreground after an auth challenge. * @param [authState] A continuation state received from [PayPalPresentAuthChallengeResult.Success] - * when calling [PayPalWebCheckoutClient.start]. This is needed to properly verify that an + * when calling [PayPalWebCheckoutClient.startTemp]. This is needed to properly verify that an * authorization completed successfully. */ @Deprecated( @@ -297,7 +398,7 @@ class PayPalWebCheckoutClient internal constructor( /** * After a merchant app has re-entered the foreground following an auth challenge - * (@see [PayPalWebCheckoutClient.start]), call this method to see if a user has + * (@see [PayPalWebCheckoutClient.startTemp]), call this method to see if a user has * successfully authorized a PayPal account as a payment source. * * @param [intent] An Android intent that holds the deep link put the merchant app @@ -331,13 +432,13 @@ class PayPalWebCheckoutClient internal constructor( /** * After a merchant app has re-entered the foreground following an auth challenge - * (@see [PayPalWebCheckoutClient.vault]), call this method to see if a user has + * (@see [PayPalWebCheckoutClient.vaultTemp]), call this method to see if a user has * successfully authorized a PayPal account for vaulting. * * @param [intent] An Android intent that holds the deep link put the merchant app * back into the foreground after an auth challenge. * @param [authState] A continuation state received from [PayPalPresentAuthChallengeResult.Success] - * when calling [PayPalWebCheckoutClient.vault]. This is needed to properly verify that an + * when calling [PayPalWebCheckoutClient.vaultTemp]. This is needed to properly verify that an * authorization completed successfully. */ @Deprecated( @@ -425,7 +526,7 @@ class PayPalWebCheckoutClient internal constructor( } /** * After a merchant app has re-entered the foreground following an auth challenge - * (@see [PayPalWebCheckoutClient.vault]), call this method to see if a user has + * (@see [PayPalWebCheckoutClient.vaultTemp]), call this method to see if a user has * successfully authorized a PayPal account for vaulting. * * @param [intent] An Android intent that holds the deep link put the merchant app diff --git a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt index 61e5ad588..a363d70ea 100644 --- a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt +++ b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt @@ -1,7 +1,7 @@ package com.paypal.android.paypalwebpayments /** - * Request to vault a PayPal payment method using [PayPalWebCheckoutClient.vault]. + * Request to vault a PayPal payment method using [PayPalWebCheckoutClient.vaultTemp]. * * @property [setupTokenId] ID for the setup token associated with the vault approval * @property [approveVaultHref] URL for the approval web page @@ -18,7 +18,7 @@ constructor( ) { /** - * Request to vault a PayPal payment method using [PayPalWebCheckoutClient.vault]. + * Request to vault a PayPal payment method using [PayPalWebCheckoutClient.vaultTemp]. * * @property [setupTokenId] ID for the setup token associated with the vault approval */ diff --git a/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClientUnitTest.kt b/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClientUnitTest.kt index 1abc19bfe..78afa2a75 100644 --- a/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClientUnitTest.kt +++ b/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClientUnitTest.kt @@ -80,7 +80,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult val request = PayPalWebCheckoutRequest("fake-order-id", appLinkUrl = appLinkUrl) - sut.start(activity, request) + sut.startTemp(activity, request) // Verify launchWithUrl is called with correct parameters verify(exactly = 1) { @@ -111,7 +111,7 @@ class PayPalWebCheckoutClientUnitTest { val appLinkUrl = "https://example.com/return" val request = PayPalWebCheckoutRequest("fake-order-id", appLinkUrl = appLinkUrl) - val result = sut.start(activity, request) + val result = sut.startTemp(activity, request) assertSame(launchResult, result) } @@ -130,7 +130,7 @@ class PayPalWebCheckoutClientUnitTest { val appLinkUrl = "https://example.com/vault/return" val request = PayPalWebVaultRequest("fake-setup-token-id", appLinkUrl = appLinkUrl) - sut.vault(activity, request) + sut.vaultTemp(activity, request) verify(exactly = 1) { payPalWebLauncher.launchWithUrl( activity = activity, @@ -159,7 +159,7 @@ class PayPalWebCheckoutClientUnitTest { val appLinkUrl = "https://example.com/vault/return" val request = PayPalWebVaultRequest("fake-setup-token-id", appLinkUrl = appLinkUrl) - val result = sut.vault(activity, request) as PayPalPresentAuthChallengeResult.Failure + val result = sut.vaultTemp(activity, request) as PayPalPresentAuthChallengeResult.Failure assertSame(sdkError, result.error) } @@ -225,7 +225,7 @@ class PayPalWebCheckoutClientUnitTest { } returns successResult val request = PayPalWebCheckoutRequest("fake-order-id") - sut.start(activity, request) + sut.startTemp(activity, request) val result = sut.finishStart(intent) assertSame(successResult, result) } @@ -261,7 +261,7 @@ class PayPalWebCheckoutClientUnitTest { patchCCOWithAppSwitchEligibility = patchCCOWithAppSwitchEligibility, ) val request = PayPalWebCheckoutRequest("fake-order-id") - launchWithUrlClient.start(activity, request) + launchWithUrlClient.startTemp(activity, request) sut.restore(launchWithUrlClient.instanceState) val result = sut.finishStart(intent) @@ -289,7 +289,7 @@ class PayPalWebCheckoutClientUnitTest { } returns failureResult val request = PayPalWebCheckoutRequest("fake-order-id") - sut.start(activity, request) + sut.startTemp(activity, request) val result = sut.finishStart(intent) assertSame(failureResult, result) } @@ -315,7 +315,7 @@ class PayPalWebCheckoutClientUnitTest { } returns failureResult val request = PayPalWebCheckoutRequest("fake-order-id") - sut.start(activity, request) + sut.startTemp(activity, request) val result = sut.finishStart(intent) assertSame(failureResult, result) } @@ -340,7 +340,7 @@ class PayPalWebCheckoutClientUnitTest { } returns canceledResult val request = PayPalWebCheckoutRequest("fake-order-id") - sut.start(activity, request) + sut.startTemp(activity, request) val result = sut.finishStart(intent) assertSame(canceledResult, result) } @@ -375,7 +375,7 @@ class PayPalWebCheckoutClientUnitTest { patchCCOWithAppSwitchEligibility = patchCCOWithAppSwitchEligibility ) val request = PayPalWebCheckoutRequest("fake-order-id") - launchWithUrlClient.start(activity, request) + launchWithUrlClient.startTemp(activity, request) sut.restore(launchWithUrlClient.instanceState) val result = sut.finishStart(intent) @@ -402,7 +402,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeCheckoutAuthRequest(intent, "auth state") } returns successResult - sut.start(activity, PayPalWebCheckoutRequest("fake-order-id")) + sut.startTemp(activity, PayPalWebCheckoutRequest("fake-order-id")) sut.finishStart(intent) assertNull(sut.finishStart(intent)) } @@ -427,7 +427,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeCheckoutAuthRequest(intent, "auth state") } returns failureResult - sut.start(activity, PayPalWebCheckoutRequest("fake-order-id")) + sut.startTemp(activity, PayPalWebCheckoutRequest("fake-order-id")) sut.finishStart(intent) assertNull(sut.finishStart(intent)) } @@ -451,7 +451,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeCheckoutAuthRequest(intent, "auth state") } returns canceledResult - sut.start(activity, PayPalWebCheckoutRequest("fake-order-id")) + sut.startTemp(activity, PayPalWebCheckoutRequest("fake-order-id")) sut.finishStart(intent) assertNull(sut.finishStart(intent)) } @@ -535,7 +535,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.start(activity, request) + val result = sut.startTemp(activity, request) // Then coVerify { @@ -577,7 +577,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.start(activity, request) + val result = sut.startTemp(activity, request) // Then verify { @@ -609,7 +609,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.start(activity, request) + val result = sut.startTemp(activity, request) // Then verify { @@ -650,7 +650,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.start(activity, request) + val result = sut.startTemp(activity, request) // Then coVerify { patchCCOWithAppSwitchEligibility(any(), any(), any(), any(), any()) } @@ -685,7 +685,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.start(activity, request) + val result = sut.startTemp(activity, request) // Then coVerify(exactly = 0) { @@ -718,7 +718,7 @@ class PayPalWebCheckoutClientUnitTest { } returns PayPalPresentAuthChallengeResult.Success("state") // When - sut.start(activity, request) + sut.startTemp(activity, request) // Then coVerify { @@ -769,7 +769,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.vault(activity, request) + val result = sut.vaultTemp(activity, request) // Then coVerify { @@ -811,7 +811,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.vault(activity, request) + val result = sut.vaultTemp(activity, request) // Then coVerify { @@ -852,7 +852,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.vault(activity, request) + val result = sut.vaultTemp(activity, request) // Then verify { @@ -889,7 +889,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.vault(activity, request) + val result = sut.vaultTemp(activity, request) // Then verify { @@ -920,7 +920,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.vault(activity, request) + val result = sut.vaultTemp(activity, request) // Then coVerify(exactly = 0) { @@ -953,7 +953,7 @@ class PayPalWebCheckoutClientUnitTest { } returns PayPalPresentAuthChallengeResult.Success("state") // When - sut.vault(activity, request) + sut.vaultTemp(activity, request) // Then coVerify { @@ -980,7 +980,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.start(activity, request) + val result = sut.startTemp(activity, request) // Then coVerify(exactly = 0) { @@ -1010,7 +1010,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.vault(activity, request) + val result = sut.vaultTemp(activity, request) // Then coVerify(exactly = 0) { @@ -1040,7 +1040,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.start(activity, request) + val result = sut.startTemp(activity, request) // Then coVerify(exactly = 0) { @@ -1070,7 +1070,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.vault(activity, request) + val result = sut.vaultTemp(activity, request) // Then coVerify(exactly = 0) { @@ -1123,7 +1123,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeVaultAuthRequest(intent, "auth state") } returns successResult - previousClient.vault(activity, PayPalWebVaultRequest("fake-setup-token-id")) + previousClient.vaultTemp(activity, PayPalWebVaultRequest("fake-setup-token-id")) sut.restore(previousClient.instanceState) val result = sut.finishVault(intent) as PayPalWebCheckoutFinishVaultResult.Success @@ -1161,7 +1161,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeVaultAuthRequest(intent, "auth state") } returns successResult - previousClient.vault(activity, PayPalWebVaultRequest("fake-setup-token-id")) + previousClient.vaultTemp(activity, PayPalWebVaultRequest("fake-setup-token-id")) sut.restore(previousClient.instanceState) val result = sut.finishVault(intent) as PayPalWebCheckoutFinishVaultResult.Success @@ -1187,7 +1187,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeVaultAuthRequest(intent, "auth state") } returns PayPalWebCheckoutFinishVaultResult.Failure(error) - sut.vault(activity, PayPalWebVaultRequest("fake-setup-token-id")) + sut.vaultTemp(activity, PayPalWebVaultRequest("fake-setup-token-id")) val result = sut.finishVault(intent) as PayPalWebCheckoutFinishVaultResult.Failure assertSame(error, result.error) } @@ -1211,7 +1211,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeVaultAuthRequest(intent, "auth state") } returns PayPalWebCheckoutFinishVaultResult.Failure(error) - sut.vault(activity, PayPalWebVaultRequest("fake-setup-token-id")) + sut.vaultTemp(activity, PayPalWebVaultRequest("fake-setup-token-id")) sut.restore(sut.instanceState) val result = sut.finishVault(intent) as PayPalWebCheckoutFinishVaultResult.Failure @@ -1236,7 +1236,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeVaultAuthRequest(intent, "auth state") } returns PayPalWebCheckoutFinishVaultResult.Canceled - sut.vault(activity, PayPalWebVaultRequest("fake-setup-token-id")) + sut.vaultTemp(activity, PayPalWebVaultRequest("fake-setup-token-id")) val result = sut.finishVault(intent) assertSame(PayPalWebCheckoutFinishVaultResult.Canceled, result) } @@ -1259,7 +1259,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeVaultAuthRequest(intent, "auth state") } returns PayPalWebCheckoutFinishVaultResult.Canceled - sut.vault(activity, PayPalWebVaultRequest("fake-setup-token-id")) + sut.vaultTemp(activity, PayPalWebVaultRequest("fake-setup-token-id")) sut.restore(sut.instanceState) val result = sut.finishVault(intent) @@ -1286,7 +1286,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeVaultAuthRequest(intent, "auth state") } returns successResult - sut.vault(activity, PayPalWebVaultRequest("fake-setup-token-id")) + sut.vaultTemp(activity, PayPalWebVaultRequest("fake-setup-token-id")) sut.finishVault(intent) assertNull(sut.finishVault(intent)) } @@ -1310,7 +1310,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeVaultAuthRequest(intent, "auth state") } returns PayPalWebCheckoutFinishVaultResult.Failure(error) - sut.vault(activity, PayPalWebVaultRequest("fake-setup-token-id")) + sut.vaultTemp(activity, PayPalWebVaultRequest("fake-setup-token-id")) sut.finishVault(intent) assertNull(sut.finishVault(intent)) } @@ -1333,7 +1333,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeVaultAuthRequest(intent, "auth state") } returns PayPalWebCheckoutFinishVaultResult.Canceled - sut.vault(activity, PayPalWebVaultRequest("fake-setup-token-id")) + sut.vaultTemp(activity, PayPalWebVaultRequest("fake-setup-token-id")) sut.finishVault(intent) assertNull(sut.finishVault(intent)) } @@ -1368,7 +1368,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - clientWithUrlScheme.start(activity, request) + clientWithUrlScheme.startTemp(activity, request) // Then verify { @@ -1413,7 +1413,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - clientWithUrlScheme.vault(activity, request) + clientWithUrlScheme.vaultTemp(activity, request) // Then verify { @@ -1460,7 +1460,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - clientWithUrlScheme.start(activity, request) + clientWithUrlScheme.startTemp(activity, request) // Then verify { @@ -1507,7 +1507,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - clientWithUrlScheme.vault(activity, request) + clientWithUrlScheme.vaultTemp(activity, request) // Then verify { From 6a5ec0d61a41c9c04dec68315feb88b36f029f50 Mon Sep 17 00:00:00 2001 From: Karthik Gangineni Date: Thu, 6 Nov 2025 15:36:14 -0600 Subject: [PATCH 04/10] * restore deprecated start function * deprecate vault function without app switch feature * introduces startAsync, vaultAsync functions to provide suspend support --- .../ui/paypalweb/PayPalCheckoutViewModel.kt | 2 +- .../ui/paypalwebvault/PayPalVaultViewModel.kt | 2 +- .../PayPalWebCheckoutClient.kt | 29 ++- .../PayPalWebVaultRequest.kt | 4 +- .../PayPalWebCheckoutClientUnitTest.kt | 246 ++++++++++++++---- 5 files changed, 219 insertions(+), 64 deletions(-) diff --git a/Demo/src/main/java/com/paypal/android/ui/paypalweb/PayPalCheckoutViewModel.kt b/Demo/src/main/java/com/paypal/android/ui/paypalweb/PayPalCheckoutViewModel.kt index 5a0f2e320..808c46551 100644 --- a/Demo/src/main/java/com/paypal/android/ui/paypalweb/PayPalCheckoutViewModel.kt +++ b/Demo/src/main/java/com/paypal/android/ui/paypalweb/PayPalCheckoutViewModel.kt @@ -118,7 +118,7 @@ class PayPalCheckoutViewModel @Inject constructor( val checkoutRequest = PayPalWebCheckoutRequest(orderId, fundingSource, appSwitchWhenEligible, APP_URL) - when (val startResult = paypalClient.startTemp(activity, checkoutRequest)) { + when (val startResult = paypalClient.startAsync(activity, checkoutRequest)) { is PayPalPresentAuthChallengeResult.Success -> { // do nothing; wait for user to authenticate PayPal checkout in Chrome Custom Tab } diff --git a/Demo/src/main/java/com/paypal/android/ui/paypalwebvault/PayPalVaultViewModel.kt b/Demo/src/main/java/com/paypal/android/ui/paypalwebvault/PayPalVaultViewModel.kt index 344de9117..34bc6fdde 100644 --- a/Demo/src/main/java/com/paypal/android/ui/paypalwebvault/PayPalVaultViewModel.kt +++ b/Demo/src/main/java/com/paypal/android/ui/paypalwebvault/PayPalVaultViewModel.kt @@ -99,7 +99,7 @@ class PayPalVaultViewModel @Inject constructor( vaultPayPalState = ActionState.Loading viewModelScope.launch { - when (val result = paypalClient.vaultTemp(activity, request)) { + when (val result = paypalClient.vaultAsync(activity, request)) { is PayPalPresentAuthChallengeResult.Success -> { // do nothing; wait for user to authenticate PayPal vault in Chrome Custom Tab } diff --git a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt index b5d063e9f..18d0afc40 100644 --- a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt +++ b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt @@ -30,6 +30,7 @@ import kotlinx.coroutines.withContext /** * Use this client to approve an order with a [PayPalWebCheckoutRequest]. */ +@Suppress("TooManyFunctions") // Necessary due to multiple method variations for backward compatibility class PayPalWebCheckoutClient internal constructor( private val analytics: PayPalWebAnalytics, private val payPalWebLauncher: PayPalWebLauncher, @@ -107,8 +108,8 @@ class PayPalWebCheckoutClient internal constructor( * @param request [PayPalWebCheckoutRequest] for requesting an order approval */ @Deprecated( - message = "Use start(activity, request, callback) instead.", - replaceWith = ReplaceWith("start(activity, request, callback)") + message = "Use startAsync(activity, request) or start(activity, request, callback) instead.", + replaceWith = ReplaceWith("startAsync(activity, request)") ) fun start( activity: Activity, @@ -158,7 +159,7 @@ class PayPalWebCheckoutClient internal constructor( * * @param request [PayPalWebCheckoutRequest] for requesting an order approval */ - suspend fun startTemp( + suspend fun startAsync( activity: Activity, request: PayPalWebCheckoutRequest ): PayPalPresentAuthChallengeResult { @@ -235,7 +236,7 @@ class PayPalWebCheckoutClient internal constructor( callback: PayPalWebStartCallback ) { applicationScope.launch { - val result = startTemp(activity, request) + val result = startAsync(activity, request) withContext(Dispatchers.Main) { callback.onPayPalWebStartResult(result) } @@ -248,8 +249,8 @@ class PayPalWebCheckoutClient internal constructor( * @param request [PayPalWebVaultRequest] for vaulting PayPal as a payment method */ @Deprecated( - message = "Use vault(activity, request, callback) instead.", - replaceWith = ReplaceWith("vault(activity, request, callback)") + message = "Use vaultAsync(activity, request) or vault(activity, request, callback) instead.", + replaceWith = ReplaceWith("vaultAsync(activity, request)") ) fun vault( activity: Activity, @@ -296,7 +297,7 @@ class PayPalWebCheckoutClient internal constructor( * * @param request [PayPalWebVaultRequest] for vaulting PayPal as a payment method */ - suspend fun vaultTemp( + suspend fun vaultAsync( activity: Activity, request: PayPalWebVaultRequest ): PayPalPresentAuthChallengeResult { @@ -358,19 +359,19 @@ class PayPalWebCheckoutClient internal constructor( callback: PayPalWebVaultCallback ) { CoroutineScope(Dispatchers.Main).launch { - callback.onPayPalWebVaultResult(vaultTemp(activity, request)) + callback.onPayPalWebVaultResult(vaultAsync(activity, request)) } } /** * After a merchant app has re-entered the foreground following an auth challenge - * (@see [PayPalWebCheckoutClient.startTemp]), call this method to see if a user has + * (@see [PayPalWebCheckoutClient.startAsync]), call this method to see if a user has * successfully authorized a PayPal account as a payment source. * * @param [intent] An Android intent that holds the deep link put the merchant app * back into the foreground after an auth challenge. * @param [authState] A continuation state received from [PayPalPresentAuthChallengeResult.Success] - * when calling [PayPalWebCheckoutClient.startTemp]. This is needed to properly verify that an + * when calling [PayPalWebCheckoutClient.startAsync]. This is needed to properly verify that an * authorization completed successfully. */ @Deprecated( @@ -398,7 +399,7 @@ class PayPalWebCheckoutClient internal constructor( /** * After a merchant app has re-entered the foreground following an auth challenge - * (@see [PayPalWebCheckoutClient.startTemp]), call this method to see if a user has + * (@see [PayPalWebCheckoutClient.startAsync]), call this method to see if a user has * successfully authorized a PayPal account as a payment source. * * @param [intent] An Android intent that holds the deep link put the merchant app @@ -432,13 +433,13 @@ class PayPalWebCheckoutClient internal constructor( /** * After a merchant app has re-entered the foreground following an auth challenge - * (@see [PayPalWebCheckoutClient.vaultTemp]), call this method to see if a user has + * (@see [PayPalWebCheckoutClient.vaultAsync]), call this method to see if a user has * successfully authorized a PayPal account for vaulting. * * @param [intent] An Android intent that holds the deep link put the merchant app * back into the foreground after an auth challenge. * @param [authState] A continuation state received from [PayPalPresentAuthChallengeResult.Success] - * when calling [PayPalWebCheckoutClient.vaultTemp]. This is needed to properly verify that an + * when calling [PayPalWebCheckoutClient.vaultAsync]. This is needed to properly verify that an * authorization completed successfully. */ @Deprecated( @@ -526,7 +527,7 @@ class PayPalWebCheckoutClient internal constructor( } /** * After a merchant app has re-entered the foreground following an auth challenge - * (@see [PayPalWebCheckoutClient.vaultTemp]), call this method to see if a user has + * (@see [PayPalWebCheckoutClient.vaultAsync]), call this method to see if a user has * successfully authorized a PayPal account for vaulting. * * @param [intent] An Android intent that holds the deep link put the merchant app diff --git a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt index a363d70ea..b2f258969 100644 --- a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt +++ b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt @@ -1,7 +1,7 @@ package com.paypal.android.paypalwebpayments /** - * Request to vault a PayPal payment method using [PayPalWebCheckoutClient.vaultTemp]. + * Request to vault a PayPal payment method using [PayPalWebCheckoutClient.vaultAsync]. * * @property [setupTokenId] ID for the setup token associated with the vault approval * @property [approveVaultHref] URL for the approval web page @@ -18,7 +18,7 @@ constructor( ) { /** - * Request to vault a PayPal payment method using [PayPalWebCheckoutClient.vaultTemp]. + * Request to vault a PayPal payment method using [PayPalWebCheckoutClient.vaultAsync]. * * @property [setupTokenId] ID for the setup token associated with the vault approval */ diff --git a/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClientUnitTest.kt b/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClientUnitTest.kt index 78afa2a75..1ac332a91 100644 --- a/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClientUnitTest.kt +++ b/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClientUnitTest.kt @@ -13,7 +13,9 @@ import com.paypal.android.corepayments.model.APIResult import com.paypal.android.corepayments.model.AppSwitchEligibility import com.paypal.android.corepayments.model.AppSwitchEligibilityData import com.paypal.android.corepayments.model.TokenType +import com.paypal.android.paypalwebpayments.analytics.CheckoutEvent import com.paypal.android.paypalwebpayments.analytics.PayPalWebAnalytics +import com.paypal.android.paypalwebpayments.analytics.VaultEvent import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every @@ -66,7 +68,7 @@ class PayPalWebCheckoutClientUnitTest { } @Test - fun `start() launches PayPal web checkout`() = runTest { + fun `startAsync() launches PayPal web checkout`() = runTest { val launchResult = PayPalPresentAuthChallengeResult.Success("auth state") every { payPalWebLauncher.launchWithUrl( @@ -80,7 +82,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult val request = PayPalWebCheckoutRequest("fake-order-id", appLinkUrl = appLinkUrl) - sut.startTemp(activity, request) + sut.startAsync(activity, request) // Verify launchWithUrl is called with correct parameters verify(exactly = 1) { @@ -95,7 +97,7 @@ class PayPalWebCheckoutClientUnitTest { } @Test - fun `start() notifies merchant of browser switch failure`() = runTest { + fun `startAsync() notifies merchant of browser switch failure`() = runTest { val sdkError = PayPalSDKError(123, "fake error description") val launchResult = PayPalPresentAuthChallengeResult.Failure(sdkError) every { @@ -111,12 +113,12 @@ class PayPalWebCheckoutClientUnitTest { val appLinkUrl = "https://example.com/return" val request = PayPalWebCheckoutRequest("fake-order-id", appLinkUrl = appLinkUrl) - val result = sut.startTemp(activity, request) + val result = sut.startAsync(activity, request) assertSame(launchResult, result) } @Test - fun `vault() launches PayPal web checkout`() = runTest { + fun `vaultAsync() launches PayPal web checkout`() = runTest { val launchResult = PayPalPresentAuthChallengeResult.Success("auth state") every { payPalWebLauncher.launchWithUrl( @@ -130,7 +132,7 @@ class PayPalWebCheckoutClientUnitTest { val appLinkUrl = "https://example.com/vault/return" val request = PayPalWebVaultRequest("fake-setup-token-id", appLinkUrl = appLinkUrl) - sut.vaultTemp(activity, request) + sut.vaultAsync(activity, request) verify(exactly = 1) { payPalWebLauncher.launchWithUrl( activity = activity, @@ -143,7 +145,7 @@ class PayPalWebCheckoutClientUnitTest { } @Test - fun `vault() notifies merchant of browser switch failure`() = runTest { + fun `vaultAsync() notifies merchant of browser switch failure`() = runTest { val sdkError = PayPalSDKError(123, "fake error description") val launchResult = PayPalPresentAuthChallengeResult.Failure(sdkError) every { @@ -159,7 +161,7 @@ class PayPalWebCheckoutClientUnitTest { val appLinkUrl = "https://example.com/vault/return" val request = PayPalWebVaultRequest("fake-setup-token-id", appLinkUrl = appLinkUrl) - val result = sut.vaultTemp(activity, request) as PayPalPresentAuthChallengeResult.Failure + val result = sut.vaultAsync(activity, request) as PayPalPresentAuthChallengeResult.Failure assertSame(sdkError, result.error) } @@ -225,7 +227,7 @@ class PayPalWebCheckoutClientUnitTest { } returns successResult val request = PayPalWebCheckoutRequest("fake-order-id") - sut.startTemp(activity, request) + sut.startAsync(activity, request) val result = sut.finishStart(intent) assertSame(successResult, result) } @@ -261,7 +263,7 @@ class PayPalWebCheckoutClientUnitTest { patchCCOWithAppSwitchEligibility = patchCCOWithAppSwitchEligibility, ) val request = PayPalWebCheckoutRequest("fake-order-id") - launchWithUrlClient.startTemp(activity, request) + launchWithUrlClient.startAsync(activity, request) sut.restore(launchWithUrlClient.instanceState) val result = sut.finishStart(intent) @@ -289,7 +291,7 @@ class PayPalWebCheckoutClientUnitTest { } returns failureResult val request = PayPalWebCheckoutRequest("fake-order-id") - sut.startTemp(activity, request) + sut.startAsync(activity, request) val result = sut.finishStart(intent) assertSame(failureResult, result) } @@ -315,7 +317,7 @@ class PayPalWebCheckoutClientUnitTest { } returns failureResult val request = PayPalWebCheckoutRequest("fake-order-id") - sut.startTemp(activity, request) + sut.startAsync(activity, request) val result = sut.finishStart(intent) assertSame(failureResult, result) } @@ -340,7 +342,7 @@ class PayPalWebCheckoutClientUnitTest { } returns canceledResult val request = PayPalWebCheckoutRequest("fake-order-id") - sut.startTemp(activity, request) + sut.startAsync(activity, request) val result = sut.finishStart(intent) assertSame(canceledResult, result) } @@ -375,7 +377,7 @@ class PayPalWebCheckoutClientUnitTest { patchCCOWithAppSwitchEligibility = patchCCOWithAppSwitchEligibility ) val request = PayPalWebCheckoutRequest("fake-order-id") - launchWithUrlClient.startTemp(activity, request) + launchWithUrlClient.startAsync(activity, request) sut.restore(launchWithUrlClient.instanceState) val result = sut.finishStart(intent) @@ -402,7 +404,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeCheckoutAuthRequest(intent, "auth state") } returns successResult - sut.startTemp(activity, PayPalWebCheckoutRequest("fake-order-id")) + sut.startAsync(activity, PayPalWebCheckoutRequest("fake-order-id")) sut.finishStart(intent) assertNull(sut.finishStart(intent)) } @@ -427,7 +429,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeCheckoutAuthRequest(intent, "auth state") } returns failureResult - sut.startTemp(activity, PayPalWebCheckoutRequest("fake-order-id")) + sut.startAsync(activity, PayPalWebCheckoutRequest("fake-order-id")) sut.finishStart(intent) assertNull(sut.finishStart(intent)) } @@ -451,7 +453,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeCheckoutAuthRequest(intent, "auth state") } returns canceledResult - sut.startTemp(activity, PayPalWebCheckoutRequest("fake-order-id")) + sut.startAsync(activity, PayPalWebCheckoutRequest("fake-order-id")) sut.finishStart(intent) assertNull(sut.finishStart(intent)) } @@ -535,7 +537,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.startTemp(activity, request) + val result = sut.startAsync(activity, request) // Then coVerify { @@ -577,7 +579,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.startTemp(activity, request) + val result = sut.startAsync(activity, request) // Then verify { @@ -609,7 +611,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.startTemp(activity, request) + val result = sut.startAsync(activity, request) // Then verify { @@ -650,7 +652,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.startTemp(activity, request) + val result = sut.startAsync(activity, request) // Then coVerify { patchCCOWithAppSwitchEligibility(any(), any(), any(), any(), any()) } @@ -685,7 +687,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.startTemp(activity, request) + val result = sut.startAsync(activity, request) // Then coVerify(exactly = 0) { @@ -718,7 +720,7 @@ class PayPalWebCheckoutClientUnitTest { } returns PayPalPresentAuthChallengeResult.Success("state") // When - sut.startTemp(activity, request) + sut.startAsync(activity, request) // Then coVerify { @@ -769,7 +771,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.vaultTemp(activity, request) + val result = sut.vaultAsync(activity, request) // Then coVerify { @@ -811,7 +813,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.vaultTemp(activity, request) + val result = sut.vaultAsync(activity, request) // Then coVerify { @@ -852,7 +854,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.vaultTemp(activity, request) + val result = sut.vaultAsync(activity, request) // Then verify { @@ -889,7 +891,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.vaultTemp(activity, request) + val result = sut.vaultAsync(activity, request) // Then verify { @@ -920,7 +922,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.vaultTemp(activity, request) + val result = sut.vaultAsync(activity, request) // Then coVerify(exactly = 0) { @@ -953,7 +955,7 @@ class PayPalWebCheckoutClientUnitTest { } returns PayPalPresentAuthChallengeResult.Success("state") // When - sut.vaultTemp(activity, request) + sut.vaultAsync(activity, request) // Then coVerify { @@ -980,7 +982,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.startTemp(activity, request) + val result = sut.startAsync(activity, request) // Then coVerify(exactly = 0) { @@ -1010,7 +1012,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.vaultTemp(activity, request) + val result = sut.vaultAsync(activity, request) // Then coVerify(exactly = 0) { @@ -1040,7 +1042,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.startTemp(activity, request) + val result = sut.startAsync(activity, request) // Then coVerify(exactly = 0) { @@ -1070,7 +1072,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - val result = sut.vaultTemp(activity, request) + val result = sut.vaultAsync(activity, request) // Then coVerify(exactly = 0) { @@ -1123,7 +1125,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeVaultAuthRequest(intent, "auth state") } returns successResult - previousClient.vaultTemp(activity, PayPalWebVaultRequest("fake-setup-token-id")) + previousClient.vaultAsync(activity, PayPalWebVaultRequest("fake-setup-token-id")) sut.restore(previousClient.instanceState) val result = sut.finishVault(intent) as PayPalWebCheckoutFinishVaultResult.Success @@ -1161,7 +1163,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeVaultAuthRequest(intent, "auth state") } returns successResult - previousClient.vaultTemp(activity, PayPalWebVaultRequest("fake-setup-token-id")) + previousClient.vaultAsync(activity, PayPalWebVaultRequest("fake-setup-token-id")) sut.restore(previousClient.instanceState) val result = sut.finishVault(intent) as PayPalWebCheckoutFinishVaultResult.Success @@ -1187,7 +1189,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeVaultAuthRequest(intent, "auth state") } returns PayPalWebCheckoutFinishVaultResult.Failure(error) - sut.vaultTemp(activity, PayPalWebVaultRequest("fake-setup-token-id")) + sut.vaultAsync(activity, PayPalWebVaultRequest("fake-setup-token-id")) val result = sut.finishVault(intent) as PayPalWebCheckoutFinishVaultResult.Failure assertSame(error, result.error) } @@ -1211,7 +1213,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeVaultAuthRequest(intent, "auth state") } returns PayPalWebCheckoutFinishVaultResult.Failure(error) - sut.vaultTemp(activity, PayPalWebVaultRequest("fake-setup-token-id")) + sut.vaultAsync(activity, PayPalWebVaultRequest("fake-setup-token-id")) sut.restore(sut.instanceState) val result = sut.finishVault(intent) as PayPalWebCheckoutFinishVaultResult.Failure @@ -1236,7 +1238,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeVaultAuthRequest(intent, "auth state") } returns PayPalWebCheckoutFinishVaultResult.Canceled - sut.vaultTemp(activity, PayPalWebVaultRequest("fake-setup-token-id")) + sut.vaultAsync(activity, PayPalWebVaultRequest("fake-setup-token-id")) val result = sut.finishVault(intent) assertSame(PayPalWebCheckoutFinishVaultResult.Canceled, result) } @@ -1259,7 +1261,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeVaultAuthRequest(intent, "auth state") } returns PayPalWebCheckoutFinishVaultResult.Canceled - sut.vaultTemp(activity, PayPalWebVaultRequest("fake-setup-token-id")) + sut.vaultAsync(activity, PayPalWebVaultRequest("fake-setup-token-id")) sut.restore(sut.instanceState) val result = sut.finishVault(intent) @@ -1286,7 +1288,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeVaultAuthRequest(intent, "auth state") } returns successResult - sut.vaultTemp(activity, PayPalWebVaultRequest("fake-setup-token-id")) + sut.vaultAsync(activity, PayPalWebVaultRequest("fake-setup-token-id")) sut.finishVault(intent) assertNull(sut.finishVault(intent)) } @@ -1310,7 +1312,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeVaultAuthRequest(intent, "auth state") } returns PayPalWebCheckoutFinishVaultResult.Failure(error) - sut.vaultTemp(activity, PayPalWebVaultRequest("fake-setup-token-id")) + sut.vaultAsync(activity, PayPalWebVaultRequest("fake-setup-token-id")) sut.finishVault(intent) assertNull(sut.finishVault(intent)) } @@ -1333,7 +1335,7 @@ class PayPalWebCheckoutClientUnitTest { payPalWebLauncher.completeVaultAuthRequest(intent, "auth state") } returns PayPalWebCheckoutFinishVaultResult.Canceled - sut.vaultTemp(activity, PayPalWebVaultRequest("fake-setup-token-id")) + sut.vaultAsync(activity, PayPalWebVaultRequest("fake-setup-token-id")) sut.finishVault(intent) assertNull(sut.finishVault(intent)) } @@ -1368,7 +1370,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - clientWithUrlScheme.startTemp(activity, request) + clientWithUrlScheme.startAsync(activity, request) // Then verify { @@ -1413,7 +1415,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - clientWithUrlScheme.vaultTemp(activity, request) + clientWithUrlScheme.vaultAsync(activity, request) // Then verify { @@ -1460,7 +1462,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - clientWithUrlScheme.startTemp(activity, request) + clientWithUrlScheme.startAsync(activity, request) // Then verify { @@ -1507,7 +1509,7 @@ class PayPalWebCheckoutClientUnitTest { } returns launchResult // When - clientWithUrlScheme.vaultTemp(activity, request) + clientWithUrlScheme.vaultAsync(activity, request) // Then verify { @@ -1522,6 +1524,158 @@ class PayPalWebCheckoutClientUnitTest { } } + // MARK: - Tests for Deprecated Methods + + @Test + fun `deprecated start() calls analytics and launchWithUrl correctly`() { + val launchResult = PayPalPresentAuthChallengeResult.Success("auth state") + every { + payPalWebLauncher.launchWithUrl( + any(), + any(), + any(), + any(), + any(), + any() + ) + } returns launchResult + + val request = PayPalWebCheckoutRequest("fake-order-id", appLinkUrl = appLinkUrl) + val result = sut.start(activity, request) + + // Verify it returns the launch result + assertSame(launchResult, result) + + // Verify analytics was called with CheckoutEvent + verify { analytics.notify(any(), "fake-order-id") } + + // Verify launchWithUrl is called (the deprecated method calls it directly) + verify(exactly = 1) { + payPalWebLauncher.launchWithUrl( + activity = activity, + uri = any(), + token = "fake-order-id", + tokenType = TokenType.ORDER_ID, + returnUrlScheme = urlScheme, + appLinkUrl = appLinkUrl + ) + } + } + + @Test + fun `deprecated start() handles failure correctly`() { + val sdkError = PayPalSDKError(123, "fake error description") + val launchResult = PayPalPresentAuthChallengeResult.Failure(sdkError) + every { + payPalWebLauncher.launchWithUrl( + any(), + any(), + any(), + any(), + any(), + any() + ) + } returns launchResult + + val request = PayPalWebCheckoutRequest("fake-order-id", appLinkUrl = appLinkUrl) + val result = sut.start(activity, request) as PayPalPresentAuthChallengeResult.Failure + + assertSame(sdkError, result.error) + } + + @Test + fun `deprecated vault() calls analytics and launchWithUrl correctly`() { + val launchResult = PayPalPresentAuthChallengeResult.Success("auth state") + every { + payPalWebLauncher.launchWithUrl( + any(), + any(), + any(), + any(), + any(), + any() + ) + } returns launchResult + + val appLinkUrl = "https://example.com/vault/return" + val request = PayPalWebVaultRequest("fake-setup-token-id", appLinkUrl = appLinkUrl) + val result = sut.vault(activity, request) + + // Verify it returns the launch result + assertSame(launchResult, result) + + // Verify analytics was called with VaultEvent + verify { analytics.notify(any(), any()) } + + // Verify launchWithUrl is called (the deprecated method calls it directly) + verify(exactly = 1) { + payPalWebLauncher.launchWithUrl( + activity = activity, + uri = any(), + token = "fake-setup-token-id", + tokenType = TokenType.VAULT_ID, + returnUrlScheme = urlScheme, + appLinkUrl = appLinkUrl + ) + } + } + + @Test + fun `deprecated vault() handles failure correctly`() { + val sdkError = PayPalSDKError(123, "fake error description") + val launchResult = PayPalPresentAuthChallengeResult.Failure(sdkError) + every { + payPalWebLauncher.launchWithUrl( + any(), + any(), + any(), + any(), + any(), + any() + ) + } returns launchResult + + val appLinkUrl = "https://example.com/vault/return" + val request = PayPalWebVaultRequest("fake-setup-token-id", appLinkUrl = appLinkUrl) + val result = sut.vault(activity, request) as PayPalPresentAuthChallengeResult.Failure + + assertSame(sdkError, result.error) + } + + // MARK: - Tests for Callback-based Methods + // Note: Callback-based methods use applicationScope.launch which doesn't integrate well + // with test coroutine dispatchers, so these are tested implicitly through the methods they call + + @Test + fun `start() with callback method exists and can be called`() { + // This test just verifies the callback method can be called without throwing + val callback = mockk(relaxed = true) + val request = PayPalWebCheckoutRequest("fake-order-id", appLinkUrl = appLinkUrl) + + // Mock the async dependencies to avoid side effects + every { + payPalWebLauncher.launchWithUrl(any(), any(), any(), any(), any(), any()) + } returns PayPalPresentAuthChallengeResult.Success("auth state") + + // This should not throw an exception + sut.start(activity, request, callback) + } + + @Test + fun `vault() with callback method exists and can be called`() { + // This test just verifies the callback method can be called without throwing + val callback = mockk(relaxed = true) + val request = PayPalWebVaultRequest("fake-setup-token-id", appLinkUrl = appLinkUrl) + + // Mock the async dependencies to avoid side effects + every { + payPalWebLauncher.launchWithUrl(any(), any(), any(), any(), any(), any()) + } returns PayPalPresentAuthChallengeResult.Success("auth state") + + // This should not throw an exception + sut.vault(activity, request, callback) + } + fun createAppSwithEligibility(launchUrl: String?) = AppSwitchEligibilityData( appSwitchEligible = !launchUrl.isNullOrEmpty(), redirectURL = launchUrl, From 37d50be2a6fbfaa771a73b3839d6d74ac6ce0eea Mon Sep 17 00:00:00 2001 From: Karthik Gangineni Date: Thu, 6 Nov 2025 16:03:21 -0600 Subject: [PATCH 05/10] * update CHANGELOG.md * fix unit tests * update unit tests --- CHANGELOG.md | 4 +- CorePayments/api/CorePayments.api | 38 ------------------- PayPalWebPayments/api/PayPalWebPayments.api | 25 ++++++++---- .../PayPalWebCheckoutClientUnitTest.kt | 2 - 4 files changed, 19 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 204d2fcd6..5f482647f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,6 @@ ## Unreleased -* Add overloaded functions with callbacks for `start` and `vault` methods in - `PayPalWebCheckoutClient` * Adds new property `appSwitchWhenEligible`in `PayPalWebCheckoutRequest` to include to control app switch behavior * Adds new property `appSwitchWhenEligible`in `PayPalWebVaultRequest` to include to control app @@ -13,6 +11,8 @@ * Adds new property `appLinkUrl` in `PayPalWebVaultRequest` to specify app link url that will be used to recognize app after vaulting * Deprecates `urlScheme` property in `PayPalWebCheckoutClient` +* Adds new functions `startAsync` and `vaultAsync` in `PayPalWebCheckoutClient` to support Kotlin + coroutines ## 2.3.0 (2025-11-03) * PayPalWebPayments diff --git a/CorePayments/api/CorePayments.api b/CorePayments/api/CorePayments.api index 24a5e36a0..eb01eec6a 100644 --- a/CorePayments/api/CorePayments.api +++ b/CorePayments/api/CorePayments.api @@ -112,18 +112,6 @@ public final class com/paypal/android/corepayments/UpdateClientConfigAPI$Default public static final field USER_EXPERIENCE_FLOW Ljava/lang/String; } -public final class com/paypal/android/corepayments/UpdateClientConfigResponse { - public static final field Companion Lcom/paypal/android/corepayments/UpdateClientConfigResponse$Companion; - public fun (Ljava/lang/String;)V - public final fun component1 ()Ljava/lang/String; - public final fun copy (Ljava/lang/String;)Lcom/paypal/android/corepayments/UpdateClientConfigResponse; - public static synthetic fun copy$default (Lcom/paypal/android/corepayments/UpdateClientConfigResponse;Ljava/lang/String;ILjava/lang/Object;)Lcom/paypal/android/corepayments/UpdateClientConfigResponse; - public fun equals (Ljava/lang/Object;)Z - public final fun getUpdateClientConfig ()Ljava/lang/String; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - public final class com/paypal/android/corepayments/UpdateClientConfigResponse$$serializer : kotlinx/serialization/internal/GeneratedSerializer { public static final field INSTANCE Lcom/paypal/android/corepayments/UpdateClientConfigResponse$$serializer; public fun childSerializers ()[Lkotlinx/serialization/KSerializer; @@ -139,9 +127,6 @@ public final class com/paypal/android/corepayments/UpdateClientConfigResponse$Co public final fun serializer ()Lkotlinx/serialization/KSerializer; } -public abstract class com/paypal/android/corepayments/UpdateClientConfigResult { -} - public final class com/paypal/android/corepayments/UpdateClientConfigResult$Failure : com/paypal/android/corepayments/UpdateClientConfigResult { public fun (Lcom/paypal/android/corepayments/PayPalSDKError;)V public final fun component1 ()Lcom/paypal/android/corepayments/PayPalSDKError; @@ -160,29 +145,6 @@ public final class com/paypal/android/corepayments/UpdateClientConfigResult$Succ public fun toString ()Ljava/lang/String; } -public final class com/paypal/android/corepayments/UpdateClientConfigVariables { - public static final field Companion Lcom/paypal/android/corepayments/UpdateClientConfigVariables$Companion; - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Ljava/lang/String; - public final fun component2 ()Ljava/lang/String; - public final fun component3 ()Ljava/lang/String; - public final fun component4 ()Ljava/lang/String; - public final fun component5 ()Ljava/lang/String; - public final fun component6 ()Ljava/lang/String; - public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lcom/paypal/android/corepayments/UpdateClientConfigVariables; - public static synthetic fun copy$default (Lcom/paypal/android/corepayments/UpdateClientConfigVariables;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/paypal/android/corepayments/UpdateClientConfigVariables; - public fun equals (Ljava/lang/Object;)Z - public final fun getButtonSessionId ()Ljava/lang/String; - public final fun getFundingSource ()Ljava/lang/String; - public final fun getIntegrationArtifact ()Ljava/lang/String; - public final fun getProductFlow ()Ljava/lang/String; - public final fun getToken ()Ljava/lang/String; - public final fun getUserExperienceFlow ()Ljava/lang/String; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - public final class com/paypal/android/corepayments/UpdateClientConfigVariables$$serializer : kotlinx/serialization/internal/GeneratedSerializer { public static final field INSTANCE Lcom/paypal/android/corepayments/UpdateClientConfigVariables$$serializer; public fun childSerializers ()[Lkotlinx/serialization/KSerializer; diff --git a/PayPalWebPayments/api/PayPalWebPayments.api b/PayPalWebPayments/api/PayPalWebPayments.api index 64c038b87..651e74da8 100644 --- a/PayPalWebPayments/api/PayPalWebPayments.api +++ b/PayPalWebPayments/api/PayPalWebPayments.api @@ -35,6 +35,7 @@ public abstract interface class com/paypal/android/paypalwebpayments/PayPalWebCh } public final class com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient { + public fun (Landroid/content/Context;Lcom/paypal/android/corepayments/CoreConfig;)V public fun (Landroid/content/Context;Lcom/paypal/android/corepayments/CoreConfig;Ljava/lang/String;)V public final fun finishStart (Landroid/content/Intent;)Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFinishStartResult; public final fun finishStart (Landroid/content/Intent;Ljava/lang/String;)Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFinishStartResult; @@ -42,10 +43,12 @@ public final class com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient public final fun finishVault (Landroid/content/Intent;Ljava/lang/String;)Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFinishVaultResult; public final fun getInstanceState ()Ljava/lang/String; public final fun restore (Ljava/lang/String;)V + public final fun start (Landroid/app/Activity;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest;)Lcom/paypal/android/paypalwebpayments/PayPalPresentAuthChallengeResult; public final fun start (Landroid/app/Activity;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest;Lcom/paypal/android/paypalwebpayments/PayPalWebStartCallback;)V - public final fun start (Landroid/app/Activity;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun vault (Landroid/app/Activity;Lcom/paypal/android/paypalwebpayments/PayPalWebVaultRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun startAsync (Landroid/app/Activity;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun vault (Landroid/app/Activity;Lcom/paypal/android/paypalwebpayments/PayPalWebVaultRequest;)Lcom/paypal/android/paypalwebpayments/PayPalPresentAuthChallengeResult; public final fun vault (Landroidx/activity/ComponentActivity;Lcom/paypal/android/paypalwebpayments/PayPalWebVaultRequest;Lcom/paypal/android/paypalwebpayments/PayPalWebVaultCallback;)V + public final fun vaultAsync (Landroid/app/Activity;Lcom/paypal/android/paypalwebpayments/PayPalWebVaultRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public abstract class com/paypal/android/paypalwebpayments/PayPalWebCheckoutFinishStartResult { @@ -116,13 +119,16 @@ public final class com/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest public fun (Ljava/lang/String;)V public fun (Ljava/lang/String;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource;)V public fun (Ljava/lang/String;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource;Z)V - public synthetic fun (Ljava/lang/String;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource;ZLjava/lang/String;)V + public synthetic fun (Ljava/lang/String;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource;ZLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource; public final fun component3 ()Z - public final fun copy (Ljava/lang/String;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource;Z)Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest; - public static synthetic fun copy$default (Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest;Ljava/lang/String;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource;ZILjava/lang/Object;)Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest; + public final fun component4 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource;ZLjava/lang/String;)Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest; + public static synthetic fun copy$default (Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest;Ljava/lang/String;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource;ZLjava/lang/String;ILjava/lang/Object;)Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest; public fun equals (Ljava/lang/Object;)Z + public final fun getAppLinkUrl ()Ljava/lang/String; public final fun getAppSwitchWhenEligible ()Z public final fun getFundingSource ()Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource; public final fun getOrderId ()Ljava/lang/String; @@ -139,15 +145,18 @@ public abstract interface class com/paypal/android/paypalwebpayments/PayPalWebVa } public final class com/paypal/android/paypalwebpayments/PayPalWebVaultRequest { - public fun (Ljava/lang/String;)V public fun (Ljava/lang/String;ZLjava/lang/String;)V public synthetic fun (Ljava/lang/String;ZLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Z public final fun component3 ()Ljava/lang/String; - public final fun copy (Ljava/lang/String;ZLjava/lang/String;)Lcom/paypal/android/paypalwebpayments/PayPalWebVaultRequest; - public static synthetic fun copy$default (Lcom/paypal/android/paypalwebpayments/PayPalWebVaultRequest;Ljava/lang/String;ZLjava/lang/String;ILjava/lang/Object;)Lcom/paypal/android/paypalwebpayments/PayPalWebVaultRequest; + public final fun component4 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;)Lcom/paypal/android/paypalwebpayments/PayPalWebVaultRequest; + public static synthetic fun copy$default (Lcom/paypal/android/paypalwebpayments/PayPalWebVaultRequest;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/paypal/android/paypalwebpayments/PayPalWebVaultRequest; public fun equals (Ljava/lang/Object;)Z + public final fun getAppLinkUrl ()Ljava/lang/String; public final fun getAppSwitchWhenEligible ()Z public final fun getApproveVaultHref ()Ljava/lang/String; public final fun getSetupTokenId ()Ljava/lang/String; diff --git a/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClientUnitTest.kt b/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClientUnitTest.kt index 1ac332a91..0e4eeefc3 100644 --- a/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClientUnitTest.kt +++ b/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClientUnitTest.kt @@ -1556,7 +1556,6 @@ class PayPalWebCheckoutClientUnitTest { uri = any(), token = "fake-order-id", tokenType = TokenType.ORDER_ID, - returnUrlScheme = urlScheme, appLinkUrl = appLinkUrl ) } @@ -1614,7 +1613,6 @@ class PayPalWebCheckoutClientUnitTest { uri = any(), token = "fake-setup-token-id", tokenType = TokenType.VAULT_ID, - returnUrlScheme = urlScheme, appLinkUrl = appLinkUrl ) } From dd39b5bb51bf692e06424fc9f678d22affc997c4 Mon Sep 17 00:00:00 2001 From: Karthik Gangineni Date: Fri, 7 Nov 2025 10:36:59 -0600 Subject: [PATCH 06/10] * update CHANGELOG.md * remove unwanted logs * removed unnecessary code --- .../main/java/com/paypal/android/DemoConstants.kt | 12 ++++++------ .../src/main/java/com/paypal/android/MainActivity.kt | 7 ------- .../android/ui/approveorder/ApproveOrderViewModel.kt | 2 +- .../android/ui/vaultcard/VaultCardViewModel.kt | 4 ++-- .../paypalwebpayments/PayPalWebCheckoutClient.kt | 1 + 5 files changed, 10 insertions(+), 16 deletions(-) diff --git a/Demo/src/main/java/com/paypal/android/DemoConstants.kt b/Demo/src/main/java/com/paypal/android/DemoConstants.kt index 615e2c3c4..54c09775a 100644 --- a/Demo/src/main/java/com/paypal/android/DemoConstants.kt +++ b/Demo/src/main/java/com/paypal/android/DemoConstants.kt @@ -1,10 +1,10 @@ package com.paypal.android object DemoConstants { - const val APP_URL = "https://ppcp-mobile-demo-sandbox-87bbd7f0a27f.herokuapp.com/" - const val SUCCESS_URL = "${APP_URL}success" - const val CANCEL_URL = "${APP_URL}cancel" - const val VAULT_SUCCESS_URL = "${APP_URL}vault/success" - const val VAULT_CANCEL_URL = "${APP_URL}vault/cancel" - const val RETURN_URL = "${APP_URL}example.com/returnUrl" + const val APP_URL = "https://ppcp-mobile-demo-sandbox-87bbd7f0a27f.herokuapp.com" + const val SUCCESS_URL = "${APP_URL}/success" + const val CANCEL_URL = "${APP_URL}/cancel" + const val VAULT_SUCCESS_URL = "${APP_URL}/vault/success" + const val VAULT_CANCEL_URL = "${APP_URL}/vault/cancel" + } diff --git a/Demo/src/main/java/com/paypal/android/MainActivity.kt b/Demo/src/main/java/com/paypal/android/MainActivity.kt index 9dbf2bf32..92b10f46f 100644 --- a/Demo/src/main/java/com/paypal/android/MainActivity.kt +++ b/Demo/src/main/java/com/paypal/android/MainActivity.kt @@ -1,6 +1,5 @@ package com.paypal.android -import android.content.Intent import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent @@ -22,10 +21,4 @@ class MainActivity : ComponentActivity() { DemoApp() } } - - override fun onNewIntent(intent: Intent?) { - println("MainActivity: $intent") - println("MainActivity: ${intent?.data}") - super.onNewIntent(intent) - } } diff --git a/Demo/src/main/java/com/paypal/android/ui/approveorder/ApproveOrderViewModel.kt b/Demo/src/main/java/com/paypal/android/ui/approveorder/ApproveOrderViewModel.kt index 0279d8e3c..cfffef552 100644 --- a/Demo/src/main/java/com/paypal/android/ui/approveorder/ApproveOrderViewModel.kt +++ b/Demo/src/main/java/com/paypal/android/ui/approveorder/ApproveOrderViewModel.kt @@ -132,7 +132,7 @@ class ApproveOrderViewModel @Inject constructor( expirationYear = dateString.formattedYear, securityCode = cardSecurityCode ) - CardRequest(orderId, card, DemoConstants.RETURN_URL, scaOption) + CardRequest(orderId, card, DemoConstants.APP_URL, scaOption) } private var createOrderState diff --git a/Demo/src/main/java/com/paypal/android/ui/vaultcard/VaultCardViewModel.kt b/Demo/src/main/java/com/paypal/android/ui/vaultcard/VaultCardViewModel.kt index 71bd58264..f2623a8a0 100644 --- a/Demo/src/main/java/com/paypal/android/ui/vaultcard/VaultCardViewModel.kt +++ b/Demo/src/main/java/com/paypal/android/ui/vaultcard/VaultCardViewModel.kt @@ -5,7 +5,7 @@ import android.content.Intent import androidx.activity.ComponentActivity import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.paypal.android.DemoConstants.RETURN_URL +import com.paypal.android.DemoConstants.APP_URL import com.paypal.android.api.model.CardSetupToken import com.paypal.android.api.services.SDKSampleServerAPI import com.paypal.android.cardpayments.Card @@ -123,7 +123,7 @@ class VaultCardViewModel @Inject constructor( private fun updateSetupTokenWithId(activity: ComponentActivity, setupTokenId: String) { updateSetupTokenState = ActionState.Loading val card = parseCard(_uiState.value) - val cardVaultRequest = CardVaultRequest(setupTokenId, card, RETURN_URL) + val cardVaultRequest = CardVaultRequest(setupTokenId, card, APP_URL) cardClient.vault(cardVaultRequest) { result -> when (result) { is CardVaultResult.Success -> { diff --git a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt index 18d0afc40..93440ad4a 100644 --- a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt +++ b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt @@ -168,6 +168,7 @@ class PayPalWebCheckoutClient internal constructor( analytics.notify(CheckoutEvent.STARTED, checkoutOrderId) val launchUri = withContext(Dispatchers.IO) { + // perform updateCCO and getLaunchUri in parallel val updateConfigDeferred = async { updateClientConfigAPI.updateClientConfig( request.orderId, From 5688fe787ad83119afc05f1de0af87838665b947 Mon Sep 17 00:00:00 2001 From: sshropshire Date: Fri, 7 Nov 2025 12:58:01 -0600 Subject: [PATCH 07/10] Add support for fallback URLs in PayPalWebCheckoutClient.start() flows. --- .idea/misc.xml | 2 +- .../java/com/paypal/android/DemoConstants.kt | 1 + .../ui/paypalweb/PayPalCheckoutViewModel.kt | 24 ++++++++++++------- .../PayPalWebCheckoutClient.kt | 4 ++-- .../PayPalWebCheckoutRequest.kt | 3 ++- .../paypalwebpayments/PayPalWebLauncher.kt | 3 +-- 6 files changed, 22 insertions(+), 15 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index a67f64297..984af5941 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - + diff --git a/Demo/src/main/java/com/paypal/android/DemoConstants.kt b/Demo/src/main/java/com/paypal/android/DemoConstants.kt index 54c09775a..adef16cd5 100644 --- a/Demo/src/main/java/com/paypal/android/DemoConstants.kt +++ b/Demo/src/main/java/com/paypal/android/DemoConstants.kt @@ -2,6 +2,7 @@ package com.paypal.android object DemoConstants { const val APP_URL = "https://ppcp-mobile-demo-sandbox-87bbd7f0a27f.herokuapp.com" + const val APP_FALLBACK_URL_SCHEME = "com.paypal.android.demo" const val SUCCESS_URL = "${APP_URL}/success" const val CANCEL_URL = "${APP_URL}/cancel" const val VAULT_SUCCESS_URL = "${APP_URL}/vault/success" diff --git a/Demo/src/main/java/com/paypal/android/ui/paypalweb/PayPalCheckoutViewModel.kt b/Demo/src/main/java/com/paypal/android/ui/paypalweb/PayPalCheckoutViewModel.kt index 808c46551..41e15aeee 100644 --- a/Demo/src/main/java/com/paypal/android/ui/paypalweb/PayPalCheckoutViewModel.kt +++ b/Demo/src/main/java/com/paypal/android/ui/paypalweb/PayPalCheckoutViewModel.kt @@ -6,6 +6,7 @@ import android.util.Log import androidx.activity.ComponentActivity import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.paypal.android.DemoConstants.APP_FALLBACK_URL_SCHEME import com.paypal.android.DemoConstants.APP_URL import com.paypal.android.api.model.Order import com.paypal.android.api.model.OrderIntent @@ -116,18 +117,23 @@ class PayPalCheckoutViewModel @Inject constructor( private suspend fun startCheckoutWithOrderId(activity: ComponentActivity, orderId: String) { payPalWebCheckoutState = ActionState.Loading - val checkoutRequest = - PayPalWebCheckoutRequest(orderId, fundingSource, appSwitchWhenEligible, APP_URL) + val checkoutRequest = PayPalWebCheckoutRequest( + orderId, + fundingSource, + appSwitchWhenEligible, + APP_URL, + APP_FALLBACK_URL_SCHEME + ) when (val startResult = paypalClient.startAsync(activity, checkoutRequest)) { - is PayPalPresentAuthChallengeResult.Success -> { - // do nothing; wait for user to authenticate PayPal checkout in Chrome Custom Tab - } - - is PayPalPresentAuthChallengeResult.Failure -> - payPalWebCheckoutState = ActionState.Failure(startResult.error) - } + is PayPalPresentAuthChallengeResult.Success -> { + // do nothing; wait for user to authenticate PayPal checkout in Chrome Custom Tab } + is PayPalPresentAuthChallengeResult.Failure -> + payPalWebCheckoutState = ActionState.Failure(startResult.error) + } + } + fun completeOrder(context: Context) { val orderId = createdOrder?.id if (orderId == null) { diff --git a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt index 93440ad4a..40bd4b8ea 100644 --- a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt +++ b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt @@ -129,7 +129,7 @@ class PayPalWebCheckoutClient internal constructor( uri = launchUri, token = request.orderId, tokenType = TokenType.ORDER_ID, - returnUrlScheme = urlScheme, + returnUrlScheme = request.fallbackUrlScheme ?: urlScheme, appLinkUrl = request.appLinkUrl ) @@ -198,7 +198,7 @@ class PayPalWebCheckoutClient internal constructor( uri = launchUri, token = request.orderId, tokenType = TokenType.ORDER_ID, - returnUrlScheme = urlScheme, + returnUrlScheme = request.fallbackUrlScheme ?: urlScheme, appLinkUrl = request.appLinkUrl ) diff --git a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest.kt b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest.kt index 1460be988..d123ca3e8 100644 --- a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest.kt +++ b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest.kt @@ -12,5 +12,6 @@ data class PayPalWebCheckoutRequest @JvmOverloads constructor( val orderId: String, val fundingSource: PayPalWebCheckoutFundingSource = PayPalWebCheckoutFundingSource.PAYPAL, val appSwitchWhenEligible: Boolean = false, - val appLinkUrl: String? = null + val appLinkUrl: String? = null, + val fallbackUrlScheme: String? = null ) diff --git a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebLauncher.kt b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebLauncher.kt index 8b6001f25..9862e989e 100644 --- a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebLauncher.kt +++ b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebLauncher.kt @@ -34,12 +34,11 @@ internal class PayPalWebLauncher( returnUrlScheme: String? = null, appLinkUrl: String? = null ): PayPalPresentAuthChallengeResult { - val urlScheme = if (appLinkUrl != null) null else returnUrlScheme val metadata = getMetadata(token, tokenType) val options = BrowserSwitchOptions() .url(uri) .requestCode(getRequestCode(tokenType)) - .returnUrlScheme(urlScheme) + .returnUrlScheme(returnUrlScheme) .appLinkUri(appLinkUrl?.toUri()) .metadata(metadata) return launchBrowserSwitch(activity, options) From e44ea0444625e236f3ce1009c76fa494b8620c03 Mon Sep 17 00:00:00 2001 From: Karthik Gangineni Date: Fri, 7 Nov 2025 15:59:11 -0600 Subject: [PATCH 08/10] * add unit tests for testing fallbackUrlScheme * add fallbackUrlScheme to PayPalWebVaultRequest --- .../PayPalWebCheckoutClient.kt | 4 +- .../PayPalWebCheckoutRequest.kt | 3 +- .../PayPalWebVaultRequest.kt | 7 +- .../PayPalWebCheckoutClientUnitTest.kt | 220 ++++++++++++++++++ .../PayPalWebLauncherUnitTest.kt | 6 +- 5 files changed, 232 insertions(+), 8 deletions(-) diff --git a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt index 40bd4b8ea..daa11fa7f 100644 --- a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt +++ b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt @@ -267,7 +267,7 @@ class PayPalWebCheckoutClient internal constructor( uri = launchUri, token = request.setupTokenId, tokenType = TokenType.VAULT_ID, - returnUrlScheme = urlScheme, + returnUrlScheme = request.fallbackUrlScheme ?: urlScheme, appLinkUrl = request.appLinkUrl ) @@ -320,7 +320,7 @@ class PayPalWebCheckoutClient internal constructor( uri = launchUri, token = request.setupTokenId, tokenType = TokenType.VAULT_ID, - returnUrlScheme = urlScheme, + returnUrlScheme = request.fallbackUrlScheme ?: urlScheme, appLinkUrl = request.appLinkUrl ) diff --git a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest.kt b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest.kt index d123ca3e8..dfb6cb5cb 100644 --- a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest.kt +++ b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest.kt @@ -6,7 +6,8 @@ package com.paypal.android.paypalwebpayments * @param orderId The ID of the order to be approved. * @param fundingSource specify funding (credit, paylater or default) * @param appSwitchWhenEligible whether to switch to the PayPal app when eligible - * @param appLinkUrl The app link URL to use for browser switch, or null to use default. + * @param appLinkUrl The app link URL to use for browser switch, or null to use default. Example values: "$myAppScheme://$myAppHost/$myPath", "https://$myDomain/$myPath" + * @param fallbackUrlScheme The fallback custom URL scheme to use when app link is not configured properly. */ data class PayPalWebCheckoutRequest @JvmOverloads constructor( val orderId: String, diff --git a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt index b2f258969..75085d593 100644 --- a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt +++ b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt @@ -7,12 +7,14 @@ package com.paypal.android.paypalwebpayments * @property [approveVaultHref] URL for the approval web page * @property [appSwitchWhenEligible] whether to switch to the PayPal app when eligible * @property [appLinkUrl] The app link URL to use for returning to app + * @property [fallbackUrlScheme] The fallback custom URL scheme to use when app link is not configured properly */ data class PayPalWebVaultRequest @Deprecated("Use PayPalWebVaultRequest(setupTokenId) instead.") constructor( val setupTokenId: String, val appSwitchWhenEligible: Boolean = false, val appLinkUrl: String? = null, + val fallbackUrlScheme: String? = null, @Deprecated("The approveVaultHref property is no longer required and will be ignored.") val approveVaultHref: String? = null // NEXT_MAJOR_VERSION: - Remove this property ) { @@ -25,6 +27,7 @@ constructor( constructor( setupTokenId: String, appSwitchWhenEligible: Boolean = false, - appLinkUrl: String? = null - ) : this(setupTokenId, appSwitchWhenEligible, appLinkUrl, null) + appLinkUrl: String? = null, + fallbackUrlScheme: String? = null + ) : this(setupTokenId, appSwitchWhenEligible, appLinkUrl, fallbackUrlScheme, null) } diff --git a/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClientUnitTest.kt b/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClientUnitTest.kt index 0e4eeefc3..f5bf0aefa 100644 --- a/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClientUnitTest.kt +++ b/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClientUnitTest.kt @@ -1674,6 +1674,226 @@ class PayPalWebCheckoutClientUnitTest { sut.vault(activity, request, callback) } + // Tests for fallbackUrlScheme functionality + + @Test + fun `startAsync() uses fallbackUrlScheme when provided`() = runTest { + val fallbackScheme = "com.example.fallback" + val launchResult = PayPalPresentAuthChallengeResult.Success("auth state") + every { + payPalWebLauncher.launchWithUrl(any(), any(), any(), any(), any(), any()) + } returns launchResult + + val request = PayPalWebCheckoutRequest( + orderId = "fake-order-id", + fallbackUrlScheme = fallbackScheme + ) + sut.startAsync(activity, request) + + verify(exactly = 1) { + payPalWebLauncher.launchWithUrl( + activity = activity, + uri = any(), + token = "fake-order-id", + tokenType = TokenType.ORDER_ID, + returnUrlScheme = fallbackScheme, + appLinkUrl = null + ) + } + } + + @Test + fun `startAsync() uses default urlScheme when fallbackUrlScheme is null`() = runTest { + val launchResult = PayPalPresentAuthChallengeResult.Success("auth state") + every { + payPalWebLauncher.launchWithUrl(any(), any(), any(), any(), any(), any()) + } returns launchResult + + val request = PayPalWebCheckoutRequest( + orderId = "fake-order-id", + fallbackUrlScheme = null + ) + sut.startAsync(activity, request) + + verify(exactly = 1) { + payPalWebLauncher.launchWithUrl( + activity = activity, + uri = any(), + token = "fake-order-id", + tokenType = TokenType.ORDER_ID, + returnUrlScheme = null, + appLinkUrl = null + ) + } + } + + @Test + @Suppress("DEPRECATION") + fun `start() with deprecated method uses fallbackUrlScheme when provided`() { + val fallbackScheme = "com.example.fallback" + val launchResult = PayPalPresentAuthChallengeResult.Success("auth state") + every { + payPalWebLauncher.launchWithUrl(any(), any(), any(), any(), any(), any()) + } returns launchResult + + val request = PayPalWebCheckoutRequest( + orderId = "fake-order-id", + fallbackUrlScheme = fallbackScheme + ) + sut.start(activity, request) + + verify(exactly = 1) { + payPalWebLauncher.launchWithUrl( + activity = activity, + uri = any(), + token = "fake-order-id", + tokenType = TokenType.ORDER_ID, + returnUrlScheme = fallbackScheme, + appLinkUrl = null + ) + } + } + + @Test + fun `start() with callback uses fallbackUrlScheme when provided`() { + val fallbackScheme = "com.example.fallback" + val callback = mockk(relaxed = true) + val launchResult = PayPalPresentAuthChallengeResult.Success("auth state") + every { + payPalWebLauncher.launchWithUrl(any(), any(), any(), any(), any(), any()) + } returns launchResult + + val request = PayPalWebCheckoutRequest( + orderId = "fake-order-id", + fallbackUrlScheme = fallbackScheme + ) + sut.start(activity, request, callback) + + verify(exactly = 1) { + payPalWebLauncher.launchWithUrl( + activity = activity, + uri = any(), + token = "fake-order-id", + tokenType = TokenType.ORDER_ID, + returnUrlScheme = fallbackScheme, + appLinkUrl = null + ) + } + } + + @Test + fun `vaultAsync() uses fallbackUrlScheme when provided`() = runTest { + val fallbackScheme = "com.example.fallback" + val launchResult = PayPalPresentAuthChallengeResult.Success("auth state") + every { + payPalWebLauncher.launchWithUrl(any(), any(), any(), any(), any(), any()) + } returns launchResult + + val request = PayPalWebVaultRequest( + setupTokenId = "fake-setup-token-id", + fallbackUrlScheme = fallbackScheme + ) + sut.vaultAsync(activity, request) + + verify(exactly = 1) { + payPalWebLauncher.launchWithUrl( + activity = activity, + uri = any(), + token = "fake-setup-token-id", + tokenType = TokenType.VAULT_ID, + returnUrlScheme = fallbackScheme, + appLinkUrl = null + ) + } + } + + @Test + fun `vaultAsync() uses default urlScheme when fallbackUrlScheme is null`() = runTest { + val launchResult = PayPalPresentAuthChallengeResult.Success("auth state") + every { + payPalWebLauncher.launchWithUrl(any(), any(), any(), any(), any(), any()) + } returns launchResult + + val request = PayPalWebVaultRequest( + setupTokenId = "fake-setup-token-id", + fallbackUrlScheme = null + ) + sut.vaultAsync(activity, request) + + verify(exactly = 1) { + payPalWebLauncher.launchWithUrl( + activity = activity, + uri = any(), + token = "fake-setup-token-id", + tokenType = TokenType.VAULT_ID, + returnUrlScheme = null, + appLinkUrl = null + ) + } + } + + @Test + @Suppress("DEPRECATION") + fun `vault() with deprecated method uses fallbackUrlScheme when provided`() { + val fallbackScheme = "com.example.fallback" + val launchResult = PayPalPresentAuthChallengeResult.Success("auth state") + every { + payPalWebLauncher.launchWithUrl(any(), any(), any(), any(), any(), any()) + } returns launchResult + + val request = PayPalWebVaultRequest( + setupTokenId = "fake-setup-token-id", + fallbackUrlScheme = fallbackScheme + ) + sut.vault(activity, request) + + verify(exactly = 1) { + payPalWebLauncher.launchWithUrl( + activity = activity, + uri = any(), + token = "fake-setup-token-id", + tokenType = TokenType.VAULT_ID, + returnUrlScheme = fallbackScheme, + appLinkUrl = null + ) + } + } + + @Test + fun `vault() with callback uses fallbackUrlScheme when provided`() { + // This test verifies the callback method can be called with fallbackUrlScheme without throwing + val fallbackScheme = "com.example.fallback" + val callback = mockk(relaxed = true) + every { + payPalWebLauncher.launchWithUrl(any(), any(), any(), any(), any(), any()) + } returns PayPalPresentAuthChallengeResult.Success("auth state") + + val request = PayPalWebVaultRequest( + setupTokenId = "fake-setup-token-id", + fallbackUrlScheme = fallbackScheme + ) + + // This should not throw an exception + sut.vault(activity, request, callback) + } + + @Test + fun `vault() with callback uses default urlScheme when fallbackUrlScheme is null`() { + // This test verifies the callback method can be called with null fallbackUrlScheme without throwing + val callback = mockk(relaxed = true) + every { + payPalWebLauncher.launchWithUrl(any(), any(), any(), any(), any(), any()) + } returns PayPalPresentAuthChallengeResult.Success("auth state") + + val request = PayPalWebVaultRequest( + setupTokenId = "fake-setup-token-id", + fallbackUrlScheme = null + ) + + // This should not throw an exception + sut.vault(activity, request, callback) + } + fun createAppSwithEligibility(launchUrl: String?) = AppSwitchEligibilityData( appSwitchEligible = !launchUrl.isNullOrEmpty(), redirectURL = launchUrl, diff --git a/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebLauncherUnitTest.kt b/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebLauncherUnitTest.kt index 7f3f7b488..7226e1a5c 100644 --- a/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebLauncherUnitTest.kt +++ b/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebLauncherUnitTest.kt @@ -505,7 +505,7 @@ class PayPalWebLauncherUnitTest { } @Test - fun `launchWithUrl() with appLinkUrl sets appLinkUri and nullifies returnUrlScheme`() { + fun `launchWithUrl() with appLinkUrl sets appLinkUri and returnUrlScheme`() { sut = PayPalWebLauncher(browserSwitchClient) val slot = slot() @@ -526,7 +526,7 @@ class PayPalWebLauncherUnitTest { val browserSwitchOptions = slot.captured expectThat(browserSwitchOptions) { get { metadata?.get("order_id") }.isEqualTo("order-123") - get { returnUrlScheme }.isEqualTo(null) // Should be null when appLinkUrl is provided + get { returnUrlScheme }.isEqualTo("custom_url_scheme") // Should use provided returnUrlScheme as fallback get { url }.isEqualTo(Uri.parse("https://paypal.com/checkout")) get { requestCode }.isEqualTo(PAYPAL_CHECKOUT) get { appLinkUri }.isEqualTo(appLinkUrl.toUri()) @@ -583,7 +583,7 @@ class PayPalWebLauncherUnitTest { val browserSwitchOptions = slot.captured expectThat(browserSwitchOptions) { get { metadata?.get("setup_token_id") }.isEqualTo("setup-456") - get { returnUrlScheme }.isEqualTo(null) // Should be null when appLinkUrl is provided + get { returnUrlScheme }.isEqualTo("custom_url_scheme") // Should use provided returnUrlScheme as fallback get { url }.isEqualTo(Uri.parse("https://paypal.com/vault")) get { requestCode }.isEqualTo(PAYPAL_VAULT) get { appLinkUri }.isEqualTo(appLinkUrl.toUri()) From 79467472fe69b034f41d6ee3fa2153d2537e451b Mon Sep 17 00:00:00 2001 From: Karthik Gangineni Date: Fri, 7 Nov 2025 16:07:57 -0600 Subject: [PATCH 09/10] * fix static analysis * update public API --- .../java/com/paypal/android/DemoConstants.kt | 9 ++++----- PayPalWebPayments/api/PayPalWebPayments.api | 19 ++++++++++++------- .../PayPalWebCheckoutRequest.kt | 3 ++- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/Demo/src/main/java/com/paypal/android/DemoConstants.kt b/Demo/src/main/java/com/paypal/android/DemoConstants.kt index adef16cd5..2b405cbf0 100644 --- a/Demo/src/main/java/com/paypal/android/DemoConstants.kt +++ b/Demo/src/main/java/com/paypal/android/DemoConstants.kt @@ -3,9 +3,8 @@ package com.paypal.android object DemoConstants { const val APP_URL = "https://ppcp-mobile-demo-sandbox-87bbd7f0a27f.herokuapp.com" const val APP_FALLBACK_URL_SCHEME = "com.paypal.android.demo" - const val SUCCESS_URL = "${APP_URL}/success" - const val CANCEL_URL = "${APP_URL}/cancel" - const val VAULT_SUCCESS_URL = "${APP_URL}/vault/success" - const val VAULT_CANCEL_URL = "${APP_URL}/vault/cancel" - + const val SUCCESS_URL = "$APP_URL/success" + const val CANCEL_URL = "$APP_URL/cancel" + const val VAULT_SUCCESS_URL = "$APP_URL/vault/success" + const val VAULT_CANCEL_URL = "$APP_URL/vault/cancel" } diff --git a/PayPalWebPayments/api/PayPalWebPayments.api b/PayPalWebPayments/api/PayPalWebPayments.api index 651e74da8..9e5dc3ec4 100644 --- a/PayPalWebPayments/api/PayPalWebPayments.api +++ b/PayPalWebPayments/api/PayPalWebPayments.api @@ -120,16 +120,19 @@ public final class com/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest public fun (Ljava/lang/String;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource;)V public fun (Ljava/lang/String;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource;Z)V public fun (Ljava/lang/String;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource;ZLjava/lang/String;)V - public synthetic fun (Ljava/lang/String;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource;ZLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource;ZLjava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource;ZLjava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource; public final fun component3 ()Z public final fun component4 ()Ljava/lang/String; - public final fun copy (Ljava/lang/String;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource;ZLjava/lang/String;)Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest; - public static synthetic fun copy$default (Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest;Ljava/lang/String;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource;ZLjava/lang/String;ILjava/lang/Object;)Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest; + public final fun component5 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource;ZLjava/lang/String;Ljava/lang/String;)Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest; + public static synthetic fun copy$default (Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest;Ljava/lang/String;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource;ZLjava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest; public fun equals (Ljava/lang/Object;)Z public final fun getAppLinkUrl ()Ljava/lang/String; public final fun getAppSwitchWhenEligible ()Z + public final fun getFallbackUrlScheme ()Ljava/lang/String; public final fun getFundingSource ()Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource; public final fun getOrderId ()Ljava/lang/String; public fun hashCode ()I @@ -145,20 +148,22 @@ public abstract interface class com/paypal/android/paypalwebpayments/PayPalWebVa } public final class com/paypal/android/paypalwebpayments/PayPalWebVaultRequest { - public fun (Ljava/lang/String;ZLjava/lang/String;)V - public synthetic fun (Ljava/lang/String;ZLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun (Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;)V public synthetic fun (Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Z public final fun component3 ()Ljava/lang/String; public final fun component4 ()Ljava/lang/String; - public final fun copy (Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;)Lcom/paypal/android/paypalwebpayments/PayPalWebVaultRequest; - public static synthetic fun copy$default (Lcom/paypal/android/paypalwebpayments/PayPalWebVaultRequest;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/paypal/android/paypalwebpayments/PayPalWebVaultRequest; + public final fun component5 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lcom/paypal/android/paypalwebpayments/PayPalWebVaultRequest; + public static synthetic fun copy$default (Lcom/paypal/android/paypalwebpayments/PayPalWebVaultRequest;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/paypal/android/paypalwebpayments/PayPalWebVaultRequest; public fun equals (Ljava/lang/Object;)Z public final fun getAppLinkUrl ()Ljava/lang/String; public final fun getAppSwitchWhenEligible ()Z public final fun getApproveVaultHref ()Ljava/lang/String; + public final fun getFallbackUrlScheme ()Ljava/lang/String; public final fun getSetupTokenId ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; diff --git a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest.kt b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest.kt index dfb6cb5cb..fceb22622 100644 --- a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest.kt +++ b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest.kt @@ -6,7 +6,8 @@ package com.paypal.android.paypalwebpayments * @param orderId The ID of the order to be approved. * @param fundingSource specify funding (credit, paylater or default) * @param appSwitchWhenEligible whether to switch to the PayPal app when eligible - * @param appLinkUrl The app link URL to use for browser switch, or null to use default. Example values: "$myAppScheme://$myAppHost/$myPath", "https://$myDomain/$myPath" + * @param appLinkUrl The app link URL to use for browser switch, or null to use default. + * Example values: "$myAppScheme://$myAppHost/$myPath", "https://$myDomain/$myPath" * @param fallbackUrlScheme The fallback custom URL scheme to use when app link is not configured properly. */ data class PayPalWebCheckoutRequest @JvmOverloads constructor( From acc3b5e3a9196c8b9e93d8c0ac50ab82caf41eb0 Mon Sep 17 00:00:00 2001 From: Karthik Gangineni Date: Mon, 10 Nov 2025 11:10:23 -0600 Subject: [PATCH 10/10] * make startAsync, vaultAsync functions internal * use appUrl and fallback app url scheme in demo app --- .../ui/paypalweb/PayPalCheckoutViewModel.kt | 21 +++++++------- .../ui/paypalwebvault/PayPalVaultViewModel.kt | 13 ++++----- PayPalWebPayments/api/PayPalWebPayments.api | 2 -- .../PayPalWebCheckoutClient.kt | 29 ++++++++++--------- .../PayPalWebVaultRequest.kt | 4 +-- 5 files changed, 34 insertions(+), 35 deletions(-) diff --git a/Demo/src/main/java/com/paypal/android/ui/paypalweb/PayPalCheckoutViewModel.kt b/Demo/src/main/java/com/paypal/android/ui/paypalweb/PayPalCheckoutViewModel.kt index 41e15aeee..1fa58e1ad 100644 --- a/Demo/src/main/java/com/paypal/android/ui/paypalweb/PayPalCheckoutViewModel.kt +++ b/Demo/src/main/java/com/paypal/android/ui/paypalweb/PayPalCheckoutViewModel.kt @@ -108,13 +108,11 @@ class PayPalCheckoutViewModel @Inject constructor( if (orderId == null) { payPalWebCheckoutState = ActionState.Failure(Exception("Create an order to continue.")) } else { - viewModelScope.launch { - startCheckoutWithOrderId(activity, orderId) - } + startCheckoutWithOrderId(activity, orderId) } } - private suspend fun startCheckoutWithOrderId(activity: ComponentActivity, orderId: String) { + private fun startCheckoutWithOrderId(activity: ComponentActivity, orderId: String) { payPalWebCheckoutState = ActionState.Loading val checkoutRequest = PayPalWebCheckoutRequest( @@ -124,13 +122,16 @@ class PayPalCheckoutViewModel @Inject constructor( APP_URL, APP_FALLBACK_URL_SCHEME ) - when (val startResult = paypalClient.startAsync(activity, checkoutRequest)) { - is PayPalPresentAuthChallengeResult.Success -> { - // do nothing; wait for user to authenticate PayPal checkout in Chrome Custom Tab - } - is PayPalPresentAuthChallengeResult.Failure -> - payPalWebCheckoutState = ActionState.Failure(startResult.error) + paypalClient.start(activity, checkoutRequest) { startResult -> + when (startResult) { + is PayPalPresentAuthChallengeResult.Success -> { + // do nothing; wait for user to authenticate PayPal checkout in Chrome Custom Tab + } + + is PayPalPresentAuthChallengeResult.Failure -> + payPalWebCheckoutState = ActionState.Failure(startResult.error) + } } } diff --git a/Demo/src/main/java/com/paypal/android/ui/paypalwebvault/PayPalVaultViewModel.kt b/Demo/src/main/java/com/paypal/android/ui/paypalwebvault/PayPalVaultViewModel.kt index 34bc6fdde..7f28a8a05 100644 --- a/Demo/src/main/java/com/paypal/android/ui/paypalwebvault/PayPalVaultViewModel.kt +++ b/Demo/src/main/java/com/paypal/android/ui/paypalwebvault/PayPalVaultViewModel.kt @@ -5,6 +5,7 @@ import android.content.Intent import androidx.activity.ComponentActivity import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.paypal.android.DemoConstants.APP_FALLBACK_URL_SCHEME import com.paypal.android.DemoConstants.APP_URL import com.paypal.android.api.model.PayPalSetupToken import com.paypal.android.api.services.SDKSampleServerAPI @@ -30,11 +31,6 @@ class PayPalVaultViewModel @Inject constructor( val createPayPalSetupTokenUseCase: CreatePayPalSetupTokenUseCase, val createPayPalPaymentTokenUseCase: CreatePayPalPaymentTokenUseCase, ) : ViewModel() { - - companion object { - const val URL_SCHEME = "com.paypal.android.demo" - } - private val coreConfig = CoreConfig(SDKSampleServerAPI.clientId) private val paypalClient = PayPalWebCheckoutClient(applicationContext, coreConfig) @@ -85,7 +81,8 @@ class PayPalVaultViewModel @Inject constructor( val request = PayPalWebVaultRequest( setupTokenId, appSwitchWhenEligible, - APP_URL + APP_URL, + APP_FALLBACK_URL_SCHEME ) vaultSetupTokenWithRequest(activity, request) } @@ -98,8 +95,8 @@ class PayPalVaultViewModel @Inject constructor( ) { vaultPayPalState = ActionState.Loading - viewModelScope.launch { - when (val result = paypalClient.vaultAsync(activity, request)) { + paypalClient.vault(activity, request) { result -> + when (result) { is PayPalPresentAuthChallengeResult.Success -> { // do nothing; wait for user to authenticate PayPal vault in Chrome Custom Tab } diff --git a/PayPalWebPayments/api/PayPalWebPayments.api b/PayPalWebPayments/api/PayPalWebPayments.api index 9e5dc3ec4..0cd4160e0 100644 --- a/PayPalWebPayments/api/PayPalWebPayments.api +++ b/PayPalWebPayments/api/PayPalWebPayments.api @@ -45,10 +45,8 @@ public final class com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient public final fun restore (Ljava/lang/String;)V public final fun start (Landroid/app/Activity;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest;)Lcom/paypal/android/paypalwebpayments/PayPalPresentAuthChallengeResult; public final fun start (Landroid/app/Activity;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest;Lcom/paypal/android/paypalwebpayments/PayPalWebStartCallback;)V - public final fun startAsync (Landroid/app/Activity;Lcom/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public final fun vault (Landroid/app/Activity;Lcom/paypal/android/paypalwebpayments/PayPalWebVaultRequest;)Lcom/paypal/android/paypalwebpayments/PayPalPresentAuthChallengeResult; public final fun vault (Landroidx/activity/ComponentActivity;Lcom/paypal/android/paypalwebpayments/PayPalWebVaultRequest;Lcom/paypal/android/paypalwebpayments/PayPalWebVaultCallback;)V - public final fun vaultAsync (Landroid/app/Activity;Lcom/paypal/android/paypalwebpayments/PayPalWebVaultRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public abstract class com/paypal/android/paypalwebpayments/PayPalWebCheckoutFinishStartResult { diff --git a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt index daa11fa7f..d3a7a1da0 100644 --- a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt +++ b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt @@ -5,6 +5,7 @@ import android.content.Context import android.content.Intent import android.net.Uri import androidx.activity.ComponentActivity +import androidx.annotation.VisibleForTesting import androidx.core.net.toUri import com.paypal.android.corepayments.CoreConfig import com.paypal.android.corepayments.Environment @@ -108,8 +109,8 @@ class PayPalWebCheckoutClient internal constructor( * @param request [PayPalWebCheckoutRequest] for requesting an order approval */ @Deprecated( - message = "Use startAsync(activity, request) or start(activity, request, callback) instead.", - replaceWith = ReplaceWith("startAsync(activity, request)") + message = "Use start(activity, request, callback) for callback-based flows, includes app switching feature", + replaceWith = ReplaceWith("start(activity, request, callback)") ) fun start( activity: Activity, @@ -159,7 +160,8 @@ class PayPalWebCheckoutClient internal constructor( * * @param request [PayPalWebCheckoutRequest] for requesting an order approval */ - suspend fun startAsync( + @VisibleForTesting + internal suspend fun startAsync( activity: Activity, request: PayPalWebCheckoutRequest ): PayPalPresentAuthChallengeResult { @@ -250,8 +252,8 @@ class PayPalWebCheckoutClient internal constructor( * @param request [PayPalWebVaultRequest] for vaulting PayPal as a payment method */ @Deprecated( - message = "Use vaultAsync(activity, request) or vault(activity, request, callback) instead.", - replaceWith = ReplaceWith("vaultAsync(activity, request)") + message = "Use vault(activity, request, callback) for callback-based flows, includes app switching feature", + replaceWith = ReplaceWith("vault(activity, request, callback)") ) fun vault( activity: Activity, @@ -298,7 +300,8 @@ class PayPalWebCheckoutClient internal constructor( * * @param request [PayPalWebVaultRequest] for vaulting PayPal as a payment method */ - suspend fun vaultAsync( + @VisibleForTesting + internal suspend fun vaultAsync( activity: Activity, request: PayPalWebVaultRequest ): PayPalPresentAuthChallengeResult { @@ -359,20 +362,20 @@ class PayPalWebCheckoutClient internal constructor( request: PayPalWebVaultRequest, callback: PayPalWebVaultCallback ) { - CoroutineScope(Dispatchers.Main).launch { + applicationScope.launch(Dispatchers.Main) { callback.onPayPalWebVaultResult(vaultAsync(activity, request)) } } /** * After a merchant app has re-entered the foreground following an auth challenge - * (@see [PayPalWebCheckoutClient.startAsync]), call this method to see if a user has + * (@see [PayPalWebCheckoutClient.start]), call this method to see if a user has * successfully authorized a PayPal account as a payment source. * * @param [intent] An Android intent that holds the deep link put the merchant app * back into the foreground after an auth challenge. * @param [authState] A continuation state received from [PayPalPresentAuthChallengeResult.Success] - * when calling [PayPalWebCheckoutClient.startAsync]. This is needed to properly verify that an + * when calling [PayPalWebCheckoutClient.start]. This is needed to properly verify that an * authorization completed successfully. */ @Deprecated( @@ -400,7 +403,7 @@ class PayPalWebCheckoutClient internal constructor( /** * After a merchant app has re-entered the foreground following an auth challenge - * (@see [PayPalWebCheckoutClient.startAsync]), call this method to see if a user has + * (@see [PayPalWebCheckoutClient.start]), call this method to see if a user has * successfully authorized a PayPal account as a payment source. * * @param [intent] An Android intent that holds the deep link put the merchant app @@ -434,13 +437,13 @@ class PayPalWebCheckoutClient internal constructor( /** * After a merchant app has re-entered the foreground following an auth challenge - * (@see [PayPalWebCheckoutClient.vaultAsync]), call this method to see if a user has + * (@see [PayPalWebCheckoutClient.vault]), call this method to see if a user has * successfully authorized a PayPal account for vaulting. * * @param [intent] An Android intent that holds the deep link put the merchant app * back into the foreground after an auth challenge. * @param [authState] A continuation state received from [PayPalPresentAuthChallengeResult.Success] - * when calling [PayPalWebCheckoutClient.vaultAsync]. This is needed to properly verify that an + * when calling [PayPalWebCheckoutClient.vault]. This is needed to properly verify that an * authorization completed successfully. */ @Deprecated( @@ -528,7 +531,7 @@ class PayPalWebCheckoutClient internal constructor( } /** * After a merchant app has re-entered the foreground following an auth challenge - * (@see [PayPalWebCheckoutClient.vaultAsync]), call this method to see if a user has + * (@see [PayPalWebCheckoutClient.vault]), call this method to see if a user has * successfully authorized a PayPal account for vaulting. * * @param [intent] An Android intent that holds the deep link put the merchant app diff --git a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt index 75085d593..55d22566e 100644 --- a/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt +++ b/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt @@ -1,7 +1,7 @@ package com.paypal.android.paypalwebpayments /** - * Request to vault a PayPal payment method using [PayPalWebCheckoutClient.vaultAsync]. + * Request to vault a PayPal payment method using [PayPalWebCheckoutClient.vault]. * * @property [setupTokenId] ID for the setup token associated with the vault approval * @property [approveVaultHref] URL for the approval web page @@ -20,7 +20,7 @@ constructor( ) { /** - * Request to vault a PayPal payment method using [PayPalWebCheckoutClient.vaultAsync]. + * Request to vault a PayPal payment method using [PayPalWebCheckoutClient.vault]. * * @property [setupTokenId] ID for the setup token associated with the vault approval */