Skip to content

ThreeDSecureClient makes duplicate API call with consumed nonce after Activity recreation on foldable devices #1517

@MAhmadi-O

Description

@MAhmadi-O

Braintree SDK Version

5.21.0

Environment

Sandbox

Android Version & Device

Android 16 & Foldable Device

Braintree dependencies

com.braintreepayments.api:three-d-secure

Describe the bug

When using the Braintree Android SDK for 3D Secure authentication on foldable devices (e.g., Samsung Galaxy Z Fold), the SDK makes a duplicate API call with an already-consumed nonce after the activity is recreated due to configuration changes (fold/unfold). This results in a 422 error with message "Nonce is already consumed".

Device: Samsung Galaxy Z Fold series and other foldable devices
Android Version: 16
Scenario: Native 3DS flow using ThreeDSecureClient

Stack Trace

com.braintreepayments.api.threedsecure.ThreeDSecureClient.initializeCardinalClient$lambda$2$lambda$1(ThreeDSecureClient.kt:132)com.braintreepayments.api.threedsecure.ThreeDSecureClient.callbackCreatePaymentAuthFailure(ThreeDSecureClient.kt:407)

Root Cause Analysis

When the activity is recreated:
The ThreeDSecureLauncher is re-registered (via ActivityResultLauncher)
Our app calls launch() again to restore the payment state
The SDK's ThreeDSecureClient.createPaymentAuthRequest() makes a new API call
The nonce from step 3 has already been consumed in the original request
Server returns 422 error
The SDK's ActivityResultLauncher correctly preserves the 3DS flow continuation, but there's no mechanism to prevent re-launching with a consumed nonce.

Overriding configuration changes on the host activity didn't help, as there is underlying view shown in the SDK, wonder if this is a real bug or can be avoided in some way.

To reproduce

  • Start a 3D Secure payment flow using ThreeDSecureLauncher.launch()
  • When the 3DS challenge screen appears, fold or unfold the device
  • This triggers Android's configuration change, causing activity recreation
  • Complete the 3DS authentication

Observe the error in logs
Expected Behavior
The SDK should handle activity recreation gracefully through its ActivityResultLauncher mechanism without making duplicate API calls. The payment should complete successfully after fold/unfold.

Actual Behavior

After activity recreation, the SDK makes a new API call to the 3DS lookup endpoint with the same nonce that was already consumed, resulting in:
Unexpected code Response{protocol=h2, code=422, message=, url=https://api.sandbox.braintreegateway.com/merchants/.../client_api/v1/payment_methods/.../three_d_secure/lookup} with body {"error":{"message":"Nonce is already consumed"},"threeDSecureInfo":{"liabilityShifted":false,"liabilityShiftPossible":false}}

Expected behavior

Activity Recreation During Active 3DS Flow
User initiates payment → launch() called with fresh nonce
3DS challenge screen displayed to user
Device folded/unfolded → Activity destroyed and recreated by Android
Activity onCreate() called again → register() called to re-establish callback
3DS challenge continues in the same session (handled by SDK's ActivityResultLauncher)
User completes authentication
SDK delivers result to the registered callback without making a new API call
Payment completes successfully using the tokenized result

Screenshots

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions