initial commit

This commit is contained in:
Harivansh Rathi 2024-11-24 20:56:03 -05:00
commit ef9ccf22d3
133 changed files with 20802 additions and 0 deletions

View file

@ -0,0 +1 @@
export { GET, POST } from '@/auth'

64
app/api/checkout/route.ts Normal file
View file

@ -0,0 +1,64 @@
import { auth } from '@/auth'
import { db } from '@/lib/db'
import { stripe } from '@/lib/stripe'
import { NextResponse } from 'next/server'
export async function POST(req: Request) {
try {
const user = await auth()
if (!user || !user.user.id) {
return new NextResponse('Unauthorized', { status: 401 })
}
const userSubscription = await db.userSubscription.findUnique({
where: {
userId: user.user.id
}
})
if (userSubscription && userSubscription.stripeCustomerId) {
const stripeSession = await stripe.billingPortal.sessions.create({
customer: userSubscription.stripeCustomerId,
return_url: process.env.APP_URL
})
return new NextResponse(JSON.stringify({ url: stripeSession.url }))
}
const stripeSession = await stripe.checkout.sessions.create({
success_url: process.env.APP_URL,
cancel_url: process.env.APP_URL,
payment_method_types: ['card'],
mode: 'subscription',
billing_address_collection: 'auto',
customer_email: user?.user.email!,
line_items: [
{
price_data: {
currency: 'USD',
product_data: {
name: 'Your SaaS Subscription Name',
description: 'Saas Subscription Description'
},
// cost (change this to the price of your product)
unit_amount: 899,
recurring: {
interval: 'month'
}
},
quantity: 1
}
],
metadata: {
userId: user.user.id
}
})
return new NextResponse(JSON.stringify({ url: stripeSession.url }))
} catch (error) {
console.log('[STRIPE_GET]', error)
return new NextResponse('Internal Error', { status: 500 })
}
}

47
app/api/emails/route.ts Normal file
View file

@ -0,0 +1,47 @@
import { Resend } from "resend"
import RepoEmail from "@/components/email/email";
import { render } from "@react-email/render"
import { NextResponse } from 'next/server';
const resend = new Resend(process.env.RESEND_API_KEY);
export async function POST(request: Request) {
try {
const { email } = await request.json();
const { data, error } = await resend.emails.send({
from: "Nizzy <noreply@nizzystarter.com>",
to: [email],
subject: "Nizzy Starter Kit",
html: render(RepoEmail()),
});
if (error) {
return new NextResponse(JSON.stringify(error), {
status: 500,
headers: {
'Content-Type': 'application/json',
},
});
}
await resend.contacts.create({
email: email,
audienceId: process.env.RESEND_AUDIENCE as string
});
return new NextResponse(JSON.stringify({ message: "Email sent successfully" }), {
status: 200,
headers: {
'Content-Type': 'application/json',
},
});
} catch (error:any) {
return new NextResponse(JSON.stringify({ error: error.message }), {
status: 500,
headers: {
'Content-Type': 'application/json',
},
});
}
}

96
app/api/webhook/route.ts Normal file
View file

@ -0,0 +1,96 @@
import Stripe from 'stripe'
import { headers } from 'next/headers'
import { NextResponse } from 'next/server'
import { db } from '@/lib/db'
import { stripe } from '@/lib/stripe'
export async function POST(req: Request) {
const body = await req.text()
const signature = headers().get('Stripe-Signature') as string
let event: Stripe.Event
try {
event = stripe.webhooks.constructEvent(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET!
)
} catch (error: any) {
return new NextResponse(`Webhook Error: ${error.message}`, { status: 400 })
}
const session = event.data.object as Stripe.Checkout.Session
// checkout.session.completed is sent after the initial purchase of the subscription from the checkout page
if (event.type === 'checkout.session.completed') {
const subscription = await stripe.subscriptions.retrieve(
session.subscription as string
)
if (!session?.metadata?.userId) {
return new NextResponse('No user id found', { status: 400 })
}
await db.userSubscription.create({
data: {
userId: session.metadata.userId,
stripeSubscriptionId: subscription.id,
stripeCustomerId: subscription.customer as string,
stripePriceId: subscription.items.data[0].price.id,
stripeCurrentPeriodEnd: new Date(subscription.current_period_end * 1000)
}
})
}
// invoice.payment_succeeded is sent on subscription renewals
if (event.type === "invoice.payment_succeeded") {
// note: sometimes the subscription we get back doesn't have the up to date current_period_end
// which is why we also need to listen for customer.subscription.updated
const subscription = await stripe.subscriptions.retrieve(
session.subscription as string
);
await db.userSubscription.update({
where: {
stripeSubscriptionId: subscription.id
},
data: {
stripePriceId: subscription.items.data[0].price.id,
stripeCurrentPeriodEnd: new Date(subscription.current_period_end * 1000)
}
})
}
// customer.subscription.updated is fired when their subscription end date changes
if (event.type === 'customer.subscription.updated') {
const subscriptionId = event.data.object.id as string;
await db.userSubscription.update({
where: {
stripeSubscriptionId: subscriptionId
},
data: {
stripeCurrentPeriodEnd: new Date(event.data.object.current_period_end * 1000)
}
})
}
// invoice.payment_failed if the renewal fails
if (event.type === 'invoice.payment_failed') {
const subscription = await stripe.subscriptions.retrieve(
session.subscription as string
)
await db.userSubscription.update({
where: {
stripeSubscriptionId: subscription.id
},
data: {
stripePriceId: subscription.items.data[0].price.id,
stripeCurrentPeriodEnd: new Date(subscription.current_period_end * 1000)
}
})
}
return new NextResponse(null, { status: 200 })
}