Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Braintree multiple setup calls yield in multiple onPaymentMethodReceived events

I'm using angular, and in an angularUI modal window I want to show the Drop In form from Braintree to get a payment method. Thus, I create the usual form (partial.html):

<form id="creditCard" >
   <div id="dropin"></div>  
   <button type="submit" id="btnPay" >Pay</button>  
</form>

and then I show the modal with this:

var modalInstance = $modal.open({
   templateUrl: 'partial.html',
   controller: 'ModalController'
});

Where ModalController contains the call to the Braintree setup:

braintree.setup($scope.clientToken, 'dropin', {
   container: 'dropin',
   onPaymentMethodReceived: function (result) {
       $scope.$apply(function() {
           $scope.success = true;
           // Do something else with result
       });
   }
});

This will show the Drop In form from braintree nicely (the setup generates the form) and accept the credit card and expiration date, all working fine so far.

The problem is, each time I call the modal, the ModalController is executed, and thus the braintree.setup() is also executed. Then, when I enter the credit card number and the expiration date and hit pay, the onPaymentMethodReceived() event is triggered once per setup execution! That is, if the first time I call the modal it will trigger the event once, the second time it will trigger it twice, and so on. Like if each time I call setup, a new hook to the event is created.

Any idea on how to avoid this? Is there a way to "unbind" the onPaymentMethodReceived() event handler? I do need to call the setup several times since each time I call the modal, the clientToken may have changed.

Thanks for any help or pointer to help.

like image 688
William Martínez Pomares Avatar asked May 18 '15 22:05

William Martínez Pomares


2 Answers

Calling braintree.setup multiple times in angular seems unavoidable, either for the asker's reasons, or simply because setup is called in a controller that may be instantiated multiple times in a browsing session – like a cart or checkout controller.

You can do something like this:

$rootScope.success = false;
braintree.setup($scope.clientToken, 'dropin', {
   container: 'dropin',
   onPaymentMethodReceived: function (result) {
       if(!$rootScope.success) {
           $scope.$apply(function() {
               $rootScope.success = true;
               // Do something else with result
           });
       }
   }
});

I found I wasn't able to avoid having the callback fire multiple times (the number of times seems to explode each time I revisit the view - yikes), but I could test whether I had performed my actions in response to the callback. Since the $scope will be destroyed if I leave the view, $scope.success is effectively reset when I need it to be. Because each new controller will have its own $scope, setting a success flag on the $scope may only halt additional executions on that $scope (which seems to still be available to the callback, even if the controller has been "destroyed"), so I found that using $rootScope meant only one execution total, even if I re-instantiated the controller multiple times. Setting $rootScope.success = false in the controller means that once the controller is loaded, the callback will succeed anew – once.

like image 142
d.jamison Avatar answered Nov 16 '22 05:11

d.jamison


I think it's handled by the API since then with teardown:

In certain scenarios you may need to remove your braintree.js integration. This is common in single page applications, modal flows, and other situations where state management is a key factor. [...] Invoking teardown will clean up any DOM nodes, event handlers, popups and/or iframes that have been created by the integration.

https://developers.braintreepayments.com/guides/client-sdk/javascript/v2#teardown

(I haven't tried it yet)

like image 1
Árpád Tamás Avatar answered Nov 16 '22 06:11

Árpád Tamás