feat: get more data for admin and user about payment

This commit is contained in:
Matin
2025-01-04 23:19:33 +03:30
parent 4aac892b27
commit 0595fa2e36
5 changed files with 170 additions and 24 deletions
-4
View File
@@ -41,8 +41,4 @@ export class CreatePaymentDto {
@IsNotEmpty() @IsNotEmpty()
@ApiProperty({ type: "string", description: "the course id ", example: "66d9ab8b45818616a48bc322" }) @ApiProperty({ type: "string", description: "the course id ", example: "66d9ab8b45818616a48bc322" })
courseId: string; courseId: string;
@IsNotEmpty()
@ApiProperty({ type: Number, description: "the amount of payment", example: 10000 })
amount: number;
} }
+27 -2
View File
@@ -41,7 +41,7 @@ export class PaymentController {
@Post() @Post()
@HttpCode(HttpStatus.CREATED) @HttpCode(HttpStatus.CREATED)
async create(@Body() createPaymentDto: CreatePaymentDto, @CurrentUser() user) { 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({ @ApiOperation({
@@ -53,7 +53,7 @@ export class PaymentController {
description: 'get authority', description: 'get authority',
}) })
@ApiQuery({ @ApiQuery({
name: 'status', name: 'Status',
type: 'string', type: 'string',
description: 'get status', description: 'get status',
}) })
@@ -63,4 +63,29 @@ export class PaymentController {
return this.paymentService.verifyPayment(authority, status); 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);
}
} }
+3 -1
View File
@@ -1,4 +1,4 @@
import { Logger, Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { UserModule } from '../user/user.module'; import { UserModule } from '../user/user.module';
import { UserDataAccess } from '../../dataAccess/user.dataAccess'; import { UserDataAccess } from '../../dataAccess/user.dataAccess';
import { PaymentService } from './payment.service'; import { PaymentService } from './payment.service';
@@ -7,6 +7,7 @@ import { PaymentDataAccess } from '../../dataAccess/payment.dataAccess';
import { PaymentGateway } from '../IPG/PaymentGateway'; import { PaymentGateway } from '../IPG/PaymentGateway';
import { ZarinPalGateway } from '../IPG/gateways/zarinpal'; import { ZarinPalGateway } from '../IPG/gateways/zarinpal';
import { PurchaseDataAccess } from '../../dataAccess/purchase.dataAccess'; import { PurchaseDataAccess } from '../../dataAccess/purchase.dataAccess';
import { CourseDataAccess } from '../../dataAccess/course.dataAccess';
@Module({ @Module({
imports: [ imports: [
@@ -20,6 +21,7 @@ import { PurchaseDataAccess } from '../../dataAccess/purchase.dataAccess';
PaymentGateway, PaymentGateway,
PaymentService, PaymentService,
UserDataAccess, UserDataAccess,
CourseDataAccess
], ],
exports: [] exports: []
}) })
+70 -13
View File
@@ -4,6 +4,8 @@ import mongoose, { Mongoose, startSession } from "mongoose";
import { PaymentGateway } from "../IPG/PaymentGateway"; import { PaymentGateway } from "../IPG/PaymentGateway";
import { UserDataAccess } from "../../dataAccess/user.dataAccess"; import { UserDataAccess } from "../../dataAccess/user.dataAccess";
import { PurchaseDataAccess } from "../../dataAccess/purchase.dataAccess"; import { PurchaseDataAccess } from "../../dataAccess/purchase.dataAccess";
import { CourseDataAccess } from "../../dataAccess/course.dataAccess";
import { PaymentStatus } from "../../common/eNums/paymentStatus.enum";
@Injectable() @Injectable()
export class PaymentService { export class PaymentService {
@@ -12,18 +14,27 @@ export class PaymentService {
private readonly paymentDataAccess: PaymentDataAccess, private readonly paymentDataAccess: PaymentDataAccess,
private readonly paymentGateway: PaymentGateway, private readonly paymentGateway: PaymentGateway,
private readonly userDataAccess: UserDataAccess, 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(); const session = await startSession();
session.startTransaction(); session.startTransaction();
try { try {
const user = await this.userDataAccess.findOne(userId); 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)); const courseExists = user.courseIds.find(id => id.equals(courseId));
if(courseExists) { if (courseExists) {
console.log("first if")
throw new HttpException( throw new HttpException(
{ {
status: HttpStatus.BAD_REQUEST, status: HttpStatus.BAD_REQUEST,
@@ -33,8 +44,45 @@ export class PaymentService {
); );
} }
const paymentData = await this.paymentGateway.requestPayment({ amount, userId, callbackPath: "cart" }); if (course.price === 0) {
console.log(paymentData) // 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) { if (!paymentData) {
throw new HttpException( throw new HttpException(
{ {
@@ -44,16 +92,16 @@ export class PaymentService {
HttpStatus.BAD_REQUEST, HttpStatus.BAD_REQUEST,
); );
} }
const transactionNumber = `DLR-${Date.now()}-${Math.floor(Math.random() * 10000)}`;
const payment = await this.paymentDataAccess.createPayment( const payment = await this.paymentDataAccess.createPayment(
userId, userId,
amount, course.price, //amount
courseId, courseId,
transactionNumber,
paymentData.authority, paymentData.authority,
session session
); );
console.log({ payment })
await session.commitTransaction(); await session.commitTransaction();
await session.endSession(); await session.endSession();
return { payment: payment, paymentData } return { payment: payment, paymentData }
@@ -75,10 +123,10 @@ export class PaymentService {
} }
const verifyData = await this.paymentGateway.verifyPayment({ authority, amount: paymentData.amount }); const verifyData = await this.paymentGateway.verifyPayment({ authority, amount: paymentData.amount });
if (verifyData.code === 100 || verifyData.code === 101) { if (verifyData.code === 100 || verifyData.code === 101) {
const purchase = await this.purchaseDataAccess.createPurchase(paymentData.userId.toString(), paymentData.courseId.toString(), paymentData._id.toString()); await this.purchaseDataAccess.createPurchase(paymentData.userId.toString(), paymentData.courseId.toString(), paymentData._id.toString());
console.log(purchase) paymentData.paymentStatus = PaymentStatus.Completed;
await paymentData.save();
const registerCourse = await this.userDataAccess.registerCourse(paymentData.userId, paymentData.courseId); const registerCourse = await this.userDataAccess.registerCourse(paymentData.userId, paymentData.courseId);
console.log(registerCourse)
if (!registerCourse.success) { if (!registerCourse.success) {
if (!paymentData) { if (!paymentData) {
throw new HttpException( 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);
}
} }
+70 -4
View File
@@ -1,14 +1,20 @@
import mongoose from "mongoose";
import Models from "../models"; import Models from "../models";
import Payment from "../models/payment.model"; import Payment from "../models/payment.model";
export class PaymentDataAccess { export class PaymentDataAccess {
async createPayment(userId: string, amount: number, courseId: string, authority: string, session: any): Promise<Payment> { async createPayment(userId: string, amount: number, courseId: string,transactionNumber:string ,authority: string, session: any): Promise<Payment> {
const paymentDoc = await Models.Payment.create([{ userId, amount, courseId, authority }], { session }); const paymentDoc = await Models.Payment.create([{ userId, amount, courseId, transactionNumber, authority }], { session });
console.log({ paymentDoc }); console.log({ paymentDoc });
const payment = paymentDoc.map(doc => doc.toObject())[0]; const payment = paymentDoc.map(doc => doc.toObject())[0];
return payment; 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<Payment | null> { async getPayment(paymentId: string): Promise<Payment | null> {
const payment = await Models.Payment.findById(paymentId); const payment = await Models.Payment.findById(paymentId);
return payment; return payment;
@@ -20,13 +26,73 @@ export class PaymentDataAccess {
console.log(payment, "ssssss") console.log(payment, "ssssss")
return payment; return payment;
} }
async getPaymentsByUserId(userId: string): Promise<Payment[]> { async getPaymentsByUserId(userId: string): Promise<Payment[]> {
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; return payments;
} }
async getPayments(): Promise<Payment[]> { async getPayments(): Promise<Payment[]> {
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; return payments;
} }
} }