From 7cdb12bbad5d6612f7cc2f0cd6e6402308bb76a9 Mon Sep 17 00:00:00 2001 From: yunjae62 Date: Wed, 23 Oct 2024 23:04:39 +0900 Subject: [PATCH 01/12] =?UTF-8?q?[#112]=20feat(payment):=20=EA=B2=B0?= =?UTF-8?q?=EC=A0=9C=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=A0=80=EC=9E=A5=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/payment/core/implementation/PaymentSaver.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/service/payment/payment-core/src/main/java/radiata/service/payment/core/implementation/PaymentSaver.java b/service/payment/payment-core/src/main/java/radiata/service/payment/core/implementation/PaymentSaver.java index 48c109ec..148ac86f 100644 --- a/service/payment/payment-core/src/main/java/radiata/service/payment/core/implementation/PaymentSaver.java +++ b/service/payment/payment-core/src/main/java/radiata/service/payment/core/implementation/PaymentSaver.java @@ -40,4 +40,8 @@ public Payment createEasyPay(String userId, Long amount) { return paymentRepository.save(payment); } + + public Payment save(Payment payment) { + return paymentRepository.save(payment); + } } From 65b4b95fcdf4faafd8d90fd9b6b26331f29c9476 Mon Sep 17 00:00:00 2001 From: yunjae62 Date: Wed, 23 Oct 2024 23:04:50 +0900 Subject: [PATCH 02/12] =?UTF-8?q?[#112]=20feat(payment):=20=EA=B2=B0?= =?UTF-8?q?=EC=A0=9C=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=A0=80=EC=9E=A5=20?= =?UTF-8?q?=EB=A6=AC=ED=8F=AC=EC=A7=80=ED=86=A0=EB=A6=AC=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payment/core/domain/repository/PaymentRepository.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/service/payment/payment-core/src/main/java/radiata/service/payment/core/domain/repository/PaymentRepository.java b/service/payment/payment-core/src/main/java/radiata/service/payment/core/domain/repository/PaymentRepository.java index 2bcd33e8..25359d2e 100644 --- a/service/payment/payment-core/src/main/java/radiata/service/payment/core/domain/repository/PaymentRepository.java +++ b/service/payment/payment-core/src/main/java/radiata/service/payment/core/domain/repository/PaymentRepository.java @@ -1,5 +1,6 @@ package radiata.service.payment.core.domain.repository; +import java.util.Optional; import org.springframework.stereotype.Repository; import radiata.service.payment.core.domain.model.entity.Payment; @@ -7,4 +8,6 @@ public interface PaymentRepository { Payment save(Payment payment); + + Optional findById(String id); } From 28acf80693f267ec7cdf8dde820cd08c485efe25 Mon Sep 17 00:00:00 2001 From: yunjae62 Date: Wed, 23 Oct 2024 23:05:10 +0900 Subject: [PATCH 03/12] =?UTF-8?q?[#112]=20feat(payment):=20=EA=B2=B0?= =?UTF-8?q?=EC=A0=9C=20=EC=B7=A8=EC=86=8C=20=EB=A1=9C=EC=A7=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/implementation/PaymentRequester.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/service/payment/payment-core/src/main/java/radiata/service/payment/core/implementation/PaymentRequester.java b/service/payment/payment-core/src/main/java/radiata/service/payment/core/implementation/PaymentRequester.java index 71cdb66d..89bf4f1b 100644 --- a/service/payment/payment-core/src/main/java/radiata/service/payment/core/implementation/PaymentRequester.java +++ b/service/payment/payment-core/src/main/java/radiata/service/payment/core/implementation/PaymentRequester.java @@ -92,4 +92,16 @@ public void requestEasyPay(Payment payment, PayUser payUser) { // 결제 승인 payment.approve(); } + + public void cancelTossPayment(Payment payment) { + // 토스 결제 취소 API 호출 + log.info("Toss payment cancel request: {}", payment.getId()); + payment.fail(); + } + + public void cancelEasyPay(Payment payment, PayUser payUser) { + // 간편결제 취소 API 호출 + payUser.deposit(payment.getAmount()); + payment.fail(); + } } From d342488b58f176854bc97c29f07e699c2d249199 Mon Sep 17 00:00:00 2001 From: yunjae62 Date: Wed, 23 Oct 2024 23:05:20 +0900 Subject: [PATCH 04/12] =?UTF-8?q?[#112]=20feat(payment):=20=EA=B2=B0?= =?UTF-8?q?=EC=A0=9C=20=EC=A1=B0=ED=9A=8C=20=EB=A1=9C=EC=A7=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/implementation/PaymentReader.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 service/payment/payment-core/src/main/java/radiata/service/payment/core/implementation/PaymentReader.java diff --git a/service/payment/payment-core/src/main/java/radiata/service/payment/core/implementation/PaymentReader.java b/service/payment/payment-core/src/main/java/radiata/service/payment/core/implementation/PaymentReader.java new file mode 100644 index 00000000..8bb819a8 --- /dev/null +++ b/service/payment/payment-core/src/main/java/radiata/service/payment/core/implementation/PaymentReader.java @@ -0,0 +1,22 @@ +package radiata.service.payment.core.implementation; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import radiata.common.annotation.Implementation; +import radiata.common.exception.BusinessException; +import radiata.common.message.ExceptionMessage; +import radiata.service.payment.core.domain.model.entity.Payment; +import radiata.service.payment.core.domain.repository.PaymentRepository; + +@Slf4j +@Implementation +@RequiredArgsConstructor +public class PaymentReader { + + private final PaymentRepository paymentRepository; + + public Payment getPaymentById(String paymentId) { + return paymentRepository.findById(paymentId) + .orElseThrow(() -> new BusinessException(ExceptionMessage.PAYMENT_NOT_FOUND)); + } +} From 181a88b826ca0e46c726fdc0fedce20fe2bc4ab7 Mon Sep 17 00:00:00 2001 From: yunjae62 Date: Wed, 23 Oct 2024 23:05:38 +0900 Subject: [PATCH 05/12] =?UTF-8?q?[#112]=20feat(payment):=20=EA=B2=B0?= =?UTF-8?q?=EC=A0=9C=20=EC=B7=A8=EC=86=8C=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/service/PaymentRollbackService.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 service/payment/payment-core/src/main/java/radiata/service/payment/core/service/PaymentRollbackService.java diff --git a/service/payment/payment-core/src/main/java/radiata/service/payment/core/service/PaymentRollbackService.java b/service/payment/payment-core/src/main/java/radiata/service/payment/core/service/PaymentRollbackService.java new file mode 100644 index 00000000..0d6863e7 --- /dev/null +++ b/service/payment/payment-core/src/main/java/radiata/service/payment/core/service/PaymentRollbackService.java @@ -0,0 +1,43 @@ +package radiata.service.payment.core.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import radiata.common.domain.payment.constant.PaymentType; +import radiata.service.payment.core.domain.model.entity.PayUser; +import radiata.service.payment.core.domain.model.entity.Payment; +import radiata.service.payment.core.implementation.PayUserReader; +import radiata.service.payment.core.implementation.PaymentReader; +import radiata.service.payment.core.implementation.PaymentRequester; +import radiata.service.payment.core.implementation.PaymentSaver; + +@Slf4j +@Service +@RequiredArgsConstructor +public class PaymentRollbackService { + + private final PaymentSaver paymentSaver; + private final PayUserReader payUserReader; + private final PaymentReader paymentReader; + private final PaymentRequester paymentRequester; + + @Transactional + public void rollbackPayment(String paymentId) { + // 결제 조회 + Payment payment = paymentReader.getPaymentById(paymentId); + // 결제 취소 + if (payment.getType().equals(PaymentType.EASY_PAY)) { + // 간편결제 유저 조회 + PayUser payUser = payUserReader.getPayUserByUserId(payment.getUserId()); + // 간편결제 취소 + paymentRequester.cancelEasyPay(payment, payUser); + } else if (payment.getType().equals(PaymentType.TOSS_PAYMENTS)) { + // 토스 결제 취소 + paymentRequester.cancelTossPayment(payment); + } + + // 결제 저장 + paymentSaver.save(payment); + } +} From 7dbe096d300682d817d5535969e1341372f61e07 Mon Sep 17 00:00:00 2001 From: yunjae62 Date: Wed, 23 Oct 2024 23:06:22 +0900 Subject: [PATCH 06/12] =?UTF-8?q?[#112]=20feat(payment):=20=EA=B2=B0?= =?UTF-8?q?=EC=A0=9C=20=EC=B7=A8=EC=86=8C=20=EB=A9=94=EC=8B=9C=EC=A7=80=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payment/dto/request/PaymentCancelRequestDto.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 common/src/main/java/radiata/common/domain/payment/dto/request/PaymentCancelRequestDto.java diff --git a/common/src/main/java/radiata/common/domain/payment/dto/request/PaymentCancelRequestDto.java b/common/src/main/java/radiata/common/domain/payment/dto/request/PaymentCancelRequestDto.java new file mode 100644 index 00000000..8f809970 --- /dev/null +++ b/common/src/main/java/radiata/common/domain/payment/dto/request/PaymentCancelRequestDto.java @@ -0,0 +1,7 @@ +package radiata.common.domain.payment.dto.request; + +public record PaymentCancelRequestDto( + String paymentId +) { + +} From f7c97eaa91df758f251edc6146eadfd1612c54ed Mon Sep 17 00:00:00 2001 From: yunjae62 Date: Wed, 23 Oct 2024 23:06:39 +0900 Subject: [PATCH 07/12] =?UTF-8?q?[#112]=20feat(payment):=20=EA=B2=B0?= =?UTF-8?q?=EC=A0=9C=20=EC=A1=B0=ED=9A=8C=20=EC=98=88=EC=99=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/radiata/common/message/ExceptionMessage.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/src/main/java/radiata/common/message/ExceptionMessage.java b/common/src/main/java/radiata/common/message/ExceptionMessage.java index 0ea2a967..185c6756 100644 --- a/common/src/main/java/radiata/common/message/ExceptionMessage.java +++ b/common/src/main/java/radiata/common/message/ExceptionMessage.java @@ -5,6 +5,7 @@ import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import static org.springframework.http.HttpStatus.PAYMENT_REQUIRED; import static org.springframework.http.HttpStatus.SERVICE_UNAVAILABLE; + import lombok.Getter; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; @@ -31,6 +32,7 @@ public enum ExceptionMessage { PAY_USER_NOT_FOUND(HttpStatus.NOT_FOUND, "2002", "간편결제 사용자가 존재하지 않습니다."), INVALID_PASSWORD(BAD_REQUEST, "2003", "비밀번호가 일치하지 않습니다."), PAY_USER_DUPLICATE(BAD_REQUEST, "2004", "이미 등록된 사용자입니다."), + PAYMENT_NOT_FOUND(HttpStatus.NOT_FOUND, "2005", "결제 정보가 존재하지 않습니다."), /* 유저 3000번대 */ From ab41b24a04ea89127ae9b6c214d77c68f524d4a3 Mon Sep 17 00:00:00 2001 From: yunjae62 Date: Wed, 23 Oct 2024 23:06:53 +0900 Subject: [PATCH 08/12] =?UTF-8?q?[#112]=20feat(payment):=20=EC=B9=B4?= =?UTF-8?q?=ED=94=84=EC=B9=B4=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service/payment/payment-api/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/service/payment/payment-api/build.gradle b/service/payment/payment-api/build.gradle index e6ee9c63..fd2f0f43 100644 --- a/service/payment/payment-api/build.gradle +++ b/service/payment/payment-api/build.gradle @@ -22,10 +22,14 @@ dependencies { // batch implementation 'org.springframework.boot:spring-boot-starter-batch' + //kafka + implementation 'org.springframework.kafka:spring-kafka' + /* test */ testImplementation platform('org.junit:junit-bom:5.10.0') testImplementation 'org.junit.jupiter:junit-jupiter' + testImplementation 'org.springframework.kafka:spring-kafka-test' } dependencyManagement { From 5ac8c76553585bfea7a84ef1d4df8e9adbe0294c Mon Sep 17 00:00:00 2001 From: yunjae62 Date: Wed, 23 Oct 2024 23:07:06 +0900 Subject: [PATCH 09/12] =?UTF-8?q?[#112]=20feat(payment):=20=EC=B9=B4?= =?UTF-8?q?=ED=94=84=EC=B9=B4=20=EC=84=9C=EB=B2=84=20=EC=A3=BC=EC=86=8C=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service/payment/payment-api/src/main/resources/application.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/service/payment/payment-api/src/main/resources/application.yml b/service/payment/payment-api/src/main/resources/application.yml index 4fed0833..46f2f6d9 100644 --- a/service/payment/payment-api/src/main/resources/application.yml +++ b/service/payment/payment-api/src/main/resources/application.yml @@ -7,6 +7,8 @@ spring: default: dev lifecycle: timeout-per-shutdown-phase: 10s + kafka: + bootstrap-servers: yunjae.click:9092 server: shutdown: graceful # 애플리케이션 종료 시 정상 종료 (Graceful Shutdown) From 67f7ff2c6c4a073a9e0c3cd081b0c0e29be4f934 Mon Sep 17 00:00:00 2001 From: yunjae62 Date: Wed, 23 Oct 2024 23:07:19 +0900 Subject: [PATCH 10/12] =?UTF-8?q?[#112]=20feat(payment):=20=EC=B9=B4?= =?UTF-8?q?=ED=94=84=EC=B9=B4=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payment/api/config/KafkaConfig.java | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 service/payment/payment-api/src/main/java/radiata/service/payment/api/config/KafkaConfig.java diff --git a/service/payment/payment-api/src/main/java/radiata/service/payment/api/config/KafkaConfig.java b/service/payment/payment-api/src/main/java/radiata/service/payment/api/config/KafkaConfig.java new file mode 100644 index 00000000..7e04fdc1 --- /dev/null +++ b/service/payment/payment-api/src/main/java/radiata/service/payment/api/config/KafkaConfig.java @@ -0,0 +1,59 @@ +package radiata.service.payment.api.config; + + +import java.util.HashMap; +import java.util.Map; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.apache.kafka.common.serialization.StringSerializer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.annotation.EnableKafka; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaProducerFactory; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.kafka.core.ProducerFactory; +import org.springframework.kafka.support.serializer.JsonSerializer; + +@EnableKafka +@Configuration +public class KafkaConfig { + + @Value("${spring.kafka.bootstrap-servers}") + private String bootstrapServers; + + @Bean + public ProducerFactory producerFactory() { + Map configProds = new HashMap<>(); + configProds.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); + configProds.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + configProds.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class); + return new DefaultKafkaProducerFactory<>(configProds); + } + + @Bean + public ConsumerFactory consumerFactory() { + HashMap config = new HashMap<>(); + config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); + config.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + config.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + + return new DefaultKafkaConsumerFactory<>(config); + } + + @Bean + public KafkaTemplate kafkaTemplate() { + return new KafkaTemplate<>(producerFactory()); + } + + @Bean + public ConcurrentKafkaListenerContainerFactory KafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(consumerFactory()); + return factory; + } +} \ No newline at end of file From 1d2350adebb330ac37e79de3567c0bb04b50c0ba Mon Sep 17 00:00:00 2001 From: yunjae62 Date: Wed, 23 Oct 2024 23:07:36 +0900 Subject: [PATCH 11/12] =?UTF-8?q?[#112]=20feat(payment):=20=EA=B2=B0?= =?UTF-8?q?=EC=A0=9C=20=EC=B7=A8=EC=86=8C=20=EB=A6=AC=EC=8A=A4=EB=84=88=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/listener/PaymentRollbackListener.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 service/payment/payment-api/src/main/java/radiata/service/payment/api/listener/PaymentRollbackListener.java diff --git a/service/payment/payment-api/src/main/java/radiata/service/payment/api/listener/PaymentRollbackListener.java b/service/payment/payment-api/src/main/java/radiata/service/payment/api/listener/PaymentRollbackListener.java new file mode 100644 index 00000000..bdecaff5 --- /dev/null +++ b/service/payment/payment-api/src/main/java/radiata/service/payment/api/listener/PaymentRollbackListener.java @@ -0,0 +1,25 @@ +package radiata.service.payment.api.listener; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Component; +import radiata.common.domain.payment.dto.request.PaymentCancelRequestDto; +import radiata.service.payment.core.service.PaymentRollbackService; + +@Slf4j +@Component +@RequiredArgsConstructor +public class PaymentRollbackListener { + + private final ObjectMapper objectMapper; + private final PaymentRollbackService paymentRollbackService; + + @KafkaListener(topics = "payment.cancel") + public void cancelPayment(String message) throws JsonProcessingException { + PaymentCancelRequestDto request = objectMapper.readValue(message, PaymentCancelRequestDto.class); + paymentRollbackService.rollbackPayment(request.paymentId()); + } +} From 413e3476144bffa38a8e481c98c28793e6a0e091 Mon Sep 17 00:00:00 2001 From: yunjae62 Date: Wed, 23 Oct 2024 23:08:17 +0900 Subject: [PATCH 12/12] =?UTF-8?q?[#112]=20feat(database):=20=EC=B5=9C?= =?UTF-8?q?=EB=8C=80=20=EC=BB=A4=EB=84=A5=EC=85=98=20=ED=92=80=20=EA=B0=9C?= =?UTF-8?q?=EC=88=98=2020=EA=B0=9C=EB=A1=9C=20=ED=95=98=ED=96=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/radiata/database/configuration/DataSourceConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/src/main/java/radiata/database/configuration/DataSourceConfig.java b/database/src/main/java/radiata/database/configuration/DataSourceConfig.java index 6b6fbcc6..625bf87e 100644 --- a/database/src/main/java/radiata/database/configuration/DataSourceConfig.java +++ b/database/src/main/java/radiata/database/configuration/DataSourceConfig.java @@ -35,7 +35,7 @@ public DataSource mainDataSource() { .password(mainDataSourceProperty.password()) .build(); - dataSource.setMaximumPoolSize(30); + dataSource.setMaximumPoolSize(20); return new LazyConnectionDataSourceProxy(dataSource); // 트랜잭션 진입 하더라도 커넥션 가져오지 않음 }