API REFERENCE / V1

하나의 Core API.

브라우저 게임은 같은 origin의 절대 경로를 사용합니다. 가격, 잔액, 영수증 상태는 항상 KOISCORE 서버 응답을 권위값으로 취급하세요.

BASE / AUTH

주소와 인증

Base URL은 https://koiscore.com입니다. koiscore.com의 브라우저 게임은 HttpOnly same-site 쿠키를 사용하고, 승인된 별도 origin 런타임은 발급된 Bearer 세션 방식을 사용합니다.

const response = await fetch('/api/profile/me', {
  credentials: 'same-origin',
  headers: { accept: 'application/json' },
});
관리자 토큰, MCP 토큰, 데이터베이스 주소를 브라우저 코드·URL·로그에 넣지 마세요.

CORE / ENDPOINTS

공개 게임 연동 엔드포인트

GET
/api/public/games

인증 없는 공개 게임 카탈로그. CORS와 캐시를 지원합니다.

GET
/api/public/games/ranking

최근 7일 플레이 지표와 게임 귀속 매출 가산점으로 계산한 포털 노출 순위입니다.

POST
/api/profile/create-guest

게스트 프로필과 세션을 생성합니다.

GET
/api/profile/me

현재 로그인 또는 게스트 프로필을 조회합니다.

PATCH
/api/profile/me

표시 이름, 프로필 이미지와 언어 설정을 수정합니다.

GET
/api/profile/connect/google

Google OAuth 로그인을 시작하고 현재 게스트 기록에 연결합니다.

GET
/api/app/:gameId/identity/context

현재 세션의 게임별 playerId와 단기 서명 assertion을 발급합니다.

GET
/api/platform/identity/jwks

게임 서버가 identity assertion을 검증할 Ed25519 공개키입니다.

POST
/api/rewards/exchange

활성화된 게임의 등록 환율로 젬을 게임 재화로 교환합니다.

POST
/api/rewards/receipts/issue

서버 상품 카탈로그를 기준으로 구매 영수증을 발급합니다.

GET
/api/rewards/receipts/:receiptId

발급된 영수증의 현재 상태를 조회합니다.

POST
/api/app/:gameId/purchase/commit

게임 지급 어댑터가 영수증을 멱등 커밋합니다.

젬 교환과 구매 API는 게임 ID, SKU, 지급 어댑터가 서버에서 활성화된 뒤 사용할 수 있습니다. 등록만 된 draft 게임은 결제를 호출할 수 없습니다.

CATALOG / EXAMPLE

공개 게임 목록

GET /api/public/games

{
  "schema": "koiscore.public-games.v1",
  "updatedAt": "2026-08-03",
  "count": 1,
  "games": [{
    "id": "example-game",
    "portalId": "example-game",
    "title": { "ko": "예제 게임", "ja": "サンプル", "en": "Example Game" },
    "genres": ["puzzle"],
    "runtimeUrl": "https://example.com/game",
    "locales": ["ko", "ja", "en"],
    "localeAware": true,
    "status": "live"
  }]
}

현재 JSON 응답 열기

PROFILE / GOOGLE

로그인과 프로필은 Core가 관리

게임은 Google SDK나 OAuth 비밀값을 직접 포함하지 않습니다. KOISCORE가 로그인을 관리하고 게임에는 게임별 playerId와 단기 서명 assertion만 전달합니다. 세이브·점수·인벤토리는 각 게임 저장소가 playerId 기준으로 관리합니다.

// 로그인 시작
location.href = '/api/profile/connect/google?returnTo=/profile/connect';

// 현재 사용자 조회
const { profile } = await fetch('/api/profile/me', {
  credentials: 'same-origin'
}).then(response => response.json());

// 프로필 설정 저장
await fetch('/api/profile/me', {
  method: 'PATCH',
  credentials: 'same-origin',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ displayName: 'Koi Player', locale: 'ko' })
});
  • 첫 Google 연결은 현재 게스트의 게임 기록과 지갑을 유지한 채 계정을 연결합니다.
  • 이후 같은 Google 계정으로 로그인하면 기존 KOISCORE 프로필 세션으로 전환합니다.
  • 게임에는 OAuth access token, Google client secret 또는 Google 이메일을 전달하지 않습니다.
  • 전역 profileId는 게임 데이터 키로 사용하지 않으며, assertion은 JWKS의 Ed25519 공개키로 검증합니다.

WALLET / RECEIPTS

젬 구매는 영수증으로

POST /api/rewards/receipts/issue
Content-Type: application/json

{
  "gameId": "example-game",
  "requestKey": "issue_01K2ABCDEF12",
  "source": "in_game_shop",
  "items": [{ "sku": "example-game.coin-pack-1", "quantity": 1 }]
}
  • requestKey는 사용자 작업마다 새 값을 만들고 재시도에는 같은 값을 사용합니다.
  • 가격과 지급량은 서버 SKU 카탈로그가 계산하며 클라이언트 값을 신뢰하지 않습니다.
  • 게임 서버는 purchase/commit을 멱등 처리해 같은 영수증을 두 번 지급하지 않습니다.

PLAYER / POSTMESSAGE

공통 플레이어 브리지

웹 포털과 앱인토스용 페이지는 같은 KOISCORE 플레이어 셸을 사용합니다. 두 채널 모두 상단에 현재 게임 타이틀과 공용 젬 잔액을 표시하고 같은 메시지 계약으로 게임 프레임을 제어합니다.

방향TYPE역할
게임 → 포털game:ready리스너 준비 완료와 초기 상태 요청
포털 → 게임portal:initlocale, shell, 초기 음소거 동기화
포털 → 게임portal:audio실행 중 마스터 음소거 변경
게임 → 포털game:shell제목과 설명 등 공통 셸 갱신
게임 → 포털game:wallet교환 후 포털 젬 잔액 갱신
window.parent.postMessage({
  protocol: 'koiscore.portal.v1',
  type: 'game:ready',
  gameId: 'example-game'
}, 'https://koiscore.com');

수신 측은 event.origin, event.source, protocol, gameId를 모두 검증해야 합니다. postMessage('*')는 사용하지 않습니다.

채널이 appintoss이면 웹 광고를 로드하지 않고 Apps in Toss 광고 SDK를 사용합니다. 타이틀, 젬 지갑, 게임 프레임 계약은 웹과 동일합니다.

전체 Portal Game API 원문 보기