0

I am following the Stripe Quickstart guide for stripe Elements using Flask: https://stripe.com/docs/stripe-js/elements/quickstart and while the token is appearing in the web console POST params:

cardholder-name jane
stripeToken     tok_1ECGDeCKSqZHGj511bnxBRad

the POST attempt is returning "400 bad request, CSRF token is missing or incorrect."

My javascript is consistent with the tutorial so I think it may be something to do with my views function:

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

token = request.form['stripeToken']


if request.method == 'POST':

    customer = stripe.Customer.create(
        email='customer@example.com',
        source=token
    )
"""
    charge = stripe.Charge.create(
        customer=customer.id,
        amount=250,
        currency='usd',
        description='Flask Charge',
        source=token
    )
"""
return render_template('billing/charge.html', token=token
)

I have been commenting out certain parts of the function to try to isolate the problem to no avail. Have I made a mistake passing the form to my server? Alternatively, is there a test I can write to debug the 400 error? Any and all feedback on all of the code is appreciated. Here is my html form code for reference

<script src="https://js.stripe.com/v3/"></script>
<body>
  <form role="form" action="{{ url_for('billing.charge') }}" method="post" id="payment-form">
    <div class="group">
      <label>
       <span>Name</span>
        <input name="cardholder-name" class="field" placeholder="Jane Doe" />
      </label>
      <label>
        <span>Phone</span>
        <input class="field" placeholder="(123) 456-7890" type="tel" />
      </label>
    </div>
    <div class="group">
     <label>
      <span>Card</span>
       <div id="card-element" class="field"></div>
     </label>
    </div>
    <button type="submit">Pay $25</button>
    <div class="outcome">
     <div class="error"></div>
      <div class="success">
    Success! Your Stripe token is <span class="token"></span>
  </div>
 </div>
</form>
</body>
koopmac
  • 936
  • 10
  • 27
  • Sounds like a Flask problem. Have a look [here](https://stackoverflow.com/questions/8089224/csrf-token-missing-or-incorrect) – Paul Asjes Mar 11 '19 at 05:44

2 Answers2

0

I think the tutorial (and the set-up-subscriptions example code) don't bother with CSRF, presumably for simplicity.

The Flask-WTF docs have a bit about CSRF tokens that I found useful.

I ended up putting a csrf_token variable in my Flask template:

<script>
  var csrf_token = {{ csrf_token()|tojson }};
</script>

And then in my Stripe Elements code, when making a call to /create-customer I added an extra header. Here's the JavaScript function from the set-up-subscriptions repo with my extra line:

// Assumes that a global csrf_token variable has been set.
async function createCustomer(paymentMethod, cardholderEmail) {
  return fetch('/create-customer', {
    method: 'post',
    headers: {
      'X-CSRFToken': csrf_token,          // This is the extra line
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      email: cardholderEmail,
      payment_method: paymentMethod
    })
  })
    .then(response => {
      return response.json();
    })
    .then(subscription => {
      handleSubscription(subscription);
    });
}

I assume a similar method will work with the other JS functions that call the Flask back end.

Phil Gyford
  • 13,432
  • 14
  • 81
  • 143
0

You need to add a CSRF token to your form as per Flask docs.

If you are using FlaskForm include {{ form.csrf_token }} in the form.

<form method="post">
    {{ form.csrf_token }}
</form>

https://flask-wtf.readthedocs.io/en/stable/csrf.html

M3RS
  • 6,720
  • 6
  • 37
  • 47