Writing the JavaScript Code for Handling Stripe Payments

December 5, 2012
This entry is part 5 of 8 in the series Processing Payments with Stripe

In my previous post in this series, I covered how you create an HTML form for securely handling payments via Stripe. As explained in that post, thanks to the Stripe.js library, the proper HTML, and a bit of JavaScript, you can easily and securely handle payments on your site without getting mired in the PCI compliance muck. The secret is the Stripe.js library: it sends the customer’s payment information from the client to Stripe’s server and returns a token that Stripe associated with that payment information. Then, when the form is submitted, the token can be used by the PHP on your site to actually process the payment. The customer’s payment information, however, never touches your server. You get paid and the customer is protected.

In this post, I’ll walk through the necessary JavaScript to handle the client-side of that process. Note that this article assumes that you have read the previous article. And comfort with JavaScript and jQuery is required, too.

Recapping the Process

As previously explained, the Stripe payment process (when using the Stripe.js JavaScript library) works like so:

  1. The customer loads an HTML form on your site. Let’s call that page buy.php.
  2. The form takes the requisite payment information: credit card number, expiration month and year, CVC, customer name, etc.
  3. The form does not use name attributes for the payment fields! This keeps the customer data from reaching your server (and therefore saving you lots of security headaches).
  4. When the form is submitted, the payment information needs to be sent to Stripe via Ajax (i.e., JavaScript). This means that JavaScript must prevent the form’s actual submission.
  5. Stripe will return a string of characters, called a token, that represents the payment information (assuming the payment information was valid).
  6. The JavaScript that handles the Ajax request needs to store that token in the form as a hidden element. This makes the token available to the server-side code (the PHP) when the form is actually submitted.
  7. The JavaScript that handles the Ajax request also needs to let the form submission go through (assuming there were no errors).
  8. If any errors occurred, the JavaScript should report upon those and not submit the form.
  9. When the form is submitted to the server, the server-side script will use the token to actually request that the payment be processed.

Steps 1-3 were discussed in the previous post. In this post, I’ll provide and explain the code for Steps 4-8. In the next post, I’ll address Step 9.

The JavaScript Scripts

My recommendation is to put all the required JavaScript code in two or three SCRIPT tags. First, you must include the Stripe.js library. It’s served from Stripe’s own servers:

<script type="text/javascript" src="https://js.stripe.com/v2/"></script>

Note that this should go in the HEAD of your HTML page. Also, for performance reasons, only the page on your site that actually accepts payments should include all this JavaScript (in other words, don’t just put this into the header and footer files so it’s invoked by every page in your site). Next, you need to set your public Stripe key:

<script type="text/javascript">Stripe.setPublishableKey('lksdajSDFmn2345nv');</script>

This can take place anywhere after you’ve included the Stripe.js library (which defines the Stripe object). The value here should be your public key, found in your Stripe account settings. Do not put your private key here as: 1) that won’t work, and 2) this value will be viewable in your source code.

As a tip, if you’re already using PHP for the page, I’d recommend storing your keys as constants in a configuration file:

define('STRIPE_PRIVATE_KEY', 'l2k35j5LKJfd20m5n_l2kj34K');
define('STRIPE_PUBLIC_KEY', 'lksdajSDFmn2345nv');

Then you can have PHP print this value within the JavaScript:

echo '<script type="text/javascript">Stripe.setPublishableKey("' . STRIPE_PUBLIC_KEY . '");</script>';

By having PHP print this value, you can quickly change from testing to live mode by only editing your configuration file.

Next, I’d toss the rest of the JavaScript code—that which will hijack the form’s submission, make and handle the Ajax request, update the form, and submit the form—in an external JavaScript file:

<script type="text/javascript" src="js/buy.js"></script>

This script is going to do the bulk of the work, and the rest of the code explained will go in it.

Handling the Form’s Submission

The first thing the JavaScript has to do is setup an event handler for the form submission. The event handler should:

  • Disable the submit button to prevent a duplicate submission
  • Prevent the form’s submission
  • Validate the form data
  • Send the form data to Stripe via Ajax

You can do all this using straight JavaScript, of course, but I’ll use jQuery for my example. It’s easier, shorter, and more reliable. This does assume that the jQuery library will have been imported prior to this script.

Using jQuery, this code creates the event handler for the form’s submission:

$(document).ready(function() {

    // Watch for a form submission:
    $("#payment-form").submit(function(event) {

    }); // form submission

}); // document ready.

This is standard jQuery stuff and only assumes that the form has a unique ID value of payment-form. Within the innermost function, you should disable the submit button and return false to prevent the form’s actual submission:

$(document).ready(function() {
    // Watch for a form submission:
    $("#payment-form").submit(function(event) {
        $('#submitBtn').attr('disabled', 'disabled');
        return false;
    }); // form submission
}); // document ready.

This takes care of the first two obligations (assuming that your submit button has an ID value of submitBtn).

Validating the Form Data

You can use any amount of standard JavaScript to perform minimal validation on the form data. However, it’s really best to be as thorough as you can in the validation, as there’s no need to send an incomplete request to Stripe, only to wait for that response to return an error. The Stripe object, defined in Stripe.js, has several validation methods built in:

  • validateCardNumber()
  • validateCVC()
  • validateExpiry()

The first will confirm that the provided number matches the right syntax for credit cards. The second does the same for the Card Verification Code (CVC). The third method confirms that the expiration month and year are both valid and in the future.

Here’s how you might use those:

var error = false;

// Get the values:
var ccNum = $('.card-number').val(),
    cvcNum = $('.card-cvc').val(),
    expMonth = $('.card-expiry-month').val(),
    expYear = $('.card-expiry-year').val();

// Validate the number:
if (!Stripe.card.validateCardNumber(ccNum)) {
    error = true;
    reportError('The credit card number appears to be invalid.');
}

// Validate the CVC:
if (!Stripe.card.validateCVC(cvcNum)) {
    error = true;
    reportError('The CVC number appears to be invalid.');
}

// Validate the expiration:
if (!Stripe.card.validateExpiry(expMonth, expYear)) {
    error = true;
    reportError('The expiration date appears to be invalid.');
}

If any validation test failed, then the error variable is set to true and the Ajax request will not be performed (shown next). The reportError() function used in the above code will be explained later in this post. You may also want to validate any other form fields, but those aren’t pertinent to Stripe.

Performing the Ajax

Finally, if no errors occurred, then the Ajax request can be made:

if (!error) {
    // Get the Stripe token:
    Stripe.card.createToken({
        number: ccNum,
        cvc: cvcNum,
        exp_month: expMonth,
        exp_year: expYear
    }, stripeResponseHandler);
 }

As you can see in that code, the request is made through the card.createToken() method of the Stripe object. It takes a generic object as its first parameter. That object should pass along the payment details. Note that you must match the names of the object attributes—number, cvc, exp_month, exp_year—exactly. You can pass along other values, such as the customer’s address, using other attributes enumerated in the Stripe documentation for Stripe.js.

The second argument to createToken() is the function that should handle the method result (aka the Ajax request response). I’m calling that stripeResponseHandler, to be written next.

Handling the Ajax

The stripeResponseHandler() function is called when Stripe responds to the Ajax request. The function should take two arguments: the status of the response and the response itself.

function stripeResponseHandler(status, response) {
}

The status argument will receive the status code, which loosely emulate standard HTTP status codes. Basically, 200 is good; 400, 401, 402, and 404 generally mean you screwed up; and codes in the 500’s means Stripe’s servers messed up. If you want to be thorough, you should check and address the status code, but I’m going to omit that step in this code and focus on the response value instead.

The response is going to be an object with several properties:

  • id, which is the token identifier
  • card, information about the card (but not the card number itself)
  • currency, indicating the payment currency, such as usd
  • livemode, which will be true or false
  • used, a Boolean indicating whether this token has already been used

If an error occurred, there will be an error property, with its own message property. You should check for this first (within the stripeResponseHandler() function):

if (response.error) {
    reportError(response.error.message);
} else { // No errors, submit the form.
}

So, in short: if an error exists, report the error. Otherwise, we’re good to go! (Again, I’ll explain the reportError() function shortly.)

Within the else clause, the JavaScript needs to store the token in the form and submit the form:

// Get a reference to the form:
var f = $("#payment-form");

// Get the token from the response:
var token = response.id;

// Add the token to the form:
f.append('<input type="hidden" name="stripeToken" value="' + token + '" />');

// Submit the form:
f.get(0).submit();

And that’s that! If everything went smoothly, the token gets stored in a hidden form input and the form is submitted to the server, but the payment information is not provided to the server, because those form elements have no name attributes. The server will be able to use $_POST[‘stripeToken’] to actually process the payment, as will be explained in the next post.

Displaying Errors

Because errors could occur in several places, I think it best to create a JavaScript function that handles any error, which I’ll call reportError(). This function takes the error message as an argument and should:

  • Add that message to the page
  • Set the error class on the message
  • Enable the submit button

This last step is necessary because the form handling function disables the submit button. If you don’t re-enable it, then the customer can never resubmit the form after correcting the error.

For the first two steps, a single line of jQuery will do. Here’s the complete function:

function reportError(msg) {

    // Show the error in the form:
    $('#payment-errors').text(msg).addClass('error');

    // Re-enable the submit button:
    $('#submitBtn').prop('disabled', false);

    return false;

}

This does assume that you’ve already created an element with an ID of payment-errors, that the submit button has an ID of submitBtn, and that you’ve defined a CSS class named error. This function is also written to only display a single error at a time. You could change it to concatenate each error to the #payment-errors content, in which case you’d need to write a clearErrors() function that gets called with each form submission (so that previous errors are not retained).

Conclusion

And there you have the JavaScript for handling the form and performing the Stripe request. It’s about 100 lines total, when well-spaced and documented (and, again, I’ll make all the code available via download later on).

In the next post, I’ll walk you through the PHP code that will handle the form data. In the meantime, if you have any questions or comments, please let me know.