Files
cgalo5758 71818de0bd Add setup checklist and empty-state guidance
Implement the ux-first-run change: a state-derived setup checklist on
/operator/setup with a landing region that recedes once required steps
are done, and empty states that distinguish blocked from empty across
operator and member surfaces. Also add production deployment and
environment reference docs, plus a config-key completeness test.
2026-08-23 03:06:11 -05:00

9.1 KiB

operator-billing-views

Purpose

Gives operators read-only visibility into the billing domain — accounts, subscriptions and their details, invoices with line items, payments, and payment methods — each attributed with its Stripe sync status, so billing state can be inspected and reconciled from the operator panel without reaching for the Stripe dashboard. These are views only: no mutation of billing state happens through this surface.

Requirements

Requirement: Operator can view billing accounts with Stripe sync status

The system SHALL provide a read-only view for operators to list all billing accounts with their associated organization, status, and Stripe customer sync status.

Scenario: Billing accounts list shows all accounts

  • WHEN an operator with the operator-member role accesses the Billing > Accounts view
  • THEN the system displays a table of all billing accounts with columns: account name, organization name, status, Stripe sync status, and creation date
  • WHEN a billing account has a stripe.customer_mappings record with sync_status = 'synced'
  • THEN the system displays a link to the Stripe dashboard customer page using the format https://dashboard.stripe.com/{mode}/customers/{stripe_customer_id}

Scenario: Billing account shows pending status

  • WHEN a billing account has a stripe.customer_mappings record with sync_status = 'pending'
  • THEN the system displays a "Pending" badge and no deep link

Requirement: Operator can view subscriptions with Stripe sync status

The system SHALL provide a read-only view for operators to list all subscriptions with their status, billing account, period dates, and Stripe sync status.

Scenario: Subscriptions list shows all subscriptions

  • WHEN an operator accesses the Billing > Subscriptions view
  • THEN the system displays a table of all subscriptions with columns: subscription ID (truncated), billing account, status, current period, cancel at period end flag, and Stripe sync status
  • WHEN a subscription has a stripe.subscription_mappings record with sync_status = 'synced'
  • THEN the system displays a link to the Stripe dashboard subscription page

Scenario: Subscription status is color-coded

  • WHEN a subscription has status active or trialing
  • THEN the system displays a green/success badge
  • WHEN a subscription has status past_due, unpaid, or incomplete
  • THEN the system displays a red/danger badge
  • WHEN a subscription has status canceled or ended
  • THEN the system displays a gray/secondary badge

Requirement: Operator can view subscription details

The system SHALL provide a detailed view of a subscription showing its items, changes history, and associated pool provision.

Scenario: Subscription detail shows items

  • WHEN an operator views a subscription detail
  • THEN the system displays all subscription items with their product name, price amount, currency, and quantity

Scenario: Subscription detail shows change history

  • WHEN an operator views a subscription detail
  • THEN the system displays the subscription change history ordered by change date descending, showing previous status, new status, and Stripe event ID

Requirement: Operator can view invoices with line items

The system SHALL provide a read-only view for operators to list all invoices with their status, amounts, and payment state.

Scenario: Invoices list shows all invoices

  • WHEN an operator accesses the Billing > Invoices view
  • THEN the system displays a table of all invoices with columns: invoice ID (truncated), billing account, status, amount due, amount paid, currency, due date, and paid date
  • WHEN an invoice has a stripe.invoice_mappings record with sync_status = 'synced'
  • THEN the system displays a link to the Stripe dashboard invoice page

Scenario: Invoice status is color-coded

  • WHEN an invoice has status paid AND amount_due = amount_paid
  • THEN the system displays a green/success badge
  • WHEN an invoice has status open or draft
  • THEN the system displays a yellow/warning badge
  • WHEN an invoice has status void or uncollectible
  • THEN the system displays a gray/secondary badge

Scenario: Invoice line items are viewable

  • WHEN an operator clicks to view invoice details
  • THEN the system displays all line items with description, quantity, amount, and period

Requirement: Operator can view payments

The system SHALL provide a read-only view for operators to list all payments with their status, amount, and payment method.

Scenario: Payments list shows all payments

  • WHEN an operator accesses the Billing > Payments view
  • THEN the system displays a table of all payments with columns: payment ID (truncated), invoice ID (truncated), billing account, status, amount, currency, and payment method (card brand + last4 if available)
  • WHEN a payment has a stripe.payment_mappings record with sync_status = 'synced'
  • THEN the system displays a link to the Stripe dashboard payment intent page

Scenario: Payment shows card details when available

  • WHEN a payment has an associated payment method with card details
  • THEN the system displays the card brand and last 4 digits (e.g., "Visa •••• 4242")

Requirement: Operator can view payment methods

The system SHALL provide a read-only view of payment methods associated with billing accounts.

Scenario: Payment methods are visible in billing account detail

  • WHEN an operator views a billing account detail
  • THEN the system displays all attached payment methods with type, card details (if applicable), and default status

Scenario: Detached payment methods are indicated

  • WHEN a payment method has detached_at set
  • THEN the system displays a "Detached" badge and shows the detached date

MODIFIED Requirements

None - this change only adds read-only views without modifying existing behavior.

Requirement: Stripe sync vocabulary is defined where it appears

Operator billing views that render a Stripe Sync state (such as Not Mapped or Synced) SHALL define the vocabulary in place — a visible legend or inline note stating that Not Mapped means the record has no linked Stripe object and what that implies — so an operator can tell deliberate absence from breakage without leaving the page.

Scenario: Not Mapped is explained on the page

  • WHEN an operator views a billing table containing Not Mapped badges
  • THEN the page carries a visible definition of the sync vocabulary
  • AND the definition states what an operator should conclude from (and do about) an unmapped record

Requirement: Billing views disclose projection recency

Because operator billing data is projected from processed webhook events, each operator billing view SHALL display when the most recent webhook event was processed (e.g. "data as of …"), and SHALL state when no webhook event has ever been processed. Stale data must be visibly stale rather than silently presented as current.

Scenario: Recency stamp renders with data

  • WHEN an operator views a billing table and webhook events have been processed
  • THEN the view shows the most recent processed event time

Scenario: No events ever processed is stated

  • WHEN an operator views a billing table on a deployment where no webhook event has ever been processed
  • THEN the view states that no billing events have been received, rather than showing rows or emptiness as if current

Requirement: Empty billing views distinguish unconfigured Stripe from no data

When the Stripe integration is not configured (its required settings are absent, per the same required-key completeness check the Stripe settings surface performs), an empty operator billing view SHALL state that billing data requires Stripe to be configured and SHALL link to the Stripe settings surface. When Stripe is configured but no webhook events have been processed, the existing no-events statement (see "Billing views disclose projection recency") applies instead. The two states SHALL NOT render identical copy: one is blocked on the operator, the other is waiting on the world.

Scenario: Unconfigured Stripe is named as the blocker

  • WHEN an operator views an empty billing view on a deployment whose Stripe integration fails the required-key completeness check
  • THEN the empty state states that billing data requires Stripe configuration
  • AND links to the Stripe settings surface

Scenario: Configured but eventless stays a projection statement

  • WHEN an operator views an empty billing view on a deployment where Stripe passes the required-key check but no webhook event has been processed
  • THEN the empty state is the no-events-received projection statement, not the unconfigured-Stripe blocker copy