From 3940956c83fcce4eabad17836bfd5ec2dd26fabc Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Sat, 24 Oct 2026 11:30:09 -0700 Subject: [PATCH 1/2] fix(billing): register the Stripe plugin only when the webhook secret is configured The Better Auth Stripe plugin was passed `STRIPE_WEBHOOK_SECRET || ''`. Stripe's verifier accepts signatures computed with an empty key, so the plugin's own falsy-secret check was the only thing keeping the receiver from accepting forged events. Billing now registers only when the webhook secret is set, logging an error otherwise, and the setup preflight requires it alongside STRIPE_SECRET_KEY. --- apps/sim/lib/auth/auth.ts | 34 +++++++++++++++++++++----------- packages/sim-setup/src/checks.ts | 2 +- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/apps/sim/lib/auth/auth.ts b/apps/sim/lib/auth/auth.ts index 30853ded401..b1b4750adf4 100644 --- a/apps/sim/lib/auth/auth.ts +++ b/apps/sim/lib/auth/auth.ts @@ -237,15 +237,27 @@ if (env.NODE_ENV === 'production') { } } -const validStripeKey = env.STRIPE_SECRET_KEY - -let stripeClient = null -if (validStripeKey) { - stripeClient = new Stripe(env.STRIPE_SECRET_KEY || '', { - apiVersion: '2025-08-27.basil', - }) +/** + * Resolves the Stripe billing plugin's client and webhook signing secret, or `null` when the plugin + * must not register. Stripe's verifier accepts a signature computed with an empty key, so a missing + * `STRIPE_WEBHOOK_SECRET` disables the plugin rather than exposing a forgeable webhook receiver. + */ +function resolveStripeBilling() { + if (!isBillingEnabled || !env.STRIPE_SECRET_KEY) return null + if (!env.STRIPE_WEBHOOK_SECRET) { + logger.error( + 'BILLING_ENABLED and STRIPE_SECRET_KEY are set but STRIPE_WEBHOOK_SECRET is not; the Stripe billing plugin is disabled until it is configured' + ) + return null + } + return { + client: new Stripe(env.STRIPE_SECRET_KEY, { apiVersion: '2025-08-27.basil' }), + webhookSecret: env.STRIPE_WEBHOOK_SECRET, + } } +const stripeBilling = resolveStripeBilling() + /** * Resolves the org's API instance URL for a freshly linked Salesforce account. * @@ -967,7 +979,7 @@ export const auth = betterAuth({ banned: false, banReason: null, banExpires: null, - ...(isBillingEnabled && stripeClient ? { stripeCustomerId: null } : {}), + ...(stripeBilling ? { stripeCustomerId: null } : {}), ...additionalFields, id, }), @@ -1547,11 +1559,11 @@ export const auth = betterAuth({ * SSO provider behind it. */ ...(isSsoEnabled ? [configuredSsoPlugin] : []), - ...(isBillingEnabled && stripeClient + ...(stripeBilling ? [ stripe({ - stripeClient, - stripeWebhookSecret: env.STRIPE_WEBHOOK_SECRET || '', + stripeClient: stripeBilling.client, + stripeWebhookSecret: stripeBilling.webhookSecret, createCustomerOnSignUp: true, onCustomerCreate: async ({ stripeCustomer, user }) => { logger.info('[onCustomerCreate] Stripe customer created', { diff --git a/packages/sim-setup/src/checks.ts b/packages/sim-setup/src/checks.ts index c94eff4a51a..a129fee3bc1 100644 --- a/packages/sim-setup/src/checks.ts +++ b/packages/sim-setup/src/checks.ts @@ -469,7 +469,7 @@ function checkCoherence(ctx: CheckContext): Finding[] { }> = [ { flag: 'BILLING_ENABLED', - needs: ['STRIPE_SECRET_KEY'], + needs: ['STRIPE_SECRET_KEY', 'STRIPE_WEBHOOK_SECRET'], label: 'billing', }, { From bc2ad3ea753e381667c9d47d60caa108bb29a778 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Sat, 24 Oct 2026 11:35:50 -0700 Subject: [PATCH 2/2] fix(billing): treat a whitespace-only Stripe webhook secret as missing --- apps/sim/lib/auth/auth.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/sim/lib/auth/auth.ts b/apps/sim/lib/auth/auth.ts index b1b4750adf4..b3fd7be1f5f 100644 --- a/apps/sim/lib/auth/auth.ts +++ b/apps/sim/lib/auth/auth.ts @@ -244,7 +244,8 @@ if (env.NODE_ENV === 'production') { */ function resolveStripeBilling() { if (!isBillingEnabled || !env.STRIPE_SECRET_KEY) return null - if (!env.STRIPE_WEBHOOK_SECRET) { + const webhookSecret = env.STRIPE_WEBHOOK_SECRET + if (!webhookSecret?.trim()) { logger.error( 'BILLING_ENABLED and STRIPE_SECRET_KEY are set but STRIPE_WEBHOOK_SECRET is not; the Stripe billing plugin is disabled until it is configured' ) @@ -252,7 +253,7 @@ function resolveStripeBilling() { } return { client: new Stripe(env.STRIPE_SECRET_KEY, { apiVersion: '2025-08-27.basil' }), - webhookSecret: env.STRIPE_WEBHOOK_SECRET, + webhookSecret, } }