diff --git a/.env b/.env index cbb117f..a550128 100644 --- a/.env +++ b/.env @@ -3,7 +3,9 @@ SECRET=fDS9j269DSfd823FhdfG65 ACCESS_TOKEN_SECERT=PNd98dsDS089ds3SDh7C5hg265SFC42bgHxTg34x ACCESS_TOKEN_SECERT_ADMIN=G89dsJsd90DFdsf96F8se3sgL7JB29265SFC42bgHxTg34x CONNECTION_STRING= -MERCHANT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +MERCHANT_ID=250ae134-635f-4728-8415-ee9a984616e8 +IPG_TYPE=sandbox +SITE_URL=http://localhost:3000/payment EMAIL_HOST= EMAIL_PORT= EMAIL_USER= @@ -23,5 +25,5 @@ STORAGE_ENDPOINT=https://storage.iran.liara.space STORAGE_BUCKET=dlearn STORAGE_REGION=default -MONGO_CONNECTION_DANAK=mongodb://root:ahh62bv6o7jndf32@srv-captain--danak-mongo-db:27017/delearn?authSource=admin +MONGO_CONNECTION_DANAK=mongodb://root:3fd87daf47fd07183a424a998d929307@89.32.249.24:32768/delearn?authSource=admin&replicaSet=rs0&directConnection=true NODE_ENV=production \ No newline at end of file diff --git a/.env.production b/.env.production index 0099963..e81dfd4 100644 --- a/.env.production +++ b/.env.production @@ -3,7 +3,7 @@ SECRET=fDS9j269DSfd823FhdfG65 ACCESS_TOKEN_SECERT=PNd98dsDS089ds3SDh7C5hg265SFC42bgHxTg34x ACCESS_TOKEN_SECERT_ADMIN=G89dsJsd90DFdsf96F8se3sgL7JB29265SFC42bgHxTg34x CONNECTION_STRING= -MERCHANT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +MERCHANT_ID=250ae134-635f-4728-8415-ee9a984616e8 EMAIL_HOST= EMAIL_PORT= EMAIL_USER= @@ -24,5 +24,5 @@ STORAGE_SECRET_KEY=95e78068-ff60-41c0-82c3-71a0de0a3e2f STORAGE_ENDPOINT=https://storage.iran.liara.space STORAGE_BUCKET=dlearn STORAGE_REGION=default -MONGO_CONNECTION_DANAK=mongodb://root:ahh62bv6o7jndf32@srv-captain--danak-mongo-db:27017/delearn?authSource=admin +MONGO_CONNECTION_DANAK=mongodb://root:3fd87daf47fd07183a424a998d929307@89.32.249.24:32768/delearn?authSource=admin&replicaSet=rs0&directConnection=true NODE_ENV=production \ No newline at end of file diff --git a/src/DTO/payment.dto.ts b/src/DTO/payment.dto.ts new file mode 100644 index 0000000..3898109 --- /dev/null +++ b/src/DTO/payment.dto.ts @@ -0,0 +1,48 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { IsMongoId, IsNotEmpty } from "class-validator"; + +export function paymentObj(payment) { + return { + id: payment.id, + course: payment.course, + authority: payment.authority, + amount: payment.amount, + user: payment.user, + createdAt: payment.createdAt, + updatedAt: payment.updatedAt, + }; +} + +export class PaymentDto { + @IsNotEmpty() + @ApiProperty({ type: String }) + @IsMongoId({ message: 'آیدی نامعتبر است.' }) + id: string; + + @IsNotEmpty() + @ApiProperty({ type: "string", description: "the course id ", example: "66d9ab8b45818616a48bc322" }) + courseId: string; + + @IsNotEmpty() + @ApiProperty({ type: String }) + authority: string; + + @ApiProperty({ type: Number }) + amount: number; + + @ApiProperty({ type: Date }) + createdAt: Date; + + @ApiProperty({ type: Date }) + updatedAt: Date; +} + +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/app.module.ts b/src/app.module.ts index 997f7c8..bd91dee 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -12,6 +12,7 @@ import { UploaderModule } from './application/common/uploader/uploader.module'; import { CourseHeadlineModule } from './application/courseHeadline/courseHeadline.module'; import { ConfigModule } from '@nestjs/config'; import { CourseCategoryModule } from './application/courseCategory/courseCategory.module'; +import { PaymentModule } from './application/payment/payment.module'; @Module({ imports: [ @@ -34,6 +35,7 @@ import { CourseCategoryModule } from './application/courseCategory/courseCategor UploaderModule, CourseHeadlineModule, CourseCategoryModule, + PaymentModule, // ServeStaticModule.forRoot({ // rootPat h: join(__dirname, '..', 'public'), // }), @@ -49,4 +51,4 @@ import { CourseCategoryModule } from './application/courseCategory/courseCategor providers: [UploaderService], exports: [UploaderService], }) -export class AppModule {} +export class AppModule { } diff --git a/src/application/IPG/PaymentGateway.ts b/src/application/IPG/PaymentGateway.ts new file mode 100644 index 0000000..43e0fb9 --- /dev/null +++ b/src/application/IPG/PaymentGateway.ts @@ -0,0 +1,18 @@ +import { Injectable } from "@nestjs/common"; +import { ZarinPalGateway } from "./gateways/zarinpal"; + +@Injectable() +export class PaymentGateway { + + constructor( + private zarinpalGateway: ZarinPalGateway, + ) { } + + async requestPayment(data: any) { + return await this.zarinpalGateway.processPayment(data.amount, data.userId, data.callbackPath); + } + + async verifyPayment(data: any) { + return await this.zarinpalGateway.verifyPayment(data.authority, data.amount); + } +} \ No newline at end of file diff --git a/src/application/IPG/gateways/zarinpal.ts b/src/application/IPG/gateways/zarinpal.ts new file mode 100644 index 0000000..995e9b3 --- /dev/null +++ b/src/application/IPG/gateways/zarinpal.ts @@ -0,0 +1,59 @@ +import { Injectable, InternalServerErrorException, Logger } from "@nestjs/common"; +import axios from "axios"; + +@Injectable() +export class ZarinPalGateway { + private logger = new Logger(ZarinPalGateway.name) + private gatewayApiURL: string = `https://${process.env.IPG_TYPE}.zarinpal.com/pg`; + private callbackURL: string = `${process.env.SITE_URL}/verify/Zarinpal`; + private merchantID: string = process.env.MERCHANT_ID; + private requestHeaders: any = { + 'Content-Type': 'application/json', + Accept: 'application/json', + }; + + + async processPayment(amount: number, userId: string, callbackPath: string) { + try { + const purchaseData = { + merchant_id: this.merchantID, + amount, + callback_url: `${this.callbackURL}/${callbackPath}`, + description: "خرید دوره", + currency: "IRR" as const, + metadata: { userId }, + } + + const response = await axios.post(`${this.gatewayApiURL}/v4/payment/request.json`, purchaseData, { headers: this.requestHeaders }); + const { data } = response.data; + console.log(data) + return { + redirectUrl: `${this.gatewayApiURL}/StartPay/${data.authority}`, + message: data.message, + authority: data.authority, + } + } catch (error) { + console.log(error) + this.logger.error("error in payment process", error); + throw new InternalServerErrorException("error in payment process"); + } + } + + async verifyPayment(authority: string, amount: number) { + try { + const verifyData = { + merchant_id: this.merchantID, + amount, + authority, + } + + const response = await axios.post(`${this.gatewayApiURL}/v4/payment/verify.json`, verifyData, { headers: this.requestHeaders }); + const { data } = response.data; + + return data; + } catch (error) { + this.logger.error("error in payment verification", error); + throw new InternalServerErrorException("error in payment verification"); + } + } +} \ No newline at end of file diff --git a/src/application/IPG/interface/IPG.ts b/src/application/IPG/interface/IPG.ts new file mode 100644 index 0000000..d9cb508 --- /dev/null +++ b/src/application/IPG/interface/IPG.ts @@ -0,0 +1,12 @@ +export interface NewPaymentData { + amount: number; + description: string; + email: string; + mobile: string; + callbackPath: string; +} + +export interface VerifyPaymentData { + authority: string; + amount: number; +} diff --git a/src/application/course/course.service.ts b/src/application/course/course.service.ts index e6e01d4..bef87b3 100644 --- a/src/application/course/course.service.ts +++ b/src/application/course/course.service.ts @@ -23,7 +23,7 @@ export class CourseService { private courseCategoryDataAccess: CourseCategoryDataAccess, private commentDataAccess: CommentDataAccess, private courseHeadlineDataAccess: CourseHeadlineDataAccess, - ) {} + ) { } async createCourse( createCourseDto: CreateCourseDto, diff --git a/src/application/payment/payment.controller.ts b/src/application/payment/payment.controller.ts new file mode 100644 index 0000000..fbf1ac0 --- /dev/null +++ b/src/application/payment/payment.controller.ts @@ -0,0 +1,66 @@ +import { + Body, + Controller, + Get, + HttpCode, + HttpStatus, + Post, + Query, + UseGuards, +} from '@nestjs/common'; +import { + ApiBearerAuth, + ApiBody, + ApiOperation, + ApiQuery, +} from '@nestjs/swagger'; +import { CurrentUser } from '../../decorators/currentUser.decorator'; +import { AuthGuard } from '../../auth/auth.guard'; +import { ApiTags } from '@nestjs/swagger'; +import { Roles } from '../../decorators/role.decorators'; +import { Role } from '../../common/eNums/role.enum'; +import { PaymentService } from './payment.service'; +import { CreatePaymentDto } from 'src/DTO/payment.dto'; +@ApiTags('payment') +@Controller('payment') +export class PaymentController { + constructor( + private readonly paymentService: PaymentService, + ) { } + + @ApiOperation({ + summary: 'ایجاد درخواست پرداخت', + }) + @ApiBody({ + type: CreatePaymentDto, + description: 'get body', + }) + @UseGuards(AuthGuard) + @ApiBearerAuth() + @Roles(Role.User) + @Post() + @HttpCode(HttpStatus.CREATED) + async create(@Body() createPaymentDto: CreatePaymentDto, @CurrentUser() user) { + return this.paymentService.createPayment(user.id, createPaymentDto.amount, createPaymentDto.courseId); + } + + @ApiOperation({ + summary: 'تایید پرداخت', + }) + @ApiQuery({ + name: 'Authority', + type: 'string', + description: 'get authority', + }) + @ApiQuery({ + name: 'status', + type: 'string', + description: 'get status', + }) + @Get('/verify/Zarinpal/cart') + async verifyPayment(@Query('Authority') authority: string, @Query('Status') status: string) { + console.log({ authorityInController: authority, status }) + return this.paymentService.verifyPayment(authority, status); + } + +} diff --git a/src/application/payment/payment.module.ts b/src/application/payment/payment.module.ts new file mode 100644 index 0000000..b31733c --- /dev/null +++ b/src/application/payment/payment.module.ts @@ -0,0 +1,24 @@ +import { Logger, Module } from '@nestjs/common'; +import { UserModule } from '../user/user.module'; +import { UserDataAccess } from '../../dataAccess/user.dataAccess'; +import { PaymentService } from './payment.service'; +import { PaymentController } from './payment.controller'; +import { PaymentDataAccess } from 'src/dataAccess/payment.dataAccess'; +import { PaymentGateway } from '../IPG/PaymentGateway'; +import { ZarinPalGateway } from '../IPG/gateways/zarinpal'; + +@Module({ + imports: [ + UserModule, + ], + controllers: [PaymentController], + providers: [ + ZarinPalGateway, + PaymentDataAccess, + PaymentGateway, + PaymentService, + UserDataAccess, + ], + exports: [] +}) +export class PaymentModule { } diff --git a/src/application/payment/payment.service.ts b/src/application/payment/payment.service.ts new file mode 100644 index 0000000..f865ee1 --- /dev/null +++ b/src/application/payment/payment.service.ts @@ -0,0 +1,63 @@ +import { Injectable, Logger } from "@nestjs/common"; +import { PaymentDataAccess } from "../../dataAccess/payment.dataAccess"; +import { startSession } from "mongoose"; +import { PaymentGateway } from "../IPG/PaymentGateway"; +import { UserDataAccess } from "../../dataAccess/user.dataAccess"; + +@Injectable() +export class PaymentService { + private readonly logger = new Logger(PaymentService.name); + constructor( + private readonly paymentDataAccess: PaymentDataAccess, + private readonly paymentGateway: PaymentGateway, + private readonly userDataAccess: UserDataAccess, + ) { } + + async createPayment(userId: string, amount: number, courseId: string) { + const session = await startSession(); + session.startTransaction(); + try { + const paymentData = await this.paymentGateway.requestPayment({ amount, userId, callbackPath: "cart" }); + console.log(paymentData) + if (!paymentData) { + throw new Error("Error in payment process"); + } + + const payment = await this.paymentDataAccess.createPayment( + userId, + amount, + courseId, + paymentData.authority, + session + ); + console.log({ payment }) + await session.commitTransaction(); + await session.endSession(); + return { payment: payment, paymentData } + } catch (error) { + await session.abortTransaction(); + await session.endSession(); + this.logger.warn(error); + } + } + + + async verifyPayment(authority: string, status: string) { + console.log({ authorityInService: authority }) + const paymentData = await this.paymentDataAccess.getPaymentByAuthority(authority); + console.log({ paymentData }) + if (!paymentData) { + throw new Error("payment not found"); + } + const verifyData = await this.paymentGateway.verifyPayment({ authority, amount: paymentData.amount }); + if (verifyData.code === 100 || verifyData.code === 101) { + const registerCourse = await this.userDataAccess.registerCourse(paymentData.userId, paymentData.courseId); + console.log(registerCourse) + if (!registerCourse.success) { + throw new Error("error in register course"); + } + return { success: true, payment: paymentData, verifyData } + } + + } +} diff --git a/src/application/user/user.controller.ts b/src/application/user/user.controller.ts index 7d61ebc..9c4e07e 100644 --- a/src/application/user/user.controller.ts +++ b/src/application/user/user.controller.ts @@ -30,7 +30,7 @@ import { CurrentUser } from '../../decorators/currentUser.decorator'; @ApiTags('user') @Controller('user') export class UserController { - constructor(private readonly userService: UserService) {} + constructor(private readonly userService: UserService) { } // profile user ************************************ @ApiOperation({ @@ -110,45 +110,45 @@ export class UserController { } } - @ApiOperation({ - summary: 'افزایش موجودی کیف پول', - }) - @ApiBody({ - type: AddBalanceDto, - description: 'get body', - }) - @UseGuards(AuthGuard) - @Roles(Role.User) - @ApiBearerAuth() - @Post('add') - addBalance(@CurrentUser() user, @Body() addBalanceDto: AddBalanceDto) { - return this.userService.addBalance( - user.id, - addBalanceDto.amount, - addBalanceDto.paymentNumber, - addBalanceDto.reason, - ); - } + // @ApiOperation({ + // summary: 'افزایش موجودی کیف پول', + // }) + // @ApiBody({ + // type: AddBalanceDto, + // description: 'get body', + // }) + // @UseGuards(AuthGuard) + // @Roles(Role.User) + // @ApiBearerAuth() + // @Post('add') + // addBalance(@CurrentUser() user, @Body() addBalanceDto: AddBalanceDto) { + // return this.userService.addBalance( + // user.id, + // addBalanceDto.amount, + // addBalanceDto.paymentNumber, + // addBalanceDto.reason, + // ); + // } - @ApiOperation({ - summary: 'موجودی کیف پول', - }) - @UseGuards(AuthGuard) - @Roles(Role.User) - @ApiBearerAuth() - @Get('balance') - getBalance(@CurrentUser() user) { - return this.userService.getBalance(user.id); - } + // @ApiOperation({ + // summary: 'موجودی کیف پول', + // }) + // @UseGuards(AuthGuard) + // @Roles(Role.User) + // @ApiBearerAuth() + // @Get('balance') + // getBalance(@CurrentUser() user) { + // return this.userService.getBalance(user.id); + // } - @ApiOperation({ - summary: 'لیست تراکنش ها', - }) - @UseGuards(AuthGuard) - @Roles(Role.User) - @ApiBearerAuth() - @Get('payments') - getPayments(@CurrentUser() user) { - return this.userService.getPayments(user.id); - } + // @ApiOperation({ + // summary: 'لیست تراکنش ها', + // }) + // @UseGuards(AuthGuard) + // @Roles(Role.User) + // @ApiBearerAuth() + // @Get('payments') + // getPayments(@CurrentUser() user) { + // return this.userService.getPayments(user.id); + // } } diff --git a/src/application/user/user.service.ts b/src/application/user/user.service.ts index 7a6fdc2..0d18c5b 100644 --- a/src/application/user/user.service.ts +++ b/src/application/user/user.service.ts @@ -21,7 +21,7 @@ export class UserService { private readonly userDiscountDataAccess: UserDiscountDataAccess, private readonly discountHistoryDataAccess: DiscountHistoryDataAccess, private readonly paymentDataAccess: PaymentDataAccess, - ) {} + ) { } async profile(id) { const result = await this.userDataAccess.findOne(id); @@ -150,40 +150,40 @@ export class UserService { ); } - async addBalance( - userId: string, - amount: number, - paymentNumber: string, - reason: string, - ) { - const user = await this.userDataAccess.findOne(userId); - const payment = await this.paymentDataAccess.createPayment( - userId, - amount, - paymentNumber, - reason, - ); - user.walletBalance += amount; - return await user.save(); - } + // async addBalance( + // userId: string, + // amount: number, + // paymentNumber: string, + // reason: string, + // ) { + // const user = await this.userDataAccess.findOne(userId); + // const payment = await this.paymentDataAccess.createPayment( + // userId, + // amount, + // paymentNumber, + // reason, + // ); + // user.walletBalance += amount; + // return await user.save(); + // } - async getBalance(userId: string): Promise { - const user = await this.userDataAccess.findOne(userId); - return user.walletBalance; - } + // async getBalance(userId: string): Promise { + // const user = await this.userDataAccess.findOne(userId); + // return user.walletBalance; + // } - async getPayments(userId: string) { - const user = await this.userDataAccess.findOne(userId); - if (!user) { - throw new HttpException( - { - status: HttpStatus.NOT_FOUND, - error: 'کاربر وجود ندارد', - }, - HttpStatus.NOT_FOUND, - ); - } - const payments = await this.paymentDataAccess.getPaymentsByUserId(userId); - return payments; - } + // async getPayments(userId: string) { + // const user = await this.userDataAccess.findOne(userId); + // if (!user) { + // throw new HttpException( + // { + // status: HttpStatus.NOT_FOUND, + // error: 'کاربر وجود ندارد', + // }, + // HttpStatus.NOT_FOUND, + // ); + // } + // const payments = await this.paymentDataAccess.getPaymentsByUserId(userId); + // return payments; + // } } diff --git a/src/common/eNums/paymentStatus.enum.ts b/src/common/eNums/paymentStatus.enum.ts new file mode 100644 index 0000000..402bc68 --- /dev/null +++ b/src/common/eNums/paymentStatus.enum.ts @@ -0,0 +1,5 @@ +export enum PaymentStatus { + Pending = "Pending", + Cancelled = "Cancelled", + Completed = "Completed", +} \ No newline at end of file diff --git a/src/dataAccess/course.dataAccess.ts b/src/dataAccess/course.dataAccess.ts index ebda680..5c8e513 100644 --- a/src/dataAccess/course.dataAccess.ts +++ b/src/dataAccess/course.dataAccess.ts @@ -90,7 +90,7 @@ export class CourseDataAccess { .populate('categories', 'id name') .skip( (filterCategoryPaginationCourseDto.page - 1) * - filterCategoryPaginationCourseDto.pageSize, + filterCategoryPaginationCourseDto.pageSize, ) .limit(filterCategoryPaginationCourseDto.pageSize); @@ -217,6 +217,12 @@ export class CourseDataAccess { return result; } + + async getUserCourses(user: string): Promise { + const result = await Models.Course.find({ studentIds: user }); + return result + } + async deleteCourse(courseId: string): Promise { const result = await Models.Course.findByIdAndDelete(courseId); return result; diff --git a/src/dataAccess/payment.dataAccess.ts b/src/dataAccess/payment.dataAccess.ts index 4752a25..384676c 100644 --- a/src/dataAccess/payment.dataAccess.ts +++ b/src/dataAccess/payment.dataAccess.ts @@ -2,8 +2,10 @@ import Models from "../models"; import Payment from "../models/payment.model"; export class PaymentDataAccess { - async createPayment(userId: string, amount: number, paymentNumber: string,reason:string):Promise { - const payment = Models.Payment.create({ userId, amount, paymentNumber,reason }); + async createPayment(userId: string, amount: number, courseId: string, authority: string, session: any): Promise { + const paymentDoc = await Models.Payment.create([{ userId, amount, courseId, authority }], { session }); + console.log({ paymentDoc }); + const payment = paymentDoc.map(doc => doc.toObject())[0]; return payment; } @@ -12,6 +14,12 @@ export class PaymentDataAccess { return payment; } + async getPaymentByAuthority(authority: string): Promise { + console.log({ authority }) + const payment = await Models.Payment.findOne({ authority }); + console.log(payment, "ssssss") + return payment; + } async getPaymentsByUserId(userId: string): Promise { const payments = await Models.Payment.find({ userId }); return payments; diff --git a/src/dataAccess/purchase.dataAccess.ts b/src/dataAccess/purchase.dataAccess.ts new file mode 100644 index 0000000..017e32a --- /dev/null +++ b/src/dataAccess/purchase.dataAccess.ts @@ -0,0 +1,25 @@ +import { Purchase } from "../models/schemas/purchase.schema"; +import Models from "../models"; + +export class PurchaseDataAccess { + async createPurchase(userId: string, courseId: string, paymentId: string): Promise { + const purchase = Models.Purchase.create({ userId, courseId, paymentId }); + return purchase; + } + + async getPurchase(purchaseId: string): Promise { + const purchase = await Models.Purchase.findById(purchaseId); + return purchase; + } + + async getPurchasesByUserId(userId: string): Promise { + const purchase = await Models.Purchase.find({ userId }); + return purchase; + } + + async getPurchases(): Promise { + const purchase = await Models.Purchase.find(); + return purchase; + } +} + diff --git a/src/dataAccess/user.dataAccess.ts b/src/dataAccess/user.dataAccess.ts index c779111..70f7d83 100644 --- a/src/dataAccess/user.dataAccess.ts +++ b/src/dataAccess/user.dataAccess.ts @@ -112,6 +112,6 @@ export class UserDataAccess { course.studentIds.push(userId); await user.save(); await course.save(); - return { message: 'ثبت نام با موفقیت انجام شد' }; + return { message: 'ثبت نام با موفقیت انجام شد', success: true }; } } diff --git a/src/main.ts b/src/main.ts index 7c78930..5bd36c4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,7 +5,7 @@ import mongoose from 'mongoose'; import * as dotenv from 'dotenv'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import * as swaggerConfig from './config/swagger.json'; -import { ValidationPipe } from '@nestjs/common'; +import { Logger, ValidationPipe } from '@nestjs/common'; import { NestExpressApplication } from '@nestjs/platform-express'; import paths from './config/paths'; dotenv.config(); @@ -61,5 +61,6 @@ async function bootstrap() { const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('api/v1', app, document); await app.listen(3000, '0.0.0.0'); + Logger.log(`Server api docs running on http://localhost:3000/api/v1`, 'Boostrap'); } bootstrap(); diff --git a/src/models/index.ts b/src/models/index.ts index 0297d2c..86728fd 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -19,6 +19,7 @@ import UserDiscount from './userDiscount.model'; import Rate from './rate.model'; import Payment from './payment.model'; import Settings from './settings.model'; +import Purchase from './Purchase.model'; const Models = { User, @@ -42,6 +43,7 @@ const Models = { CourseHeadlineComment, Rate, Payment, + Purchase }; export default Models; diff --git a/src/models/purchase.model.ts b/src/models/purchase.model.ts new file mode 100644 index 0000000..54311fd --- /dev/null +++ b/src/models/purchase.model.ts @@ -0,0 +1,12 @@ +import mongoose from 'mongoose'; +import { softDeletePlugin, SoftDeleteModel } from 'soft-delete-plugin-mongoose'; +import { cleanJson } from './plugins'; +import { Purchase, PurchaseSchema } from './schemas/purchase.schema'; + +PurchaseSchema.plugin(softDeletePlugin); +PurchaseSchema.plugin(cleanJson); +const Purchase = mongoose.model>( + 'purchases', + PurchaseSchema, +); +export default Purchase; diff --git a/src/models/schemas/payment.schema.ts b/src/models/schemas/payment.schema.ts index bf1ac0a..f9b2ded 100644 --- a/src/models/schemas/payment.schema.ts +++ b/src/models/schemas/payment.schema.ts @@ -1,5 +1,6 @@ import mongoose from "mongoose"; +import { PaymentStatus } from "src/common/eNums/paymentStatus.enum"; export const PaymentSchema = new mongoose.Schema({ userId: { @@ -7,31 +8,43 @@ export const PaymentSchema = new mongoose.Schema({ ref: 'users', required: true }, + courseId: { + type: mongoose.Types.ObjectId, + ref: 'courses', + required: true + }, + authority: { + type: String, + required: true + }, amount: { type: Number, required: true }, - paymentNumber: { + transactionNumber: { type: String, - required: true, + default: null }, - reason: { + paymentStatus: { type: String, - enum: ['deposit', 'withdrawal'], + enum: PaymentStatus, + default: PaymentStatus.Pending, required: true }, - }, -{ - collection: 'payments', - timestamps: true, }, + { + collection: 'payments', + timestamps: true, + }, ); export interface Payment extends mongoose.Document { userId: mongoose.Types.ObjectId; amount: number; - paymentNumber: string; - reason: 'deposit' | 'withdrawal'; + authority: string; + courseId: mongoose.Types.ObjectId; + transactionNumber: string; + paymentStatus: PaymentStatus; createdAt: Date; updatedAt: Date; } diff --git a/src/models/schemas/purchase.schema.ts b/src/models/schemas/purchase.schema.ts new file mode 100644 index 0000000..0074241 --- /dev/null +++ b/src/models/schemas/purchase.schema.ts @@ -0,0 +1,33 @@ + +import mongoose from "mongoose"; + +export const PurchaseSchema = new mongoose.Schema({ + userId: { + type: mongoose.Types.ObjectId, + ref: 'users', + required: true + }, + courseId: { + type: mongoose.Types.ObjectId, + ref: 'courses', + required: true + }, + paymentId: { + type: mongoose.Types.ObjectId, + ref: 'payments', + required: true + }, +}, + { + collection: 'purchases', + timestamps: true, + }, +); + +export interface Purchase extends mongoose.Document { + userId: mongoose.Types.ObjectId; + courseId: mongoose.Types.ObjectId; + paymentId: mongoose.Types.ObjectId; + createdAt: Date; + updatedAt: Date; +}