Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ThreeDSecureClient it never finish instantiation #698

Open
salosoft opened this issue Mar 7, 2023 · 9 comments
Open

ThreeDSecureClient it never finish instantiation #698

salosoft opened this issue Mar 7, 2023 · 9 comments

Comments

@salosoft
Copy link

salosoft commented Mar 7, 2023

Braintree SDK Version

three-d-secure:4.26.1

Environment

Sandbox

Android Version & Device

Android API 30 Motorola

Braintree dependencies

implementation 'com.braintreepayments.api:card:4.26.0'
implementation 'com.braintreepayments.api:data-collector:4.26.0'
implementation 'com.braintreepayments.api:paypal:4.26.0'
implementation 'com.braintreepayments.api:local-payment:4.26.0'
implementation 'com.braintreepayments.api:three-d-secure:4.26.1'

Describe the bug

We are using Braintree to perform payment on our platform, using ower own UI to collect card data. We are trying to migrate from v3 to v4 but we are facing some issues on Payment process. We also provide PayPal method as payment which is working just fine, but when I want to pay through Credit Card by using the Braintree I can't make the ThreeDSecureClient instantiate.
Here is how I am instantiating the code on my Acticity:

private fun initializeBrainTreeClient(paymentClientToken: String?) {
        brainTreeClient = BraintreeClient(this, paymentClientToken.toString())
        payPalClient = PayPalClient(this, brainTreeClient!!)
        payPalClient?.setListener(this)
        threeDClient = ThreeDSecureClient(this, brainTreeClient!!)
        threeDClient!!.setListener(this)
    }

paymentClientToken is supplied after fetching the data from my backend. But while debugging I realize that when the line threeDClient = ThreeDSecureClient(this, brainTreeClient!!) is executed the follow line threeDClient!!.setListener(this) is never reached. I have no idea how it can happen because my method is not suspended. So I tryied to instantiate ThreeDSecureClient by using the deprecated constructor, which does not require the Activity, this one worked, and executed the setListener but the listener itself is never called when I get back from the confirmation code screen.

Can you help me solve this? Or either how to make the ThreeDSecureClient(this, brainTreeClient) works or get the listener being triggered when I get back from confirmation code screen?

To reproduce

ThreeDSecureClient is never instantiate

Inside an Activity:

  1. Fetch your Token from API
  2. After grabing the Token, Paste this code to initialize the Client and ThreeDSecure:
brainTreeClient = BraintreeClient(this, paymentClientToken.toString())
        payPalClient = PayPalClient(this, brainTreeClient!!)
        payPalClient?.setListener(this)
        threeDClient = ThreeDSecureClient(this, brainTreeClient!!)
        threeDClient!!.setListener(this)
  1. Run the app in debug
  2. Observe the threeDClient!!.setListener(this) is never reached

ThreeDSecureClient is instantiate but onThreeDSecureSuccess or onThreeDSecureFailure never triggered

Inside an Activity:

  1. Fetch your Token from API
  2. After grabing the Token, Paste this code to initialize the Client and ThreeDSecure:
brainTreeClient = BraintreeClient(this, paymentClientToken.toString())
        payPalClient = PayPalClient(this, brainTreeClient!!)
        payPalClient?.setListener(this)
        threeDClient = ThreeDSecureClient(brainTreeClient!!)
        threeDClient!!.setListener(this)
  1. Verify the card nounce and user data AND Perform the continuePerformVerification with the code bellow
val threeDSecureRequest = ThreeDSecureRequest()
        threeDSecureRequest.amount = convertedAmount
        threeDSecureRequest.email = userEmail
        threeDSecureRequest.nonce = creditCardNonce
        threeDSecureRequest.versionRequested = ThreeDSecureRequest.VERSION_2

        threeDClient?.performVerification(
            this,
            threeDSecureRequest
        ) { threeDSecureResult, error ->
            if (threeDSecureResult != null) {
                // examine lookup response (if necessary), then continue verification
                threeDClient?.continuePerformVerification(
                    this@ReviewConfirmOrderActivity,
                    threeDSecureRequest,
                    threeDSecureResult
                )
            } else {
                // handle error
                Log.e("DEBUG", "" + error)
            }
        }
  1. The verification Code will be displayed
  2. Type your code.
  3. Observe the screen will be dismissed but either onThreeDSecureSuccess or onThreeDSecureFailure will be triggered.

Expected behavior

  1. ThreeDSecureRequest is initialized when passing the activity on the constructor
  2. When instantiate ThreeDSecureRequest with deprecated method, the listeners onThreeDSecureSuccess or onThreeDSecureFailure will be called when returning to the app, after adding the Confirmation Code.

Screenshots

No response

@sshropshire
Copy link
Contributor

Hi @salosoft thanks for using the Braintree SDK for Android. Can you make sure all libraries have the same version and see if that resolves the issue?

@salosoft
Copy link
Author

salosoft commented Mar 7, 2023

Hi @sshropshire, thanks for the propt response.
It was like that before, and I just updated here anyway. Unfortunatly had same behaviour. Any other idea?

@KunJeongPark
Copy link
Contributor

hello @salosoft,
Did you mean on #8, neither "onThreeDSecureSuccess or onThreeDSecureFailure will be triggered"?

@salosoft
Copy link
Author

salosoft commented Mar 10, 2023

Sorry for my english.
I ment that when I instantiate the ThreeDSecureClient using the deprecated way, without passing the activity, I am able to ge tin the line that set the listener threeDClient!!.setListener(this).
Then I run the app, execute continuePerformVerification during the checkout process, the app display the confirmation code screen, as expected. But after I enter the confirmation code and confirm, the screen dismiss, as expected but the Listeners onThreeDSecureSuccess or onThreeDSecureFailure are never triggered. I mean:

  1. When I enter a correct code it return to the app and doesn't trigger onThreeDSecureSuccess
  2. When I press back button from confirmation code, it doesn't trigger onThreeDSecureFailure

Would that be because I am using the new way of creating the BraintreeClient and passing it into ThreeDSecureClient deprecated constructor?

Please take a look on a representation of how our code is looks like:


class MyActicityX : BaseActivity(), PayPalListener, ThreeDSecureListener {

    private var paymentTypes: String? = ""
    private var brainTreeClient: BraintreeClient? = null
    private var payPalClient: PayPalClient? = null
    private var dataCollector: DataCollector? = null
    private var threeDClient: ThreeDSecureClient? = null
    private var collectiveDataString: String? = ""
    private var amount: String? = null
    private var storeID: String = "32"
    private var userId: String? = null
    private var userEmail: String? = ""
    private var creditCardNonce: String? = ""
    private var sessionManager: SessionManager? = null
    private var currency: String? = null
    private var convertedAmount = ""

    override fun onCreate(arg0: Bundle?) {
        super.onCreate(arg0)
        loadArguments()
        binding.btnProceedReview.setOnClickListener {
            checkPaymentTypes()
        }
        getPaymentCredentials()
        setupFetchConfiguration()
    }

    override fun onNewIntent(newIntent: Intent?) {
        super.onNewIntent(intent)
        intent = newIntent
    }

    private fun loadArguments() {
        creditCardNonce = intent.getStringExtra("CCNonce")
    }

    /**
     * This method will fetch the Braintree credentials
     */
    private fun getPaymentCredentials() {
        ApiUtil.getBraintreeCredentials(mActivity, llReviewOrder, storeID) {
            initializeBrainTreeClient(it.payment_client_token)
        }
    }

    private fun initializeBrainTreeClient(paymentClientToken: String?) {
        brainTreeClient = BraintreeClient(this, paymentClientToken.toString())
        payPalClient = PayPalClient(this, brainTreeClient!!)
        payPalClient?.setListener(this)
        threeDClient = ThreeDSecureClient(brainTreeClient!!)
        threeDClient!!.setListener(this)
    }

    private fun setupFetchConfiguration() {
        if (brainTreeClient != null) {
            dataCollector = DataCollector(brainTreeClient!!)
            brainTreeClient?.getConfiguration { configuration, error ->
                dataCollector?.collectDeviceData(this) { deviceData, error ->
                    collectiveDataString = deviceData
                }
            }

        }
    }

    private fun checkPaymentTypes() {
        if (paymentTypes == "braintree") {
            checkCard()
        } else if (paymentTypes == "braintree_paypal") {
            launchPaypalPayment()
        }
    }


    private fun checkCard() {
        ProgressDialogUtil.showProgressDialog(this)

        val amountString = amount?.contains(",")
        if (amountString!!) {
            convertedAmount = amount.toString().replace(",", ".", false)
        } else {
            convertedAmount = amount.toString()
        }

        if (!userId.isNullOrEmpty()) {
            userEmail = sessionManager?.userDetails?.get(ApiParam.EMAIL)
        }


        val threeDSecureRequest = ThreeDSecureRequest()
        threeDSecureRequest.amount = convertedAmount
        threeDSecureRequest.email = userEmail
        threeDSecureRequest.nonce = creditCardNonce
        threeDSecureRequest.versionRequested = ThreeDSecureRequest.VERSION_2

        threeDClient?.performVerification(
            this,
            threeDSecureRequest
        ) { threeDSecureResult, error ->
            if (threeDSecureResult != null) {
                // examine lookup response (if necessary), then continue verification
                threeDClient?.continuePerformVerification(
                    this@MyActicityX,
                    threeDSecureRequest,
                    threeDSecureResult
                )
            } else {
                // handle error
                ...
            }
        }
    }

    private fun launchPaypalPayment() {
        ProgressDialogUtil.showProgressDialog(this)

        val amountString = "10.0"

        val request = PayPalCheckoutRequest(convertedAmount)
        request.currencyCode = currency
        request.intent = PayPalPaymentIntent.AUTHORIZE

        payPalClient?.tokenizePayPalAccount(this, request)
    }

    override fun onPayPalSuccess(payPalAccountNonce: PayPalAccountNonce) {
        TODO("Proceed with checkout")
    }

    override fun onPayPalFailure(error: java.lang.Exception) {
        TODO("Display error")
    }

    override fun onThreeDSecureSuccess(threeDSecureResult: ThreeDSecureResult) {
        TODO("Proceed with checkout")
    }

    override fun onThreeDSecureFailure(error: java.lang.Exception) {
        TODO("Display error")
    }
}

Like I said, PayPal payment is working just fine. The issue is that on credit card after this screen I don't receive the callback above.

Screenshot 2023-03-08 at 07 53 36

@salosoft
Copy link
Author

Hi @KunJeongPark,

I wanted to let you know that I have found some important information regarding the issue with the ThreeDSecureClient initialization. It seems that when passing "this", as activity, in the constructor it does not work, and this may be related to the fact that my activity inherits from a BaseActivity. However, when I remove the BaseActivity and make it inherit from AppCompatActivity, the constructor and listener work as expected.

@sshropshire
Copy link
Contributor

Hi @salosoft does your BaseActivity inherit from AppCompatActivity?

@salosoft
Copy link
Author

Hi @sshropshire It inherit from my class BaseActivity that only then inherit from AppCompatActivity

@sshropshire
Copy link
Contributor

@salosoft

If your BaseActivity extends AppCompatActivity then it should work properly, unless there's some customization down in BaseActivity that prevents AppCompatActivity from functioning as it normally should.

Usually a super.onCreate() call could be missing or something that breaks the inheritance chain could cause unpredictable behavior from AppCompatActivity.

@sshropshire
Copy link
Contributor

Hey @salosoft any update on this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants