{ "item": [ { "name": "결제 보고", "description": "", "item": [ { "id": "2239aa26-a2bc-47ea-883d-f0e17175fee3", "name": "결제 취소", "request": { "name": "결제 취소", "description": { "content": "등록된 결제를 취소합니다. **등록한 앱만 취소 가능**합니다.\n\n- **보류(Pending) 중 취소**: 지갑 변동 없이 취소 처리 (`recoveredAmount=0`)\n- **확정(Confirmed) 후 취소**: 적립된 채널 수수료를 회수. 채널주가 이미 출금해 잔액이\n 부족하면 가용분만 회수하고 부족분을 `shortfallAmount` 로 응답 (DPOT 운영진 검토 대상)\n\n### 필수 scope\n- `write:purchases`\n\n### 인증 제한\n- **OAuth2 Client Credentials 앱 토큰 전용** — PAT 으로 호출 시 `403`\n\n### 경로/쿼리\n| 이름 | 위치 | 필수 | 설명 |\n|---|---|---|---|\n| `orderID` | path | ✅ | 등록 시 사용한 마켓 거래 ID |\n| `marketplace` | query | 조건 | 동일 orderID 가 여러 마켓에 등록된 경우만 필수 |\n\n### 에러\n- `400` — orderID 누락 / 마켓 모호 (`Purchase.AmbiguousOrderID`)\n- `403` — PAT 호출\n- `404` — 등록 내역 없음 (타 앱 등록 건 포함 — 조회 불가)\n- `409` — 이미 취소됨 (`Purchase.AlreadyCanceled`)", "type": "text/plain" }, "url": { "path": [ "v1", "purchases", ":orderID", "cancel" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "marketplace", "value": "" } ], "variable": [ { "type": "any", "value": "", "key": "orderID", "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" } } ] }, "header": [ { "key": "Accept", "value": "application/json" } ], "method": "POST", "body": {}, "auth": null }, "response": [ { "id": "5ab7271f-c726-4b01-9cd0-caeef0e503de", "name": "OK", "originalRequest": { "url": { "path": [ "v1", "purchases", ":orderID", "cancel" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "marketplace", "value": "" } ], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "orderID" } ] }, "header": [ { "key": "Accept", "value": "application/json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "POST", "body": {} }, "status": "OK", "code": 200, "header": [ { "key": "Content-Type", "value": "application/json" } ], "body": "{\n \"success\": \"\",\n \"data\": \"\",\n \"status\": \"\",\n \"message\": \"\",\n \"errors\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "314d3e68-5a5b-46ca-b739-c1eb5894bc12", "name": "Bad Request", "originalRequest": { "url": { "path": [ "v1", "purchases", ":orderID", "cancel" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "marketplace", "value": "" } ], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "orderID" } ] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "POST", "body": {} }, "status": "Bad Request", "code": 400, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "f56e17e7-4327-49de-9abb-f82143554bfc", "name": "Unauthorized", "originalRequest": { "url": { "path": [ "v1", "purchases", ":orderID", "cancel" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "marketplace", "value": "" } ], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "orderID" } ] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "POST", "body": {} }, "status": "Unauthorized", "code": 401, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "2c92e968-caf3-4791-afb5-c92eda616dbd", "name": "Forbidden", "originalRequest": { "url": { "path": [ "v1", "purchases", ":orderID", "cancel" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "marketplace", "value": "" } ], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "orderID" } ] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "POST", "body": {} }, "status": "Forbidden", "code": 403, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "cba43e1d-8001-4352-b513-298c6931281a", "name": "Not Found", "originalRequest": { "url": { "path": [ "v1", "purchases", ":orderID", "cancel" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "marketplace", "value": "" } ], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "orderID" } ] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "POST", "body": {} }, "status": "Not Found", "code": 404, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "27fcfada-4305-4969-a576-6310ad853d49", "name": "Conflict", "originalRequest": { "url": { "path": [ "v1", "purchases", ":orderID", "cancel" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "marketplace", "value": "" } ], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "orderID" } ] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "POST", "body": {} }, "status": "Conflict", "code": 409, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" } ], "event": [], "protocolProfileBehavior": { "disableBodyPruning": true } }, { "id": "e89733a2-046d-4af3-b85c-d93a1d4e5d5c", "name": "결제 단건 조회", "request": { "name": "결제 단건 조회", "description": { "content": "등록한 결제의 상태를 조회합니다 (대사용).\n\n- OAuth2 앱 토큰: 해당 앱이 등록한 건만 조회\n- PAT: 본인 소유 앱 전체의 등록 건 조회\n\n### 필수 scope\n- `read:purchases`\n\n### status 값\n| 값 | 의미 |\n|---|---|\n| `Pending` | 보류 — 확정 예정 (`confirmDueAt` 참조) |\n| `Confirmed` | 확정 — 채널 수수료 적립 완료 |\n| `Canceled` | 취소됨 |\n\n### 에러\n- `400` — 여러 건 조회됨 (marketplace 지정 필요)\n- `404` — 등록 내역 없음", "type": "text/plain" }, "url": { "path": [ "v1", "purchases", ":orderID" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "marketplace", "value": "" } ], "variable": [ { "type": "any", "value": "", "key": "orderID", "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" } } ] }, "header": [ { "key": "Accept", "value": "application/json" } ], "method": "GET", "body": {}, "auth": null }, "response": [ { "id": "4f0bb3eb-a443-4fa0-8fcc-46b0d315fedf", "name": "OK", "originalRequest": { "url": { "path": [ "v1", "purchases", ":orderID" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "marketplace", "value": "" } ], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "orderID" } ] }, "header": [ { "key": "Accept", "value": "application/json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "OK", "code": 200, "header": [ { "key": "Content-Type", "value": "application/json" } ], "body": "{\n \"success\": \"\",\n \"data\": \"\",\n \"status\": \"\",\n \"message\": \"\",\n \"errors\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "c5c9a4b2-b362-4cbd-b6f8-203515c053fc", "name": "Bad Request", "originalRequest": { "url": { "path": [ "v1", "purchases", ":orderID" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "marketplace", "value": "" } ], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "orderID" } ] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Bad Request", "code": 400, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "c924fffe-4938-41e3-94ae-5d1cda84ae49", "name": "Unauthorized", "originalRequest": { "url": { "path": [ "v1", "purchases", ":orderID" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "marketplace", "value": "" } ], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "orderID" } ] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Unauthorized", "code": 401, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "34e915c0-d2c7-43dd-ba86-f7c90d6df622", "name": "Forbidden", "originalRequest": { "url": { "path": [ "v1", "purchases", ":orderID" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "marketplace", "value": "" } ], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "orderID" } ] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Forbidden", "code": 403, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "4b2f6521-e569-4ac2-9a40-20d2c03dc264", "name": "Not Found", "originalRequest": { "url": { "path": [ "v1", "purchases", ":orderID" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "marketplace", "value": "" } ], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "orderID" } ] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Not Found", "code": 404, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" } ], "event": [], "protocolProfileBehavior": { "disableBodyPruning": true } }, { "id": "bdfbd1fa-983e-4f0d-a683-0da7ffb1b66f", "name": "결제 목록 조회", "request": { "name": "결제 목록 조회", "description": { "content": "등록한 결제 내역을 페이지네이션해 반환합니다 (정산 대사용).\n\n- OAuth2 앱 토큰: 해당 앱이 등록한 건만\n- PAT: 본인 소유 앱 전체\n\n### 필수 scope\n- `read:purchases`\n\n### 쿼리 파라미터\n| 이름 | 타입 | 기본 | 설명 |\n|---|---|---|---|\n| `status` | string? | 전체 | `pending` / `confirmed` / `canceled` |\n| `from` | datetime? | — | 등록 시각(UTC) 시작 |\n| `to` | datetime? | — | 등록 시각(UTC) 끝 |\n| `page` | int | 1 | 1부터 시작 |\n| `size` | int | 50 | 1~100 |\n\n### 사용 예\n```\nGET /v1/purchases?status=pending\nGET /v1/purchases?from=2026-06-01&to=2026-06-30&page=1&size=100\n```", "type": "text/plain" }, "url": { "path": [ "v1", "purchases" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "status", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "from", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "to", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "page", "value": "1" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "size", "value": "50" } ], "variable": [] }, "header": [ { "key": "Accept", "value": "application/json" } ], "method": "GET", "body": {}, "auth": null }, "response": [ { "id": "60037008-dc1a-4785-a34b-6a1732faf6e3", "name": "OK", "originalRequest": { "url": { "path": [ "v1", "purchases" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "status", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "from", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "to", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "page", "value": "1" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "size", "value": "50" } ], "variable": [] }, "header": [ { "key": "Accept", "value": "application/json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "OK", "code": 200, "header": [ { "key": "Content-Type", "value": "application/json" } ], "body": "{\n \"success\": \"\",\n \"data\": \"\",\n \"status\": \"\",\n \"message\": \"\",\n \"errors\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "5b0c3210-2126-47e8-bf4f-0bbe3c533853", "name": "Bad Request", "originalRequest": { "url": { "path": [ "v1", "purchases" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "status", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "from", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "to", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "page", "value": "1" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "size", "value": "50" } ], "variable": [] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Bad Request", "code": 400, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "39274fd0-3817-4935-8733-d77cc71e8438", "name": "Unauthorized", "originalRequest": { "url": { "path": [ "v1", "purchases" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "status", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "from", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "to", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "page", "value": "1" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "size", "value": "50" } ], "variable": [] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Unauthorized", "code": 401, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "413b86f5-648b-434c-b487-e582e36e130c", "name": "Forbidden", "originalRequest": { "url": { "path": [ "v1", "purchases" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "status", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "from", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "to", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "page", "value": "1" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "size", "value": "50" } ], "variable": [] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Forbidden", "code": 403, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" } ], "event": [], "protocolProfileBehavior": { "disableBodyPruning": true } }, { "id": "6fb29de0-41ed-4c36-a668-f31e47a879d3", "name": "결제 등록", "request": { "name": "결제 등록", "description": { "content": "게임 내 결제를 DPOT 에 보고합니다. 검증 통과 시 **보류(Pending) 원장**이 생성되고,\n등록 시점 +14일(달력일, 주말·공휴일 포함) 후 확정 배치가 채널 소유 회원의 지갑에\n판매 수수료(`orderPrice × 게임별 수수료율`)를 적립합니다.\n\n### 필수 scope\n- `write:purchases`\n\n### 인증 제한\n- **OAuth2 Client Credentials 앱 토큰 전용** — PAT 으로 호출 시 `403`\n\n### 요청 본문\n| 필드 | 타입 | 필수 | 설명 |\n|---|---|---|---|\n| `channelCode` | string | ✅ | 채널 후원 코드 (4~7자 영문+숫자, 게임 내 유저 입력값) |\n| `orderID` | string(255) | ✅ | **마켓 거래 ID 원문** — Google `GPA.xxxx-xxxx-xxxx-xxxxx`, Apple transactionId 등. 가공·축약 금지 |\n| `marketplace` | int | ✅ | 1=구글, 2=애플, 3=MS, 4=갤럭시, 5=원스토어, 6=기타 |\n| `gameCode` | string | ✅ | DPOT 에 등록된 게임 코드 |\n| `orderPrice` | int | ✅ | 결제 금액 (KRW, 1 이상) |\n| `productID` | string(100) | 권장 | 인앱 상품 SKU (예: `diamond_100`) — 금액 검증에 사용 |\n| `subID` | string(100) | 선택 | 파트너 측 보조 식별자 |\n\n### 멱등성\n동일 (앱, `marketplace`, `orderID`) 조합은 1회만 등록 가능 — 재시도 시 `409`.\n\n### 응답 (201)\n`commissionAmount`(채널 수수료), `status`(`Pending`), `confirmDueAt`(확정 예정 시각) 포함.\n\n### 에러\n- `400` — 필드 누락/형식 오류\n- `403` — PAT 호출 / 앱 비활성\n- `404` — 후원 코드(`Channel.NotFound`) 또는 게임(`Game.NotFound`) 없음\n- `409` — 중복 주문 (`Purchase.Duplicate`)", "type": "text/plain" }, "url": { "path": [ "v1", "purchases" ], "host": [ "{{baseUrl}}" ], "query": [], "variable": [] }, "header": [ { "key": "Content-Type", "value": "application/json" }, { "key": "Accept", "value": "application/json" } ], "method": "POST", "body": { "mode": "raw", "raw": "{\n \"applicationID\": \"\",\n \"channelCode\": \"\",\n \"orderID\": \"\",\n \"marketplace\": \"\",\n \"gameCode\": \"\",\n \"orderPrice\": \"\",\n \"productID\": \"\",\n \"subID\": \"\"\n}", "options": { "raw": { "headerFamily": "json", "language": "json" } } }, "auth": null }, "response": [ { "id": "53e9f2cd-cf39-410f-bc05-8619b8515267", "name": "Created", "originalRequest": { "url": { "path": [ "v1", "purchases" ], "host": [ "{{baseUrl}}" ], "query": [], "variable": [] }, "header": [ { "key": "Content-Type", "value": "application/json" }, { "key": "Accept", "value": "application/json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "POST", "body": { "mode": "raw", "raw": "{\n \"applicationID\": \"\",\n \"channelCode\": \"\",\n \"orderID\": \"\",\n \"marketplace\": \"\",\n \"gameCode\": \"\",\n \"orderPrice\": \"\",\n \"productID\": \"\",\n \"subID\": \"\"\n}", "options": { "raw": { "headerFamily": "json", "language": "json" } } } }, "status": "Created", "code": 201, "header": [ { "key": "Content-Type", "value": "application/json" } ], "body": "{\n \"success\": \"\",\n \"data\": \"\",\n \"status\": \"\",\n \"message\": \"\",\n \"errors\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "bf904a41-2085-4e61-b50f-47ac95e092fd", "name": "Bad Request", "originalRequest": { "url": { "path": [ "v1", "purchases" ], "host": [ "{{baseUrl}}" ], "query": [], "variable": [] }, "header": [ { "key": "Content-Type", "value": "application/json" }, { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "POST", "body": { "mode": "raw", "raw": "{\n \"applicationID\": \"\",\n \"channelCode\": \"\",\n \"orderID\": \"\",\n \"marketplace\": \"\",\n \"gameCode\": \"\",\n \"orderPrice\": \"\",\n \"productID\": \"\",\n \"subID\": \"\"\n}", "options": { "raw": { "headerFamily": "json", "language": "json" } } } }, "status": "Bad Request", "code": 400, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "cefba167-a556-4563-a261-6a20727efeea", "name": "Unauthorized", "originalRequest": { "url": { "path": [ "v1", "purchases" ], "host": [ "{{baseUrl}}" ], "query": [], "variable": [] }, "header": [ { "key": "Content-Type", "value": "application/json" }, { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "POST", "body": { "mode": "raw", "raw": "{\n \"applicationID\": \"\",\n \"channelCode\": \"\",\n \"orderID\": \"\",\n \"marketplace\": \"\",\n \"gameCode\": \"\",\n \"orderPrice\": \"\",\n \"productID\": \"\",\n \"subID\": \"\"\n}", "options": { "raw": { "headerFamily": "json", "language": "json" } } } }, "status": "Unauthorized", "code": 401, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "95149382-4a6f-4d97-a86f-7b7bfe89bddc", "name": "Forbidden", "originalRequest": { "url": { "path": [ "v1", "purchases" ], "host": [ "{{baseUrl}}" ], "query": [], "variable": [] }, "header": [ { "key": "Content-Type", "value": "application/json" }, { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "POST", "body": { "mode": "raw", "raw": "{\n \"applicationID\": \"\",\n \"channelCode\": \"\",\n \"orderID\": \"\",\n \"marketplace\": \"\",\n \"gameCode\": \"\",\n \"orderPrice\": \"\",\n \"productID\": \"\",\n \"subID\": \"\"\n}", "options": { "raw": { "headerFamily": "json", "language": "json" } } } }, "status": "Forbidden", "code": 403, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "7c7f273c-b788-472c-8002-28217a17b253", "name": "Not Found", "originalRequest": { "url": { "path": [ "v1", "purchases" ], "host": [ "{{baseUrl}}" ], "query": [], "variable": [] }, "header": [ { "key": "Content-Type", "value": "application/json" }, { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "POST", "body": { "mode": "raw", "raw": "{\n \"applicationID\": \"\",\n \"channelCode\": \"\",\n \"orderID\": \"\",\n \"marketplace\": \"\",\n \"gameCode\": \"\",\n \"orderPrice\": \"\",\n \"productID\": \"\",\n \"subID\": \"\"\n}", "options": { "raw": { "headerFamily": "json", "language": "json" } } } }, "status": "Not Found", "code": 404, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "ae95cf15-7ec4-43af-9ad8-1de4e9c318aa", "name": "Conflict", "originalRequest": { "url": { "path": [ "v1", "purchases" ], "host": [ "{{baseUrl}}" ], "query": [], "variable": [] }, "header": [ { "key": "Content-Type", "value": "application/json" }, { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "POST", "body": { "mode": "raw", "raw": "{\n \"applicationID\": \"\",\n \"channelCode\": \"\",\n \"orderID\": \"\",\n \"marketplace\": \"\",\n \"gameCode\": \"\",\n \"orderPrice\": \"\",\n \"productID\": \"\",\n \"subID\": \"\"\n}", "options": { "raw": { "headerFamily": "json", "language": "json" } } } }, "status": "Conflict", "code": 409, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" } ], "event": [], "protocolProfileBehavior": { "disableBodyPruning": true } } ] }, { "name": "상품", "description": "", "item": [ { "id": "df72b170-f811-4e38-bc10-e3ce1d5d9388", "name": "상품 목록 조회", "request": { "name": "상품 목록 조회", "description": { "content": "활성 상품을 페이지네이션해 반환합니다.\r\n\r\n**응답에서 의도적으로 제외되는 항목** (사용자 요구):\r\n- 실물/쿠폰 여부 (`type`) — 별도 표시 없음. 다만 쿠폰형 상품인 경우 `coupon` 필드가 채워져 식별 가능.\r\n- 판매 정책 — 할인(`discountType`/`discountValue`), 판매 기간(`saleStartAt`/`saleEndAt`), 구매 수량 제약(`minPurchase`/`maxPurchase`).\r\n- 상품 정렬 순서 (`order`).\r\n\r\n**응답 포함 항목**: 이름, 설명, 썸네일, 정가, 재고, 활성 여부, 등록일, (쿠폰 상품인 경우) 연결된 쿠폰 정보.\r\n\r\n### 필수 scope\r\n- `read:products`\r\n\r\n### 쿼리 파라미터\r\n| 이름 | 타입 | 기본 | 설명 |\r\n|---|---|---|---|\r\n| `gameID` | int? | null | 게임 ID — 지정 시 해당 게임의 상품만 |\r\n| `page` | int | 1 | 1부터 시작 |\r\n| `size` | int | 50 | 1~100 |\r\n\r\n### 응답 — coupon 필드 (쿠폰 상품에만 채워짐)\r\n```json\r\n{\r\n \"id\": 1,\r\n \"name\": \"리그 오브 레전드 RP 5만 쿠폰\",\r\n \"generatedCount\": 1000,\r\n \"usedCount\": 432,\r\n \"expiresAt\": \"2026-12-31T23:59:59Z\"\r\n}\r\n```", "type": "text/plain" }, "url": { "path": [ "v1", "products" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "gameID", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "page", "value": "1" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "size", "value": "50" } ], "variable": [] }, "header": [ { "key": "Accept", "value": "application/json" } ], "method": "GET", "body": {}, "auth": null }, "response": [ { "id": "957f02d2-35c4-43f0-aebd-f8bb30be3bb9", "name": "OK", "originalRequest": { "url": { "path": [ "v1", "products" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "gameID", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "page", "value": "1" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "size", "value": "50" } ], "variable": [] }, "header": [ { "key": "Accept", "value": "application/json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "OK", "code": 200, "header": [ { "key": "Content-Type", "value": "application/json" } ], "body": "{\n \"success\": \"\",\n \"data\": \"\",\n \"status\": \"\",\n \"message\": \"\",\n \"errors\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "f5e5d627-3777-45dd-b4b6-d31a8cfa3df9", "name": "Unauthorized", "originalRequest": { "url": { "path": [ "v1", "products" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "gameID", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "page", "value": "1" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "size", "value": "50" } ], "variable": [] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Unauthorized", "code": 401, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "83107054-f5c4-450b-b888-dd59ec14e3a4", "name": "Forbidden", "originalRequest": { "url": { "path": [ "v1", "products" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "gameID", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "page", "value": "1" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "size", "value": "50" } ], "variable": [] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Forbidden", "code": 403, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" } ], "event": [], "protocolProfileBehavior": { "disableBodyPruning": true } } ] }, { "name": "채널", "description": "", "item": [ { "id": "3962fe32-27bb-4eec-b569-a5dc2e398c52", "name": "채널 후원 코드 확인", "request": { "name": "채널 후원 코드 확인", "description": { "content": "채널 후원 코드의 존재·활성 여부를 확인합니다.\n게임 내에서 유저가 입력한 코드를 **결제 보고 전에 사전 검증**하는 용도입니다.\n\n### 필수 scope\n- `read:channels`\n\n### 경로 변수\n- `code` — 채널 후원 코드 (4~7자 영문+숫자, 대소문자 무관)\n\n### 응답 필드\n- `channelSID` / `channelName` — 채널 식별 정보\n- `active` — `false` 면 해당 코드로 결제 등록이 거부됩니다\n\n### 에러\n- `404` — 코드 없음", "type": "text/plain" }, "url": { "path": [ "v1", "channels", ":code" ], "host": [ "{{baseUrl}}" ], "query": [], "variable": [ { "type": "any", "value": "", "key": "code", "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" } } ] }, "header": [ { "key": "Accept", "value": "application/json" } ], "method": "GET", "body": {}, "auth": null }, "response": [ { "id": "49a7321f-cec2-44e8-908e-2edb4c912688", "name": "OK", "originalRequest": { "url": { "path": [ "v1", "channels", ":code" ], "host": [ "{{baseUrl}}" ], "query": [], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "code" } ] }, "header": [ { "key": "Accept", "value": "application/json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "OK", "code": 200, "header": [ { "key": "Content-Type", "value": "application/json" } ], "body": "{\n \"success\": \"\",\n \"data\": \"\",\n \"status\": \"\",\n \"message\": \"\",\n \"errors\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "00dfdb94-9246-47c8-b81b-c74824e1d928", "name": "Bad Request", "originalRequest": { "url": { "path": [ "v1", "channels", ":code" ], "host": [ "{{baseUrl}}" ], "query": [], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "code" } ] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Bad Request", "code": 400, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "ed945e67-256c-4db3-a2d4-0dd8a1b7e541", "name": "Unauthorized", "originalRequest": { "url": { "path": [ "v1", "channels", ":code" ], "host": [ "{{baseUrl}}" ], "query": [], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "code" } ] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Unauthorized", "code": 401, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "0a5873d2-2ee9-411a-a905-c3da1b093d67", "name": "Forbidden", "originalRequest": { "url": { "path": [ "v1", "channels", ":code" ], "host": [ "{{baseUrl}}" ], "query": [], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "code" } ] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Forbidden", "code": 403, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "23a2627a-5111-4c17-bf7d-612f702fb52c", "name": "Not Found", "originalRequest": { "url": { "path": [ "v1", "channels", ":code" ], "host": [ "{{baseUrl}}" ], "query": [], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "code" } ] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Not Found", "code": 404, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" } ], "event": [], "protocolProfileBehavior": { "disableBodyPruning": true } } ] }, { "name": "쿠폰", "description": "", "item": [ { "id": "09d68c0e-f279-458f-849d-cc78d81b23b9", "name": "쿠폰 코드 상세 조회", "request": { "name": "쿠폰 코드 상세 조회", "description": { "content": "단일 쿠폰 코드의 **사용 여부 / 상태 / 사용 일시**를 반환합니다.\r\n\r\n- **등록일시는 의도적으로 제공하지 않습니다** (보안/사용자 요구).\r\n- 상태(`status`) + 발급일(`issuedAt`) + 사용일(`usedAt`) 만 노출.\r\n\r\n### 필수 scope\r\n- `read:coupons`\r\n\r\n### 경로 변수\r\n- `code` — 쿠폰 코드 평문 (예: `ABCD-1234-EFGH-5678`)\r\n - 서버 측에서 SHA-256 해시 후 검색하므로 평문이 로그에 남지 않습니다.\r\n\r\n### Status 값\r\n| 값 | 라벨 | 의미 |\r\n|---|---|---|\r\n| `1` | 미발급 | Available — 결제 가능 |\r\n| `2` | 결제 진행 중 | Reserved — 점유됨 |\r\n| `3` | 발급됨 | Issued — 회원 보관함 |\r\n| `4` | 사용 완료 | Used — 사용 처리됨 |\r\n| `5` | 만료 | Expired — 관리자 만료 |\r\n\r\n### 에러\r\n- `400` — code 누락\r\n- `404` — 해당 코드 없음", "type": "text/plain" }, "url": { "path": [ "v1", "coupons", "codes", ":code" ], "host": [ "{{baseUrl}}" ], "query": [], "variable": [ { "type": "any", "value": "", "key": "code", "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" } } ] }, "header": [ { "key": "Accept", "value": "application/json" } ], "method": "GET", "body": {}, "auth": null }, "response": [ { "id": "4a1d814d-a849-4c62-b331-79d7fcfaf669", "name": "OK", "originalRequest": { "url": { "path": [ "v1", "coupons", "codes", ":code" ], "host": [ "{{baseUrl}}" ], "query": [], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "code" } ] }, "header": [ { "key": "Accept", "value": "application/json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "OK", "code": 200, "header": [ { "key": "Content-Type", "value": "application/json" } ], "body": "{\n \"success\": \"\",\n \"data\": \"\",\n \"status\": \"\",\n \"message\": \"\",\n \"errors\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "40f24a12-db70-45db-9ce5-76f7d3055f00", "name": "Bad Request", "originalRequest": { "url": { "path": [ "v1", "coupons", "codes", ":code" ], "host": [ "{{baseUrl}}" ], "query": [], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "code" } ] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Bad Request", "code": 400, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "d0b4f6c7-889c-490b-bfa0-cefed9b851f6", "name": "Unauthorized", "originalRequest": { "url": { "path": [ "v1", "coupons", "codes", ":code" ], "host": [ "{{baseUrl}}" ], "query": [], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "code" } ] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Unauthorized", "code": 401, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "a2bdfeab-14cd-48fe-88df-b92783ed24aa", "name": "Forbidden", "originalRequest": { "url": { "path": [ "v1", "coupons", "codes", ":code" ], "host": [ "{{baseUrl}}" ], "query": [], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "code" } ] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Forbidden", "code": 403, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "a48bf288-4e36-4917-ae50-aa656352607a", "name": "Not Found", "originalRequest": { "url": { "path": [ "v1", "coupons", "codes", ":code" ], "host": [ "{{baseUrl}}" ], "query": [], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "code" } ] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Not Found", "code": 404, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" } ], "event": [], "protocolProfileBehavior": { "disableBodyPruning": true } }, { "id": "0e7a6641-83d2-439e-9f82-bd196079cf33", "name": "게임 쿠폰 코드 대량 조회", "request": { "name": "게임 쿠폰 코드 대량 조회", "description": { "content": "특정 게임에 발급된 **쿠폰 코드를 대량으로** 페이지네이션해 반환합니다.\r\n\r\n각 코드의 사용 여부(`status`) + 발급/사용 일시가 함께 제공됩니다.\r\n\r\n### 필수 scope\r\n- `read:coupons`\r\n\r\n### 경로 변수\r\n- `gameID` — 게임 ID\r\n\r\n### 쿼리 파라미터\r\n| 이름 | 타입 | 기본 | 설명 |\r\n|---|---|---|---|\r\n| `couponID` | int? | null | 특정 쿠폰의 코드만 필터 (생략 시 게임 전체) |\r\n| `page` | int | 1 | 1부터 시작 |\r\n| `limit` | int | 100 | 1~500 |\r\n\r\n### Status 값\r\n| 값 | 의미 |\r\n|---|---|\r\n| `1` | Available (미발급) |\r\n| `2` | Reserved (결제 진행 중) |\r\n| `3` | Issued (발급됨) |\r\n| `4` | Used (사용 완료) |\r\n| `5` | Expired (만료) |", "type": "text/plain" }, "url": { "path": [ "v1", "games", ":gameID", "coupons" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "couponID", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "page", "value": "1" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "limit", "value": "100" } ], "variable": [ { "type": "any", "value": "", "key": "gameID", "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" } } ] }, "header": [ { "key": "Accept", "value": "application/json" } ], "method": "GET", "body": {}, "auth": null }, "response": [ { "id": "771cd837-fa83-49a9-aff5-18d3b5a2e88b", "name": "OK", "originalRequest": { "url": { "path": [ "v1", "games", ":gameID", "coupons" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "couponID", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "page", "value": "1" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "limit", "value": "100" } ], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "gameID" } ] }, "header": [ { "key": "Accept", "value": "application/json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "OK", "code": 200, "header": [ { "key": "Content-Type", "value": "application/json" } ], "body": "{\n \"success\": \"\",\n \"data\": \"\",\n \"status\": \"\",\n \"message\": \"\",\n \"errors\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "e5753978-a007-4a0d-b81c-d361f0cbfe27", "name": "Bad Request", "originalRequest": { "url": { "path": [ "v1", "games", ":gameID", "coupons" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "couponID", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "page", "value": "1" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "limit", "value": "100" } ], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "gameID" } ] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Bad Request", "code": 400, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "61628dc6-26a1-4d40-b1aa-e011a557689b", "name": "Unauthorized", "originalRequest": { "url": { "path": [ "v1", "games", ":gameID", "coupons" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "couponID", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "page", "value": "1" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "limit", "value": "100" } ], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "gameID" } ] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Unauthorized", "code": 401, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "f23f426b-1290-485a-bc30-3732a9d29737", "name": "Forbidden", "originalRequest": { "url": { "path": [ "v1", "games", ":gameID", "coupons" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "couponID", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "page", "value": "1" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "limit", "value": "100" } ], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "gameID" } ] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Forbidden", "code": 403, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "bad7bc53-c693-43b1-a3b6-625f37c899cc", "name": "Not Found", "originalRequest": { "url": { "path": [ "v1", "games", ":gameID", "coupons" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "couponID", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "page", "value": "1" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "limit", "value": "100" } ], "variable": [ { "disabled": false, "description": { "content": "(Required) ", "type": "text/plain" }, "type": "any", "value": "", "key": "gameID" } ] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Not Found", "code": 404, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" } ], "event": [], "protocolProfileBehavior": { "disableBodyPruning": true } } ] }, { "name": "통계", "description": "", "item": [ { "id": "b2b1c7f6-fe9d-42c3-988a-54773d0a0404", "name": "상품 판매 통계 (게임사별)", "request": { "name": "상품 판매 통계 (게임사별)", "description": { "content": "특정 게임사의 **모든 상품별 판매 통계**를 반환합니다.\r\n\r\n### 포함 수치 (상품 별)\r\n| 필드 | 의미 |\r\n|---|---|\r\n| `soldQuantity` | 누적 판매 수량 (Pending/Cancelled/Refunded 제외) |\r\n| `salesAmount` | 누적 판매 금액 (단가 × 수량) |\r\n| `couponGeneratedCount` | 발급된 총 쿠폰 코드 수 (쿠폰 상품만) |\r\n| `couponIssuedCount` | 회원 보관함에 발급된 코드 수 (Status=Issued) |\r\n| `couponUsedCount` | 사용 완료된 코드 수 (Status=Used) |\r\n| `couponAvailableCount` | 미사용 / 미발급 코드 수 (Status=Available) |\r\n\r\n### 필수 scope\r\n- `read:stats`\r\n\r\n### 쿼리 파라미터\r\n- `gameID` (필수) — 게임 ID\r\n\r\n### 에러\r\n- `400` — gameID 누락\r\n- `404` — 해당 게임 없음", "type": "text/plain" }, "url": { "path": [ "v1", "stats", "products" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "gameID", "value": "0" } ], "variable": [] }, "header": [ { "key": "Accept", "value": "application/json" } ], "method": "GET", "body": {}, "auth": null }, "response": [ { "id": "94883a3d-34ca-4e82-9f03-2198ee05cf9a", "name": "OK", "originalRequest": { "url": { "path": [ "v1", "stats", "products" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "gameID", "value": "0" } ], "variable": [] }, "header": [ { "key": "Accept", "value": "application/json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "OK", "code": 200, "header": [ { "key": "Content-Type", "value": "application/json" } ], "body": "{\n \"success\": \"\",\n \"data\": \"\",\n \"status\": \"\",\n \"message\": \"\",\n \"errors\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "5a86ca14-befc-4542-bee2-4bf99b821a08", "name": "Bad Request", "originalRequest": { "url": { "path": [ "v1", "stats", "products" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "gameID", "value": "0" } ], "variable": [] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Bad Request", "code": 400, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "f8ad8f68-71c1-4ae1-9b13-0369c7f8934a", "name": "Unauthorized", "originalRequest": { "url": { "path": [ "v1", "stats", "products" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "gameID", "value": "0" } ], "variable": [] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Unauthorized", "code": 401, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "df096118-65ba-4193-84db-c1db4159f569", "name": "Forbidden", "originalRequest": { "url": { "path": [ "v1", "stats", "products" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "gameID", "value": "0" } ], "variable": [] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Forbidden", "code": 403, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "8b9ebd3b-ae00-49f2-b387-77c6a2161814", "name": "Not Found", "originalRequest": { "url": { "path": [ "v1", "stats", "products" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "gameID", "value": "0" } ], "variable": [] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Not Found", "code": 404, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" } ], "event": [], "protocolProfileBehavior": { "disableBodyPruning": true } } ] }, { "name": "회원", "description": "", "item": [ { "id": "8f4557e1-8dcc-4bbd-853c-517cb06d66a2", "name": "회원 조회", "request": { "name": "회원 조회", "description": { "content": "DPOT 회원을 페이지네이션해 반환합니다.\r\n\r\n탈퇴/삭제된 회원은 자동 제외됩니다.\r\n\r\n### 필수 scope\r\n- `read:members`\r\n\r\n### 쿼리 파라미터\r\n| 이름 | 타입 | 기본 | 설명 |\r\n|---|---|---|---|\r\n| `hasChannel` | bool? | null | `true` 크리에이터만 / `false` 일반 회원만 / 생략 시 전체 |\r\n| `page` | int | 1 | 1부터 시작 |\r\n| `size` | int | 50 | 1~200 |\r\n\r\n### 응답 필드\r\n- `emailMasked` — 이메일은 `ji***@example.com` 형식으로 마스킹\r\n- `name` — 닉네임 (null 가능)\r\n- `hasChannel` — 채널 소유 여부\r\n- `channelSID` / `channelName` — **채널 소유 회원에만** 채워짐 (그 외 `null`)\r\n- `createdAt` — 가입일 (UTC ISO 8601)\r\n\r\n### 사용 예\r\n```\r\nGET /v1/members # 전체\r\nGET /v1/members?hasChannel=true # 크리에이터만\r\nGET /v1/members?hasChannel=false # 일반 회원만\r\nGET /v1/members?hasChannel=true&page=2&size=100\r\n```", "type": "text/plain" }, "url": { "path": [ "v1", "members" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "hasChannel", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "page", "value": "1" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "size", "value": "50" } ], "variable": [] }, "header": [ { "key": "Accept", "value": "application/json" } ], "method": "GET", "body": {}, "auth": null }, "response": [ { "id": "92441e9e-1ba8-44e9-843f-5b55aab499ee", "name": "OK", "originalRequest": { "url": { "path": [ "v1", "members" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "hasChannel", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "page", "value": "1" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "size", "value": "50" } ], "variable": [] }, "header": [ { "key": "Accept", "value": "application/json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "OK", "code": 200, "header": [ { "key": "Content-Type", "value": "application/json" } ], "body": "{\n \"success\": \"\",\n \"data\": \"\",\n \"status\": \"\",\n \"message\": \"\",\n \"errors\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "4e3d29d7-8ffe-463c-859d-3e80d6e83c04", "name": "Unauthorized", "originalRequest": { "url": { "path": [ "v1", "members" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "hasChannel", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "page", "value": "1" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "size", "value": "50" } ], "variable": [] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Unauthorized", "code": 401, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" }, { "id": "6e0b7b36-1d90-4738-9236-6102f9ebabe9", "name": "Forbidden", "originalRequest": { "url": { "path": [ "v1", "members" ], "host": [ "{{baseUrl}}" ], "query": [ { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "hasChannel", "value": "" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "page", "value": "1" }, { "disabled": false, "description": { "content": "", "type": "text/plain" }, "key": "size", "value": "50" } ], "variable": [] }, "header": [ { "key": "Accept", "value": "application/problem+json" }, { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "GET", "body": {} }, "status": "Forbidden", "code": 403, "header": [ { "key": "Content-Type", "value": "application/problem+json" } ], "body": "{\n \"type\": \"\",\n \"title\": \"\",\n \"status\": \"\",\n \"detail\": \"\",\n \"instance\": \"\"\n}", "cookie": [], "_postman_previewlanguage": "json" } ], "event": [], "protocolProfileBehavior": { "disableBodyPruning": true } } ] }, { "name": "OAuth", "description": "", "item": [ { "id": "bfff48fa-ae5e-4497-84ab-17ed54314140", "name": "/oauth/token", "request": { "name": "/oauth/token", "description": {}, "url": { "path": [ "oauth", "token" ], "host": [ "{{baseUrl}}" ], "query": [], "variable": [] }, "method": "POST", "body": {}, "auth": null }, "response": [ { "id": "19c3ba56-9e6a-462f-904d-c304e5f1b6c2", "name": "OK", "originalRequest": { "url": { "path": [ "oauth", "token" ], "host": [ "{{baseUrl}}" ], "query": [], "variable": [] }, "header": [ { "description": { "content": "Added as a part of security scheme: bearer", "type": "text/plain" }, "key": "Authorization", "value": "Bearer " } ], "method": "POST", "body": {} }, "status": "OK", "code": 200, "header": [], "cookie": [], "_postman_previewlanguage": "text" } ], "event": [], "protocolProfileBehavior": { "disableBodyPruning": true } } ] } ], "auth": { "type": "bearer", "bearer": [ { "type": "any", "value": "{{bearerToken}}", "key": "token" } ] }, "event": [], "variable": [ { "key": "baseUrl", "value": "https://localhost:4000" } ], "info": { "_postman_id": "62adb938-4217-4c40-a73e-de007c4687da", "name": "DPOT Public API", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", "description": { "content": "DPOT 크리에이터 후원·도네이션 플랫폼의 외부 개발자용 공개 API 입니다.\r\n\r\n## 1. 사전 준비사항\r\n\r\n공개 API 호출을 위해서는 아래 절차를 **순서대로** 완료해야 합니다.\r\n\r\n### 1-1. 개발자 계정 등록\r\n1. https://developers.dpot.live 에서 일반 DPOT 계정으로 로그인\r\n2. **온보딩 페이지에서 사업자/개인 정보 입력**\r\n3. **NICE 본인인증(KYC)** 으로 실명/CI 검증\r\n4. 관리자 승인 대기 (영업일 기준 1~3 일)\r\n - 승인 상태는 `GET /api/developers/profile` 또는 포털 헤더에서 확인 가능\r\n\r\n### 1-2. 자격 증명 발급\r\n승인 후 두 가지 방식 중 선택해 토큰을 발급 받습니다.\r\n\r\n| 방식 | 발급 위치 | 인증 헤더 | 권장 용도 |\r\n|---|---|---|---|\r\n| **OAuth2 Client Credentials** | `developers.dpot.live/apps` 에서 앱 생성 | `Authorization: Bearer {access_token}` | 서버 ↔ 서버, 토큰 만료/갱신 |\r\n| **Personal Access Token (PAT)** | `developers.dpot.live/tokens` | `Authorization: Bearer dpot_pat_xxx` | 개인 스크립트, 단기 테스트 |\r\n\r\nOAuth2 access token 발급 예시:\r\n```bash\r\ncurl -X POST https://api.dpot.live/oauth/token \\\r\n -d \"grant_type=client_credentials\" \\\r\n -d \"client_id=YOUR_CLIENT_ID\" \\\r\n -d \"client_secret=YOUR_CLIENT_SECRET\" \\\r\n -d \"scope=read:members read:products\"\r\n```\r\n\r\n### 1-3. Scope 요청\r\n각 API 는 **flat scope** 단위로 권한을 검사합니다. 토큰 발급 시 필요한 scope 를 명시하세요.\r\nscope 가 부족하면 `403 Forbidden` 으로 응답합니다.\r\n\r\n| Scope | 설명 |\r\n|---|---|\r\n| `read:members` | 회원(일반/크리에이터) 목록 조회 |\r\n| `read:products` | 상품 목록 조회 |\r\n| `read:coupons` | 쿠폰 코드 목록·상세 조회 |\r\n| `read:stats` | 상품 판매 통계 조회 |\r\n| `read:channels` | 채널 후원 코드 확인 |\r\n| `read:purchases` | 결제 보고 조회 (단건/목록 대사) |\r\n| `write:purchases` | 결제 등록/취소 보고 — **OAuth2 앱 토큰 전용** |\r\n\r\n### 1-4. Rate Limit\r\n기본 한도: **분당 100 회**, **일일 10,000 회** (앱/PAT 별 독립 카운터).\r\n초과 시 `429 Too Many Requests` + `Retry-After` 헤더가 반환됩니다.\r\n\r\n---\r\n\r\n## 2. 제공 API\r\n\r\nv1 에서 제공하는 엔드포인트는 다음과 같습니다. 각 항목 클릭 시 상세 스펙으로 이동합니다.\r\n\r\n| # | 메서드 | 경로 | 요약 | 필요 scope |\r\n|---|---|---|---|---|\r\n| 1 | GET | `/v1/members?hasChannel=` | 회원 조회 (`hasChannel` 으로 일반/크리에이터/전체 필터) | `read:members` |\r\n| 2 | GET | `/v1/products` | 상품 목록 (쿠폰 정보 포함) | `read:products` |\r\n| 3 | GET | `/v1/games/{gameID}/coupons` | 게임 쿠폰 코드 대량 조회 | `read:coupons` |\r\n| 4 | GET | `/v1/coupons/codes/{code}` | 쿠폰 코드 단일 상세 | `read:coupons` |\r\n| 5 | GET | `/v1/stats/products` | 게임사별 상품 판매 통계 | `read:stats` |\r\n| 6 | GET | `/v1/channels/{code}` | 채널 후원 코드 확인 (결제 보고 사전 검증) | `read:channels` |\r\n| 7 | POST | `/v1/purchases` | 결제 등록 — 채널 수수료 보류 적립 (+14일 확정) | `write:purchases` |\r\n| 8 | POST | `/v1/purchases/{orderID}/cancel` | 결제 취소 — 보류 취소 또는 확정 수수료 회수 | `write:purchases` |\r\n| 9 | GET | `/v1/purchases/{orderID}` | 결제 단건 조회 | `read:purchases` |\r\n| 10 | GET | `/v1/purchases?status=&from=&to=` | 결제 목록 조회 (대사) | `read:purchases` |\r\n\r\n### 회원 정보 비공개 정책\r\n회원 응답에서 다음 정보는 **노출되지 않습니다**:\r\n- 이메일 평문 (대신 `a***@example.com` 형식 마스킹)\r\n- 휴대전화번호 / CI / DI / 실명\r\n- 결제 수단 정보\r\n\r\n---\r\n\r\n## 3. 게임사 결제 보고 연동 가이드\r\n\r\n게임 내 결제(인앱 결제)가 발생하면 DPOT 에 보고하여, 유저가 입력한 **채널 후원 코드**의\r\n크리에이터에게 판매 수수료가 적립되도록 하는 연동입니다.\r\n전 구간 **게임사 서버 ↔ DPOT 서버** 통신이며, 게임 클라이언트에서 직접 호출하지 않습니다\r\n(`client_secret` 이 클라이언트에 노출되면 안 됩니다).\r\n\r\n### 3-1. 전체 흐름\r\n\r\n```text\r\n[게임 유저] [게임사 서버] [DPOT]\r\n │ ① 후원코드 입력 │ │\r\n ├──────────────────────▶│ ② GET /v1/channels/{code} │\r\n │ ├───────────────────────────────▶│ 코드 존재/활성 확인\r\n │ ◀── 확인 결과 ────────┤◀───────────────────────────────┤\r\n │ │ │\r\n │ ③ 인앱 결제 완료 │ │\r\n ├──────────────────────▶│ ④ POST /v1/purchases │\r\n │ ├───────────────────────────────▶│ 보류(Pending) 적립 생성\r\n │ │ │ ⑤ +14일 후 자동 확정\r\n │ │ │ → 채널에 수수료 입금\r\n │ ⑥ 스토어 환불 발생 │ │\r\n ├──────────────────────▶│ ⑦ POST /v1/purchases/{orderID}/cancel\r\n │ ├───────────────────────────────▶│ 보류 취소 / 확정분 회수\r\n```\r\n\r\n### 3-2. 사전 조건\r\n\r\n1. DPOT 운영팀과 제휴 계약 → **게임 등록** (게임 코드 발급 + 수수료율 설정은 DPOT 측에서 수행)\r\n2. 개발자 계정 승인 (섹션 1-1) 후 앱 생성 — scope 는 `write:purchases read:purchases read:channels`\r\n3. **결제 등록/취소는 OAuth2 앱 토큰 전용**입니다. PAT 으로는 조회(`read:purchases`)만 가능합니다.\r\n\r\n### 3-3. Step 1 — 후원 코드 입력과 검증\r\n\r\n게임 설정 화면 등에 \"크리에이터 후원 코드\" 입력란을 제공하세요 (4~7자 영문+숫자, 대소문자 무관).\r\n입력 시점에 `GET /v1/channels/{code}` 로 검증해 유저에게 즉시 피드백합니다.\r\n\r\n| 응답 | 처리 |\r\n|---|---|\r\n| `200` + `active: true` | 코드 저장, 이후 결제 보고에 사용 |\r\n| `200` + `active: false` | \"사용할 수 없는 코드\" 안내 — 결제 보고가 거부되므로 저장하지 않음 |\r\n| `404` | \"존재하지 않는 코드\" 안내 |\r\n\r\n### 3-4. Step 2 — 결제 보고 (`POST /v1/purchases`)\r\n\r\n유저의 인앱 결제가 **스토어에서 확정된 후** 서버에서 보고합니다.\r\n\r\n| 필드 | 규칙 |\r\n|---|---|\r\n| `orderID` | **마켓 거래 ID 원문 그대로** — Google `GPA.xxxx-xxxx-xxxx-xxxxx`, Apple Transaction ID 등. 자체 채번/가공 금지 (분쟁 시 영수증 대조 근거) |\r\n| `marketplace` | 1=구글, 2=애플, 3=MS, 4=갤럭시, 5=원스토어, 6=기타 |\r\n| `gameCode` | DPOT 이 발급한 게임 코드 |\r\n| `orderPrice` | 유저 실결제 금액 (KRW) |\r\n| `productID` | 인앱 상품 SKU — **전달 권장** (DPOT 측 금액 검증에 사용) |\r\n| `channelCode` | 유저가 입력한 후원 코드 |\r\n\r\n```bash\r\ncurl -X POST https://api.dpot.live/v1/purchases \\\r\n -H \"Authorization: Bearer {access_token}\" \\\r\n -H \"Content-Type: application/json\" \\\r\n -d '{\r\n \"channelCode\": \"ABC123\",\r\n \"orderID\": \"GPA.3312-8767-0710-71943\",\r\n \"marketplace\": 1,\r\n \"gameCode\": \"XXXXXX\",\r\n \"orderPrice\": 11000,\r\n \"productID\": \"diamond_1000\"\r\n }'\r\n```\r\n\r\n`201` 응답의 `status` 는 항상 `Pending` 이며, 수수료는 `confirmDueAt`(등록 +14일, 주말·공휴일 포함)\r\n에 자동 확정되어 채널에 입금됩니다. 응답의 `commissionAmount` 는 채널 적립 예정액입니다.\r\n\r\n**멱등성과 재시도** — 동일 (`marketplace`, `orderID`) 조합은 1회만 등록됩니다.\r\n\r\n| 상황 | 대응 |\r\n|---|---|\r\n| 타임아웃 / 네트워크 오류 / `5xx` | **동일 페이로드로 재시도해도 안전** (이미 등록됐다면 `409`) |\r\n| `409 Purchase.Duplicate` | 이미 등록된 주문 — **성공으로 간주**하고 종료 |\r\n| `400` / `403` / `404` | 재시도 금지 — 페이로드·scope·게임 코드 점검 |\r\n\r\n### 3-5. Step 3 — 환불 시 취소 보고 (`POST /v1/purchases/{orderID}/cancel`)\r\n\r\n스토어 환불을 확인하면 **반드시** 취소를 보고하세요. 미보고 시 환불된 결제의 수수료가\r\n크리에이터에게 확정 지급되며, 정산 대사에서 불일치로 기록됩니다.\r\n\r\n```bash\r\ncurl -X POST \"https://api.dpot.live/v1/purchases/GPA.3312-8767-0710-71943/cancel\" \\\r\n -H \"Authorization: Bearer {access_token}\"\r\n```\r\n\r\n| 케이스 | 응답 |\r\n|---|---|\r\n| 보류 중(14일 내) 취소 | 적립 자체가 소멸 — `recoveredAmount: 0`, `shortfallAmount: 0` |\r\n| 확정 후 취소 | 채널 적립분 회수 — `recoveredAmount` 에 회수액, 잔액 부족분은 `shortfallAmount` |\r\n| `409 Purchase.AlreadyCanceled` | 이미 처리됨 — 성공으로 간주 |\r\n\r\n취소는 **등록한 앱만** 가능합니다. 동일 `orderID` 가 여러 마켓에 있으면 `?marketplace=` 를 지정하세요.\r\n\r\n### 3-6. Step 4 — 정산 대사\r\n\r\n`GET /v1/purchases?from=&to=&status=&page=&size=` 로 기간별 등록 내역을 받아\r\n자체 결제 DB 와 주기적으로 대조하세요 (일 1회 권장).\r\n누락 건은 추가 보고하고, 금액 불일치는 DPOT 운영팀과 협의합니다.\r\n계약에 따라 월 정산 시 스토어 매출 리포트 제출이 요구될 수 있습니다.\r\n\r\n### 3-7. 토큰 관리 구현 예시\r\n\r\n`access_token` 은 **1시간(3600초)** 유효합니다. 매 호출마다 발급하지 말고 캐시 후\r\n만료 60초 전에 재발급하세요. (토큰 발급 호출도 rate limit 에 포함됩니다.)\r\n\r\nC# (.NET):\r\n```csharp\r\npublic sealed class DpotApiClient(HttpClient http, string clientID, string clientSecret)\r\n{\r\n private string? _token;\r\n private DateTime _expiresAt;\r\n\r\n private async Task GetTokenAsync()\r\n {\r\n if (_token is not null && DateTime.UtcNow < _expiresAt.AddSeconds(-60))\r\n {\r\n return _token;\r\n }\r\n\r\n var res = await http.PostAsync(\"https://api.dpot.live/oauth/token\", new FormUrlEncodedContent(new Dictionary\r\n {\r\n [\"grant_type\"] = \"client_credentials\",\r\n [\"client_id\"] = clientID,\r\n [\"client_secret\"] = clientSecret,\r\n [\"scope\"] = \"write:purchases read:purchases read:channels\"\r\n }));\r\n res.EnsureSuccessStatusCode();\r\n\r\n var json = await res.Content.ReadFromJsonAsync();\r\n _token = json.GetProperty(\"access_token\").GetString()!;\r\n _expiresAt = DateTime.UtcNow.AddSeconds(json.GetProperty(\"expires_in\").GetInt32());\r\n return _token;\r\n }\r\n\r\n public async Task ReportPurchaseAsync(object payload)\r\n {\r\n var req = new HttpRequestMessage(HttpMethod.Post, \"https://api.dpot.live/v1/purchases\");\r\n req.Headers.Authorization = new(\"Bearer\", await GetTokenAsync());\r\n req.Content = JsonContent.Create(payload);\r\n return await http.SendAsync(req);\r\n }\r\n}\r\n```\r\n\r\nNode.js:\r\n```javascript\r\nlet cached = { token: null, expiresAt: 0 };\r\n\r\nasync function getToken() {\r\n if (cached.token && Date.now() < cached.expiresAt - 60_000) return cached.token;\r\n\r\n const res = await fetch('https://api.dpot.live/oauth/token', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\r\n body: new URLSearchParams({\r\n grant_type: 'client_credentials',\r\n client_id: process.env.DPOT_CLIENT_ID,\r\n client_secret: process.env.DPOT_CLIENT_SECRET,\r\n scope: 'write:purchases read:purchases read:channels'\r\n })\r\n });\r\n const json = await res.json();\r\n cached = { token: json.access_token, expiresAt: Date.now() + json.expires_in * 1000 };\r\n return cached.token;\r\n}\r\n\r\nasync function reportPurchase(payload) {\r\n const res = await fetch('https://api.dpot.live/v1/purchases', {\r\n method: 'POST',\r\n headers: { 'Authorization': `Bearer ${await getToken()}`, 'Content-Type': 'application/json' },\r\n body: JSON.stringify(payload)\r\n });\r\n if (res.status === 409) return { duplicated: true }; // 이미 등록 — 성공 간주\r\n if (!res.ok) throw new Error(`DPOT report failed: ${res.status}`);\r\n return await res.json();\r\n}\r\n```\r\n\r\n### 3-8. 상태 / 오류 빠른 참조\r\n\r\n결제 보고의 `status`:\r\n\r\n| status | 의미 |\r\n|---|---|\r\n| `Pending` | 보류 — `confirmDueAt` 에 자동 확정 예정 |\r\n| `Confirmed` | 확정 — 채널 수수료 입금 완료 |\r\n| `Canceled` | 취소됨 |\r\n\r\n결제 보고 관련 `errors[].code`:\r\n\r\n| Code | HTTP | 의미 / 대응 |\r\n|---|---|---|\r\n| `Channel.NotFound` | 404 | 후원 코드 없음 — 유저 입력 재확인 |\r\n| `Channel.Inactive` | 400 | 비활성/탈퇴 채널 — 저장된 코드 해제 안내 |\r\n| `Game.NotFound` | 404 | 게임 코드 오류 — 발급받은 코드 확인 |\r\n| `Game.Inactive` | 400 | 게임 비활성 — DPOT 운영팀 문의 |\r\n| `Game.ApiCommissionNotConfigured` | 400 | 수수료율 미설정 — DPOT 운영팀 문의 |\r\n| `Purchase.Duplicate` | 409 | 이미 등록된 주문 — 성공 간주 |\r\n| `Purchase.NotFound` | 404 | 취소/조회 대상 없음 (타 앱이 등록한 건 포함) |\r\n| `Purchase.AlreadyCanceled` | 409 | 이미 취소됨 — 성공 간주 |\r\n| `Purchase.AmbiguousOrderID` | 400 | 여러 마켓에 동일 orderID — `marketplace` 지정 필요 |\r\n| `Purchase.AppNotActive` | 403 | 앱 정지 상태 — DPOT 운영팀 문의 |\r\n\r\n---\r\n\r\n## 4. API 변경 이력\r\n\r\n### v1.1.0 — 2026-06-11\r\n**결제 보고 (게임사 파트너)**\r\n- `POST /v1/purchases` 추가 — 게임 내 결제 등록, 채널 후원 코드 기반 판매 수수료 보류 적립 (등록 +14일 후 확정)\r\n- `POST /v1/purchases/{orderID}/cancel` 추가 — 결제 취소 (등록한 앱만 가능)\r\n- `GET /v1/purchases/{orderID}` / `GET /v1/purchases` 추가 — 파트너 대사용 조회\r\n- `GET /v1/channels/{code}` 추가 — 채널 후원 코드 사전 검증\r\n- 신규 scope: `write:purchases`(OAuth2 앱 토큰 전용), `read:purchases`, `read:channels`\r\n\r\n### v1.0.0 — 2026-06-05\r\n**Initial Release**\r\n- `GET /v1/members?hasChannel=` 추가 — `hasChannel` 쿼리로 일반/크리에이터/전체 필터\r\n- `GET /v1/products` 추가 (쿠폰 상품의 경우 `coupon` 필드 포함)\r\n- `GET /v1/games/{gameID}/coupons` 추가\r\n- `GET /v1/coupons/codes/{code}` 추가\r\n- `GET /v1/stats/products` 추가\r\n\r\n---\r\n\r\n## 5. 응답 / 오류 코드\r\n\r\n### 정상 응답\r\n모든 정상 응답은 다음 envelope 으로 감쌉니다.\r\n```json\r\n{\r\n \"success\": true,\r\n \"data\": { ... }\r\n}\r\n```\r\n\r\n### 오류 응답\r\n오류는 RFC 7807 ProblemDetails 형식으로 반환됩니다.\r\n```json\r\n{\r\n \"type\": \"https://tools.ietf.org/html/rfc7231#section-6.5.4\",\r\n \"title\": \"Member not found\",\r\n \"status\": 404,\r\n \"detail\": \"ID 12345 에 해당하는 회원이 없습니다.\",\r\n \"errors\": [\r\n { \"code\": \"Member.NotFound\", \"description\": \"...\" }\r\n ]\r\n}\r\n```\r\n\r\n### HTTP Status Code\r\n| Status | 의미 | 처리 가이드 |\r\n|---|---|---|\r\n| `200` | 성공 | — |\r\n| `400` | 요청 파라미터 검증 실패 | `errors[].description` 확인 후 재호출 |\r\n| `401` | 인증 토큰 없음 / 만료 | 토큰 재발급 |\r\n| `403` | scope 부족 또는 권한 거부 | 앱의 scope 설정 확인 |\r\n| `404` | 대상 리소스 없음 | 식별자 재확인 |\r\n| `409` | 충돌 (중복/상태 불일치) | 현재 상태 재조회 후 재시도 |\r\n| `429` | Rate limit 초과 | `Retry-After` 초 만큼 대기 |\r\n| `500` | 서버 내부 오류 | DPOT 운영팀에 문의 |\r\n\r\n### 비즈니스 오류 코드\r\n`errors[].code` 필드로 세분화된 코드가 제공됩니다.\r\n\r\n| Code | HTTP | 의미 |\r\n|---|---|---|\r\n| `Auth.MissingToken` | 401 | Authorization 헤더 누락 |\r\n| `Auth.InvalidToken` | 401 | 토큰 형식 오류 / 만료 / 위조 |\r\n| `Auth.ScopeRequired` | 403 | 토큰 scope 부족 |\r\n| `Member.NotFound` | 404 | 회원 없음 |\r\n| `Game.NotFound` | 404 | 게임 없음 |\r\n| `Product.NotFound` | 404 | 상품 없음 |\r\n| `Coupon.NotFound` | 404 | 쿠폰 코드 없음 |\r\n| `Coupon.InvalidCode` | 400 | 코드 형식 오류 (길이/문자) |\r\n| `Channel.NotFound` | 404 | 채널 후원 코드 없음 |\r\n| `Channel.Inactive` | 400 | 비활성/탈퇴 채널 |\r\n| `Game.Inactive` | 400 | 비활성 게임 |\r\n| `Game.ApiCommissionNotConfigured` | 400 | 결제 보고 수수료율 미설정 게임 |\r\n| `Purchase.Duplicate` | 409 | 결제 보고 중복 (marketplace + orderID) |\r\n| `Purchase.NotFound` | 404 | 결제 보고 없음 |\r\n| `Purchase.AlreadyCanceled` | 409 | 이미 취소된 결제 보고 |\r\n| `Purchase.AmbiguousOrderID` | 400 | 여러 마켓에 동일 orderID — marketplace 지정 필요 |\r\n| `Purchase.AppNotActive` | 403 | 앱 비활성 상태 |\r\n| `RateLimit.Exceeded` | 429 | 호출량 초과 |", "type": "text/plain" } } }