More billing work
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
# Billing Architecture (Agreed)
|
||||
|
||||
This document summarizes the agreed billing architecture for Caravel backend.
|
||||
|
||||
## Billing model
|
||||
|
||||
- Usage-based billing: **sats/hour** for relay operation.
|
||||
- A relay is billable when it is provisioned/active in lifecycle terms.
|
||||
- Billing is **monthly**, with a **rolling cycle anchored to tenant signup**.
|
||||
- One **consolidated invoice per tenant** per billing period.
|
||||
|
||||
## Metering and lifecycle
|
||||
|
||||
- Add an append-only lifecycle event table in the backend database.
|
||||
- Events are the source for usage computation.
|
||||
- Canonical event timestamp field name: `created_at` (UTC).
|
||||
- Lifecycle behavior is treated as a state machine for billing math (idempotent outcomes for repeated/no-op transitions).
|
||||
- Transition validation is permissive (any transition can be recorded); billing logic interprets sequences.
|
||||
- Billable time behavior:
|
||||
- Start on `provisioned`
|
||||
- Pause on `suspended`
|
||||
- Stop on `deactivated`
|
||||
- Resume immediately on unsuspend
|
||||
|
||||
## Pricing
|
||||
|
||||
- Price is per relay plan/tier in **sats/hour**.
|
||||
- Rates are stored in a mutable `plans` table (current rate only).
|
||||
- Mid-cycle plan changes are billed by time spent in each plan.
|
||||
- Plan rate changes are retroactive for un-invoiced usage in the current open period.
|
||||
|
||||
## Rounding and minimums
|
||||
|
||||
- Round usage up to the next full hour.
|
||||
- Minimum charge: **1 billable hour per relay per month**.
|
||||
|
||||
## Invoice generation
|
||||
|
||||
- A periodic worker creates invoices at billing boundaries.
|
||||
- Existing relays at launch start billing from launch timestamp only (no historical backfill).
|
||||
- Avoid duplicate invoices with a DB unique constraint on:
|
||||
- `(tenant, period_start, period_end)`
|
||||
|
||||
## Invoice status and attempts
|
||||
|
||||
- `invoice_attempts` is the canonical history/state source.
|
||||
- `invoices.status` is a synchronous projection updated in the same transaction as attempt writes.
|
||||
- Each payment method attempt is its own row in `invoice_attempts`.
|
||||
- Attempts in a single retry pass share a `run_id` UUID.
|
||||
|
||||
## Collection order and fallback
|
||||
|
||||
For each invoice collection run:
|
||||
|
||||
1. Try **NWC** auto-pay
|
||||
2. If not paid, try **Stripe** auto-pay
|
||||
3. If still unpaid/unavailable, create Lightning invoice and show QR in-app
|
||||
4. If neither NWC nor Stripe is configured, send a one-time **NIP-17 DM** with invoice/subscription status
|
||||
|
||||
Notes:
|
||||
|
||||
- Retry cadence: every 24 hours (NWC/Stripe retries).
|
||||
- Do **not** resend DMs on retries.
|
||||
- Lightning invoice refresh is in-app only when prior invoice expires.
|
||||
- DM send is recorded as an `invoice_attempts` row (same `run_id` as triggering run).
|
||||
|
||||
## Due dates, grace, and enforcement
|
||||
|
||||
- Invoice due time is derived as: `invoice.created_at + 7 days`.
|
||||
- Grace period: 7 days, relay service remains fully active during grace.
|
||||
- If still unpaid after grace, billing flow marks tenant/account past due and performs billing-side handling.
|
||||
- Full outstanding balance must be paid before billing status is considered clear.
|
||||
|
||||
## Tenant and integration storage
|
||||
|
||||
- Store billing cycle anchor on `tenants` (e.g., `billing_anchor_at`).
|
||||
- Anchor can be reset when tenant goes from no non-free relays to having one again.
|
||||
- Determine “has billable relays” by querying relays on demand (no counter cache).
|
||||
- Keep NWC config in `tenants.nwc_url`.
|
||||
- Store Stripe IDs directly on `tenants`.
|
||||
|
||||
## Worker and runtime model
|
||||
|
||||
- Scheduler runs inside backend service process.
|
||||
- Multiple instances may run; correctness relies on DB idempotency and unique constraints.
|
||||
|
||||
## Repository impact
|
||||
|
||||
- Add migration(s) for lifecycle events and billing-related schema changes.
|
||||
- Add repository methods in `backend/src/repo.rs` for:
|
||||
- writing lifecycle events
|
||||
- reading lifecycle events by relay/tenant/time window
|
||||
- creating/fetching invoices with period boundaries
|
||||
- writing invoice attempts and projecting invoice status atomically
|
||||
Reference in New Issue
Block a user