Rovenue SDK
Guides

Virtual Currencies

Read virtual-currency balances in your app and understand how granting and spending work server-side.

Virtual currencies let you define any number of named balances per subscriber — for example COINS, GEMS, or ENERGY — useful for AI generations, messages, or any metered feature. Balances are server-authoritative: the SDK caches the last-known values locally and syncs with the server automatically.

Granting virtual currency happens automatically when a user purchases a currency-pack product configured in the Rovenue dashboard. Spending (debiting) is a server-side operation — call POST /v1/virtual-currencies/:appUserId/:code/transactions from your backend using your secret API key. There is no client-side spend method.

Types on this page

Virtual-currency balances are represented as a map of currency code to integer amount. The methods below are defined in the Methods reference.

Method / HookReturnsNotes
virtualCurrencies()Record<string, number>Cache read; returns {} if no balances are cached
virtualCurrency(code)numberCache read; returns 0 if the code is absent
refreshVirtualCurrencies()voidNetwork call; updates the local cache
useVirtualCurrencies() (RN)Record<string, number>Reactive hook; re-renders on VirtualCurrenciesChanged
useVirtualCurrency(code) (RN)numberReactive hook; re-renders on VirtualCurrenciesChanged

Swift uses virtualCurrencyBalances() -> [String: Int64] and virtualCurrency(_ code: String) -> Int64. Kotlin uses virtualCurrencyBalances(): Map<String, Long> and virtualCurrency(code: String): Long.


Read balances

Use the reactive hooks in React Native components, or the imperative methods elsewhere.

import { Rovenue, useVirtualCurrencies, useVirtualCurrency } from '@rovenue/react-native-sdk';

// Reactive hook — re-renders automatically when any balance changes
function WalletSummary() {
  const balances = useVirtualCurrencies(); // Record<string, number>
  return (
    <>
      {Object.entries(balances).map(([code, amount]) => (
        <Text key={code}>{code}: {amount}</Text>
      ))}
    </>
  );
}

// Single-currency reactive hook
function CoinsLabel() {
  const coins = useVirtualCurrency('COINS'); // 0 if absent
  return <Text>{coins} coins</Text>;
}

// Imperative cache reads (outside a component)
const all = Rovenue.virtualCurrencies();   // instant, no network
const coins = Rovenue.virtualCurrency('COINS'); // 0 if absent
// Cache read — instant, no network
let balances = await Rovenue.shared.virtualCurrencyBalances() // [String: Int64]
let coins = await Rovenue.shared.virtualCurrency("COINS")     // 0 if absent
// Cache read — instant, no network
val balances = Rovenue.shared.virtualCurrencyBalances() // Map<String, Long>
val coins = Rovenue.shared.virtualCurrency("COINS")     // 0 if absent

virtualCurrencies() and virtualCurrency(code) never hit the network. They are safe to call on the main thread and return instantly even in offline scenarios. Values reflect the last successful sync with the server.

Refresh from the server

Call refreshVirtualCurrencies() when you need a guaranteed-fresh set of balances — for example, after a server-side spend/grant that your app triggered.

import { Rovenue } from '@rovenue/react-native-sdk';

// Pull latest balances from server and update the local cache
await Rovenue.refreshVirtualCurrencies();

// Now virtualCurrencies() and useVirtualCurrencies() reflect the server values
const fresh = Rovenue.virtualCurrencies();
// Pull latest balances from server and update the local cache
try await Rovenue.shared.refreshVirtualCurrencies()

// Now virtualCurrencyBalances() reflects the server values
let fresh = await Rovenue.shared.virtualCurrencyBalances()
// Pull latest balances from server and update the local cache
Rovenue.shared.refreshVirtualCurrencies()

// Now virtualCurrencyBalances() reflects the server values
val fresh = Rovenue.shared.virtualCurrencyBalances()

Balances also refresh automatically while the app is in the foreground. See App lifecycle for the polling behaviour. Manual refreshVirtualCurrencies() is only needed when you require immediate consistency after a server-side operation.

Grant virtual currency (server-side)

Virtual currency is granted when a user purchases a currency-pack product. Product-to-currency mapping is configured in the Rovenue dashboard — no client code is required.

After a successful purchase, PurchaseResult.virtualCurrencies already reflects the updated balances:

const result = await Rovenue.purchase(pkg);
console.log('COINS after purchase:', result.virtualCurrencies['COINS']);

Spend virtual currency (server-side only)

Spending a virtual currency is a server-side operation. From your backend, call the secret-key transactions endpoint:

POST /v1/virtual-currencies/:appUserId/:code/transactions
Authorization: Bearer <rov_sec_…>
Content-Type: application/json

{
  "amount": -5,
  "idempotencyKey": "<unique-key-per-spend>",
  "description": "AI image generation"
}

A negative amount debits the balance; a positive amount grants currency. Always supply an idempotencyKey — the server rejects duplicate keys so a retry after a network drop does not double-deduct.

Never put your secret API key (rov_sec_…) in the app binary. The transactions endpoint is server-to-server only. Expose a lightweight endpoint in your own backend that verifies the user session and calls Rovenue on their behalf. After the spend completes, call refreshVirtualCurrencies() on the client (or wait for the next foreground sync) to update the local cache.

On this page