Create a Stripe Subscription with ReactJS and NodeJS
How to collect recurring payments from your customers using Stripe in ReactJS and NodeJS
Stripe is one of the most popular methods for collecting payments from your customers. It’s straightforward to use and an essential skill for any web developer.
In a previous article, we saw how to collect payments in a ReactJS application.
Today we will learn how we can create a subscription and make it super easy for you.
Let’s begin!
First Step
To set up a subscription, first, you will need two secret values from the stripe dashboard
For the front-end
STRIPE_PUBLISHABLE_KEY
and for the backend
STRIPE_SECRET_KEY
Store these values because we will need it soon!
Create Products and Prices
In the next step. Go to your dashboard and create a product.
In this example, we are setting two prices for that product. One monthly and another yearly.
Now get the API ID for prices. That key starts with price_somerandomstuff
The actual flow
There are several steps in the whole subscription workflow.
First, you collect the user's name, email, and plan from the front end.
From the last step, we want to collect the price API ID that the user selects.
The user also fills in their payment details. To collect their payment details, we use
CardElement
. Stripe SDK provides this component.Then we send these four pieces of information (name, email, paymentDetails, and priceId)to the backend.
The backend creates a customer with the name, email, and payment details. You can store the customer created in this step in your database.
The backend creates a subscription using that customer and pricedId and passes back
clientSecret
to the front end.The front end uses that clientSecret to create a
paymentIntent
.If successful, you will see that the subscription is active on the stripe dashboard.
This is a lot of steps, so let’s begin.
Prepare the Backend
First, install the dependency.
yarn add stripe
Then initialize the stripe with your secret key.
import Stripe from 'stripe'
const stripe = new Stripe('your_stripe_secret_key')
Then create a route that accepts a post request.
app.post('/create-subscription', ( req ,res ) => {
createSubscription(req);
})
Then the actual function to create the subscription.
async createSubscription(createSubscriptionRequest) {
// create a stripe customer
const customer = await this.stripe.customers.create({
name: createSubscriptionRequest.name,
email: createSubscriptionRequest.email,
payment_method: createSubscriptionRequest.paymentMethod,
invoice_settings: {
default_payment_method: createSubscriptionRequest.paymentMethod,
},
});
// get the price id from the front-end
const priceId = createSubscriptionRequest.priceId;
// create a stripe subscription
const subscription = await this.stripe.subscriptions.create({
customer: customer.id,
items: [{ price: priceId }],
payment_settings: {
payment_method_options: {
card: {
request_three_d_secure: 'any',
},
},
payment_method_types: ['card'],
save_default_payment_method: 'on_subscription',
},
expand: ['latest_invoice.payment_intent'],
});
// return the client secret and subscription id
return {
clientSecret: subscription.latest_invoice.payment_intent.client_secret,
subscriptionId: subscription.id,
};
}
Prepare the front-end
We are creating integration with a ReactJs application. You will need to install two libraries.
yarn add @stripe/react-stripe-js @stripe/stripe-js
Then initialize the stripe instance and wrap your application.
import React from "react";
import { loadStripe } from "@stripe/stripe-js";
import { Elements } from "@stripe/react-stripe-js";
import CheckoutForm from "./CheckoutForm";
const stripePromise = loadStripe("your_publishable_key"); // starts with pk_
function App() {
return (
<div>
<Elements stripe={stripePromise}>
<CheckoutForm />
</Elements>
</div>
);
}
export default App;
Notice we have a CheckoutForm component. Let’s create that. It will take care of the whole flow.
Let’s take a look at the main function.
import { CardElement, useElements, useStripe } from "@stripe/react-stripe-js";
const createSubscription = async () => {
const stripe = useStripe();
const elements = useElements();
// create a payment method
const paymentMethod = await stripe?.createPaymentMethod({
type: "card",
card: elements?.getElement(CardElement)!,
billing_details: {
name,
email,
},
});
// call the backend to create subscription
const response = await fetch("http://localhost:4000/create-subscription", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
paymentMethod: paymentMethod?.paymentMethod?.id,
name,
email,
priceId
}),
}).then((res) => res.json());
// confirm the payment by the user
const confirmPayment = await stripe?.confirmCardPayment(
response.clientSecret
);
};
In this function:
We are creating the payment method by getting the details using the
CardElement
provided by stripe.Then, we call the backend, where a subscription will be created for us.
We are getting back the clientSecret from the backend, which can be used to confirm the payment.
If everything goes well, we will have a working subscription!
And that’s it!
Final Code:
The final component will look like this.
import { CardElement, useElements, useStripe } from "@stripe/react-stripe-js";
import { Button, Input } from "antd";
import React, { useState } from "react";
function CheckoutForm() {
// collect data from the user
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [priceId, setPriceId] = useState("");
// stripe items
const stripe = useStripe();
const elements = useElements();
// main function
const createSubscription = async () => {
try {
// create a payment method
const paymentMethod = await stripe?.createPaymentMethod({
type: "card",
card: elements?.getElement(CardElement)!,
billing_details: {
name,
email,
},
});
// call the backend to create subscription
const response = await fetch("http://localhost:4000/create-subscription", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
paymentMethod: paymentMethod?.paymentMethod?.id,
name,
email,
priceId
}),
}).then((res) => res.json());
const confirmPayment = await stripe?.confirmCardPayment(
response.clientSecret
);
if (confirmPayment?.error) {
alert(confirmPayment.error.message);
} else {
alert("Success! Check your email for the invoice.");
}
} catch (error) {
console.log(error);
}
};
return (
<div className="grid gap-4 m-auto">
<input // this should not be a text field. maybe a radio button ro something
placeholder="Price Id"
type="text"
value={name}
onChange={(e) => setPriceId(e.target.value)}
/>
<input
placeholder="Name"
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<br />
<input
placeholder="Email"
type="text"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<CardElement />
<button onClick={createSubscription} disabled={!stripe}>
Subscribe
</button>
</div>
);
}
export default CheckoutForm;
That’s about it. Hope you learned something new today!
Resources:
https://stripe.com/docs/billing/subscriptions/build-subscriptions?ui=checkout