Skip to main content

mojowallet

Python SDK for MojoWallet — digital wallet operations, sessions, balance management, and financial ledger tools.

Installation

pip install mojowallet

Requirements: Python 3.10+ | Dependencies: httpx, pyobjict

Quick Start

import mojowallet

client = mojowallet.Client("your-api-key", base_url="https://api.example.com")

wallet = client.Wallet.get(42)
wallet.add_funds(1000, "SC_REAL", source="CREDIT_CARD")
print(wallet.balance("SC_REAL"))

Each Client carries its own credentials, so one process can serve multiple tenants by holding one Client per group.

Since 2.8.0 the client shares ONE pooled keep-alive httpx.Client per Client across threads (bounded: 20 max connections / 10 kept alive), so connections survive across the fresh threads an ASGI server spins up per request. Timeouts split as Client(..., connect_timeout=3, timeout=30) (connect / read). A connect-phase failure OR a reused-connection death is retried exactly once for GET/DELETE and for POSTs whose payload carries a reference_id; read timeouts are never retried. Set the MOJOWALLET_DISABLE_POOL env var to revert to a one-shot per-call request, call client.close() to dispose the pool, and pass on_request_metrics=callable to observe one event per call.

Since 2.6.0 that event carries a stable op label and path_template, both timing views (elapsed_ms = final attempt, total_ms = caller-perceived, including a failed connect), attempts, session_new, the terminal exception's class name, and the server's own Server-Timing breakdown — so one log line on your side splits a slow call into network vs. server work. Wrap calls in mojowallet.correlation(request_id) to tie both halves together. See Request Timing.

Since 2.5.0 a bet/game spend is one call — the server resolves the customer's wallet, checks funds and your cap against the same holdings it draws from, and returns the complete post-spend family snapshot:

data = client.Wallet.spend_by_customer(
    "cust-abc123", 10000, "SC", "bet-round-42", cap_units=25000)
data.replayed, data.split, data.balances

Retrying a committed reference_id returns the original result with replayed=True and moves no money. See Wallet Operations.

All responses support both dict-style (resp["id"]) and attribute-style (resp.id) access.

Core Concepts

Concept Description
Wallet OO wrapper — get a wallet, call actions on it
Sessions Concurrency control for withdrawals (context manager)
Error Handling Exception hierarchy with domain-specific errors

Usage Examples

# Funds (wallet obtained via client.Wallet.get / get_by_customer)
wallet.add_funds(1000, "SC_REAL", source="CREDIT_CARD", category="deposited")
wallet.purchase(500, "SC_REAL", merchant="Coffee Shop")
wallet.cashout(800, "SC_REAL", reference_id="cashout-001")

# Sessions (context manager for safety)
with wallet.session("game-xyz", expires_in_seconds=3600) as session:
    session.withdraw(500, "SC_REAL", reference_id="bet-001")
    session.extend(1800)

# Balances
balance = wallet.balance("SC_REAL")
summary = wallet.balance_summary()

# Reserve flow
wallet.reserve(2500, "SC_REAL", "SC_HOLD", reference_id="payout-001")
wallet.confirm_reservation("payout-001")

# Lock/unlock
wallet.lock(reason="Chargeback")
wallet.unlock(reason="Investigation cleared")

Spending across a currency family

# Walk the SC family in priority order and debit across holdings as needed.
wallet.spend(750, root_code="SC", reference_id="game-bet-001", merchant="Casino")

Balance reads

balance_summary() returns balances grouped by root currency family (root_code), with per-currency totals exposed separately:

s = wallet.balance_summary()
s.balances.SC.available          # 200000
s.balances.SC.by_code.SC_REAL    # {quantity: 150000, formatted: "$1500.00"}
s.total_by_currency.SC_REAL      # per-currency rollup, unchanged from 2.2.x

For per-holding detail or a single family lookup:

holdings   = wallet.get_holdings(root_code="SC")
sc_balance = wallet.get_root_balance("SC")

⚠ Breaking change in 2.3.0: balance_summary().balances is now keyed by root_code (was currency_code). Callers reading s.balances["SC_REAL"] must switch to s.total_by_currency["SC_REAL"] or s.balances["SC"].by_code["SC_REAL"].

Error Handling

from mojowallet.exceptions import InsufficientBalanceError, SessionConflictError

try:
    wallet.cashout(10000, "SC_REAL", reference_id="cashout-002")
except InsufficientBalanceError as e:
    print(f"Not enough funds: {e.message}")
except SessionConflictError:
    print("Another session is already active")

Error codes

Wallet errors carry a numeric code (5000-block) and an HTTP status from the table below. The SDK raises a matching subclass whenever the response body includes a code in this range — the dispatch is authoritative regardless of HTTP status.

Code HTTP Class
5001 500 WalletInvariantError
5002 402 InsufficientBalanceError
5003 423 WalletLockedError
5004 423 WalletSuspendedError
5005 423 WalletInactiveError
5006 400 InvalidReferenceError
5007 409 IdempotentReplayError
5008 422 SpendCapExceededError

WalletInvariantError deliberately surfaces only a generic "please retry" message — never the server's diff text — so safety-critical balance-check failures don't leak internal state.

SpendCapExceededError (2.5.0) is not a subclass of InsufficientBalanceError — the wallet could afford the spend and the caller's own cap_units rejected it, so the two need different handling.

Documentation

Development

pip install poetry
poetry install --with dev

# Run all tests (unit tests only — no API key needed)
pytest -v

# With sandbox credentials
cp .env.example .env
# Edit .env with your sandbox API key
pytest -v

License

MIT

Release files for mojowallet 2.8.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mojowallet 2.8.0
File Size Uploaded
mojowallet-2.8.0.tar.gz 26.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mojowallet 2.8.0
File Interpreter ABI Platform
mojowallet-2.8.0-py3-none-any.whl Python 3 none any Details

Total release size: 56.0 kB

Release files / mojowallet-2.8.0.tar.gz

Download URL mojowallet-2.8.0.tar.gz
Size 26.9 kB
Tags Source
SHA-256 checksum
How to use checksums
169eef4019beb54b79629d1bc0bb2096cea6c7f1c11635dffa49a10e7ee5c840
BLAKE2b-256 checksum
How to use checksums
4de7489600b89884c8a98cfecb86c4ffd3acf2c83e9cd69e434f3fde72ebd855
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / mojowallet-2.8.0-py3-none-any.whl

Download URL mojowallet-2.8.0-py3-none-any.whl
Size 29.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d1419da9e7f55285c2b99e53f0df526220049f5dfa73ad1ef35fd3d1097b83f2
BLAKE2b-256 checksum
How to use checksums
a71e3adb2cedc3e41111100e2a618bf7ce8262e2dca5194a5feba2caa2521f71
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

2.8.1

2 release files

This release

2.8.0 This release

2 release files

2.7.1

2 release files

2.7.0

2 release files

2.6.0

2 release files

2.5.0

2 release files

2.3.1

2 release files

2.2.1

2 release files

2.1.1

2 release files

2.0.1

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page