Support for annual billing option
Part of https://github.com/FlowFuse/customer/issues/233
We want to provide the option for a user to pay annually, rather than the existing monthly model. This issue is for the technical work needed to achieve that.
Current Model
- A TeamType can have:
- (A) a stripe product/price associated with it
- (B) a stripe product/price associated with each instance type and device
- Our Starter team has (A) - a monthly cost for the team to exist.
- Our Team/Enterprise teams have (B) - no monthly cost for the team to exist, but cost per instance/device that exists (that varies based on the size of the instance)
Desired Model
The desired model described in the linked issue is to have a minimum cost for Team/Enterprise - and for annual billing to be available in order to apply a discounted rate.
We have two options for implementing that:
-
Use the current model to add a stripe price/product to the Team/Enterprise tier teams. Then set the 'free' allocation of the available instance type to match what is included in that base price. This way the billing system won't do on-demand charging for the first X instances - only when they go above X. The problem with this approach is it doesn't allow the user to commit to more than the minimum number that the team price factors in. So whilst this may offer a quick solution, it doesn't get us closer to where we want to be.
-
The better model is, when setting up billing, let the user pick the desired team type, then be shown a slider for how many instances they want included - with the minimum applied. When they checkout, we then add the appropriate number of instances items to the subscription. The billing system can then ensure the item count on the subscription does not go below the defined minimum.
Annual billing
Supporting annual billing option is fairly straight forward to do the basics:
- The current billing interval is determined by the stripe price. So the starting point is to create yearly price objects for each stripe product.
- The TeamType configuration (which holds tier-specific pricing for each instance type), needs extending to include the yearly price details (two fields to add: stripe price ID, and the price we show on the page) for the team.
- Add toggle on Upgrade Team page (where the user picks Team Tier), for monthly/yearly pricing.
- Add flag on api to indicate monthly/yearly billing choice
Problems to resolve
If a user subscribes with Annual billing for 5 instances, what happens when they create their 6th instance.
- we currently add prorata usage to their next invoice. On a monthly schedule, that's acceptable - they pay for what they have used over the last month.
- For annual billing, that model doesn't work: user subscribes on Jan 1st for 5 instances. On Jan 2nd they create 30 instances - we don't charge them until their next invoice a year from now.
- The alternative is we invoice immediately for any billing changes beyond their annual commitment. But then we are then back to raising an invoice for each billable change the user makes. That is something we were specifically asked to stop doing.
We need to resolve the desired behaviour for on-demand usage that exceeds their initial subscription.
I've looked at how other services approach this to see if there's a model we can easily adopt.
- Figma - ref.
- You can pay annually for X seats.
- If you add additional seats, they are billed monthly
- You are given an opportunity to convert monthly seats to yearly seats. They get added, pro-rata, to the annual subscription
- 30 days prior to yearly renewal, they send a reminder email, giving you a chance to convert any monthly to yearly
With my mental model of Stripe subscriptions, this is a model I can relate to. Stripe doesn't let you have a single subscription with mixed billing cycles - so this looks like the account has separate annual and monthly subscriptions - with a chore of moving items from the monthly-sub to the annual-sub.
All of our billing code operates on the principle of a team having a single subscription. Having a model with two different subscriptions to manage will require a fair amount of work - both backend and frontend.
@knolleary To keep invoice changes to a minimum when a customer is in an ongoing annual plan, I propose that customers on Team and Enterprise (1) start a new annual subscription whenever they add instances and (2) buy instances in bundles of 5.
(1) is for simplicity and because it is compatible with our current implementation of Stripe. Michael informs me that this is how Salesforce handles mid-term contract upgrades. (2) will minimize the number of contract changes that are needed. If a customer isn't sure whether they want to buy 12 instances or 14 instances, we inform them that they'll actually be paying for 15, and the next bump up is to 20 instances. This will allow them to allocate their flows appropriately, provide the opportunity for upsells, and minimize the number of invoicing changes that have to be made during a 12 month period.
@gstout52 what you are proposing means a team can have multiple subscriptions. That is a fundamental change to our billing system that has always worked on the basis that a team has a single subscription. It opens up a lot of edge cases to deal with - what happens if they fail to pay on one subscription but not another for example. I don't know if that can be achieved in the next 3 weeks with all of the other pieces we still need to do for the pricing changes.
(1) start a new annual subscription whenever they add instances and (2) buy instances in bundles of 5.
The more I think about how this would work, the bigger the set of changes are.
Our current model is very simple; each time an instance is created/deleted, we validate the quantity of the corresponding stripe product matches what they have so they are charged for what they are using.
As discussed previously, I could see a path forward to have a minimum purchase when they create the team; have the $-amount of the team price be equivalent to X instances - where X is a fixed amount.
To support pre-purchasing bundles of instances is a significant change. We now need to track separately what they have purchased from what they are using. Plus all of the UX work around what happens if a user has paid for 5 instances and they want to create a 6th. In our current model, they just create the instance. But now we have to block that until they go via stripe to create a new subscription (entering in all their billing details again) to increase their allowed instance count.
For this, let's start with providing annual billing on Starter only. It can serve as a first iteration, has value in improving ARR and reducing churn, and won't require answering some of these more challenging questions. What do you think, @knolleary ?
@knolleary I'm resurrecting this one, only for the Team tier. Starter churn is low, and we do not have a business reason for offering a price below $20/mo for Starter. So, focusing just on Team FFC, I believe the following is possible:
- Offer Team at an annual price equal to ($175 x 11) , for the platform fee itself. That constitutes a free month for annual subscribers.
- Continue to bill additional instances on a per-instance basis, as now.
@knolleary I added this to the Development board, but before scheduling it I'd like to understand the scope of work. If it sounds worthwhile, I'll then schedule it afterward.
I will spend some time today/tomorrow scoping out what is possible. We can then decide how we want to schedule the work.
Interested Team customers:
- CIRRO ([email protected])
- Butchko, Inc ([email protected])
- iTheta ([email protected])
Possibly Interested Team customers:
- L2L ([email protected])
- Enrisk Consulting ([email protected])
I think https://stackoverflow.com/questions/63076853/stripe-charge-changes-monthly-for-yearly-subscription outlines the approach we can take at the low level. I am going to try to run a manual test so I can document the invoicing behaviour it leads to. From there, I can scope the UI changes needed to drive it.
Good news; I have tested this stripe behaviour and I think it is what we've been looking for.
To summarise:
- we can create an annual subscription. It invoices immediately on day 0. The 'next' invoice is then scheduled 1 year from now.
- whenever updates are made (instance added/removed) we can use
pending_invoice_item_intervalto say 'invoice this change in 1 month', rather than wait for the invoice on the 1 year anniversary.
With that understood, I can now scope/size the platform changes needed to support this.
- Add configuration options to TeamType to capture annual billing config (price/product/description) items
- Update UI to provide monthly/annual toggle. Not sure where that best fits in the flow of things - need to stare of the screens a bit more.
- Update billing table to reflect annual pricing choice.
- Update billing logic to apply the
pending_invoice_item_intervalsetting. I need to run a test to see what happens if that is set on a monthly billing subscription; ie, can we get away with always setting it, or does it need to be conditional.
@knolleary Checking on this.
We discussed this on the strategy call today. Concern was raised over the engineering time needed to do this fully, compared to the immediate upside it brings.
A smaller iteration was suggested to add an annual billing prompt in the UX that acts as a Contact Us call to action - so not fully self-service, but allows a team to raise their hand with Sales that they are interested in annual billing. I'll raise a separate item for that with a sizing attached.
We discussed this on the strategy call today. Concern was raised over the engineering time needed to do this fully, compared to the immediate upside it brings.
A smaller iteration was suggested to add an annual billing prompt in the UX that acts as a Contact Us call to action - so not fully self-service, but allows a team to raise their hand with Sales that they are interested in annual billing. I'll raise a separate item for that with a sizing attached.
If the consensus is that self-service annual billing is too much work, I am concerned that this is not the solution because then we will have to put self-service customers on manual billing.
Thanks, @knolleary ! Passing along a question from ZJ: is it possible for the customer to purchase additional instances for the year and make that a part of the annual purchase? The goal would be to set a high water mark for usage, and provide a discount for the accordingly.
We're moving forward with Nick's proposed version of this, beginning with the Starter tier. This will reduce churn on the Starter tier, which directly correlates to increased MRR. We can consider a high water mark proposal down the line as a subsequent iteration.
I have raised #5896 that adds support for annual billing.
A user will be able to select monthly/annual billing for any TeamTypes that have been configured with Annual billing stripe prices.
They can chose the billing cycle at the following points:
- Creating a new team
- Whilst in trial and they choose to setup billing
- Upgrading (or Downgrading) their existing subscription.
What this does not support currently is:
- A user currently pays monthly and wants to move to annual at the same tier.
I will see if I can get that done before I'm out - otherwise, will get it raised as a follow-on issue for someone to pick up.