GameServer.Economy (game_server_sdk v1.0.1070)

Copy Markdown View Source

Virtual-currency wallets with an append-only ledger.

Currencies are free-form string codes ("gold", "gems", "energy") — the game decides which exist. Every balance change is atomic and recorded in the ledger, so two concurrent spends can never overspend and every mutation is auditable.

Usage (server-side / hooks)

Economy.grant(user_id, "gold", 100, reason: "match_reward")
case Economy.spend(user_id, "gold", 30, reason: "store_purchase") do
  {:ok, balance} -> :ok
  {:error, :insufficient_funds} -> :not_enough_gold
end

Economy.balance(user_id, "gold")   #=> 70
Economy.balances(user_id)          #=> %{"gold" => 70}

Idempotency

Pass :idempotency_key so a retried request (network retry, at-least-once job) can't double-apply — the second call is a no-op that returns the current balance:

Economy.grant(user_id, "gems", 5, idempotency_key: "purchase:#{order_id}")

Safety

These are server-authoritative: expose them from hooks and admin tools, never as a raw client "add currency" endpoint. Clients only read their wallet.

Note: This is an SDK stub. Calling these functions will raise an error. The actual implementation runs on the GameServer.

Summary

Functions

Current balance of one currency (0 when the user has no wallet for it).

All non-zero balances for a user, as a %{currency => balance} map.

Add amount of currency to a user's wallet.

Remove amount of currency from a user's wallet, atomically.

Subscribe the calling process to a user's live wallet updates.

Stop receiving a user's wallet updates.

Types

currency()

@type currency() :: String.t()

user_id()

@type user_id() :: Ecto.UUID.t()

Functions

balance(user_id, currency)

@spec balance(user_id(), currency()) :: non_neg_integer()

Current balance of one currency (0 when the user has no wallet for it).

balances(user_id)

@spec balances(user_id()) :: %{required(currency()) => non_neg_integer()}

All non-zero balances for a user, as a %{currency => balance} map.

grant(user_id, currency, amount, opts)

@spec grant(user_id(), currency(), pos_integer(), keyword()) ::
  {:ok, non_neg_integer()} | {:error, term()}

Add amount of currency to a user's wallet.

Options: :reason (ledger label), :idempotency_key, :metadata. Returns {:ok, new_balance}.

spend(user_id, currency, amount, opts)

@spec spend(user_id(), currency(), pos_integer(), keyword()) ::
  {:ok, non_neg_integer()} | {:error, :insufficient_funds | term()}

Remove amount of currency from a user's wallet, atomically.

Returns {:ok, new_balance} or {:error, :insufficient_funds} — the balance is never left negative.

subscribe(user_id)

@spec subscribe(user_id()) :: :ok | {:error, term()}

Subscribe the calling process to a user's live wallet updates.

unsubscribe(user_id)

@spec unsubscribe(user_id()) :: :ok

Stop receiving a user's wallet updates.