From 28188bb3a63e883356842e16f8b837c4484dde15 Mon Sep 17 00:00:00 2001 From: Zaid Marzguioui Date: Thu, 2 Apr 2026 23:34:30 +0200 Subject: [PATCH] fix(billing): wire up Stripe checkout with real price IDs and env vars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace 8 placeholder price IDs (price_starter_monthly, etc.) with real Stripe test-mode price IDs created via API - Add STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, NEXT_PUBLIC_BILLING_PROVIDER, and NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY to docker-compose.yml (build args + runtime) - Add NEXT_PUBLIC_BILLING_PROVIDER ARG/ENV to Dockerfile - Enable team and personal account billing (was 'false') - Created Stripe webhook endpoint for production URL - Created 4 Stripe products (Starter/Pro/Verband/Enterprise) with monthly+yearly prices Checkout was crashing because: 1. STRIPE_SECRET_KEY was missing → Zod validation failed at createStripeClient() 2. STRIPE_WEBHOOK_SECRET was missing → same Zod schema rejection 3. NEXT_PUBLIC_BILLING_PROVIDER was unset → BillingProviderSchema.parse() failed 4. Price IDs were placeholders, not real Stripe price_xxx IDs --- Dockerfile | 2 ++ apps/web/config/billing.config.ts | 16 ++++++++-------- docker-compose.yml | 12 ++++++++++-- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3d280e6aa..d14450b2b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,6 +22,7 @@ ARG NEXT_PUBLIC_ENABLE_FISCHEREI=true ARG NEXT_PUBLIC_ENABLE_MEETING_PROTOCOLS=true ARG NEXT_PUBLIC_ENABLE_VERBANDSVERWALTUNG=true ARG NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY= +ARG NEXT_PUBLIC_BILLING_PROVIDER=stripe ENV NEXT_PUBLIC_CI=${NEXT_PUBLIC_CI} ENV NEXT_PUBLIC_SITE_URL=${NEXT_PUBLIC_SITE_URL} ENV NEXT_PUBLIC_SUPABASE_URL=${NEXT_PUBLIC_SUPABASE_URL} @@ -31,6 +32,7 @@ ENV NEXT_PUBLIC_ENABLE_FISCHEREI=${NEXT_PUBLIC_ENABLE_FISCHEREI} ENV NEXT_PUBLIC_ENABLE_MEETING_PROTOCOLS=${NEXT_PUBLIC_ENABLE_MEETING_PROTOCOLS} ENV NEXT_PUBLIC_ENABLE_VERBANDSVERWALTUNG=${NEXT_PUBLIC_ENABLE_VERBANDSVERWALTUNG} ENV NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=${NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY} +ENV NEXT_PUBLIC_BILLING_PROVIDER=${NEXT_PUBLIC_BILLING_PROVIDER} RUN pnpm --filter web build # --- Run --- diff --git a/apps/web/config/billing.config.ts b/apps/web/config/billing.config.ts index 71b08b732..38ceb7254 100644 --- a/apps/web/config/billing.config.ts +++ b/apps/web/config/billing.config.ts @@ -33,7 +33,7 @@ export default createBillingSchema({ interval: 'month', lineItems: [ { - id: 'price_starter_monthly', + id: 'price_1THsqKKttnWb7SsFttMu9VzG', name: 'Starter', cost: 29, type: 'flat' as const, @@ -47,7 +47,7 @@ export default createBillingSchema({ interval: 'year', lineItems: [ { - id: 'price_starter_yearly', + id: 'price_1THsqLKttnWb7SsFgvjsKXzs', name: 'Starter', cost: 290, type: 'flat' as const, @@ -82,7 +82,7 @@ export default createBillingSchema({ interval: 'month', lineItems: [ { - id: 'price_pro_monthly', + id: 'price_1THsqLKttnWb7SsFlWPf5IdP', name: 'Pro', cost: 59, type: 'flat' as const, @@ -96,7 +96,7 @@ export default createBillingSchema({ interval: 'year', lineItems: [ { - id: 'price_pro_yearly', + id: 'price_1THsqMKttnWb7SsFZq3A4QkU', name: 'Pro', cost: 590, type: 'flat' as const, @@ -130,7 +130,7 @@ export default createBillingSchema({ interval: 'month', lineItems: [ { - id: 'price_verband_monthly', + id: 'price_1THsqNKttnWb7SsFGv7YskgJ', name: 'Verband', cost: 199, type: 'flat' as const, @@ -144,7 +144,7 @@ export default createBillingSchema({ interval: 'year', lineItems: [ { - id: 'price_verband_yearly', + id: 'price_1THsqNKttnWb7SsFhNl2bVn8', name: 'Verband', cost: 1990, type: 'flat' as const, @@ -178,7 +178,7 @@ export default createBillingSchema({ interval: 'month', lineItems: [ { - id: 'price_enterprise_monthly', + id: 'price_1THsqOKttnWb7SsFlLjfLw72', name: 'Enterprise', cost: 349, type: 'flat' as const, @@ -192,7 +192,7 @@ export default createBillingSchema({ interval: 'year', lineItems: [ { - id: 'price_enterprise_yearly', + id: 'price_1THsqOKttnWb7SsF8Sr12isW', name: 'Enterprise', cost: 3490, type: 'flat' as const, diff --git a/docker-compose.yml b/docker-compose.yml index 19ef51ee3..8eb41c0bc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -330,6 +330,9 @@ services: # Browser-side Supabase URL — goes through external domain (Traefik → Kong) NEXT_PUBLIC_SUPABASE_URL: ${API_EXTERNAL_URL:-http://localhost:8000} NEXT_PUBLIC_SUPABASE_PUBLIC_KEY: ${SUPABASE_ANON_KEY} + # Stripe (build-time) + NEXT_PUBLIC_BILLING_PROVIDER: stripe + NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: ${NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY} restart: unless-stopped depends_on: supabase-kong: @@ -352,12 +355,17 @@ services: NEXT_PUBLIC_ENABLE_THEME_TOGGLE: 'true' NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS: 'true' NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_CREATION: 'true' - NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_BILLING: 'false' - NEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_BILLING: 'false' + NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_BILLING: 'true' + NEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_BILLING: 'true' NEXT_PUBLIC_ENABLE_NOTIFICATIONS: 'true' NEXT_PUBLIC_ENABLE_FISCHEREI: 'true' NEXT_PUBLIC_ENABLE_MEETING_PROTOCOLS: 'true' NEXT_PUBLIC_ENABLE_VERBANDSVERWALTUNG: 'true' + # Stripe (runtime) + NEXT_PUBLIC_BILLING_PROVIDER: stripe + NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: ${NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY} + STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY} + STRIPE_WEBHOOK_SECRET: ${STRIPE_WEBHOOK_SECRET} volumes: supabase-db-data: