Need some help with your project? Contact me

Integrating Stripe with Drupal

This is a guest post from Eric C Brown.

When the need to accept credit card payments within a Drupal site arises, there are many solutions that one could choose from. Third-party services such as Paypal and Authorize.net provide ways to accept payments, but as most developers I've talked with can attest, integrating with the API's from these sites can be a very challenging task.

Stripe, a relative newcomer to the scene, offers much of the same functionality but with one major difference: the service was built from the ground up by and for developers. Stripe's API's and unique approach to providing PCI-compliant integration into your site makes it dead-simple to start accepting payments with very little work. Couple that with Drupal's flexibility and you can be up and running in no time.

Our Use Case

Recently, we needed a way to replace a legacy implementation of a simple photocopy ordering site that used Authorize.net to accept and process credit card payments, within a very quick turnaround. Since we didn't have much time, we decided to keep the Drupal 5 legacy site in place and replace the Authorize.net process with Stripe.

In researching our options, I realized we could do one of the following:

  • Utilize the contrib module Stripe which was not ready for production use at the time
  • Use Stripe's Embedded Form which provides a simple way to present customers with a checkout form
  • Create a custom module and create a checkout form using Drupal's Form API

First, I evaluated the Stripe module - which by the way looks like it is on it's way to becoming a great one - but I decided against using it for this case. It wasn't quite ready for production nor did it fit our exact needs.

I then looked at implementing Stripe using their embedded form. Embedding a form in this manner is as simple as including the following code (taken from the Stripe documentation):

<form action="" method="POST">
  <script
    src="https://checkout.stripe.com/v2/checkout.js" class="stripe-button"
    data-key="YourKEYhere"
    data-amount="2000"
    data-name="Demo Site"
    data-description="2 widgets ($20.00)"
    data-image="/128x128.png">
  </script>
</form>

While the simplicity of this approach was very attractive, I wanted a solution that would be a bit more flexible. Plus, if I'm creating forms, why not use the power of Drupal's Form API?

Form API to the Rescue

In the end, I decided that utilizing Drupal's Form API in a custom module was the way to go for this project (more on this later). To create our solution using this route, I had to do the following:

  1. Create a custom module to provide our form
  2. Create a menu callback to display our form using hook_menu() and drupal_get_form()
  3. Since Drupal's form API creates form elements with names by default, and using Stripe's approach requires creating fields for the credit card information without the name attribute (so that they don't get submitted to your server), do the following:
    1. Add a custom callback to the #after_build property of each element that should not have a name attribute
    2. In the custom callback, unset $element['#name'] (the Stripe contrib module uses this same approach)
  4. Add a "stripeToken" hidden element in the form that will be populated with the charge token from Stripe later
  5. Add any other custom form elements that may be needed (ie order number, etc)
  6. Use drupal_add_js() (or hook_library() and drupal_add_library()) to add a javascript file
  7. In the javascript file, use a script (similar to Stripe's example - Step 2) that intercepts the form submission and sends the cc info to Stripe via an ajax call
  8. Upon successful token creation, set the value of the hidden stripeToken field to that of the token returned from Stripe
  9. Finally, create a form submission callback that:
    1. Includes Stripe's sdk
    2. Creates a charge object with the token that was returned from Stripe

Voila!

When finished, you should have a form that, when submitted, sends the payment info to Stripe using ajax (which never touches your server), then upon successful receipt of a token, finishes submitting the form (without the card info) and calls any submission callbacks for your form.

While the above steps may seem like a lot of work, it's honestly not that much at all. Best of all, if you stick to the Drupal way of doing things (which this tutorial aims to do), you should have a relatively small module that integrates nicely with Stripe.

But wait, there's more!

If you want to kick it up a notch, you could do things like use Stripe's Stripe.js to do client-side validation of credit card numbers, expiration dates, and cvc fields. You could also pretty things up by using Twitter Bootstrap (or the Drupal Theme Bootstrap). Additionally, and this is something that we did in our implementation, you can put the code that creates a charge in a try/catch block so that you can rollback and return an error if something went wrong.

However, one thing you'll definitely want to do before you go live is to enable SSL encryption on your server. This was probably the biggest hurdle I had to fight through since it was something I'd never done before but Stripe recommends that you have this in place before going into production.

Extending

Aside from what I already mentioned, the reason I decided not to use the Stripe contrib module was that we anticipate having other sites/projects in the future that will need to integrate a payment form. And instead of repeating this same process over and over again, and possibly having issues with SSL certificates for different domains, I came up with the idea to create a base Stripe module that exposes hooks for other custom modules to define checkout forms that extend the form.

Back to our use case, there is data residing in the tables of another database and instead of trying to pass it between sites (via GET or POST), I created a separate module that makes the needed connections to the tables and adds the required fields to the payment form.

This approach keeps the Stripe module agnostic about any collected info other than what it needs, yet gives other modules the opportunity to extend the form, respond to webhooks and log data as needed for each payment.

Thoughts?

I'm still in the finishing touches phase of prepping my Stripe module for contribution back to the community but hopefully I can share it soon.

I'd love to hear your thoughts about this approach. Obviously, processing credit card payments is serious business and I want to make sure I'm taking all the necessary precautions but hopefully this gives others some insight as to how easy it is to integrate third-party services with Drupal.

Comments

One more thing you might want to do is make sure the Stripe form fields are not submitted even if the user has JS turned off or the stripe javascript file is not able to be loaded (if the Stripe servers are offline, for instance). I used the same method on http://servercheck.in/user/register, but I removed one of the attributes from the CC fields so they wouldn't be submitted to Drupal with the rest of the form, even if stripe.js failed to load. This helps ensure even further that your site is PCI compliant.

Great Point Jeff! I didn't mention it but in the form submission callback I do check for a valid token from Stripe, and then try to create the charge, before I continue on.

Great primer! — just wondering if you could post come of your code to help point us in the right direction.

I am trying to do exactly as you have described on Drupal 7 / UberCart without using the module.

Can you help?

Where do I install the code?

Thanks for a good tutorial...

But I'm not able to create a workable solution. Please help.

Thanks
Vijesh

Hey All,

I've set up a demo module on github (https://github.com/ericthelast/drupal-stripe-form). Feel free to use that, along with this tutorial, to get you going. Also, feel free to fork the repo if you find errors/improvements.

Hope that helps!

. I hope it will be much more user friendly than the beta version that is available for us now. Hmm I will definitely take this widget when it is all set up and ready to go

Add new comment

You can post comments in Markdown and basic HTML tags.
For code blocks, wrap your code within '~~~'. For example:
~~~
$var = 'my variable';
~~~