Skip to content

Commit

Permalink
Merge pull request #21 from lotteon2/fix/develop
Browse files Browse the repository at this point in the history
fix: 배치 정기결제 로직 수정 및 정기결제 취소 API 추가
  • Loading branch information
qwerty1434 authored Jan 15, 2024
2 parents 8bcbc51 + 93fce7e commit 49ecd78
Show file tree
Hide file tree
Showing 14 changed files with 329 additions and 22 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.springframework.kafka:spring-kafka'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'io.github.lotteon-maven:blooming-blooms-utils:202401091420'
implementation 'io.github.lotteon-maven:blooming-blooms-utils:202401140749'
runtimeOnly 'com.h2database:h2'
implementation 'mysql:mysql-connector-java:8.0.33'
testImplementation 'org.mock-server:mockserver-netty:5.11.2' // 사용 중인 MockServer 버전
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package kr.bb.payment.controller.clientcontroller;

import bloomingblooms.domain.batch.SubscriptionBatchDtoList;
import bloomingblooms.domain.payment.KakaopayApproveRequestDto;
import bloomingblooms.domain.payment.KakaopayReadyRequestDto;
import bloomingblooms.domain.payment.KakaopayReadyResponseDto;
import bloomingblooms.domain.payment.PaymentInfoDto;
import bloomingblooms.response.CommonResponse;
import java.time.LocalDateTime;
import java.util.List;
import kr.bb.payment.dto.request.KakaopayCancelRequestDto;
import kr.bb.payment.service.KakaopayService;
import kr.bb.payment.service.PaymentService;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -47,4 +49,22 @@ CommonResponse<List<PaymentInfoDto>> getPaymentInfo(@RequestBody List<String> or
CommonResponse<String> getPaymentDate(@RequestParam String orderGroupId){
return CommonResponse.success(paymentService.getPaymentDate(orderGroupId));
}

@PostMapping(value = "/cancel")
CommonResponse<Void> cancel(@RequestBody KakaopayCancelRequestDto cancelRequestDto){
kakaopayService.cancelPayment(cancelRequestDto);
return CommonResponse.success(null);
}

@PostMapping(value = "/subscription")
CommonResponse<Void> subscription(@RequestBody SubscriptionBatchDtoList subscriptionBatchDtolist){
kakaopayService.renewSubscription(subscriptionBatchDtolist);
return CommonResponse.success(null);
}

@PostMapping(value = "/subscription/cancel")
CommonResponse<Void> cancelSubscription(@RequestBody KakaopayCancelRequestDto cancelRequestDto){
kakaopayService.cancelSubscription(cancelRequestDto);
return CommonResponse.success(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package kr.bb.payment.dto.request;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class KakaopayCancelRequestDto {
private String orderId;
private Long cancelAmount;
}
17 changes: 17 additions & 0 deletions src/main/java/kr/bb/payment/dto/response/ApprovedCancelAmount.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package kr.bb.payment.dto.response;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ApprovedCancelAmount { // 이번 요청으로 취소된 금액
private Integer total;
private Integer tax_free;
private Integer vat;
private Integer point;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package kr.bb.payment.dto.response;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CancelAvailableAmount { // 남은 취소 가능 금액
private Integer total;
}
14 changes: 14 additions & 0 deletions src/main/java/kr/bb/payment/dto/response/CanceledAmount.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package kr.bb.payment.dto.response;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CanceledAmount { // 누계 취소된 금액
private Integer total;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package kr.bb.payment.dto.response;

import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class KakaopayCancelResponseDto {
private String cid;
private String status;
private String partner_order_id;
private String partner_user_id;
private ApprovedCancelAmount approved_cancel_amount; // 금번 취소 금액
private CanceledAmount canceled_amount; // 누적 취소 금액
private CancelAvailableAmount cancel_available_amount;
private LocalDateTime created_at;
private LocalDateTime canceled_at;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package kr.bb.payment.dto.response;

import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class KakaopayCancelSubscriptionResponseDto {
private String status;
private LocalDateTime created_at;
private LocalDateTime inactivated_at;
private LocalDateTime last_approved_at;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package kr.bb.payment.repository;

import java.util.Optional;
import kr.bb.payment.entity.Subscription;
import org.springframework.data.jpa.repository.JpaRepository;

public interface SubscriptionRepository extends JpaRepository<Subscription, Long> {
Subscription findBySubscriptionSid(String subscriptionSid);
Optional<Subscription> findByOrderSubscriptionId(String orderSubscriptionId);
}
56 changes: 49 additions & 7 deletions src/main/java/kr/bb/payment/service/KakaopayService.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@
import java.util.List;
import java.util.Map;
import javax.validation.constraints.NotNull;
import kr.bb.payment.dto.request.KakaopayCancelRequestDto;
import kr.bb.payment.dto.response.KakaopayApproveResponseDto;
import kr.bb.payment.dto.response.KakaopayCancelResponseDto;
import kr.bb.payment.dto.response.KakaopayCancelSubscriptionResponseDto;
import kr.bb.payment.entity.Payment;
import kr.bb.payment.entity.Subscription;
import kr.bb.payment.feign.DeliveryServiceClient;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
Expand Down Expand Up @@ -91,17 +96,22 @@ public void renewSubscription(SubscriptionBatchDtoList subscriptionBatchDtoList)
Map<Long, Long> oldDeliveryIdsMap = new HashMap<>(); // <결제기록id, old 배송id>

for(SubscriptionBatchDto subscriptionBatchDto : subscriptionBatchDtoList.getSubscriptionBatchDtoList()){
Long userId = subscriptionBatchDto.getUserId();
String orderSubscriptionId = subscriptionBatchDto.getOrderSubscriptionId();

Subscription subscription = paymentService.getSubscriptionEntity(
subscriptionBatchDto.getOrderSubscriptionId());

MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();

parameters.add("cid", subscriptionBatchDto.getCid());
parameters.add("sid", subscriptionBatchDto.getSid());
parameters.add("partner_order_id", String.valueOf(subscriptionBatchDto.getPartnerOrderId()));
parameters.add("partner_user_id", String.valueOf(subscriptionBatchDto.getPartnerUserId()));
parameters.add("quantity", String.valueOf(subscriptionBatchDto.getQuantity()));
parameters.add("total_amount", String.valueOf(subscriptionBatchDto.getTotalAmount()));
parameters.add("cid", subscription.getSubscriptionCid());
parameters.add("sid", subscription.getSubscriptionSid());
parameters.add("partner_order_id", String.valueOf(orderSubscriptionId));
parameters.add("partner_user_id", String.valueOf(userId));
parameters.add("quantity", String.valueOf(subscription.getSubscriptionQuantity()));
parameters.add("total_amount", String.valueOf(subscription.getSubscriptionTotalAmount()));
parameters.add("tax_free_amount", String.valueOf(0));


HttpEntity<MultiValueMap<String, String>> requestEntity =
new HttpEntity<>(parameters, this.getHeaders());

Expand All @@ -119,6 +129,38 @@ public void renewSubscription(SubscriptionBatchDtoList subscriptionBatchDtoList)

}

public void cancelPayment(KakaopayCancelRequestDto cancelRequestDto){
Payment paymentEntity = paymentService.getPaymentEntity(cancelRequestDto.getOrderId());

MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();

parameters.add("cid", paymentEntity.getPaymentCid());
parameters.add("tid", paymentEntity.getPaymentTid());
parameters.add("cancel_amount", String.valueOf(cancelRequestDto.getCancelAmount()));
parameters.add("cancel_tax_free_amount", String.valueOf(0L));

HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(parameters, this.getHeaders());

String url = "https://kapi.kakao.com/v1/payment/cancel";

restTemplate.postForObject(url, requestEntity, KakaopayCancelResponseDto.class);
}

public void cancelSubscription(KakaopayCancelRequestDto cancelRequestDto ){
Subscription subscription = paymentService.getSubscriptionEntity(cancelRequestDto.getOrderId());

MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();

parameters.add("cid", subscription.getSubscriptionCid());
parameters.add("sid", subscription.getSubscriptionSid());

HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(parameters, this.getHeaders());

String url = "https://kapi.kakao.com/v1/payment/manage/subscription/inactive";

restTemplate.postForObject(url, requestEntity, KakaopayCancelSubscriptionResponseDto.class);
}

@NotNull
private HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/kr/bb/payment/service/PaymentService.java
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,14 @@ public String getPaymentDate(String orderGroupId){
}
return "";
}

@Transactional(readOnly = true)
public Payment getPaymentEntity(String orderGroupId){
return paymentRepository.findByOrderId(orderGroupId);
}

@Transactional
public Subscription getSubscriptionEntity(String orderSubscriptionId) {
return subscriptionRepository.findByOrderSubscriptionId(orderSubscriptionId).orElseThrow(EntityNotFoundException::new);
}
}
94 changes: 94 additions & 0 deletions src/test/java/kr/bb/payment/service/KakaopayCancelTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package kr.bb.payment.service;

import bloomingblooms.domain.notification.order.OrderType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.time.LocalDateTime;
import kr.bb.payment.dto.request.KakaopayCancelRequestDto;
import kr.bb.payment.dto.response.ApprovedCancelAmount;
import kr.bb.payment.dto.response.CancelAvailableAmount;
import kr.bb.payment.dto.response.CanceledAmount;
import kr.bb.payment.dto.response.KakaopayCancelResponseDto;
import kr.bb.payment.entity.Payment;
import kr.bb.payment.entity.PaymentStatus;
import kr.bb.payment.repository.PaymentRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.test.web.client.match.MockRestRequestMatchers;
import org.springframework.test.web.client.response.MockRestResponseCreators;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

@SpringBootTest
@Transactional
public class KakaopayCancelTest {
@Autowired private RestTemplate restTemplate;
@Autowired private KakaopayService kakaopayService;
@Autowired private PaymentRepository paymentRepository;
private MockRestServiceServer mockServer;

@BeforeEach
void setUp() throws Exception {
mockServer = MockRestServiceServer.createServer(restTemplate);

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
String responseJson = objectMapper.writeValueAsString(kakaopayCancelResponseDto());

mockServer
.expect(MockRestRequestMatchers.requestTo("https://kapi.kakao.com/v1/payment/cancel"))
.andExpect(MockRestRequestMatchers.method(HttpMethod.POST))
.andRespond(MockRestResponseCreators.withSuccess(responseJson, MediaType.APPLICATION_JSON));
}

@Test
@DisplayName("카카오 결제 취소 테스트")
void cancelPay() {
// given
KakaopayCancelRequestDto cancelRequestDto =
KakaopayCancelRequestDto.builder().cancelAmount(2000L).orderId("orderGroupId").build();

Payment payment = Payment.builder()
.orderId("orderGroupId")
.orderType(OrderType.DELIVERY)
.paymentActualAmount(10000L)
.paymentCid("TC0ONETIME")
.paymentStatus(PaymentStatus.PENDING)
.paymentTid("T59eb9072dff7a6a6515")
.paymentType("MONEY")
.userId(1L)
.build();
paymentRepository.save(payment);

// when
kakaopayService.cancelPayment(cancelRequestDto);

mockServer.verify();
}

private KakaopayCancelResponseDto kakaopayCancelResponseDto() {
ApprovedCancelAmount approvedCancelAmount =
ApprovedCancelAmount.builder().total(10000).tax_free(0).vat(0).point(0).build();
CanceledAmount canceledAmount = CanceledAmount.builder().total(10000).build();
CancelAvailableAmount cancelAvailableAmount =
CancelAvailableAmount.builder().total(40000).build();

return KakaopayCancelResponseDto.builder()
.cid("cid 번호")
.status("주문취소")
.partner_order_id("orderGroupId")
.partner_user_id("userId")
.approved_cancel_amount(approvedCancelAmount)
.canceled_amount(canceledAmount)
.cancel_available_amount(cancelAvailableAmount)
.created_at(LocalDateTime.now().minusDays(10))
.canceled_at(LocalDateTime.now())
.build();
}
}
Loading

0 comments on commit 49ecd78

Please sign in to comment.