From Convex
Add Stripe billing/payments/subscriptions to a Convex app via @convex-dev/stripe — checkout action, auto-verified webhook, server-side subscription gating. TRIGGER when the user wants payments/billing/subscriptions/a paywall/Stripe.
How this skill is triggered — by the user, by Claude, or both
Slash command
/convex:billingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
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.
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.
npm install @convex-dev/stripe.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;
env micro power): STRIPE_SECRET_KEY (sk_test_… / sk_live_…) and STRIPE_WEBHOOK_SECRET (whsec_…).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;
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');
},
});
npx convex dev --once — it will install the component and push the functions. Verify output shows ✔ Installed component stripe.https://<deployment>.convex.site/stripe/webhook, subscribe to checkout.session.completed, customer.subscription.*, invoice.*, payment_intent.*. Copy the signing secret as STRIPE_WEBHOOK_SECRET.env micro power): STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET.claude plugin install convex@claude-plugins-officialGuides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Guides creation and editing of skills using test-driven development with pressure scenarios and subagents to verify agent compliance.
Dispatches multiple subagents concurrently for independent tasks without shared state. Use when facing 2+ unrelated failures or subsystems that can be investigated in parallel.