Skip to content

Find a component

Loading the directory…

↑ ↓ to select · Enter to open · Esc to closeBrowse all →
Contributor login

Stripe

Checkout, subscriptions and billing

Bijection source
git clone https://github.com/bijectionhq/stripe.git
cd stripe
git checkout 22e23e2bdfcc88b48a1604a80bf60d8f15494360

Bijection repository access is required. Request access.

Description

Checkout, subscriptions and billing

Explore the package README for API concepts, configuration, and examples. Check the Bijection source for adaptation-specific setup.

Included in Bijection’s source workspace

This package has a pinned source revision in Bijection’s workspace. Source availability does not establish npm publication or runtime qualification.

Getting started

  1. Get the source at the revision above and read its package documentation.
  2. Check the required Bijection SDK version, dependencies, and external service credentials.
  3. Follow the package setup instructions and the guide to using components.
  4. Exercise the component in your deployment according to your project’s verification rules.

Open source

Maintained source: bijectionhq/stripe. Based on convex/stripe. License: Apache-2.0. Consult the repository for license terms, notices, and contributor attribution.

From the upstream publisher

A stripe component for Convex.

Explore the demo ↗

Demo video

Upstream overview

From the upstream package documentation.

A Convex component that handles the complete Stripe integration lifecycle including checkout sessions, subscription management, customer billing portals, and webhook synchronization. It automatically creates and manages customers, processes payments and subscriptions, and keeps Stripe data synchronized with your Convex database through webhook handlers. The component provides both a StripeSubscriptions client API and direct database queries for real-time payment data.

Use cases

From the upstream package documentation.

  • SaaS subscription billing - Implement recurring billing with seat-based pricing, subscription upgrades/downgrades, and automatic customer portal access for plan management
  • One-time payment processing - Handle product purchases, service fees, or donations with Stripe Checkout integration and payment status tracking
  • Team billing management - Link subscriptions to organizations with quantity-based pricing, allowing teams to add or remove seats dynamically
  • Customer self-service billing - Generate Customer Portal sessions so users can update payment methods, download invoices, and manage their billing without support
  • Multi-tenant payment tracking - Query payments, subscriptions, and invoices in real-time across users and organizations with automatic webhook synchronization

How it works

From the upstream package documentation.

The component uses a StripeSubscriptions client that wraps Stripe API calls within Convex actions, handling customer creation, checkout sessions, and subscription management. You configure it by adding the component to your convex.config.ts and setting up webhook routes in http.ts that point to the component's webhook handler at /stripe/webhook.

Webhook events automatically synchronize Stripe data to dedicated Convex tables (customers, subscriptions, payments, invoices) in the component's namespace. The component handles 10+ webhook events including checkout.session.completed, customer.subscription.updated, and payment_intent.succeeded, maintaining real-time data consistency.

You access payment data through the component's public queries like listSubscriptionsByUserId and getPayment, or use the client methods like createCheckoutSession and updateSubscriptionQuantity in your actions. The component supports linking payments to both individual users and organizations through metadata, enabling flexible billing models.

Upstream package FAQ

Does this component handle Stripe webhook verification?

Yes, the @convex-dev/stripe component automatically verifies webhook signatures using your Stripe webhook secret. It validates all incoming webhooks and rejects unauthorized requests, ensuring secure payment processing in your Convex application.

Can I customize which Stripe events are processed?

The @convex-dev/stripe component allows you to configure which Stripe events trigger database updates. You can enable or disable specific event handlers and add custom logic for processing payments, subscriptions, and customer data according to your application needs.

How does this integrate with Convex's real-time features?

When Stripe webhooks update your database through @convex-dev/stripe, all connected clients receive real-time updates via Convex's reactive queries. This means payment confirmations, subscription changes, and billing updates instantly reflect across your application without manual polling.

What Stripe data gets stored in my Convex database?

The @convex-dev/stripe component stores essential payment and subscription data in your Convex database, including customer IDs, payment statuses, subscription tiers, and billing cycles. You control which fields are saved and can extend the schema to include additional Stripe metadata for your application.

Use with agents and CLI

Give your coding agent the component’s source identity and setup guidance.

agent-prompt.md
Help me integrate Stripe with Bijection.

Read: https://bijection.com/components/stripe/SKILL.md

Check SDK compatibility and dependency availability.
Follow this project's execution and verification rules.

Upstream README.md

View repository ↗

Documentation from convex/stripe, recorded October 23, 2026. APIs and setup may differ in the Bijection adaptation.

@convex-dev/stripe

A Convex component for integrating Stripe payments, subscriptions, and billing into your Convex application.

Requires Node.js 18 or later, matching the Stripe Node SDK v22 runtime requirement.

Features

  • 🛒 Checkout Sessions - Create one-time payment and subscription checkouts
  • 📦 Subscription Management - Create, update, cancel subscriptions
  • 👥 Customer Management - Automatic customer creation and linking
  • 💳 Customer Portal - Let users manage their billing
  • 🪑 Seat-Based Pricing - Update subscription quantities for team billing
  • 🔗 User/Org Linking - Link payments and subscriptions to users or organizations
  • 🔔 Webhook Handling - Automatic sync of Stripe data to your Convex database
  • 📊 Real-time Data - Query payments, subscriptions, invoices in real-time

Quick Start

1. Install the Component

npm install @convex-dev/stripe

2. Add to Your Convex App

Create or update 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. Set Up Environment Variables

Add these to your Convex Dashboard → Settings → Environment Variables:

VariableDescription
STRIPE_SECRET_KEYYour Stripe secret key (sk_test_... or sk_live_...)
STRIPE_WEBHOOK_SECRETWebhook signing secret (whsec_...) - see Step 4

4. Configure Stripe Webhooks

  1. Go to Stripe Dashboard → Developers → Webhooks
  2. Click "Add endpoint"
  3. Enter your webhook URL:
    https://<your-convex-deployment>.convex.site/stripe/webhook
    
    (Find your deployment name in the Convex dashboard - it's the part before .convex.cloud in your URL)
  4. Select these events:
    • checkout.session.completed
    • customer.created
    • customer.updated
    • customer.deleted
    • customer.subscription.created
    • customer.subscription.updated
    • customer.subscription.deleted
    • invoice.created
    • invoice.finalized
    • invoice.updated
    • invoice.paid
    • invoice.payment_failed
    • payment_intent.succeeded
    • payment_intent.payment_failed
  5. Click "Add endpoint"
  6. Copy the Signing secret and add it as STRIPE_WEBHOOK_SECRET in Convex

5. Register Webhook Routes

Create convex/http.ts:

import { httpRouter } from "convex/server";
import { components } from "./_generated/api";
import { registerRoutes } from "@convex-dev/stripe";

const http = httpRouter();

// Register Stripe webhook handler at /stripe/webhook
registerRoutes(http, components.stripe, {
  webhookPath: "/stripe/webhook",
  apiVersion: "2026-04-22.dahlia", // Optional
});

export default http;

6. Use the Component

Create convex/stripe.ts:

import { action } 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, {});

// Create a checkout session for a subscription
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");

    // Get or create a Stripe customer
    const customer = await stripeClient.getOrCreateCustomer(ctx, {
      userId: identity.subject,
      email: identity.email,
      name: identity.name,
    });

    // Create checkout session
    return await stripeClient.createCheckoutSession(ctx, {
      priceId: args.priceId,
      customerId: customer.customerId,
      mode: "subscription",
      successUrl: "http://localhost:5173/?success=true",
      cancelUrl: "http://localhost:5173/?canceled=true",
      subscriptionMetadata: { userId: identity.subject },
    });
  },
});

// Create a checkout session for a one-time payment
export const createPaymentCheckout = 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: "payment",
      successUrl: "http://localhost:5173/?success=true",
      cancelUrl: "http://localhost:5173/?canceled=true",
      paymentIntentMetadata: { userId: identity.subject },
    });
  },
});

API Reference

StripeSubscriptions Client

import { StripeSubscriptions } from "@convex-dev/stripe";

const stripeClient = new StripeSubscriptions(components.stripe, {
  STRIPE_SECRET_KEY: "sk_...", // Optional, defaults to process.env.STRIPE_SECRET_KEY
  apiVersion: "2026-04-22.dahlia", // Optional Stripe API version
});

Methods

MethodDescription
createCheckoutSession()Create a Stripe Checkout session
createCustomerPortalSession()Generate a Customer Portal URL
createCustomer()Create a new Stripe customer
getOrCreateCustomer()Get existing or create new customer
cancelSubscription()Cancel a subscription
reactivateSubscription()Reactivate a subscription set to cancel
updateSubscriptionQuantity()Update seat count

createCheckoutSession

await stripeClient.createCheckoutSession(ctx, {
  priceId: "price_...",
  customerId: "cus_...",           // Optional
  mode: "subscription",             // "subscription" | "payment" | "setup"
  successUrl: "https://...",
  cancelUrl: "https://...",
  quantity: 1,                      // Optional, default 1
  metadata: {},                     // Optional, session metadata
  subscriptionMetadata: {},         // Optional, attached to subscription
  paymentIntentMetadata: {},        // Optional, attached to payment intent
  params: {
    allow_promotion_codes: true,
    ui_mode: "embedded_page",
    return_url: "https://...",
  },                                // Optional Stripe Checkout Session params
});

params is a typed passthrough for Stripe Checkout Session fields that the component does not model directly. Fields in params override constructed defaults, except mode, which remains controlled by the top-level mode argument. For non-hosted Checkout UI modes, the component omits successUrl and cancelUrl from the Stripe request and expects redirect behavior to be supplied through Stripe-supported params such as return_url.

Component Queries

Access data directly via the component's public queries:

import { query } from "./_generated/server";
import { components } from "./_generated/api";

// List subscriptions for a user
export const getUserSubscriptions = query({
  args: {},
  handler: async (ctx) => {
    const identity = await ctx.auth.getUserIdentity();
    if (!identity) return [];

    return await ctx.runQuery(
      components.stripe.public.listSubscriptionsByUserId,
      { userId: identity.subject },
    );
  },
});

// List payments for a user
export const getUserPayments = query({
  args: {},
  handler: async (ctx) => {
    const identity = await ctx.auth.getUserIdentity();
    if (!identity) return [];

    return await ctx.runQuery(components.stripe.public.listPaymentsByUserId, {
      userId: identity.subject,
    });
  },
});

Available Public Queries

QueryArgumentsDescription
getCustomerstripeCustomerIdGet a customer by Stripe ID
listSubscriptionsstripeCustomerIdList subscriptions for a customer
listSubscriptionsByUserIduserIdList subscriptions for a user
getSubscriptionstripeSubscriptionIdGet a subscription by ID
getSubscriptionByOrgIdorgIdGet subscription for an org
getPaymentstripePaymentIntentIdGet a payment by ID
listPaymentsstripeCustomerIdList payments for a customer
listPaymentsByUserIduserIdList payments for a user
listPaymentsByOrgIdorgIdList payments for an org
listInvoicesstripeCustomerIdList invoices for a customer
listInvoicesByUserIduserIdList invoices for a user
listInvoicesByOrgIdorgIdList invoices for an org

Webhook Events

The component automatically handles these Stripe webhook events:

EventAction
customer.createdCreates customer record
customer.updatedUpdates customer record
customer.deletedScrubs customer PII
customer.subscription.createdCreates subscription record
customer.subscription.updatedUpdates subscription record
customer.subscription.deletedMarks subscription as canceled
payment_intent.succeededCreates payment record
payment_intent.payment_failedUpdates payment status
invoice.createdCreates invoice record
invoice.finalizedUpserts invoice record
invoice.updatedMirrors invoice metadata changes
invoice.paidUpserts invoice as paid
invoice.payment_failedMarks invoice as failed
checkout.session.completedHandles completed checkout sessions

Custom Webhook Handlers

Add custom logic to webhook events:

import { httpRouter } from "convex/server";
import { components } from "./_generated/api";
import { registerRoutes } from "@convex-dev/stripe";
import type Stripe from "stripe";

const http = httpRouter();

registerRoutes(http, components.stripe, {
  events: {
    "customer.subscription.updated": async (ctx, event: Stripe.CustomerSubscriptionUpdatedEvent) => {
      const subscription = event.data.object;
      console.log("Subscription updated:", subscription.id, subscription.status);
      // Add custom logic here
    },
  },
  onEvent: async (ctx, event: Stripe.Event) => {
    // Called for ALL events - useful for logging/analytics
    console.log("Stripe event:", event.type);
  },
});

export default http;

Database Schema

The component creates these tables in its namespace:

customers

FieldTypeDescription
stripeCustomerIdstringStripe customer ID
emailstring?Customer email
namestring?Customer name
metadataobject?Custom metadata

subscriptions

FieldTypeDescription
stripeSubscriptionIdstringStripe subscription ID
stripeCustomerIdstringCustomer ID
statusstringSubscription status
priceIdstringPrice ID
quantitynumber?Seat count
currentPeriodEndnumberPeriod end timestamp
cancelAtPeriodEndbooleanWill cancel at period end
userIdstring?Linked user ID
orgIdstring?Linked org ID
metadataobject?Custom metadata

checkout_sessions

FieldTypeDescription
stripeCheckoutSessionIdstringCheckout session ID
stripeCustomerIdstring?Customer ID
statusstringSession status
modestringSession mode (payment/subscription/setup)
metadataobject?Custom metadata

payments

FieldTypeDescription
stripePaymentIntentIdstringPayment intent ID
stripeCustomerIdstring?Customer ID
amountnumberAmount in cents
currencystringCurrency code
statusstringPayment status
creatednumberCreated timestamp
userIdstring?Linked user ID
orgIdstring?Linked org ID
metadataobject?Custom metadata

invoices

FieldTypeDescription
stripeInvoiceIdstringInvoice ID
stripeCustomerIdstringCustomer ID
stripeSubscriptionIdstring?Subscription ID
statusstringInvoice status
amountDuenumberAmount due
amountPaidnumberAmount paid
creatednumberCreated timestamp
userIdstring?Linked user ID
orgIdstring?Linked org ID
metadataobject?Invoice metadata

Example App

Check out the full example app in the example/ directory:

git clone https://github.com/get-convex/convex-stripe
cd convex-stripe
npm install
npm run dev

The example includes:

  • Landing page with product showcase
  • One-time payments and subscriptions
  • User profile with order history
  • Subscription management (cancel, update seats)
  • Customer portal integration
  • Team/organization billing

Authentication

This component works with any Convex authentication provider. The example uses Clerk:

  1. Create a Clerk application at clerk.com
  2. Add VITE_CLERK_PUBLISHABLE_KEY to your .env.local
  3. Create convex/auth.config.ts:
export default {
  providers: [
    {
      domain: "https://your-clerk-domain.clerk.accounts.dev",
      applicationID: "convex",
    },
  ],
};

Troubleshooting

Tables are empty after checkout

Make sure you've:

  1. Set STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET in Convex environment variables
  2. Configured the webhook endpoint in Stripe with the correct events
  3. Added invoice.created and invoice.finalized events (not just invoice.paid)

"Not authenticated" errors

Ensure your auth provider is configured:

  1. Create convex/auth.config.ts with your provider
  2. Run npx convex dev to push the config
  3. Verify the user is signed in before calling actions

Webhooks returning 400/500

Check the Convex logs in your dashboard for errors. Common issues:

  • Missing STRIPE_WEBHOOK_SECRET
  • Wrong webhook URL (should be https://<deployment>.convex.site/stripe/webhook)
  • Missing events in webhook configuration

License

Apache-2.0

Keywords