Skip to content

A couple of billing updates#2849

Closed
jshearer wants to merge 2 commits into
masterfrom
billing/dry_run
Closed

A couple of billing updates#2849
jshearer wants to merge 2 commits into
masterfrom
billing/dry_run

Conversation

@jshearer
Copy link
Copy Markdown
Contributor

  • Finally got around to adding a real --dry-run mode. Running against the Stripe testmode API key was starting to cause too many issues. See more details in the commit message

    The invoice generator's trial-run workflow ran against a sandbox Stripe account, but produced inaccurate results because the sandbox lacked livemode invoice state (manual bills with existing open/paid invoices appeared as phantom creates). --dry-run runs against livemode Stripe in read-only mode, showing what would happen without creating invoices, customers, or modifying anything.

  • Change the behavior for manual bills. Contract customers don't want us to use their payment method on file to pay for their contracted invoices, just for monthly overages. So from now on, all Manual bills will be forced to charge_type: SendInvoice during the final send/charge step.

jshearer added 2 commits April 1, 2026 16:32
The invoice generator's trial-run workflow ran against a sandbox Stripe account, but produced inaccurate results because the sandbox lacked livemode invoice state (manual bills with existing open/paid invoices appeared as phantom creates). `--dry-run` runs against livemode Stripe in read-only mode, showing what would happen without creating invoices, customers, or modifying anything.

Structural changes:

* Split `upsert_invoice` into `classify` (read-only: validation, Stripe searches) and `execute` (writes: customer/invoice creation, line items, verification). `--dry-run` stops after classify.
* Decompose `get_or_create_customer_for_tenant` into `find_customer` (read-only search) and `ensure_customer_for_invoicing` (find-or-create + email backfill).
* Reorder classify checks so cheap local validations (FreeTier, FutureTrialStart, LessThanMinimum) run before any Stripe API calls.
* Multi-month manual bills that already have an `open`, `paid`, `void`, or `uncollectible` invoice in Stripe are now classified as `AlreadyProcessed` instead of erroring. These are expected when date-range-overlapping manual bills were invoiced in a previous billing run.
* Per-tenant summary output annotates manual bills with their date range (`[manual: 2026-01-01 - 2026-06-30]`).
* Dry-run with `--clean-up` previews which stale draft invoices would be deleted. `--recreate-finalized` logs which invoices would be deleted and recreated.
Customers' stored payment methods are for monthly usage overages. Manual bills (contracts, one-off charges, etc.) should be sent as invoices so the customer can decide how to pay, rather than being automatically charged to their payment method.

* Override `charge_type` to `SendInvoice` for manual invoices during creation in `publish`
* Switch manual invoices from `charge_automatically` to `send_invoice` during the send phase, even if the customer has a payment method on file
@jshearer
Copy link
Copy Markdown
Contributor Author

rolling into #2883 is simpler

@jshearer jshearer closed this Apr 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant