Documentation index for AI agents

GS2-Money2 SDK for Game Engine API 레퍼런스

게임 엔진용 GS2-Money2 SDK의 모델 사양과 API 레퍼런스

모델

EzWallet

지갑

지갑 내 통화는 유료로 구매한 통화와 무료로 얻은 통화로 크게 나누어 관리됩니다.
유료로 구매한 통화는 다시 구매 시점의 단가별로 관리되어, 서비스가 종료되었을 때의 환불이나 자금결제법에 해당하는 잔액이 존재하는지를 집계할 수 있습니다.

지갑에는 슬롯이 있으며, 슬롯마다 서로 다른 잔액을 관리할 수 있습니다.
플랫폼 간에 잔액을 공유할 수 없는 경우에는 플랫폼마다 다른 슬롯을 사용함으로써 나누어 관리할 수 있습니다.
이때 무료로 얻은 통화는 모든 플랫폼에서 공통된 값을 사용할 수도 있습니다.

타입활성화 조건필수기본값값 제한설명
slotint
0 ~ 100000000슬롯 번호
지갑 슬롯을 식별합니다. 플랫폼 간 잔액 공유가 허용되지 않는 경우, 서로 다른 슬롯을 사용하여 플랫폼별로 통화를 나누어 관리할 수 있습니다(예: iOS용과 Android용).
summaryEzWalletSummary
지갑 상태
지갑의 현재 잔액 요약으로, 유료 통화, 무료 통화, 합계 금액으로 나누어 표시됩니다. 입금 트랜잭션으로부터 산출됩니다.
sharedFreeCurrencybool
무료 통화 공유 여부
이 지갑의 무료 통화가 모든 슬롯 간에 공유되는지 여부를 나타냅니다. 지갑 생성 시 네임스페이스 설정으로부터 상속됩니다.
updatedAtlong
현재 시각최종 갱신일시
UNIX 시간·밀리초
※ 서버가 자동으로 설정

EzSubscribeTransaction

구독 구매 정보

스토어 플랫폼으로부터의 구독 구매 레코드를 나타냅니다. 유효, 트라이얼, 초회 할인, 유예 기간, 해지, 기한 만료, 취소 등 라이프사이클을 통한 상세한 구독 상태를 추적합니다. 각 트랜잭션은 특정 스토어 플랫폼과 사용자에 연결됩니다.

타입활성화 조건필수기본값값 제한설명
contentNamestring
~ 128자스토어 구독 콘텐츠 모델 이름
store문자열 열거형
enum {
  “AppleAppStore”,
  “GooglePlay”,
  “fake”
}
스토어
구매가 이루어진 스토어 플랫폼입니다. 영수증 검증에 사용되는 검증 방법을 결정합니다.
정의설명
AppleAppStoreApple App Store
GooglePlayGoogle Play
fakeFake
transactionIdstring
~ 1024자트랜잭션 ID
스토어 플랫폼에 의해 할당된 고유한 트랜잭션 식별자입니다. 동일 구매의 중복 처리를 방지하는 데 사용됩니다.
statusDetail문자열 열거형
enum {
  “active@active”,
  “active@converted_from_trial”,
  “active@in_trial”,
  “active@in_intro_offer”,
  “grace@canceled”,
  “grace@grace_period”,
  “grace@on_hold”,
  “inactive@expired”,
  “inactive@revoked”
}
상태
상세한 구독 상태입니다. 간략 카테고리(active/grace/inactive) 뒤에 구체적인 상태가 이어집니다. active 상태는 구독을 사용할 수 있음을, grace 상태는 결제에 문제가 있지만 일시적으로 이용 가능함을, inactive 상태는 구독이 더 이상 유효하지 않음을 나타냅니다.
정의설명
active@active유효
active@converted_from_trial무료 체험판이 종료되고 유료 플랜으로 전환됨
active@in_trial무료 체험판 이용 중
active@in_intro_offer최초 할인 기간 중
grace@canceled사용자가 수동으로 해지함
grace@grace_period결제가 실패했지만 유예 기간 이내
grace@on_hold결제가 실패했으며, 일정 기간 내에 결제가 완료되지 않으면 이용이 제한됨
inactive@expired구독이 만료됨
inactive@revoked환불 등으로 강제로 취소됨
expiresAtlong
유효기간
이 구독 트랜잭션이 만료되는 일시입니다. 스토어 플랫폼에 의해 구독이 갱신되면 함께 업데이트됩니다.

EzSubscriptionStatus

구독 계약 상태

특정 구독 콘텐츠에 대한 사용자의 구독 계약 상태를 추적합니다. 상세한 구독 트랜잭션 상태로부터 도출된 간이 유효/무효 상태와 유효기간, 관련 구독 트랜잭션 목록을 제공합니다.

타입활성화 조건필수기본값값 제한설명
contentNamestring
~ 128자스토어 구독 콘텐츠 모델 이름
userIdstring~ 128자사용자ID
status문자열 열거형
enum {
  “active”,
  “inactive”
}
상태
간이 구독 상태입니다. “active"는 유효, 트라이얼, 초회 할인, 유예 기간 상태를 포함합니다. “inactive"는 기한 만료와 취소 상태를 포함합니다.
정의설명
active유효
inactive비활성
expiresAtlong
유효기간
구독이 만료되는 일시입니다. 구독이 갱신되거나 상태가 변경될 때 업데이트됩니다.
detailList<EzSubscribeTransaction>[]0 ~ 100 items계약 상태 상세 정보
이 구독과 관련된 구독 트랜잭션 목록입니다. 각 트랜잭션은 스토어 플랫폼의 구매 레코드로, 상세한 상태 정보(유효, 트라이얼, 유예 기간, 기한 만료, 취소 등)를 가집니다.

EzStoreContentModel

스토어 콘텐츠 모델

다양한 스토어 플랫폼의 콘텐츠를 저장하는 모델입니다.

타입활성화 조건필수기본값값 제한설명
namestring
~ 128자스토어 콘텐츠 모델 이름
metadatastring~ 1024자메타데이터
메타데이터에는 임의의 값을 설정할 수 있습니다.
이 값들은 GS2의 동작에는 영향을 주지 않으므로, 게임 내에서 사용하는 정보를 저장하는 용도로 사용할 수 있습니다.
appleAppStoreEzAppleAppStoreContentApple App Store의 콘텐츠
이 스토어 콘텐츠의 Apple App Store 상품 정보(프로덕트 ID)입니다. 영수증 검증 시 구매한 상품과의 대조에 사용됩니다.
googlePlayEzGooglePlayContentGoogle Play의 콘텐츠
이 스토어 콘텐츠의 Google Play 상품 정보(프로덕트 ID)입니다. 영수증 검증 시 구매한 상품과의 대조에 사용됩니다.

EzWalletSummary

지갑 상태

지갑의 통화 잔액을 요약해서 보여주며, 유상 금액과 무상 금액을 구분합니다. 지갑 내 모든 입금 트랜잭션을 가격을 기준으로 집계하여 산출됩니다(가격 > 0은 유상, 가격 = 0은 무상).

타입활성화 조건필수기본값값 제한설명
paidint00 ~ 2147483646유상 통화
실제 돈으로 구매된 통화의 합계량입니다(가격 > 0인 입금 트랜잭션).
freeint00 ~ 2147483646무상 통화
무상으로 획득한 통화의 합계량입니다(가격 = 0인 입금 트랜잭션). 로그인 보너스나 이벤트 보상 등이 포함됩니다.
totalint00 ~ 2147483646총수
통화 잔액의 합계(유상 + 무상)입니다. 지갑에서 이용 가능한 전체 수량을 나타냅니다.

EzDepositTransaction

입금 트랜잭션

지갑 내 단일 입금 레코드를 나타냅니다. 유상 입금(가격 > 0)은 정확한 환불 계산과 자금결제법 준수를 위해 단가별로 추적됩니다. 무상 입금(가격 = 0)은 별도로 추적됩니다. 출금 시에는 네임스페이스의 재화 소비 우선순위에 기반하여 입금 트랜잭션이 소비됩니다.

타입활성화 조건필수기본값값 제한설명
pricedouble
0.0 ~ 100000000.0구매 가격
이 입금에 대해 현지 통화로 지급된 실제 금액입니다. 0은 무료 통화를 나타냅니다. 환불 목적의 단가 계산에 사용됩니다.
currencystring{price} > 0
✓※
~ 8자통화 코드
실제 결제의 ISO 통화 코드(예: “JPY”, “USD”)입니다. 유료 입금(가격 > 0)의 경우에만 적용됩니다.

※ price이(가) 0 보다 크면 필수
countint
0 ~ 2147483646과금 통화 수량
이 입금에서의 가상 통화 단위 수입니다. 지갑에서 출금되면 감소합니다.

EzAppleAppStoreContent

Apple App Store의 콘텐츠

인앱 결제 상품에 대응하는 Apple App Store의 프로덕트 ID를 포함합니다. 영수증 검증 시 대조에 사용됩니다.

타입활성화 조건필수기본값값 제한설명
productIdstring~ 1024자프로덕트 ID
이 인앱 결제 아이템에 대해 App Store Connect에 등록된 Apple App Store의 프로덕트 식별자입니다.

EzGooglePlayContent

Google Play의 콘텐츠

인앱 결제 상품에 대응하는 Google Play의 프로덕트 ID를 포함합니다. 영수증 검증 시 대조에 사용됩니다.

타입활성화 조건필수기본값값 제한설명
productIdstring~ 1024자프로덕트 ID
이 인앱 결제 아이템에 대해 Google Play Console에 등록되어 있는 Google Play의 프로덕트 식별자입니다.

EzAppleAppStoreSubscriptionContent

Apple App Store의 기간 결제 콘텐츠

구독 기반 상품의 Apple App Store 구독 그룹 식별자를 포함합니다. 자동 갱신 구독의 관리와 검증에 사용됩니다.

타입활성화 조건필수기본값값 제한설명
subscriptionGroupIdentifierstring~ 64자구독 그룹 ID
App Store Connect에 등록된 구독 그룹 식별자입니다. 동일 그룹 내의 구독은 상호 배타적이며, 사용자는 동시에 하나만 계약할 수 있습니다.

EzGooglePlaySubscriptionContent

Google Play 구독 콘텐츠

구독 기반 상품의 Google Play 프로덕트 ID를 포함합니다. Google Play에서 자동 갱신 구독을 관리하고 검증하는 데 사용됩니다.

타입활성화 조건필수기본값값 제한설명
productIdstring~ 1024자프로덕트 ID

메서드

get

플레이어의 유료 통화 지갑 잔액을 취득한다

지정한 슬롯의 플레이어 지갑을 취득하여 유료 통화와 무료 통화 각각의 현재 잔액을 확인할 수 있습니다.
“유료” 통화는 플레이어가 실제 돈으로 구매한 것(예: 100 젬을 ¥120에 구매)이고, “무료” 통화는 게임 플레이를 통해 획득한 것(예: 이벤트 보상, 로그인 보너스)입니다.
일부 기능에서는 유료 통화만을 요구하는 경우가 있기 때문에(예: 특정 가챠나 특별 오퍼) 별도로 관리됩니다.
플레이어의 통화 잔액을 표시하는 데 사용합니다. 예를 들어 상점 화면이나 헤더 UI에서 “젬: 350(유료: 100, 무료: 250)“과 같이 표시하는 데 유용합니다.

Request

타입활성화 조건필수기본값값 제한설명
namespaceNamestring
~ 128자네임스페이스 이름
네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다.
gameSessionGameSession
GameSession
slotint
0 ~ 100000000슬롯 번호
지갑 슬롯을 식별합니다. 플랫폼 간 잔액 공유가 허용되지 않는 경우, 서로 다른 슬롯을 사용하여 플랫폼별로 통화를 나누어 관리할 수 있습니다(예: iOS용과 Android용).

Result

타입설명
itemEzWallet지갑

구현 예제

    var domain = gs2.Money2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Wallet(
        slot: 0
    );
    var item = await domain.ModelAsync();
    var domain = gs2.Money2.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->Money2->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.money2.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.Money2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Wallet(
        slot: 0
    );

    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    domain.Unsubscribe(callbackId);
    var domain = gs2.Money2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Wallet(
        slot: 0
    );

    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    domain.Unsubscribe(callbackId);
    const auto Domain = Gs2->Money2->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->Wallet(
        0 // slot
    );

    // 이벤트 핸들링 시작
    const auto CallbackId = Domain->Subscribe(
        [](TSharedPtr<Gs2::Money2::Model::FWallet> value) {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    Domain->Unsubscribe(CallbackId);
var domain = ez.money2.namespace_(
        "namespace-0001"
    ).me(game_session).wallet(
        0
    )

# 이벤트 핸들링 시작
var callback_id = domain.subscribe_model(func(value):
    # 값이 변화했을 때 호출됨
    # value에는 변경 후의 값이 전달됩니다
    pass
)

# 이벤트 핸들링 정지
domain.unsubscribe_model(callback_id)

list

플레이어의 과금 통화 지갑 목록 조회

플레이어가 현재 보유한 모든 지갑을 조회합니다. 각 지갑은 독립적인 잔액을 가지며, 유상 통화와 무상 통화가 별도로 관리됩니다.
“유상” 통화는 플레이어가 실제 화폐로 구매한 것(예: 100젬을 120엔에 구매)이며, “무상” 통화는 게임 플레이를 통해 획득한 것(예: 이벤트 보상, 로그인 보너스)입니다.
플레이어의 통화 전체 현황을 표시할 때 사용합니다. 예를 들어, 통화 관리 화면에서 모든 지갑 슬롯과 잔액을 목록으로 표시하는 경우에 유용합니다.

Request

타입활성화 조건필수기본값값 제한설명
namespaceNamestring
~ 128자네임스페이스 이름
네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다.
gameSessionGameSession
GameSession

Result

타입설명
itemsList<EzWallet>지갑 목록
nextPageTokenstring목록의 나머지를 취득하기 위한 페이지 토큰

구현 예제

    var domain = gs2.Money2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var items = await domain.WalletsAsync(
    ).ToListAsync();
    var domain = gs2.Money2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var it = domain.Wallets(
    );
    List<EzWallet> items = new List<EzWallet>();
    while (it.HasNext())
    {
        yield return it.Next();
        if (it.Error != null)
        {
            onError.Invoke(it.Error, null);
            break;
        }
        if (it.Current != null)
        {
            items.Add(it.Current);
        }
        else
        {
            break;
        }
    }
    const auto Domain = Gs2->Money2->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    );
    const auto It = Domain->Wallets(
    );
    TArray<Gs2::UE5::Money2::Model::FEzWalletPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }
값 변경 이벤트 핸들링
    var domain = gs2.Money2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );

    // 이벤트 핸들링 시작
    var callbackId = domain.SubscribeWallets(
        () => {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    domain.UnsubscribeWallets(callbackId);
    var domain = gs2.Money2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );

    // 이벤트 핸들링 시작
    var callbackId = domain.SubscribeWallets(
        () => {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    domain.UnsubscribeWallets(callbackId);
    const auto Domain = Gs2->Money2->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    );

    // 이벤트 핸들링 시작
    const auto CallbackId = Domain->SubscribeWallets(
        []() {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    Domain->UnsubscribeWallets(CallbackId);

withdraw

플레이어의 지갑에서 유료 통화 소비하기

플레이어의 지갑에서 지정된 수량의 유료 통화를 차감합니다.
기본값(paidOnly = false)에서는 무료 통화가 먼저 소비되고, 부족한 만큼 유료 통화가 소비됩니다. 이를 통해 플레이어는 구매한 통화를 사용하기 전에 무료 통화를 모두 소진할 수 있습니다.
paidOnly를 true로 설정하면 유료 통화만 소비됩니다. 법률상 유료 통화만 사용해야 하는 기능(예: 일부 지역의 유료 전용 가챠)에 필요합니다.
플레이어가 유료 통화로 구매할 때 사용합니다. 예를 들어 특별한 아이템을 구매하거나 가챠를 뽑기 위해 젬 100개를 소비하는 경우입니다.
참고: GS2-Showcase를 통한 상품 구매의 대가로 통화를 소비하는 경우에는 자동으로 처리되므로, 이 API를 호출할 필요가 없습니다.

Request

타입활성화 조건필수기본값값 제한설명
namespaceNamestring
~ 128자네임스페이스 이름
네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다.
gameSessionGameSession
GameSession
slotint
0 ~ 100000000슬롯 번호
지갑 슬롯을 식별합니다. 플랫폼 간 잔액 공유가 허용되지 않는 경우, 서로 다른 슬롯을 사용하여 플랫폼별로 통화를 나누어 관리할 수 있습니다(예: iOS용과 Android용).
withdrawCountint
1 ~ 2147483646소비할 유료 통화 수량
paidOnlyboolfalse유료 통화만을 대상으로 할지 여부

Result

타입설명
itemEzWallet소비 후 지갑
withdrawTransactionsList<EzDepositTransaction>소비한 입금 트랜잭션 목록

Error

이 API에는 특별한 예외가 정의되어 있습니다.
GS2-SDK for Game Engine에서는 게임 내에서 핸들링이 필요할 것으로 예상되는 에러를, 일반적인 예외에서 파생된 특수화된 예외로 제공하여 다루기 쉽게 하고 있습니다.
일반적인 에러의 종류와 핸들링 방법은 여기 문서를 참고해 주세요.

타입베이스 클래스설명
ConflictExceptionConflictException지갑 조작 처리가 충돌했습니다. 재시도가 필요합니다
InsufficientExceptionBadRequestException지갑의 잔액이 부족합니다

구현 예제

try {
    var domain = gs2.Money2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Wallet(
        slot: 0
    );
    var result = await domain.WithdrawAsync(
        withdrawCount: 50,
        paidOnly: null
    );
    var item = await result.ModelAsync();
    var withdrawTransactions = result.WithdrawTransactions;
} catch(Gs2.Gs2Money2.Exception.ConflictException e) {
    // The wallet operation process conflicted. Retry required.
} catch(Gs2.Gs2Money2.Exception.InsufficientException e) {
    // Wallet balance is insufficient.
}
    var domain = gs2.Money2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Wallet(
        slot: 0
    );
    var future = domain.WithdrawFuture(
        withdrawCount: 50,
        paidOnly: null
    );
    yield return future;
    if (future.Error != null)
    {
        if (future.Error is Gs2.Gs2Money2.Exception.ConflictException)
        {
            // The wallet operation process conflicted. Retry required.
        }
        if (future.Error is Gs2.Gs2Money2.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 withdrawTransactions = future.Result.WithdrawTransactions;
    const auto Domain = Gs2->Money2->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->Wallet(
        0 // slot
    );
    const auto Future = Domain->Withdraw(
        50 // withdrawCount
        // paidOnly
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        auto e = Future->GetTask().Error();
        if (e->IsChildOf(Gs2::Money2::Error::FConflictError::Class))
        {
            // The wallet operation process conflicted. Retry required.
        }
        if (e->IsChildOf(Gs2::Money2::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 WithdrawTransactions = Result->WithdrawTransactions;
var domain = ez.money2.namespace_(
        "namespace-0001"
    ).me(game_session).wallet(
        0
    )

var async_result = await domain.withdraw(
    50, # withdraw_count
    null # paid_only
)
if async_result.error != null:
    if async_result.error is Gs2Money2ConflictException:
        # 지갑 조작 처리가 충돌했습니다. 재시도가 필요합니다
        pass
    if async_result.error is Gs2Money2InsufficientException:
        # 지갑의 잔액이 부족합니다
        pass
    push_error(str(async_result.error))
    return

var result = async_result.result

allocateSubscriptionStatus

스토어 영수증을 사용하여 구독 등록

App Store나 Google Play의 영수증을 검증하여 구독 구매를 플레이어에게 연결합니다.
플레이어가 단말기에서 구독(예: “월정액 패스”)을 구매한 후, 스토어 영수증을 이 API에 전달하여 서버 측에서 구독을 활성화합니다.
스토어 구매를 플레이어의 계정에 연결하기 위한 필수 단계입니다. 이 호출이 없으면 서버는 플레이어가 계약했음을 인식할 수 없습니다.
구매 흐름에서 사용합니다. 예를 들어, 인앱 결제 대화상자에서 플레이어가 “월정액 패스"를 구매한 후 영수증을 전송하여 VIP 혜택을 활성화하는 경우입니다.

Request

타입활성화 조건필수기본값값 제한설명
namespaceNamestring
~ 128자네임스페이스 이름
네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다.
gameSessionGameSessionGameSession
receiptstring
~ 1024자영수증

Result

타입설명
itemEzSubscriptionStatus구독 계약 상태

Error

이 API에는 특별한 예외가 정의되어 있습니다.
GS2-SDK for Game Engine에서는 게임 내에서 핸들링이 필요할 것으로 예상되는 에러를, 일반적인 예외에서 파생된 특수화된 예외로 제공하여 다루기 쉽게 하고 있습니다.
일반적인 에러의 종류와 핸들링 방법은 여기 문서를 참고해 주세요.

타입베이스 클래스설명
AlreadyUsedExceptionBadRequestException이미 해당 구독 계약은 다른 사용자가 사용하고 있습니다

구현 예제

try {
    var domain = gs2.Money2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var result = await domain.AllocateSubscriptionStatusAsync(
        receipt: "{\"Store\": \"AppleAppStore\", \"TransactionID\": \"transaction-0001\", \"Payload\": \"payload\"}"
    );
    var item = await result.ModelAsync();
} catch(Gs2.Gs2Money2.Exception.AlreadyUsedException e) {
    // The subscription contract for that period has already been used by another user.
}
    var domain = gs2.Money2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var future = domain.AllocateSubscriptionStatusFuture(
        receipt: "{\"Store\": \"AppleAppStore\", \"TransactionID\": \"transaction-0001\", \"Payload\": \"payload\"}"
    );
    yield return future;
    if (future.Error != null)
    {
        if (future.Error is Gs2.Gs2Money2.Exception.AlreadyUsedException)
        {
            // The subscription contract for that period has already been used by another user.
        }
        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;
    const auto Domain = Gs2->Money2->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    );
    const auto Future = Domain->AllocateSubscriptionStatus(
        "{\"Store\": \"AppleAppStore\", \"TransactionID\": \"transaction-0001\", \"Payload\": \"payload\"}" // receipt
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        auto e = Future->GetTask().Error();
        if (e->IsChildOf(Gs2::Money2::Error::FAlreadyUsedError::Class))
        {
            // The subscription contract for that period has already been used by another user.
        }
        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();
var domain = ez.money2.namespace_(
        "namespace-0001"
    ).me(game_session)

var async_result = await domain.allocate_subscription_status(
    "{\"Store\": \"AppleAppStore\", \"TransactionID\": \"transaction-0001\", \"Payload\": \"payload\"}" # receipt
)
if async_result.error != null:
    if async_result.error is Gs2Money2AlreadyUsedException:
        # 이미 해당 구독 계약은 다른 사용자가 사용하고 있습니다
        pass
    push_error(str(async_result.error))
    return

var result = async_result.result

getSubscriptionStatus

특정 구독 계약 상태 조회

콘텐츠 이름을 지정하여 특정 구독의 계약 상태를 조회합니다.
상태에는 플레이어가 현재 계약 중인지 여부와 유효기간 등의 관련 정보가 포함됩니다.
플레이어가 특정 구독을 보유하고 있는지 확인할 때 사용합니다. 예를 들어, 데일리 보너스 보상을 지급하기 전에 “월정액 패스"가 유효한지 확인하거나, 플레이어 프로필에 “VIP 멤버십: 유효"라고 표시하는 경우에 유용합니다.

Request

타입활성화 조건필수기본값값 제한설명
namespaceNamestring
~ 128자네임스페이스 이름
네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다.
gameSessionGameSessionGameSession
contentNamestring
~ 128자스토어 구독 콘텐츠 모델 이름

Result

타입설명
itemEzSubscriptionStatus구독 계약 상태

구현 예제

    var domain = gs2.Money2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).SubscriptionStatus(
        contentName: "content-0001"
    );
    var item = await domain.ModelAsync();
    var domain = gs2.Money2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).SubscriptionStatus(
        contentName: "content-0001"
    );
    var future = domain.ModelFuture();
    yield return future;
    var item = future.Result;
    const auto Domain = Gs2->Money2->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->SubscriptionStatus(
        "content-0001" // contentName
    );
    const auto Future = Domain->Model();
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }
var domain = ez.money2.namespace_(
        "namespace-0001"
    ).me(game_session).subscription_status(
        "content-0001"
    )

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.Money2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).SubscriptionStatus(
        contentName: "content-0001"
    );

    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    domain.Unsubscribe(callbackId);
    var domain = gs2.Money2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).SubscriptionStatus(
        contentName: "content-0001"
    );

    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    domain.Unsubscribe(callbackId);
    const auto Domain = Gs2->Money2->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->SubscriptionStatus(
        "content-0001" // contentName
    );

    // 이벤트 핸들링 시작
    const auto CallbackId = Domain->Subscribe(
        [](TSharedPtr<Gs2::Money2::Model::FSubscriptionStatus> value) {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    Domain->Unsubscribe(CallbackId);
var domain = ez.money2.namespace_(
        "namespace-0001"
    ).me(game_session).subscription_status(
        "content-0001"
    )

# 이벤트 핸들링 시작
var callback_id = domain.subscribe_model(func(value):
    # 값이 변화했을 때 호출됨
    # value에는 변경 후의 값이 전달됩니다
    pass
)

# 이벤트 핸들링 정지
domain.unsubscribe_model(callback_id)

listSubscriptionStatuses

플레이어의 구독 계약 상태 목록 조회

플레이어가 계약했을 가능성이 있는 모든 구독 콘텐츠의 계약 상태를 조회합니다.
구독이란 App Store나 Google Play에서 구매하는 “월정액 패스"나 “VIP 멤버십"과 같은 정기 결제를 의미합니다.
각 상태는 플레이어가 해당 콘텐츠를 현재 계약하고 있는지 여부를 나타냅니다.
구독 개요를 표시할 때 사용합니다. 예를 들어, 멤버십 화면에서 “월정액 패스: 유효(3월 15일까지)”, “VIP 멤버십: 미계약"과 같이 표시하는 경우에 유용합니다.

Request

타입활성화 조건필수기본값값 제한설명
namespaceNamestring
~ 128자네임스페이스 이름
네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다.
gameSessionGameSessionGameSession

Result

타입설명
itemsList<EzSubscriptionStatus>구독 계약 상태 목록

구현 예제

    var domain = gs2.Money2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var items = await domain.SubscriptionStatusesAsync(
    ).ToListAsync();
    var domain = gs2.Money2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var it = domain.SubscriptionStatuses(
    );
    List<EzSubscriptionStatus> items = new List<EzSubscriptionStatus>();
    while (it.HasNext())
    {
        yield return it.Next();
        if (it.Error != null)
        {
            onError.Invoke(it.Error, null);
            break;
        }
        if (it.Current != null)
        {
            items.Add(it.Current);
        }
        else
        {
            break;
        }
    }
    const auto Domain = Gs2->Money2->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    );
    const auto It = Domain->SubscriptionStatuses(
    );
    TArray<Gs2::UE5::Money2::Model::FEzSubscriptionStatusPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }
값 변경 이벤트 핸들링
    var domain = gs2.Money2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );

    // 이벤트 핸들링 시작
    var callbackId = domain.SubscribeSubscriptionStatuses(
        () => {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    domain.UnsubscribeSubscriptionStatuses(callbackId);
    var domain = gs2.Money2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );

    // 이벤트 핸들링 시작
    var callbackId = domain.SubscribeSubscriptionStatuses(
        () => {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    domain.UnsubscribeSubscriptionStatuses(callbackId);
    const auto Domain = Gs2->Money2->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    );

    // 이벤트 핸들링 시작
    const auto CallbackId = Domain->SubscribeSubscriptionStatuses(
        []() {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    Domain->UnsubscribeSubscriptionStatuses(CallbackId);

takeOverSubscriptionStatus

다른 계정에서 이 플레이어에게 구독 이전

현재 다른 플레이어 계정에 연결되어 있는 구독을 이 플레이어의 계정으로 이동합니다.
플레이어가 계정 이전을 수행하는 경우에 사용합니다. 예를 들어, 기기 변경으로 새 계정을 생성한 경우, 스토어 영수증을 사용하여 기존 계정의 “월정액 패스” 구독을 새 계정으로 이전할 수 있습니다.
구독은 기존 계정에서 해제되어 새 계정에 연결되므로, 기존 계정에서는 더 이상 구독 혜택을 받을 수 없게 됩니다.
계정 이전 흐름에서 사용합니다. 예를 들어, 플레이어가 새 계정으로 로그인하여 이전을 확인한 후 호출합니다.

Request

타입활성화 조건필수기본값값 제한설명
namespaceNamestring
~ 128자네임스페이스 이름
네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다.
gameSessionGameSessionGameSession
receiptstring
~ 1024자영수증

Result

타입설명
itemEzSubscriptionStatus구독 계약 상태

Error

이 API에는 특별한 예외가 정의되어 있습니다.
GS2-SDK for Game Engine에서는 게임 내에서 핸들링이 필요할 것으로 예상되는 에러를, 일반적인 예외에서 파생된 특수화된 예외로 제공하여 다루기 쉽게 하고 있습니다.
일반적인 에러의 종류와 핸들링 방법은 여기 문서를 참고해 주세요.

타입베이스 클래스설명
LockPeriodNotElapsedExceptionBadRequestException마지막 사용자 전환 이후 잠금 기간이 경과하지 않았습니다

구현 예제

try {
    var domain = gs2.Money2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var result = await domain.TakeOverSubscriptionStatusAsync(
        receipt: "{\"Store\": \"AppleAppStore\", \"TransactionID\": \"transaction-0001\", \"Payload\": \"payload\"}"
    );
    var item = await result.ModelAsync();
} catch(Gs2.Gs2Money2.Exception.LockPeriodNotElapsedException e) {
    // The lock period has not elapsed since the last user change.
}
    var domain = gs2.Money2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var future = domain.TakeOverSubscriptionStatusFuture(
        receipt: "{\"Store\": \"AppleAppStore\", \"TransactionID\": \"transaction-0001\", \"Payload\": \"payload\"}"
    );
    yield return future;
    if (future.Error != null)
    {
        if (future.Error is Gs2.Gs2Money2.Exception.LockPeriodNotElapsedException)
        {
            // The lock period has not elapsed since the last user change.
        }
        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;
    const auto Domain = Gs2->Money2->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    );
    const auto Future = Domain->TakeOverSubscriptionStatus(
        "{\"Store\": \"AppleAppStore\", \"TransactionID\": \"transaction-0001\", \"Payload\": \"payload\"}" // receipt
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        auto e = Future->GetTask().Error();
        if (e->IsChildOf(Gs2::Money2::Error::FLockPeriodNotElapsedError::Class))
        {
            // The lock period has not elapsed since the last user change.
        }
        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();
var domain = ez.money2.namespace_(
        "namespace-0001"
    ).me(game_session)

var async_result = await domain.take_over_subscription_status(
    "{\"Store\": \"AppleAppStore\", \"TransactionID\": \"transaction-0001\", \"Payload\": \"payload\"}" # receipt
)
if async_result.error != null:
    if async_result.error is Gs2Money2LockPeriodNotElapsedException:
        # 마지막 사용자 전환 이후 잠금 기간이 경과하지 않았습니다
        pass
    push_error(str(async_result.error))
    return

var result = async_result.result

이벤트 핸들러

OnChangeSubscriptionStatus

구독 계약 상태가 변경되었을 때 사용하는 푸시 알림

이름타입설명
namespaceNamestring네임스페이스 이름
네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다.
userIdstring사용자ID
contentNamestring스토어 구독 콘텐츠 모델 이름

구현 예제

    gs2.Money2.OnChangeSubscriptionStatus += notification =>
    {
        var namespaceName = notification.NamespaceName;
        var userId = notification.UserId;
        var contentName = notification.ContentName;
    };
    gs2.Money2.OnChangeSubscriptionStatus += notification =>
    {
        var namespaceName = notification.NamespaceName;
        var userId = notification.UserId;
        var contentName = notification.ContentName;
    };
    Gs2->Money2->OnChangeSubscriptionStatus().AddLambda([](const auto Notification)
    {
        const auto NamespaceName = Notification->NamespaceNameValue;
        const auto UserId = Notification->UserIdValue;
        const auto ContentName = Notification->ContentNameValue;
    });
    ez.money2.change_subscription_status.connect(func(notification):
        var namespace_name = notification.namespace_name
        var user_id = notification.user_id
        var content_name = notification.content_name
    )