Processing Payments with Stripe
- Introduction to Stripe
- Creating a Stripe Payments Test Account
- Getting an SSL Certificate/Setting Up HTTPS
- Creating a Form for Handling Payments with Stripe
- Writing the JavaScript Code for Handling Stripe Payments
- Writing the PHP Code to Process Payments with Stripe
- Handling Stripe Errors
- Stripe Testing and Tricks
In an earlier post in this series, I covered how you create an HTML form for securely handling payments via Stripe. In the previous post, I walked through the JavaScript that goes with that form. 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 PHP code that actually processes the payment request. Note that this article assumes that you have read the previous articles and are comfortable with PHP.
Recapping the Process
As previously explained, the Stripe payment process (when using the Stripe.js JavaScript library) works like so:
- The customer loads an HTML form on your site. Let’s call that page buy.php.
- The form takes the requisite payment information: credit card number, expiration month and year, CVC, customer name, etc.
- 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).
- 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.
- Stripe will return a string of characters, called a token, that represents the payment information (assuming the payment information was valid).
- 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.
- The JavaScript that handles the Ajax request also needs to let the form submission go through (assuming there were no errors).
- If any errors occurred, the JavaScript should report upon those and not submit the form.
- 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 an earlier post. The previous post explained the code for Steps 4-8. In this post, I’ll address Step 9.
What PHP Needs to Do
At this point, the customer’s payment information has been sent to Stripe, a representative token has been returned by Stripe, that token has been stored in a hidden form input, and the form has been submitted to PHP on the server. No payment has actually been made yet (i.e., the customer’s credit card has not been charged). And, so long as your form does not use name attributes for the payment fields, the customer’s payment information will not be submitted to your server. Next, the PHP script should:
- Validate that a token was received by PHP
- Validate all of the other form data (everything required by your site; not by Stripe)
- Make the charge request of Stripe
- Handle Stripe’s response
- Handle any errors
Let’s walk through all of those in detail. As a head’s up, because this post is already rather long, and because handling errors is so critical, the last item in this list will be addressed in my next post in the series.
Validate the Token
In order to actually charge the customer via Stripe, you need the payment token that Stripe has already associated with the customer’s payment information (e.g., credit card data). Using the JavaScript already explained, that token will be stored in a hidden input named stripeToken. The PHP code therefore needs to check for a form submission, and for the presence of that token:
if ($_SERVER['REQUEST_METHOD'] == 'POST') { $errors = array(); if (isset($_POST['stripeToken'])) { $token = $_POST['stripeToken']; } else { $errors['token'] = 'The order cannot be processed. You have not been charged. Please confirm that you have JavaScript enabled and try again.'; } } // End of form submission conditional.
One way of handling all the possible errors is to create an array, as in the above code. Then, if $_POST[‘stripeToken’] is set, it’s assigned to a local variable for easy reference. If $_POST[‘stripeToken’] is not set, then the order can’t be processed. This could be because you screwed up the JavaScript or someone is trying to be tricky or a user just has JavaScript disabled. In any case, let the user know that: A) an error occurred, B) they weren’t charged, and C) what to do next. I’d also have the system email you (or the site administrator) because it means something’s amiss.
TIP: To prevent a duplicate form submission, you can use JavaScript to disable the submit button. But in PHP, I would also recommend storing the token in the session. Then confirm that the submitted token has not already been stored in the session (i.e., this is a new submission).
Validating the Other Form Fields
Next, have the PHP script validate every other form field: those not pertinent to Stripe. What these are will depend upon your situation. Maybe there’s an email field, plus password, name, etc. There’s probably something related to what the customer is purchasing: items in a cart, a donation amount, whatever. Validate these items and add errors to the $errors array.
Making the Charge Request (Getting Paid!)
Stripe Library Versions
In 2015, Stripe updated its PHP library to version 2, which requires PHP 5.3 or greater. It also assumes you’re using Composer (which you really ought to be using). Version 2 of the library also uses namespaces, so its syntax is quite different. In this post, I’ll first use the most current code and syntax, and then show the older code at the end of the post. The downloadable scripts have two PHP examples, one in each style (i.e., for version 1 and version 2 of the Stripe library).
The next step is to actually request the payment. This is not hard thanks to the Stripe PHP library, which you first have to install. As of version 2 of the Stripe PHP library, this is normally done via Composer. Add the following to your project’s composer.json file:
{ "require": { "stripe/stripe-php": "2.*" } }
Then, from the project’s directory, execute this command:
composer install
This does assume you’ve installed Composer and generally know what you’re doing in this area.
Once you’ve done all that, you can make use of the library, which starts by including Composer’s autoloader:
require_once('vendor/autoload.php');
Alternatively, if you’re not comfortable with Composer, you can use the most current version of the Stripe PHP library by downloading the latest release, unzipping it, and copying it to your web server. Then include the init.php file:
require_once('path/to/stripe-php/init.php');
Next, set your secret API key:
\Stripe\Stripe::setApiKey(STRIPE_PRIVATE_KEY);
My earlier recommendation was to store this as a constant (like in a configuration file). Make sure this value stays private!
Then create the charge by invoking \Stripe\Charge::create(). It takes an array as an argument:
$charge = \Stripe\Charge::create(array( ));
So what goes in the array? The full Stripe API docs list all the possible options, but at the very least you want these array indexes and values:
- amount
- currency
- source
The amount needs to be an integer in cents! This is easy to miss. If you’ve got a decimal, like charging a client $1.95, just multiply that times 100. (And, frankly, it’s pretty much standard to do e-commerce in cents anyway.)
The currency is a three-letter ISO code in lowercase, such as usd or cad. In other words, what currency are you being paid in (regardless of what the customer’s native currency is)?
The source should be the token.
TIP: If you have customers with repeating charges, you can use different code to first create a Stripe customer object and then provide the customer ID as the “card” value to \Stripe\Charge::create().
You can optionally provide a “description” value. This is something you’ll be able to use to associate other information with the charge. The recommended and logical choice is to provide a unique customer identifier with the description, such as the customer’s email address.
Putting this altogether, here’s a charge:
$charge = \Stripe\Charge::create(array( 'amount' => $amount, // Amount in cents! 'currency' => 'usd', 'source' => $token, 'description' => $email ));
That code does assume that all the validation routines have been passed, the amount has been set in cents, and all of the variables have proper values. Behind the scenes, Stripe will use cURL to perform the request, and if all went well, that code just made you money!
Handling the Charge Request
Of course, you’re not going to assume it all worked, are you? No. If an error occurred during the charge (e.g., the payment attempt was rejected), an exception will be thrown, so you should first wrap all of the above code in a try…catch block:
require_once('vendor/autoload.php'); try { \Stripe\Stripe::setApiKey(STRIPE_PRIVATE_KEY); $charge = \Stripe\Charge::create(array( 'amount' => $amount, // Amount in cents! 'currency' => 'usd', 'source' => $token, 'description' => $email )); } catch (\Stripe\Error\Card $e) { }
I’ll discuss the exceptions in the next post, but first, within the try block, after the invocation of create(), you can use the $charge object to test the success of the operation (assuming no exceptions occurred). The charge object will have many attributes, including:
- amount, the amount charged
- id, a unique identifier
- livemode, a Boolean
- source, an object that stores information about the source charged, which is normally a card (but nothing too revealing beyond the address and the last four digits of the card)
- currency, the ISO code
- paid, a Boolean
- description, the description you provided
To confirm that the charge went through, and that you were paid, you can check the paid attribute:
if ($charge->paid == true) { // Whohoo!
For added protection, you could confirm that the livemode is true, that the currency is your currency, that the amount is the expected amount, and so forth. But if paid is true, and all the other conditions you want to test are correct, the customer has paid and you should consider the order completed.
Storing the Order
If the order is completed, you’ll next want to do one or more of the following:
- Reflect the order completion to the customer in the browser
- Send the customer an email
- Send the administrator an email
- Store the order in the database
- Fulfill the order
I’m going to leave all of that up to you, as it’s mostly basic PHP/MySQL, and many of the particulars will differ from site to site. You can use the $charge variable in these steps, such as the unique charge ID ($charge->id), the amount, the customer’s email address (which you hopefully sent along in $charge->description), etc. Don’t forget that the amount is in cents, though: you’ll want to store it that way, but probably the information presented to the customer would be in dollars and cents.
TIP: By submitting a unique customer identifier from your site to Stripe (in the “description” index), each Stripe transaction can easily be reconciled with a customer on your site. By storing the charge ID in the database on your site, you can easily reconcile each order with the transaction in Stripe.
And that is how you write the PHP code to actually process a payment with Stripe. Again, this all does assume you’re using the Stripe.js JavaScript library.
In the next post in this series, I’ll look at the Stripe exceptions in detail. Thanks for reading and do let me know if you have any questions or comments!
Using version 1.x of the Stripe PHP Library
Version 1.x of the Stripe PHP library did not use Composer or namespaces, so both its installation and its use differ greatly from version 2.x of the Stripe PHP library.
To install version 1 of the Stripe PHP library:
- Download the legacy version of the Stripe PHP library from Stripe.
- Expand the downloaded ZIP file. The result will be a folder named stripe-php-version.
- Copy the folder’s lib folder to your web server. You probably ought to put this in a subdirectory, such as stripe or libraries/stripe.
With the library installed, start by including it:
require_once('path/to/lib/Stripe.php');
You’ll need to specifically include the Stripe.php file, found in the lib folder. Obviously change the path/to part to match where you stored the lib directory.
The complete syntax for using version 1 of the Stripe PHP library is:
require_once('path/to/lib/Stripe.php'); try { Stripe::setApiKey(STRIPE_PRIVATE_KEY); $charge = Stripe_Charge::create(array( 'amount' => $amount, // Amount in cents! 'currency' => 'usd', 'card' => $token, 'description' => $email )); } catch (Stripe_CardError $e) { }