Adding Payments to your Web Application: An Introduction to Stripe Integration
In this article, we will build together a single-page web application that allows the user to securely execute payments online.
We will offer a top-down explanation of the entire Stripe integration process, before deep-diving into the implementation details of the payment solution discussed. Our demo application will be written in React Typescript and Python Flask. Don’t worry if you are not familiar with the programming stack, the application will only contain basic features and functionalities, easily transposable to any other language.
Online payments, and FinTech in general, have become a crucial part of our lives. Recall the last time you had to execute a payment on a web or mobile application. Fueled by the development of e-commerce, chances are that you’re using e-payments on a weekly basis. The tendency to switch to online payments is not new, but the fast move to digitalization that we are witnessing is making it more important than ever. To follow this trend, a large number of developers are now trying to develop a basic knowledge of building robust and secure online payment systems.
In the past, building an online payment solution was way more complicated than it is today. Before Stripe, there were many payment services providers like Worldpay and Sagepay (UK) that offered an all-combined package — for a price. For online-only, Paypal can arguably be a great payments solution as well, especially that its integration is very simple. However, Stripe’s success lies in how they’ve made it a lot easier to integrate. None of the other established parties really offer what contemporary web entrepreneurs would recognize as a proper API and they certainly speak the language of the tech community.
Fortunately, Stripe has made online payment solutions way easier to build and manage. Stripe is a FinTech launched in 2011 to rebuild the economic infrastructure of the internet. Stripe offers a lot of services for developers to manage payments, transfers, refunds, and fees both on the front-end and the back-end side. Developers no longer need to worry about the infrastructure needed to execute payments, Stripe has developed a complete layer that takes care of all the hassle for you.
Some other tools are available in the market like Square and Paypal. Square comes with a fully integrated POS system with an online store and a payment solution, it is better suited for brick and mortar businesses. A point of sale system, or POS, is the place where your customer makes a payment for products or services at your store. Simply put, every time a customer makes a purchase, they’re completing a POS transaction.
Paypal offers a variety of options for accepting online payments, it is also known for its ease of use. It is available in many more countries than Stripe. However, Stripe remains more customizable.
One additional criterion that you want to consider, besides worldwide adoption and ease of integration, is the fees structure. Although Paypal is more attractive for micropayments (payments under 10$), Stripe certainly has the edge for higher-value payments, especially international payments.
Paypal Micropayment Fees
- $1.00: 5% of $1 = 5¢ and 5¢ + 5¢ = 10¢
- $5.00: 5% of $5 = 25¢ and 25¢ +5¢ = 30¢
Stripe Micropayment Fees
- $1.00: 2.9% of $1 = 2.9¢ and 2.9¢ + 30¢ = 32.9¢
- $5.00: 2.9% of $5 = 14.5¢ and 14.5¢ +30¢ = 44.5¢
- PayPal charges 4.4% for transactions where the funds originate from outside of the US (including Europe). This means, even if the customer is now US-based but uses an international card, you’ll get hit with the same transaction fee.
- Stripe is gentler in its fees here, charging a total of 3.9% in addition to its $0.30 fixed fee.
Unless you’re processing US-based microtransactions on the regular, the answer is blissfully conclusive in this case: Stripe.
Stripe offers developers large libraries to take care of the front-end and back-end sides of the payment execution. Amongst other capabilities, the libraries contain objects that allow you to:
- Authenticate to your Stripe standard or connected account
- Send specific requests easily to Stripe like payment creation, refund requests, transfers, and many more and parse the corresponding responses
- Handle webhooks for event notifications: for every event that happens on the account like a payment, a payout, a refund, etc … a request is sent from Stripe to a pre-registered route on your backend to notify your app of any change you want to listen to.
Stripe libraries also offer UI components that you can import directly in your front-end. As you will see in the demo application, we will be using a Stripe-ready input field to manage credit card number inputs.
Create your Stripe account
To use Stripe in your project, you will need to create a Stripe account. You can easily do that by visiting the Stripe dashboard login page and clicking on sign up. Once you have completed the setup of your account, you will be able to access your Stripe dashboard.
The Stripe dashboard allows you to easily check all the transactions happening on your account and all the details that you need to successfully integrate payments into your application. The developers' tab in the side menu is extremely important to check logs during testing, to retrieve your secret keys to log in successfully from your application, and to configure webhooks to get notified of Stripe events in your application.
During testing, make sure that you turn on ‘View test data’ in the side menu. It allows you to switch to Stripe’s full testing environment where you can test all features without having to execute real transactions or upload official documents.
Note that API keys are different between the testing and production environment. You can account for this in your application by separating development and testing configuration from production configuration, which we will not demonstrate in our demo app, for the sake of simplicity.
How does payment on Stripe work
Payments are delicate to manage since we’re dealing with very important user data: their credit card numbers. Credit card processing is completely handled by Stripe. Thus, the security and compliance requirements on our application are not as heavy. Payments transit directly via the frontend towards Stripe APIs. However, we would want to keep track of the entire history of payments that were executed via our platform. Therefore, there should be a mechanism that allows us simultaneously store payment information in our database without communicating sensitive data to the backend.
Fortunately, Stripe has a system of ‘Payment Intents’ that allows us to achieve this.
Step 1: The client application (or frontend) sends a request to the backend to signal that the user is willing to pay a certain amount for a specific order. The request contains all the information necessary about the order like prices and article details for example.
Step 2: The backend calls Stripe’s API to create a ‘Payment Intent’. The intent is created by Stripe and sent back to the backend.
Step 3: The backend returns a
client_secret contained inside the Payment Intent object to the frontend. This code will allow Stripe to recognize that the payment made with this client corresponds to the payment intent created by the corresponding backend.
Step 4: The client executes the payment using the Stripe frontend development tools. The
client_secret is sent with the payment request to Stripe to complete the requirements needed.
Step 5: A status is sent back from Stripe to the client.
Example: Building a payment solution with Stripe
After explaining the theory behind executing payments in Stripe, we want to deep dive into a practical example of implementing a payment form in your web application.
You can check the source code on Github.
We will begin by deep-diving into the API source code. As we have previously seen, the API's goal is to create a
payment_intent with the amount that the user is willing to pay, and any additional information that describes the payment like the purchase type and the purchase details. Once this
payment_intent is created, the API will return a
client_secret to the client.
The stripe package should be installed in your Flask project to import it. Moreover, you will need to add your stripe API key that you can have in the Developers menu in your Stripe dashboard once you create your account.
The most important thing that we need to verify in the request sent from the client is that it contains an
amount field that specifies the amount of the payment. You might add other information verification depending on your use case.We create the
payment_intent in line 19 and return the
client_secret in line 22.
It is worthy to note that in this example, for demo purposes, we’re sending the error to the user as a response to the request. This is in general a bad practice! We should always personalize the errors we send to the client to avoid any leakage of sensitive information in the exception messages like passwords, secret keys, and library versions that might be vulnerable.
On the client side, we build a
CheckoutForm which will contain the fields to input the credit cardholder's name, address, and card details. To connect this
CheckoutForm to the stripe account of the application, we need to wrap the form with a
<Elements> tag containing a
stripePromise generated with your specific
publishable key that you can get from the API keys section on your stripe dashboard. This enables the stripe elements that we are going to use in our application to communicate very easily to the stripe servers.
We will take a quick look at the
CheckoutForm component where all the logic behind the payment happens.
First, you can see that when the component first renders, a call is sent to the
create-payment-intent root with the payment details in the request’s body as we have seen in the server-side implementation. The client secret received will populate a state variable and will be used later on to execute the payment.
The form contains 3
Field components to update the billing information stored in a state variable.
CardElement component is directly connected to Stripe, thanks to the
Elements wrapper previously discussed. On every change in this field, the change event is sent to the Stripe API and a response is received and processed in the
handleChange function. So the user will know in real-time if the details of the card are acceptable.
handleSubmit function checks that all Stripe elements are correctly loaded before executing any payment. The payment is executed by calling
stripe.confirmCardPayment with the client secret and the card element containing the card information.
If the payment is successful, the error is set to
null and a link to the stripe dashboard is shown to the developer where he can go and verify the payment’s execution. For customers, this should be obviously changed to an adapted success message.
As you have seen in the example above, Stripe has made the integration of payment forms very simple in any web application. However, this is a small demo of all the capabilities that Stripe can provide. The next step is to dig deeper into handling payment events like payment success and refunds in the backend via webhooks.