# Payments and metering Version 0.1 — SAND-WP-0006. OpenRouter-style credits for metered SaaS extensions. ## Credits store Path: `~/.local/share/sandboxer/credits.json` (override via test injection). | Env | Default | |-----|---------| | `SANDBOXER_DEFAULT_CREDITS` | `10.0` USD on first use | ```bash sandboxer credits show sandboxer credits add 25.00 ``` ## Metered lifecycle 1. **create** — `estimate_cost` on metered extension; block if balance insufficient 2. **ready** — `SandboxStatus.meter.estimate_usd` recorded 3. **destroy** — `meter_actual` (or prorated estimate); debit credits; State Hub note event Self-hosted extensions (`pricing_model: self-hosted`) skip credits. ## Extension contract Metered extensions implement on `SandboxExtension`: ```python def estimate_cost(self, profile, inputs, *, duration_s=3600) -> MeterQuote | None def meter_actual(self, handle, *, duration_s: float) -> float | None ``` Reference: `ext.saas-stub` (no external API). ## BYOK Provider API keys resolve at provision boundary — never stored on `SandboxStatus` or emitted to State Hub. 1. Operator lookup: `warden route find " API key" --json` 2. Inject env before `sandboxer create` (e.g. `E2B_API_KEY`, `MODAL_TOKEN_ID`) 3. Or map `secret_ref` from extension config to `SANDBOXER_SECRET_` env See `docs/cloud-adapters.md`. ## Billing export On metered destroy, optional fin-hub hook when `SANDBOXER_FIN_HUB_URL` is set. Posts `sandbox_id`, `extension_id`, `duration_s`, `actual_usd` to `/usage/sandbox`. Implementation: `src/sandboxer/payments/billing_export.py`.