The first release of SaasRock came with only 2 pricing models: Flat-rate and Per-seat. Soon enough I would realize 2 things: I need to support more pricing models, and that pricing is VERY complicated.
This is the biggest SaasRock update since v0.3.
I could not have built this in 2 weeks without the SaasRock pillars:
Get SaasRock now to lock-in the current price before it increases!
Look at some examples of what SaasRock did not support:
Those are just pricing models, I wanted to improve the overall pricing and subscription UX:
/pricing
page with Stripe Checkout SessionAs a good developer, I promised to have this ready by the end of August, and we're almost halfway through September (but I guess this does not only apply to software development).
I'm not going to lie, this was way more complicated than I anticipated, and I'm aware that even with these new features, I'm just scratching the surface in terms of pricing. No wonder why Stripe's worth is in the billions.
The main problem was allowing for multiple subscriptions because the previous model was designed to support one, just take a look at the models before/after diff:
These new models look scary, but gave me a lot of flexibility to handle:
The development stage when building SaaS apps is a top priority for SaasRock, so creating, updating, and deleting pricing plans to test all scenarios should be as simple as possible.
This is the reason I built a plans.server.ts
file with default Pricing Plans (Basic, Starter, and Enterprise):
And before creating them, you can preview each pricing model that would fit your SaaS at the /pricing
page with a ?model=
parameter.
Once you're somewhat happy with the plans, devs can easily generate them from /admin/setup/pricing
:
But you can also create them one by one at /admin/setup/pricing/new
:
My recommended way of doing it though, is using the plans.server.ts
file, this way you would also have to think about your core features and their limits: are API calls monthly? unlimited? not included?
Once the plan is created, there's no way to update the price(s). This is because that's how Stripe works, and it makes sense since updating the price when there are subscribed customers would make an invoicing mess.
Charging users based on their usage requires a separate blog post on its own, but I'll try to talk about the main things.
If you're a developer, I don't have to tell you why Pay-as-you-go gives a lot of flexibility when building software, and that it's a trend.
Let's test with the following pricing plans, which include Flat rate + Usage-based prices:
Our Starter product, should look like this, note that I'm supporting USD and MXN currencies.
The Stripe Checkout session would look like this:
The Stripe subscription will be Active, have two Subscription Items, and have an Upcoming Invoice.
Now the fun part, let's report some usage. Since my plan will track API calls, you could follow this guide to learn how to use the API.
The first API call would give us the following response:
The Upcoming invoice will still be $199 because API calls are free until we reach 10 units:
After reaching 11 units (API calls), we'll reach the 2nd tier, where each unit costs $0.04 and there's a flat fee of $10:
And our customer can see the Upcoming Invoice at /app/:tenant/settings/subscription
:
Now let's get to the 3rd tier, with 21 units (API calls), where units cost $0.02 (cheaper) but there's a flat fee of $20:
These examples were with VOLUME tiers, I'm going to do the following steps to try GRADUATED tiers (read more about this here):
/app/:tenant/settings/subscription
)plans.server.ts
/admin/setup/pricing
/pricing
and subscribe to the Starter planAfter reaching 21 units (API calls), the Upcoming Invoice total calculation is different. As we're using GRADUATED prices, each tier accumulates.
You may wonder how I'm reporting usage, it's really simple:
The reportUsage(tenant, unit) function handles everything:
Finally, we could use this model to charge for:
And of course, once bought, it belongs to the customer for ever.
To showcase this, I went and created a new plan called "All-access" at /admin/setup/pricing/new
.
Using the same account that I used to test the usage-based pricing (I used the /admin/users
impersonate button), I'll go to the /subscription/:tenant
page to buy the All-access plan.
Finally, our subscription looks like this:
Almost every SaaS requires you to sign up before going to a /subscription page. For me, this is good UX.
But there's one problem, SPAM. You can solve using one or multiple strategies:
SaasRock already supports the three of them.
After our users create their accounts, they can use our app (with its limits), and they can manage their subscription at /app/:tenant/settings/subscription
and click on View all plans & prices to be redirected to /subscribe/:tenant
page.
But what if we could reduce SPAM even more?
Asking users to subscribe before creating an account will turn off a lot of prospects for sure, but hey, it will also turn off a lot of bots almost. For me, this is great UX.
This is actually what TailwindUI does: Provide your email and payment details first and set up your account after.
SaasRock uses Stripe Checkout for subscriptions, so 2 main issues arise from this:
/subscribe/:tenant
route instead? Or is he/she trying to create a separate account?First things first, our user paid from our /pricing
page, so it makes sense that the success redirection URL should be /pricing/session_id/success
.
Upon arrival to the Checkout Success page, we need to store the session with a pending=true state in the database, AND send an email to the user with the redirected URL: /pricing/session_id/success
.
This way, we've backed up the session with the subscribed plan(s) by storing it in the database, and by sending it to the user.
And of course, the session cannot be used more than once (only if our session status is pending=false):
If you want to enable/disable one sign-up flow, just update the following flags:
/subscribe/:tenant
/pricing
are hidden/register
redirects to /pricing
I encountered a few tedious problems, but I solved them so you don't have to.
If you're planning to have customers in England and Spain, better to have English and Spanish translations.
Take a look a the following translation keys:
You can use those keys when creating/updating Plan Features:
So your pricing page would be dynamic (currency and language):
If that's not internationalization, I don't know what is 😜.
Take a look at the following plans that I have subscribed to:
I should have the right to:
You could customize this functionality by modifying the mergeFeatures(features) function at the subscriptionService.ts
file:
And what about per seat pricing plans? Let's see if it also works. 3 Starter Seats:
3 times the feature limit values:
I created the following plans, with 2 usage-based units:
And a flat fee, so every plan has 3 prices.
If I subscribe to the Starter plan, this is the Checkout page:
You could imagine that if I create 10 employees from the API, that would be 20 units 😂, but I'll do it just for demonstration purposes - I'm using VOLUME tiers.
11 Employees created + 11 successful API calls = $219.88
v0.6 is fun and all, but I'm already hearing SaasRock subscribers asking for:
I can't promise any feature, but I can tell you that the majority of the community wishes for a 100% stripe implementation, so maybe forget about Paddle for a while (or forever):
Now that the Pricing & Subscriptions got a huge upgrade, I'll start working on another feature that is getting me excited to build: Affiliate + Referrals:
/affiliates
: Become an affiliate/affiliate
: My dashboard as an affiliate, my analytics, my payouts, my referrals/admin/affiliates
: CRUD affiliates, accept requests, view links/admin/affiliates/referrals
: Visitors/subscriptions per link/admin/affiliates/commissions
: Commission per sale/affiliate, and Paid status (payments would be manual for MVP of course, but I guess a PayPal integration mid-term or Stripe Connect)Some notes:
Subscribe to the SaasRock newsletter and stay in the loop!
PS: v0.6 will be released Sunday 11th, 2022 at night GMT-5.
Thanks for reading!
- Alex
We respect your privacy. We respect your privacy.
TLDR: We use cookies for language selection, theme, and analytics. Learn more. TLDR: We use cookies for language selection, theme, and analytics. Learn more