mirror of
https://github.com/arthur-pbty/binouz.git
synced 2026-06-03 23:36:27 +02:00
b7010a1704
- Implemented AuthButton component for Discord sign-in and sign-out functionality. - Created CopyButton component for copying server IP addresses. - Developed EventCard and GradeCard components for displaying events and grades. - Added Footer and Navbar components for site navigation and information. - Introduced PurchaseButton for handling grade purchases with Stripe integration. - Created SectionHeader component for consistent section titles. - Implemented session management with SessionProvider for NextAuth. - Set up PostgreSQL database with Docker and Prisma for data management. - Added admin guard functionality to restrict access to certain routes. - Configured NextAuth with Discord provider for user authentication. - Defined Prisma schema for user, admin, grade, event, and purchase models. - Seeded database with initial grades and events data. - Added SVG hero image for the landing page. - Extended NextAuth types to include additional user properties.
98 lines
2.5 KiB
TypeScript
98 lines
2.5 KiB
TypeScript
import Stripe from "stripe";
|
|
import { db } from "@/lib/db";
|
|
import { fallbackGrades } from "@/lib/site";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
const stripeSecretKey = process.env.STRIPE_SECRET_KEY;
|
|
const stripe = stripeSecretKey
|
|
? new Stripe(stripeSecretKey, {
|
|
apiVersion: "2023-10-16",
|
|
})
|
|
: null;
|
|
|
|
const minecraftNameRegex = /^[A-Za-z0-9_]{3,16}$/;
|
|
|
|
type GradeInfo = {
|
|
id: string;
|
|
name: string;
|
|
price: number;
|
|
description: string;
|
|
};
|
|
|
|
const getGradeById = async (gradeId: string): Promise<GradeInfo | null> => {
|
|
try {
|
|
const grade = await db.grade.findUnique({ where: { id: gradeId } });
|
|
if (grade) {
|
|
return {
|
|
id: grade.id,
|
|
name: grade.name,
|
|
price: grade.price,
|
|
description: grade.description,
|
|
};
|
|
}
|
|
} catch {
|
|
// ignore db errors and fall back to static data
|
|
}
|
|
|
|
return fallbackGrades.find((grade) => grade.id === gradeId) ?? null;
|
|
};
|
|
|
|
export async function POST(request: Request) {
|
|
const body = await request.json().catch(() => ({}));
|
|
const gradeId = body?.gradeId?.toString();
|
|
const minecraftUsername = body?.minecraftUsername?.toString().trim();
|
|
|
|
if (!gradeId || !minecraftUsername) {
|
|
return Response.json({ error: "Missing checkout details." }, { status: 400 });
|
|
}
|
|
|
|
if (!minecraftNameRegex.test(minecraftUsername)) {
|
|
return Response.json(
|
|
{ error: "Invalid Minecraft username." },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
const grade = await getGradeById(gradeId);
|
|
if (!grade) {
|
|
return Response.json({ error: "Grade not found." }, { status: 404 });
|
|
}
|
|
|
|
if (!stripe) {
|
|
return Response.json({ error: "Stripe not configured." }, { status: 500 });
|
|
}
|
|
|
|
const baseUrl = process.env.NEXTAUTH_URL ?? "http://localhost:3000";
|
|
const session = await stripe.checkout.sessions.create({
|
|
mode: "payment",
|
|
payment_method_types: ["card"],
|
|
success_url: `${baseUrl}/?checkout=success`,
|
|
cancel_url: `${baseUrl}/?checkout=cancel`,
|
|
line_items: [
|
|
{
|
|
price_data: {
|
|
currency: "eur",
|
|
unit_amount: Math.round(grade.price * 100),
|
|
product_data: {
|
|
name: grade.name,
|
|
description: grade.description,
|
|
},
|
|
},
|
|
quantity: 1,
|
|
},
|
|
],
|
|
metadata: {
|
|
gradeId: grade.id,
|
|
minecraftUsername,
|
|
},
|
|
client_reference_id: minecraftUsername,
|
|
});
|
|
|
|
if (!session.url) {
|
|
return Response.json({ error: "Stripe session failed." }, { status: 500 });
|
|
}
|
|
|
|
return Response.json({ url: session.url });
|
|
}
|