Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ LINGO_API_KEY=your_lingo_api_key
ELEVENLABS_API_KEY=your_elevenlabs_api_key
RESEND_API_KEY=your_resend_api_key
YOUTUBE_API_KEY=your_youtube_api_key
NEXT_PUBLIC_BASE_URL=your_base_url

# Google OAuth Credentials
GOOGLE_CLIENT_SECRET=your_google_client_secret
GOOGLE_CLIENT_ID=your_google_client_id
NEXT_PUBLIC_BASE_URL=http://localhost:3000/
GOOGLE_CLIENT_SECRET=your_google_aouth_client_secret
GOOGLE_CLIENT_ID=your_google_aouth_client_id
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=your_stripe_publishable_key
STRIPE_SECRET_KEY=your_stripe_secret_key
STRIPE_WEBHOOK_SECRET=your_stripe_webhook_secret
19 changes: 16 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,20 @@
```
- Note: This uses the schema from `packages/supabase/migrations/` and won't add any seed data.

4. **Set up environment variables**
4. **Set up Stripe Integration (Local Setup)**
- Get pk_test_***** and sk_test_**** from your stripe dashboard [https://dashboard.stripe.com/acct_*****/test/apikeys](https://dashboard.stripe.com/)
- In another terminal, run Stripe CLI to forward events to your local webhook endpoint
```bash
stripe listen --forward-to localhost:3000/api/stripe/webhook
- A webhook secret will be generated in your terminal, formatted like this: whsec_******
- Edit `apps/web/.env` and `apps/api/.env` to include your stripe credentials:
```
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=whsec_******
STRIPE_SECRET_KEY=pk_test_******
STRIPE_WEBHOOK_SECRET=sk_test_******
```

5. **Set up environment variables**
```bash
# Copy example environment files
cp apps/web/.env.example apps/web/.env
Expand All @@ -70,12 +83,12 @@
```
(Get these from your Supabase dashboard under Settings > API.)

5. **Start development servers**
6. **Start development servers**
```bash
pnpm run dev
```

6. **Open your browser**
7. **Open your browser**
- Frontend: [http://localhost:3000](http://localhost:3000)
- Backend: [http://localhost:8000](http://localhost:8000)

Expand Down
2 changes: 2 additions & 0 deletions apps/api/env.example
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ SUPABASE_SERVICE_KEY=your_supabase_service_role_key
# Google AI Studio API Key for script generation
GOOGLE_AI_STUDIO_API_KEY=your_google_ai_studio_api_key


# Optional: Additional AI Services
# OPENAI_API_KEY=your_openai_api_key
# ELEVENLABS_API_KEY=your_elevenlabs_api_key

# Optional: Payment Processing
# NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=your_stripe_publishable_key
# STRIPE_SECRET_KEY=your_stripe_secret_key
# STRIPE_WEBHOOK_SECRET=your_stripe_webhook_secret

Expand Down
9 changes: 8 additions & 1 deletion apps/web/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,11 @@ NEXT_PUBLIC_BASE_URL=your_base_url

# Google OAuth Credentials
GOOGLE_CLIENT_SECRET=your_google_client_secret
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_ID=your_google_client_id

# Stripe Api Keys
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_****
STRIPE_SECRET_KEY=sk_test_****
STRIPE_WEBHOOK_SECRET=whsec_*****
NEXT_PUBLIC_PRO_PRICE = price_*****
NEXT_PUBLIC__ENTERPRICE_PLAN = price_*****
26 changes: 26 additions & 0 deletions apps/web/app/api/stripe/cancel-subscription/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { createClient } from "@/lib/supabase/server";
import { stripe } from "@/lib/stripe";
import { NextResponse } from "next/server";


export async function POST(req: Request) {
const supabase = await createClient();
const { data: { user }, error: userError } = await supabase.auth.getUser();

try {
const { data: existing } = await supabase
.from("subscriptions")
.select("stripe_subscription_id")
.eq("user_id", user?.id)
.single()

const subscriptionId = existing?.stripe_subscription_id
const updatedSubscription = await stripe.subscriptions.update(subscriptionId, {
cancel_at_period_end: true,
});

return NextResponse.json(updatedSubscription);
} catch (error:any) {
return NextResponse.json({ error: error.message }, { status: 500 });
}
}
86 changes: 86 additions & 0 deletions apps/web/app/api/stripe/create-subscription/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { NextResponse } from "next/server";
import { stripe } from "@/lib/stripe";
import { createClient } from "@/lib/supabase/server";


export async function POST(req: Request) {
const supabase = await createClient();
const pro__plan = process.env.NEXT_PUBLIC_PRO_PRICE
const enterprice_plan = process.env.NEXT_PUBLIC__ENTERPRICE_PLAN
try {
const { data: { user }, error: userError } = await supabase.auth.getUser();
if (userError || !user) {
return NextResponse.json({ message: 'Unauthorized' }, { status: 401 });
}

const { sub_type, plan_id, stripe_customer_id } = await req.json();

const { data: existing } = await supabase
.from("subscriptions")
.select("stripe_subscription_id, plan_id, stripe_customer_id")
.eq("user_id", user.id)
.single()
const { data: profile } = await supabase
.from("profiles")
.select("full_name")
.eq("user_id", user.id)
.single()

// if(existing?.plan_id === plan_id){
// return NextResponse.json({ error: `${sub_type} is your active plan, Please select a different active. plan if you want to upgrade` }, { status: 400 });
// }

let customerId;

if(stripe_customer_id) {
customerId = stripe_customer_id
}else {
const customer = await stripe.customers.create({
name: profile?.full_name,
metadata: { user_id: user.id },
email: user.email
})
customerId = customer.id
}




const session = await stripe.checkout.sessions.create({
mode: "subscription",
payment_method_types: ["card"],
customer: customerId,
line_items: [
{
price: sub_type === "Pro" ? pro__plan : enterprice_plan,
quantity: 1,
},
],
success_url: `${process.env.NEXT_PUBLIC_BASE_URL}/dashboard?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.NEXT_PUBLIC_BASE_URL}/cancel`,
metadata: {
user_id: user.id,
sub_type: sub_type,
plan_id: plan_id
},
subscription_data: {
metadata: {
user_id: user.id,
sub_type,
plan_id,
}
}
});




return NextResponse.json({
url: session.url

});
} catch (error: any) {
console.error("Stripe Error:", error);
return NextResponse.json({ error: error.message }, { status: 500 });
}
}
38 changes: 38 additions & 0 deletions apps/web/app/api/stripe/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { stripe } from "@/lib/stripe";
import { createClient } from "@/lib/supabase/server";
import { NextResponse } from "next/server";

export async function GET(request: Request) {
try {
const supabase = await createClient();
const { data: { user }, error: userError } = await supabase.auth.getUser();

if (userError || !user) {
return NextResponse.json({ message: 'Unauthorized' }, { status: 401 });
}

const { data: existing } = await supabase
.from("subscriptions")
.select("stripe_customer_id")
.eq("user_id", user.id)
.single()

const charges = await stripe.charges.list({
customer: existing?.stripe_customer_id, // Customer ID
limit: 100,
});

// Or using PaymentIntents (recommended for newer integrations)
const paymentIntents = await stripe.paymentIntents.list({
customer: existing?.stripe_customer_id,
limit: 100,
});
return NextResponse.json({
charges,
paymentIntents

});
} catch (error:any) {
return NextResponse.json({ error: error.message }, { status: 500 });
}
}
34 changes: 34 additions & 0 deletions apps/web/app/api/stripe/update-subscription/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { createClient } from "@/lib/supabase/server";
import { stripe } from "@/lib/stripe";
import { NextResponse } from "next/server";


export async function POST(req: Request) {
const supabase = await createClient();
const { data: { user }, error: userError } = await supabase.auth.getUser();

const { price_id, sub_type } = await req.json();

const { data: existing } = await supabase
.from("subscriptions")
.select("stripe_subscription_id")
.eq("user_id", user?.id)
.single()

const subscriptionId = existing?.stripe_subscription_id

const subscription: any = await stripe.subscriptions.retrieve(existing?.stripe_subscription_id);
const subscriptionItemId = subscription.items.data[0].id;

const updatedSubscription = await stripe.subscriptions.update(subscriptionId, {
items: [
{
id: subscriptionItemId,
price: price_id,
},
],
proration_behavior: "none",
});

return NextResponse.json({ message: "Success!, New plan will start at the end of the month", updatedSubscription, status: 200 });
}
Loading
Loading