Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.threestar.trainus.domain.coupon.user.repository;

import java.util.List;
import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
Expand All @@ -12,6 +13,8 @@
public interface UserCouponRepository extends JpaRepository<UserCoupon, Long> {
boolean existsByUserIdAndCouponId(Long userId, Long couponId);

Optional<UserCoupon> findByUserIdAndCouponId(Long userId, Long couponId);

@Query("SELECT uc FROM UserCoupon uc JOIN FETCH uc.coupon WHERE uc.user.id = :userId")
List<UserCoupon> findAllByUserIdWithCoupon(@Param("userId") Long userId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,35 @@ public CouponPageResponseDto getCoupons(Long userId) {
.coupons(dtoList)
.build();
}

@Transactional
public UserCoupon getValidUserCoupon(Long userCouponId, Long userId) {
return userCouponRepository.findByUserIdAndCouponId(userId, userCouponId)
.filter(c -> c.getStatus() == CouponStatus.ACTIVE)
.orElseThrow(() -> new BusinessException(ErrorCode.COUPON_NOT_FOUND));
}

public int calculateDiscountedPrice(int originalPrice, UserCoupon coupon) {
String discountPrice = coupon.getCoupon().getDiscountPrice();
if (discountPrice.contains("%")) {
int discountPercentage = Integer.parseInt(discountPrice.substring(0, discountPrice.indexOf("%")));
return originalPrice * discountPercentage / 100;
} else {
return Integer.parseInt(discountPrice.replace("원", ""));
}
}

public void useCoupon(UserCoupon coupon) {
if (coupon.getStatus() == CouponStatus.ACTIVE) {
coupon.use();
userCouponRepository.save(coupon);
}
}

public void restoreCoupon(UserCoupon coupon) {
if (coupon.getStatus() == CouponStatus.INACTIVE) {
coupon.restore();
userCouponRepository.save(coupon);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -306,4 +306,24 @@ public LessonSimpleResponseDto getLessonSimple(Long lessonId) {

return LessonSimpleMapper.toLessonSimpleDto(lesson);
}

@Transactional
public void cancelPayment(Long lessonId, Long userId) {
Lesson lesson = adminLessonService.findLessonById(lessonId);
lesson.decrementParticipantCount();
lessonRepository.save(lesson);

LessonParticipant lessonParticipant = lessonParticipantRepository.findByLessonIdAndUserId(lessonId, userId)
.orElseThrow(() -> new BusinessException(ErrorCode.INVALID_LESSON_PARTICIPANT));

lessonParticipantRepository.delete(lessonParticipant);
}

@Transactional
public void checkValidLessonParticipant(Lesson lesson, User user) {
boolean ifExists = lessonParticipantRepository.existsByLessonIdAndUserId(lesson.getId(), user.getId());
if (!ifExists) {
throw new BusinessException(ErrorCode.INVALID_LESSON_PARTICIPANT);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,16 @@ public void incrementParticipantCount() {
}
}

// 참가자 수 감소
public void decrementParticipantCount() {
this.participantCount--;

// 정원 달성 시 -> 모집완료로 상태 변경
if (this.participantCount < this.maxParticipants) {
this.status = LessonStatus.RECRUITING;
}
}

//레슨 이름 수정
public void updateLessonName(String lessonName) {
if (lessonName != null && !lessonName.trim().isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.threestar.trainus.domain.lesson.teacher.repository;

import java.util.Optional;

import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
Expand All @@ -12,6 +14,8 @@ public interface LessonParticipantRepository extends CrudRepository<LessonPartic

boolean existsByLessonIdAndUserId(Long lessonId, Long userId);

Optional<LessonParticipant> findByLessonIdAndUserId(Long lessonId, Long userId);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사용되는 부분을 추후에 공통 로직으로 빼야할지 고민해봐야겠군요!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 추후에 리팩토링 진행해봐도 좋을 것 같습니다!


long countByLessonId(Long lessonId);

@Modifying
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,33 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.threestar.trainus.domain.payment.dto.CancelPaymentRequestDto;
import com.threestar.trainus.domain.payment.dto.ConfirmPaymentRequestDto;
import com.threestar.trainus.domain.payment.dto.PaymentClient;
import com.threestar.trainus.domain.payment.dto.PaymentRequestDto;
import com.threestar.trainus.domain.payment.dto.PaymentResponseDto;
import com.threestar.trainus.domain.payment.dto.SaveAmountRequestDto;
import com.threestar.trainus.domain.payment.dto.TossPaymentResponseDto;
import com.threestar.trainus.domain.payment.dto.failure.FailurePaymentResponseDto;
import com.threestar.trainus.domain.payment.dto.failure.PaymentFailureHistoryPageDto;
import com.threestar.trainus.domain.payment.dto.failure.PaymentFailurePageWrapperDto;
import com.threestar.trainus.domain.payment.dto.cancel.CancelPaymentRequestDto;
import com.threestar.trainus.domain.payment.dto.cancel.CancelPaymentResponseDto;
import com.threestar.trainus.domain.payment.dto.cancel.PaymentCancelHistoryPageDto;
import com.threestar.trainus.domain.payment.dto.cancel.PaymentCancelPageWrapperDto;
import com.threestar.trainus.domain.payment.dto.success.PaymentSuccessHistoryPageDto;
import com.threestar.trainus.domain.payment.dto.success.PaymentSuccessPageWrapperDto;
import com.threestar.trainus.domain.payment.dto.success.SuccessfulPaymentResponseDto;
import com.threestar.trainus.domain.payment.mapper.PaymentMapper;
import com.threestar.trainus.domain.payment.service.PaymentService;
import com.threestar.trainus.global.annotation.LoginUser;
import com.threestar.trainus.global.unit.BaseResponse;
import com.threestar.trainus.global.unit.PagedResponse;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpSession;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

Expand All @@ -42,33 +41,32 @@
@RequiredArgsConstructor
public class PaymentController {

private final PaymentClient tossPaymentClient;
private final PaymentService paymentService;

@Value("${spring.page.size.limit}")
private int pageSizeLimit;

@PostMapping("/prepare")
@Operation(summary = "결제 준비", description = "실제 결제 전 최종 가격 적용 후 결제 준비")
public ResponseEntity<BaseResponse<PaymentResponseDto>> preparePayment(HttpSession session,
@RequestBody PaymentRequestDto request) {
Long userId = (Long)session.getAttribute("LOGIN_USER");
public ResponseEntity<BaseResponse<PaymentResponseDto>> preparePayment(
@Valid @RequestBody PaymentRequestDto request,
@LoginUser Long userId) {
PaymentResponseDto response = paymentService.preparePayment(request, userId);
return BaseResponse.ok("결제 정보 준비 완료", response, HttpStatus.OK);
}

@PostMapping("/saveAmount")
@Operation(summary = "결제 검증 데이터 저장", description = "결제 무결성 검증을 위한 데이터 저장")
public ResponseEntity<BaseResponse<Void>> saveAmount(HttpSession session,
@RequestBody SaveAmountRequestDto request) {
@Valid @RequestBody SaveAmountRequestDto request) {
session.setAttribute(request.orderId(), request.amount());
return BaseResponse.ok("Payment temp save Successful", null, HttpStatus.OK);
}

@PostMapping("/verifyAmount")
@Operation(summary = "결제 검증 데이터 확인", description = "결제 무결성 검증을 위한 데이터 확인")
public ResponseEntity<BaseResponse<Void>> verifyAmount(HttpSession session,
@RequestBody SaveAmountRequestDto request) {
@Valid @RequestBody SaveAmountRequestDto request) {
Integer amount = (Integer)session.getAttribute(request.orderId());
try {
if (amount == null || !amount.equals(request.amount())) {
Expand All @@ -83,27 +81,23 @@ public ResponseEntity<BaseResponse<Void>> verifyAmount(HttpSession session,
@PostMapping("/confirm")
@Operation(summary = "결제 진행", description = "결제 진행")
public ResponseEntity<BaseResponse<SuccessfulPaymentResponseDto>> confirm(
@RequestBody ConfirmPaymentRequestDto request) {
TossPaymentResponseDto tossResponse = tossPaymentClient.confirmPayment(request);
SuccessfulPaymentResponseDto payResult = paymentService.processConfirm(tossResponse);
return BaseResponse.ok("결제 성공", payResult, HttpStatus.OK);
@Valid @RequestBody ConfirmPaymentRequestDto request) {
return BaseResponse.ok("결제 성공", paymentService.processConfirm(request), HttpStatus.OK);
}

@PostMapping("/cancel")
@Operation(summary = "결제 취소", description = "결제 취소")
public ResponseEntity<BaseResponse<FailurePaymentResponseDto>> cancel(
@RequestBody CancelPaymentRequestDto request) {
TossPaymentResponseDto tossResponse = tossPaymentClient.cancelPayment(request);
FailurePaymentResponseDto payResult = paymentService.processCancel(tossResponse, request.cancelReason());
return BaseResponse.ok("결제 취소 성공", payResult, HttpStatus.OK);
public ResponseEntity<BaseResponse<CancelPaymentResponseDto>> cancel(
@Valid @RequestBody CancelPaymentRequestDto request) {
return BaseResponse.ok("결제 취소 성공", paymentService.processCancel(request), HttpStatus.OK);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

더 깔끔한 응답 형태 반환인 것 같습니다! 참고해보겠습니다

}

@GetMapping("/view/success")
@Operation(summary = "완료 결제 조회", description = "완료된 결제 내역 조회")
public ResponseEntity<PagedResponse<PaymentSuccessPageWrapperDto>> readAll(HttpSession session,
public ResponseEntity<PagedResponse<PaymentSuccessPageWrapperDto>> readAll(
@RequestParam("page") int page,
@RequestParam("pageSize") int pageSize) {
Long userId = (Long)session.getAttribute("LOGIN_USER");
@RequestParam("pageSize") int pageSize,
@LoginUser Long userId) {
int correctPage = Math.max(page, 1);
int correctPageSize = Math.max(1, Math.min(pageSize, pageSizeLimit));
PaymentSuccessHistoryPageDto paymentSuccessHistoryPageDto = paymentService.viewAllSuccessTransaction(userId,
Expand All @@ -115,24 +109,16 @@ public ResponseEntity<PagedResponse<PaymentSuccessPageWrapperDto>> readAll(HttpS

@GetMapping("/view/cancel")
@Operation(summary = "취소 결제 조회", description = "취소된 결제 내역 조회")
public ResponseEntity<PagedResponse<PaymentFailurePageWrapperDto>> readAllFailure(HttpSession session,
public ResponseEntity<PagedResponse<PaymentCancelPageWrapperDto>> readAllFailure(HttpSession session,
@RequestParam("page") int page,
@RequestParam("pageSize") int pageSize) {
Long userId = (Long)session.getAttribute("LOGIN_USER");
@RequestParam("pageSize") int pageSize,
@LoginUser Long userId) {
int correctPage = Math.max(page, 1);
int correctPageSize = Math.max(1, Math.min(pageSize, pageSizeLimit));
PaymentFailureHistoryPageDto paymentFailureHistoryPageDto = paymentService.viewAllFailureTransaction(userId,
PaymentCancelHistoryPageDto paymentCancelHistoryPageDto = paymentService.viewAllFailureTransaction(userId,
correctPage, correctPageSize);
PaymentFailurePageWrapperDto payments = PaymentMapper.toPaymentFailurePageWrapperDto(
paymentFailureHistoryPageDto);
return PagedResponse.ok("취소 결제 조회 성공", payments, paymentFailureHistoryPageDto.count(), HttpStatus.OK);
PaymentCancelPageWrapperDto payments = PaymentMapper.toPaymentFailurePageWrapperDto(
paymentCancelHistoryPageDto);
return PagedResponse.ok("취소 결제 조회 성공", payments, paymentCancelHistoryPageDto.count(), HttpStatus.OK);
}

@GetMapping("/{paymentKey}")
@Operation(summary = "상세 결제 조회", description = "상세 결제 내역 조회")
public ResponseEntity<BaseResponse<TossPaymentResponseDto>> readDetailPayment(
@PathVariable("paymentKey") String paymentKey) {
return BaseResponse.ok("상세 결제 조회 완료", tossPaymentClient.viewDetailPayment(paymentKey), HttpStatus.OK);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;

import com.threestar.trainus.domain.payment.dto.cancel.TossCancelRequestDto;
import com.threestar.trainus.global.exception.domain.ErrorCode;
import com.threestar.trainus.global.exception.handler.BusinessException;

Expand Down Expand Up @@ -50,7 +51,7 @@ public TossPaymentResponseDto confirmPayment(ConfirmPaymentRequestDto request) {
.body(TossPaymentResponseDto.class);
}

public TossPaymentResponseDto cancelPayment(CancelPaymentRequestDto request) {
public TossPaymentResponseDto cancelPayment(TossCancelRequestDto request) {
return restClient.post()
.uri(String.format(paymentProperties.getCancelEndPoint(), request.paymentKey()))
.contentType(MediaType.APPLICATION_JSON)
Expand All @@ -61,15 +62,4 @@ public TossPaymentResponseDto cancelPayment(CancelPaymentRequestDto request) {
})
.body(TossPaymentResponseDto.class);
}

public TossPaymentResponseDto viewDetailPayment(String paymentKey) {
return restClient.get()
.uri(paymentProperties.getViewEndPoint() + "/" + paymentKey)
.retrieve()
.onStatus(HttpStatusCode::isError, (req, res) -> {
throw new BusinessException(ErrorCode.INVALID_PAYMENT);
})
.body(TossPaymentResponseDto.class);
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.threestar.trainus.domain.payment.dto;

import java.util.List;

import com.threestar.trainus.domain.payment.dto.cancel.CancelDetail;

public record TossPaymentResponseDto(
String paymentKey,
String orderId,
Expand All @@ -8,6 +12,7 @@ public record TossPaymentResponseDto(
String requestedAt,
String approvedAt,
int totalAmount,
String method
String method,
List<CancelDetail> cancels
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.threestar.trainus.domain.payment.dto.cancel;

public record CancelDetail(
String cancelReason,
String canceledAt,
int cancelAmount,
int refundableAmount
) {
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.threestar.trainus.domain.payment.dto;
package com.threestar.trainus.domain.payment.dto.cancel;

import jakarta.validation.constraints.NotNull;

public record CancelPaymentRequestDto(
@NotNull(message = "필수 값입니다.")
String paymentKey,
String orderId,
@NotNull(message = "실패 원인은 필수입니다.")
String cancelReason
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package com.threestar.trainus.domain.payment.dto.failure;
package com.threestar.trainus.domain.payment.dto.cancel;

import java.time.LocalDateTime;

import lombok.Builder;

@Builder
public record FailurePaymentResponseDto(
public record CancelPaymentResponseDto(
String lessonName,
String cancelReason,
LocalDateTime startAt,
LocalDateTime endAt,
LocalDateTime paymentCancelledAt,
int payPrice
int payPrice,
int refundableAmount
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.threestar.trainus.domain.payment.dto.cancel;

import java.util.List;

import lombok.Builder;

@Builder
public record PaymentCancelHistoryPageDto(
Integer count,
List<PaymentCancelHistoryResponseDto> failureHistory
) {
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.threestar.trainus.domain.payment.dto.failure;
package com.threestar.trainus.domain.payment.dto.cancel;

import java.time.LocalDateTime;

Expand All @@ -8,7 +8,7 @@
import lombok.Builder;

@Builder
public record PaymentFailureHistoryResponseDto(
public record PaymentCancelHistoryResponseDto(
String lessonTitle,
LocalDateTime paymentCancelledAt,
LocalDateTime lessonStartAt,
Expand All @@ -17,9 +17,9 @@ public record PaymentFailureHistoryResponseDto(
String city,
String district,
String dong,
Integer originalPrice,
Integer payPrice,
Integer refundPrice,
PaymentStatus paymentStatus,
String paymentKey,
String orderId,
String detailAddress,
String cancelReason
Expand Down
Loading