diff --git a/BraintreeCore/src/main/java/com/braintreepayments/api/core/BraintreeClient.kt b/BraintreeCore/src/main/java/com/braintreepayments/api/core/BraintreeClient.kt index efc3d56cab..737e00bbdd 100644 --- a/BraintreeCore/src/main/java/com/braintreepayments/api/core/BraintreeClient.kt +++ b/BraintreeCore/src/main/java/com/braintreepayments/api/core/BraintreeClient.kt @@ -7,7 +7,6 @@ import androidx.annotation.RestrictTo import com.braintreepayments.api.sharedutils.HttpResponseCallback import com.braintreepayments.api.sharedutils.HttpResponseTiming import com.braintreepayments.api.sharedutils.ManifestValidator -import com.braintreepayments.api.sharedutils.Time import org.json.JSONException import org.json.JSONObject @@ -22,12 +21,12 @@ class BraintreeClient internal constructor( authorization: Authorization, returnUrlScheme: String, appLinkReturnUri: Uri?, + deepLinkFallbackUrlScheme: String? = null, sdkComponent: SdkComponent = SdkComponent.create(applicationContext), private val httpClient: BraintreeHttpClient = BraintreeHttpClient(), private val graphQLClient: BraintreeGraphQLClient = BraintreeGraphQLClient(), private val configurationLoader: ConfigurationLoader = ConfigurationLoader.instance, private val manifestValidator: ManifestValidator = ManifestValidator(), - private val time: Time = Time(), private val merchantRepository: MerchantRepository = MerchantRepository.instance, private val analyticsClient: AnalyticsClient = AnalyticsClient(), ) { @@ -47,6 +46,7 @@ class BraintreeClient internal constructor( returnUrlScheme: String? = null, appLinkReturnUri: Uri? = null, integrationType: IntegrationType? = null, + deepLinkFallbackUrlScheme: String? = null, ) : this( applicationContext = context.applicationContext, authorization = Authorization.fromString(authorization), @@ -54,6 +54,7 @@ class BraintreeClient internal constructor( ?: "${getAppPackageNameWithoutUnderscores(context.applicationContext)}.braintree", appLinkReturnUri = appLinkReturnUri, integrationType = integrationType ?: IntegrationType.CUSTOM, + deepLinkFallbackUrlScheme = deepLinkFallbackUrlScheme ) init { @@ -73,6 +74,9 @@ class BraintreeClient internal constructor( if (appLinkReturnUri != null) { it.appLinkReturnUri = appLinkReturnUri } + if (deepLinkFallbackUrlScheme != null) { + it.deepLinkFallbackUrlScheme = deepLinkFallbackUrlScheme + } } } diff --git a/BraintreeCore/src/main/java/com/braintreepayments/api/core/GetReturnLinkUseCase.kt b/BraintreeCore/src/main/java/com/braintreepayments/api/core/GetReturnLinkUseCase.kt new file mode 100644 index 0000000000..7fc8d7f3e1 --- /dev/null +++ b/BraintreeCore/src/main/java/com/braintreepayments/api/core/GetReturnLinkUseCase.kt @@ -0,0 +1,48 @@ +package com.braintreepayments.api.core + +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import androidx.annotation.RestrictTo +import com.braintreepayments.api.core.GetReturnLinkUseCase.ReturnLinkResult + +/** + * Use case that returns a return link that should be used for navigating from App Switch / CCT back into the merchant + * app. It handles both App Links and Deep Links. + * + * If a user unchecks the "Open supported links" checkbox in the Android OS settings for the merchant's app. If this + * setting is unchecked, this use case will return [ReturnLinkResult.DeepLink], otherwise [ReturnLinkResult.AppLink] + * will be returned. + */ +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +class GetReturnLinkUseCase(private val merchantRepository: MerchantRepository) { + + sealed class ReturnLinkResult { + data class AppLink(val appLinkReturnUri: Uri) : ReturnLinkResult() + + data class DeepLink(val deepLinkFallbackUrlScheme: String) : ReturnLinkResult() + + data class Failure(val exception: Exception) : ReturnLinkResult() + } + + operator fun invoke(): ReturnLinkResult { + val context = merchantRepository.applicationContext + val intent = Intent(Intent.ACTION_VIEW, merchantRepository.appLinkReturnUri).apply { + addCategory(Intent.CATEGORY_BROWSABLE) + } + val resolvedActivity = context.packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) + return if (resolvedActivity?.activityInfo?.packageName == context.packageName) { + merchantRepository.appLinkReturnUri?.let { + ReturnLinkResult.AppLink(it) + } ?: run { + ReturnLinkResult.Failure(BraintreeException("App Link Return Uri is null")) + } + } else { + merchantRepository.deepLinkFallbackUrlScheme?.let { + ReturnLinkResult.DeepLink(it) + } ?: run { + ReturnLinkResult.Failure(BraintreeException("Deep Link fallback return url is null")) + } + } + } +} diff --git a/BraintreeCore/src/main/java/com/braintreepayments/api/core/MerchantRepository.kt b/BraintreeCore/src/main/java/com/braintreepayments/api/core/MerchantRepository.kt index d8ad972c5c..ce0fccb246 100644 --- a/BraintreeCore/src/main/java/com/braintreepayments/api/core/MerchantRepository.kt +++ b/BraintreeCore/src/main/java/com/braintreepayments/api/core/MerchantRepository.kt @@ -16,6 +16,8 @@ class MerchantRepository { lateinit var returnUrlScheme: String var appLinkReturnUri: Uri? = null + var deepLinkFallbackUrlScheme: String? = null + companion object { /** diff --git a/BraintreeCore/src/test/java/com/braintreepayments/api/core/BraintreeClientUnitTest.kt b/BraintreeCore/src/test/java/com/braintreepayments/api/core/BraintreeClientUnitTest.kt index ecba21eace..c7d6d5cbf9 100644 --- a/BraintreeCore/src/test/java/com/braintreepayments/api/core/BraintreeClientUnitTest.kt +++ b/BraintreeCore/src/test/java/com/braintreepayments/api/core/BraintreeClientUnitTest.kt @@ -10,7 +10,6 @@ import com.braintreepayments.api.BrowserSwitchClient import com.braintreepayments.api.sharedutils.HttpResponseCallback import com.braintreepayments.api.sharedutils.ManifestValidator import com.braintreepayments.api.sharedutils.NetworkResponseCallback -import com.braintreepayments.api.sharedutils.Time import com.braintreepayments.api.testutils.Fixtures import io.mockk.* import org.json.JSONException @@ -319,10 +318,7 @@ class BraintreeClientUnitTest { .configuration(configuration) .build() - val time: Time = mockk() - every { time.currentTime } returns 123 - - val sut = createBraintreeClient(configurationLoader, time) + val sut = createBraintreeClient(configurationLoader) sut.sendAnalyticsEvent("event.started") verify { @@ -432,7 +428,6 @@ class BraintreeClientUnitTest { private fun createBraintreeClient( configurationLoader: ConfigurationLoader = mockk(), - time: Time = Time(), appLinkReturnUri: Uri? = Uri.parse("https://example.com"), merchantRepository: MerchantRepository = MerchantRepository.instance ) = BraintreeClient( @@ -446,7 +441,6 @@ class BraintreeClientUnitTest { analyticsClient = analyticsClient, manifestValidator = manifestValidator, configurationLoader = configurationLoader, - time = time, merchantRepository = merchantRepository, ) } diff --git a/BraintreeCore/src/test/java/com/braintreepayments/api/core/GetReturnLinkUseCaseUnitTest.kt b/BraintreeCore/src/test/java/com/braintreepayments/api/core/GetReturnLinkUseCaseUnitTest.kt new file mode 100644 index 0000000000..81ed10d334 --- /dev/null +++ b/BraintreeCore/src/test/java/com/braintreepayments/api/core/GetReturnLinkUseCaseUnitTest.kt @@ -0,0 +1,87 @@ +package com.braintreepayments.api.core + +import android.content.Context +import android.content.Intent +import android.content.pm.ActivityInfo +import android.content.pm.ResolveInfo +import android.net.Uri +import io.mockk.every +import io.mockk.mockk +import org.junit.Before +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +@RunWith(RobolectricTestRunner::class) +class GetReturnLinkUseCaseUnitTest { + + private val merchantRepository: MerchantRepository = mockk(relaxed = true) + private val context: Context = mockk(relaxed = true) + private val resolveInfo = ResolveInfo() + private val activityInfo = ActivityInfo() + private val contextPackageName = "context.package.name" + private val appLinkReturnUri = Uri.parse("https://example.com") + private val deepLinkFallbackUrlScheme = "com.braintreepayments.demo" + + lateinit var subject: GetReturnLinkUseCase + + @Before + fun setUp() { + every { merchantRepository.applicationContext } returns context + every { merchantRepository.appLinkReturnUri } returns appLinkReturnUri + every { merchantRepository.deepLinkFallbackUrlScheme } returns deepLinkFallbackUrlScheme + every { context.packageName } returns contextPackageName + resolveInfo.activityInfo = activityInfo + every { context.packageManager.resolveActivity(any(), any()) } returns resolveInfo + + subject = GetReturnLinkUseCase(merchantRepository) + } + + @Test + fun `when invoke is called and app link is available, APP_LINK is returned`() { + activityInfo.packageName = "context.package.name" + + val result = subject() + + assertEquals(GetReturnLinkUseCase.ReturnLinkResult.AppLink(appLinkReturnUri), result) + } + + @Test + fun `when invoke is called and app link is not available, DEEP_LINK is returned`() { + activityInfo.packageName = "different.package.name" + + val result = subject() + + assertEquals(GetReturnLinkUseCase.ReturnLinkResult.DeepLink(deepLinkFallbackUrlScheme), result) + } + + @Test + fun `when invoke is called and deep link is available but null, Failure is returned`() { + activityInfo.packageName = "different.package.name" + every { merchantRepository.deepLinkFallbackUrlScheme } returns null + + val result = subject() + + assertTrue { result is GetReturnLinkUseCase.ReturnLinkResult.Failure } + assertEquals( + "Deep Link fallback return url is null", + (result as GetReturnLinkUseCase.ReturnLinkResult.Failure).exception.message + ) + } + + @Test + fun `when invoke is called and app link is available but null, Failure is returned`() { + activityInfo.packageName = "context.package.name" + every { merchantRepository.appLinkReturnUri } returns null + + val result = subject() + + assertTrue { result is GetReturnLinkUseCase.ReturnLinkResult.Failure } + assertEquals( + "App Link Return Uri is null", + (result as GetReturnLinkUseCase.ReturnLinkResult.Failure).exception.message + ) + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 18b3f5d9b8..bc38ca05bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## unreleased +* PayPal + * Add `deepLinkFallbackUrlScheme` to `PayPalClient` constructor params for supporting deep link fallback * LocalPayment * Make LocalPaymentAuthRequestParams public (fixes #1207) diff --git a/Demo/src/main/java/com/braintreepayments/demo/PayPalFragment.java b/Demo/src/main/java/com/braintreepayments/demo/PayPalFragment.java index fa90df0b62..86c067e408 100644 --- a/Demo/src/main/java/com/braintreepayments/demo/PayPalFragment.java +++ b/Demo/src/main/java/com/braintreepayments/demo/PayPalFragment.java @@ -75,9 +75,10 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c }); payPalClient = new PayPalClient( - requireContext(), - super.getAuthStringArg(), - Uri.parse("https://mobile-sdk-demo-site-838cead5d3ab.herokuapp.com/braintree-payments") + requireContext(), + super.getAuthStringArg(), + Uri.parse("https://mobile-sdk-demo-site-838cead5d3ab.herokuapp.com/braintree-payments"), + "com.braintreepayments.demo.braintree" ); payPalLauncher = new PayPalLauncher(); diff --git a/PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalClient.kt b/PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalClient.kt index ccdfbd46ad..5d00d97370 100644 --- a/PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalClient.kt +++ b/PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalClient.kt @@ -9,6 +9,7 @@ import com.braintreepayments.api.core.BraintreeClient import com.braintreepayments.api.core.BraintreeException import com.braintreepayments.api.core.BraintreeRequestCodes import com.braintreepayments.api.core.Configuration +import com.braintreepayments.api.core.GetReturnLinkUseCase import com.braintreepayments.api.core.LinkType import com.braintreepayments.api.core.MerchantRepository import com.braintreepayments.api.core.UserCanceledException @@ -24,6 +25,7 @@ class PayPalClient internal constructor( private val braintreeClient: BraintreeClient, private val internalPayPalClient: PayPalInternalClient = PayPalInternalClient(braintreeClient), private val merchantRepository: MerchantRepository = MerchantRepository.instance, + private val getReturnLinkUseCase: GetReturnLinkUseCase = GetReturnLinkUseCase(merchantRepository) ) { /** @@ -48,14 +50,23 @@ class PayPalClient internal constructor( * @param context an Android Context * @param authorization a Tokenization Key or Client Token used to authenticate * @param appLinkReturnUrl A [Uri] containing the Android App Link website associated with - * your application to be used to return to your app from the PayPal - * payment flows. + * your application to be used to return to your app from the PayPal payment flows. + * @param deepLinkFallbackUrlScheme A return url scheme that will be used as a deep link fallback when returning to + * your app via App Link is not available (buyer unchecks the "Open supported links" setting). */ constructor( context: Context, authorization: String, - appLinkReturnUrl: Uri - ) : this(BraintreeClient(context, authorization, null, appLinkReturnUrl)) + appLinkReturnUrl: Uri, + deepLinkFallbackUrlScheme: String? = null + ) : this( + BraintreeClient( + context = context, + authorization = authorization, + deepLinkFallbackUrlScheme = deepLinkFallbackUrlScheme, + appLinkReturnUri = appLinkReturnUrl + ) + ) /** * Starts the PayPal payment flow by creating a [PayPalPaymentAuthRequestParams] to be @@ -89,6 +100,7 @@ class PayPalClient internal constructor( } } + @Suppress("TooGenericExceptionCaught") private fun sendPayPalRequest( context: Context, payPalRequest: PayPalRequest, @@ -112,14 +124,16 @@ class PayPalClient internal constructor( braintreeClient.sendAnalyticsEvent(PayPalAnalytics.APP_SWITCH_STARTED, analyticsParams) } - callback.onPayPalPaymentAuthRequest( - PayPalPaymentAuthRequest.ReadyToLaunch(payPalResponse) - ) - } catch (exception: JSONException) { - callbackCreatePaymentAuthFailure( - callback, - PayPalPaymentAuthRequest.Failure(exception) - ) + callback.onPayPalPaymentAuthRequest(PayPalPaymentAuthRequest.ReadyToLaunch(payPalResponse)) + } catch (exception: Exception) { + when (exception) { + is JSONException, + is BraintreeException -> { + callbackCreatePaymentAuthFailure(callback, PayPalPaymentAuthRequest.Failure(exception)) + } + + else -> throw exception + } } } else { callbackCreatePaymentAuthFailure( @@ -130,7 +144,7 @@ class PayPalClient internal constructor( } } - @Throws(JSONException::class) + @Throws(JSONException::class, BraintreeException::class) private fun buildBrowserSwitchOptions( paymentAuthRequest: PayPalPaymentAuthRequestParams ): BrowserSwitchOptions { @@ -152,10 +166,22 @@ class PayPalClient internal constructor( return BrowserSwitchOptions() .requestCode(BraintreeRequestCodes.PAYPAL.code) - .appLinkUri(merchantRepository.appLinkReturnUri) .url(Uri.parse(paymentAuthRequest.approvalUrl)) .launchAsNewTask(braintreeClient.launchesBrowserSwitchAsNewTask()) .metadata(metadata) + .apply { + when (val returnLinkResult = getReturnLinkUseCase()) { + is GetReturnLinkUseCase.ReturnLinkResult.AppLink -> { + appLinkUri(returnLinkResult.appLinkReturnUri) + } + + is GetReturnLinkUseCase.ReturnLinkResult.DeepLink -> { + returnUrlScheme(returnLinkResult.deepLinkFallbackUrlScheme) + } + + is GetReturnLinkUseCase.ReturnLinkResult.Failure -> throw returnLinkResult.exception + } + } } /** diff --git a/PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalInternalClient.kt b/PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalInternalClient.kt index cfb9427d40..38746419bf 100644 --- a/PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalInternalClient.kt +++ b/PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalInternalClient.kt @@ -7,6 +7,7 @@ import com.braintreepayments.api.core.BraintreeClient import com.braintreepayments.api.core.BraintreeException import com.braintreepayments.api.core.Configuration import com.braintreepayments.api.core.DeviceInspector +import com.braintreepayments.api.core.GetReturnLinkUseCase import com.braintreepayments.api.core.MerchantRepository import com.braintreepayments.api.datacollector.DataCollector import com.braintreepayments.api.datacollector.DataCollectorInternalRequest @@ -20,10 +21,8 @@ internal class PayPalInternalClient( private val apiClient: ApiClient = ApiClient(braintreeClient), private val deviceInspector: DeviceInspector = DeviceInspector(), private val merchantRepository: MerchantRepository = MerchantRepository.instance, + private val getReturnLinkUseCase: GetReturnLinkUseCase = GetReturnLinkUseCase(merchantRepository) ) { - private val cancelUrl = "${merchantRepository.appLinkReturnUri}://onetouch/v1/cancel" - private val successUrl = "${merchantRepository.appLinkReturnUri}://onetouch/v1/success" - private val appLink = merchantRepository.appLinkReturnUri?.toString() fun sendRequest( context: Context, @@ -44,18 +43,37 @@ internal class PayPalInternalClient( CREATE_SINGLE_PAYMENT_ENDPOINT } val url = "/v1/$endpoint" - val appLinkReturn = if (isBillingAgreement) appLink else null if (isBillingAgreement && (payPalRequest as PayPalVaultRequest).enablePayPalAppSwitch) { payPalRequest.enablePayPalAppSwitch = isPayPalInstalled(context) } + val returnLinkResult = getReturnLinkUseCase() + val navigationLink: String = when (returnLinkResult) { + is GetReturnLinkUseCase.ReturnLinkResult.AppLink -> returnLinkResult.appLinkReturnUri.toString() + is GetReturnLinkUseCase.ReturnLinkResult.DeepLink -> returnLinkResult.deepLinkFallbackUrlScheme + is GetReturnLinkUseCase.ReturnLinkResult.Failure -> { + callback.onResult(null, returnLinkResult.exception) + return@getConfiguration + } + } + val appLinkParam = if ( + returnLinkResult is GetReturnLinkUseCase.ReturnLinkResult.AppLink && isBillingAgreement + ) { + merchantRepository.appLinkReturnUri?.toString() + } else { + null + } + + val cancelUrl = "$navigationLink://onetouch/v1/cancel" + val successUrl = "$navigationLink://onetouch/v1/success" + val requestBody = payPalRequest.createRequestBody( configuration = configuration, authorization = merchantRepository.authorization, successUrl = successUrl, cancelUrl = cancelUrl, - appLink = appLinkReturn + appLink = appLinkParam ) ?: throw JSONException("Error creating requestBody") sendPost( @@ -123,12 +141,21 @@ internal class PayPalInternalClient( ) } + val returnLink: String = when (val returnLinkResult = getReturnLinkUseCase()) { + is GetReturnLinkUseCase.ReturnLinkResult.AppLink -> returnLinkResult.appLinkReturnUri.toString() + is GetReturnLinkUseCase.ReturnLinkResult.DeepLink -> returnLinkResult.deepLinkFallbackUrlScheme + is GetReturnLinkUseCase.ReturnLinkResult.Failure -> { + callback.onResult(null, returnLinkResult.exception) + return@sendPOST + } + } + val paymentAuthRequest = PayPalPaymentAuthRequestParams( payPalRequest = payPalRequest, browserSwitchOptions = null, clientMetadataId = clientMetadataId, pairingId = pairingId, - successUrl = successUrl + successUrl = "$returnLink://onetouch/v1/success" ) if (isAppSwitchEnabled(payPalRequest) && isPayPalInstalled(context)) { diff --git a/PayPal/src/test/java/com/braintreepayments/api/paypal/PayPalClientUnitTest.java b/PayPal/src/test/java/com/braintreepayments/api/paypal/PayPalClientUnitTest.java index ea68b5b6ed..3bb81af02a 100644 --- a/PayPal/src/test/java/com/braintreepayments/api/paypal/PayPalClientUnitTest.java +++ b/PayPal/src/test/java/com/braintreepayments/api/paypal/PayPalClientUnitTest.java @@ -17,8 +17,10 @@ import com.braintreepayments.api.BrowserSwitchOptions; import com.braintreepayments.api.core.AnalyticsEventParams; import com.braintreepayments.api.core.BraintreeClient; +import com.braintreepayments.api.core.BraintreeException; import com.braintreepayments.api.core.BraintreeRequestCodes; import com.braintreepayments.api.core.Configuration; +import com.braintreepayments.api.core.GetReturnLinkUseCase; import com.braintreepayments.api.core.MerchantRepository; import com.braintreepayments.api.testutils.Fixtures; import com.braintreepayments.api.testutils.MockBraintreeClientBuilder; @@ -43,6 +45,7 @@ public class PayPalClientUnitTest { private PayPalPaymentAuthCallback paymentAuthCallback; private MerchantRepository merchantRepository; + private GetReturnLinkUseCase getReturnLinkUseCase; @Before public void beforeEach() throws JSONException { @@ -55,6 +58,12 @@ public void beforeEach() throws JSONException { paymentAuthCallback = mock(PayPalPaymentAuthCallback.class); merchantRepository = mock(MerchantRepository.class); + getReturnLinkUseCase = mock(GetReturnLinkUseCase.class); + + when(merchantRepository.getReturnUrlScheme()).thenReturn("com.braintreepayments.demo"); + when(getReturnLinkUseCase.invoke()).thenReturn(new GetReturnLinkUseCase.ReturnLinkResult.AppLink( + Uri.parse("www.example.com") + )); } @Test @@ -77,7 +86,7 @@ public void createPaymentAuthRequest_callsBackPayPalResponse_sendsStartedAnalyti BraintreeClient braintreeClient = new MockBraintreeClientBuilder().configuration(payPalEnabledConfig).build(); - PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository); + PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository, getReturnLinkUseCase); sut.createPaymentAuthRequest(activity, payPalVaultRequest, paymentAuthCallback); ArgumentCaptor captor = @@ -131,7 +140,7 @@ public void createPaymentAuthRequest_whenLaunchesBrowserSwitchAsNewTaskEnabled_s new MockBraintreeClientBuilder().configuration(payPalEnabledConfig) .launchesBrowserSwitchAsNewTask(true).build(); - PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository); + PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository, getReturnLinkUseCase); sut.createPaymentAuthRequest(activity, payPalVaultRequest, paymentAuthCallback); ArgumentCaptor captor = @@ -166,7 +175,7 @@ public void createPaymentAuthRequest_setsAppLinkReturnUrl() { BraintreeClient braintreeClient = new MockBraintreeClientBuilder().configuration(payPalEnabledConfig) .build(); - PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository); + PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository, getReturnLinkUseCase); sut.createPaymentAuthRequest(activity, payPalVaultRequest, paymentAuthCallback); ArgumentCaptor captor = @@ -179,6 +188,78 @@ public void createPaymentAuthRequest_setsAppLinkReturnUrl() { ((PayPalPaymentAuthRequest.ReadyToLaunch) request).getRequestParams().getBrowserSwitchOptions().getAppLinkUri()); } + @Test + public void createPaymentAuthRequest_setsDeepLinkReturnUrlScheme() { + when(getReturnLinkUseCase.invoke()).thenReturn(new GetReturnLinkUseCase.ReturnLinkResult.DeepLink( + "com.braintreepayments.demo" + )); + PayPalVaultRequest payPalVaultRequest = new PayPalVaultRequest(true); + payPalVaultRequest.setMerchantAccountId("sample-merchant-account-id"); + + PayPalPaymentAuthRequestParams paymentAuthRequest = new PayPalPaymentAuthRequestParams( + payPalVaultRequest, + null, + "https://example.com/approval/url", + "sample-client-metadata-id", + null, + "https://example.com/success/url" + ); + + PayPalInternalClient payPalInternalClient = + new MockPayPalInternalClientBuilder().sendRequestSuccess(paymentAuthRequest) + .build(); + + BraintreeClient braintreeClient = new MockBraintreeClientBuilder().configuration(payPalEnabledConfig) + .build(); + + PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository, getReturnLinkUseCase); + sut.createPaymentAuthRequest(activity, payPalVaultRequest, paymentAuthCallback); + + ArgumentCaptor captor = + ArgumentCaptor.forClass(PayPalPaymentAuthRequest.class); + verify(paymentAuthCallback).onPayPalPaymentAuthRequest(captor.capture()); + + PayPalPaymentAuthRequest request = captor.getValue(); + assertTrue(request instanceof PayPalPaymentAuthRequest.ReadyToLaunch); + assertEquals("com.braintreepayments.demo", + ((PayPalPaymentAuthRequest.ReadyToLaunch) request).getRequestParams().getBrowserSwitchOptions().getReturnUrlScheme()); + } + + @Test + public void createPaymentAuthRequest_returnsAnErrorWhen_getReturnLinkUseCase_returnsAFailure() { + BraintreeException exception = new BraintreeException(); + when(getReturnLinkUseCase.invoke()).thenReturn(new GetReturnLinkUseCase.ReturnLinkResult.Failure(exception)); + + PayPalVaultRequest payPalVaultRequest = new PayPalVaultRequest(true); + payPalVaultRequest.setMerchantAccountId("sample-merchant-account-id"); + + PayPalPaymentAuthRequestParams paymentAuthRequest = new PayPalPaymentAuthRequestParams( + payPalVaultRequest, + null, + "https://example.com/approval/url", + "sample-client-metadata-id", + null, + "https://example.com/success/url" + ); + + PayPalInternalClient payPalInternalClient = + new MockPayPalInternalClientBuilder().sendRequestSuccess(paymentAuthRequest) + .build(); + + BraintreeClient braintreeClient = new MockBraintreeClientBuilder().configuration(payPalEnabledConfig) + .build(); + + PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository, getReturnLinkUseCase); + sut.createPaymentAuthRequest(activity, payPalVaultRequest, paymentAuthCallback); + + ArgumentCaptor captor = ArgumentCaptor.forClass(PayPalPaymentAuthRequest.class); + verify(paymentAuthCallback).onPayPalPaymentAuthRequest(captor.capture()); + + PayPalPaymentAuthRequest request = captor.getValue(); + assertTrue(request instanceof PayPalPaymentAuthRequest.Failure); + assertEquals(exception, ((PayPalPaymentAuthRequest.Failure) request).getError()); + } + @Test public void createPaymentAuthRequest_whenPayPalNotEnabled_returnsError() { PayPalInternalClient payPalInternalClient = new MockPayPalInternalClientBuilder().build(); @@ -186,7 +267,7 @@ public void createPaymentAuthRequest_whenPayPalNotEnabled_returnsError() { BraintreeClient braintreeClient = new MockBraintreeClientBuilder().configuration(payPalDisabledConfig).build(); - PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository); + PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository, getReturnLinkUseCase); sut.createPaymentAuthRequest(activity, new PayPalCheckoutRequest("1.00", true), paymentAuthCallback); @@ -215,7 +296,7 @@ public void createPaymentAuthRequest_whenCheckoutRequest_whenConfigError_forward .configurationError(authError) .build(); - PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository); + PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository, getReturnLinkUseCase); sut.createPaymentAuthRequest(activity, new PayPalCheckoutRequest("1.00", true), paymentAuthCallback); ArgumentCaptor captor = @@ -240,7 +321,7 @@ public void requestBillingAgreement_whenConfigError_forwardsErrorToListener() { .configurationError(authError) .build(); - PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository); + PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository, getReturnLinkUseCase); sut.createPaymentAuthRequest(activity, new PayPalVaultRequest(true), paymentAuthCallback); ArgumentCaptor captor = @@ -266,7 +347,7 @@ public void createPaymentAuthRequest_whenVaultRequest_sendsPayPalRequestViaInter PayPalVaultRequest payPalRequest = new PayPalVaultRequest(true); - PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository); + PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository, getReturnLinkUseCase); sut.createPaymentAuthRequest(activity, payPalRequest, paymentAuthCallback); verify(payPalInternalClient).sendRequest(same(activity), same(payPalRequest), @@ -282,7 +363,7 @@ public void createPaymentAuthRequest_whenCheckoutRequest_sendsPayPalRequestViaIn PayPalCheckoutRequest payPalRequest = new PayPalCheckoutRequest("1.00", true); - PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository); + PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository, getReturnLinkUseCase); sut.createPaymentAuthRequest(activity, payPalRequest, paymentAuthCallback); verify(payPalInternalClient).sendRequest(same(activity), same(payPalRequest), @@ -314,7 +395,7 @@ public void createPaymentAuthRequest_whenVaultRequest_sendsAppSwitchStartedEvent BraintreeClient braintreeClient = new MockBraintreeClientBuilder().configuration(payPalEnabledConfig).build(); - PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository); + PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository, getReturnLinkUseCase); sut.createPaymentAuthRequest(activity, payPalVaultRequest, paymentAuthCallback); ArgumentCaptor captor = @@ -359,7 +440,7 @@ public void tokenize_withBillingAgreement_tokenizesResponseOnSuccess() throws JS PayPalPaymentAuthResult.Success payPalPaymentAuthResult = new PayPalPaymentAuthResult.Success( browserSwitchResult); BraintreeClient braintreeClient = new MockBraintreeClientBuilder().build(); - PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository); + PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository, getReturnLinkUseCase); sut.tokenize(payPalPaymentAuthResult, payPalTokenizeCallback); @@ -403,7 +484,7 @@ public void tokenize_withOneTimePayment_tokenizesResponseOnSuccess() throws JSON PayPalPaymentAuthResult.Success payPalPaymentAuthResult = new PayPalPaymentAuthResult.Success( browserSwitchResult); BraintreeClient braintreeClient = new MockBraintreeClientBuilder().build(); - PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository); + PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository, getReturnLinkUseCase); sut.tokenize(payPalPaymentAuthResult, payPalTokenizeCallback); @@ -448,7 +529,7 @@ public void tokenize_whenCancelUriReceived_notifiesCancellationAndSendsAnalytics PayPalPaymentAuthResult.Success payPalPaymentAuthResult = new PayPalPaymentAuthResult.Success( browserSwitchResult); BraintreeClient braintreeClient = new MockBraintreeClientBuilder().build(); - PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository); + PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository, getReturnLinkUseCase); sut.tokenize(payPalPaymentAuthResult, payPalTokenizeCallback); @@ -488,7 +569,7 @@ public void tokenize_whenPayPalInternalClientTokenizeResult_callsBackResult() PayPalPaymentAuthResult.Success payPalPaymentAuthResult = new PayPalPaymentAuthResult.Success( browserSwitchResult); BraintreeClient braintreeClient = new MockBraintreeClientBuilder().build(); - PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository); + PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository, getReturnLinkUseCase); sut.tokenize(payPalPaymentAuthResult, payPalTokenizeCallback); @@ -530,7 +611,7 @@ public void tokenize_whenPayPalInternalClientTokenizeResult_sendsAppSwitchSuccee PayPalPaymentAuthResult.Success payPalPaymentAuthResult = new PayPalPaymentAuthResult.Success( browserSwitchResult); BraintreeClient braintreeClient = new MockBraintreeClientBuilder().build(); - PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository); + PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository, getReturnLinkUseCase); sut.tokenize(payPalPaymentAuthResult, payPalTokenizeCallback); @@ -569,7 +650,7 @@ public void tokenize_whenPayPalNotEnabled_sendsAppSwitchFailedEvents() throws JS PayPalPaymentAuthResult.Success payPalPaymentAuthResult = new PayPalPaymentAuthResult.Success( browserSwitchResult); BraintreeClient braintreeClient = new MockBraintreeClientBuilder().build(); - PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository); + PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository, getReturnLinkUseCase); sut.tokenize(payPalPaymentAuthResult, payPalTokenizeCallback); @@ -601,7 +682,7 @@ public void tokenize_whenCancelUriReceived_sendsAppSwitchCanceledEvents() PayPalPaymentAuthResult.Success payPalPaymentAuthResult = new PayPalPaymentAuthResult.Success( browserSwitchResult); BraintreeClient braintreeClient = new MockBraintreeClientBuilder().build(); - PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository); + PayPalClient sut = new PayPalClient(braintreeClient, payPalInternalClient, merchantRepository, getReturnLinkUseCase); sut.tokenize(payPalPaymentAuthResult, payPalTokenizeCallback); diff --git a/PayPal/src/test/java/com/braintreepayments/api/paypal/PayPalInternalClientUnitTest.java b/PayPal/src/test/java/com/braintreepayments/api/paypal/PayPalInternalClientUnitTest.java index d743584131..0de7604dc5 100644 --- a/PayPal/src/test/java/com/braintreepayments/api/paypal/PayPalInternalClientUnitTest.java +++ b/PayPal/src/test/java/com/braintreepayments/api/paypal/PayPalInternalClientUnitTest.java @@ -20,9 +20,11 @@ import com.braintreepayments.api.core.ApiClient; import com.braintreepayments.api.core.BraintreeClient; +import com.braintreepayments.api.core.BraintreeException; import com.braintreepayments.api.core.ClientToken; import com.braintreepayments.api.core.Configuration; import com.braintreepayments.api.core.DeviceInspector; +import com.braintreepayments.api.core.GetReturnLinkUseCase; import com.braintreepayments.api.core.MerchantRepository; import com.braintreepayments.api.core.PostalAddress; import com.braintreepayments.api.core.TokenizationKey; @@ -63,6 +65,7 @@ public class PayPalInternalClientUnitTest { PayPalInternalClientCallback payPalInternalClientCallback; private MerchantRepository merchantRepository = mock(MerchantRepository.class); + private GetReturnLinkUseCase getReturnLinkUseCase = mock(GetReturnLinkUseCase.class); @Before public void beforeEach() throws JSONException { @@ -75,6 +78,10 @@ public void beforeEach() throws JSONException { apiClient = mock(ApiClient.class); deviceInspector = mock(DeviceInspector.class); payPalInternalClientCallback = mock(PayPalInternalClientCallback.class); + + when(getReturnLinkUseCase.invoke()).thenReturn(new GetReturnLinkUseCase.ReturnLinkResult.AppLink( + Uri.parse("https://example.com") + )); } @Test @@ -92,7 +99,8 @@ public void sendRequest_withPayPalVaultRequest_sendsAllParameters() throws JSONE dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); PostalAddress shippingAddressOverride = new PostalAddress(); @@ -153,6 +161,87 @@ public void sendRequest_withPayPalVaultRequest_sendsAllParameters() throws JSONE JSONAssert.assertEquals(expected, actual, true); } + @Test + public void sendRequest_withPayPalVaultRequest_sendsAllParameters_with_deep_link() throws JSONException { + when(getReturnLinkUseCase.invoke()).thenReturn(new GetReturnLinkUseCase.ReturnLinkResult.DeepLink( + "com.braintreepayments.demo" + )); + + BraintreeClient braintreeClient = new MockBraintreeClientBuilder() + .configuration(configuration) + .build(); + when(clientToken.getBearer()).thenReturn("client-token-bearer"); + + when(merchantRepository.getAuthorization()).thenReturn(clientToken); + when(merchantRepository.getReturnUrlScheme()).thenReturn("com.braintreepayments.demo"); + + PayPalInternalClient sut = new PayPalInternalClient( + braintreeClient, + dataCollector, + apiClient, + deviceInspector, + merchantRepository, + getReturnLinkUseCase + ); + + PostalAddress shippingAddressOverride = new PostalAddress(); + shippingAddressOverride.setRecipientName("Brianna Tree"); + shippingAddressOverride.setStreetAddress("123 Fake St."); + shippingAddressOverride.setExtendedAddress("Apt. v.0"); + shippingAddressOverride.setLocality("Oakland"); + shippingAddressOverride.setRegion("CA"); + shippingAddressOverride.setPostalCode("12345"); + shippingAddressOverride.setCountryCodeAlpha2("US"); + + PayPalVaultRequest payPalRequest = new PayPalVaultRequest(true); + payPalRequest.setBillingAgreementDescription("Billing Agreement Description"); + payPalRequest.setMerchantAccountId("sample-merchant-account-id"); + payPalRequest.setLandingPageType(PayPalLandingPageType.LANDING_PAGE_TYPE_BILLING); + payPalRequest.setDisplayName("sample-display-name"); + payPalRequest.setLocaleCode("US"); + payPalRequest.setShippingAddressRequired(true); + payPalRequest.setShippingAddressEditable(true); + payPalRequest.setShouldOfferCredit(true); + payPalRequest.setShippingAddressOverride(shippingAddressOverride); + + sut.sendRequest(context, payPalRequest, payPalInternalClientCallback); + + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(braintreeClient).sendPOST( + eq("/v1/paypal_hermes/setup_billing_agreement"), + captor.capture(), + anyMap(), + any(HttpResponseCallback.class) + ); + + String result = captor.getValue(); + JSONObject actual = new JSONObject(result); + + JSONObject expected = new JSONObject() + .put("authorization_fingerprint", "client-token-bearer") + .put("return_url", "com.braintreepayments.demo://onetouch/v1/success") + .put("cancel_url", "com.braintreepayments.demo://onetouch/v1/cancel") + .put("offer_paypal_credit", true) + .put("description", "Billing Agreement Description") + .put("experience_profile", new JSONObject() + .put("no_shipping", false) + .put("landing_page_type", "billing") + .put("brand_name", "sample-display-name") + .put("locale_code", "US") + .put("address_override", false)) + .put("shipping_address", new JSONObject() + .put("line1", "123 Fake St.") + .put("line2", "Apt. v.0") + .put("city", "Oakland") + .put("state", "CA") + .put("postal_code", "12345") + .put("country_code", "US") + .put("recipient_name", "Brianna Tree")) + .put("merchant_account_id", "sample-merchant-account-id"); + + JSONAssert.assertEquals(expected, actual, true); + } + @Test public void sendRequest_withPayPalCheckoutRequest_sendsAllParameters() throws JSONException { BraintreeClient braintreeClient = new MockBraintreeClientBuilder() @@ -161,14 +250,14 @@ public void sendRequest_withPayPalCheckoutRequest_sendsAllParameters() throws JS when(clientToken.getBearer()).thenReturn("client-token-bearer"); when(merchantRepository.getAuthorization()).thenReturn(clientToken); - when(merchantRepository.getAppLinkReturnUri()).thenReturn(Uri.parse("https://example.com")); PayPalInternalClient sut = new PayPalInternalClient( braintreeClient, dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); PostalAddress shippingAddressOverride = new PostalAddress(); shippingAddressOverride.setRecipientName("Brianna Tree"); @@ -269,7 +358,8 @@ public void sendRequest_withTokenizationKey_sendsClientKeyParam() throws JSONExc dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); PayPalVaultRequest payPalRequest = new PayPalVaultRequest(true); @@ -304,7 +394,8 @@ public void sendRequest_withEmptyDisplayName_fallsBackToPayPalConfigurationDispl dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); PayPalVaultRequest payPalRequest = new PayPalVaultRequest(false); @@ -339,7 +430,8 @@ public void sendRequest_withLocaleNotSpecified_omitsLocale() throws JSONExceptio dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); PayPalVaultRequest payPalRequest = new PayPalVaultRequest(true); @@ -374,7 +466,8 @@ public void sendRequest_withMerchantAccountIdNotSpecified_omitsMerchantAccountId dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); PayPalVaultRequest payPalRequest = new PayPalVaultRequest(true); @@ -409,7 +502,8 @@ public void sendRequest_withShippingAddressOverrideNotSpecified_sendsAddressOver dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); PayPalVaultRequest payPalRequest = new PayPalVaultRequest(true); @@ -445,7 +539,8 @@ public void sendRequest_withShippingAddressSpecified_sendsAddressOverrideBasedOn dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); PayPalVaultRequest payPalRequest = new PayPalVaultRequest(true); @@ -482,7 +577,8 @@ public void sendRequest_withPayPalVaultRequest_omitsEmptyBillingAgreementDescrip dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); PayPalVaultRequest payPalRequest = new PayPalVaultRequest(true); @@ -517,7 +613,8 @@ public void sendRequest_withPayPalCheckoutRequest_fallsBackToPayPalConfiguration dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); PayPalCheckoutRequest payPalRequest = new PayPalCheckoutRequest("1.00", true); @@ -550,7 +647,8 @@ public void sendRequest_withPayPalCheckoutRequest_omitsEmptyLineItems() throws J dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); PayPalCheckoutRequest payPalRequest = new PayPalCheckoutRequest("1.00", true); @@ -587,7 +685,8 @@ public void sendRequest_whenRiskCorrelationIdNotNull_setsClientMetadataIdToRiskC dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); PayPalCheckoutRequest payPalRequest = new PayPalCheckoutRequest("1.00", true); @@ -619,7 +718,8 @@ public void sendRequest_whenRiskCorrelationIdNull_setsClientMetadataIdFromPayPal dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); PayPalCheckoutRequest payPalRequest = new PayPalCheckoutRequest("1.00", true); @@ -649,7 +749,8 @@ public void sendRequest_withPayPalCheckoutRequest_whenRequestBillingAgreementFal dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); PayPalCheckoutRequest payPalRequest = new PayPalCheckoutRequest("1.00", true); @@ -689,7 +790,8 @@ public void sendRequest_withPayPalVaultRequest_callsBackPayPalResponseOnSuccess( dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); PayPalVaultRequest payPalRequest = new PayPalVaultRequest(true); @@ -728,7 +830,8 @@ public void sendRequest_withPayPalVaultRequest_callsBackPayPalResponseOnSuccess_ dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); PayPalVaultRequest payPalRequest = new PayPalVaultRequest(true); @@ -770,7 +873,8 @@ public void sendRequest_withPayPalVaultRequest_callsBackPayPalResponseOnSuccess_ dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); PayPalVaultRequest payPalRequest = new PayPalVaultRequest(true); @@ -805,7 +909,8 @@ public void sendRequest_withPayPalCheckoutRequest_callsBackPayPalResponseOnSucce dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); PayPalCheckoutRequest payPalRequest = new PayPalCheckoutRequest("1.00", true); @@ -847,7 +952,8 @@ public void sendRequest_propagatesHttpErrors() { dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); PayPalCheckoutRequest payPalRequest = new PayPalCheckoutRequest("1.00", true); @@ -870,7 +976,8 @@ public void sendRequest_propagatesMalformedJSONResponseErrors() { dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); PayPalCheckoutRequest payPalRequest = new PayPalCheckoutRequest("1.00", true); @@ -894,7 +1001,8 @@ public void sendRequest_onConfigurationFailure_forwardsError() { dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); PayPalCheckoutRequest payPalRequest = new PayPalCheckoutRequest("1.00", true); @@ -903,6 +1011,33 @@ public void sendRequest_onConfigurationFailure_forwardsError() { verify(payPalInternalClientCallback).onResult(null, configurationError); } + @Test + public void sendRequest_returnLinkResultFailure_forwardsError() { + BraintreeException exception = new BraintreeException(); + + when(getReturnLinkUseCase.invoke()).thenReturn(new GetReturnLinkUseCase.ReturnLinkResult.Failure(exception)); + BraintreeClient braintreeClient = new MockBraintreeClientBuilder() + .configuration(configuration) + .sendPOSTSuccessfulResponse("{bad:") + .build(); + + when(merchantRepository.getAuthorization()).thenReturn(clientToken); + + PayPalInternalClient sut = new PayPalInternalClient( + braintreeClient, + dataCollector, + apiClient, + deviceInspector, + merchantRepository, + getReturnLinkUseCase + ); + + PayPalCheckoutRequest payPalRequest = new PayPalCheckoutRequest("1.00", true); + sut.sendRequest(context, payPalRequest, payPalInternalClientCallback); + + verify(payPalInternalClientCallback).onResult(isNull(), eq(exception)); + } + @Test public void tokenize_tokenizesWithApiClient() { BraintreeClient braintreeClient = new MockBraintreeClientBuilder().build(); @@ -914,7 +1049,8 @@ public void tokenize_tokenizesWithApiClient() { dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); sut.tokenize(payPalAccount, callback); @@ -937,7 +1073,8 @@ public void tokenize_onTokenizeResult_returnsAccountNonceToCallback() throws JSO dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); sut.tokenize(payPalAccount, callback); @@ -967,7 +1104,8 @@ public void tokenize_onTokenizeError_returnsErrorToCallback() { dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); sut.tokenize(payPalAccount, callback); @@ -991,7 +1129,8 @@ public void payPalDataCollector_passes_correct_arguments_to_getClientMetadataId( dataCollector, apiClient, deviceInspector, - merchantRepository + merchantRepository, + getReturnLinkUseCase ); PayPalCheckoutRequest payPalRequest = new PayPalCheckoutRequest("1.00", true);