2
votes

I'm trying to integrate Stripe custom checkout https://stripe.com/docs/checkout#integration-custom with Flask and WTForms. My problem at the moment is the payment form doesn't seem to be posting so the credit card charge cannot be created.

It seems the form is recognised because the token is being posted to stripe's api with a 200 response:

XHRPOST
https://api.stripe.com/v1/tokens
[HTTP/2.0 200 OK 1444ms]


Form data   
card[cvc]   123
card[exp_month] 10
card[exp_year]  20
card[name]      [email protected]
card[number]    4242424242424242
email   [email protected]
guid    4a6cfd25-8c4b-4d98-9dd2-9e9c1770e290
key pk_test_DVVO0zxtWjXSZx4yHsZGJxtv
muid    c6b9d635-20de-4fc6-8995-5d5b2d165881
payment_user_agent  Stripe+Checkout+v3+checkout-manhattan+    (stripe.js/9dc17ab)
referrer    http://localhost:8000/subscription/index
sid 494d70dd-e854-497b-945b-de0e96a0d646
time_on_page    26657
validation_type card

However the token (and the form) is not being posted to my server to create the charge that stripe requires.

Here is the javascript code to load stripe custom checkout, which is in /index.html:

<script src="https://checkout.stripe.com/checkout.js"></script>
<form role="form" id = "payment_form" action="{{ url_for('billing.charge') }}" method="post">
  {{ form.hidden_tag }}
  <input type="hidden" id="stripeToken" name="stripeToken" />
  <input type="hidden" id="stripeEmail" name="stripeEmail" />
<div class="form-group">
              <div class="col-md-12 button-field" style = "text-align: center;">
                <button type="confirm" id = 'confirm' onclick = "runStripe('https://checkout.stripe.com/checkout.js')" class="btn btn-default btn-responsive btn-lg">Confirm Order</button>
              </div>
            </div>  
        <script>
          var handler = StripeCheckout.configure({
            key: "{{ stripe_key }}",
            locale: 'auto',
            token: function(token) {

                    // token ID as a hidden field 
                var form = document.createElement("form");
                form.setAttribute('method', "POST");
                form.setAttribute('action', "{{ url_for('billing.charge') }}");
                form.setAttribute('name', "payment-form");

                var inputToken = document.createElement("input");
                inputToken.setAttribute('type', "hidden");
                inputToken.setAttribute('name', "stripeToken");
                inputToken.setAttribute('value', token.id);
                form.appendChild(inputToken);

                // email as a hidden field 
                var inputEmail = document.createElement("input");
                inputEmail.setAttribute('type', "hidden");
                inputEmail.setAttribute('name', "stripeEmail");
                inputEmail.setAttribute('value', token.email);
                form.appendChild(inputEmail);

                document.body.appendChild(form);
            }
          });

        document.getElementById('confirm').addEventListener('click', function(e) {
        // Open Checkout with further options:
        handler.open({
          name: 'Stripe.com',
          description: '2 widgets',
          amount: '{{ amount }}'
        });
        e.preventDefault();
      });

      // Close Checkout on page navigation:
      window.addEventListener('popstate', function() {
        handler.close();
      });
    </script>
    <script>
    document.getElementsByClassName("stripe-button-el")[0].style.display = 'none';
  </script>

I have attempted a post method within the html tag with no success. I have also tried adding a form variable within the javascript token to post to my charge route, adapted from this question: Stripe Checkout Link onClick does not process payment

Here is my index and charge routes for reference:

@billing.route('/index', methods=['GET', 'POST'])
def index():
stripe_key = current_app.config.get('STRIPE_PUBLISHABLE_KEY') 
amount = 1010

form = CreditCardForm(stripe_key=stripe_key)

return render_template('billing/index.html', stripe_key=stripe_key, form=form)

@billing.route('/charge', methods=['GET', 'POST'])
def charge():

if request.method == 'POST':
        customer = stripe.Customer.create(
            email = current_user,
            source = request.form['stripeToken']
        )

        charge = stripe.Charge.create(
            customer = customer.id,
            amount = 2000,
            currency = 'usd',
            description = 'payment'
        )

return render_template('charge.html', customer=customer, charge=charge)
1
First you do not need to recreate the form in the token callback function; simply update the existing hidden field value will do; secondly, you would want to post by form.submit(); in the token function, I did not see any POST call in the javascript code.wsw
@wsw Thanks for your help! I decided to use jquery for the token instead which was much simpler. I've added the code below if you're interested.polymath

1 Answers

1
votes

I decided to change the token to jquery, which now seems to work perfectly and is far simpler:

    <script>
      var handler = StripeCheckout.configure({
        key: "{{ stripe_key }}",
        locale: 'auto',
        token: function(token) {

          $(document).ready(function(){
            $("#stripeToken").val(token.id);
            $("#stripeEmail").val(token.email);
            $("#payment_form").submit();
})
</script>

In order for the jquery to be recognised, I also added the script for the jquery package at the top of the html file:

script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>

Finally, for anone else who needs help in flask, here is my adjusted route:

@billing.route('/index', methods=['GET', 'POST'])
@handle_stripe_exceptions
@login_required

def index():
stripe_key = current_app.config.get('STRIPE_PUBLISHABLE_KEY') 
amount = 1010

form = CreditCardForm(stripe_key=stripe_key, name=current_user.name, amount=amount )

if request.method == 'POST':

    customer = stripe.Customer.create(
        email='[email protected]',
        source=request.form['stripeToken']
    )

    charge = stripe.Charge.create(
        customer=customer.id,
        amount=amount,
        currency='usd',
        description='Flask Charge'
    )

return render_template('billing/index.html', stripe_key=stripe_key, form=form)