using MediatR; using Web.Api.Common; using Web.Api.Extensions; namespace Web.Api.Endpoints.V1.Coupons; internal sealed class GetCode : IEndpoint { public void MapEndpoint(IEndpointRouteBuilder app) { app.MapGet("v1/coupons/codes/{code}", async ( string code, ISender sender, CancellationToken ct ) => { var query = new Application.Features.Api.V1.Coupons.GetCode.Query(code); var result = await sender.Send(query, ct); return result.Match( data => ApiResponse.Ok(data), CustomResults.Problem ); }) .WithTags("쿠폰") .WithGroupName("public") .WithName("GetCouponCode") .WithSummary("쿠폰 코드 상세 조회") .WithDescription(""" 단일 쿠폰 코드의 **사용 여부 / 상태 / 사용 일시**를 반환합니다. - **등록일시는 의도적으로 제공하지 않습니다** (보안/사용자 요구). - 상태(`status`) + 발급일(`issuedAt`) + 사용일(`usedAt`) 만 노출. ### 필수 scope - `read:coupons` ### 경로 변수 - `code` — 쿠폰 코드 평문 (예: `ABCD-1234-EFGH-5678`) - 서버 측에서 SHA-256 해시 후 검색하므로 평문이 로그에 남지 않습니다. ### Status 값 | 값 | 라벨 | 의미 | |---|---|---| | `1` | 미발급 | Available — 결제 가능 | | `2` | 결제 진행 중 | Reserved — 점유됨 | | `3` | 발급됨 | Issued — 회원 보관함 | | `4` | 사용 완료 | Used — 사용 처리됨 | | `5` | 만료 | Expired — 관리자 만료 | ### 에러 - `400` — code 누락 - `404` — 해당 코드 없음 """) .Produces(StatusCodes.Status200OK) .ProducesProblem(StatusCodes.Status400BadRequest) .ProducesProblem(StatusCodes.Status401Unauthorized) .ProducesProblem(StatusCodes.Status403Forbidden) .ProducesProblem(StatusCodes.Status404NotFound) .RequireAuthorization(policy => policy .AddAuthenticationSchemes("ApiKey", "OAuth2Bearer") .RequireAuthenticatedUser()) .RequireScope("read:coupons"); } }