프로젝트에서 결제를 구현하는 게 이번 프로젝트에서 내가 원했던 거였다.
알아본 결과 Iamport라는 게 현재는 PortOne으로 바뀌었고 이걸로 결제를 구현했다.
트레이닝을 예약하는 걸로, 날짜와 시간을 선택하고 예약하기를 누르면 결제가 진행되어야 한다.
포트원을 통한 결제는 사전 검증 → 결제 요청 → 사후 검증으로 나뉜다.
우리 프로젝트에서 결제 시 주의해야 할 점은 날짜, 시간을 예약하기 때문에 먼저 예약하기로 한 시간이 있다면 그 시간을 다음에 누른 사람은 예약이 돼서는 안 된다는 거다. 이를 위해 적용했던 거는 다음 포스트에서 다루기로 하고, 이걸 위해 여기서는 미리 예약 정보를 만드는 작업을 진행했다.
@Override
@Transactional
public Long saveOrder(ReserveReqDto dto, User user) {
Training training = trainingRepository.findById(dto.getTrainingId()).orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND, "존재하지 않는 트레이닝입니다."));
if (training.isClosed()) {
throw new CustomException(ErrorCode.BAD_REQUEST, "마감된 트레이닝은 예약할 수 없습니다.");
}
AvailableDate availableDate = availableDateRepository.findById(dto.getReservationDateId()).orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND, "해당 예약 날짜는 존재하지 않습니다."));
if (!availableDate.isEnabled()) {
throw new CustomException(ErrorCode.DATE_OR_TIME_ERROR, "해당 날짜에 예약 가능한 시간대가 없습니다.");
}
AvailableTime availableTime = availableTimeRepository.findById(dto.getReservationTimeId()).orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND, "해당 예약 시간은 존재하지 않습니다."));
if (!availableTime.isEnabled()) {
throw new CustomException(ErrorCode.DATE_OR_TIME_ERROR, "해당 시간은 이미 예약되었습니다.");
}
if (!availableTime.getAvailableDate().equals(availableDate)) {
throw new CustomException(ErrorCode.DATE_OR_TIME_ERROR, "예약 하려는 날짜에 해당하는 시간이 아닙니다.");
}
closeReservationDateTime(availableTime, availableDate);
availableDateRepository.saveAndFlush(availableDate);
availableTimeRepository.saveAndFlush(availableTime);
updateTrainingStatus(training, availableDate.getId());
ReserveInfo reserveInfo = reserveInfoRepository.save(createReserveInfo(user, training, availableDate, availableTime));
eventPublisher.publishEvent(createReservationNotifyRequest(training));
return reserveInfo.getId();
}
날짜와 시간에 대한 여러 검증을 거치고 난 후에 미리 예약 정보를 만둘어둔다.
다음 글에서 동시성을 다루기도 하는데 여기서 하나의 시간에 동시성 문제가 있기때문에 시간 객체 수정 작업이 끝난 후, saveAndFlush로 db에 바로 적용되도록 ㅐ줬다.