Published 

20

 

Jul 2023

Building Seamless Payment Interfaces with Stripe and FL0

Learn about new FL0 features and how developers are using FL0 to build their next big idea.

Payment Gateway

DevOps

Tutorial

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 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. ⬆️

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

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

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

High Level Overview

Folder Structure

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

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” 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://fl0.com/blog/building-and-deploying-a-nodejs-postgres-api-in-less-than-30-minutes

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

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

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 📦

npm install bcrypt cookie-parser cors jsonwebtoken pg-hstore stripe
Installing Packages

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.

Stripe Keys

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

!!.env.example!!

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!!

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 3: 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!!

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!!

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

const router = express.Router();
router.post("/create-checkout-session", updatePlan);
module.exports = router;

Step 4: 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!!

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

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

!!controllers/userController.js!!

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 5: 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!!

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!!

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 6: Deploying with FL0

🚀 For deploying our project to FL0 we will start with pushing our repo to a new GitHub repository first.

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

Now we would head on to app.fl0.com 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! ⏳
  • After we have our database all set up, we would need to go ahead and deploy our backend in the same project.
  • 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 7: Setting up the Frontend

For setting up the frontend we would get started with the 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.

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

Step 8: Setting up the Frontend

To build our UI components quickly we would take help of the Pricing Section Component and Sign-in and Registration Component from tailwind UI.

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

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

!!src/components/PricingPlans.jsx!!

...

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 9: Deploying the Frontend

For frontend we would need to again create a new repository 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 👇

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

Live Demo

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.

To get started with building your own applications with payment capabilities, head on to fl0.com 🚀

Dale Brett
Founder & CEO, FL0

Copy link

Our blog

Latest blog posts

Tool and strategies modern teams need to help their companies grow.

View all posts

View all posts

ready to ship

We’re excited to see you launch your next big idea.

Get started for free

arrow right