|
| 1 | +package com.loopers.application.payment |
| 2 | + |
| 3 | +import com.loopers.domain.payment.Payment |
| 4 | +import com.loopers.domain.payment.PaymentEvent |
| 5 | +import com.loopers.domain.payment.PaymentEventPublisher |
| 6 | +import com.loopers.domain.payment.PaymentRelay |
| 7 | +import com.loopers.domain.payment.PaymentRepository |
| 8 | +import com.loopers.domain.payment.TransactionKeyGenerator |
| 9 | +import com.loopers.domain.user.UserInfo |
| 10 | +import com.loopers.support.error.CoreException |
| 11 | +import com.loopers.support.error.ErrorType |
| 12 | +import org.springframework.stereotype.Component |
| 13 | +import org.springframework.transaction.annotation.Transactional |
| 14 | + |
| 15 | +@Component |
| 16 | +class PaymentApplicationService( |
| 17 | + private val paymentRepository: PaymentRepository, |
| 18 | + private val paymentEventPublisher: PaymentEventPublisher, |
| 19 | + private val paymentRelay: PaymentRelay, |
| 20 | + private val transactionKeyGenerator: TransactionKeyGenerator, |
| 21 | +) { |
| 22 | + companion object { |
| 23 | + private val RATE_LIMIT_EXCEEDED = (1..20) |
| 24 | + private val RATE_INVALID_CARD = (21..30) |
| 25 | + } |
| 26 | + |
| 27 | + @Transactional |
| 28 | + fun createTransaction(command: PaymentCommand.CreateTransaction): TransactionInfo { |
| 29 | + command.validate() |
| 30 | + |
| 31 | + val transactionKey = transactionKeyGenerator.generate() |
| 32 | + val payment = paymentRepository.save( |
| 33 | + Payment( |
| 34 | + transactionKey = transactionKey, |
| 35 | + userId = command.userId, |
| 36 | + orderId = command.orderId, |
| 37 | + cardType = command.cardType, |
| 38 | + cardNo = command.cardNo, |
| 39 | + amount = command.amount, |
| 40 | + callbackUrl = command.callbackUrl, |
| 41 | + ), |
| 42 | + ) |
| 43 | + |
| 44 | + paymentEventPublisher.publish(PaymentEvent.PaymentCreated.from(payment = payment)) |
| 45 | + |
| 46 | + return TransactionInfo.from(payment) |
| 47 | + } |
| 48 | + |
| 49 | + @Transactional(readOnly = true) |
| 50 | + fun getTransactionDetailInfo(userInfo: UserInfo, transactionKey: String): TransactionInfo { |
| 51 | + val payment = paymentRepository.findByTransactionKey(userId = userInfo.userId, transactionKey = transactionKey) |
| 52 | + ?: throw CoreException(ErrorType.NOT_FOUND, "(transactionKey: $transactionKey) 결제건이 존재하지 않습니다.") |
| 53 | + return TransactionInfo.from(payment) |
| 54 | + } |
| 55 | + |
| 56 | + @Transactional(readOnly = true) |
| 57 | + fun findTransactionsByOrderId(userInfo: UserInfo, orderId: String): OrderInfo { |
| 58 | + val payments = paymentRepository.findByOrderId(userId = userInfo.userId, orderId = orderId) |
| 59 | + if (payments.isEmpty()) { |
| 60 | + throw CoreException(ErrorType.NOT_FOUND, "(orderId: $orderId) 에 해당하는 결제건이 존재하지 않습니다.") |
| 61 | + } |
| 62 | + |
| 63 | + return OrderInfo( |
| 64 | + orderId = orderId, |
| 65 | + transactions = payments.map { TransactionInfo.from(it) }, |
| 66 | + ) |
| 67 | + } |
| 68 | + |
| 69 | + @Transactional |
| 70 | + fun handle(transactionKey: String) { |
| 71 | + val payment = paymentRepository.findByTransactionKey(transactionKey) |
| 72 | + ?: throw CoreException(ErrorType.NOT_FOUND, "(transactionKey: $transactionKey) 결제건이 존재하지 않습니다.") |
| 73 | + |
| 74 | + val rate = (1..100).random() |
| 75 | + when (rate) { |
| 76 | + in RATE_LIMIT_EXCEEDED -> payment.limitExceeded() |
| 77 | + in RATE_INVALID_CARD -> payment.invalidCard() |
| 78 | + else -> payment.approve() |
| 79 | + } |
| 80 | + paymentEventPublisher.publish(event = PaymentEvent.PaymentHandled.from(payment)) |
| 81 | + } |
| 82 | + |
| 83 | + fun notifyTransactionResult(transactionKey: String) { |
| 84 | + val payment = paymentRepository.findByTransactionKey(transactionKey) |
| 85 | + ?: throw CoreException(ErrorType.NOT_FOUND, "(transactionKey: $transactionKey) 결제건이 존재하지 않습니다.") |
| 86 | + paymentRelay.notify(callbackUrl = payment.callbackUrl, transactionInfo = TransactionInfo.from(payment)) |
| 87 | + } |
| 88 | +} |
0 commit comments