diff --git a/.env.example b/.env.example index c7fdb940d..9277feef9 100644 --- a/.env.example +++ b/.env.example @@ -1,37 +1,42 @@ # ----------------------------------------------------------------------------- -# App - Don't add "/" in the end of the url (same in production) +# Required Environment Variables # ----------------------------------------------------------------------------- -NEXT_PUBLIC_APP_URL=http://localhost:3000 -# ----------------------------------------------------------------------------- -# Authentication (NextAuth.js) -# ----------------------------------------------------------------------------- -AUTH_SECRET= +# App - Don't add "/" in the end of the url (same in production) +NEXT_PUBLIC_APP_URL="http://localhost:3000" -GOOGLE_CLIENT_ID= -GOOGLE_CLIENT_SECRET= +# Database (PostgreSQL - Neon DB) +DATABASE_URL='postgres://[user]:[password]@[neon_hostname]/[dbname]?sslmode=require' -GITHUB_OAUTH_TOKEN= +# Auth secret for token encryption. Generate one using `npx auth secret` or `openssl rand -base64 33`. +AUTH_SECRET="" # ----------------------------------------------------------------------------- -# Database (MySQL - Neon DB) +# Optional Authentication (NextAuth.js) # ----------------------------------------------------------------------------- -DATABASE_URL='postgres://[user]:[password]@[neon_hostname]/[dbname]?sslmode=require' +# Required only if you want authentication +# GOOGLE_CLIENT_ID="" +# GOOGLE_CLIENT_SECRET="" +# GITHUB_OAUTH_TOKEN="" + # ----------------------------------------------------------------------------- -# Email (Resend) +# Optional Email (Resend) # ----------------------------------------------------------------------------- -RESEND_API_KEY= -EMAIL_FROM="SaaS Starter App " +# Required only if you want email features +# RESEND_API_KEY="" +# EMAIL_FROM="SaaS Starter App " + # ----------------------------------------------------------------------------- -# Subscriptions (Stripe) +# Optional Subscriptions (Stripe) # ----------------------------------------------------------------------------- -STRIPE_API_KEY= -STRIPE_WEBHOOK_SECRET= +# Required only if you want payment features +# STRIPE_API_KEY="" +# STRIPE_WEBHOOK_SECRET="" -NEXT_PUBLIC_STRIPE_PRO_MONTHLY_PLAN_ID= -NEXT_PUBLIC_STRIPE_PRO_YEARLY_PLAN_ID= +# NEXT_PUBLIC_STRIPE_PRO_MONTHLY_PLAN_ID="" +# NEXT_PUBLIC_STRIPE_PRO_YEARLY_PLAN_ID="" -NEXT_PUBLIC_STRIPE_BUSINESS_MONTHLY_PLAN_ID= -NEXT_PUBLIC_STRIPE_BUSINESS_YEARLY_PLAN_ID= \ No newline at end of file +# NEXT_PUBLIC_STRIPE_BUSINESS_MONTHLY_PLAN_ID="" +# NEXT_PUBLIC_STRIPE_BUSINESS_YEARLY_PLAN_ID="" \ No newline at end of file diff --git a/README.md b/README.md index c854b30e1..04bff141a 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,48 @@ pnpm install ```sh cp .env.example .env.local ``` +> +> #### Where to get each of these environment variables: +> +> 1. **AUTH_SECRET** +> - Generate a random string using `openssl rand -base64 32` in terminal +> - Or use any secure random string generator +> +> 2. **Google OAuth Credentials (GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET)** +> - Go to [Google Cloud Console](https://console.cloud.google.com/) +> - Create a new project +> - Enable Google OAuth API +> - Create OAuth 2.0 credentials +> - Set authorized redirect URI to `http://localhost:3000/api/auth/callback/google` +> +> 3. **GITHUB_OAUTH_TOKEN** +> - Go to [GitHub Settings > Developer Settings](https://github.com/settings/tokens) +> - Generate new Personal Access Token +> - Select required scopes +> +> 4. **DATABASE_URL** +> - Sign up at [Neon](https://neon.tech) +> - Create a new project +> - Get connection string from dashboard +> - Replace `[user]`, `[password]`, `[neon_hostname]`, and `[dbname]` with your values +> +> 5. **RESEND_API_KEY** +> - Sign up at [Resend](https://resend.com) +> - Get API key from dashboard +> - Update EMAIL_FROM with your verified domain (or use the default for testing) +> +> 6. **Stripe Variables** +> - Sign up for [Stripe](https://stripe.com) +> - Get API key from dashboard (STRIPE_API_KEY) +> - Set up webhook in Stripe dashboard to get STRIPE_WEBHOOK_SECRET +> - Create products/prices in Stripe dashboard to get plan IDs: +> - NEXT_PUBLIC_STRIPE_PRO_MONTHLY_PLAN_ID +> - NEXT_PUBLIC_STRIPE_PRO_YEARLY_PLAN_ID +> - NEXT_PUBLIC_STRIPE_BUSINESS_MONTHLY_PLAN_ID +> - NEXT_PUBLIC_STRIPE_BUSINESS_YEARLY_PLAN_ID +> +> Remember to use test keys during development and switch to production keys when deploying. +> 3. Start the development server: diff --git a/actions/generate-user-stripe.ts b/actions/generate-user-stripe.ts index 5c143d9c7..4cbc0bfd2 100644 --- a/actions/generate-user-stripe.ts +++ b/actions/generate-user-stripe.ts @@ -15,6 +15,10 @@ export type responseAction = { const billingUrl = absoluteUrl("/pricing") export async function generateUserStripe(priceId: string): Promise { + if (!stripe) { + throw new Error("Stripe is not configured"); + } + let redirectUrl: string = ""; try { diff --git a/app/api/webhooks/stripe/route.ts b/app/api/webhooks/stripe/route.ts index ff39f25f6..574c15e5d 100644 --- a/app/api/webhooks/stripe/route.ts +++ b/app/api/webhooks/stripe/route.ts @@ -6,6 +6,10 @@ import { prisma } from "@/lib/db"; import { stripe } from "@/lib/stripe"; export async function POST(req: Request) { + if (!stripe || !env.STRIPE_WEBHOOK_SECRET) { + return new Response("Stripe is not configured", { status: 400 }); + } + const body = await req.text(); const signature = headers().get("Stripe-Signature") as string; diff --git a/auth.config.ts b/auth.config.ts index ea4eb30b0..4371b49f8 100644 --- a/auth.config.ts +++ b/auth.config.ts @@ -3,18 +3,26 @@ import Google from "next-auth/providers/google"; import Resend from "next-auth/providers/resend"; import { env } from "@/env.mjs"; -import { sendVerificationRequest } from "@/lib/email"; export default { providers: [ - Google({ - clientId: env.GOOGLE_CLIENT_ID, - clientSecret: env.GOOGLE_CLIENT_SECRET, - }), - Resend({ - apiKey: env.RESEND_API_KEY, - from: env.EMAIL_FROM, - // sendVerificationRequest, - }), + // Only add Google provider if credentials are present + ...(env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET + ? [ + Google({ + clientId: env.GOOGLE_CLIENT_ID, + clientSecret: env.GOOGLE_CLIENT_SECRET, + }), + ] + : []), + // Only add Resend provider if credentials are present + ...(env.RESEND_API_KEY && env.EMAIL_FROM + ? [ + Resend({ + apiKey: env.RESEND_API_KEY, + from: env.EMAIL_FROM, + }), + ] + : []), ], } satisfies NextAuthConfig; diff --git a/env.mjs b/env.mjs index a9e1a7a9e..de33437d6 100644 --- a/env.mjs +++ b/env.mjs @@ -7,21 +7,21 @@ export const env = createEnv({ // See https://next-auth.js.org/deployment. NEXTAUTH_URL: z.string().url().optional(), AUTH_SECRET: z.string().min(1), - GOOGLE_CLIENT_ID: z.string().min(1), - GOOGLE_CLIENT_SECRET: z.string().min(1), - GITHUB_OAUTH_TOKEN: z.string().min(1), + GOOGLE_CLIENT_ID: z.string().optional(), + GOOGLE_CLIENT_SECRET: z.string().optional(), + GITHUB_OAUTH_TOKEN: z.string().optional(), DATABASE_URL: z.string().min(1), - RESEND_API_KEY: z.string().min(1), - EMAIL_FROM: z.string().min(1), - STRIPE_API_KEY: z.string().min(1), - STRIPE_WEBHOOK_SECRET: z.string().min(1), + RESEND_API_KEY: z.string().optional(), + EMAIL_FROM: z.string().optional(), + STRIPE_API_KEY: z.string().optional(), + STRIPE_WEBHOOK_SECRET: z.string().optional(), }, client: { NEXT_PUBLIC_APP_URL: z.string().min(1), - NEXT_PUBLIC_STRIPE_PRO_MONTHLY_PLAN_ID: z.string().min(1), - NEXT_PUBLIC_STRIPE_PRO_YEARLY_PLAN_ID: z.string().min(1), - NEXT_PUBLIC_STRIPE_BUSINESS_MONTHLY_PLAN_ID: z.string().min(1), - NEXT_PUBLIC_STRIPE_BUSINESS_YEARLY_PLAN_ID: z.string().min(1), + NEXT_PUBLIC_STRIPE_PRO_MONTHLY_PLAN_ID: z.string().optional(), + NEXT_PUBLIC_STRIPE_PRO_YEARLY_PLAN_ID: z.string().optional(), + NEXT_PUBLIC_STRIPE_BUSINESS_MONTHLY_PLAN_ID: z.string().optional(), + NEXT_PUBLIC_STRIPE_BUSINESS_YEARLY_PLAN_ID: z.string().optional(), }, runtimeEnv: { NEXTAUTH_URL: process.env.NEXTAUTH_URL, diff --git a/lib/stripe.ts b/lib/stripe.ts index 5938b8179..aa5aee3d5 100644 --- a/lib/stripe.ts +++ b/lib/stripe.ts @@ -1,8 +1,11 @@ import Stripe from "stripe" - import { env } from "@/env.mjs" -export const stripe = new Stripe(env.STRIPE_API_KEY, { - apiVersion: "2024-04-10", - typescript: true, -}) +export let stripe: Stripe | null = null; + +if (env.STRIPE_API_KEY) { + stripe = new Stripe(env.STRIPE_API_KEY, { + apiVersion: "2024-04-10", + typescript: true, + }); +}