Files
Gnuboard7/modules/_bundled/sirsoft-ecommerce/docs/api/coupons.md
T
HeuJung b5c0ec1178 fix(core,ecommerce,board,page,admin_basic,basic): 목록 응답의 하위 컬렉션 전량 직렬화 해소
관리자 상품목록을 한 페이지 여는 것만으로 그 페이지 모든 상품의 옵션이 응답에 실렸다.
같은 패턴을 저장소 전역에서 찾아 14개 목록 엔드포인트를 함께 정리했다.

근본 원인은 둘이다. Resource 가 whenLoaded 로 방어하는데 Repository 가 목록 쿼리에서
관계를 무조건 로드해 가드가 항상 참이 되는 가짜 가드, 그리고 toListArray 경량 표현을
정의해 두고도 컬렉션이 toArray 를 부르는 목록/상세 미분리다. 둘 다 응답만 보면
정상이라 오류도 경고도 없이 페이로드만 불어난다.

목록은 화면이 실제로 그리는 것만 싣는다. 개수·합계는 PHP 컬렉션 연산이 아니라 DB
집계로, 대표 1건이 필요한 곳은 관계 자체를 oldestOfMany 로 좁힌다. eager load 의
limit(1) 은 부모별이 아니라 배치 쿼리 전체에 걸려 첫 행만 값을 갖게 되므로 쓸 수 없다.

뺀 값에는 대체 경로를 먼저 만들었다. 상품 옵션은 행을 펼칠 때 배치로 불러오고(상품 수와
무관하게 쿼리 상수), 종전 동작이 필요한 호출자를 위해 ?with_options=1 등 opt-in 을 남겼다.
배송정책 국가설정과 리뷰 첨부 이미지는 소비처를 실측한 결과 화면이 실제로 그리고 있어
제거하지 않았다 — 그 소비 사실을 회귀 테스트로 고정했다.

재발 방지로 정적 검사 룰 4종과 규정 문서 항목을 함께 넣었다.
2026-08-05 21:20:05 +09:00

556 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Coupons API 레퍼런스
> **소유**: module `sirsoft-ecommerce` · **생성**: `php artisan api:docgen` (실측 기반). @generated 블록은 재생성 시 갱신되며, 사람이 작성한 설명은 보존됩니다.
---
## TL;DR (5초 요약)
```text
1. 이 문서는 실제 API 호출로 실측한 Coupons 엔드포인트 레퍼런스입니다
2. 각 엔드포인트: 메서드/URI/권한 + 요청 파라미터 표 + 요청 예시(curl) + 실측 응답 필드 표 + 응답 예시(envelope)
3. 응답 필드의 예시값·응답 예시 JSON 은 실제 호출 응답에서 관측된 값입니다
4. 갱신: 코드 변경 후 php artisan api:docgen 재실행
5. 설명(TODO) 칸은 사람이 채웁니다
```
---
### GET /api/modules/sirsoft-ecommerce/user/coupons
<!-- @generated:start:api.modules.sirsoft-ecommerce.user.coupons.index -->
- **라우트명**: `api.modules.sirsoft-ecommerce.user.coupons.index`
- **컨트롤러**: `Modules\Sirsoft\Ecommerce\Http\Controllers\User\UserCouponController@index`
- **인증/권한**: `auth:sanctum`
**요청 파라미터**
| 이름 | 위치 | 타입 | 필수 | 허용값 | 용도 |
| --- | --- | --- | --- | --- | --- |
| status | query | string | 아니오 | `available`, `used`, `expired` | 상태 필터 (해당 상태의 항목만 조회) |
| per_page | query | integer | 아니오 | min 1, max 50 | 페이지당 항목 수 |
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.coupon.user_list_validation_rules`).
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/user/coupons?status=available&per_page=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| coupons | object | `{"data":[{"id":2512,"coupon_id":107,"user_id":"a26219fc-9…` | 회원이 발급받은 쿠폰(발급 내역) 페이지네이션 객체 (`data[]` 발급 건 + `pagination` — CouponIssueCollection 직렬화, 쿠폰 정의가 아닌 회원별 발급 건) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "쿠폰 목록을 조회했습니다.",
"data": {
"coupons": {
"data": [
{
"id": 2512,
"coupon_id": 107,
"user_id": "a26219fc-94a0-4f63-9404-04c2a6ac99e4",
"coupon_code": "DL-ORG8PPAH",
"status": "used",
"...": "(12개 키 생략, 총 17개)"
},
{
"id": 2513,
"coupon_id": 61,
"user_id": "a26219fc-94a0-4f63-9404-04c2a6ac99e4",
"coupon_code": "DR-WC3RLEDB",
"status": "used",
"...": "(12개 키 생략, 총 17개)"
},
{
"id": 2514,
"coupon_id": 62,
"user_id": "a26219fc-94a0-4f63-9404-04c2a6ac99e4",
"coupon_code": "DR-VHQFNYRR",
"status": "used",
"...": "(12개 키 생략, 총 17개)"
},
{
"id": 2515,
"coupon_id": 63,
"user_id": "a26219fc-94a0-4f63-9404-04c2a6ac99e4",
"coupon_code": "DR-4QRXNXAI",
"status": "used",
"...": "(12개 키 생략, 총 17개)"
},
{
"id": 2516,
"coupon_id": 64,
"user_id": "a26219fc-94a0-4f63-9404-04c2a6ac99e4",
"coupon_code": "DR-IT4CCTSM",
"status": "used",
"...": "(12개 키 생략, 총 17개)"
},
"... (총 25건 중 5건 표시)"
],
"pagination": {
"current_page": 1,
"last_page": 2,
"per_page": 25,
"total": 41,
"from": 1,
"...": "(2개 키 생략, 총 7개)"
}
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
| --- | --- | --- |
| 401 | Unauthenticated | 유효한 Bearer 토큰이 없거나 만료된 경우 |
| 422 | Unprocessable Entity | 요청 파라미터가 검증 규칙을 위반한 경우 (`error.errors` 에 필드별 메시지) |
<!-- @generated:end -->
**설명** 로그인한 회원이 마이페이지 쿠폰함에서 자신이 발급받은 쿠폰(발급 내역)을 페이지네이션으로 조회합니다. `auth:sanctum` 인증이 필요하며, `status` 필터(available·used·expired)로 사용 가능/사용 완료/만료 쿠폰을 구분합니다. `UserCouponService::getUserCoupons()`가 조회하고 `CouponIssueCollection`으로 직렬화되므로, 여기의 항목은 쿠폰 정의(마스터)가 아니라 회원별 발급 건입니다.
### GET /api/modules/sirsoft-ecommerce/user/coupons/available
<!-- @generated:start:api.modules.sirsoft-ecommerce.user.coupons.available -->
- **라우트명**: `api.modules.sirsoft-ecommerce.user.coupons.available`
- **컨트롤러**: `Modules\Sirsoft\Ecommerce\Http\Controllers\User\UserCouponController@available`
- **인증/권한**: `auth:sanctum`
**요청 파라미터**
| 이름 | 위치 | 타입 | 필수 | 허용값 | 용도 |
| --- | --- | --- | --- | --- | --- |
| product_ids | query | array | 아니오 | — | product 식별자 배열 |
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.coupon.user_available_validation_rules`).
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/user/coupons/available?product_ids=%EC%98%88%EC%8B%9C%EA%B0%92 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| coupons | array | `[{"id":2534,"coupon_id":85,"user_id":1209,"coupon_code":"…` | 현재 장바구니 상품에 적용 가능한 보유 쿠폰(발급 건) 배열 (상품/카테고리 범위·최소 주문금액·유효기간을 만족해 주문에 곧바로 선택 가능한 후보만) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "사용 가능한 쿠폰 목록을 조회했습니다.",
"data": {
"coupons": [
{
"id": 2534,
"coupon_id": 85,
"user_id": 1209,
"coupon_code": "DR-PDZ7HT4G",
"status": "available",
"...": "(8개 키 생략, 총 13개)"
},
{
"id": 2542,
"coupon_id": 94,
"user_id": 1209,
"coupon_code": "DR-ROQPOOED",
"status": "available",
"...": "(8개 키 생략, 총 13개)"
},
{
"id": 2552,
"coupon_id": 106,
"user_id": 1209,
"coupon_code": "DR-XBLQUABC",
"status": "available",
"...": "(8개 키 생략, 총 13개)"
},
{
"id": 2529,
"coupon_id": 79,
"user_id": 1209,
"coupon_code": "DR-90IO2SWI",
"status": "available",
"...": "(8개 키 생략, 총 13개)"
},
{
"id": 2532,
"coupon_id": 83,
"user_id": 1209,
"coupon_code": "DR-FEKYVAFT",
"status": "available",
"...": "(8개 키 생략, 총 13개)"
},
"... (총 27건 중 5건 표시)"
]
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
| --- | --- | --- |
| 401 | Unauthenticated | 유효한 Bearer 토큰이 없거나 만료된 경우 |
| 422 | Unprocessable Entity | 요청 파라미터가 검증 규칙을 위반한 경우 (`error.errors` 에 필드별 메시지) |
<!-- @generated:end -->
**설명** 체크아웃 화면에서 회원이 현재 장바구니 상품에 실제로 적용할 수 있는 보유 쿠폰만 추려서 반환합니다. `auth:sanctum` 인증이 필요하고 `product_ids` 로 대상 상품을 전달하면, `UserCouponService::getAvailableCoupons()`가 보유 쿠폰 중 상품/카테고리 적용 범위·최소 주문금액·유효기간 등을 만족하는 것만 필터링해 내려줍니다. 쿠폰함 목록(index)과 달리 주문에 곧바로 선택 가능한 후보만 반환하는 점이 다릅니다.
### GET /api/modules/sirsoft-ecommerce/user/coupons/downloadable
<!-- @generated:start:api.modules.sirsoft-ecommerce.user.coupons.downloadable -->
- **라우트명**: `api.modules.sirsoft-ecommerce.user.coupons.downloadable`
- **컨트롤러**: `Modules\Sirsoft\Ecommerce\Http\Controllers\User\UserCouponController@downloadable`
- **인증/권한**: `auth:sanctum`
**요청 파라미터**
| 이름 | 위치 | 타입 | 필수 | 허용값 | 용도 |
| --- | --- | --- | --- | --- | --- |
| per_page | query | integer | 아니오 | min 1, max 50 | 페이지당 항목 수 |
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.coupon.user_downloadable_validation_rules`).
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/user/coupons/downloadable?per_page=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `107` | 기본 키 (내부 식별자) |
| name | object | `{"ko":"카테고리 배송비 3,000원 할인","en":"Category Shipping 3,000 …` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| description | object | `{"ko":"특정 카테고리 배송비 할인","en":"Shipping discount on categor…` | 설명 (다국어 필드는 로케일별 값 객체) |
| target_type | string | `shipping_fee` | 할인 적용 대상 (product_amount=상품금액, order_amount=주문금액, shipping_fee=배송비) |
| discount_type | string | `fixed` | 혜택 유형 (fixed=정액 할인, rate=정률(%) 할인) |
| discount_value | string | `3000.00` | 혜택값 (정액이면 할인 금액, 정률이면 할인율 %) |
| discount_max_amount | string | `3000.00` | 정률 할인 시 최대 할인 금액 상한 (없으면 null) |
| min_order_amount | string | `20000.00` | 쿠폰 적용 최소 주문금액 (미만 주문에는 사용 불가) |
| issue_method | string | `download` | 발급 방법 (direct=직접발급, download=다운로드, auto=자동발급) |
| issue_condition | string | `manual` | 발급 조건 (manual=수동, signup=회원가입, first_purchase=첫구매, birthday=생일) |
| issue_status | string | `issuing` | 발급 상태 (issuing=발급중, stopped=발급중단) |
| total_quantity | integer | `300` | 총 발급 수량 상한 (null=무제한) |
| issued_count | integer | `1` | issued 개수 (집계) |
| per_user_limit | integer | `1` | 회원 1인당 발급 제한 수량 |
| valid_type | string | `period` | 유효기간 유형 (period=기간지정, days_from_issue=발급일로부터 N일) |
| valid_days | integer | `14` | 발급일로부터 유효한 일수 (valid_type=days_from_issue 인 경우) |
| valid_from | string | `2026-07-23T14:36:17.000000Z` | 유효기간 시작일 (쿠폰 사용 가능 시작 시각) |
| valid_to | string | `2026-09-21T14:36:17.000000Z` | 유효기간 종료일 (쿠폰 사용 가능 종료 시각) |
| issue_from | string | `2026-07-23T14:36:17.000000Z` | 발급기간 시작일 (다운로드 가능 시작 시각) |
| issue_to | string | `2026-08-29T14:36:17.000000Z` | 발급기간 종료일 (다운로드 가능 종료 시각) |
| is_combinable | boolean | `true` | combinable 여부 |
| target_scope | string | `categories` | 적용 범위 (all=전체 상품, products=특정 상품, categories=특정 카테고리) |
| created_by | null | `null` | 쿠폰 등록자(관리자) 식별자 (users 참조, 삭제 시 null) |
| created_at | string | `2026-07-30T14:36:17.000000Z` | 생성 일시 |
| updated_at | string | `2026-07-31T12:51:54.000000Z` | 최종 수정 일시 |
| deleted_at | null | `null` | 소프트 삭제 일시 (미삭제 시 null) |
| is_downloaded | boolean | `true` | downloaded 여부 |
| user_issued_count | integer | `1` | user issued 개수 (집계) |
| coupon_id | integer | `107` | coupon 식별자 (연관 리소스 참조) |
| localized_name | string | `카테고리 배송비 3,000원 할인` | `name` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| target_type_short_label | string | `배송비` | `target_type_short` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| valid_period_formatted | string | `2026-07-23 ~ 2026-09-21` | `valid_period` 값의 표시용 포맷 문자열 (통화/용량/일시 등 로케일·단위 포맷) |
| min_order_amount_formatted | string | `¥20,000` | `min_order_amount` 값의 표시용 포맷 문자열 (통화/용량/일시 등 로케일·단위 포맷) |
| remaining_quantity | integer | `299` | 잔여 발급 가능 수량 (total_quantity − issued_count, 무제한이면 null) |
| benefit_formatted | string | `¥3,000 할인` | `benefit` 값의 표시용 포맷 문자열 (통화/용량/일시 등 로케일·단위 포맷) |
| included_products | array | `[]` | 적용 대상 포함 상품 목록 (target_scope=products 시 이 상품에만 적용) |
| excluded_products | array | `[]` | 적용 제외 상품 목록 (해당 상품은 쿠폰 적용에서 제외) |
| included_categories | array | `[{"id":32,"name":{"ko":"바지","en":"Pants"},"description":{…` | 적용 대상 포함 카테고리 목록 (target_scope=categories 시 이 카테고리에만 적용) |
| excluded_categories | array | `[]` | 적용 제외 카테고리 목록 (해당 카테고리는 쿠폰 적용에서 제외) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "다운로드 가능한 쿠폰 목록을 불러왔습니다.",
"data": {
"current_page": 1,
"data": [
{
"id": 107,
"name": {
"ko": "카테고리 배송비 3,000원 할인",
"en": "Category Shipping 3,000 KRW Off"
},
"description": {
"ko": "특정 카테고리 배송비 할인",
"en": "Shipping discount on categories"
},
"target_type": "shipping_fee",
"discount_type": "fixed",
"discount_value": "3000.00",
"discount_max_amount": null,
"min_order_amount": "20000.00",
"issue_method": "download",
"issue_condition": "manual",
"issue_status": "issuing",
"total_quantity": 300,
"issued_count": 1,
"per_user_limit": 1,
"valid_type": "period",
"valid_days": null,
"valid_from": "2026-07-23T14:36:17.000000Z",
"valid_to": "2026-09-21T14:36:17.000000Z",
"issue_from": "2026-07-23T14:36:17.000000Z",
"issue_to": "2026-08-29T14:36:17.000000Z",
"is_combinable": true,
"target_scope": "categories",
"created_by": null,
"created_at": "2026-07-30T14:36:17.000000Z",
"updated_at": "2026-07-31T12:51:54.000000Z",
"deleted_at": null,
"is_downloaded": true,
"user_issued_count": 1,
"coupon_id": 107,
"localized_name": "카테고리 배송비 3,000원 할인",
"target_type_short_label": "배송비",
"valid_period_formatted": "2026-07-23 ~ 2026-09-21",
"min_order_amount_formatted": "¥20,000",
"remaining_quantity": 299,
"benefit_formatted": "¥3,000 할인",
"included_products": [],
"excluded_products": [],
"included_categories": [
{
"id": 32,
"name": {
"ko": "바지",
"en": "Pants"
},
"description": {
"ko": "다양한 스타일의 바지",
"en": "Various styles of pants"
},
"parent_id": 30,
"path": "29/30/32",
"depth": 2,
"sort_order": 1,
"is_active": true,
"slug": "men-pants",
"meta_title": null,
"meta_description": null,
"created_at": "2026-07-30T14:35:47.000000Z",
"updated_at": "2026-07-30T14:35:47.000000Z",
"pivot": {
"coupon_id": 107,
"category_id": 32
}
},
{
"id": 42,
"name": {
"ko": "노트북",
"en": "Laptops"
},
"description": {
"ko": "노트북 컴퓨터",
"en": "Laptop computers"
},
"parent_id": 38,
"path": "38/42",
"depth": 1,
"sort_order": 1,
"is_active": true,
"slug": "laptops",
"meta_title": null,
"meta_description": null,
"created_at": "2026-07-30T14:35:47.000000Z",
"updated_at": "2026-07-30T14:35:47.000000Z",
"pivot": {
"coupon_id": 107,
"category_id": 42
}
}
],
"excluded_categories": []
},
{
"id": 103,
"name": {
"ko": "배송비 50% 할인",
"en": "Shipping 50% Off"
},
"description": {
"ko": "배송비 반값 할인",
"en": "50% off shipping fee"
},
"target_type": "shipping_fee",
"discount_type": "rate",
"discount_value": "50.00",
"discount_max_amount": "3000.00",
"min_order_amount": "20000.00",
"issue_method": "download",
"issue_condition": "manual",
"issue_status": "issuing",
"total_quantity": 1000,
"issued_count": 62,
"per_user_limit": 1,
"valid_type": "days_from_issue",
"valid_days": 14,
"valid_from": null,
"valid_to": null,
"issue_from": "2026-07-25T14:36:17.000000Z",
"issue_to": "2026-09-28T14:36:17.000000Z",
"is_combinable": true,
"target_scope": "all",
"created_by": null,
"created_at": "2026-07-30T14:36:17.000000Z",
"updated_at": "2026-07-31T12:52:41.000000Z",
"deleted_at": null,
"is_downloaded": true,
"user_issued_count": 1,
"coupon_id": 103,
"localized_name": "배송비 50% 할인",
"target_type_short_label": "배송비",
"valid_period_formatted": "발급일로부터 14일",
"min_order_amount_formatted": "¥20,000",
"remaining_quantity": 938,
"benefit_formatted": "50.00% 할인 (최대 ¥3,000)",
"included_products": [],
"excluded_products": [],
"included_categories": [],
"excluded_categories": []
},
"... (총 11건 중 2건 표시)"
],
"first_page_url": "https://api.example.com/api/modules/sirsoft-ecommerce/user/coupons/downloadable?page=1",
"from": 1,
"last_page": 1,
"last_page_url": "https://api.example.com/api/modules/sirsoft-ecommerce/user/coupons/downloadable?page=1",
"links": [
{
"url": null,
"label": "pagination.previous",
"page": null,
"active": false
},
{
"url": "https://api.example.com/api/modules/sirsoft-ecommerce/user/coupons/downloadable?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": null,
"label": "pagination.next",
"page": null,
"active": false
}
],
"next_page_url": null,
"path": "https://api.example.com/api/modules/sirsoft-ecommerce/user/coupons/downloadable",
"per_page": 25,
"prev_page_url": null,
"to": 11,
"total": 11
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
| --- | --- | --- |
| 401 | Unauthenticated | 유효한 Bearer 토큰이 없거나 만료된 경우 |
| 422 | Unprocessable Entity | 요청 파라미터가 검증 규칙을 위반한 경우 (`error.errors` 에 필드별 메시지) |
<!-- @generated:end -->
**설명** 로그인한 회원이 지금 다운로드해 발급받을 수 있는 쿠폰(다운로드형) 목록을 페이지네이션으로 조회합니다. `auth:sanctum` 인증이 필요하며, `UserCouponService::getDownloadableCoupons()`가 발급기간·수량·회원당 한도를 만족하는 다운로드형 쿠폰을 반환하고 각 항목에 `is_downloaded`·`user_issued_count`로 이미 받았는지 여부를 표시합니다. 여기 항목은 아직 발급 전이므로 쿠폰 정의(마스터) 기준이며, 실제 발급은 download 엔드포인트로 수행합니다.
### POST /api/modules/sirsoft-ecommerce/user/coupons/{couponId}/download
<!-- @generated:start:api.modules.sirsoft-ecommerce.user.coupons.download -->
- **라우트명**: `api.modules.sirsoft-ecommerce.user.coupons.download`
- **컨트롤러**: `Modules\Sirsoft\Ecommerce\Http\Controllers\User\UserCouponController@download`
- **인증/권한**: `auth:sanctum`
**요청 파라미터**
| 이름 | 위치 | 타입 | 필수 | 허용값 | 용도 |
| --- | --- | --- | --- | --- | --- |
| couponId | path | string | 예 | — | 대상 coupon의 식별자 |
| coupon_id | body | integer | 예 | — | coupon 식별자 |
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/user/coupons/{couponId}/download HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"coupon_id": 1
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
| --- | --- | --- |
| 401 | Unauthenticated | 유효한 Bearer 토큰이 없거나 만료된 경우 |
| 422 | Unprocessable Entity | 요청 파라미터가 검증 규칙을 위반한 경우 (`error.errors` 에 필드별 메시지) |
| 404 | Not Found | path 파라미터에 해당하는 리소스가 없는 경우 |
<!-- @generated:end -->
**설명** 로그인한 회원이 다운로드형 쿠폰 1건을 실제로 발급받습니다. `auth:sanctum` 인증이 필요하고 대상 쿠폰은 `couponId`(경로)로 지정하며, `UserCouponService::downloadCoupon()`이 발급기간·수량·회원당 한도·중복 발급 여부를 검증한 뒤 발급 내역을 생성해 201로 반환합니다. 한도 초과·발급기간 종료 등 발급 불가 사유는 서비스 예외의 메시지와 코드가 그대로 오류 응답에 전달됩니다.