Stripe Payment Workflow In A React SPA Frontend and Rails API backend
For my Flatiron Capstone Project, I made a platform for an artist to sell their art. After some research into different options regarding accepting payment, I found out several things:
- Attempting to process payments on your own is an incredibly complex task and could land you in legal hot water if done improperly.
- Stripe was the easiest payment platform to integrate into my project and despite their thorough documentation, doing so is not a straightforward process.
Stripe, as a platform, is designed to do as much heavy lifting for you as it can when it comes to processing payments, from storing information about your products and customers to rendering a checkout page and emailing receipts to your customer, Stripe will take care of it for you which means that the less you want it to do, the more customization you have to implement.
In my case, I just wanted Stripe to process payments while I store product and customer information on my rails backend instead of storing it in Stripe’s servers and I had trouble finding straightforward information on how to implement Stripe in such a bare bones fashion so I figured that I would make an attempt to compile all of the information that I wished would be readily available when I was doing the research necessary to complete my project.
Setup
Most of the processing that stripe does will be from the backend. I used Rails for my backend and react for my frontend, so I will be discussing some steps that are specific to my set up, but the overall flow will be the same. The first step is to install the stripe gem and node package.
Next, you’ll want to set your secret key in rails. Due to the secret key being, well, secret you’ll want to rails encrypted secrets(which there is plethora of information out there on how to use and is outside of the scope of this article) and use an initializer to set the secret as an environment variable. The benefit of using the encrypted credentials(beside security) is that you are able to set different values for the same credentials based on your environment which is handy because you will have a key for testing and a key for production use. I created my initializer at config/initializers/stripe.rb which consists of a single line of code:
Stripe.api_key = Rails.application.credentials.stripe[:secret_key]
With this set up, the Stripe private key is well protected and accessible to your rails application to authenticate and authorize you for any requests that your backend may make to Stripe.
You’ll also want to set your publishable key on your frontend. Your publishable key will also change depending on your environment. I used env-cmdrc to accomplish this and then initialized stripe in my App.js using the following code:
import { loadStripe } from '@stripe/stripe-js';const stripePromise = loadStripe(REACT_APP_STRIPE_PUBLISHABLE_KEY)
Loading the payment element
The React Stripe package makes it really easy to add a payment form to your checkout page. There is plenty of information regarding doing this provided by stripe, but the best way that I have found to do this is to create a route pointing to a Stripe Element component which wraps your checkout page component. In your checkout page, you’ll include your payment element and a button inside of a form tag on your checkout as you would any form. It may help to think of it as Stripe generating the form for you and you get to supply the submit button.
Creating the Payment Intent
Before we proceed, it is important to understand how orders are kept track of on my backend. My database is set up with the orders table being a pivot table between users and order items(users have many orders and orders have many order items). Now that we have that out of the way, lets discuss payment intents.
Payment intents in Stripe are essentially a collection of all of the data Stripe will use to process your payments. The bare minimum information stripe needs is the total of your order and what payment methods will be accepted for the order. These settings are set on the backend so you will need to make a request from your frontend to your backend. Most settings you will want to set from information on your backend(for instance, you can grab the user’s email, shipping information, etc. from your database). Any information not stored on your backend may be sent in your request but keep in mind that the user may attempt to manipulate data by intercepting traffic or modifying the data before it is sent in their developer tools.
When your backend handles your request, it will make a request to Stripe including all of the settings you have which will return your payment intent. The payment intent includes quite a bit of information including all of the settings of the order as well as a client secret and a payment intent. The guides I found provided through stripe only return the client secret from this request, but I found difficulty processing the order on the backend(more on that coming up)without the payment intent id so I store that in a column in the order table.
Processing payments
When the payment is submitted, the information skips your backend and goes directly to Stripe(this is important to because if the customer’s payment information hits your server, you are on the hook for making sure that you are in compliance with laws that surround storing PII which is a big part of what Stripe offers). So, you may be asking, “How do I update the order in my backend when the payment is processed?” and the answer is to use webhooks.
In development, you’ll want to be running Stripe CLI as sort of a server for processing stripe events. In the CLI, you’ll set up a listener to make a request to your server which will make a request to an endpoint on your backend that you specify any time there is an event is processed in Stripe. These events include, but are not limited to, a payment intent being created, updated or processed.
When in production, you’ll want to register a webhook to handle different actions, but for right now, we will want to create one to handle payments being processed. Stripe has a pretty good guide on creating a webhook to detect the event, but provides no information on how to make updates to your orders on the database. This is why I store the payment intent id with my order as the request to the webhook does not contain information regarding the order as it is stored on your database, but ti does include the payment intent id so you’re able to look up the order using that and update it accordingly.
Implementing stripe into my database took a lot of reading over documentation, stackexchange questions, and other sources that i feel could have been cut down if the information I’ve provided here was readily available. My hope is that anyone who is reading this article is able to spare themselves some time and effort as well as facing less intimidation by implementing an unfamiliar technology.