braintree_android
braintree_android copied to clipboard
ThreeDSecureClient it never finish instantiation
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:
- Fetch your Token from API
- 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)
- Run the app in debug
- Observe the
threeDClient!!.setListener(this)
is never reached
ThreeDSecureClient is instantiate but onThreeDSecureSuccess
or onThreeDSecureFailure
never triggered
Inside an Activity:
- Fetch your Token from API
- 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)
- 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)
}
}
- The verification Code will be displayed
- Type your code.
- Observe the screen will be dismissed but either
onThreeDSecureSuccess
oronThreeDSecureFailure
will be triggered.
Expected behavior
- ThreeDSecureRequest is initialized when passing the activity on the constructor
- When instantiate
ThreeDSecureRequest
with deprecated method, the listenersonThreeDSecureSuccess
oronThreeDSecureFailure
will be called when returning to the app, after adding the Confirmation Code.
Screenshots
No response
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?
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?
hello @salosoft, Did you mean on #8, neither "onThreeDSecureSuccess or onThreeDSecureFailure will be triggered"?
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:
- When I enter a correct code it return to the app and doesn't trigger
onThreeDSecureSuccess
- 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.

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.
Hi @salosoft does your BaseActivity
inherit from AppCompatActivity
?
Hi @sshropshire It inherit from my class BaseActivity
that only then inherit from AppCompatActivity
@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
.
Hey @salosoft any update on this?
Hey @salosoft! We just released a beta for the next major version, v5.
In this new version, we updated the SDK callback interface. We'd love for you to try out the new version and provide feedback.
v5 Migration Guide: https://github.com/braintree/braintree_android/blob/main/v5_MIGRATION_GUIDE.md v5 Release: https://github.com/braintree/braintree_android/releases/tag/5.0.0-beta1