GS2-Money SDK for Game Engine API Reference
Model
EzWallet
Wallet
Currency in the wallet is managed separately for currency purchased for a fee and currency obtained for free. Currency purchased for a fee is further managed by the unit price at the time of purchase, allowing for refunds in the event of service termination, or to determine if the balance is sufficient to meet the requirements of the Funds Settlement Act.
The wallet has slots and each slot has a different balance. If balances cannot be shared across platforms, they can be managed separately by using different slots for each platform. Currency acquired for free can also be shared across all platforms.
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| slot | int | ✓ | 0 ~ 100000000 | Slot Number An identifier for separating wallet balances by platform or context. Different slots allow managing separate paid currency pools (e.g., iOS purchases in slot 0, Android in slot 1). Free currency can optionally be shared across all slots via the namespace’s shareFree setting. | ||
| paid | int | 0 | 0 ~ 2147483646 | Paid Currency Amount The total amount of paid (purchased) currency in this wallet slot. This is the sum of all WalletDetail entries with a non-zero unit price. Increased by deposits from store purchases, decreased by withdrawals according to the consumption priority. | ||
| free | int | 0 | 0 ~ 2147483646 | Free Currency Amount The total amount of free (granted) currency in this wallet slot. This corresponds to the WalletDetail entry with unit price of 0. When shareFree is enabled on the namespace, this value is synchronized from slot 0 across all wallet slots. | ||
| shareFree | bool | false | Share Free Currency Whether free currency in this wallet is shared across all slots. This value is inherited from the namespace setting at wallet creation time. When true, free currency is synchronized from slot 0 to all other wallet slots. | |||
| updatedAt | long | * | Now | Datetime of last update Unix time, milliseconds * Set automatically by the server |
Methods
get
Get the player’s premium currency wallet balance
Retrieves the player’s wallet for the specified slot, showing the current balance of both paid and free premium currency. “Paid” currency is what the player purchased with real money (e.g., bought 100 Gems for $0.99), while “free” currency is earned through gameplay (e.g., event rewards, login bonuses). These are tracked separately because some features may require paid currency only (e.g., certain gacha or special offers). Use this to display the player’s currency balance — for example, “Gems: 350 (Paid: 100, Free: 250)” on the shop or header UI.
Request
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128 chars | Namespace name Namespace-specific name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). | ||
| gameSession | GameSession | ✓ | GameSession | |||
| slot | int | ✓ | 0 ~ 100000000 | Slot Number An identifier for separating wallet balances by platform or context. Different slots allow managing separate paid currency pools (e.g., iOS purchases in slot 0, Android in slot 1). Free currency can optionally be shared across all slots via the namespace’s shareFree setting. |
Result
| Type | Description | |
|---|---|---|
| item | EzWallet | Wallet |
Implementation Example
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;
}Value change event handling
var domain = gs2.Money.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Wallet(
slot: 0
);
// Start event handling
var callbackId = domain.Subscribe(
value => {
// Called when the value changes
// The "value" is passed the value after the change.
}
);
// Stop event handling
domain.Unsubscribe(callbackId); var domain = gs2.Money.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Wallet(
slot: 0
);
// Start event handling
var callbackId = domain.Subscribe(
value => {
// Called when the value changes
// The "value" is passed the value after the change.
}
);
// Stop event handling
domain.Unsubscribe(callbackId); const auto Domain = Gs2->Money->Namespace(
"namespace-0001" // namespaceName
)->Me(
GameSession
)->Wallet(
0 // slot
);
// Start event handling
const auto CallbackId = Domain->Subscribe(
[](TSharedPtr<Gs2::Money::Model::FWallet> value) {
// Called when the value changes
// The "value" is passed the value after the change.
}
);
// Stop event handling
Domain->Unsubscribe(CallbackId);Warning
This event is triggered when the value stored in the SDK’s local cache changes.
The local cache is updated only when executing the SDK’s API, or by executing stamp sheets via GS2-Distributor with GS2-Gateway notification enabled, or by executing a GS2-JobQueue with GS2-Gateway notification enabled.
Therefore, callbacks will not be invoked if the value is changed in any other way.
withdraw
Spend premium currency from the player’s wallet
Deducts the specified amount of premium currency from the player’s wallet. By default (paidOnly = false), free currency is spent first, then paid currency is used for the remainder. This ensures players use up free currency before dipping into purchased currency. If paidOnly is set to true, only paid currency is consumed — this is required for features that legally must use paid currency only (e.g., paid-only gacha in some regions). Use this when the player makes a purchase using premium currency — for example, spending 100 Gems to buy a special item or pull a gacha. Note: if currency is consumed as a cost for purchasing products via GS2-Showcase, the consumption is handled automatically — you don’t need to call this API for those cases.
Request
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128 chars | Namespace name Namespace-specific name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). | ||
| gameSession | GameSession | ✓ | GameSession | |||
| slot | int | ✓ | 0 ~ 100000000 | Slot Number An identifier for separating wallet balances by platform or context. Different slots allow managing separate paid currency pools (e.g., iOS purchases in slot 0, Android in slot 1). Free currency can optionally be shared across all slots via the namespace’s shareFree setting. | ||
| count | int | ✓ | 1 ~ 2147483646 | Quantity of premium currency to be consumed | ||
| paidOnly | bool | false | Only for paid currency |
Result
| Type | Description | |
|---|---|---|
| item | EzWallet | Post-withdraw Wallet |
| price | float | Price of currency consumed |
Error
Special exceptions are defined in this API. GS2-SDK for GameEngine provides specialized exceptions derived from general exceptions to facilitate handling of errors that may need to be handled in games. Please refer to the documentation here for more information on common error types and handling methods.
| Type | Base Type | Description |
|---|---|---|
| ConflictException | ConflictException | The wallet operation process conflicted. Retry required. |
| InsufficientException | BadRequestException | Wallet balance is insufficient. |
Implementation Example
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;
}
// 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 Price = Result->Price;