Angular Stripe - Converting Stripe Payment Form to Stripe Elements
C

1

6

I used to have Angular Stripe Checkout form and I am trying to update my form to the new Stripe Card Elements which was introduced recently.

After removing the Form Input fields and replacing them with the Stripe Card element, my form looks like this:

<form name="payment" ng-submit="vm.submit()">
<div class="row">
    <label for="card-element">
      Credit or debit card
    </label>
    <div id="card-element">
      <!-- a Stripe Element will be inserted here. -->
    </div>
</div>
<button class="btn btn-primary" type="submit" ng-disabled="vm.submitting">Pay!</button>
<div ng-show="vm.cardError" class="row">
  <div class="has-error">
     <p class="help-block">* {{vm.cardError}}</p>
  </div>
</div>
</form>

Previously in Angular, when the form is submitted, I was handling the submit() and stripeResponseHandler from the controller. After updating my Angular Controller with the new changes, my controller now looks like this:

function PaymentController() {
        var vm = this;
        var elements = stripe.elements();
        var style = {
                          base: {
                            color: '#32325d',
                            lineHeight: '24px',
                            fontFamily: 'Helvetica Neue',
                            fontSmoothing: 'antialiased',
                            fontSize: '16px',
                            '::placeholder': {
                              color: '#aab7c4'
                            }
                          },
                          invalid: {
                            color: '#fa755a',
                            iconColor: '#fa755a'
                          }
                     };
        vm.card = elements.create('card', {style: style});
        vm.card.mount('#card-element');

        // Handle real-time validation errors from the card Element.
        vm.card.addEventListener('change', function(event) {
              if (event.error) {
                vm.cardError = event.error.message;
              } else {
                vm.cardError = '';
              }
        });

        function submit() {
            vm.cardError = '';
            vm.submitting = true;
            createToken();
        }

        // Send data directly to stripe 
        function createToken() {
            stripe.createToken(vm.card).then(function(result) {
              if (result.error) {
                vm.cardError = result.error.message;
                vm.submitting = false;
              } else {
                // Send the token to your server
                stripeTokenHandler(result.token);
              }
            });
        }

        // Response Handler callback to handle the response from Stripe server
        function stripeTokenHandler(token) {
            vm.tokenData = {
                token: token.id
            };
            .. Process the rest in server ...
        }
}

The above code works as is. But I am confused on these:

1) Since Stripe now uses DOM Manipulation to insert the card elements inside the form, does that make my above method wrong as in Angular way? Meaning, should I not be doing these in Controller anymore and move them to a directive instead? Or should that not be necessary since the element manipulated is using stripe.elements().

2) If I do need to have them inside directive, I am just not sure how to convert the above to an angular directive. It first manipulates the element by mounting it (which can be added to the directive link function), but later it goes on to use the card element for form submit and event handlers. Do I need to do all this inside the directive link itself of have the submits inside directive controller and the element manipulation in link?

I am so confused and stuck on what to do here. Can someone give me a sample on how I can address this if I am doing it wrong please?

I am using Angular 1.5.

Coppinger answered 21/2, 2017 at 7:59 Comment(0)
C
5

I have now changed the Controller to a directive and placed all the jquery and angular codes inside the Link function. This is how my directive code looks like after the update:

function stripeForm() {

        // Link Function
        function link(scope, element, attrs) {

            scope.submitCard = submitCard;

            var elements = stripe.elements();
            var style = {
                          iconStyle: 'solid',
                          style: {
                            base: {
                              iconColor: '#8898AA',
                              color: '#000',
                              lineHeight: '36px',
                              fontWeight: 300,
                              fontFamily: 'Helvetica Neue',
                              fontSize: '19px',

                              '::placeholder': {
                                color: '#8898AA',
                              },
                            },
                            invalid: {
                              iconColor: '#e85746',
                              color: '#e85746',
                            }
                          },
                          classes: {
                            focus: 'is-focused',
                            empty: 'is-empty',
                          },
                        };
            var card = elements.create('card', style);
            card.mount('#card-element');

            // Handle real-time validation errors from the card Element.
            card.on('change', function(event) {
                setOutcome(event);
            });

            // Form Submit Button Click
            function submitCard() {
                var errorElement = document.querySelector('.error');
                errorElement.classList.remove('visible');
                createToken();
            }

            // Send data directly to stripe server to create a token (uses stripe.js)
            function createToken() {
                stripe.createToken(card).then(setOutcome);
            }

            // Common SetOutcome Function
            function setOutcome(result) {
                var errorElement = document.querySelector('.error');
                errorElement.classList.remove('visible');
                if (result.token) {
                  // Use the token to create a charge or a customer
                  stripeTokenHandler(result.token);
                } else if (result.error) {
                  errorElement.textContent = result.error.message;
                  errorElement.classList.add('visible');
                }
            }

            // Response Handler callback to handle the response from Stripe server
            function stripeTokenHandler(token) {
                ..send to server ...
            }
        }

        // DIRECTIVE
        return {
            restrict: 'A',
            replace: true,
            templateUrl: 'payment/PaymentForm.html'
            link: link
        }
    }

My HTML File is now like this:

<form ng-submit="submitCard()">
    <div>
      <label>
        <div id="card-element" class="field"></div>
      </label>
    </div>
    <div>
        <button class="btn btn-primary pull-right" type="submit">Pay!</button>
        <button class="btn btn-danger pull-left" type="button" ng-click="cancel()">Cancel</button>
    </div>
    <div>
      <div class="error"></div>
    </div>
</form>
Coppinger answered 21/2, 2017 at 20:34 Comment(2)
Hi Neel, how does your html look in this case? I am presented with the error when trying to submit the card details 'Error: We could not retrieve data from the specified Element. Please make sure the Element you are attempting to use is still mounted.'Futrell
@SachinKaria I have updated my answer with the HTML code that I am using. Hope this helps.Coppinger

© 2022 - 2024 — McMap. All rights reserved.