Open soarb opened 1 year ago
Hey @soarb thanks for filing this. Are there multiple build variants present on this test device? Also does the same thing happen on an emulator?
Thanks for getting back to me @sshropshire ... there's only ever a single build variant on the test device and I tend not to use emulators as I like to see things working on the real thing - especially on Android!
I just don't quite understand how the 3DS browser switch back to the device works, but the PayPal browser switch throws up this dialog and then errors with a UserCanceledException if I tap "Just Once"?
@soarb understood! A real device is the best way to test. I want to rule out some possibilities here though, I'm wondering if this issue could have something to do with a specific device. I'm testing on a Samsung Galaxy S21 5G and I don't get the disambiguation dialog.
I'm not sure what would cause "Just Once" to fail either. If possible, I'd ⌘ + Shift + O
in Android Studio to do a symbol search for DropInInternalClient
, set a breakpoint here, and reproduce the issue to inspect URL from the deep link intent.
Hi @sshropshire,
I can confirm we have the same issue on the following test devices:-
Samsung S22 OS 13 Pixel 3 with Android 12 Pixel 6 pro with Android 13
I'll report back when I've debugged the URL from the deep link intent though :)
So in all cases @sshropshire, when choosing our App and the "Just Once" option the PayPalClient's onBrowserSwitchResult.deepLinkUrl is always null. This is the case on repeated attempts.
The only time the url resolves to a value is when selecting "Always" in which case we get the following:-
{our app's package name}.braintree://onetouch/v1/success?paymentId=PAYID-MRJ66YI8H858885D1798690T&token=EC-07G27203YY045835S&PayerID=UGR2UJXVNXW7W
and everything works.
In order to recreate this, please ensure whatever app you're testing on, is removed from your device's Settings -> Apps -> Choose default apps -> Opening links -> {App Name} by clicking on "Clear defaults". This is on our S10 so the location may differ slightly.
With this setting removed, we get the OS prompt about how the App should handle links in the future. Selecting "Just Once" always returns a null url, selecting "Always" works.
It's worth reiterating, that when we trigger 3DS 2FA via our custom checkout (which performs a similar browser switch) we don't go via the drop-in. Instead we use an instance of ThreeDSecureClient. Once the browser switch is complete, we're returned to our App and everything works as expected (with no OS prompt).
Hi @sshropshire, any update on this please? I can also add that dismissing the OS prompt leaves us "stuck" on the PayPal "One more step" phase in the browser screen. We never get returned to our app unless we just close the screen. I can provide a video if required?
I know this doesn't help much but the drop-in experience on iOS performs without any issue whatsoever.
Hi, I'm having the same issue when using the DropInClient to request to vault a PayPal account, here is the code (kotlin) that I used on a test jetpack compose app:
class MainActivity : FragmentActivity(), ClientTokenProvider, DropInListener {
private lateinit var braintreeDropInClient: DropInClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
braintreeDropInClient = DropInClient(
this,
this
)
braintreeDropInClient.setListener(this)
setContent {
TestTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Text("Hello")
Button(content = {
Text("test")
}, onClick = {
launchBraintreeDropIn()
})
}
}
}
}
fun launchBraintreeDropIn() {
braintreeDropInClient.invalidateClientToken()
val request = DropInRequest().apply {
isCardDisabled = true
isVaultManagerEnabled = false
payPalRequest = PayPalVaultRequest().apply {
billingAgreementDescription = "."
}
}
braintreeDropInClient.launchDropIn(request);
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
setIntent(intent) // Breakpoint here
}
override fun getClientToken(callback: ClientTokenCallback) {
callback.onSuccess("token_from_server")
}
override fun onDropInSuccess(dropInResult: DropInResult) {
TODO("Not yet implemented") // Breakpoint here
}
override fun onDropInFailure(error: Exception) {
TODO("Not yet implemented") // Breakpoint here (execution stop there first)
}
}
manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Test"
tools:targetApi="34">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleInstance"
android:label="@string/app_name"
android:theme="@style/Theme.Test">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="${applicationId}.braintree" />
</intent-filter>
</activity>
</application>
</manifest>
The breakpoint on setIntent shows that the data of the intent contains this url: (app_id).braintree://onetouch/v1/success?ba_token=BA-1234567890ABCDEFG
I'm not sure of anything, but from what i understand, it looks like something like this happen:
Versions of what I use:
I hope thank we can find a solution, don't hesitate to tell me how I can help you investigate.
@sshropshire Hi any update on this? I need to bump braintree SDK in my project
Hi @Chaos2805 @kevin68 @soarb apologies for the extreme delay. I'm circling back and I believe I know the root cause of this issue, but will need verification to make sure as I'm unable to reproduce on my end.
There seems to be an error in the documentation. For DropIn, you won't need to register an intent filter. We launch DropInActivity
internally, and this activity is registered as the deep link target.
It appears that when the DropIn library's AndroidManifest.xml
is merged with the manifest of the host application, it's possible for duplicate intent filters to be created. This will cause the Android OS to disambiguate the next course of action by requesting the user's preference with a system popup dialog.
If this resolves the issue, I'll update the docs to remove this additional unnecessary step.
@sshropshire It's working after removing the intent-filter, thanks !
@sshropshire It didn't solve my issue, if I remove the intent-filter, drop-in activity not receiving the deeplink. For my case, I upgrade drop-in SDK from 6.5.0 to 6.11.0, and i found that previously BraintreeClient will convert default params to lowercase, and now it removed. In my application the applicationId contains capital letter, I think the scheme is not accepting capital case, since it prompt AppLinkUrlError if I put capital for android:scheme. @sshropshire Can you try with a capitalized applicationId to reproduce this?
I found the BrainTreeClient constructor able to pass the custom url scheme, but on top DropInClient not able to modify the setting of BrainTreeClient
Braintree SDK Version
4.27.0
Environment
Sandbox
Android Version & Device
Samsung S10 with Android 12
Braintree dependencies
com.braintreepayments.api:drop-in:6.9.0
Describe the bug
On completion of the PayPal flow triggered by the drop-in, the device presents two options. Our App name - "Just Once" and our App name - "Always".
I assume this is the OS asking the user if our App should be the default App for handling all links of a specific type?
Selecting "Just Once" fires the drop-in onFailure callback with a UserCanceled error?
The only way to get an instance of PayPalAccountNonce to charge the customer with (again this is in Sandbox) is to select "Always".
I'm not even sure why these two options get presented at all by the OS?
Completing a similar browser switch to perform 3DS 2FA auth immediately returns to our App on success or cancellation, no prompt to set the default App is presented.
To reproduce
Present the drop-in UI. Include a PayPalCheckoutRequest instance configured with the correct order total and a request to present the PayPal PayIn3 option.
Complete the PayPal sign in flow, select a test payment instrument and select the "Just Once" option when the OS presents it.
Get returned to our App, but our logs clearly show that the drop-in onFailure method was called with a UserCanceledException.
Expected behavior
The drop-in UI doesn't error with a UserCanceled exception. Instead a PayPalAccountNonce is returned with which we can complete the Sandbox transaction.
Also ideally, the OS shouldn't be presenting the user with the two options requesting how supported links should be handled in the future. The 3DS flow doesn't do this.
Screenshots
Here's a link to a video I posted demonstrating the issue.