From a6dfa81ba616c4f38029eb9618ac44e4abc43eea Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 18 Sep 2025 22:49:35 +0900 Subject: [PATCH 01/44] =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8A=94=20'=EC=A7=80=EC=A0=95=20=EC=9B=90=EC=9E=91?= =?UTF-8?q?=EC=97=90=20=EC=86=8D=ED=95=9C=20=ED=99=9C=EC=84=B1=20=EC=BA=90?= =?UTF-8?q?=EB=A6=AD=ED=84=B0=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?API'=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/OriginalWorkController.kt | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/chat/original/controller/OriginalWorkController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/chat/original/controller/OriginalWorkController.kt index 62ccf19..43ad355 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/chat/original/controller/OriginalWorkController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/chat/original/controller/OriginalWorkController.kt @@ -1,7 +1,6 @@ package kr.co.vividnext.sodalive.chat.original.controller import kr.co.vividnext.sodalive.chat.character.dto.Character -import kr.co.vividnext.sodalive.chat.original.dto.OriginalWorkCharactersPageResponse import kr.co.vividnext.sodalive.chat.original.dto.OriginalWorkDetailResponse import kr.co.vividnext.sodalive.chat.original.dto.OriginalWorkListItemResponse import kr.co.vividnext.sodalive.chat.original.dto.OriginalWorkListResponse @@ -79,37 +78,4 @@ class OriginalWorkController( val response = OriginalWorkDetailResponse.from(ow, imageHost, characters) ApiResponse.ok(response) } - - /** - * 지정 원작에 속한 활성 캐릭터 목록 조회 (페이징) - * - 로그인 및 본인인증 필수 - * - 기본 페이지 사이즈 20 - */ - @GetMapping("/{id}/characters") - fun listCharacters( - @PathVariable id: Long, - @RequestParam(defaultValue = "0") page: Int, - @RequestParam(defaultValue = "20") size: Int, - @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? - ) = run { - if (member == null) throw SodaException("로그인 정보를 확인해주세요.") - if (member.auth == null) throw SodaException("본인인증을 하셔야 합니다.") - - val pageRes = queryService.getActiveCharactersPage(id, page, size) - val content = pageRes.content.map { - val path = it.imagePath ?: "profile/default-profile.png" - Character( - characterId = it.id!!, - name = it.name, - description = it.description, - imageUrl = "$imageHost/$path" - ) - } - ApiResponse.ok( - OriginalWorkCharactersPageResponse( - totalCount = pageRes.totalElements, - content = content - ) - ) - } } -- 2.49.1 From 97e95b51ab6692b60cddd3bc312d521244620e2a Mon Sep 17 00:00:00 2001 From: Klaus Date: Mon, 22 Sep 2025 13:36:36 +0900 Subject: [PATCH 02/44] =?UTF-8?q?feat(calculate-ratio):=20=EC=A0=95?= =?UTF-8?q?=EC=82=B0=20=EB=B9=84=EC=9C=A8=20=EC=88=98=EC=A0=95/=EC=82=AD?= =?UTF-8?q?=EC=A0=9C(=EC=86=8C=ED=94=84=ED=8A=B8=20=EC=82=AD=EC=A0=9C)?= =?UTF-8?q?=EC=99=80=20=EC=97=85=EC=84=9C=ED=8A=B8,=20=EC=BF=BC=EB=A6=AC?= =?UTF-8?q?=20=EB=B3=B4=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - deletedAt 기반 소프트 삭제 도입 및 restore/updateValues 추가 - 생성 시 기존(삭제 포함) 레코드 복구 후 값 갱신(업서트) - /admin/calculate/ratio/update, /delete 엔드포인트 추가 - 정산 쿼리 조인에 deletedAt.isNull 적용하여 삭제 데이터 배제 - 목록/카운트 조회에서도 삭제 데이터 제외 --- .../AdminCalculateQueryRepository.kt | 50 +++++++++++++++---- .../calculate/ratio/CreatorSettlementRatio.kt | 26 ++++++++-- .../ratio/CreatorSettlementRatioController.kt | 10 ++++ .../ratio/CreatorSettlementRatioRepository.kt | 6 ++- .../ratio/CreatorSettlementRatioService.kt | 44 +++++++++++++++- .../CreatorAdminCalculateQueryRepository.kt | 20 ++++++-- 6 files changed, 135 insertions(+), 21 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/AdminCalculateQueryRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/AdminCalculateQueryRepository.kt index e7b6e54..390d75a 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/AdminCalculateQueryRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/AdminCalculateQueryRepository.kt @@ -39,7 +39,10 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) { .innerJoin(useCan.room, liveRoom) .innerJoin(liveRoom.member, member) .leftJoin(creatorSettlementRatio) - .on(member.id.eq(creatorSettlementRatio.member.id)) + .on( + member.id.eq(creatorSettlementRatio.member.id) + .and(creatorSettlementRatio.deletedAt.isNull) + ) .where( useCan.isRefund.isFalse .and(useCan.createdAt.goe(startDate)) @@ -75,7 +78,10 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) { .innerJoin(order.audioContent, audioContent) .innerJoin(audioContent.member, member) .leftJoin(creatorSettlementRatio) - .on(member.id.eq(creatorSettlementRatio.member.id)) + .on( + member.id.eq(creatorSettlementRatio.member.id) + .and(creatorSettlementRatio.deletedAt.isNull) + ) .where( order.createdAt.goe(startDate) .and(order.createdAt.loe(endDate)) @@ -142,7 +148,10 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) { .innerJoin(order.audioContent, audioContent) .innerJoin(audioContent.member, member) .leftJoin(creatorSettlementRatio) - .on(member.id.eq(creatorSettlementRatio.member.id)) + .on( + member.id.eq(creatorSettlementRatio.member.id) + .and(creatorSettlementRatio.deletedAt.isNull) + ) .where(order.isActive.isTrue) .groupBy( member.id, @@ -230,7 +239,10 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) { .innerJoin(useCan.communityPost, creatorCommunity) .innerJoin(creatorCommunity.member, member) .leftJoin(creatorSettlementRatio) - .on(member.id.eq(creatorSettlementRatio.member.id)) + .on( + member.id.eq(creatorSettlementRatio.member.id) + .and(creatorSettlementRatio.deletedAt.isNull) + ) .where( useCan.isRefund.isFalse .and(useCan.canUsage.eq(CanUsage.PAID_COMMUNITY_POST)) @@ -251,7 +263,10 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) { .innerJoin(useCan.room, liveRoom) .innerJoin(liveRoom.member, member) .leftJoin(creatorSettlementRatio) - .on(member.id.eq(creatorSettlementRatio.member.id)) + .on( + member.id.eq(creatorSettlementRatio.member.id) + .and(creatorSettlementRatio.deletedAt.isNull) + ) .where( useCan.isRefund.isFalse .and(useCan.createdAt.goe(startDate)) @@ -281,7 +296,10 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) { .innerJoin(useCan.room, liveRoom) .innerJoin(liveRoom.member, member) .leftJoin(creatorSettlementRatio) - .on(member.id.eq(creatorSettlementRatio.member.id)) + .on( + member.id.eq(creatorSettlementRatio.member.id) + .and(creatorSettlementRatio.deletedAt.isNull) + ) .where( useCan.isRefund.isFalse .and(useCan.createdAt.goe(startDate)) @@ -301,7 +319,10 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) { .innerJoin(order.audioContent, audioContent) .innerJoin(audioContent.member, member) .leftJoin(creatorSettlementRatio) - .on(member.id.eq(creatorSettlementRatio.member.id)) + .on( + member.id.eq(creatorSettlementRatio.member.id) + .and(creatorSettlementRatio.deletedAt.isNull) + ) .where( order.createdAt.goe(startDate) .and(order.createdAt.loe(endDate)) @@ -331,7 +352,10 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) { .innerJoin(order.audioContent, audioContent) .innerJoin(audioContent.member, member) .leftJoin(creatorSettlementRatio) - .on(member.id.eq(creatorSettlementRatio.member.id)) + .on( + member.id.eq(creatorSettlementRatio.member.id) + .and(creatorSettlementRatio.deletedAt.isNull) + ) .where( order.createdAt.goe(startDate) .and(order.createdAt.loe(endDate)) @@ -351,7 +375,10 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) { .innerJoin(useCan.communityPost, creatorCommunity) .innerJoin(creatorCommunity.member, member) .leftJoin(creatorSettlementRatio) - .on(member.id.eq(creatorSettlementRatio.member.id)) + .on( + member.id.eq(creatorSettlementRatio.member.id) + .and(creatorSettlementRatio.deletedAt.isNull) + ) .where( useCan.isRefund.isFalse .and(useCan.canUsage.eq(CanUsage.PAID_COMMUNITY_POST)) @@ -382,7 +409,10 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) { .innerJoin(useCan.communityPost, creatorCommunity) .innerJoin(creatorCommunity.member, member) .leftJoin(creatorSettlementRatio) - .on(member.id.eq(creatorSettlementRatio.member.id)) + .on( + member.id.eq(creatorSettlementRatio.member.id) + .and(creatorSettlementRatio.deletedAt.isNull) + ) .where( useCan.isRefund.isFalse .and(useCan.canUsage.eq(CanUsage.PAID_COMMUNITY_POST)) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatio.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatio.kt index 33ce414..ade5326 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatio.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatio.kt @@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.admin.calculate.ratio import kr.co.vividnext.sodalive.common.BaseEntity import kr.co.vividnext.sodalive.member.Member +import java.time.LocalDateTime import javax.persistence.Entity import javax.persistence.FetchType import javax.persistence.JoinColumn @@ -9,12 +10,29 @@ import javax.persistence.OneToOne @Entity data class CreatorSettlementRatio( - val subsidy: Int, - val liveSettlementRatio: Int, - val contentSettlementRatio: Int, - val communitySettlementRatio: Int + var subsidy: Int, + var liveSettlementRatio: Int, + var contentSettlementRatio: Int, + var communitySettlementRatio: Int ) : BaseEntity() { @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id", nullable = false) var member: Member? = null + + var deletedAt: LocalDateTime? = null + + fun softDelete() { + this.deletedAt = LocalDateTime.now() + } + + fun restore() { + this.deletedAt = null + } + + fun updateValues(subsidy: Int, live: Int, content: Int, community: Int) { + this.subsidy = subsidy + this.liveSettlementRatio = live + this.contentSettlementRatio = content + this.communitySettlementRatio = community + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatioController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatioController.kt index 10cd41e..089733d 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatioController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatioController.kt @@ -27,4 +27,14 @@ class CreatorSettlementRatioController(private val service: CreatorSettlementRat limit = pageable.pageSize.toLong() ) ) + + @PostMapping("/update") + fun updateCreatorSettlementRatio( + @RequestBody request: CreateCreatorSettlementRatioRequest + ) = ApiResponse.ok(service.updateCreatorSettlementRatio(request)) + + @PostMapping("/delete") + fun deleteCreatorSettlementRatio( + @RequestBody memberId: Long + ) = ApiResponse.ok(service.deleteCreatorSettlementRatio(memberId)) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatioRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatioRepository.kt index e788f53..177887c 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatioRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatioRepository.kt @@ -7,7 +7,9 @@ import org.springframework.data.jpa.repository.JpaRepository interface CreatorSettlementRatioRepository : JpaRepository, - CreatorSettlementRatioQueryRepository + CreatorSettlementRatioQueryRepository { + fun findByMemberId(memberId: Long): CreatorSettlementRatio? +} interface CreatorSettlementRatioQueryRepository { fun getCreatorSettlementRatio(offset: Long, limit: Long): List @@ -30,6 +32,7 @@ class CreatorSettlementRatioQueryRepositoryImpl( ) .from(creatorSettlementRatio) .innerJoin(creatorSettlementRatio.member, member) + .where(creatorSettlementRatio.deletedAt.isNull) .orderBy(creatorSettlementRatio.id.asc()) .offset(offset) .limit(limit) @@ -40,6 +43,7 @@ class CreatorSettlementRatioQueryRepositoryImpl( return queryFactory .select(creatorSettlementRatio.id) .from(creatorSettlementRatio) + .where(creatorSettlementRatio.deletedAt.isNull) .fetch() .size } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatioService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatioService.kt index 140fb4a..d114f8c 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatioService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatioService.kt @@ -14,8 +14,6 @@ class CreatorSettlementRatioService( ) { @Transactional fun createCreatorSettlementRatio(request: CreateCreatorSettlementRatioRequest) { - val creatorSettlementRatio = request.toEntity() - val creator = memberRepository.findByIdOrNull(request.memberId) ?: throw SodaException("잘못된 크리에이터 입니다.") @@ -23,10 +21,52 @@ class CreatorSettlementRatioService( throw SodaException("잘못된 크리에이터 입니다.") } + val existing = repository.findByMemberId(request.memberId) + if (existing != null) { + // revive if soft-deleted, then update values + existing.restore() + existing.updateValues( + request.subsidy, + request.liveSettlementRatio, + request.contentSettlementRatio, + request.communitySettlementRatio + ) + repository.save(existing) + return + } + + val creatorSettlementRatio = request.toEntity() creatorSettlementRatio.member = creator repository.save(creatorSettlementRatio) } + @Transactional + fun updateCreatorSettlementRatio(request: CreateCreatorSettlementRatioRequest) { + val creator = memberRepository.findByIdOrNull(request.memberId) + ?: throw SodaException("잘못된 크리에이터 입니다.") + if (creator.role != MemberRole.CREATOR) { + throw SodaException("잘못된 크리에이터 입니다.") + } + val existing = repository.findByMemberId(request.memberId) + ?: throw SodaException("해당 크리에이터의 정산 비율 설정이 없습니다.") + existing.restore() + existing.updateValues( + request.subsidy, + request.liveSettlementRatio, + request.contentSettlementRatio, + request.communitySettlementRatio + ) + repository.save(existing) + } + + @Transactional + fun deleteCreatorSettlementRatio(memberId: Long) { + val existing = repository.findByMemberId(memberId) + ?: throw SodaException("해당 크리에이터의 정산 비율 설정이 없습니다.") + existing.softDelete() + repository.save(existing) + } + @Transactional(readOnly = true) fun getCreatorSettlementRatio(offset: Long, limit: Long): GetCreatorSettlementRatioResponse { val totalCount = repository.getCreatorSettlementRatioTotalCount() diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/calculate/CreatorAdminCalculateQueryRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/calculate/CreatorAdminCalculateQueryRepository.kt index f9b4289..d4267c4 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/calculate/CreatorAdminCalculateQueryRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/calculate/CreatorAdminCalculateQueryRepository.kt @@ -53,7 +53,10 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac .innerJoin(useCan.room, liveRoom) .innerJoin(liveRoom.member, member) .leftJoin(creatorSettlementRatio) - .on(member.id.eq(creatorSettlementRatio.member.id)) + .on( + member.id.eq(creatorSettlementRatio.member.id) + .and(creatorSettlementRatio.deletedAt.isNull) + ) .where( useCan.isRefund.isFalse .and(useCan.createdAt.goe(startDate)) @@ -119,7 +122,10 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac .innerJoin(order.audioContent, audioContent) .innerJoin(audioContent.member, member) .leftJoin(creatorSettlementRatio) - .on(member.id.eq(creatorSettlementRatio.member.id)) + .on( + member.id.eq(creatorSettlementRatio.member.id) + .and(creatorSettlementRatio.deletedAt.isNull) + ) .where( order.createdAt.goe(startDate) .and(order.createdAt.loe(endDate)) @@ -196,7 +202,10 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac .innerJoin(order.audioContent, audioContent) .innerJoin(audioContent.member, member) .leftJoin(creatorSettlementRatio) - .on(member.id.eq(creatorSettlementRatio.member.id)) + .on( + member.id.eq(creatorSettlementRatio.member.id) + .and(creatorSettlementRatio.deletedAt.isNull) + ) .where( audioContent.member.id.eq(memberId) .and(order.isActive.isTrue) @@ -318,7 +327,10 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac .innerJoin(useCan.communityPost, creatorCommunity) .innerJoin(creatorCommunity.member, member) .leftJoin(creatorSettlementRatio) - .on(member.id.eq(creatorSettlementRatio.member.id)) + .on( + member.id.eq(creatorSettlementRatio.member.id) + .and(creatorSettlementRatio.deletedAt.isNull) + ) .where( useCan.isRefund.isFalse .and(useCan.canUsage.eq(CanUsage.PAID_COMMUNITY_POST)) -- 2.49.1 From 6bc65ec4125d09f81f2d1c83154465d39dc67499 Mon Sep 17 00:00:00 2001 From: Klaus Date: Mon, 22 Sep 2025 14:01:28 +0900 Subject: [PATCH 03/44] =?UTF-8?q?feat(calculate-ratio):=20=EC=A0=95?= =?UTF-8?q?=EC=82=B0=20=EB=B9=84=EC=9C=A8=20=EC=A1=B0=ED=9A=8C=20-=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=EC=97=90=20memberId=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/calculate/ratio/CreatorSettlementRatioRepository.kt | 1 + .../admin/calculate/ratio/GetCreatorSettlementRatioResponse.kt | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatioRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatioRepository.kt index 177887c..b19f015 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatioRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatioRepository.kt @@ -23,6 +23,7 @@ class CreatorSettlementRatioQueryRepositoryImpl( return queryFactory .select( QGetCreatorSettlementRatioItem( + member.id, member.nickname, creatorSettlementRatio.subsidy, creatorSettlementRatio.liveSettlementRatio, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/GetCreatorSettlementRatioResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/GetCreatorSettlementRatioResponse.kt index 24a4d96..b6603ff 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/GetCreatorSettlementRatioResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/GetCreatorSettlementRatioResponse.kt @@ -8,6 +8,7 @@ data class GetCreatorSettlementRatioResponse( ) data class GetCreatorSettlementRatioItem @QueryProjection constructor( + val memberId: Long, val nickname: String, val subsidy: Int, val liveSettlementRatio: Int, -- 2.49.1 From 59ca353b25242d01b3f66533bfa5866852fd0d6f Mon Sep 17 00:00:00 2001 From: Klaus Date: Mon, 22 Sep 2025 14:17:18 +0900 Subject: [PATCH 04/44] =?UTF-8?q?feat(calculate-ratio):=20=EC=A0=95?= =?UTF-8?q?=EC=82=B0=20=EB=B9=84=EC=9C=A8=20=EC=82=AD=EC=A0=9C=20URL=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../calculate/ratio/CreatorSettlementRatioController.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatioController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatioController.kt index 089733d..658f373 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatioController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/ratio/CreatorSettlementRatioController.kt @@ -4,6 +4,7 @@ import kr.co.vividnext.sodalive.common.ApiResponse import org.springframework.data.domain.Pageable import org.springframework.security.access.prepost.PreAuthorize import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping @@ -33,8 +34,8 @@ class CreatorSettlementRatioController(private val service: CreatorSettlementRat @RequestBody request: CreateCreatorSettlementRatioRequest ) = ApiResponse.ok(service.updateCreatorSettlementRatio(request)) - @PostMapping("/delete") + @PostMapping("/delete/{memberId}") fun deleteCreatorSettlementRatio( - @RequestBody memberId: Long + @PathVariable memberId: Long ) = ApiResponse.ok(service.deleteCreatorSettlementRatio(memberId)) } -- 2.49.1 From bc6c05b3ea8a2d7e196a87d1d57a1bfbe322ba67 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 25 Sep 2025 20:37:39 +0900 Subject: [PATCH 05/44] =?UTF-8?q?feat(charge):=20payverse=20pg=20-=20?= =?UTF-8?q?=EC=B6=A9=EC=A0=84/=EA=B2=80=EC=A6=9D=20API=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 --- .../sodalive/can/charge/ChargeController.kt | 26 ++++ .../sodalive/can/charge/ChargeData.kt | 28 ++++ .../sodalive/can/charge/ChargeService.kt | 140 +++++++++++++++++- .../sodalive/can/payment/PaymentGateway.kt | 2 +- src/main/resources/application.yml | 6 + 5 files changed, 200 insertions(+), 2 deletions(-) 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 3748c96..1d78533 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 @@ -20,6 +20,32 @@ class ChargeController( private val trackingService: AdTrackingService ) { + @PostMapping("/payverse") + fun payverseCharge( + @RequestBody request: PayverseChargeRequest, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? + ) = run { + if (member == null) { + throw SodaException("로그인 정보를 확인해주세요.") + } + + ApiResponse.ok(service.payverseCharge(member, request)) + } + + @PostMapping("/payverse/verify") + fun payverseVerify( + @RequestBody verifyRequest: PayverseVerifyRequest, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? + ) = run { + if (member == null) { + throw SodaException("로그인 정보를 확인해주세요.") + } + + val response = service.payverseVerify(memberId = member.id!!, verifyRequest) + trackingCharge(member, response) + ApiResponse.ok(Unit) + } + @PostMapping fun charge( @RequestBody chargeRequest: ChargeRequest, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt index 2fc7a14..68b0eb0 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt @@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.can.charge import com.fasterxml.jackson.annotation.JsonProperty import kr.co.vividnext.sodalive.can.payment.PaymentGateway +import java.math.BigDecimal data class ChargeRequest(val canId: Long, val paymentGateway: PaymentGateway) @@ -44,3 +45,30 @@ data class GoogleChargeRequest( val purchaseToken: String, val paymentGateway: PaymentGateway ) + +data class PayverseChargeRequest( + val canId: Long +) + +data class PayverseChargeResponse( + val chargeId: Long, + val payloadJson: String +) + +data class PayverseVerifyRequest( + val transactionId: String, + val orderId: String +) + +data class PayverseVerifyResponse( + val resultStatus: String, + val tid: String, + val schemeCode: String, + val transactionType: String, + val transactionStatus: String, + val transactionMessage: String, + val orderId: String, + val customerId: String, + val processingCurrency: String, + val processingAmount: BigDecimal +) 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 dfc7a4b..c71028e 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 @@ -22,6 +22,7 @@ import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody +import org.apache.commons.codec.digest.DigestUtils import org.json.JSONObject import org.springframework.beans.factory.annotation.Value import org.springframework.context.ApplicationEventPublisher @@ -34,6 +35,8 @@ import org.springframework.transaction.annotation.Transactional import java.math.BigDecimal import java.math.RoundingMode import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import java.util.UUID @Service @Transactional(readOnly = true) @@ -63,7 +66,19 @@ class ChargeService( @Value("\${apple.iap-verify-sandbox-url}") private val appleInAppVerifySandBoxUrl: String, @Value("\${apple.iap-verify-url}") - private val appleInAppVerifyUrl: String + private val appleInAppVerifyUrl: String, + + @Value("\${payverse.mid}") + private val payverseMid: String, + @Value("\${payverse.client-key}") + private val payverseClientKey: String, + @Value("\${payverse.secret-key}") + private val payverseSecretKey: String, + @Value("\${payverse.host}") + private val payverseHost: String, + + @Value("\${server.env}") + private val serverEnv: String ) { @Transactional @@ -126,6 +141,129 @@ class ChargeService( } } + @Transactional + fun payverseCharge(member: Member, request: PayverseChargeRequest): PayverseChargeResponse { + val can = canRepository.findByIdOrNull(request.canId) + ?: throw SodaException("잘못된 요청입니다\n앱 종료 후 다시 시도해 주세요.") + + val charge = Charge(can.can, can.rewardCan) + charge.title = can.title + charge.member = member + charge.can = can + + val payment = Payment(paymentGateway = PaymentGateway.PAYVERSE) + payment.price = can.price.toDouble() + charge.payment = payment + + val savedCharge = chargeRepository.save(charge) + + val chargeId = savedCharge.id!! + val amount = BigDecimal(savedCharge.payment!!.price) + val reqDate = savedCharge.createdAt!!.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + val sign = DigestUtils.sha512Hex( + String.format("||%s||%s||%s||%s||%s||", payverseSecretKey, payverseMid, chargeId, amount, reqDate) + ) + val customerId = UUID.nameUUIDFromBytes("${serverEnv}_user_${member.id!!}".toByteArray()).toString() + val requestCurrency = "KRW" + + val payload = linkedMapOf( + "mid" to payverseMid, + "clientKey" to payverseClientKey, + "orderId" to chargeId.toString(), + "customerId" to customerId, + "productName" to can.title, + "requestCurrency" to requestCurrency, + "requestAmount" to amount.toString(), + "reqDate" to reqDate, + "billkeyReq" to "N", + "mallReserved" to "", + "sign" to sign + ) + val payloadJson = objectMapper.writeValueAsString(payload) + + return PayverseChargeResponse(chargeId = charge.id!!, payloadJson = payloadJson) + } + + @Transactional + fun payverseVerify(memberId: Long, verifyRequest: PayverseVerifyRequest): ChargeCompleteResponse { + val charge = chargeRepository.findByIdOrNull(verifyRequest.orderId.toLong()) + ?: throw SodaException("결제정보에 오류가 있습니다.") + val member = memberRepository.findByIdOrNull(memberId) + ?: throw SodaException("로그인 정보를 확인해주세요.") + + // 결제수단 확인 + if (charge.payment?.paymentGateway != PaymentGateway.PAYVERSE) { + throw SodaException("결제정보에 오류가 있습니다.") + } + + // 결제 상태에 따른 분기 처리 + when (charge.payment?.status) { + PaymentStatus.REQUEST -> { + try { + val url = "$payverseHost/payment/search/transaction/${verifyRequest.transactionId}" + val request = Request.Builder() + .url(url) + .addHeader("mid", payverseMid) + .addHeader("clientKey", payverseClientKey) + .get() + .build() + + val response = okHttpClient.newCall(request).execute() + if (!response.isSuccessful) { + throw SodaException("결제정보에 오류가 있습니다.") + } + + val body = response.body?.string() ?: throw SodaException("결제정보에 오류가 있습니다.") + val verifyResponse = objectMapper.readValue(body, PayverseVerifyResponse::class.java) + + val isSuccess = verifyResponse.resultStatus == "SUCCESS" && + verifyResponse.transactionStatus == "SUCCESS" && + verifyResponse.orderId.toLongOrNull() == charge.id && + verifyResponse.processingAmount.compareTo(BigDecimal.valueOf(charge.can!!.price.toLong())) == 0 + + if (isSuccess) { + // verify 함수의 232~248 라인과 동일 처리 + charge.payment?.receiptId = verifyResponse.tid + charge.payment?.method = verifyResponse.schemeCode + charge.payment?.status = PaymentStatus.COMPLETE + // 통화코드 설정 + charge.payment?.locale = verifyResponse.processingCurrency + + member.charge(charge.chargeCan, charge.rewardCan, "pg") + + applicationEventPublisher.publishEvent( + ChargeSpringEvent( + chargeId = charge.id!!, + memberId = member.id!! + ) + ) + + return ChargeCompleteResponse( + price = BigDecimal(charge.payment!!.price).setScale(2, RoundingMode.HALF_UP).toDouble(), + currencyCode = charge.payment!!.locale?.takeLast(3) ?: "KRW", + isFirstCharged = chargeRepository.isFirstCharged(memberId) + ) + } else { + throw SodaException("결제정보에 오류가 있습니다.") + } + } catch (_: Exception) { + throw SodaException("결제정보에 오류가 있습니다.") + } + } + PaymentStatus.COMPLETE -> { + // 이미 결제가 완료된 경우, 동일한 데이터로 즉시 반환 + return ChargeCompleteResponse( + price = BigDecimal(charge.payment!!.price).setScale(2, RoundingMode.HALF_UP).toDouble(), + currencyCode = charge.payment!!.locale?.takeLast(3) ?: "KRW", + isFirstCharged = chargeRepository.isFirstCharged(memberId) + ) + } + else -> { + throw SodaException("결제정보에 오류가 있습니다.") + } + } + } + @Transactional fun charge(member: Member, request: ChargeRequest): ChargeResponse { val can = canRepository.findByIdOrNull(request.canId) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/PaymentGateway.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/PaymentGateway.kt index a37459d..b5b414a 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/PaymentGateway.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/PaymentGateway.kt @@ -1,5 +1,5 @@ package kr.co.vividnext.sodalive.can.payment enum class PaymentGateway { - PG, GOOGLE_IAP, APPLE_IAP, POINT_CLICK_AD + PG, PAYVERSE, GOOGLE_IAP, APPLE_IAP, POINT_CLICK_AD } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index aa37c6d..1851faf 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -13,6 +13,12 @@ weraser: apiUrl: ${WERASER_API_URL} apiKey: ${WERASER_API_KEY} +payverse: + mid: ${PAYVERSE_MID} + clientKey: ${PAYVERSE_CLIENT_KEY} + secretKey: ${PAYVERSE_SECRET_KEY} + host: ${PAYVERSE_HOST} + bootpay: applicationId: ${BOOTPAY_APPLICATION_ID} privateKey: ${BOOTPAY_PRIVATE_KEY} -- 2.49.1 From 03149a637d1cd799f03c1f560b210217a411d773 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 25 Sep 2025 21:18:45 +0900 Subject: [PATCH 06/44] =?UTF-8?q?feat(charge):=20payverse=20pg=20-=20webho?= =?UTF-8?q?ok=20API=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/can/charge/ChargeController.kt | 27 +++++++- .../sodalive/can/charge/ChargeData.kt | 16 +++++ .../sodalive/can/charge/ChargeService.kt | 67 +++++++++++++++++++ src/main/resources/application.yml | 1 + 4 files changed, 110 insertions(+), 1 deletion(-) 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 1d78533..983239f 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 @@ -6,18 +6,25 @@ import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.marketing.AdTrackingHistoryType import kr.co.vividnext.sodalive.marketing.AdTrackingService import kr.co.vividnext.sodalive.member.Member +import org.springframework.beans.factory.annotation.Value +import org.springframework.http.HttpStatus import org.springframework.security.core.annotation.AuthenticationPrincipal 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 +import org.springframework.web.server.ResponseStatusException import java.time.LocalDateTime +import javax.servlet.http.HttpServletRequest @RestController @RequestMapping("/charge") class ChargeController( private val service: ChargeService, - private val trackingService: AdTrackingService + private val trackingService: AdTrackingService, + + @Value("\${payverse.inbound-ip}") + private val payverseInboundIp: String ) { @PostMapping("/payverse") @@ -46,6 +53,24 @@ class ChargeController( ApiResponse.ok(Unit) } + // Payverse Webhook 엔드포인트 (payverseVerify 아래) + @PostMapping("/payverse/webhook") + fun payverseWebhook( + @RequestBody request: PayverseWebhookRequest, + servletRequest: HttpServletRequest + ): PayverseWebhookResponse { + val remoteIp = servletRequest.remoteAddr ?: "" + if (remoteIp != payverseInboundIp) { + throw ResponseStatusException(HttpStatus.NOT_FOUND) + } + + val success = service.payverseWebhook(request) + if (!success) { + throw ResponseStatusException(HttpStatus.NOT_FOUND) + } + return PayverseWebhookResponse(receiveResult = "SUCCESS") + } + @PostMapping fun charge( @RequestBody chargeRequest: ChargeRequest, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt index 68b0eb0..312c713 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt @@ -72,3 +72,19 @@ data class PayverseVerifyResponse( val processingCurrency: String, val processingAmount: BigDecimal ) + +data class PayverseWebhookRequest( + val type: String, + val mid: String, + val tid: String, + val schemeCode: String, + val orderId: String, + val productName: String, + val requestCurrency: String, + val requestAmount: BigDecimal, + val resultStatus: String, + val approvalDay: String, + val sign: String +) + +data class PayverseWebhookResponse(val receiveResult: String) 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 c71028e..cdfe200 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 @@ -81,6 +81,73 @@ class ChargeService( private val serverEnv: String ) { + @Transactional + fun payverseWebhook(request: PayverseWebhookRequest): Boolean { + val chargeId = request.orderId.toLongOrNull() ?: return false + val charge = chargeRepository.findByIdOrNull(chargeId) ?: return false + + // 결제수단 확인 + if (charge.payment?.paymentGateway != PaymentGateway.PAYVERSE) { + return false + } + + // 결제 상태 분기 처리 + return when (charge.payment?.status) { + PaymentStatus.REQUEST -> { + // 성공 조건 검증 + val expectedSign = DigestUtils.sha512Hex( + String.format( + "||%s||%s||%s||%s||%s||", + payverseSecretKey, + payverseMid, + request.orderId, + request.requestAmount, + request.approvalDay + ) + ) + + val isAmountMatch = request.requestAmount.compareTo( + BigDecimal.valueOf(charge.payment!!.price) + ) == 0 + + val isSuccess = request.resultStatus == "SUCCESS" && + request.mid == payverseMid && + charge.title == request.productName && + isAmountMatch && + request.sign == expectedSign + + if (isSuccess) { + // payverseVerify의 226~246 라인과 동일 처리 + charge.payment?.receiptId = request.tid + charge.payment?.method = request.schemeCode + charge.payment?.status = PaymentStatus.COMPLETE + charge.payment?.locale = request.requestCurrency + + val member = charge.member!! + member.charge(charge.chargeCan, charge.rewardCan, "pg") + + applicationEventPublisher.publishEvent( + ChargeSpringEvent( + chargeId = charge.id!!, + memberId = member.id!! + ) + ) + true + } else { + false + } + } + PaymentStatus.COMPLETE -> { + // 이미 결제가 완료된 경우 성공 처리(idempotent) + true + } + else -> { + // 그 외 상태는 404 + false + } + } + } + @Transactional fun chargeByCoupon(couponNumber: String, member: Member): String { val canCouponNumber = couponNumberRepository.findByCouponNumber(couponNumber = couponNumber) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 1851faf..cf444de 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -18,6 +18,7 @@ payverse: clientKey: ${PAYVERSE_CLIENT_KEY} secretKey: ${PAYVERSE_SECRET_KEY} host: ${PAYVERSE_HOST} + inboundIp: ${PAYVERSE_INBOUND_IP} bootpay: applicationId: ${BOOTPAY_APPLICATION_ID} -- 2.49.1 From d536a65fb4e9b25bfa6f976c451bd005621b765d Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 26 Sep 2025 16:23:11 +0900 Subject: [PATCH 07/44] =?UTF-8?q?fix(charge):=20payverse=20pg=20payload=20?= =?UTF-8?q?-=20requestAmount=EC=9D=98=20=EA=B0=92=EC=9D=84=20BigDecimal?= =?UTF-8?q?=EB=A1=9C=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../co/vividnext/sodalive/can/charge/ChargeService.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) 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 cdfe200..ad79c1e 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 @@ -137,10 +137,12 @@ class ChargeService( false } } + PaymentStatus.COMPLETE -> { // 이미 결제가 완료된 경우 성공 처리(idempotent) true } + else -> { // 그 외 상태는 404 false @@ -240,7 +242,7 @@ class ChargeService( "customerId" to customerId, "productName" to can.title, "requestCurrency" to requestCurrency, - "requestAmount" to amount.toString(), + "requestAmount" to amount, "reqDate" to reqDate, "billkeyReq" to "N", "mallReserved" to "", @@ -317,6 +319,7 @@ class ChargeService( throw SodaException("결제정보에 오류가 있습니다.") } } + PaymentStatus.COMPLETE -> { // 이미 결제가 완료된 경우, 동일한 데이터로 즉시 반환 return ChargeCompleteResponse( @@ -325,6 +328,7 @@ class ChargeService( isFirstCharged = chargeRepository.isFirstCharged(memberId) ) } + else -> { throw SodaException("결제정보에 오류가 있습니다.") } @@ -388,7 +392,7 @@ class ChargeService( } else { throw SodaException("결제정보에 오류가 있습니다.") } - } catch (e: Exception) { + } catch (_: Exception) { throw SodaException("결제정보에 오류가 있습니다.") } } else { @@ -438,7 +442,7 @@ class ChargeService( } else { throw SodaException("결제정보에 오류가 있습니다.") } - } catch (e: Exception) { + } catch (_: Exception) { throw SodaException("결제정보에 오류가 있습니다.") } } else { -- 2.49.1 From 22bd1bf0427a5a0cd5e3356c1799926ba69eceac Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 26 Sep 2025 16:51:54 +0900 Subject: [PATCH 08/44] =?UTF-8?q?fix(payverse):=20=EA=B2=B0=EC=A0=9C=20pay?= =?UTF-8?q?load=EC=97=90=20customerId=20=EA=B8=B8=EC=9D=B4=2030=EC=9E=90?= =?UTF-8?q?=EB=A1=9C=20=EC=A0=9C=ED=95=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - customerId를 sha1 기반 30자 이내로 생성하도록 변경하여 스펙 준수 - 불필요한 billkeyReq 제거 --- .../kr/co/vividnext/sodalive/can/charge/ChargeService.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) 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 ad79c1e..caa25e4 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 @@ -36,7 +36,6 @@ import java.math.BigDecimal import java.math.RoundingMode import java.time.LocalDateTime import java.time.format.DateTimeFormatter -import java.util.UUID @Service @Transactional(readOnly = true) @@ -232,7 +231,7 @@ class ChargeService( val sign = DigestUtils.sha512Hex( String.format("||%s||%s||%s||%s||%s||", payverseSecretKey, payverseMid, chargeId, amount, reqDate) ) - val customerId = UUID.nameUUIDFromBytes("${serverEnv}_user_${member.id!!}".toByteArray()).toString() + val customerId = "${serverEnv}_user_${member.id!!}" val requestCurrency = "KRW" val payload = linkedMapOf( @@ -244,8 +243,6 @@ class ChargeService( "requestCurrency" to requestCurrency, "requestAmount" to amount, "reqDate" to reqDate, - "billkeyReq" to "N", - "mallReserved" to "", "sign" to sign ) val payloadJson = objectMapper.writeValueAsString(payload) -- 2.49.1 From 1bd6f8da4edbf74a3298f4b47a0ad13ab224bf19 Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 30 Sep 2025 17:02:02 +0900 Subject: [PATCH 09/44] =?UTF-8?q?fix(payverse):=20PVKR=20=EC=B9=B4?= =?UTF-8?q?=EB=93=9C=20=EC=BD=94=EB=93=9C=EB=A9=B4=20method=EB=A5=BC=20"?= =?UTF-8?q?=EC=B9=B4=EB=93=9C"=EB=A1=9C=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vividnext/sodalive/can/CanRepository.kt | 2 ++ .../sodalive/can/charge/ChargeData.kt | 2 ++ .../sodalive/can/charge/ChargeService.kt | 24 +++++++++++++++++-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt index 61863a0..12fc614 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt @@ -64,11 +64,13 @@ class CanQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : CanQue val chargeStatusCondition = when (container) { "aos" -> { charge.payment.paymentGateway.eq(PaymentGateway.PG) + .or(charge.payment.paymentGateway.eq(PaymentGateway.PAYVERSE)) .or(charge.payment.paymentGateway.eq(PaymentGateway.GOOGLE_IAP)) } "ios" -> { charge.payment.paymentGateway.eq(PaymentGateway.PG) + .or(charge.payment.paymentGateway.eq(PaymentGateway.PAYVERSE)) .or(charge.payment.paymentGateway.eq(PaymentGateway.APPLE_IAP)) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt index 312c713..9689415 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt @@ -63,6 +63,7 @@ data class PayverseVerifyRequest( data class PayverseVerifyResponse( val resultStatus: String, val tid: String, + val schemeGroup: String, val schemeCode: String, val transactionType: String, val transactionStatus: String, @@ -77,6 +78,7 @@ data class PayverseWebhookRequest( val type: String, val mid: String, val tid: String, + val schemeGroup: String, val schemeCode: String, val orderId: String, val productName: String, 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 caa25e4..83c0a0b 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 @@ -118,7 +118,12 @@ class ChargeService( if (isSuccess) { // payverseVerify의 226~246 라인과 동일 처리 charge.payment?.receiptId = request.tid - charge.payment?.method = request.schemeCode + val mappedMethod = if (request.schemeGroup == "PVKR") { + mapPayverseSchemeToMethodByCode(request.schemeCode) + } else { + null + } + charge.payment?.method = mappedMethod ?: request.schemeCode charge.payment?.status = PaymentStatus.COMPLETE charge.payment?.locale = request.requestCurrency @@ -280,6 +285,7 @@ class ChargeService( } val body = response.body?.string() ?: throw SodaException("결제정보에 오류가 있습니다.") + print(body) val verifyResponse = objectMapper.readValue(body, PayverseVerifyResponse::class.java) val isSuccess = verifyResponse.resultStatus == "SUCCESS" && @@ -290,7 +296,12 @@ class ChargeService( if (isSuccess) { // verify 함수의 232~248 라인과 동일 처리 charge.payment?.receiptId = verifyResponse.tid - charge.payment?.method = verifyResponse.schemeCode + val mappedMethod = if (verifyResponse.schemeGroup == "PVKR") { + mapPayverseSchemeToMethodByCode(verifyResponse.schemeCode) + } else { + null + } + charge.payment?.method = mappedMethod ?: verifyResponse.schemeCode charge.payment?.status = PaymentStatus.COMPLETE // 통화코드 설정 charge.payment?.locale = verifyResponse.processingCurrency @@ -642,4 +653,13 @@ class ChargeService( throw SodaException("결제를 완료하지 못했습니다.") } } + + // 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 + } } -- 2.49.1 From aab2417976bb9308a12bf0b513d7844d14ca6bd0 Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 30 Sep 2025 17:22:39 +0900 Subject: [PATCH 10/44] =?UTF-8?q?fix(payverse):=20print=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/kr/co/vividnext/sodalive/can/charge/ChargeService.kt | 1 - 1 file changed, 1 deletion(-) 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 83c0a0b..271f11b 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 @@ -285,7 +285,6 @@ class ChargeService( } val body = response.body?.string() ?: throw SodaException("결제정보에 오류가 있습니다.") - print(body) val verifyResponse = objectMapper.readValue(body, PayverseVerifyResponse::class.java) val isSuccess = verifyResponse.resultStatus == "SUCCESS" && -- 2.49.1 From 8990bd07222de193e02e9ee9cbeff230bd9e8aad Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 30 Sep 2025 17:37:15 +0900 Subject: [PATCH 11/44] =?UTF-8?q?fix(payverse):=20webhook=20=EC=97=94?= =?UTF-8?q?=EB=93=9C=ED=8F=AC=EC=9D=B8=ED=8A=B8=EB=8A=94=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8D=94?= =?UTF-8?q?=EB=9D=BC=EB=8F=84=20=EC=8B=A4=ED=96=89=EB=90=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/kr/co/vividnext/sodalive/configs/SecurityConfig.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/configs/SecurityConfig.kt b/src/main/kotlin/kr/co/vividnext/sodalive/configs/SecurityConfig.kt index cc42fbb..337f74e 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/configs/SecurityConfig.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/configs/SecurityConfig.kt @@ -96,6 +96,7 @@ class SecurityConfig( .antMatchers(HttpMethod.GET, "/api/chat/character/main").permitAll() .antMatchers(HttpMethod.GET, "/api/chat/room/list").permitAll() .antMatchers(HttpMethod.GET, "/api/chat/original/list").permitAll() + .antMatchers(HttpMethod.POST, "/charge/payverse/webhook").permitAll() .anyRequest().authenticated() .and() .build() -- 2.49.1 From 51988471cf5f799d36b7eab809a82f2f2e2e1657 Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 30 Sep 2025 17:55:31 +0900 Subject: [PATCH 12/44] =?UTF-8?q?temp(payverse):=20=ED=98=B8=EC=B6=9C?= =?UTF-8?q?=EB=90=98=EB=8A=94=20INBOUND=5FIP=20=ED=99=95=EC=9D=B8=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EC=9C=84=ED=95=B4=20=EC=B6=9C=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/co/vividnext/sodalive/can/charge/ChargeController.kt | 4 ++++ 1 file changed, 4 insertions(+) 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 983239f..00c2baf 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 @@ -60,6 +60,10 @@ class ChargeController( servletRequest: HttpServletRequest ): PayverseWebhookResponse { val remoteIp = servletRequest.remoteAddr ?: "" + + print("Payverse Webhook Request: $remoteIp") + print("Payverse Webhook Request: $payverseInboundIp") + if (remoteIp != payverseInboundIp) { throw ResponseStatusException(HttpStatus.NOT_FOUND) } -- 2.49.1 From 3c32614d1c5e28f46ddc480d7268e9f1ebc50b9c Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 30 Sep 2025 17:59:55 +0900 Subject: [PATCH 13/44] =?UTF-8?q?temp(Exception):=20ResponseStatusExceptio?= =?UTF-8?q?n=EC=9D=80=20ApiResponse=EB=A1=9C=20=EB=9E=98=ED=95=91=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EA=B3=A0=20=EA=B7=B8=EB=8C=80=EB=A1=9C=20?= =?UTF-8?q?=EC=A0=84=EB=8B=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../co/vividnext/sodalive/common/SodaExceptionHandler.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/common/SodaExceptionHandler.kt b/src/main/kotlin/kr/co/vividnext/sodalive/common/SodaExceptionHandler.kt index 045cf83..8bc17d7 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/common/SodaExceptionHandler.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/common/SodaExceptionHandler.kt @@ -3,6 +3,7 @@ package kr.co.vividnext.sodalive.common import org.slf4j.LoggerFactory import org.springframework.dao.DataIntegrityViolationException import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity import org.springframework.security.access.AccessDeniedException import org.springframework.security.authentication.BadCredentialsException import org.springframework.security.authentication.InternalAuthenticationServiceException @@ -10,6 +11,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler import org.springframework.web.bind.annotation.ResponseStatus import org.springframework.web.bind.annotation.RestControllerAdvice import org.springframework.web.multipart.MaxUploadSizeExceededException +import org.springframework.web.server.ResponseStatusException @RestControllerAdvice class SodaExceptionHandler { @@ -24,6 +26,13 @@ class SodaExceptionHandler { ) } + // ResponseStatusException은 ApiResponse로 래핑하지 않고 그대로 전달 + @ExceptionHandler(ResponseStatusException::class) + fun handleResponseStatusException(e: ResponseStatusException): ResponseEntity { + // 별도 바디 없이 상태코드만 반환하여 기본 예외 형태를 유지 + return ResponseEntity.status(e.status).build() + } + @ExceptionHandler(MaxUploadSizeExceededException::class) fun handleMaxUploadSizeExceededException(e: MaxUploadSizeExceededException) = run { logger.error("API error", e) -- 2.49.1 From 9016a72046e572b1d799bd531e697b6af38527e5 Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 30 Sep 2025 18:16:13 +0900 Subject: [PATCH 14/44] =?UTF-8?q?fix:=20ResponseStatusException=EC=9D=B4?= =?UTF-8?q?=20ApiResponse=EB=A1=9C=20=EB=9E=98=ED=95=91=EB=90=98=EC=A7=80?= =?UTF-8?q?=20=EC=95=8A=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기본 에러 JSON 반환 유지 --- .../co/vividnext/sodalive/common/SodaExceptionHandler.kt | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/common/SodaExceptionHandler.kt b/src/main/kotlin/kr/co/vividnext/sodalive/common/SodaExceptionHandler.kt index 8bc17d7..74e1396 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/common/SodaExceptionHandler.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/common/SodaExceptionHandler.kt @@ -3,7 +3,6 @@ package kr.co.vividnext.sodalive.common import org.slf4j.LoggerFactory import org.springframework.dao.DataIntegrityViolationException import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity import org.springframework.security.access.AccessDeniedException import org.springframework.security.authentication.BadCredentialsException import org.springframework.security.authentication.InternalAuthenticationServiceException @@ -26,13 +25,6 @@ class SodaExceptionHandler { ) } - // ResponseStatusException은 ApiResponse로 래핑하지 않고 그대로 전달 - @ExceptionHandler(ResponseStatusException::class) - fun handleResponseStatusException(e: ResponseStatusException): ResponseEntity { - // 별도 바디 없이 상태코드만 반환하여 기본 예외 형태를 유지 - return ResponseEntity.status(e.status).build() - } - @ExceptionHandler(MaxUploadSizeExceededException::class) fun handleMaxUploadSizeExceededException(e: MaxUploadSizeExceededException) = run { logger.error("API error", e) @@ -72,6 +64,7 @@ class SodaExceptionHandler { @ExceptionHandler(Exception::class) fun handleException(e: Exception) = run { + if (e is ResponseStatusException) throw e logger.error("API error", e) ApiResponse.error("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") } -- 2.49.1 From a3d74c0b57acc81c3501e66134a8a9841983c653 Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 30 Sep 2025 18:22:46 +0900 Subject: [PATCH 15/44] =?UTF-8?q?fix:=20Payverse=20Webhook=20=EC=97=94?= =?UTF-8?q?=EB=93=9C=ED=8F=AC=EC=9D=B8=ED=8A=B8=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=8B=A4=EC=A0=9C=20=ED=81=B4=EB=9D=BC=EC=9D=B4=EC=96=B8?= =?UTF-8?q?=ED=8A=B8=20IP=EB=A5=BC=20=EA=B0=80=EC=A0=B8=EC=98=AC=20?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../co/vividnext/sodalive/can/charge/ChargeController.kt | 7 ++++++- src/main/resources/application.yml | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) 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 00c2baf..db56535 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 @@ -59,7 +59,12 @@ class ChargeController( @RequestBody request: PayverseWebhookRequest, servletRequest: HttpServletRequest ): PayverseWebhookResponse { - val remoteIp = servletRequest.remoteAddr ?: "" + val header = servletRequest.getHeader("X-Forwarded-For") + val remoteIp = if (header.isNullOrEmpty()) { + servletRequest.remoteAddr + } else { + header.split(",")[0].trim() // 첫 번째 값이 클라이언트 IP + } print("Payverse Webhook Request: $remoteIp") print("Payverse Webhook Request: $payverseInboundIp") diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index cf444de..15abb0b 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,6 +1,7 @@ server: shutdown: graceful env: ${SERVER_ENV} + forward-headers-strategy: framework logging: level: -- 2.49.1 From 863c2850492c69fad3951577fbbbd4bb4617d7fa Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 30 Sep 2025 18:32:12 +0900 Subject: [PATCH 16/44] =?UTF-8?q?fix:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20print=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/co/vividnext/sodalive/can/charge/ChargeController.kt | 3 --- 1 file changed, 3 deletions(-) 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 db56535..acb2493 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 @@ -66,9 +66,6 @@ class ChargeController( header.split(",")[0].trim() // 첫 번째 값이 클라이언트 IP } - print("Payverse Webhook Request: $remoteIp") - print("Payverse Webhook Request: $payverseInboundIp") - if (remoteIp != payverseInboundIp) { throw ResponseStatusException(HttpStatus.NOT_FOUND) } -- 2.49.1 From 78ff13a654c44219b15f91dce621bd858a32ff53 Mon Sep 17 00:00:00 2001 From: Klaus Date: Wed, 1 Oct 2025 16:07:34 +0900 Subject: [PATCH 17/44] =?UTF-8?q?temp:=20=EC=BA=94=20=EA=B0=80=EA=B2=A9=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20Int=20->=20Double=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/co/vividnext/sodalive/admin/can/AdminCanRequest.kt | 2 +- .../sodalive/admin/charge/AdminChargeStatusService.kt | 4 ++-- .../sodalive/admin/charge/GetChargeStatusDetailQueryDto.kt | 2 +- .../sodalive/admin/charge/GetChargeStatusQueryDto.kt | 2 +- .../sodalive/admin/charge/GetChargeStatusResponse.kt | 2 +- src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt | 2 +- src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt | 2 +- .../kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt | 2 +- .../vividnext/sodalive/can/charge/temp/ChargeTempService.kt | 4 ++-- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRequest.kt index 92258ff..5fa6811 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRequest.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRequest.kt @@ -7,7 +7,7 @@ import kr.co.vividnext.sodalive.extensions.moneyFormat data class AdminCanRequest( val can: Int, val rewardCan: Int, - val price: Int + val price: Double ) { fun toEntity(): Can { var title = "${can.moneyFormat()} 캔" diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusService.kt index fef1205..345ba64 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusService.kt @@ -20,7 +20,7 @@ class AdminChargeStatusService(val repository: AdminChargeStatusQueryRepository) .withZoneSameInstant(ZoneId.of("UTC")) .toLocalDateTime() - var totalChargeAmount = 0 + var totalChargeAmount = 0.toDouble() var totalChargeCount = 0L val chargeStatusList = repository.getChargeStatus(startDate, endDate) @@ -29,7 +29,7 @@ class AdminChargeStatusService(val repository: AdminChargeStatusQueryRepository) val chargeAmount = if (it.paymentGateWay == PaymentGateway.PG) { it.pgChargeAmount } else { - it.appleChargeAmount.toInt() + it.appleChargeAmount } val chargeCount = it.chargeCount diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusDetailQueryDto.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusDetailQueryDto.kt index d4b5e91..fe28627 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusDetailQueryDto.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusDetailQueryDto.kt @@ -7,7 +7,7 @@ data class GetChargeStatusDetailQueryDto @QueryProjection constructor( val nickname: String, val method: String, val appleChargeAmount: Double, - val pgChargeAmount: Int, + val pgChargeAmount: Double, val locale: String, val datetime: String ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusQueryDto.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusQueryDto.kt index c5c16d0..53c581f 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusQueryDto.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusQueryDto.kt @@ -6,7 +6,7 @@ import kr.co.vividnext.sodalive.can.payment.PaymentGateway data class GetChargeStatusQueryDto @QueryProjection constructor( val date: String, val appleChargeAmount: Double, - val pgChargeAmount: Int, + val pgChargeAmount: Double, val chargeCount: Long, val paymentGateWay: PaymentGateway ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusResponse.kt index efbe3aa..e532b23 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusResponse.kt @@ -2,7 +2,7 @@ package kr.co.vividnext.sodalive.admin.charge data class GetChargeStatusResponse( val date: String, - val chargeAmount: Int, + val chargeAmount: Double, val chargeCount: Long, val pg: String ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt index 1f0f4f6..64f5f3c 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt @@ -10,7 +10,7 @@ data class Can( var title: String, var can: Int, var rewardCan: Int, - var price: Int, + var price: Double, @Enumerated(value = EnumType.STRING) var status: CanStatus ) : BaseEntity() diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt index e3c5d48..f655f13 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt @@ -7,5 +7,5 @@ data class CanResponse @QueryProjection constructor( val title: String, val can: Int, val rewardCan: Int, - val price: Int + val price: Double ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt index 9689415..a6785c4 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt @@ -21,7 +21,7 @@ data class VerifyResult( val method: String, val pg: String, val status: Int, - val price: Int + val price: Double ) data class AppleChargeRequest( 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 8e2e645..c94dc7d 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 @@ -66,7 +66,7 @@ class ChargeTempService( VerifyResult::class.java ) - if (verifyResult.status == 1 && verifyResult.price == charge.payment!!.price.toInt()) { + if (verifyResult.status == 1 && verifyResult.price == charge.payment!!.price) { charge.payment?.receiptId = verifyResult.receiptId charge.payment?.method = verifyResult.method charge.payment?.status = PaymentStatus.COMPLETE @@ -74,7 +74,7 @@ class ChargeTempService( } else { throw SodaException("결제정보에 오류가 있습니다.") } - } catch (e: Exception) { + } catch (_: Exception) { throw SodaException("결제정보에 오류가 있습니다.") } } else { -- 2.49.1 From 0c17ea2dcdbb609b6214fb3aa432713cbdba6429 Mon Sep 17 00:00:00 2001 From: Klaus Date: Wed, 1 Oct 2025 20:37:53 +0900 Subject: [PATCH 18/44] =?UTF-8?q?fix:=20=EC=BA=94=20=EA=B0=80=EA=B2=A9,=20?= =?UTF-8?q?Payment=EC=9D=98=20price=20=ED=83=80=EC=9E=85=20Int,=20Double?= =?UTF-8?q?=20->=20BigDecimal=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/admin/can/AdminCanRequest.kt | 3 +- .../admin/charge/AdminChargeStatusService.kt | 3 +- .../charge/GetChargeStatusDetailQueryDto.kt | 5 ++-- .../admin/charge/GetChargeStatusQueryDto.kt | 5 ++-- .../admin/charge/GetChargeStatusResponse.kt | 4 ++- .../ad/AdminAdStatisticsRepository.kt | 22 ++++----------- .../ad/GetAdminAdStatisticsResponse.kt | 7 +++-- .../kr/co/vividnext/sodalive/can/Can.kt | 3 +- .../co/vividnext/sodalive/can/CanResponse.kt | 3 +- .../can/charge/ChargeCompleteResponse.kt | 4 ++- .../sodalive/can/charge/ChargeController.kt | 3 +- .../sodalive/can/charge/ChargeData.kt | 6 ++-- .../sodalive/can/charge/ChargeService.kt | 28 +++++++++---------- .../can/charge/temp/ChargeTempRequest.kt | 3 +- .../can/charge/temp/ChargeTempService.kt | 2 +- .../vividnext/sodalive/can/payment/Payment.kt | 3 +- .../sodalive/marketing/AdTrackingHistory.kt | 3 +- .../sodalive/marketing/AdTrackingService.kt | 5 ++-- 18 files changed, 56 insertions(+), 56 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRequest.kt index 5fa6811..597f5b9 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRequest.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRequest.kt @@ -3,11 +3,12 @@ package kr.co.vividnext.sodalive.admin.can import kr.co.vividnext.sodalive.can.Can import kr.co.vividnext.sodalive.can.CanStatus import kr.co.vividnext.sodalive.extensions.moneyFormat +import java.math.BigDecimal data class AdminCanRequest( val can: Int, val rewardCan: Int, - val price: Double + val price: BigDecimal ) { fun toEntity(): Can { var title = "${can.moneyFormat()} 캔" diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusService.kt index 345ba64..cc6d4ca 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/AdminChargeStatusService.kt @@ -20,11 +20,10 @@ class AdminChargeStatusService(val repository: AdminChargeStatusQueryRepository) .withZoneSameInstant(ZoneId.of("UTC")) .toLocalDateTime() - var totalChargeAmount = 0.toDouble() + var totalChargeAmount = 0.toBigDecimal() var totalChargeCount = 0L val chargeStatusList = repository.getChargeStatus(startDate, endDate) - .asSequence() .map { val chargeAmount = if (it.paymentGateWay == PaymentGateway.PG) { it.pgChargeAmount diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusDetailQueryDto.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusDetailQueryDto.kt index fe28627..3164e91 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusDetailQueryDto.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusDetailQueryDto.kt @@ -1,13 +1,14 @@ package kr.co.vividnext.sodalive.admin.charge import com.querydsl.core.annotations.QueryProjection +import java.math.BigDecimal data class GetChargeStatusDetailQueryDto @QueryProjection constructor( val memberId: Long, val nickname: String, val method: String, - val appleChargeAmount: Double, - val pgChargeAmount: Double, + val appleChargeAmount: BigDecimal, + val pgChargeAmount: BigDecimal, val locale: String, val datetime: String ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusQueryDto.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusQueryDto.kt index 53c581f..5d4c6a4 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusQueryDto.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusQueryDto.kt @@ -2,11 +2,12 @@ package kr.co.vividnext.sodalive.admin.charge import com.querydsl.core.annotations.QueryProjection import kr.co.vividnext.sodalive.can.payment.PaymentGateway +import java.math.BigDecimal data class GetChargeStatusQueryDto @QueryProjection constructor( val date: String, - val appleChargeAmount: Double, - val pgChargeAmount: Double, + val appleChargeAmount: BigDecimal, + val pgChargeAmount: BigDecimal, val chargeCount: Long, val paymentGateWay: PaymentGateway ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusResponse.kt index e532b23..75dffcb 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/charge/GetChargeStatusResponse.kt @@ -1,8 +1,10 @@ package kr.co.vividnext.sodalive.admin.charge +import java.math.BigDecimal + data class GetChargeStatusResponse( val date: String, - val chargeAmount: Double, + val chargeAmount: BigDecimal, val chargeCount: Long, val pg: String ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/statistics/ad/AdminAdStatisticsRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/statistics/ad/AdminAdStatisticsRepository.kt index 4485152..f7f55ce 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/statistics/ad/AdminAdStatisticsRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/statistics/ad/AdminAdStatisticsRepository.kt @@ -3,7 +3,6 @@ package kr.co.vividnext.sodalive.admin.statistics.ad import com.querydsl.core.types.dsl.CaseBuilder import com.querydsl.core.types.dsl.DateTimePath import com.querydsl.core.types.dsl.Expressions -import com.querydsl.core.types.dsl.NumberExpression import com.querydsl.core.types.dsl.StringTemplate import com.querydsl.jpa.impl.JPAQueryFactory import kr.co.vividnext.sodalive.marketing.AdTrackingHistoryType @@ -67,7 +66,7 @@ class AdminAdStatisticsRepository(private val queryFactory: JPAQueryFactory) { val firstPaymentTotalAmount = CaseBuilder() .`when`(adTrackingHistory.type.eq(AdTrackingHistoryType.FIRST_PAYMENT)) .then(adTrackingHistory.price) - .otherwise(Expressions.constant(0.0)) + .otherwise(0.toBigDecimal()) .sum() val repeatPaymentCount = CaseBuilder() @@ -79,7 +78,7 @@ class AdminAdStatisticsRepository(private val queryFactory: JPAQueryFactory) { val repeatPaymentTotalAmount = CaseBuilder() .`when`(adTrackingHistory.type.eq(AdTrackingHistoryType.REPEAT_PAYMENT)) .then(adTrackingHistory.price) - .otherwise(Expressions.constant(0.0)) + .otherwise(0.toBigDecimal()) .sum() val allPaymentCount = CaseBuilder() @@ -97,7 +96,7 @@ class AdminAdStatisticsRepository(private val queryFactory: JPAQueryFactory) { .or(adTrackingHistory.type.eq(AdTrackingHistoryType.REPEAT_PAYMENT)) ) .then(adTrackingHistory.price) - .otherwise(Expressions.constant(0.0)) + .otherwise(0.toBigDecimal()) .sum() return queryFactory @@ -111,11 +110,11 @@ class AdminAdStatisticsRepository(private val queryFactory: JPAQueryFactory) { loginCount, signUpCount, firstPaymentCount, - roundedValueDecimalPlaces2(firstPaymentTotalAmount), + firstPaymentTotalAmount, repeatPaymentCount, - roundedValueDecimalPlaces2(repeatPaymentTotalAmount), + repeatPaymentTotalAmount, allPaymentCount, - roundedValueDecimalPlaces2(allPaymentTotalAmount) + allPaymentTotalAmount ) ) .from(adTrackingHistory) @@ -148,13 +147,4 @@ class AdminAdStatisticsRepository(private val queryFactory: JPAQueryFactory) { "%Y-%m-%d" ) } - - private fun roundedValueDecimalPlaces2(valueExpression: NumberExpression): NumberExpression { - return Expressions.numberTemplate( - Double::class.java, - "ROUND({0}, {1})", - valueExpression, - 2 - ) - } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/statistics/ad/GetAdminAdStatisticsResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/statistics/ad/GetAdminAdStatisticsResponse.kt index 2c9627b..b968e3e 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/statistics/ad/GetAdminAdStatisticsResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/statistics/ad/GetAdminAdStatisticsResponse.kt @@ -1,6 +1,7 @@ package kr.co.vividnext.sodalive.admin.statistics.ad import com.querydsl.core.annotations.QueryProjection +import java.math.BigDecimal data class GetAdminAdStatisticsResponse( val totalCount: Int, @@ -16,9 +17,9 @@ data class GetAdminAdStatisticsItem @QueryProjection constructor( val loginCount: Int, val signUpCount: Int, val firstPaymentCount: Int, - val firstPaymentTotalAmount: Double, + val firstPaymentTotalAmount: BigDecimal, val repeatPaymentCount: Int, - val repeatPaymentTotalAmount: Double, + val repeatPaymentTotalAmount: BigDecimal, val allPaymentCount: Int, - val allPaymentTotalAmount: Double + val allPaymentTotalAmount: BigDecimal ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt index 64f5f3c..11528c6 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt @@ -1,6 +1,7 @@ package kr.co.vividnext.sodalive.can import kr.co.vividnext.sodalive.common.BaseEntity +import java.math.BigDecimal import javax.persistence.Entity import javax.persistence.EnumType import javax.persistence.Enumerated @@ -10,7 +11,7 @@ data class Can( var title: String, var can: Int, var rewardCan: Int, - var price: Double, + var price: BigDecimal, @Enumerated(value = EnumType.STRING) var status: CanStatus ) : BaseEntity() diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt index f655f13..37eb80d 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt @@ -1,11 +1,12 @@ package kr.co.vividnext.sodalive.can import com.querydsl.core.annotations.QueryProjection +import java.math.BigDecimal data class CanResponse @QueryProjection constructor( val id: Long, val title: String, val can: Int, val rewardCan: Int, - val price: Double + val price: BigDecimal ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeCompleteResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeCompleteResponse.kt index 066cd8f..8083f13 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeCompleteResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeCompleteResponse.kt @@ -1,7 +1,9 @@ package kr.co.vividnext.sodalive.can.charge +import java.math.BigDecimal + data class ChargeCompleteResponse( - val price: Double, + val price: BigDecimal, val currencyCode: String, val isFirstCharged: Boolean ) 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 acb2493..73948f3 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 @@ -168,8 +168,7 @@ class ChargeController( memberId = member.id!!, chargeId = chargeId, productId = request.productId, - purchaseToken = request.purchaseToken, - paymentGateway = request.paymentGateway + purchaseToken = request.purchaseToken ) trackingCharge(member, response) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt index a6785c4..f2b159c 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt @@ -21,14 +21,14 @@ data class VerifyResult( val method: String, val pg: String, val status: Int, - val price: Double + val price: BigDecimal ) data class AppleChargeRequest( val title: String, val chargeCan: Int, val paymentGateway: PaymentGateway, - var price: Double? = null, + var price: BigDecimal? = null, var locale: String? = null ) @@ -39,7 +39,7 @@ data class AppleVerifyResponse(val status: Int) data class GoogleChargeRequest( val title: String, val chargeCan: Int, - val price: Double, + val price: BigDecimal, val currencyCode: String, val productId: String, val purchaseToken: String, 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 271f11b..73beb86 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 @@ -33,7 +33,6 @@ import org.springframework.retry.annotation.Retryable import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import java.math.BigDecimal -import java.math.RoundingMode import java.time.LocalDateTime import java.time.format.DateTimeFormatter @@ -106,7 +105,7 @@ class ChargeService( ) val isAmountMatch = request.requestAmount.compareTo( - BigDecimal.valueOf(charge.payment!!.price) + charge.payment!!.price ) == 0 val isSuccess = request.resultStatus == "SUCCESS" && @@ -225,13 +224,13 @@ class ChargeService( charge.can = can val payment = Payment(paymentGateway = PaymentGateway.PAYVERSE) - payment.price = can.price.toDouble() + payment.price = can.price charge.payment = payment val savedCharge = chargeRepository.save(charge) val chargeId = savedCharge.id!! - val amount = BigDecimal(savedCharge.payment!!.price) + val amount = savedCharge.payment!!.price val reqDate = savedCharge.createdAt!!.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) val sign = DigestUtils.sha512Hex( String.format("||%s||%s||%s||%s||%s||", payverseSecretKey, payverseMid, chargeId, amount, reqDate) @@ -315,7 +314,7 @@ class ChargeService( ) return ChargeCompleteResponse( - price = BigDecimal(charge.payment!!.price).setScale(2, RoundingMode.HALF_UP).toDouble(), + price = charge.payment!!.price, currencyCode = charge.payment!!.locale?.takeLast(3) ?: "KRW", isFirstCharged = chargeRepository.isFirstCharged(memberId) ) @@ -330,7 +329,7 @@ class ChargeService( PaymentStatus.COMPLETE -> { // 이미 결제가 완료된 경우, 동일한 데이터로 즉시 반환 return ChargeCompleteResponse( - price = BigDecimal(charge.payment!!.price).setScale(2, RoundingMode.HALF_UP).toDouble(), + price = charge.payment!!.price, currencyCode = charge.payment!!.locale?.takeLast(3) ?: "KRW", isFirstCharged = chargeRepository.isFirstCharged(memberId) ) @@ -353,7 +352,7 @@ class ChargeService( charge.can = can val payment = Payment(paymentGateway = request.paymentGateway) - payment.price = can.price.toDouble() + payment.price = can.price charge.payment = payment chargeRepository.save(charge) @@ -392,7 +391,7 @@ class ChargeService( ) return ChargeCompleteResponse( - price = BigDecimal(charge.payment!!.price).setScale(2, RoundingMode.HALF_UP).toDouble(), + price = charge.payment!!.price, currencyCode = charge.payment!!.locale?.takeLast(3) ?: "KRW", isFirstCharged = chargeRepository.isFirstCharged(memberId) ) @@ -442,7 +441,7 @@ class ChargeService( ) return ChargeCompleteResponse( - price = BigDecimal(charge.payment!!.price).setScale(2, RoundingMode.HALF_UP).toDouble(), + price = charge.payment!!.price, currencyCode = charge.payment!!.locale?.takeLast(3) ?: "KRW", isFirstCharged = chargeRepository.isFirstCharged(memberId) ) @@ -467,7 +466,7 @@ class ChargeService( payment.price = if (request.price != null) { request.price!! } else { - 0.toDouble() + 0.toBigDecimal() } payment.locale = request.locale @@ -502,7 +501,7 @@ class ChargeService( ) return ChargeCompleteResponse( - price = BigDecimal(charge.payment!!.price).setScale(2, RoundingMode.HALF_UP).toDouble(), + price = charge.payment!!.price, currencyCode = charge.payment!!.locale?.takeLast(3) ?: "KRW", isFirstCharged = chargeRepository.isFirstCharged(memberId) ) @@ -519,7 +518,7 @@ class ChargeService( member: Member, title: String, chargeCan: Int, - price: Double, + price: BigDecimal, currencyCode: String, productId: String, purchaseToken: String, @@ -547,8 +546,7 @@ class ChargeService( memberId: Long, chargeId: Long, productId: String, - purchaseToken: String, - paymentGateway: PaymentGateway + purchaseToken: String ): ChargeCompleteResponse { val charge = chargeRepository.findByIdOrNull(id = chargeId) ?: throw SodaException("결제정보에 오류가 있습니다.") @@ -570,7 +568,7 @@ class ChargeService( ) return ChargeCompleteResponse( - price = BigDecimal(charge.payment!!.price).setScale(2, RoundingMode.HALF_UP).toDouble(), + price = charge.payment!!.price, currencyCode = charge.payment!!.locale?.takeLast(3) ?: "KRW", isFirstCharged = chargeRepository.isFirstCharged(memberId) ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/temp/ChargeTempRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/temp/ChargeTempRequest.kt index d24f907..5afdfa8 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/temp/ChargeTempRequest.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/temp/ChargeTempRequest.kt @@ -1,9 +1,10 @@ package kr.co.vividnext.sodalive.can.charge.temp import kr.co.vividnext.sodalive.can.payment.PaymentGateway +import java.math.BigDecimal data class ChargeTempRequest( val can: Int, - val price: Int, + val price: BigDecimal, val paymentGateway: PaymentGateway ) 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 c94dc7d..3f2f1aa 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 @@ -41,7 +41,7 @@ class ChargeTempService( charge.member = member val payment = Payment(paymentGateway = request.paymentGateway) - payment.price = request.price.toDouble() + payment.price = request.price charge.payment = payment chargeRepository.save(charge) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/Payment.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/Payment.kt index 2c0c527..cc18f08 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/Payment.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/Payment.kt @@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.can.payment import kr.co.vividnext.sodalive.can.charge.Charge import kr.co.vividnext.sodalive.common.BaseEntity +import java.math.BigDecimal import javax.persistence.Column import javax.persistence.Entity import javax.persistence.EnumType @@ -25,7 +26,7 @@ data class Payment( var receiptId: String? = null var method: String? = null - var price: Double = 0.toDouble() + var price: BigDecimal = 0.toBigDecimal() var locale: String? = null var orderId: String? = null } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/marketing/AdTrackingHistory.kt b/src/main/kotlin/kr/co/vividnext/sodalive/marketing/AdTrackingHistory.kt index ee5615f..51b6a11 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/marketing/AdTrackingHistory.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/marketing/AdTrackingHistory.kt @@ -1,5 +1,6 @@ package kr.co.vividnext.sodalive.marketing +import java.math.BigDecimal import java.time.LocalDateTime import javax.persistence.Entity import javax.persistence.EnumType @@ -19,7 +20,7 @@ data class AdTrackingHistory( val pidName: String, @Enumerated(value = EnumType.STRING) val type: AdTrackingHistoryType, - val price: Double = 0.toDouble(), + val price: BigDecimal = 0.toBigDecimal(), val locale: String? = null, val memberId: Long, val createdAt: LocalDateTime = LocalDateTime.now(), diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/marketing/AdTrackingService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/marketing/AdTrackingService.kt index 99b6c9b..538b8db 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/marketing/AdTrackingService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/marketing/AdTrackingService.kt @@ -4,6 +4,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.springframework.stereotype.Service +import java.math.BigDecimal @Service class AdTrackingService( @@ -17,7 +18,7 @@ class AdTrackingService( pid: String, type: AdTrackingHistoryType, memberId: Long, - price: Double? = null, + price: BigDecimal? = null, locale: String? = null ) { coroutineScope.launch { @@ -30,7 +31,7 @@ class AdTrackingService( pid = pid, pidName = mediaPartner.pidName, type = type, - price = price ?: 0.toDouble(), + price = price ?: 0.toBigDecimal(), locale = locale, memberId = memberId ) -- 2.49.1 From d7ad110b9eeb63fde8cbc92ff3c68ede573ecac1 Mon Sep 17 00:00:00 2001 From: Klaus Date: Wed, 1 Oct 2025 20:55:52 +0900 Subject: [PATCH 19/44] =?UTF-8?q?feat:=20=EC=BA=94=20=EB=93=B1=EB=A1=9D/?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20-=20currency=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/co/vividnext/sodalive/admin/can/AdminCanRequest.kt | 4 +++- src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt | 1 + src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt | 3 ++- src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt | 3 ++- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRequest.kt index 597f5b9..83bda60 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRequest.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRequest.kt @@ -8,7 +8,8 @@ import java.math.BigDecimal data class AdminCanRequest( val can: Int, val rewardCan: Int, - val price: BigDecimal + val price: BigDecimal, + val currency: String ) { fun toEntity(): Can { var title = "${can.moneyFormat()} 캔" @@ -21,6 +22,7 @@ data class AdminCanRequest( can = can, rewardCan = rewardCan, price = price, + currency = currency, status = CanStatus.SALE ) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt index 11528c6..9d4a249 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt @@ -12,6 +12,7 @@ data class Can( var can: Int, var rewardCan: Int, var price: BigDecimal, + var currency: String, @Enumerated(value = EnumType.STRING) var status: CanStatus ) : BaseEntity() diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt index 12fc614..ee17507 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt @@ -40,7 +40,8 @@ class CanQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : CanQue can1.title, can1.can, can1.rewardCan, - can1.price + can1.price, + can1.currency ) ) .from(can1) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt index 37eb80d..9e3b692 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt @@ -8,5 +8,6 @@ data class CanResponse @QueryProjection constructor( val title: String, val can: Int, val rewardCan: Int, - val price: BigDecimal + val price: BigDecimal, + val currency: String ) -- 2.49.1 From 81e7e7129cf1cd43b287e47d26675ccd52fd8371 Mon Sep 17 00:00:00 2001 From: Klaus Date: Wed, 1 Oct 2025 21:05:51 +0900 Subject: [PATCH 20/44] =?UTF-8?q?feat:=20=EC=BA=94=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20currency=20-=20length=203=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EA=B3=A0=EC=A0=95=ED=95=98=EC=97=AC=20CHAR(3)=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=EC=9D=91=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt index 9d4a249..b1e7ae6 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt @@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.can import kr.co.vividnext.sodalive.common.BaseEntity import java.math.BigDecimal +import javax.persistence.Column import javax.persistence.Entity import javax.persistence.EnumType import javax.persistence.Enumerated @@ -11,7 +12,9 @@ data class Can( var title: String, var can: Int, var rewardCan: Int, + @Column(precision = 10, scale = 4, nullable = false) var price: BigDecimal, + @Column(length = 3, nullable = false) var currency: String, @Enumerated(value = EnumType.STRING) var status: CanStatus -- 2.49.1 From 3c7ba669e25eb7886419b700e2f23a43b85a69a2 Mon Sep 17 00:00:00 2001 From: Klaus Date: Wed, 1 Oct 2025 21:08:35 +0900 Subject: [PATCH 21/44] =?UTF-8?q?feat:=20Payment,=20AdTrackingHistory=20?= =?UTF-8?q?=EC=97=94=ED=8B=B0=ED=8B=B0=20price=20-=20Decimal(10,=204)?= =?UTF-8?q?=EC=97=90=20=EB=8C=80=EC=9D=91=EB=90=98=EB=8F=84=EB=A1=9D=20Col?= =?UTF-8?q?umn=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/kr/co/vividnext/sodalive/can/payment/Payment.kt | 1 + .../kr/co/vividnext/sodalive/marketing/AdTrackingHistory.kt | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/Payment.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/Payment.kt index cc18f08..c146458 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/Payment.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/Payment.kt @@ -26,6 +26,7 @@ data class Payment( var receiptId: String? = null var method: String? = null + @Column(precision = 10, scale = 4, nullable = false) var price: BigDecimal = 0.toBigDecimal() var locale: String? = null var orderId: String? = null diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/marketing/AdTrackingHistory.kt b/src/main/kotlin/kr/co/vividnext/sodalive/marketing/AdTrackingHistory.kt index 51b6a11..31a58cd 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/marketing/AdTrackingHistory.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/marketing/AdTrackingHistory.kt @@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.marketing import java.math.BigDecimal import java.time.LocalDateTime +import javax.persistence.Column import javax.persistence.Entity import javax.persistence.EnumType import javax.persistence.Enumerated @@ -20,6 +21,7 @@ data class AdTrackingHistory( val pidName: String, @Enumerated(value = EnumType.STRING) val type: AdTrackingHistoryType, + @Column(precision = 10, scale = 4, nullable = false) val price: BigDecimal = 0.toBigDecimal(), val locale: String? = null, val memberId: Long, -- 2.49.1 From b244944f419051b2d665bb4169dad17c254b4cbc Mon Sep 17 00:00:00 2001 From: Klaus Date: Wed, 1 Oct 2025 21:21:57 +0900 Subject: [PATCH 22/44] =?UTF-8?q?feat:=20=EC=BA=94=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20currency=20-=20length=203=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EA=B3=A0=EC=A0=95=ED=95=98=EC=97=AC=20CHAR(3)=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=EC=9D=91=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt index b1e7ae6..ec8f887 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/Can.kt @@ -14,7 +14,7 @@ data class Can( var rewardCan: Int, @Column(precision = 10, scale = 4, nullable = false) var price: BigDecimal, - @Column(length = 3, nullable = false) + @Column(length = 3, nullable = false, columnDefinition = "CHAR(3)") var currency: String, @Enumerated(value = EnumType.STRING) var status: CanStatus -- 2.49.1 From 3d852a83568f939a5da726a444500c65b6dbc59e Mon Sep 17 00:00:00 2001 From: Klaus Date: Wed, 1 Oct 2025 22:16:44 +0900 Subject: [PATCH 23/44] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90?= =?UTF-8?q?=EC=9A=A9=20=EC=BA=94=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/admin/can/AdminCanController.kt | 7 ++++ .../sodalive/admin/can/AdminCanRepository.kt | 33 ++++++++++++++++++- .../sodalive/admin/can/AdminCanService.kt | 5 +++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanController.kt index 042eb29..0a79c94 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanController.kt @@ -1,8 +1,10 @@ package kr.co.vividnext.sodalive.admin.can +import kr.co.vividnext.sodalive.can.CanResponse import kr.co.vividnext.sodalive.common.ApiResponse import org.springframework.security.access.prepost.PreAuthorize import org.springframework.web.bind.annotation.DeleteMapping +import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody @@ -13,6 +15,11 @@ import org.springframework.web.bind.annotation.RestController @RequestMapping("/admin/can") @PreAuthorize("hasRole('ADMIN')") class AdminCanController(private val service: AdminCanService) { + @GetMapping + fun getCans(): ApiResponse> { + return ApiResponse.ok(service.getCans()) + } + @PostMapping fun insertCan(@RequestBody request: AdminCanRequest) = ApiResponse.ok(service.saveCan(request)) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRepository.kt index e784d13..60f4e6b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRepository.kt @@ -1,6 +1,37 @@ package kr.co.vividnext.sodalive.admin.can +import com.querydsl.jpa.impl.JPAQueryFactory import kr.co.vividnext.sodalive.can.Can +import kr.co.vividnext.sodalive.can.CanResponse +import kr.co.vividnext.sodalive.can.CanStatus +import kr.co.vividnext.sodalive.can.QCan.can1 +import kr.co.vividnext.sodalive.can.QCanResponse import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository -interface AdminCanRepository : JpaRepository +interface AdminCanRepository : JpaRepository, AdminCanQueryRepository + +interface AdminCanQueryRepository { + fun findAllByStatus(status: CanStatus): List +} + +@Repository +class AdminCanQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : AdminCanQueryRepository { + override fun findAllByStatus(status: CanStatus): List { + return queryFactory + .select( + QCanResponse( + can1.id, + can1.title, + can1.can, + can1.rewardCan, + can1.price, + can1.currency + ) + ) + .from(can1) + .where(can1.status.eq(status)) + .orderBy(can1.currency.asc(), can1.price.asc()) + .fetch() + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanService.kt index 786138b..612e414 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanService.kt @@ -1,6 +1,7 @@ package kr.co.vividnext.sodalive.admin.can import kr.co.vividnext.sodalive.admin.member.AdminMemberRepository +import kr.co.vividnext.sodalive.can.CanResponse import kr.co.vividnext.sodalive.can.CanStatus import kr.co.vividnext.sodalive.can.charge.Charge import kr.co.vividnext.sodalive.can.charge.ChargeRepository @@ -20,6 +21,10 @@ class AdminCanService( private val chargeRepository: ChargeRepository, private val memberRepository: AdminMemberRepository ) { + fun getCans(): List { + return repository.findAllByStatus(status = CanStatus.SALE) + } + @Transactional fun saveCan(request: AdminCanRequest) { repository.save(request.toEntity()) -- 2.49.1 From e45fe1bf104b171b6ffbd6313c4ad7ebbdfc21a0 Mon Sep 17 00:00:00 2001 From: Klaus Date: Wed, 1 Oct 2025 22:29:39 +0900 Subject: [PATCH 24/44] =?UTF-8?q?feat:=20=EC=9D=BC=EB=B0=98=20=EC=9C=A0?= =?UTF-8?q?=EC=A0=80=EC=9A=A9=20=EC=BA=94=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20API=20=EC=B6=94=EA=B0=80,=20GeoCountryFilt?= =?UTF-8?q?er(GeoCountry.OTHER,=20GeoCountry.KR=20=EA=B5=AC=EB=B6=84?= =?UTF-8?q?=EC=9A=A9)=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vividnext/sodalive/can/CanController.kt | 8 +++++-- .../vividnext/sodalive/can/CanRepository.kt | 22 +++++++++++++++++++ .../co/vividnext/sodalive/can/CanService.kt | 9 ++++++-- .../vividnext/sodalive/common/GeoCountry.kt | 8 +++++++ .../sodalive/common/GeoCountryFilter.kt | 20 +++++++++++++++++ 5 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/common/GeoCountry.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/common/GeoCountryFilter.kt 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 af53b78..3ec4ef4 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanController.kt @@ -1,6 +1,7 @@ package kr.co.vividnext.sodalive.can import kr.co.vividnext.sodalive.common.ApiResponse +import kr.co.vividnext.sodalive.common.GeoCountry import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.member.Member import org.springframework.data.domain.Pageable @@ -9,13 +10,16 @@ import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController +import javax.servlet.http.HttpServletRequest @RestController @RequestMapping("/can") class CanController(private val service: CanService) { @GetMapping - fun getCans(): ApiResponse> { - return ApiResponse.ok(service.getCans()) + fun getCans(request: HttpServletRequest): ApiResponse> { + val geoCountry = request.getAttribute("geoCountry") as? GeoCountry ?: GeoCountry.OTHER + println("geoCountry: $geoCountry") + return ApiResponse.ok(service.getCans(geoCountry)) } @GetMapping("/status") diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt index ee17507..c7f08a8 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt @@ -24,6 +24,7 @@ interface CanRepository : JpaRepository, CanQueryRepository interface CanQueryRepository { fun findAllByStatus(status: CanStatus): List + fun findAllByStatusAndCurrency(status: CanStatus, currency: String): List fun getCanUseStatus(member: Member, pageable: Pageable): List fun getCanChargeStatus(member: Member, pageable: Pageable, container: String): List fun isExistPaidLiveRoom(memberId: Long, roomId: Long): UseCan? @@ -50,6 +51,27 @@ class CanQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : CanQue .fetch() } + override fun findAllByStatusAndCurrency(status: CanStatus, currency: String): List { + return queryFactory + .select( + QCanResponse( + can1.id, + can1.title, + can1.can, + can1.rewardCan, + can1.price, + can1.currency + ) + ) + .from(can1) + .where( + can1.status.eq(status), + can1.currency.eq(currency) + ) + .orderBy(can1.can.asc()) + .fetch() + } + override fun getCanUseStatus(member: Member, pageable: Pageable): List { return queryFactory .selectFrom(useCan) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt index 555cc6e..27ef531 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt @@ -3,6 +3,7 @@ package kr.co.vividnext.sodalive.can import kr.co.vividnext.sodalive.can.charge.ChargeStatus import kr.co.vividnext.sodalive.can.payment.PaymentGateway import kr.co.vividnext.sodalive.can.use.CanUsage +import kr.co.vividnext.sodalive.common.GeoCountry import kr.co.vividnext.sodalive.member.Member import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service @@ -11,8 +12,12 @@ import java.time.format.DateTimeFormatter @Service class CanService(private val repository: CanRepository) { - fun getCans(): List { - return repository.findAllByStatus(status = CanStatus.SALE) + fun getCans(geoCountry: GeoCountry): List { + val currency = when (geoCountry) { + GeoCountry.KR -> "KRW" + else -> "USD" + } + return repository.findAllByStatusAndCurrency(status = CanStatus.SALE, currency = currency) } fun getCanStatus(member: Member, container: String): GetCanStatusResponse { diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/common/GeoCountry.kt b/src/main/kotlin/kr/co/vividnext/sodalive/common/GeoCountry.kt new file mode 100644 index 0000000..be44f32 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/common/GeoCountry.kt @@ -0,0 +1,8 @@ +package kr.co.vividnext.sodalive.common + +const val WAF_GEO_HEADER = "x-amzn-waf-geo-country" + +enum class GeoCountry { KR, OTHER } + +fun parseGeo(headerValue: String?): GeoCountry = + if (headerValue?.trim()?.uppercase() == "KR") GeoCountry.KR else GeoCountry.OTHER diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/common/GeoCountryFilter.kt b/src/main/kotlin/kr/co/vividnext/sodalive/common/GeoCountryFilter.kt new file mode 100644 index 0000000..1981f60 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/common/GeoCountryFilter.kt @@ -0,0 +1,20 @@ +package kr.co.vividnext.sodalive.common + +import org.springframework.stereotype.Component +import org.springframework.web.filter.OncePerRequestFilter +import javax.servlet.FilterChain +import javax.servlet.http.HttpServletRequest +import javax.servlet.http.HttpServletResponse + +@Component +class GeoCountryFilter : OncePerRequestFilter() { + override fun doFilterInternal( + request: HttpServletRequest, + response: HttpServletResponse, + filterChain: FilterChain + ) { + val country = parseGeo(request.getHeader(WAF_GEO_HEADER)) + request.setAttribute("geoCountry", country) + filterChain.doFilter(request, response) + } +} -- 2.49.1 From fb0a9e98a17e0dc261ec96b048bf8b9cc30831a1 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 2 Oct 2025 12:20:25 +0900 Subject: [PATCH 25/44] =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8A=94=20print=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/kr/co/vividnext/sodalive/can/CanController.kt | 1 - 1 file changed, 1 deletion(-) 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 3ec4ef4..e002aba 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanController.kt @@ -18,7 +18,6 @@ class CanController(private val service: CanService) { @GetMapping fun getCans(request: HttpServletRequest): ApiResponse> { val geoCountry = request.getAttribute("geoCountry") as? GeoCountry ?: GeoCountry.OTHER - println("geoCountry: $geoCountry") return ApiResponse.ok(service.getCans(geoCountry)) } -- 2.49.1 From 2ab2a047480b2a711db97a44b382430209eb131f Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 2 Oct 2025 15:07:57 +0900 Subject: [PATCH 26/44] =?UTF-8?q?feat(can):=20=EC=BA=94=20=EC=9D=91?= =?UTF-8?q?=EB=8B=B5=20-=20String=20=ED=98=95=ED=83=9C=20=EA=B0=80?= =?UTF-8?q?=EA=B2=A9=20=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/admin/can/AdminCanRepository.kt | 3 ++- .../vividnext/sodalive/can/CanRepository.kt | 22 ++----------------- .../co/vividnext/sodalive/can/CanResponse.kt | 3 ++- 3 files changed, 6 insertions(+), 22 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRepository.kt index 60f4e6b..f7bb09b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRepository.kt @@ -26,7 +26,8 @@ class AdminCanQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : A can1.can, can1.rewardCan, can1.price, - can1.currency + can1.currency, + can1.price.stringValue() ) ) .from(can1) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt index c7f08a8..9ed5af7 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt @@ -23,7 +23,6 @@ import org.springframework.stereotype.Repository interface CanRepository : JpaRepository, CanQueryRepository interface CanQueryRepository { - fun findAllByStatus(status: CanStatus): List fun findAllByStatusAndCurrency(status: CanStatus, currency: String): List fun getCanUseStatus(member: Member, pageable: Pageable): List fun getCanChargeStatus(member: Member, pageable: Pageable, container: String): List @@ -33,24 +32,6 @@ interface CanQueryRepository { @Repository class CanQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : CanQueryRepository { - override fun findAllByStatus(status: CanStatus): List { - return queryFactory - .select( - QCanResponse( - can1.id, - can1.title, - can1.can, - can1.rewardCan, - can1.price, - can1.currency - ) - ) - .from(can1) - .where(can1.status.eq(status)) - .orderBy(can1.can.asc()) - .fetch() - } - override fun findAllByStatusAndCurrency(status: CanStatus, currency: String): List { return queryFactory .select( @@ -60,7 +41,8 @@ class CanQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : CanQue can1.can, can1.rewardCan, can1.price, - can1.currency + can1.currency, + can1.price.stringValue() ) ) .from(can1) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt index 9e3b692..bc82579 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt @@ -9,5 +9,6 @@ data class CanResponse @QueryProjection constructor( val can: Int, val rewardCan: Int, val price: BigDecimal, - val currency: String + val currency: String, + val priceStr: String ) -- 2.49.1 From 6327a5d2bf5eec268cb9e8e814e8213911c8931a Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 2 Oct 2025 18:59:52 +0900 Subject: [PATCH 27/44] =?UTF-8?q?feat(charge):=20=EC=BA=94=20=EC=B6=A9?= =?UTF-8?q?=EC=A0=84=EC=8B=9C=20=ED=86=B5=ED=99=94(KRW,=20USD)=EB=B3=84?= =?UTF-8?q?=EB=A1=9C=20=EB=B6=84=EA=B8=B0=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/can/charge/ChargeService.kt | 68 ++++++++++++++++--- src/main/resources/application.yml | 7 +- 2 files changed, 65 insertions(+), 10 deletions(-) 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 73beb86..ab466db 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 @@ -72,6 +72,14 @@ class ChargeService( private val payverseClientKey: String, @Value("\${payverse.secret-key}") private val payverseSecretKey: String, + + @Value("\${payverse.usd-mid}") + private val payverseUsdMid: String, + @Value("\${payverse.usd-client-key}") + private val payverseUsdClientKey: String, + @Value("\${payverse.usd-secret-key}") + private val payverseUsdSecretKey: String, + @Value("\${payverse.host}") private val payverseHost: String, @@ -96,8 +104,16 @@ class ChargeService( val expectedSign = DigestUtils.sha512Hex( String.format( "||%s||%s||%s||%s||%s||", - payverseSecretKey, - payverseMid, + if (request.requestCurrency == "KRW") { + payverseSecretKey + } else { + payverseUsdSecretKey + }, + if (request.requestCurrency == "KRW") { + payverseMid + } else { + payverseUsdMid + }, request.orderId, request.requestAmount, request.approvalDay @@ -218,6 +234,24 @@ class ChargeService( val can = canRepository.findByIdOrNull(request.canId) ?: throw SodaException("잘못된 요청입니다\n앱 종료 후 다시 시도해 주세요.") + val requestCurrency = can.currency + val isKrw = requestCurrency == "KRW" + val mid = if (isKrw) { + payverseMid + } else { + payverseUsdMid + } + val clientKey = if (isKrw) { + payverseClientKey + } else { + payverseUsdClientKey + } + val secretKey = if (isKrw) { + payverseSecretKey + } else { + payverseUsdSecretKey + } + val charge = Charge(can.can, can.rewardCan) charge.title = can.title charge.member = member @@ -233,14 +267,20 @@ class ChargeService( val amount = savedCharge.payment!!.price val reqDate = savedCharge.createdAt!!.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) val sign = DigestUtils.sha512Hex( - String.format("||%s||%s||%s||%s||%s||", payverseSecretKey, payverseMid, chargeId, amount, reqDate) + String.format( + "||%s||%s||%s||%s||%s||", + secretKey, + mid, + chargeId, + amount, + reqDate + ) ) val customerId = "${serverEnv}_user_${member.id!!}" - val requestCurrency = "KRW" val payload = linkedMapOf( - "mid" to payverseMid, - "clientKey" to payverseClientKey, + "mid" to mid, + "clientKey" to clientKey, "orderId" to chargeId.toString(), "customerId" to customerId, "productName" to can.title, @@ -261,6 +301,18 @@ class ChargeService( val member = memberRepository.findByIdOrNull(memberId) ?: throw SodaException("로그인 정보를 확인해주세요.") + val isKrw = charge.can?.currency == "KRW" + val mid = if (isKrw) { + payverseMid + } else { + payverseUsdMid + } + val clientKey = if (isKrw) { + payverseClientKey + } else { + payverseUsdClientKey + } + // 결제수단 확인 if (charge.payment?.paymentGateway != PaymentGateway.PAYVERSE) { throw SodaException("결제정보에 오류가 있습니다.") @@ -273,8 +325,8 @@ class ChargeService( val url = "$payverseHost/payment/search/transaction/${verifyRequest.transactionId}" val request = Request.Builder() .url(url) - .addHeader("mid", payverseMid) - .addHeader("clientKey", payverseClientKey) + .addHeader("mid", mid) + .addHeader("clientKey", clientKey) .get() .build() diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 15abb0b..c268ead 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -15,11 +15,14 @@ weraser: apiKey: ${WERASER_API_KEY} payverse: + host: ${PAYVERSE_HOST} + inboundIp: ${PAYVERSE_INBOUND_IP} mid: ${PAYVERSE_MID} clientKey: ${PAYVERSE_CLIENT_KEY} secretKey: ${PAYVERSE_SECRET_KEY} - host: ${PAYVERSE_HOST} - inboundIp: ${PAYVERSE_INBOUND_IP} + usdMid: ${PAYVERSE_USD_MID} + usdClientKey: ${PAYVERSE_USD_CLIENT_KEY} + usdSecretKey: ${PAYVERSE_USD_SECRET_KEY} bootpay: applicationId: ${BOOTPAY_APPLICATION_ID} -- 2.49.1 From bc378cc619dc5c13209cf4532445589212c524e5 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 2 Oct 2025 19:03:42 +0900 Subject: [PATCH 28/44] =?UTF-8?q?temp(charge):=20=EC=BA=94=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 해외 충전 테스트를 위해 전체 캔 리스트 표시 --- .../vividnext/sodalive/can/CanRepository.kt | 20 +++++++++++++++++++ .../co/vividnext/sodalive/can/CanService.kt | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt index 9ed5af7..76ea545 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt @@ -23,6 +23,7 @@ import org.springframework.stereotype.Repository interface CanRepository : JpaRepository, CanQueryRepository interface CanQueryRepository { + fun findAllByStatus(status: CanStatus): List fun findAllByStatusAndCurrency(status: CanStatus, currency: String): List fun getCanUseStatus(member: Member, pageable: Pageable): List fun getCanChargeStatus(member: Member, pageable: Pageable, container: String): List @@ -32,6 +33,25 @@ interface CanQueryRepository { @Repository class CanQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : CanQueryRepository { + override fun findAllByStatus(status: CanStatus): List { + return queryFactory + .select( + QCanResponse( + can1.id, + can1.title, + can1.can, + can1.rewardCan, + can1.price, + can1.currency, + can1.price.stringValue() + ) + ) + .from(can1) + .where(can1.status.eq(status)) + .orderBy(can1.currency.asc(), can1.can.asc()) + .fetch() + } + override fun findAllByStatusAndCurrency(status: CanStatus, currency: String): List { return queryFactory .select( diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt index 27ef531..9157f6d 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt @@ -14,7 +14,7 @@ import java.time.format.DateTimeFormatter class CanService(private val repository: CanRepository) { fun getCans(geoCountry: GeoCountry): List { val currency = when (geoCountry) { - GeoCountry.KR -> "KRW" + GeoCountry.KR -> "USD" else -> "USD" } return repository.findAllByStatusAndCurrency(status = CanStatus.SALE, currency = currency) -- 2.49.1 From c72adbfc4bd84308473d7d96f43d95832998b0cc Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 2 Oct 2025 19:56:23 +0900 Subject: [PATCH 29/44] =?UTF-8?q?temp(charge):=20=EC=BA=94=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 해외 충전 테스트를 위해 전체 캔 리스트 표시 --- src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt index 9157f6d..b399882 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt @@ -14,10 +14,10 @@ import java.time.format.DateTimeFormatter class CanService(private val repository: CanRepository) { fun getCans(geoCountry: GeoCountry): List { val currency = when (geoCountry) { - GeoCountry.KR -> "USD" + GeoCountry.KR -> "KRW" else -> "USD" } - return repository.findAllByStatusAndCurrency(status = CanStatus.SALE, currency = currency) + return repository.findAllByStatus(status = CanStatus.SALE) } fun getCanStatus(member: Member, container: String): GetCanStatusResponse { -- 2.49.1 From 32935aed885e1ad2d6343b165cf9ab9e8fd4b375 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 2 Oct 2025 19:59:04 +0900 Subject: [PATCH 30/44] =?UTF-8?q?feat(charge):=20payloadJson=EC=9D=98=20am?= =?UTF-8?q?ount?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 소수점 아래 불필요한 0을 제거 --- .../kr/co/vividnext/sodalive/can/charge/ChargeService.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 ab466db..75c604d 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 @@ -33,6 +33,7 @@ import org.springframework.retry.annotation.Retryable import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import java.math.BigDecimal +import java.math.RoundingMode import java.time.LocalDateTime import java.time.format.DateTimeFormatter @@ -264,7 +265,7 @@ class ChargeService( val savedCharge = chargeRepository.save(charge) val chargeId = savedCharge.id!! - val amount = savedCharge.payment!!.price + val amount = savedCharge.payment!!.price.setScale(4, RoundingMode.HALF_UP).stripTrailingZeros() val reqDate = savedCharge.createdAt!!.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) val sign = DigestUtils.sha512Hex( String.format( -- 2.49.1 From 8cc9641bbfc8ce7e017e319da3ed80674044ab37 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 2 Oct 2025 20:29:04 +0900 Subject: [PATCH 31/44] =?UTF-8?q?feat(charge):=20payloadJson=EC=9D=98=20am?= =?UTF-8?q?ount?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 소수점 아래 불필요한 0을 제거 --- .../co/vividnext/sodalive/can/charge/ChargeService.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) 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 75c604d..d26e2df 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 @@ -265,7 +265,12 @@ class ChargeService( val savedCharge = chargeRepository.save(charge) val chargeId = savedCharge.id!! - val amount = savedCharge.payment!!.price.setScale(4, RoundingMode.HALF_UP).stripTrailingZeros() + val amount = BigDecimal( + savedCharge.payment!!.price + .setScale(4, RoundingMode.HALF_UP) + .stripTrailingZeros() + .toPlainString() + ) val reqDate = savedCharge.createdAt!!.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) val sign = DigestUtils.sha512Hex( String.format( @@ -374,7 +379,8 @@ class ChargeService( } else { throw SodaException("결제정보에 오류가 있습니다.") } - } catch (_: Exception) { + } catch (e: Exception) { + e.printStackTrace() throw SodaException("결제정보에 오류가 있습니다.") } } -- 2.49.1 From 79cd2b8123c2873730ec3f63a1a7f7bc9635cb7e Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 2 Oct 2025 20:40:34 +0900 Subject: [PATCH 32/44] =?UTF-8?q?debug(charge):=20=ED=95=B4=EC=99=B8?= =?UTF-8?q?=EA=B2=B0=EC=A0=9C=20DEBUG=EB=A5=BC=20=EC=9C=84=ED=95=B4=20prin?= =?UTF-8?q?t=20=EC=9E=84=EC=8B=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/co/vividnext/sodalive/can/charge/ChargeService.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 d26e2df..f6dbb69 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 @@ -342,7 +342,9 @@ class ChargeService( } val body = response.body?.string() ?: throw SodaException("결제정보에 오류가 있습니다.") + println(body) val verifyResponse = objectMapper.readValue(body, PayverseVerifyResponse::class.java) + println(verifyResponse) val isSuccess = verifyResponse.resultStatus == "SUCCESS" && verifyResponse.transactionStatus == "SUCCESS" && @@ -380,7 +382,8 @@ class ChargeService( throw SodaException("결제정보에 오류가 있습니다.") } } catch (e: Exception) { - e.printStackTrace() + print(e.localizedMessage) + print(e.message) throw SodaException("결제정보에 오류가 있습니다.") } } -- 2.49.1 From 4b13265737f0704d072668612bb3397f6438b9a9 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 2 Oct 2025 22:23:06 +0900 Subject: [PATCH 33/44] =?UTF-8?q?fix(charge):=20payverseVerify=20=EA=B2=B0?= =?UTF-8?q?=EC=A0=9C=EA=B8=88=EC=95=A1=20=EB=B9=84=EA=B5=90=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - BigDecimal끼리 비교하는데 casting 로직이 추가되어 문제가 생기던 버그 수정 --- .../kr/co/vividnext/sodalive/can/charge/ChargeService.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) 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 f6dbb69..d0dd85e 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 @@ -342,15 +342,15 @@ class ChargeService( } val body = response.body?.string() ?: throw SodaException("결제정보에 오류가 있습니다.") - println(body) val verifyResponse = objectMapper.readValue(body, PayverseVerifyResponse::class.java) println(verifyResponse) val isSuccess = verifyResponse.resultStatus == "SUCCESS" && verifyResponse.transactionStatus == "SUCCESS" && verifyResponse.orderId.toLongOrNull() == charge.id && - verifyResponse.processingAmount.compareTo(BigDecimal.valueOf(charge.can!!.price.toLong())) == 0 + verifyResponse.processingAmount.compareTo(charge.can!!.price) == 0 + println(isSuccess) if (isSuccess) { // verify 함수의 232~248 라인과 동일 처리 charge.payment?.receiptId = verifyResponse.tid @@ -381,9 +381,7 @@ class ChargeService( } else { throw SodaException("결제정보에 오류가 있습니다.") } - } catch (e: Exception) { - print(e.localizedMessage) - print(e.message) + } catch (_: Exception) { throw SodaException("결제정보에 오류가 있습니다.") } } -- 2.49.1 From 0866e0972a22bc2644245580e88263a22a5d62ad Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 2 Oct 2025 22:31:23 +0900 Subject: [PATCH 34/44] =?UTF-8?q?=EA=B0=92=20=ED=99=95=EC=9D=B8=EC=9D=84?= =?UTF-8?q?=20=EC=9C=84=ED=95=B4=20=EC=B6=94=EA=B0=80=ED=96=88=EB=8D=98=20?= =?UTF-8?q?println=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/kr/co/vividnext/sodalive/can/charge/ChargeService.kt | 2 -- 1 file changed, 2 deletions(-) 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 d0dd85e..894dba0 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 @@ -343,14 +343,12 @@ class ChargeService( val body = response.body?.string() ?: throw SodaException("결제정보에 오류가 있습니다.") val verifyResponse = objectMapper.readValue(body, PayverseVerifyResponse::class.java) - println(verifyResponse) val isSuccess = verifyResponse.resultStatus == "SUCCESS" && verifyResponse.transactionStatus == "SUCCESS" && verifyResponse.orderId.toLongOrNull() == charge.id && verifyResponse.processingAmount.compareTo(charge.can!!.price) == 0 - println(isSuccess) if (isSuccess) { // verify 함수의 232~248 라인과 동일 처리 charge.payment?.receiptId = verifyResponse.tid -- 2.49.1 From 085b217abbad32ddc5b36d1ebdc428249871fad7 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 3 Oct 2025 00:02:47 +0900 Subject: [PATCH 35/44] =?UTF-8?q?fix(can):=20=EC=9D=B4=EC=A0=84=20?= =?UTF-8?q?=EB=B2=84=EC=A0=84=EC=9D=98=20=ED=98=B8=ED=99=98=EC=84=B1?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=B4=20=EA=B8=B0=EC=A1=B4=EC=9D=98=20?= =?UTF-8?q?int=20price=EB=A5=BC=20=EC=9C=A0=EC=A7=80=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/co/vividnext/sodalive/admin/can/AdminCanRepository.kt | 2 +- src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt | 4 ++-- src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRepository.kt index f7bb09b..e9c0793 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/can/AdminCanRepository.kt @@ -25,7 +25,7 @@ class AdminCanQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : A can1.title, can1.can, can1.rewardCan, - can1.price, + can1.price.intValue(), can1.currency, can1.price.stringValue() ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt index 76ea545..b66d609 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt @@ -41,7 +41,7 @@ class CanQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : CanQue can1.title, can1.can, can1.rewardCan, - can1.price, + can1.price.intValue(), can1.currency, can1.price.stringValue() ) @@ -60,7 +60,7 @@ class CanQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : CanQue can1.title, can1.can, can1.rewardCan, - can1.price, + can1.price.intValue(), can1.currency, can1.price.stringValue() ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt index bc82579..ffc5d91 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanResponse.kt @@ -1,14 +1,13 @@ package kr.co.vividnext.sodalive.can import com.querydsl.core.annotations.QueryProjection -import java.math.BigDecimal data class CanResponse @QueryProjection constructor( val id: Long, val title: String, val can: Int, val rewardCan: Int, - val price: BigDecimal, + val price: Int, val currency: String, val priceStr: String ) -- 2.49.1 From 02ef706fc2c6ece82cedd380c467d4ea98a5840a Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 3 Oct 2025 00:57:50 +0900 Subject: [PATCH 36/44] =?UTF-8?q?temp:=20=EB=94=94=EB=B2=84=EA=B9=85?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=B4=20print=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/co/vividnext/sodalive/can/charge/ChargeService.kt | 3 +++ 1 file changed, 3 insertions(+) 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 894dba0..c0d9223 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 @@ -342,12 +342,15 @@ class ChargeService( } val body = response.body?.string() ?: throw SodaException("결제정보에 오류가 있습니다.") + println(body) val verifyResponse = objectMapper.readValue(body, PayverseVerifyResponse::class.java) + println(verifyResponse) val isSuccess = verifyResponse.resultStatus == "SUCCESS" && verifyResponse.transactionStatus == "SUCCESS" && verifyResponse.orderId.toLongOrNull() == charge.id && verifyResponse.processingAmount.compareTo(charge.can!!.price) == 0 + println(isSuccess) if (isSuccess) { // verify 함수의 232~248 라인과 동일 처리 -- 2.49.1 From 42eaf1d5e322e0313b3f8ec6e25a367137b0d0fc Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 3 Oct 2025 01:25:27 +0900 Subject: [PATCH 37/44] =?UTF-8?q?fix(payverse-verify):=20=EA=B2=B0?= =?UTF-8?q?=EC=A0=9C=20=EC=84=B1=EA=B3=B5=20=EC=97=AC=EB=B6=80=20=ED=8C=90?= =?UTF-8?q?=EB=8B=A8=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - processingAmount 대신 requestAmount와 can 가격 비교 - productName, customerId 비교 추가 --- .../kr/co/vividnext/sodalive/can/charge/ChargeData.kt | 5 +++-- .../kr/co/vividnext/sodalive/can/charge/ChargeService.kt | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt index f2b159c..d366ee8 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt @@ -70,8 +70,9 @@ data class PayverseVerifyResponse( val transactionMessage: String, val orderId: String, val customerId: String, - val processingCurrency: String, - val processingAmount: BigDecimal + val productName: String, + val requestCurrency: String, + val requestAmount: BigDecimal ) data class PayverseWebhookRequest( 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 c0d9223..28b028e 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 @@ -346,10 +346,13 @@ class ChargeService( val verifyResponse = objectMapper.readValue(body, PayverseVerifyResponse::class.java) println(verifyResponse) + val customerId = "${serverEnv}_user_${member.id!!}" val isSuccess = verifyResponse.resultStatus == "SUCCESS" && verifyResponse.transactionStatus == "SUCCESS" && verifyResponse.orderId.toLongOrNull() == charge.id && - verifyResponse.processingAmount.compareTo(charge.can!!.price) == 0 + verifyResponse.productName == charge.title && + verifyResponse.customerId == customerId && + verifyResponse.requestAmount.compareTo(charge.can!!.price) == 0 println(isSuccess) if (isSuccess) { @@ -363,7 +366,7 @@ class ChargeService( charge.payment?.method = mappedMethod ?: verifyResponse.schemeCode charge.payment?.status = PaymentStatus.COMPLETE // 통화코드 설정 - charge.payment?.locale = verifyResponse.processingCurrency + charge.payment?.locale = verifyResponse.requestCurrency member.charge(charge.chargeCan, charge.rewardCan, "pg") -- 2.49.1 From cb2e3ea581bf4320cf7eac558a2f3b2122f17142 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 3 Oct 2025 01:29:59 +0900 Subject: [PATCH 38/44] =?UTF-8?q?fix(payverse-wehook):=20=ED=95=9C?= =?UTF-8?q?=EA=B5=AD=20=EC=9B=90=ED=99=94=EC=9D=BC=20=EB=95=8C=EC=99=80=20?= =?UTF-8?q?USD=EC=9D=BC=20=EB=95=8C=20mid=20=EA=B0=92=EC=9D=B4=20=EB=8B=AC?= =?UTF-8?q?=EB=9D=BC=EC=95=BC=20=ED=95=98=EB=8A=94=EB=8D=B0=20=EC=84=B1?= =?UTF-8?q?=EA=B3=B5=20=EC=97=AC=EB=B6=80=20=EB=B9=84=EA=B5=90=EC=8B=9C=20?= =?UTF-8?q?=EC=9B=90=ED=99=94=20mid=EB=A1=9C=20=EA=B3=A0=EC=A0=95=ED=95=98?= =?UTF-8?q?=EC=97=AC=20=EB=B9=84=EA=B5=90=ED=95=98=EB=8D=98=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vividnext/sodalive/can/charge/ChargeService.kt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) 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 28b028e..f716c1b 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 @@ -102,6 +102,11 @@ class ChargeService( return when (charge.payment?.status) { PaymentStatus.REQUEST -> { // 성공 조건 검증 + val mid = if (request.requestCurrency == "KRW") { + payverseMid + } else { + payverseUsdMid + } val expectedSign = DigestUtils.sha512Hex( String.format( "||%s||%s||%s||%s||%s||", @@ -110,11 +115,7 @@ class ChargeService( } else { payverseUsdSecretKey }, - if (request.requestCurrency == "KRW") { - payverseMid - } else { - payverseUsdMid - }, + mid, request.orderId, request.requestAmount, request.approvalDay @@ -126,7 +127,7 @@ class ChargeService( ) == 0 val isSuccess = request.resultStatus == "SUCCESS" && - request.mid == payverseMid && + request.mid == mid && charge.title == request.productName && isAmountMatch && request.sign == expectedSign -- 2.49.1 From a86a24ca3415ab90c32fc76dec70804557e7113c Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 3 Oct 2025 01:46:52 +0900 Subject: [PATCH 39/44] =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8A=94=20print=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/co/vividnext/sodalive/can/charge/ChargeService.kt | 3 --- 1 file changed, 3 deletions(-) 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 f716c1b..f86ed51 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 @@ -343,9 +343,7 @@ class ChargeService( } val body = response.body?.string() ?: throw SodaException("결제정보에 오류가 있습니다.") - println(body) val verifyResponse = objectMapper.readValue(body, PayverseVerifyResponse::class.java) - println(verifyResponse) val customerId = "${serverEnv}_user_${member.id!!}" val isSuccess = verifyResponse.resultStatus == "SUCCESS" && @@ -354,7 +352,6 @@ class ChargeService( verifyResponse.productName == charge.title && verifyResponse.customerId == customerId && verifyResponse.requestAmount.compareTo(charge.can!!.price) == 0 - println(isSuccess) if (isSuccess) { // verify 함수의 232~248 라인과 동일 처리 -- 2.49.1 From c5bc610e2fa0caa50303bedc4303697daf375d48 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 3 Oct 2025 01:48:19 +0900 Subject: [PATCH 40/44] =?UTF-8?q?webhook=20=ED=98=B8=EC=B6=9C=20IP=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=EC=9D=84=20=EC=9C=84=ED=95=B4=20print=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 --- .../kr/co/vividnext/sodalive/can/charge/ChargeController.kt | 3 +++ 1 file changed, 3 insertions(+) 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 73948f3..01f8365 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 @@ -66,6 +66,9 @@ class ChargeController( header.split(",")[0].trim() // 첫 번째 값이 클라이언트 IP } + println(remoteIp) + println(payverseInboundIp) + if (remoteIp != payverseInboundIp) { throw ResponseStatusException(HttpStatus.NOT_FOUND) } -- 2.49.1 From 06c0374f16fd75c4c4670239455b8e9f96139299 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 3 Oct 2025 01:56:55 +0900 Subject: [PATCH 41/44] =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8A=94=20print=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/co/vividnext/sodalive/can/charge/ChargeController.kt | 3 --- 1 file changed, 3 deletions(-) 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 01f8365..73948f3 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 @@ -66,9 +66,6 @@ class ChargeController( header.split(",")[0].trim() // 첫 번째 값이 클라이언트 IP } - println(remoteIp) - println(payverseInboundIp) - if (remoteIp != payverseInboundIp) { throw ResponseStatusException(HttpStatus.NOT_FOUND) } -- 2.49.1 From 0308e9ad837ddd9bbe33d46ea99cf377b41c3bd3 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 3 Oct 2025 02:10:30 +0900 Subject: [PATCH 42/44] =?UTF-8?q?fix(payverse):=20productName=20=EB=B9=84?= =?UTF-8?q?=EA=B5=90=20=EB=A1=9C=EC=A7=81=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - productName에 +가 있는 경우 저장된 데이터와 검증을 위한 데이터가 다르게 나오기 때문에 비교 불가능 --- .../kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt | 2 -- .../kotlin/kr/co/vividnext/sodalive/can/charge/ChargeService.kt | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt index d366ee8..b3917e6 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeData.kt @@ -70,7 +70,6 @@ data class PayverseVerifyResponse( val transactionMessage: String, val orderId: String, val customerId: String, - val productName: String, val requestCurrency: String, val requestAmount: BigDecimal ) @@ -82,7 +81,6 @@ data class PayverseWebhookRequest( val schemeGroup: String, val schemeCode: String, val orderId: String, - val productName: String, val requestCurrency: String, val requestAmount: BigDecimal, val resultStatus: String, 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 f86ed51..d77689b 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 @@ -128,7 +128,6 @@ class ChargeService( val isSuccess = request.resultStatus == "SUCCESS" && request.mid == mid && - charge.title == request.productName && isAmountMatch && request.sign == expectedSign @@ -349,7 +348,6 @@ class ChargeService( val isSuccess = verifyResponse.resultStatus == "SUCCESS" && verifyResponse.transactionStatus == "SUCCESS" && verifyResponse.orderId.toLongOrNull() == charge.id && - verifyResponse.productName == charge.title && verifyResponse.customerId == customerId && verifyResponse.requestAmount.compareTo(charge.can!!.price) == 0 -- 2.49.1 From 36e20bf0d10218aab46a659603324659a92d8b5f Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 3 Oct 2025 02:17:48 +0900 Subject: [PATCH 43/44] =?UTF-8?q?fix(payverse-webhook):=20orderId=20?= =?UTF-8?q?=EB=B9=84=EA=B5=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - orderId와 chargeId 비교 로직 추가 --- .../kotlin/kr/co/vividnext/sodalive/can/charge/ChargeService.kt | 1 + 1 file changed, 1 insertion(+) 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 d77689b..2597d3d 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 @@ -128,6 +128,7 @@ class ChargeService( val isSuccess = request.resultStatus == "SUCCESS" && request.mid == mid && + request.orderId.toLongOrNull() == charge.id && isAmountMatch && request.sign == expectedSign -- 2.49.1 From 41183b46484f7b2c6c1cba0ec2cd4cb1fc6a214c Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 10 Oct 2025 14:32:12 +0900 Subject: [PATCH 44/44] =?UTF-8?q?fix(can-list):=20=EA=B5=AD=EA=B0=80?= =?UTF-8?q?=EB=B3=84=EB=A1=9C=20=ED=86=B5=ED=99=94=EA=B0=80=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vividnext/sodalive/can/CanRepository.kt | 20 ------------------- .../co/vividnext/sodalive/can/CanService.kt | 2 +- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt index b66d609..fcc3f2f 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt @@ -23,7 +23,6 @@ import org.springframework.stereotype.Repository interface CanRepository : JpaRepository, CanQueryRepository interface CanQueryRepository { - fun findAllByStatus(status: CanStatus): List fun findAllByStatusAndCurrency(status: CanStatus, currency: String): List fun getCanUseStatus(member: Member, pageable: Pageable): List fun getCanChargeStatus(member: Member, pageable: Pageable, container: String): List @@ -33,25 +32,6 @@ interface CanQueryRepository { @Repository class CanQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : CanQueryRepository { - override fun findAllByStatus(status: CanStatus): List { - return queryFactory - .select( - QCanResponse( - can1.id, - can1.title, - can1.can, - can1.rewardCan, - can1.price.intValue(), - can1.currency, - can1.price.stringValue() - ) - ) - .from(can1) - .where(can1.status.eq(status)) - .orderBy(can1.currency.asc(), can1.can.asc()) - .fetch() - } - override fun findAllByStatusAndCurrency(status: CanStatus, currency: String): List { return queryFactory .select( diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt index b399882..27ef531 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt @@ -17,7 +17,7 @@ class CanService(private val repository: CanRepository) { GeoCountry.KR -> "KRW" else -> "USD" } - return repository.findAllByStatus(status = CanStatus.SALE) + return repository.findAllByStatusAndCurrency(status = CanStatus.SALE, currency = currency) } fun getCanStatus(member: Member, container: String): GetCanStatusResponse { -- 2.49.1