Accrue.Billing.SubscriptionActions (accrue v1.0.0)

Copy Markdown View Source

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

Pause and resume

  • pause/2 — pause collection (void, mark_uncollectible, or keep_as_draft)
  • unpause/2 — resume a paused subscription
  • resume/2 — cancel a pending cancellation (undo cancel_at_period_end)

Cancel

Read

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

cancel(sub, opts \\ [])

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

cancel!(sub, opts \\ [])

cancel_at_period_end(sub, opts \\ [])

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

cancel_at_period_end!(sub, opts \\ [])

@spec cancel_at_period_end!(
  Accrue.Billing.Subscription.t(),
  keyword()
) :: Accrue.Billing.Subscription.t()

comp_subscription(billable, price_spec, opts \\ [])

@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.

comp_subscription!(billable, price_spec, opts \\ [])

@spec comp_subscription!(term(), term(), keyword()) :: Accrue.Billing.Subscription.t()

Raising variant of comp_subscription/3.

get_subscription(id, opts \\ [])

@spec get_subscription(
  String.t(),
  keyword()
) :: {:ok, Accrue.Billing.Subscription.t()} | {:error, :not_found}

get_subscription!(id, opts \\ [])

@spec get_subscription!(
  String.t(),
  keyword()
) :: Accrue.Billing.Subscription.t()

pause(sub, opts \\ [])

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

pause!(sub, opts \\ [])

preview_upcoming_invoice(sub_or_customer, opts \\ [])

@spec preview_upcoming_invoice(
  Accrue.Billing.Subscription.t() | Accrue.Billing.Customer.t(),
  keyword()
) :: {:ok, Accrue.Billing.UpcomingInvoice.t()} | {:error, term()}

preview_upcoming_invoice!(sub_or_customer, opts \\ [])

resume(sub, opts \\ [])

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

resume!(sub, opts \\ [])

subscribe(billable, price_spec, opts \\ [])

@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()).

subscribe!(billable, price_spec, opts \\ [])

@spec subscribe!(term(), term(), keyword()) :: Accrue.Billing.Subscription.t()

Raising variant of subscribe/3.

swap_plan(sub, new_price_id, opts)

@spec swap_plan(Accrue.Billing.Subscription.t(), String.t(), keyword()) ::
  {:ok, Accrue.Billing.Subscription.t()}
  | {:ok, :requires_action, map()}
  | {:error, term()}

swap_plan!(sub, price, opts)

unpause(sub, opts \\ [])

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

unpause!(sub, opts \\ [])

update_quantity(sub, new_quantity, opts \\ [])

@spec update_quantity(Accrue.Billing.Subscription.t(), pos_integer(), keyword()) ::
  {:ok, Accrue.Billing.Subscription.t()} | {:error, term()}

update_quantity!(sub, new_quantity, opts \\ [])