Skip to content

연동 체크리스트 ​

운영 전환 전에 오퍼레이터 쪽에서 확인할 항목입니다. 검수 때 이 목록을 기준으로 함께 점검합니다.

서명 · 보안 ​

  • [ ] 테스트 벡터 3개(POST · 인코딩된 쿼리 GET · 지갑 debit)가 모두 기대 서명과 같다.
  • [ ] 서명 비밀키를 hex 디코딩하지 않고 64자리 문자열 그대로 키로 쓴다.
  • [ ] 서명한 본문 바이트와 실제로 보내는 본문 바이트가 같다(두 번 직렬화하지 않는다).
  • [ ] 쿼리 값을 퍼센트 인코딩하고(+ → %2B), 그 문자열 그대로 서명한다.
  • [ ] 모든 Operator API 요청에 X-Api-Key 를 싣는다.
  • [ ] 재시도할 때 타임스탬프 · nonce · 서명을 새로 만든다.
  • [ ] 오퍼레이터 서버 시계가 NTP 로 맞춰져 있다(허용 오차 ±5분).
  • [ ] API 키 · 서명 비밀키가 서버 설정에만 있고, 브라우저 코드 · 앱 · 저장소 · 로그에 남지 않는다.
  • [ ] Operator API 를 부르는 모든 서버의 공인 IP 가 IP 허용 목록에 등록돼 있다.
  • [ ] 지갑 URL 이 HTTPS 이고, 방화벽이 플랫폼의 지갑 콜백 발신 IP 를 허용한다.
  • [ ] 운영 환경 자격증명 · 베이스 URL · 게임 화면 오리진으로 바꿨다(테스트 값이 남아 있지 않다).

지갑 콜백 — 공통 ​

  • [ ] 콜백 서명을 원문 본문 바이트로 검증하고, X-Operator · 타임스탬프(±5분) · 상수 시간 비교를 모두 한다.
  • [ ] 콜백 nonce 를 5분 이상, 여러 서버가 공유하는 저장소에 보관해 재사용을 거절한다.
  • [ ] 리버스 프록시가 경로를 바꾸더라도 등록된 지갑 URL 의 원래 경로로 서명을 검증한다.
  • [ ] 업무 결과는 거절을 포함해 모두 HTTP 200 + 본문 status 로 돌려준다.
  • [ ] 응답 balance 는 처리 후 잔액이고, 통화 최소 단위 정수다.
  • [ ] tx_id 멱등: 이미 처리한 tx_id 가 다시 오면 잔액을 다시 바꾸지 않고 처음과 같은 응답(거절이었으면 같은 거절)을 돌려준다.
  • [ ] 같은 플레이어의 거래를 동시에 처리해도 잔액이 꼬이지 않는다(DB 트랜잭션 · 행 잠금).
  • [ ] 응답 시간이 충분히 짧다 — 플랫폼은 balance · debit 을 3초, credit · rollback 을 5초 기다린다.
  • [ ] 지갑 원장에 tx_id · round_id · reason · amount · 처리 결과를 남겨 대사할 수 있다.

balance ​

  • [ ] 없는 플레이어는 PLAYER_NOT_FOUND, 통화가 다르면 CURRENCY_MISMATCH 로 응답한다.

debit ​

  • [ ] 잔액이 모자라면 차감하지 않고 INSUFFICIENT_FUNDS 를 돌려준다.
  • [ ] 차단 · 자기 배제 중인 플레이어는 PLAYER_BLOCKED 로 거절한다.
  • [ ] 이미 rollback 이 먼저 도착한 tx_id 의 debit 이 늦게 오면 차감하지 않고 TX_ALREADY_ROLLED_BACK 을 돌려준다.
  • [ ] debit 이 타임아웃 · HTTP 오류 · UNKNOWN_ERROR 로 끝나면 플랫폼이 debit 을 재시도하지 않고 곧바로 rollback 을 보낸다는 것을 전제로 구현했다.
  • [ ] reason(bet · ante · double · split · buy_in · top_up)을 원장에 남긴다.

credit ​

  • [ ] 금액 0 인 credit(패배한 라운드 정산)을 정상 처리하고 OK 를 돌려준다.
  • [ ] 차단된 플레이어에게도 credit 을 반영한다(진행 중이던 판의 당첨 · 캐시아웃).
  • [ ] credit 은 OK 가 아니면 상태와 관계없이 계속 재시도된다는 것을 알고, 정상 거래에 OK 외의 상태를 돌려주지 않는다.
  • [ ] 재시도 간격(1초 → 2초 → 5초 → 15초 → 1분 → 5분, 이후 5분마다)으로 같은 tx_id 가 반복돼도 한 번만 반영된다.

rollback ​

  • [ ] ref_tx_id 로 원 debit 을 찾는다.
  • [ ] 원 debit 이 성공했으면 그 금액을 되돌리고 OK.
  • [ ] 원 debit 기록이 없으면(debit 이 닿지 않음) 아무것도 되돌리지 않고 OK 를 돌려주며, 그 ref_tx_id 를 기록해 늦게 오는 debit 을 거절한다.
  • [ ] 원 debit 이 이미 되돌려졌거나 거절됐으면 다시 되돌리지 않고 OK.
  • [ ] rollback 도 OK 가 아니면 계속 재시도된다는 것을 전제로 구현했다.
  • [ ] reason(cancel_bet · round_void · timeout · system)을 원장에 남긴다.

장애 시나리오 ​

검수 때 테스트 지갑에 일부러 장애를 넣어 확인하면 좋습니다.

  • [ ] debit 응답을 3초 넘게 늦추면 → 베팅 · 착석이 실패하고, rollback(timeout)이 와서 잔액이 원래대로 돌아온다.
  • [ ] debit 에 HTTP 500 을 돌려주면 → 위와 같다.
  • [ ] credit 에 몇 번 HTTP 500 을 돌려준 뒤 정상 응답하면 → 재시도로 결국 한 번만 반영된다.
  • [ ] 같은 tx_id 를 두 번 보내도 잔액이 한 번만 바뀐다.
  • [ ] 지갑 서버를 잠시 내렸다 올려도, 다시 올라온 뒤 밀린 credit · rollback 이 모두 반영된다.

게임 실행 ​

  • [ ] POST /launch 를 오퍼레이터 서버에서만 부른다.
  • [ ] 플레이어가 누를 때마다 launch_url 을 새로 받고(60초 · 1회용), 받은 그대로 연다.
  • [ ] 새 창은 클릭 처리기 안에서 바로 열고, noopener 를 쓰지 않는다.
  • [ ] iframe 으로 넣는다면 임베드 도메인을 등록했고, game.closed 를 받으면 iframe 을 닫는다.
  • [ ] return_url 을 보내거나 기본 복귀 URL 이 등록돼 있다(없으면 postMessage 가 오지 않는다).
  • [ ] postMessage 를 받을 때 event.origin 과 data.source === "games-platform" 을 확인한다.
  • [ ] external_id 는 플레이어마다 바뀌지 않는 값이고, 64바이트 이하다.
  • [ ] nickname 을 2~16자로 맞춰 보낸다.
  • [ ] 플레이어 통화가 고정된다는 것(CURRENCY_MISMATCH)을 반영했다.
  • [ ] operator_session_id 에 로그인 토큰 원문 대신 불투명한 ID 를 쓴다.
  • [ ] PLAYER_BLOCKED · GAME_MAINTENANCE · TABLE_NOT_FOUND 같은 실행 오류를 플레이어에게 알맞게 안내한다.

플레이어 관리 ​

  • [ ] 사이트 로그아웃 · 자기 배제 · 계정 정지 때 POST /players/{external_id}/logout 를 부른다(정지는 block: true).
  • [ ] 정지를 풀 때 block: false 로 다시 부른다.

대사 · 운영 ​

  • [ ] GET /transactions 로 기간별 지갑 거래를 받아 자기 지갑 원장과 tx_id 단위로 맞춰 본다(정기 작업).
  • [ ] 대사에서 pending · uncertain 상태 거래를 따로 살핀다.
  • [ ] 베팅 내역 화면이 필요하면 GET /rounds · GET /rounds/{round_id} 를 쓴다.
  • [ ] prepaid 운영이면 GET /credit 로 알 잔액을 감시하고, 경고 기준(warn_below) 아래로 내려가기 전에 충전을 요청한다.
  • [ ] Operator API 의 429 · 5xx 에 대기 후 재시도한다(쓰기 요청은 결과를 먼저 조회).
  • [ ] 장애 · 대사 불일치 때 연락할 담당자와 경로를 정했다.

문서 v1.0.0 · Operator API v1