### Install Dependencies and Run Locally Source: https://github.com/get-convex/stripe/blob/main/CONTRIBUTING.md Install project dependencies using npm and start the development server. ```sh npm i npm run dev ``` -------------------------------- ### Start Convex Development Server Source: https://github.com/get-convex/stripe/blob/main/example/README.md Initiate the Convex development server, which will guide through the initial setup. ```bash npm run dev ``` -------------------------------- ### Clone and Run Example App Source: https://github.com/get-convex/stripe/blob/main/README.md Clone the Convex Stripe example repository, install dependencies, and run the development server. ```bash git clone https://github.com/get-convex/convex-stripe cd convex-stripe npm install npm run dev ``` -------------------------------- ### Example: List Invoices for a User Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/public-queries.md Provides an example of executing the `listInvoicesByUserId` query to get invoices associated with a particular user ID. ```typescript const invoices = await ctx.runQuery(components.stripe.public.listInvoicesByUserId, { userId: "user_123", }); ``` -------------------------------- ### Example: Get Customer by Email Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/public-queries.md Shows how to execute the getCustomerByEmail query with a customer's email address. ```typescript const customer = await ctx.runQuery(components.stripe.public.getCustomerByEmail, { email: "user@example.com", }); ``` -------------------------------- ### Install Dependencies with npm Source: https://github.com/get-convex/stripe/blob/main/example/README.md Clone the repository and install project dependencies using npm. ```bash git clone https://github.com/get-convex/convex-stripe cd convex-stripe npm install ``` -------------------------------- ### Example: Get Customer by Stripe ID Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/public-queries.md Demonstrates how to call the getCustomer query with a Stripe customer ID and handle the result. Ensure you have the necessary imports. ```typescript import { components } from "./_generated/api"; const customer = await ctx.runQuery(components.stripe.public.getCustomer, { stripeCustomerId: "cus_abc123", }); if (customer) { console.log(customer.email); } ``` -------------------------------- ### Run Project Tests Source: https://github.com/get-convex/stripe/blob/main/CONTRIBUTING.md Install dependencies, clean previous builds, and run type checking, linting, and tests. ```sh npm ci npm run clean npm run typecheck npm run lint npm run test ``` -------------------------------- ### Stripe Integration Configuration Example Source: https://github.com/get-convex/stripe/blob/main/_autodocs/configuration.md Example demonstrating how to use `registerRoutes` to set up a Stripe webhook endpoint. It shows how to specify the webhook path, provide Stripe API keys, and define specific event handlers or a generic handler for all events. ```typescript import { httpRouter } from "convex/server"; import { components } from "./_generated/api"; import { registerRoutes } from "@convex-dev/stripe"; const http = httpRouter(); registerRoutes(http, components.stripe, { webhookPath: "/api/stripe-webhook", STRIPE_WEBHOOK_SECRET: process.env.STRIPE_WEBHOOK_SECRET, STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY, apiVersion: "2024-12-18.acacia", events: { "customer.subscription.updated": async (ctx, event) => { // Handle subscription updates }, }, onEvent: async (ctx, event) => { console.log(`Event: ${event.type}`); }, }); export default http; ``` -------------------------------- ### Example: Get Customer by User ID Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/public-queries.md Illustrates calling the getCustomerByUserId query with a specific application user ID. ```typescript const customer = await ctx.runQuery(components.stripe.public.getCustomerByUserId, { userId: "user_123", }); ``` -------------------------------- ### Install Stripe Component Source: https://github.com/get-convex/stripe/blob/main/README.md Install the Stripe component using npm. This is the first step to integrate Stripe functionality into your Convex application. ```bash npm install @convex-dev/stripe ``` -------------------------------- ### Example: List Invoices for an Organization Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/public-queries.md Shows how to use the `listInvoicesByOrgId` query to fetch invoices for a given organization ID. ```typescript const invoices = await ctx.runQuery(components.stripe.public.listInvoicesByOrgId, { orgId: "org_456", }); ``` -------------------------------- ### Install Stripe Component in Convex Config Source: https://github.com/get-convex/stripe/blob/main/example/convex/README.md Installs the `@convex-dev/stripe` component by importing and using it in your `convex.config.ts` file. ```typescript // 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; ``` -------------------------------- ### Component Configuration Setup Source: https://github.com/get-convex/stripe/blob/main/_autodocs/README.md Configures the Convex app to use the Stripe component. Ensure 'defineApp' is available in your scope. ```typescript import stripe from "@convex-dev/stripe/convex.config.js"; const app = defineApp(); app.use(stripe); ``` -------------------------------- ### RegisterRoutesConfig Example Usage Source: https://github.com/get-convex/stripe/blob/main/_autodocs/types.md An example demonstrating how to instantiate the `RegisterRoutesConfig` object with specific webhook path, event handlers, and API keys. This configuration is used when calling the `registerRoutes()` function. ```typescript const config: RegisterRoutesConfig = { webhookPath: "/api/webhooks/stripe", STRIPE_WEBHOOK_SECRET: process.env.STRIPE_WEBHOOK_SECRET, STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY, events: { "customer.created": async (ctx, event) => { // Handle }, }, onEvent: async (ctx, event) => { console.log("Event:", event.type); }, }; ``` -------------------------------- ### Example: List Invoices for a Customer Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/public-queries.md Demonstrates how to execute the `listInvoices` query to retrieve and process invoices for a given Stripe customer ID. Logs the invoice ID and status. ```typescript const invoices = await ctx.runQuery(components.stripe.public.listInvoices, { stripeCustomerId: "cus_abc123", }); invoices.forEach(inv => { console.log(`Invoice ${inv.stripeInvoiceId}: ${inv.status}`); }); ``` -------------------------------- ### Custom Webhook Handler Example Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/process-event.md Demonstrates how to implement a custom webhook handler by first calling the default `processEvent` and then adding custom logic for specific event types. ```typescript import { processEvent } from "@convex-dev/stripe"; import type Stripe from "stripe"; const customWebhookHandler = async (ctx, event: Stripe.Event) => { // First do default processing await processEvent(ctx, components.stripe, event, stripe); // Then add custom logic if (event.type === "customer.subscription.updated") { const sub = event.data.object as Stripe.Subscription; // Custom logic here } }; ``` -------------------------------- ### Stripe Webhook Request Body Example Source: https://github.com/get-convex/stripe/blob/main/_autodocs/endpoints.md This is an example of the JSON payload received by the Stripe webhook endpoint. It contains details about the Stripe event that occurred. ```json { "id": "evt_test_abc123", "object": "event", "type": "customer.subscription.updated", "created": 1704067200, "data": { "object": { "id": "sub_1234567890", "object": "subscription", "customer": "cus_abc123", "status": "active", "current_period_end": 1706745600, "cancel_at_period_end": false, "items": { "object": "list", "data": [ { "id": "si_abc123", "price": { "id": "price_123", "object": "price" }, "quantity": 1, "current_period_end": 1706745600 } ] }, "metadata": { "userId": "user_123", "orgId": "org_456" } } } } ``` -------------------------------- ### Implement a Specific Stripe Event Handler Source: https://github.com/get-convex/stripe/blob/main/_autodocs/types.md Example of implementing a `StripeEventHandler` for the `customer.updated` event. Ensure the handler function signature matches the `StripeEventHandler` type. ```typescript import type Stripe from "stripe"; const customerUpdatedHandler: StripeEventHandler<"customer.updated"> = async (ctx, event: Stripe.Event & { type: "customer.updated" }) => { const customer = event.data.object as Stripe.Customer; console.log("Customer updated:", customer.id); // Custom logic here }; ``` -------------------------------- ### Create a Map of Stripe Event Handlers Source: https://github.com/get-convex/stripe/blob/main/_autodocs/types.md Example of creating an object that conforms to the `StripeEventHandlers` type. Each key is a Stripe event type, and the value is the handler function for that event. ```typescript const handlers: StripeEventHandlers = { "customer.created": async (ctx, event) => { const customer = event.data.object as Stripe.Customer; // Handle creation }, "subscription.updated": async (ctx, event) => { const subscription = event.data.object as Stripe.Subscription; // Handle update }, }; ``` -------------------------------- ### Configure Stripe API Version Source: https://github.com/get-convex/stripe/blob/main/_autodocs/configuration.md Example of specifying a Stripe API version when initializing StripeSubscriptions. The default is the version bundled with Stripe Node SDK v22. ```typescript new StripeSubscriptions(components.stripe, { apiVersion: "2024-12-18.acacia", }); ``` -------------------------------- ### Configure STRIPE_WEBHOOK_SECRET for Webhook Registration Source: https://github.com/get-convex/stripe/blob/main/_autodocs/errors.md This example shows how to register Stripe webhook routes, ensuring the STRIPE_WEBHOOK_SECRET is correctly configured. It highlights passing the secret via environment variables during the registration process. ```typescript registerRoutes(http, components.stripe, { STRIPE_WEBHOOK_SECRET: process.env.STRIPE_WEBHOOK_SECRET, }); ``` -------------------------------- ### Handle Stripe Events with `processEvent` Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/process-event.md Use this function to process incoming Stripe events. Ensure you have the necessary Stripe client setup before calling this function. ```typescript import { Stripe } from "@stripe/stripe-js"; const stripe = new Stripe("pk_test_..."); async function handleEvent(event: Stripe.Event) { // Process the event using the stripe.processEvent method const result = await stripe.processEvent(event); if (result.error) { console.error("Error processing event:", result.error); } else { console.log("Event processed successfully:", result.data); } } // Example usage: // const exampleEvent: Stripe.Event = { ... }; // Replace with actual event data // handleEvent(exampleEvent); ``` -------------------------------- ### Basic Stripe Route Registration Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/register-routes.md Registers the default Stripe routes using the provided HTTP router and Convex API components. This is the most basic setup. ```typescript import { httpRouter } from "convex/server"; import { components } from "./_generated/api"; import { registerRoutes } from "@convex-dev/stripe"; const http = httpRouter(); registerRoutes(http, components.stripe); export default http; ``` -------------------------------- ### Register Custom Stripe Webhook Handler Source: https://github.com/get-convex/stripe/blob/main/_autodocs/README.md Sets up a handler for Stripe events, specifically `customer.subscription.updated`. This example shows where to place logic for updating user profiles or sending notifications upon subscription changes. ```typescript registerRoutes(http, components.stripe, { events: { "customer.subscription.updated": async (ctx, event) => { const sub = event.data.object; // Send email notification // Update user profile // Trigger analytics }, }, }); ``` -------------------------------- ### Manage Stripe Customers Source: https://github.com/get-convex/stripe/blob/main/example/convex/README.md Handles getting or creating a Stripe customer using user identity and creating a customer portal session for managing subscriptions. ```typescript // Get or create a customer const customer = await stripeClient.getOrCreateCustomer(ctx, { userId: identity.subject, email: identity.email, name: identity.name, }); // Create customer portal session const portal = await stripeClient.createCustomerPortalSession(ctx, { customerId: "cus_...", returnUrl: "https://example.com/profile", }); ``` -------------------------------- ### Create Subscription Checkout Session Source: https://github.com/get-convex/stripe/blob/main/README.md Creates a Stripe checkout session for a subscription. This action handles getting or creating a Stripe customer and then initiating the checkout process. ```APIDOC ## Create Subscription Checkout Session ### Description Creates a Stripe checkout session for a subscription. This action handles getting or creating a Stripe customer and then initiating the checkout process. ### Method `createSubscriptionCheckout(ctx, args)` ### Parameters #### Arguments - `args` (object) - `priceId` (string) - Required. The Stripe Price ID for the subscription. ### Returns - `sessionId` (string) - The ID of the created Stripe checkout session. - `url` (string | null) - The URL to redirect the user to for checkout, or null if an error occurred. ### Request Example ```typescript 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, {}); 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: "http://localhost:5173/?success=true", cancelUrl: "http://localhost:5173/?canceled=true", subscriptionMetadata: { userId: identity.subject }, }); }, }); ``` ``` -------------------------------- ### Get or Create Stripe Customer Source: https://github.com/get-convex/stripe/blob/main/_autodocs/README.md Use this pattern to retrieve an existing Stripe customer or create a new one if it doesn't exist. Requires the user's ID, email, and name. ```typescript const stripeClient = new StripeSubscriptions(components.stripe); const result = await stripeClient.getOrCreateCustomer(ctx, { userId: identity.subject, email: identity.email, name: identity.name, }); // Use result.customerId for checkout ``` -------------------------------- ### Customer Management Source: https://github.com/get-convex/stripe/blob/main/example/convex/README.md Manage Stripe customers. Get or create a customer using user identity and email, or create a customer portal session for managing subscriptions. ```APIDOC ## Customer Management ### Description Manage Stripe customers. Get or create a customer using user identity and email, or create a customer portal session for managing subscriptions. ### Methods #### `getOrCreateCustomer(ctx, { userId, email, name })` ##### Parameters - **userId** (string) - Required - The unique identifier for the user. - **email** (string) - Required - The user's email address. - **name** (string) - Optional - The user's name. ##### Request Example ```typescript const customer = await stripeClient.getOrCreateCustomer(ctx, { userId: identity.subject, email: identity.email, name: identity.name, }); ``` #### `createCustomerPortalSession(ctx, { customerId, returnUrl })` ##### Parameters - **customerId** (string) - Required - The Stripe Customer ID. - **returnUrl** (string) - Required - The URL to return to after the portal session. ##### Request Example ```typescript const portal = await stripeClient.createCustomerPortalSession(ctx, { customerId: "cus_...", returnUrl: "https://example.com/profile", }); ``` ``` -------------------------------- ### Configure Frontend Environment Variables Source: https://github.com/get-convex/stripe/blob/main/example/README.md Set up frontend environment variables for Clerk and Stripe Price IDs in a .env.local file. ```env # 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_... ``` -------------------------------- ### Push Convex Authentication Configuration Source: https://github.com/get-convex/stripe/blob/main/example/README.md Apply the authentication configuration to your Convex deployment. ```bash npx convex dev --once ``` -------------------------------- ### StripeSubscriptions Client Initialization Source: https://github.com/get-convex/stripe/blob/main/README.md Initializes the StripeSubscriptions client with your Stripe components and optional configuration. ```APIDOC ## StripeSubscriptions Client ```typescript 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 }); ``` ``` -------------------------------- ### Get Subscription by Organization ID Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/public-queries.md Retrieve a subscription linked to a specific organization ID. Returns null if no matching subscription is found. ```typescript export const getSubscriptionByOrgId = query({ args: { orgId: v.string() }, returns: v.union(subscriptionValidator, v.null()) }) ``` ```typescript const subscription = await ctx.runQuery(components.stripe.public.getSubscriptionByOrgId, { orgId: "org_456", }); ``` -------------------------------- ### Get Subscription by Stripe ID Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/public-queries.md Retrieve a single subscription using its unique Stripe subscription ID. Returns null if no subscription is found. ```typescript export const getSubscription = query({ args: { stripeSubscriptionId: v.string() }, returns: v.union(subscriptionValidator, v.null()) }) ``` ```typescript const subscription = await ctx.runQuery(components.stripe.public.getSubscription, { stripeSubscriptionId: "sub_1234567890", }); if (subscription) { console.log(`Status: ${subscription.status}`); console.log(`Expires: ${new Date(subscription.currentPeriodEnd * 1000)}`); } ``` -------------------------------- ### Initialize StripeSubscriptions Client Source: https://github.com/get-convex/stripe/blob/main/README.md Instantiate the StripeSubscriptions client with your Stripe components and optional configuration. The secret key defaults to the environment variable STRIPE_SECRET_KEY. ```typescript 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 }); ``` -------------------------------- ### Get Invoice Metadata Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/process-event.md Extracts metadata from a Stripe invoice. If the invoice metadata is empty or not present, it falls back to using subscription metadata. ```typescript function getInvoiceMetadata(invoice: StripeSDK.Invoice): Record ``` -------------------------------- ### StripeSubscriptions Constructor Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/stripe-subscriptions.md Initializes the StripeSubscriptions client. It requires a StripeComponent and optionally accepts Stripe secret key and API version. ```APIDOC ## Constructor ```typescript new StripeSubscriptions( component: StripeComponent, options?: { STRIPE_SECRET_KEY?: string; apiVersion?: StripeApiVersion; } ) ``` ### Parameters #### Path Parameters - **component** (StripeComponent) - Required - Convex component API reference for Stripe component - **options.STRIPE_SECRET_KEY** (string) - Optional - Stripe secret API key for authentication. Defaults to `process.env.STRIPE_SECRET_KEY`. - **options.apiVersion** (StripeApiVersion) - Optional - Optional Stripe API version to use (e.g., "2024-12-18.acacia") The constructor validates that a Stripe secret key is available either via options or environment variables, throwing an error at first use if missing. ``` -------------------------------- ### createCustomer Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/stripe-subscriptions.md Creates a new Stripe customer in the database and Stripe. It is recommended to use an idempotency key to prevent duplicate creations. ```APIDOC ## createCustomer ### Description Create a new Stripe customer in the database and Stripe. ### Method `async createCustomer(ctx: ActionCtx, args: { email?: string; name?: string; metadata?: Record; idempotencyKey?: string; }): Promise<{ customerId: string; }>` ### Parameters #### Arguments - **email** (string) - Optional - Customer email address - **name** (string) - Optional - Customer name - **metadata** (Record) - Optional - Custom metadata to attach to customer - **idempotencyKey** (string) - Optional - Idempotency key to prevent duplicate creation (recommended: pass userId) ### Response #### Success Response - **customerId** (string) - The Stripe customer ID. ### Request Example ```typescript const customer = await stripeClient.createCustomer(ctx, { email: "user@example.com", name: "John Doe", metadata: { userId: "user_123" }, idempotencyKey: "user_123", // Prevents race condition duplicates }); console.log(customer.customerId); // "cus_..." ``` ### Error Handling - Throws if `STRIPE_SECRET_KEY` is not available - Stripe API errors propagate directly ``` -------------------------------- ### Initialize StripeSubscriptions with Environment Variable Source: https://github.com/get-convex/stripe/blob/main/_autodocs/configuration.md Instantiate StripeSubscriptions using the default STRIPE_SECRET_KEY from environment variables. Ensure the environment variable is set in your Convex project settings. ```typescript import { StripeSubscriptions } from "@convex-dev/stripe"; import { components } from "./_generated/api"; // Use environment variable const stripeClient = new StripeSubscriptions(components.stripe, {}); ``` -------------------------------- ### Configure Convex Authentication Provider Source: https://github.com/get-convex/stripe/blob/main/example/README.md Set up the Clerk authentication provider for your Convex project. ```typescript export default { providers: [ { domain: "https://your-app-name.clerk.accounts.dev", applicationID: "convex", }, ], }; ``` -------------------------------- ### Get Stripe Object ID Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/process-event.md Normalizes Stripe object references into a string ID. This function accepts either a string ID or an object with an 'id' property. ```typescript function getStripeObjectId(object: string | { id: string }): string ``` -------------------------------- ### Project File Structure Source: https://github.com/get-convex/stripe/blob/main/_autodocs/INDEX.md Illustrates the organization of files within the Stripe integration project. This structure helps in navigating and understanding the different components of the integration. ```text output/ INDEX.md (this file) README.md (start here) types.md (type definitions and schemas) configuration.md (setup and env vars) endpoints.md (HTTP webhook endpoint) errors.md (error conditions) api-reference/ stripe-subscriptions.md register-routes.md process-event.md public-queries.md public-mutations.md private-mutations.md ``` -------------------------------- ### Main Entry Point Imports Source: https://github.com/get-convex/stripe/blob/main/_autodocs/README.md Imports necessary classes and functions from the main Stripe module for client-side integration. ```typescript import { StripeSubscriptions, registerRoutes, processEvent, } from "@convex-dev/stripe"; import type { StripeComponent, RegisterRoutesConfig, StripeEventHandlers, } from "@convex-dev/stripe"; ``` -------------------------------- ### List Subscriptions with Creation Time Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/private-mutations.md Internal query for webhook processing. Returns subscriptions with creation timestamps for webhook ordering. ```typescript export const listSubscriptionsWithCreationTime = query({ args: { stripeCustomerId: v.string() }, returns: v.array(v.object({ _creationTime: v.number(), stripeSubscriptionId: v.string(), stripeCustomerId: v.string(), status: v.string(), })) }) ``` -------------------------------- ### StripeSubscriptions Constructor Source: https://github.com/get-convex/stripe/blob/main/_autodocs/README.md Initializes the StripeSubscriptions client with component configuration and optional Stripe secret key and API version. ```typescript const client = new StripeSubscriptions(components.stripe, { STRIPE_SECRET_KEY?: string; apiVersion?: StripeApiVersion; }); ``` -------------------------------- ### Get Checkout Session by Stripe ID Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/public-queries.md Retrieve a specific checkout session using its Stripe checkout session ID. Returns the session object or null if not found. ```typescript export const getCheckoutSession = query({ args: { stripeCheckoutSessionId: v.string() }, returns: v.union(checkoutSessionValidator, v.null()) }) ``` ```typescript const session = await ctx.runQuery(components.stripe.public.getCheckoutSession, { stripeCheckoutSessionId: "cs_test_abc123", }); ``` -------------------------------- ### Configure Convex Authentication with Clerk Source: https://github.com/get-convex/stripe/blob/main/README.md Set up authentication for your Convex application using Clerk. Ensure your Clerk publishable key is added to your .env.local file and configure the auth.config.ts file with your Clerk domain and application ID. ```typescript export default { providers: [ { domain: "https://your-clerk-domain.clerk.accounts.dev", applicationID: "convex", }, ], }; ``` -------------------------------- ### Get Payment by Stripe Payment Intent ID Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/public-queries.md Retrieve a specific payment using its Stripe Payment Intent ID. Returns the payment object or null if not found. ```typescript export const getPayment = query({ args: { stripePaymentIntentId: v.string() }, returns: v.union(paymentValidator, v.null()) }) ``` ```typescript const payment = await ctx.runQuery(components.stripe.public.getPayment, { stripePaymentIntentId: "pi_1234567890", }); if (payment) { console.log(`Amount: ${payment.amount / 100} ${payment.currency.toUpperCase()}`); } ``` -------------------------------- ### Create a Stripe Checkout Session Source: https://github.com/get-convex/stripe/blob/main/README.md Use createCheckoutSession to initiate a Stripe Checkout flow. Configure price, customer, URLs, and optional metadata. The `params` object allows passthrough of additional Stripe Checkout Session fields. ```typescript 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 }); ``` -------------------------------- ### Create Stripe Checkout Session Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/stripe-subscriptions.md Creates a Stripe Checkout session for payments, subscriptions, or setup intents. Supports various options for customization, including metadata and return URLs. ```typescript async createCheckoutSession( ctx: ActionCtx, args: { priceId: string; customerId?: string; mode: "payment" | "subscription" | "setup"; successUrl: string; cancelUrl: string; quantity?: number; metadata?: Record; subscriptionMetadata?: Record; paymentIntentMetadata?: Record; params?: Partial; } ): Promise<{ sessionId: string; url: string | null; }> ``` ```typescript const stripeClient = new StripeSubscriptions(components.stripe, {}); // Create a subscription checkout const result = await stripeClient.createCheckoutSession(ctx, { priceId: "price_1234567890", customerId: "cus_abc123", mode: "subscription", successUrl: "https://yourapp.com/billing?success=true", cancelUrl: "https://yourapp.com/billing?canceled=true", subscriptionMetadata: { userId: "user_123", orgId: "org_456" }, }); // Create a one-time payment checkout const paymentCheckout = await stripeClient.createCheckoutSession(ctx, { priceId: "price_payment_123", customerId: "cus_xyz789", mode: "payment", successUrl: "https://yourapp.com/order?success=true", cancelUrl: "https://yourapp.com/order?canceled=true", quantity: 2, paymentIntentMetadata: { userId: "user_123" }, }); // Using non-hosted embedded UI const embedded = await stripeClient.createCheckoutSession(ctx, { priceId: "price_123", mode: "payment", successUrl: "https://yourapp.com", cancelUrl: "https://yourapp.com", params: { ui_mode: "embedded", return_url: "https://yourapp.com/return", }, }); ``` -------------------------------- ### Create Stripe Subscriptions Client Source: https://github.com/get-convex/stripe/blob/main/example/convex/README.md Initializes the `StripeSubscriptions` client in `stripe.ts` for interacting with Stripe functionalities. ```typescript // convex/stripe.ts import { components } from "./_generated/api"; import { StripeSubscriptions } from "@convex-dev/stripe"; const stripeClient = new StripeSubscriptions(components.stripe, {}); ``` -------------------------------- ### Create One-Time Payment Checkout Session Source: https://github.com/get-convex/stripe/blob/main/README.md Creates a Stripe checkout session for a one-time payment. This action handles getting or creating a Stripe customer and then initiating the checkout process. ```APIDOC ## Create One-Time Payment Checkout Session ### Description Creates a Stripe checkout session for a one-time payment. This action handles getting or creating a Stripe customer and then initiating the checkout process. ### Method `createPaymentCheckout(ctx, args)` ### Parameters #### Arguments - `args` (object) - `priceId` (string) - Required. The Stripe Price ID for the one-time product. ### Returns - `sessionId` (string) - The ID of the created Stripe checkout session. - `url` (string | null) - The URL to redirect the user to for checkout, or null if an error occurred. ### Request Example ```typescript 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, {}); 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 }, }); }, }); ``` ``` -------------------------------- ### Rebuild Component and Sync Convex Source: https://github.com/get-convex/stripe/blob/main/example/README.md Use these commands to rebuild the component and re-sync with Convex during development or after configuration changes. ```bash # Rebuild the component npm run build # Re-sync Convex npx convex dev --once ``` -------------------------------- ### Get Invoice Subscription ID Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/process-event.md Extracts the subscription ID from a Stripe invoice. It attempts to find the ID using multiple fallback approaches, prioritizing nested subscription details. ```typescript function getInvoiceSubscriptionId(invoice: StripeSDK.Invoice): string | undefined ``` -------------------------------- ### Checkout Sessions Source: https://github.com/get-convex/stripe/blob/main/example/convex/README.md Create checkout sessions for subscriptions or one-time payments. Requires price ID, customer ID, mode, and success/cancel URLs. Optionally include metadata for subscriptions or payment intents. ```APIDOC ## Checkout Sessions ### Description Create checkout sessions for subscriptions or one-time payments. Requires price ID, customer ID, mode, and success/cancel URLs. Optionally include metadata for subscriptions or payment intents. ### Method `stripeClient.createCheckoutSession(ctx, { ...options }) ### Parameters #### Request Body - **priceId** (string) - Required - The Stripe Price ID for the product. - **customerId** (string) - Required - The Stripe Customer ID. - **mode** (string) - Required - Either "subscription" or "payment". - **successUrl** (string) - Required - The URL to redirect to on success. - **cancelUrl** (string) - Required - The URL to redirect to on cancellation. - **subscriptionMetadata** (object) - Optional - Metadata to attach to the subscription. - **paymentIntentMetadata** (object) - Optional - Metadata to attach to the payment intent. ### Request Example ```typescript // Subscription checkout await stripeClient.createCheckoutSession(ctx, { priceId: "price_...", customerId: "cus_...", mode: "subscription", successUrl: "https://example.com/success", cancelUrl: "https://example.com/cancel", subscriptionMetadata: { userId: "user_123" }, }); // One-time payment checkout await stripeClient.createCheckoutSession(ctx, { priceId: "price_...", customerId: "cus_...", mode: "payment", successUrl: "https://example.com/success", cancelUrl: "https://example.com/cancel", paymentIntentMetadata: { userId: "user_123" }, }); ``` ``` -------------------------------- ### createCheckoutSession Source: https://github.com/get-convex/stripe/blob/main/README.md Creates a Stripe Checkout session for initiating payments or subscriptions. ```APIDOC ## createCheckoutSession ### Description Create a Stripe Checkout session to facilitate payments or subscriptions. ### Method `createCheckoutSession(ctx, { priceId, customerId, mode, successUrl, cancelUrl, quantity, metadata, subscriptionMetadata, paymentIntentMetadata, params })` ### Parameters - **ctx**: The Convex context object. - **priceId** (string) - Required - The ID of the Stripe price. - **customerId** (string) - Optional - The ID of the Stripe customer. - **mode** (string) - Required - The mode of the session: "subscription", "payment", or "setup". - **successUrl** (string) - Required - The URL to redirect to upon successful completion. - **cancelUrl** (string) - Required - The URL to redirect to if the session is canceled. - **quantity** (number) - Optional - The quantity of the item, defaults to 1. - **metadata** (object) - Optional - Session metadata. - **subscriptionMetadata** (object) - Optional - Metadata attached to the subscription. - **paymentIntentMetadata** (object) - Optional - Metadata attached to the payment intent. - **params** (object) - Optional - Additional Stripe Checkout Session parameters. Fields in `params` override constructed defaults, except `mode`. ### Request Example ```typescript await stripeClient.createCheckoutSession(ctx, { priceId: "price_...", customerId: "cus_...", // Optional mode: "subscription", // "subscription" | "payment" | "setup" successUrl: "https://...", cancelUrl: "https://...", quantity: 1, // Optional, default 1 metadata: {}, subscriptionMetadata: {}, paymentIntentMetadata: {}, params: { allow_promotion_codes: true, ui_mode: "embedded_page", return_url: "https://...", }, }); ``` ### Notes For non-hosted Checkout UI modes, `successUrl` and `cancelUrl` are omitted from the Stripe request, and redirect behavior should be handled via Stripe-supported params like `return_url`. ``` -------------------------------- ### Create a Stripe Customer Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/stripe-subscriptions.md Use this function to create a new customer in Stripe and your database. It's recommended to use an idempotency key, such as the user ID, to prevent duplicate creations. ```typescript async createCustomer( ctx: ActionCtx, args: { email?: string; name?: string; metadata?: Record; idempotencyKey?: string; } ): Promise<{ customerId: string; }> ``` ```typescript const customer = await stripeClient.createCustomer(ctx, { email: "user@example.com", name: "John Doe", metadata: { userId: "user_123" }, idempotencyKey: "user_123", // Prevents race condition duplicates }); console.log(customer.customerId); // "cus_..." ``` -------------------------------- ### Custom Stripe Webhook Handler Source: https://github.com/get-convex/stripe/blob/main/README.md Add custom logic to specific Stripe webhook events or implement a general handler for all events. This example shows how to log subscription updates and all incoming event types. ```typescript 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; ``` -------------------------------- ### createCustomerPortalSession Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/stripe-subscriptions.md Generates a Stripe Customer Portal session URL, allowing customers to manage their subscriptions and billing information. ```APIDOC ## createCustomerPortalSession ### Description Generate a Stripe Customer Portal session URL for managing subscriptions and billing. ### Method `async createCustomerPortalSession(ctx: ActionCtx, args: { customerId: string; returnUrl: string; }): Promise<{ url: string; }>` ### Parameters #### Arguments - **customerId** (string) - Required - Stripe customer ID - **returnUrl** (string) - Required - URL to redirect to when user closes portal ### Response #### Success Response - **url** (string) - The Customer Portal session URL. ### Request Example ```typescript const portal = await stripeClient.createCustomerPortalSession(ctx, { customerId: "cus_abc123", returnUrl: "https://yourapp.com/dashboard", }); // Redirect user to portal return { portalUrl: portal.url }; ``` ### Error Handling - Throws if `STRIPE_SECRET_KEY` is not available - Stripe API errors propagate directly ``` -------------------------------- ### Handle Stripe API Errors in TypeScript Source: https://github.com/get-convex/stripe/blob/main/_autodocs/errors.md Use a try-catch block to handle potential Stripe SDK errors. This example demonstrates how to catch and log specific error types like invalid requests, rate limits, and authentication failures. ```typescript import StripeSDK from "stripe"; try { await stripeClient.createCheckoutSession(ctx, { priceId: "price_123", mode: "payment", successUrl: "https://...", cancelUrl: "https://...", }); } catch (error) { if (error instanceof StripeSDK.errors.StripeInvalidRequestError) { console.error("Invalid parameters:", error.message); } else if (error instanceof StripeSDK.errors.StripeRateLimitError) { console.error("Rate limited - retry later"); } else if (error instanceof StripeSDK.errors.StripeAuthenticationError) { console.error("Authentication failed - check API key"); } else { console.error("Stripe API error:", error); } } ``` -------------------------------- ### Get or Create a Stripe Customer Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/stripe-subscriptions.md This function retrieves an existing Stripe customer or creates a new one if not found. It checks multiple sources, including user ID and email, to ensure a unique customer record. The `userId` parameter is mandatory for linking. ```typescript async getOrCreateCustomer( ctx: ActionCtx, args: { userId: string; email?: string; name?: string; } ): Promise<{ customerId: string; isNew: boolean; }> ``` ```typescript const identity = await ctx.auth.getUserIdentity(); const result = await stripeClient.getOrCreateCustomer(ctx, { userId: identity.subject, email: identity.email, name: identity.name, }); if (result.isNew) { console.log("Created new customer:", result.customerId); } else { console.log("Found existing customer:", result.customerId); } ``` -------------------------------- ### handleCheckoutSessionCompleted Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/private-mutations.md Creates or updates a checkout session record in the system. It handles both new sessions and updates existing ones to a 'complete' status. ```APIDOC ## handleCheckoutSessionCompleted ### Description Creates or updates a checkout session record in the system. It handles both new sessions and updates existing ones to a 'complete' status. ### Method mutation ### Arguments - **stripeCheckoutSessionId** (string) - Required - The ID of the Stripe checkout session. - **stripeCustomerId** (string) - Optional - The ID of the Stripe customer associated with the session. - **mode** (string) - Required - The mode of the checkout session (e.g., 'payment', 'subscription'). - **metadata** (any) - Optional - Additional metadata for the checkout session. ### Returns null ``` -------------------------------- ### Stripe Subscriptions Client Methods Source: https://github.com/get-convex/stripe/blob/main/README.md Provides a list of available methods on the StripeSubscriptions client for managing subscriptions and customers. ```APIDOC ## Stripe Subscriptions Client Methods | Method | Description | |---|---| | `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 | ``` -------------------------------- ### Initialize StripeSubscriptions with Overridden API Key Source: https://github.com/get-convex/stripe/blob/main/_autodocs/configuration.md Initialize StripeSubscriptions by providing a custom STRIPE_SECRET_KEY directly in the constructor options. This overrides the environment variable. ```typescript import { StripeSubscriptions } from "@convex-dev/stripe"; import { components } from "./_generated/api"; // Override API key const stripeClient = new StripeSubscriptions(components.stripe, { STRIPE_SECRET_KEY: "sk_test_custom_key", }); ``` -------------------------------- ### Create Stripe Checkout Sessions Source: https://github.com/get-convex/stripe/blob/main/example/convex/README.md Creates checkout sessions for both subscriptions and one-time payments. Requires `priceId`, `customerId`, `mode`, `successUrl`, and `cancelUrl`. Metadata can be included for tracking. ```typescript // Subscription checkout await stripeClient.createCheckoutSession(ctx, { priceId: "price_...", customerId: "cus_...", mode: "subscription", successUrl: "https://example.com/success", cancelUrl: "https://example.com/cancel", subscriptionMetadata: { userId: "user_123" }, }); // One-time payment checkout await stripeClient.createCheckoutSession(ctx, { priceId: "price_...", customerId: "cus_...", mode: "payment", successUrl: "https://example.com/success", cancelUrl: "https://example.com/cancel", paymentIntentMetadata: { userId: "user_123" }, }); ``` -------------------------------- ### Subscription Queries Source: https://github.com/get-convex/stripe/blob/main/_autodocs/README.md Provides functions to query subscription information from Stripe, accessible via `components.stripe.public`. Supports retrieving subscriptions by Stripe ID, customer ID, organization ID, or user ID. ```APIDOC ## Subscription Queries All accessed via `components.stripe.public.queryName(args)`. ### getSubscription - **Arguments**: `{stripeSubscriptionId}` - **Returns**: `Subscription | null` ### listSubscriptions - **Arguments**: `{stripeCustomerId}` - **Returns**: `Subscription[]` ### getSubscriptionByOrgId - **Arguments**: `{orgId}` - **Returns**: `Subscription | null` ### listSubscriptionsByOrgId - **Arguments**: `{orgId}` - **Returns**: `Subscription[]` ### listSubscriptionsByUserId - **Arguments**: `{userId}` - **Returns**: `Subscription[]` ### Further Details See [Public Queries](./api-reference/public-queries.md) for full signatures and examples. ``` -------------------------------- ### Register Stripe Component in convex/convex.config.ts Source: https://github.com/get-convex/stripe/blob/main/_autodocs/configuration.md Register the Stripe component by importing and using it in your `convex/convex.config.ts` file. This makes its queries and mutations accessible. ```typescript import { defineApp } from "convex/server"; import stripe from "@convex-dev/stripe/convex.config.js"; const app = defineApp(); app.use(stripe); export default app; ``` -------------------------------- ### Handle Missing STRIPE_SECRET_KEY Source: https://github.com/get-convex/stripe/blob/main/_autodocs/errors.md This snippet demonstrates how to catch errors that occur when the STRIPE_SECRET_KEY environment variable is not set. It shows the use of a try-catch block to handle potential exceptions when initializing StripeSubscriptions. ```typescript try { const client = new StripeSubscriptions(components.stripe, {}); // Will throw if apiKey is accessed and env var not set } catch (error) { console.error("Missing API key configuration:", error); } ``` -------------------------------- ### Environment Variables for Stripe Integration Source: https://github.com/get-convex/stripe/blob/main/example/convex/README.md Lists the required environment variables for Stripe integration: `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET`, and `APP_URL`. These should be set in the Convex Dashboard. ```bash STRIPE_SECRET_KEY=sk_test_... STRIPE_WEBHOOK_SECRET=whsec_... APP_URL=http://localhost:5173 ``` -------------------------------- ### Create Stripe Customer Portal Session Source: https://github.com/get-convex/stripe/blob/main/_autodocs/api-reference/stripe-subscriptions.md Generates a URL for the Stripe Customer Portal, allowing users to manage their subscriptions and billing information. The `returnUrl` is crucial for redirecting the user back to your application after they close the portal. ```typescript async createCustomerPortalSession( ctx: ActionCtx, args: { customerId: string; returnUrl: string; } ): Promise<{ url: string; }> ``` ```typescript const portal = await stripeClient.createCustomerPortalSession(ctx, { customerId: "cus_abc123", returnUrl: "https://yourapp.com/dashboard", }); // Redirect user to portal return { portalUrl: portal.url }; ``` -------------------------------- ### Create Stripe Subscription Checkout Session Source: https://github.com/get-convex/stripe/blob/main/_autodocs/README.md Initiates a Stripe checkout session for a subscription. Requires a price ID, customer ID, and URLs for success and cancellation. The `subscriptionMetadata` can be used to store custom data like the user ID. ```typescript const result = await stripeClient.createCheckoutSession(ctx, { priceId: "price_123", customerId: customer.customerId, mode: "subscription", successUrl: "https://app.com/success", cancelUrl: "https://app.com/cancel", subscriptionMetadata: { userId: identity.subject }, }); return { url: result.url }; ``` -------------------------------- ### Public Queries Source: https://github.com/get-convex/stripe/blob/main/_autodocs/INDEX.md Provides functions for querying customer, subscription, payment, invoice, and checkout session data. ```APIDOC ## Public Queries ### Description Functions for querying Stripe-related data. ### Available Queries - **Customer Queries**: `getCustomer`, `getCustomerByEmail`, `getCustomerByUserId` - **Subscription Queries**: `getSubscription`, `listSubscriptions`, `getSubscriptionByOrgId`, `listSubscriptionsByOrgId`, `listSubscriptionsByUserId` - **Payment Queries**: `getPayment`, `listPayments`, `listPaymentsByUserId`, `listPaymentsByOrgId` - **Invoice Queries**: `listInvoices`, `listInvoicesByOrgId`, `listInvoicesByUserId` - **Checkout Session Queries**: `getCheckoutSession`, `listCheckoutSessions` ``` -------------------------------- ### StripeSubscriptions Constructor Signature Source: https://github.com/get-convex/stripe/blob/main/_autodocs/configuration.md The constructor for StripeSubscriptions accepts the Convex component and an optional configuration object. The configuration object can override the Stripe API key and specify the Stripe API version. ```typescript new StripeSubscriptions(component, { STRIPE_SECRET_KEY?: string; apiVersion?: StripeApiVersion; }) ``` -------------------------------- ### getUserSubscriptions Query Source: https://github.com/get-convex/stripe/blob/main/README.md A Convex query to retrieve a list of subscriptions for the authenticated user. ```APIDOC ## getUserSubscriptions Query ### Description List subscriptions for a user. ### Handler `getUserSubscriptions()` ### Usage ```typescript import { query } from "./_generated/server"; import { components } from "./_generated/api"; 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 }, ); }, }); ``` ```