Compare commits
8 Commits
cc98768a90
...
a864ed9999
Author | SHA1 | Date |
---|---|---|
|
a864ed9999 | 1 month ago |
|
a79e6c68e4 | 1 month ago |
|
15b8d229d4 | 1 month ago |
|
1ce941b31c | 1 month ago |
|
79a36d2b82 | 1 month ago |
|
112e0598b5 | 1 month ago |
|
89051142da | 1 month ago |
|
725412c59c | 1 month ago |
37 changed files with 903 additions and 1182 deletions
@ -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); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -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 {} |
|
@ -1,6 +0,0 @@ |
|||||||
import { Cart } from "./entities/cart.entity"; |
|
||||||
|
|
||||||
export interface CartResponse { |
|
||||||
message: string; |
|
||||||
cartItem: Cart;
|
|
||||||
} |
|
@ -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<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); |
|
||||||
} |
|
||||||
|
|
||||||
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); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -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); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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 {} |
|
@ -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<Invoice> { |
|
||||||
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<Invoice> { |
|
||||||
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); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -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<any> { |
|
||||||
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}`); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -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 {} |
|
@ -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<string> { |
|
||||||
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<string> { |
|
||||||
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}`); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,45 +0,0 @@ |
|||||||
import { Controller, Get, Post, Body, Param, Delete, Query, Put, UseGuards } from "@nestjs/common"; |
|
||||||
import { ProductsService } from "./products.service"; |
|
||||||
import { Product } from "./entities/product.entity"; |
|
||||||
import { CreateProductDto } from "./dto/create-product.dto"; |
|
||||||
import { UpdateProductDto } from "./dto/update-product.dto"; |
|
||||||
import { RoleGuard } from "src/guard/role.guard"; |
|
||||||
|
|
||||||
@Controller("products") |
|
||||||
export class ProductsController { |
|
||||||
constructor(private readonly productsService: ProductsService) {} |
|
||||||
|
|
||||||
@UseGuards(RoleGuard) |
|
||||||
@Post() |
|
||||||
async create(@Body() createProductDto: CreateProductDto) { |
|
||||||
const product = await this.productsService.create(createProductDto); |
|
||||||
return { |
|
||||||
message: "Product created successfully!", |
|
||||||
product, |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
@Get() |
|
||||||
async findAll(@Query() query: { search?: string; priceMin?: number; priceMax?: number }) { |
|
||||||
const { search, priceMin, priceMax } = query; |
|
||||||
return this.productsService.findAll(search, priceMin, priceMax); |
|
||||||
} |
|
||||||
|
|
||||||
@Get(":id") |
|
||||||
async findOne(@Param("id") id: string): Promise<Product> { |
|
||||||
return this.productsService.findOne(id); |
|
||||||
} |
|
||||||
@UseGuards(RoleGuard) |
|
||||||
@Put(":id") |
|
||||||
async update( |
|
||||||
@Param("id") id: string, |
|
||||||
@Body() updateProductDto: UpdateProductDto |
|
||||||
): Promise<Product> { |
|
||||||
return this.productsService.update(id, updateProductDto); |
|
||||||
} |
|
||||||
@UseGuards(RoleGuard) |
|
||||||
@Delete(':id') |
|
||||||
async remove(@Param('id') id: string): Promise<{ message: string }> { |
|
||||||
return this.productsService.remove(id); |
|
||||||
} |
|
||||||
} |
|
@ -1,19 +0,0 @@ |
|||||||
import { Module } from "@nestjs/common"; |
|
||||||
import { ProductsService } from "./products.service"; |
|
||||||
import { ProductsController } from "./products.controller"; |
|
||||||
import { SequelizeModule } from "@nestjs/sequelize"; |
|
||||||
import { Product } from "./entities/product.entity"; |
|
||||||
import { RoleGuard } from "src/guard/role.guard"; |
|
||||||
import { JwtModule } from "@nestjs/jwt"; |
|
||||||
|
|
||||||
@Module({ |
|
||||||
imports: [SequelizeModule.forFeature([Product]), |
|
||||||
JwtModule.register({
|
|
||||||
secret: process.env.JWT_SECRET, |
|
||||||
signOptions: { expiresIn: '1h' }, |
|
||||||
}) |
|
||||||
], |
|
||||||
controllers: [ProductsController], |
|
||||||
providers: [ProductsService,RoleGuard], |
|
||||||
}) |
|
||||||
export class ProductsModule {} |
|
@ -1,184 +0,0 @@ |
|||||||
import { Injectable } from "@nestjs/common"; |
|
||||||
import { InjectModel } from "@nestjs/sequelize"; |
|
||||||
import { Product } from "./entities/product.entity"; |
|
||||||
import { CreateProductDto } from "./dto/create-product.dto"; |
|
||||||
import { UpdateProductDto } from "./dto/update-product.dto"; |
|
||||||
import { Op } from "sequelize"; |
|
||||||
import { HttpException, HttpStatus } from "@nestjs/common"; |
|
||||||
|
|
||||||
@Injectable() |
|
||||||
export class ProductsService { |
|
||||||
constructor(@InjectModel(Product) private readonly productModel: typeof Product) {} |
|
||||||
|
|
||||||
// create a new product
|
|
||||||
async create(createProductDto: CreateProductDto): Promise<Product> { |
|
||||||
try { |
|
||||||
const existingProduct = await this.productModel.findOne({ |
|
||||||
where: { name: createProductDto.name }, |
|
||||||
}); |
|
||||||
|
|
||||||
if (existingProduct) { |
|
||||||
existingProduct.quantity += createProductDto.quantity || 0; |
|
||||||
await existingProduct.save(); |
|
||||||
|
|
||||||
return existingProduct; |
|
||||||
} |
|
||||||
const newProduct = await this.productModel.create(createProductDto); |
|
||||||
return newProduct; |
|
||||||
} catch (error) { |
|
||||||
throw new HttpException("An error occurred while creating or updating the product.", HttpStatus.INTERNAL_SERVER_ERROR); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// find a product by id
|
|
||||||
async findOne(id: string): Promise<Product> { |
|
||||||
try { |
|
||||||
const product = await this.productModel.findByPk(id); |
|
||||||
|
|
||||||
if (!product) { |
|
||||||
throw new HttpException("Product not found with the given ID.", HttpStatus.NOT_FOUND); |
|
||||||
} |
|
||||||
|
|
||||||
return product; |
|
||||||
} catch (error) { |
|
||||||
if (error instanceof HttpException) { |
|
||||||
throw error; |
|
||||||
} |
|
||||||
|
|
||||||
throw new HttpException("An unexpected error occurred while fetching the product.", HttpStatus.INTERNAL_SERVER_ERROR); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// list of all product
|
|
||||||
async findAll( |
|
||||||
search?: string, |
|
||||||
priceMin?: number, |
|
||||||
priceMax?: number, |
|
||||||
page: number = 1, |
|
||||||
limit: number = 10, |
|
||||||
): Promise<{ products: Product[]; total: number; totalPages: number; currentPage: number }> { |
|
||||||
try { |
|
||||||
const where: Record<string, any> = {}; |
|
||||||
|
|
||||||
if (search) { |
|
||||||
where.name = { [Op.iLike]: `%${search}%` }; |
|
||||||
} |
|
||||||
|
|
||||||
if (priceMin !== undefined || priceMax !== undefined) { |
|
||||||
where.price = {}; |
|
||||||
if (priceMin !== undefined) { |
|
||||||
where.price[Op.gte] = priceMin; |
|
||||||
} |
|
||||||
if (priceMax !== undefined) { |
|
||||||
where.price[Op.lte] = priceMax; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
const offset = (page - 1) * limit; |
|
||||||
|
|
||||||
const { rows: products, count: total } = await this.productModel.findAndCountAll({ |
|
||||||
where, |
|
||||||
limit, |
|
||||||
offset, |
|
||||||
}); |
|
||||||
|
|
||||||
const totalPages = Math.ceil(total / limit); |
|
||||||
|
|
||||||
return { |
|
||||||
products, |
|
||||||
total, |
|
||||||
totalPages, |
|
||||||
currentPage: page, |
|
||||||
}; |
|
||||||
} catch (error) { |
|
||||||
console.error("Error retrieving products:", error.message); |
|
||||||
|
|
||||||
if (error instanceof HttpException) { |
|
||||||
throw error; |
|
||||||
} |
|
||||||
|
|
||||||
throw new HttpException("An unexpected error occurred while retrieving products.", HttpStatus.INTERNAL_SERVER_ERROR); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// update a product info
|
|
||||||
async update(id: string, updateProductDto: UpdateProductDto): Promise<Product> { |
|
||||||
const product = await this.productModel.findByPk(id); |
|
||||||
|
|
||||||
if (!product) { |
|
||||||
throw new HttpException("Product not found.", HttpStatus.NOT_FOUND); |
|
||||||
} |
|
||||||
try { |
|
||||||
const { name, description, price, imageUrl, tags, quantity, brand, color, category } = updateProductDto; |
|
||||||
|
|
||||||
let updated = false; // متغیر برای بررسی تغییرات
|
|
||||||
|
|
||||||
if (name && name !== product.name) { |
|
||||||
product.name = name; |
|
||||||
updated = true; |
|
||||||
} |
|
||||||
if (description && description !== product.description) { |
|
||||||
product.description = description; |
|
||||||
updated = true; |
|
||||||
} |
|
||||||
if (price !== undefined && price !== product.price) { |
|
||||||
product.price = price; |
|
||||||
updated = true; |
|
||||||
} |
|
||||||
if (imageUrl && imageUrl !== product.imageUrl) { |
|
||||||
product.imageUrl = imageUrl; |
|
||||||
updated = true; |
|
||||||
} |
|
||||||
if (tags && tags !== product.tags) { |
|
||||||
product.tags = tags; |
|
||||||
updated = true; |
|
||||||
} |
|
||||||
if (quantity !== undefined && quantity !== product.quantity) { |
|
||||||
product.quantity = quantity; |
|
||||||
updated = true; |
|
||||||
} |
|
||||||
if (brand && brand !== product.brand) { |
|
||||||
product.brand = brand; |
|
||||||
updated = true; |
|
||||||
} |
|
||||||
if (color && color !== product.color) { |
|
||||||
product.color = color; |
|
||||||
updated = true; |
|
||||||
} |
|
||||||
if (category && category !== product.category) { |
|
||||||
product.category = category; |
|
||||||
updated = true; |
|
||||||
} |
|
||||||
|
|
||||||
if (updated) { |
|
||||||
await product.save(); |
|
||||||
} |
|
||||||
|
|
||||||
return product; |
|
||||||
} catch (error) { |
|
||||||
throw new HttpException("An error occurred while updating the product.", HttpStatus.INTERNAL_SERVER_ERROR); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// delete a product
|
|
||||||
async remove(id: string): Promise<{ message: string }> { |
|
||||||
try { |
|
||||||
const product = await this.productModel.findByPk(id); |
|
||||||
|
|
||||||
if (!product) { |
|
||||||
throw new HttpException(`Product with id ${id} not found.`, HttpStatus.NOT_FOUND); |
|
||||||
} |
|
||||||
|
|
||||||
await product.destroy(); |
|
||||||
|
|
||||||
return { message: "Product deleted successfully." }; |
|
||||||
} catch (error) { |
|
||||||
console.error("Error during product deletion:", error.message); |
|
||||||
if (error instanceof HttpException) { |
|
||||||
throw error; |
|
||||||
} |
|
||||||
|
|
||||||
throw new HttpException("An unexpected error occurred while deleting the product.", HttpStatus.INTERNAL_SERVER_ERROR); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,7 +1,7 @@ |
|||||||
import { Model, Table, Column, ForeignKey, BelongsTo, DataType } from "sequelize-typescript"; |
import { Model, Table, Column, ForeignKey, BelongsTo, DataType } from "sequelize-typescript"; |
||||||
import { User } from "../../users/entities/user.entity"; |
import { User } from "../../users/entities/user.entity"; |
||||||
import { Product } from "../../products/entities/product.entity"; |
import { Product } from "../../shop/entities/product.entity"; |
||||||
import { Invoice } from "src/invoice/entities/invoice.entity"; |
import { Invoice } from "./invoice.entity"; |
||||||
|
|
||||||
@Table |
@Table |
||||||
export class Cart extends Model<Cart> { |
export class Cart extends Model<Cart> { |
@ -1,6 +1,6 @@ |
|||||||
import { BelongsTo, Column, DataType, ForeignKey, Model, Table } from "sequelize-typescript"; |
import { BelongsTo, Column, DataType, ForeignKey, Model, Table } from "sequelize-typescript"; |
||||||
import { User } from "src/users/entities/user.entity"; |
import { User } from "src/users/entities/user.entity"; |
||||||
import { Wallet } from "src/wallet/entities/wallet.entity"; |
import { Wallet } from "./wallet.entity"; |
||||||
|
|
||||||
@Table |
@Table |
||||||
export class Payment extends Model<Payment> { |
export class Payment extends Model<Payment> { |
@ -0,0 +1,221 @@ |
|||||||
|
import { Controller, Get, Post, Body, Param, Delete, Query, Put, UseGuards, Request, Patch, HttpException, HttpStatus } from "@nestjs/common"; |
||||||
|
import { ShopService } 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"; |
||||||
|
import { RoleGuard } from "src/users/guard/role.guard"; |
||||||
|
import { AddToCartDto } from "./dto/cart/add-to-cart.dto"; |
||||||
|
import { JwtAuthGuard } from "src/users/guard/auth.guard"; |
||||||
|
import { UpdateCartDto } from "./dto/cart/update-cart.dto"; |
||||||
|
import { InjectModel } from "@nestjs/sequelize"; |
||||||
|
import { Transaction } from "./entities/transaction.entity"; |
||||||
|
import { Payment } from "./entities/payment.entity"; |
||||||
|
|
||||||
|
@Controller("shop") |
||||||
|
export class ShopController { |
||||||
|
constructor( |
||||||
|
private readonly shopService: ShopService, |
||||||
|
@InjectModel(Transaction) private readonly transactionModel: typeof Transaction, |
||||||
|
@InjectModel(Payment) private readonly paymentModel: typeof Payment, |
||||||
|
) {} |
||||||
|
|
||||||
|
////////////////////////////////////////products////////////////////////////////////////
|
||||||
|
//create a new product (admin)
|
||||||
|
@UseGuards(RoleGuard) |
||||||
|
@Post("product") |
||||||
|
async create(@Body() createProductDto: CreateProductDto) { |
||||||
|
const product = await this.shopService.create(createProductDto); |
||||||
|
return { |
||||||
|
message: "Product created successfully!", |
||||||
|
product, |
||||||
|
}; |
||||||
|
} |
||||||
|
//list of all product
|
||||||
|
@Get("product") |
||||||
|
async findAll(@Query() query: { search?: string; priceMin?: number; priceMax?: number }) { |
||||||
|
const { search, priceMin, priceMax } = query; |
||||||
|
return this.shopService.findAll(search, priceMin, priceMax); |
||||||
|
} |
||||||
|
//get a product detail
|
||||||
|
@Get("product/:id") |
||||||
|
async findOne(@Param("id") id: string) { |
||||||
|
return this.shopService.findOne(id); |
||||||
|
} |
||||||
|
//edit a product info (admin)
|
||||||
|
@UseGuards(RoleGuard) |
||||||
|
@Put("product/:id") |
||||||
|
async update(@Param("id") id: string, @Body() updateProductDto: UpdateProductDto) { |
||||||
|
const product = await this.shopService.update(id, updateProductDto); |
||||||
|
return { |
||||||
|
message: "product updated successful", |
||||||
|
product, |
||||||
|
}; |
||||||
|
} |
||||||
|
//delete a product (admin)
|
||||||
|
@UseGuards(RoleGuard) |
||||||
|
@Delete("product/:id") |
||||||
|
async remove(@Param("id") id: string): Promise<{ message: string }> { |
||||||
|
return this.shopService.remove(id); |
||||||
|
} |
||||||
|
////////////////////////////////////////cart////////////////////////////////////////
|
||||||
|
//create and a item to cart (user)
|
||||||
|
@UseGuards(JwtAuthGuard) |
||||||
|
@Post("cart") |
||||||
|
async createAndAddItemToCart(@Body() addToCartDto: AddToCartDto, @Request() req: any) { |
||||||
|
const userId = req.user.id; |
||||||
|
return this.shopService.createAndAddItemToCart({ ...addToCartDto, userId }); |
||||||
|
} |
||||||
|
//get user cart items(user)
|
||||||
|
@UseGuards(JwtAuthGuard) |
||||||
|
@Get("cart") |
||||||
|
async getUserOpenCart(@Request() req: any) { |
||||||
|
const userId = req.user.id; |
||||||
|
return this.shopService.getUserOpenCart(userId); |
||||||
|
} |
||||||
|
//edit quantity an item in cart (user)
|
||||||
|
@UseGuards(JwtAuthGuard) |
||||||
|
@Patch("cart/:productId") |
||||||
|
async updateCart(@Param("productId") productId: number, @Body() updateCartDto: UpdateCartDto, @Request() req: any) { |
||||||
|
const userId = req.user.id; |
||||||
|
const updatedCart = await this.shopService.updateCart(userId, productId, updateCartDto.quantity); |
||||||
|
return { |
||||||
|
message: "Cart updated successfully", |
||||||
|
updatedCart, |
||||||
|
}; |
||||||
|
} |
||||||
|
//delete an item from cart (user)
|
||||||
|
@UseGuards(JwtAuthGuard) |
||||||
|
@Delete("cart/:productId") |
||||||
|
async removeFromCart(@Param("productId") productId: number, @Request() req: any) { |
||||||
|
const userId = req.user.id; |
||||||
|
return await this.shopService.removeFromCart(userId, productId); |
||||||
|
} |
||||||
|
//clear whole cart (user)
|
||||||
|
@UseGuards(JwtAuthGuard) |
||||||
|
@Get("cart/clear-cart") |
||||||
|
async clearCart(@Request() req: any) { |
||||||
|
const userId = req.user.id; |
||||||
|
return await this.shopService.clearCart(userId); |
||||||
|
} |
||||||
|
//get checkout process (user)
|
||||||
|
@UseGuards(JwtAuthGuard) |
||||||
|
@Get("cart/checkout") |
||||||
|
async processOrder(@Request() req: any) { |
||||||
|
const userId = req.user.id; |
||||||
|
try { |
||||||
|
const totalAmount = (await this.shopService.getUserOpenCart(userId)).totalPrice; |
||||||
|
const result = await this.shopService.processOrder(userId, totalAmount); |
||||||
|
return result; |
||||||
|
} catch (error) { |
||||||
|
throw new HttpException(error.message || "Order processing failed.", HttpStatus.INTERNAL_SERVER_ERROR); |
||||||
|
} |
||||||
|
} |
||||||
|
////////////////////////////////////////wallet////////////////////////////////////////
|
||||||
|
//getting wallet balance (user)
|
||||||
|
@UseGuards(JwtAuthGuard) |
||||||
|
@Get("wallet") |
||||||
|
async getBalance(@Request() req) { |
||||||
|
const userId = req.user.id; |
||||||
|
return this.shopService.getBalance(userId); |
||||||
|
} |
||||||
|
//charging wallet (user)
|
||||||
|
@UseGuards(JwtAuthGuard) |
||||||
|
@Post("wallet/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.shopService.requestPayment(amount, "Wallet Charge", callbackUrl); |
||||||
|
return paymentUrl; |
||||||
|
} |
||||||
|
//get transaction (user)
|
||||||
|
@UseGuards(JwtAuthGuard) |
||||||
|
@Get("wallet/transaction") |
||||||
|
async getTransactionById(@Request() req) { |
||||||
|
const userId = req.user.id; |
||||||
|
return this.shopService.getTransactionById(userId); |
||||||
|
} |
||||||
|
//get specific user transaction (admin)
|
||||||
|
@UseGuards(RoleGuard) |
||||||
|
@Get("wallet/transaction/:id") |
||||||
|
async getTransactionByIdForAdmin(@Param("id") id: number) { |
||||||
|
return this.shopService.getTransactionByIdForAdmin(id); |
||||||
|
} |
||||||
|
////////////////////////////////////////payment////////////////////////////////////////
|
||||||
|
//payment request
|
||||||
|
@UseGuards(JwtAuthGuard) |
||||||
|
@Get("payment/request") |
||||||
|
async requestPayment(@Request() req) { |
||||||
|
const userId = req.user.id; |
||||||
|
const invoice = await this.shopService.getInvoicePendingByUser(userId); |
||||||
|
const totalAmount = invoice.totalPaymentAmount; |
||||||
|
if (totalAmount < 1000) { |
||||||
|
return { message: "please enter amount above 1000" }; |
||||||
|
} |
||||||
|
const callbackUrl = `http://localhost:3000/shop/payment/verify?userId=${userId}&amount=${totalAmount}`; |
||||||
|
const paymentUrl = await this.shopService.requestPayment(totalAmount, "Purchase products", callbackUrl); |
||||||
|
|
||||||
|
return { url: paymentUrl }; |
||||||
|
} |
||||||
|
//payment verify
|
||||||
|
@Get("payment/verify") |
||||||
|
async verifyPayment(@Query() query: { Authority: string; Status: string; userId: number; amount: number }): Promise<any> { |
||||||
|
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.shopService.getWalletInfo(userId); |
||||||
|
try { |
||||||
|
const refId = await this.shopService.verifyPayment(Authority, amount); |
||||||
|
await this.shopService.addBalance(userId, amount); |
||||||
|
const wallet = this.shopService.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) { |
||||||
|
if(error instanceof HttpException){ |
||||||
|
throw error |
||||||
|
} |
||||||
|
await this.paymentModel.create({ |
||||||
|
userId, |
||||||
|
walletId: (await wallet).walletId, |
||||||
|
paymentAmount: amount, |
||||||
|
status: "failed", |
||||||
|
}); |
||||||
|
throw new Error(`Error during payment verification: ${error.message}`); |
||||||
|
} |
||||||
|
} |
||||||
|
////////////////////////////////////////invoice////////////////////////////////////////
|
||||||
|
//get invoice (user)
|
||||||
|
@UseGuards(JwtAuthGuard) |
||||||
|
@Get('invoice') |
||||||
|
async getInvoiceByUser(@Request() req) { |
||||||
|
const userId = req.user.id; |
||||||
|
return this.shopService.getInvoiceByUser(userId); |
||||||
|
} |
||||||
|
//get invoices list (admin)
|
||||||
|
@UseGuards(RoleGuard) |
||||||
|
@Get('invoice/list') |
||||||
|
async getInvoices() { |
||||||
|
return this.shopService.getInvoices(); |
||||||
|
} |
||||||
|
//get specific user invoices
|
||||||
|
@UseGuards(RoleGuard) |
||||||
|
@Get('invoice/:id') |
||||||
|
async getUserInvoice(@Param('id') id:number) { |
||||||
|
return this.shopService.getUserInvoices(id); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
import { Module } from "@nestjs/common"; |
||||||
|
import { ShopService } from "./shop.service"; |
||||||
|
import { ShopController } from "./shop.controller"; |
||||||
|
import { SequelizeModule } from "@nestjs/sequelize"; |
||||||
|
import { Product } from "./entities/product.entity"; |
||||||
|
import { RoleGuard } from "src/users/guard/role.guard"; |
||||||
|
import { JwtModule } from "@nestjs/jwt"; |
||||||
|
import { Cart } from "./entities/cart.entity"; |
||||||
|
import { JwtAuthGuard } from "src/users/guard/auth.guard"; |
||||||
|
import { Wallet } from "./entities/wallet.entity"; |
||||||
|
import { Transaction } from "./entities/transaction.entity"; |
||||||
|
import { Payment } from "./entities/payment.entity"; |
||||||
|
import { Invoice } from "./entities/invoice.entity"; |
||||||
|
|
||||||
|
@Module({ |
||||||
|
imports: [ |
||||||
|
SequelizeModule.forFeature([Product, Cart, Invoice, Wallet, Transaction, Payment]), |
||||||
|
JwtModule.register({ |
||||||
|
secret: process.env.JWT_SECRET, |
||||||
|
signOptions: { expiresIn: "1h" }, |
||||||
|
}), |
||||||
|
], |
||||||
|
controllers: [ShopController], |
||||||
|
providers: [ShopService, RoleGuard, JwtAuthGuard], |
||||||
|
}) |
||||||
|
export class ProductsModule {} |
@ -0,0 +1,647 @@ |
|||||||
|
import { Injectable } from "@nestjs/common"; |
||||||
|
import { InjectModel } from "@nestjs/sequelize"; |
||||||
|
import { Product } from "./entities/product.entity"; |
||||||
|
import { CreateProductDto } from "./dto/products/create-product.dto"; |
||||||
|
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 { 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() |
||||||
|
export class ShopService { |
||||||
|
private zarinpal; |
||||||
|
constructor( |
||||||
|
@InjectModel(Product) private readonly productModel: typeof Product, |
||||||
|
@InjectModel(Cart) private readonly cartModel: typeof Cart, |
||||||
|
@InjectModel(Invoice) private readonly invoiceModel: typeof Invoice, |
||||||
|
@InjectModel(Wallet) private walletModel: typeof Wallet, |
||||||
|
@InjectModel(Transaction) private transactionModel: typeof Transaction, |
||||||
|
) { |
||||||
|
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); |
||||||
|
} |
||||||
|
///////////////////////////////////////////products//////////////////////////////////////////////
|
||||||
|
// create a new product
|
||||||
|
async create(createProductDto: CreateProductDto): Promise<Product> { |
||||||
|
try { |
||||||
|
const existingProduct = await this.productModel.findOne({ |
||||||
|
where: { name: createProductDto.name }, |
||||||
|
}); |
||||||
|
|
||||||
|
if (existingProduct) { |
||||||
|
existingProduct.quantity += createProductDto.quantity || 0; |
||||||
|
await existingProduct.save(); |
||||||
|
|
||||||
|
return existingProduct; |
||||||
|
} |
||||||
|
const newProduct = await this.productModel.create(createProductDto); |
||||||
|
return newProduct; |
||||||
|
} catch (error) { |
||||||
|
if (error instanceof HttpException) { |
||||||
|
throw error; |
||||||
|
} |
||||||
|
throw new HttpException("An error occurred while creating or updating the product.", HttpStatus.INTERNAL_SERVER_ERROR); |
||||||
|
} |
||||||
|
} |
||||||
|
// find a product by id
|
||||||
|
async findOne(id: string): Promise<Product> { |
||||||
|
try { |
||||||
|
const product = await this.productModel.findByPk(id); |
||||||
|
|
||||||
|
if (!product) { |
||||||
|
throw new HttpException("Product not found with the given ID.", HttpStatus.NOT_FOUND); |
||||||
|
} |
||||||
|
|
||||||
|
return product; |
||||||
|
} catch (error) { |
||||||
|
if (error instanceof HttpException) { |
||||||
|
throw error; |
||||||
|
} |
||||||
|
|
||||||
|
throw new HttpException("An unexpected error occurred while fetching the product.", HttpStatus.INTERNAL_SERVER_ERROR); |
||||||
|
} |
||||||
|
} |
||||||
|
// list of all product
|
||||||
|
async findAll( |
||||||
|
search?: string, |
||||||
|
priceMin?: number, |
||||||
|
priceMax?: number, |
||||||
|
page: number = 1, |
||||||
|
limit: number = 10, |
||||||
|
): Promise<{ products: Product[]; total: number; totalPages: number; currentPage: number }> { |
||||||
|
try { |
||||||
|
const where: Record<string, any> = {}; |
||||||
|
|
||||||
|
if (search) { |
||||||
|
where.name = { [Op.iLike]: `%${search}%` }; |
||||||
|
} |
||||||
|
|
||||||
|
if (priceMin !== undefined || priceMax !== undefined) { |
||||||
|
where.price = {}; |
||||||
|
if (priceMin !== undefined) { |
||||||
|
where.price[Op.gte] = priceMin; |
||||||
|
} |
||||||
|
if (priceMax !== undefined) { |
||||||
|
where.price[Op.lte] = priceMax; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const offset = (page - 1) * limit; |
||||||
|
|
||||||
|
const { rows: products, count: total } = await this.productModel.findAndCountAll({ |
||||||
|
where, |
||||||
|
limit, |
||||||
|
offset, |
||||||
|
attributes: { exclude: ["description", "quantity", "createdAt", "updatedAt", "tags"] }, |
||||||
|
}); |
||||||
|
|
||||||
|
const totalPages = Math.ceil(total / limit); |
||||||
|
|
||||||
|
return { |
||||||
|
products, |
||||||
|
total, |
||||||
|
totalPages, |
||||||
|
currentPage: page, |
||||||
|
}; |
||||||
|
} catch (error) { |
||||||
|
console.error("Error retrieving products:", error.message); |
||||||
|
|
||||||
|
if (error instanceof HttpException) { |
||||||
|
throw error; |
||||||
|
} |
||||||
|
|
||||||
|
throw new HttpException("An unexpected error occurred while retrieving products.", HttpStatus.INTERNAL_SERVER_ERROR); |
||||||
|
} |
||||||
|
} |
||||||
|
// update a product info
|
||||||
|
async update(id: string, updateProductDto: UpdateProductDto): Promise<Product> { |
||||||
|
const product = await this.productModel.findByPk(id); |
||||||
|
|
||||||
|
if (!product) { |
||||||
|
throw new HttpException("Product not found.", HttpStatus.NOT_FOUND); |
||||||
|
} |
||||||
|
try { |
||||||
|
const { name, description, price, imageUrl, tags, quantity, brand, color, category } = updateProductDto; |
||||||
|
|
||||||
|
let updated = false; |
||||||
|
|
||||||
|
if (name && name !== product.name) { |
||||||
|
product.name = name; |
||||||
|
updated = true; |
||||||
|
} |
||||||
|
if (description && description !== product.description) { |
||||||
|
product.description = description; |
||||||
|
updated = true; |
||||||
|
} |
||||||
|
if (price !== undefined && price !== product.price) { |
||||||
|
product.price = price; |
||||||
|
updated = true; |
||||||
|
} |
||||||
|
if (imageUrl && imageUrl !== product.imageUrl) { |
||||||
|
product.imageUrl = imageUrl; |
||||||
|
updated = true; |
||||||
|
} |
||||||
|
if (tags && tags !== product.tags) { |
||||||
|
product.tags = tags; |
||||||
|
updated = true; |
||||||
|
} |
||||||
|
if (quantity !== undefined && quantity !== product.quantity) { |
||||||
|
product.quantity = quantity; |
||||||
|
updated = true; |
||||||
|
} |
||||||
|
if (brand && brand !== product.brand) { |
||||||
|
product.brand = brand; |
||||||
|
updated = true; |
||||||
|
} |
||||||
|
if (color && color !== product.color) { |
||||||
|
product.color = color; |
||||||
|
updated = true; |
||||||
|
} |
||||||
|
if (category && category !== product.category) { |
||||||
|
product.category = category; |
||||||
|
updated = true; |
||||||
|
} |
||||||
|
|
||||||
|
if (updated) { |
||||||
|
await product.save(); |
||||||
|
} |
||||||
|
|
||||||
|
return product; |
||||||
|
} catch (error) { |
||||||
|
if (error instanceof HttpException) { |
||||||
|
throw error; |
||||||
|
} |
||||||
|
throw new HttpException("An error occurred while updating the product.", HttpStatus.INTERNAL_SERVER_ERROR); |
||||||
|
} |
||||||
|
} |
||||||
|
// delete a product
|
||||||
|
async remove(id: string): Promise<{ message: string }> { |
||||||
|
try { |
||||||
|
const product = await this.productModel.findByPk(id); |
||||||
|
|
||||||
|
if (!product) { |
||||||
|
throw new HttpException(`Product with id ${id} not found.`, HttpStatus.NOT_FOUND); |
||||||
|
} |
||||||
|
|
||||||
|
await product.destroy(); |
||||||
|
|
||||||
|
return { message: "Product deleted successfully." }; |
||||||
|
} catch (error) { |
||||||
|
console.error("Error during product deletion:", error.message); |
||||||
|
if (error instanceof HttpException) { |
||||||
|
throw error; |
||||||
|
} |
||||||
|
throw new HttpException("An unexpected error occurred while deleting the product.", HttpStatus.INTERNAL_SERVER_ERROR); |
||||||
|
} |
||||||
|
} |
||||||
|
////////////////////////////////////////////cart/////////////////////////////////////////////////
|
||||||
|
//create and add item to a 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.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.updateTotalPayment(userId); |
||||||
|
|
||||||
|
return { |
||||||
|
message: cart.id ? "Product quantity updated in cart successfully!" : "Product added to cart successfully!", |
||||||
|
cartItem: cart, |
||||||
|
}; |
||||||
|
} catch (error) { |
||||||
|
if (error instanceof HttpException) { |
||||||
|
throw 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) { |
||||||
|
if (error instanceof HttpException) { |
||||||
|
throw 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<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); |
||||||
|
} |
||||||
|
|
||||||
|
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.updateTotalPayment(userId); |
||||||
|
return cartItem; |
||||||
|
} catch (error) { |
||||||
|
if (error instanceof HttpException) { |
||||||
|
throw 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.updateTotalPayment(userId); |
||||||
|
return { message: "Item deleted from your cart successfully.", cartItem }; |
||||||
|
} catch (error) { |
||||||
|
if (error instanceof HttpException) { |
||||||
|
throw 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.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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
///////////////////////////////////////////wallet//////////////////////////////////////////////
|
||||||
|
//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 }; |
||||||
|
} |
||||||
|
//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 }, |
||||||
|
}); |
||||||
|
} |
||||||
|
//charge balance of wallet by user
|
||||||
|
async addBalance(userId: number, amount: number) { |
||||||
|
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<string> { |
||||||
|
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); |
||||||
|
} |
||||||
|
} |
||||||
|
///////////////////////////////////////////payment//////////////////////////////////////////////
|
||||||
|
//payment request
|
||||||
|
async requestPayment(amount: number, description: string, callbackUrl: string): Promise<string> { |
||||||
|
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}`); |
||||||
|
} |
||||||
|
} |
||||||
|
//payment verify
|
||||||
|
async verifyPayment(authority: string, amount: number): Promise<string> { |
||||||
|
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}`); |
||||||
|
} |
||||||
|
} |
||||||
|
///////////////////////////////////////////invoice//////////////////////////////////////////////
|
||||||
|
// get invoice by user
|
||||||
|
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); |
||||||
|
} |
||||||
|
} |
||||||
|
//get list of invoices by admin
|
||||||
|
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); |
||||||
|
} |
||||||
|
} |
||||||
|
//get user invoices
|
||||||
|
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); |
||||||
|
} |
||||||
|
} |
||||||
|
//create invoices from cart
|
||||||
|
async createInvoiceFromCart(userId: number): Promise<Invoice> { |
||||||
|
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) { |
||||||
|
if (error instanceof HttpException) { |
||||||
|
throw error; |
||||||
|
} |
||||||
|
throw new HttpException("An error occurred while creating the invoice.", HttpStatus.INTERNAL_SERVER_ERROR); |
||||||
|
} |
||||||
|
} |
||||||
|
//update total payment
|
||||||
|
async updateTotalPayment(userId: number) { |
||||||
|
const userCartItems = await this.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(); |
||||||
|
} |
||||||
|
//get pending user invoices
|
||||||
|
async getInvoicePendingByUser(userId: number): Promise<Invoice> { |
||||||
|
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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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<AddBalanceResponse> { |
|
||||||
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<string> { |
|
||||||
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 }, |
|
||||||
}); |
|
||||||
} |
|
||||||
} |
|
@ -1,4 +0,0 @@ |
|||||||
export interface AddBalanceResponse { |
|
||||||
message: string; |
|
||||||
balance: number; |
|
||||||
} |
|
@ -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); |
|
||||||
} |
|
||||||
} |
|
@ -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 {} |
|
@ -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<AddBalanceResponse> { |
|
||||||
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<string> { |
|
||||||
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 }, |
|
||||||
}); |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue