diff --git a/src/main/java/com/readyvery/readyverydemo/domain/Order.java b/src/main/java/com/readyvery/readyverydemo/domain/Order.java index c4f20df..c438386 100644 --- a/src/main/java/com/readyvery/readyverydemo/domain/Order.java +++ b/src/main/java/com/readyvery/readyverydemo/domain/Order.java @@ -103,4 +103,20 @@ public class Order extends BaseTimeEntity { @OneToOne(mappedBy = "order", fetch = FetchType.LAZY) private Receipt receipt; + + public void completeOrder(Progress progress) { + this.progress = progress; + } + + public void cancelOrder(Progress progress) { + this.progress = progress; + } + + public void orderTime(Long time) { + this.estimatedTime = LocalDateTime.now().plusMinutes(time); + } + + public void cancelPayStatus() { + this.payStatus = false; + } } diff --git a/src/main/java/com/readyvery/readyverydemo/domain/repository/OrderRepository.java b/src/main/java/com/readyvery/readyverydemo/domain/repository/OrderRepository.java index 4bc7192..25bb2db 100644 --- a/src/main/java/com/readyvery/readyverydemo/domain/repository/OrderRepository.java +++ b/src/main/java/com/readyvery/readyverydemo/domain/repository/OrderRepository.java @@ -1,6 +1,7 @@ package com.readyvery.readyverydemo.domain.repository; import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; @@ -11,4 +12,6 @@ public interface OrderRepository extends JpaRepository { List findAllById(Long id); List findAllByProgressAndStoreId(Progress progress, Long storeId); + + Optional findByOrderId(String orderId); } diff --git a/src/main/java/com/readyvery/readyverydemo/global/exception/ExceptionCode.java b/src/main/java/com/readyvery/readyverydemo/global/exception/ExceptionCode.java index e4af861..20e03bc 100644 --- a/src/main/java/com/readyvery/readyverydemo/global/exception/ExceptionCode.java +++ b/src/main/java/com/readyvery/readyverydemo/global/exception/ExceptionCode.java @@ -15,7 +15,11 @@ public enum ExceptionCode { NOT_FOUND_STORE(404, "Not found store."), NOT_LOGIN_USER(401, "Not login user."), NOT_FOUND_ORDER(404, "Not found order."), - NOT_PROGRESS_ORDER(400, "Not progress order."); + NOT_PROGRESS_ORDER(400, "Not progress order."), + NOT_CHANGE_ORDER(400, "Not change order."), + NOT_FOUND_TIME(404, "Not found time."), + TOSS_PAYMENT_SUCCESS_FAIL(400, "Toss payment success fail."), + NOT_FOUND_REJECT_REASON(404, "Not found reject reason."); private int status; private String message; diff --git a/src/main/java/com/readyvery/readyverydemo/src/order/OrderController.java b/src/main/java/com/readyvery/readyverydemo/src/order/OrderController.java index 162959f..12e0b5d 100644 --- a/src/main/java/com/readyvery/readyverydemo/src/order/OrderController.java +++ b/src/main/java/com/readyvery/readyverydemo/src/order/OrderController.java @@ -2,6 +2,8 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.GetMapping; +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; @@ -9,6 +11,8 @@ import com.readyvery.readyverydemo.domain.Progress; import com.readyvery.readyverydemo.security.jwt.dto.CustomUserDetails; import com.readyvery.readyverydemo.src.order.dto.OrderRegisterRes; +import com.readyvery.readyverydemo.src.order.dto.OrderStatusRes; +import com.readyvery.readyverydemo.src.order.dto.OrderStatusUpdateReq; import lombok.RequiredArgsConstructor; @@ -22,6 +26,18 @@ public class OrderController { @GetMapping("/order") public OrderRegisterRes getOrder(@AuthenticationPrincipal CustomUserDetails userDetails, @RequestParam(required = false) Progress status) { - return orderServiceImpl.getOrder(userDetails.getId(), status); + return orderServiceImpl.getOrders(userDetails.getId(), status); + } + + @PostMapping("/order/complete") + public OrderStatusRes completeOrder(@AuthenticationPrincipal CustomUserDetails userDetails, + @RequestBody OrderStatusUpdateReq request) { + return orderServiceImpl.completeOrder(userDetails.getId(), request); + } + + @PostMapping("/order/cancel") + public OrderStatusRes cancelOrder(@AuthenticationPrincipal CustomUserDetails userDetails, + @RequestBody OrderStatusUpdateReq request) { + return orderServiceImpl.cancelOrder(userDetails.getId(), request); } } diff --git a/src/main/java/com/readyvery/readyverydemo/src/order/OrderService.java b/src/main/java/com/readyvery/readyverydemo/src/order/OrderService.java index 5c936fa..cf66194 100644 --- a/src/main/java/com/readyvery/readyverydemo/src/order/OrderService.java +++ b/src/main/java/com/readyvery/readyverydemo/src/order/OrderService.java @@ -1,9 +1,18 @@ package com.readyvery.readyverydemo.src.order; +import com.readyvery.readyverydemo.domain.Order; import com.readyvery.readyverydemo.domain.Progress; import com.readyvery.readyverydemo.src.order.dto.OrderRegisterRes; +import com.readyvery.readyverydemo.src.order.dto.OrderStatusRes; +import com.readyvery.readyverydemo.src.order.dto.OrderStatusUpdateReq; public interface OrderService { - OrderRegisterRes getOrder(Long id, Progress progress); + OrderRegisterRes getOrders(Long id, Progress progress); + + OrderStatusRes completeOrder(Long id, OrderStatusUpdateReq request); + + OrderStatusRes cancelOrder(Long id, OrderStatusUpdateReq request); + + void cancelTossPayment(Order order, OrderStatusUpdateReq request); } diff --git a/src/main/java/com/readyvery/readyverydemo/src/order/OrderServiceImpl.java b/src/main/java/com/readyvery/readyverydemo/src/order/OrderServiceImpl.java index 65eba71..995c1a3 100644 --- a/src/main/java/com/readyvery/readyverydemo/src/order/OrderServiceImpl.java +++ b/src/main/java/com/readyvery/readyverydemo/src/order/OrderServiceImpl.java @@ -1,8 +1,18 @@ package com.readyvery.readyverydemo.src.order; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Collections; import java.util.List; +import java.util.Objects; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import net.minidev.json.JSONObject; import com.readyvery.readyverydemo.domain.CeoInfo; import com.readyvery.readyverydemo.domain.Order; @@ -11,8 +21,12 @@ import com.readyvery.readyverydemo.domain.repository.OrderRepository; import com.readyvery.readyverydemo.global.exception.BusinessLogicException; import com.readyvery.readyverydemo.global.exception.ExceptionCode; +import com.readyvery.readyverydemo.src.order.config.TossPaymentConfig; import com.readyvery.readyverydemo.src.order.dto.OrderMapper; import com.readyvery.readyverydemo.src.order.dto.OrderRegisterRes; +import com.readyvery.readyverydemo.src.order.dto.OrderStatusRes; +import com.readyvery.readyverydemo.src.order.dto.OrderStatusUpdateReq; +import com.readyvery.readyverydemo.src.order.dto.TosspaymentDto; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -25,9 +39,10 @@ public class OrderServiceImpl implements OrderService { private final OrderRepository orderRepository; private final OrderMapper orderMapper; private final CeoRepository ceoRepository; + private final TossPaymentConfig tosspaymentConfig; @Override - public OrderRegisterRes getOrder(Long id, Progress progress) { + public OrderRegisterRes getOrders(Long id, Progress progress) { CeoInfo ceoInfo = getCeoInfo(id); if (progress == null) { @@ -35,17 +50,123 @@ public OrderRegisterRes getOrder(Long id, Progress progress) { } List orders = orderRepository.findAllByProgressAndStoreId(progress, ceoInfo.getStore().getId()); - log.info("progress: {}", orders); + if (orders.isEmpty()) { throw new BusinessLogicException(ExceptionCode.NOT_FOUND_ORDER); } return orderMapper.orderToOrderRegisterRes(orders); } + @Override + public OrderStatusRes completeOrder(Long id, OrderStatusUpdateReq request) { + CeoInfo ceoInfo = getCeoInfo(id); + Order order = getOrder(request.getOrderId()); + + verifyPostOrder(ceoInfo, order); + verifyPostProgress(order, request); + order.completeOrder(request.getStatus()); + orderRepository.save(order); + return OrderStatusRes.builder() + .success(true) + .build(); + } + + @Override + public OrderStatusRes cancelOrder(Long id, OrderStatusUpdateReq request) { + CeoInfo ceoInfo = getCeoInfo(id); + Order order = getOrder(request.getOrderId()); + + verifyPostOrder(ceoInfo, order); + verifyPostProgress(order, request); + //order.cancelOrder(request.getStatus()); + orderRepository.save(order); + return OrderStatusRes.builder() + .success(true) + .build(); + } + + @Override + public void cancelTossPayment(Order order, OrderStatusUpdateReq request) { + + TosspaymentDto tosspaymentDto = requestTossPaymentCancel(order.getPaymentKey(), request.getRejectReason()); + + applyCancelTosspaymentDto(order, tosspaymentDto); + + } + + private void verifyPostProgress(Order order, OrderStatusUpdateReq request) { + if (order.getProgress() == Progress.ORDER && request.getStatus() == Progress.MAKE) { // 주문 -> 제조 + if (request.getTime() == null) { + throw new BusinessLogicException(ExceptionCode.NOT_FOUND_TIME); + } + order.orderTime(request.getTime()); + return; + } else if (order.getProgress() == Progress.ORDER && request.getStatus() == Progress.CANCEL) { + if (request.getRejectReason() == null) { + throw new BusinessLogicException(ExceptionCode.NOT_FOUND_REJECT_REASON); + } + cancelTossPayment(order, request); + return; + } else if (order.getProgress() == Progress.MAKE && request.getStatus() == Progress.COMPLETE) { // 제조 -> 완료 + return; + } else if (order.getProgress() == Progress.COMPLETE && request.getStatus() == Progress.PICKUP) { // 완료 -> 픽업 + return; + } else { + throw new BusinessLogicException(ExceptionCode.NOT_CHANGE_ORDER); + } + } + + private void verifyPostOrder(CeoInfo ceoInfo, Order order) { + if (Objects.equals(order.getStore(), ceoInfo.getStore())) { + return; + } + throw new BusinessLogicException(ExceptionCode.NOT_FOUND_ORDER); + } + + private Order getOrder(String orderId) { + return orderRepository.findByOrderId(orderId).orElseThrow( + () -> new BusinessLogicException(ExceptionCode.NOT_FOUND_ORDER) + ); + } + private CeoInfo getCeoInfo(Long id) { return ceoRepository.findById(id).orElseThrow( () -> new BusinessLogicException(ExceptionCode.USER_NOT_FOUND) ); } + private void applyCancelTosspaymentDto(Order order, TosspaymentDto tosspaymentDto) { + order.cancelOrder(Progress.CANCEL); + order.cancelPayStatus(); + order.getReceipt().setCancels(tosspaymentDto.getCancels().toString()); + order.getReceipt().setStatus(tosspaymentDto.getStatus()); + } + + private TosspaymentDto requestTossPaymentCancel(String paymentKey, String rejectReason) { + RestTemplate restTemplate = new RestTemplate(); + HttpHeaders headers = makeTossHeader(); + JSONObject params = new JSONObject(); + + params.put("cancelReason", rejectReason); + + try { + return restTemplate.postForObject(TossPaymentConfig.PAYMENT_URL + paymentKey + "/cancel", + new HttpEntity<>(params, headers), + TosspaymentDto.class); + } catch (Exception e) { + System.out.println("e.getMessage() = " + e.getMessage()); + throw new BusinessLogicException(ExceptionCode.TOSS_PAYMENT_SUCCESS_FAIL); + } + } + + private HttpHeaders makeTossHeader() { + HttpHeaders headers = new HttpHeaders(); + String encodedAuthKey = new String( + Base64.getEncoder().encode((tosspaymentConfig.getTossSecretKey() + ":").getBytes(StandardCharsets.UTF_8))); + headers.setBasicAuth(encodedAuthKey); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + return headers; + } + } diff --git a/src/main/java/com/readyvery/readyverydemo/src/order/config/TossPaymentConfig.java b/src/main/java/com/readyvery/readyverydemo/src/order/config/TossPaymentConfig.java new file mode 100644 index 0000000..0110a6f --- /dev/null +++ b/src/main/java/com/readyvery/readyverydemo/src/order/config/TossPaymentConfig.java @@ -0,0 +1,25 @@ +package com.readyvery.readyverydemo.src.order.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +import lombok.Getter; + +@Configuration +@Getter +public class TossPaymentConfig { + public static final String PAYMENT_URL = "https://api.tosspayments.com/v1/payments/"; + public static final String CONFIRM_URL = "https://api.tosspayments.com/v1/payments/confirm"; + + @Value("${payment.toss.client_key}") + private String tossClientKey; + + @Value("${payment.toss.secret_key}") + private String tossSecretKey; + + @Value("${payment.toss.success_url}") + private String tossSuccessUrl; + + @Value("${payment.toss.fail_url}") + private String tossFailUrl; +} diff --git a/src/main/java/com/readyvery/readyverydemo/src/order/dto/DefaultRes.java b/src/main/java/com/readyvery/readyverydemo/src/order/dto/DefaultRes.java new file mode 100644 index 0000000..20d995d --- /dev/null +++ b/src/main/java/com/readyvery/readyverydemo/src/order/dto/DefaultRes.java @@ -0,0 +1,10 @@ +package com.readyvery.readyverydemo.src.order.dto; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class DefaultRes { + private String message; +} diff --git a/src/main/java/com/readyvery/readyverydemo/src/order/dto/OrderCancelStatusUpdateReq.java b/src/main/java/com/readyvery/readyverydemo/src/order/dto/OrderCancelStatusUpdateReq.java new file mode 100644 index 0000000..d24d861 --- /dev/null +++ b/src/main/java/com/readyvery/readyverydemo/src/order/dto/OrderCancelStatusUpdateReq.java @@ -0,0 +1,15 @@ +package com.readyvery.readyverydemo.src.order.dto; + +import com.readyvery.readyverydemo.domain.Progress; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class OrderCancelStatusUpdateReq { + private String orderId; + private Progress status; + private String rejectReason; + +} diff --git a/src/main/java/com/readyvery/readyverydemo/src/order/dto/OrderDto.java b/src/main/java/com/readyvery/readyverydemo/src/order/dto/OrderDto.java index 7045f99..93f059e 100644 --- a/src/main/java/com/readyvery/readyverydemo/src/order/dto/OrderDto.java +++ b/src/main/java/com/readyvery/readyverydemo/src/order/dto/OrderDto.java @@ -11,6 +11,7 @@ public class OrderDto { private Long idx; private String orderNum; + private String orderId; private Long pickUp; private List foodies; private String phone; diff --git a/src/main/java/com/readyvery/readyverydemo/src/order/dto/OrderMapper.java b/src/main/java/com/readyvery/readyverydemo/src/order/dto/OrderMapper.java index c2333bf..914320f 100644 --- a/src/main/java/com/readyvery/readyverydemo/src/order/dto/OrderMapper.java +++ b/src/main/java/com/readyvery/readyverydemo/src/order/dto/OrderMapper.java @@ -23,7 +23,8 @@ public OrderRegisterRes orderToOrderRegisterRes(List order) { private OrderDto toOrderDto(Order order) { return OrderDto.builder() .idx(order.getId()) - .orderNum(order.getOrderId()) + .orderNum(order.getOrderNumber()) + .orderId(order.getOrderId()) .pickUp(order.getInOut()) .time(order.getCreatedAt()) .phone(order.getUserInfo().getPhone()) @@ -41,4 +42,10 @@ private FoodieDto cartItemToFoodieDto(CartItem cartItem) { cartItem.getCartOptions().stream().map(cartOption -> cartOption.getFoodieOption().getName()).toList()) .build(); } + + public DefaultRes tosspaymentDtoToCancelRes() { + return DefaultRes.builder() + .message("취소 성공") + .build(); + } } diff --git a/src/main/java/com/readyvery/readyverydemo/src/order/dto/OrderStatusRes.java b/src/main/java/com/readyvery/readyverydemo/src/order/dto/OrderStatusRes.java new file mode 100644 index 0000000..a48568f --- /dev/null +++ b/src/main/java/com/readyvery/readyverydemo/src/order/dto/OrderStatusRes.java @@ -0,0 +1,10 @@ +package com.readyvery.readyverydemo.src.order.dto; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class OrderStatusRes { + private boolean success; +} diff --git a/src/main/java/com/readyvery/readyverydemo/src/order/dto/OrderStatusUpdateReq.java b/src/main/java/com/readyvery/readyverydemo/src/order/dto/OrderStatusUpdateReq.java new file mode 100644 index 0000000..ebbe9ca --- /dev/null +++ b/src/main/java/com/readyvery/readyverydemo/src/order/dto/OrderStatusUpdateReq.java @@ -0,0 +1,15 @@ +package com.readyvery.readyverydemo.src.order.dto; + +import com.readyvery.readyverydemo.domain.Progress; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class OrderStatusUpdateReq { + private String orderId; + private Progress status; + private Long time; + private String rejectReason; +} diff --git a/src/main/java/com/readyvery/readyverydemo/src/order/dto/TosspaymentDto.java b/src/main/java/com/readyvery/readyverydemo/src/order/dto/TosspaymentDto.java new file mode 100644 index 0000000..3a4564c --- /dev/null +++ b/src/main/java/com/readyvery/readyverydemo/src/order/dto/TosspaymentDto.java @@ -0,0 +1,36 @@ +package com.readyvery.readyverydemo.src.order.dto; + +import lombok.Getter; + +@Getter +public class TosspaymentDto { + private String paymentKey; + private String type; + private String orderId; + private String orderName; + private String mid; + private String method; + private String currency; + private Long totalAmount; + private Long balanceAmount; + private Long suppliedAmount; + private String status; + private String requestedAt; + private String approvedAt; + private String lastTransactionKey; + private Long vat; + private Long taxFreeAmount; + private Long taxExemptionAmount; + private Object cancels; // JSON 문자열로 변경 + private Object card; // JSON 문자열로 변경 + private Object receipt; // JSON 문자열로 변경 + private Object checkout; // JSON 문자열로 변경 + private Object easyPay; // JSON 문자열로 변경 + private String country; + private Object failure; // JSON 문자열로 변경 + private Object discount; // JSON 문자열로 변경 + private Object virtualAccount; // JSON 문자열로 변경 + private Object transfer; // JSON 문자열로 변경 + private Object cashReceipt; // JSON 문자열로 변경 + private Object cashReceipts; // JSON 문자열로 변경 +}