Processing credit cards is unfortunately far more difficult than we might hope, as developers. Given that it’s such a common task, is it really necessary that we jump through countless hoops (surrounded by fire, of course) for the sole purpose of processing a payment? Merchants? Gateways? SSL? Security? Very quickly, a seemingly simple operation can become an overwhelmingly confusing and, more importantly, dangerous task. Any time that you find yourself handling a user’s sensitive data, you better be on your toes.
Wouldn’t be it amazing if there was a service that made this process as easy as it can possibly be? A service built by developers for developers. What a thought! Enter Stripe; no merchant accounts, no gateways. An API call, along with a few safety guidelines, is all that you need to begin accepting credit card payments today.
While Stripe isn’t free, they only ask for 2.9% of each charge (plus .30 cents). That’s it. No setup fees, storage fees, hidden costs - none of that. Just 2.9%. Not bad!
5 Key Stripe Features
- Simple: Processing credit cards manually is difficult and dangerous. With Stripe, doing so is a cinch! You can even process charges from the command line!
- Cheap: Various payment merchants are notorious for their hidden fees. Stripe tells you up front exactly what you can expect to pay: 2.9% per charge + 30 cents. No setup fees. No hidden fees. No card storage fees.
- Intuitive API: Stripe’s API is clean, restful, and easy to use.
- Used By Cool Kids: If adoption is a concern, it needn’t be. Countless services leverage Stripe, including Reddit, Grooveshark, and Shopify.
- Built By Developers: Very clearly, Stripe was built to scratch a developer’s itch. The team is full of developers and entrepreneurs, just like you.
Let’s Do This
Sold? I was too. Let’s process our first test payment. Of course, before beginning, visit stripe.com
, create a new account (free), and fill out the various forms, such as the statement descriptor and your banking information.
Charging a user requires two core steps:
- Fetch the user’s credit card information, and send an AJAX request to Stripe’s server, which will return a unique token that represents this secure data.
- Using your server-side language of choice (PHP for this article), create a new Stripe charge, passing through the unique token.
Build a Payment Form
Naturally, the first step is to build a payment form for your product. You have two options: use Stripe’s checkout script, which will automatically create the form, validate the user’s input, and generate the unique token for the user’s credit card data. In situations when configuration and styling are flexible, this is an excellent route to take. Insert a script tag, along with a handful of custom HTML5 attributes, and you’re done!
<form action=" method="POST"> <script src="https://checkout.stripe.com/v2/checkout.js" class="stripe-button" data-key="pk_test_G5YhIkq2PEq84lwU064TZENT" data-amount="2000" data-name="Demo Site" data-description="2 widgets ($20.00)" data-image="/128x128.png"> </script> </form>
However, in most situations, you’ll require full control. As such, for the purposes of this article, we’ll use a custom form. In this section, we’ll accomplish three things:
- Use a form to collect the user’s credit card information
- Convert that data to a unique single-use token
- Submit the form to the server, along with the token
A basic payment form might look like:
<form action=" method="POST" id="payment-form"> <span class="payment-errors"></span> <div class="row"> <label> <span>Card Number</span> <input type="text" data-stripe="number"> </label> </div> <div class="row"> <label> <span>CVC</span> <input type="text" data-stripe="cvc"> </label> </div> <div class="row"> <label> <span>Expiration (MM/YYYY)</span> <input type="text" data-stripe="exp-month"> </label> <input type="text" data-stripe="exp-year"> </div> <button type="submit">Buy Now</button> </form>
Notice how we don’t require much information to process a credit card. Technically, the only information that Stripe demands is a credit card number and expiration date. However, as a rule of thumb, the more information that you fetch from the user, the better. Should the charge be disputed, this extra information will come in handy. Or, in other words, the more information you request, the more likely it is that the true owner of the credit card is placing the transaction. The key is to find the line between enough and so much that the user doesn’t bother filling out the form. At minimum, request the user’s name, email address, credit card number, expiration date, and CVC number.
To continue drilling into your head, never allow sensitive credit card data to touch your server. Doing so has the potential to create a world of hurt, if performed incorrectly. Instead, take the easy road: ensure that the input
s for the user’s credit card data do not contain name
attributes. By omitting this attribute, the data can’t be posted to your server.
Pay close attention to the custom attributes on the inputs, such as data-stripe="number"
. Stripe offers a plugin, stripe.js
, which assists in the process of compiling the user’s provided data, and generating the token. Stripe will search for those attributes and fetch their respective values.
To make use of stripe.js
, reference the script within your project, and set your publishable key, which will be provided when you sign up with Stripe. We’ll also be using jQuery in this article, though it’s certainly not required.
<script src="//code.jquery.com/jquery-2.0.2.min.js"></script> <script src="//js.stripe.com/v2/"></script> <script> (function() { Stripe.setPublishableKey('YOUR PUBLISHABLE KEY'); })(); </script>
Think of setPublishableKey
as a way of identifying your website when communicating with Stripe. Upon signing up, you’ll be presented with two different versions of this key, for testing and production, respectively.
Next, we need to create the unique, single-use token for the user’s credit card data. We can use the Stripe
object, provided by the script that we imported, for this purpose. Even better, we needn’t worry about serializing the payment form’s data; simply pass through the form jQuery object, and Stripe will handle the rest.
// Event Listeners $('#payment-form').on('submit', generateToken); var generateToken = function(e) { var form = $(this); // No pressing the buy now button more than once form.find('button').prop('disabled', true); // Create the token, based on the form object Stripe.create(form, stripeResponseHandler); // Prevent the form from submitting e.preventDefault(); }; var stripeResponseHandler = function(status, response) {};
With this bit of JavaScript, when the payment form is submitted, Stripe will attempt to generate a single-use token, using the related data from the inputs that include stripe-
specific custom attributes. The second argument to the create
method is a callback that will receive the token (response.id
) from Stripe’s server, and proceed accordingly.
Within this callback, it’s important to verify the result (was all of the information provided correctly), insert the token into a hidden input
, and submit the form to your server. Again, note that the credit card information should/will not hit your server - only the token and non-sensitive data. This is important, so write acceptance or functional tests to verify it.
Your callback might look like so:
var stripeResponseHandler = function(status, response) { var form = $('#payment-form'); // Any validation errors? if (response.error) { // Show the user what they did wrong form.find('.payment-errors').text(response.error.message); // Make the submit clickable again form.find('button').prop('disabled', false); } else { // Otherwise, we're good to go! Submit the form. // Insert the unique token into the form $('<input>', { 'type': 'hidden', 'name': 'stripeToken', 'value': response.id }).appendTo(form); // Call the native submit method on the form // to keep the submission from being canceled form.get(0).submit(); } };
It’s really quite simple! Submit an AJAX request to Stripe’s API (using their helpful JavaScript plugin), fetch the generated token, insert it into the form, and post it to your server!
Charging
If following along, at this point, you’ve successfully generated a single-use token and submitted the payment form. Now, it’s time for your server-side language of choice to physically create the charge. Remember, in the previous section, no charge was made. We only generated a token that represented the credit card data.
Stripe offers a number of server-side libraries for registering new charges, or even arranging subscriptions. Chances are high that your preferred language is represented (PHP, Ruby, Python, etc.).
Similar to the previous section, submitting a new charge may be accomplished in a few steps:
- Declare your API key
- Using the Stripe library, make an API call, passing through the details of the transaction
- Validate the charge, and proceed accordingly.
Refer to Stripe’s Library page for installation instructions. If using PHP, as we will be in this article, it’s recommended that you leverage Composer to download the Stripe package.
{ "require": { "stripe/stripe-php": "dev-master" } }
Composer is the future of PHP dependency management, so get on board now, if you haven’t already. A basic Stripe charge might take the form of:
// Set your API key Stripe::setApiKey("YOUR API KEY"); try { Stripe_Charge::create([ 'amount' => 2000, // this is in cents: $20 'currency' => 'usd', 'card' => $_POST['stripeToken'], 'description' => 'Describe your product' ]); } catch (Stripe_CardError $e) { // Declined. Don't process their purchase. // Go back, and tell the user to try a new card }
That’s it! The API key will authenticate you as a valid Stripe user. Similar to the publishable key, Stripe will provide you with two different versions of this key: one for testing and production, respectively.
Please note that all charges to Stripe should be declared in cents (based on the currency, of course). If prices are stored in your database as dollars, euros, or pounds, then you’ll want to compensate accordingly, when making the charge.
If no exception is thrown, you may rest assured that the charge has successfully processed. Proceed by offering the user their digital download, or registering their purchase with your system.
Believe it or not, Stripe’s work is finished. There’s certainly more that you can do, such as creating customers and managing subscriptions, but, when it comes to simply processing a single payment, you’re done! …Except you’re not.
SSL
While, yes, Stripe’s work is finished, yours, on the other hand, is not. Regardless of the payment provider, any time that you work with credit card information, security should be a top concern. We’ve already taken the first steps, by ensuring that the credit card data never touches the server, but there’s still more to do. We next must secure the user’s connection to your server. In other words, you need an SSL certificate. Under no circumstances should you skip this step!
“SSL (Secure Sockets Layer) is the standard security technology for establishing an encrypted link between a web server and a browser. This link ensures that all data passed between the web server and browsers remain private and integral.” - info.ssl.com
When a user offers a website their credit card information, they will expect to see https
within the address bar. Luckily, purchasing an SSL certificate is far easier than it used to be. In fact, most hosts offer an SSL add-on, which turns the entire process into a single click. The same is true for various SaaS options, such as Pagoda Box or Heroku.
Tip: Once you enable SSL, it’s possible that images and assets will break. To fix this, ensure that all URLs use
https
, rather thanhttp
. Or, as a better solution, use protocol-relative URLs.
<!-- Not good for SSL --> <img src="http://domain.com/images/foo.jpg" alt="Foo Bar"> <!-- Better --> <img src="//domain.com/images/foo.jpg" alt="Foo Bar">
With this techique, popularized by Paul Irish, if the current page is using HTTPS, then the asset will also be requested with HTTPS.
Assuming that your host offers a one-click SSL add-on, simply point your user to the https://
version of the order page, and you’re all set to go!
Tips and Tricks
The examples in this article are simple and mostly procedural. Chances are high, though, that you’ll be working with a framework that supports multiple environments, routing, and testing facilities. Use the following tips as a head-start for integrating Stripe with your framework of choice.
1. Special Credit Card Numbers
Clearly, you don’t want to use real credit card numbers to test your payment forms! Luckily, Stripe has already thought of this; they include a number of credit card numbers that simulate specific responses, such as a successful charge, invalid number, incorrect CVC code, and many more.
Here are a few card numbers that you’ll frequently reference:
- Visa Approved: 4242424242424242
- Mastercard Approved: 5555555555554444
- Card Declined: 4000000000000002
- Incorrect Number: 4242424242424241
2. Use Environments Wisely
When working with Stripe, you’ll have two unique keys, which represent the API and publishable keys. Further, there are testing and production variants for each of these. Most frameworks offer a way to manage multiple environments. This way, for development, your application will correctly use the testing keys, while, once deployed, the production versions will be referenced.
Below is a Laravel-specific project. Laravel provides a simple environment system. Add a configuration file within a folder that corresponds to the environment name, and those values will take precendence over the defaults.
First, we set the production keys:
<?php // app/config/stripe.php return [ 'apiKey' => 'PRODUCTION API KEY', 'publishableKey' => 'PRODUCTION PUBLISHABLE KEY' ];
And for development, we override the production keys with their test counterparts:
<?php // app/config/development/stripe.php return [ 'apiKey' => 'TEST API KEY', 'publishableKey' => 'TEST PUBLISHABLE KEY' ];
Now, when the application requires the API key, using Config::get('stripe.apiKey')
, the value that is returned will be determined by the environment. Success!
3. Don’t Hardwire Your App to Stripe
A common mistake that beginning developers make stems from linking their applications to various providers, like Stripe. Your application shouldn’t care which billing provider is being used. It’s only concerned with the fact that one is available. By hard-coding references to Stripe in your classes, you are creating a direct connection between the two - one that will likely be difficult to change.
Ask yourself, “If, in the future, I need to swap out Stripe with a different provider, how difficult will that be?” Hint: anything greater than “just a moment” is a code smell.
Instead, code to an interface - perhaps BillingProvider
or BillingGateway
. This way, you may create various implementations of the interface: one for Stripe, or one for a different service entirely, should the need arise. These various implementations will house the service-specific functionality. If, at some point, you find a cheaper billing provider than Stripe, swapping out the Stripe implementation of BillingProvider
with a ServiceX
version will only take a moment - that is, once you’ve created the new implementation that queries the ServiceX
billing API.
Here’s a skeleton for how this might look:
// Define the interface interface BillingProvider { public function charge($creditInfo); } // Create a Stripe implementation class StripeBilling { public function charge($creditInfo) { // Stripe_Charge::charge(...); } } // Create a ServiceX implementation class ServiceXBilling { public function charge($creditInfo) { // charge user with ServiceX } }
Now that we have two implementations, we may reference our current preferred billing service, using dependency injection.
class PaymentController { protected $billing; public function __construct(BillingProvider $billing) { $this->billing = $billing; } }
With this style of development, if you do end up needing to move away from Stripe, the controller won’t need to be touched. Because Stripe isn’t hard-coded, it doesn’t know the difference!
4. Don’t Leave the Buyer Hanging
When selling digital goods, ask yourself, “What should the buyer do if something goes wrong on my end?” Always provide some way for the buyer to contact you or your company. What if the confirmation email that includes a link to download the digital file never arrives in the buyer’s inbox? What should they do?
A support site, or even a simple email address on the home page should help in these inevitable situations.
5. SSL Certificates
If you must manually purchase an SSL certificate, there are a number of services to choose from. Based upon prior experience, you can expect to spend thirty minutes to an hour setting things up. Keep in mind that most certificates are not free, and can range from $10-$500, dependent upon the provider.
The Stripe team recommends DigiCert and Namecheap, though, if you prefer, you might consider a free solution, such as StartSSL.
6. Don’t Rely on the Form’s Price
A frequent mistake stems from using form data to contain the price of the product being purchased, possibly via a hidden input. Because a user can easily edit this input’s value, it’s unwise to depend on it. Always fetch the price of the product from the server-side. Never rely on the form to tell you. A simple database query is the preferred option.
7. The Digital File Should Not Be Publicly Accessible
The asset that you are selling should never be accessible by the public, even if the URL is, in your opinion, long and confusing enough to the point that most would never learn it. This is a bad practice for a number of reasons.
Instead, create a downloads
table which houses unique purchase codes, along with their associate product ids. This way, when a URI, such as /downloads/23gsfga831g
, is requested, your application will:
- Verify the provided token against what is stored in the database table.
- Respond by offering a download for the file that is associated with the purchase token.
To take things further, you might also enforce a download limit. Allowing for this would simply require that a download_count
field be added to the purchases
table. With each request, that number should be incremented by one. Once this number reaches your designated threshold, the download should no longer be provided. This can be helpful in instances when you want to ensure that download links aren’t shared.
Conclusion
The wonderful thing about Stripe is that it translates a complicated, confusing, and dangerous operation into a single, simple API call. No merchant accounts, no gateways, no hidden fees. There’s a reason why they say that Stripe is laughably easy to use. It is!
Comments