콜백 명세
이 페이지에서는 Seamless Wallet 연동을 지원하기 위해 귀사의 서버에 구현해야 하는 4개의 HTTP 엔드포인트를 설명합니다. Ruby는 모든 게임 이벤트에 대해 이 엔드포인트들을 실시간으로 호출합니다.
개요
4개의 엔드포인트는 다음과 같은 공통 속성을 갖습니다:
- 메서드:
POST - Base URL: 귀사가 설정한
callback_url - Content-Type:
application/json(요청 및 응답) - 인증: HMAC-SHA256 서명 헤더 (콜백 인증 참조)
- 타임아웃: 5초 하드 타임아웃; 목표 3초 미만
- 성공 기준: HTTP 2xx 및 아래의 유효한 JSON 응답 계약
- 실패 동작: 비-2xx, 타임아웃, 64 KiB 초과 본문, 잘못된 JSON 또는 응답 필드 -> 즉시 오류, 애플리케이션 수준 자동 재시도 없음
URL 구성
Ruby는 경로를 추가하기 전에 귀사의 callback_url에서 후행 슬래시를 제거합니다:
callback_url = "https://wallet.example.com/ruby"
→ POST https://wallet.example.com/ruby/balance
→ POST https://wallet.example.com/ruby/debit
→ POST https://wallet.example.com/ruby/credit
→ POST https://wallet.example.com/ruby/rollback
URL은 사용자 정보, 쿼리 문자열, 프래그먼트가 없는 절대 HTTPS URL이어야 합니다. 후행 슬래시는 허용되며 Brand 저장 시 제거됩니다.
성공 응답 계약
성공 응답은 압축되지 않은 64 KiB 이하의 JSON object여야 합니다. Ruby는
Accept-Encoding: identity를 전송하며 압축된 Content-Encoding 응답을 거부합니다.
각 엔드포인트에서 요구하는 balance 또는 balance_before는 유한하고 음수가 아닌 DECIMAL(18,2) 문자열이어야
합니다. JSON 숫자, 부호, 지수 표기, NaN, Infinity, 누락 필드 및 범위 초과 값은
거부됩니다.
POST /balance
플레이어의 현재 잔액을 조회합니다. 이것은 상태 비저장(Stateless) 읽기로, 자금 이동이나 부수 효과가 발생하지 않습니다.
요청
{
"player_id": 12345,
"username": "player_handle",
"external_player_id": "brand-player-guid-001",
"provider_code": "evo"
}
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
player_id | integer | 예 | Ruby의 내부 플레이어 식별자 |
username | string | 아니오 | 플레이어의 표시 이름 (가용한 경우) |
external_player_id | string | 아니오 | 게임 시작 시 전달한 귀사의 안정적인 플레이어 식별자 |
provider_code | string | 예 | 게임 제공사 코드 (예: "evo", "pg") |
응답
{
"balance": "1250.00"
}
| 필드 | 타입 | 설명 |
|---|---|---|
balance | string | 소수점 문자열로 표현된 현재 플레이어 잔액 (예: "1250.00") |
참고사항
balance는 상태 비저장 조회입니다.transaction_id가 없으며 멱등성(Idempotency) 요구사항이 없습니다.- 항상 해당 플레이어의 현재 실시간 잔액을 반환하십시오.
POST /debit
플레이어의 지갑에서 자금을 차감합니다. 플레이어가 베팅할 때 호출됩니다.
요청
{
"player_id": 12345,
"amount": "100.50",
"transaction_id": "txn_bet_abc123",
"username": "player_handle",
"external_player_id": "brand-player-guid-001",
"provider_code": "evo",
"round_id": "round_xyz",
"game_code": "baccarat_classic",
"ref_id": "external_ref_001",
"memo": "Bet on Baccarat round 7"
}
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
player_id | integer | 예 | Ruby의 내부 플레이어 식별자 |
amount | string | 예 | 차감할 금액, 소수점 문자열 (예: "100.50") |
transaction_id | string | 예 | 고유 트랜잭션 식별자 -- 멱등성 처리에 사용 |
username | string | 아니오 | 플레이어의 표시 이름 |
external_player_id | string | 아니오 | 게임 시작 시 전달한 귀사의 안정적인 플레이어 식별자 |
provider_code | string | 예 | 게임 제공사 코드 |
round_id | string | 아니오 | 게임 라운드 식별자 |
game_code | string | 아니오 | 해당 제공사 내 게임 식별자 |
ref_id | string | 아니오 | 게임 제공사의 외부 참조 ID |
memo | string | 아니오 | 트랜잭션에 대한 사람이 읽을 수 있는 설명 |
응답
{
"balance": "1149.50",
"balance_before": "1250.00"
}
| 필드 | 타입 | 설명 |
|---|---|---|
balance | string | 차감 후 플레이어 잔액 |
balance_before | string | 차감 전 플레이어 잔액 |
멱등성
이미 성공적으로 처리한 transaction_id로 POST /debit 요청을 수신한 경우, 원래 호출과 동일한 응답을 반환하십시오. 자금을 다시 차감하지 마십시오.
원래 트랜잭션이 아직 처리 중인 경우 (예: 경쟁 상태로 인해), 이중 차감을 방지하기 위해 접근을 직렬화하십시오.
오류 케이스
| 시나리오 | 권장 HTTP 상태 |
|---|---|
| 잔액 부족 | 402 Payment Required 또는 422 Unprocessable Entity |
| 플레이어 미발견 | 404 Not Found |
| 플레이어 계정 정지 | 403 Forbidden |
| 내부 서버 오류 | 500 Internal Server Error |
비-2xx 응답은 Ruby에 의해 실패로 처리됩니다.
POST /credit
플레이어의 지갑에 자금을 추가합니다. 플레이어가 당첨되거나 정산이 이루어질 때 호출됩니다.
요청
{
"player_id": 12345,
"amount": "200.00",
"transaction_id": "txn_win_def456",
"username": "player_handle",
"external_player_id": "brand-player-guid-001",
"provider_code": "evo",
"round_id": "round_xyz",
"game_code": "baccarat_classic",
"bet_amount": "100.50",
"original_transaction_id": "txn_bet_abc123",
"ref_id": "external_ref_002",
"memo": "Win payout for Baccarat round 7"
}
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
player_id | integer | 예 | Ruby의 내부 플레이어 식별자 |
amount | string | 예 | 입금할 금액, 소수점 문자열 |
transaction_id | string | 예 | 고유 트랜잭션 식별자 -- 멱등성 처리에 사용 |
username | string | 아니오 | 플레이어의 표시 이름 |
external_player_id | string | 아니오 | 게임 시작 시 전달한 귀사의 안정적인 플레이어 식별자 |
provider_code | string | 예 | 게임 제공사 코드 |
round_id | string | 아니오 | 게임 라운드 식별자 |
game_code | string | 아니오 | 해당 제공사 내 게임 식별자 |
bet_amount | string | 아니오 | 이 입금이 기존 차감을 정산할 때의 원래 베팅 금액 |
original_transaction_id | string | 아니오 | 이 입금과 연결된 원래 지갑 거래 ID |
standalone_credit | boolean | 아니오 | 기존 차감 없이 발생한 독립 입금이면 true로 포함되며, 그 외에는 생략 |
ref_id | string | 아니오 | 게임 제공사의 외부 참조 ID |
memo | string | 아니오 | 트랜잭션에 대한 사람이 읽을 수 있는 설명 |
응답
{
"balance": "1349.50",
"balance_before": "1149.50"
}
| 필드 | 타입 | 설명 |
|---|---|---|
balance | string | 입금 후 플레이어 잔액 |
balance_before | string | 입금 전 플레이어 잔액 |
멱등성
이미 성공적으로 처리한 transaction_id로 POST /credit 요청을 수신한 경우, 원래 호출과 동일한 응답을 반환하십시오. 자금을 다시 입금하지 마십시오.
POST /rollback
이전 차감(Debit) 트랜잭션을 취소합니다. 베팅이 이미 완료된 후 라운드가 무효화되거나 취소될 때 호출됩니다.
요청
{
"player_id": 12345,
"amount": "100.50",
"transaction_id": "txn_rollback_ghi789",
"username": "player_handle",
"external_player_id": "brand-player-guid-001",
"provider_code": "evo",
"round_id": "round_xyz",
"ref_id": "external_ref_003"
}
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
player_id | integer | 예 | Ruby의 내부 플레이어 식별자 |
amount | string | 예 | 복원할 금액, 소수점 문자열 |
transaction_id | string | 예 | 이 롤백의 고유 트랜잭션 식별자 -- 멱등성 처리에 사용 |
username | string | 아니오 | 플레이어의 표시 이름 |
external_player_id | string | 아니오 | 게임 시작 시 전달한 귀사의 안정적인 플레이어 식별자 |
provider_code | string | 예 | 게임 제공사 코드 |
round_id | string | 아니오 | 게임 라운드 식별자 |
ref_id | string | 아니오 | 게임 제공사의 외부 참조 ID |
참고: game_code와 memo는 롤백 요청에 포함되지 않습니다.
응답
{
"balance": "1250.00"
}
| 필드 | 타입 | 설명 |
|---|---|---|
balance | string | 롤백 후 플레이어 잔액 |
참고: balance_before는 롤백 응답에 포함되지 않습니다.
멱등성
이미 성공적으로 처리한 transaction_id로 POST /rollback 요청을 수신한 경우, 원래 호출과 동일한 응답을 반환하십시오. 자금을 다시 복원하지 마십시오.
필드 타입 참조
| 필드 | 타입 | 비고 |
|---|---|---|
player_id | integer | 4개의 모든 콜백에 항상 포함 |
amount | string | 소수점 표현, 예: "100.50". Decimal 또는 동등한 타입으로 파싱하십시오 -- 부동소수점(Floating-point)을 사용하지 마십시오 |
balance | string | 트랜잭션 후 잔액의 소수점 표현 |
balance_before | string | 트랜잭션 전 잔액의 소수점 표현 (debit/credit에만 해당) |
transaction_id | string | 불투명한 고유 문자열; 중복 제거 키로 사용 |
username | string | 없을 수 있음; 플레이어 조회에 의존하지 마십시오 |
external_player_id | string | 게임 시작 시 전달된 귀사의 안정적인 플레이어 식별자; 없을 수 있음 |
provider_code | string | 게임 제공사를 식별하는 짧은 코드 |
round_id | string | 게임 수준의 라운드 식별자 |
game_code | string | 제공사별 게임 식별자 (롤백에는 포함되지 않음) |
bet_amount | string | Credit이 기존 Debit을 정산할 때의 원래 베팅 금액; 없을 수 있음 |
original_transaction_id | string | Credit과 연결된 원래 지갑 거래 ID; 없을 수 있음 |
standalone_credit | boolean | 기존 Debit 없이 발생한 Credit 여부 |
ref_id | string | 게임 제공사의 외부 참조; 없을 수 있음 |
memo | string | 사람이 읽을 수 있는 메모; 없을 수 있음 (롤백에는 포함되지 않음) |
멱등성 요약
| 엔드포인트 | transaction_id | 멱등성 필요 여부 |
|---|---|---|
/balance | 아니오 | 아니오 -- 상태 비저장 읽기 |
/debit | 예 | 예 |
/credit | 예 | 예 |
/rollback | 예 | 예 |
Ruby는 각 transaction_id에 대해 성공 응답을 기록합니다. 네트워크 불확실성으로 인해 Ruby가 동일한 transaction_id로 다시 호출하는 경우, 귀사의 서버는 원래 결과를 반환해야 합니다. Ruby는 재처리에 의존하여 안전성을 보장하지 않지만, 귀사의 서버는 독립적으로 이를 방어해야 합니다.
각 키를 최초 전체 요청에 바인딩하십시오. 완전히 동일한 재시도에는 저장된 결과를 반환하고, 다른 플레이어, 제공사, 금액 또는 컨텍스트로 재사용되면 거부하고 알림을 발생시켜야 합니다.
재시도 정책
현재 실패한 콜백에 대한 애플리케이션 수준 자동 재시도는 없습니다. 비-2xx, 잘못된 성공 응답 또는 5초 초과 타임아웃은 즉시 게임 제공사에 오류로 전달됩니다. 게임 제공사는 원래 이벤트를 독립적으로 재시도할 수 있으며, 이때 동일한 지갑 transaction_id가 사용되므로 멱등성 보장은 필수입니다.
이에 따라 오류 처리를 계획하십시오:
- 수동으로 재시도할 것으로 예상되는 일시적 오류에 대해서만 5xx를 반환하십시오
- 영구적인 비즈니스 오류 (잔액 부족, 플레이어 정지)에 대해서는 4xx를 반환하십시오
- 부하 상태에서도 서버가 5초 이내에 응답할 수 있도록 보장하십시오