Skip to content

Commit

Permalink
Merge branch 'backend-final' of github.com:atlp-rwanda/e-commerce-cra…
Browse files Browse the repository at this point in the history
…fters-bn into backend-final
  • Loading branch information
iamfrerot committed Jun 16, 2024
2 parents 6c23924 + 3946568 commit c3efa1e
Show file tree
Hide file tree
Showing 11 changed files with 10,896 additions and 10,714 deletions.
21,288 changes: 10,664 additions & 10,624 deletions package-lock.json

Large diffs are not rendered by default.

159 changes: 82 additions & 77 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,79 +1,84 @@
{
"name": "e-commerce-crafters-bn",
"version": "1.0.0",
"description": "Crafter's backend repo",
"main": "index.js",
"scripts": {
"dev": "nodemon src/index.ts",
"start": "node dist/index.js",
"build": "tsc -p .",
"test": "jest",
"migrate": "sequelize-cli db:migrate",
"seed": "sequelize-cli db:seed:all"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@types/helmet": "^4.0.0",
"@types/sequelize": "^4.28.20",
"@types/supertest": "^6.0.2",
"base32.js": "^0.1.0",
"bcrypt": "^5.1.1",
"body-parser": "^1.20.2",
"cloudinary": "^2.2.0",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"cron": "^3.1.7",
"crypto": "^1.0.1",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"express-session": "^1.18.0",
"jsonwebtoken": "^9.0.2",
"mysql2": "^3.9.7",
"node-cron": "^3.0.3",
"nodemailer": "^6.9.13",
"npm": "^10.7.0",
"otpauth": "^9.2.4",
"passport": "^0.7.0",
"passport-google-oauth20": "^2.0.0",
"pg": "^8.11.5",
"pg-hstore": "^2.3.4",
"sequelize": "^6.37.3",
"sequelize-cli": "^6.6.2",
"sequelize-typescript": "^2.1.6",
"sinon": "^18.0.0",
"socket.io": "^4.7.5",
"supertest": "^7.0.0",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.0",
"ts-node": "^10.9.2",
"uuid": "^9.0.1"
},
"devDependencies": {
"@types/bcrypt": "^5.0.2",
"@types/body-parser": "^1.19.5",
"@types/cookie-parser": "^1.4.7",
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/express-session": "^1.18.0",
"@types/jest": "^29.5.12",
"@types/jsonwebtoken": "^9.0.6",
"@types/node": "^20.12.11",
"@types/node-cron": "^3.0.11",
"@types/nodemailer": "^6.4.15",
"@types/passport": "^1.0.16",
"@types/passport-google-oauth20": "^2.0.16",
"@types/pg": "^8.11.6",
"@types/sequelize": "^4.28.20",
"@types/sinon": "^17.0.3",
"@types/swagger-jsdoc": "^6.0.4",
"@types/swagger-ui-express": "^4.1.6",
"jest": "^29.7.0",
"jest-globals": "^0.1.7",
"node-mocks-http": "^1.14.1",
"nodemon": "^3.1.0",
"ts-jest": "^29.1.2",
"typescript": "^5.4.5"
}
"name": "e-commerce-crafters-bn",
"version": "1.0.0",
"description": "Crafter's backend repo",
"main": "index.js",
"scripts": {
"dev": "nodemon src/index.ts",
"start": "node dist/index.js",
"build": "tsc -p .",
"test": "jest --detectOpenHandles",
"migrate": "sequelize-cli db:migrate",
"seed": "sequelize-cli db:seed:all"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@types/helmet": "^4.0.0",
"@types/sequelize": "^4.28.20",
"@types/sinon": "^17.0.3",
"@types/supertest": "^6.0.2",
"base32.js": "^0.1.0",
"bcrypt": "^5.1.1",
"body-parser": "^1.20.2",
"cloudinary": "^2.2.0",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"cron": "^3.1.7",
"crypto": "^1.0.1",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"express-session": "^1.18.0",
"jsonwebtoken": "^9.0.2",
"mysql2": "^3.9.7",
"node-cron": "^3.0.3",
"node-mocks-http": "^1.14.1",
"nodemailer": "^6.9.13",
"npm": "^10.7.0",
"otpauth": "^9.2.4",
"passport": "^0.7.0",
"passport-google-oauth20": "^2.0.0",
"pg": "^8.11.5",
"pg-hstore": "^2.3.4",
"sequelize": "^6.37.3",
"sequelize-cli": "^6.6.2",
"sequelize-typescript": "^2.1.6",
"sinon": "^18.0.0",
"socket.io": "^4.7.5",
"stripe": "^15.10.0",
"supertest": "^7.0.0",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.0",
"ts-node": "^10.9.2",
"uuid": "^9.0.1"
},
"devDependencies": {
"@types/bcrypt": "^5.0.2",
"@types/body-parser": "^1.19.5",
"@types/cookie-parser": "^1.4.7",
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/express-session": "^1.18.0",
"@types/jest": "^29.5.12",
"@types/jsonwebtoken": "^9.0.6",
"@types/mocha": "^10.0.6",
"@types/node": "^20.12.11",
"@types/node-cron": "^3.0.11",
"@types/nodemailer": "^6.4.15",
"@types/passport": "^1.0.16",
"@types/passport-google-oauth20": "^2.0.16",
"@types/pg": "^8.11.6",
"@types/sequelize": "^4.28.20",
"@types/sinon": "^17.0.3",
"@types/socket.io": "^3.0.2",
"@types/swagger-jsdoc": "^6.0.4",
"@types/swagger-ui-express": "^4.1.6",
"cross-env": "^7.0.3",
"jest": "^29.7.0",
"jest-globals": "^0.1.7",
"nodemon": "^3.1.0",
"ts-jest": "^29.1.4",
"typescript": "^5.4.5"
}
}
96 changes: 96 additions & 0 deletions src/controllers/Payment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import express, { Request, Response } from "express";
import Stripe from "stripe";
import dotenv from "dotenv";
import { findOrderById, findProductById } from "../services/paymentService";

dotenv.config();

const stripe = new Stripe(`${process.env.STRIPE_SECRET_KEY}`);
export const checkout = async (req: Request, res: Response) => {
try {
const orderId = req.params.id;
const order = await findOrderById(orderId);
if (!order) {
return res.status(404).json({ message: "Order not found" });
}
const line_items: any[] = await Promise.all(
order.products.map(async (item: any) => {
const productDetails = await findProductById(item.productId);
const unit_amount = Math.round(productDetails!.price * 100);
return {
price_data: {
currency: "usd",
product_data: {
name: item.productName,
images: [productDetails?.image],
},
unit_amount: unit_amount,
},
quantity: item.quantity,
};
})
);

const session = await stripe.checkout.sessions.create({
line_items,
mode: "payment",
success_url: process.env.SUCCESS_PAYMENT_URL,
cancel_url: process.env.CANCEL_PAYMENT_URL,
metadata: {
orderId: orderId,
},
});

res.status(200).json({ url: session.url });
} catch (error: any) {
res.status(500).json({ message: error.message });
}
};

export const webhook = async (req: Request, res: Response) => {
const sig: any = req.headers["stripe-signature"];
const webhookSecret: any = process.env.WEBHOOK_SECRET_KEY;
let event: any;
try {
event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret);
} catch (err: any) {
console.log(`⚠️ Webhook signature verification failed.`, err.message);
return res.status(400).send(`Webhook Error: ${err.message}`);
}

switch (event.type) {
case "checkout.session.completed":
const session = event.data.object;

try {
const lineItems = await stripe.checkout.sessions.listLineItems(
session.id
);

const orderId = session.metadata.orderId;
const order = await findOrderById(orderId);
if (order) {
order.status = "paid";
await order.save();
} else {
console.error("Order not found:", orderId);
}
} catch (err) {
console.error("Error processing session completed event:", err);
}
break;

case "payment_intent.succeeded":
const paymentIntent = event.data.object;
console.log("Payment Intent succeeded: ", paymentIntent);
break;
case "payment_method.attached":
const paymentMethod = event.data.object;
console.log("Payment Method attached: ", paymentMethod);
break;

default:
console.log(`Unhandled event type ${event.type}`);
}
res.json({ received: true });
};
6 changes: 6 additions & 0 deletions src/database/config/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ const config = {
development: {
url: process.env.DATABASE_DEVELOPMENT_URL,
dialect: "postgres",
dialectOptions: {
ssl: {
require: true,
rejectUnauthorized: true,
}
}
},
test: {
url: process.env.DATABASE_TEST_URL,
Expand Down
2 changes: 1 addition & 1 deletion src/database/models/product.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class Product extends Model {
public description!: string;
public image!: string;
public discount!: number;
public price!: string;
public price!: number;
public quantity!: number;
public category!: string;
public expiringDate?: Date;
Expand Down
7 changes: 4 additions & 3 deletions src/database/seeders/20240522075149-seed-orders.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@ module.exports = {
userId: user.userId,
paymentMethod: 'Bank Transfer',
status: 'pending',
products: JSON.stringify({
products: JSON.stringify([{
// @ts-ignore
productId: product.productId,
// @ts-ignore
productName: product.name
}),
productName: product.name,
quantity: 3
}]),
createdAt: new Date(),
updatedAt: new Date()
})))
Expand Down
8 changes: 7 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,13 @@ app.use(passport.initialize());
app.use(passport.session());

app.use(express.static("public"));
app.use(express.json());
app.use((req, res, next) => {
if (req.originalUrl === '/webhook') {
next();
} else {
express.json()(req, res, next);
}
});

app.use("/", userRoute);
app.use("/", authRoute);
Expand Down
22 changes: 19 additions & 3 deletions src/routes/checkout.router.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
import express from 'express'
import { createOrder } from '../controllers/checkout.controller'
import express,{Request,Response} from 'express'
import { VerifyAccessToken } from '../middleware/verfiyToken'

import { createOrder } from '../controllers/checkout.controller'
import { checkout, webhook } from '../controllers/Payment';
const router = express.Router()


router.post('/checkout', VerifyAccessToken, createOrder)
router.post("/payment/:id", VerifyAccessToken, checkout);

router.post('/webhook', express.raw({ type: 'application/json' }), webhook);

router.get("/success", async(req:Request,res:Response)=>{
res.send("Succesfully")
});
router.get("/cancel", async(req:Request,res:Response)=>{
res.send("Cancel")
});






export default router
2 changes: 1 addition & 1 deletion src/routes/order.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ const router = express.Router();
router.put("/order/:orderId/order-status", VerifyAccessToken, updateOrderStatus);

router.get('/order/:orderId/status',VerifyAccessToken, getOrderStatus);
router.put('/order/:orderId/status',VerifyAccessToken, verifyAdmin, updateOrderStatus);
router.put('/order/:orderId/status',VerifyAccessToken, modifyOrderStatus);

export default router;
8 changes: 4 additions & 4 deletions src/routes/user.route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { twoFAController } from "./../middleware/2fa.middleware";
import express from "express";
import express,{Request,Response} from "express";
import {
Welcome,
deleteUser,
Expand All @@ -11,19 +11,19 @@ import {
import { VerifyAccessToken } from "../middleware/verfiyToken";

import { addFeedback, addReview } from "../controllers/review.controller";

const route = express.Router();

route.get("/", Welcome);
route.post("/register", register);
route.patch("/updateuser/:id", editUser)
route.patch("/updatepassword/:id", updatePassword)
route.delete("/deleteuser/:id",VerifyAccessToken,deleteUser);
route.post("/login",twoFAController, login);
route.patch("/updateuser/:id", VerifyAccessToken, editUser)
route.patch("/updatepassword/:id", VerifyAccessToken, updatePassword)
route.delete("/deleteuser/:id", VerifyAccessToken, deleteUser);
route.post("/addreview/:id", VerifyAccessToken, addReview);
route.post("/addfeedback/:id", addFeedback);



export default route;
12 changes: 12 additions & 0 deletions src/services/paymentService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Order from "../database/models/order"
import Product from "../database/models/product"


export const findOrderById = async (orderId: any)=>{
const order = await Order.findByPk(orderId)
return order
}
export const findProductById = async (productId:any)=>{
const product = await Product.findByPk(productId)
return product
}

0 comments on commit c3efa1e

Please sign in to comment.