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

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.

Product Pricing

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.

  1. First, you collect the user's name, email, and plan from the front end.

  2. From the last step, we want to collect the price API ID that the user selects.

  3. The user also fills in their payment details. To collect their payment details, we use CardElement. Stripe SDK provides this component.

  4. Then we send these four pieces of information (name, email, paymentDetails, and priceId)to the backend.

  5. The backend creates a customer with the name, email, and payment details. You can store the customer created in this step in your database.

  6. The backend creates a subscription using that customer and pricedId and passes back clientSecret to the front end.

  7. The front end uses that clientSecret to create a paymentIntent .

  8. 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:

  1. We are creating the payment method by getting the details using the CardElement provided by stripe.

  2. Then, we call the backend, where a subscription will be created for us.

  3. We are getting back the clientSecret from the backend, which can be used to confirm the payment.

  4. If everything goes well, we will have a working subscription!

Active 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

Profile Image

Who I am

Hi, I amMohammad Faisal, A full-stack software engineer @Cruise , working remotely from a small but beautiful country named Bangladesh.

I am most experienced inReactJS,NodeJS andAWS

Buy Me a Coffee Widget