diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanController.kt index e002aba4..6418cd14 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanController.kt @@ -27,7 +27,7 @@ class CanController(private val service: CanService) { @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { if (member == null) { - throw SodaException("로그인 정보를 확인해주세요.") + throw SodaException(messageKey = "common.error.bad_credentials") } ApiResponse.ok(service.getCanStatus(member, container)) @@ -41,7 +41,7 @@ class CanController(private val service: CanService) { pageable: Pageable ) = run { if (member == null) { - throw SodaException("로그인 정보를 확인해주세요.") + throw SodaException(messageKey = "common.error.bad_credentials") } ApiResponse.ok(service.getCanUseStatus(member, pageable, timezone, container)) @@ -55,7 +55,7 @@ class CanController(private val service: CanService) { pageable: Pageable ) = run { if (member == null) { - throw SodaException("로그인 정보를 확인해주세요.") + throw SodaException(messageKey = "common.error.bad_credentials") } ApiResponse.ok(service.getCanChargeStatus(member, pageable, timezone, container)) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeController.kt index 73948f3b..042da59f 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeController.kt @@ -33,7 +33,7 @@ class ChargeController( @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { if (member == null) { - throw SodaException("로그인 정보를 확인해주세요.") + throw SodaException(messageKey = "common.error.bad_credentials") } ApiResponse.ok(service.payverseCharge(member, request)) @@ -45,7 +45,7 @@ class ChargeController( @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { if (member == null) { - throw SodaException("로그인 정보를 확인해주세요.") + throw SodaException(messageKey = "common.error.bad_credentials") } val response = service.payverseVerify(memberId = member.id!!, verifyRequest) @@ -83,7 +83,7 @@ class ChargeController( @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { if (member == null) { - throw SodaException("로그인 정보를 확인해주세요.") + throw SodaException(messageKey = "common.error.bad_credentials") } ApiResponse.ok(service.charge(member, chargeRequest)) @@ -95,7 +95,7 @@ class ChargeController( @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { if (member == null) { - throw SodaException("로그인 정보를 확인해주세요.") + throw SodaException(messageKey = "common.error.bad_credentials") } val response = service.verify(memberId = member.id!!, verifyRequest) @@ -109,7 +109,7 @@ class ChargeController( @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { if (member == null) { - throw SodaException("로그인 정보를 확인해주세요.") + throw SodaException(messageKey = "common.error.bad_credentials") } val response = service.verifyHecto(memberId = member.id!!, verifyRequest) @@ -123,7 +123,7 @@ class ChargeController( @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { if (member == null) { - throw SodaException("로그인 정보를 확인해주세요.") + throw SodaException(messageKey = "common.error.bad_credentials") } ApiResponse.ok(service.appleCharge(member, chargeRequest)) @@ -135,7 +135,7 @@ class ChargeController( @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { if (member == null) { - throw SodaException("로그인 정보를 확인해주세요.") + throw SodaException(messageKey = "common.error.bad_credentials") } val response = service.appleVerify(memberId = member.id!!, verifyRequest) @@ -149,7 +149,7 @@ class ChargeController( @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { if (member == null) { - throw SodaException("로그인 정보를 확인해주세요.") + throw SodaException(messageKey = "common.error.bad_credentials") } if (request.paymentGateway == PaymentGateway.GOOGLE_IAP) { @@ -174,7 +174,7 @@ class ChargeController( trackingCharge(member, response) ApiResponse.ok(Unit) } else { - throw SodaException("결제정보에 오류가 있습니다.") + throw SodaException(messageKey = "can.charge.invalid_payment_info") } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeService.kt index c3610413..2c631b16 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeService.kt @@ -11,6 +11,8 @@ import kr.co.vividnext.sodalive.can.payment.PaymentGateway import kr.co.vividnext.sodalive.can.payment.PaymentStatus import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.google.GooglePlayService +import kr.co.vividnext.sodalive.i18n.LangContext +import kr.co.vividnext.sodalive.i18n.SodaMessageSource import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.member.MemberRepository import kr.co.vividnext.sodalive.point.MemberPoint @@ -53,6 +55,8 @@ class ChargeService( private val applicationEventPublisher: ApplicationEventPublisher, private val googlePlayService: GooglePlayService, + private val messageSource: SodaMessageSource, + private val langContext: LangContext, @Value("\${bootpay.application-id}") private val bootpayApplicationId: String, @@ -174,10 +178,10 @@ class ChargeService( @Transactional fun chargeByCoupon(couponNumber: String, member: Member): String { val canCouponNumber = couponNumberRepository.findByCouponNumber(couponNumber = couponNumber) - ?: throw SodaException("잘못된 쿠폰번호입니다.\n고객센터로 문의해 주시기 바랍니다.") + ?: throw SodaException(messageKey = "can.coupon.invalid_number_contact") if (canCouponNumber.member != null) { - throw SodaException("이미 사용한 쿠폰번호 입니다.") + throw SodaException(messageKey = "can.coupon.already_used") } canCouponNumber.member = member @@ -186,7 +190,7 @@ class ChargeService( when (coupon.couponType) { CouponType.CAN -> { val couponCharge = Charge(0, coupon.can, status = ChargeStatus.COUPON) - couponCharge.title = "${coupon.can} 캔" + couponCharge.title = formatMessage("can.charge.title", coupon.can) couponCharge.member = member val payment = Payment( @@ -198,7 +202,7 @@ class ChargeService( chargeRepository.save(couponCharge) member.charge(0, coupon.can, "pg") - return "쿠폰 사용이 완료되었습니다.\n${coupon.can}캔이 지급되었습니다." + return formatMessage("can.coupon.use_complete", coupon.can) } CouponType.POINT -> { @@ -226,7 +230,7 @@ class ChargeService( ) ) - return "쿠폰 사용이 완료되었습니다.\n${coupon.can}포인트가 지급되었습니다." + return formatMessage("can.coupon.use_complete_point", coupon.can) } } } @@ -234,7 +238,7 @@ class ChargeService( @Transactional fun payverseCharge(member: Member, request: PayverseChargeRequest): PayverseChargeResponse { val can = canRepository.findByIdOrNull(request.canId) - ?: throw SodaException("잘못된 요청입니다\n앱 종료 후 다시 시도해 주세요.") + ?: throw SodaException(messageKey = "can.charge.invalid_request_restart") val requestCurrency = can.currency val isKrw = requestCurrency == "KRW" @@ -304,9 +308,9 @@ class ChargeService( @Transactional fun payverseVerify(memberId: Long, verifyRequest: PayverseVerifyRequest): ChargeCompleteResponse { val charge = chargeRepository.findByIdOrNull(verifyRequest.orderId.toLong()) - ?: throw SodaException("결제정보에 오류가 있습니다.") + ?: throw SodaException(messageKey = "can.charge.invalid_payment_info") val member = memberRepository.findByIdOrNull(memberId) - ?: throw SodaException("로그인 정보를 확인해주세요.") + ?: throw SodaException(messageKey = "common.error.bad_credentials") val isKrw = charge.can?.currency == "KRW" val mid = if (isKrw) { @@ -322,7 +326,7 @@ class ChargeService( // 결제수단 확인 if (charge.payment?.paymentGateway != PaymentGateway.PAYVERSE) { - throw SodaException("결제정보에 오류가 있습니다.") + throw SodaException(messageKey = "can.charge.invalid_payment_info") } // 결제 상태에 따른 분기 처리 @@ -339,10 +343,11 @@ class ChargeService( val response = okHttpClient.newCall(request).execute() if (!response.isSuccessful) { - throw SodaException("결제정보에 오류가 있습니다.") + throw SodaException(messageKey = "can.charge.invalid_payment_info") } - val body = response.body?.string() ?: throw SodaException("결제정보에 오류가 있습니다.") + val body = response.body?.string() + ?: throw SodaException(messageKey = "can.charge.invalid_payment_info") val verifyResponse = objectMapper.readValue(body, PayverseVerifyResponse::class.java) val customerId = "${serverEnv}_user_${member.id!!}" @@ -380,10 +385,10 @@ class ChargeService( isFirstCharged = chargeRepository.isFirstCharged(memberId) ) } else { - throw SodaException("결제정보에 오류가 있습니다.") + throw SodaException(messageKey = "can.charge.invalid_payment_info") } } catch (_: Exception) { - throw SodaException("결제정보에 오류가 있습니다.") + throw SodaException(messageKey = "can.charge.invalid_payment_info") } } @@ -397,7 +402,7 @@ class ChargeService( } else -> { - throw SodaException("결제정보에 오류가 있습니다.") + throw SodaException(messageKey = "can.charge.invalid_payment_info") } } } @@ -405,7 +410,7 @@ class ChargeService( @Transactional fun charge(member: Member, request: ChargeRequest): ChargeResponse { val can = canRepository.findByIdOrNull(request.canId) - ?: throw SodaException("잘못된 요청입니다\n앱 종료 후 다시 시도해 주세요.") + ?: throw SodaException(messageKey = "can.charge.invalid_request_restart") val charge = Charge(can.can, can.rewardCan) charge.title = can.title @@ -424,9 +429,9 @@ class ChargeService( @Transactional fun verify(memberId: Long, verifyRequest: VerifyRequest): ChargeCompleteResponse { val charge = chargeRepository.findByIdOrNull(verifyRequest.orderId.toLong()) - ?: throw SodaException("결제정보에 오류가 있습니다.") + ?: throw SodaException(messageKey = "can.charge.invalid_payment_info") val member = memberRepository.findByIdOrNull(memberId) - ?: throw SodaException("로그인 정보를 확인해주세요.") + ?: throw SodaException(messageKey = "common.error.bad_credentials") if (charge.payment!!.paymentGateway == PaymentGateway.PG) { val bootpay = Bootpay(bootpayApplicationId, bootpayPrivateKey) @@ -457,22 +462,22 @@ class ChargeService( isFirstCharged = chargeRepository.isFirstCharged(memberId) ) } else { - throw SodaException("결제정보에 오류가 있습니다.") + throw SodaException(messageKey = "can.charge.invalid_payment_info") } } catch (_: Exception) { - throw SodaException("결제정보에 오류가 있습니다.") + throw SodaException(messageKey = "can.charge.invalid_payment_info") } } else { - throw SodaException("결제정보에 오류가 있습니다.") + throw SodaException(messageKey = "can.charge.invalid_payment_info") } } @Transactional fun verifyHecto(memberId: Long, verifyRequest: VerifyRequest): ChargeCompleteResponse { val charge = chargeRepository.findByIdOrNull(verifyRequest.orderId.toLong()) - ?: throw SodaException("결제정보에 오류가 있습니다.") + ?: throw SodaException(messageKey = "can.charge.invalid_payment_info") val member = memberRepository.findByIdOrNull(memberId) - ?: throw SodaException("로그인 정보를 확인해주세요.") + ?: throw SodaException(messageKey = "common.error.bad_credentials") if (charge.payment!!.paymentGateway == PaymentGateway.PG) { val bootpay = Bootpay(bootpayHectoApplicationId, bootpayHectoPrivateKey) @@ -507,13 +512,13 @@ class ChargeService( isFirstCharged = chargeRepository.isFirstCharged(memberId) ) } else { - throw SodaException("결제정보에 오류가 있습니다.") + throw SodaException(messageKey = "can.charge.invalid_payment_info") } } catch (_: Exception) { - throw SodaException("결제정보에 오류가 있습니다.") + throw SodaException(messageKey = "can.charge.invalid_payment_info") } } else { - throw SodaException("결제정보에 오류가 있습니다.") + throw SodaException(messageKey = "can.charge.invalid_payment_info") } } @@ -542,15 +547,17 @@ class ChargeService( @Transactional fun appleVerify(memberId: Long, verifyRequest: AppleVerifyRequest): ChargeCompleteResponse { val charge = chargeRepository.findByIdOrNull(verifyRequest.chargeId) - ?: throw SodaException("결제정보에 오류가 있습니다.") + ?: throw SodaException(messageKey = "can.charge.invalid_payment_info") val member = memberRepository.findByIdOrNull(memberId) - ?: throw SodaException("로그인 정보를 확인해주세요.") + ?: throw SodaException(messageKey = "common.error.bad_credentials") if (charge.payment!!.paymentGateway == PaymentGateway.APPLE_IAP) { // 검증로직 if (requestRealServerVerify(verifyRequest)) { charge.payment?.receiptId = verifyRequest.receiptString - charge.payment?.method = "애플(인 앱 결제)" + charge.payment?.method = messageSource + .getMessage("can.charge.payment_method.apple_iap", langContext.lang) + .orEmpty() charge.payment?.status = PaymentStatus.COMPLETE member.charge(charge.chargeCan, charge.rewardCan, "ios") @@ -567,10 +574,10 @@ class ChargeService( isFirstCharged = chargeRepository.isFirstCharged(memberId) ) } else { - throw SodaException("결제정보에 오류가 있습니다.") + throw SodaException(messageKey = "can.charge.invalid_payment_info") } } else { - throw SodaException("결제정보에 오류가 있습니다.") + throw SodaException(messageKey = "can.charge.invalid_payment_info") } } @@ -594,7 +601,9 @@ class ChargeService( payment.locale = currencyCode payment.price = price payment.receiptId = purchaseToken - payment.method = "구글(인 앱 결제)" + payment.method = messageSource + .getMessage("can.charge.payment_method.google_iap", langContext.lang) + .orEmpty() charge.payment = payment chargeRepository.save(charge) @@ -610,9 +619,9 @@ class ChargeService( purchaseToken: String ): ChargeCompleteResponse { val charge = chargeRepository.findByIdOrNull(id = chargeId) - ?: throw SodaException("결제정보에 오류가 있습니다.") + ?: throw SodaException(messageKey = "can.charge.invalid_payment_info") val member = memberRepository.findByIdOrNull(id = memberId) - ?: throw SodaException("로그인 정보를 확인해주세요.") + ?: throw SodaException(messageKey = "common.error.bad_credentials") if (charge.payment!!.status == PaymentStatus.REQUEST) { val orderId = verifyPurchase(purchaseToken, productId) @@ -634,10 +643,10 @@ class ChargeService( isFirstCharged = chargeRepository.isFirstCharged(memberId) ) } else { - throw SodaException("구매를 하지 못했습니다.\n고객센터로 문의해 주세요") + throw SodaException(messageKey = "can.charge.purchase_failed_contact") } } else { - throw SodaException("결제정보에 오류가 있습니다.") + throw SodaException(messageKey = "can.charge.invalid_payment_info") } } @@ -670,14 +679,14 @@ class ChargeService( } else -> { - throw SodaException("결제정보에 오류가 있습니다.") + throw SodaException(messageKey = "can.charge.invalid_payment_info") } } } else { - throw SodaException("결제를 완료하지 못했습니다.") + throw SodaException(messageKey = "can.charge.payment_incomplete") } } else { - throw SodaException("결제를 완료하지 못했습니다.") + throw SodaException(messageKey = "can.charge.payment_incomplete") } } @@ -701,23 +710,31 @@ class ChargeService( } else -> { - throw SodaException("결제정보에 오류가 있습니다.") + throw SodaException(messageKey = "can.charge.invalid_payment_info") } } } else { - throw SodaException("결제를 완료하지 못했습니다.") + throw SodaException(messageKey = "can.charge.payment_incomplete") } } else { - throw SodaException("결제를 완료하지 못했습니다.") + throw SodaException(messageKey = "can.charge.payment_incomplete") } } + private fun formatMessage(key: String, vararg args: Any): String { + val template = messageSource.getMessage(key, langContext.lang) ?: return "" + return String.format(template, *args) + } + // Payverse 결제수단 매핑: 특정 schemeCode는 "카드"로 표기, 아니면 null 반환 private fun mapPayverseSchemeToMethodByCode(schemeCode: String?): String? { val cardCodes = setOf( "041", "044", "361", "364", "365", "366", "367", "368", "369", "370", "371", "372", "373", "374", "381", "218", "071", "002", "089", "045", "050", "048", "090", "092" ) - return if (schemeCode != null && cardCodes.contains(schemeCode)) "카드" else null + if (schemeCode == null || !cardCodes.contains(schemeCode)) { + return null + } + return messageSource.getMessage("can.charge.payment_method.card", langContext.lang) } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/event/ChargeEventService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/event/ChargeEventService.kt index a37ebb1d..625377c1 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/event/ChargeEventService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/event/ChargeEventService.kt @@ -9,6 +9,8 @@ import kr.co.vividnext.sodalive.can.payment.PaymentStatus import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.fcm.FcmEvent import kr.co.vividnext.sodalive.fcm.FcmEventType +import kr.co.vividnext.sodalive.i18n.LangContext +import kr.co.vividnext.sodalive.i18n.SodaMessageSource import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.member.MemberRepository import kr.co.vividnext.sodalive.member.auth.AuthRepository @@ -26,15 +28,17 @@ class ChargeEventService( private val memberRepository: MemberRepository, private val chargeRepository: ChargeRepository, private val chargeEventRepository: ChargeEventRepository, - private val applicationEventPublisher: ApplicationEventPublisher + private val applicationEventPublisher: ApplicationEventPublisher, + private val messageSource: SodaMessageSource, + private val langContext: LangContext ) { @Transactional fun applyChargeEvent(chargeId: Long, memberId: Long) { val charge = chargeRepository.findByIdOrNull(chargeId) - ?: throw SodaException("이벤트가 적용되지 않았습니다.\n고객센터에 문의해 주세요.") + ?: throw SodaException(messageKey = "can.charge.event.not_applied_contact") val member = memberRepository.findByIdOrNull(memberId) - ?: throw SodaException("이벤트가 적용되지 않았습니다.\n고객센터에 문의해 주세요.") + ?: throw SodaException(messageKey = "can.charge.event.not_applied_contact") if (member.auth != null) { val authDate = authRepository.getOldestCreatedAtByDi(member.auth!!.di) @@ -79,7 +83,10 @@ class ChargeEventService( FcmEvent( type = FcmEventType.INDIVIDUAL, title = chargeEvent.title, - message = "$additionalCan 캔이 추가 지급되었습니다.", + message = formatMessage( + "can.charge.event.additional_can_paid", + additionalCan + ), recipients = listOf(member.id!!), isAuth = null ) @@ -94,14 +101,21 @@ class ChargeEventService( additionalCan = additionalCan, member = member, paymentGateway = charge.payment?.paymentGateway!!, - method = "첫 충전 이벤트" + method = messageSource + .getMessage("can.charge.event.first_title", langContext.lang) + .orEmpty() ) applicationEventPublisher.publishEvent( FcmEvent( type = FcmEventType.INDIVIDUAL, - title = "첫 충전 이벤트", - message = "$additionalCan 캔이 추가 지급되었습니다.", + title = messageSource + .getMessage("can.charge.event.first_title", langContext.lang) + .orEmpty(), + message = formatMessage( + "can.charge.event.additional_can_paid", + additionalCan + ), recipients = listOf(member.id!!), isAuth = null ) @@ -110,7 +124,7 @@ class ChargeEventService( private fun applyEvent(additionalCan: Int, member: Member, paymentGateway: PaymentGateway, method: String) { val eventCharge = Charge(0, additionalCan, status = ChargeStatus.EVENT) - eventCharge.title = "$additionalCan 캔" + eventCharge.title = formatMessage("can.charge.title", additionalCan) eventCharge.member = member val payment = Payment( @@ -127,4 +141,9 @@ class ChargeEventService( else -> member.charge(0, additionalCan, "pg") } } + + private fun formatMessage(key: String, vararg args: Any): String { + val template = messageSource.getMessage(key, langContext.lang) ?: return "" + return String.format(template, *args) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/temp/ChargeTempController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/temp/ChargeTempController.kt index aafbc790..b1943d43 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/temp/ChargeTempController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/temp/ChargeTempController.kt @@ -20,7 +20,7 @@ class ChargeTempController(private val service: ChargeTempService) { @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { if (member == null) { - throw SodaException("로그인 정보를 확인해주세요.") + throw SodaException(messageKey = "common.error.bad_credentials") } ApiResponse.ok(service.charge(member, request)) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/temp/ChargeTempService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/temp/ChargeTempService.kt index 3f2f1aa4..2eaa248a 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/temp/ChargeTempService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/temp/ChargeTempService.kt @@ -12,6 +12,8 @@ import kr.co.vividnext.sodalive.can.payment.PaymentGateway import kr.co.vividnext.sodalive.can.payment.PaymentStatus import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.extensions.moneyFormat +import kr.co.vividnext.sodalive.i18n.LangContext +import kr.co.vividnext.sodalive.i18n.SodaMessageSource import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.member.MemberRepository import org.springframework.beans.factory.annotation.Value @@ -27,6 +29,8 @@ class ChargeTempService( private val memberRepository: MemberRepository, private val objectMapper: ObjectMapper, + private val messageSource: SodaMessageSource, + private val langContext: LangContext, @Value("\${bootpay.hecto-application-id}") private val bootpayApplicationId: String, @@ -37,7 +41,7 @@ class ChargeTempService( @Transactional fun charge(member: Member, request: ChargeTempRequest): ChargeResponse { val charge = Charge(request.can, 0) - charge.title = "${request.can.moneyFormat()} 캔" + charge.title = formatMessage("can.charge.title", request.can.moneyFormat()) charge.member = member val payment = Payment(paymentGateway = request.paymentGateway) @@ -52,9 +56,9 @@ class ChargeTempService( @Transactional fun verify(user: User, verifyRequest: VerifyRequest) { val charge = chargeRepository.findByIdOrNull(verifyRequest.orderId.toLong()) - ?: throw SodaException("결제정보에 오류가 있습니다.") + ?: throw SodaException(messageKey = "can.charge.invalid_payment_info") val member = memberRepository.findByEmail(user.username) - ?: throw SodaException("로그인 정보를 확인해주세요.") + ?: throw SodaException(messageKey = "common.error.bad_credentials") if (charge.payment!!.paymentGateway == PaymentGateway.PG) { val bootpay = Bootpay(bootpayApplicationId, bootpayPrivateKey) @@ -72,13 +76,18 @@ class ChargeTempService( charge.payment?.status = PaymentStatus.COMPLETE member.charge(charge.chargeCan, charge.rewardCan, "pg") } else { - throw SodaException("결제정보에 오류가 있습니다.") + throw SodaException(messageKey = "can.charge.invalid_payment_info") } } catch (_: Exception) { - throw SodaException("결제정보에 오류가 있습니다.") + throw SodaException(messageKey = "can.charge.invalid_payment_info") } } else { - throw SodaException("결제정보에 오류가 있습니다.") + throw SodaException(messageKey = "can.charge.invalid_payment_info") } } + + private fun formatMessage(key: String, vararg args: Any): String { + val template = messageSource.getMessage(key, langContext.lang) ?: return "" + return String.format(template, *args) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/coupon/CanCouponController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/coupon/CanCouponController.kt index 628145ac..dfa4f37c 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/coupon/CanCouponController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/coupon/CanCouponController.kt @@ -2,6 +2,8 @@ package kr.co.vividnext.sodalive.can.coupon import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.common.SodaException +import kr.co.vividnext.sodalive.i18n.LangContext +import kr.co.vividnext.sodalive.i18n.SodaMessageSource import kr.co.vividnext.sodalive.member.Member import org.springframework.core.io.InputStreamResource import org.springframework.data.domain.Pageable @@ -22,14 +24,18 @@ import java.nio.charset.StandardCharsets @RestController @RequestMapping("/can/coupon") -class CanCouponController(private val service: CanCouponService) { +class CanCouponController( + private val service: CanCouponService, + private val messageSource: SodaMessageSource, + private val langContext: LangContext +) { @PostMapping @PreAuthorize("hasRole('ADMIN')") fun generateCoupon( @RequestBody request: GenerateCanCouponRequest, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { - if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + if (member == null) throw SodaException(messageKey = "common.error.bad_credentials") ApiResponse.ok(service.generateCoupon(request)) } @@ -40,7 +46,7 @@ class CanCouponController(private val service: CanCouponService) { @RequestBody request: ModifyCanCouponRequest, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { - if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + if (member == null) throw SodaException(messageKey = "common.error.bad_credentials") ApiResponse.ok(service.modifyCoupon(request)) } @@ -51,7 +57,7 @@ class CanCouponController(private val service: CanCouponService) { @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, pageable: Pageable ) = run { - if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + if (member == null) throw SodaException(messageKey = "common.error.bad_credentials") ApiResponse.ok(service.getCouponList(offset = pageable.offset, limit = pageable.pageSize.toLong())) } @@ -63,7 +69,7 @@ class CanCouponController(private val service: CanCouponService) { @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, pageable: Pageable ) = run { - if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + if (member == null) throw SodaException(messageKey = "common.error.bad_credentials") ApiResponse.ok( service.getCouponNumberList( @@ -80,9 +86,11 @@ class CanCouponController(private val service: CanCouponService) { @RequestParam couponId: Long, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { - if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + if (member == null) throw SodaException(messageKey = "common.error.bad_credentials") - val fileName = "쿠폰번호리스트.xlsx" + val fileName = messageSource + .getMessage("can.coupon.download_filename", langContext.lang) + .orEmpty() val encodedFileName = URLEncoder.encode( fileName, StandardCharsets.UTF_8.toString() @@ -107,7 +115,7 @@ class CanCouponController(private val service: CanCouponService) { @RequestBody request: UseCanCouponRequest, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { - if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + if (member == null) throw SodaException(messageKey = "common.error.bad_credentials") val completeMessage = service.useCanCoupon( couponNumber = request.couponNumber, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/coupon/CanCouponIssueService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/coupon/CanCouponIssueService.kt index 2af50b14..3492d8e9 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/coupon/CanCouponIssueService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/coupon/CanCouponIssueService.kt @@ -12,7 +12,7 @@ class CanCouponIssueService(private val couponNumberRepository: CanCouponNumberR if (!isMultipleUse(canCouponNumber)) { val canCouponNumberList = couponNumberRepository.findByMemberId(memberId = memberId) if (canCouponNumberList.isNotEmpty()) { - throw SodaException("해당 쿠폰은 1회만 충전이 가능합니다.") + throw SodaException(messageKey = "can.coupon.single_use_only") } } @@ -21,10 +21,10 @@ class CanCouponIssueService(private val couponNumberRepository: CanCouponNumberR private fun checkCanCouponNumber(couponNumber: String): CanCouponNumber { val canCouponNumber = couponNumberRepository.findByCouponNumber(couponNumber = couponNumber) - ?: throw SodaException("잘못된 쿠폰번호입니다.\n고객센터로 문의해 주시기 바랍니다.") + ?: throw SodaException(messageKey = "can.coupon.invalid_number_contact") if (canCouponNumber.member != null) { - throw SodaException("이미 사용한 쿠폰번호 입니다.") + throw SodaException(messageKey = "can.coupon.already_used") } return canCouponNumber @@ -34,17 +34,17 @@ class CanCouponIssueService(private val couponNumberRepository: CanCouponNumberR private fun validateCoupon(canCoupon: CanCoupon) { if (canCoupon.validity < LocalDateTime.now()) { - throw SodaException("유효기간이 경과된 쿠폰입니다.") + throw SodaException(messageKey = "can.coupon.expired") } if (!canCoupon.isActive) { - throw SodaException("이용이 불가능한 쿠폰입니다.") + throw SodaException(messageKey = "can.coupon.inactive") } } fun checkAnyChanges(request: ModifyCanCouponRequest) { if (request.isMultipleUse == null && request.isActive == null && request.validity == null) { - throw SodaException("변경사항이 없습니다.") + throw SodaException(messageKey = "can.coupon.no_changes") } } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/coupon/CanCouponService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/coupon/CanCouponService.kt index ff882833..54f17365 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/coupon/CanCouponService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/coupon/CanCouponService.kt @@ -5,6 +5,8 @@ import kr.co.vividnext.sodalive.aws.sqs.SqsEvent import kr.co.vividnext.sodalive.aws.sqs.SqsEventType import kr.co.vividnext.sodalive.can.charge.ChargeService import kr.co.vividnext.sodalive.common.SodaException +import kr.co.vividnext.sodalive.i18n.LangContext +import kr.co.vividnext.sodalive.i18n.SodaMessageSource import kr.co.vividnext.sodalive.member.MemberRepository import org.apache.poi.xssf.usermodel.XSSFWorkbook import org.springframework.context.ApplicationEventPublisher @@ -29,7 +31,9 @@ class CanCouponService( private val memberRepository: MemberRepository, private val objectMapper: ObjectMapper, - private val applicationEventPublisher: ApplicationEventPublisher + private val applicationEventPublisher: ApplicationEventPublisher, + private val messageSource: SodaMessageSource, + private val langContext: LangContext ) { fun generateCoupon(request: GenerateCanCouponRequest) { val message = objectMapper.writeValueAsString(request) @@ -41,7 +45,7 @@ class CanCouponService( issueService.checkAnyChanges(request) val canCoupon = repository.findByIdOrNull(id = request.couponId) - ?: throw SodaException("잘못된 쿠폰번호입니다.\n고객센터로 문의해 주시기 바랍니다.") + ?: throw SodaException(messageKey = "can.coupon.invalid_number_contact") if (request.validity != null) { val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") @@ -51,7 +55,7 @@ class CanCouponService( .toLocalDateTime() if (validity <= canCoupon.validity) { - throw SodaException("유효기간은 기존 유효기간 이후 날짜로 설정하실 수 있습니다.") + throw SodaException(messageKey = "can.coupon.validity_after_current") } canCoupon.validity = validity @@ -85,7 +89,11 @@ class CanCouponService( } fun downloadCouponNumberList(couponId: Long): ByteArrayInputStream { - val header = listOf("순번", "쿠폰번호", "사용여부") + val header = listOf( + messageSource.getMessage("can.coupon.download_header.index", langContext.lang).orEmpty(), + messageSource.getMessage("can.coupon.download_header.number", langContext.lang).orEmpty(), + messageSource.getMessage("can.coupon.download_header.used", langContext.lang).orEmpty() + ) val byteArrayOutputStream = ByteArrayOutputStream() val couponNumberList = couponNumberRepository.getAllCouponNumberList(couponId) @@ -104,9 +112,9 @@ class CanCouponService( couponNumberRow.createCell(1).setCellValue(insertHyphens(item.couponNumber)) couponNumberRow.createCell(2).setCellValue( if (item.isUsed) { - "O" + messageSource.getMessage("can.coupon.download_used_mark", langContext.lang).orEmpty() } else { - "X" + messageSource.getMessage("can.coupon.download_unused_mark", langContext.lang).orEmpty() } ) } @@ -114,7 +122,7 @@ class CanCouponService( workbook.write(byteArrayOutputStream) return ByteArrayInputStream(byteArrayOutputStream.toByteArray()) } catch (e: IOException) { - throw SodaException("다운로드를 하지 못했습니다.\n다시 시도해 주세요.") + throw SodaException(messageKey = "can.coupon.download_failed_retry") } finally { workbook.close() byteArrayOutputStream.close() @@ -123,9 +131,9 @@ class CanCouponService( fun useCanCoupon(couponNumber: String, memberId: Long): String { val member = memberRepository.findByIdOrNull(id = memberId) - ?: throw SodaException("로그인 정보를 확인해주세요.") + ?: throw SodaException(messageKey = "common.error.bad_credentials") - if (member.auth == null) throw SodaException("쿠폰은 본인인증을 하셔야 사용이 가능합니다.") + if (member.auth == null) throw SodaException(messageKey = "can.coupon.auth_required") issueService.validateAvailableUseCoupon(couponNumber, memberId) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/CanPaymentService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/CanPaymentService.kt index ef43b378..ffc8101b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/CanPaymentService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/CanPaymentService.kt @@ -18,6 +18,8 @@ import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.content.AudioContent import kr.co.vividnext.sodalive.content.order.Order import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.CreatorCommunity +import kr.co.vividnext.sodalive.i18n.LangContext +import kr.co.vividnext.sodalive.i18n.SodaMessageSource import kr.co.vividnext.sodalive.live.room.LiveRoom import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.member.MemberRepository @@ -31,7 +33,9 @@ class CanPaymentService( private val memberRepository: MemberRepository, private val chargeRepository: ChargeRepository, private val useCanRepository: UseCanRepository, - private val useCanCalculateRepository: UseCanCalculateRepository + private val useCanCalculateRepository: UseCanCalculateRepository, + private val messageSource: SodaMessageSource, + private val langContext: LangContext ) { @Transactional fun spendCan( @@ -49,7 +53,7 @@ class CanPaymentService( container: String ) { val member = memberRepository.findByIdOrNull(id = memberId) - ?: throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.") + ?: throw SodaException(messageKey = "can.payment.invalid_request_retry") val useRewardCan = spendRewardCan(member, needCan, container) val useChargeCan = if (needCan - useRewardCan.total > 0) { spendChargeCan(member, needCan = needCan - useRewardCan.total, container = container) @@ -58,14 +62,14 @@ class CanPaymentService( } if (needCan - useRewardCan.total - (useChargeCan?.total ?: 0) > 0) { + val shortCan = needCan - useRewardCan.total - (useChargeCan?.total ?: 0) throw SodaException( - "${needCan - useRewardCan.total - (useChargeCan?.total ?: 0)} " + - "캔이 부족합니다. 충전 후 이용해 주세요." + formatMessage("can.payment.insufficient_can", shortCan) ) } if (!useRewardCan.verify() || useChargeCan?.verify() == false) { - throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.") + throw SodaException(messageKey = "can.payment.invalid_request_retry") } val useCan = UseCan( @@ -121,7 +125,7 @@ class CanPaymentService( useCan.chatRoomId = chatRoomId useCan.characterId = characterId } else { - throw SodaException("잘못된 요청입니다.") + throw SodaException(messageKey = "common.error.invalid_request") } useCanRepository.save(useCan) @@ -306,20 +310,20 @@ class CanPaymentService( @Transactional fun refund(memberId: Long, roomId: Long) { val member = memberRepository.findByIdOrNull(memberId) - ?: throw SodaException("잘못된 예약정보 입니다.") + ?: throw SodaException(messageKey = "can.payment.invalid_reservation") val useCan = repository.getCanUsedForLiveRoomNotRefund( memberId = memberId, roomId = roomId, canUsage = CanUsage.LIVE - ) ?: throw SodaException("잘못된 예약정보 입니다.") + ) ?: throw SodaException(messageKey = "can.payment.invalid_reservation") useCan.isRefund = true val useCanCalculates = useCanCalculateRepository.findByUseCanIdAndStatus(useCan.id!!) useCanCalculates.forEach { it.status = UseCanCalculateStatus.REFUND val charge = Charge(0, it.can, status = ChargeStatus.REFUND_CHARGE) - charge.title = "${it.can} 캔" + charge.title = formatMessage("can.charge.title", it.can) charge.useCan = useCan when (it.paymentGateway) { @@ -333,7 +337,9 @@ class CanPaymentService( status = PaymentStatus.COMPLETE, paymentGateway = it.paymentGateway ) - payment.method = "환불" + payment.method = messageSource + .getMessage("can.payment.method.refund", langContext.lang) + .orEmpty() charge.payment = payment chargeRepository.save(charge) @@ -348,7 +354,7 @@ class CanPaymentService( container: String ) { val member = memberRepository.findByIdOrNull(id = memberId) - ?: throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.") + ?: throw SodaException(messageKey = "can.payment.invalid_request_retry") val useRewardCan = spendRewardCan(member, needCan, container) val useChargeCan = if (needCan - useRewardCan.total > 0) { @@ -358,14 +364,14 @@ class CanPaymentService( } if (needCan - useRewardCan.total - (useChargeCan?.total ?: 0) > 0) { + val shortCan = needCan - useRewardCan.total - (useChargeCan?.total ?: 0) throw SodaException( - "${needCan - useRewardCan.total - (useChargeCan?.total ?: 0)} " + - "캔이 부족합니다. 충전 후 이용해 주세요." + formatMessage("can.payment.insufficient_can", shortCan) ) } if (!useRewardCan.verify() || useChargeCan?.verify() == false) { - throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.") + throw SodaException(messageKey = "can.payment.invalid_request_retry") } val useCan = UseCan( @@ -394,7 +400,7 @@ class CanPaymentService( container: String ) { val member = memberRepository.findByIdOrNull(id = memberId) - ?: throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.") + ?: throw SodaException(messageKey = "can.payment.invalid_request_retry") val useRewardCan = spendRewardCan(member, needCan, container) val useChargeCan = if (needCan - useRewardCan.total > 0) { @@ -404,14 +410,14 @@ class CanPaymentService( } if (needCan - useRewardCan.total - (useChargeCan?.total ?: 0) > 0) { + val shortCan = needCan - useRewardCan.total - (useChargeCan?.total ?: 0) throw SodaException( - "${needCan - useRewardCan.total - (useChargeCan?.total ?: 0)} " + - "캔이 부족합니다. 충전 후 이용해 주세요." + formatMessage("can.payment.insufficient_can", shortCan) ) } if (!useRewardCan.verify() || useChargeCan?.verify() == false) { - throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.") + throw SodaException(messageKey = "can.payment.invalid_request_retry") } val useCan = UseCan( @@ -435,4 +441,9 @@ class CanPaymentService( setUseCanCalculate(null, useRewardCan, useChargeCan, useCan, paymentGateway = PaymentGateway.GOOGLE_IAP) setUseCanCalculate(null, useRewardCan, useChargeCan, useCan, paymentGateway = PaymentGateway.APPLE_IAP) } + + private fun formatMessage(key: String, vararg args: Any): String { + val template = messageSource.getMessage(key, langContext.lang) ?: return "" + return String.format(template, *args) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/i18n/SodaMessageSource.kt b/src/main/kotlin/kr/co/vividnext/sodalive/i18n/SodaMessageSource.kt index 04940ef5..3dd61bbc 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/i18n/SodaMessageSource.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/i18n/SodaMessageSource.kt @@ -182,6 +182,178 @@ class SodaMessageSource { ) ) + private val canChargeMessages = mapOf( + "can.charge.invalid_payment_info" to mapOf( + Lang.KO to "결제정보에 오류가 있습니다.", + Lang.EN to "There is an error with the payment information.", + Lang.JA to "決済情報に誤りがあります。" + ), + "can.charge.invalid_request_restart" to mapOf( + Lang.KO to "잘못된 요청입니다\n앱 종료 후 다시 시도해 주세요.", + Lang.EN to "Invalid request.\nPlease restart the app and try again.", + Lang.JA to "不正なリクエストです。\nアプリを終了して再度お試しください。" + ), + "can.charge.purchase_failed_contact" to mapOf( + Lang.KO to "구매를 하지 못했습니다.\n고객센터로 문의해 주세요", + Lang.EN to "Purchase could not be completed.\nPlease contact customer support.", + Lang.JA to "購入を完了できませんでした。\nカスタマーサポートへお問い合わせください。" + ), + "can.charge.payment_incomplete" to mapOf( + Lang.KO to "결제를 완료하지 못했습니다.", + Lang.EN to "Payment could not be completed.", + Lang.JA to "決済を完了できませんでした。" + ), + "can.charge.payment_method.apple_iap" to mapOf( + Lang.KO to "애플(인 앱 결제)", + Lang.EN to "Apple (In-App Purchase)", + Lang.JA to "Apple(アプリ内課金)" + ), + "can.charge.payment_method.google_iap" to mapOf( + Lang.KO to "구글(인 앱 결제)", + Lang.EN to "Google (In-App Purchase)", + Lang.JA to "Google(アプリ内課金)" + ), + "can.charge.payment_method.card" to mapOf( + Lang.KO to "카드", + Lang.EN to "Card", + Lang.JA to "カード" + ), + "can.charge.title" to mapOf( + Lang.KO to "%s 캔", + Lang.EN to "%s cans", + Lang.JA to "%s缶" + ) + ) + + private val canChargeEventMessages = mapOf( + "can.charge.event.not_applied_contact" to mapOf( + Lang.KO to "이벤트가 적용되지 않았습니다.\n고객센터에 문의해 주세요.", + Lang.EN to "The event was not applied.\nPlease contact customer support.", + Lang.JA to "イベントが適用されていません。\nカスタマーサポートへお問い合わせください。" + ), + "can.charge.event.additional_can_paid" to mapOf( + Lang.KO to "%s 캔이 추가 지급되었습니다.", + Lang.EN to "%s cans have been added.", + Lang.JA to "%s缶が追加で支給されました。" + ), + "can.charge.event.first_title" to mapOf( + Lang.KO to "첫 충전 이벤트", + Lang.EN to "First Recharge Event", + Lang.JA to "初回チャージイベント" + ) + ) + + private val canCouponMessages = mapOf( + "can.coupon.invalid_number_contact" to mapOf( + Lang.KO to "잘못된 쿠폰번호입니다.\n고객센터로 문의해 주시기 바랍니다.", + Lang.EN to "Invalid coupon number.\nPlease contact customer support.", + Lang.JA to "無効なクーポン番号です。\nカスタマーサポートへお問い合わせください。" + ), + "can.coupon.already_used" to mapOf( + Lang.KO to "이미 사용한 쿠폰번호 입니다.", + Lang.EN to "This coupon number has already been used.", + Lang.JA to "すでに使用されたクーポン番号です。" + ), + "can.coupon.use_complete" to mapOf( + Lang.KO to "쿠폰 사용이 완료되었습니다.\n%s캔이 지급되었습니다.", + Lang.EN to "Coupon redeemed successfully.\n%s cans have been granted.", + Lang.JA to "クーポンの使用が完了しました。\n%s缶が支給されました。" + ), + "can.coupon.use_complete_point" to mapOf( + Lang.KO to "쿠폰 사용이 완료되었습니다.\n%s포인트가 지급되었습니다.", + Lang.EN to "Coupon redeemed successfully.\n%s points have been granted.", + Lang.JA to "クーポンの使用が完了しました。\n%sポイントが付与されました。" + ), + "can.coupon.single_use_only" to mapOf( + Lang.KO to "해당 쿠폰은 1회만 충전이 가능합니다.", + Lang.EN to "This coupon can be used only once for charging.", + Lang.JA to "このクーポンは1回のみチャージに使用できます。" + ), + "can.coupon.expired" to mapOf( + Lang.KO to "유효기간이 경과된 쿠폰입니다.", + Lang.EN to "This coupon has expired.", + Lang.JA to "有効期限が切れたクーポンです。" + ), + "can.coupon.inactive" to mapOf( + Lang.KO to "이용이 불가능한 쿠폰입니다.", + Lang.EN to "This coupon is not available.", + Lang.JA to "利用できないクーポンです。" + ), + "can.coupon.no_changes" to mapOf( + Lang.KO to "변경사항이 없습니다.", + Lang.EN to "No changes found.", + Lang.JA to "変更事項がありません。" + ), + "can.coupon.validity_after_current" to mapOf( + Lang.KO to "유효기간은 기존 유효기간 이후 날짜로 설정하실 수 있습니다.", + Lang.EN to "Validity must be set after the current expiration date.", + Lang.JA to "有効期限は現在の有効期限以降に設定できます。" + ), + "can.coupon.auth_required" to mapOf( + Lang.KO to "쿠폰은 본인인증을 하셔야 사용이 가능합니다.", + Lang.EN to "You must verify your identity to use coupons.", + Lang.JA to "クーポンの使用には本人認証が必要です。" + ), + "can.coupon.download_failed_retry" to mapOf( + Lang.KO to "다운로드를 하지 못했습니다.\n다시 시도해 주세요.", + Lang.EN to "Download failed.\nPlease try again.", + Lang.JA to "ダウンロードできませんでした。\nもう一度お試しください。" + ), + "can.coupon.download_filename" to mapOf( + Lang.KO to "쿠폰번호리스트.xlsx", + Lang.EN to "coupon_number_list.xlsx", + Lang.JA to "クーポン番号リスト.xlsx" + ), + "can.coupon.download_header.index" to mapOf( + Lang.KO to "순번", + Lang.EN to "No.", + Lang.JA to "番号" + ), + "can.coupon.download_header.number" to mapOf( + Lang.KO to "쿠폰번호", + Lang.EN to "Coupon Number", + Lang.JA to "クーポン番号" + ), + "can.coupon.download_header.used" to mapOf( + Lang.KO to "사용여부", + Lang.EN to "Used", + Lang.JA to "使用有無" + ), + "can.coupon.download_used_mark" to mapOf( + Lang.KO to "O", + Lang.EN to "O", + Lang.JA to "O" + ), + "can.coupon.download_unused_mark" to mapOf( + Lang.KO to "X", + Lang.EN to "X", + Lang.JA to "X" + ) + ) + + private val canPaymentMessages = mapOf( + "can.payment.invalid_request_retry" to mapOf( + Lang.KO to "잘못된 요청입니다.\n다시 시도해 주세요.", + Lang.EN to "Invalid request.\nPlease try again.", + Lang.JA to "不正なリクエストです。\nもう一度お試しください。" + ), + "can.payment.invalid_reservation" to mapOf( + Lang.KO to "잘못된 예약정보 입니다.", + Lang.EN to "Invalid reservation information.", + Lang.JA to "無効な予約情報です。" + ), + "can.payment.method.refund" to mapOf( + Lang.KO to "환불", + Lang.EN to "Refund", + Lang.JA to "返金" + ), + "can.payment.insufficient_can" to mapOf( + Lang.KO to "%s 캔이 부족합니다. 충전 후 이용해 주세요.", + Lang.EN to "You are short of %s cans. Please recharge and try again.", + Lang.JA to "%s缶が不足しています。チャージしてからご利用ください。" + ) + ) + private val adminChatBannerMessages = mapOf( "admin.chat.banner.image_save_failed" to mapOf( Lang.KO to "이미지 저장에 실패했습니다.", @@ -1546,6 +1718,10 @@ class SodaMessageSource { auditionVoteMessages, settlementRatioMessages, adminCanMessages, + canChargeMessages, + canChargeEventMessages, + canCouponMessages, + canPaymentMessages, adminChatBannerMessages, adminChatCalculateMessages, adminChatCharacterMessages,