visit
We would be using NodeJs
for the backend and Postgres as our database. On the frontend we are using ReactJs
with vite
.
For this we would need to create 2 seperate repositories, one for our backend
and another one for frontend
.
Now, let's get started.
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:
npm install bcrypt cookie-parser cors jsonwebtoken pg-hstore stripe
Here we would be using Test Mode
for demo.
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=
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;
};
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
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;
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,
};
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,
};
This is where we will integrate Stripe Checkout
into our application.
We will use the Stripe API
to manage payments and handle user subscriptions.
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) {}
});
Now let's go ahead and try to deploy it on FL0
. 🔼
Here we would need to create a new project, let's name it stripe-fl0
for example.
This includes everything we need to get our React-Vite
project up and running.
npm install @heroicons/react axios react-router-dom
npm install postcss tailwindcss autoprefixer --save-dev
src/components/PricingPlans.jsx
...
const handleCheckout = (product) => {
axios
.post(
`//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
.
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.
In this tutorial, we learnt how to build payment pages by integrating Stripe Checkout
easily into our full-stack applications. 🎉