19

I'm using Stripe and Checkout to create a payment form and I want to be able to use Checkout's awesome javascript library, but I also want to change the form submission from just a normal POST to an AJAX POST.

So I tried adding a handler to the form element you're supposed to have, but my console line was never triggered, so it's not submitting using the given form.

Then I tried looking into the code that's brought up when the overlay is triggered. It's a bit confusing and I'm just wondering if anybody else was able to figure it out, or if it's made difficult because it's a security matter?

// Stripe plugin
<form id="payment_form" method='post' action="{{url_for('process_payment')}}">
        <script
            src="https://checkout.stripe.com/checkout.js" class="stripe-button"
            data-key="test key">
        </script>
</form>

// Form submit handler
$(document).ready(function(){
    $("#payment_form").submit(function(e) {
        console.log("Processing...");
        ajax_payment();
        return false;
    });

});
Caskman
  • 359
  • 4
  • 9

3 Answers3

21

Stripe triggers the form's submit() function. You can set event handler (not listener!) to it to prevent sending normal POST request.

Example using plain javascript:

var form = document.getElementById('myStripeForm');
form.submit = function() {
    // ... get form data here and send it through ajax

    // Prevent form submit.
    return false;
}

Example using jQuery:

$('#myStripeForm').get(0).submit = function() {
    var data = $(this).serializeArray();
    // process data and send ajax request

    $.ajax(...);

    // Prevent form submit.
    return false;
}
ischenkodv
  • 4,205
  • 2
  • 26
  • 34
  • 4
    This is actually the simplest and cleaner option to do this. Thanks! – Alessandro Prete Feb 25 '16 at 15:44
  • how do I get the data on the first option ? – coiso Mar 28 '16 at 16:56
  • 1
    You can take the data you need from `form.elements` array. Or use one of the solutions here: http://stackoverflow.com/q/11661187/533711 – ischenkodv Mar 29 '16 at 08:52
  • Juste use `$('#myStripeForm').submit(function () { ... })` for the jquery part – Luca Steeb Jul 30 '17 at 22:27
  • 1
    @LucaSteeb that is a **listener** (similar to `.on('submit', function(e) {...})` which even with `e.preventDefault()` redirects to the URL in the `action` property. How did you manage to make it work? Only reassigning the **handler** (as provided in this answer) worked out for me. – CPHPython May 18 '18 at 10:33
9

There are two options for Checkout integrations, the first, which you're using, is the 'simple' integration. The second is a custom integration which has a success callback ('token' function). It looks like this:

<script>
  var handler = StripeCheckout.configure({
    key: 'pk_test_6pRNASCoBOKtIshFeQd4XMUh',
    image: '/square-image.png',
    token: function(token, args) {
      // Use the token to create the charge with a server-side script.
      // You can access the token ID with `token.id`
    }
  });

  document.getElementById('customButton').addEventListener('click', function(e) {
    // Open Checkout with further options
    handler.open({
      name: 'Demo Site',
      description: '2 widgets ($20.00)',
      amount: 2000
    });
    e.preventDefault();
  });
</script>

You would put your ajax_payment function within the token function.

user3250670
  • 1,172
  • 8
  • 5
  • Yes but how can I intercept simple integration to instead send an ajax request? Their plugin is really nice and I'd like to keep using that – Caskman Feb 20 '14 at 03:29
  • I was going to ask a very same question, I even recorded a video - https://www.youtube.com/watch?v=WnGQugK7FVY - because I thought redirecting is an usability bug :) Apparently it's how things work. @user3250670 has an answer for you (and you can still use their plugin) – Mars Robertson Mar 09 '14 at 23:52
1

You can do it this way. This example uses PHP, but swap in your preferred server-side language:

1) Create a nocontent.php file that contains this line: header("HTTP/1.0 204 No Content"); which surprisingly returns a 'HTTP/1.0 204 No Content' header. The browser will appear to do nothing when it requests this page. You can optionally have this page process the POST data, or you can have this simply be a dummy page with that single line.

2) In your HTML put the nocontent.php path into the <form tag's action attribute : <form action="nocontent.php".

3) Attach a javascript function to the window.onbeforeunload event. This event is triggered as the browser requests the new page, and so is fired even though the browser appears to do nothing. This function is called after Stripe generates its token.

4) Your form now contains the <input type="hidden" name="stripeToken" value="..."/> and <input type="hidden" name="stripeEmail" value="..."/> elements.

5) Now you have options, depending on when you handled the form's POST data.

  • a) If your nocontent.php page simply returns a 'no content' header, you can now AJAX re-submit your form's data to another page (as normal) that will actually process the POST data and return Stripe's status information. That's the route I took.

  • b) If your nocontent.php page itself processes the form's POST data, you can store the Stripe response on the server and use your token as a key in an ajax request to retrieve payment status whenever it arrives from Stripe.

Note that it might be better practice to intercept and prevent Stripe from triggering the submit event on the form itself, but I did not have reliable success with that method.

  • 3
    Please note there are cross-browser issues with this which stung me quite badly! Safari just plain does not work with the HTTP 204 No Content fix and Firefox brings up a dialogue box asking users to confirm if they wish to leave the page, which is very confusing for the user as they have to click 'Leave the page' even though to them they are staying on the same page. I don't think these are issues with this solution per se but there are browser issues which render this solution unusable, at least for me. – Roy Dec 16 '14 at 14:28