PluginBench
Skill
Review
Audit score 70

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
Prerequisites
  • 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
Claude Code
Cursor
Windsurf
Cline

How to use convex-billing

  1. 1.Install @convex-dev/stripe: npm install @convex-dev/stripe
  2. 2.Create convex/convex.config.ts and import the stripe component
  3. 3.Store STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET in Convex environment variables
  4. 4.Create convex/http.ts and register webhook routes using registerRoutes
  5. 5.Create convex/billing.ts with createSubscriptionCheckout action and isSubscribed query
  6. 6.Run npx convex dev --once to install the component and push functions
  7. 7.Add webhook endpoint in Stripe Dashboard pointing to https://<deployment>.convex.site/stripe/webhook and subscribe to checkout and subscription events

Use cases

Good for
  • 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
Who it's for
  • SaaS developers building subscription-based products
  • Backend engineers integrating Stripe into Convex deployments
  • Product teams implementing feature gating behind paid tiers

convex-billing FAQ

Do I need to manually verify Stripe webhook signatures?

No. The @convex-dev/stripe component handles signature verification automatically via registerRoutes — do not write manual constructEvent code.

Where should I store Stripe API keys?

Store STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET in Convex environment variables using the env micro power, not in code.

How do I check if a user has an active subscription?

Call the isSubscribed query on the server, which reads subscription state from the component's tables and checks for 'active' or 'trialing' status.

What file extension is required for convex.config.ts imports?

Use '@convex-dev/stripe/convex.config.js' with the .js extension — the Convex bundler requires it, not .ts.

What Stripe webhook events should I subscribe to?

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

  1. Install the component: npm install @convex-dev/stripe.
  2. 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;
    
  3. Store Stripe keys in Convex env (use the env micro power): STRIPE_SECRET_KEY (sk_test_… / sk_live_…) and STRIPE_WEBHOOK_SECRET (whsec_…).
  4. Create convex/http.ts to 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;
    
  5. Create convex/billing.ts with 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');
      },
    });
    
  6. Run npx convex dev --once — it will install the component and push the functions. Verify output shows ✔ Installed component stripe.
  7. In Stripe Dashboard → Webhooks: add endpoint https://<deployment>.convex.site/stripe/webhook, subscribe to checkout.session.completed, customer.subscription.*, invoice.*, payment_intent.*. Copy the signing secret as STRIPE_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 env micro 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.