diff --git a/src/main/java/com/dangsim/pg/controller/PaymentGatewayController.java b/src/main/java/com/dangsim/pg/controller/PaymentGatewayController.java index 00a7b95..162cfd4 100644 --- a/src/main/java/com/dangsim/pg/controller/PaymentGatewayController.java +++ b/src/main/java/com/dangsim/pg/controller/PaymentGatewayController.java @@ -1,7 +1,6 @@ package com.dangsim.pg.controller; import org.springframework.http.ResponseEntity; -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.RestController; @@ -24,7 +23,7 @@ public ResponseEntity completePayment(@RequestBody PortOneRespo paymentGatewayService.verifyPaymentDetail(portOneResponseDto.getImpUid(), portOneResponseDto.getMerchantUid()); // 결제 성공 시 결제 및 테스크 상태 업데이트 -// paymentGatewayService.updatePaymentAndTaskStatus(portOneResponseDto.getMerchantUid()); + paymentGatewayService.updatePaymentAndTaskStatus(portOneResponseDto.getMerchantUid()); return ResponseEntity.ok() .body(new PaymentResponse(true, "결제 및 검증 성공", portOneResponseDto.getTaskId())); diff --git a/src/main/java/com/dangsim/pg/service/PaymentGatewayService.java b/src/main/java/com/dangsim/pg/service/PaymentGatewayService.java index c2f2bd3..521e875 100644 --- a/src/main/java/com/dangsim/pg/service/PaymentGatewayService.java +++ b/src/main/java/com/dangsim/pg/service/PaymentGatewayService.java @@ -6,8 +6,6 @@ import java.util.HashMap; import java.util.Map; -import com.dangsim.payment.entity.PaymentStatus; -import com.dangsim.task.entity.TaskStatus; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; @@ -20,6 +18,8 @@ import com.dangsim.common.exception.runtime.BaseException; import com.dangsim.payment.entity.Payment; +import com.dangsim.payment.entity.PaymentStatus; +import com.dangsim.payment.exception.PaymentErrorCode; import com.dangsim.payment.repository.PaymentRepository; import com.dangsim.pg.dto.InicisResponse; import com.dangsim.pg.dto.PortOneTokenResponse; @@ -36,130 +36,131 @@ @RequiredArgsConstructor public class PaymentGatewayService { - @Value("${portone.imp_key}") - private String apiKey; - - @Value("${portone.imp_secret}") - private String apiSecret; - - private final RestTemplate restTemplate; - - private final PaymentRepository paymentRepository; - private final PaymentGatewayRepository paymentGatewayRepository; - - public String getAccessToken() { - String url = "https://api.iamport.kr/users/getToken"; // 포트원 토큰 발급 API URL : 여기에 post 요청 - - Map body = new HashMap<>(); // 서버에 보낼 데이터를 담을 Map - body.put("imp_key", apiKey); - body.put("imp_secret", apiSecret); - - // http 요청 body에 json을 보내기 위해 Java 객체를 JSON 문자열로 변환 - // portone api는 json 형태를 요구하기 때문에 body를 json 형태로 변환 - ObjectMapper objectMapper = new ObjectMapper(); - String requestBody; - try { - requestBody = objectMapper.writeValueAsString(body); - } catch (Exception e) { - throw new BaseException(PaymentGatewayErrorCode.JSON_CONVERT_FAILED); - } - - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity entity = new HttpEntity<>(requestBody, headers); - - ResponseEntity response = restTemplate.postForEntity(url, entity, PortOneTokenResponse.class); - - PortOneTokenResponse responseBody = response.getBody(); - if (responseBody == null || responseBody.getCode() != 0) { - throw new BaseException(PaymentGatewayErrorCode.PAYMENT_VERIFICATION_FAILED); - } - - return response.getBody().getResponse().getAccess_token(); - } - - @Transactional - public void verifyPaymentDetail(String impUid, String merchantUid) { - String token = getAccessToken(); - - String PORTONE_PAYMENT_LOOKUP_URL = "https://api.iamport.kr/payments/"; - String url = PORTONE_PAYMENT_LOOKUP_URL + impUid; - - HttpHeaders headers = new HttpHeaders(); - headers.setBearerAuth(token); - HttpEntity entity = new HttpEntity<>(headers); - - ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, entity, InicisResponse.class); - - InicisResponse.Response paymentData = response.getBody().getResponse(); - - BigDecimal portOneAmount = BigDecimal.valueOf(paymentData.getAmount()); - - Payment payment = paymentRepository.findByMerchantUid(merchantUid) -// Payment payment = paymentRepository.findByMerchantUid(paymentData.getMerchant_uid()) - .orElseThrow(() -> new BaseException(PaymentGatewayErrorCode.PAYMENT_NOT_FOUND)); - - PaymentGateway paymentGateway = PaymentGateway.of( - paymentData.getImp_uid(), -// paymentData.getMerchant_uid(), - merchantUid, - paymentData.getPay_method(), - paymentData.getPg_provider(), - paymentData.getPg_tid(), - paymentData.getPg_id(), - BigDecimal.valueOf(paymentData.getAmount()), - paymentData.getCurrency(), - paymentData.getApply_num(), - paymentData.getBuyer_name(), - paymentData.getCard_code(), - paymentData.getCard_name(), - paymentData.getCard_quota(), - paymentData.getCard_number(), - PaymentGatewayStatus.valueOf(paymentData.getStatus().toUpperCase()), - paymentData.getCard_type(), - parseDateTimePG(paymentData.getStarted_at()), - parseDateTimePG(paymentData.getPaid_at()), - paymentData.getCanceled_at() == null ? null : parseDateTimePG(paymentData.getCanceled_at()), - parseDateTimePG(paymentData.getFailed_at()), - payment - ); - - validatePaymentAmount(paymentGateway, portOneAmount); - paymentGatewayRepository.save(paymentGateway); - } - - public void validatePaymentAmount(PaymentGateway paymentGateway, BigDecimal portOneAmount) { - if (paymentGateway == null) { - throw new IllegalArgumentException("PaymentGateway 정보가 없습니다."); - } - - Payment payment = paymentGateway.getPayment(); - if (payment == null) { - throw new IllegalArgumentException("PaymentGateway에 연결된 Payment 정보가 없습니다."); - } - - Task task = payment.getTask(); - if (task == null) { - throw new IllegalArgumentException("Payment에 연결된 Task 정보가 없습니다."); - } - - BigDecimal taskReward = task.getReward(); - if (taskReward == null || portOneAmount == null) { - throw new IllegalArgumentException("Task 리워드 값 또는 결제 금액이 null입니다."); - } - - if (taskReward.compareTo(portOneAmount) != 0) { - throw new IllegalArgumentException("Task 리워드 금액과 결제 금액이 일치하지 않습니다."); - } - } - - @Transactional - public void updatePaymentAndTaskStatus(String merchantUid) { - Payment payment = paymentRepository.findByMerchantUid(merchantUid) - .orElseThrow(() -> new IllegalArgumentException("해당 merchantUid의 결제를 찾을 수 없습니다.")); - - // 상태 업데이트 - payment.updatePaymentSuccessStatus(PaymentStatus.PAYMENT_SUCCESSES); - payment.getTask().updateStatus(TaskStatus.TASK_IN_PROGRESS); - } + @Value("${portone.imp_key}") + private String apiKey; + + @Value("${portone.imp_secret}") + private String apiSecret; + + private final RestTemplate restTemplate; + + private final PaymentRepository paymentRepository; + private final PaymentGatewayRepository paymentGatewayRepository; + + public String getAccessToken() { + String url = "https://api.iamport.kr/users/getToken"; // 포트원 토큰 발급 API URL : 여기에 post 요청 + + Map body = new HashMap<>(); // 서버에 보낼 데이터를 담을 Map + body.put("imp_key", apiKey); + body.put("imp_secret", apiSecret); + + // http 요청 body에 json을 보내기 위해 Java 객체를 JSON 문자열로 변환 + // portone api는 json 형태를 요구하기 때문에 body를 json 형태로 변환 + ObjectMapper objectMapper = new ObjectMapper(); + String requestBody; + try { + requestBody = objectMapper.writeValueAsString(body); + } catch (Exception e) { + throw new BaseException(PaymentGatewayErrorCode.JSON_CONVERT_FAILED); + } + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity<>(requestBody, headers); + + ResponseEntity response = restTemplate.postForEntity(url, entity, + PortOneTokenResponse.class); + + PortOneTokenResponse responseBody = response.getBody(); + if (responseBody == null || responseBody.getCode() != 0) { + throw new BaseException(PaymentGatewayErrorCode.PAYMENT_VERIFICATION_FAILED); + } + + return response.getBody().getResponse().getAccess_token(); + } + + @Transactional + public void verifyPaymentDetail(String impUid, String merchantUid) { + String token = getAccessToken(); + + String PORTONE_PAYMENT_LOOKUP_URL = "https://api.iamport.kr/payments/"; + String url = PORTONE_PAYMENT_LOOKUP_URL + impUid; + + HttpHeaders headers = new HttpHeaders(); + headers.setBearerAuth(token); + HttpEntity entity = new HttpEntity<>(headers); + + ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, entity, + InicisResponse.class); + + InicisResponse.Response paymentData = response.getBody().getResponse(); + + BigDecimal portOneAmount = BigDecimal.valueOf(paymentData.getAmount()); + + Payment payment = paymentRepository.findByMerchantUid(merchantUid) + // Payment payment = paymentRepository.findByMerchantUid(paymentData.getMerchant_uid()) + .orElseThrow(() -> new BaseException(PaymentGatewayErrorCode.PAYMENT_NOT_FOUND)); + + PaymentGateway paymentGateway = PaymentGateway.of( + paymentData.getImp_uid(), + // paymentData.getMerchant_uid(), + merchantUid, + paymentData.getPay_method(), + paymentData.getPg_provider(), + paymentData.getPg_tid(), + paymentData.getPg_id(), + BigDecimal.valueOf(paymentData.getAmount()), + paymentData.getCurrency(), + paymentData.getApply_num(), + paymentData.getBuyer_name(), + paymentData.getCard_code(), + paymentData.getCard_name(), + paymentData.getCard_quota(), + paymentData.getCard_number(), + PaymentGatewayStatus.valueOf(paymentData.getStatus().toUpperCase()), + paymentData.getCard_type(), + parseDateTimePG(paymentData.getStarted_at()), + parseDateTimePG(paymentData.getPaid_at()), + paymentData.getCanceled_at() == null ? null : parseDateTimePG(paymentData.getCanceled_at()), + parseDateTimePG(paymentData.getFailed_at()), + payment + ); + + validatePaymentAmount(paymentGateway, portOneAmount); + paymentGatewayRepository.save(paymentGateway); + } + + public void validatePaymentAmount(PaymentGateway paymentGateway, BigDecimal portOneAmount) { + if (paymentGateway == null) { + throw new IllegalArgumentException("PaymentGateway 정보가 없습니다."); + } + + Payment payment = paymentGateway.getPayment(); + if (payment == null) { + throw new IllegalArgumentException("PaymentGateway에 연결된 Payment 정보가 없습니다."); + } + + Task task = payment.getTask(); + if (task == null) { + throw new IllegalArgumentException("Payment에 연결된 Task 정보가 없습니다."); + } + + BigDecimal taskReward = task.getReward(); + if (taskReward == null || portOneAmount == null) { + throw new IllegalArgumentException("Task 리워드 값 또는 결제 금액이 null입니다."); + } + + if (taskReward.compareTo(portOneAmount) != 0) { + throw new IllegalArgumentException("Task 리워드 금액과 결제 금액이 일치하지 않습니다."); + } + } + + @Transactional + public void updatePaymentAndTaskStatus(String merchantUid) { + Payment payment = paymentRepository.findByMerchantUid(merchantUid) + .orElseThrow(() -> new BaseException(PaymentErrorCode.NOT_FOUND_PAYMENT)); + + // 상태 업데이트 + payment.updatePaymentSuccessStatus(PaymentStatus.PAYMENT_SUCCESSES); + } } \ No newline at end of file diff --git a/src/main/java/com/dangsim/task/service/TaskService.java b/src/main/java/com/dangsim/task/service/TaskService.java index d491bb3..85f4729 100644 --- a/src/main/java/com/dangsim/task/service/TaskService.java +++ b/src/main/java/com/dangsim/task/service/TaskService.java @@ -55,7 +55,7 @@ public TaskResponseDto createTask(TaskRequestDto requestDto, User user) { String merchantUid = IdentifierUtils.generateMerchantUid(saveTask.getId(), LocalDateTime.now()); - paymentRepository.save(Payment.of(saveTask.getReward(), PAYMENT_SUCCESSES, merchantUid, saveTask, user)); + paymentRepository.save(Payment.of(saveTask.getReward(), PAYMENT_WAITING, merchantUid, saveTask, user)); return TaskMapper.toTaskResponseDto(saveTask, merchantUid); } diff --git a/src/test/java/com/dangsim/task/service/TaskServiceTest.java b/src/test/java/com/dangsim/task/service/TaskServiceTest.java index 8cb8a84..8498509 100644 --- a/src/test/java/com/dangsim/task/service/TaskServiceTest.java +++ b/src/test/java/com/dangsim/task/service/TaskServiceTest.java @@ -31,7 +31,6 @@ import com.dangsim.common.fixture.UserFixture; import com.dangsim.common.util.DateTimeFormatUtils; import com.dangsim.payment.entity.Payment; -import com.dangsim.payment.entity.PaymentStatus; import com.dangsim.payment.exception.PaymentErrorCode; import com.dangsim.payment.repository.PaymentRepository; import com.dangsim.pg.repository.PaymentGatewayRepository; @@ -325,20 +324,21 @@ void updatePaymentAndTaskStatus_successfullyUpdatesStatuses() { User requester = UserFixture.user(Role.USER, BigDecimal.ZERO); ReflectionTestUtils.setField(requester, "id", 1L); + User performer = UserFixture.user(Role.USER, BigDecimal.ZERO); + ReflectionTestUtils.setField(requester, "id", 2L); Task task = TaskFixture.task(title, content, requester); + ReflectionTestUtils.setField(task, "id", 1L); ReflectionTestUtils.setField(task, "status", TaskStatus.TASK_NOT_ASSIGNED); - Payment payment = mock(Payment.class); + Payment payment = PaymentFixture.payment(task, requester, performer); given(paymentRepository.findByMerchantUid(merchantUid)).willReturn(Optional.of(payment)); - given(payment.getTask()).willReturn(task); // when paymentGatewayService.updatePaymentAndTaskStatus(merchantUid); // then - verify(payment).updatePaymentSuccessStatus(PaymentStatus.PAYMENT_SUCCESSES); - assertThat(task.getStatus()).isEqualTo(TaskStatus.TASK_IN_PROGRESS); + assertThat(payment.getStatus()).isEqualTo(PAYMENT_SUCCESSES); } @DisplayName("수행자를 매칭할 때 심부름 상태가 완료 상태라면 예외가 발생한다.")