Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 35 additions & 13 deletions src/main/java/com/chaineeproject/chainee/did/DidController.java
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// src/main/java/com/chaineeproject/chainee/did/DidController.java
package com.chaineeproject.chainee.did;

import com.chaineeproject.chainee.did.dto.DidVerifyRequest;
Expand All @@ -20,7 +21,7 @@
@RestController
@RequestMapping("/api/did")
@RequiredArgsConstructor
@Tag(name = "DID", description = "프론트 검증 결과를 반영해 didVerified 상태를 업데이트")
@Tag(name = "DID", description = "프론트 검증 결과를 반영해 didVerified 상태와 did 주소를 업데이트")
@SecurityRequirement(name = "bearerAuth")
public class DidController {

Expand All @@ -33,16 +34,24 @@ private User me(Jwt jwt) {
}

@Operation(
summary = "DID 검증 상태 반영",
description = "프론트에서 DID 검증을 끝낸 뒤 결과를 서버에 반영합니다. (문자열 DID는 저장하지 않음)",
summary = "DID 검증 상태 + 주소 반영",
description = """
프론트에서 DID 검증을 끝낸 뒤 결과를 서버에 반영합니다.
- verified=true이면 DID 주소(did:로 시작)를 함께 보내 저장합니다.
- verified=false이면 검증 상태를 해제하고 저장된 DID를 제거합니다(정책).
""",
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
required = true,
content = @Content(
mediaType = MediaType.APPLICATION_JSON_VALUE,
schema = @Schema(implementation = DidVerifyRequest.class),
examples = {
@ExampleObject(name="검증 완료로 반영", value = "{ \"verified\": true }"),
@ExampleObject(name="검증 해제로 반영", value = "{ \"verified\": false }")
@ExampleObject(name="검증 완료 반영", value = """
{ "verified": true, "did": "did:key:z6Mkabc123..." }
"""),
@ExampleObject(name="검증 해제 반영", value = """
{ "verified": false }
""")
}
)
),
Expand All @@ -56,13 +65,15 @@ private User me(Jwt jwt) {
{
"success": true,
"didVerified": true,
"did": "did:key:z6Mkabc123...",
"didVerifiedAt": "2025-10-14T21:35:12.345"
}
"""),
@ExampleObject(name="해제 응답", value = """
{
"success": true,
"didVerified": false,
"did": null,
"didVerifiedAt": null
}
""")
Expand All @@ -71,13 +82,24 @@ private User me(Jwt jwt) {
)
)
@PostMapping(value = "/verify", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<DidVerifyResponse> verify(@AuthenticationPrincipal Jwt jwt,
@RequestBody @Valid DidVerifyRequest req) {
var updated = didService.updateDidVerified(me(jwt), req.verified());
return ResponseEntity.ok(new DidVerifyResponse(
true,
updated.isDidVerified(),
updated.getDidVerifiedAt() != null ? updated.getDidVerifiedAt().toString() : null
));
public ResponseEntity<?> verify(@AuthenticationPrincipal Jwt jwt,
@RequestBody @Valid DidVerifyRequest req) {
try {
var updated = didService.updateDidVerified(me(jwt), req.verified(), req.did());
return ResponseEntity.ok(new DidVerifyResponse(
true,
updated.isDidVerified(),
updated.getDid(),
updated.getDidVerifiedAt() != null ? updated.getDidVerifiedAt().toString() : null
));
} catch (IllegalArgumentException e) {
// 형식 오류 등 클라이언트 책임 에러는 400으로 반환
return ResponseEntity.badRequest().body(new DidVerifyResponse(
false,
false,
null,
null
));
}
}
}
31 changes: 28 additions & 3 deletions src/main/java/com/chaineeproject/chainee/did/DidService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

@Service
@RequiredArgsConstructor
Expand All @@ -14,9 +15,33 @@ public class DidService {
private final UserRepository userRepository;

@Transactional
public User updateDidVerified(User me, boolean verified) {
if (verified) me.markDidVerified();
else me.unmarkDidVerified();
public User updateDidVerified(User me, boolean verified, String didAddress) {
if (verified) {
String normalized = normalizeDid(didAddress);
validateDid(normalized);
me.markDidVerified(normalized);
} else {
me.unmarkDidVerified();
}
return userRepository.save(me);
}

/** 필요 시 향후 스킴별 정규화 확장 */
private String normalizeDid(String did) {
if (!StringUtils.hasText(did)) return null;
return did.trim();
}

/** 최소 검증: "did:" 프리픽스 확인 + 길이 체크 */
private void validateDid(String did) {
if (!StringUtils.hasText(did)) {
throw new IllegalArgumentException("verified=true 일 때 DID 주소가 필요합니다.");
}
if (!did.startsWith("did:")) {
throw new IllegalArgumentException("유효하지 않은 DID 형식입니다. 'did:'로 시작해야 합니다.");
}
if (did.length() > 255) {
throw new IllegalArgumentException("DID 길이가 너무 깁니다.(최대 255자)");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
// src/main/java/com/chaineeproject/chainee/did/dto/DidVerifyRequest.java
package com.chaineeproject.chainee.did.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;

public record DidVerifyRequest(
@Schema(description = "프론트에서 검증한 DID 결과(true=검증완료로 반영, false=해제)", example = "true")
@NotNull Boolean verified
@NotNull Boolean verified,

@Schema(description = "검증이 끝난 사용자의 DID 주소(did: 로 시작). verified=true일 때 권장/사실상 필수",
example = "did:key:z6Mkw...abc")
String did
) {}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// src/main/java/com/chaineeproject/chainee/did/dto/DidVerifyResponse.java
package com.chaineeproject.chainee.did.dto;

import io.swagger.v3.oas.annotations.media.Schema;
Expand All @@ -7,6 +8,8 @@ public record DidVerifyResponse(
boolean success,
@Schema(description = "현재 DID 검증 상태", example = "true")
boolean didVerified,
@Schema(description = "현재 저장된 DID 주소(해제 시 null)", example = "did:key:z6Mkw...abc")
String did,
@Schema(description = "검증 완료 시각(ISO-8601) - 해제 시 null", example = "2025-10-14T21:35:12.345")
String didVerifiedAt
) {}
8 changes: 7 additions & 1 deletion src/main/java/com/chaineeproject/chainee/entity/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,16 @@ public class User {
private String provider;
private String providerId;

//kyc 관련 필드
@Column(name = "kyc_verified", nullable = false)
private boolean kycVerified; // 기본 false
@Column(name = "kyc_phone", length = 20)
private String kycPhone;
private LocalDateTime kycVerifiedAt;

//did 관련 필드
@Column(name = "did", length = 255, unique = true)
private String did;
@Column(name = "did_verified", nullable = false)
private boolean didVerified; // 기본 false
@Column(name = "did_verified_at")
Expand Down Expand Up @@ -101,12 +105,14 @@ protected void onUpdate() {
this.updatedAt = LocalDateTime.now();
}

public void markDidVerified() {
public void markDidVerified(String didAddress) {
this.did = didAddress;
this.didVerified = true;
this.didVerifiedAt = LocalDateTime.now();
}

public void unmarkDidVerified() {
this.did = null;
this.didVerified = false;
this.didVerifiedAt = null;
}
Expand Down