Operations on Stripe Subscription objects.
A Subscription represents a customer's recurring charge against one or more Prices. Subscriptions drive Stripe's billing engine: they create Invoices on schedule, handle proration when items change, and transition through a well-defined lifecycle.
Lifecycle
incomplete
|
v
incomplete_expired (if first payment fails permanently)
|
trialing --> active --> past_due --> unpaid
| |
paused canceledState transitions are driven by Stripe's internal billing engine, not by SDK calls. Always drive your application state from webhook events, not from SDK responses — an SDK response reflects the state at the moment of the call, but Stripe may transition the subscription a moment later (trial ending, payment failing, dunning retries, scheduled cancellation, etc.).
Wire customer.subscription.updated, customer.subscription.deleted,
invoice.payment_failed, and invoice.payment_succeeded into your webhook
handler via LatticeStripe.Webhook.
Proration
When changing a subscription's items (swapping a price, changing quantity,
adding/removing items), Stripe prorates charges by default. If your client
was configured with require_explicit_proration: true, you MUST pass
"proration_behavior" either at the top level of params, inside
"subscription_details", or inside any element of the "items" array.
Valid values: "create_prorations" (default), "always_invoice", "none".
Pause collection
Use pause_collection/5 to temporarily pause automatic invoice collection.
The behavior atom is guarded at the function head — only :keep_as_draft,
:mark_uncollectible, and :void are accepted. Any other value raises
FunctionClauseError at compile-time (for literals) or runtime.
Telemetry
Subscription CRUD piggybacks on the general [:lattice_stripe, :request, *]
events emitted by Client.request/2. No subscription-specific telemetry
events are emitted — subscription state transitions belong to webhook
handlers, not the SDK layer.
Stripe API Reference
See the Stripe Subscriptions API for the full object reference and available parameters.
Summary
Functions
Cancels a Subscription.
Like cancel/3 but raises on failure.
Like cancel/4 but raises on failure.
Creates a new Subscription.
Like create/3 but raises on failure.
Converts a decoded Stripe API map to a %Subscription{} struct.
Lists Subscriptions with optional filters.
Like list/3 but raises on failure.
Pauses collection on a Subscription.
Like pause_collection/5 but raises on failure.
Resumes a paused Subscription.
Like resume/3 but raises on failure.
Retrieves a Subscription by ID.
Like retrieve/3 but raises on failure.
Searches Subscriptions.
Like search/3 but raises on failure.
Returns a lazy stream of all Subscriptions matching a search query.
Returns a lazy stream of all Subscriptions matching the given params.
Updates a Subscription by ID.
Like update/4 but raises on failure.
Types
@type t() :: %LatticeStripe.Subscription{ application: String.t() | nil, application_fee_percent: number() | nil, automatic_tax: LatticeStripe.Invoice.AutomaticTax.t() | nil, billing_cycle_anchor: integer() | nil, billing_thresholds: map() | nil, cancel_at: integer() | nil, cancel_at_period_end: boolean() | nil, canceled_at: integer() | nil, cancellation_details: LatticeStripe.Subscription.CancellationDetails.t() | nil, collection_method: String.t() | nil, created: integer() | nil, currency: String.t() | nil, current_period_end: integer() | nil, current_period_start: integer() | nil, customer: String.t() | nil, days_until_due: integer() | nil, default_payment_method: String.t() | nil, default_source: String.t() | nil, default_tax_rates: list() | nil, description: String.t() | nil, discount: map() | nil, discounts: list() | nil, ended_at: integer() | nil, extra: map(), id: String.t() | nil, invoice_settings: map() | nil, items: [LatticeStripe.SubscriptionItem.t()] | map() | nil, latest_invoice: String.t() | nil, livemode: boolean() | nil, metadata: map() | nil, next_pending_invoice_item_interval: map() | nil, object: String.t(), on_behalf_of: String.t() | nil, pause_collection: LatticeStripe.Subscription.PauseCollection.t() | nil, payment_settings: map() | nil, pending_invoice_item_interval: map() | nil, pending_setup_intent: String.t() | nil, pending_update: map() | nil, plan: map() | nil, quantity: integer() | nil, schedule: String.t() | nil, start_date: integer() | nil, status: String.t() | nil, test_clock: String.t() | nil, transfer_data: map() | nil, trial_end: integer() | nil, trial_settings: LatticeStripe.Subscription.TrialSettings.t() | nil, trial_start: integer() | nil }
A Stripe Subscription object.
See the Stripe Subscription API for field definitions.
Functions
@spec cancel(LatticeStripe.Client.t(), String.t(), keyword()) :: {:ok, t()} | {:error, LatticeStripe.Error.t()}
Cancels a Subscription.
Sends DELETE /v1/subscriptions/:id with optional pass-through params such
as "prorate", "invoice_now", and "cancellation_details".
The 3-arity form is a convenience for cancel(client, id, %{}, opts).
@spec cancel(LatticeStripe.Client.t(), String.t(), map(), keyword()) :: {:ok, t()} | {:error, LatticeStripe.Error.t()}
@spec cancel!(LatticeStripe.Client.t(), String.t(), keyword()) :: t()
Like cancel/3 but raises on failure.
@spec cancel!(LatticeStripe.Client.t(), String.t(), map(), keyword()) :: t()
Like cancel/4 but raises on failure.
@spec create(LatticeStripe.Client.t(), map(), keyword()) :: {:ok, t()} | {:error, LatticeStripe.Error.t()}
Creates a new Subscription.
Sends POST /v1/subscriptions. Runs the proration guard before dispatching.
Parameters
client-%LatticeStripe.Client{}params- Map of subscription attributes. Common keys:"customer"- Customer ID (required)"items"- List of%{"price" => "price_..."}maps
opts- Per-request overrides (e.g.,[idempotency_key: "..."])
Returns
{:ok, %Subscription{}}on success{:error, %LatticeStripe.Error{}}on failure or guard rejection
@spec create!(LatticeStripe.Client.t(), map(), keyword()) :: t()
Like create/3 but raises on failure.
Converts a decoded Stripe API map to a %Subscription{} struct.
Decodes nested typed structs:
automatic_tax→%LatticeStripe.Invoice.AutomaticTax{}pause_collection→%LatticeStripe.Subscription.PauseCollection{}cancellation_details→%LatticeStripe.Subscription.CancellationDetails{}trial_settings→%LatticeStripe.Subscription.TrialSettings{}items.data→[%LatticeStripe.SubscriptionItem{}](id preserved — regression guard against stripity_stripe #208)
Unknown top-level fields are collected into :extra.
@spec list(LatticeStripe.Client.t(), map(), keyword()) :: {:ok, LatticeStripe.Response.t()} | {:error, LatticeStripe.Error.t()}
Lists Subscriptions with optional filters.
Sends GET /v1/subscriptions.
@spec list!(LatticeStripe.Client.t(), map(), keyword()) :: LatticeStripe.Response.t()
Like list/3 but raises on failure.
@spec pause_collection(LatticeStripe.Client.t(), String.t(), atom(), map(), keyword()) :: {:ok, t()} | {:error, LatticeStripe.Error.t()}
Pauses collection on a Subscription.
Dispatches to update/4 with "pause_collection" merged into params. The
behavior is a compile-time atom — only :keep_as_draft,
:mark_uncollectible, and :void are accepted. Any other atom raises
FunctionClauseError.
Example
Subscription.pause_collection(client, "sub_123", :keep_as_draft)
# With custom resumes_at:
Subscription.pause_collection(client, "sub_123", :void, %{
"pause_collection" => %{"resumes_at" => 1_800_000_000}
})Note: Stripe has no dedicated pause endpoint — this helper is a thin wrapper
around update/4. We expose it under the exact field name (pause_collection)
rather than a generic pause/4 so IDE autocomplete and Stripe docs align.
Like pause_collection/5 but raises on failure.
@spec resume(LatticeStripe.Client.t(), String.t(), keyword()) :: {:ok, t()} | {:error, LatticeStripe.Error.t()}
Resumes a paused Subscription.
Sends POST /v1/subscriptions/:id/resume.
@spec resume!(LatticeStripe.Client.t(), String.t(), keyword()) :: t()
Like resume/3 but raises on failure.
@spec retrieve(LatticeStripe.Client.t(), String.t(), keyword()) :: {:ok, t()} | {:error, LatticeStripe.Error.t()}
Retrieves a Subscription by ID.
Sends GET /v1/subscriptions/:id.
@spec retrieve!(LatticeStripe.Client.t(), String.t(), keyword()) :: t()
Like retrieve/3 but raises on failure.
@spec search(LatticeStripe.Client.t(), map(), keyword()) :: {:ok, LatticeStripe.Response.t()} | {:error, LatticeStripe.Error.t()}
Searches Subscriptions.
Sends GET /v1/subscriptions/search. Requires "query" in params.
Eventual Consistency
Search results may not reflect changes made within the last ~1 second.
@spec search!(LatticeStripe.Client.t(), map(), keyword()) :: LatticeStripe.Response.t()
Like search/3 but raises on failure.
@spec search_stream!(LatticeStripe.Client.t(), map(), keyword()) :: Enumerable.t()
Returns a lazy stream of all Subscriptions matching a search query.
Requires "query" in params. Emits individual %Subscription{} structs via
auto-pagination; raises LatticeStripe.Error if any page fetch fails
mid-stream.
The ! suffix here does not pair with a tuple-returning search_stream/3
counterpart — there is none. Elixir Streams are inherently eager across
pages and cannot return {:ok, _} | {:error, _} for mid-stream failures,
so the only sensible behavior is to raise. This matches the convention used
by LatticeStripe.Invoice.search_stream!/3 and
LatticeStripe.Checkout.Session.search_stream!/3.
Eventual Consistency
Search results may not reflect changes made within the last ~1 second.
@spec stream!(LatticeStripe.Client.t(), map(), keyword()) :: Enumerable.t()
Returns a lazy stream of all Subscriptions matching the given params.
Auto-paginates via LatticeStripe.List.stream!/2. Raises on fetch failure.
@spec update(LatticeStripe.Client.t(), String.t(), map(), keyword()) :: {:ok, t()} | {:error, LatticeStripe.Error.t()}
Updates a Subscription by ID.
Sends POST /v1/subscriptions/:id. Runs the proration guard before dispatching.
@spec update!(LatticeStripe.Client.t(), String.t(), map(), keyword()) :: t()
Like update/4 but raises on failure.