Ranking API

v1 · REST · JSON

Overview

  • Base URL: https://toolzip.kr
  • 응답: JSON (UTF-8). 에러도 HTTP 200 + ok:false
  • 유저당 최고 점수 1건만 저장 (lower 점수 제출은 무시됨)
상황필요한 인증사용 엔드포인트
외부 사이트/게임이 toolzip 랭킹에 점수 등록·조회API Keysubmit / top / me
toolzip에 올린 게임(iframe 내부)에서 점수·평점 기록세션(로그인 쿠키)submit-self / me-self / rate / playtime

API Key 기반 (외부 통합)

Developer 페이지에서 Key 발급 → api_key + api_secret 확보. 점수 등록엔 HMAC-SHA256 서명 필수. 분당 30회 레이트 리미트.

점수 등록 POST

POST https://toolzip.kr/api/rank?action=submit
  api_key       *  (발급받은 Key)
  game_id       *  게임 ID
  player_name   *  1~30자
  score         *  정수
  ts            *  UNIX timestamp (±5분)
  sign          *  HMAC-SHA256(secret, "api_key|game_id|player_name|score|ts")
  meta             선택, JSON 문자열 (2KB 이하)
# JS 서명 예시
const ts = Math.floor(Date.now() / 1000);
const payload = [apiKey, gameId, name, score, ts].join('|');
const key = await crypto.subtle.importKey('raw', new TextEncoder().encode(secret),
  { name:'HMAC', hash:'SHA-256' }, false, ['sign']);
const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(payload));
const sign = [...new Uint8Array(sig)].map(b => b.toString(16).padStart(2,'0')).join('');
// 응답
{ "ok": true, "data": {
    "score": 12345,
    "improved": true,        // 기존 최고점 갱신 여부
    "best_score": 12345,     // 현재 최고점
    "rank": 7                // 현재 순위 (null = 숨김 처리된 유저)
}}

랭킹 조회 GET

GET https://toolzip.kr/api/rank?action=top&api_key=...&game_id=1&limit=10&offset=0

// 응답
{ "ok": true, "data": {
    "game_id": 1, "total": 124,
    "items": [ { "rank":1, "player_name":"Alice", "score":9999, "created_at":"..." }, ... ]
}}

특정 플레이어 조회 GET

GET https://toolzip.kr/api/rank?action=me&api_key=...&game_id=1&player_name=Alice

// 응답: { ok, data: { player_name, best_score, rank } }

cURL 예제

API_KEY=tz_xxxxxxxxxxxx
SECRET=yyyyyyyyyyyyyyyy
GAME=1 NAME=Alice SCORE=9999 TS=$(date +%s)
SIGN=$(printf "%s|%s|%s|%s|%s" "$API_KEY" "$GAME" "$NAME" "$SCORE" "$TS" \
       | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')
curl -X POST "https://toolzip.kr/api/rank?action=submit" \
  -d "api_key=$API_KEY" -d "game_id=$GAME" -d "player_name=$NAME" \
  -d "score=$SCORE" -d "ts=$TS" -d "sign=$SIGN"

curl "https://toolzip.kr/api/rank?action=top&api_key=$API_KEY&game_id=$GAME&limit=10"

세션 기반 (toolzip 내부 게임)

toolzip에 업로드된 게임은 같은 도메인에서 iframe으로 실행되므로 로그인 쿠키가 자동 전송됩니다. API Key 불필요. player_name은 자동으로 현재 유저의 username.

부모창은 iframe에 window.parent.__toolzip = { gameId, username, canSubmit }를 주입합니다.

점수 등록 POST

POST https://toolzip.kr/api/rank?action=submit-self
  game_id *, score *, meta (optional)

// 응답
{ "ok": true, "data": {
    "score": 1500, "improved": true,
    "best_score": 1500, "rank": 3, "total_players": 42,
    "is_dev_record": false,     // 로그인 유저 == 게임 개발자
    "is_admin_record": false    // 로그인 유저 == 관리자
}}

내 순위 GET

GET https://toolzip.kr/api/rank?action=me-self&game_id=1

// 응답: { ok, data: { player_name, best_score, rank, total_players, is_dev_record, is_admin_record } }

평점 POST

POST   https://toolzip.kr/api/rate?game_id=1   stars=1..10  (0.5단위, 10=5.0)
DELETE https://toolzip.kr/api/rate?game_id=1   (POST + _method=DELETE 도 가능)
GET    https://toolzip.kr/api/rate?game_id=1   로그인 유저 평가+평균 조회

// 응답: { ok, data: { avg: 4.2, count: 18, mine: 8 } }

플레이타임 누적 POST

POST https://toolzip.kr/api/playtime    game_id *, seconds (1..300)

// 응답: { ok, data: { total_playtime_seconds } }
// 내부 play 페이지가 30초 간격으로 자동 전송.

JS 사용 예

const tz = window.parent && window.parent.__toolzip;
if (tz && tz.username) {
  const fd = new FormData();
  fd.append('game_id', tz.gameId);
  fd.append('score', 9999);
  await fetch('/api/rank?action=submit-self', {
    method: 'POST', body: fd, credentials: 'include'
  });
}

에러 코드

code의미
missing_param필수 파라미터 누락
invalid_keyAPI Key 유효하지 않음
key_revoked폐기된 Key
key_game_mismatchKey가 연결된 게임과 불일치
invalid_game존재하지 않거나 승인되지 않은 게임
invalid_signature서명 불일치
expired_tstimestamp 만료 (±5분)
rate_limited요청 빈도 초과
not_logged_in세션 기반 엔드포인트에서 로그인 쿠키 없음
invalid_stars별점이 1..10 범위가 아님