diff --git a/src/main/java/com/example/scoi/domain/auth/dto/AuthReqDTO.java b/src/main/java/com/example/scoi/domain/auth/dto/AuthReqDTO.java index 5b369b7..68528da 100644 --- a/src/main/java/com/example/scoi/domain/auth/dto/AuthReqDTO.java +++ b/src/main/java/com/example/scoi/domain/auth/dto/AuthReqDTO.java @@ -57,10 +57,9 @@ public record SignupRequest( @Schema(description = "AES 암호화된 6자리 간편비밀번호 (Base64)", example = "ItfrsoB1J0hl3O60mahB1A==") String simplePassword, - @Schema(description = "회원 타입 (INDIVIDUAL: 개인, CORPORATION: 법인)", + @Schema(description = "회원 타입 (미입력 시 INDIVIDUAL 기본값)", example = "INDIVIDUAL", allowableValues = {"INDIVIDUAL", "CORPORATION"}) - @NotNull(message = "회원 타입은 필수입니다.") MemberType memberType, @Schema(description = "바이오 인증 등록 여부 (true: 등록, false: 나중에)", example = "false") diff --git a/src/main/java/com/example/scoi/domain/auth/service/AuthService.java b/src/main/java/com/example/scoi/domain/auth/service/AuthService.java index 38b3665..4a0c44e 100644 --- a/src/main/java/com/example/scoi/domain/auth/service/AuthService.java +++ b/src/main/java/com/example/scoi/domain/auth/service/AuthService.java @@ -6,6 +6,7 @@ import com.example.scoi.domain.auth.exception.code.AuthErrorCode; import com.example.scoi.domain.member.dto.MemberReqDTO; import com.example.scoi.domain.member.entity.Member; +import com.example.scoi.domain.member.enums.MemberType; import com.example.scoi.domain.member.entity.MemberToken; import com.example.scoi.domain.member.repository.MemberRepository; import com.example.scoi.domain.member.repository.MemberTokenRepository; @@ -200,7 +201,7 @@ public AuthResDTO.SignupResponse signup(AuthReqDTO.SignupRequest request) { .koreanName(request.koreanName()) .residentNumber(request.residentNumber()) .simplePassword(hashedPassword) - .memberType(request.memberType()) + .memberType(request.memberType() != null ? request.memberType() : MemberType.INDIVIDUAL) .isBioRegistered(request.isBioRegistered() != null ? request.isBioRegistered() : false) .build(); diff --git a/src/main/java/com/example/scoi/domain/charge/controller/ChargeController.java b/src/main/java/com/example/scoi/domain/charge/controller/ChargeController.java index 5a46ae4..70335be 100644 --- a/src/main/java/com/example/scoi/domain/charge/controller/ChargeController.java +++ b/src/main/java/com/example/scoi/domain/charge/controller/ChargeController.java @@ -8,10 +8,6 @@ import com.example.scoi.domain.member.enums.ExchangeType; import com.example.scoi.global.apiPayload.ApiResponse; import com.example.scoi.global.apiPayload.code.BaseSuccessCode; - -import com.example.scoi.domain.charge.exception.ChargeException; -import com.example.scoi.domain.charge.exception.code.ChargeErrorCode; -import com.example.scoi.domain.member.enums.ExchangeType; import com.example.scoi.global.security.userdetails.CustomUserDetails; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -70,19 +66,12 @@ public ApiResponse getBalances( // 입금 주소 확인하기 @GetMapping("/deposits/address") - public ApiResponse> getDepositAddress( + public ApiResponse getDepositAddress( @AuthenticationPrincipal CustomUserDetails user, - @RequestParam ExchangeType exchangeType, - @RequestParam(defaultValue = "") List coinType, - @RequestParam(defaultValue = "") List netType + @RequestParam ExchangeType exchangeType ){ BaseSuccessCode code = ChargeSuccessCode.OK; - return ApiResponse.onSuccess(code, chargeService.getDepositAddress( - user.getUsername(), - exchangeType, - coinType.stream().map(String::toUpperCase).toList(), - netType.stream().map(String::toUpperCase).toList() - )); + return ApiResponse.onSuccess(code, chargeService.getDepositAddress(user.getUsername(), exchangeType)); } // 입금 주소 생성하기 diff --git a/src/main/java/com/example/scoi/domain/charge/controller/ChargeControllerDocs.java b/src/main/java/com/example/scoi/domain/charge/controller/ChargeControllerDocs.java index 5f25b2d..ed9e869 100644 --- a/src/main/java/com/example/scoi/domain/charge/controller/ChargeControllerDocs.java +++ b/src/main/java/com/example/scoi/domain/charge/controller/ChargeControllerDocs.java @@ -40,13 +40,11 @@ ApiResponse getBalances( @Operation( summary = "입금 주소 확인하기 API By 김주헌", - description = "코인의 입금 주소를 확인합니다. 리스트로 보내실때 꼭! 인덱스 맞춰서 보내주세요. 입금 주소가 없는 경우, 해당 코인은 제외하고 응답이 옵니다." + description = "코인의 입금 주소를 확인합니다. 해당 유저의 입금 주소 전체를 조회하고 최상단에 잇는 입금 주소를 반환합니다." ) - ApiResponse> getDepositAddress( + ApiResponse getDepositAddress( @AuthenticationPrincipal CustomUserDetails user, - @RequestParam ExchangeType exchangeType, - @RequestParam(defaultValue = "") List coinType, - @RequestParam(defaultValue = "") List netType + @RequestParam ExchangeType exchangeType ); @Operation( diff --git a/src/main/java/com/example/scoi/domain/charge/exception/code/ChargeErrorCode.java b/src/main/java/com/example/scoi/domain/charge/exception/code/ChargeErrorCode.java index 94dff2a..054ffd8 100644 --- a/src/main/java/com/example/scoi/domain/charge/exception/code/ChargeErrorCode.java +++ b/src/main/java/com/example/scoi/domain/charge/exception/code/ChargeErrorCode.java @@ -47,7 +47,7 @@ public enum ChargeErrorCode implements BaseErrorCode { "주문 내역을 찾을 수 없습니다."), ADDRESS_NOT_FOUND(HttpStatus.NOT_FOUND, "CHARGE404_3", - "해당 코인의 입금 주소가 없습니다."), + "해당 유저의 입금 주소가 없습니다."), ; private final HttpStatus status; diff --git a/src/main/java/com/example/scoi/domain/charge/service/ChargeService.java b/src/main/java/com/example/scoi/domain/charge/service/ChargeService.java index ce4944d..3ecd534 100644 --- a/src/main/java/com/example/scoi/domain/charge/service/ChargeService.java +++ b/src/main/java/com/example/scoi/domain/charge/service/ChargeService.java @@ -9,9 +9,7 @@ import com.example.scoi.domain.charge.exception.ChargeException; import com.example.scoi.domain.charge.exception.code.ChargeErrorCode; import com.example.scoi.domain.member.enums.ExchangeType; -import com.example.scoi.domain.member.repository.MemberApiKeyRepository; import com.example.scoi.domain.member.exception.MemberException; -import com.example.scoi.domain.member.repository.MemberRepository; import com.example.scoi.global.client.BithumbClient; import com.example.scoi.global.client.UpbitClient; import com.example.scoi.global.client.converter.BithumbConverter; @@ -27,9 +25,7 @@ import java.security.GeneralSecurityException; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; @Service @RequiredArgsConstructor @@ -40,8 +36,6 @@ public class ChargeService { private final JwtApiUtil jwtApiUtil; private final BithumbClient bithumbClient; private final UpbitClient upbitClient; - private final MemberRepository memberRepository; - private final MemberApiKeyRepository memberApiKeyRepository; // 원화 충전 요청하기 public ChargeResDTO.ChargeKrw chargeKrw( @@ -300,146 +294,56 @@ public BalanceResDTO.BalanceListDTO getBalancesByPhone(String phoneNumber, Excha } // 입금 주소 확인하기 - public List getDepositAddress( + public String getDepositAddress( String phoneNumber, - ExchangeType exchangeType, - List coinType, - List netType + ExchangeType exchangeType ) { // 거래소별 요청 보내기 String token; - List result = new ArrayList<>(); - Map bindError = new HashMap<>(); - switch (exchangeType){ - case UPBIT: - for (int idx = 0; idx < coinType.size(); idx++) { - try { - token = jwtApiUtil - .createUpBitJwt( - phoneNumber, - "currency=" + coinType.get(idx) + "&net_type=" + netType.get(idx), - null - ); - - UpbitResDTO.GetDepositAddress upbitResult = upbitClient - .getDepositAddress(token, coinType.get(idx), netType.get(idx)); - - // 그게 아닌 경우 - result.add( - ChargeConverter.toGetDepositAddress( - upbitResult.currency(), - upbitResult.deposit_address() - ) - ); - // JWT 토큰 제작 실패 - } catch (GeneralSecurityException e) { - throw new ChargeException(ChargeErrorCode.EXCHANGE_BAD_REQUEST); - // 해당 코인의 입금주소가 없는 경우 - } catch (FeignException.NotFound e) { - ObjectMapper objectMapper = new ObjectMapper(); - ClientErrorDTO.Errors error = objectMapper.readValue(e.contentUTF8(), ClientErrorDTO.Errors.class); - - if (!error.error().name().equals("coin_address_not_found")) { - throw new ChargeException(ChargeErrorCode.EXCHANGE_BAD_REQUEST); - } - // 잘못된 파라미터, 거래소에서 지원하지 않는 코인 - } catch (FeignException.BadRequest e) { - ObjectMapper objectMapper = new ObjectMapper(); - ClientErrorDTO.Errors error = objectMapper.readValue(e.contentUTF8(), ClientErrorDTO.Errors.class); - - // 잘못된 파라미터인 경우: DTO 코인 정보를 잘못 기입 - switch (error.error().name()) { - - // 잘못된 파라미터 입력 - case "validation_error": - case "invalid_parameter": - throw new ChargeException(ChargeErrorCode.WRONG_COIN_TYPE); - - // 거래소에서 지원하지 않는 코인 - case "currency does not have a valid value": - bindError.put(coinType.get(idx), netType.get(idx)); - break; - - default: - throw new ChargeException(ChargeErrorCode.EXCHANGE_BAD_REQUEST); - } - // JWT 토큰 생성 오류 - } catch (FeignException.Unauthorized e) { - throw new ChargeException(ChargeErrorCode.EXCHANGE_BAD_REQUEST); - } - } - break; + String result; + try{ + switch (exchangeType) { + case UPBIT: + token = jwtApiUtil.createUpBitJwt(phoneNumber, null, null); + List upbitResult = upbitClient.getDepositAddresses(token); + + // USDT, USDC 중 가장 먼저 찾은 입금 주소를 result로 + result = upbitResult.stream().filter(depositAddress -> + depositAddress.currency().equals("USDT") || depositAddress.currency().equals("USDC")) + .findFirst() + .orElseThrow(() -> new ChargeException(ChargeErrorCode.ADDRESS_NOT_FOUND)) + .deposit_address(); + + break; case BITHUMB: - for (int idx = 0; idx < coinType.size(); idx++){ - try { - token = jwtApiUtil - .createBithumbJwt( - phoneNumber, - "currency="+coinType.get(idx)+"&net_type="+netType.get(idx), - null - ); - - BithumbResDTO.GetDepositAddress bithumbResult = bithumbClient - .getDepositAddress(token, coinType.get(idx), netType.get(idx)); - - // 그게 아닌 경우 - result.add( - ChargeConverter.toGetDepositAddress( - bithumbResult.currency(), - bithumbResult.deposit_address() - ) - ); - // JWT 토큰 제작 실패 - } catch (GeneralSecurityException e) { - throw new ChargeException(ChargeErrorCode.EXCHANGE_BAD_REQUEST); - // 해당 코인의 입금주소가 없는 경우 - } catch (FeignException.NotFound e) { - ObjectMapper objectMapper = new ObjectMapper(); - ClientErrorDTO.Errors error = objectMapper.readValue(e.contentUTF8(), ClientErrorDTO.Errors.class); - - if (!error.error().name().equals("coin_address_not_found")) { - throw new ChargeException(ChargeErrorCode.EXCHANGE_BAD_REQUEST); - } - // 잘못된 파라미터, 거래소에서 지원하지 않는 코인 - } catch (FeignException.BadRequest e) { - ObjectMapper objectMapper = new ObjectMapper(); - ClientErrorDTO.Errors error = objectMapper.readValue(e.contentUTF8(), ClientErrorDTO.Errors.class); - - // 잘못된 파라미터인 경우: DTO 코인 정보를 잘못 기입 - switch (error.error().name()) { - - // 잘못된 파라미터 입력 - case "validation_error": - case "invalid_parameter": - throw new ChargeException(ChargeErrorCode.WRONG_COIN_TYPE); - - // 네트워크 미지원: 넘기기 - case "request_for_address_of_not_supported_currency": - bindError.put(coinType.get(idx), netType.get(idx)); - break; - - // 거래소에서 지원하지 않는 코인 - case "currency does not have a valid value": - throw new ChargeException(ChargeErrorCode.NOT_SUPPORT_COIN); - - default: - throw new ChargeException(ChargeErrorCode.EXCHANGE_BAD_REQUEST); - } - // JWT 토큰 생성 오류 - } catch (FeignException.Unauthorized e) { - throw new ChargeException(ChargeErrorCode.EXCHANGE_BAD_REQUEST); - } - } + token = jwtApiUtil.createBithumbJwt(phoneNumber, null, null); + List bithumbResult = bithumbClient.getDepositAddresses(token); + + // USDT, USDC 중 가장 먼저 찾은 입금 주소를 result로 + result = bithumbResult.stream().filter(depositAddress -> + depositAddress.currency().equals("USDT") || depositAddress.currency().equals("USDC")) + .findFirst() + .orElseThrow(() -> new ChargeException(ChargeErrorCode.ADDRESS_NOT_FOUND)) + .deposit_address(); break; default: throw new ChargeException(ChargeErrorCode.WRONG_EXCHANGE_TYPE); - } + } + // 거래소 JWT 토큰 오류 + } catch (GeneralSecurityException e) { + throw new ChargeException(ChargeErrorCode.EXCHANGE_API_KEY_NOT_FOUND); + // 거래소 JWT 토큰 인증 오류 + } catch (FeignException.Unauthorized e) { + ObjectMapper objectMapper = new ObjectMapper(); + ClientErrorDTO.Errors error = objectMapper.readValue(e.contentUTF8(), ClientErrorDTO.Errors.class); - // 만약 bindError에 값이 있으면 - if (!bindError.isEmpty()){ - throw new ChargeException(ChargeErrorCode.ADDRESS_NOT_FOUND, bindError); + if (error.error().name().equals("out_of_scope")) { + throw new ChargeException(ChargeErrorCode.EXCHANGE_FORBIDDEN); + } + throw new ChargeException(ChargeErrorCode.EXCHANGE_BAD_REQUEST); } + return result; } @@ -469,7 +373,7 @@ public List createDepositAddress( UpbitResDTO.CreateDepositAddress upbitResult = upbitClient .createDepositAddress(token, upbitDto); - result.add(coin); + result.add(upbitResult.currency()); } break; case BITHUMB: @@ -487,7 +391,7 @@ public List createDepositAddress( BithumbResDTO.CreateDepositAddress bithumbResult = bithumbClient .createDepositAddress(token, bithumbDto); - result.add(coin); + result.add(bithumbResult.currency()); } break; default: diff --git a/src/main/java/com/example/scoi/domain/member/controller/MemberController.java b/src/main/java/com/example/scoi/domain/member/controller/MemberController.java index c45b1e9..7860e6f 100644 --- a/src/main/java/com/example/scoi/domain/member/controller/MemberController.java +++ b/src/main/java/com/example/scoi/domain/member/controller/MemberController.java @@ -11,7 +11,6 @@ import com.example.scoi.global.security.userdetails.CustomUserDetails; import lombok.RequiredArgsConstructor; import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -53,7 +52,7 @@ public ApiResponse> changePassword( // 간편 비밀번호 재설정 @PostMapping("/members/me/password/reset") public ApiResponse resetPassword( - @Validated @RequestBody MemberReqDTO.ResetPassword dto, + @RequestBody MemberReqDTO.ResetPassword dto, @AuthenticationPrincipal CustomUserDetails user ){ BaseSuccessCode code = MemberSuccessCode.RESET_SIMPLE_PASSWORD; diff --git a/src/main/java/com/example/scoi/domain/member/dto/MemberReqDTO.java b/src/main/java/com/example/scoi/domain/member/dto/MemberReqDTO.java index 4b670bc..7edb298 100644 --- a/src/main/java/com/example/scoi/domain/member/dto/MemberReqDTO.java +++ b/src/main/java/com/example/scoi/domain/member/dto/MemberReqDTO.java @@ -1,7 +1,6 @@ package com.example.scoi.domain.member.dto; import com.example.scoi.domain.member.enums.ExchangeType; -import jakarta.validation.constraints.Pattern; public class MemberReqDTO { @@ -13,8 +12,7 @@ public record ChangePassword( // 간편 비밀번호 재설정 public record ResetPassword( - @Pattern(regexp = "^\\d{11}$") - String phoneNumber, + String verificationCode, String newPassword ){} diff --git a/src/main/java/com/example/scoi/domain/member/service/MemberService.java b/src/main/java/com/example/scoi/domain/member/service/MemberService.java index 411be85..4ab4e00 100644 --- a/src/main/java/com/example/scoi/domain/member/service/MemberService.java +++ b/src/main/java/com/example/scoi/domain/member/service/MemberService.java @@ -126,7 +126,7 @@ public Void resetPassword( .orElseThrow(() -> new MemberException(MemberErrorCode.MEMBER_NOT_FOUND)); // 인증된 전화번호인지 확인 - if (!redisUtil.exists(VERIFICATION_PREFIX+dto.phoneNumber())){ + if (!redisUtil.exists(VERIFICATION_PREFIX+dto.verificationCode())){ throw new MemberException(MemberErrorCode.UNVERIFIED_PHONE_NUMBER); } diff --git a/src/main/java/com/example/scoi/global/client/BithumbClient.java b/src/main/java/com/example/scoi/global/client/BithumbClient.java index bcb9b37..c380a27 100644 --- a/src/main/java/com/example/scoi/global/client/BithumbClient.java +++ b/src/main/java/com/example/scoi/global/client/BithumbClient.java @@ -1,10 +1,8 @@ package com.example.scoi.global.client; -import com.example.scoi.domain.member.dto.MemberReqDTO; import com.example.scoi.domain.transfer.dto.TransferReqDTO; import com.example.scoi.global.client.dto.BithumbReqDTO; import com.example.scoi.global.client.dto.BithumbResDTO; -import com.example.scoi.global.client.dto.UpbitResDTO; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.*; @@ -75,6 +73,12 @@ BithumbResDTO.GetDepositAddress getDepositAddress( @RequestParam("net_type") String netType ); + // 전체 입금 주소 조회 + @GetMapping("/v1/deposits/coin_addresses") + List getDepositAddresses( + @RequestHeader("Authorization") String token + ); + // 이체 출금 가능 정보 // 쿼리 파라미터 O @GetMapping("/v1/withdraws/chance") diff --git a/src/main/java/com/example/scoi/global/client/UpbitClient.java b/src/main/java/com/example/scoi/global/client/UpbitClient.java index da8e2b5..202ad69 100644 --- a/src/main/java/com/example/scoi/global/client/UpbitClient.java +++ b/src/main/java/com/example/scoi/global/client/UpbitClient.java @@ -1,8 +1,6 @@ package com.example.scoi.global.client; -import com.example.scoi.domain.member.dto.MemberReqDTO; import com.example.scoi.domain.transfer.dto.TransferReqDTO; -import com.example.scoi.global.client.dto.BithumbResDTO; import com.example.scoi.global.client.dto.UpbitReqDTO; import com.example.scoi.global.client.dto.UpbitResDTO; import org.springframework.cloud.openfeign.FeignClient; @@ -79,6 +77,12 @@ UpbitResDTO.GetDepositAddress getDepositAddress( @RequestParam("net_type") String netType ); + // 입금 주소 목록 조회 + @GetMapping("/v1/deposits/coin_addresses") + List getDepositAddresses( + @RequestHeader("Authorization") String token + ); + // 출금(이체) 가능 금액 조회 // 쿼리파라미터 O @GetMapping("/v1/withdraws/chance") diff --git a/src/main/java/com/example/scoi/global/config/SecurityConfig.java b/src/main/java/com/example/scoi/global/config/SecurityConfig.java index f098bb4..091297d 100644 --- a/src/main/java/com/example/scoi/global/config/SecurityConfig.java +++ b/src/main/java/com/example/scoi/global/config/SecurityConfig.java @@ -49,7 +49,7 @@ public PasswordEncoder passwordEncoder() { } @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + public SecurityFilterChain securityFilterChain(HttpSecurity http){ http .csrf(AbstractHttpConfigurer::disable) .cors(cors -> cors.configurationSource(corsConfigurationSource())) @@ -75,8 +75,8 @@ public CorsConfigurationSource corsConfigurationSource() { // 프로덕션 환경: 실제 도메인으로 제한 configuration.setAllowedOrigins(List.of( "http://localhost:3000", - "http://localhost:8080" - // "https://scoi.~~~" // 배포 시 추가 + "http://localhost:8080", + "https://scoi.shop" )); configuration.setAllowedMethods(Arrays.asList( diff --git a/src/main/java/com/example/scoi/global/config/SwaggerConfig.java b/src/main/java/com/example/scoi/global/config/SwaggerConfig.java index d90f3a0..e4961ca 100644 --- a/src/main/java/com/example/scoi/global/config/SwaggerConfig.java +++ b/src/main/java/com/example/scoi/global/config/SwaggerConfig.java @@ -29,7 +29,8 @@ public OpenAPI swagger() { return new OpenAPI() .info(info) - .addServersItem(new Server().url("http://13.209.12.10:8081")) + .addServersItem(new Server().url("https://scoi.shop").description("개발 서버")) + .addServersItem(new Server().url("http://localhost:8080").description("로컬")) .addSecurityItem(securityRequirement) .components(components); }