우리는 백엔드에 NodeJs
사용하고 데이터베이스로 Postgres를 사용할 것입니다. 프론트엔드에서는 vite
와 함께 ReactJs
사용하고 있습니다.
이를 위해 우리는 2개의 별도 저장소를 생성해야 합니다. 하나는 backend
용이고 다른 하나는 frontend
용입니다.
이제 시작해 보겠습니다.
우리 템플릿은 기본 Node.js
애플리케이션과 Docker화된 PostgreSQL
데이터베이스를 캡슐화합니다.
다음은 설정에 해당하는 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:
npm install bcrypt cookie-parser cors jsonwebtoken pg-hstore stripe
여기서는 데모를 위해 Test Mode
사용하겠습니다.
이 프로젝트에 필요한 환경 변수 목록은 다음과 같습니다.
.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
- 사용자 로그인 및 세션 저장을 돕습니다.
POST /signup
- 새 계정을 만드는 데 도움이 됩니다.
POST /create-checkout-session
- 스트라이프 체크아웃 페이지 링크를 생성하고 반환합니다.
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, };
여기가 Stripe Checkout
애플리케이션에 통합하는 곳입니다.
우리는 Stripe API
사용하여 결제를 관리하고 사용자 구독을 처리할 것입니다.
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, };
마지막으로 모든 경로를 진입점인 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) {} });
이제 FL0
에 배포해 보겠습니다. 🔼
여기서는 새 프로젝트를 만들어야 합니다. 예를 들어 이름을 stripe-fl0
으로 지정하겠습니다.
여기에는 React-Vite
프로젝트를 시작하고 실행하는 데 필요한 모든 것이 포함됩니다.
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")); }; ...
이 함수는 백엔드의 /create-checkout-session
경로를 호출하고, 링크를 수신하고, 사용자를 체크아웃 페이지로 리디렉션합니다. 📄
이 외에도 signup
및 login
페이지를 각 경로에 연결하고 사용자 데이터를 localstorage
에 저장해야 합니다.
그런 다음 백엔드의 URL로 설정되어야 하는 VITE_APP_API_BASE_URL
환경 변수를 프런트엔드 배포에 추가해야 합니다.
또한 백엔드의 CLIENT_URL
환경 변수를 프런트엔드의 호스팅 URL로 설정해야 합니다.
이 튜토리얼에서는 Stripe Checkout
풀 스택 애플리케이션에 쉽게 통합하여 결제 페이지를 구축하는 방법을 배웠습니다. 🎉