feat: payment service with IPG
This commit is contained in:
@@ -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
|
||||
+2
-2
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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'),
|
||||
// }),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
export interface NewPaymentData {
|
||||
amount: number;
|
||||
description: string;
|
||||
email: string;
|
||||
mobile: string;
|
||||
callbackPath: string;
|
||||
}
|
||||
|
||||
export interface VerifyPaymentData {
|
||||
authority: string;
|
||||
amount: number;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 { }
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -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<number> {
|
||||
const user = await this.userDataAccess.findOne(userId);
|
||||
return user.walletBalance;
|
||||
}
|
||||
// async getBalance(userId: string): Promise<number> {
|
||||
// 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;
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export enum PaymentStatus {
|
||||
Pending = "Pending",
|
||||
Cancelled = "Cancelled",
|
||||
Completed = "Completed",
|
||||
}
|
||||
@@ -217,6 +217,12 @@ export class CourseDataAccess {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
async getUserCourses(user: string): Promise<Course[]> {
|
||||
const result = await Models.Course.find({ studentIds: user });
|
||||
return result
|
||||
}
|
||||
|
||||
async deleteCourse(courseId: string): Promise<Course> {
|
||||
const result = await Models.Course.findByIdAndDelete(courseId);
|
||||
return result;
|
||||
|
||||
@@ -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<Payment> {
|
||||
const payment = Models.Payment.create({ userId, amount, paymentNumber,reason });
|
||||
async createPayment(userId: string, amount: number, courseId: string, authority: string, session: any): Promise<Payment> {
|
||||
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<Payment | null> {
|
||||
console.log({ authority })
|
||||
const payment = await Models.Payment.findOne({ authority });
|
||||
console.log(payment, "ssssss")
|
||||
return payment;
|
||||
}
|
||||
async getPaymentsByUserId(userId: string): Promise<Payment[]> {
|
||||
const payments = await Models.Payment.find({ userId });
|
||||
return payments;
|
||||
|
||||
@@ -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<Purchase> {
|
||||
const purchase = Models.Purchase.create({ userId, courseId, paymentId });
|
||||
return purchase;
|
||||
}
|
||||
|
||||
async getPurchase(purchaseId: string): Promise<Purchase | null> {
|
||||
const purchase = await Models.Purchase.findById(purchaseId);
|
||||
return purchase;
|
||||
}
|
||||
|
||||
async getPurchasesByUserId(userId: string): Promise<Purchase[]> {
|
||||
const purchase = await Models.Purchase.find({ userId });
|
||||
return purchase;
|
||||
}
|
||||
|
||||
async getPurchases(): Promise<Purchase[]> {
|
||||
const purchase = await Models.Purchase.find();
|
||||
return purchase;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,6 +112,6 @@ export class UserDataAccess {
|
||||
course.studentIds.push(userId);
|
||||
await user.save();
|
||||
await course.save();
|
||||
return { message: 'ثبت نام با موفقیت انجام شد' };
|
||||
return { message: 'ثبت نام با موفقیت انجام شد', success: true };
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Purchase, SoftDeleteModel<Purchase>>(
|
||||
'purchases',
|
||||
PurchaseSchema,
|
||||
);
|
||||
export default Purchase;
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
import mongoose from "mongoose";
|
||||
import { PaymentStatus } from "src/common/eNums/paymentStatus.enum";
|
||||
|
||||
export const PaymentSchema = new mongoose.Schema({
|
||||
userId: {
|
||||
@@ -7,17 +8,27 @@ 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
|
||||
},
|
||||
},
|
||||
@@ -30,8 +41,10 @@ export const PaymentSchema = new mongoose.Schema({
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user