Skip to content

Commit

Permalink
Merge pull request #1955 from Adyen/feature/3ds2-cancelled
Browse files Browse the repository at this point in the history
Propagate additionalDetails on native 3ds2 cancel flow
  • Loading branch information
OscarSpruit authored Jan 15, 2025
2 parents 9d24663 + 9e09331 commit 7d22932
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.adyen.checkout.core.exception.ComponentException
/**
* This exception is an indication that the 3DS2 Authentication was cancelled by the user.
*/
@Deprecated("This exception is no longer in use")
class Cancelled3DS2Exception(errorMessage: String) : ComponentException(errorMessage) {
companion object {
private const val serialVersionUID = 3858008275644429050L
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import androidx.annotation.VisibleForTesting
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.SavedStateHandle
import com.adyen.checkout.adyen3ds2.Authentication3DS2Exception
import com.adyen.checkout.adyen3ds2.Cancelled3DS2Exception
import com.adyen.checkout.adyen3ds2.internal.analytics.ThreeDS2Events
import com.adyen.checkout.adyen3ds2.internal.data.api.SubmitFingerprintRepository
import com.adyen.checkout.adyen3ds2.internal.data.model.Adyen3DS2Serializer
Expand Down Expand Up @@ -573,10 +572,20 @@ internal class DefaultAdyen3DS2Delegate(
}
}

private fun onCancelled() {
private fun onCancelled(result: ChallengeResult.Cancelled) {
adyenLog(AdyenLogLevel.DEBUG) { "challenge cancelled" }
emitError(Cancelled3DS2Exception("Challenge canceled."))
closeTransaction()
try {
val details = makeDetails(result.transactionStatus, result.additionalDetails)
emitDetails(details)
} catch (e: CheckoutException) {
trackChallengeErrorEvent(
errorEvent = ErrorEvent.THREEDS2_CHALLENGE_HANDLING,
message = "Challenge is cancelled and details cannot be created",
)
emitError(e)
} finally {
closeTransaction()
}
}

private fun onTimeout(result: ChallengeResult.Timeout) {
Expand Down Expand Up @@ -615,7 +624,7 @@ internal class DefaultAdyen3DS2Delegate(
when (result) {
is ChallengeResult.Cancelled -> {
trackChallengeCompletedEvent(ThreeDS2Events.Result.CANCELLED)
onCancelled()
onCancelled(result)
}

is ChallengeResult.Completed -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import android.content.Context
import android.content.Intent
import androidx.lifecycle.SavedStateHandle
import com.adyen.checkout.adyen3ds2.Authentication3DS2Exception
import com.adyen.checkout.adyen3ds2.Cancelled3DS2Exception
import com.adyen.checkout.adyen3ds2.internal.analytics.ThreeDS2Events
import com.adyen.checkout.adyen3ds2.internal.data.api.SubmitFingerprintRepository
import com.adyen.checkout.adyen3ds2.internal.data.model.Adyen3DS2Serializer
Expand Down Expand Up @@ -489,8 +488,8 @@ internal class DefaultAdyen3DS2DelegateTest(
}

@Test
fun `cancelled, then an error is emitted`() = runTest {
val exceptionFlow = delegate.exceptionFlow.test(testScheduler)
fun `cancelled, then details are emitted`() = runTest {
val detailsFlow = delegate.detailsFlow.test(testScheduler)

delegate.onCompletion(
result = ChallengeResult.Cancelled(
Expand All @@ -499,11 +498,11 @@ internal class DefaultAdyen3DS2DelegateTest(
),
)

assertTrue(exceptionFlow.latestValue is Cancelled3DS2Exception)
assertNotNull(detailsFlow.latestValue.details)
}

@Test
fun `timedout, then details are emitted`() = runTest {
fun `timed out, then details are emitted`() = runTest {
val detailsFlow = delegate.detailsFlow.test(testScheduler)

delegate.onCompletion(
Expand Down
4 changes: 4 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@
## Improved

## Changed
- For 3DS2 native flow, cancellations by shopper trigger `onAdditionalDetails()` event. You can make a `/payments/details` call to gain more insight into the transaction.
- If you already handle other 3DS2 errors by making a `/payments/details` call, then no changes are required and the specific handling for `Cancelled3DS2Exception` can be removed.
- If not yet implemented, you should update your systems to handle shopper cancellations using the new flow.
- Dependency versions:
| Name | Version |
|--------------------------------------------------------------------------------------------------------|-------------------------------|
| | |

## Deprecated
- `Cancelled3DS2Exception` is now deprecated. Shopper cancellation of the 3DS2 native flow triggers the `onAdditionalDetails()` event, enabling a `/payments/details` call for transaction insights.
- The styles and strings for the Cash App Pay loading indicator. Use the new styles and strings instead.
| Previous | Now |
|-----------------------------------------------------------|------------------------------------------------------------------|
Expand Down

0 comments on commit 7d22932

Please sign in to comment.