From 648a58441035b7f4180eedfeabb79c64ad9efa3d Mon Sep 17 00:00:00 2001 From: sshropshire Date: Wed, 4 Dec 2024 14:35:06 -0600 Subject: [PATCH 01/18] Rename CardResult to LegacyCardResult before refactoring. --- .../android/cardpayments/ApproveOrderListener.kt | 4 ++-- .../paypal/android/cardpayments/CardAuthLauncher.kt | 2 +- .../com/paypal/android/cardpayments/CardCallback.kt | 13 +++++++++++++ .../com/paypal/android/cardpayments/CardClient.kt | 6 +++++- .../com/paypal/android/cardpayments/CardStatus.kt | 2 +- .../{CardResult.kt => LegacyCardResult.kt} | 2 +- .../android/cardpayments/CardClientUnitTest.kt | 6 +++--- .../android/ui/approveorder/ApproveOrderUiState.kt | 4 ++-- .../ui/approveorder/ApproveOrderViewModel.kt | 4 ++-- .../android/uishared/components/CardResultView.kt | 8 ++++---- 10 files changed, 34 insertions(+), 17 deletions(-) create mode 100644 CardPayments/src/main/java/com/paypal/android/cardpayments/CardCallback.kt rename CardPayments/src/main/java/com/paypal/android/cardpayments/{CardResult.kt => LegacyCardResult.kt} (86%) diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/ApproveOrderListener.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/ApproveOrderListener.kt index 8d07590bc..0dad7128b 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/ApproveOrderListener.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/ApproveOrderListener.kt @@ -10,10 +10,10 @@ interface ApproveOrderListener { /** * Called when the order is approved. - * @param result [CardResult] with order information. + * @param result [LegacyCardResult] with order information. */ @MainThread - fun onApproveOrderSuccess(result: CardResult) + fun onApproveOrderSuccess(result: LegacyCardResult) /** * Called when authorization is required to continue. diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardAuthLauncher.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardAuthLauncher.kt index 2b3b8900f..abdba4c67 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardAuthLauncher.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardAuthLauncher.kt @@ -94,7 +94,7 @@ internal class CardAuthLauncher( return if (orderId == null) { CardStatus.ApproveOrderError(CardError.unknownError, null) } else { - val result = CardResult(orderId = orderId, didAttemptThreeDSecureAuthentication = true) + val result = LegacyCardResult(orderId = orderId, didAttemptThreeDSecureAuthentication = true) CardStatus.ApproveOrderSuccess(result) } } diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardCallback.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardCallback.kt new file mode 100644 index 000000000..d345e130f --- /dev/null +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardCallback.kt @@ -0,0 +1,13 @@ +package com.paypal.android.cardpayments + +import androidx.annotation.MainThread + +object CardCallback { + fun interface ApproveOrder { + /** + * Called when the order is approved. + */ + @MainThread + fun onApproveOrderResult() + } +} \ No newline at end of file diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt index 5e1fd2a82..b6305ccea 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt @@ -54,6 +54,10 @@ class CardClient internal constructor( ) // NEXT MAJOR VERSION: Consider renaming approveOrder() to confirmPaymentSource() + fun approveOrder(cardRequest: CardRequest, callback: CardCallback.ApproveOrder) { + + } + /** * Confirm [Card] payment source for an order. * @@ -70,7 +74,7 @@ class CardClient internal constructor( if (response.payerActionHref == null) { analytics.notifyApproveOrderSucceeded(response.orderId) val result = - response.run { CardResult(orderId = orderId, status = status?.name) } + response.run { LegacyCardResult(orderId = orderId, status = status?.name) } approveOrderListener?.onApproveOrderSuccess(result) } else { analytics.notifyApproveOrderAuthChallengeReceived(cardRequest.orderId) diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardStatus.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardStatus.kt index 9c16897ce..934fe0747 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardStatus.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardStatus.kt @@ -5,7 +5,7 @@ import com.paypal.android.corepayments.PayPalSDKError sealed class CardStatus { class ApproveOrderError(val error: PayPalSDKError, val orderId: String?) : CardStatus() - class ApproveOrderSuccess(val result: CardResult) : CardStatus() + class ApproveOrderSuccess(val result: LegacyCardResult) : CardStatus() class ApproveOrderCanceled(val orderId: String?) : CardStatus() class VaultError(val error: PayPalSDKError) : CardStatus() diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/LegacyCardResult.kt similarity index 86% rename from CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt rename to CardPayments/src/main/java/com/paypal/android/cardpayments/LegacyCardResult.kt index 82aceaee1..a26baa3c9 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/LegacyCardResult.kt @@ -10,7 +10,7 @@ import androidx.annotation.RestrictTo * @property [didAttemptThreeDSecureAuthentication] 3DS verification was attempted. * Use v2/checkout/orders/{orderId} in your server to get verification results. */ -data class CardResult @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( +data class LegacyCardResult @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( val orderId: String, val status: String? = null, val didAttemptThreeDSecureAuthentication: Boolean = false diff --git a/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt b/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt index 0cbd87d39..8b9cce812 100644 --- a/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt +++ b/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt @@ -95,7 +95,7 @@ class CardClientUnitTest { sut.approveOrder(cardRequest) advanceUntilIdle() - val resultSlot = slot() + val resultSlot = slot() verify(exactly = 1) { approveOrderListener.onApproveOrderSuccess(capture(resultSlot)) } val actual = resultSlot.captured @@ -219,7 +219,7 @@ class CardClientUnitTest { val sut = createCardClient(testScheduler) sut.approveOrderListener = approveOrderListener - val successResult = CardResult( + val successResult = LegacyCardResult( orderId = "fake-order-id", status = OrderStatus.APPROVED.name, didAttemptThreeDSecureAuthentication = false @@ -230,7 +230,7 @@ class CardClientUnitTest { sut.completeAuthChallenge(intent, "auth state") - val slot = slot() + val slot = slot() verify(exactly = 1) { approveOrderListener.onApproveOrderSuccess(capture(slot)) } assertSame(successResult, slot.captured) } diff --git a/Demo/src/main/java/com/paypal/android/ui/approveorder/ApproveOrderUiState.kt b/Demo/src/main/java/com/paypal/android/ui/approveorder/ApproveOrderUiState.kt index e920d6f1b..e709d875b 100644 --- a/Demo/src/main/java/com/paypal/android/ui/approveorder/ApproveOrderUiState.kt +++ b/Demo/src/main/java/com/paypal/android/ui/approveorder/ApproveOrderUiState.kt @@ -3,7 +3,7 @@ package com.paypal.android.ui.approveorder import androidx.compose.runtime.Immutable import com.paypal.android.api.model.Order import com.paypal.android.api.model.OrderIntent -import com.paypal.android.cardpayments.CardResult +import com.paypal.android.cardpayments.LegacyCardResult import com.paypal.android.cardpayments.threedsecure.SCA import com.paypal.android.uishared.enums.StoreInVaultOption import com.paypal.android.uishared.state.ActionState @@ -11,7 +11,7 @@ import com.paypal.android.uishared.state.ActionState @Immutable data class ApproveOrderUiState( val createOrderState: ActionState = ActionState.Idle, - val approveOrderState: ActionState = ActionState.Idle, + val approveOrderState: ActionState = ActionState.Idle, val completeOrderState: ActionState = ActionState.Idle, val scaOption: SCA = SCA.SCA_ALWAYS, val cardNumber: String = "", 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 fcaef0c19..aea944c89 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 @@ -15,7 +15,7 @@ import com.paypal.android.cardpayments.CardAuthChallenge import com.paypal.android.cardpayments.CardClient import com.paypal.android.cardpayments.CardPresentAuthChallengeResult import com.paypal.android.cardpayments.CardRequest -import com.paypal.android.cardpayments.CardResult +import com.paypal.android.cardpayments.LegacyCardResult import com.paypal.android.cardpayments.threedsecure.SCA import com.paypal.android.corepayments.CoreConfig import com.paypal.android.corepayments.PayPalSDKError @@ -91,7 +91,7 @@ class ApproveOrderViewModel @Inject constructor( cardClient = CardClient(activity, coreConfig) cardClient?.approveOrderListener = object : ApproveOrderListener { - override fun onApproveOrderSuccess(result: CardResult) { + override fun onApproveOrderSuccess(result: LegacyCardResult) { approveOrderState = ActionState.Success(result) } diff --git a/Demo/src/main/java/com/paypal/android/uishared/components/CardResultView.kt b/Demo/src/main/java/com/paypal/android/uishared/components/CardResultView.kt index 1ff679b34..033cf5956 100644 --- a/Demo/src/main/java/com/paypal/android/uishared/components/CardResultView.kt +++ b/Demo/src/main/java/com/paypal/android/uishared/components/CardResultView.kt @@ -8,11 +8,11 @@ import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview -import com.paypal.android.cardpayments.CardResult +import com.paypal.android.cardpayments.LegacyCardResult import com.paypal.android.utils.UIConstants @Composable -fun CardResultView(result: CardResult) { +fun CardResultView(result: LegacyCardResult) { Column( verticalArrangement = UIConstants.spacingMedium, modifier = Modifier @@ -31,7 +31,7 @@ fun CardResultView(result: CardResult) { fun CardResultViewWith3DSAuth() { MaterialTheme { Surface(modifier = Modifier.fillMaxWidth()) { - val result = CardResult( + val result = LegacyCardResult( orderId = "fake-order-id", status = "fake-status", didAttemptThreeDSecureAuthentication = true @@ -46,7 +46,7 @@ fun CardResultViewWith3DSAuth() { fun CardResultViewWithout3DSAuth() { MaterialTheme { Surface(modifier = Modifier.fillMaxWidth()) { - val result = CardResult( + val result = LegacyCardResult( orderId = "fake-order-id", status = "fake-status", didAttemptThreeDSecureAuthentication = false From 1362c126b0ae8f9e82462264c4eeacdaecaea488 Mon Sep 17 00:00:00 2001 From: sshropshire Date: Wed, 4 Dec 2024 15:52:52 -0600 Subject: [PATCH 02/18] Migrate ApproveOrder away from listener pattern. --- .../android/cardpayments/CardCallback.kt | 2 +- .../paypal/android/cardpayments/CardClient.kt | 76 ++++++++------- .../paypal/android/cardpayments/CardResult.kt | 30 ++++++ .../cardpayments/CardClientUnitTest.kt | 14 +-- .../android/corepayments/PayPalSDKError.kt | 2 +- .../ui/approveorder/ApproveOrderUiState.kt | 3 +- .../ui/approveorder/ApproveOrderViewModel.kt | 97 +++++++++++-------- .../android/ui/approveorder/OrderInfo.kt | 7 ++ .../ui/vaultcard/VaultCardViewModel.kt | 2 +- .../uishared/components/CardResultView.kt | 8 +- 10 files changed, 150 insertions(+), 91 deletions(-) create mode 100644 CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt create mode 100644 Demo/src/main/java/com/paypal/android/ui/approveorder/OrderInfo.kt diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardCallback.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardCallback.kt index d345e130f..2e30616d3 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardCallback.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardCallback.kt @@ -8,6 +8,6 @@ object CardCallback { * Called when the order is approved. */ @MainThread - fun onApproveOrderResult() + fun onApproveOrderResult(result: CardResult.ApproveOrder) } } \ No newline at end of file diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt index b6305ccea..5c95e047a 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt @@ -18,7 +18,6 @@ import kotlinx.coroutines.launch /** * Use this client to approve an order with a [Card]. * - * @property approveOrderListener listener to receive callbacks from [CardClient.approveOrder]. * @property cardVaultListener listener to receive callbacks form [CardClient.vault]. */ class CardClient internal constructor( @@ -29,8 +28,6 @@ class CardClient internal constructor( private val dispatcher: CoroutineDispatcher ) { - var approveOrderListener: ApproveOrderListener? = null - // NEXT MAJOR VERSION: rename to vaultListener /** * @suppress @@ -55,15 +52,6 @@ class CardClient internal constructor( // NEXT MAJOR VERSION: Consider renaming approveOrder() to confirmPaymentSource() fun approveOrder(cardRequest: CardRequest, callback: CardCallback.ApproveOrder) { - - } - - /** - * Confirm [Card] payment source for an order. - * - * @param cardRequest [CardRequest] for requesting an order approval - */ - fun approveOrder(cardRequest: CardRequest) { // TODO: deprecate this method and offer auth challenge integration pattern (similar to vault) approveOrderId = cardRequest.orderId analytics.notifyApproveOrderStarted(cardRequest.orderId) @@ -73,22 +61,27 @@ class CardClient internal constructor( is ConfirmPaymentSourceResult.Success -> { if (response.payerActionHref == null) { analytics.notifyApproveOrderSucceeded(response.orderId) - val result = - response.run { LegacyCardResult(orderId = orderId, status = status?.name) } - approveOrderListener?.onApproveOrderSuccess(result) + val result: CardResult.ApproveOrder = response.run { + CardResult.ApproveOrder.Success( + orderId = orderId, + status = status?.name + ) + } + callback.onApproveOrderResult(result) } else { analytics.notifyApproveOrderAuthChallengeReceived(cardRequest.orderId) - approveOrderListener?.onApproveOrderThreeDSecureWillLaunch() val url = Uri.parse(response.payerActionHref) val authChallenge = CardAuthChallenge.ApproveOrder(url, cardRequest) - approveOrderListener?.onApproveOrderAuthorizationRequired(authChallenge) + val result = CardResult.ApproveOrder.AuthorizationRequired(authChallenge) + callback.onApproveOrderResult(result) } } is ConfirmPaymentSourceResult.Failure -> { analytics.notifyApproveOrderFailed(cardRequest.orderId) - approveOrderListener?.onApproveOrderFailure(response.error) + val result = CardResult.ApproveOrder.Failure(response.error) + callback.onApproveOrderResult(result) } } } @@ -177,7 +170,34 @@ class CardClient internal constructor( } } - fun completeAuthChallenge(intent: Intent, authState: String): CardStatus { + fun finishApproveOrder(intent: Intent, authState: String): CardResult.FinishApproveOrder { + return when (val result = authChallengeLauncher.completeAuthRequest(intent, authState)) { + is CardStatus.ApproveOrderSuccess -> { + analytics.notifyApproveOrderAuthChallengeSucceeded(result.result.orderId) + result.result.run { CardResult.FinishApproveOrder.Success(orderId, status) } + } + + is CardStatus.ApproveOrderError -> { + analytics.notifyApproveOrderAuthChallengeFailed(result.orderId) + CardResult.FinishApproveOrder.Failure(result.error) + } + + is CardStatus.ApproveOrderCanceled -> { + analytics.notifyApproveOrderAuthChallengeCanceled(result.orderId) + CardResult.FinishApproveOrder.Canceled + } + + is CardStatus.UnknownError -> { + val description = "An unknown error occurred: ${result.error.message}" + val error = PayPalSDKError(0, description, reason = result.error) + CardResult.FinishApproveOrder.Failure(error) + } + + else -> CardResult.FinishApproveOrder.NoResult + } + } + + fun legacyCompleteAuthChallenge(intent: Intent, authState: String): CardStatus { val status = authChallengeLauncher.completeAuthRequest(intent, authState) when (status) { is CardStatus.VaultSuccess -> { @@ -198,26 +218,11 @@ class CardClient internal constructor( cardVaultListener?.onVaultFailure(PayPalSDKError(1, "User Canceled")) } - is CardStatus.ApproveOrderError -> { - analytics.notifyApproveOrderAuthChallengeFailed(status.orderId) - approveOrderListener?.onApproveOrderFailure(status.error) - } - - is CardStatus.ApproveOrderSuccess -> { - analytics.notifyApproveOrderAuthChallengeSucceeded(status.result.orderId) - approveOrderListener?.onApproveOrderSuccess(status.result) - } - - is CardStatus.ApproveOrderCanceled -> { - analytics.notifyApproveOrderAuthChallengeCanceled(status.orderId) - approveOrderListener?.onApproveOrderCanceled() - } - is CardStatus.UnknownError -> { Log.d("PayPalSDK", "An unknown error occurred: ${status.error.message}") } - CardStatus.NoResult -> { + else -> { // ignore } } @@ -228,7 +233,6 @@ class CardClient internal constructor( * Call this method at the end of the card flow to clear out all observers and listeners */ fun removeObservers() { - approveOrderListener = null cardVaultListener = null } } diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt new file mode 100644 index 000000000..79ead2b33 --- /dev/null +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt @@ -0,0 +1,30 @@ +package com.paypal.android.cardpayments + +import com.paypal.android.corepayments.PayPalSDKError + +object CardResult { + + sealed class ApproveOrder { + + data class Success( + val orderId: String, + val status: String? = null, + val didAttemptThreeDSecureAuthentication: Boolean = false + ) : ApproveOrder() + + data class AuthorizationRequired(val authChallenge: CardAuthChallenge) : ApproveOrder() + data class Failure(val error: PayPalSDKError) : ApproveOrder() + } + + sealed class FinishApproveOrder { + data class Success( + val orderId: String, + val status: String? = null, + val didAttemptThreeDSecureAuthentication: Boolean = false + ) : FinishApproveOrder() + + data class Failure(val error: PayPalSDKError) : FinishApproveOrder() + data object Canceled : FinishApproveOrder() + data object NoResult : FinishApproveOrder() + } +} \ No newline at end of file diff --git a/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt b/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt index 8b9cce812..1b6a6f07e 100644 --- a/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt +++ b/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt @@ -228,7 +228,7 @@ class CardClientUnitTest { cardAuthLauncher.completeAuthRequest(intent, "auth state") } returns CardStatus.ApproveOrderSuccess(successResult) - sut.completeAuthChallenge(intent, "auth state") + sut.legacyCompleteAuthChallenge(intent, "auth state") val slot = slot() verify(exactly = 1) { approveOrderListener.onApproveOrderSuccess(capture(slot)) } @@ -245,7 +245,7 @@ class CardClientUnitTest { cardAuthLauncher.completeAuthRequest(intent, "auth state") } returns CardStatus.ApproveOrderError(error, "fake-order-id") - sut.completeAuthChallenge(intent, "auth state") + sut.legacyCompleteAuthChallenge(intent, "auth state") val slot = slot() verify(exactly = 1) { approveOrderListener.onApproveOrderFailure(capture(slot)) } @@ -261,7 +261,7 @@ class CardClientUnitTest { cardAuthLauncher.completeAuthRequest(intent, "auth state") } returns CardStatus.ApproveOrderCanceled("fake-order-id") - sut.completeAuthChallenge(intent, "auth state") + sut.legacyCompleteAuthChallenge(intent, "auth state") verify(exactly = 1) { approveOrderListener.onApproveOrderCanceled() } } @@ -275,7 +275,7 @@ class CardClientUnitTest { cardAuthLauncher.completeAuthRequest(intent, "auth state") } returns CardStatus.VaultSuccess(successResult) - sut.completeAuthChallenge(intent, "auth state") + sut.legacyCompleteAuthChallenge(intent, "auth state") val slot = slot() verify(exactly = 1) { cardVaultListener.onVaultSuccess(capture(slot)) } @@ -292,7 +292,7 @@ class CardClientUnitTest { cardAuthLauncher.completeAuthRequest(intent, "auth state") } returns CardStatus.VaultError(error) - sut.completeAuthChallenge(intent, "auth state") + sut.legacyCompleteAuthChallenge(intent, "auth state") val slot = slot() verify(exactly = 1) { cardVaultListener.onVaultFailure(capture(slot)) } @@ -308,7 +308,7 @@ class CardClientUnitTest { cardAuthLauncher.completeAuthRequest(intent, "auth state") } returns CardStatus.VaultCanceled("fake-setup-token-id") - sut.completeAuthChallenge(intent, "auth state") + sut.legacyCompleteAuthChallenge(intent, "auth state") // BREAKING CHANGE CALLOUT: if we introduce an "onVaultCanceled()" listener method, it could // break existing merchant integrations verify(exactly = 1) { cardVaultListener.onVaultFailure(any()) } @@ -325,7 +325,7 @@ class CardClientUnitTest { cardAuthLauncher.completeAuthRequest(intent, "auth state") } returns CardStatus.NoResult - sut.completeAuthChallenge(intent, "auth state") + sut.legacyCompleteAuthChallenge(intent, "auth state") verify { sut.approveOrderListener?.wasNot(Called) } verify { sut.cardVaultListener?.wasNot(Called) } } diff --git a/CorePayments/src/main/java/com/paypal/android/corepayments/PayPalSDKError.kt b/CorePayments/src/main/java/com/paypal/android/corepayments/PayPalSDKError.kt index 9bc4bab9d..9cf4121ff 100644 --- a/CorePayments/src/main/java/com/paypal/android/corepayments/PayPalSDKError.kt +++ b/CorePayments/src/main/java/com/paypal/android/corepayments/PayPalSDKError.kt @@ -9,5 +9,5 @@ class PayPalSDKError @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( val code: Int, val errorDescription: String, val correlationId: String? = null, - reason: Exception? = null + reason: Throwable? = null ) : Exception("Error: $code - Description: $errorDescription", reason) diff --git a/Demo/src/main/java/com/paypal/android/ui/approveorder/ApproveOrderUiState.kt b/Demo/src/main/java/com/paypal/android/ui/approveorder/ApproveOrderUiState.kt index e709d875b..8b3ddf3f3 100644 --- a/Demo/src/main/java/com/paypal/android/ui/approveorder/ApproveOrderUiState.kt +++ b/Demo/src/main/java/com/paypal/android/ui/approveorder/ApproveOrderUiState.kt @@ -3,6 +3,7 @@ package com.paypal.android.ui.approveorder import androidx.compose.runtime.Immutable import com.paypal.android.api.model.Order import com.paypal.android.api.model.OrderIntent +import com.paypal.android.cardpayments.CardResult import com.paypal.android.cardpayments.LegacyCardResult import com.paypal.android.cardpayments.threedsecure.SCA import com.paypal.android.uishared.enums.StoreInVaultOption @@ -11,7 +12,7 @@ import com.paypal.android.uishared.state.ActionState @Immutable data class ApproveOrderUiState( val createOrderState: ActionState = ActionState.Idle, - val approveOrderState: ActionState = ActionState.Idle, + val approveOrderState: ActionState = ActionState.Idle, val completeOrderState: ActionState = ActionState.Idle, val scaOption: SCA = SCA.SCA_ALWAYS, val cardNumber: String = "", 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 aea944c89..4cfaf5aed 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 @@ -2,23 +2,20 @@ package com.paypal.android.ui.approveorder import android.content.Context import android.content.Intent -import android.util.Log import androidx.activity.ComponentActivity import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.paypal.android.api.model.Order import com.paypal.android.api.model.OrderIntent import com.paypal.android.api.services.SDKSampleServerResult -import com.paypal.android.cardpayments.ApproveOrderListener import com.paypal.android.cardpayments.Card import com.paypal.android.cardpayments.CardAuthChallenge import com.paypal.android.cardpayments.CardClient import com.paypal.android.cardpayments.CardPresentAuthChallengeResult import com.paypal.android.cardpayments.CardRequest -import com.paypal.android.cardpayments.LegacyCardResult +import com.paypal.android.cardpayments.CardResult import com.paypal.android.cardpayments.threedsecure.SCA import com.paypal.android.corepayments.CoreConfig -import com.paypal.android.corepayments.PayPalSDKError import com.paypal.android.fraudprotection.PayPalDataCollector import com.paypal.android.fraudprotection.PayPalDataCollectorRequest import com.paypal.android.models.OrderRequest @@ -76,6 +73,24 @@ class ApproveOrderViewModel @Inject constructor( } } + private fun presentAuthChallenge( + activity: ComponentActivity, + authChallenge: CardAuthChallenge + ) { + cardClient?.presentAuthChallenge(activity, authChallenge)?.let { presentAuthResult -> + when (presentAuthResult) { + is CardPresentAuthChallengeResult.Success -> { + authState = presentAuthResult.authState + } + + is CardPresentAuthChallengeResult.Failure -> { + approveOrderState = + ActionState.Failure(presentAuthResult.error) + } + } + } + } + private suspend fun approveOrderWithId(activity: ComponentActivity, orderId: String) { approveOrderState = ActionState.Loading @@ -89,47 +104,26 @@ class ApproveOrderViewModel @Inject constructor( val coreConfig = CoreConfig(clientId = clientId) payPalDataCollector = PayPalDataCollector(coreConfig) + val cardRequest = mapUIStateToCardRequestWithOrderId(orderId) cardClient = CardClient(activity, coreConfig) - cardClient?.approveOrderListener = object : ApproveOrderListener { - override fun onApproveOrderSuccess(result: LegacyCardResult) { - approveOrderState = ActionState.Success(result) - } - - override fun onApproveOrderAuthorizationRequired(authChallenge: CardAuthChallenge) { - cardClient?.presentAuthChallenge(activity, authChallenge) - ?.let { presentAuthResult -> - when (presentAuthResult) { - is CardPresentAuthChallengeResult.Success -> { - authState = presentAuthResult.authState - } - - is CardPresentAuthChallengeResult.Failure -> { - approveOrderState = - ActionState.Failure(presentAuthResult.error) - } - } + cardClient?.approveOrder(cardRequest) { result -> + when (result) { + is CardResult.ApproveOrder.Success -> { + val orderInfo = result.run { + OrderInfo(orderId, status, didAttemptThreeDSecureAuthentication) } - } + approveOrderState = ActionState.Success(orderInfo) + } - override fun onApproveOrderFailure(error: PayPalSDKError) { - approveOrderState = ActionState.Failure(error) - } + is CardResult.ApproveOrder.AuthorizationRequired -> { + presentAuthChallenge(activity, result.authChallenge) + } - override fun onApproveOrderCanceled() { - approveOrderState = ActionState.Failure(Exception("USER CANCELED")) - } - - override fun onApproveOrderThreeDSecureWillLaunch() { - Log.d(TAG, "3DS Auth Requested") - } - - override fun onApproveOrderThreeDSecureDidFinish() { - Log.d(TAG, "3DS Success") + is CardResult.ApproveOrder.Failure -> { + approveOrderState = ActionState.Failure(result.error) + } } } - - val cardRequest = mapUIStateToCardRequestWithOrderId(orderId) - cardClient?.approveOrder(cardRequest) } } } @@ -236,6 +230,29 @@ class ApproveOrderViewModel @Inject constructor( } fun completeAuthChallenge(intent: Intent) { - authState?.let { cardClient?.completeAuthChallenge(intent, it) } + authState?.let { cardClient?.finishApproveOrder(intent, it) }?.let { result -> + when (result) { + is CardResult.FinishApproveOrder.Success -> { + val orderInfo = result.run { + OrderInfo(orderId, status, didAttemptThreeDSecureAuthentication) + } + approveOrderState = ActionState.Success(orderInfo) + } + + is CardResult.FinishApproveOrder.Failure -> { + approveOrderState = ActionState.Failure(result.error) + } + + CardResult.FinishApproveOrder.Canceled -> { + approveOrderState = ActionState.Failure(Exception("USER CANCELED")) + } + + CardResult.FinishApproveOrder.NoResult -> { + // ignore + } + } + } + + authState?.let { cardClient?.legacyCompleteAuthChallenge(intent, it) } } } diff --git a/Demo/src/main/java/com/paypal/android/ui/approveorder/OrderInfo.kt b/Demo/src/main/java/com/paypal/android/ui/approveorder/OrderInfo.kt new file mode 100644 index 000000000..4d98eb6c5 --- /dev/null +++ b/Demo/src/main/java/com/paypal/android/ui/approveorder/OrderInfo.kt @@ -0,0 +1,7 @@ +package com.paypal.android.ui.approveorder + +data class OrderInfo( + val orderId: String, + val status: String?, + val didAttemptThreeDSecureAuthentication: Boolean +) 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 cb9c1092c..d587cf906 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 @@ -193,6 +193,6 @@ class VaultCardViewModel @Inject constructor( } fun completeAuthChallenge(intent: Intent) { - authState?.let { cardClient?.completeAuthChallenge(intent, it) } + authState?.let { cardClient?.legacyCompleteAuthChallenge(intent, it) } } } diff --git a/Demo/src/main/java/com/paypal/android/uishared/components/CardResultView.kt b/Demo/src/main/java/com/paypal/android/uishared/components/CardResultView.kt index 033cf5956..94c78286b 100644 --- a/Demo/src/main/java/com/paypal/android/uishared/components/CardResultView.kt +++ b/Demo/src/main/java/com/paypal/android/uishared/components/CardResultView.kt @@ -8,11 +8,11 @@ import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview -import com.paypal.android.cardpayments.LegacyCardResult +import com.paypal.android.ui.approveorder.OrderInfo import com.paypal.android.utils.UIConstants @Composable -fun CardResultView(result: LegacyCardResult) { +fun CardResultView(result: OrderInfo) { Column( verticalArrangement = UIConstants.spacingMedium, modifier = Modifier @@ -31,7 +31,7 @@ fun CardResultView(result: LegacyCardResult) { fun CardResultViewWith3DSAuth() { MaterialTheme { Surface(modifier = Modifier.fillMaxWidth()) { - val result = LegacyCardResult( + val result = OrderInfo( orderId = "fake-order-id", status = "fake-status", didAttemptThreeDSecureAuthentication = true @@ -46,7 +46,7 @@ fun CardResultViewWith3DSAuth() { fun CardResultViewWithout3DSAuth() { MaterialTheme { Surface(modifier = Modifier.fillMaxWidth()) { - val result = LegacyCardResult( + val result = OrderInfo( orderId = "fake-order-id", status = "fake-status", didAttemptThreeDSecureAuthentication = false From e396f7441af39dfe91d17f431368e6ef2b1ee06e Mon Sep 17 00:00:00 2001 From: sshropshire Date: Thu, 5 Dec 2024 14:09:07 -0600 Subject: [PATCH 03/18] Remove ApproveOrderListener. --- .../cardpayments/ApproveOrderListener.kt | 48 ------------------- .../android/cardpayments/CardAuthLauncher.kt | 44 ++++++++++++----- .../paypal/android/cardpayments/CardClient.kt | 30 +++++------- .../paypal/android/cardpayments/CardStatus.kt | 4 -- .../android/cardpayments/LegacyCardResult.kt | 17 ------- .../ui/approveorder/ApproveOrderUiState.kt | 2 - .../ui/approveorder/ApproveOrderViewModel.kt | 11 +++-- 7 files changed, 49 insertions(+), 107 deletions(-) delete mode 100644 CardPayments/src/main/java/com/paypal/android/cardpayments/ApproveOrderListener.kt delete mode 100644 CardPayments/src/main/java/com/paypal/android/cardpayments/LegacyCardResult.kt diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/ApproveOrderListener.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/ApproveOrderListener.kt deleted file mode 100644 index 0dad7128b..000000000 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/ApproveOrderListener.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.paypal.android.cardpayments - -import androidx.annotation.MainThread -import com.paypal.android.corepayments.PayPalSDKError - -/** - * Implement this callback to receive results from [CardClient]. - */ -interface ApproveOrderListener { - - /** - * Called when the order is approved. - * @param result [LegacyCardResult] with order information. - */ - @MainThread - fun onApproveOrderSuccess(result: LegacyCardResult) - - /** - * Called when authorization is required to continue. - */ - @MainThread - fun onApproveOrderAuthorizationRequired(authChallenge: CardAuthChallenge) - - /** - * Called when the approval fails. - * @param error [PayPalSDKError] explaining the reason for failure. - */ - @MainThread - fun onApproveOrderFailure(error: PayPalSDKError) - - /** - * Called when user cancels the flow. - */ - @MainThread - fun onApproveOrderCanceled() - - /** - * Called when the 3DS challenge will launch. - */ - @MainThread - fun onApproveOrderThreeDSecureWillLaunch() - - /** - * Called when the 3DS challenge has finished. - */ - @MainThread - fun onApproveOrderThreeDSecureDidFinish() -} diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardAuthLauncher.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardAuthLauncher.kt index abdba4c67..e30705b5f 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardAuthLauncher.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardAuthLauncher.kt @@ -7,6 +7,7 @@ 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.PayPalSDKError import org.json.JSONObject internal class CardAuthLauncher( @@ -60,6 +61,36 @@ internal class CardAuthLauncher( } } + fun completeApproveOrderAuthRequest( + intent: Intent, + authState: String + ): CardResult.FinishApproveOrder = + when (val finalResult = browserSwitchClient.completeRequest(intent, authState)) { + is BrowserSwitchFinalResult.Success -> { + if (finalResult.requestCode == BrowserSwitchRequestCodes.CARD_APPROVE_ORDER) { + val orderId = finalResult.requestMetadata?.optString(METADATA_KEY_ORDER_ID) + if (orderId == null) { + CardResult.FinishApproveOrder.Failure(CardError.unknownError) + } else { + CardResult.FinishApproveOrder.Success( + orderId = orderId, + didAttemptThreeDSecureAuthentication = true + ) + } + } else { + CardResult.FinishApproveOrder.NoResult + } + } + + is BrowserSwitchFinalResult.Failure -> { + val message = "Browser switch failed" + val browserSwitchError = PayPalSDKError(0, message, reason = finalResult.error) + CardResult.FinishApproveOrder.Failure(browserSwitchError) + } + + BrowserSwitchFinalResult.NoResult -> CardResult.FinishApproveOrder.NoResult + } + fun completeAuthRequest(intent: Intent, authState: String): CardStatus = when (val finalResult = browserSwitchClient.completeRequest(intent, authState)) { is BrowserSwitchFinalResult.Success -> parseBrowserSwitchSuccessResult(finalResult) @@ -69,7 +100,6 @@ internal class CardAuthLauncher( private fun parseBrowserSwitchSuccessResult(result: BrowserSwitchFinalResult.Success): CardStatus = when (result.requestCode) { - BrowserSwitchRequestCodes.CARD_APPROVE_ORDER -> parseApproveOrderSuccessResult(result) BrowserSwitchRequestCodes.CARD_VAULT -> parseVaultSuccessResult(result) else -> CardStatus.NoResult } @@ -86,16 +116,4 @@ internal class CardAuthLauncher( CardStatus.VaultSuccess(result) } } - - private fun parseApproveOrderSuccessResult( - finalResult: BrowserSwitchFinalResult.Success, - ): CardStatus { - val orderId = finalResult.requestMetadata?.optString(METADATA_KEY_ORDER_ID) - return if (orderId == null) { - CardStatus.ApproveOrderError(CardError.unknownError, null) - } else { - val result = LegacyCardResult(orderId = orderId, didAttemptThreeDSecureAuthentication = true) - CardStatus.ApproveOrderSuccess(result) - } - } } diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt index 5c95e047a..16ef5f152 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt @@ -171,30 +171,22 @@ class CardClient internal constructor( } fun finishApproveOrder(intent: Intent, authState: String): CardResult.FinishApproveOrder { - return when (val result = authChallengeLauncher.completeAuthRequest(intent, authState)) { - is CardStatus.ApproveOrderSuccess -> { - analytics.notifyApproveOrderAuthChallengeSucceeded(result.result.orderId) - result.result.run { CardResult.FinishApproveOrder.Success(orderId, status) } - } + val result = authChallengeLauncher.completeApproveOrderAuthRequest(intent, authState) + when (result) { + is CardResult.FinishApproveOrder.Success -> + analytics.notifyApproveOrderAuthChallengeSucceeded(result.orderId) - is CardStatus.ApproveOrderError -> { - analytics.notifyApproveOrderAuthChallengeFailed(result.orderId) - CardResult.FinishApproveOrder.Failure(result.error) - } + is CardResult.FinishApproveOrder.Failure -> + analytics.notifyApproveOrderAuthChallengeFailed(null) - is CardStatus.ApproveOrderCanceled -> { - analytics.notifyApproveOrderAuthChallengeCanceled(result.orderId) - CardResult.FinishApproveOrder.Canceled - } + CardResult.FinishApproveOrder.Canceled -> + analytics.notifyApproveOrderAuthChallengeCanceled(null) - is CardStatus.UnknownError -> { - val description = "An unknown error occurred: ${result.error.message}" - val error = PayPalSDKError(0, description, reason = result.error) - CardResult.FinishApproveOrder.Failure(error) + else -> { + // no analytics tracking required at the moment } - - else -> CardResult.FinishApproveOrder.NoResult } + return result } fun legacyCompleteAuthChallenge(intent: Intent, authState: String): CardStatus { diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardStatus.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardStatus.kt index 934fe0747..6ab8dc1d2 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardStatus.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardStatus.kt @@ -4,10 +4,6 @@ import com.paypal.android.corepayments.PayPalSDKError sealed class CardStatus { - class ApproveOrderError(val error: PayPalSDKError, val orderId: String?) : CardStatus() - class ApproveOrderSuccess(val result: LegacyCardResult) : CardStatus() - class ApproveOrderCanceled(val orderId: String?) : CardStatus() - class VaultError(val error: PayPalSDKError) : CardStatus() class VaultSuccess(val result: CardVaultResult) : CardStatus() class VaultCanceled(val setupTokenId: String?) : CardStatus() diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/LegacyCardResult.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/LegacyCardResult.kt deleted file mode 100644 index a26baa3c9..000000000 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/LegacyCardResult.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.paypal.android.cardpayments - -import androidx.annotation.RestrictTo - -/** - * A result returned by [CardClient] when an order was successfully approved with a [Card]. - * - * @property [orderId] associated order ID. - * @property [status] status of the order - * @property [didAttemptThreeDSecureAuthentication] 3DS verification was attempted. - * Use v2/checkout/orders/{orderId} in your server to get verification results. - */ -data class LegacyCardResult @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( - val orderId: String, - val status: String? = null, - val didAttemptThreeDSecureAuthentication: Boolean = false -) diff --git a/Demo/src/main/java/com/paypal/android/ui/approveorder/ApproveOrderUiState.kt b/Demo/src/main/java/com/paypal/android/ui/approveorder/ApproveOrderUiState.kt index 8b3ddf3f3..85feeea2c 100644 --- a/Demo/src/main/java/com/paypal/android/ui/approveorder/ApproveOrderUiState.kt +++ b/Demo/src/main/java/com/paypal/android/ui/approveorder/ApproveOrderUiState.kt @@ -3,8 +3,6 @@ package com.paypal.android.ui.approveorder import androidx.compose.runtime.Immutable import com.paypal.android.api.model.Order import com.paypal.android.api.model.OrderIntent -import com.paypal.android.cardpayments.CardResult -import com.paypal.android.cardpayments.LegacyCardResult import com.paypal.android.cardpayments.threedsecure.SCA import com.paypal.android.uishared.enums.StoreInVaultOption import com.paypal.android.uishared.state.ActionState 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 4cfaf5aed..e8ab8c8ea 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 @@ -229,18 +229,21 @@ class ApproveOrderViewModel @Inject constructor( cardClient?.removeObservers() } + private fun checkIfApproveOrderFinished(intent: Intent): CardResult.FinishApproveOrder? + = authState?.let { cardClient?.finishApproveOrder(intent, it) } + fun completeAuthChallenge(intent: Intent) { - authState?.let { cardClient?.finishApproveOrder(intent, it) }?.let { result -> - when (result) { + checkIfApproveOrderFinished(intent)?.let { approveOrderResult -> + when (approveOrderResult) { is CardResult.FinishApproveOrder.Success -> { - val orderInfo = result.run { + val orderInfo = approveOrderResult.run { OrderInfo(orderId, status, didAttemptThreeDSecureAuthentication) } approveOrderState = ActionState.Success(orderInfo) } is CardResult.FinishApproveOrder.Failure -> { - approveOrderState = ActionState.Failure(result.error) + approveOrderState = ActionState.Failure(approveOrderResult.error) } CardResult.FinishApproveOrder.Canceled -> { From 0cb7358afd0d08b663d627ce530508b9aaad23f1 Mon Sep 17 00:00:00 2001 From: sshropshire Date: Fri, 6 Dec 2024 11:06:50 -0600 Subject: [PATCH 04/18] Fix unit test compilation errors. --- .../cardpayments/CardAuthLauncherUnitTest.kt | 13 ++- .../cardpayments/CardClientUnitTest.kt | 85 ++++++++----------- 2 files changed, 42 insertions(+), 56 deletions(-) diff --git a/CardPayments/src/test/java/com/paypal/android/cardpayments/CardAuthLauncherUnitTest.kt b/CardPayments/src/test/java/com/paypal/android/cardpayments/CardAuthLauncherUnitTest.kt index ffe827b3e..6ca09df1a 100644 --- a/CardPayments/src/test/java/com/paypal/android/cardpayments/CardAuthLauncherUnitTest.kt +++ b/CardPayments/src/test/java/com/paypal/android/cardpayments/CardAuthLauncherUnitTest.kt @@ -147,7 +147,7 @@ class CardAuthLauncherUnitTest { } @Test - fun `completeAuthRequest() returns approve order success`() { + fun `completeApproveOrderAuthRequest() returns approve order success`() { sut = CardAuthLauncher(browserSwitchClient) val scheme = "com.paypal.android.demo" @@ -164,13 +164,12 @@ class CardAuthLauncherUnitTest { browserSwitchClient.completeRequest(intent, "pending request") } returns finalResult - val status = sut.completeAuthRequest(intent, "pending request") - as CardStatus.ApproveOrderSuccess + val result = sut.completeApproveOrderAuthRequest(intent, "pending request") + as CardResult.FinishApproveOrder.Success - val cardResult = status.result - assertEquals("fake-order-id", cardResult.orderId) - assertTrue(cardResult.didAttemptThreeDSecureAuthentication) - assertNull(cardResult.status) + assertEquals("fake-order-id", result.orderId) + assertTrue(result.didAttemptThreeDSecureAuthentication) + assertNull(result.status) } @Test diff --git a/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt b/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt index 1b6a6f07e..313542fd4 100644 --- a/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt +++ b/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt @@ -63,7 +63,7 @@ class CardClientUnitTest { private val activity = mockk(relaxed = true) - private val approveOrderListener = mockk(relaxed = true) + private val approveOrderCallback = mockk() private val cardVaultListener = mockk(relaxed = true) private val intent = Intent() @@ -92,16 +92,16 @@ class CardClientUnitTest { coEvery { checkoutOrdersAPI.confirmPaymentSource(cardRequest) } returns confirmPaymentSourceResult - sut.approveOrder(cardRequest) + sut.approveOrder(cardRequest, approveOrderCallback) advanceUntilIdle() - val resultSlot = slot() - verify(exactly = 1) { approveOrderListener.onApproveOrderSuccess(capture(resultSlot)) } + val resultSlot = slot() + verify(exactly = 1) { approveOrderCallback.onApproveOrderResult(capture(resultSlot)) } - val actual = resultSlot.captured - assertEquals("sample-order-id", actual.orderId) - assertEquals(OrderStatus.APPROVED.name, actual.status) - assertFalse(actual.didAttemptThreeDSecureAuthentication) + val result = resultSlot.captured as CardResult.ApproveOrder.Success + assertEquals("sample-order-id", result.orderId) + assertEquals(OrderStatus.APPROVED.name, result.status) + assertFalse(result.didAttemptThreeDSecureAuthentication) } @Test @@ -113,14 +113,14 @@ class CardClientUnitTest { checkoutOrdersAPI.confirmPaymentSource(cardRequest) } returns ConfirmPaymentSourceResult.Failure(error) - sut.approveOrder(cardRequest) + sut.approveOrder(cardRequest, approveOrderCallback) advanceUntilIdle() - val errorSlot = slot() - verify(exactly = 1) { approveOrderListener.onApproveOrderFailure(capture(errorSlot)) } + val resultSlot = slot() + verify(exactly = 1) { approveOrderCallback.onApproveOrderResult(capture(resultSlot)) } - val capturedError = errorSlot.captured - assertEquals("mock_error_message", capturedError.errorDescription) + val result = resultSlot.captured as CardResult.ApproveOrder.Failure + assertEquals("mock_error_message", result.error.errorDescription) } @Test @@ -131,13 +131,15 @@ class CardClientUnitTest { coEvery { checkoutOrdersAPI.confirmPaymentSource(cardRequest) } returns threeDSecureAuthChallengeResponse val sut = createCardClient(testScheduler) - sut.approveOrder(cardRequest) + sut.approveOrder(cardRequest, approveOrderCallback) advanceUntilIdle() - val authChallengeSlot = slot() - verify { approveOrderListener.onApproveOrderAuthorizationRequired(capture(authChallengeSlot)) } + val resultSlot = slot() + verify(exactly = 1) { approveOrderCallback.onApproveOrderResult(capture(resultSlot)) } + + val result = resultSlot.captured as CardResult.ApproveOrder.AuthorizationRequired + val authChallenge = result.authChallenge as CardAuthChallenge.ApproveOrder - val authChallenge = authChallengeSlot.captured as CardAuthChallenge.ApproveOrder assertEquals(Uri.parse("/payer/action/href"), authChallenge.url) assertEquals(cardRequest, authChallenge.request) assertEquals("merchant.app", authChallenge.returnUrlScheme) @@ -215,54 +217,47 @@ class CardClientUnitTest { } @Test - fun `completeAuthChallenge() notifies merchant of approve order success`() = runTest { + fun `completeApproveOrderAuthRequest() notifies merchant of approve order success`() = runTest { val sut = createCardClient(testScheduler) - sut.approveOrderListener = approveOrderListener - val successResult = LegacyCardResult( + val successResult = CardResult.FinishApproveOrder.Success( orderId = "fake-order-id", status = OrderStatus.APPROVED.name, didAttemptThreeDSecureAuthentication = false ) every { - cardAuthLauncher.completeAuthRequest(intent, "auth state") - } returns CardStatus.ApproveOrderSuccess(successResult) - - sut.legacyCompleteAuthChallenge(intent, "auth state") + cardAuthLauncher.completeApproveOrderAuthRequest(intent, "auth state") + } returns successResult - val slot = slot() - verify(exactly = 1) { approveOrderListener.onApproveOrderSuccess(capture(slot)) } - assertSame(successResult, slot.captured) + val actual = sut.finishApproveOrder(intent, "auth state") + assertSame(successResult, actual) } @Test - fun `completeAuthChallenge() notifies merchant of approve order failure`() = runTest { + fun `completeApproveOrderAuthRequest() notifies merchant of approve order failure`() = runTest { val sut = createCardClient(testScheduler) - sut.approveOrderListener = approveOrderListener val error = PayPalSDKError(123, "fake-error-description") + val failureResult = CardResult.FinishApproveOrder.Failure(error) every { - cardAuthLauncher.completeAuthRequest(intent, "auth state") - } returns CardStatus.ApproveOrderError(error, "fake-order-id") + cardAuthLauncher.completeApproveOrderAuthRequest(intent, "auth state") + } returns failureResult - sut.legacyCompleteAuthChallenge(intent, "auth state") - - val slot = slot() - verify(exactly = 1) { approveOrderListener.onApproveOrderFailure(capture(slot)) } - assertSame(error, slot.captured) + val actual = sut.finishApproveOrder(intent, "auth state") + assertSame(failureResult, actual) } @Test fun `completeAuthChallenge() notifies merchant of approve order cancelation`() = runTest { val sut = createCardClient(testScheduler) - sut.approveOrderListener = approveOrderListener + val canceledResult = CardResult.FinishApproveOrder.Canceled every { - cardAuthLauncher.completeAuthRequest(intent, "auth state") - } returns CardStatus.ApproveOrderCanceled("fake-order-id") + cardAuthLauncher.completeApproveOrderAuthRequest(intent, "auth state") + } returns canceledResult - sut.legacyCompleteAuthChallenge(intent, "auth state") - verify(exactly = 1) { approveOrderListener.onApproveOrderCanceled() } + val result = sut.finishApproveOrder(intent, "auth state") + assertSame(canceledResult, result) } @Test @@ -318,7 +313,6 @@ class CardClientUnitTest { fun `completeAuthChallenge() doesn't deliver result when browserSwitchResult is null`() = runTest { val sut = createCardClient(testScheduler) - sut.approveOrderListener = approveOrderListener sut.cardVaultListener = cardVaultListener every { @@ -326,7 +320,6 @@ class CardClientUnitTest { } returns CardStatus.NoResult sut.legacyCompleteAuthChallenge(intent, "auth state") - verify { sut.approveOrderListener?.wasNot(Called) } verify { sut.cardVaultListener?.wasNot(Called) } } @@ -334,7 +327,6 @@ class CardClientUnitTest { fun `presentAuthChallenge() presents an approve order auth challenge using auth launcher`() = runTest { val sut = createCardClient(testScheduler) - sut.approveOrderListener = approveOrderListener val url = Uri.parse("https://fake.com/url") val authChallenge = CardAuthChallenge.ApproveOrder(url, cardRequest) @@ -344,14 +336,12 @@ class CardClientUnitTest { sut.presentAuthChallenge(activity, authChallenge) verify { cardAuthLauncher.presentAuthChallenge(activity, authChallenge) } - verify(exactly = 0) { approveOrderListener.onApproveOrderFailure(any()) } } @Test fun `presentAuthChallenge() forwards approve order auth challenge presentation result to caller`() = runTest { val sut = createCardClient(testScheduler) - sut.approveOrderListener = approveOrderListener val url = Uri.parse("https://fake.com/url") val authChallenge = CardAuthChallenge.ApproveOrder(url, cardRequest) @@ -410,7 +400,6 @@ class CardClientUnitTest { cardAuthLauncher, dispatcher ) - sut.approveOrderListener = approveOrderListener sut.cardVaultListener = cardVaultListener return sut } @@ -423,8 +412,6 @@ class CardClientUnitTest { every { activity.lifecycle } returns lifeCycle sut.removeObservers() - - expectThat(sut.approveOrderListener).isNull() expectThat(sut.cardVaultListener).isNull() } } From 3b321916b85c018c2c305db9924b72f7a448ae6f Mon Sep 17 00:00:00 2001 From: sshropshire Date: Fri, 6 Dec 2024 11:08:04 -0600 Subject: [PATCH 05/18] Fix lint errors. --- .../main/java/com/paypal/android/cardpayments/CardCallback.kt | 2 +- .../main/java/com/paypal/android/cardpayments/CardResult.kt | 2 +- .../paypal/android/ui/approveorder/ApproveOrderViewModel.kt | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardCallback.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardCallback.kt index 2e30616d3..a3dd91891 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardCallback.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardCallback.kt @@ -10,4 +10,4 @@ object CardCallback { @MainThread fun onApproveOrderResult(result: CardResult.ApproveOrder) } -} \ No newline at end of file +} diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt index 79ead2b33..dbbc0e6ff 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt @@ -27,4 +27,4 @@ object CardResult { data object Canceled : FinishApproveOrder() data object NoResult : FinishApproveOrder() } -} \ No newline at end of file +} 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 e8ab8c8ea..7914d5b16 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 @@ -229,8 +229,8 @@ class ApproveOrderViewModel @Inject constructor( cardClient?.removeObservers() } - private fun checkIfApproveOrderFinished(intent: Intent): CardResult.FinishApproveOrder? - = authState?.let { cardClient?.finishApproveOrder(intent, it) } + private fun checkIfApproveOrderFinished(intent: Intent): CardResult.FinishApproveOrder? = + authState?.let { cardClient?.finishApproveOrder(intent, it) } fun completeAuthChallenge(intent: Intent) { checkIfApproveOrderFinished(intent)?.let { approveOrderResult -> From b141c345d52122e2657800f300abe4b0cd49796d Mon Sep 17 00:00:00 2001 From: sshropshire Date: Fri, 6 Dec 2024 11:18:51 -0600 Subject: [PATCH 06/18] Refactor CardAuthLauncher parsing. --- .../android/cardpayments/CardAuthLauncher.kt | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardAuthLauncher.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardAuthLauncher.kt index e30705b5f..185692470 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardAuthLauncher.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardAuthLauncher.kt @@ -66,21 +66,7 @@ internal class CardAuthLauncher( authState: String ): CardResult.FinishApproveOrder = when (val finalResult = browserSwitchClient.completeRequest(intent, authState)) { - is BrowserSwitchFinalResult.Success -> { - if (finalResult.requestCode == BrowserSwitchRequestCodes.CARD_APPROVE_ORDER) { - val orderId = finalResult.requestMetadata?.optString(METADATA_KEY_ORDER_ID) - if (orderId == null) { - CardResult.FinishApproveOrder.Failure(CardError.unknownError) - } else { - CardResult.FinishApproveOrder.Success( - orderId = orderId, - didAttemptThreeDSecureAuthentication = true - ) - } - } else { - CardResult.FinishApproveOrder.NoResult - } - } + is BrowserSwitchFinalResult.Success -> parseApproveOrderSuccessResult(finalResult) is BrowserSwitchFinalResult.Failure -> { val message = "Browser switch failed" @@ -116,4 +102,21 @@ internal class CardAuthLauncher( CardStatus.VaultSuccess(result) } } + + private fun parseApproveOrderSuccessResult( + finalResult: BrowserSwitchFinalResult.Success + ): CardResult.FinishApproveOrder = + if (finalResult.requestCode == BrowserSwitchRequestCodes.CARD_APPROVE_ORDER) { + val orderId = finalResult.requestMetadata?.optString(METADATA_KEY_ORDER_ID) + if (orderId == null) { + CardResult.FinishApproveOrder.Failure(CardError.unknownError) + } else { + CardResult.FinishApproveOrder.Success( + orderId = orderId, + didAttemptThreeDSecureAuthentication = true + ) + } + } else { + CardResult.FinishApproveOrder.NoResult + } } From 9beadea33de1b54c90655b2417536dd029340a3d Mon Sep 17 00:00:00 2001 From: sshropshire Date: Fri, 6 Dec 2024 11:24:30 -0600 Subject: [PATCH 07/18] Clean up code. --- .../java/com/paypal/android/cardpayments/CardClient.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt index 16ef5f152..053ab9c82 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt @@ -61,11 +61,8 @@ class CardClient internal constructor( is ConfirmPaymentSourceResult.Success -> { if (response.payerActionHref == null) { analytics.notifyApproveOrderSucceeded(response.orderId) - val result: CardResult.ApproveOrder = response.run { - CardResult.ApproveOrder.Success( - orderId = orderId, - status = status?.name - ) + val result = response.run { + CardResult.ApproveOrder.Success(orderId, status = status?.name) } callback.onApproveOrderResult(result) } else { From d8398ce42c075fec01d29cd2f445e4ba8e6a1305 Mon Sep 17 00:00:00 2001 From: sshropshire Date: Fri, 6 Dec 2024 11:25:45 -0600 Subject: [PATCH 08/18] Keep CardClient formatting consistent. --- .../main/java/com/paypal/android/cardpayments/CardClient.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt index 053ab9c82..a8eaf2f0a 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt @@ -62,7 +62,10 @@ class CardClient internal constructor( if (response.payerActionHref == null) { analytics.notifyApproveOrderSucceeded(response.orderId) val result = response.run { - CardResult.ApproveOrder.Success(orderId, status = status?.name) + CardResult.ApproveOrder.Success( + orderId = orderId, + status = status?.name + ) } callback.onApproveOrderResult(result) } else { From 3e543ab88a6977f4cd2910301111604b1ae7e041 Mon Sep 17 00:00:00 2001 From: sshropshire Date: Fri, 6 Dec 2024 11:28:13 -0600 Subject: [PATCH 09/18] Clean up legacy named methods. --- .../paypal/android/cardpayments/CardClient.kt | 2 +- .../cardpayments/CardClientUnitTest.kt | 8 ++-- .../ui/approveorder/ApproveOrderViewModel.kt | 37 +++++++++---------- .../ui/vaultcard/VaultCardViewModel.kt | 2 +- 4 files changed, 24 insertions(+), 25 deletions(-) diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt index a8eaf2f0a..e19b6da53 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt @@ -189,7 +189,7 @@ class CardClient internal constructor( return result } - fun legacyCompleteAuthChallenge(intent: Intent, authState: String): CardStatus { + fun completeAuthChallenge(intent: Intent, authState: String): CardStatus { val status = authChallengeLauncher.completeAuthRequest(intent, authState) when (status) { is CardStatus.VaultSuccess -> { diff --git a/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt b/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt index 313542fd4..44a0d2a59 100644 --- a/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt +++ b/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt @@ -270,7 +270,7 @@ class CardClientUnitTest { cardAuthLauncher.completeAuthRequest(intent, "auth state") } returns CardStatus.VaultSuccess(successResult) - sut.legacyCompleteAuthChallenge(intent, "auth state") + sut.completeAuthChallenge(intent, "auth state") val slot = slot() verify(exactly = 1) { cardVaultListener.onVaultSuccess(capture(slot)) } @@ -287,7 +287,7 @@ class CardClientUnitTest { cardAuthLauncher.completeAuthRequest(intent, "auth state") } returns CardStatus.VaultError(error) - sut.legacyCompleteAuthChallenge(intent, "auth state") + sut.completeAuthChallenge(intent, "auth state") val slot = slot() verify(exactly = 1) { cardVaultListener.onVaultFailure(capture(slot)) } @@ -303,7 +303,7 @@ class CardClientUnitTest { cardAuthLauncher.completeAuthRequest(intent, "auth state") } returns CardStatus.VaultCanceled("fake-setup-token-id") - sut.legacyCompleteAuthChallenge(intent, "auth state") + sut.completeAuthChallenge(intent, "auth state") // BREAKING CHANGE CALLOUT: if we introduce an "onVaultCanceled()" listener method, it could // break existing merchant integrations verify(exactly = 1) { cardVaultListener.onVaultFailure(any()) } @@ -319,7 +319,7 @@ class CardClientUnitTest { cardAuthLauncher.completeAuthRequest(intent, "auth state") } returns CardStatus.NoResult - sut.legacyCompleteAuthChallenge(intent, "auth state") + sut.completeAuthChallenge(intent, "auth state") verify { sut.cardVaultListener?.wasNot(Called) } } 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 7914d5b16..fa687fab8 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 @@ -73,24 +73,6 @@ class ApproveOrderViewModel @Inject constructor( } } - private fun presentAuthChallenge( - activity: ComponentActivity, - authChallenge: CardAuthChallenge - ) { - cardClient?.presentAuthChallenge(activity, authChallenge)?.let { presentAuthResult -> - when (presentAuthResult) { - is CardPresentAuthChallengeResult.Success -> { - authState = presentAuthResult.authState - } - - is CardPresentAuthChallengeResult.Failure -> { - approveOrderState = - ActionState.Failure(presentAuthResult.error) - } - } - } - } - private suspend fun approveOrderWithId(activity: ComponentActivity, orderId: String) { approveOrderState = ActionState.Loading @@ -128,6 +110,23 @@ class ApproveOrderViewModel @Inject constructor( } } + private fun presentAuthChallenge( + activity: ComponentActivity, + authChallenge: CardAuthChallenge + ) { + cardClient?.presentAuthChallenge(activity, authChallenge)?.let { presentAuthResult -> + when (presentAuthResult) { + is CardPresentAuthChallengeResult.Success -> { + authState = presentAuthResult.authState + } + + is CardPresentAuthChallengeResult.Failure -> { + approveOrderState = ActionState.Failure(presentAuthResult.error) + } + } + } + } + fun completeOrder(context: Context) { val orderId = createdOrder?.id if (orderId == null) { @@ -256,6 +255,6 @@ class ApproveOrderViewModel @Inject constructor( } } - authState?.let { cardClient?.legacyCompleteAuthChallenge(intent, it) } + authState?.let { cardClient?.completeAuthChallenge(intent, it) } } } 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 d587cf906..cb9c1092c 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 @@ -193,6 +193,6 @@ class VaultCardViewModel @Inject constructor( } fun completeAuthChallenge(intent: Intent) { - authState?.let { cardClient?.legacyCompleteAuthChallenge(intent, it) } + authState?.let { cardClient?.completeAuthChallenge(intent, it) } } } From 5ba2db7bb86964be99ec52c1690d22ab438a19a1 Mon Sep 17 00:00:00 2001 From: sshropshire Date: Fri, 6 Dec 2024 11:34:02 -0600 Subject: [PATCH 10/18] Move CardCallback.ApproveOrder to it's own top level class. --- .../cardpayments/CardApproveOrderCallback.kt | 11 +++++++++++ .../com/paypal/android/cardpayments/CardCallback.kt | 13 ------------- .../com/paypal/android/cardpayments/CardClient.kt | 2 +- .../android/cardpayments/CardClientUnitTest.kt | 2 +- 4 files changed, 13 insertions(+), 15 deletions(-) create mode 100644 CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt delete mode 100644 CardPayments/src/main/java/com/paypal/android/cardpayments/CardCallback.kt diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt new file mode 100644 index 000000000..b6f9d5ff4 --- /dev/null +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt @@ -0,0 +1,11 @@ +package com.paypal.android.cardpayments + +import androidx.annotation.MainThread + +fun interface CardApproveOrderCallback { + /** + * Called when the order is approved. + */ + @MainThread + fun onApproveOrderResult(result: CardResult.ApproveOrder) +} \ No newline at end of file diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardCallback.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardCallback.kt deleted file mode 100644 index a3dd91891..000000000 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardCallback.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.paypal.android.cardpayments - -import androidx.annotation.MainThread - -object CardCallback { - fun interface ApproveOrder { - /** - * Called when the order is approved. - */ - @MainThread - fun onApproveOrderResult(result: CardResult.ApproveOrder) - } -} diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt index e19b6da53..2d8ed9f97 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt @@ -51,7 +51,7 @@ class CardClient internal constructor( ) // NEXT MAJOR VERSION: Consider renaming approveOrder() to confirmPaymentSource() - fun approveOrder(cardRequest: CardRequest, callback: CardCallback.ApproveOrder) { + fun approveOrder(cardRequest: CardRequest, callback: CardApproveOrderCallback) { // TODO: deprecate this method and offer auth challenge integration pattern (similar to vault) approveOrderId = cardRequest.orderId analytics.notifyApproveOrderStarted(cardRequest.orderId) diff --git a/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt b/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt index 44a0d2a59..c047dd207 100644 --- a/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt +++ b/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt @@ -63,7 +63,7 @@ class CardClientUnitTest { private val activity = mockk(relaxed = true) - private val approveOrderCallback = mockk() + private val approveOrderCallback = mockk() private val cardVaultListener = mockk(relaxed = true) private val intent = Intent() From f07654b253d15205c887d2f8383d78e9fc161ca2 Mon Sep 17 00:00:00 2001 From: sshropshire Date: Fri, 6 Dec 2024 11:38:59 -0600 Subject: [PATCH 11/18] Add docstrings. --- .../android/cardpayments/CardApproveOrderCallback.kt | 2 ++ .../java/com/paypal/android/cardpayments/CardClient.kt | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt index b6f9d5ff4..1772ce0ed 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt @@ -5,6 +5,8 @@ import androidx.annotation.MainThread fun interface CardApproveOrderCallback { /** * Called when the order is approved. + * + * @param result [CardResult.ApproveOrder] result with details */ @MainThread fun onApproveOrderResult(result: CardResult.ApproveOrder) diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt index 2d8ed9f97..2f5d75dda 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt @@ -51,6 +51,13 @@ class CardClient internal constructor( ) // NEXT MAJOR VERSION: Consider renaming approveOrder() to confirmPaymentSource() + + /** + * Confirm [Card] payment source for an order. + * + * @param cardRequest [CardRequest] for requesting an order approval + * @param callback [CardApproveOrderCallback] callback for receiving result asynchronously + */ fun approveOrder(cardRequest: CardRequest, callback: CardApproveOrderCallback) { // TODO: deprecate this method and offer auth challenge integration pattern (similar to vault) approveOrderId = cardRequest.orderId @@ -131,6 +138,8 @@ class CardClient internal constructor( /** * Present an auth challenge received from a [CardClient.approveOrder] or [CardClient.vault] result. + * @param activity [ComponentActivity] activity reference used to present a Chrome Custom Tab. + * @param authChallenge [CardAuthChallenge] auth challenge to present (see [CardResult.ApproveOrder.AuthorizationRequired]) */ fun presentAuthChallenge( activity: ComponentActivity, From 411096a63832b3002f6e2f9c28032e5fd07bbbc9 Mon Sep 17 00:00:00 2001 From: sshropshire Date: Fri, 6 Dec 2024 11:41:05 -0600 Subject: [PATCH 12/18] Add comment. --- .../java/com/paypal/android/cardpayments/CardAuthLauncher.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardAuthLauncher.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardAuthLauncher.kt index 185692470..baccb3eb4 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardAuthLauncher.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardAuthLauncher.kt @@ -69,6 +69,9 @@ internal class CardAuthLauncher( is BrowserSwitchFinalResult.Success -> parseApproveOrderSuccessResult(finalResult) is BrowserSwitchFinalResult.Failure -> { + // TODO: remove error codes and error description from project; the built in + // Throwable type already has a message property and error codes are only required + // for iOS Error protocol conformance val message = "Browser switch failed" val browserSwitchError = PayPalSDKError(0, message, reason = finalResult.error) CardResult.FinishApproveOrder.Failure(browserSwitchError) From 7993da0bb99df6dea77cbc9371a274db68ee935e Mon Sep 17 00:00:00 2001 From: sshropshire Date: Fri, 6 Dec 2024 11:44:02 -0600 Subject: [PATCH 13/18] Flatten result for approve order sealed class hierarchy. --- .../cardpayments/CardApproveOrderCallback.kt | 2 +- .../cardpayments/CardApproveOrderResult.kt | 15 +++++++++++++++ .../com/paypal/android/cardpayments/CardClient.kt | 6 +++--- .../com/paypal/android/cardpayments/CardResult.kt | 12 ------------ .../android/cardpayments/CardClientUnitTest.kt | 12 ++++++------ .../ui/approveorder/ApproveOrderViewModel.kt | 7 ++++--- 6 files changed, 29 insertions(+), 25 deletions(-) create mode 100644 CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderResult.kt diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt index 1772ce0ed..519b32cad 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt @@ -9,5 +9,5 @@ fun interface CardApproveOrderCallback { * @param result [CardResult.ApproveOrder] result with details */ @MainThread - fun onApproveOrderResult(result: CardResult.ApproveOrder) + fun onApproveOrderResult(result: CardApproveOrderResult) } \ No newline at end of file diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderResult.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderResult.kt new file mode 100644 index 000000000..4f9c54172 --- /dev/null +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderResult.kt @@ -0,0 +1,15 @@ +package com.paypal.android.cardpayments + +import com.paypal.android.corepayments.PayPalSDKError + +sealed class CardApproveOrderResult { + + data class Success( + val orderId: String, + val status: String? = null, + val didAttemptThreeDSecureAuthentication: Boolean = false + ) : CardApproveOrderResult() + + data class AuthorizationRequired(val authChallenge: CardAuthChallenge) : CardApproveOrderResult() + data class Failure(val error: PayPalSDKError) : CardApproveOrderResult() +} \ No newline at end of file diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt index 2f5d75dda..76d4f74a2 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt @@ -69,7 +69,7 @@ class CardClient internal constructor( if (response.payerActionHref == null) { analytics.notifyApproveOrderSucceeded(response.orderId) val result = response.run { - CardResult.ApproveOrder.Success( + CardApproveOrderResult.Success( orderId = orderId, status = status?.name ) @@ -80,14 +80,14 @@ class CardClient internal constructor( val url = Uri.parse(response.payerActionHref) val authChallenge = CardAuthChallenge.ApproveOrder(url, cardRequest) - val result = CardResult.ApproveOrder.AuthorizationRequired(authChallenge) + val result = CardApproveOrderResult.AuthorizationRequired(authChallenge) callback.onApproveOrderResult(result) } } is ConfirmPaymentSourceResult.Failure -> { analytics.notifyApproveOrderFailed(cardRequest.orderId) - val result = CardResult.ApproveOrder.Failure(response.error) + val result = CardApproveOrderResult.Failure(response.error) callback.onApproveOrderResult(result) } } diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt index dbbc0e6ff..ee6d802d4 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt @@ -4,18 +4,6 @@ import com.paypal.android.corepayments.PayPalSDKError object CardResult { - sealed class ApproveOrder { - - data class Success( - val orderId: String, - val status: String? = null, - val didAttemptThreeDSecureAuthentication: Boolean = false - ) : ApproveOrder() - - data class AuthorizationRequired(val authChallenge: CardAuthChallenge) : ApproveOrder() - data class Failure(val error: PayPalSDKError) : ApproveOrder() - } - sealed class FinishApproveOrder { data class Success( val orderId: String, diff --git a/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt b/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt index c047dd207..710c44062 100644 --- a/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt +++ b/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt @@ -95,10 +95,10 @@ class CardClientUnitTest { sut.approveOrder(cardRequest, approveOrderCallback) advanceUntilIdle() - val resultSlot = slot() + val resultSlot = slot() verify(exactly = 1) { approveOrderCallback.onApproveOrderResult(capture(resultSlot)) } - val result = resultSlot.captured as CardResult.ApproveOrder.Success + val result = resultSlot.captured as CardApproveOrderResult.Success assertEquals("sample-order-id", result.orderId) assertEquals(OrderStatus.APPROVED.name, result.status) assertFalse(result.didAttemptThreeDSecureAuthentication) @@ -116,10 +116,10 @@ class CardClientUnitTest { sut.approveOrder(cardRequest, approveOrderCallback) advanceUntilIdle() - val resultSlot = slot() + val resultSlot = slot() verify(exactly = 1) { approveOrderCallback.onApproveOrderResult(capture(resultSlot)) } - val result = resultSlot.captured as CardResult.ApproveOrder.Failure + val result = resultSlot.captured as CardApproveOrderResult.Failure assertEquals("mock_error_message", result.error.errorDescription) } @@ -134,10 +134,10 @@ class CardClientUnitTest { sut.approveOrder(cardRequest, approveOrderCallback) advanceUntilIdle() - val resultSlot = slot() + val resultSlot = slot() verify(exactly = 1) { approveOrderCallback.onApproveOrderResult(capture(resultSlot)) } - val result = resultSlot.captured as CardResult.ApproveOrder.AuthorizationRequired + val result = resultSlot.captured as CardApproveOrderResult.AuthorizationRequired val authChallenge = result.authChallenge as CardAuthChallenge.ApproveOrder assertEquals(Uri.parse("/payer/action/href"), authChallenge.url) 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 fa687fab8..1af483b9d 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 @@ -9,6 +9,7 @@ import com.paypal.android.api.model.Order import com.paypal.android.api.model.OrderIntent import com.paypal.android.api.services.SDKSampleServerResult import com.paypal.android.cardpayments.Card +import com.paypal.android.cardpayments.CardApproveOrderResult import com.paypal.android.cardpayments.CardAuthChallenge import com.paypal.android.cardpayments.CardClient import com.paypal.android.cardpayments.CardPresentAuthChallengeResult @@ -90,18 +91,18 @@ class ApproveOrderViewModel @Inject constructor( cardClient = CardClient(activity, coreConfig) cardClient?.approveOrder(cardRequest) { result -> when (result) { - is CardResult.ApproveOrder.Success -> { + is CardApproveOrderResult.Success -> { val orderInfo = result.run { OrderInfo(orderId, status, didAttemptThreeDSecureAuthentication) } approveOrderState = ActionState.Success(orderInfo) } - is CardResult.ApproveOrder.AuthorizationRequired -> { + is CardApproveOrderResult.AuthorizationRequired -> { presentAuthChallenge(activity, result.authChallenge) } - is CardResult.ApproveOrder.Failure -> { + is CardApproveOrderResult.Failure -> { approveOrderState = ActionState.Failure(result.error) } } From 60ac0fb1e46ddbadf68e8eff627095e32244e557 Mon Sep 17 00:00:00 2001 From: sshropshire Date: Fri, 6 Dec 2024 11:45:06 -0600 Subject: [PATCH 14/18] Flatten Card FinishApproveOrder sealed class hierarchy. --- .../android/cardpayments/CardAuthLauncher.kt | 14 +++++++------- .../com/paypal/android/cardpayments/CardClient.kt | 8 ++++---- .../cardpayments/CardFinishApproveOrderResult.kt | 15 +++++++++++++++ .../com/paypal/android/cardpayments/CardResult.kt | 13 ------------- .../cardpayments/CardAuthLauncherUnitTest.kt | 2 +- .../android/cardpayments/CardClientUnitTest.kt | 6 +++--- .../ui/approveorder/ApproveOrderViewModel.kt | 12 ++++++------ 7 files changed, 36 insertions(+), 34 deletions(-) create mode 100644 CardPayments/src/main/java/com/paypal/android/cardpayments/CardFinishApproveOrderResult.kt diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardAuthLauncher.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardAuthLauncher.kt index baccb3eb4..6880461d5 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardAuthLauncher.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardAuthLauncher.kt @@ -64,7 +64,7 @@ internal class CardAuthLauncher( fun completeApproveOrderAuthRequest( intent: Intent, authState: String - ): CardResult.FinishApproveOrder = + ): CardFinishApproveOrderResult = when (val finalResult = browserSwitchClient.completeRequest(intent, authState)) { is BrowserSwitchFinalResult.Success -> parseApproveOrderSuccessResult(finalResult) @@ -74,10 +74,10 @@ internal class CardAuthLauncher( // for iOS Error protocol conformance val message = "Browser switch failed" val browserSwitchError = PayPalSDKError(0, message, reason = finalResult.error) - CardResult.FinishApproveOrder.Failure(browserSwitchError) + CardFinishApproveOrderResult.Failure(browserSwitchError) } - BrowserSwitchFinalResult.NoResult -> CardResult.FinishApproveOrder.NoResult + BrowserSwitchFinalResult.NoResult -> CardFinishApproveOrderResult.NoResult } fun completeAuthRequest(intent: Intent, authState: String): CardStatus = @@ -108,18 +108,18 @@ internal class CardAuthLauncher( private fun parseApproveOrderSuccessResult( finalResult: BrowserSwitchFinalResult.Success - ): CardResult.FinishApproveOrder = + ): CardFinishApproveOrderResult = if (finalResult.requestCode == BrowserSwitchRequestCodes.CARD_APPROVE_ORDER) { val orderId = finalResult.requestMetadata?.optString(METADATA_KEY_ORDER_ID) if (orderId == null) { - CardResult.FinishApproveOrder.Failure(CardError.unknownError) + CardFinishApproveOrderResult.Failure(CardError.unknownError) } else { - CardResult.FinishApproveOrder.Success( + CardFinishApproveOrderResult.Success( orderId = orderId, didAttemptThreeDSecureAuthentication = true ) } } else { - CardResult.FinishApproveOrder.NoResult + CardFinishApproveOrderResult.NoResult } } diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt index 76d4f74a2..1ee0915c7 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt @@ -179,16 +179,16 @@ class CardClient internal constructor( } } - fun finishApproveOrder(intent: Intent, authState: String): CardResult.FinishApproveOrder { + fun finishApproveOrder(intent: Intent, authState: String): CardFinishApproveOrderResult { val result = authChallengeLauncher.completeApproveOrderAuthRequest(intent, authState) when (result) { - is CardResult.FinishApproveOrder.Success -> + is CardFinishApproveOrderResult.Success -> analytics.notifyApproveOrderAuthChallengeSucceeded(result.orderId) - is CardResult.FinishApproveOrder.Failure -> + is CardFinishApproveOrderResult.Failure -> analytics.notifyApproveOrderAuthChallengeFailed(null) - CardResult.FinishApproveOrder.Canceled -> + CardFinishApproveOrderResult.Canceled -> analytics.notifyApproveOrderAuthChallengeCanceled(null) else -> { diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardFinishApproveOrderResult.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardFinishApproveOrderResult.kt new file mode 100644 index 000000000..a1ff6178c --- /dev/null +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardFinishApproveOrderResult.kt @@ -0,0 +1,15 @@ +package com.paypal.android.cardpayments + +import com.paypal.android.corepayments.PayPalSDKError + +sealed class CardFinishApproveOrderResult { + data class Success( + val orderId: String, + val status: String? = null, + val didAttemptThreeDSecureAuthentication: Boolean = false + ) : CardFinishApproveOrderResult() + + data class Failure(val error: PayPalSDKError) : CardFinishApproveOrderResult() + data object Canceled : CardFinishApproveOrderResult() + data object NoResult : CardFinishApproveOrderResult() +} \ No newline at end of file diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt index ee6d802d4..62503a1de 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt @@ -1,18 +1,5 @@ package com.paypal.android.cardpayments -import com.paypal.android.corepayments.PayPalSDKError - object CardResult { - sealed class FinishApproveOrder { - data class Success( - val orderId: String, - val status: String? = null, - val didAttemptThreeDSecureAuthentication: Boolean = false - ) : FinishApproveOrder() - - data class Failure(val error: PayPalSDKError) : FinishApproveOrder() - data object Canceled : FinishApproveOrder() - data object NoResult : FinishApproveOrder() - } } diff --git a/CardPayments/src/test/java/com/paypal/android/cardpayments/CardAuthLauncherUnitTest.kt b/CardPayments/src/test/java/com/paypal/android/cardpayments/CardAuthLauncherUnitTest.kt index 6ca09df1a..d7be9fa4b 100644 --- a/CardPayments/src/test/java/com/paypal/android/cardpayments/CardAuthLauncherUnitTest.kt +++ b/CardPayments/src/test/java/com/paypal/android/cardpayments/CardAuthLauncherUnitTest.kt @@ -165,7 +165,7 @@ class CardAuthLauncherUnitTest { } returns finalResult val result = sut.completeApproveOrderAuthRequest(intent, "pending request") - as CardResult.FinishApproveOrder.Success + as CardFinishApproveOrderResult.Success assertEquals("fake-order-id", result.orderId) assertTrue(result.didAttemptThreeDSecureAuthentication) diff --git a/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt b/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt index 710c44062..46c5e431c 100644 --- a/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt +++ b/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt @@ -220,7 +220,7 @@ class CardClientUnitTest { fun `completeApproveOrderAuthRequest() notifies merchant of approve order success`() = runTest { val sut = createCardClient(testScheduler) - val successResult = CardResult.FinishApproveOrder.Success( + val successResult = CardFinishApproveOrderResult.Success( orderId = "fake-order-id", status = OrderStatus.APPROVED.name, didAttemptThreeDSecureAuthentication = false @@ -238,7 +238,7 @@ class CardClientUnitTest { val sut = createCardClient(testScheduler) val error = PayPalSDKError(123, "fake-error-description") - val failureResult = CardResult.FinishApproveOrder.Failure(error) + val failureResult = CardFinishApproveOrderResult.Failure(error) every { cardAuthLauncher.completeApproveOrderAuthRequest(intent, "auth state") } returns failureResult @@ -251,7 +251,7 @@ class CardClientUnitTest { fun `completeAuthChallenge() notifies merchant of approve order cancelation`() = runTest { val sut = createCardClient(testScheduler) - val canceledResult = CardResult.FinishApproveOrder.Canceled + val canceledResult = CardFinishApproveOrderResult.Canceled every { cardAuthLauncher.completeApproveOrderAuthRequest(intent, "auth state") } returns canceledResult 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 1af483b9d..b40222df9 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 @@ -12,9 +12,9 @@ import com.paypal.android.cardpayments.Card import com.paypal.android.cardpayments.CardApproveOrderResult import com.paypal.android.cardpayments.CardAuthChallenge import com.paypal.android.cardpayments.CardClient +import com.paypal.android.cardpayments.CardFinishApproveOrderResult import com.paypal.android.cardpayments.CardPresentAuthChallengeResult import com.paypal.android.cardpayments.CardRequest -import com.paypal.android.cardpayments.CardResult import com.paypal.android.cardpayments.threedsecure.SCA import com.paypal.android.corepayments.CoreConfig import com.paypal.android.fraudprotection.PayPalDataCollector @@ -229,28 +229,28 @@ class ApproveOrderViewModel @Inject constructor( cardClient?.removeObservers() } - private fun checkIfApproveOrderFinished(intent: Intent): CardResult.FinishApproveOrder? = + private fun checkIfApproveOrderFinished(intent: Intent): CardFinishApproveOrderResult? = authState?.let { cardClient?.finishApproveOrder(intent, it) } fun completeAuthChallenge(intent: Intent) { checkIfApproveOrderFinished(intent)?.let { approveOrderResult -> when (approveOrderResult) { - is CardResult.FinishApproveOrder.Success -> { + is CardFinishApproveOrderResult.Success -> { val orderInfo = approveOrderResult.run { OrderInfo(orderId, status, didAttemptThreeDSecureAuthentication) } approveOrderState = ActionState.Success(orderInfo) } - is CardResult.FinishApproveOrder.Failure -> { + is CardFinishApproveOrderResult.Failure -> { approveOrderState = ActionState.Failure(approveOrderResult.error) } - CardResult.FinishApproveOrder.Canceled -> { + CardFinishApproveOrderResult.Canceled -> { approveOrderState = ActionState.Failure(Exception("USER CANCELED")) } - CardResult.FinishApproveOrder.NoResult -> { + CardFinishApproveOrderResult.NoResult -> { // ignore } } From 3366f00a63535ff6ec94c2a75086c801a60f8471 Mon Sep 17 00:00:00 2001 From: sshropshire Date: Fri, 6 Dec 2024 11:46:44 -0600 Subject: [PATCH 15/18] Remove unecessary CardResult type. --- .../paypal/android/cardpayments/CardApproveOrderCallback.kt | 2 +- .../main/java/com/paypal/android/cardpayments/CardClient.kt | 2 +- .../main/java/com/paypal/android/cardpayments/CardResult.kt | 5 ----- 3 files changed, 2 insertions(+), 7 deletions(-) delete mode 100644 CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt index 519b32cad..34a7fe9e9 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt @@ -6,7 +6,7 @@ fun interface CardApproveOrderCallback { /** * Called when the order is approved. * - * @param result [CardResult.ApproveOrder] result with details + * @param result [CardApproveOrderResult] result with details */ @MainThread fun onApproveOrderResult(result: CardApproveOrderResult) diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt index 1ee0915c7..c627901db 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt @@ -139,7 +139,7 @@ class CardClient internal constructor( /** * Present an auth challenge received from a [CardClient.approveOrder] or [CardClient.vault] result. * @param activity [ComponentActivity] activity reference used to present a Chrome Custom Tab. - * @param authChallenge [CardAuthChallenge] auth challenge to present (see [CardResult.ApproveOrder.AuthorizationRequired]) + * @param authChallenge [CardAuthChallenge] auth challenge to present (see [CardApproveOrderResult.AuthorizationRequired]) */ fun presentAuthChallenge( activity: ComponentActivity, diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt deleted file mode 100644 index 62503a1de..000000000 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardResult.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.paypal.android.cardpayments - -object CardResult { - -} From 31e72c25c8fc2bd7062b8f8fdf59f827bbf28950 Mon Sep 17 00:00:00 2001 From: sshropshire Date: Fri, 6 Dec 2024 11:48:09 -0600 Subject: [PATCH 16/18] Clean up lint errors. --- .../paypal/android/cardpayments/CardApproveOrderCallback.kt | 2 +- .../com/paypal/android/cardpayments/CardApproveOrderResult.kt | 2 +- .../main/java/com/paypal/android/cardpayments/CardClient.kt | 3 ++- .../android/cardpayments/CardFinishApproveOrderResult.kt | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt index 34a7fe9e9..653a5a5ad 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt @@ -10,4 +10,4 @@ fun interface CardApproveOrderCallback { */ @MainThread fun onApproveOrderResult(result: CardApproveOrderResult) -} \ No newline at end of file +} diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderResult.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderResult.kt index 4f9c54172..170a344f8 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderResult.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderResult.kt @@ -12,4 +12,4 @@ sealed class CardApproveOrderResult { data class AuthorizationRequired(val authChallenge: CardAuthChallenge) : CardApproveOrderResult() data class Failure(val error: PayPalSDKError) : CardApproveOrderResult() -} \ No newline at end of file +} diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt index c627901db..4704ba406 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt @@ -139,7 +139,8 @@ class CardClient internal constructor( /** * Present an auth challenge received from a [CardClient.approveOrder] or [CardClient.vault] result. * @param activity [ComponentActivity] activity reference used to present a Chrome Custom Tab. - * @param authChallenge [CardAuthChallenge] auth challenge to present (see [CardApproveOrderResult.AuthorizationRequired]) + * @param authChallenge [CardAuthChallenge] auth challenge to present + * (see [CardApproveOrderResult.AuthorizationRequired]) */ fun presentAuthChallenge( activity: ComponentActivity, diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardFinishApproveOrderResult.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardFinishApproveOrderResult.kt index a1ff6178c..eba3ed506 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardFinishApproveOrderResult.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardFinishApproveOrderResult.kt @@ -12,4 +12,4 @@ sealed class CardFinishApproveOrderResult { data class Failure(val error: PayPalSDKError) : CardFinishApproveOrderResult() data object Canceled : CardFinishApproveOrderResult() data object NoResult : CardFinishApproveOrderResult() -} \ No newline at end of file +} From e62b819c13f4084b04cdf384651313afd81a69f2 Mon Sep 17 00:00:00 2001 From: sshropshire Date: Fri, 6 Dec 2024 11:53:45 -0600 Subject: [PATCH 17/18] Update CHANGELOG.md. --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71200607c..d65fae0de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,16 @@ ## unreleased * CorePayments * Fix issue that causes analytics version number to always be `null` +* Breaking Changes + * CardPayments + * Remove `ApproveOrderListener` type + * Add `CardApproveOrderCallback` type + * Add `CardApproveOrderResult` type + * Remove `CardClient.approveOrder(CardRequest)` method + * Add `CardClient.approveOrder(CardRequest, CardApproveOrderCallback)` method + * Add `CardClient.finishApproveOrder(Intent, String)` method + * Add `CardFinishApproveOrderResult` type + * Remove `CardResult` type ## 2.0.0-beta1 (2024-11-20) * Breaking Changes From 9c40315cbbf791b091febe44de8d9e1dbb6e0e7b Mon Sep 17 00:00:00 2001 From: sshropshire Date: Fri, 6 Dec 2024 15:12:11 -0600 Subject: [PATCH 18/18] Rename callback. --- .../paypal/android/cardpayments/CardApproveOrderCallback.kt | 2 +- .../main/java/com/paypal/android/cardpayments/CardClient.kt | 6 +++--- .../com/paypal/android/cardpayments/CardClientUnitTest.kt | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt index 653a5a5ad..69b3dee1f 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt @@ -9,5 +9,5 @@ fun interface CardApproveOrderCallback { * @param result [CardApproveOrderResult] result with details */ @MainThread - fun onApproveOrderResult(result: CardApproveOrderResult) + fun onCardApproveOrderResult(result: CardApproveOrderResult) } diff --git a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt index 4704ba406..3a07718cc 100644 --- a/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt +++ b/CardPayments/src/main/java/com/paypal/android/cardpayments/CardClient.kt @@ -74,21 +74,21 @@ class CardClient internal constructor( status = status?.name ) } - callback.onApproveOrderResult(result) + callback.onCardApproveOrderResult(result) } else { analytics.notifyApproveOrderAuthChallengeReceived(cardRequest.orderId) val url = Uri.parse(response.payerActionHref) val authChallenge = CardAuthChallenge.ApproveOrder(url, cardRequest) val result = CardApproveOrderResult.AuthorizationRequired(authChallenge) - callback.onApproveOrderResult(result) + callback.onCardApproveOrderResult(result) } } is ConfirmPaymentSourceResult.Failure -> { analytics.notifyApproveOrderFailed(cardRequest.orderId) val result = CardApproveOrderResult.Failure(response.error) - callback.onApproveOrderResult(result) + callback.onCardApproveOrderResult(result) } } } diff --git a/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt b/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt index 46c5e431c..453d80f59 100644 --- a/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt +++ b/CardPayments/src/test/java/com/paypal/android/cardpayments/CardClientUnitTest.kt @@ -96,7 +96,7 @@ class CardClientUnitTest { advanceUntilIdle() val resultSlot = slot() - verify(exactly = 1) { approveOrderCallback.onApproveOrderResult(capture(resultSlot)) } + verify(exactly = 1) { approveOrderCallback.onCardApproveOrderResult(capture(resultSlot)) } val result = resultSlot.captured as CardApproveOrderResult.Success assertEquals("sample-order-id", result.orderId) @@ -117,7 +117,7 @@ class CardClientUnitTest { advanceUntilIdle() val resultSlot = slot() - verify(exactly = 1) { approveOrderCallback.onApproveOrderResult(capture(resultSlot)) } + verify(exactly = 1) { approveOrderCallback.onCardApproveOrderResult(capture(resultSlot)) } val result = resultSlot.captured as CardApproveOrderResult.Failure assertEquals("mock_error_message", result.error.errorDescription) @@ -135,7 +135,7 @@ class CardClientUnitTest { advanceUntilIdle() val resultSlot = slot() - verify(exactly = 1) { approveOrderCallback.onApproveOrderResult(capture(resultSlot)) } + verify(exactly = 1) { approveOrderCallback.onCardApproveOrderResult(capture(resultSlot)) } val result = resultSlot.captured as CardApproveOrderResult.AuthorizationRequired val authChallenge = result.authChallenge as CardAuthChallenge.ApproveOrder