Stripe Payments Integration
Purpose and Scope
The Stripe payment integration lets a self-hosted Cal.diy instance offer payment-related scheduling features, such as paid events and the app-store payment connection flow. This page explains the setup contract that the repository documents for Stripe: which dashboard features must be enabled, which environment variables must be populated, which callback and webhook URLs Stripe must call, and which app-store API entry points expose the integration handlers. It is written for operators configuring a deployed instance and for developers who need to understand where the Stripe app package connects to the wider app-store integration model.
Sources: apps/docs/content/apps/stripe.mdx, packages/app-store/stripepayment/README.md, packages/app-store/stripepayment/api/index.ts
Stripe setup has two distinct parts that are easy to confuse. The first is API-key configuration, where Cal.diy needs both a publishable key and a private key from the Stripe dashboard. The second is Connect and webhook configuration, where Stripe must be told how to return users to Cal.diy after OAuth and how to notify Cal.diy about payment and setup intent activity. The docs use the same callback and webhook paths in both the app documentation and the package README, which makes those URLs the operational contract for self-hosted deployments.
Relevant Source Files
apps/docs/content/apps/stripe.mdx- First-party app documentation for setting up Stripe, including API keys, Connect OAuth, callback URL, webhook URL, selected events, and webhook secret.packages/app-store/stripepayment/README.md- Package-level README for the Stripe Payment Integration that mirrors the setup sequence and documents the app package's intended configuration.packages/app-store/stripepayment/api/index.ts- API barrel for the Stripe payment app package, exporting the handlers that the app-store integration makes available to the rest of the repository.
Setup Flow
Start in the Stripe dashboard by creating or opening a Stripe account. For non-production validation, the docs explicitly point readers to Stripe Test Mode, which lets the same setup be exercised without live payment activity. From the API keys page, copy the publishable key that starts with pk_ into NEXT_PUBLIC_STRIPE_PUBLIC_KEY and the private key that starts with sk_ into STRIPE_PRIVATE_KEY. These names matter because the troubleshooting guidance says missing or empty Stripe variables cause the app to be treated as not installed, disabling payment features rather than partially enabling them.
Sources: apps/docs/content/apps/stripe.mdx, packages/app-store/stripepayment/README.md
After the keys are available, enable OAuth for Standard Accounts in Stripe Connect settings. This is the step that allows a user or connected account to authorize Cal.diy through Stripe Connect instead of only relying on platform-level API keys. Add the redirect URL using the externally reachable application origin followed by /api/integrations/stripepayment/callback. In the docs this appears as <Cal.diy URL>/api/integrations/stripepayment/callback, while the package README uses the older placeholder <CALENDSO URL> with the same path. In either case, use the deployed Cal.diy base URL, not a local placeholder.
Sources: apps/docs/content/apps/stripe.mdx, packages/app-store/stripepayment/README.md
Next, copy the Stripe Connect client ID, which starts with ca_, into STRIPE_CLIENT_ID. Then create a connected-applications webhook in Stripe and point it at the deployed webhook endpoint, /api/integrations/stripepayment/webhook. The app documentation says to select all payment_intent and setup_intent events, while the package README specifically calls out all payment_intent events. For an operator following the current app docs, include both event families so payment collection and setup-intent flows can be observed by the integration.
Sources: apps/docs/content/apps/stripe.mdx, packages/app-store/stripepayment/README.md
Configuration Reference
| Name or path | Kind | Required value or behavior |
|---|---|---|
NEXT_PUBLIC_STRIPE_PUBLIC_KEY | Environment variable | Stripe publishable key, usually beginning with pk_, copied from Stripe API Keys. |
STRIPE_PRIVATE_KEY | Environment variable | Stripe private key, usually beginning with sk_, copied from Stripe API Keys. |
STRIPE_CLIENT_ID | Environment variable | Stripe Connect client ID, beginning with ca_, copied after enabling OAuth for Standard Accounts. |
STRIPE_WEBHOOK_SECRET | Environment variable | Webhook signing secret, beginning with whsec_, copied from the Stripe webhook configuration. |
/api/integrations/stripepayment/callback | Redirect path | Stripe Connect OAuth redirect URL appended to the public Cal.diy origin. |
/api/integrations/stripepayment/webhook | Webhook path | Stripe webhook endpoint appended to the public Cal.diy origin for connected applications. |
payment_intent events | Webhook events | Payment intent events selected for the Stripe webhook. |
setup_intent events | Webhook events | Setup intent events selected according to the current app documentation. |
A minimal local configuration commonly looks like this, with real values substituted from the Stripe dashboard. Keep the private key and webhook secret server-side; only the publishable key is intentionally public because it is prefixed with NEXT_PUBLIC_. The troubleshooting documentation also notes that some deployments separate root .env settings from .env.appStore; if your setup uses a dedicated app-store env file, ensure the public Stripe key is available there as well as in the runtime environment used by the web app.
NEXT_PUBLIC_STRIPE_PUBLIC_KEY=pk_test_...
STRIPE_PRIVATE_KEY=sk_test_...
STRIPE_CLIENT_ID=ca_...
STRIPE_WEBHOOK_SECRET=whsec_...Sources: apps/docs/content/apps/stripe.mdx, packages/app-store/stripepayment/README.md
API Components
The Stripe payment app package exposes its API surface through packages/app-store/stripepayment/api/index.ts. That barrel exports add, callback, portal, subscription, and paymentCallback from sibling modules. For developers, this means consumers should treat the package API as a set of named handlers rather than reaching directly into individual handler files. The exported callback name corresponds to the integration callback concept documented in setup instructions, while paymentCallback indicates a separate payment-return flow that can be wired by the application where required.
Sources: packages/app-store/stripepayment/api/index.ts
One implementation detail is especially important for operators expecting a webhook endpoint: the API barrel contains a commented webhook export with a note about figuring out how to handle webhook endpoints from the app store. The setup documentation still instructs administrators to configure /api/integrations/stripepayment/webhook in Stripe, so the deployment contract and handler packaging need to be considered together when troubleshooting. If webhook-related behavior does not fire, verify both the environment variables and the route wiring in the deployed application, because the package barrel alone does not publish a webhook export.
Sources: apps/docs/content/apps/stripe.mdx, packages/app-store/stripepayment/api/index.ts
Troubleshooting Signals
When Stripe payment features are unavailable, the first diagnostic step is not to inspect the Stripe dashboard but to confirm that Cal.diy can see the expected variables. The official troubleshooting text explains that missing or empty Stripe variables make the Stripe app appear not installed, which disables paid events and app-store payment integration. Check both .env and .env.appStore patterns used by your deployment, because the public key may be loaded with app-store variables while private keys and webhook secrets are loaded by the main application runtime.
Sources: apps/docs/content/apps/stripe.mdx, packages/app-store/stripepayment/README.md
If OAuth returns to the wrong place, re-check the callback URL and the deployment origin used in the Stripe dashboard. The callback must use the same public scheme and host users use to access Cal.diy, followed by /api/integrations/stripepayment/callback. If webhooks are created but events do not affect bookings or payments, confirm that the webhook URL uses /api/integrations/stripepayment/webhook, that connected-application events were selected, and that the copied secret matches STRIPE_WEBHOOK_SECRET. Also confirm you did not mix live keys with Test Mode webhooks, because Stripe separates those environments.
Sources: apps/docs/content/apps/stripe.mdx, packages/app-store/stripepayment/README.md
Next Steps
After configuring Stripe, test the full loop with a non-production Stripe account or Test Mode: create or connect the Stripe app, configure a paid booking flow, complete a test payment, and confirm the webhook secret is accepted by the deployed instance. Developers changing the integration should start at the app package API barrel, then follow the exported handler names into the package implementation before changing public routes or environment contracts. Operators should keep this page alongside the general apps overview and the payments troubleshooting page, because most Stripe failures are caused by mismatched keys, missing client IDs, incorrect callback origins, or incomplete webhook event selection.
Sources: apps/docs/content/apps/stripe.mdx, packages/app-store/stripepayment/README.md, packages/app-store/stripepayment/api/index.ts