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

6.5.0 DropInClient - Method addObserver must be called on the main thread #374

Open
molchanovskiy opened this issue Dec 7, 2022 · 18 comments

Comments

@molchanovskiy
Copy link

I am working on react native module for DropIn and getting this error on initialization of DropInClient

implementation "com.braintreepayments.api:drop-in:6.5.0

         val dropInClient = DropInClient(currentActivity as AppCompatActivity?,clientToken)
         

Method addObserver must be called on the main thread

Why this error can appear?

@sshropshire
Copy link
Contributor

Hi @molchanovskiy thanks for using the Braintree SDK for Android. This limitation exists because we use the underlying Jetpack Lifecycle#addObserver() method internally, which imposes the @MainThread restriction.

Are you able to instantiate a DropIn client in your Activity / Fragment's onCreate() method?

@molchanovskiy
Copy link
Author

Hi @sshropshire
Got it, thank you! I will try to resolve it and give you feedback.

@molchanovskiy
Copy link
Author

@sshropshire
I am not sure if it is possible to instantiate a DropIn client in my MainActivity onCreate() method because clientToken is dynamic our app is passing it to braintree module on checkout process.

Here is our module which should open DropIn on user click Pay button

class RNBrainTreeModule constructor (
    reactContext: ReactApplicationContext
) : ReactContextBaseJavaModule(reactContext) {

  override fun getName(): String {
        return "RNBrainTreeModule"
    }
    
    
      @ReactMethod()
    fun onClick(options: ReadableMap, promise: Promise) {
val dropInRequest = DropInRequest()
.......
.......
            val dropInClient = DropInClient(
                currentActivity as AppCompatActivity?,
                options.getString("clientToken")
            )
            dropInClient.setListener(PaymentDropInListener())
            dropInClient.launchDropIn(dropInRequest)
}
}

@sshropshire
Copy link
Contributor

Correct yes. One option is to asynchronously fetch a client token and forward it via bundle args, although this can be tricker.

The best option here is to instantiate a DropInClient using your own implementation of a ClientTokenProvider. Here is an example in our migration docs. This allows the SDK to fetch a client token on your behalf when it is needed.

@wayson
Copy link

wayson commented Dec 7, 2022

@sshropshire The new change makes react-native development a lot harder. Because my current flow is let our backend API to return customer braintree token and let the DropInClient launch the dropin UI by token in v5. But it requires a provider to retrieve the customer braintree token in native Android/iOS instead of JS thread in v6, which will double the work for mobile cross platform tools.

Will Braintree Android SDK implement the same way of dropin UI as v5 in future? Or Braintree Android/iOS dropin SDK no longer consider mobile cross platform development tools in terms of usability and ease of use?

@wayson
Copy link

wayson commented Dec 7, 2022

@molchanovskiy You can try the old way to call something onCreate() in MainActivity. facebook/react-native#3334 (comment)

@molchanovskiy
Copy link
Author

Hi @sshropshire

Is there any way to update DropInClient token on already instantiated client? I mean if client is instantiated in my MainActivity onCreate() method and later to update token from react native module?

@sshropshire
Copy link
Contributor

Is there any way to update DropInClient token on already instantiated client?

@molchanovskiy at the moment no. Internally the SDK needs the client token to make backend calls on the merchant's behalf. Adding this feature could create a ton of race conditions for the SDK.

@sshropshire
Copy link
Contributor

Will Braintree Android SDK implement the same way of dropin UI as v5 in future? Or Braintree Android/iOS dropin SDK no longer consider mobile cross platform development tools in terms of usability and ease of use?

@wayson the old way of instantiating a DropInClient is technically still supported (see constructor here). The authorization parameter can be either a tokenization key or a client token. Does this help?

@wayson
Copy link

wayson commented Dec 11, 2022

@sshropshire Thanks for your reply. I've tried that but DropInClient can only be called in onCreate() on Activity. Otherwise we will have error of Method addObserver must be called on the main thread.

I've also tried

currentActivity.runOnUiThread(new Runnable() {
    @Override
    public void run() {
        // initialize DropInClient here
    }
});

But the app just crash straight away after this call.

@molchanovskiy
Copy link
Author

molchanovskiy commented Dec 12, 2022

@wayson
I found a solution. You can init the drop in client with an empty token in onCreate() of Activity with shared instance and then use it inside the module by providing token and calling invalidateClientToken which will trigger fetch client token callback.

Activity onCreate():

      val dropInClient = DropInClient(this, ClientTokenProvider { callback ->
            // fetch client token asynchronously...
            callback.onSuccess(BraintreeSharedInstance.instance.clientToken);
        })
        BraintreeSharedInstance.instance.dropInClient = dropInClient
    Module call:
        BraintreeSharedInstance.instance.clientToken = token
          val dropInClient:DropInClient? = BraintreeSharedInstance.instance.dropInClient
          dropInClient?.invalidateClientToken()
          dropInClient?.setListener(PaymentDropInListener())
          dropInClient?.launchDropIn(dropInRequest)
        

@Kowshika-aspire
Copy link

Hi @molchanovskiy @sshropshire

I have followed the same solution which is given by @molchanovskiy. But the problem I'm facing is, dropInClient.setListener is not triggered once I completed the payment. Both onDropInSuccess and onDropInFailure is not triggered.

My Mainactivity snippet:

  public static DropInClient client = null;
  public static String token = "";

client=new DropInClient(this, new ClientTokenProvider() {
           @Override
           public void getClientToken(@NonNull ClientTokenCallback callback) {
               callback.onSuccess(token);
           }
       });

In module call:

DropInClient dropInClient = MainActivity.client;
       dropInClient.invalidateClientToken();
       dropInClient.setListener(new DropInListener() {
           @Override
           public void onDropInSuccess(@NonNull DropInResult dropInResult) {
               resolvePayment(dropInResult, jsPromise);
           }

           @Override
           public void onDropInFailure(@NonNull Exception error) {
               if (error.hashCode() == Activity.RESULT_CANCELED) {
                   jsPromise.reject("USER_CANCELLATION", "The user cancelled");
               } else {
                   jsPromise.reject(error.getMessage(), error.getMessage());
               }

           }
       });
       dropInClient.launchDropIn(dropInRequest);

Please look into this issue.

@sshropshire
Copy link
Contributor

@Kowshika-aspire I can see now how this makes it difficult to use DropIn with React Native.

We may have to consider this as a feature request for our next major version. Would the ideal DropIn API for React Native be to allow authorization as a DropInRequest parameter? Also are you able to use 5.x in the meantime?

@Kowshika-aspire
Copy link

@Kowshika-aspire I can see now how this makes it difficult to use DropIn with React Native.

We may have to consider this as a feature request for our next major version. Would the ideal DropIn API for React Native be to allow authorization as a DropInRequest parameter? Also are you able to use 5.x in the meantime?

@sshropshire Thanks for the reply. Currently, We are using 5.3.0 which has cardinal SDK issue. So, We need to update drop-in version to the latest one as you suggested on this PR Click here

Can you suggest any solution to resolve the issue?

@zhenghow93
Copy link

I am also trying to upgrade the drop-in library in our RN app to 6.5.0, due to cardinal SDK issue.
Thanks to @molchanovskiy , I finally am able to bring up the drop in UI.
However, similar to @Kowshika-aspire 's issue, the callbacks in the listener isn't called after payment is completed.
I have tried the following separately, but all didn't work:

  • implement DropInListener in the MainActivity and set the listener right after DropInClient initialisation in the onCreate method of MainActivity .setListener(this);
  • create a listener subclass that implements DropInListener and set the listener in the onCreate method of MainActivity .setListener(new BTDropInListener());
  • re-set the listener in the module code after .invalidateClientToken()

I'm writing in Java and it didn't work, while @molchanovskiy seems to be able to get it working in Kotlin.

@sshropshire
Copy link
Contributor

@Kowshika-aspire @zhenghow93 we've released 5.4.0 to help in the meantime while migrating to 6.x.

@zhenghow93 it is ideal that the listener is set shortly after the DropInClient instance is instantiated in the host Activity's (or Fragment's) onCreate method. This is required by the activity result API we use internally. We may investigate some de-abstraction in the future. One of the goals of our SDK is to support a consistent Developer Experience across all platforms.

Is there an ideal API for DropIn on React Native we should consider for our next major version? It seems like the Activity lifecycle are unavailable at the JavaScript level?

@zhenghow93
Copy link

@sshropshire thanks for your prompt reply. Is v5.4.0 using compliant cardinal SDK?

@sshropshire
Copy link
Contributor

@zhenghow93 yes. We made a small patch to include the latest version of the Cardinal SDK to help extend the migration window for merchants.

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

5 participants