---
title: "API 규격서"
site: "Game8 Store – Developer Center"
lang: "ko"
category: "기술 사양 & API"
canonical: "https://developers.store.game8.jp/ko/api-v5/"
---

# API 규격서

📝 본 페이지는 참고용 번역(한국어)입니다. 일본어 원본과 차이가 있는 경우 일본어판을 우선합니다.

본 문서에서는 Game8 Store와 연동하기 위한 API 사양을 설명합니다.

## API 목록

### 퍼블리셔 측에서 구현해 주시는 API

Game8 Store에서 요청이 전송됩니다.

| API명 / API Name | 엔드포인트 / Endpoint | 메서드 / Method | 필수 / Required | 설명 / Description |
| --- | --- | --- | --- | --- |
| **아이템 구매 가능 여부 확인** / Purchase Eligibility Check | `/check` | `GET` | 필수 | 사용자가 지정 아이템을 구매할 수 있는지 확인 |
| **아이템 구매 등록** / Register Purchase | `/register` | `POST` | 필수 | 구매 완료 후 게임 내 아이템을 지급 |
| **플레이어 정보 조회 / Retrieve Player Information** | `/user_info` | `GET` | 필수 | 게임 내 ID를 기반으로 플레이어 이름과 레벨 정보를 조회. |
| 재고 확인 / Inventory Check | `/stock` | `GET` | 권장 | 지정 아이템의 재고 상황을 확인 |

#### Game8 Store 측에서 제공하는 API

퍼블리셔 측에서 Game8 Store로 요청을 전송합니다.

| API명 / API Name | 엔드포인트 / Endpoint | 메서드 / Method | 필수 / Required | 설명 / Description |
| --- | --- | --- | --- | --- |
| **주문 조회** / Order Verify | `/orders/verify` | `GET` | 선택 | `paid_transaction_id` 를 지정하여 주문 상태를 조회 |

## 1. 공통 사양

| 항목 | 내용 |
| --- | --- |
| 호스트 | Game8 제공 API: `<Game8가 지정>`<br>퍼블리셔 제공 API:`<퍼블리셔가 지정>` |
| 프로토콜 | HTTPS |
| 문자 인코딩 | UTF-8 |

### 1.1. 요청 사양

#### 요청 헤더

| 키 | 값 | 내용 |
| --- | --- | --- |
| Authorization | Bearer `<ACCESS_TOKEN>` | 인증 정보 |
| Content-Type | application/json | 콘텐츠 타입 |
| X-Signature | `<SIGNED_DATA>` | 서명 검증용 서명 |

> 액세스 토큰은 게임별로 발급된 영숫자(대문자·소문자 포함)를 포함하는 16〜36자의 랜덤 문자열이어야 합니다.
>
> Game8, Inc.에서 생성하여 전달해 드립니다. 직접 생성하시는 것도 가능하므로 원하시는 경우 문의해 주시기 바랍니다.

### 1.2. 응답 사양

#### 공통 응답 헤더

| 키 | 값 |
| --- | --- |
| Content-Type | application/json |

#### 공통 응답 바디

| 키 | 값 | 내용 |
| --- | --- | --- |
| request_id | `string` | 고유한 요청 ID. 트러블슈팅 등에서 요청을 추적할 때 사용. |
| timestamp | `string (ISO8601)` | 응답 타임스탬프 |
| result_code | `string` | 결과 코드 |
| message | `string` | 메시지 |

> [에러 코드 목록](https://developers.store.game8.jp/ko/api-v5/#337bef2a-13a2-8102-b2d0-c0c36f98ad7e) 에 기재된 에러가 발생한 경우, 공통 응답 바디의 항목만 반환.

### X-Signature 설정

요청이 정상적인 것인지 판별하기 위해, 게임별로 발급된 시크릿 키를 사용하여 서명하고 요청 헤더에 설정합니다.

#### 서명 설정 방법

1. GET의 경우 쿼리 문자열, POST의 경우 요청 바디의 내용으로 다이제스트를 계산합니다. 다이제스트는 시크릿 키의 시크릿을 이용하여 HMAC-SHA256으로 산출합니다.

> 다이제스트 계산에 사용하는 메시지 예
>
> GET의 경우：game=game123&user=user456&…
>
> POST의 경우：{\”game\”:\”game123\”,\”user\”:\”user456\”,…}

1. 산출한 다이제스트를 Base64로 인코딩하여 요청 헤더의 X-Signature에 설정합니다.

#### 서명 검증 방법

1. GET의 경우 쿼리 문자열, POST의 경우 요청 바디의 내용으로 다이제스트를 계산합니다. 다이제스트는 시크릿 키의 시크릿을 이용하여 HMAC-SHA256으로 산출합니다.

> 다이제스트 계산에 사용하는 메시지 예
>
> GET의 경우：game=game123&user=user456&…
>
> POST의 경우：{\”game\”:\”game123\”,\”user\”:\”user456\”,…}

1. 산출한 다이제스트를 Base64로 인코딩하여 요청 헤더의 X-Signature 내용과 일치하는지 검증합니다.
  1. 일치하는 경우 정상적인 요청으로 처리를 수행합니다.
  2. 불일치하는 경우 부정한 요청으로 간주하여 에러를 반환합니다.

#### 시크릿 키 취득 방법

시크릿 키는 Game8, Inc.에서 생성하여 공유해 드립니다.

## 2. 퍼블리셔 측에서 구현해 주시는 API 상세

### 2.1. 아이템 구매 가능 여부 확인（GET /check）`필수`

#### 개요

게임 내 아이템을 구매할 수 있는지 확인하고, 연령 제한이나 구매 상한을 고려한 결과를 반환.
또한 수량 한정 등의 아이템 구매 시 결제까지의 시간 차로 인한 구매 중단을 피하기 위해, transaction id를 설정하여 대상 아이템을 확보.

> 선물 캠페인을 실시할 때는 0엔 상품에 대해 연령 인증 없이도 아이템을 구매 가능한 것으로 판정해 주시도록 요청드리는 경우가 있습니다.

#### 요청 사양

| 항목 | 내용 |
| --- | --- |
| 메서드 | GET |
| 엔드포인트 | `/check` |
| 프로토콜 | HTTPS |
| 콘텐츠 타입 | application/json |

#### 요청 헤더

| 키 | 값 | 내용 |
| --- | --- | --- |
| Authorization | Bearer `<ACCESS_TOKEN>` | 인증 정보 |
| Content-Type | application/json | 콘텐츠 타입 |
| X-Signature | `<SIGNED_DATA>` | 서명 검증용 서명 |

#### 쿼리 파라미터

| 키 | 타입 | 필수 | 설명 |
| --- | --- | --- | --- |
| game | string | ✅ | Game8 Store 내에서 발급된 게임의 고유 ID |
| user | string | ✅ | 게임 내에서 발급된 게임 내 사용자 고유 ID |
| transaction_id | string | ✅ | 거래를 고유하게 식별하는 식별자 |
| item | string | ✅ | 게임 내에서 발급된, 구매 대상 아이템 ID （[？](https://developers.store.game8.jp/ko/faq/#acc-faq-523efe3d)） |
| item_category | string | ✅ | 구매 대상 아이템의 종류<br>`paid` : 유료 상품（일반 과금 아이템）<br>`free` : 무료 상품（선물·캠페인 배포품 등 가격이 0엔인 상품） |
| price | integer | ✅ | 구매 대상 아이템의 상품 금액（세금 포함·엔）. 할인 적용 전 정가. |
| selling_price | integer | ✅ | 퍼블리셔 부담의 할인 적용 후 구매 대금（세금 포함·엔）. 할인이 없는 경우 price와 동일한 금액. |
| billing_amount | integer | ✅ | 구매 대금에서 포인트나 쿠폰 사용 등을 차감한 최종 결제 금액（세금 포함·엔）. 적용이 없는 경우 selling_price와 동일한 금액. |

#### 요청 예

```json
GET /check HTTP/1.1
Host: <퍼블리셔 임의>
Authorization: Bearer <ACCESS_TOKEN>
Content-Type: application/json
X-Signature: <SIGNED_DATA>
Query Parameter:
  ?game=game123&user=user456&transaction_id=txn_123456&item=item789&item_category=paid&price=500
```

#### 응답 사양

#### 응답 바디

| 키 | 타입 | 설명 |
| --- | --- | --- |
| request_id | string | 고유한 요청 ID |
| timestamp | string (ISO8601) | 응답 타임스탬프 |
| result_code | string | 결과 코드 |
| message | string | 메시지 |
| purchasable | string | 구매 가능：`available`<br>구매 불가：`unavailable`<br>점검 중：`maintenance` |
| age_category | string | 사용자의 연령 구분. 예를 들어 `under_15` / `under_18` / `adult` / `under_17` / `over_17` 등이 있으며, 필요에 따라 다른 값을 설정할 수 있습니다. |
| account_limit | Object \| null | 계정의 구매 한도액. 구매 한도액이 없으면 null |
| stock | Object \| null | 재고 정보. 무제한 재고인 경우 null |
| requested_price | integer | 요청된 아이템의 가격（엔） |

#### account_limit의 구조

| 키 | 타입 | 설명 |
| --- | --- | --- |
| total | integer | 구매 한도액（엔） |
| remaining | integer | 남은 구매 가능 금액（엔） |
| reset_at | string (ISO8601) \| null | 구매 한도 해제 예정 일시. 미정인 경우 null |

#### stock의 구조

| 키 | 타입 | 설명 |
| --- | --- | --- |
| total | integer | 총 재고 수 |
| remaining | integer | 남은 재고 수 |
| restock_interval | string | 일 단위：`daily`<br>주 단위：`weekly`<br>월 단위： `monthly`<br>입고 예정이 미정인 경우 null |
| restock_at | string (ISO8601) \| null | 다음 입고 예정 일시（예정이 있는 경우）. 미정 또는 무제한인 경우 null |

#### 응답 예（구매 가능）

```json
{
  "request_id": "abc123",
  "timestamp": "2025-02-04T12:34:56Z",
  "result_code": "PUB0000",
  "message": "구매 가능",
  "purchasable": "available",
  "age_category": "under_15",
  "account_limit": {
    "total": 10000,
    "remaining": 9000,
    "reset_at": "2025-03-01T00:00:00Z"
  },
  "stock": {
    "total": 10,
    "remaining": 9,
    "restock_interval": "daily",
    "restock_at": "2025-02-05T00:00:00Z"
  },
  "requested_price": 500
}
```

#### 응답 예（구매 불가 - 연령 인증 미제출）

```json
{
  "request_id": "abc124",
  "timestamp": "2025-02-04T12:35:00Z",
  "result_code": "PUB2006",
  "message": "연령 인증이 완료되지 않아 구매할 수 없습니다.",
  "purchasable": "unavailable",
  "age_category": "unset",
  "account_limit": null,
  "stock": null,
  "requested_price": 500
}
```

#### 응답 예（구매 불가 - 구매 한도 초과）

```json
{
  "request_id": "abc125",
  "timestamp": "2025-02-04T12:36:00Z",
  "result_code": "PUB2005",
  "message": "구매 한도의 상한에 도달했습니다.",
  "purchasable": "unavailable",
  "age_category": "under_18",
  "account_limit": {
    "total": 10000,
    "remaining": 0,
    "reset_at": "2025-03-01T00:00:00Z"
  },
  "stock": {
    "total": 10,
    "remaining": 9,
    "restock_interval": "daily",
    "restock_at": "2025-02-05T00:00:00Z"
  },
  "requested_price": 500
}
```

#### 응답 예（구매 불가 - 아이템 미판매）

```json
{
  "request_id": "abc125",
  "timestamp": "2025-02-04T12:36:00Z",
  "result_code": "PUB2007",
  "message": "대상 아이템은 판매 불가 상태입니다.",
  "purchasable": "unavailable",
  "age_category": "under_18",
  "account_limit": {
    "total": 10000,
    "remaining": 9000,
    "reset_at": "2025-03-01T00:00:00Z"
  },
  "stock": {
    "total": 10,
    "remaining": 9,
    "restock_interval": "daily",
    "restock_at": "2025-02-05T00:00:00Z"
  },
  "requested_price": 500
}
```

#### 응답 예（구매 불가 - 점검 중）

```json
{
  "request_id": "abc125",
  "timestamp": "2025-02-04T12:36:00Z",
  "result_code": "PUB6000",
  "message": "서비스 점검 중입니다.",
  "purchasable": "maintenance",
  "age_category": "under_18",
  "account_limit": {
    "total": 10000,
    "remaining": 9000,
    "reset_at": "2025-03-01T00:00:00Z"
  },
  "stock": {
    "total": 10,
    "remaining": 9,
    "restock_interval": "daily",
    "restock_at": "2025-02-05T00:00:00Z"
  },
  "requested_price": 500
}
```

### 2.2. 아이템 구매 실적 등록（POST /register）`필수`

#### 개요

구매 완료 후 게임 내 아이템을 증가시키기 위한 처리.

> 구매 실적을 등록하기 전에 다음 처리를 추가하는 것을 권장합니다.
>
> - 아이템 구매 가능 여부 확인 API를 거친 transaction_id를 포함하는 요청만 수락한다
> - 구매 한도에 저촉되지 않는지 검증한다

> 선물 캠페인을 실시할 때는 0엔 상품에 대해 연령 인증 없이도 아이템을 구매 가능한 것으로 판정해 주시도록 요청드리는 경우가 있습니다.

#### 요청 사양

| 항목 | 내용 |
| --- | --- |
| 메서드 | POST |
| 엔드포인트 | `/register` |
| 프로토콜 | HTTPS |
| 콘텐츠 타입 | application/json |

#### 요청 헤더

| 키 | 값 | 내용 |
| --- | --- | --- |
| Authorization | Bearer `<ACCESS_TOKEN>` | 인증 정보 |
| Content-Type | application/json | 콘텐츠 타입 |
| X-Signature | `<SIGNED_DATA>` | 서명 검증용 서명 |

#### 요청 바디

| 키 | 타입 | 필수 | 설명 |
| --- | --- | --- | --- |
| game | string | ✅ | Game8 Store 내에서 발급된 게임의 고유 ID |
| user | string | ✅ | 게임 내에서 발급된 게임 내 사용자 고유 ID |
| item | string | ✅ | 게임 내에서 발급된, 구매 대상 아이템 ID （[？](https://developers.store.game8.jp/ko/faq/#acc-faq-523efe3d)） |
| transaction_id | string | ✅ | 거래를 고유하게 식별하는 식별자 |
| item_category | string | ✅ | 구매 대상 아이템의 종류<br>`paid` : 유료 상품（일반 과금 아이템）<br>`free` : 무료 상품（선물·캠페인 배포품 등 가격이 0엔인 상품） |
| item_name | string | ✅ | 구매 대상 아이템의 명칭（예: 젬 100개 팩） |
| price | integer | ✅ | 구매 대상 아이템의 상품 금액（세금 포함·엔）. 할인 적용 전 정가. |
| selling_price | integer | ✅ | 퍼블리셔 부담의 할인 적용 후 구매 대금（세금 포함·엔）. 할인이 없는 경우 price와 동일한 금액. |
| billing_amount | integer | ✅ | 구매 대금에서 포인트나 쿠폰 사용 등을 차감한 최종 결제 금액（세금 포함·엔）. 적용이 없는 경우 selling_price와 동일한 금액. |
| purchased_at | string (ISO8601) | ✅ | 구매 일시 |
| ref | string \| null | ✅ | 유입 출처의 도메인명（예: game8.jp）. 유입 출처를 알 수 없는 경우 null. |
| payment_method | string \| null | ✅ | 결제 방법. 신용카드는 Card, 그 외에는 결제 서비스명（메루페이, PayPal 등）. 전액 포인트 결제 등 결제 서비스를 경유하지 않는 경우 null. |
| contents | array | ✅ | 아이템에 포함되는 내용물 목록 |

#### contents 배열의 구조

| 키 | 타입 | 필수 | 설명 |
| --- | --- | --- | --- |
| content_id | string | ✅ | `item` 에 서픽스를 붙인 아이템 내 내용물의 ID를 자동 부여（예：`pack001-1`） |
| content_name | string | ✅ | 아이템 내 내용물의 명칭（예: 젬） |
| quantity | integer | ✅ | 아이템 내 내용물의 개수（예: 1000） |

#### 요청 예

```json
POST /register HTTP/1.1
Host: <퍼블리셔 임의>
Authorization: Bearer <ACCESS_TOKEN>
Content-Type: application/json
X-Signature: <SIGNED_DATA>

{
  "game": "game123",
  "user": "user456",
  "transaction_id": "txn789",
  "item": "pack001",
  "item_category": "paid",
  "item_name": "젬 1000개",
  "price": 5000,
  "selling_price": 5000,
  "billing_amount": 5000,
  "purchased_at": "2025-02-04T12:36:00Z",
  "ref": "game8.jp",
  "payment_method": "Card",
  "contents": [
    {
      "content_id": "pack001-1",
      "content_name": "젬",
      "quantity": 1000
    },
    {
      "content_id": "pack001-2",
      "content_name": "보너스 금화",
      "quantity": 100
    }
  ]
}
```

#### 응답 사양

#### 응답 바디

| 키 | 타입 | 설명 |
| --- | --- | --- |
| request_id | string | 고유한 요청 ID |
| timestamp | string (ISO8601) | 응답 타임스탬프 |
| result_code | string | 결과 코드 |
| message | string | 메시지 |
| item_granted | boolean | 게임 내 아이템이 지급되었는지 (`true` / `false`) |

#### 응답 예（성공）

```json
{
  "request_id": "def456",
  "timestamp": "2025-02-04T12:36:00Z",
  "result_code": "PUB0000",
  "message": "구매 실적을 등록했습니다.",
  "item_granted": true
}
```

#### 응답 예（실패 - 사용자 불명）

```json
{
  "request_id": "def458",
  "timestamp": "2025-02-04T12:38:00Z",
  "result_code": "PUB3002",
  "message": "지정된 사용자는 존재하지 않습니다.",
  "item_granted": false
}
```

#### 응답 예시(실패 - 이미 등록됨)

```json
{
  "request_id": "def458",
  "timestamp": "2025-02-04T12:38:00Z",
  "result_code": "PUB3004",
  "message": "지정된 TransactionID는 이미 등록되어 있습니다.",
  "item_granted": false
}
```

### 2.3. 플레이어 정보 조회（GET /user_info）`필수`

#### 개요

사용자가 입력한 게임 내 ID를 바탕으로 플레이어 이름과 레벨 정보를 조회합니다. 조회한 정보를 구매 확인 화면에 표시함으로써 사용자는 입력한 ID의 정확성과 게임과의 연결이 정상인지 확인할 수 있습니다.

#### 요청 사양

| 항목 | 내용 |
| --- | --- |
| 메서드 | GET |
| 엔드포인트 | `/user_info` |
| 프로토콜 | HTTPS |
| 콘텐츠 타입 | `application/json` |

#### 요청 헤더

| 키 | 값 | 내용 |
| --- | --- | --- |
| Authorization | `Bearer <ACCESS_TOKEN>` | 인증 정보 |
| Content-Type | `application/json` | 콘텐츠 타입 |
| X-Signature | `<SIGNED_DATA>` | 서명 검증용 서명 |

#### 쿼리 파라미터

| 키 | 타입 | 필수 | 설명 |
| --- | --- | --- | --- |
| game | string | ✅ | Game8 Store 내에서 발급된 게임의 고유 ID |
| user | string | ✅ | 게임 내에서 발급된 게임 내 사용자 고유 ID |

#### 요청 예

```json
GET /user_info HTTP/1.1
Host: <퍼블리셔 임의>
Authorization: Bearer <ACCESS_TOKEN>
Content-Type: application/json
X-Signature: <SIGNED_DATA>
Query Parameter:
  ?game=game123&user=Player5678
```

#### 응답 사양

#### 응답 바디

| 키 | 타입 | 필수 | 설명 |
| --- | --- | --- | --- |
| request_id | string | ✅ | 고유한 요청 ID |
| timestamp | string (ISO8601) | ✅ | 응답 타임스탬프 |
| result_code | string | ✅ | 결과 코드 |
| message | string | ✅ | 메시지 |
| user_name | string | ✅ | 플레이어 이름(마스킹 가능) |
| user_level | integer | ✅ | 플레이어 레벨이나 랭크. 게임 내에 해당 정보가 존재하지 않는 경우 -1을 반환합니다. |

> 입력한 게임 내 ID를 바탕으로 다른 플레이어의 이름도 조회할 수 있으므로, 필요에 따라 플레이어 이름을 마스킹해 주십시오.

#### user_name의 마스킹 예시

| 플레이어 이름 | 마스킹한 user_name |
| --- | --- |
| aa | *a |
| aaa | *aa |
| aaaa | **aa |
| aaaaa | ***aa |

#### 응답 예（성공）

```json
{
  "request_id": "abc123",
  "timestamp": "2025-03-12T14:30:00Z",
  "result_code": "PUB0000",
  "message": "조회 성공",
  "user_name": "****rX",
  "user_level": 45
}
```

#### 응답 예시(실패 - 플레이어 ID를 찾을 수 없음)

```json
{
  "request_id": "abc124",
  "timestamp": "2025-03-12T14:31:00Z",
  "result_code": "PUB2004",
  "message": "플레이어 ID를 찾을 수 없습니다."
}
```

#### 응답 예시(실패 - 계정이 BAN 등으로 인해 비활성 상태)

```json
{
  "request_id": "abc124",
  "timestamp": "2025-03-12T14:31:00Z",
  "result_code": "PUB2008",
  "message": "계정 정지로 인해 구매할 수 없습니다."
}
```

#### 응답 예시(실패 - 서버 점검 중)

```json
{
  "request_id": "abc125",
  "timestamp": "2025-03-12T14:32:00Z",
  "result_code": "PUB6000",
  "message": "서비스 점검 중입니다."
}
```

### 2.4. 재고 확인（GET /stock）`권장`

#### 개요

지정된 아이템의 재고 상황을 확인하고 구매 가능한 잔여 수량을 반환합니다.
수량 한정 상품이나 캠페인 상품 등 재고 관리가 필요한 아이템에 대해, 사용자에게 재고 상황을 표시하거나 구매 플로우 시작 전 사전 확인에 사용합니다.

#### 요청 사양

| 항목 | 내용 |
| --- | --- |
| 메서드 | GET |
| 엔드포인트 | `/stock` |
| 프로토콜 | HTTPS |
| 콘텐츠 타입 | `application/json` |

#### 요청 헤더

| 키 | 값 | 내용 |
| --- | --- | --- |
| Authorization | `Bearer <ACCESS_TOKEN>` | 인증 정보 |
| Content-Type | `application/json` | 콘텐츠 타입 |
| X-Signature | `<SIGNED_DATA>` | 서명 검증용 서명 |

#### 쿼리 파라미터

| 키 | 타입 | 필수 | 설명 |
| --- | --- | --- | --- |
| game | string | ✅ | Game8 Store 내에서 발급된 게임의 고유 ID |
| user | string | ✅ | 게임 내에서 발급된 게임 내 사용자 고유 ID |
| transaction_id | string | ✅ | 거래를 고유하게 식별하는 식별자 |
| items | string | ✅ | 게임 내에서 발급된, 구매 대상 아이템 ID （[？](https://developers.store.game8.jp/ko/faq/#acc-faq-523efe3d)）<br>쉼표로 구분하여 복수 지정 |

#### 요청 예

```json
GET /stock HTTP/1.1
Host: <퍼블리셔 임의>
Authorization: Bearer <ACCESS_TOKEN>
Content-Type: application/json
X-Signature: <SIGNED_DATA>
Query Parameter:
	?game=game123&user=user456&transaction_id=txn_123456&items=item789,item012
```

#### 응답 사양

#### 응답 바디

| 키 | 타입 | 설명 |
| --- | --- | --- |
| request_id | string | 고유한 요청 ID |
| timestamp | string (ISO8601) | 응답 타임스탬프 |
| result_code | string | 결과 코드 |
| message | string | 메시지 |
| stock | Object[] | 재고 정보 |

#### stock의 구조

| 키 | 타입 | 설명 |
| --- | --- | --- |
| item | string | 게임 내에서 발급된, 구매 대상 아이템 ID （[？](https://developers.store.game8.jp/ko/faq/#acc-faq-523efe3d)） |
| total | integer | 총 재고 수. 무제한인 경우 -1을 반환 |
| remaining | integer | 남은 재고 수. 무제한인 경우 -1을 반환 |
| restock_at | string (ISO8601) \| null | 다음 입고 예정 일시（예정이 있는 경우）. 미정 또는 무제한인 경우 null |

#### 응답 예시(재고 있음)

```json
{
	"request_id": "abc123",
	"timestamp": "2025-02-04T12:34:56Z",
	"result_code": "PUB0000",
	"message": "성공",
	"stock": [
	  {
		  "item": "item789",
		  "total": 10,
		  "remaining": 8,
		  "restock_at": null
	  }
	]
}
```

#### 응답 예시(재고 없음)

```json
{
	"request_id": "abc124",
	"timestamp": "2025-02-04T12:35:00Z",
	"result_code": "PUB2009",
	"message": "아이템 구매 수량 제한의 상한에 도달함",
	"stock": [
	  {
		  "item": "item789",
		  "total": 10,
		  "remaining": 0,
		  "restock_at": null
	  }
	]
}
```

#### 응답 예시(무제한 재고)

```json
{
	"request_id": "abc124",
	"timestamp": "2025-02-04T12:36:00Z",
	"result_code": "PUB0000",
	"message": "성공",
	"stock": [
	  {
	    "item": "item789",
		  "total": -1,
		  "remaining": -1,
		  "restock_at": null
	  }
	]
}
```

## 3. Game8 Store 측에서 제공하는 API 상세

### 주문 조회（GET /orders/verify）`사용 선택`

#### 개요

`/register` API로 통지한 주문의 상태를 `paid_transaction_id` 를 사용하여 조회하기 위한 API입니다.
`/register` API에서 사용한 `transaction_id` 와 동일한 값을 `paid_transaction_id` 로 지정해 주십시오.
다음과 같은 사용 사례를 상정하고 있습니다:

- `/register` 수신 후 주문 상태 확인
- API 호출 실패 시 재조회

이를 통해 **통지(/register) + 조회(/orders/verify)**의 이중 확인이 가능해집니다.

본 API는 Game8 Store 측에서 상시 가동되고 있으며, 사전 신청은 필요하지 않습니다. 퍼블리셔 측에서 엔드포인트를 준비할 필요는 없으며, 이용하는 경우 본 API를 호출하는 처리만 구현하시면 됩니다. 결제 플로우에 포함하지 않은 경우에도 요청을 전송하면 주문의 결제 상태를 확인할 수 있습니다(이용은 임의입니다).

#### 요청 사양

| 항목 | 내용 |
| --- | --- |
| 메서드 | GET |
| 엔드포인트 | `/api/public/marketplace/publishers/{publisher_slug}/games/{game_slug}/orders/verify` |
| 프로토콜 | HTTPS |
| 콘텐츠 타입 | application/json |

> `{publisher_slug}` 는 퍼블리셔의 식별자, `{game_slug}` 는 게임의 식별자입니다. Game8, Inc.에서 알려드립니다.

#### 요청 헤더

| 키 | 값 | 내용 |
| --- | --- | --- |
| Authorization | Bearer `<ACCESS_TOKEN>` | 인증 정보 |
| X-Signature | `<SIGNED_DATA>` | 서명 검증용 서명 |

> 서명 생성 방법은 '1.3. X-Signature 설정'과 동일합니다. GET 요청이므로 쿼리 문자열을 서명 대상으로 합니다.

#### 쿼리 파라미터

| 키 | 타입 | 필수 | 설명 |
| --- | --- | --- | --- |
| paid_transaction_id | string | ✅ | `/register` API에서 사용한 `transaction_id` 와 동일한 값. 영숫자, 하이픈, 언더스코어만 사용할 수 있습니다. |

#### 요청 예

```
GET /api/public/marketplace/publishers/example-publisher/games/example-game/orders/verify HTTP/1.1
Host: <Game8가 지정>
Authorization: Bearer <ACCESS_TOKEN>
X-Signature: <SIGNED_DATA>
Query Parameter:
  ?paid_transaction_id=txn_123456
```

#### 응답 사양

#### 응답 본문(성공 시)

| 키 | 타입 | 설명 |
| --- | --- | --- |
| request_id | string | 고유한 요청 ID |
| timestamp | string (ISO8601) | 응답 타임스탬프 |
| result_code | string | 결과 코드 |
| message | string | 메시지 |
| paid_transaction_id | string | 주문의 트랜잭션 ID |
| status | string | 주문 상태(아래 참조) |
| item_id | string | 퍼블리셔 측 아이템 ID |
| price | integer | 구매 대상 아이템의 상품 금액（세금 포함·엔）. 할인 적용 전 정가. |
| selling_price | integer | 퍼블리셔 부담의 할인 적용 후 구매 대금（세금 포함·엔）. 할인이 없는 경우 price와 동일한 금액. |
| billing_amount | integer | 구매 대금에서 포인트나 쿠폰 사용 등을 차감한 최종 결제 금액（세금 포함·엔）. 적용이 없는 경우 selling_price와 동일한 금액. |
| purchased_at | string (ISO8601) \| null | 구매 일시. 결제 처리 중(`pending`)인 경우 null |

#### status의 값

| 값 | 설명 |
| --- | --- |
| `pending` | 결제 처리 중 |
| `payment_confirmed` | 결제 확인 완료(결제는 완료되었으나 아이템 지급 처리가 아직 완료되지 않은 상태) |
| `completed` | 완료(아이템 지급 완료) |
| `cancelled` | 취소 완료 |

#### 응답 예시(성공 - 완료된 주문)

```json
{
  "request_id": "abc123",
  "timestamp": "2025-04-01T12:00:00+09:00",
  "result_code": "PUB0000",
  "message": "성공",
  "paid_transaction_id": "txn_123456",
  "status": "completed",
  "item_id": "item789",
  "price": 1000,
  "selling_price": 900,
  "billing_amount": 800,
  "purchased_at": "2025-04-01T12:00:00+09:00"
}
```

#### 응답 예시(성공 - 결제 처리 중)

```json
{
  "request_id": "abc124",
  "timestamp": "2025-04-01T12:00:01+09:00",
  "result_code": "PUB0000",
  "message": "성공",
  "paid_transaction_id": "txn_123456",
  "status": "pending",
  "item_id": "item789",
  "price": 1000,
  "selling_price": 1000,
  "billing_amount": 1000,
  "purchased_at": null
}
```

#### 오류 응답

오류 발생 시에는 공통 응답 본문만 반환됩니다:

```json
{
  "request_id": "<요청ID>",
  "timestamp": "<타임스탬프>",
  "result_code": "<오류 코드>",
  "message": "<오류 메시지>"
}
```

| HTTP 상태 | result_code | 메시지 | 설명 |
| --- | --- | --- | --- |
| 400 | `PUB2001` | 필수 파라미터가 누락되었습니다 / 파라미터가 올바르지 않습니다 | `paid_transaction_id` 가 미지정, 포맷 오류 또는 경로 식별자가 올바르지 않음 |
| 401 | `PUB1002` | 인증 또는 서명 검증에 실패했습니다 | 액세스 토큰 또는 서명이 올바르지 않음 |
| 403 | `PUB1003` | 다른 퍼블리셔·다른 게임의 주문에는 접근할 수 없습니다 | 자사 이외의 주문에 대한 조회 |
| 404 | `PUB3001` | 지정된 주문을 찾을 수 없습니다 | 해당하는 주문이 존재하지 않음 |
| 429 | - | Rate limit exceeded. Please try again later. | 레이트 리밋 초과(분당 60요청). 응답 본문은 `{"error": "..."}` 형식 |
| 500 | `PUB4000` | 예기치 않은 오류가 발생했습니다 | 서버 내부 오류 |
| 503 | `PUB4001` | 서비스에 연결할 수 없습니다 | 일시적인 네트워크 장애. 시간을 두고 다시 시도해 주십시오 |

#### 응답 예시(오류 - 인증 실패)

```json
{
  "request_id": "abc125",
  "timestamp": "2025-04-01T12:00:02+09:00",
  "result_code": "PUB1002",
  "message": "인증 또는 서명 검증에 실패했습니다"
}
```

#### 응답 예시(오류 - 주문을 찾을 수 없음)

```json
{
  "request_id": "abc126",
  "timestamp": "2025-04-01T12:00:03+09:00",
  "result_code": "PUB3001",
  "message": "지정된 주문을 찾을 수 없습니다"
}
```

## 4. 에러 핸들링

### 4.1. 에러 코드 목록

| 오류 코드 | 설명 | 대응 방법 |
| --- | --- | --- |
| `PUB0000` | 성공 | - |
| `PUB1000` | 잘못된 요청 포맷 | 요청 구조와 필수 파라미터를 확인하고 올바른 형식으로 전송하십시오 |
| `PUB1001` | 인증 정보가 누락됨 | `Authorization` 헤더에 올바른 `Bearer <ACCESS_TOKEN>` 를 설정하십시오 |
| `PUB1002` | 유효하지 않은 액세스 토큰 | 액세스 토큰의 유효 기간을 확인하고 필요에 따라 재발급하십시오 |
| `PUB1003` | 접근 권한이 부족함 | API 엔드포인트에 대한 적절한 권한이 있는지 확인하십시오 |
| `PUB1004` | 서명 내용 불일치 | 서명에 사용하는 시크릿 키의 시크릿 값이 올바른지 확인하십시오 |
| `PUB1005` | 이미 처리된 요청 | 중복된 `request_id` 를 전송하지 않도록 하십시오 |
| `PUB2001` | 필수 파라미터가 누락됨 | 필수 파라미터를 확인하고 요청을 수정하십시오 |
| `PUB2002` | 파라미터 타입이 유효하지 않음 | 예: `price` 에 `string` 를 전달하고 있는 경우 정수 값으로 수정하십시오 |
| `PUB2003` | 파라미터 값이 올바르지 않음 | 허용되지 않은 값이나 범위를 벗어난 값을 전송하고 있지 않은지 확인하십시오 |
| `PUB2004` | 해당하는 데이터가 존재하지 않음 | 존재하는 `game` 나 `user` 를 지정하고 있는지 확인하십시오 |
| `PUB2005` | 연령에 따른 구매 금액 한도의 상한에 도달함 | `account_limit.remaining` 의 값을 확인하고 한도를 초과하지 않았는지 확인하십시오 |
| `PUB2006` | 연령 인증이 이루어지지 않음 | 사용자에게 게임 내에서 연령 인증을 진행하도록 안내하십시오 |
| `PUB2007` | 아이템 판매 중지 | 스토어 측 판매 아이템의 조정에 대해 영업·개발팀에 문의하십시오 |
| `PUB2008` | 계정 정지로 인해 구매 불가 | - |
| `PUB2009` | 아이템 구매 수량 제한의 상한에 도달함 | 구매 이력을 확인하고 제한을 초과하지 않았는지 확인하십시오 |
| `PUB2010` | 이전 거래가 완료되지 않아 일시적으로 구매할 수 없는 상태 | 미완료 거래는 Game8 Store 측에서 일정 시간 후 자동으로 취소되어 구매 가능한 상태로 돌아갑니다. 사용자에게 시간을 두고 다시 구매하도록 안내하십시오 |
| `PUB3001` | 유효하지 않은 거래 ID | `transaction_id` 가 올바른지 확인하십시오 |
| `PUB3002` | 지정된 사용자가 존재하지 않음 | `user` 가 올바른지 확인하십시오 |
| `PUB3003` | 지정된 아이템이 존재하지 않음 | `item` 를 확인하고 올바른 값을 지정하십시오 |
| `PUB3004` | 이미 등록된 거래 ID | `transaction_id` 가 올바른지 확인하십시오 |
| `PUB4000` | 서버 내부 오류 | 서버 측 문제일 가능성이 있으므로 일정 시간 후 다시 시도하십시오 |
| `PUB4001` | 일시적인 시스템 장애 | 다시 시도하십시오(권장: 5초 후 재시도) |
| `PUB5000` | 알 수 없는 오류 | 상세한 오류 메시지를 확인하고 개발팀에 문의하십시오 |
| `PUB6000` | 점검 중 | 서비스 중단 상태로 간주하고 일정 시간 후 다시 시도하십시오 |

### 4.2. 에러 핸들링 권장 플로우

1. **`result_code`****를 확인**
  - `PUB0000`(성공)이면 정상 처리합니다.
  - 그 외의 경우 오류의 종류를 판정합니다.
2. **클라이언트 측에서 수정 가능한 오류인지 확인**
  - `PUB1000` 계열, `PUB2000` 계열은 요청 내용 수정으로 대응할 수 있습니다.
  - `PUB3000` 계열은 데이터의 정합성을 확인합니다.
3. **재시도 가능한 오류인지 확인**
  - `PUB4000`, `PUB4001` 는 일시적인 오류일 가능성이 있으므로 몇 초 후 재시도합니다.
4. **오류 메시지 기록·통지**
  - 알 수 없는 오류(`PUB5000`)는 로그를 남기고 개발팀에 보고합니다.

### 4.3. 재시도 정책

| 오류 코드 | 재시도 간격 | 최대 재시도 횟수 | 비고 |
| --- | --- | --- | --- |
| `PUB4000` | 5초 후 | 3회 | 서버 내부 오류이므로 짧은 시간 내에 해소될 가능성이 있음 |
| `PUB4001` | 10초 후 | 5회 | 일시적인 시스템 장애이므로 비교적 긴 간격을 둠 |
| `PUB5000` | 없음 | 0회 | 알 수 없는 오류는 즉시 보고 |

이 오류 처리 목록을 참고하여 적절한 오류 처리를 구현해 주십시오.
