GS2-Money2
ゲーム内のリソースのうち、現金相当の価値を持つリソースを扱う機能です。 日本の資金決済法の前払い式支払い手段(自家型)に該当する資産を取り扱う場合は必ずこの機能を利用してください。
残高
GS2-Money2 はプレイヤーのもつ課金通貨の残高を単純な数量では管理せず、購入時の価値ごとに数量を管理します。
例えば、100円で100個の課金通貨を購入した場合は課金通貨1個の価値は1円相当となります。 同時に1000円で1200個の課金通貨を購入できるとしましょう。あくまで課金通貨の単価は1円とし、200個はおまけとして無料で処理するのも一つの手段ですし、1000円で購入した場合は単価を 0.8334円 として異なる単価で扱うのも一つの手段です。
後者のような方式を採用した場合、GS2-Money2 は「単価1円の課金通貨の残高」「単価0.8334円の課金通貨の残高」をそれぞれ分けて管理する機能を持っています。
後者を選択するメリットは?
明らかに後者は会計処理が複雑になり、メリットがないように感じるかもしれません。 サービス提供側としては全くのその通りです。しかし、立場を変えてゲームプレイヤーの立場で考えてみましょう。
ゲームの中には運営によって配られた現金相当の価値が0円の課金通貨(通称 無償通貨)があります。 プレイヤーに課金通貨を購入してもらうために、有償で購入した課金通貨(通称 有償通貨)でしか購入できない魅力的な商品を課金通貨300個で販売したとしましょう。
1000円 で 1200個の課金通貨を購入した際に、1000個の有償通貨と200個の無償通貨 を付与するように処理した場合 プレイヤーは有償通貨300個で購入できる商品を3回しか購入できません。 一方で、単価を0.8334円として1200個全てを有償通貨として取り扱う方法であればプレイヤーは4回購入できます。 この差はプレイヤー心理に多少なりとも影響を与えます。
会計上の都合を優先するか、プレイヤーの利益を優先するか 慎重に検討するべき仕様でしょう。
スロット
GS2-Money ではウォレットを複数持つことができます。 その複数のウォレットを区別するためのキーがスロットです。
この機能は他のプラットフォームで購入した課金通貨を持ち込ませないようにしているプラットフォーマーが存在するため、そのガイドラインを遵守するために存在する機能です。
しかし、これらのガイドラインは有償通貨にのみ適用されるため、無償通貨については全てのスロットで共有できる機能があります。
購入通貨
GS2-Money2 では GS2-Money から変わった点として、1つのウォレットに複数の通貨で購入した課金通貨を保持できるようになりました。
消費優先度
プレイヤーが課金通貨を消費する時に、無償通貨を優先して消費するか、有償通貨を優先して消費するかを選択できます。 一般的に無償通貨を優先して消費する仕様が採用されますが、会計上の都合がある場合は有償通貨を優先できます。
有償通貨を消費する際には入金した時期が古いものから順番に消費されます。
レシートの検証
ゲームの配信プラットフォームで追加コンテンツの購入時に得られるレシートの検証機能を有しています。 レシートを検証し、正しくプラットフォーマーによって発行された内容であることを確認するとともに、過去にゲーム内で使用したことがないかも確認します。
レシートの検証には、ネームスペースの platformSetting
に Apple App Store や Google Play の認証情報を登録しておく必要があります。
この機能を利用することで、不正なレシートを利用して課金通貨を入手しようとする攻撃を回避できます。
トランザクションログ
レシートの検証履歴はもちろんですが、課金通貨の加算・減算履歴も全て記録されます。 そして、毎日数回 現在ゲーム内には未使用の課金通貨が現金相当額でいくらプールされているかを集計します。
状況に応じて未使用残高の一部を第三者機関に供託する対応が必要になることがありますが、その時にこの計算結果を利用できます。
法的手続き
GS2-Money2 は各種法的手続きをとるために必要なデータを収集し、APIによってアクセス可能な状態で保持しますが、 法的手続き自体はGS2の利用者である あなた/あなたが所属する組織 が実行する必要があります。
どのような手続きが必要となるかは、GS2では責任を持つことができないためアドバイスもできません。顧問弁護士に相談するようにしてください。
期間課金(サブスクリプション)
GS2-Money2 では、都度課金の課金通貨だけでなく期間課金(サブスクリプション)を扱えます。 あらかじめ StoreSubscriptionContentModel のマスターデータを登録し、scheduleNamespaceId と triggerName を使って GS2-Schedule と連動させることで、契約期間の開始・更新・終了を自動的に反映できます。
契約情報の管理
各購入は SubscribeTransaction に記録され、トランザクションごとの契約詳細やストア情報を追跡できます。 プレイヤーごとの契約状態は SubscriptionStatus に保存され、現在のステータス (active / inactive) や expiresAt の確認が可能です。
ライフサイクルに応じたスクリプトトリガー
契約操作の前後にはスクリプトを実行できます。同期・非同期のいずれかを選択し、外部サービス連携にも対応します。
トリガー名 | 主な用途 |
---|---|
subscribeScript | 新規契約時(ユーザーの紐づけ変更時は除く) |
renewScript | 契約更新時 |
unsubscribeScript | 解約時(ユーザーの紐づけ変更時は除く) |
takeOverScript | 契約のユーザー割り当てを変更する際 |
契約状況の通知
サブスクリプションの状態が変化した場合、changeSubscriptionStatusNotification
に設定したプッシュ通知を送信できます。
プレイ中に期限切れや解約といった変化をハンドリングし、UIへ反映する用途に活用してください。
スクリプトトリガー
ネームスペースに depositBalanceScript
や withdrawBalanceScript
verifyReceiptScript
を設定すると、入出金処理の前後でカスタムスクリプトを実行できます。トリガーは同期・非同期の実行方式を選択でき、Amazon EventBridge を利用した外部連携にも対応します。
設定できる主なイベントトリガーとスクリプト設定名は以下の通りです。
depositBalanceScript
(完了通知:depositBalanceDone
): 入金処理の前後withdrawBalanceScript
(完了通知:withdrawBalanceDone
): 出金処理の前後verifyReceiptScript
(完了通知:verifyReceiptDone
): レシート検証の前後
プッシュ通知
設定できる主なプッシュ通知と設定名は以下の通りです。
changeSubscriptionStatusNotification
: 期間課金の契約状況が変化したときに通知
マスターデータ運用
マスターデータを登録することでマイクロサービスで利用可能なデータや振る舞いを設定できます。
マスターデータの種類には以下があります。
StoreContentModel
: 販売コンテンツ定義StoreSubscriptionContentModel
: 定期購読コンテンツ定義
マスターデータの登録はマネージメントコンソールから登録する他、GitHubからデータを反映したり、GS2-Deployを使ってCIから登録するようなワークフローを組むことが可能です。
バフによる補正
GS2-Buff と連携すると DepositByUserId
の count
や Withdraw
・WithdrawByUserId
の withdrawCount
にバフを適用して入金量や消費量を一時的に増減できます。イベントやキャンペーンに応じて柔軟に調整できます。
実装例
残高を取得
var item = await gs2.Money2.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Wallet(
slot: 0
).ModelAsync();
const auto Domain = Gs2->Money2->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Wallet(
0 // slot
);
const auto item = Domain.Model();
残高を加算
残高を加算する処理はゲームエンジン用の SDK では処理できません。
GS2-Showcase の購入時の報酬として残高を加算するといった方法で実装してください。
残高を消費
このAPIで残高の消費処理を行うことは推奨していません。 GS2-Showcase といったサービスを通して課金通貨の消費を行う代わりに 何らかの処理を実行することを推奨します。
var result = await gs2.Money2.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Wallet(
slot: 0
).WithdrawAsync(
withdrawCount: 50,
paidOnly: false
);
var item = await result.ModelAsync();
const auto Future = Gs2->Money2->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Wallet(
0 // slot
)->Withdraw(
50,
nullptr // paidOnly
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;