diff --git a/src/DTO/payment.dto.ts b/src/DTO/payment.dto.ts index 3898109..d2fbc07 100644 --- a/src/DTO/payment.dto.ts +++ b/src/DTO/payment.dto.ts @@ -41,8 +41,4 @@ export class CreatePaymentDto { @IsNotEmpty() @ApiProperty({ type: "string", description: "the course id ", example: "66d9ab8b45818616a48bc322" }) courseId: string; - - @IsNotEmpty() - @ApiProperty({ type: Number, description: "the amount of payment", example: 10000 }) - amount: number; } \ No newline at end of file diff --git a/src/application/payment/payment.controller.ts b/src/application/payment/payment.controller.ts index fbf1ac0..b3af482 100644 --- a/src/application/payment/payment.controller.ts +++ b/src/application/payment/payment.controller.ts @@ -41,7 +41,7 @@ export class PaymentController { @Post() @HttpCode(HttpStatus.CREATED) async create(@Body() createPaymentDto: CreatePaymentDto, @CurrentUser() user) { - return this.paymentService.createPayment(user.id, createPaymentDto.amount, createPaymentDto.courseId); + return this.paymentService.createPayment(user.id, createPaymentDto.courseId); } @ApiOperation({ @@ -53,7 +53,7 @@ export class PaymentController { description: 'get authority', }) @ApiQuery({ - name: 'status', + name: 'Status', type: 'string', description: 'get status', }) @@ -63,4 +63,29 @@ export class PaymentController { return this.paymentService.verifyPayment(authority, status); } + @ApiOperation({ + summary: 'دریافت لیست پرداخت ها ==> Login as admin', + }) + @UseGuards(AuthGuard) + @ApiBearerAuth() + @Roles(Role.Admin) + @Get('/admin') + @HttpCode(HttpStatus.OK) + async getPayments() { + return this.paymentService.getPayments(); + } + + @ApiOperation({ + summary: 'دریافت لیست پرداخت ها ==> Login as user', + }) + @UseGuards(AuthGuard) + @ApiBearerAuth() + @Roles(Role.User) + @Get('/user') + @HttpCode(HttpStatus.OK) + async getPaymentsByUser(@CurrentUser() user) { + const userId = user.id; + return this.paymentService.getPaymentsByUser(userId); + } + } diff --git a/src/application/payment/payment.module.ts b/src/application/payment/payment.module.ts index 950a4dc..f6c76cf 100644 --- a/src/application/payment/payment.module.ts +++ b/src/application/payment/payment.module.ts @@ -1,4 +1,4 @@ -import { Logger, Module } from '@nestjs/common'; +import { Module } from '@nestjs/common'; import { UserModule } from '../user/user.module'; import { UserDataAccess } from '../../dataAccess/user.dataAccess'; import { PaymentService } from './payment.service'; @@ -7,6 +7,7 @@ import { PaymentDataAccess } from '../../dataAccess/payment.dataAccess'; import { PaymentGateway } from '../IPG/PaymentGateway'; import { ZarinPalGateway } from '../IPG/gateways/zarinpal'; import { PurchaseDataAccess } from '../../dataAccess/purchase.dataAccess'; +import { CourseDataAccess } from '../../dataAccess/course.dataAccess'; @Module({ imports: [ @@ -20,6 +21,7 @@ import { PurchaseDataAccess } from '../../dataAccess/purchase.dataAccess'; PaymentGateway, PaymentService, UserDataAccess, + CourseDataAccess ], exports: [] }) diff --git a/src/application/payment/payment.service.ts b/src/application/payment/payment.service.ts index 38e8725..97c38a8 100644 --- a/src/application/payment/payment.service.ts +++ b/src/application/payment/payment.service.ts @@ -4,6 +4,8 @@ import mongoose, { Mongoose, startSession } from "mongoose"; import { PaymentGateway } from "../IPG/PaymentGateway"; import { UserDataAccess } from "../../dataAccess/user.dataAccess"; import { PurchaseDataAccess } from "../../dataAccess/purchase.dataAccess"; +import { CourseDataAccess } from "../../dataAccess/course.dataAccess"; +import { PaymentStatus } from "../../common/eNums/paymentStatus.enum"; @Injectable() export class PaymentService { @@ -12,18 +14,27 @@ export class PaymentService { private readonly paymentDataAccess: PaymentDataAccess, private readonly paymentGateway: PaymentGateway, private readonly userDataAccess: UserDataAccess, - private readonly purchaseDataAccess: PurchaseDataAccess + private readonly purchaseDataAccess: PurchaseDataAccess, + private readonly courseDataAccess: CourseDataAccess ) { } - async createPayment(userId: string, amount: number, courseId: string) { + async createPayment(userId: string, courseId: string) { const session = await startSession(); session.startTransaction(); try { const user = await this.userDataAccess.findOne(userId); - console.log({courseId: new mongoose.Types.ObjectId(courseId)}) + const course = await this.courseDataAccess.getOneCourse(courseId); + if (!course) { + throw new HttpException( + { + status: HttpStatus.BAD_REQUEST, + error: 'دوره مورد نظر یافت نشد', + }, + HttpStatus.BAD_REQUEST, + ); + } const courseExists = user.courseIds.find(id => id.equals(courseId)); - if(courseExists) { - console.log("first if") + if (courseExists) { throw new HttpException( { status: HttpStatus.BAD_REQUEST, @@ -33,8 +44,45 @@ export class PaymentService { ); } - const paymentData = await this.paymentGateway.requestPayment({ amount, userId, callbackPath: "cart" }); - console.log(paymentData) + if (course.price === 0) { + // Handle free course registration + const transactionNumber = `FREE-${Date.now()}-${Math.floor(Math.random() * 10000)}`; + const payment = await this.paymentDataAccess.createPayment( + userId, + 0, + courseId, + transactionNumber, + 'FREE', + session + ); + + await session.commitTransaction(); + + await this.purchaseDataAccess.createPurchase(payment.userId.toString(), payment.courseId.toString(), payment._id.toString()); + + payment.paymentStatus = PaymentStatus.Completed; + await this.paymentDataAccess.updatePaymentStatus(payment._id, PaymentStatus.Completed); + + const registerCourse = await this.userDataAccess.registerCourse(payment.userId, payment.courseId); + if (!registerCourse.success) { + if (!payment) { + throw new HttpException( + { + status: HttpStatus.BAD_REQUEST, + error: 'درخواست خرید دوره با مشکل مواجه شد', + }, + HttpStatus.BAD_REQUEST, + ); + } + } + + await session.commitTransaction(); + await session.endSession(); + + return { success: true, payment: payment } + } + + const paymentData = await this.paymentGateway.requestPayment({ amount: course.price, userId, callbackPath: "cart" }); if (!paymentData) { throw new HttpException( { @@ -44,16 +92,16 @@ export class PaymentService { HttpStatus.BAD_REQUEST, ); } - + const transactionNumber = `DLR-${Date.now()}-${Math.floor(Math.random() * 10000)}`; const payment = await this.paymentDataAccess.createPayment( userId, - amount, + course.price, //amount courseId, + transactionNumber, paymentData.authority, session ); - console.log({ payment }) await session.commitTransaction(); await session.endSession(); return { payment: payment, paymentData } @@ -75,10 +123,10 @@ export class PaymentService { } const verifyData = await this.paymentGateway.verifyPayment({ authority, amount: paymentData.amount }); if (verifyData.code === 100 || verifyData.code === 101) { - const purchase = await this.purchaseDataAccess.createPurchase(paymentData.userId.toString(), paymentData.courseId.toString(), paymentData._id.toString()); - console.log(purchase) + await this.purchaseDataAccess.createPurchase(paymentData.userId.toString(), paymentData.courseId.toString(), paymentData._id.toString()); + paymentData.paymentStatus = PaymentStatus.Completed; + await paymentData.save(); const registerCourse = await this.userDataAccess.registerCourse(paymentData.userId, paymentData.courseId); - console.log(registerCourse) if (!registerCourse.success) { if (!paymentData) { throw new HttpException( @@ -94,4 +142,13 @@ export class PaymentService { } } + + + async getPayments() { + return this.paymentDataAccess.getPayments(); + } + + async getPaymentsByUser(userId: string) { + return this.paymentDataAccess.getPaymentsByUserId(userId); + } } diff --git a/src/dataAccess/payment.dataAccess.ts b/src/dataAccess/payment.dataAccess.ts index 384676c..a3a2161 100644 --- a/src/dataAccess/payment.dataAccess.ts +++ b/src/dataAccess/payment.dataAccess.ts @@ -1,14 +1,20 @@ +import mongoose from "mongoose"; import Models from "../models"; import Payment from "../models/payment.model"; export class PaymentDataAccess { - async createPayment(userId: string, amount: number, courseId: string, authority: string, session: any): Promise { - const paymentDoc = await Models.Payment.create([{ userId, amount, courseId, authority }], { session }); + async createPayment(userId: string, amount: number, courseId: string,transactionNumber:string ,authority: string, session: any): Promise { + const paymentDoc = await Models.Payment.create([{ userId, amount, courseId, transactionNumber, authority }], { session }); console.log({ paymentDoc }); const payment = paymentDoc.map(doc => doc.toObject())[0]; return payment; } + async updatePaymentStatus(paymentId: string,status: string) { + const updatedPayment = await Models.Payment.findByIdAndUpdate(paymentId, { paymentStatus: status }, { new: true }); + return updatedPayment; + } + async getPayment(paymentId: string): Promise { const payment = await Models.Payment.findById(paymentId); return payment; @@ -20,13 +26,73 @@ export class PaymentDataAccess { console.log(payment, "ssssss") return payment; } + async getPaymentsByUserId(userId: string): Promise { - const payments = await Models.Payment.find({ userId }); + const payments = await Models.Payment.aggregate([ + { + $match: { userId: new mongoose.Types.ObjectId(userId) } + }, + { + $lookup: { + from: "courses", + localField: "courseId", + foreignField: "_id", + as: "course", + } + }, + { + $unwind: "$course", + }, + { + $sort: { createdAt: -1 } + }, + { + $project: { + _id:1, + authority: 1, + amount: 1, + transactionNumber: 1, + paymentStatus: 1, + course: { + name: 1, + slug: 1, + image: 1, + price: 1 + } + } + } + ]); return payments; } async getPayments(): Promise { - const payments = await Models.Payment.find(); + const payments = await Models.Payment.aggregate([ + { + $lookup: { + from: "users", + localField: "userId", + foreignField: "_id", + as: "user", + }, + }, + { + $unwind: "$user", + }, + { + $lookup: { + from: "courses", + localField: "courseId", + foreignField: "_id", + as: "course", + } + }, + { + $unwind: "$course", + }, + { + $sort: { createdAt: -1 } + } + ]); return payments; } }