From e5fd8a72cb855d6d30a61b3fec23ac01f7101269 Mon Sep 17 00:00:00 2001 From: Sejin Park Date: Thu, 27 Jul 2023 05:52:32 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=EA=B2=B0=EC=A0=9C=20=EB=AA=A9=EB=A1=9D=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20api=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/module/payment/api/payment.controller.ts | 17 +++++-- src/module/payment/data/payment.db.ts | 50 +++++++++++++++---- src/module/payment/data/payment.entity.ts | 2 +- .../payment/domain/payment.repository.ts | 27 ++++++++++ src/module/payment/domain/payment.service.ts | 14 ++++-- 5 files changed, 90 insertions(+), 20 deletions(-) diff --git a/src/module/payment/api/payment.controller.ts b/src/module/payment/api/payment.controller.ts index ae21dc8..aacc4a4 100755 --- a/src/module/payment/api/payment.controller.ts +++ b/src/module/payment/api/payment.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common'; +import { Body, Controller, Get, Post, Param, UseGuards } from '@nestjs/common'; import { PaymentService } from '../domain/payment.service'; import { PaymentInfo, PaymentInfoForRefund } from '../domain/payment.model'; import { ApiOperation } from '@nestjs/swagger'; @@ -8,7 +8,7 @@ import { Payment } from '../domain/payment.model'; import { RefundPaymentInfo } from '../domain/payment.model'; import { JwtAuthGuard } from "../../auth/auth.jwtAuthGuard"; import Logger from 'src/logger'; -//@UseGuards(AuthGuard) +@UseGuards(JwtAuthGuard) @Controller('/payment') export class PaymentController { @@ -18,7 +18,6 @@ export class PaymentController { this.logger = new Logger('PaymentController') } - @UseGuards(JwtAuthGuard) @ApiOperation({summary : '결제하기'}) @Post() async makePayment(@Body() paymentData: PaymentRequestDto): Promise { @@ -44,12 +43,22 @@ export class PaymentController { return await this.paymentService.makePayment(paymentInfo) } - @UseGuards(JwtAuthGuard) + @ApiOperation({summary : '유저별 결제 목록 조회하기(환불된 것 제외)'}) + @Get(':userId') + async getPaymenList(@Param('userId') userId: number): Promise { + + this.logger.info('request userId', userId) + return await this.paymentService.getPaymenList(userId) + } + + @ApiOperation({summary : '결제 취소하기'}) @Post('/refund') refundPayment(@Body() refundData: RefundPaymentRequestDto): Promise { const { paymentId, userId} = refundData; + this.logger.info('request userId', refundData.userId) + this.logger.info('request paymentId',refundData.paymentId) // dto - > model const refundInfo: RefundPaymentInfo = { paymentId: paymentId, diff --git a/src/module/payment/data/payment.db.ts b/src/module/payment/data/payment.db.ts index 73a697f..4658783 100755 --- a/src/module/payment/data/payment.db.ts +++ b/src/module/payment/data/payment.db.ts @@ -8,7 +8,7 @@ import { import { PaymentInfo, Payment } from '../domain/payment.model'; import { PaymentEntity } from './payment.entity'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; +import { Repository, FindOperator } from 'typeorm'; import { InvalidPaymentInfoException } from '../payment.error'; import { UserEntity } from 'src/module/user/data/user.entity'; import { AlarmService } from 'src/module/alarm/alarm.service'; @@ -28,6 +28,9 @@ export class PaymentRepositoryImpl implements PaymentRepository { ) { this.logger = new Logger('PaymentRepositoryImpl'); } + + + // 결제 저장하기 async savePayment(paymentInfo: PaymentInfo): Promise { try { // user가 db에 존재하는지?? @@ -83,19 +86,42 @@ export class PaymentRepositoryImpl implements PaymentRepository { throw error; // 예외를 다시 던져서 호출한 쪽에서 처리할 수 있도록 함 } } - async refundPayment( - refundInfo: RefundPaymentInfo, - ): Promise { + + // 유저pk로 결제 목록 조회 + async findPaymentsByUserId(userId: number): Promise { + const payments = await this.PaymentDB + .createQueryBuilder('payment') + .innerJoinAndSelect('payment.User', 'user') + .where('user.id = :userId', { userId }) + .andWhere('payment.is_refund <> :isRefund', { isRefund: true }) // is_refund가 1이 아닌 조건 추가 + .getMany(); + + // PaymentEntity와 매핑된 Payment 객체를 생성하여 배열로 변환 + const paymentList: Payment[] = payments.map((paymentEntity) => ({ + paymentId: paymentEntity.id, + userId: paymentEntity.User.id, + cardNum: paymentEntity.cardNum, + endDate: paymentEntity.endDate, + cvc: paymentEntity.cvc, + cardCompany: paymentEntity.cardCompany, + price: paymentEntity.price, + })); + + return paymentList; + } + + // 환불하기 + async refundPayment(refundInfo: RefundPaymentInfo): Promise { + // 요청에 들어온 paymentId가 db에 존재하는지? const payment = await this.PaymentDB.findOne({ - where: { id: refundInfo.paymentId }, + where: { id: refundInfo.paymentId, User: { id: refundInfo.userId } }, + relations: ['User'], }); - if (payment === null) throw new InvalidPaymentInfoException('결제 PK'); - // user가 db에 존재하는지?? - const user = await this.UserDB.findOne({ - where: { id: refundInfo.userId }, - }); - if (user === null) throw new InvalidPaymentInfoException('유저'); + + if (payment === null) throw new InvalidPaymentInfoException('결제PK와 유저PK'); + + // 소프트 딜리트 - 환불 처리됨 payment.isRefund = true; // 원하는 칼럼을 true로 변경 await this.PaymentDB.save(payment); // 변경 내용을 저장 @@ -110,11 +136,13 @@ export class PaymentRepositoryImpl implements PaymentRepository { }; return refundPaymentDomain; } + async findUserPhoneNumber(userId: number): Promise { // user가 db에 존재하는지?? const user = await this.UserDB.findOne({ where: { id: userId }, }); + if (user === null) throw new InvalidPaymentInfoException('유저'); return user.phoneNumber; } diff --git a/src/module/payment/data/payment.entity.ts b/src/module/payment/data/payment.entity.ts index 9cbe2f8..d9b9c2b 100755 --- a/src/module/payment/data/payment.entity.ts +++ b/src/module/payment/data/payment.entity.ts @@ -18,7 +18,7 @@ export class PaymentEntity { User: UserEntity | null; // nullable로 변경 //@JoinColumn([{ name: 'userId', referencedColumnName: 'id' }]) - @Column('int', { name: 'card_num', nullable: true }) + @Column('bigint', { name: 'card_num', nullable: true }) cardNum: number; @Column('varchar', { name: 'end_date', length: 45, nullable: true }) diff --git a/src/module/payment/domain/payment.repository.ts b/src/module/payment/domain/payment.repository.ts index 350047e..f48b2a1 100755 --- a/src/module/payment/domain/payment.repository.ts +++ b/src/module/payment/domain/payment.repository.ts @@ -6,6 +6,7 @@ export interface PaymentRepository { savePayment(paymentInfo: PaymentInfo): Promise; refundPayment(paymentInfo: RefundPaymentInfo): Promise; findUserPhoneNumber(userId: number): Promise; + findPaymentsByUserId(userId: number): Promise; } // 테스트용 @@ -27,6 +28,32 @@ export class TestPaymentRepository implements PaymentRepository { }; } + async findPaymentsByUserId(userId: number): Promise { + // 테스트용 가짜 데이터 생성 + const fakeData: Payment[] = [ + { + paymentId: 1, + userId: userId, + cardNum: 1234, + endDate: '2307', + cvc: 567, + cardCompany: 'hyundai', + price: 1000, + }, + { + paymentId: 2, + userId: userId, + cardNum: 5678, + endDate: '2308', + cvc: 789, + cardCompany: 'shinhan', + price: 2000, + } + ]; + + return fakeData; + } + async refundPayment( paymentInfo: RefundPaymentInfo, ): Promise { diff --git a/src/module/payment/domain/payment.service.ts b/src/module/payment/domain/payment.service.ts index 96151f8..7277117 100755 --- a/src/module/payment/domain/payment.service.ts +++ b/src/module/payment/domain/payment.service.ts @@ -27,7 +27,7 @@ export class PaymentService { } public async makePayment(paymentInfo: PaymentInfo): Promise { - this.validatePaymentInfo(paymentInfo); + await this.validatePaymentInfo(paymentInfo); // DB작업 - 결제 정보 저장 const savePaymentInfo = await this.repository.savePayment(paymentInfo); @@ -39,7 +39,7 @@ export class PaymentService { const userPhoneNumber = await this.repository.findUserPhoneNumber( paymentInfo.userId, ); - const message = '결제가 완료되었습니다'; + let message = '결제가 완료되었습니다'; const alarmData: AlarmData = { recipient: userPhoneNumber, @@ -51,6 +51,12 @@ export class PaymentService { return savePaymentInfo; } + public async getPaymenList(userId: number): Promise { + + const paymentList: Payment[] = await this.repository.findPaymentsByUserId(userId); + return paymentList; + } + public async refundPayment( refundInfo: RefundPaymentInfo, ): Promise { @@ -65,7 +71,7 @@ export class PaymentService { refundInfo.userId, ); - const message = '결제가 취소되었습니다'; + let message = '결제가 취소되었습니다'; const alarmData: AlarmData = { recipient: userPhoneNumber, @@ -145,4 +151,4 @@ export class PaymentService { } return true; } -} +} \ No newline at end of file From a2b54267eed3f2f4fae045557c2030aa5f2d3896 Mon Sep 17 00:00:00 2001 From: Sejin Park Date: Thu, 27 Jul 2023 05:58:41 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=EC=9D=B4=EB=AF=B8=20=EA=B2=B0=EC=A0=9C=20?= =?UTF-8?q?=EC=B7=A8=EC=86=8C=ED=96=88=EC=9D=84=20=EA=B2=BD=EC=9A=B0,=2040?= =?UTF-8?q?0=EC=9D=B5=EC=85=89=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/module/payment/data/payment.db.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/module/payment/data/payment.db.ts b/src/module/payment/data/payment.db.ts index 4658783..9e53519 100755 --- a/src/module/payment/data/payment.db.ts +++ b/src/module/payment/data/payment.db.ts @@ -121,6 +121,10 @@ export class PaymentRepositoryImpl implements PaymentRepository { if (payment === null) throw new InvalidPaymentInfoException('결제PK와 유저PK'); + // 이미 환불 처리된 결제인지 확인 + if (payment.isRefund) { + throw new InvalidPaymentInfoException('요청이며, 이미 환불 처리된 결제'); + } // 소프트 딜리트 - 환불 처리됨 payment.isRefund = true; // 원하는 칼럼을 true로 변경