convex-billing
get-convex/agent-skills
Add Stripe billing and subscription management to Convex apps with checkout, webhooks, and server-side gating.
What is convex-billing?
Integrates Stripe payments into Convex backends using the @convex-dev/stripe component. Provides checkout sessions, automatic webhook handling with signature verification, subscription state storage, and server-side access control. Use this when you need production-ready billing in a Convex app.
- Create Stripe checkout sessions for subscriptions
- Automatically verify and process Stripe webhooks
- Store subscription state in Convex database tables
- Gate features behind active subscription status via server queries
- Handle customer creation and subscription lifecycle management
- Support both test and live Stripe keys via environment variables
How to install convex-billing
npx skills add https://github.com/get-convex/agent-skills --skill convex-billing- Convex project set up and running
- Stripe account with API keys (test or live)
- npm or package manager to install @convex-dev/stripe@^0.1.4
How to use convex-billing
- 1.Install @convex-dev/stripe: npm install @convex-dev/stripe
- 2.Create convex/convex.config.ts and import the stripe component
- 3.Store STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET in Convex environment variables
- 4.Create convex/http.ts and register webhook routes using registerRoutes
- 5.Create convex/billing.ts with createSubscriptionCheckout action and isSubscribed query
- 6.Run npx convex dev --once to install the component and push functions
- 7.Add webhook endpoint in Stripe Dashboard pointing to https://<deployment>.convex.site/stripe/webhook and subscribe to checkout and subscription events
Use cases
- Monetize a SaaS app by requiring active Stripe subscriptions for premium features
- Process recurring billing with automatic webhook updates from Stripe
- Implement subscription tiers with different access levels gated server-side
- Manage customer checkout flows with success/cancel redirects
- Track subscription status (active, trialing, canceled) in your Convex backend
- SaaS developers building subscription-based products
- Backend engineers integrating Stripe into Convex deployments
- Product teams implementing feature gating behind paid tiers
convex-billing FAQ
No. The @convex-dev/stripe component handles signature verification automatically via registerRoutes — do not write manual constructEvent code.
Store STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET in Convex environment variables using the env micro power, not in code.
Call the isSubscribed query on the server, which reads subscription state from the component's tables and checks for 'active' or 'trialing' status.
Use '@convex-dev/stripe/convex.config.js' with the .js extension — the Convex bundler requires it, not .ts.
Subscribe to checkout.session.completed, customer.subscription.*, invoice.*, and payment_intent.* events in the Stripe Dashboard.
Full instructions (SKILL.md)
Source of truth, from get-convex/agent-skills.
name: convex-billing description: "Add Stripe billing/payments to the Convex app via @convex-dev/stripe (checkout + webhook + gating)."
<!-- GENERATED from convex-agents content/capabilities/billing.json — do not edit by hand. -->Add billing / payments
Wire Stripe to Convex using @convex-dev/stripe: a checkout action, an httpAction webhook registered by the component (signature-verified automatically), subscription state stored in the component's tables, and server-side gating via a query.
Workflow
- Install the component:
npm install @convex-dev/stripe. - Create
convex/convex.config.ts:import { defineApp } from 'convex/server'; import stripe from '@convex-dev/stripe/convex.config.js'; const app = defineApp(); app.use(stripe); export default app; - Store Stripe keys in Convex env (use the
envmicro power):STRIPE_SECRET_KEY(sk_test_… / sk_live_…) andSTRIPE_WEBHOOK_SECRET(whsec_…). - Create
convex/http.tsto register the webhook route (the component handles signature verification automatically):import { httpRouter } from 'convex/server'; import { components } from './_generated/api'; import { registerRoutes } from '@convex-dev/stripe'; const http = httpRouter(); registerRoutes(http, components.stripe, { webhookPath: '/stripe/webhook' }); export default http; - Create
convex/billing.tswith a checkout action and a subscription-gate query:import { action, query } from './_generated/server'; import { components } from './_generated/api'; import { StripeSubscriptions } from '@convex-dev/stripe'; import { v } from 'convex/values'; const stripeClient = new StripeSubscriptions(components.stripe, {}); export const createSubscriptionCheckout = action({ args: { priceId: v.string() }, returns: v.object({ sessionId: v.string(), url: v.union(v.string(), v.null()) }), handler: async (ctx, args) => { const identity = await ctx.auth.getUserIdentity(); if (!identity) throw new Error('Not authenticated'); const customer = await stripeClient.getOrCreateCustomer(ctx, { userId: identity.subject, email: identity.email, name: identity.name }); return await stripeClient.createCheckoutSession(ctx, { priceId: args.priceId, customerId: customer.customerId, mode: 'subscription', successUrl: `${process.env.SITE_URL ?? 'http://localhost:3000'}/?success=true`, cancelUrl: `${process.env.SITE_URL ?? 'http://localhost:3000'}/?canceled=true`, subscriptionMetadata: { userId: identity.subject } }); }, }); export const isSubscribed = query({ args: {}, returns: v.boolean(), handler: async (ctx) => { const identity = await ctx.auth.getUserIdentity(); if (!identity) return false; const subscriptions = await ctx.runQuery(components.stripe.public.listSubscriptionsByUserId, { userId: identity.subject }); return subscriptions.some((sub) => sub.status === 'active' || sub.status === 'trialing'); }, }); - Run
npx convex dev --once— it will install the component and push the functions. Verify output shows✔ Installed component stripe. - In Stripe Dashboard → Webhooks: add endpoint
https://<deployment>.convex.site/stripe/webhook, subscribe tocheckout.session.completed,customer.subscription.*,invoice.*,payment_intent.*. Copy the signing secret asSTRIPE_WEBHOOK_SECRET.
Rules
- Use @convex-dev/stripe (npm: @convex-dev/stripe@^0.1.4) — it handles webhook signature verification internally via registerRoutes; do NOT write a manual constructEvent webhook.
- Stripe keys live in Convex env (use the
envmicro power): STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET. - Gate on server-stored subscription state via isSubscribed query (reads component tables), not client claims.
- convex/convex.config.ts must import from '@convex-dev/stripe/convex.config.js' (not .ts) — the .js extension is required by the Convex bundler.
Related skills
More from get-convex/agent-skills and the wider catalog.

convex-cost
Preview Convex spend by ranking functions on bytes-read × call-volume, project cost curves, and name the cheapest fix.

convex-create-component
Build reusable Convex components with isolated tables and app-facing APIs.

convex-crons
Add recurring scheduled jobs to Convex apps with idempotent handlers.

convex-deploy-guard
Classify and announce Convex deployment targets before acting; gate production changes with fresh per-action consent.

convex-design
Design and build reactive, type-safe production backends on Convex with schema, auth, real-time, and LLM workflows.

convex-docs
Fetch version-current Convex docs instead of writing stale APIs from memory.