refactor: 결제 확정 로직 변화에 따른 테스트 및 API 수정

- 기존의 결제 확정 API 및 테스트 제거(취소는 유지)
- PaymentWriter 제거
- 테스트 코드 반영
This commit is contained in:
이상진 2025-10-16 13:55:13 +09:00
parent c1eb1aa2b4
commit c3330e5652
8 changed files with 73 additions and 505 deletions

View File

@ -1,5 +1,6 @@
package com.sangdol.roomescape.payment.business
import com.sangdol.common.persistence.IDGenerator
import com.sangdol.common.persistence.TransactionExecutionUtil
import com.sangdol.roomescape.payment.business.domain.UserFacingPaymentErrorCode
import com.sangdol.roomescape.payment.dto.*
@ -8,6 +9,7 @@ import com.sangdol.roomescape.payment.exception.PaymentErrorCode
import com.sangdol.roomescape.payment.exception.PaymentException
import com.sangdol.roomescape.payment.infrastructure.client.TosspayClient
import com.sangdol.roomescape.payment.infrastructure.persistence.*
import com.sangdol.roomescape.payment.mapper.toEntity
import com.sangdol.roomescape.payment.mapper.toEvent
import com.sangdol.roomescape.payment.mapper.toResponse
import io.github.oshai.kotlinlogging.KLogger
@ -20,11 +22,11 @@ private val log: KLogger = KotlinLogging.logger {}
@Service
class PaymentService(
private val idGenerator: IDGenerator,
private val paymentClient: TosspayClient,
private val paymentRepository: PaymentRepository,
private val paymentDetailRepository: PaymentDetailRepository,
private val canceledPaymentRepository: CanceledPaymentRepository,
private val paymentWriter: PaymentWriter,
private val transactionExecutionUtil: TransactionExecutionUtil,
private val eventPublisher: ApplicationEventPublisher
) {
@ -60,19 +62,6 @@ class PaymentService(
}
}
fun savePayment(
reservationId: Long,
paymentGatewayResponse: PaymentGatewayResponse
): PaymentCreateResponse {
val payment: PaymentEntity = paymentWriter.createPayment(
reservationId = reservationId,
paymentGatewayResponse = paymentGatewayResponse
)
val detail: PaymentDetailEntity = paymentWriter.createDetail(paymentGatewayResponse, payment.id)
return PaymentCreateResponse(paymentId = payment.id, detailId = detail.id)
}
fun cancel(userId: Long, request: PaymentCancelRequest) {
val payment: PaymentEntity = findByReservationIdOrThrow(request.reservationId)
@ -83,12 +72,17 @@ class PaymentService(
)
transactionExecutionUtil.withNewTransaction(isReadOnly = false) {
paymentWriter.cancel(
userId = userId,
payment = payment,
requestedAt = request.requestedAt,
cancelResponse = clientCancelResponse
)
val payment = findByReservationIdOrThrow(request.reservationId).apply { this.cancel() }
clientCancelResponse.cancels.toEntity(
id = idGenerator.create(),
paymentId = payment.id,
cancelRequestedAt = request.requestedAt,
canceledBy = userId
).also {
canceledPaymentRepository.save(it)
log.debug { "[cancel] 결제 취소 정보 저장 완료: payment.id=${payment.id}" }
}
}.also {
log.info { "[cancel] 결제 취소 완료: paymentId=${payment.id}" }
}

View File

@ -1,80 +0,0 @@
package com.sangdol.roomescape.payment.business
import com.sangdol.common.persistence.IDGenerator
import com.sangdol.roomescape.payment.exception.PaymentErrorCode
import com.sangdol.roomescape.payment.exception.PaymentException
import com.sangdol.roomescape.payment.business.domain.PaymentMethod
import com.sangdol.roomescape.payment.dto.PaymentGatewayCancelResponse
import com.sangdol.roomescape.payment.dto.PaymentGatewayResponse
import com.sangdol.roomescape.payment.mapper.toCardDetailEntity
import com.sangdol.roomescape.payment.mapper.toEasypayPrepaidDetailEntity
import com.sangdol.roomescape.payment.mapper.toEntity
import com.sangdol.roomescape.payment.mapper.toTransferDetailEntity
import com.sangdol.roomescape.payment.infrastructure.persistence.*
import io.github.oshai.kotlinlogging.KLogger
import io.github.oshai.kotlinlogging.KotlinLogging
import org.springframework.stereotype.Component
import java.time.Instant
private val log: KLogger = KotlinLogging.logger {}
@Component
class PaymentWriter(
private val paymentRepository: PaymentRepository,
private val paymentDetailRepository: PaymentDetailRepository,
private val canceledPaymentRepository: CanceledPaymentRepository,
private val idGenerator: IDGenerator,
) {
fun createPayment(
reservationId: Long,
paymentGatewayResponse: PaymentGatewayResponse
): PaymentEntity {
log.info { "[PaymentWriterV2.createPayment] 결제 승인 및 결제 정보 저장 시작: reservationId=${reservationId}, paymentKey=${paymentGatewayResponse.paymentKey}" }
return paymentGatewayResponse.toEntity(id = idGenerator.create(), reservationId = reservationId).also {
paymentRepository.save(it)
log.info { "[PaymentWriterV2.createPayment] 결제 승인 및 결제 정보 저장 완료: reservationId=${reservationId}, payment.id=${it.id}" }
}
}
fun createDetail(
paymentGatewayResponse: PaymentGatewayResponse,
paymentId: Long,
): PaymentDetailEntity {
val method: PaymentMethod = paymentGatewayResponse.method
val id = idGenerator.create()
if (method == PaymentMethod.TRANSFER) {
return paymentDetailRepository.save(paymentGatewayResponse.toTransferDetailEntity(id, paymentId))
}
if (method == PaymentMethod.EASY_PAY && paymentGatewayResponse.card == null) {
return paymentDetailRepository.save(paymentGatewayResponse.toEasypayPrepaidDetailEntity(id, paymentId))
}
if (paymentGatewayResponse.card != null) {
return paymentDetailRepository.save(paymentGatewayResponse.toCardDetailEntity(id, paymentId))
}
throw PaymentException(PaymentErrorCode.NOT_SUPPORTED_PAYMENT_TYPE)
}
fun cancel(
userId: Long,
payment: PaymentEntity,
requestedAt: Instant,
cancelResponse: PaymentGatewayCancelResponse
): CanceledPaymentEntity {
log.debug { "[PaymentWriterV2.cancelPayment] 결제 취소 정보 저장 시작: payment.id=${payment.id}" }
paymentRepository.save(payment.apply { this.cancel() })
return cancelResponse.cancels.toEntity(
id = idGenerator.create(),
paymentId = payment.id,
cancelRequestedAt = requestedAt,
canceledBy = userId
).also {
canceledPaymentRepository.save(it)
log.debug { "[PaymentWriterV2.cancelPayment] 결제 취소 정보 저장 완료: payment.id=${payment.id}" }
}
}
}

View File

@ -2,11 +2,8 @@ package com.sangdol.roomescape.payment.docs
import com.sangdol.common.types.web.CommonApiResponse
import com.sangdol.roomescape.auth.web.support.User
import com.sangdol.roomescape.auth.web.support.UserOnly
import com.sangdol.roomescape.common.types.CurrentUserContext
import com.sangdol.roomescape.payment.dto.PaymentGatewayResponse
import com.sangdol.roomescape.payment.dto.PaymentCancelRequest
import com.sangdol.roomescape.payment.dto.PaymentConfirmRequest
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
@ -16,13 +13,6 @@ import org.springframework.web.bind.annotation.RequestBody
interface PaymentAPI {
@UserOnly
@Operation(summary = "결제 승인")
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun confirmPayment(
@Valid @RequestBody request: PaymentConfirmRequest
): ResponseEntity<CommonApiResponse<PaymentGatewayResponse>>
@Operation(summary = "결제 취소")
@ApiResponses(ApiResponse(responseCode = "200", useReturnTypeSchema = true))
fun cancelPayment(

View File

@ -6,79 +6,9 @@ import com.sangdol.roomescape.payment.dto.CancelDetail
import com.sangdol.roomescape.payment.dto.PaymentGatewayResponse
import com.sangdol.roomescape.payment.exception.PaymentErrorCode
import com.sangdol.roomescape.payment.exception.PaymentException
import com.sangdol.roomescape.payment.infrastructure.persistence.*
import com.sangdol.roomescape.payment.infrastructure.persistence.CanceledPaymentEntity
import java.time.Instant
fun PaymentGatewayResponse.toEntity(
id: Long,
reservationId: Long,
) = PaymentEntity(
id = id,
reservationId = reservationId,
paymentKey = this.paymentKey,
orderId = this.orderId,
totalAmount = this.totalAmount,
requestedAt = this.requestedAt.toInstant(),
approvedAt = this.approvedAt.toInstant(),
type = this.type,
method = this.method,
status = this.status,
)
fun PaymentGatewayResponse.toCardDetailEntity(id: Long, paymentId: Long): PaymentCardDetailEntity {
val cardDetail = this.card ?: throw PaymentException(PaymentErrorCode.PAYMENT_UNEXPECTED_ERROR)
return PaymentCardDetailEntity(
id = id,
paymentId = paymentId,
suppliedAmount = this.suppliedAmount,
vat = this.vat,
issuerCode = cardDetail.issuerCode,
cardType = cardDetail.cardType,
ownerType = cardDetail.ownerType,
amount = cardDetail.amount,
cardNumber = cardDetail.number,
approvalNumber = cardDetail.approveNo,
installmentPlanMonths = cardDetail.installmentPlanMonths,
isInterestFree = cardDetail.isInterestFree,
easypayProviderCode = this.easyPay?.provider,
easypayDiscountAmount = this.easyPay?.discountAmount,
)
}
fun PaymentGatewayResponse.toEasypayPrepaidDetailEntity(
id: Long,
paymentId: Long
): PaymentEasypayPrepaidDetailEntity {
val easyPayDetail = this.easyPay ?: throw PaymentException(PaymentErrorCode.PAYMENT_UNEXPECTED_ERROR)
return PaymentEasypayPrepaidDetailEntity(
id = id,
paymentId = paymentId,
suppliedAmount = this.suppliedAmount,
vat = this.vat,
easypayProviderCode = easyPayDetail.provider,
amount = easyPayDetail.amount,
discountAmount = easyPayDetail.discountAmount
)
}
fun PaymentGatewayResponse.toTransferDetailEntity(
id: Long,
paymentId: Long
): PaymentBankTransferDetailEntity {
val transferDetail = this.transfer ?: throw PaymentException(PaymentErrorCode.PAYMENT_UNEXPECTED_ERROR)
return PaymentBankTransferDetailEntity(
id = id,
paymentId = paymentId,
suppliedAmount = this.suppliedAmount,
vat = this.vat,
bankCode = transferDetail.bankCode,
settlementStatus = transferDetail.settlementStatus
)
}
fun CancelDetail.toEntity(
id: Long,
paymentId: Long,
@ -115,7 +45,7 @@ fun PaymentGatewayResponse.toEvent(reservationId: Long): PaymentEvent {
}
fun PaymentGatewayResponse.toDetail(): PaymentDetail {
return when(this.method) {
return when (this.method) {
PaymentMethod.TRANSFER -> this.toBankTransferDetail()
PaymentMethod.CARD -> this.toCardDetail()
PaymentMethod.EASY_PAY -> {

View File

@ -6,27 +6,18 @@ import com.sangdol.roomescape.common.types.CurrentUserContext
import com.sangdol.roomescape.payment.business.PaymentService
import com.sangdol.roomescape.payment.docs.PaymentAPI
import com.sangdol.roomescape.payment.dto.PaymentCancelRequest
import com.sangdol.roomescape.payment.dto.PaymentConfirmRequest
import com.sangdol.roomescape.payment.dto.PaymentGatewayResponse
import jakarta.validation.Valid
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/payments")
class PaymentController(
private val paymentService: PaymentService
) : PaymentAPI {
@PostMapping("/confirm")
override fun confirmPayment(
@Valid @RequestBody request: PaymentConfirmRequest
): ResponseEntity<CommonApiResponse<PaymentGatewayResponse>> {
val response = paymentService.requestConfirm(request)
return ResponseEntity.ok(CommonApiResponse(response))
}
@PostMapping("/cancel")
override fun cancelPayment(
@User user: CurrentUserContext,

View File

@ -4,22 +4,21 @@ import com.ninjasquad.springmockk.MockkBean
import com.sangdol.common.types.web.HttpStatus
import com.sangdol.roomescape.auth.exception.AuthErrorCode
import com.sangdol.roomescape.payment.business.PaymentService
import com.sangdol.roomescape.payment.business.domain.*
import com.sangdol.roomescape.payment.dto.*
import com.sangdol.roomescape.payment.exception.ExternalPaymentException
import com.sangdol.roomescape.payment.business.domain.PaymentMethod
import com.sangdol.roomescape.payment.business.domain.PaymentStatus
import com.sangdol.roomescape.payment.dto.PaymentConfirmRequest
import com.sangdol.roomescape.payment.exception.PaymentErrorCode
import com.sangdol.roomescape.payment.infrastructure.client.TosspayClient
import com.sangdol.roomescape.payment.infrastructure.persistence.CanceledPaymentRepository
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.supports.FunSpecSpringbootTest
import com.sangdol.roomescape.supports.PaymentFixture
import com.sangdol.roomescape.supports.runExceptionTest
import com.sangdol.roomescape.supports.runTest
import com.sangdol.roomescape.payment.mapper.toDetailEntity
import com.sangdol.roomescape.payment.mapper.toEntity
import com.sangdol.roomescape.payment.mapper.toEvent
import com.sangdol.roomescape.supports.*
import io.kotest.matchers.shouldBe
import io.mockk.clearMocks
import io.mockk.every
import org.hamcrest.CoreMatchers.containsString
import org.hamcrest.CoreMatchers.equalTo
import org.springframework.data.repository.findByIdOrNull
import org.springframework.http.HttpMethod
@ -28,211 +27,10 @@ class PaymentAPITest(
private val tosspayClient: TosspayClient,
private val paymentService: PaymentService,
private val paymentRepository: PaymentRepository,
private val paymentDetailRepository: PaymentDetailRepository,
private val canceledPaymentRepository: CanceledPaymentRepository
) : FunSpecSpringbootTest() {
init {
context("결제를 승인한다.") {
context("권한이 없으면 접근할 수 없다.") {
val endpoint = "/payments/confirm"
test("비회원") {
runExceptionTest(
method = HttpMethod.POST,
endpoint = endpoint,
expectedErrorCode = AuthErrorCode.TOKEN_NOT_FOUND
)
}
test("관리자") {
runExceptionTest(
token = testAuthUtil.defaultHqAdminLogin().second,
method = HttpMethod.POST,
endpoint = endpoint,
expectedErrorCode = AuthErrorCode.ACCESS_DENIED
)
}
}
val amount = 100_000
context("간편결제 + 카드로 ${amount}원을 결제한다.") {
context("일시불") {
test("토스페이 + 토스뱅크카드(신용)") {
runConfirmTest(
amount = amount,
cardDetail = PaymentFixture.cardDetail(
amount = amount,
issuerCode = CardIssuerCode.TOSS_BANK,
cardType = CardType.CREDIT,
),
easyPayDetail = PaymentFixture.easypayDetail(
amount = 0,
provider = EasyPayCompanyCode.TOSSPAY
)
)
}
test("삼성페이 + 삼성카드(법인)") {
runConfirmTest(
amount = amount,
cardDetail = PaymentFixture.cardDetail(
amount = amount,
issuerCode = CardIssuerCode.SAMSUNG,
cardType = CardType.CREDIT,
ownerType = CardOwnerType.CORPORATE
),
easyPayDetail = PaymentFixture.easypayDetail(
amount = 0,
provider = EasyPayCompanyCode.SAMSUNGPAY
)
)
}
}
context("할부") {
val installmentPlanMonths = 12
test("네이버페이 + 신한카드 / 12개월") {
runConfirmTest(
amount = amount,
cardDetail = PaymentFixture.cardDetail(
amount = amount,
issuerCode = CardIssuerCode.SHINHAN,
installmentPlanMonths = installmentPlanMonths
),
easyPayDetail = PaymentFixture.easypayDetail(
amount = 0,
provider = EasyPayCompanyCode.NAVERPAY
)
)
}
}
context("간편결제사 포인트 일부 사용") {
val point = (amount * 0.1).toInt()
test("토스페이 + 국민카드(체크) / 일시불 / $point 포인트 사용") {
runConfirmTest(
amount = amount,
cardDetail = PaymentFixture.cardDetail(
amount = (amount - point),
issuerCode = CardIssuerCode.KOOKMIN,
cardType = CardType.CHECK
),
easyPayDetail = PaymentFixture.easypayDetail(
amount = 0,
provider = EasyPayCompanyCode.TOSSPAY,
discountAmount = point
)
)
}
}
}
context("간편결제사의 선불 충전금액으로 ${amount}원을 결제한다.") {
test("토스페이 + 토스페이머니 / 전액") {
runConfirmTest(
easyPayDetail = PaymentFixture.easypayDetail(
amount = amount,
provider = EasyPayCompanyCode.TOSSPAY
)
)
}
val point = (amount * 0.05).toInt()
test("카카오페이 + 카카오페이머니 / $point 사용") {
runConfirmTest(
easyPayDetail = PaymentFixture.easypayDetail(
amount = (amount - point),
provider = EasyPayCompanyCode.KAKAOPAY,
discountAmount = point
)
)
}
}
context("계좌이체로 결제한다.") {
test("토스뱅크") {
runConfirmTest(
transferDetail = PaymentFixture.transferDetail(bankCode = BankCode.TOSS_BANK)
)
}
}
context("결제 처리중 오류가 발생한다.") {
lateinit var token: String
val commonRequest = PaymentFixture.confirmRequest
beforeTest {
token = testAuthUtil.defaultUserLogin().second
}
afterTest {
clearMocks(tosspayClient)
}
test("예외 코드가 UserFacingPaymentErrorCode에 있으면 결제 실패 메시지를 같이 담는다.") {
val statusCode = HttpStatus.BAD_REQUEST.value()
val message = "거래금액 한도를 초과했습니다."
every {
tosspayClient.confirm(commonRequest.paymentKey, commonRequest.orderId, commonRequest.amount)
} throws ExternalPaymentException(
httpStatusCode = statusCode,
errorCode = UserFacingPaymentErrorCode.EXCEED_MAX_AMOUNT.name,
message = message
)
runTest(
token = token,
using = {
body(commonRequest)
},
on = {
post("/payments/confirm")
},
expect = {
statusCode(statusCode)
body("code", equalTo(PaymentErrorCode.PAYMENT_CLIENT_ERROR.errorCode))
body("message", containsString(message))
}
)
}
context("예외 코드가 UserFacingPaymentErrorCode에 없으면 Client의 상태 코드에 따라 다르게 처리한다.") {
mapOf(
HttpStatus.BAD_REQUEST.value() to PaymentErrorCode.PAYMENT_CLIENT_ERROR,
HttpStatus.INTERNAL_SERVER_ERROR.value() to PaymentErrorCode.PAYMENT_PROVIDER_ERROR
).forEach { (statusCode, expectedErrorCode) ->
test("statusCode=${statusCode}") {
val message = "잘못된 시크릿키 연동 정보 입니다."
every {
tosspayClient.confirm(commonRequest.paymentKey, commonRequest.orderId, commonRequest.amount)
} throws ExternalPaymentException(
httpStatusCode = statusCode,
errorCode = "INVALID_API_KEY",
message = message
)
runTest(
token = token,
using = {
body(commonRequest)
},
on = {
post("/payments/confirm")
},
expect = {
statusCode(statusCode)
body("code", equalTo(expectedErrorCode.errorCode))
body("message", equalTo(expectedErrorCode.message))
}
)
}
}
}
}
}
context("결제를 취소한다.") {
context("권한이 없으면 접근할 수 없다.") {
val endpoint = "/payments/cancel"
@ -262,7 +60,7 @@ class PaymentAPITest(
val reservation = dummyInitializer.createConfirmReservation(user = user)
val confirmRequest = PaymentFixture.confirmRequest
val paymentCreateResponse = createPayment(
val paymentEntity = createPayment(
request = confirmRequest,
reservationId = reservation.id
)
@ -289,10 +87,10 @@ class PaymentAPITest(
statusCode(HttpStatus.OK.value())
}
).also {
val payment = paymentRepository.findByIdOrNull(paymentCreateResponse.paymentId)
val payment = paymentRepository.findByIdOrNull(paymentEntity.id)
?: throw AssertionError("Unexpected Exception Occurred.")
val canceledPayment =
canceledPaymentRepository.findByPaymentId(paymentCreateResponse.paymentId)
canceledPaymentRepository.findByPaymentId(paymentEntity.id)
?: throw AssertionError("Unexpected Exception Occurred.")
payment.status shouldBe PaymentStatus.CANCELED
@ -319,7 +117,7 @@ class PaymentAPITest(
private fun createPayment(
request: PaymentConfirmRequest,
reservationId: Long,
): PaymentCreateResponse {
): PaymentEntity {
every {
tosspayClient.confirm(request.paymentKey, request.orderId, request.amount)
} returns PaymentFixture.confirmResponse(
@ -331,49 +129,10 @@ class PaymentAPITest(
transferDetail = null,
)
val paymentResponse = paymentService.requestConfirm(request)
return paymentService.savePayment(reservationId, paymentResponse)
}
val paymentEvent = paymentService.requestConfirm(reservationId, request).toEvent(reservationId)
fun runConfirmTest(
cardDetail: CardDetailResponse? = null,
easyPayDetail: EasyPayDetailResponse? = null,
transferDetail: TransferDetailResponse? = null,
paymentKey: String = "paymentKey",
amount: Int = 10000,
) {
val token = testAuthUtil.defaultUserLogin().second
val request = PaymentFixture.confirmRequest.copy(paymentKey = paymentKey, amount = amount)
val method = if (easyPayDetail != null) {
PaymentMethod.EASY_PAY
} else if (cardDetail != null) {
PaymentMethod.CARD
} else if (transferDetail != null) {
PaymentMethod.TRANSFER
} else {
throw AssertionError("결제타입 확인 필요.")
return paymentRepository.save(paymentEvent.toEntity(IDGenerator.create())).also {
paymentDetailRepository.save(paymentEvent.toDetailEntity(IDGenerator.create(), it.id))
}
val clientResponse = PaymentFixture.confirmResponse(
paymentKey, amount, method, cardDetail, easyPayDetail, transferDetail
)
every {
tosspayClient.confirm(request.paymentKey, request.orderId, request.amount)
} returns clientResponse
runTest(
token = token,
using = {
body(request)
},
on = {
post("/payments/confirm")
},
expect = {
statusCode(HttpStatus.OK.value())
}
)
}
}

View File

@ -1,11 +1,11 @@
package com.sangdol.roomescape.supports
import com.sangdol.roomescape.payment.business.PaymentWriter
import com.sangdol.roomescape.payment.business.domain.PaymentMethod
import com.sangdol.roomescape.payment.dto.*
import com.sangdol.roomescape.payment.infrastructure.persistence.CanceledPaymentEntity
import com.sangdol.roomescape.payment.infrastructure.persistence.PaymentEntity
import com.sangdol.roomescape.payment.infrastructure.persistence.PaymentRepository
import com.sangdol.roomescape.payment.infrastructure.persistence.*
import com.sangdol.roomescape.payment.mapper.toDetailEntity
import com.sangdol.roomescape.payment.mapper.toEntity
import com.sangdol.roomescape.payment.mapper.toEvent
import com.sangdol.roomescape.payment.mapper.toResponse
import com.sangdol.roomescape.reservation.dto.PendingReservationCreateRequest
import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationEntity
@ -33,7 +33,8 @@ class DummyInitializer(
private val scheduleRepository: ScheduleRepository,
private val reservationRepository: ReservationRepository,
private val paymentRepository: PaymentRepository,
private val paymentWriter: PaymentWriter
private val paymentDetailRepository: PaymentDetailRepository,
private val canceledPaymentRepository: CanceledPaymentRepository
) {
fun createStore(
@ -156,27 +157,6 @@ class DummyInitializer(
}
}
fun createExpiredOrCanceledReservation(
user: UserEntity,
status: ReservationStatus,
storeId: Long = IDGenerator.create(),
themeRequest: ThemeCreateRequest = ThemeFixture.createRequest,
scheduleRequest: ScheduleCreateRequest = ScheduleFixture.createRequest,
reservationRequest: PendingReservationCreateRequest = ReservationFixture.pendingCreateRequest,
): ReservationEntity {
return createPendingReservation(user, storeId, themeRequest, scheduleRequest, reservationRequest).apply {
this.status = status
}.also {
reservationRepository.save(it)
scheduleRepository.findByIdOrNull(it.scheduleId)?.let { schedule ->
schedule.status = ScheduleStatus.AVAILABLE
schedule.holdExpiredAt = null
scheduleRepository.save(schedule)
}
}
}
fun createPayment(
reservationId: Long,
request: PaymentConfirmRequest = PaymentFixture.confirmRequest,
@ -204,12 +184,10 @@ class DummyInitializer(
transferDetail = transferDetail
)
val payment = paymentWriter.createPayment(
reservationId = reservationId,
paymentGatewayResponse = clientConfirmResponse
)
val paymentEvent = clientConfirmResponse.toEvent(reservationId)
val payment = paymentRepository.save(paymentEvent.toEntity(IDGenerator.create()))
val detail = paymentWriter.createDetail(clientConfirmResponse, payment.id)
val detail = paymentDetailRepository.save(paymentEvent.toDetailEntity(IDGenerator.create(), payment.id))
return payment.toResponse(detail = detail.toResponse(), cancel = null)
}
@ -227,11 +205,14 @@ class DummyInitializer(
cancelReason = cancelReason,
)
return paymentWriter.cancel(
userId,
payment,
requestedAt = Instant.now(),
clientCancelResponse
)
return clientCancelResponse.cancels.toEntity(
id = IDGenerator.create(),
paymentId = payment.id,
cancelRequestedAt = Instant.now(),
canceledBy = userId
).also {
canceledPaymentRepository.save(it)
}
}
}

View File

@ -1,7 +1,8 @@
package com.sangdol.roomescape.supports
import com.sangdol.roomescape.admin.infrastructure.persistence.AdminRepository
import com.sangdol.roomescape.payment.business.PaymentWriter
import com.sangdol.roomescape.payment.infrastructure.persistence.CanceledPaymentRepository
import com.sangdol.roomescape.payment.infrastructure.persistence.PaymentDetailRepository
import com.sangdol.roomescape.payment.infrastructure.persistence.PaymentRepository
import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationRepository
import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleRepository
@ -43,13 +44,7 @@ abstract class FunSpecSpringbootTest(
}
}) {
@Autowired
private lateinit var userRepository: UserRepository
@Autowired
private lateinit var adminRepository: AdminRepository
@Autowired
private lateinit var storeRepository: StoreRepository
lateinit var testAuthUtil: TestAuthUtil
@Autowired
lateinit var dummyInitializer: DummyInitializer
@ -57,32 +52,40 @@ abstract class FunSpecSpringbootTest(
@LocalServerPort
var port: Int = 0
lateinit var testAuthUtil: TestAuthUtil
override suspend fun beforeSpec(spec: Spec) {
RestAssured.port = port
testAuthUtil = TestAuthUtil(userRepository, adminRepository, storeRepository)
}
}
@TestConfiguration
class TestConfig {
@Bean
fun testAuthUtil(
userRepository: UserRepository,
adminRepository: AdminRepository,
storeRepository: StoreRepository
): TestAuthUtil {
return TestAuthUtil(userRepository, adminRepository, storeRepository)
}
@Bean
fun dummyInitializer(
storeRepository: StoreRepository,
themeRepository: ThemeRepository,
scheduleRepository: ScheduleRepository,
reservationRepository: ReservationRepository,
paymentWriter: PaymentWriter,
paymentRepository: PaymentRepository
paymentRepository: PaymentRepository,
paymentDetailRepository: PaymentDetailRepository,
canceledPaymentRepository: CanceledPaymentRepository
): DummyInitializer {
return DummyInitializer(
themeRepository = themeRepository,
scheduleRepository = scheduleRepository,
reservationRepository = reservationRepository,
paymentWriter = paymentWriter,
paymentRepository = paymentRepository,
storeRepository = storeRepository
storeRepository = storeRepository,
paymentDetailRepository = paymentDetailRepository,
canceledPaymentRepository = canceledPaymentRepository
)
}
}