Invoice write surface.
Exposes five user-path invoice actions on Accrue.Billing via a
defdelegate facade: finalize_invoice, void_invoice,
pay_invoice, mark_uncollectible, send_invoice.
Each action follows a consistent pattern: call the Stripe API, decompose
the response into local schema changes via InvoiceProjection, write
the updated invoice row and upsert its line items, and record an audit
event — all in a single database transaction.
pay_invoice/2 returns an intent result ({:ok, %Invoice{}} or
{:ok, :requires_action, pi}) because Stripe may surface SCA/3DS.
The other four actions return plain {:ok, %Invoice{}}.
Every action has a bang variant that raises on {:error, _};
pay_invoice!/2 additionally raises Accrue.ActionRequiredError on
:requires_action.
The webhook path uses the force-status bypass on the Invoice schema —
that bypass is NOT reachable from this module. Illegal user-path
transitions (e.g. draft -> paid) are rejected by Invoice.changeset/2
with an error on :status and propagate as
{:error, %Ecto.Changeset{}}.
Summary
Functions
@spec finalize_invoice( Accrue.Billing.Invoice.t(), keyword() ) :: {:ok, Accrue.Billing.Invoice.t()} | {:error, term()}
@spec finalize_invoice!( Accrue.Billing.Invoice.t(), keyword() ) :: Accrue.Billing.Invoice.t()
@spec mark_uncollectible( Accrue.Billing.Invoice.t(), keyword() ) :: {:ok, Accrue.Billing.Invoice.t()} | {:error, term()}
@spec mark_uncollectible!( Accrue.Billing.Invoice.t(), keyword() ) :: Accrue.Billing.Invoice.t()
@spec pay_invoice( Accrue.Billing.Invoice.t(), keyword() ) :: {:ok, Accrue.Billing.Invoice.t()} | {:ok, :requires_action, map()} | {:error, term()}
@spec pay_invoice!( Accrue.Billing.Invoice.t(), keyword() ) :: Accrue.Billing.Invoice.t()
@spec send_invoice( Accrue.Billing.Invoice.t(), keyword() ) :: {:ok, Accrue.Billing.Invoice.t()} | {:error, term()}
@spec send_invoice!( Accrue.Billing.Invoice.t(), keyword() ) :: Accrue.Billing.Invoice.t()
@spec void_invoice( Accrue.Billing.Invoice.t(), keyword() ) :: {:ok, Accrue.Billing.Invoice.t()} | {:error, term()}
@spec void_invoice!( Accrue.Billing.Invoice.t(), keyword() ) :: Accrue.Billing.Invoice.t()