feat: payment service with IPG
This commit is contained in:
@@ -3,7 +3,9 @@ SECRET=fDS9j269DSfd823FhdfG65
|
|||||||
ACCESS_TOKEN_SECERT=PNd98dsDS089ds3SDh7C5hg265SFC42bgHxTg34x
|
ACCESS_TOKEN_SECERT=PNd98dsDS089ds3SDh7C5hg265SFC42bgHxTg34x
|
||||||
ACCESS_TOKEN_SECERT_ADMIN=G89dsJsd90DFdsf96F8se3sgL7JB29265SFC42bgHxTg34x
|
ACCESS_TOKEN_SECERT_ADMIN=G89dsJsd90DFdsf96F8se3sgL7JB29265SFC42bgHxTg34x
|
||||||
CONNECTION_STRING=
|
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_HOST=
|
||||||
EMAIL_PORT=
|
EMAIL_PORT=
|
||||||
EMAIL_USER=
|
EMAIL_USER=
|
||||||
@@ -23,5 +25,5 @@ STORAGE_ENDPOINT=https://storage.iran.liara.space
|
|||||||
STORAGE_BUCKET=dlearn
|
STORAGE_BUCKET=dlearn
|
||||||
STORAGE_REGION=default
|
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
|
NODE_ENV=production
|
||||||
+2
-2
@@ -3,7 +3,7 @@ SECRET=fDS9j269DSfd823FhdfG65
|
|||||||
ACCESS_TOKEN_SECERT=PNd98dsDS089ds3SDh7C5hg265SFC42bgHxTg34x
|
ACCESS_TOKEN_SECERT=PNd98dsDS089ds3SDh7C5hg265SFC42bgHxTg34x
|
||||||
ACCESS_TOKEN_SECERT_ADMIN=G89dsJsd90DFdsf96F8se3sgL7JB29265SFC42bgHxTg34x
|
ACCESS_TOKEN_SECERT_ADMIN=G89dsJsd90DFdsf96F8se3sgL7JB29265SFC42bgHxTg34x
|
||||||
CONNECTION_STRING=
|
CONNECTION_STRING=
|
||||||
MERCHANT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
MERCHANT_ID=250ae134-635f-4728-8415-ee9a984616e8
|
||||||
EMAIL_HOST=
|
EMAIL_HOST=
|
||||||
EMAIL_PORT=
|
EMAIL_PORT=
|
||||||
EMAIL_USER=
|
EMAIL_USER=
|
||||||
@@ -24,5 +24,5 @@ STORAGE_SECRET_KEY=95e78068-ff60-41c0-82c3-71a0de0a3e2f
|
|||||||
STORAGE_ENDPOINT=https://storage.iran.liara.space
|
STORAGE_ENDPOINT=https://storage.iran.liara.space
|
||||||
STORAGE_BUCKET=dlearn
|
STORAGE_BUCKET=dlearn
|
||||||
STORAGE_REGION=default
|
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
|
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;
|
||||||
|
}
|
||||||
+3
-1
@@ -12,6 +12,7 @@ import { UploaderModule } from './application/common/uploader/uploader.module';
|
|||||||
import { CourseHeadlineModule } from './application/courseHeadline/courseHeadline.module';
|
import { CourseHeadlineModule } from './application/courseHeadline/courseHeadline.module';
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { CourseCategoryModule } from './application/courseCategory/courseCategory.module';
|
import { CourseCategoryModule } from './application/courseCategory/courseCategory.module';
|
||||||
|
import { PaymentModule } from './application/payment/payment.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -34,6 +35,7 @@ import { CourseCategoryModule } from './application/courseCategory/courseCategor
|
|||||||
UploaderModule,
|
UploaderModule,
|
||||||
CourseHeadlineModule,
|
CourseHeadlineModule,
|
||||||
CourseCategoryModule,
|
CourseCategoryModule,
|
||||||
|
PaymentModule,
|
||||||
// ServeStaticModule.forRoot({
|
// ServeStaticModule.forRoot({
|
||||||
// rootPat h: join(__dirname, '..', 'public'),
|
// rootPat h: join(__dirname, '..', 'public'),
|
||||||
// }),
|
// }),
|
||||||
@@ -49,4 +51,4 @@ import { CourseCategoryModule } from './application/courseCategory/courseCategor
|
|||||||
providers: [UploaderService],
|
providers: [UploaderService],
|
||||||
exports: [UploaderService],
|
exports: [UploaderService],
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
export class AppModule { }
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@ export class CourseService {
|
|||||||
private courseCategoryDataAccess: CourseCategoryDataAccess,
|
private courseCategoryDataAccess: CourseCategoryDataAccess,
|
||||||
private commentDataAccess: CommentDataAccess,
|
private commentDataAccess: CommentDataAccess,
|
||||||
private courseHeadlineDataAccess: CourseHeadlineDataAccess,
|
private courseHeadlineDataAccess: CourseHeadlineDataAccess,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
async createCourse(
|
async createCourse(
|
||||||
createCourseDto: CreateCourseDto,
|
createCourseDto: CreateCourseDto,
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,7 +30,7 @@ import { CurrentUser } from '../../decorators/currentUser.decorator';
|
|||||||
@ApiTags('user')
|
@ApiTags('user')
|
||||||
@Controller('user')
|
@Controller('user')
|
||||||
export class UserController {
|
export class UserController {
|
||||||
constructor(private readonly userService: UserService) {}
|
constructor(private readonly userService: UserService) { }
|
||||||
|
|
||||||
// profile user ************************************
|
// profile user ************************************
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
@@ -110,45 +110,45 @@ export class UserController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({
|
// @ApiOperation({
|
||||||
summary: 'افزایش موجودی کیف پول',
|
// summary: 'افزایش موجودی کیف پول',
|
||||||
})
|
// })
|
||||||
@ApiBody({
|
// @ApiBody({
|
||||||
type: AddBalanceDto,
|
// type: AddBalanceDto,
|
||||||
description: 'get body',
|
// description: 'get body',
|
||||||
})
|
// })
|
||||||
@UseGuards(AuthGuard)
|
// @UseGuards(AuthGuard)
|
||||||
@Roles(Role.User)
|
// @Roles(Role.User)
|
||||||
@ApiBearerAuth()
|
// @ApiBearerAuth()
|
||||||
@Post('add')
|
// @Post('add')
|
||||||
addBalance(@CurrentUser() user, @Body() addBalanceDto: AddBalanceDto) {
|
// addBalance(@CurrentUser() user, @Body() addBalanceDto: AddBalanceDto) {
|
||||||
return this.userService.addBalance(
|
// return this.userService.addBalance(
|
||||||
user.id,
|
// user.id,
|
||||||
addBalanceDto.amount,
|
// addBalanceDto.amount,
|
||||||
addBalanceDto.paymentNumber,
|
// addBalanceDto.paymentNumber,
|
||||||
addBalanceDto.reason,
|
// addBalanceDto.reason,
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
@ApiOperation({
|
// @ApiOperation({
|
||||||
summary: 'موجودی کیف پول',
|
// summary: 'موجودی کیف پول',
|
||||||
})
|
// })
|
||||||
@UseGuards(AuthGuard)
|
// @UseGuards(AuthGuard)
|
||||||
@Roles(Role.User)
|
// @Roles(Role.User)
|
||||||
@ApiBearerAuth()
|
// @ApiBearerAuth()
|
||||||
@Get('balance')
|
// @Get('balance')
|
||||||
getBalance(@CurrentUser() user) {
|
// getBalance(@CurrentUser() user) {
|
||||||
return this.userService.getBalance(user.id);
|
// return this.userService.getBalance(user.id);
|
||||||
}
|
// }
|
||||||
|
|
||||||
@ApiOperation({
|
// @ApiOperation({
|
||||||
summary: 'لیست تراکنش ها',
|
// summary: 'لیست تراکنش ها',
|
||||||
})
|
// })
|
||||||
@UseGuards(AuthGuard)
|
// @UseGuards(AuthGuard)
|
||||||
@Roles(Role.User)
|
// @Roles(Role.User)
|
||||||
@ApiBearerAuth()
|
// @ApiBearerAuth()
|
||||||
@Get('payments')
|
// @Get('payments')
|
||||||
getPayments(@CurrentUser() user) {
|
// getPayments(@CurrentUser() user) {
|
||||||
return this.userService.getPayments(user.id);
|
// return this.userService.getPayments(user.id);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export class UserService {
|
|||||||
private readonly userDiscountDataAccess: UserDiscountDataAccess,
|
private readonly userDiscountDataAccess: UserDiscountDataAccess,
|
||||||
private readonly discountHistoryDataAccess: DiscountHistoryDataAccess,
|
private readonly discountHistoryDataAccess: DiscountHistoryDataAccess,
|
||||||
private readonly paymentDataAccess: PaymentDataAccess,
|
private readonly paymentDataAccess: PaymentDataAccess,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
async profile(id) {
|
async profile(id) {
|
||||||
const result = await this.userDataAccess.findOne(id);
|
const result = await this.userDataAccess.findOne(id);
|
||||||
@@ -150,40 +150,40 @@ export class UserService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async addBalance(
|
// async addBalance(
|
||||||
userId: string,
|
// userId: string,
|
||||||
amount: number,
|
// amount: number,
|
||||||
paymentNumber: string,
|
// paymentNumber: string,
|
||||||
reason: string,
|
// reason: string,
|
||||||
) {
|
// ) {
|
||||||
const user = await this.userDataAccess.findOne(userId);
|
// const user = await this.userDataAccess.findOne(userId);
|
||||||
const payment = await this.paymentDataAccess.createPayment(
|
// const payment = await this.paymentDataAccess.createPayment(
|
||||||
userId,
|
// userId,
|
||||||
amount,
|
// amount,
|
||||||
paymentNumber,
|
// paymentNumber,
|
||||||
reason,
|
// reason,
|
||||||
);
|
// );
|
||||||
user.walletBalance += amount;
|
// user.walletBalance += amount;
|
||||||
return await user.save();
|
// return await user.save();
|
||||||
}
|
// }
|
||||||
|
|
||||||
async getBalance(userId: string): Promise<number> {
|
// async getBalance(userId: string): Promise<number> {
|
||||||
const user = await this.userDataAccess.findOne(userId);
|
// const user = await this.userDataAccess.findOne(userId);
|
||||||
return user.walletBalance;
|
// return user.walletBalance;
|
||||||
}
|
// }
|
||||||
|
|
||||||
async getPayments(userId: string) {
|
// async getPayments(userId: string) {
|
||||||
const user = await this.userDataAccess.findOne(userId);
|
// const user = await this.userDataAccess.findOne(userId);
|
||||||
if (!user) {
|
// if (!user) {
|
||||||
throw new HttpException(
|
// throw new HttpException(
|
||||||
{
|
// {
|
||||||
status: HttpStatus.NOT_FOUND,
|
// status: HttpStatus.NOT_FOUND,
|
||||||
error: 'کاربر وجود ندارد',
|
// error: 'کاربر وجود ندارد',
|
||||||
},
|
// },
|
||||||
HttpStatus.NOT_FOUND,
|
// HttpStatus.NOT_FOUND,
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
const payments = await this.paymentDataAccess.getPaymentsByUserId(userId);
|
// const payments = await this.paymentDataAccess.getPaymentsByUserId(userId);
|
||||||
return payments;
|
// return payments;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export enum PaymentStatus {
|
||||||
|
Pending = "Pending",
|
||||||
|
Cancelled = "Cancelled",
|
||||||
|
Completed = "Completed",
|
||||||
|
}
|
||||||
@@ -90,7 +90,7 @@ export class CourseDataAccess {
|
|||||||
.populate('categories', 'id name')
|
.populate('categories', 'id name')
|
||||||
.skip(
|
.skip(
|
||||||
(filterCategoryPaginationCourseDto.page - 1) *
|
(filterCategoryPaginationCourseDto.page - 1) *
|
||||||
filterCategoryPaginationCourseDto.pageSize,
|
filterCategoryPaginationCourseDto.pageSize,
|
||||||
)
|
)
|
||||||
.limit(filterCategoryPaginationCourseDto.pageSize);
|
.limit(filterCategoryPaginationCourseDto.pageSize);
|
||||||
|
|
||||||
@@ -217,6 +217,12 @@ export class CourseDataAccess {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async getUserCourses(user: string): Promise<Course[]> {
|
||||||
|
const result = await Models.Course.find({ studentIds: user });
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
async deleteCourse(courseId: string): Promise<Course> {
|
async deleteCourse(courseId: string): Promise<Course> {
|
||||||
const result = await Models.Course.findByIdAndDelete(courseId);
|
const result = await Models.Course.findByIdAndDelete(courseId);
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ 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, paymentNumber: string,reason:string):Promise<Payment> {
|
async createPayment(userId: string, amount: number, courseId: string, authority: string, session: any): Promise<Payment> {
|
||||||
const payment = Models.Payment.create({ userId, amount, paymentNumber,reason });
|
const paymentDoc = await Models.Payment.create([{ userId, amount, courseId, authority }], { session });
|
||||||
|
console.log({ paymentDoc });
|
||||||
|
const payment = paymentDoc.map(doc => doc.toObject())[0];
|
||||||
return payment;
|
return payment;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12,6 +14,12 @@ export class PaymentDataAccess {
|
|||||||
return payment;
|
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[]> {
|
async getPaymentsByUserId(userId: string): Promise<Payment[]> {
|
||||||
const payments = await Models.Payment.find({ userId });
|
const payments = await Models.Payment.find({ userId });
|
||||||
return payments;
|
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);
|
course.studentIds.push(userId);
|
||||||
await user.save();
|
await user.save();
|
||||||
await course.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 * as dotenv from 'dotenv';
|
||||||
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
||||||
import * as swaggerConfig from './config/swagger.json';
|
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 { NestExpressApplication } from '@nestjs/platform-express';
|
||||||
import paths from './config/paths';
|
import paths from './config/paths';
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
@@ -61,5 +61,6 @@ async function bootstrap() {
|
|||||||
const document = SwaggerModule.createDocument(app, config);
|
const document = SwaggerModule.createDocument(app, config);
|
||||||
SwaggerModule.setup('api/v1', app, document);
|
SwaggerModule.setup('api/v1', app, document);
|
||||||
await app.listen(3000, '0.0.0.0');
|
await app.listen(3000, '0.0.0.0');
|
||||||
|
Logger.log(`Server api docs running on http://localhost:3000/api/v1`, 'Boostrap');
|
||||||
}
|
}
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import UserDiscount from './userDiscount.model';
|
|||||||
import Rate from './rate.model';
|
import Rate from './rate.model';
|
||||||
import Payment from './payment.model';
|
import Payment from './payment.model';
|
||||||
import Settings from './settings.model';
|
import Settings from './settings.model';
|
||||||
|
import Purchase from './Purchase.model';
|
||||||
|
|
||||||
const Models = {
|
const Models = {
|
||||||
User,
|
User,
|
||||||
@@ -42,6 +43,7 @@ const Models = {
|
|||||||
CourseHeadlineComment,
|
CourseHeadlineComment,
|
||||||
Rate,
|
Rate,
|
||||||
Payment,
|
Payment,
|
||||||
|
Purchase
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Models;
|
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 mongoose from "mongoose";
|
||||||
|
import { PaymentStatus } from "src/common/eNums/paymentStatus.enum";
|
||||||
|
|
||||||
export const PaymentSchema = new mongoose.Schema({
|
export const PaymentSchema = new mongoose.Schema({
|
||||||
userId: {
|
userId: {
|
||||||
@@ -7,31 +8,43 @@ export const PaymentSchema = new mongoose.Schema({
|
|||||||
ref: 'users',
|
ref: 'users',
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
courseId: {
|
||||||
|
type: mongoose.Types.ObjectId,
|
||||||
|
ref: 'courses',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
authority: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
amount: {
|
amount: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
paymentNumber: {
|
transactionNumber: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
default: null
|
||||||
},
|
},
|
||||||
reason: {
|
paymentStatus: {
|
||||||
type: String,
|
type: String,
|
||||||
enum: ['deposit', 'withdrawal'],
|
enum: PaymentStatus,
|
||||||
|
default: PaymentStatus.Pending,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
},
|
|
||||||
{
|
|
||||||
collection: 'payments',
|
|
||||||
timestamps: true,
|
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
collection: 'payments',
|
||||||
|
timestamps: true,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export interface Payment extends mongoose.Document {
|
export interface Payment extends mongoose.Document {
|
||||||
userId: mongoose.Types.ObjectId;
|
userId: mongoose.Types.ObjectId;
|
||||||
amount: number;
|
amount: number;
|
||||||
paymentNumber: string;
|
authority: string;
|
||||||
reason: 'deposit' | 'withdrawal';
|
courseId: mongoose.Types.ObjectId;
|
||||||
|
transactionNumber: string;
|
||||||
|
paymentStatus: PaymentStatus;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
updatedAt: 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