Absolutely, this is a common scenario for SaaS apps with per-seat pricing. Laravel Spark (and underlying Laravel Cashier) supports per-seat billing, but you need to handle seat count changes in your application logic to ensure the subscription quantity (which represents seats) is kept in sync with your actual seat count.
Answers to your questions:
-
Can this be done without defining multiple fixed plans in Spark?
Yes. You do not need to define multiple plans for different seat counts. Instead, you use a single plan with a per-seat price, and update the subscription "quantity" to match the seat count. -
Or is the right approach to define multiple plans but programmatically move the user to the right plan when their seat count changes?
No, that's not necessary for per-seat billing. Use a single plan and adjust the quantity. -
Is there a best practice for handling this scenario in Spark/Cashier?
Yes: update the subscription quantity whenever the seat count changes. Cashier will automatically handle proration and billing adjustments.
How to Implement Automatic Seat-Based Billing
Step 1: Set Up a Per-Seat Plan in Stripe
- In Stripe, create a plan/product with a per-seat price (e.g., $10 per seat per month).
- In Spark, reference this plan in your configuration.
Step 2: Update Subscription Quantity When Seat Count Changes
Whenever the user's seat count changes (e.g., they add/remove a team member), update the subscription's quantity:
// Assume $user is the billable model (e.g., Team or User)
// $newSeatCount is the updated number of seats
$user->subscription('default')->updateQuantity($newSeatCount);
- This will update the Stripe subscription quantity and automatically adjust the billing amount.
- Cashier will handle proration if the seat count changes mid-cycle.
Step 3: Automate the Update
You should call updateQuantity() whenever your seat count changes. For example, if seats are tied to team members:
public function addTeamMember(Request $request, Team $team)
{
// ... logic to add member ...
$team->subscription('default')->updateQuantity($team->members()->count());
}
And similarly when removing a member.
Summary
- Do not create multiple plans for different seat counts.
- Use a single per-seat plan and update the subscription quantity to match the actual seat count.
- Call
$subscription->updateQuantity($seatCount)whenever the seat count changes.
References:
Let me know if you need a more detailed code example or have a specific use case!