Building Seamless Payment Interfaces with Stripe and FL0 💳
Tags: stripe, payment, node
Published: 2023-07-24 06:15:51

## TLDR;

In this tutorial, we'll explore how to seamlessly integrate Stripe payment gateway into our full-stack applications, and effortlessly host them on FL0. 🚀

## Introduction

Whether it be an e-commerce or a SaaS application, payment gateways are a central component of our projects. 💳

In this guide, we will explore how to simplify these integrations, specifically focusing on [Stripe Checkout](https://stripe.com/docs/payments/checkout) for online payment processing.

Stripe's developer-friendly API ensures secure and efficient transactions while cutting down on our development time.

Just for example, we have taken the case of a SaaS applications payment page.

We would be using `NodeJs` for the backend and Postgres as our database. On the frontend we are using `ReactJs` with `vite`.

Later we would go ahead and effortlessly host our project on [FL0](https://fl0.com/). ⬆️

So, let's start with a pinch of humor:

![Comic Strip - Online Shopping](https://francisbonnet.com/archive/sft/wp-content/uploads/2019/03/2019-03-11.jpg)

## Overview

🧑‍💻 In this tutorial, we will create a simple demo application where a user could sign up, select their plan, and checkout with their credit card.

![User Journey Diagram](https://i.imgur.com/9NzLx5K.png)

For this we would need to create 2 seperate repositories, one for our ` backend` and another one for `frontend`.

![High Level Overview](https://i.imgur.com/fgOciAF.png)

## Folder Structure

🗂️ Here's how both of our folder structures would look like, just for reference:

![Folder Structure](https://i.imgur.com/2VULlRZ.png)

Now, let's get started.

## Step 1: Setting Up the Backend

For the sake of efficiency, in this tutorial, we'll leverage the "[fl0zone/blog-express-pg-sequelize](https://github.com/fl0zone/blog-express-pg-sequelize)" template.

Then we would remove any files or folders not important for our project. 🧑‍💻

For a more comprehensive understanding of the tutorial, you may want to refer to this blog post:

[https://blog.fl0.com/building-a-software-startup-on-fl0-part-1-f0624174e738](https://blog.fl0.com/building-a-software-startup-on-fl0-part-1-f0624174e738)

Our template encapsulates a basic `Node.js` application and a dockerized `PostgreSQL` database.

Here is the corresponding `docker-compose.yaml` file for our setup 🐳:

```yaml
version: "3"
services:
  app:
    build:
      context: .
      target: development
    env_file: .env
    volumes:
      - ./src:/usr/src/app/src
    ports:
      - 8081:80
    depends_on:
      - db
  db:
    image: postgres:14
    restart: always
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: admin
      POSTGRES_DB: my-startup-db
    volumes:
      - postgres-data:/var/lib/postgresql/data
    ports:
      - 5432:5432
volumes:
  postgres-data:
```

Now we would go ahead and install some essential packages 📦

```bash
npm install bcrypt cookie-parser cors jsonwebtoken pg-hstore stripe
```

![Installing Packages](https://i.imgur.com/7gyfKBB.gif)

Now, we would need to get our Stripe API keys 🔑. For this we would need to create a new account on Stripe.

Here we would be using `Test Mode` for demo.

![https://i.imgur.com/pP5qFIr.gif](https://i.imgur.com/pP5qFIr.gif)

Here are the list of environment variables we would need for this project.

`.env.example`

```plaintext
STRIPE_PUBLISHABLE_KEY=
STRIPE_SECRET_KEY=
POSTGRES_DB_URI=
secretKey=
CLIENT_URL=
```

## Step 2: Creating Database Models

Let's begin by setting up our database now. 🐘

Since we're utilizing the Sequelize ORM, we'll need to create a model for our user data.

Here's the code for our model 👇

`models/userModel.js`

```JavaScript
module.exports = (sequelize, DataTypes) => {
  const User = sequelize.define(
    "user",
    {
      email: {
        type: DataTypes.STRING,
        unique: true,
        isEmail: true, //checks for email format
        allowNull: false,
      },
      password: {
        type: DataTypes.STRING,
        allowNull: false,
      },
      tier: {
        type: DataTypes.STRING,
        allowNull: true,
      },
    },
    { timestamps: true }
  );
  return User;
};
```

## Step 2: Setting up the Routes

Now, let's go ahead and create our routes

`POST /login` - Helps to log in user and store the session

`POST /signup` - Helps create a new account

`POST /create-checkout-session` - Generates and Returns the stripe checkout page link

These 3 routes are separated into 2 files as follows:

`routes/userRoutes.js`

```JavaScript
const express = require("express");
const userController = require("../controllers/userController");

const { signup, login } = userController;
const userAuth = require("../middleware/userAuth");

const router = express.Router();

router.post("/signup", userAuth.saveUser, signup);

router.post("/login", login);

module.exports = router;

```

`routes/stripeRoute.js`

```JavaScript
const express = require("express");
const { updatePlan } = require("../controllers/stripeController");

const router = express.Router();

router.post("/create-checkout-session", updatePlan);

module.exports = router;

```

## Step 3: Setting Up User Profile

🧑‍💻 For setting up the user profile, first we will define a middleware to check if the email address of a new user already exists in the database during signup.

`middleware/userAuth.js`

```JavaScript
//importing modules
const express = require("express");
const db = require("../models");

const User = db.users;

const saveUser = async (req, res, next) => {
  console.log("here");

  try {
    const checkEmail = await User.findOne({
      where: {
        email: req.body.email,
      },
    });

    if (checkEmail) {
      return res.json(409).send("Authentication failed");
    }

    next();
  } catch (error) {
    console.log(error);
  }
};

module.exports = {
  saveUser,
};

```

Then we would go ahead and define our login and signup functions 👇

`controllers/userController.js`

```JavaScript
const bcrypt = require("bcrypt");
const db = require("../models");
const jwt = require("jsonwebtoken");

const User = db.users;

const signup = async (req, res) => {
  try {
    const { email, password } = req.body;
    console.log(email);
    const data = {
      email,
      password: await bcrypt.hash(password, 10),
    };
    //saving the user
    const user = await User.create(data);

    if (user) {
      let token = jwt.sign({ id: user.id }, process.env.secretKey, {
        expiresIn: 1 * 24 * 60 * 60 * 1000,
      });

      res.cookie("jwt", token, { maxAge: 1 * 24 * 60 * 60, httpOnly: true });
      console.log("user", JSON.stringify(user, null, 2));
      console.log(token);

      return res.status(201).send(user);
    } else {
      return res.status(409).send("Details are not correct");
    }
  } catch (error) {
    console.log(error);
  }
};

// Login Authentication
const login = async (req, res) => {
  try {
    const { email, password } = req.body;

    const user = await User.findOne({
      where: {
        email: email,
      },
    });

    if (user) {
      const isSame = await bcrypt.compare(password, user.password);

      if (isSame) {
        let token = jwt.sign({ id: user.id }, process.env.secretKey, {
          expiresIn: 1 * 24 * 60 * 60 * 1000,
        });

        res.cookie("jwt", token, { maxAge: 1 * 24 * 60 * 60, httpOnly: true });

        //send user data
        return res.status(201).send(user);
      } else {
        return res.status(401).send("Authentication failed");
      }
    } else {
      return res.status(401).send("Authentication failed");
    }
  } catch (error) {
    console.log(error);
  }
};

module.exports = {
  signup,
  login,
};

```

## Step 4: Setting Up Stripe Checkout

This is where we will integrate `Stripe Checkout` into our application.

We will use the `Stripe API` to manage payments and handle user subscriptions.

The following code creates a new Stripe checkout session. 💳

We will provide it with the payment method type, the product data, and the quantity.

We also need to specify the URLs where the user will be redirected upon a successful payment or if they cancel the transaction.

And, the server will respond back with the URL for the Stripe Session if everything is fine. ✅

`controllers/stripeController.js`

```JavaScript
const db = require("../models");
const Stripe = require("stripe");

const User = db.users;

require("dotenv").config();
const stripe = Stripe(process.env.STRIPE_SECRET_KEY);

const updatePlan = async (req, res) => {
  try {
    const { email, product } = req.body;

    const session = await stripe.checkout.sessions.create({
      payment_method_types: ["card"],
      line_items: [
        {
          price_data: {
            currency: "usd",
            product_data: {
              name: product.name,
            },
            unit_amount: product.price * 100,
          },
          quantity: product.quantity,
        },
      ],
      mode: "payment",
      success_url: `${process.env.CLIENT_URL}/success`,
      cancel_url: `${process.env.CLIENT_URL}/`,
    });

    //find a user by their email
    const user = await User.findOne({
      where: {
        email: email,
      },
    });

    if (user) {
      await user.update({ tier: product.name });
      return res.send({ url: session.url });
    } else {
      return res.status(401).send("User not found");
    }
  } catch (error) {
    console.log(error);
  }
};

module.exports = {
  updatePlan,
};
```

At last, we would need to add all our routes to our entry point, which is `server.js`

`server.js`

```JavaScript
const cors = require("cors");
const express = require("express");
require("dotenv").config();
const cookieParser = require("cookie-parser");

const db = require("./models");
const userRoutes = require("./routes/userRoutes");
const PORT = process.env.PORT || 8080;
const stripeRoute = require("./routes/stripeRoute");

const app = express();

// Middlewares
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());
app.use(cors());

// Routes
app.use("/api/v1/users", userRoutes);
app.use("/api/v1/stripe", stripeRoute);

app.listen(PORT, () => {
  console.log("Server started at port 8080");

  try {
    db.sequelize.sync({ force: true }).then(() => {
      console.log("db has been re sync");
    });
  } catch (error) {}
});

```

And we are done with the backend ✅

Now let's go ahead and try to deploy it on `FL0`. 🔼

## Step 5: Deploying with FL0

🚀 For deploying our project to FL0 we will start with [pushing our repo](https://www.theserverside.com/blog/Coffee-Talk-Java-News-Stories-and-Opinions/How-to-push-an-existing-project-to-GitHub) to a new GitHub repository first.

This is the link to our repository for reference: [https://github.com/dalefl0/stripe-fl0-backend](https://github.com/dalefl0/stripe-fl0-backend)

Now we would head on to [app.fl0.dev](https://app.fl0.dev/) to start deploying.

- Here we would need to create a new project, let's name it `stripe-fl0` for example.

- Now we would need to create a new Postgres instance. With Fl0, this takes less than 30 seconds! ⏳

  ![Creating a postgres instance](https://i.imgur.com/CNHtnex.gif)

- After we have our database all set up, we would need to go ahead and deploy our backend in the same project.

  ![Deploying backend](https://i.imgur.com/c7zQwMf.gif)

- After the backend is deployed we would need to import our database connection string as shown above ☝️

🙌 Now we have our backend up and running.

Time for the UI ✨

## Step 6: Setting up the Frontend

For setting up the frontend we would get started with the [template-react-vite](https://github.com/fl0zone/template-react-vite). ⚡️

This includes everything we need to get our `React-Vite` project up and running.

Now we would go ahead and install a few packages.

```bash
npm install @heroicons/react axios react-router-dom
npm install postcss tailwindcss autoprefixer --save-dev
```

![Installing packages](https://i.imgur.com/OuSBWxh.gif)

## Step 7: Setting up the Frontend

To build our UI components quickly we would take help of the [Pricing Section Component](https://tailwindui.com/components/marketing/sections/pricing) and [Sign-in and Registration Component](https://tailwindui.com/components/application-ui/forms/sign-in-forms) from [tailwind UI](https://tailwindui.com/components/).

For the sake of brevity, we will only look at the important functions of the frontend.

The complete project could be found at: [https://github.com/dalefl0/stripe-fl0-frontend](https://github.com/dalefl0/stripe-fl0-frontend)

Now, we would need to add a function to handle stripe checkouts

`src/components/PricingPlans.jsx`

```JavaScript
...

const handleCheckout = (product) => {
    axios
      .post(
        `https://stripe-fl0-backend-dev.fl0.io/api/v1/stripe/create-checkout-session`,
        {
          email,
          product,
        }
      )
      .then((res) => {
        if (res.data.url) {
          setTier(product.name);
          localStorage.setItem("tier", product.name);
          window.location.href = res.data.url;
        }
      })
      .catch((err) => navigate("/cancel"));
  };

  ...
```

This function calls our backend's `/create-checkout-session` route, receives a link, and redirects the user to the checkout page. 📄

Apart from this, we need to also connect our `signup` and `login` pages to respective routes and store the user data in `localstorage`.

## Step 8: Deploying the Frontend

For frontend we would need to again create a new [repository](https://github.com/dalefl0/stripe-fl0-frontend) and deploy it in the same project in a similar manner.

We would then need to add the `VITE_APP_API_BASE_URL` environment variable to the frontend deployment which should be set to the URL of our backend.

We would also need to set the `CLIENT_URL` environment variable in the backend to the hosted URL of the frontend.

Once done, our FL0 project would look like this 👇

![FL0 project dashboard](https://i.imgur.com/R1ABh06.png)

Now, let's go ahead and try our application using this live demo link: [https://stripe-fl0-frontend-q8oo-dev.fl0.io/](https://stripe-fl0-frontend-q8oo-dev.fl0.io/)

![Live Demo](https://i.imgur.com/uc78fv7.gif)

## Wrapping up

Thanks for sticking by till the end!

In this tutorial, we learnt how to build payment pages by integrating `Stripe Checkout` easily into our full-stack applications. 🎉

We also did blazingly-fast deployments of our project using [FL0](https://fl0.com/).

To get started with building your own applications with payment capabilities, head on to [fl0.com](https://fl0.com/) 🚀

![Building your own applications with stripe](https://media4.giphy.com/media/13GIgrGdslD9oQ/giphy.gif?cid=ecf05e47kx9qh8z45pat8a3w06lb2avauj35t7topfv5t5h2&ep=v1_gifs_search&rid=giphy.gif&ct=g)