A complete example app demonstrating the @convex-dev/stripe component with Clerk
authentication.
- ✅ One-time payments (Buy a Hat)
- ✅ Subscriptions (Hat of the Month Club)
- ✅ User profile with order history
- ✅ Subscription management (cancel, update seats)
- ✅ Customer portal integration
- ✅ Team/organization billing
- ✅ Failed payment handling
- ✅ Real-time data sync via webhooks
git clone https://github.com/get-convex/convex-stripe
cd convex-stripe
npm install
- Create a new application at clerk.com
- Copy your Publishable Key from the Clerk dashboard
- Note your Clerk domain (e.g.,
your-app-name.clerk.accounts.dev)
-
Copy your Secret Key (
sk_test_...) -
Create two products in Stripe Dashboard → Products:
Product 1: Single Hat
- Name: "Premium Hat"
- One-time payment: $49.00
- Copy the Price ID (
price_...)
Product 2: Hat Subscription
- Name: "Hat of the Month Club"
- Recurring: $29.00/month
- Copy the Price ID (
price_...)
-
Create
.env.localin the project root with all frontend variables:
# Clerk
VITE_CLERK_PUBLISHABLE_KEY=pk_test_...
# Stripe Price IDs (from Stripe Dashboard → Products)
VITE_STRIPE_ONE_TIME_PRICE_ID=price_...
VITE_STRIPE_SUBSCRIPTION_PRICE_ID=price_...
- Start the development server (this will prompt you to set up Convex):
npm run dev
- Add environment variables in Convex Dashboard → Settings → Environment Variables:
| Variable | Value |
|---|---|
STRIPE_SECRET_KEY |
sk_test_... (from Stripe) |
STRIPE_WEBHOOK_SECRET |
whsec_... (from Step 5) |
APP_URL |
http://localhost:5173 (or your production URL) |
- Create
example/convex/auth.config.ts:
export default {
providers: [
{
domain: "https://your-app-name.clerk.accounts.dev",
applicationID: "convex",
},
],
};
- Push the auth config:
npx convex dev --once
-
Click "Add endpoint"
-
Enter your Convex webhook URL:
https://YOUR_CONVEX_DEPLOYMENT.convex.site/stripe/webhookFind your deployment name in the Convex dashboard. It's the part before
.convex.cloudin your URL:VITE_CONVEX_URL=https://YOUR_CONVEX_DEPLOYMENT.convex.cloud # Webhook URL uses .convex.site instead: YOUR_CONVEX_DEPLOYMENT.convex.site/stripe/webhook -
Select these events:
customer.createdcustomer.updatedcustomer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deletedpayment_intent.succeededpayment_intent.payment_failedinvoice.createdinvoice.finalizedinvoice.paidinvoice.payment_failedcheckout.session.completed
-
Click "Add endpoint"
-
Click on your endpoint and copy the Signing secret (
whsec_...) -
Add it to Convex environment variables as
STRIPE_WEBHOOK_SECRET
npm run dev
Use these Stripe test cards:
| Scenario | Card Number |
|---|---|
| Successful payment | 4242 4242 4242 4242 |
| Declined | 4000 0000 0000 0002 |
| Requires authentication | 4000 0025 0000 3155 |
| Insufficient funds | 4000 0000 0000 9995 |
Use any future expiration date and any 3-digit CVC.
example/
├── src/
│ ├── App.tsx # Main React app with all pages
│ ├── main.tsx # Entry point with Clerk/Convex providers
│ └── index.css # Styling
└── convex/
├── auth.config.ts # Clerk authentication config
├── convex.config.ts # Component installation
├── http.ts # Webhook route registration
├── schema.ts # App schema (extends component)
└── stripe.ts # Stripe actions and queries
Contains all the Stripe integration logic:
createSubscriptionCheckout- Create subscription checkoutcreatePaymentCheckout- Create one-time payment checkoutcreateTeamSubscriptionCheckout- Create team/org subscription checkoutcancelSubscription- Cancel a subscriptionreactivateSubscription- Reactivate a canceled subscriptionupdateSeats- Update subscription quantitygetCustomerPortalUrl- Get customer portal URLgetUserSubscriptions- List user's subscriptionsgetUserPayments- List user's paymentsgetOrgSubscription- Get org's subscriptiongetOrgInvoices- List org's invoices
Registers the Stripe webhook handler with optional custom event handlers.
React app with four pages:
- Home - Landing page with product showcase
- Store - Product cards with purchase buttons (single-user subscriptions)
- Profile - Order history and subscription management
- Team - Team/organization billing with seat-based subscriptions
- Make sure Clerk is configured in
.env.local - Create
convex/auth.config.tswith your Clerk domain - Run
npx convex dev --onceto push the config
- Check the webhook URL matches your Convex deployment
- Verify all required events are selected
- Check
STRIPE_WEBHOOK_SECRETis set correctly - Look at Stripe webhook logs for delivery status
- Ensure
invoice.createdandinvoice.finalizedevents are enabled - Check Convex logs for webhook processing errors
- Verify
STRIPE_SECRET_KEYis set
# Rebuild the component
npm run build
# Re-sync Convex
npx convex dev --once