braintree / braintree-android-drop-in

Braintree Drop-In SDK for Android
https://developers.braintreepayments.com/guides/drop-in/android/v2
MIT License
124 stars 79 forks source link

Regarding onDropInFailure: User Canceled Dropin #390

Closed rohitgarg29 closed 1 year ago

rohitgarg29 commented 1 year ago

Hi BrainTree Team,

I am working on resolving the CardinalComm SDK changes, I have updated the android-drop-in to latest version 6.6.0. I am facing one problem:

I am getting the following error on LaunchDropIn. onDropInFailure: brain tree error com.braintreepayments.api.UserCanceledException: User canceled DropIn. it is not even showing the select payment method dialog, "Google Pay, PayPal, Debit or Credit card one". Also I am not even canceling the or doing anything.

IsExplicitCancelation = false

Thanks in advance. As I am on time crunch, Please Help!

scannillo commented 1 year ago

👋 Hi @rohitgarg29 - thanks for using the Braintree Android Drop-In SDK.

This issue thread is for if think you've found an issue with our SDK.

Your issue sounds instead like you need help troubleshooting your integration, so we recommend you reach out to Braintree Support at https://help.braintreepayments.com. They will need steps to replicate your issue. Also, please refer to our integration documentation here, to make sure you have everything set up correctly.

If you still think there is a bug with our SDK, please let us know here!

rohitgarg29 commented 1 year ago

Hi @scannillo Thanks for replying.

I am not sure, Implementation is pretty straight forward. I am doing all whatever is required, What are the reasons for "IsExplicitCancelation = false"? I am posting the stacktrace as well here.

com.braintreepayments.api.UserCanceledException: User canceled DropIn.
W/System.err:     at com.braintreepayments.api.DropInActivityResultContract.parseResult(DropInActivityResultContract.java:39)
W/System.err:     at com.braintreepayments.api.DropInActivityResultContract.parseResult(DropInActivityResultContract.java:18)
W/System.err:     at androidx.activity.result.ActivityResultRegistry.doDispatch(ActivityResultRegistry.java:392)
W/System.err:     at androidx.activity.result.ActivityResultRegistry.dispatchResult(ActivityResultRegistry.java:351)
W/System.err:     at androidx.activity.ComponentActivity.onActivityResult(ComponentActivity.java:638)
W/System.err:     at androidx.fragment.app.FragmentActivity.onActivityResult(FragmentActivity.java:140)
W/System.err:     at android.app.Activity.dispatchActivityResult(Activity.java:8659)
W/System.err:     at android.app.ActivityThread.deliverResults(ActivityThread.java:5857)
W/System.err:     at android.app.ActivityThread.handleSendResult(ActivityThread.java:5903)
W/System.err:     at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:54)
W/System.err:     at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
W/System.err:     at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
W/System.err:     at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
W/System.err:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2438)
W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:106)
W/System.err:     at android.os.Looper.loopOnce(Looper.java:226)
W/System.err:     at android.os.Looper.loop(Looper.java:313)
W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:8663)
W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
W/System.err:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135) 
sshropshire commented 1 year ago

@rohitgarg29 can you share a code snippet of your integration and verify that you are instantiating DropInClient in onCreate()?

rohitgarg29 commented 1 year ago

Hi @sshropshire Authentication token is not empty, that I have already checked as well.

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        TealiumTracker.ctaViewBasket()
        TealiumTracker.checkout(
            EVENT_CHECKOUT_OPEN_STEP_NUMBER,
            EVENT_STEP_TITLE
        )
        ExponeaTracker.checkout(
            EVENT_CHECKOUT_OPEN_STEP_NUMBER,
            EVENT_STEP_TITLE
        )

        journeyHolder.apply {
            showTitle(true)
            title = getString(R.string.payment)
        }

        setUpTicketsAdapter()
        setUpViewPagerWithTabLayout()
        setUpViewPagerAdapter()
        setClickListeners()
        viewModel.clearPaymentToken()
        viewModel.isUserGuest()
        viewModel.listenUserLogin()
        viewModel.listenUserSignUp()
        viewModel.getPassengerTypes()

        dropInClient = DropInClient(this, this)
        dropInClient.setListener(this)
    }
override fun getClientToken(callback: ClientTokenCallback) {
        callback.onSuccess(authenticationToken)
    }
private fun startDropInActivity() {
        if (this::authenticationToken.isInitialized) {
            val dropInRequest = DropInRequest()
            dropInRequest.isVaultManagerEnabled = true
            dropInRequest.vaultCardDefaultValue = true

            val price = viewModel.totalPrice.value!!
            val gPayRequest = GooglePayRequest()
            gPayRequest.transactionInfo = TransactionInfo.newBuilder()
                .setTotalPrice(price.valueAsString())
                .setTotalPriceStatus(WalletConstants.TOTAL_PRICE_STATUS_FINAL)
                .setCurrencyCode(price.currency.currencyCode)
                .build()
            gPayRequest.isBillingAddressRequired = true

            if (!BuildConfig.DEBUG && BuildConfig.USE_GOOGLE_MERCHANT_ID && BuildConfig.GOOGLE_MERCHANT_ID.isNotEmpty()) {
                gPayRequest.googleMerchantId = BuildConfig.GOOGLE_MERCHANT_ID
            }
            dropInRequest.googlePayRequest = gPayRequest

            val address = ThreeDSecurePostalAddress()

            viewModel.userDetails?.let {
                if (!it.firstName.isBlank()) address.givenName = it.firstName
                if (!it.lastName.isBlank()) address.surname = it.lastName
                if (!it.phone.isBlank()) address.phoneNumber = it.phone
            }

            val additionalInformation = ThreeDSecureAdditionalInformation()
            additionalInformation.shippingAddress = address

            val threeDSecureRequest = ThreeDSecureRequest()
            threeDSecureRequest.amount = price.valueAsString()
            threeDSecureRequest.email = getEmail()
            threeDSecureRequest.billingAddress = address
            threeDSecureRequest.versionRequested = ThreeDSecureRequest.VERSION_2
            threeDSecureRequest.additionalInformation = additionalInformation

            dropInRequest.threeDSecureRequest = threeDSecureRequest

            dropInClient.launchDropIn(dropInRequest)

        } else {
            Toast.makeText(this, "Authentication Token Does not Exist.", Toast.LENGTH_SHORT).show()
        }
    }
override fun onDropInSuccess(dropInResult: DropInResult) {
        viewModel.setBrainTreeResult(dropInResult)
    }

    override fun onDropInFailure(error: java.lang.Exception) {
        Log.v("Error", error.stackTrace.contentDeepToString())
    }
scannillo commented 1 year ago

This also sounds like a similar issue. Can you include a snippet of your AndroidManifest (see docs)?

rohitgarg29 commented 1 year ago

Hi @scannillo @sshropshire

applicationId is "com.company" but while publishing, we are adding applicationIdSuffix ".bus" for release. Payment is sub-module here.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.company.payment">

    <application>
<activity
            android:name="com.company.payment.checkoutflow.ui.CheckoutActivity"
            android:label="@string/app_name"
            android:screenOrientation="portrait"
            android:exported="true"
            android:theme="@style/SearchJourneyTheme" />
</application>
</manifest>

Update: I tried adding as suggested but it is still giving me the same error.

<activity
            android:name=".checkoutflow.ui.CheckoutActivity"
            android:label="@string/app_name"
            android:screenOrientation="portrait"
            android:exported="true"
            android:launchMode="singleTask"
            android:theme="@style/SearchJourneyTheme" />
        <activity
            android:name="com.braintreepayments.api.DropInActivity"
            android:exported="true"
            tools:node="merge">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <data android:scheme="${applicationId}.braintree" />

                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
            </intent-filter>
        </activity>

I have tried com.company and com.company.payment as well instead of ${applicationId}

sshropshire commented 1 year ago

@rohitgarg29 Can you take a look at the manifest merger for your app and see what the final android:scheme value is for DropInActivity?

rohitgarg29 commented 1 year ago

@sshropshire its value is android:scheme = com.company.debug.braintree

rohitgarg29 commented 1 year ago

image

sshropshire commented 1 year ago

@rohitgarg29 thank you. That looks fine.

Can you update your client token provider to make sure authorization is not null?

Example:

override fun getClientToken(callback: ClientTokenCallback) {
  callback.onSuccess(requireNotNull(authenticationToken))
}
rohitgarg29 commented 1 year ago

@sshropshire I did the same as you suggested, it is still same.

sshropshire commented 1 year ago

@rohitgarg29 the only scenario I can think of where DropIn cancels before displaying any UI is if the authorization is invalid. Can you provide a video reproducing the error in your app to help provide more context?

rohitgarg29 commented 1 year ago

@sshropshire I was thinking that it should work with sandbox authorization string like demo, should I try that?

sshropshire commented 1 year ago

@rohitgarg29 you can either a tokenization key or a client token will work, although if you're using a tokenization key you can use the DropInClient(this, "<TOKENIZATION_KEY>") constructor and won't need a ClientTokenProvider since it is already synchronously available to you.

rohitgarg29 commented 1 year ago

@sshropshire i am trying in this way now

1.) Below code is in onCreate()

dropInClient = DropInClient(this, "sandbox_tmxhyf7d_dcpspy2brwdjr3qn")
dropInClient.setListener(this)
  1. I have removed the callback

But i am still getting the same error.

bendoddvista commented 1 year ago

We had a problem with this.

I solved it in two ways.

  1. When you launch the DropIn, recreate the dropInSubject to clear any current/historical error messages. EG: dropInSubject = PublishSubject.create();
  2. When you handle the Error, you need to ignore it for UserCanceledException. It's not really an exception. In our example, a user just clicked out of the bounds of the fragment.
hollabaq86 commented 1 year ago

@rohitgarg29 did @bendoddvista's suggestion resolve your issue?

rohitgarg29 commented 1 year ago

@hollabaq86 I was able to resolve it using the solution provided in #379 . Thanks for the help.