In Part 2, we gave Learnflow AI a solid foundation — authentication, protected routes, and credit-based usage.
Now comes the most important phase for sustainability: turning Learnflow AI into a real product.
This means:
- Pricing tiers
- Usage caps per plan
- Upgrade flows
- Real billing integration
Kinde Billing + Convex gave me the stack to launch subscriptions in hours, not days — and tightly tied into the infrastructure I’d already built.
Why Subscriptions?
Talking to GPT-4 with your voice is magical — but also expensive.
Every user session involves real-time audio, transcription, and GPT-4 tokens. If I wanted to avoid burning cash at scale, I needed a billing model that made sense.
Here’s what was non-negotiable:
- Stripe-powered payments
- Tiers with clear limits
- Upgrade/downgrade support
- Backend feature gating
Kinde Billing delivered all of that — fully integrated with the existing auth layer from Part 2. No custom Stripe server. No fragile webhooks. Just config, plans, and secure checkout.
Learnflow AI’s Pricing Model
For launch, I kept pricing simple:
Tier | Credits | Access |
---|---|---|
Free | 10 | Limited voice sessions |
Pro | 100+ | Unlimited GPT sessions, faster voice latency |
Every logged-in user already had a credits
field and plan
in Convex. Now I just needed to plug billing on top.
Step 1: Enable Billing in Kinde
Kinde handles Stripe on your behalf. Inside the Kinde dashboard:
- Go to "Billing" → click Enable billing
- Connect your Stripe account
- Define your plans (free, pro)
- Add pricing table keys and plan slugs
This gives you:
- Hosted checkout pages
- Subscription syncing
- Webhook support
You don’t have to spin up a Stripe backend — it’s all managed by Kinde.
You can read this post for a complete walkthrough on setting this up.
Step 2: Access Billing Info in User Sessions
Once a user logs in, Kinde injects their billing tier directly into their session. You can fetch it like this:
import { getKindeServerSession } from "@kinde-oss/kinde-auth-nextjs/server";
export async function getCurrentUser() {
const { getUser } = getKindeServerSession();
const user = await getUser();
return {
email: user?.email ?? "",
name: user?.given_name ?? "",
tier: user?.user_metadata?.plan || "free",
};
}
You now have access to a user’s billing tier (free
, pro
, etc.) in any page, layout, or server action.
Step 3: Sync Plan into Convex
If you want full control or offline logic, store the billing tier in Convex too.
export const syncUserPlan = mutation({
args: {
email: v.string(),
plan: v.string(),
},
handler: async (ctx, args) => {
const user = await ctx.db
.query("users")
.filter((q) => q.eq(q.field("email"), args.email))
.unique();
if (!user) return null;
return await ctx.db.patch(user._id, { plan: args.plan });
},
});
You can call this during login, onboarding, or even inside a webhook handler.
Step 4: Add an Upgrade Page with Billing Portal Access
Once your users hit their usage limit, you want them to see a clear path to upgrade — not just a “you’re out of credits” error.
To support this, we created a dedicated upgrade screen. Kinde makes this possible with a hosted billing portal, powered by Stripe and tied to the user’s existing Kinde account.
Here’s how we implemented it.
"use client";
import { PortalLink } from "@kinde-oss/kinde-auth-react";
export default function UpgradePage() {
return (
<div className="p-6">
<h1 className="text-2xl font-bold mb-4">Upgrade your plan</h1>
<p className="mb-6">
Get more credits and unlock advanced features like voice-first learning and longer sessions.
</p>
<PortalLink path="organization_billing">
<button className="btn btn-primary">Upgrade to Pro</button>
</PortalLink>
</div>
);
}
This redirects the user to a Kinde-hosted portal under the hood.
UX Tip: Show This Prompt When Users Hit a Limit
If a user runs out of credits or tries to access a Pro-only feature, use a helpful banner or modal to direct them here:
<div className="p-4 bg-yellow-50 text-yellow-700 text-sm rounded">
You’ve used all your credits. Upgrade to continue learning.
<Link href="/dashboard/upgrade" className="underline ml-2">
Upgrade now →
</Link>
</div>
This kind of prompt makes it easy to convert trial users into paying customers at the moment of friction — without needing a separate email flow or reminder.
Once enabled, users can:
- View or change their billing plan
- See usage and payment history
- Cancel or resume subscriptions
- Trigger plan upgrades in real time (reflected in your app via Kinde session metadata)
And you don’t have to manage any of that yourself — it’s all Stripe + Kinde.
Step 5: Gate Features by Plan (in UI + Server)
Once the plan is available in Convex or your session, gating is easy.
Backend check:
if (user.plan !== "pro" && sessionType === "voice") {
throw new Error("Upgrade to Pro to use voice sessions.");
}
Frontend lock:
{user?.plan === "free" && (
<div className="p-4 bg-yellow-50 text-yellow-700 text-sm rounded">
Voice companions are available on the Pro plan.
<Link href="/dashboard/upgrade" className="underline ml-2">
Upgrade now →
</Link>
</div>
)}
This helps nudge users without blocking the entire experience.
Step 6: Tier-Specific Credit Handling
Let’s say you want Pro users to have unlimited or discounted usage. You can enforce that per request:
const creditCost = user.plan === "pro" ? 0 : 1;
if (user.credits < creditCost) {
throw new Error("You’re out of credits. Please upgrade.");
}
await ctx.db.patch(user._id, {
credits: user.credits - creditCost,
});
You can also:
- Give monthly credit resets
- Add bonus credits on upgrade
- Vary cost per feature
What You’ve Just Built
- Subscription billing via Kinde + Stripe
- Checkout page with upgrade logic
- Tier-based feature locking
- Flexible credit deductions by plan
- Convex-powered backend logic
You’ve now got:
- A real monetization engine
- A scalable way to support GPT costs
- The infrastructure to go from hobby → business
What’s Next?
You’re ready to:
- Onboard your first users
- Get real usage data
- Ship a polished V1
With auth, access, usage tracking, and billing — you’re now operating like a real SaaS.
Final Thoughts
It’s never been easier to build real AI-first products.
The combination of:
- Next.js App Router
- Kinde for auth + billing
- Convex for reactive logic + data
- Vapi for voice
...makes it possible to go from idea to paid product in 3 weekends.
If you’ve got something valuable, don’t stop at a prototype.
Don’t just ship it. Sell it.
Top comments (0)