From e0e7902654efec1e50ac775ee711e12796c0f6d1 Mon Sep 17 00:00:00 2001 From: pricelees Date: Thu, 16 Oct 2025 13:15:52 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EA=B2=B0=EC=A0=9C=20=EC=A0=95=EB=B3=B4?= =?UTF-8?q?=EB=A5=BC=20=EC=A0=80=EC=9E=A5=ED=95=98=EB=8A=94=20PaymentEvent?= =?UTF-8?q?Listener=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=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 --- .../business/event/PaymentEventListener.kt | 48 +++++++++++++++++ .../event/PaymentEventListenerTest.kt | 53 +++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 service/src/main/kotlin/com/sangdol/roomescape/payment/business/event/PaymentEventListener.kt create mode 100644 service/src/test/kotlin/com/sangdol/roomescape/payment/business/event/PaymentEventListenerTest.kt diff --git a/service/src/main/kotlin/com/sangdol/roomescape/payment/business/event/PaymentEventListener.kt b/service/src/main/kotlin/com/sangdol/roomescape/payment/business/event/PaymentEventListener.kt new file mode 100644 index 00000000..6ef93f0b --- /dev/null +++ b/service/src/main/kotlin/com/sangdol/roomescape/payment/business/event/PaymentEventListener.kt @@ -0,0 +1,48 @@ +package com.sangdol.roomescape.payment.business.event + +import com.sangdol.common.persistence.IDGenerator +import com.sangdol.roomescape.payment.infrastructure.persistence.PaymentDetailEntity +import com.sangdol.roomescape.payment.infrastructure.persistence.PaymentDetailRepository +import com.sangdol.roomescape.payment.infrastructure.persistence.PaymentEntity +import com.sangdol.roomescape.payment.infrastructure.persistence.PaymentRepository +import com.sangdol.roomescape.payment.mapper.toDetailEntity +import com.sangdol.roomescape.payment.mapper.toEntity +import io.github.oshai.kotlinlogging.KLogger +import io.github.oshai.kotlinlogging.KotlinLogging +import org.springframework.context.event.EventListener +import org.springframework.scheduling.annotation.Async +import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional + +private val log: KLogger = KotlinLogging.logger {} + +@Component +class PaymentEventListener( + private val idGenerator: IDGenerator, + private val paymentRepository: PaymentRepository, + private val paymentDetailRepository: PaymentDetailRepository +) { + + @Async + @EventListener + @Transactional + fun handlePaymentEvent(event: PaymentEvent) { + val reservationId = event.reservationId + + log.info { "[handlePaymentEvent] 결제 정보 저장 이벤트 수신: reservationId=${reservationId}, paymentKey=${event.paymentKey}" } + + val paymentId = idGenerator.create() + val paymentEntity: PaymentEntity = event.toEntity(paymentId) + paymentRepository.save(paymentEntity).also { + log.info { "[handlePaymentEvent] 결제 정보 저장 완료: paymentId=${paymentId}" } + } + + val paymentDetailId = idGenerator.create() + val paymentDetailEntity: PaymentDetailEntity = event.toDetailEntity(id = paymentDetailId, paymentId = paymentId) + paymentDetailRepository.save(paymentDetailEntity).also { + log.info { "[handlePaymentEvent] 결제 상세 저장 완료: paymentDetailId=${paymentDetailId}" } + } + + log.info { "[handlePaymentEvent] 결제 정보 저장 이벤트 처리 완료" } + } +} diff --git a/service/src/test/kotlin/com/sangdol/roomescape/payment/business/event/PaymentEventListenerTest.kt b/service/src/test/kotlin/com/sangdol/roomescape/payment/business/event/PaymentEventListenerTest.kt new file mode 100644 index 00000000..c8a987f9 --- /dev/null +++ b/service/src/test/kotlin/com/sangdol/roomescape/payment/business/event/PaymentEventListenerTest.kt @@ -0,0 +1,53 @@ +package com.sangdol.roomescape.payment.business.event + +import com.sangdol.roomescape.payment.business.domain.PaymentMethod +import com.sangdol.roomescape.payment.infrastructure.persistence.PaymentCardDetailEntity +import com.sangdol.roomescape.payment.infrastructure.persistence.PaymentDetailRepository +import com.sangdol.roomescape.payment.infrastructure.persistence.PaymentRepository +import com.sangdol.roomescape.payment.mapper.toEvent +import com.sangdol.roomescape.supports.FunSpecSpringbootTest +import com.sangdol.roomescape.supports.PaymentFixture +import com.sangdol.roomescape.supports.initialize +import io.kotest.assertions.assertSoftly +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe + +class PaymentEventListenerTest( + private val paymentEventListener: PaymentEventListener, + private val paymentRepository: PaymentRepository, + private val paymentDetailRepository: PaymentDetailRepository, +) : FunSpecSpringbootTest() { + + init { + test("결제 완료 이벤트를 처리한다.") { + val reservationId = initialize("FK 제약조건 해소를 위한 예약 생성") { + val user = testAuthUtil.defaultUser() + dummyInitializer.createPendingReservation(user) + }.id + + val paymentExternalAPIResponse = PaymentFixture.confirmResponse( + paymentKey = "paymentKey", + amount = 100_000, + method = PaymentMethod.CARD + ) + + paymentEventListener.handlePaymentEvent(paymentExternalAPIResponse.toEvent(reservationId)).also { + Thread.sleep(100) + } + + val payment = paymentRepository.findByReservationId(reservationId) + assertSoftly(payment!!) { + this.paymentKey shouldBe paymentExternalAPIResponse.paymentKey + this.totalAmount shouldBe paymentExternalAPIResponse.totalAmount + this.method shouldBe paymentExternalAPIResponse.method + } + + val paymentDetail = paymentDetailRepository.findByPaymentId(payment.id) + assertSoftly(paymentDetail) { + this.shouldNotBeNull() + this::class shouldBe PaymentCardDetailEntity::class + (this as PaymentCardDetailEntity).amount shouldBe paymentExternalAPIResponse.totalAmount + } + } + } +}