diff --git a/src/app.module.ts b/src/app.module.ts index 089f0aa..37c23cd 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -5,11 +5,8 @@ import { ConfigModule } from "@nestjs/config"; import { SequelizeModule } from "@nestjs/sequelize"; import { databaseConfig } from "../config/database.config"; import { UsersModule } from './users/users.module'; -import { ProductsModule } from './products/products.module'; -import { CartModule } from './cart/cart.module'; -import { WalletModule } from './wallet/wallet.module'; -import { InvoiceModule } from './invoice/invoice.module'; -import { PaymentModule } from "./payment/payment.module"; +import { ProductsModule } from './products/shop.module'; + @Module({ imports: [ @@ -19,11 +16,6 @@ import { PaymentModule } from "./payment/payment.module"; SequelizeModule.forRoot(databaseConfig), UsersModule, ProductsModule, - CartModule, - WalletModule, - InvoiceModule, - PaymentModule, - ], controllers: [AppController], diff --git a/src/cart/cart.controller.ts b/src/cart/cart.controller.ts deleted file mode 100644 index 9b35dfe..0000000 --- a/src/cart/cart.controller.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Controller, Get, Post, Patch, Delete, Body, Param, UseGuards, Request, HttpException, HttpStatus } from "@nestjs/common"; -import { CartService } from "./cart.service"; -import { JwtAuthGuard } from "src/guard/auth.guard"; -import { AddToCartDto } from "./dto/add-to-cart.dto"; -import { UpdateCartDto } from "./dto/update-cart.dto"; -import { Cart } from "./entities/cart.entity"; -import { Invoice } from "src/invoice/entities/invoice.entity"; - -@Controller("cart") -export class CartController { - constructor(private readonly cartService: CartService) {} - - //create and a item to cart by user - @UseGuards(JwtAuthGuard) - @Post() - async createAndAddItemToCart(@Body() addToCartDto: AddToCartDto, @Request() req: any): Promise<{ message: string; cartItem: Cart }> { - const userId = req.user.id; - return this.cartService.createAndAddItemToCart({ ...addToCartDto, userId }); - } - //get user cart items - @UseGuards(JwtAuthGuard) - @Get() - async getUserOpenCart(@Request() req: any): Promise<{ cartItems: Cart[]; totalPrice: number }> { - const userId = req.user.id; - return this.cartService.getUserOpenCart(userId); - } - //edit quantity an item in cart by user - @UseGuards(JwtAuthGuard) - @Patch(":productId") - async updateCart(@Param("productId") productId: number, @Body() updateCartDto: UpdateCartDto, @Request() req: any): Promise<{ message: string; updatedCart: Cart }> { - const userId = req.user.id; - const updatedCart = await this.cartService.updateCart(userId, productId, updateCartDto.quantity); - return { - message: "Cart updated successfully", - updatedCart, - }; - } - //delete an item from cart by user - @UseGuards(JwtAuthGuard) - @Delete(":productId") - async removeFromCart(@Param("productId") productId: number, @Request() req: any) { - const userId = req.user.id; - return await this.cartService.removeFromCart(userId, productId); - } - //clear whole cart by user - @UseGuards(JwtAuthGuard) - @Get("clear-cart") - async clearCart(@Request() req: any) { - const userId = req.user.id; - return await this.cartService.clearCart(userId); - } - //get checkout process - @UseGuards(JwtAuthGuard) - @Get("checkout") - async processOrder(@Request() req: any): Promise<{ message: string; invoice: Invoice }> { - const userId = req.user.id; - try { - const totalAmount = (await this.cartService.getUserOpenCart(userId)).totalPrice - const result = await this.cartService.processOrder(userId, totalAmount); - return result; - } catch (error) { - throw new HttpException(error.message || "Order processing failed.", HttpStatus.INTERNAL_SERVER_ERROR); - } - } -} diff --git a/src/cart/cart.module.ts b/src/cart/cart.module.ts deleted file mode 100644 index ecb9bab..0000000 --- a/src/cart/cart.module.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Module, forwardRef } from "@nestjs/common"; -import { CartService } from "./cart.service"; -import { CartController } from "./cart.controller"; -import { Cart } from "./entities/cart.entity"; -import { SequelizeModule } from "@nestjs/sequelize"; -import { User } from "src/users/entities/user.entity"; -import { Product } from "src/products/entities/product.entity"; -import { JwtModule } from "@nestjs/jwt"; -import { JwtAuthGuard } from "src/guard/auth.guard"; -import { WalletModule } from "src/wallet/wallet.module"; -import { InvoiceModule } from "src/invoice/invoice.module"; -import { Invoice } from "src/invoice/entities/invoice.entity"; - -@Module({ - imports: [ - SequelizeModule.forFeature([Cart, User, Product,Invoice]), - JwtModule.register({ - secret: process.env.JWT_SECRET, - signOptions: { expiresIn: "1h" }, - }), - WalletModule, - forwardRef(()=>InvoiceModule), - ], - controllers: [CartController], - providers: [CartService, JwtAuthGuard], - exports: [CartService], -}) -export class CartModule {} diff --git a/src/cart/cart.response.ts b/src/cart/cart.response.ts deleted file mode 100644 index 829790c..0000000 --- a/src/cart/cart.response.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Cart } from "./entities/cart.entity"; - -export interface CartResponse { - message: string; - cartItem: Cart; -} diff --git a/src/cart/cart.service.ts b/src/cart/cart.service.ts deleted file mode 100644 index ab5310e..0000000 --- a/src/cart/cart.service.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { Injectable, HttpException, HttpStatus, Inject, forwardRef } from "@nestjs/common"; -import { InjectModel } from "@nestjs/sequelize"; -import { Cart } from "./entities/cart.entity"; -import { Product } from "src/products/entities/product.entity"; -import { WalletService } from "src/wallet/WalletService"; -import { InvoiceService } from "src/invoice/invoice.service"; -import { Invoice } from "src/invoice/entities/invoice.entity"; - -@Injectable() -export class CartService { - constructor( - @InjectModel(Cart) private readonly cartModel: typeof Cart, - @InjectModel(Invoice) private readonly invoiceModel: typeof Invoice, - @InjectModel(Product) private readonly productModel: typeof Product, - private readonly walletService: WalletService, - @Inject(forwardRef(() => InvoiceService)) - private invoiceService: InvoiceService, - ) {} - //create a cart and add item to cart - async createAndAddItemToCart(addToCartDto: { userId: number; productId: number; quantity: number }): Promise<{ message: string; cartItem: Cart }> { - const { userId, productId, quantity } = addToCartDto; - - if (!userId || !productId || !quantity || isNaN(Number(quantity)) || Number(quantity) <= 0) { - throw new HttpException("Invalid parameters: userId, productId, and a positive quantity are required.", HttpStatus.BAD_REQUEST); - } - const product = await this.productModel.findByPk(productId); - if (!product) { - throw new HttpException("Product not found!", HttpStatus.NOT_FOUND); - } - if (product.quantity < quantity) { - throw new HttpException("Product quantity insufficient!", HttpStatus.CONFLICT); - } - try { - let invoice = await this.invoiceModel.findOne({ where: { userId, status: "pending" } }); - if (!invoice) { - invoice = await this.invoiceService.createInvoiceFromCart(userId); - } - const invoiceId = invoice.id; - - let cart = await this.cartModel.findOne({ where: { userId, productId, status: "open" } }); - - if (!cart) { - cart = await this.cartModel.create({ - userId, - productId, - invoiceId, - quantity, - productPrice: product.price, - status: "open", - }); - await cart.save(); - } else { - cart.quantity += Number(quantity); - await cart.save(); - } - - await this.invoiceService.updateTotalPayment(userId); - - return { - message: cart.id ? "Product quantity updated in cart successfully!" : "Product added to cart successfully!", - cartItem: cart, - }; - } catch (error) { - throw new HttpException("An unexpected error occurred while adding the product to cart. Please try again later.", HttpStatus.INTERNAL_SERVER_ERROR); - } - } - // Get user's cart - async getUserOpenCart(userId: number): Promise<{ cartItems: Cart[]; totalPrice: number }> { - if (!userId) { - throw new HttpException("User ID is required.", HttpStatus.BAD_REQUEST); - } - - try { - const cartItems = await this.cartModel.findAll({ - where: { userId, status: "open" }, - include: [ - { - model: Product, - attributes: ["name", "price"], - }, - ], - }); - - if (!cartItems || cartItems.length === 0) { - return { cartItems: [], totalPrice: 0 }; - } - - const totalPrice = cartItems.reduce((sum, item) => { - return sum + (Number(item.productPrice) * item.quantity || 0); - }, 0); - - return { cartItems, totalPrice }; - } catch (error) { - console.error("Error fetching cart items:", error); - throw new HttpException("An unexpected error occurred while fetching the cart. Please try again later.", HttpStatus.INTERNAL_SERVER_ERROR); - } - } - - // Update cart item quantity - async updateCart(userId: number, productId: number, quantity: number): Promise { - const cartItem = await this.cartModel.findOne({ where: { userId, productId, status: "open" } }); - - if (!cartItem) { - throw new HttpException("Product not found in the cart.", HttpStatus.NOT_FOUND); - } - - const product = await this.productModel.findByPk(productId); - - if (!product) { - throw new HttpException("Product not found.", HttpStatus.NOT_FOUND); - } - - if (product.quantity < quantity) { - throw new HttpException("Insufficient product quantity.", HttpStatus.CONFLICT); - } - - try { - cartItem.quantity = quantity; - await cartItem.save(); - await this.invoiceService.updateTotalPayment(userId); - return cartItem; - } catch (error) { - throw new HttpException("An unexpected error occurred while updating the cart. Please try again later.", HttpStatus.INTERNAL_SERVER_ERROR); - } - } - - // Remove an item from cart - async removeFromCart(userId: number, productId: number): Promise<{ message: string; cartItem: Cart }> { - const cartItem = await this.cartModel.findOne({ where: { userId, productId, status: "open" } }); - if (!cartItem) { - throw new HttpException("Product not found in the cart.", HttpStatus.NOT_FOUND); - } - try { - await cartItem.destroy(); - await this.invoiceService.updateTotalPayment(userId); - return { message: "Item deleted from your cart successfully.", cartItem }; - } catch (error) { - throw new HttpException("An unexpected error occurred while removing the item from the cart. Please try again later.", HttpStatus.INTERNAL_SERVER_ERROR); - } - } - - //delete whole cart by user - async clearCart(userId: number) { - await this.cartModel.destroy({ - where: { userId, status: "open" }, - }); - return { message: "Cart cleared successfully" }; - } - - //order - async processOrder(userId: number, totalAmount: number): Promise<{ message: string; invoice: Invoice }> { - try { - const carts = await this.cartModel.findAll({ where: { userId, status: "open" } }); - if (!carts || carts.length === 0) { - throw new HttpException("No open carts found for this user.", HttpStatus.NOT_FOUND); - } - - let invoice: Invoice | null = null; - for (const cart of carts) { - const invoiceId = cart.invoiceId; - invoice = await this.invoiceModel.findOne({ where: { id: invoiceId, userId } }); - - if (invoice && invoice.status === "paid") { - return { - message: `Order for cart ID ${cart.id} has already been processed.`, - invoice, - }; - } - } - - await this.walletService.processPayment(userId, totalAmount); - - for (const cartItem of carts) { - const { productId, quantity } = cartItem; - - const product = await this.productModel.findOne({ where: { id: productId } }); - - if (!product) { - throw new HttpException(`Product with ID ${productId} not found.`, HttpStatus.NOT_FOUND); - } - - if (product.quantity < quantity) { - throw new HttpException(`Insufficient stock for product ID ${productId}.`, HttpStatus.BAD_REQUEST); - } - - product.quantity -= quantity; - await product.save(); - } - - for (const cart of carts) { - cart.status = "closed"; - await cart.save(); - } - - if (invoice) { - invoice.status = "paid"; - await invoice.save(); - } - - return { message: "Order processed successfully!", invoice }; - } catch (error) { - console.error(error); - if (error instanceof HttpException) { - throw error; - } else { - throw new HttpException(`An error occurred while processing the order: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR); - } - } - } -} diff --git a/src/cart/dto/add-to-cart.dto.ts b/src/cart/dto/add-to-cart.dto.ts deleted file mode 100644 index 0df51ee..0000000 --- a/src/cart/dto/add-to-cart.dto.ts +++ /dev/null @@ -1,13 +0,0 @@ -// add-to-cart.dto.ts -import { isInt, IsInt, IsNotEmpty, IsNumber, min, Min } from 'class-validator'; - -export class AddToCartDto { - @IsInt() - @IsNotEmpty() - productId: number; - - @IsInt() - @IsNotEmpty() - @Min(1) - quantity: number; -} diff --git a/src/cart/dto/update-cart.dto.ts b/src/cart/dto/update-cart.dto.ts deleted file mode 100644 index 1cab8bb..0000000 --- a/src/cart/dto/update-cart.dto.ts +++ /dev/null @@ -1,8 +0,0 @@ -// update-cart.dto.ts -import { IsInt, Min } from 'class-validator'; - -export class UpdateCartDto { - @IsInt() - @Min(1) - quantity: number; -} diff --git a/src/cart/entities/cart.entity.ts b/src/cart/entities/cart.entity.ts deleted file mode 100644 index 79816c3..0000000 --- a/src/cart/entities/cart.entity.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Model, Table, Column, ForeignKey, BelongsTo, DataType } from "sequelize-typescript"; -import { User } from "../../users/entities/user.entity"; -import { Product } from "../../products/entities/product.entity"; -import { Invoice } from "src/invoice/entities/invoice.entity"; - -@Table -export class Cart extends Model { - @ForeignKey(() => User) - @Column - userId: number; - - @BelongsTo(() => User, { onDelete: "CASCADE" }) - user: User; - - @ForeignKey(() => Product) - @Column - productId: number; - - @BelongsTo(() => Product, { onDelete: "CASCADE" }) - product: Product; - - @ForeignKey(() => Invoice) - @Column - invoiceId: number; - - @BelongsTo(() => Invoice, { onDelete: "CASCADE" }) - invoice: Invoice; - - @Column({ - type: DataType.INTEGER, - allowNull: true, - }) - quantity: number; - - @Column({ - type: DataType.INTEGER, - allowNull: true, - }) - productPrice: number; - - @Column({ - type: DataType.ENUM("open", "closed"), - allowNull: false, - defaultValue: "open", - }) - status: "open" | "closed"; -} diff --git a/src/invoice/entities/invoice.entity.ts b/src/invoice/entities/invoice.entity.ts deleted file mode 100644 index 8f90710..0000000 --- a/src/invoice/entities/invoice.entity.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { - Table, - Column, - ForeignKey, - BelongsTo, - DataType, - Model, - HasMany, -} from "sequelize-typescript"; -import { User } from "../../users/entities/user.entity"; -import { Cart } from "src/cart/entities/cart.entity"; - -@Table -export class Invoice extends Model { - @ForeignKey(() => User) - @Column - userId: number; - - @BelongsTo(() => User, { onDelete: "CASCADE" }) - user: User; - - @HasMany(() => Cart) - carts: Cart[]; - - @Column({ - type: DataType.INTEGER, - allowNull: false, - }) - totalPaymentAmount: number; - - @Column({ - type: DataType.ENUM("pending", "paid"), - allowNull: false, - defaultValue: "pending", - }) - status: string; -} diff --git a/src/invoice/invoice.controller.ts b/src/invoice/invoice.controller.ts deleted file mode 100644 index 100f285..0000000 --- a/src/invoice/invoice.controller.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Controller, Get, Post, Body, Patch, Param, Delete, UseGuards, Request } from "@nestjs/common"; -import { InvoiceService } from "./invoice.service"; -import { JwtAuthGuard } from "src/guard/auth.guard"; -import { RoleGuard } from "src/guard/role.guard"; - -@Controller("invoice") -export class InvoiceController { - constructor(private readonly invoiceService: InvoiceService) {} - @UseGuards(JwtAuthGuard) - @Get() - async getInvoiceByUser(@Request() req) { - const userId = req.user.id; - return this.invoiceService.getInvoiceByUser(userId); - } - @UseGuards(RoleGuard) - @Get('list') - async getInvoices() { - return this.invoiceService.getInvoices(); - } - @UseGuards(RoleGuard) - @Get(':id') - async getUserInvoice(@Param('id') id:number) { - return this.invoiceService.getUserInvoices(id); - } - -} diff --git a/src/invoice/invoice.module.ts b/src/invoice/invoice.module.ts deleted file mode 100644 index 31959a3..0000000 --- a/src/invoice/invoice.module.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Module, forwardRef } from "@nestjs/common"; -import { SequelizeModule } from "@nestjs/sequelize"; -import { InvoiceController } from "./invoice.controller"; -import { InvoiceService } from "./invoice.service"; -import { Invoice } from "./entities/invoice.entity"; -import { CartModule } from "src/cart/cart.module"; -import { JwtModule } from "@nestjs/jwt"; -import { JwtAuthGuard } from "src/guard/auth.guard"; -import { RoleGuard } from "src/guard/role.guard"; - -@Module({ - imports: [SequelizeModule.forFeature([Invoice]), - JwtModule.register({ - secret: process.env.JWT_SECRET, - signOptions: { expiresIn: "1h" }, - }), - forwardRef(()=>CartModule)], - controllers: [InvoiceController], - providers: [InvoiceService,JwtAuthGuard,RoleGuard], - exports: [InvoiceService], -}) -export class InvoiceModule {} diff --git a/src/invoice/invoice.service.ts b/src/invoice/invoice.service.ts deleted file mode 100644 index 85949aa..0000000 --- a/src/invoice/invoice.service.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from "@nestjs/common"; -import { InjectModel } from "@nestjs/sequelize"; -import { Invoice } from "./entities/invoice.entity"; -import { CartService } from "src/cart/cart.service"; -import { User } from "src/users/entities/user.entity"; - -@Injectable() -export class InvoiceService { - constructor( - @InjectModel(Invoice) private readonly invoiceModel: typeof Invoice, - @Inject(forwardRef(() => CartService)) - private cartService: CartService, - ) {} - - async createInvoiceFromCart(userId: number): Promise { - const user = await User.findByPk(userId); - if (!user) { - throw new HttpException("User not found", HttpStatus.NOT_FOUND); - } - - try { - const invoice = await this.invoiceModel.create({ - userId, - totalPaymentAmount: 0, - }); - - if (!invoice) { - throw new HttpException("Failed to create invoice", HttpStatus.INTERNAL_SERVER_ERROR); - } - - return invoice; - } catch (error) { - console.error("Error during invoice creation:", error); - throw new HttpException("An error occurred while creating the invoice.", HttpStatus.INTERNAL_SERVER_ERROR); - } - } - async updateTotalPayment(userId: number) { - const user = await User.findByPk(userId); - if (!user) { - throw new HttpException("User not found", HttpStatus.NOT_FOUND); - } - - const userCartItems = await this.cartService.getUserOpenCart(userId); - if (!userCartItems || !userCartItems.cartItems || userCartItems.cartItems.length === 0) { - throw new HttpException("Cart is empty", HttpStatus.BAD_REQUEST); - } - - let invoice = await this.invoiceModel.findOne({ where: { userId, status: "pending" } }); - if (!invoice) { - throw new HttpException("Invoice not found", HttpStatus.NOT_FOUND); - } - - invoice.totalPaymentAmount = userCartItems.totalPrice; - await invoice.save(); - } - - async getInvoicePendingByUser(userId: number): Promise { - try { - if (!userId) { - throw new HttpException("User ID are required.", HttpStatus.BAD_REQUEST); - } - - const invoice = await this.invoiceModel.findOne({ - where: { userId, status: "pending" }, - }); - - if (!invoice) { - throw new HttpException("Invoice not found for this user and cart.", HttpStatus.NOT_FOUND); - } - - return invoice; - } catch (error) { - if (error instanceof HttpException) { - throw error; - } - throw new HttpException("An error occurred while retrieving the invoice.", HttpStatus.INTERNAL_SERVER_ERROR); - } - } - async getInvoiceByUser(userId: number) { - try { - if (!userId) { - throw new HttpException("User ID are required.", HttpStatus.BAD_REQUEST); - } - - const invoices = await this.invoiceModel.findAll({ - where: { userId }, - }); - - if (!invoices) { - throw new HttpException("Invoice not found for this user and cart.", HttpStatus.NOT_FOUND); - } - - return { invoices }; - } catch (error) { - if (error instanceof HttpException) { - throw error; - } - throw new HttpException("An error occurred while retrieving the invoice.", HttpStatus.INTERNAL_SERVER_ERROR); - } - } - async getInvoices() { - try { - const invoices = await this.invoiceModel.findAll(); - - if (!invoices) { - throw new HttpException("Invoice not found for this user and cart.", HttpStatus.NOT_FOUND); - } - - return { invoices }; - } catch (error) { - if (error instanceof HttpException) { - throw error; - } - throw new HttpException("An error occurred while retrieving the invoice.", HttpStatus.INTERNAL_SERVER_ERROR); - } - } - async getUserInvoices(userId: number) { - try { - if (!userId) { - throw new HttpException("User ID are required.", HttpStatus.BAD_REQUEST); - } - - const invoices = await this.invoiceModel.findAll({ - where: { userId }, - }); - - if (!invoices) { - throw new HttpException("Invoice not found for this user and cart.", HttpStatus.NOT_FOUND); - } - - return { invoices }; - } catch (error) { - if (error instanceof HttpException) { - throw error; - } - throw new HttpException("An error occurred while retrieving the invoice.", HttpStatus.INTERNAL_SERVER_ERROR); - } - } -} diff --git a/src/payment/entities/payment.entity.ts b/src/payment/entities/payment.entity.ts deleted file mode 100644 index 4535b29..0000000 --- a/src/payment/entities/payment.entity.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { BelongsTo, Column, DataType, ForeignKey, Model, Table } from "sequelize-typescript"; -import { User } from "src/users/entities/user.entity"; -import { Wallet } from "src/wallet/entities/wallet.entity"; - -@Table -export class Payment extends Model { - @ForeignKey(() => User) - @Column - userId: number; - - @BelongsTo(() => User, { onDelete: "CASCADE" }) - user: User; - - @ForeignKey(() => Wallet) - @Column - walletId: number; - - @BelongsTo(() => Wallet, { onDelete: "CASCADE" }) - wallet: Wallet; - - @Column({ - type: DataType.INTEGER, - allowNull: false, - }) - paymentAmount: number; - - - @Column({ - type: DataType.ENUM( "completed", "failed",), - allowNull: false, - defaultValue: "failed", - }) - status: string; - - @Column({ - type: DataType.DATE, - allowNull: false, - defaultValue: DataType.NOW, - }) - paymentDate: Date; -} diff --git a/src/payment/payment.controller.ts b/src/payment/payment.controller.ts deleted file mode 100644 index 2275681..0000000 --- a/src/payment/payment.controller.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { Controller, Post, Body, Param, Get, Query, UseGuards, Request } from "@nestjs/common"; -import { PaymentService } from "./payment.service"; -import { InvoiceService } from "../invoice/invoice.service"; -import { WalletService } from "src/wallet/WalletService"; -import { console } from "inspector"; -import { InjectModel } from "@nestjs/sequelize"; -import { Payment } from "./entities/payment.entity"; -import { JwtAuthGuard } from "src/guard/auth.guard"; -import { Transaction } from "src/wallet/entities/transaction.entity"; -import { RoleGuard } from "src/guard/role.guard"; - -@Controller("payment") -export class PaymentController { - constructor( - @InjectModel(Payment) private readonly paymentModel: typeof Payment, - private readonly walletService: WalletService, - private readonly paymentService: PaymentService, - private readonly invoiceService: InvoiceService, - @InjectModel(Transaction) private readonly transactionModel: typeof Transaction, - ) {} - - @UseGuards(JwtAuthGuard) - @Post("request") - async requestPayment(@Request() req) { - const userId = req.user.id; - const invoice = await this.invoiceService.getInvoicePendingByUser(userId); - const totalAmount = invoice.totalPaymentAmount; - if (totalAmount < 1000) { - return { message: "please enter amount above 1000" }; - } - const callbackUrl = `http://localhost:3000/payment/verify?userId=${userId}&amount=${totalAmount}`; - const paymentUrl = await this.paymentService.requestPayment(totalAmount, "Purchase products", callbackUrl); - - return { url: paymentUrl }; - } - - @Get("verify") - async verifyPayment(@Query() query: { Authority: string; Status: string; userId: number; amount: number }): Promise { - const { Authority, Status, userId, amount } = query; - - if (Status !== "OK") { - throw new Error("Payment failed"); - } - - if (!userId) { - throw new Error("User ID is required."); - } - const wallet = this.walletService.getWalletInfo(userId); - try { - const refId = await this.paymentService.verifyPayment(Authority, amount); - await this.walletService.addBalance(userId, amount); - const wallet = this.walletService.getWalletInfo(userId); - await this.paymentModel.create({ - userId, - walletId: (await wallet).walletId, - paymentAmount: amount, - status: "completed", - }); - await this.transactionModel.create({ - walletId: (await wallet).walletId, - amount: String(amount).startsWith("+") ? String(amount) : `+${amount}`, - }); - return { message: "Payment successful", refId }; - } catch (error) { - console.log(error); - await this.paymentModel.create({ - userId, - walletId: (await wallet).walletId, - paymentAmount: amount, - status: "failed", - }); - throw new Error(`Error during payment verification: ${error.message}`); - } - } -} diff --git a/src/payment/payment.module.ts b/src/payment/payment.module.ts deleted file mode 100644 index dbf4664..0000000 --- a/src/payment/payment.module.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Module } from '@nestjs/common'; -import { PaymentService } from './payment.service'; -import { PaymentController } from './payment.controller'; -import { InvoiceService } from 'src/invoice/invoice.service'; -import { CartModule } from 'src/cart/cart.module'; -import { WalletModule } from 'src/wallet/wallet.module'; -import { InvoiceModule } from 'src/invoice/invoice.module'; -import { Payment } from './entities/payment.entity'; -import { SequelizeModule } from '@nestjs/sequelize'; -import { JwtModule } from '@nestjs/jwt'; -import { Transaction } from 'src/wallet/entities/transaction.entity'; - -@Module({ - imports:[SequelizeModule.forFeature([Payment,Transaction]), - JwtModule.register({ - secret: process.env.JWT_SECRET, - signOptions: { expiresIn: "1h" }, - }), - CartModule,WalletModule,InvoiceModule], - controllers: [PaymentController], - providers: [PaymentService], -}) -export class PaymentModule {} diff --git a/src/payment/payment.service.ts b/src/payment/payment.service.ts deleted file mode 100644 index 0700e33..0000000 --- a/src/payment/payment.service.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Injectable, InternalServerErrorException } from "@nestjs/common"; -import { InjectModel } from "@nestjs/sequelize"; -import { Payment } from "./entities/payment.entity"; - -const ZarinpalCheckout = require("zarinpal-checkout"); - -@Injectable() -export class PaymentService { - private zarinpal; - - constructor() { - this.zarinpal = this.initializeZarinpal(); - } - - private initializeZarinpal() { - const merchantId = "00000000-0000-0000-0000-000000000000"; // Merchant ID should be valid - const sandboxMode = true; - return ZarinpalCheckout.create(merchantId, sandboxMode); - } - - async requestPayment(amount: number, description: string, callbackUrl: string): Promise { - try { - const result = await this.zarinpal.PaymentRequest({ - Amount: amount, - CallbackURL: callbackUrl, - Description: description, - }); - - if (result.status === 100) { - return result.url; - } else { - throw new Error(`Payment request failed with status: ${result.status}`); - } - } catch (error) { - console.log("Error in PaymentRequest:", error.message || error); - throw new InternalServerErrorException(`Error in payment request: ${error.message}`); - } - } - - async verifyPayment(authority: string, amount: number): Promise { - try { - const result = await this.zarinpal.PaymentVerification({ - Amount: amount, - Authority: authority, - }); - if (result.status === 100) { - return result.RefID; - } else { - throw new Error(`Payment verification failed with status: ${result.status}`); - } - } catch (error) { - throw new InternalServerErrorException(`Error in payment verification: ${error.message}`); - } - } - -} diff --git a/src/products/entities/cart.entity.ts b/src/products/entities/cart.entity.ts index 79816c3..f2daca2 100644 --- a/src/products/entities/cart.entity.ts +++ b/src/products/entities/cart.entity.ts @@ -1,7 +1,7 @@ import { Model, Table, Column, ForeignKey, BelongsTo, DataType } from "sequelize-typescript"; import { User } from "../../users/entities/user.entity"; import { Product } from "../../products/entities/product.entity"; -import { Invoice } from "src/invoice/entities/invoice.entity"; +import { Invoice } from "./invoice.entity"; @Table export class Cart extends Model { diff --git a/src/products/entities/invoice.entity.ts b/src/products/entities/invoice.entity.ts index 8f90710..79792a3 100644 --- a/src/products/entities/invoice.entity.ts +++ b/src/products/entities/invoice.entity.ts @@ -8,7 +8,7 @@ import { HasMany, } from "sequelize-typescript"; import { User } from "../../users/entities/user.entity"; -import { Cart } from "src/cart/entities/cart.entity"; +import { Cart } from "./cart.entity"; @Table export class Invoice extends Model { diff --git a/src/products/entities/payment.entity.ts b/src/products/entities/payment.entity.ts index 4535b29..b5ba836 100644 --- a/src/products/entities/payment.entity.ts +++ b/src/products/entities/payment.entity.ts @@ -1,6 +1,6 @@ import { BelongsTo, Column, DataType, ForeignKey, Model, Table } from "sequelize-typescript"; import { User } from "src/users/entities/user.entity"; -import { Wallet } from "src/wallet/entities/wallet.entity"; +import { Wallet } from "./wallet.entity"; @Table export class Payment extends Model { diff --git a/src/products/products.controller.ts b/src/products/shop.controller.ts similarity index 99% rename from src/products/products.controller.ts rename to src/products/shop.controller.ts index fa7856f..ab2d3a4 100644 --- a/src/products/products.controller.ts +++ b/src/products/shop.controller.ts @@ -1,5 +1,5 @@ import { Controller, Get, Post, Body, Param, Delete, Query, Put, UseGuards, Request, Patch, HttpException, HttpStatus } from "@nestjs/common"; -import { ProductsService } from "./products.service"; +import { ProductsService } from "./shop.service"; import { Product } from "./entities/product.entity"; import { CreateProductDto } from "./dto/products/create-product.dto"; import { UpdateProductDto } from "./dto/products/update-product.dto"; @@ -211,7 +211,7 @@ export class ProductsController { } //get specific user invoices @UseGuards(RoleGuard) - @Get(':id') + @Get('invoice/:id') async getUserInvoice(@Param('id') id:number) { return this.productsService.getUserInvoices(id); } diff --git a/src/products/products.module.ts b/src/products/shop.module.ts similarity index 63% rename from src/products/products.module.ts rename to src/products/shop.module.ts index 0dfda18..9f2a7d3 100644 --- a/src/products/products.module.ts +++ b/src/products/shop.module.ts @@ -1,19 +1,16 @@ import { Module } from "@nestjs/common"; -import { ProductsService } from "./products.service"; -import { ProductsController } from "./products.controller"; +import { ProductsService } from "./shop.service"; +import { ProductsController } from "./shop.controller"; import { SequelizeModule } from "@nestjs/sequelize"; import { Product } from "./entities/product.entity"; import { RoleGuard } from "src/guard/role.guard"; import { JwtModule } from "@nestjs/jwt"; import { Cart } from "./entities/cart.entity"; import { JwtAuthGuard } from "src/guard/auth.guard"; -import { Invoice } from "src/invoice/entities/invoice.entity"; -import { InvoiceModule } from "src/invoice/invoice.module"; -import { WalletModule } from "src/wallet/wallet.module"; import { Wallet } from "./entities/wallet.entity"; import { Transaction } from "./entities/transaction.entity"; -import { PaymentService } from "src/payment/payment.service"; import { Payment } from "./entities/payment.entity"; +import { Invoice } from "./entities/invoice.entity"; @Module({ imports: [SequelizeModule.forFeature([Product,Cart,Invoice,Wallet, Transaction,Payment]), @@ -21,10 +18,8 @@ import { Payment } from "./entities/payment.entity"; secret: process.env.JWT_SECRET, signOptions: { expiresIn: '1h' }, }), - WalletModule, - InvoiceModule ], controllers: [ProductsController], - providers: [ProductsService,RoleGuard,JwtAuthGuard,PaymentService], + providers: [ProductsService,RoleGuard,JwtAuthGuard], }) export class ProductsModule {} diff --git a/src/products/products.service.ts b/src/products/shop.service.ts similarity index 97% rename from src/products/products.service.ts rename to src/products/shop.service.ts index f9ca74c..3bbb034 100644 --- a/src/products/products.service.ts +++ b/src/products/shop.service.ts @@ -6,12 +6,10 @@ import { UpdateProductDto } from "./dto/products/update-product.dto"; import { Op } from "sequelize"; import { HttpException, HttpStatus } from "@nestjs/common"; import { Cart } from "./entities/cart.entity"; -import { Invoice } from "src/invoice/entities/invoice.entity"; -import { InvoiceService } from "src/invoice/invoice.service"; -import { WalletService } from "src/wallet/WalletService"; import { Wallet } from "./entities/wallet.entity"; import { Transaction } from "./entities/transaction.entity"; import { InternalServerErrorException } from "@nestjs/common"; +import { Invoice } from "./entities/invoice.entity"; const ZarinpalCheckout = require("zarinpal-checkout"); @Injectable() @@ -23,8 +21,6 @@ export class ProductsService { @InjectModel(Invoice) private readonly invoiceModel: typeof Invoice, @InjectModel(Wallet) private walletModel: typeof Wallet, @InjectModel(Transaction) private transactionModel: typeof Transaction, - private invoiceService: InvoiceService, - private walletService: WalletService, ) { this.zarinpal = this.initializeZarinpal(); } @@ -225,7 +221,7 @@ export class ProductsService { try { let invoice = await this.invoiceModel.findOne({ where: { userId, status: "pending" } }); if (!invoice) { - invoice = await this.invoiceService.createInvoiceFromCart(userId); + invoice = await this.createInvoiceFromCart(userId); } const invoiceId = invoice.id; @@ -246,7 +242,7 @@ export class ProductsService { await cart.save(); } - await this.invoiceService.updateTotalPayment(userId); + await this.updateTotalPayment(userId); return { message: cart.id ? "Product quantity updated in cart successfully!" : "Product added to cart successfully!", @@ -313,7 +309,7 @@ export class ProductsService { try { cartItem.quantity = quantity; await cartItem.save(); - await this.invoiceService.updateTotalPayment(userId); + await this.updateTotalPayment(userId); return cartItem; } catch (error) { if (error instanceof HttpException) { @@ -330,7 +326,7 @@ export class ProductsService { } try { await cartItem.destroy(); - await this.invoiceService.updateTotalPayment(userId); + await this.updateTotalPayment(userId); return { message: "Item deleted from your cart successfully.", cartItem }; } catch (error) { if (error instanceof HttpException) { @@ -366,7 +362,7 @@ export class ProductsService { } } - await this.walletService.processPayment(userId, totalAmount); + await this.processPayment(userId, totalAmount); for (const cartItem of carts) { const { productId, quantity } = cartItem; diff --git a/src/wallet/WalletService.ts b/src/wallet/WalletService.ts deleted file mode 100644 index 0cde7ec..0000000 --- a/src/wallet/WalletService.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { Injectable, HttpException, HttpStatus } from "@nestjs/common"; -import { InjectModel } from "@nestjs/sequelize"; -import { AddBalanceResponse } from "./add-balance-response.interface"; -import { Transaction } from "./entities/transaction.entity"; -import { Wallet } from "./entities/wallet.entity"; - -@Injectable() -export class WalletService { - constructor( - @InjectModel(Wallet) private walletModel: typeof Wallet, - @InjectModel(Transaction) private transactionModel: typeof Transaction, - ) {} - //get wallet info - async getWalletInfo(userId: number) { - const wallet = await this.walletModel.findOne({ where: { userId } }); - - if (!wallet) { - const newWallet = await this.walletModel.create({ userId, balance: 0 }); - return { walletId: newWallet.id, userId: newWallet.userId, balance: newWallet.balance }; - } - - return { walletId: wallet.id, userId: wallet.userId, balance: wallet.balance }; - } - //get wallet balance - async getBalance(userId: number) { - const wallet = await this.walletModel.findOne({ where: { userId } }); - - if (!wallet) { - throw new HttpException("Wallet not found!", HttpStatus.NOT_FOUND); - } - - return { balance: wallet.balance }; - } - //charge balance of wallet by user - async addBalance(userId: number, amount: number): Promise { - try { - const wallet = await this.walletModel.findOne({ where: { userId } }); - if (wallet) { - wallet.balance += Number(amount); - await wallet.save(); - return { message: "Balance updated successfully.", balance: wallet.balance }; - } else { - const newWallet = await this.walletModel.create({ userId, balance: amount }); - return { message: "Wallet created and balance added successfully.", balance: newWallet.balance }; - } - } catch (error) { - throw new HttpException("An error occurred while adding balance to the wallet.", HttpStatus.INTERNAL_SERVER_ERROR); - } - } - //process of payment - async processPayment(userId: number, amount: number): Promise { - const wallet = await this.walletModel.findOne({ where: { userId } }); - - if (!wallet) { - throw new HttpException("Please Charge your wallet", HttpStatus.NOT_FOUND); - } - - if (wallet.balance < amount) { - throw new HttpException("Insufficient funds", HttpStatus.BAD_REQUEST); - } - try { - wallet.balance -= amount; - - await this.transactionModel.create({ - walletId: wallet.id, - amount: `-${amount}`, - }); - - await wallet.save(); - - return "Payment processed successfully"; - } catch (error) { - console.error("Error processing payment:", error.message); - throw new HttpException("An error occurred while processing the payment.", HttpStatus.INTERNAL_SERVER_ERROR); - } - } - //getting transaction - async getTransactionById(userId: number) { - const wallet = await this.getWalletInfo(userId); - if (!wallet) { - throw new HttpException("Wallet not found for the user.", HttpStatus.NOT_FOUND); - } - return await this.transactionModel.findAll({ - where: { walletId: wallet.walletId }, - }); - } - //getting transaction a user (admin) - async getTransactionByIdForAdmin(userId: number) { - const wallet = await this.getWalletInfo(userId); - if (!wallet) { - throw new HttpException("Wallet not found for the user.", HttpStatus.NOT_FOUND); - } - return await this.transactionModel.findAll({ - where: { walletId: wallet.walletId }, - }); - } -} diff --git a/src/wallet/add-balance-response.interface.ts b/src/wallet/add-balance-response.interface.ts deleted file mode 100644 index 3cd0b52..0000000 --- a/src/wallet/add-balance-response.interface.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface AddBalanceResponse { - message: string; - balance: number; -} diff --git a/src/wallet/entities/transaction.entity.ts b/src/wallet/entities/transaction.entity.ts deleted file mode 100644 index 10e27e6..0000000 --- a/src/wallet/entities/transaction.entity.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Model, Table, Column, ForeignKey, BelongsTo, DataType } from 'sequelize-typescript'; -import { Wallet } from './wallet.entity'; - -@Table -export class Transaction extends Model { - @ForeignKey(() => Wallet) - @Column - walletId: number; - - @BelongsTo(() => Wallet, { onDelete: 'CASCADE' }) - wallet: Wallet; - - @Column({ - type: DataType.STRING, - allowNull: false, - defaultValue: "0", - }) - amount: string; -} diff --git a/src/wallet/entities/wallet.entity.ts b/src/wallet/entities/wallet.entity.ts deleted file mode 100644 index 6c70b98..0000000 --- a/src/wallet/entities/wallet.entity.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Model, Table, Column, ForeignKey, BelongsTo, DataType } from 'sequelize-typescript'; -import { User } from '../../users/entities/user.entity'; - -@Table -export class Wallet extends Model { - @ForeignKey(() => User) - @Column - userId: number; - - @BelongsTo(() => User, { onDelete: 'CASCADE' }) - user: User; - - @Column({ - type: DataType.INTEGER, - allowNull: false, - defaultValue: 0, - }) - balance: number; -} diff --git a/src/wallet/wallet.controller.ts b/src/wallet/wallet.controller.ts deleted file mode 100644 index 72a4ab4..0000000 --- a/src/wallet/wallet.controller.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Controller, Get, Post, Body, Patch, Param, Delete, UseGuards, Request, forwardRef, Inject } from "@nestjs/common"; -import { WalletService } from "./WalletService"; -import { JwtAuthGuard } from "src/guard/auth.guard"; -import { PaymentService } from "src/payment/payment.service"; -import { RoleGuard } from "src/guard/role.guard"; - -@Controller("wallet") -export class WalletController { - constructor( - private readonly walletService: WalletService, - @Inject(forwardRef(() => PaymentService)) - private paymentService: PaymentService, - ) {} - - //getting wallet balance by user - @UseGuards(JwtAuthGuard) - @Get() - async getBalance(@Request() req) { - const userId = req.user.id; - return this.walletService.getBalance(userId); - } - - @UseGuards(JwtAuthGuard) - @Post("charge") - async addBalance(@Body("amount") amount: number, @Request() req) { - const userId = req.user.id; - const callbackUrl = `http://localhost:3000/payment/verify?userId=${userId}&amount=${amount}`; - const paymentUrl = this.paymentService.requestPayment(amount, "Wallet Charge", callbackUrl); - return paymentUrl; - } - - @UseGuards(JwtAuthGuard) - @Get("transaction") - async getTransactionById(@Request() req) { - const userId = req.user.id; - return this.walletService.getTransactionById(userId); - } - - @UseGuards(RoleGuard) - @Get("transaction/:id") - async getTransactionByIdForAdmin(@Param("id") id: number) { - return this.walletService.getTransactionByIdForAdmin(id); - } -} diff --git a/src/wallet/wallet.module.ts b/src/wallet/wallet.module.ts deleted file mode 100644 index 07fe12d..0000000 --- a/src/wallet/wallet.module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Module } from "@nestjs/common"; -import { WalletService } from "./WalletService"; -import { WalletController } from "./wallet.controller"; -import { Wallet } from "./entities/wallet.entity"; -import { SequelizeModule } from "@nestjs/sequelize"; -import { JwtModule } from "@nestjs/jwt"; -import { RoleGuard } from "src/guard/role.guard"; -import { JwtAuthGuard } from "src/guard/auth.guard"; -import { PaymentService } from "src/payment/payment.service"; -import { Transaction } from "./entities/transaction.entity"; - -@Module({ - imports: [ - SequelizeModule.forFeature([Wallet, Transaction]), - JwtModule.register({ - secret: process.env.JWT_SECRET, - signOptions: { expiresIn: "1h" }, - }), - ], - controllers: [WalletController], - providers: [WalletService, JwtAuthGuard, RoleGuard, PaymentService], - exports: [WalletService], -}) -export class WalletModule {} diff --git a/src/wallet/wallet.service.ts b/src/wallet/wallet.service.ts deleted file mode 100644 index 35c0fb1..0000000 --- a/src/wallet/wallet.service.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { InjectModel } from "@nestjs/sequelize"; -import { Wallet } from "./entities/wallet.entity"; -import { HttpException, HttpStatus } from "@nestjs/common"; -import { AddBalanceResponse } from "./add-balance-response.interface"; -import { Transaction } from "./entities/transaction.entity"; - -@Injectable() -export class WalletService { - constructor( - @InjectModel(Wallet) private walletModel: typeof Wallet, - @InjectModel(Transaction) private transactionModel: typeof Transaction, - ) {} - //get wallet info - async getWalletInfo(userId: number) { - const wallet = await this.walletModel.findOne({ where: { userId } }); - - if (!wallet) { - const newWallet = await this.walletModel.create({ userId, balance: 0 }); - return { walletId: newWallet.id, userId: newWallet.userId, balance: newWallet.balance }; - } - - return { walletId: wallet.id, userId: wallet.userId, balance: wallet.balance }; - } - //get wallet balance - async getBalance(userId: number) { - const wallet = await this.walletModel.findOne({ where: { userId } }); - - if (!wallet) { - throw new HttpException("Wallet not found!", HttpStatus.NOT_FOUND); - } - - return { balance: wallet.balance }; - } - //charge balance of wallet by user - async addBalance(userId: number, amount: number): Promise { - try { - const wallet = await this.walletModel.findOne({ where: { userId } }); - if (wallet) { - wallet.balance += Number(amount); - await wallet.save(); - return { message: "Balance updated successfully.", balance: wallet.balance }; - } else { - const newWallet = await this.walletModel.create({ userId, balance: amount }); - return { message: "Wallet created and balance added successfully.", balance: newWallet.balance }; - } - } catch (error) { - throw new HttpException("An error occurred while adding balance to the wallet.", HttpStatus.INTERNAL_SERVER_ERROR); - } - } - //process of payment - async processPayment(userId: number, amount: number): Promise { - const wallet = await this.walletModel.findOne({ where: { userId } }); - - if (!wallet) { - throw new HttpException("Please Charge your wallet", HttpStatus.NOT_FOUND); - } - - if (wallet.balance < amount) { - throw new HttpException("Insufficient funds", HttpStatus.BAD_REQUEST); - } - try { - wallet.balance -= amount; - - await this.transactionModel.create({ - walletId: wallet.id, - amount: `-${amount}`, - }); - - await wallet.save(); - - return "Payment processed successfully"; - } catch (error) { - console.error("Error processing payment:", error.message); - throw new HttpException("An error occurred while processing the payment.", HttpStatus.INTERNAL_SERVER_ERROR); - } - } - //getting transaction - async getTransactionById(userId: number) { - const wallet = await this.getWalletInfo(userId); - if (!wallet) { - throw new HttpException("Wallet not found for the user.", HttpStatus.NOT_FOUND); - } - return await this.transactionModel.findAll({ - where: { walletId: wallet.walletId }, - }); - } - //getting transaction a user (admin) - async getTransactionByIdForAdmin(userId: number) { - const wallet = await this.getWalletInfo(userId); - if (!wallet) { - throw new HttpException("Wallet not found for the user.", HttpStatus.NOT_FOUND); - } - return await this.transactionModel.findAll({ - where: { walletId: wallet.walletId }, - }); - } -}