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

braintreeClient.deliverBrowserSwitchResult set the Activity intent to 'null' #634

Closed
skauss opened this issue Dec 16, 2022 · 15 comments
Closed

Comments

@skauss
Copy link
Contributor

skauss commented Dec 16, 2022

Integration Details (please complete the following information):

  • SDK/Library version: com.braintreepayments.api:drop-in:6.4.0
  • Environment: Sandbox or Production
  • Android Version and Device: Android all version

Hi
I have to implement Braintree with browser switch.
Code to start the browser switch

     browserSwitchOptions.returnUrlScheme(urlScheme);
     browserSwitchOptions.url( Uri.parse(urlToLoad));  
     BrowserSwitchClient browserSwitchClient = new BrowserSwitchClient();
     browserSwitchClient.start(activity,browserSwitchOptions);

To get the result intent

   @Override
    protected void onNewIntent(Intent newIntent) {
        setIntent(newIntent);
        super.onNewIntent(newIntent);
    }

In onResume I check the BrowserSwitch result

BrowserSwitchResult browserSwitchResult = braintreeClient.deliverBrowserSwitchResult(activity);
 // now the intent is null

if (browserSwitchResult != null) {
    if (browserSwitchResult.getStatus() == BrowserSwitchStatus.SUCCESS) {
        String returnUrl = browserSwitchResult.getDeepLinkUrl() != null ? browserSwitchResult.getDeepLinkUrl().toString() : "";
         onPayPalPlusSuccess( activity, null, returnUrl);
     } else {
         onPayPalPlusFailure(activity,null);
      }

Inside the BraintreeClient deliverBrowserSwitchResult line 123 the intent of the activity is set to null

 public BrowserSwitchResult deliverResult(@NonNull FragmentActivity activity) {
        BrowserSwitchResult result = getResult(activity);
        if (result != null) {

            Context appContext = activity.getApplicationContext();
            BrowserSwitchRequest request = persistentStore.getActiveRequest(appContext);

            @BrowserSwitchStatus int status = result.getStatus();
            switch (status) {
                case BrowserSwitchStatus.SUCCESS:
                    // ensure that success result is delivered exactly once
                    persistentStore.clearActiveRequest(appContext);

                    // clear activity intent to prevent deep links from being parsed multiple times
                    activity.setIntent(null);
                    break;
                case BrowserSwitchStatus.CANCELED:
                    // ensure that cancellation result is delivered exactly once, but allow for
                    // a cancellation result to remain in shared storage in case it
                    // later becomes successful
                    request.setShouldNotifyCancellation(false);
                    persistentStore.putActiveRequest(request, activity);
                    break;
            }
        }
        return result;
    }

The following code which checks the intent now is missing his information.
The BraintreeClient should only remove the intent data it needs.

@sshropshire
Copy link
Contributor

@skauss thanks for using the Braintree SDK for Android. The browser switch happens internally in DropIn. We clear the intent to make sure a browser switch SUCCESS result is delivered only once.

I'd like to know more about your use case. Is there a reason you need your own BrowserSwitchClient instead of allowing DropInClient to use the one it has internally?

@skauss
Copy link
Contributor Author

skauss commented Dec 19, 2022

I need to do a setIntent in onNewIntent to get the intent to braintreeClient.deliverBrowserSwitchResult in onResume

All data which start my activity are gone if the deliverResult do an activity.setIntent(null);

The BrowserSwitchClient getResult (around line 160) check only the UrlScheme

 Uri deepLinkUrl = intent.getData();
        if (deepLinkUrl != null && request.matchesDeepLinkUrlScheme(deepLinkUrl)) {
            result = new BrowserSwitchResult(BrowserSwitchStatus.SUCCESS, request, deepLinkUrl);
        } else if (request.getShouldNotifyCancellation()) {
            result = new BrowserSwitchResult(BrowserSwitchStatus.CANCELED, request);
        }

To work only with scheme in not enough.

In the old BrainTree client we have a succes and cancel path on the return path
like
${lowerApplicationId}.ppp://return_url
${lowerApplicationId}.ppp://cancel_url

In your case you it would help if you reset the data
intent.setData(null);

Then all other data from the Activity survive an payment.

Our use case

PayPalPlus

@skauss
Copy link
Contributor Author

skauss commented Dec 19, 2022

For this I need a clientMetaId

@sshropshire
Copy link
Contributor

Hey @skauss I see your point. Setting the activity's Intent to null may be a bug in this case. I've made a PR for this to update the library. Does this resolve your issue? braintree/browser-switch-android#59

@sshropshire
Copy link
Contributor

For this I need a clientMetaId

We can figure out a separate fix for clientMetadataId we can add a getter for this information if needed.

@skauss
Copy link
Contributor Author

skauss commented Dec 19, 2022

Hi @sshropshire
I check the fix tomorrow
Thanks
Stephan

@skauss
Copy link
Contributor Author

skauss commented Dec 19, 2022

I am looking forward for the clientMetadataId Fix :-)

@skauss
Copy link
Contributor Author

skauss commented Dec 20, 2022

@sshropshire
Is there any release plan of the drop-in SDK ?
I ask this because we have to update the drop-in:5.2.1 SDK until end of January in case of Cardinal Mobile SDK which is used inside of version 5.2.1

@sshropshire
Copy link
Contributor

@skauss this is now available in version 4.23.0 of the Braintree SDK and version 6.6.0 of our DropIn SDK.

I did draft a PR for adding a clientMetadataId accessor, but I would like to get some more testing done internally to make sure this is the best approach.

In the meantime, you can use the following code snippet to extract the clientMetadataId from DropInResult:

fun onDropInSuccess(dropInResult: DropInResult) {
  val deviceDataJSON = JSONObject(dropInResult.deviceData)
  val clientMetadataId = deviceDataJSON.getString("correlation_id")
}

Let us know if this helps!

@skauss
Copy link
Contributor Author

skauss commented Dec 23, 2022

@sshropshire
I take a short look into the
public DropInClient(FragmentActivity activity, String authorization)

To create a DropInClient I need an authorization or clientTokenProvider which I didn't have.

val dropInClient = DropInClient(this, "<#CLIENT_AUTHORIZATION#>")
dropInClient.setListener(this)
dropInClient.launchDropIn(dropInRequest

Or is there some pice of code which I didn't found.

@sshropshire
Copy link
Contributor

@skauss that's correct a ClientTokenProvider allows the SDK to fetch a client token on demand. This allows a DropInClient to be instantiated in onCreate(). Is your integration working now?

@skauss
Copy link
Contributor Author

skauss commented Jan 5, 2023

@sshropshire
Hi
I have no "<#CLIENT_AUTHORIZATION#>" and have no idea where to get one.

In the flowchart "Our use case" in point #4 the JS Application ask me for an clientMetaId, which I can't deliver because I have no "<#CLIENT_AUTHORIZATION#>".
And it is not an option to change the flow (flowchart)

In my client I have no problems to fetch the clientMetaId for PaypalVault or PayPalCheckout ( in this case I use BraintreeClient with an authorization I get from the JS Application).

Just now for PayPalPlus (BrowserSwitch from my flowchart (PayPalPlus is our name for this payment flow)) I use an clientMetaId like this :

clientMetaDataId = UUID.randomUUID().toString();

The BrowserSwitch is started this way :

     BrowserSwitchOptions browserSwitchOptions = new BrowserSwitchOptions();
     browserSwitchOptions.returnUrlScheme(urlScheme);
     browserSwitchOptions.url( Uri.parse(urlToLoad)); 
                   
     browserSwitchClient = new BrowserSwitchClient();
     browserSwitchClient.start(activity,browserSwitchOptions);

Which work in the SandBox environment.

Would this work also in production ?

We need to deliver a new version of our app at the end of January to the Google Play Store.
At the moment I see the risk we have to disable the PayPal payment for this release.

@sshropshire
Copy link
Contributor

@skauss take a look at this doc for a reference on obtaining a client token.

As for the clientMetaDataId, you'll want the same unique id generated by the SDK because it is correlated with each transaction for risk analysis. As a workaround for now, it can be parsed in the onDropInSuccess callback (as mentioned here).

@skauss
Copy link
Contributor Author

skauss commented Jan 5, 2023

@sshropshire
Hi
for a onDropInSuccess callback I need some kind of client (BraintreeClient with PayPalCheckoutRequest or BraintreeClient with PayPalVaultRequest or ...)

In our scenario the Java App give a clientMetaDataId to the JavaScript payment application which do some magic stuff and than I get an callback to do a BrowserSwitch.

In case of success I send an JavaScript event to our basket application with the clientMetaId.

As I notice before in my sandbox environment I create an random string (UUID.randomUUID().toString()).

This work in my BrainTree sandbox environment, will this be work also in the production environment ?

@sshropshire
Copy link
Contributor

Hi @skauss a random UUID may not work. I believe the UUID needs to match the one produced by the DropIn SDK.

Also, the fix for this issue is now available if you upgrade to the latest 4.23.1 version. Can you file a separate issue for this in our DropIn repo? I'm going to close this one for now to mark it as done.

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

No branches or pull requests

2 participants