Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[2 ~ 4 단계 방탈출 예약 결제 / 배포] 알파카(최휘용) 미션 제출합니다. #127

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
444 changes: 5 additions & 439 deletions README.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// JWT 라이브러리. https://github.com/jwtk/jjwt
implementation 'io.jsonwebtoken:jjwt:0.12.5'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'
runtimeOnly 'com.h2database:h2'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/roomescape/config/SwaggerConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package roomescape.config;

import io.swagger.v3.oas.models.OpenAPI;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI chatOpenApi() {
return new OpenAPI();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package roomescape.domain.payment;

record ApproveApiResponse(String orderId, String paymentKey, long amount) {
record ApproveApiResponse(String orderId, String paymentKey, long totalAmount) {
}
51 changes: 39 additions & 12 deletions src/main/java/roomescape/domain/payment/Payment.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import roomescape.domain.Member;
import java.util.Objects;
import roomescape.domain.Reservation;

@Entity
Expand All @@ -17,40 +17,67 @@ public class Payment {
private String orderId;
private String paymentKey;
private long amount;
@ManyToOne(fetch = FetchType.LAZY)
private Member member;

@ManyToOne(fetch = FetchType.LAZY)
private Reservation reservation;

protected Payment() {
}

public Payment(String orderId, String paymentKey, long amount, Member member) {
this(null, orderId, paymentKey, amount, member, null);
public Payment(String orderId, String paymentKey, long amount) {
this(null, orderId, paymentKey, amount, null);
}

public Payment(Long id, String orderId, String paymentKey, long amount, Member member, Reservation reservation) {
public Payment(Long id, String orderId, String paymentKey, long amount, Reservation reservation) {
this.id = id;
this.orderId = orderId;
this.paymentKey = paymentKey;
this.amount = amount;
this.member = member;
this.reservation = reservation;
}

public Payment(long id, Payment payment) {
this(id, payment.orderId, payment.paymentKey, payment.amount, payment.member, payment.reservation);
this(id, payment.orderId, payment.paymentKey, payment.amount, payment.reservation);
}

public Long getId() {
return id;
}

public Member getMember() {
return member;
}

public Reservation getReservation() {
return reservation;
}

public String getPaymentKey() {
return paymentKey;
}

public long getAmount() {
return amount;
}

public String getOrderId() {
return orderId;
}

public void updateReservation(Reservation reservation) {
this.reservation = reservation;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Payment payment = (Payment) o;
return Objects.equals(id, payment.id);
}

@Override
public int hashCode() {
return Objects.hash(id);
}
}
12 changes: 5 additions & 7 deletions src/main/java/roomescape/domain/payment/PaymentClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,14 @@
import org.springframework.boot.web.client.ClientHttpRequestFactories;
import org.springframework.boot.web.client.ClientHttpRequestFactorySettings;
import org.springframework.http.HttpHeaders;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestClient;
import roomescape.domain.Member;
import roomescape.dto.PaymentApproveRequest;

@Component
public class PaymentClient {
private final static String UNKNOWN_ERROR = "알 수 없는 오류가 발생했습니다.";
private static final String UNKNOWN_ERROR = "알 수 없는 오류가 발생했습니다.";
private final String apiUri;
private final String approveSecretKey;
private final RestClient restClient;
Expand All @@ -39,15 +37,15 @@ public PaymentClient(
this.errorHandler = errorHandler;
}

public Payment approve(PaymentApproveRequest paymentApproveRequest, Member member) {
public Payment approve(PaymentApproveRequest paymentApproveRequest) {
try {
return callApiRequest(paymentApproveRequest, member);
return callApiRequest(paymentApproveRequest);
} catch (ResourceAccessException e) {
throw new ApiCallException(UNKNOWN_ERROR);
}
}

private Payment callApiRequest(PaymentApproveRequest paymentApproveRequest, Member member) {
private Payment callApiRequest(PaymentApproveRequest paymentApproveRequest) {
String encryptedKey = Base64.getEncoder().encodeToString(approveSecretKey.getBytes());
ApproveApiResponse response = Optional.ofNullable(restClient.post()
.uri(apiUri)
Expand All @@ -57,6 +55,6 @@ private Payment callApiRequest(PaymentApproveRequest paymentApproveRequest, Memb
.onStatus(errorHandler)
.body(ApproveApiResponse.class))
.orElseThrow(() -> new ApiCallException(UNKNOWN_ERROR));
return new Payment(response.orderId(), response.paymentKey(), response.amount(), member);
return new Payment(response.orderId(), response.paymentKey(), response.totalAmount());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ public record LoginMemberReservationResponse(long reservationId,
String theme,
LocalDate date,
LocalTime time,
String status) {
String status,
String paymentKey,
Long amount) {
}
23 changes: 23 additions & 0 deletions src/main/java/roomescape/repository/JpaPaymentRepository.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package roomescape.repository;

import java.util.List;
import java.util.Optional;
import org.springframework.stereotype.Repository;
import roomescape.domain.Reservation;
import roomescape.domain.payment.Payment;
import roomescape.repository.jpa.JpaPaymentDao;

Expand All @@ -16,4 +19,24 @@ public JpaPaymentRepository(JpaPaymentDao jpaPaymentDao) {
public Payment save(Payment payment) {
return jpaPaymentDao.save(payment);
}

@Override
public List<Payment> findAllByReservationIn(List<Reservation> reservations) {
return jpaPaymentDao.findAllByReservationIn(reservations);
}

@Override
public Optional<Payment> findByOrderIdAndPaymentKey(String orderId, String paymentKey) {
return jpaPaymentDao.findByOrderIdAndPaymentKey(orderId, paymentKey);
}

@Override
public Optional<Payment> findByReservationId(Long reservationId) {
return jpaPaymentDao.findByReservationId(reservationId);
}

@Override
public void deleteByReservationId(Long reservationId) {
jpaPaymentDao.deleteByReservationId(reservationId);
}
}
11 changes: 11 additions & 0 deletions src/main/java/roomescape/repository/PaymentRepository.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
package roomescape.repository;

import java.util.List;
import java.util.Optional;
import roomescape.domain.Reservation;
import roomescape.domain.payment.Payment;

public interface PaymentRepository {
Payment save(Payment payment);

List<Payment> findAllByReservationIn(List<Reservation> reservations);

Optional<Payment> findByOrderIdAndPaymentKey(String orderId, String paymentKey);

Optional<Payment> findByReservationId(Long reservationId);

void deleteByReservationId(Long reservationId);
}
11 changes: 11 additions & 0 deletions src/main/java/roomescape/repository/jpa/JpaPaymentDao.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
package roomescape.repository.jpa;

import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import roomescape.domain.Reservation;
import roomescape.domain.payment.Payment;

public interface JpaPaymentDao extends JpaRepository<Payment, Long> {

public List<Payment> findAllByReservationIn(List<Reservation> reservations);

public Optional<Payment> findByOrderIdAndPaymentKey(String orderId, String paymentKey);

public Optional<Payment> findByReservationId(Long reservationId);

public void deleteByReservationId(Long reservationId);
}
8 changes: 4 additions & 4 deletions src/main/java/roomescape/service/PaymentService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import roomescape.domain.Member;
import roomescape.domain.payment.Payment;
import roomescape.domain.payment.PaymentClient;
import roomescape.dto.PaymentApproveRequest;
Expand All @@ -28,9 +27,10 @@ public PaymentService(PaymentClient paymentClient, PaymentRepository paymentRepo
}

public void approve(PaymentApproveRequest paymentApproveRequest, long memberId) {
Member paidMember = memberRepository.findById(memberId)
.orElseThrow(() -> new RoomescapeException(NOT_FOUND_MEMBER));
Payment payment = paymentClient.approve(paymentApproveRequest, paidMember);
if (memberRepository.findById(memberId).isEmpty()) {
throw new RoomescapeException(NOT_FOUND_MEMBER);
}
Payment payment = paymentClient.approve(paymentApproveRequest);
paymentRepository.save(payment);
}
}
29 changes: 25 additions & 4 deletions src/main/java/roomescape/service/ReservationService.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@
import roomescape.domain.Member;
import roomescape.domain.Reservation;
import roomescape.domain.ReservationWaiting;
import roomescape.domain.payment.Payment;
import roomescape.dto.LoginMemberReservationResponse;
import roomescape.dto.PaymentApproveRequest;
import roomescape.dto.ReservationRequest;
import roomescape.dto.ReservationResponse;
import roomescape.exception.RoomescapeException;
import roomescape.repository.MemberRepository;
import roomescape.repository.PaymentRepository;
import roomescape.repository.ReservationRepository;
import roomescape.repository.ReservationWaitingRepository;
import roomescape.service.finder.ReservationFinder;
Expand All @@ -30,14 +33,17 @@ public class ReservationService {
private final ReservationWaitingRepository waitingRepository;
private final ReservationFinder reservationFinder;
private final MemberRepository memberRepository;
private final PaymentRepository paymentRepository;

public ReservationService(ReservationRepository reservationRepository,
ReservationWaitingRepository waitingRepository,
ReservationFinder reservationFinder, MemberRepository memberFinder) {
ReservationFinder reservationFinder, MemberRepository memberFinder,
PaymentRepository paymentRepository) {
this.reservationRepository = reservationRepository;
this.waitingRepository = waitingRepository;
this.reservationFinder = reservationFinder;
this.memberRepository = memberFinder;
this.paymentRepository = paymentRepository;
}

public ReservationResponse save(ReservationRequest reservationRequest) {
Expand All @@ -46,9 +52,21 @@ public ReservationResponse save(ReservationRequest reservationRequest) {
beforeSave.validatePastTimeReservation();

Reservation saved = reservationRepository.save(beforeSave);
updatePayment(saved, reservationRequest.approveRequest());
return toResponse(saved);
}

private void updatePayment(Reservation saved, PaymentApproveRequest approveRequest) {
if (approveRequest == null) {
return;
}
paymentRepository.findByOrderIdAndPaymentKey(approveRequest.orderId(), approveRequest.paymentKey())
.ifPresent(payment -> {
payment.updateReservation(saved);
paymentRepository.save(payment);
});
}

public List<ReservationResponse> findAll() {
return reservationRepository.findAll().stream()
.map(ReservationResponseMapper::toResponse)
Expand All @@ -64,9 +82,11 @@ public List<ReservationResponse> findByMemberAndThemeBetweenDates(long memberId,
}

public List<LoginMemberReservationResponse> findByMemberId(long memberId) {
return reservationRepository.findByMemberId(memberId)
.stream()
.map(LoginMemberReservationResponseMapper::toResponse)
List<Reservation> reservations = reservationRepository.findByMemberId(memberId);
List<Payment> payments = paymentRepository.findAllByReservationIn(reservations);

return reservations.stream()
.map(reservation -> LoginMemberReservationResponseMapper.toResponse(reservation, payments))
.toList();
}

Expand Down Expand Up @@ -107,6 +127,7 @@ private void updateReservationAndDeleteTopWaiting(Reservation reservation, Reser
}

private void deleteReservation(long reservationId) {
paymentRepository.deleteByReservationId(reservationId);
reservationRepository.delete(reservationId);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package roomescape.service.mapper;

import java.util.List;
import java.util.Optional;
import roomescape.domain.Reservation;
import roomescape.domain.payment.Payment;
import roomescape.dto.LoginMemberReservationResponse;
import roomescape.dto.ReservationWaitingResponse;

Expand All @@ -11,7 +14,27 @@ public static LoginMemberReservationResponse toResponse(Reservation reservation)
reservation.getThemeName(),
reservation.getDate(),
reservation.getTime(),
"예약");
"예약",
null, null);
}

public static LoginMemberReservationResponse toResponse(Reservation reservation, Optional<Payment> payment) {
return payment.map(value -> new LoginMemberReservationResponse(
reservation.getId(),
reservation.getThemeName(),
reservation.getDate(),
reservation.getTime(),
"예약",
value.getPaymentKey(),
value.getAmount()))
.orElseGet(() -> toResponse(reservation));
}

public static LoginMemberReservationResponse toResponse(Reservation reservation, List<Payment> payments) {
Optional<Payment> paymentByReservation = payments.stream()
.filter(payment -> payment.getReservation().equals(reservation))
.findFirst();
return toResponse(reservation, paymentByReservation);
}

public static LoginMemberReservationResponse from(ReservationWaitingResponse waitingResponse) {
Expand All @@ -20,7 +43,8 @@ public static LoginMemberReservationResponse from(ReservationWaitingResponse wai
waitingResponse.themeName(),
waitingResponse.date(),
waitingResponse.startAt(),
"%d번째 예약 대기".formatted(waitingResponse.priority())
"%d번째 예약 대기".formatted(waitingResponse.priority()),
null, null
);
}
}
Loading