Write surface for subscription lifecycle operations.
Every function here is exposed on Accrue.Billing — you rarely call
this module directly. Use Accrue.Billing.subscribe/3,
Accrue.Billing.cancel/2, etc. instead.
When you reach for this module
If you need to go deeper than Accrue.Billing exposes — for example,
to call a function in a context where defdelegate wrapping is in the
way — these are the underlying implementations.
Lifecycle groups
Create
subscribe/3— subscribe a billable to a price; returns an intent result (SCA-safe: may return{:ok, :requires_action, payment_intent})comp_subscription/3— create a 100%-off comped subscription
Manage plan
swap_plan/3— replace the current price with a new one (proration required)update_quantity/3— change the quantity on a single-item subscriptionpreview_upcoming_invoice/2— fetch the next invoice before it's finalized
Pause and resume
pause/2— pause collection (void, mark_uncollectible, or keep_as_draft)unpause/2— resume a paused subscriptionresume/2— cancel a pending cancellation (undocancel_at_period_end)
Cancel
cancel/2— cancel immediatelycancel_at_period_end/2— schedule cancellation at the end of the billing period
Read
get_subscription/2— fetch a local subscription row by id
Return types
Operations that involve a PaymentIntent (subscribe, swap_plan, cancel
with invoice_now: true) return intent_result(Subscription.t()):
{:ok, %Subscription{}}
| {:ok, :requires_action, payment_intent_map}
| {:error, term()}All other operations return {:ok, %Subscription{}} | {:error, term()}.
Atomicity
Every write atomically persists the local row change and appends an
event to accrue_events inside the same Repo.transact/1 call.
Summary
Functions
Creates a free-tier ("comped") subscription with a 100%-off coupon applied. Skips the payment_method guard since there is nothing to charge.
Raising variant of comp_subscription/3.
Creates a subscription for the given billable (or %Customer{}) against
the configured processor. Returns intent_result(Subscription.t()).
Raising variant of subscribe/3.
Functions
@spec cancel( Accrue.Billing.Subscription.t(), keyword() ) :: {:ok, Accrue.Billing.Subscription.t()} | {:ok, :requires_action, map()} | {:error, term()}
@spec cancel!( Accrue.Billing.Subscription.t(), keyword() ) :: Accrue.Billing.Subscription.t()
@spec cancel_at_period_end( Accrue.Billing.Subscription.t(), keyword() ) :: {:ok, Accrue.Billing.Subscription.t()} | {:error, term()}
@spec cancel_at_period_end!( Accrue.Billing.Subscription.t(), keyword() ) :: Accrue.Billing.Subscription.t()
@spec comp_subscription(term(), term(), keyword()) :: {:ok, Accrue.Billing.Subscription.t()} | {:error, term()}
Creates a free-tier ("comped") subscription with a 100%-off coupon applied. Skips the payment_method guard since there is nothing to charge.
The coupon referenced by coupon_id must exist in the processor's
dashboard. Defaults to "accrue_comp_100_forever"; host apps create
this once via Accrue.Billing.create_coupon/2 (landed in 04-05) or
the Stripe Dashboard.
@spec comp_subscription!(term(), term(), keyword()) :: Accrue.Billing.Subscription.t()
Raising variant of comp_subscription/3.
@spec get_subscription( String.t(), keyword() ) :: {:ok, Accrue.Billing.Subscription.t()} | {:error, :not_found}
@spec get_subscription!( String.t(), keyword() ) :: Accrue.Billing.Subscription.t()
@spec pause( Accrue.Billing.Subscription.t(), keyword() ) :: {:ok, Accrue.Billing.Subscription.t()} | {:error, term()}
@spec pause!( Accrue.Billing.Subscription.t(), keyword() ) :: Accrue.Billing.Subscription.t()
@spec preview_upcoming_invoice( Accrue.Billing.Subscription.t() | Accrue.Billing.Customer.t(), keyword() ) :: {:ok, Accrue.Billing.UpcomingInvoice.t()} | {:error, term()}
@spec preview_upcoming_invoice!( Accrue.Billing.Subscription.t() | Accrue.Billing.Customer.t(), keyword() ) :: Accrue.Billing.UpcomingInvoice.t()
@spec resume( Accrue.Billing.Subscription.t(), keyword() ) :: {:ok, Accrue.Billing.Subscription.t()} | {:error, term()}
@spec resume!( Accrue.Billing.Subscription.t(), keyword() ) :: Accrue.Billing.Subscription.t()
@spec subscribe(term(), term(), keyword()) :: {:ok, Accrue.Billing.Subscription.t()} | {:ok, :requires_action, map()} | {:error, term()}
Creates a subscription for the given billable (or %Customer{}) against
the configured processor. Returns intent_result(Subscription.t()).
@spec subscribe!(term(), term(), keyword()) :: Accrue.Billing.Subscription.t()
Raising variant of subscribe/3.
@spec swap_plan(Accrue.Billing.Subscription.t(), String.t(), keyword()) :: {:ok, Accrue.Billing.Subscription.t()} | {:ok, :requires_action, map()} | {:error, term()}
@spec swap_plan!(Accrue.Billing.Subscription.t(), String.t(), keyword()) :: Accrue.Billing.Subscription.t()
@spec unpause( Accrue.Billing.Subscription.t(), keyword() ) :: {:ok, Accrue.Billing.Subscription.t()} | {:error, term()}
@spec unpause!( Accrue.Billing.Subscription.t(), keyword() ) :: Accrue.Billing.Subscription.t()
@spec update_quantity(Accrue.Billing.Subscription.t(), pos_integer(), keyword()) :: {:ok, Accrue.Billing.Subscription.t()} | {:error, term()}
@spec update_quantity!(Accrue.Billing.Subscription.t(), pos_integer(), keyword()) :: Accrue.Billing.Subscription.t()