2

I want to be able to take this form - get a stripe ID with stripe checkout (code implemented and working) and then submit the form via ajax and insert the stripe id into the hidden value in the form.

What jQuery code would allow me to do this?

class signupForm(Form):
forename = StringField('Forename', validators = [ Required()])
surname = StringField('Surname', validators = [Required()])
username = StringField('Username', validators = [Required(), Length(min = 4, max = 20)])
password1 = PasswordField('Password', validators = [Required(), Length(min = 6, max=50)])
password2 = PasswordField('Password', validators = [Required(), Length(min = 6, max=50)])
email = StringField('Email', validators = [Email(), Required()])
phone = StringField('Phone number', validators = [Required()])
addressLine1 = StringField('Address Line 1', validators = [Required()])
addressLine2 = StringField('Address Line 2')
town = StringField('Town', validators = [Required()])
county = StringField('County', validators = [Required()])
postcode = StringField('Postcode', validators = [Required()])
recurringDonationQuantity = StringField('If you would like to make a monthly recurring donation, please enter the amount in Sterling below. <br> Recurring Donation Amount')
stripetoken = HiddenField('')

My page:

            {% with messages = get_flashed_messages() %}
                {% if messages %}
                    <div class="alert alert-warning" role="alert">
                        <ul>
                                {% for message in messages %}
                                    <li>{{ message | safe }}</li>
                                {% endfor %}
                        </ul>
                    </div>
                {% endif %}
            {% endwith %}

            {{ wtf.quick_form(form) }}

I also have this javascrpt in-page

            <script>
            var handler = StripeCheckout.configure({
                key: '{{key}}',
                image: '{{ url_for("static", filename="logo.png") }}',
                locale: 'english',
                token: function(token,args) {
                    $('#stripe-token').val = token;
                    wt
                    console.log(token)
                }
            });


            $('#pay').on('click', function(e) {
                // Open Checkout with further options

                    if ('{{type}}' == 'normal') {
                        description = 'Normal Membership';
                        amount = 2500;

                        console.log('membership type is NORMAL')
                    } else {
                        description = 'Associate Membership';
                        amount = 2500;

                        console.log('membership type is ASSOCIATE')
                    }



                    handler.open({
                        name: 'My Organisation',
                        description: description,
                        amount: amount,
                        currency: 'GBP',
                        panelLabel: 'Okay',
                        billingAddress: true,
                        zipCode: true
                    });

                    e.preventDefault();
            });


            // Close Checkout on page navigation
            $(window).on('popstate', function() {
            handler.close();
            });
        </script>
user3818253
  • 45
  • 1
  • 6
  • 1
    `amount = 2500;` Please tell me you're not trusting the client to tell you how much to charge! – Kyle Pittman Sep 28 '15 at 14:28
  • Nope - the amount is defined by a url param which defines the type of sign up - amount defined by backend logic. That just shows the user how much they'll be charged (assuming they're not intentionally breaking things) - either way they'll still be charged according to the sign up type. – user3818253 Sep 28 '15 at 14:31
  • Phew ;) that's a relief! – Kyle Pittman Sep 28 '15 at 15:21

2 Answers2

3

I might try something like this using jQuery's serialize method.

<script>
    $(document).ready(function(){    
        $('#submit').click(function() {
            console.log('submit clicked');

            //This part keeps you D.R.Y.
            url_params = $(this).serialize();

            //left this code the same...
            var csrftoken = $('meta[name=csrf-token]').attr('content')

            $.ajaxSetup({
                beforeSend: function(xhr, settings) {
                    if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
                        xhr.setRequestHeader("X-CSRFToken", csrftoken)
                    }
                }
            })

            $.ajax({
                //more changes here
                url: url_params, //you may need to modify this to hit the
                                 //correct URL depending on your forms action param
                                 //ex: url: $(this).attr('action') + url_params,
                type: 'GET',     //GET instead of POST so we can use url_params
                contentType:'application/json;charset=UTF-8',
                success: function(response) {
                    console.log(response);
                },
                error: function(error) {
                    console.log(error);
                }
            });
        });
    });
</script>

Hopefully this points you in the right direction to avoid having to have so many fields hard-coded into your javascript.

You may also choose to keep the ajax call as a 'POST', to do that you would need to take a look at jQuery's serializeArray if you want to roll your own solution, or see the following code I adapted from this stackoverflow question:

// add a helper function to the $ jQuery object.
// this can be included in your page or hosted in a separate JS file.
$.fn.serializeObject = function()
{
    var o = {};
    var a = this.serializeArray();
    $.each(a, function() {
        if (o[this.name] !== undefined) {
            if (!o[this.name].push) {
                o[this.name] = [o[this.name]];
            }
            o[this.name].push(this.value || '');
        } else {
            o[this.name] = this.value || '';
        }
    });
    return o;
};

// then inside your form page:
<script>
    $(document).ready(function(){    
        $('#submit').click(function() {
            console.log('submit clicked');

            //This part keeps you D.R.Y.
            data = $(this).serializeObject();

            //left this code the same...
            var csrftoken = $('meta[name=csrf-token]').attr('content')

            $.ajaxSetup({
                beforeSend: function(xhr, settings) {
                    if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
                        xhr.setRequestHeader("X-CSRFToken", csrftoken)
                    }
                }
            })

            $.ajax({
                //more changes here
                data: data,
                url: '',
                contentType:'application/json;charset=UTF-8',
                success: function(response) {
                    console.log(response);
                },
                error: function(error) {
                    console.log(error);
                }
            });
        });
    });
</script>

The key to take away here is that you can use javascript or jQuery to read your <form> data and parameterize it. You want to avoid hard-coding your post data if at all possible. It's a hassle to do, and it's a bigger hassle to change it down the line.

Kyle Pittman
  • 2,858
  • 1
  • 30
  • 38
1

In the end I did this, I was hoping there was an easier way:

<script>
        $(document).ready(function(){    
            $('#submit').click(function() {
                console.log('submit clicked')

                data = {
                    'csrf_token': '{{ csrf_token() }}',
                    'csrftoken': '{{ csrf_token() }}',
                    'stripetoken': gtoken,
                    'forename': $('#forename').val(),
                    'surname': $('#surname').val(),
                    'username': $('#username').val(),
                    'password1': $('#password1').val(),
                    'password2': $('#password2').val(),
                    'email':  $('#email').val(),
                    'phone': $('#phone').val(),
                    'addressLine1': $('#addressLine1').val(),
                    'addressLine2': $('#addressLine2').val(),
                    'town': $('#town').val(),
                    'county': $('#county').val(),
                    'postcode': $('#postcode').val(),
                    'recurringDonationQuantity':  $('#recurringDonationQuantity').val(),
                }

                var csrftoken = $('meta[name=csrf-token]').attr('content')

                $.ajaxSetup({
                    beforeSend: function(xhr, settings) {
                        if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
                            xhr.setRequestHeader("X-CSRFToken", csrftoken)
                        }
                    }
                })

                $.ajax({
                    url: '',
                    data: JSON.stringify(data, null, '\t'),
                    type: 'POST',
                    contentType:'application/json;charset=UTF-8',
                    success: function(response) {
                        console.log(response);
                    },
                    error: function(error) {
                        console.log(error);
                    }
                });
            });
        });
    </script>

There are so many csrf things because I haven't gotten around to working out which one made it work.

user3818253
  • 45
  • 1
  • 6