TL;DR 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 for the backend and Postgres as our database. On the frontend we are using with . NodeJs ReactJs 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. For this we would need to create 2 seperate repositories, one for our and another one for . backend frontend 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 " " template. fl0zone/blog-express-pg-sequelize 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 blog post. this Our template encapsulates a basic application and a dockerized database. Node.js PostgreSQL Here is the corresponding file for our setup 🐳: docker-compose.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 📦 npm install bcrypt cookie-parser cors jsonwebtoken pg-hstore stripe 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 for demo. Test Mode 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 2: Setting up the Routes Now, let's go ahead and create our routes - Helps to log in user and store the session POST /login - Helps create a new account POST /signup - Generates and Returns the stripe checkout page link POST /create-checkout-session 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 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 //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 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 into our application. Stripe Checkout We will use the to manage payments and handle user subscriptions. Stripe API 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 5: Deploying with FL0 🚀 For deploying our project to FL0 we will start with to a new GitHub repository first. pushing our repo This is the link to our repository for reference: https://github.com/dalefl0/stripe-fl0-backend Now we would head on to to start deploying. app.fl0.dev Here we would need to create a new project, let's name it for example. stripe-fl0 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 6: 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 project up and running. React-Vite 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 7: Setting up the Frontend To build our UI components quickly we would take help of the and from . Pricing Section Component Sign-in and Registration Component 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 route, receives a link, and redirects the user to the checkout page. 📄 /create-checkout-session Apart from this, we need to also connect our and pages to respective routes and store the user data in . signup login localstorage Step 8: Deploying the Frontend For frontend we would need to again create a new and deploy it in the same project in a similar manner. repository We would then need to add the environment variable to the frontend deployment which should be set to the URL of our backend. VITE_APP_API_BASE_URL We would also need to set the environment variable in the backend to the hosted URL of the frontend. CLIENT_URL 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/ Wrapping up Thanks for sticking by till the end! In this tutorial, we learnt how to build payment pages by integrating easily into our full-stack applications. 🎉 Stripe Checkout 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 🚀