Skip to content

Commit

Permalink
Kyungwhan (#51)
Browse files Browse the repository at this point in the history
* feat: Recharge

* fix: git pull

* feat: git pull

* feat: git pull

* merge : 충돌 해결

* fix

* fix : cashlog 중복 제거

* fix : 달력 예약되어있는 날짜 수정

* fix

* fix

* fix

* refactor: Refactor

* feat: Inheritance of payment Entities, Add Querydsl on CashLogRepository

* for merge

* fix: Bug fix

* feat: CashLog 쿼리 최적화, 결제 내역에 Pay 관련 데이터만 출력되도록 변경, 2차 프로젝트 엔티티 추가

* for merge

* refactor : reservation save, modify 서비스와 컨트롤러 리팩토링

* feat : HOST, GUEST 역할 분리

* feat : 웹소켓 연결 설정

* commit

* feat : 채팅방 생성 후 db에 저장, 생성된 채팅방 id를 응답

* fix : 특정 채팅방만 구독

* feat : 클라이언트로부터 받는 메세지 정보 DTO로 받음, DB에 저장 후 DTO로 응답, 채팅방 정보 전송

* fix : WebSocketConfig에서 CORS 설정에 배포환경 추가

* refactor : 채팅 api와 웹소켓 컨트롤러 구분

* feat : 채팅방에 이전 채팅 내역 찾아서 보내기

* feat: Add Batch, Settle, modify ReservationInit etc

* merge

* feat : 마이페이지 나의 문의내역을 위한 DTO

* fix : 채팅방에 다른 유저 접근 못하게 설정

* feat : 마이페이지 나의 문의내역 채팅방 리스트
채팅방 생성된 순서로 정렬(수정 필요)

* feat : 숙소 예약 현황 불러오기1

* feat : 숙소 호스트 숙소 관리 페이지 초안 완성

* feat : 채팅방 최신 메세지 순으로 정렬

* feat : 쿠폰 관련 CRUD 패키지 생성

* feat : 쿠폰 생성 로직 구현1

* feat : 첫 예약 쿠폰 발급 로직 초안 구현

* feat: SettleController

* feat : Guest의 쿠폰 가지고 오는 로직 구현

* feat : 채팅방 퇴장을 위한 필드 추가, 채팅방 생성 로직 변경

* feat : 마이페이지 문의내역 게스트와 호스트 구분
- 게스트는 퇴장한 채팅방 보이지 않게

* feat : 종료된 채팅방 정보를 위해 DTO에 필드 추가

* feat : 채팅 내역은 없고 생성만 된 채팅방 마이페이지에서 채팅방이 생성된 시간 표시
- ChatRoom이 DateEntity를 상속받게 함
- DTO에도 생성시간 필드 추가

* feat: Settle Api

* feat : 쿠폰 사용 로직 초안 구현

* feat : 결제 시 쿠폰 사용 구현 초안

* feat : 쿠폰 사용 시 삭제하는 컨트롤러, 서비스 구현 중1

* feat: Settle Page

* feat : 마이페이지 문의내역 호스트 진행중/종료 구분

* fix: bug

* fix

* fix: bug fix

* fix : 마이페이지 호스트와 게스트 분리 후 페이징 문제 해결

* fix : 마이페이지 문의내역 응답 유저 역할로만 구분

* fix : 채팅내역이 없는 채팅방은 마이페이지 문의내역에 표시하지 않음

* feat : 채팅방 입장 및 퇴장 시 메세지 읽음 처리

* feat : 채팅방 목록 응답 시 안 읽은 메세지 개수 포함

* feat : 채팅방 목록 응답 시 안 읽은 메세지 개수 포함

* fix : 채팅방 목록 응답 시 안 읽은 메세지 개수 포함(상대방이 보낸 메세지들 중)

* refactor : 코드 정리

* feat : 쿠폰 사용 시 CouponRecord에 사용기록 저장 및 쿠폰 삭제

* feat: settleKw

* merge

* feat: Refactor Init

* feat : ReservationService 테스트 코드 작성

* fix: Fix bug on Settle

* fix

* fix

* merge

* test : ReservationController 테스트 코드 작성 중

* test : ReservationControllerTest 코드 작성(추가 확인 필요)

* merge

* feat: CashLogControllerTest

* delete : Withdraw 엔티티 삭제됨

* fix : ReservationControllerTest 수정

* fix : ReservationControllerTest 수정 - ObjectMapper로 응답 데이터 비교

* fix : ReservationControllerTest 수정 - ObjectMapper로 응답 데이터 비교

* feat : ReservationControllerTest post

* fix : ReservationControllerTest post 응답 데이터 비교

* fix : ReservationControllerTest MemberOnly 체커 작동하게 설정, 접근 권한까지 확인

* feat: Add test code

* fix: Fix test code

* feat: Payment ControllerTest

* fix: Delete SecurityTestConfig

* feat: CashLogServiceTest

* feat: CashLogServiceTest 테스트 추가

* feat: 테스트 추가

---------

Co-authored-by: jkeum-dev <[email protected]>
Co-authored-by: kyumho kim <[email protected]>
  • Loading branch information
3 people authored Apr 16, 2024
1 parent d1beb15 commit 51dcefd
Show file tree
Hide file tree
Showing 10 changed files with 302 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.example.hotsix_be.payment.payment.dto.request.TossPaymentRequest;
import com.example.hotsix_be.payment.payment.exception.PaymentException;
import com.example.hotsix_be.payment.payment.service.TossService;
import com.example.hotsix_be.payment.payment.service.TossService;
import com.example.hotsix_be.payment.recharge.entity.Recharge;
import com.example.hotsix_be.payment.recharge.service.RechargeService;
import com.example.hotsix_be.reservation.entity.Reservation;
Expand All @@ -35,8 +36,7 @@ public class PayService {
private final CouponService couponService;

// 예치금 사용 결제
@Transactional
public Pay doPay(final Reservation reservation, final EventType eventType, final Long discountAmount) {
private Pay doPay(final Reservation reservation, final EventType eventType, final Long discountAmount) {

Member buyer = reservation.getMember();
Long payPrice = reservation.getPrice();
Expand Down Expand Up @@ -66,16 +66,12 @@ public Pay doPay(final Reservation reservation, final EventType eventType, final
public CashLog payByCashOnly(final Reservation reservation, final UseCouponRequest useCouponRequest) {
Long discountAmount = useCouponRequest.getDiscountAmount();

if (!canPay(reservation, reservation.getPrice(), discountAmount))
if (!canPay(reservation, 0L, discountAmount))
throw new PaymentException(INSUFFICIENT_DEPOSIT);

reservation.updateOrderId(randomNanoId());
reservation.updateOrderId("o" + randomNanoId());

Pay pay = doPay(reservation, 결제__예치금, discountAmount);

reservation.payDone();

return pay;
return doPay(reservation, 결제__예치금, discountAmount);
}


Expand All @@ -87,7 +83,7 @@ public CashLog payByTossPayments(
final Long discountAmount
) {
if (!canPay(reservation, Long.parseLong(tossConfirmRequest.getAmount()), tossConfirmRequest.getDiscountAmount())) {
throw new PaymentException(INSUFFICIENT_DEPOSIT);
throw new PaymentException(INVALID_REQUEST);
}

if (tossConfirmRequest.getDiscountAmount() > 0) {
Expand All @@ -105,9 +101,9 @@ public CashLog payByTossPayments(
// orderId 입력
reservation.updateOrderId(orderId);

Recharge recharge = rechargeService.doRecharge(tossConfirmRequest, buyer, discountAmount);
rechargeService.doRecharge(tossConfirmRequest, buyer, discountAmount);

return doPay(reservation, EventType.결제__토스페이먼츠, discountAmount); // TODO 토스페이먼츠 결제와 포인트 복합 결제 명확히 하기
return doPay(reservation, EventType.결제__토스페이먼츠, discountAmount);
}

public boolean canPay(final Reservation reservation, final Long pgPayPrice, final Long discountAmount) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class TossConfirmRequest {
@Schema(description = "결제 타입", example = "NORMAL")
private String paymentType;

@Schema(description = "주문 고유 식별 코드", example = "a4CWyWY5m89PNh7xJwhk1")
@Schema(description = "주문 고유 식별 코드", example = "oa4CWyWY5m89PNh7xJwhk1")
private String orderId;

@Schema(description = "주문 금액", example = "100000")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.example.hotsix_be.payment.payment.service;

import com.example.hotsix_be.payment.payment.dto.request.TossConfirmRequest;
import com.example.hotsix_be.payment.payment.dto.request.TossPaymentRequest;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

@Service
@Profile("test")
public class TestTossServiceImplForEasyPay implements TossService {
@Override
public Mono<TossPaymentRequest> confirmTossPayment(TossConfirmRequest tossConfirmRequest) {
TossPaymentRequest testRequest = new TossPaymentRequest(
tossConfirmRequest.getOrderId(),
Long.parseLong(tossConfirmRequest.getAmount()),
"간편결제",
"DONE",
null,
null,
null
);

return Mono.just(testRequest);
}
}
Original file line number Diff line number Diff line change
@@ -1,48 +1,9 @@
package com.example.hotsix_be.payment.payment.service;


import com.example.hotsix_be.payment.payment.dto.request.TossConfirmRequest;
import com.example.hotsix_be.payment.payment.dto.request.TossPaymentRequest;
import com.example.hotsix_be.payment.payment.exception.PaymentException;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.util.Base64;

import static com.example.hotsix_be.common.exception.ExceptionCode.PAYMENT_API_CALL_FAILED;
import static java.nio.charset.StandardCharsets.UTF_8;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class TossService {
private final WebClient webClient;

private static String authorization;

@Value("${custom.tossPayments.widget.secretKey}")
private void setTossPaymentsWidgetSecretKey(final String tossPaymentsWidgetSecretKey) {
String encodedKey = Base64.getEncoder().encodeToString((tossPaymentsWidgetSecretKey + ":").getBytes(UTF_8));
authorization = "Basic " + encodedKey;
}

@Transactional
public Mono<TossPaymentRequest> confirmTossPayment(final TossConfirmRequest tossConfirmRequest) {
return webClient.post()
.uri("https://api.tosspayments.com/v1/payments/confirm")
.contentType(MediaType.APPLICATION_JSON)
.header("Authorization", authorization)
.bodyValue(tossConfirmRequest)
.retrieve()
.onStatus(
HttpStatusCode::isError,
clientResponse -> Mono.error(new PaymentException(PAYMENT_API_CALL_FAILED)))
.bodyToMono(TossPaymentRequest.class);
}
public interface TossService {
Mono<TossPaymentRequest> confirmTossPayment(final TossConfirmRequest tossConfirmRequest);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.example.hotsix_be.payment.payment.service;


import com.example.hotsix_be.payment.payment.dto.request.TossConfirmRequest;
import com.example.hotsix_be.payment.payment.dto.request.TossPaymentRequest;
import com.example.hotsix_be.payment.payment.exception.PaymentException;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.util.Base64;

import static com.example.hotsix_be.common.exception.ExceptionCode.PAYMENT_API_CALL_FAILED;
import static java.nio.charset.StandardCharsets.UTF_8;

@Service
@RequiredArgsConstructor
@Profile("!test")
@Transactional(readOnly = true)
public class TossServiceImpl implements TossService {
private final WebClient webClient;

private static String authorization;

@Value("${custom.tossPayments.widget.secretKey}")
private void setTossPaymentsWidgetSecretKey(final String tossPaymentsWidgetSecretKey) {
String encodedKey = Base64.getEncoder().encodeToString((tossPaymentsWidgetSecretKey + ":").getBytes(UTF_8));
authorization = "Basic " + encodedKey;
}

@Transactional
public Mono<TossPaymentRequest> confirmTossPayment(final TossConfirmRequest tossConfirmRequest) {
return webClient.post()
.uri("https://api.tosspayments.com/v1/payments/confirm")
.contentType(MediaType.APPLICATION_JSON)
.header("Authorization", authorization)
.bodyValue(tossConfirmRequest)
.retrieve()
.onStatus(
HttpStatusCode::isError,
clientResponse -> Mono.error(new PaymentException(PAYMENT_API_CALL_FAILED)))
.bodyToMono(TossPaymentRequest.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,19 @@ public class RechargeService {
private final TossService tossService;

@Transactional
public Recharge doRecharge(final TossConfirmRequest tossConfirmRequest, final Member member, final Long discountAmount) {
public void doRecharge(final TossConfirmRequest tossConfirmRequest, final Member member, final Long discountAmount) {
TossPaymentRequest tossPaymentRequest = tossService.confirmTossPayment(tossConfirmRequest).block();

if (isVirtual(tossPaymentRequest)) {
Recharge recharge = requestVirtualRecharge(tossPaymentRequest, member);
return rechargeRepository.save(recharge);
rechargeRepository.save(recharge);
return;
}

if (isEasyPay(tossPaymentRequest)) {
Recharge recharge = easyPayRecharge(tossPaymentRequest, member, discountAmount);
return rechargeRepository.save(recharge);
rechargeRepository.save(recharge);
return;
}

throw new PaymentException(INVALID_REQUEST);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.List;
import java.util.Optional;

import static com.example.hotsix_be.common.exception.ExceptionCode.ALREADY_BEEN_INITIALIZED;
import static com.example.hotsix_be.common.exception.ExceptionCode.INVALID_AUTHORITY;
import static com.example.hotsix_be.payment.cashlog.entity.EventType.결제__예치금;
import static org.assertj.core.api.Assertions.assertThat;
Expand All @@ -55,7 +56,7 @@ public class CashLogServiceTest {

@Test
@DisplayName("새로운 CashLog 를 생성하여 CashLogMarker 에 입력 후 포인트를 이동시킨다.")
void addCashLog() {
void addCashLog() {
// given
Member member = Member.builder().restCash(30_000L).build();
Long price = -10_000L;
Expand All @@ -72,6 +73,24 @@ void addCashLog() {
assertThat(cashLogMarker.isInitialized()).isTrue();
}

@Test
@DisplayName("CashLogMarker 에 initCashLog 를 통한 초기화를 두번 진행할 경우 오류를 발생시킨다.")
void initCashLog() {
// given
Member member = Member.builder().restCash(30_000L).build();
Long price = -10_000L;
CashLogMarker cashLogMarker = Pay.builder().build();

// when
cashLogService.initCashLog(member, price, "o8sJILLP1EP6V1nLksCBL", 결제__예치금, cashLogMarker);

Throwable thrown = catchThrowable(() -> cashLogService.initCashLog(member, price, "o8sJILLP1EP6V1nLksCBL", 결제__예치금, cashLogMarker));

// then
assertThat(thrown).isInstanceOf(PaymentException.class)
.hasMessage(ALREADY_BEEN_INITIALIZED.getMessage());
}

@Test
@DisplayName("나의 CashLog 들을 MyCashLogResponse 형태로 반환한다.")
void findMyPageList() {
Expand Down
Loading

0 comments on commit 51dcefd

Please sign in to comment.