diff --git a/src/main/java/com/example/Tokkit_server/TokkitServerApplication.java b/src/main/java/com/example/Tokkit_server/TokkitServerApplication.java index b9d7cf5..f15a5c4 100644 --- a/src/main/java/com/example/Tokkit_server/TokkitServerApplication.java +++ b/src/main/java/com/example/Tokkit_server/TokkitServerApplication.java @@ -5,9 +5,11 @@ import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication(scanBasePackages = "com.example") @EnableJpaAuditing +@EnableScheduling @EnableJpaRepositories(basePackages = "com.example") @EntityScan(basePackages = "com.example") public class TokkitServerApplication { diff --git a/src/main/java/com/example/Tokkit_server/global/apiPayload/code/status/ErrorStatus.java b/src/main/java/com/example/Tokkit_server/global/apiPayload/code/status/ErrorStatus.java index 0523916..330aaa3 100644 --- a/src/main/java/com/example/Tokkit_server/global/apiPayload/code/status/ErrorStatus.java +++ b/src/main/java/com/example/Tokkit_server/global/apiPayload/code/status/ErrorStatus.java @@ -34,7 +34,7 @@ public enum ErrorStatus implements BaseErrorCode { // Wallet 관련 USER_WALLET_NOT_FOUND(HttpStatus.NOT_FOUND, "WALLET_001", "사용자 지갑이 존재하지 않습니다."), MERCHANT_WALLET_NOT_FOUND(HttpStatus.NOT_FOUND, "WALLET_002", "가맹점 지갑이 존재하지 않습니다."), - INSUFFICIENT_BALANCE(HttpStatus.BAD_REQUEST, "WALLET_003", "토큰 잔액이 부족합니다."), + INSUFFICIENT_BALANCE(HttpStatus.BAD_REQUEST, "WALLET_003", "토큰/바우처 잔액이 부족합니다."), INSUFFICIENT_TOKEN_BALANCE(HttpStatus.BAD_REQUEST,"WALLET_004", "토큰 잔액이 부족합니다."), // Transaction 관련 @@ -94,7 +94,10 @@ public enum ErrorStatus implements BaseErrorCode { TOKEN_TRANSFER_FAILED(HttpStatus.INTERNAL_SERVER_ERROR,"TOKEN_001" , "스마트컨트랙트 전송 중 오류가 발생했습니다."), TOKEN_MINT_FAILED(HttpStatus.INTERNAL_SERVER_ERROR,"TOKEN_002" , "토큰 발행(mint) 처리 중 오류가 발생했습니다."), TOKEN_BALANCE_QUERY_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "TOKEN_003", "스마트컨트랙트 잔액 조회 중 오류가 발생했습니다."), - TOKEN_BURN_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "TOKEN_004", "토큰 소각(burn) 처리 중 오류가 발생했습니다."); + TOKEN_BURN_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "TOKEN_004", "토큰 소각(burn) 처리 중 오류가 발생했습니다."), + BALANCE_MISMATCH(HttpStatus.INTERNAL_SERVER_ERROR, "TOKEN_005","스마트컨트랙트와 DB의 토큰 잔액이 일치하지 않습니다."), + BALANCE_VERIFICATION_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "TOKEN_005", "스마트컨트랙트 잔액 조회 중 오류가 발생했습니다."); + private final HttpStatus httpStatus; diff --git a/src/main/java/com/example/Tokkit_server/global/config/CorsConfig.java b/src/main/java/com/example/Tokkit_server/global/config/CorsConfig.java index 3d29ed8..389f86d 100644 --- a/src/main/java/com/example/Tokkit_server/global/config/CorsConfig.java +++ b/src/main/java/com/example/Tokkit_server/global/config/CorsConfig.java @@ -18,7 +18,9 @@ public CorsConfigurationSource apiConfigurationSource() { "http://localhost:3000", "http://localhost:8000", "http://localhost:8080", - "http://localhost:8800" + "http://localhost:8800", + "https://www.tokkit.site", + "https://admin.tokkit.site" )); config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); config.setAllowedHeaders(List.of("*")); diff --git a/src/main/java/com/example/Tokkit_server/notification/dto/response/NotificationResponseDto.java b/src/main/java/com/example/Tokkit_server/notification/dto/response/NotificationResponseDto.java index edee989..ed5b3c6 100644 --- a/src/main/java/com/example/Tokkit_server/notification/dto/response/NotificationResponseDto.java +++ b/src/main/java/com/example/Tokkit_server/notification/dto/response/NotificationResponseDto.java @@ -14,6 +14,7 @@ public class NotificationResponseDto { private String title; private String content; private NotificationCategory category; + private String deleted; private LocalDateTime createdAt; public static NotificationResponseDto from(Notification notification) { @@ -22,6 +23,7 @@ public static NotificationResponseDto from(Notification notification) { .title(notification.getTitle()) .content(notification.getContent()) .category(notification.getCategory()) + .deleted(notification.isDeleted() ? "deleted" : "active") .createdAt(notification.getCreatedAt()) .build(); } diff --git a/src/main/java/com/example/Tokkit_server/notification/enums/NotificationCategory.java b/src/main/java/com/example/Tokkit_server/notification/enums/NotificationCategory.java index 5fbc3e9..f2ce6c1 100644 --- a/src/main/java/com/example/Tokkit_server/notification/enums/NotificationCategory.java +++ b/src/main/java/com/example/Tokkit_server/notification/enums/NotificationCategory.java @@ -6,6 +6,5 @@ public enum NotificationCategory { SYSTEM, // 시스템 점검 등 PAYMENT, // 결제 - VOUCHER, // 바우처 TOKEN, // 지갑/토큰 } diff --git a/src/main/java/com/example/Tokkit_server/notification/enums/NotificationTemplate.java b/src/main/java/com/example/Tokkit_server/notification/enums/NotificationTemplate.java index 58674da..2f92586 100644 --- a/src/main/java/com/example/Tokkit_server/notification/enums/NotificationTemplate.java +++ b/src/main/java/com/example/Tokkit_server/notification/enums/NotificationTemplate.java @@ -11,9 +11,8 @@ public enum NotificationTemplate { // PAYMENT 알림 PAYMENT_SUCCESS(NotificationCategory.PAYMENT, "결제 완료", "%d원이 결제되었습니다."), PAYMENT_REFUND(NotificationCategory.PAYMENT, "환불 완료", "%d원이 환불되었습니다."), - - // VOUCHER 알림 - VOUCHER_EXPIRED(NotificationCategory.VOUCHER, "바우처 만료", "[%s] 바우처가 만료되었습니다."), + VOUCHER_PURCHASED(NotificationCategory.PAYMENT, "바우처 구매 완료", "[%s] 바우처를 %d원에 구매하였습니다."), + VOUCHER_EXPIRED(NotificationCategory.PAYMENT, "바우처 만료", "[%s] 바우처가 만료되었습니다."), // TOKEN 알림 TOKEN_CONVERTED(NotificationCategory.TOKEN, "토큰 전환 완료", "토큰이 성공적으로 전환되었습니다."); diff --git a/src/main/java/com/example/Tokkit_server/notification/service/NotificationServiceImpl.java b/src/main/java/com/example/Tokkit_server/notification/service/NotificationServiceImpl.java index 91e8822..8533726 100644 --- a/src/main/java/com/example/Tokkit_server/notification/service/NotificationServiceImpl.java +++ b/src/main/java/com/example/Tokkit_server/notification/service/NotificationServiceImpl.java @@ -21,8 +21,12 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; +import java.io.IOException; import java.util.List; import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @Slf4j @@ -68,15 +72,47 @@ public void sendNotification(User user, NotificationTemplate template, Object... } } - @Transactional public SseEmitter subscribe(Long userId) { SseEmitter emitter = new SseEmitter(DEFAULT_TIMEOUT); sseEmitters.add(userId, emitter); + log.info("[SSE] 유저 {} 구독 등록됨", userId); + + try { + emitter.send(SseEmitter.event() + .name("connect") + .data("SSE 연결이 완료되었습니다.")); + } catch (IOException e) { + emitter.completeWithError(e); + sseEmitters.remove(userId); + } - emitter.onCompletion(() -> sseEmitters.remove(userId)); - emitter.onTimeout(() -> sseEmitters.remove(userId)); - emitter.onError((e) -> sseEmitters.remove(userId)); + // 연결 유지용 ping + ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); + scheduler.scheduleAtFixedRate(() -> { + try { + emitter.send(SseEmitter.event() + .name("ping") + .data("keep-alive")); + } catch (Exception e) { + emitter.complete(); + } + }, 30, 30, TimeUnit.SECONDS); + + emitter.onCompletion(() -> { + sseEmitters.remove(userId); + scheduler.shutdown(); + }); + + emitter.onTimeout(() -> { + sseEmitters.remove(userId); + scheduler.shutdown(); + }); + + emitter.onError((e) -> { + sseEmitters.remove(userId); + scheduler.shutdown(); + }); return emitter; } diff --git a/src/main/java/com/example/Tokkit_server/notification/service/SseNotificationService.java b/src/main/java/com/example/Tokkit_server/notification/service/SseNotificationService.java index 89fe78a..0ef3f1a 100644 --- a/src/main/java/com/example/Tokkit_server/notification/service/SseNotificationService.java +++ b/src/main/java/com/example/Tokkit_server/notification/service/SseNotificationService.java @@ -9,6 +9,8 @@ import java.io.IOException; +import static org.apache.commons.lang3.StringEscapeUtils.escapeJson; + @Slf4j @Service @RequiredArgsConstructor @@ -20,13 +22,18 @@ public void sendSse(Long userId, String title, String content) { SseEmitter emitter = sseEmitters.get(userId); if (emitter != null) { try { + log.info("[SSE] 유저 {}에게 알림 전송 시도", userId); // 로그 추가 + String json = String.format("{\"title\": \"%s\", \"content\": \"%s\"}", title, content); emitter.send(SseEmitter.event() .name("notification") - .data(title + ": " + content, MediaType.valueOf("text/plain;charset=UTF-8"))); + .data(json, MediaType.APPLICATION_JSON)); + log.info("[SSE] 유저 {}에게 알림 전송 성공", userId); } catch (IOException e) { sseEmitters.remove(userId); - log.error("[SseNotificationService] SSE 전송 실패 - 연결 제거됨: {}", e.getMessage()); + log.error("[SSE] 전송 실패 - emitter 제거됨: {}", e.getMessage()); } + } else { + log.warn("[SSE] emitter 없음 → 전송 실패 (userId: {})", userId); // 로그 추가 } } } \ No newline at end of file diff --git a/src/main/java/com/example/Tokkit_server/voucher_ownership/repository/VoucherOwnershipRepository.java b/src/main/java/com/example/Tokkit_server/voucher_ownership/repository/VoucherOwnershipRepository.java index 3c34193..2789b1c 100644 --- a/src/main/java/com/example/Tokkit_server/voucher_ownership/repository/VoucherOwnershipRepository.java +++ b/src/main/java/com/example/Tokkit_server/voucher_ownership/repository/VoucherOwnershipRepository.java @@ -5,6 +5,8 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import java.time.LocalDateTime; import java.util.List; @@ -14,4 +16,29 @@ public interface VoucherOwnershipRepository extends JpaRepository findByWalletUserId(Long userId, Pageable pageable); Optional findByIdAndWalletUserId(Long id, Long userId); List findByStatusAndVoucher_ValidDateBefore(VoucherOwnershipStatus status, LocalDateTime time); + + // 바우처 + 바우처 스토어 + 스토어를 모두 fetch join으로 한 번에 조회 + @Query(""" + SELECT vo + FROM VoucherOwnership vo + JOIN FETCH vo.voucher v + LEFT JOIN FETCH v.voucherStores vs + LEFT JOIN FETCH vs.store s + WHERE vo.wallet.user.id = :userId + """) + List findAllWithVoucherAndStoresByUserId(@Param("userId") Long userId); + + // 만료된 바우처만 fetch join으로 조회 (성능 개선용) + @Query(""" + SELECT vo + FROM VoucherOwnership vo + JOIN FETCH vo.voucher v + WHERE vo.status = :status + AND v.validDate < :now + """) + List findByStatusAndVoucherValidDateBeforeWithFetchJoin( + @Param("status") VoucherOwnershipStatus status, + @Param("now") LocalDateTime now + ); + } diff --git a/src/main/java/com/example/Tokkit_server/voucher_ownership/repository/VoucherOwnershipRepositoryCustom.java b/src/main/java/com/example/Tokkit_server/voucher_ownership/repository/VoucherOwnershipRepositoryCustom.java index fd7bee0..8958a37 100644 --- a/src/main/java/com/example/Tokkit_server/voucher_ownership/repository/VoucherOwnershipRepositoryCustom.java +++ b/src/main/java/com/example/Tokkit_server/voucher_ownership/repository/VoucherOwnershipRepositoryCustom.java @@ -4,11 +4,12 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import com.example.Tokkit_server.voucher_ownership.dto.request.VoucherOwnershipSearchRequest; import com.example.Tokkit_server.voucher_ownership.entity.VoucherOwnership; interface VoucherOwnershipRepositoryCustom { Page searchMyVoucher(VoucherOwnershipSearchRequest request, Long userId, Pageable pageable); - List findAllWithVoucherAndStoresByUserId(Long userId); } \ No newline at end of file diff --git a/src/main/java/com/example/Tokkit_server/voucher_ownership/repository/VoucherOwnershipRepositoryImpl.java b/src/main/java/com/example/Tokkit_server/voucher_ownership/repository/VoucherOwnershipRepositoryImpl.java index 5eabe3c..62a8fb1 100644 --- a/src/main/java/com/example/Tokkit_server/voucher_ownership/repository/VoucherOwnershipRepositoryImpl.java +++ b/src/main/java/com/example/Tokkit_server/voucher_ownership/repository/VoucherOwnershipRepositoryImpl.java @@ -69,21 +69,6 @@ public Page searchMyVoucher(VoucherOwnershipSearchRequest requ return new PageImpl<>(result, pageable, total); } - @Override - public List findAllWithVoucherAndStoresByUserId(Long userId) { - return em.createQuery(""" - SELECT vo FROM VoucherOwnership vo - JOIN FETCH vo.voucher v - LEFT JOIN FETCH v.voucherStores vs - LEFT JOIN FETCH vs.store s - JOIN vo.wallet w - JOIN w.user u - WHERE u.id = :userId - """, VoucherOwnership.class) - .setParameter("userId", userId) - .getResultList(); - } - } diff --git a/src/main/java/com/example/Tokkit_server/voucher_ownership/scheduler/VoucherExpirationScheduler.java b/src/main/java/com/example/Tokkit_server/voucher_ownership/scheduler/VoucherExpirationScheduler.java index e618700..37ba83d 100644 --- a/src/main/java/com/example/Tokkit_server/voucher_ownership/scheduler/VoucherExpirationScheduler.java +++ b/src/main/java/com/example/Tokkit_server/voucher_ownership/scheduler/VoucherExpirationScheduler.java @@ -23,7 +23,7 @@ public void expireVouchers() { log.info("바우처 유효기간 만료 체크 시작"); List expiredList = - voucherOwnershipRepository.findByStatusAndVoucher_ValidDateBefore( + voucherOwnershipRepository.findByStatusAndVoucherValidDateBeforeWithFetchJoin( VoucherOwnershipStatus.AVAILABLE, LocalDateTime.now()); expiredList.forEach(VoucherOwnership::expire); diff --git a/src/main/java/com/example/Tokkit_server/wallet/service/command/WalletCommandService.java b/src/main/java/com/example/Tokkit_server/wallet/service/command/WalletCommandService.java index f1102ac..4d5e6ec 100644 --- a/src/main/java/com/example/Tokkit_server/wallet/service/command/WalletCommandService.java +++ b/src/main/java/com/example/Tokkit_server/wallet/service/command/WalletCommandService.java @@ -4,6 +4,8 @@ import com.example.Tokkit_server.global.apiPayload.exception.GeneralException; import com.example.Tokkit_server.merchant.entity.Merchant; import com.example.Tokkit_server.merchant.repository.MerchantRepository; +import com.example.Tokkit_server.notification.enums.NotificationTemplate; +import com.example.Tokkit_server.notification.service.NotificationService; import com.example.Tokkit_server.store.entity.Store; import com.example.Tokkit_server.store.repository.StoreRepository; import com.example.Tokkit_server.transaction.entity.Transaction; @@ -29,6 +31,7 @@ import com.example.contract.service.TokkitTokenService; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.slf4j.MDC; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @@ -44,6 +47,7 @@ @Service @RequiredArgsConstructor +@Slf4j public class WalletCommandService { private final WalletRepository walletRepository; @@ -235,6 +239,30 @@ public VoucherPurchaseResponse purchaseVoucher(Long userId,VoucherPurchaseReques // 7. 수량 차감 voucher.decreaseRemainingCount(); + // 7.1 온체인 잔액 검증 + try { + + BigInteger onChainBalance = tokkitTokenService.getBalanceOf(wallet.getWalletAddress()); + if (!onChainBalance.equals(BigInteger.valueOf(wallet.getTokenBalance()))) { + throw new GeneralException(ErrorStatus.BALANCE_MISMATCH); + } + } catch (Exception e) { + throw new GeneralException(ErrorStatus.BALANCE_VERIFICATION_FAILED); + } + + // 8. 토큰 소각 + TransactionReceipt receipt; + try { + receipt = tokkitTokenService.burn( + wallet.getWalletAddress(), + BigInteger.valueOf(amount) + ); + } catch (Exception e) { + throw new GeneralException(ErrorStatus.TOKEN_BURN_FAILED); + } + + String txHash = receipt.getTransactionHash(); + // 8. 토큰 차감 wallet.updateBalance(wallet.getDepositBalance(), wallet.getTokenBalance() - amount); @@ -254,7 +282,7 @@ public VoucherPurchaseResponse purchaseVoucher(Long userId,VoucherPurchaseReques String displayDescription = voucher.getName(); logAndSave(wallet, user.getId(), null, TransactionType.PURCHASE, TransactionStatus.SUCCESS, - (long) amount, logDescription, displayDescription); + (long) amount, logDescription, displayDescription,txHash); // 11. 응답 반환 @@ -326,6 +354,7 @@ public VoucherPaymentResponse payWithVoucher(Long userId,VoucherPaymentRequest r String userDisplayDescription = store.getStoreName(); + // 9. 사용자 거래 기록 생성 logAndSave(ownership.getWallet(), user.getId(), null, TransactionType.PURCHASE, TransactionStatus.SUCCESS, request.getAmount(), userLogDescription, userDisplayDescription); @@ -342,10 +371,10 @@ public VoucherPaymentResponse payWithVoucher(Long userId,VoucherPaymentRequest r // 스마트 컨트랙트 적용 TransactionReceipt receipt; try{ - receipt = tokkitTokenService.payToMerchant( + receipt = tokkitTokenService.mint( merchantWallet.getWalletAddress(), - BigInteger.valueOf(request.getAmount()), - "Voucher settlement from User ID : " + user.getId()); + BigInteger.valueOf(request.getAmount()) + ); } catch (Exception e) { throw new GeneralException(ErrorStatus.TOKEN_TRANSFER_FAILED); } @@ -404,6 +433,18 @@ public DirectPaymentResponse payDirectlyWithToken(Long userId,DirectPaymentReque throw new GeneralException(ErrorStatus.INVALID_SIMPLE_PASSWORD); } + + // 5.1 스마트컨트랙트 이전 상태 검증 (user) + try { + BigInteger userOnChainBalance = tokkitTokenService.getBalanceOf(userWallet.getWalletAddress()); + if (!userOnChainBalance.equals(BigInteger.valueOf(userWallet.getTokenBalance()))) { + throw new GeneralException(ErrorStatus.BALANCE_MISMATCH); + } + } catch (Exception e) { + throw new GeneralException(ErrorStatus.BALANCE_VERIFICATION_FAILED); + } + + // 스마트 컨트랙트 적용 TransactionReceipt receipt; try { diff --git a/src/main/java/com/example/Tokkit_server/wallet/service/query/WalletQueryService.java b/src/main/java/com/example/Tokkit_server/wallet/service/query/WalletQueryService.java index 536c559..3ff4bd7 100644 --- a/src/main/java/com/example/Tokkit_server/wallet/service/query/WalletQueryService.java +++ b/src/main/java/com/example/Tokkit_server/wallet/service/query/WalletQueryService.java @@ -111,6 +111,15 @@ public void convertDepositToToken(Long userId, DepositToTokenRequest request) { } String txHash = receipt.getTransactionHash(); + try { + BigInteger onChainBalance = tokkitTokenService.getBalanceOf(wallet.getWalletAddress()); + if (!onChainBalance.equals(BigInteger.valueOf(wallet.getTokenBalance()))) { + throw new GeneralException(ErrorStatus.BALANCE_MISMATCH); + } + } catch (Exception e) { + throw new GeneralException(ErrorStatus.BALANCE_VERIFICATION_FAILED); + } + logAndSave(wallet, user.getId(), null, TransactionType.CONVERT, TransactionStatus.SUCCESS, @@ -143,21 +152,31 @@ public void convertTokenToDeposit(Long userId ,TokenToDepositRequest request) { } - // 스마트컨트랙트에 burn 요청 - String txHash; + // 스마트컨트랙트 burn + TransactionReceipt receipt; try { - TransactionReceipt receipt = tokkitTokenService.burn(wallet.getWalletAddress(), BigInteger.valueOf(request.getAmount())); - txHash = receipt.getTransactionHash(); + receipt = tokkitTokenService.burn(wallet.getWalletAddress(), BigInteger.valueOf(request.getAmount())); } catch (Exception e) { throw new GeneralException(ErrorStatus.TOKEN_BURN_FAILED); } + String txHash = receipt.getTransactionHash(); + // 잔액 업데이트 wallet.updateBalance(wallet.getDepositBalance() + request.getAmount(), wallet.getTokenBalance() - request.getAmount()); + try { + BigInteger onChainBalance = tokkitTokenService.getBalanceOf(wallet.getWalletAddress()); + if (!onChainBalance.equals(BigInteger.valueOf(wallet.getTokenBalance()))) { + throw new GeneralException(ErrorStatus.BALANCE_MISMATCH); + } + } catch (Exception e) { + throw new GeneralException(ErrorStatus.BALANCE_VERIFICATION_FAILED); + } + logAndSave(wallet, user.getId(), null, TransactionType.CONVERT, diff --git a/src/main/java/com/example/contract/controller/ContractController.java b/src/main/java/com/example/contract/controller/ContractController.java index e4c8054..141b10e 100644 --- a/src/main/java/com/example/contract/controller/ContractController.java +++ b/src/main/java/com/example/contract/controller/ContractController.java @@ -26,10 +26,7 @@ public class ContractController { @Operation(summary = "스마트 컨트랙트 주소 등록", description = "Hardhat에서 배포된 스마트컨트랙트 주소를 전달받아 메모리 및 DB에 저장합니다.") public ResponseEntity receiveContractAddress(@RequestBody ContractAddressDto dto) { - // 1. 메모리에 저장 - contractAddressStorage.setTokkitTokenAddress(dto.getTokkitToken()); - // 2. DB에도 저장 contractAddressService.save("TokkitToken", dto.getTokkitToken(), dto.getNetwork()); System.out.println("📥 Stored contract address: " + dto.getTokkitToken()); diff --git a/src/main/java/com/example/contract/service/ContractAddressService.java b/src/main/java/com/example/contract/service/ContractAddressService.java index 1ab1338..e72394a 100644 --- a/src/main/java/com/example/contract/service/ContractAddressService.java +++ b/src/main/java/com/example/contract/service/ContractAddressService.java @@ -2,13 +2,19 @@ import com.example.contract.entity.ContractAddress; import com.example.contract.repository.ContractAddressRepository; +import com.example.contract.storage.ContractAddressStorage; + import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor +@Slf4j public class ContractAddressService { private final ContractAddressRepository repository; + private final ContractAddressStorage contractAddressStorage; /** * 컨트랙트 주소 저장 */ @@ -19,8 +25,15 @@ public void save(String contractName, String address, String network) { .network(network) .build(); repository.save(entity); + + // 메모리에도 반영 + if ("TokkitToken".equals(contractName)) { + contractAddressStorage.setTokkitTokenAddress(address); + log.info("📦 ContractAddressStorage 업데이트 완료: {} = {}", contractName, address); + } } + /** * 저장된 컨트랙트 주소중 최신 주소를 가져옴 */ diff --git a/src/main/java/com/example/contract/service/TokkitTokenService.java b/src/main/java/com/example/contract/service/TokkitTokenService.java index 137ad7a..7906dc2 100644 --- a/src/main/java/com/example/contract/service/TokkitTokenService.java +++ b/src/main/java/com/example/contract/service/TokkitTokenService.java @@ -58,6 +58,7 @@ public TransactionReceipt transfer(String toAddress, BigInteger amount) throws E * 잔액 조회 */ public BigInteger getBalanceOf(String address) throws Exception { + log.info("🧾 [getBalanceOf] address to query = {}", address); return loadContract().balanceOf(address).send(); } @@ -81,4 +82,12 @@ public TransactionReceipt payToMerchant(String merchantAddress, BigInteger amoun public String getName() throws Exception { return loadContract().name().send(); } + + /** + * Credentials 주소 조회 + */ + public String getOwnerAddress() { + return credentials.getAddress(); + } + }