Skip to content

Economy System — Public API

This page defines the supported public API for the RevFramework Economy System.

This is the contract

If something is not listed here, it is not supported as a public integration point.

Audience: Developers integrating Economy into gameplay or UI
Scope: Runtime public API only (Editor / Teaching helpers excluded)


Core Concepts

  • Economy is service-based and adapter-driven (money + optional items).
  • Integration is commonly done through the EconomyBootstrap façade, which returns interfaces only.
  • All operations return an EcoOpResult (success flag + code + optional message).
  • Semantics are explicit and consistent:
  • Shop & Crafting: multi-step orchestration with rollback attempts
  • Rewards: money granted first; no money rollback if item grants fail (by design)

Rollback semantics

Rollback attempts are best-effort, not hard transactional guarantees.

Failures in underlying systems (inventory, policy, authority, etc.) may prevent perfect rollback. Callers should always rely on the returned EcoOpResult rather than assuming strict atomicity.

Telemetry rules

  • Currency operations pass through sourceId when supported by the underlying implementation
  • Item operations do not propagate sourceId

Composition Entry Point

EconomyBootstrap

A convenience façade for composing the built-in Economy services.

It wires the default implementations and returns abstractions only, keeping concrete service implementations internal.

Using EconomyBootstrap is optional. Developers may construct their own implementations of the public interfaces.


Currency-only builds

(IShopService shop, IRewardService rewards, ICraftingService crafting,
 IValueLedger ledger, IItemStore store)
BuildForPlayer(GameObject player, ICurrencyService currency, CurrencyPolicy policy = null);

Characteristics:

  • Inventory is not present
  • store will be null
  • Item-based operations require a non-null IItemStore to succeed

Inventory-enabled builds (REV_INVENTORY_PRESENT)

(IShopService shop, IRewardService rewards, ICraftingService crafting,
 IValueLedger ledger, IItemStore store)
BuildForPlayer(
    GameObject player,
    ICurrencyService currency,
    IInventoryService inventory,
    Func<string, ItemDefinition> resolveDef,
    string container = "Backpack",
    CurrencyPolicy policy = null
);

Characteristics:

  • Enables item-based pricing, crafting, and rewards
  • Inventory-backed IItemStore is wired automatically

Core Service Interfaces

These interfaces define the supported gameplay contract for Economy.

IShopService

Handles purchase and sell transactions.

Buy

  1. Charge currency
  2. Remove item costs
  3. Deliver purchased items

Rollback is attempted if later stages fail.

Sell

  1. Remove items first
  2. Grant currency afterwards

Rollback of item removal is attempted if payout fails.

Idempotency

Buy and Sell dedup by requestId: a repeated requestId for the same owner replays the first successful result as a no-op — no second charge, no second delivery. Request ids must be unique per logical transaction; omit requestId to opt out.

The payload is checked as well as the id. Reusing a requestId for a genuinely different purchase is refused with EcoOpCode.IdempotencyMismatch rather than answered with the first purchase's cached result, so a mismatched reuse is visible instead of silently reporting success for a transaction that never ran.

Line order and duplicate-line spelling do not affect this. Rebuilding the same basket from an unordered collection is still recognised as a retry, and so is sending [gold:50, gold:50] where you first sent [gold:100] — those are the same transaction once money lines are merged.

Generate a fresh id per transaction, not per player action or per frame. Reuse one only when you intend a retry of that exact transaction.

Only successful results are cached, so a failed attempt re-runs on retry. Cached results are held in a small per-owner recent window; once an id ages out of it, a later replay re-runs as a new transaction.


ICraftingService

Sequential crafting flow with best-effort rollback:

  1. Charge currency
  2. Remove ingredient items
  3. Add crafted result

Craft dedups on the request id embedded in sourceId ("…|rid:…", as built by EcoSource.Build): a repeated craft replays the first successful result as a no-op. A sourceId without a request id opts out.

If a later step fails, previously applied steps attempt rollback.


IRewardService

Reward / payout flow.

  1. Money granted first
  2. Item grants attempted afterwards

If item grants fail, money is not rolled back (intentional design choice).


Replay Window

IEconomyRequestWindow

Rewinds a service's request-replay window. Implemented by the built-in ShopService and CraftingService; reach it by casting the service you already hold.

if (shop is IEconomyRequestWindow window)
    window.ClearForOwner(player);   // or ClearAll() for a whole-world load
Member What it does
ClearForOwner(GameObject) Forgets every remembered request for one owner. A null or destroyed reference does nothing
ClearAll() Forgets every remembered request for every owner

Call it when a load rewinds the state the request ids describe, and for no other reason. The window remembers a successful request id so a retry short-circuits instead of charging and delivering twice, which is correct while the world moves forward. An in-process load moves it backwards — RevSaveManager.Load restores the wallet and the containers a transaction touched — and the window does not follow, so an id used before the load is still remembered after it and re-issuing it applies nothing against balances that have been put back. Clearing the window at any other moment re-opens double application for exactly the requests it was protecting.

Nothing calls this for you, and that is not an oversight

CurrencySaveParticipant clears the currency window as part of its restore, because Currency has state to restore. Economy has none of its own — it composes Currency and Inventory and holds nothing across a session but this window — so it ships no save participant and there is no restore for the clear to hang off. Call it from wherever you handle a load, as you would CraftingService.ClearAppliedCompletions outside the crafting participant.

Each service keeps its own window. A shop and a crafting service from the same bootstrap do not share one, so clear each service you hold.


Wallet Abstraction

IValueLedger

Abstracts all currency operations used by Economy.

Methods

  • CanPay — side-effect-free preflight
  • Pay — authoritative debit
  • Grant — authoritative credit

A true from CanPay is advisory: Pay can still fail. A false is not — the built-in shop and crafting services refuse the transaction, so a custom ledger returning false cancels sales rather than greying out a button.

Currency operations pass through sourceId when supported.

Gameplay code should depend on IValueLedger, not on specific implementations.


Item Store Abstraction

IItemStore

Abstracts item storage and mutation operations.

Methods

  • HasSpaceFor
  • CanRemove
  • Add
  • Remove

HasSpaceFor and CanRemove are side-effect-free preflight, and they carry the same asymmetry as CanPay: a true is advisory and the Add or Remove can still fail, but a false is not — the built-in shop, crafting and reward services refuse the transaction with NoSpace or NotOwned before anything moves. A custom store returning false cancels transactions.

Item operations do not propagate sourceId.

Gameplay code should depend on IItemStore, not on concrete inventory implementations.


Models & Results

These value types are part of the public contract.

EcoOpResult

Represents the outcome of any economy operation.

Fields:

  • Success
  • Code
  • Message (optional)

Inspect the result explicitly

Callers must explicitly inspect Success or Code.
Implicit boolean conversion is not supported.


EcoOpCode

Enumeration describing operation outcomes such as:

  • InvalidArgs
  • InsufficientFunds
  • PolicyBlocked
  • NoSpace
  • NotOwned
  • ServiceMissing
  • ContainerMissing
  • Overflow
  • EscrowUnavailable
  • IdempotencyMismatch
  • Partial

These codes are intended for gameplay branching, UI feedback, and telemetry.

PolicyBlocked is the widest of them. An authority refusal arrives as PolicyBlocked, and so do a cap, a floor, a Strict-preflight mismatch, a zero effective debit and an expired escrow hold — Economy owns no authority of its own and collapses every policy answer from Currency and Inventory into one code. Only the message distinguishes them, so branch on PolicyBlocked for "the rules said no" and read the message when you need to say which rule.

IdempotencyMismatch reports caller error rather than a game-state condition: a requestId was replayed with a different transaction payload than the one it first succeeded with. Treat it as a bug to fix in id generation, not a condition to retry.

Partial is the one failure code that reports a mutation which happened — part of the transaction applied and the rest did not. It arrives from the currency layer (CurOpCode.Partial), which an item-backed wallet returns after moving some of the coins and not all of them. Before it existed that surfaced as UnknownError, which says the opposite. Do not retry it and do not treat it as success; read the balances.


PriceBundle

Represents a bundle of optional:

  • money lines (ChargeLine)
  • item lines (ItemLine)

Lists are not cloned or frozen. Callers should treat them as read-only; mutating them may affect consumers.


ChargeLine

Represents a currency line:

  • normalized currency id
  • amount in minor units

ItemLine

Represents an item quantity:

  • item GUID/key
  • quantity

Telemetry Contracts

EcoReasons

Canonical reason strings for economy operations.

Using these constants helps prevent analytics drift and inconsistent logging.


EcoSource

Canonical builder for sourceId correlation strings.

string src = EcoSource.Build(vendorId, requestId);

The resulting sourceId is passed through currency operations that support telemetry. The vendor and request value are preserved for correlation, but the rid: token is forwarded to the currency layer in a neutral form so that a currency-level idempotency decorator does not short-circuit Economy's per-leg currency calls — Economy's own saga dedup owns Buy/Sell/Craft idempotency.

The embedded request id ("…|rid:…") is not only telemetry: it is the idempotency key for shop and craft transactions (see IShopService / ICraftingService above). EcoSource.TryGetRequestId extracts it back out.


Debug-only Surface

IItemStoreDebug

Optional diagnostics interface used for:

  • Teachables
  • Editor tooling
  • Debug UI

This interface is public only for diagnostics tooling.

Gameplay code must not depend on it.


Explicitly Not Supported

Explicitly not supported

The following are not part of the supported public API:

  • Internal rollback helpers
  • Internal normalization utilities
  • Internal policy computation helpers
  • Concrete service implementations
  • Concrete adapter implementations
  • Any namespace under:
RevGaming.RevFramework.Economy.Internal.*
  • Casting or relying on debug handles in gameplay code

TL;DR

TL;DR

If it’s not on this page, it’s not part of the supported API.