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,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;
Expand All @@ -24,7 +23,7 @@ public ResponseEntity<PaymentResponse> 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()));
Expand Down
257 changes: 129 additions & 128 deletions src/main/java/com/dangsim/pg/service/PaymentGatewayService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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<String, String> 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<String> entity = new HttpEntity<>(requestBody, headers);

ResponseEntity<PortOneTokenResponse> 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<Void> entity = new HttpEntity<>(headers);

ResponseEntity<InicisResponse> 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<String, String> 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<String> entity = new HttpEntity<>(requestBody, headers);

ResponseEntity<PortOneTokenResponse> 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<Void> entity = new HttpEntity<>(headers);

ResponseEntity<InicisResponse> 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);
}
}
2 changes: 1 addition & 1 deletion src/main/java/com/dangsim/task/service/TaskService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
10 changes: 5 additions & 5 deletions src/test/java/com/dangsim/task/service/TaskServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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("수행자를 매칭할 때 심부름 상태가 완료 상태라면 예외가 발생한다.")
Expand Down