Skip to content

Feature Request: Flexible Billing Mode Support #1832

@JoshSalway

Description

@JoshSalway

Stripe's flexible billing mode provides more accurate and predictable billing behavior for prorations, usage-based pricing, flexible invoicing, and trial settings. It also unlocks new capabilities like mixed interval subscriptions that aren't available in classic mode. Stripe recommends all new integrations use flexible billing mode, and as of API version 2025-09-30.clover, it's the default for all new subscriptions created through the API.

Cashier doesn't currently support it. This issue documents what's needed, the Stripe API requirements, and the known incompatibilities with the current codebase.

What flexible billing enables

  • Usage-based billing: charge customers based on actual consumption via Stripe meters, with usage billed at the price in effect when reported (not the most recent price)
  • Hybrid subscriptions: combine a fixed base price with metered usage on a single subscription
  • Mixed interval subscriptions: bill for multiple recurring prices with different intervals (e.g. monthly and yearly) on one subscription
  • Accurate credit prorations: calculated based on the original debited amount, accurately accounting for historical taxes, discounts, and billing cycles
  • Proportional discount application: discounts applied proportionally to each subscription item during proration instead of distributed evenly
  • Configurable proration on cancellation: disable prorations for truncated first billing periods when using cancel_at
  • Consolidated invoicing: a single invoice on renewal, eliminating separate invoices for removed metered items
  • No zero-amount line items: metered items added to a subscription don't generate zero-amount line items
  • Consistent billing cycle anchor: no implicit resets during operations like setting cancel_at or switching to a non-zero price
  • One-way migration: migrate existing classic subscriptions to flexible via the /v1/subscriptions/:subscription/migrate endpoint, with timestamp tracking via billing_mode_details.updated_at

What Cashier needs

Core billing mode support

  1. Set billing_mode[type] when creating subscriptions via SubscriptionBuilder, CheckoutBuilder, and subscription schedules
  2. A global default via Cashier::defaultBillingMode('flexible') and per-subscription override via withBillingMode('flexible')
  3. Migration of existing subscriptions to flexible mode via Stripe's /migrate endpoint (migrateToFlexibleBillingMode())
  4. A way to check whether a subscription uses flexible billing (usesFlexibleBilling())
  5. A local billing_mode column on the subscriptions table to avoid an API call on every check

Parameter guards for flexible mode

Flexible billing changes which parameters Stripe accepts. Cashier needs to conditionally omit or adjust incompatible parameters when a subscription is in flexible mode:

  • clear_usage must not be sent when removing or swapping metered prices
  • billing_mode is a top-level param on subscription create, but lives inside subscription_data on checkout sessions and quotes
  • billing_mode cannot be sent on subscription update; migration uses the dedicated /migrate endpoint
  • Subscription schedules on 2025-09-30.clover use duration instead of the removed iterations parameter

Currently incompatible Stripe features

Per Stripe's documentation, the following features return a 400 error when used with flexible billing mode:

  • Prebilling (Preview)
  • Legacy usage-based billing (non-meter based)
  • Legacy 3P tax integrations using pay_immediately=false (e.g. Avalara)
  • The legacy max_occurrences parameter
  • Subscription-specific retry_settings (Preview)

Note: billing thresholds were initially incompatible but Stripe added support as of API version 2025-07-30.basil.

Broader features (subsequent work)

  • Subscription schedules: multi-phase lifecycle management with flexible billing mode support and duration-based phases
  • Quotes: draft, finalize, accept/cancel lifecycle with billing mode in subscription_data
  • Billing credits: credit balance management building on existing creditBalance()/debitBalance()
  • Usage thresholds: database-backed usage monitoring with overage calculations
  • Webhook handlers: subscription_schedule.* and quote.* events

Stripe API version requirements

API Version What it introduces stripe-php version
2025-06-30.basil billing_mode parameter, /migrate endpoint, flexible billing opt-in v17.4.0
2025-07-30.basil Billing thresholds compatible with flexible mode, mixed interval subscriptions, duration parameter for schedule phases v17.5.0
2025-09-30.clover Flexible becomes the default for new subscriptions, removes iterations on subscription schedules v18.0.0

Stripe recommends new integrations use 2025-09-30.clover or later. Existing integrations on older API versions default to classic mode and are unaffected.

Meter behavior note

Flexible billing requires usage products to use meters. Meters are customer-level, not subscription-level. If a customer has multiple subscriptions with the same metered price, reported usage aggregates across all subscriptions. This is a fundamental behavioral change from classic metered billing that Cashier's documentation should call out.

Known incompatibilities in current Cashier

Issue Description Status
#1819 clear_usage = true sent when removing/swapping metered prices on flexible subscriptions causes Stripe to reject the request Fixed in #1820
#1772 (comment) @j3j5 independently identified the same clear_usage incompatibility Documented

Prior work

Design decisions from the review process

These emerged from @crynobone's review on #1772 and subsequent discussion:

  1. Cashier::defaultBillingMode('flexible') in a service provider rather than a config file value
  2. cashier_quotes table name (prefixed) to avoid conflicts with existing quotes tables in apps
  3. migrateToFlexibleBillingMode() method naming instead of generic migrateBillingMode()
  4. assertFlexibleBillingSupport() for the validation method name (per @yoeriboven)
  5. 'classic'|'flexible' type hint on billing mode parameters

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions