GS2-Money2 SDK for Game Engine API リファレンス

ゲームエンジン向け GS2-SDK の モデルの仕様 と API のリファレンス

モデル

EzWallet

ウォレット

ウォレット内の通貨は大きく有償で購入した通貨と、無償で入手した通貨が分けて管理されます。
有償で購入した通貨は更に購入時の単価毎に管理され、サービス終了になってしまった際の返金や資金決済法に該当するだけの残高が存在するかが集計できます。

ウォレットにはスロットがあり、スロットごとに異なる残高を管理できます。
プラットフォームをまたいで残高を共有できない場合にはプラットフォーム毎に異なるスロットを利用することで分けて管理することができます。
その際に無償で入手した通貨は全てのプラットフォームで共通した値を利用することもできます。

必須デフォルト値の制限説明
slotint~ 100000000スロット番号
summaryEzWalletSummaryウォレットの状態
sharedFreeCurrencybool無償課金通貨を共有するか
updatedAtlong現在時刻最終更新日時 (UNIX時間 単位:ミリ秒)

EzWalletSummary

ウォレットの状態

必須デフォルト値の制限説明
paidint0~ 2147483646有償課金通貨
freeint0~ 2147483646無償課金通貨
totalint0~ 2147483646総数

EzDepositTransaction

入金トランザクション

必須デフォルト値の制限説明
pricefloat~ 1000000.0購入価格
currencystring{price} > 0~ 8文字通貨コード
countint~ 2147483646所持量

EzSubscribeTransaction

サブスクリプションの購入情報

必須デフォルト値の制限説明
contentNamestring~ 128文字定期課金ストアコンテンツモデル名
storeenum {
    “AppleAppStore”,
    “GooglePlay”,
    “fake”
}
~ 128文字ストア
transactionIdstring~ 1024文字トランザクションID
statusDetailenum {
    “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”
}
~ 128文字ステータス
expiresAtlong有効期限 (UNIX時間 単位:ミリ秒)

store に指定する列挙型の定義

定義説明
AppleAppStoreApple App Store
GooglePlayGoogle Play
fakeFake

statusDetail に指定する列挙型の定義

定義説明
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払い戻しなどで強制的にキャンセルされた

EzSubscriptionStatus

サブスクリプションの契約状況

必須デフォルト値の制限説明
contentNamestring~ 128文字定期課金ストアコンテンツモデル名
userIdstring~ 128文字ユーザーID
statusenum {
    “active”,
    “inactive”
}
~ 128文字ステータス
expiresAtlong有効期限 (UNIX時間 単位:ミリ秒)
detailList<EzSubscribeTransaction>[]~ 100 items契約状況の詳細

status に指定する列挙型の定義

定義説明
active有効
inactive無効

メソッド

get

ウォレットを取得

Request

必須デフォルト値の制限説明
namespaceNamestring~ 128文字ネームスペースの名前
accessTokenstring~ 128文字アクセストークン
slotint~ 100000000スロット番号

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 = 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 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 CallbackId = Domain->Subscribe(
        [](TSharedPtr<Gs2::Money2::Model::FWallet> value) {
            // 値が変化した時に呼び出される
            // value には変更後の値が渡ってくる
        }
    );

    // イベントハンドリングを停止
    Domain->Unsubscribe(CallbackId);

list

ウォレットの一覧を取得

Request

必須デフォルト値の制限説明
namespaceNamestring~ 128文字ネームスペースの名前
accessTokenstring~ 128文字アクセストークン

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 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 CallbackId = Domain->SubscribeWallets(
        []() {
            // リストの要素が変化した時に呼び出される
        }
    );

    // イベントハンドリングを停止
    Domain->UnsubscribeWallets(CallbackId);

withdraw

ウォレットから残高を消費

Request

必須デフォルト値の制限説明
namespaceNamestring~ 128文字ネームスペースの名前
accessTokenstring~ 128文字アクセストークン
slotint~ 100000000スロット番号
withdrawCountint1 ~ 2147483646消費する課金通貨の数量
paidOnlyboolfalse有償課金通貨のみを対象とするか

Result

説明
itemEzWallet消費後のウォレット
withdrawTransactionsList<EzDepositTransaction>消費した入金トランザクションリスト

Error

このAPIには特別な例外が定義されています。
GS2-SDK for GameEngine ではゲーム内でハンドリングが必要そうなエラーは一般的な例外から派生した特殊化した例外を用意することでハンドリングしやすくしています。
一般的なエラーの種類や、ハンドリング方法は こちら のドキュメントを参考にしてください。

基底クラス説明
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.Conflict e) {
    // The wallet operation process conflicted. Retry required.
} catch(Gs2.Gs2Money2.Exception.Insufficient 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;
    }

    // obtain changed values / result values
    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;

allocateSubscriptionStatus

レシートからサブスクリプションの契約状況を割り当て

Request

必須デフォルト値の制限説明
namespaceNamestring~ 128文字ネームスペースの名前
accessTokenstring~ 128文字アクセストークン
receiptstring~ 1024文字レシート

Result

説明
itemEzSubscriptionStatusサブスクリプションの契約状況

Error

このAPIには特別な例外が定義されています。
GS2-SDK for GameEngine ではゲーム内でハンドリングが必要そうなエラーは一般的な例外から派生した特殊化した例外を用意することでハンドリングしやすくしています。
一般的なエラーの種類や、ハンドリング方法は こちら のドキュメントを参考にしてください。

基底クラス説明
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.AlreadyUsed 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;
    }

    // obtain changed values / result values
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();

getSubscriptionStatus

サブスクリプションの契約状況を取得

Request

必須デフォルト値の制限説明
namespaceNamestring~ 128文字ネームスペースの名前
accessTokenstring~ 128文字アクセストークン
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 = 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 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 CallbackId = Domain->Subscribe(
        [](TSharedPtr<Gs2::Money2::Model::FSubscriptionStatus> value) {
            // 値が変化した時に呼び出される
            // value には変更後の値が渡ってくる
        }
    );

    // イベントハンドリングを停止
    Domain->Unsubscribe(CallbackId);

listSubscriptionStatuses

サブスクリプションの契約状況の一覧を取得

Request

必須デフォルト値の制限説明
namespaceNamestring~ 128文字ネームスペースの名前
accessTokenstring~ 128文字アクセストークン

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 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 CallbackId = Domain->SubscribeSubscriptionStatuses(
        []() {
            // リストの要素が変化した時に呼び出される
        }
    );

    // イベントハンドリングを停止
    Domain->UnsubscribeSubscriptionStatuses(CallbackId);

takeOverSubscriptionStatus

すでに他のユーザーに割り当てられているサブスクリプションの契約状況を引き継ぐ

Request

必須デフォルト値の制限説明
namespaceNamestring~ 128文字ネームスペースの名前
accessTokenstring~ 128文字アクセストークン
receiptstring~ 1024文字レシート

Result

説明
itemEzSubscriptionStatusサブスクリプションの契約状況

Error

このAPIには特別な例外が定義されています。
GS2-SDK for GameEngine ではゲーム内でハンドリングが必要そうなエラーは一般的な例外から派生した特殊化した例外を用意することでハンドリングしやすくしています。
一般的なエラーの種類や、ハンドリング方法は こちら のドキュメントを参考にしてください。

基底クラス説明
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.LockPeriodNotElapsed 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;
    }

    // obtain changed values / result values
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().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;
    });