最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Braintree multiple setup calls yield in multiple onPaymentMethodReceived events - Stack Overflow

programmeradmin0浏览0评论

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.

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.

Share Improve this question edited May 18, 2015 at 22:28 PascalVKooten 21.5k18 gold badges113 silver badges167 bronze badges asked May 18, 2015 at 22:26 William Martínez PomaresWilliam Martínez Pomares 1097 bronze badges 2
  • I work at Braintree. We're working on improving this; there isn't a great, simple solution for this at the moment. If you get in touch with our support team they should be able to help you. – agf Commented May 19, 2015 at 18:40
  • 1 Any update on this, @agf? – d.jamison Commented Jul 29, 2015 at 18:20
Add a ment  | 

3 Answers 3

Reset to default 2

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.

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 mon 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./guides/client-sdk/javascript/v2#teardown

(I haven't tried it yet)

The link given by Arpad Tamas does not contain the info anymore. So I am posting the info given by BrainTree for posterity ;) Especially since it took me a few tries to find it with a Google search.

In certain scenarios you may need to remove your Braintree.js integration. This is mon in single page applications, modal flows, and other situations where state management is a key factor. When calling braintree.setup, you can attach a callback to onReady which will provide an object containing a teardown method.

Invoking teardown will clean up any DOM nodes, event handlers, popups and/or iframes that have been created by the integration. Additionally, teardown accepts a callback which you can use to know when it is safe to proceed.

var checkout;

braintree.setup('CLIENT_TOKEN_FROM_SERVER', 'dropin', {
  onReady: function (integration) {
    checkout = integration;
  }
});

// When you are ready to tear down your integration
checkout.teardown(function () {
  checkout = null;
  // braintree.setup can safely be run again!
});

You can only invoke teardown once per .setup call. If you happen to call this method while another teardown is in progress, you'll receive an error stating Cannot call teardown while in progress. Once pleted, subsequent calls to teardown will throw an error with this message: Cannot teardown integration more than once.

I've wrapped this code in a function that I call each time the related checkout ionic view is entered.

$scope.$on('$ionicView.enter', function() {
    ctrl.setBraintree(CLIENT_TOKEN_FROM_SERVER);
});

var checkout;

ctrl.setBrainTree = function (token) {
    braintree.setup(token, "dropin", {
        container: "dropin-container",

        onReady: function (integration) {
            checkout = integration;
            $scope.$emit('BTReady');
        },

        onPaymentMethodReceived: function(result) {
            ...
        },

        onError: function(type) {
            ...
        }
    });

    // Prevents a call to checkout when entering the view for the first time (not initialized yet).
    if (checkout) {
    // When you are ready to tear down your integration
        checkout.teardown(function () {
            checkout = null; // braintree.setup can safely be run again!
        });
    }
};
发布评论

评论列表(0)

  1. 暂无评论