Some tips with integrating Stripe with a subscription-based Django application
I just finished integrating Stripe with a subscription-based Django application a few weeks ago for processing credit card payments. This was the first time I've used this service and found the Stripe API and documentation to be very well written. Below are some things I learned while working on this project.
Avoid storing any credit card data in your database if you can for Payment Card Industry (PCI) compliance
One of the main reasons to use Stripe is they take care of the PCI compliance requirements for you. The credit card data is sent to Stripe in the form of a “token" so the data is not visible or stored anywhere on your server. Stripe will then store the credit card data on their system which you can retrieve for future use via their API or through their website.
According to Stripe, it is ok to store the credit card information returned by their API, such as the last 4 digits, type, and expiration date of the credit card. But if you can, avoid storing any credit card data at all just to prevent any potential security issues. See the the video “Building PCI Compliant Django Applicatons" to learn more about this.
Use an existing project
There's an open source project on GitHub called django-stripe-payments which does most of the things you'll probably need. It didn't fit with our schema so we had to write our own app, but I took a lot of the code and ideas from this project.
Disable the submit button on form submission
To prevent users from accidentally clicking the submit button multiple times which would send multiple requests to the server, consider using this JavaScript snippet in your template to disable the button as soon as the user clicks it:
<script> $('form').submit(function() { $(this).find("button[id='submit-button']").prop('disabled',true); }); </script>
Using and testing 'webhooks'
Webhooks are neat as they eliminate polling. For one-time charges, you probably won't need it. But for subscription-based apps where recurring charges are common, it's a good idea to use webhooks.
For example, at the beginning of the month when the customer's credit card gets charged, you want Stripe to send a POST request to your app to tell it whether the credit card was charged successfully so you can automatically update the subscription information in your database or notify the customer if the charge failed.
Testing this during development could be tricky, though, as Stripe needs to be able to communicate with your app on your local development machine. A way around this is to use a service such as localtunnel.
An even better option if you already have a server publicly accessible over the Internet is to use a reverse SSH Tunnel. More info here on how to do this.
Limiting access to your application based on subscription status
Once the customer's trial period ends or if the subscription is canceled due to unpaid invoices, you may want to still keep the customer's account active but limit the pages they can access. A good way to handle this with Django is to write a middleware.
In our app, we basically check if the user has an active subscription and if not we automatically redirect the user to the Settings page where the credit card information can be updated. With the exception of some pages such as the Sales or FAQ pages, trying to access any URL on the site with an inactive subscription will result in a redirect to the Settings page. In the Django settings file, we have a list URLs/views that customers with an inactive subscription are allowed to view.
Here's a sample implementation of this middleware
Consider using Stripe Checkout instead of creating your own payment form
Unless you want your credit card forms very customized, Stripe Checkout would probably be more than good enough for your needs. It looks pretty, takes care of all the field validations, tested to work with most desktop and mobile browsers, and easily customizable.
Handling credit card errors
As far as error handling goes, the ones related to credit card charges/validation are probably the most important. The Stripe API has an exception class called stripe.CardError that you should catch and handle.
To test different types of potential credit card errors see this page: https://stripe.com/docs/testing
If you have other tips please share them by commenting below.
Tags: python, django, tech, software development