GS2-Money SDK for Game Engine API 레퍼런스
모델
EzWallet
지갑
지갑 내 통화는 유료로 구매한 통화와 무료로 얻은 통화로 크게 나누어 관리됩니다.
유료로 구매한 통화는 다시 구매 시점의 단가별로 관리되어, 서비스가 종료되었을 때의 환불이나 자금결제법에 해당하는 잔액이 존재하는지를 집계할 수 있습니다.
지갑에는 슬롯이 있으며, 슬롯마다 서로 다른 잔액을 관리할 수 있습니다.
플랫폼 간에 잔액을 공유할 수 없는 경우에는 플랫폼마다 다른 슬롯을 사용함으로써 나누어 관리할 수 있습니다.
이때 무료로 얻은 통화는 모든 플랫폼에서 공통된 값을 사용할 수도 있습니다.
| 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 | |
|---|---|---|---|---|---|---|
| slot | int | ✓ | 0 ~ 100000000 | 슬롯 번호 플랫폼이나 컨텍스트별로 지갑 잔액을 분리하기 위한 식별자입니다. 슬롯을 달리하면 서로 다른 유료 통화 풀을 관리할 수 있습니다(예: iOS 구매는 슬롯 0, Android는 슬롯 1). 무료 통화는 네임스페이스의 shareFree 설정에 따라 모든 슬롯 간에 공유할 수도 있습니다. | ||
| paid | int | 0 | 0 ~ 2147483646 | 유료 통화 보유량 이 지갑 슬롯 내 유료(구매한) 통화의 합계량입니다. 단가가 0이 아닌 모든 WalletDetail 항목의 합계입니다. 스토어 구매를 통한 충전으로 증가하고, 소비 우선순위에 따라 소비되어 감소합니다. | ||
| free | int | 0 | 0 ~ 2147483646 | 무료 통화 보유량 이 지갑 슬롯 내 무료(지급된) 통화의 합계량입니다. 단가가 0인 WalletDetail 항목에 해당합니다. 네임스페이스에서 shareFree가 활성화된 경우, 이 값은 슬롯 0에서 모든 지갑 슬롯으로 동기화됩니다. | ||
| shareFree | bool | false | 무상 통화 공유 이 지갑의 무상 통화가 모든 슬롯 간에 공유되는지 여부입니다. 이 값은 지갑 생성 시 네임스페이스 설정에서 상속됩니다. true인 경우, 무상 통화가 슬롯 0에서 다른 모든 지갑 슬롯으로 동기화됩니다. | |||
| updatedAt | long | ※ | 현재 시각 | 최종 갱신일시 UNIX 시간・밀리초 ※ 서버가 자동으로 설정 |
메서드
get
플레이어의 유료 통화 지갑 잔액을 취득한다
지정한 슬롯의 플레이어 지갑을 취득하여 유료 통화와 무료 통화 각각의 현재 잔액을 확인할 수 있습니다.
“유료” 통화는 플레이어가 실제 돈으로 구매한 것(예: 100 젬을 ¥120에 구매)이고, “무료” 통화는 게임 플레이를 통해 획득한 것(예: 이벤트 보상, 로그인 보너스)입니다.
일부 기능에서는 유료 통화만을 요구하는 경우가 있기 때문에(예: 특정 가챠나 특별 오퍼) 별도로 관리됩니다.
플레이어의 통화 잔액을 표시하는 데 사용합니다. 예를 들어 상점 화면이나 헤더 UI에서 “젬: 350(유료: 100, 무료: 250)“과 같이 표시하는 데 유용합니다.
Request
| 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128자 | 네임스페이스 이름 네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. | ||
| gameSession | GameSession | ✓ | GameSession | |||
| slot | int | ✓ | 0 ~ 100000000 | 슬롯 번호 플랫폼이나 컨텍스트별로 지갑 잔액을 분리하기 위한 식별자입니다. 슬롯을 달리하면 서로 다른 유료 통화 풀을 관리할 수 있습니다(예: iOS 구매는 슬롯 0, Android는 슬롯 1). 무료 통화는 네임스페이스의 shareFree 설정에 따라 모든 슬롯 간에 공유할 수도 있습니다. |
Result
| 타입 | 설명 | |
|---|---|---|
| item | EzWallet | 지갑 |
구현 예제
var domain = gs2.Money.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Wallet(
slot: 0
);
var item = await domain.ModelAsync(); var domain = gs2.Money.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Wallet(
slot: 0
);
var future = domain.ModelFuture();
yield return future;
var item = future.Result; const auto Domain = Gs2->Money->Namespace(
"namespace-0001" // namespaceName
)->Me(
GameSession
)->Wallet(
0 // slot
);
const auto Future = Domain->Model();
Future->StartSynchronousTask();
if (Future->GetTask().IsError())
{
return false;
}var domain = ez.money.namespace_(
"namespace-0001"
).me(game_session).wallet(
0
)
var async_result = await domain.model()
if async_result.error != null:
push_error(str(async_result.error))
return
var result = async_result.result값 변경 이벤트 핸들링
var domain = gs2.Money.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Wallet(
slot: 0
);
// 이벤트 핸들링 시작
var callbackId = domain.Subscribe(
value => {
// 값이 변화했을 때 호출됨
// value에는 변경 후의 값이 전달됨
}
);
// 이벤트 핸들링 정지
domain.Unsubscribe(callbackId); var domain = gs2.Money.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Wallet(
slot: 0
);
// 이벤트 핸들링 시작
var callbackId = domain.Subscribe(
value => {
// 값이 변화했을 때 호출됨
// value에는 변경 후의 값이 전달됨
}
);
// 이벤트 핸들링 정지
domain.Unsubscribe(callbackId); const auto Domain = Gs2->Money->Namespace(
"namespace-0001" // namespaceName
)->Me(
GameSession
)->Wallet(
0 // slot
);
// 이벤트 핸들링 시작
const auto CallbackId = Domain->Subscribe(
[](TSharedPtr<Gs2::Money::Model::FWallet> value) {
// 값이 변화했을 때 호출됨
// value에는 변경 후의 값이 전달됨
}
);
// 이벤트 핸들링 정지
Domain->Unsubscribe(CallbackId);var domain = ez.money.namespace_(
"namespace-0001"
).me(game_session).wallet(
0
)
# 이벤트 핸들링 시작
var callback_id = domain.subscribe_model(func(value):
# 값이 변화했을 때 호출됨
# value에는 변경 후의 값이 전달됩니다
pass
)
# 이벤트 핸들링 정지
domain.unsubscribe_model(callback_id)이 이벤트는 SDK가 가진 로컬 캐시의 값이 변경되었을 때 호출됩니다.
로컬 캐시는 SDK가 가진 API의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-Distributor를 통한 스탬프 시트의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-JobQueue의 실행에 의해 변화한 것만이 대상이 됩니다.
따라서 이 방법 이외로 값이 변경된 경우 콜백은 호출되지 않습니다.
withdraw
플레이어의 지갑에서 유료 통화 소비하기
플레이어의 지갑에서 지정된 수량의 유료 통화를 차감합니다.
기본값(paidOnly = false)에서는 무료 통화가 먼저 소비되고, 부족한 만큼 유료 통화가 소비됩니다. 이를 통해 플레이어는 구매한 통화를 사용하기 전에 무료 통화를 모두 소진할 수 있습니다.
paidOnly를 true로 설정하면 유료 통화만 소비됩니다. 법률상 유료 통화만 사용해야 하는 기능(예: 일부 지역의 유료 전용 가챠)에 필요합니다.
플레이어가 유료 통화로 구매할 때 사용합니다. 예를 들어 특별한 아이템을 구매하거나 가챠를 뽑기 위해 젬 100개를 소비하는 경우입니다.
참고: GS2-Showcase를 통한 상품 구매의 대가로 통화를 소비하는 경우에는 자동으로 처리되므로, 이 API를 호출할 필요가 없습니다.
Request
| 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128자 | 네임스페이스 이름 네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. | ||
| gameSession | GameSession | ✓ | GameSession | |||
| slot | int | ✓ | 0 ~ 100000000 | 슬롯 번호 플랫폼이나 컨텍스트별로 지갑 잔액을 분리하기 위한 식별자입니다. 슬롯을 달리하면 서로 다른 유료 통화 풀을 관리할 수 있습니다(예: iOS 구매는 슬롯 0, Android는 슬롯 1). 무료 통화는 네임스페이스의 shareFree 설정에 따라 모든 슬롯 간에 공유할 수도 있습니다. | ||
| count | int | ✓ | 1 ~ 2147483646 | 소비할 유료 통화 수량 | ||
| paidOnly | bool | false | 유료 통화만을 대상으로 할지 여부 |
Result
| 타입 | 설명 | |
|---|---|---|
| item | EzWallet | 소비 후 지갑 |
| price | float | 소비한 통화의 가격 |
Error
이 API에는 특별한 예외가 정의되어 있습니다.
GS2-SDK for Game Engine에서는 게임 내에서 핸들링이 필요할 것으로 예상되는 에러를, 일반적인 예외에서 파생된 특수화된 예외로 제공하여 다루기 쉽게 하고 있습니다.
일반적인 에러의 종류와 핸들링 방법은 여기 문서를 참고해 주세요.
| 타입 | 베이스 클래스 | 설명 |
|---|---|---|
| ConflictException | ConflictException | 지갑 조작 처리가 충돌했습니다. 재시도가 필요합니다 |
| InsufficientException | BadRequestException | 지갑의 잔액이 부족합니다 |
구현 예제
try {
var domain = gs2.Money.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Wallet(
slot: 0
);
var result = await domain.WithdrawAsync(
count: 50,
paidOnly: null
);
var item = await result.ModelAsync();
var price = result.Price;
} catch(Gs2.Gs2Money.Exception.ConflictException e) {
// The wallet operation process conflicted. Retry required.
} catch(Gs2.Gs2Money.Exception.InsufficientException e) {
// Wallet balance is insufficient.
} var domain = gs2.Money.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Wallet(
slot: 0
);
var future = domain.WithdrawFuture(
count: 50,
paidOnly: null
);
yield return future;
if (future.Error != null)
{
if (future.Error is Gs2.Gs2Money.Exception.ConflictException)
{
// The wallet operation process conflicted. Retry required.
}
if (future.Error is Gs2.Gs2Money.Exception.InsufficientException)
{
// Wallet balance is insufficient.
}
onError.Invoke(future.Error, null);
yield break;
}
var future2 = future.Result.ModelFuture();
yield return future2;
if (future2.Error != null)
{
onError.Invoke(future2.Error, null);
yield break;
}
var result = future2.Result;
var price = future.Result.Price; const auto Domain = Gs2->Money->Namespace(
"namespace-0001" // namespaceName
)->Me(
GameSession
)->Wallet(
0 // slot
);
const auto Future = Domain->Withdraw(
50 // count
// paidOnly
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError())
{
auto e = Future->GetTask().Error();
if (e->IsChildOf(Gs2::Money::Error::FConflictError::Class))
{
// The wallet operation process conflicted. Retry required.
}
if (e->IsChildOf(Gs2::Money::Error::FInsufficientError::Class))
{
// Wallet balance is insufficient.
}
return false;
}
// 변경된 값 / 결과 값을 취득
const auto Future2 = Future->GetTask().Result()->Model();
Future2->StartSynchronousTask();
if (Future2->GetTask().IsError())
{
return Future2->GetTask().Error();
}
const auto Result = Future2->GetTask().Result();
const auto Price = Result->Price;var domain = ez.money.namespace_(
"namespace-0001"
).me(game_session).wallet(
0
)
var async_result = await domain.withdraw(
50, # count
null # paid_only
)
if async_result.error != null:
if async_result.error is Gs2MoneyConflictException:
# 지갑 조작 처리가 충돌했습니다. 재시도가 필요합니다
pass
if async_result.error is Gs2MoneyInsufficientException:
# 지갑의 잔액이 부족합니다
pass
push_error(str(async_result.error))
return
var result = async_result.result