Accrue.Billing.InvoiceActions (accrue v1.0.0)

Copy Markdown View Source

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

finalize_invoice(inv, opts \\ [])

@spec finalize_invoice(
  Accrue.Billing.Invoice.t(),
  keyword()
) :: {:ok, Accrue.Billing.Invoice.t()} | {:error, term()}

finalize_invoice!(inv, opts \\ [])

@spec finalize_invoice!(
  Accrue.Billing.Invoice.t(),
  keyword()
) :: Accrue.Billing.Invoice.t()

mark_uncollectible(inv, opts \\ [])

@spec mark_uncollectible(
  Accrue.Billing.Invoice.t(),
  keyword()
) :: {:ok, Accrue.Billing.Invoice.t()} | {:error, term()}

mark_uncollectible!(inv, opts \\ [])

@spec mark_uncollectible!(
  Accrue.Billing.Invoice.t(),
  keyword()
) :: Accrue.Billing.Invoice.t()

pay_invoice(inv, opts \\ [])

@spec pay_invoice(
  Accrue.Billing.Invoice.t(),
  keyword()
) ::
  {:ok, Accrue.Billing.Invoice.t()}
  | {:ok, :requires_action, map()}
  | {:error, term()}

pay_invoice!(inv, opts \\ [])

send_invoice(inv, opts \\ [])

@spec send_invoice(
  Accrue.Billing.Invoice.t(),
  keyword()
) :: {:ok, Accrue.Billing.Invoice.t()} | {:error, term()}

send_invoice!(inv, opts \\ [])

@spec send_invoice!(
  Accrue.Billing.Invoice.t(),
  keyword()
) :: Accrue.Billing.Invoice.t()

void_invoice(inv, opts \\ [])

@spec void_invoice(
  Accrue.Billing.Invoice.t(),
  keyword()
) :: {:ok, Accrue.Billing.Invoice.t()} | {:error, term()}

void_invoice!(inv, opts \\ [])

@spec void_invoice!(
  Accrue.Billing.Invoice.t(),
  keyword()
) :: Accrue.Billing.Invoice.t()