From 1ce941b31c176df7771953c15e35542dc7bab042 Mon Sep 17 00:00:00 2001 From: aliMohtarami Date: Wed, 22 Jan 2025 11:28:08 +0330 Subject: [PATCH] invoices module refactoring --- src/products/entities/invoice.entity.ts | 37 ++++++ src/products/products.controller.ts | 33 +++-- src/products/products.service.ts | 166 ++++++++++++++++++++++++ 3 files changed, 228 insertions(+), 8 deletions(-) create mode 100644 src/products/entities/invoice.entity.ts diff --git a/src/products/entities/invoice.entity.ts b/src/products/entities/invoice.entity.ts new file mode 100644 index 0000000..8f90710 --- /dev/null +++ b/src/products/entities/invoice.entity.ts @@ -0,0 +1,37 @@ +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/products/products.controller.ts b/src/products/products.controller.ts index 1933f16..fa7856f 100644 --- a/src/products/products.controller.ts +++ b/src/products/products.controller.ts @@ -7,8 +7,6 @@ import { RoleGuard } from "src/guard/role.guard"; import { AddToCartDto } from "./dto/cart/add-to-cart.dto"; import { JwtAuthGuard } from "src/guard/auth.guard"; import { UpdateCartDto } from "./dto/cart/update-cart.dto"; -import { PaymentService } from "src/payment/payment.service"; -import { InvoiceService } from "src/invoice/invoice.service"; import { InjectModel } from "@nestjs/sequelize"; import { Transaction } from "./entities/transaction.entity"; import { Payment } from "./entities/payment.entity"; @@ -17,8 +15,6 @@ import { Payment } from "./entities/payment.entity"; export class ProductsController { constructor( private readonly productsService: ProductsService, - private paymentService: PaymentService, - private readonly invoiceService: InvoiceService, @InjectModel(Transaction) private readonly transactionModel: typeof Transaction, @InjectModel(Payment) private readonly paymentModel: typeof Payment, ) {} @@ -128,7 +124,7 @@ export class ProductsController { 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); + const paymentUrl = this.productsService.requestPayment(amount, "Wallet Charge", callbackUrl); return paymentUrl; } //get transaction (user) @@ -150,13 +146,13 @@ export class ProductsController { @Get("payment/request") async requestPayment(@Request() req) { const userId = req.user.id; - const invoice = await this.invoiceService.getInvoicePendingByUser(userId); + const invoice = await this.productsService.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); + const paymentUrl = await this.productsService.requestPayment(totalAmount, "Purchase products", callbackUrl); return { url: paymentUrl }; } @@ -174,7 +170,7 @@ export class ProductsController { } const wallet = this.productsService.getWalletInfo(userId); try { - const refId = await this.paymentService.verifyPayment(Authority, amount); + const refId = await this.productsService.verifyPayment(Authority, amount); await this.productsService.addBalance(userId, amount); const wallet = this.productsService.getWalletInfo(userId); await this.paymentModel.create({ @@ -199,4 +195,25 @@ export class ProductsController { 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.productsService.getInvoiceByUser(userId); + } + //get invoices list (admin) + @UseGuards(RoleGuard) + @Get('invoice/list') + async getInvoices() { + return this.productsService.getInvoices(); + } + //get specific user invoices + @UseGuards(RoleGuard) + @Get(':id') + async getUserInvoice(@Param('id') id:number) { + return this.productsService.getUserInvoices(id); + } + } diff --git a/src/products/products.service.ts b/src/products/products.service.ts index 384b937..f9ca74c 100644 --- a/src/products/products.service.ts +++ b/src/products/products.service.ts @@ -463,7 +463,35 @@ export class ProductsService { 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); + } + } ///////////////////////////////////////////payment////////////////////////////////////////////// + //payment request async requestPayment(amount: number, description: string, callbackUrl: string): Promise { try { const result = await this.zarinpal.PaymentRequest({ @@ -482,4 +510,142 @@ export class ProductsService { throw new InternalServerErrorException(`Error in payment request: ${error.message}`); } } + //payment verify + 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}`); + } + } + ///////////////////////////////////////////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 { + 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 { + 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); + } + } }