Skip to Content
DocumentationSubscriptions

Subscriptions

Create a subscription

use CashierBundle\Contract\BillableEntityInterface; use CashierBundle\Service\SubscriptionService; final readonly class SubscriptionController { public function __construct( private SubscriptionService $subscriptionService, ) { } public function create(BillableEntityInterface $user): void { $subscription = $user->newSubscription('default', 'price_monthly') ->trialDays(14) ->create('pm_card_visa'); } }

SubscriptionBuilder capabilities

$builder = $user->newSubscription('default'); $builder->price('price_monthly', quantity: 1); $builder->meteredPrice('price_metered'); $builder->trialDays(14); $builder->withCoupon('coupon_xxx'); $builder->withPromotionCode('promo_xxx'); $builder->withMetadata(['segment' => 'pro']); $builder->withOptions(['collection_method' => 'charge_automatically']); $builder->withBillingThresholds(['threshold_cycles' => 3]); $builder->anchorBillingCycleOn(new \DateTime('2025-01-01')); $builder->withPaymentBehavior('default_incomplete'); $builder->noProrate(); // or prorate()

Check state

$user->subscribed('default'); $user->onTrial('default'); $subscription = $user->subscription('default');

Full lifecycle

MethodDescription
active()Status active or trialing
valid()active OR onTrial OR onGracePeriod — common use case for “subscription grants access to service”
onGracePeriod()Cancelled subscription but ends_at in the future — access should still be granted
paused()Stripe paused subscription
notPaused()Stripe non-paused subscription
onPausedGracePeriod()paused subscription with grace period
ended()Cancelled AND ends_at in the past
incomplete()Payment failure status (configure Cashier::$deactivateIncomplete)
pastDue()Pending payment status (configure Cashier::$deactivatePastDue)
recurring()Active, not in trial, not in grace period
$subscription->active(); $subscription->pastDue(); $subscription->paused(); $subscription->onGracePeriod();

Evolve the subscription

$subscriptionService->swap($subscription, 'price_yearly'); // in-place swap, default proration $subscriptionService->swapAndInvoice($subscription, 'price_yearly'); // + invoice the proration now $subscriptionService->updateQuantity($subscription, 5); $subscriptionService->incrementQuantity($subscription, 2); $subscriptionService->decrementQuantity($subscription, 1); $subscriptionService->cancel($subscription); // end of period $subscriptionService->cancel($subscription, immediately: true); // immediate $subscriptionService->resume($subscription); // requires onGracePeriod() to be true

Multi-price

For a subscription with multiple prices (base + add-on):

$user->newSubscription('default') ->price('price_monthly_base') ->price('price_add_on', 1) ->create('pm_card_visa');

Evolve a multi-plan subscription after creation — add or remove an add-on, or target a specific price when several items coexist:

$subscriptionService->addPrice($subscription, 'price_add_on', quantity: 1); $subscriptionService->removePrice($subscription, 'price_add_on'); // refuses the last item // Deterministic targeting on multi-item subscriptions $subscriptionService->swap($subscription, 'price_pro', fromPrice: 'price_basic'); $subscriptionService->updateQuantity($subscription, 10, price: 'price_seats'); $subscriptionService->incrementQuantity($subscription, 3, price: 'price_seats');

When a subscription has several items, quantity and swap operations require the target price (or fromPrice); otherwise they throw instead of guessing.

SubscriptionItem

Each price in a subscription corresponds to a SubscriptionItem:

$subscription->items; // Collection of SubscriptionItem $item = $subscription->items()->first(); $item->stripePrice; // price_xxx $item->quantity; $item->meterId; // for usage-based $item->meterEventName;

Usage-based billing (Meters API)

Metered usage is reported through Stripe’s Meters API via MeterService. Each metered SubscriptionItem stores the meter event name it reports to.

// Create a meter once (defaults to sum aggregation) $meterService->createMeter('API requests', 'api_requests'); // Report usage for a metered item (uses its meter_event_name + the customer's Stripe id) $item = $subscription->items()->first(); $meterService->reportEventForItem($item, 100); // Or report an arbitrary meter event directly $meterService->reportEvent('api_requests', 100, $customer->getStripeId());

From the CLI: php bin/console cashier:report-usage <local-item-id> <quantity>.

Checkout subscription

If you prefer a subscription created via Stripe Checkout, use CheckoutService::createSubscription().

Checkout →

Last updated on