You're building a Convex application. It's fast. It's smooth. It's fully reactive. And you're at the point it's time to integrate Stripe. It's time to get paid. Now, you can integrate Stripe yourself. Or you can use Convex's Stripe component, which is what I'm going to be showing you in today's video. And all it takes, ladies and gents, is one npm command, a couple minutes of setup, and you have one-time payments and subscriptions ready in your Convex applications. Instead of just talking, why don't we set this up together? So, I have my terminal here. We're going to spin up a fresh convex project. MPM create convex at latest. We're going to call this demo app YouTube. Um, let's call it please subscribe because if you are watching this, I would appreciate a like and a subscribe. Thank you so much. We're going to use Nex.js. We're going to use Offkit Work OS for our authentication. And the CLI is going to scaffold this project. So, our project is ready to go. We're just going to run npm install. And then we're going to do npm rundev because we're going to have to set up our convex project. So we're going to call it the same name. We're not going to change the name. We're going to do a cloud deployment. And this is basically setting up our convex backend, setting up our offkit instance with works. And we are ready to go. If I go to localhost 3000, we're going to see our convex nex.js and works application. Now to set up Stripe using the Convex Stripe component, we're just going to follow this quick start guide. So first things first, we're going to run npm install. So I'm just going to do that in a new tab. And then we're told to in a convex.config.ts file to add this code. We're going to go under convex. We're going to create convex.config.ts. We're going to paste this here. This is setting up the Stripe component. And then we're told we're going to need two environment variables, a Stripe secret key and a Stripe web hook secret. So in our Convex backend, we're going to go to settings, environment variables, and it's here. We're going to add these environment variables. Now, to set this up, we're going to need a Stripe account. Hopefully you have one. I have a test version, a sandbox version here. No, I did I do not have a $25,000 withdrawal coming soon. Hopefully one day. But what we're going to do is we're going to get that secret key and that web hook secret. So in the search I'm going to search API keys. And you see here my Stripe secret key. So I'm going to go back to the docs. Just copy the name. I'm going to add Stripe secret key. And I'm going to copy the secret key on Stripe. Go back to my dashboard. Click save. Stripe secret key has been added. Now to add the web hook secret, I'm going to need to set up a web hook. So, I'm going to search web hooks. And then this pops up. Now, what I want us to do is on web hooks, click on add destination. Right now, there are some events. There's specific events we're going to have to select. You can select whichever ones you want, but in order to get this example going, I need to set up these 12 events. So, I'm just going to select these events one by one. Many hours later. So, I selected all the events. They're 12 in total. I'm going to click continue. Click continue. And now it's going to ask us for our endpoint URL. And in order to get this, I'm going to go back to my dashboard, click on URL and deploy key, and we're going to get the HTTP actions URL, not the deployment URL, the HTTP actions URL. I'm going to copy this. I'm going to go back here in my split screen. I'm going to paste it here. But notice here it says we need to add the /stripe/webhook. So this is the web hook endpoint. Once we've done that, we're just going to click create destination. And we now have our web hook secret right in front of us. So I'm going to copy this right here. And I'm going to go back to environment variables in the dashboard. Paste this here. I'm going to go back to the docs so I can copy the environment variable name exactly as is. And perfect. I click save and we are good to go. Now I need to register the web hook route. So what we're going to do is in our convex folder we're going to create an http.t ts file and just copy this code. So, let's go back to convex http.ts. Paste this right here. We're good to go. Let's go back to the next step. Now, it's saying in the stripe.ts file, here are some actions I can use to create a subscription checkout or to create a payment checkout. So, I'm going to copy this right here. And under convex, I'm going to create a stripe.ts file. And I'm going to paste this here. So this action is going to help me set up a subscription checkout and this one is going to help me set up a payment checkout. I also have multiple methods available to me. I can use create checkout session, create customer portal session, create customer, get or create customer, cancel subscription, reactivate subscription, update subscription quantity, and you can see all the available queries. I have the web hook events we track. And even in the web hook handler, if there's a specific event you'd like to track, you can add that simply. All this stuff will be in the repo down in the description down below and on this page as well. I'll link this down below. One more thing is when you go to the repo, if you click on example, there's actually an example app here called Benji store. You can run the example app and you can look at the code. Particularly if you look at the stripe.ts file, there's a couple more uh actions that I have here that I use and I think it'd be a great starting point for you. So, what I'm going to do is I'm literally going to copy this code right here. go back to the stripe.ts and I'm just going to paste this in the Stripe.ts. I do have an environment variable for app URL, but for the sake of time, I'm just going to hardcode this to localhost 3000. Our Stripe component is set up. Let's set up some subscriptions now. So, what I'm going to do is I'm going to leave the homepage as is, but I'm going to create a dashboard page where a user has to be authenticated, but also has to pay in order to have access to that page. If they do not pay, they're going to see a nice, beautiful pricing card telling them that they need to subscribe. So, with the beautiful help of AI, we created a dashboard page. You can see I'm authenticated, but I am not subscribed. So, I see subscription required, and all we did was create a dashboard page under app page.tsx. Now, there's one thing that we're missing in order to make subscriptions work, and that's a price ID. And in order to set up a price ID, all you do is you go back to Stripe. In your product catalog, you're going to create a new product. I already have made one, so I'm going to use an existing one. I'm going to click on pricing and then I'm going to grab price ID. I can click copy here. And in the code, I just have it hardcoded here for ease of use. But what you're going to do is this subscription ID is going to be passed in create checkout. We're going to pass the price ID here. And the reason why we need the price ID, we need Stripe to know to which checkout to redirect us, which product to sell us. Now, how do we have subscription required popping up here? If I go back to dashboard.tsx, all we have here is a query that queries get user subscription, right? And all get user subscription does is it returns the user subscription. Again, all we're calling is components.stripe.public.list subscriptions by user ID, right? And we're getting the subscriptions. And then we're checking for any active subscriptions. And essentially, we just have a simple if statement here. If it's undefined, it's loading. And if there's no subscription, then we see the subscription required page. Now, in order to set up the checkout, all we did was call api.stripe.create subscription checkout. And then we pass this in handle subscribe. So now back here, I'm going to click on subscribe now. And then you can see I'm brought up to the hats of the month club subscription. I've entered the stripe test card 4242 4242. And just some random credentials. After that, I'm going to click subscribe. And let's see if it works. And you see this time in the dashboard page, I see the contents of the dashboard. If I go back to my convex dashboard and I click on data, I see app here. But if I click on app and I click on Stripe, you can see I have the checkout session here. I have customer information here. I have invoices here. I have payments here. I have subscriptions here. So I have all the data that I need is in my database thanks to the Stripe component. And again, back on the dashboard page, I have access to the contents of the dashboard because I've made a payment. Let's say I wanted to cancel my subscription. I go to manage billing and then all I do is click cancel subscription. I will cancel. All of that is set up for me. Now again, the code for that is found in hand manage billing. And I can see here I call get portal URL which if I go to is an action called get customer portal URL. And you can see here that I'm getting the customer ID from the subscription. And then what I'm doing is I am creating a customer portal session. And all that's needed to set that up was just this action right here. And that is pretty much it ladies and gentlemen. There's so much more that you can do with the Stripe component. I highly encourage you look at the repo down below and check out the example app here. It uses one-time payments, subscription payments. Uh you can also do team based like seatbased payments or linking an organization to a specific payment. There's a lot of stuff here for you and the code is all here and you can boot this up into cursor whatever AI agent you want to use. You can really maximize the power of convex and stripe easily all for you under one mpm package ready for you to go. But yeah, make sure to like, comment, subscribe, hit the notification bell. Let me know what other videos you'd like to see. Thank you so much for watching this video. You've been awesome. I've been Ross. I will see you in the next one.
You've built a Convex app. It's fast, it's smooth, it's fully reactive, and now it's time to get paid. You could wire up Stripe by hand. That means checkout sessions, webhook signature verification, and keeping subscription state in sync with whatever Stripe just told you happened. Or you install Convex's official Stripe component and get one-time payments, subscriptions, and a customer portal working in about the time it takes to read this.
I'm Ross. Here's exactly how I set it up, end to end.
Diagram of the Stripe payment loop
Scaffolding a fresh app
I started from nothing. npm create convex@latest, Next.js for the frontend, WorkOS AuthKit for auth, and the CLI scaffolds the project for you. Then npm install and npm run dev, which stands up a fresh Convex backend and a WorkOS AuthKit instance in one go. Open localhost:3000 and you've got a working Convex plus Next.js plus AuthKit starter. That's the base everything else gets layered onto.
Installing the Stripe component
The component itself is one package:
1npm install @convex-dev/stripe
2
Then register it in convex/convex.config.ts, the same way you'd register any other Convex component:
That's the whole install. Everything after this is connecting it to a real Stripe account.
Wiring up Stripe: keys and webhooks
The component needs two environment variables in the Convex dashboard, under Settings, Environment Variables: STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET. I'm using a Stripe test sandbox for all of this, so nothing here touches real money.
The secret key is the easy half. Search "API keys" in Stripe's dashboard and it's right there, a straight copy-paste into Convex's environment variables.
The webhook secret takes more work, because it doesn't exist until you create a webhook endpoint. Over in Stripe's dashboard:
Go to Webhooks and click add destination.
Select the events the component tracks. They cover customers, subscriptions, invoices, payment intents, and completed checkout sessions, and the component's docs list the exact set.
For the endpoint URL, use the HTTP Actions URL from the Convex dashboard, not the regular deployment URL, with /stripe/webhook appended to it.
Click create destination.
Stripe generates the signing secret at that point. Copy it into STRIPE_WEBHOOK_SECRET back in Convex and the wiring is done.
Registering the webhook route
With both environment variables set, the webhook needs somewhere to land. That's a convex/http.ts file:
registerRoutes does the real work here. It verifies the webhook signature against STRIPE_WEBHOOK_SECRET and turns incoming Stripe events into writes against the component's own tables, all as an HTTP action. None of that is code I had to write.
Checkout actions
Next up is convex/stripe.ts, where the checkout logic lives:
The StripeSubscriptions client carries a lot more than those two actions. It also exposes createCustomerPortalSession, createCustomer, getOrCreateCustomer, cancelSubscription, reactivateSubscription, and updateSubscriptionQuantity, plus public queries for reading customers, subscriptions, payments, and invoices straight out of Convex. The repo's Benji Store example app puts several more of them to work. Clone it if you want a fuller reference than the quick start.
Gating a page behind a subscription
With the component wired up, I added a dashboard page that requires authentication and an active subscription. Miss either one and you get a pricing card instead of the dashboard content. The gate comes down to one query against the component's own data:
1// TODO: Replace with your actual Stripe Price ID from your Stripe Dashboard2constSUBSCRIPTION_PRICE_ID="price_1SXtxIGadJM86FzJ4R7ojxYf";34exportdefaultfunctionDashboard(){5const{ user, signOut }=useAuth();6const subscriptions =useQuery(api.stripe.getUserSubscriptions);7const payments =useQuery(api.stripe.getUserPayments);8const getPortalUrl =useAction(api.stripe.getCustomerPortalUrl);9const createCheckout =useAction(api.stripe.createSubscriptionCheckout);10const[portalLoading, setPortalLoading]=useState(false);11const[checkoutLoading, setCheckoutLoading]=useState(false);1213// Check if user has an active subscription14const activeSubscription = subscriptions?.find(15(sub)=> sub.status==='active'|| sub.status==='trialing'16);17
No activeSubscription means no dashboard, just the pricing card. Because it's a Convex query, the page re-renders the moment the subscription lands, with no refresh and no polling.
On the backend, getUserSubscriptions is a thin wrapper over the component's own public query:
Checkout needs one more thing before it works, a real price ID. You get that from a product in Stripe's product catalog. I hardcoded mine for the demo, but it's the value that gets passed as priceId into createSubscriptionCheckout, and it's how Stripe knows which product it's selling.
Testing the flow
Clicking subscribe calls createSubscriptionCheckout and redirects to a real Stripe Checkout page for the configured product. Pay with Stripe's test card, 4242 4242 4242 4242, any future expiration date, any CVC, and the checkout completes without touching real money.
The dashboard unlocks right away. Open the Convex dashboard's Data tab, look under the stripe component, and you can see exactly what the webhook wrote: the checkout session, the customer record, invoices, payments, and the subscription itself. All of it synced from Stripe's events into Convex tables I never had to define.
Managing billing
Canceling is just as short. A "manage billing" action calls createCustomerPortalSession with the customer ID pulled off the user's subscription record. Stripe's own hosted portal handles the rest, cancellation included.
Where to go from here
That's the core loop: one-time payments, subscriptions, webhooks, and a customer portal, all from one npm install. The component goes further than that. It supports seat-based team pricing and lets you link payments or subscriptions to an organization instead of an individual user, both of which show up in the example app. Everything here is normal, readable TypeScript, so it's easy to hand to whatever AI coding agent you're already using and ask it to extend.
All gas, no breakages
Convex is the reactive backend platform that keeps up with you and your agents. Database, functions, workflow, sync, search, file storage, and more. All TypeScript, zero glue.