> ## Documentation Index
> Fetch the complete documentation index at: https://api-docs.vpms.io/llms.txt
> Use this file to discover all available pages before exploring further.

# 쿠폰 (Coupon)

> 쿠폰 발급 및 사용 API

## 개요

Core 서비스의 쿠폰 도메인은 할인 쿠폰의 발급, 적용, 만료 처리 및 사용 이력 관리 기능을 제공합니다. 숙소별 쿠폰과 전체 시스템 쿠폰을 모두 지원하며, 다양한 할인 유형과 사용 제한 조건을 설정할 수 있습니다.

## Queries

### getAccommodationCoupons

숙소의 쿠폰 목록을 조회합니다. (관리자 전용)

#### GraphQL Signature

```graphql theme={null}
query GetAccommodationCoupons(
  $accommodationId: ID!
  $first: Int
  $after: String
) {
  getAccommodationCoupons(
    accommodationId: $accommodationId
    first: $first
    after: $after
  ) {
    nodes {
      id
      code
      name
      couponType
      discountType
      discount
      maxDiscountAmount
      maxUsableCount
      usableCount
      minUsableAmount
      expireAt
      isExhausted
      createdAt
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
```

#### 파라미터

<ParamField path="accommodationId" type="ID!" required>
  조회할 숙소 ID (관리자 권한 필요)
</ParamField>

<ParamField path="first" type="Int">
  페이지당 항목 수 (기본값: 20)
</ParamField>

<ParamField path="after" type="String">
  페이지네이션 커서
</ParamField>

#### 응답

<ResponseField name="nodes" type="Coupon[]">
  쿠폰 목록

  * `id`: 쿠폰 고유 ID
  * `code`: 쿠폰 코드 (고객이 입력하는 코드)
  * `name`: 쿠폰명
  * `couponType`: 쿠폰 유형
  * `discountType`: 할인 유형 (정액/정률)
  * `discount`: 할인 금액/비율
  * `maxDiscountAmount`: 최대 할인 금액
  * `maxUsableCount`: 최대 사용 가능 횟수
  * `usableCount`: 남은 사용 가능 횟수
  * `minUsableAmount`: 최소 사용 금액
  * `expireAt`: 만료일
  * `isExhausted`: 소진 여부
  * `createdAt`: 생성일
</ResponseField>

<ResponseField name="pageInfo" type="PageInfo">
  페이지네이션 정보
</ResponseField>

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  query {
    getAccommodationCoupons(
      accommodationId: "acc_123"
      first: 10
    ) {
      nodes {
        id
        code
        name
        discountType
        discount
        usableCount
        expireAt
      }
      pageInfo {
        hasNextPage
      }
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "getAccommodationCoupons": {
        "nodes": [
          {
            "id": 1,
            "code": "WELCOME2024",
            "name": "신규 고객 환영 쿠폰",
            "discountType": "FIXED",
            "discount": 10000,
            "usableCount": 50,
            "expireAt": "2024-12-31T23:59:59Z"
          }
        ],
        "pageInfo": {
          "hasNextPage": false
        }
      }
    }
  }
  ```
</CodeGroup>

## Mutations

### issueCoupon

새로운 쿠폰을 발급합니다. (관리자 또는 시스템 관리자 전용)

#### GraphQL Signature

```graphql theme={null}
mutation IssueCoupon($input: IssueCouponInput!) {
  issueCoupon(input: $input) {
    id
    code
    name
    couponType
    discountType
    discount
    maxDiscountAmount
    maxUsableCount
    minUsableAmount
    expireAt
    createdAt
  }
}
```

#### 파라미터

<ParamField path="input" type="IssueCouponInput!" required>
  쿠폰 발급 정보:

  * `accommodationId` (ID): 숙소 ID (null이면 전체 시스템 쿠폰)
  * `name` (String!): 쿠폰명
  * `couponType` (String!): 쿠폰 유형
  * `discountType` (String!): 할인 유형 ("FIXED" 또는 "PERCENT")
  * `discount` (Int!): 할인 금액 또는 비율
  * `maxDiscountAmount` (Int): 최대 할인 금액 (정률 할인 시 필수)
  * `maxUsableCount` (Int): 최대 사용 가능 횟수 (기본값: 1)
  * `minUsableAmount` (Int): 최소 사용 금액
  * `quantity` (Int!): 발급 수량
  * `expireAt` (DateTime): 만료일 (기본값: 9999-12-31)
  * `mask` (String): 사용 제한 마스크 (JSON 형식)
</ParamField>

#### 응답

<ResponseField name="coupons" type="Coupon[]">
  발급된 쿠폰 목록 (quantity만큼 생성됨)
</ResponseField>

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  mutation {
    issueCoupon(input: {
      accommodationId: "acc_123"
      name: "신규 고객 환영 쿠폰"
      couponType: "WELCOME"
      discountType: "FIXED"
      discount: 10000
      maxUsableCount: 1
      minUsableAmount: 50000
      quantity: 100
      expireAt: "2024-12-31T23:59:59Z"
    }) {
      id
      code
      name
      discount
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "issueCoupon": [
        {
          "id": 1,
          "code": "A1B2C3D4",
          "name": "신규 고객 환영 쿠폰",
          "discount": 10000
        },
        {
          "id": 2,
          "code": "E5F6G7H8",
          "name": "신규 고객 환영 쿠폰",
          "discount": 10000
        }
      ]
    }
  }
  ```
</CodeGroup>

<Note>
  * 쿠폰 코드는 쿠폰 ID를 기반으로 자동 생성됩니다
  * `quantity`만큼 동일한 조건의 쿠폰이 생성됩니다
  * `accommodationId`를 null로 설정하면 전체 시스템에서 사용 가능한 쿠폰이 됩니다
</Note>

***

### applyCoupon

구매 시 쿠폰을 적용하여 할인 금액을 계산하고 사용 이력을 생성합니다.

#### GraphQL Signature

```graphql theme={null}
mutation ApplyCoupon(
  $code: String!
  $purchaseAmount: Int!
  $accommodationId: ID
  $roomTypeId: ID
  $usageHashMask: String
) {
  applyCoupon(
    code: $code
    purchaseAmount: $purchaseAmount
    accommodationId: $accommodationId
    roomTypeId: $roomTypeId
    usageHashMask: $usageHashMask
  ) {
    id
    usageHash
    couponId
    purchaseAmount
    discountAmount
    status
    createdAt
  }
}
```

#### 파라미터

<ParamField path="code" type="String!" required>
  쿠폰 코드
</ParamField>

<ParamField path="purchaseAmount" type="Int!" required>
  구매 금액
</ParamField>

<ParamField path="accommodationId" type="ID">
  숙소 ID (숙소 전용 쿠폰 검증용)
</ParamField>

<ParamField path="roomTypeId" type="ID">
  객실 유형 ID (특정 객실 유형 제한 쿠폰용)
</ParamField>

<ParamField path="usageHashMask" type="String">
  이전 사용 이력 무효화용 해시 (쿠폰 재적용 시)
</ParamField>

#### 응답

<ResponseField name="couponUsageHistory" type="CouponUsageHistory">
  쿠폰 사용 이력

  * `id`: 사용 이력 ID
  * `usageHash`: 사용 고유 해시 (결제 완료 시 필요)
  * `couponId`: 쿠폰 ID
  * `purchaseAmount`: 구매 금액
  * `discountAmount`: 할인 금액
  * `status`: 사용 상태 ("PENDING")
  * `createdAt`: 생성일
</ResponseField>

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  mutation {
    applyCoupon(
      code: "WELCOME2024"
      purchaseAmount: 100000
      accommodationId: "acc_123"
    ) {
      id
      usageHash
      discountAmount
      status
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "applyCoupon": {
        "id": 1,
        "usageHash": "01HQKS9V8X2N3P4Q5R6S7T8U9V",
        "discountAmount": 10000,
        "status": "PENDING"
      }
    }
  }
  ```
</CodeGroup>

#### 할인 금액 계산 로직

1. **정액 할인 (FIXED)**: `discount` 값을 그대로 적용
2. **정률 할인 (PERCENT)**: `purchaseAmount * (discount / 100)`, 단 `maxDiscountAmount`를 초과할 수 없음
3. **최소 사용 금액**: `purchaseAmount`가 `minUsableAmount`보다 작으면 사용 불가
4. **할인 금액 검증**: `discountAmount`가 `purchaseAmount`보다 클 수 없음

#### 에러 처리

<ResponseField name="DATA_NOT_FOUND_ON_ID">
  해당 코드의 쿠폰을 찾을 수 없습니다.
</ResponseField>

<ResponseField name="INVALID_PARAMETER">
  할인 금액이 유효하지 않거나 구매 금액을 초과합니다.
</ResponseField>

<Warning>
  쿠폰 적용 후 실제 결제 완료 시 `usageHash`를 사용하여 사용 상태를 "USED"로 변경해야 합니다.
  결제가 취소되면 `usageHash`를 사용하여 사용 이력을 "INVALID"로 변경할 수 있습니다.
</Warning>

***

### expireCoupon

쿠폰을 수동으로 만료 처리합니다. (관리자 전용)

#### GraphQL Signature

```graphql theme={null}
mutation ExpireCoupon($code: String!) {
  expireCoupon(code: $code) {
    id
    code
    isExhausted
    expireAt
  }
}
```

#### 파라미터

<ParamField path="code" type="String!" required>
  만료 처리할 쿠폰 코드
</ParamField>

#### 응답

<ResponseField name="coupon" type="Coupon">
  만료 처리된 쿠폰 정보

  * `id`: 쿠폰 ID
  * `code`: 쿠폰 코드
  * `isExhausted`: 소진 여부 (true)
  * `expireAt`: 만료일 (현재 시각으로 변경됨)
</ResponseField>

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  mutation {
    expireCoupon(code: "WELCOME2024") {
      id
      code
      isExhausted
      expireAt
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "expireCoupon": {
        "id": 1,
        "code": "WELCOME2024",
        "isExhausted": true,
        "expireAt": "2024-06-15T10:30:00Z"
      }
    }
  }
  ```
</CodeGroup>

#### 에러 처리

<ResponseField name="DATA_NOT_FOUND_ON_ID">
  해당 코드의 쿠폰을 찾을 수 없습니다.
</ResponseField>

<Note>
  만료된 쿠폰은 더 이상 사용할 수 없으며, `isExhausted`가 true로 설정되고 `expireAt`이 현재 시각으로 변경됩니다.
</Note>

## 사용 흐름

### 쿠폰 발급 및 사용 흐름

1. **쿠폰 발급**: `issueCoupon`으로 쿠폰 생성 (관리자)
2. **쿠폰 배포**: 생성된 쿠폰 코드를 고객에게 제공
3. **쿠폰 적용**: 고객이 결제 시 `applyCoupon`으로 할인 금액 계산
4. **결제 처리**: 할인된 금액으로 결제 진행
5. **사용 완료**: 결제 완료 시 `usageHash`로 사용 상태 업데이트

### 쿠폰 재적용 흐름 (결제 취소 시)

1. **기존 쿠폰 무효화**: `usageHashMask`에 이전 `usageHash` 전달
2. **새로운 사용 이력 생성**: 새로운 `usageHash` 발급
3. **재결제**: 새로운 할인 금액으로 결제 진행

## 쿠폰 유형

### 할인 유형 (discountType)

* **FIXED**: 정액 할인 (예: 10,000원 할인)
* **PERCENT**: 정률 할인 (예: 10% 할인, 최대 5,000원)

### 사용 제한

* **maxUsableCount**: 쿠폰당 최대 사용 가능 횟수
* **minUsableAmount**: 최소 구매 금액 제한
* **expireAt**: 쿠폰 만료일
* **mask**: 특정 조건 제한 (JSON 형식)
* **accommodationId**: 특정 숙소 전용 쿠폰

## 관련 API

* [예약 API](/api-reference/core-svc/reservation) - 예약 시 쿠폰 적용
* [결제 API](/api-reference/core-svc/payment) - 쿠폰 할인 후 결제 처리
* [인증 API](/api-reference/core-svc/auth) - 관리자 권한 인증
