Skip to content

Commit

Permalink
Added user registration and Course purchase system
Browse files Browse the repository at this point in the history
  • Loading branch information
dhruvilmehta committed May 21, 2024
1 parent 308b8df commit 59c18e5
Show file tree
Hide file tree
Showing 17 changed files with 866 additions and 62 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"nextjs-toploader": "^1.6.11",
"node-fetch": "^3.3.2",
"notion-client": "^6.16.0",
"razorpay": "^2.9.2",
"pdf-lib": "^1.17.1",
"react": "^18",
"react-dom": "^18",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
-- AlterTable
ALTER TABLE "Course" ADD COLUMN "price" INTEGER NOT NULL DEFAULT 1000;

-- CreateTable
CREATE TABLE "Receipt" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"courseId" INTEGER NOT NULL DEFAULT 0,
"razorpayOrderId" TEXT,

CONSTRAINT "Receipt_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "Purchase" (
"id" TEXT NOT NULL,
"receiptId" TEXT NOT NULL,
"razorpay_payment_id" TEXT NOT NULL,
"razorpay_order_id" TEXT NOT NULL,
"razorpay_signature" TEXT NOT NULL,
"purchasedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"paymentVerified" BOOLEAN NOT NULL DEFAULT false,
"purchasedCourseId" INTEGER NOT NULL,
"purchasedById" TEXT NOT NULL,

CONSTRAINT "Purchase_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "Receipt_razorpayOrderId_key" ON "Receipt"("razorpayOrderId");

-- CreateIndex
CREATE UNIQUE INDEX "Purchase_receiptId_key" ON "Purchase"("receiptId");

-- CreateIndex
CREATE UNIQUE INDEX "Purchase_razorpay_payment_id_key" ON "Purchase"("razorpay_payment_id");

-- CreateIndex
CREATE UNIQUE INDEX "Purchase_razorpay_order_id_key" ON "Purchase"("razorpay_order_id");

-- AddForeignKey
ALTER TABLE "Receipt" ADD CONSTRAINT "Receipt_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "Receipt" ADD CONSTRAINT "Receipt_courseId_fkey" FOREIGN KEY ("courseId") REFERENCES "Course"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "Purchase" ADD CONSTRAINT "Purchase_receiptId_fkey" FOREIGN KEY ("receiptId") REFERENCES "Receipt"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "Purchase" ADD CONSTRAINT "Purchase_purchasedCourseId_fkey" FOREIGN KEY ("purchasedCourseId") REFERENCES "Course"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "Purchase" ADD CONSTRAINT "Purchase_purchasedById_fkey" FOREIGN KEY ("purchasedById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
132 changes: 82 additions & 50 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ datasource db {

model Course {
id Int @id @default(autoincrement())
price Int @default(1000)
appxCourseId Int
discordRoleId String
title String
Expand All @@ -19,8 +20,10 @@ model Course {
slug String
content CourseContent[]
purchasedBy UserPurchases[]
purchases Purchase[]
receipts Receipt[]
certificate Certificate[]
certIssued Boolean @default(false)
certIssued Boolean @default(false)
}

model UserPurchases {
Expand Down Expand Up @@ -64,12 +67,12 @@ model CourseContent {
}

model Certificate {
id String @id @default(cuid())
slug String @default("certId")
user User @relation(fields: [userId], references: [id])
userId String
course Course @relation(fields: [courseId], references: [id])
courseId Int
id String @id @default(cuid())
slug String @default("certId")
user User @relation(fields: [userId], references: [id])
userId String
course Course @relation(fields: [courseId], references: [id])
courseId Int
@@unique([userId, courseId])
}
Expand Down Expand Up @@ -147,6 +150,33 @@ model User {
questions Question[]
answers Answer[]
certificate Certificate[]
transactions Purchase[]
receipts Receipt[]
}

model Receipt {
id String @id @default(cuid())
user User @relation(fields: [userId], references: [id])
userId String
course Course @relation(fields: [courseId], references: [id])
courseId Int @default(0)
razorpayOrderId String? @unique
purchase Purchase?
}

model Purchase {
id String @id @default(cuid())
receipt Receipt @relation(fields: [receiptId], references: [id])
receiptId String @unique
razorpay_payment_id String @unique
razorpay_order_id String @unique
razorpay_signature String
purchasedAt DateTime @default(now())
paymentVerified Boolean @default(false)
purchasedCourse Course @relation(fields: [purchasedCourseId], references: [id])
purchasedCourseId Int
purchasedBy User @relation(fields: [purchasedById], references: [id])
purchasedById String
}

model DiscordConnect {
Expand Down Expand Up @@ -206,74 +236,76 @@ model Comment {
votes Vote[]
isPinned Boolean @default(false)
}

model Question {
id Int @id @default(autoincrement())
title String
content String
slug String @unique
createdAt DateTime @default(now())
author User @relation(fields: [authorId], references: [id])
authorId String
upvotes Int @default(0)
downvotes Int @default(0)
id Int @id @default(autoincrement())
title String
content String
slug String @unique
createdAt DateTime @default(now())
author User @relation(fields: [authorId], references: [id])
authorId String
upvotes Int @default(0)
downvotes Int @default(0)
totalanswers Int @default(0)
answers Answer[]
votes Vote[]
tags String[]
updatedAt DateTime @updatedAt
answers Answer[]
votes Vote[]
tags String[]
updatedAt DateTime @updatedAt
@@index([authorId])
}

model Answer {
id Int @id @default(autoincrement())
content String
createdAt DateTime @default(now())
question Question @relation(fields: [questionId], references: [id])
questionId Int
author User @relation(fields: [authorId], references: [id])
authorId String
votes Vote[]
upvotes Int @default(0)
downvotes Int @default(0)
totalanswers Int @default(0)
parentId Int?
responses Answer[] @relation("AnswerToAnswer")
parent Answer? @relation("AnswerToAnswer", fields: [parentId], references: [id])
updatedAt DateTime @updatedAt
id Int @id @default(autoincrement())
content String
createdAt DateTime @default(now())
question Question @relation(fields: [questionId], references: [id])
questionId Int
author User @relation(fields: [authorId], references: [id])
authorId String
votes Vote[]
upvotes Int @default(0)
downvotes Int @default(0)
totalanswers Int @default(0)
parentId Int?
responses Answer[] @relation("AnswerToAnswer")
parent Answer? @relation("AnswerToAnswer", fields: [parentId], references: [id])
updatedAt DateTime @updatedAt
@@index([questionId])
@@index([authorId])
@@index([parentId])
}


model Vote {
id Int @id @default(autoincrement())
questionId Int?
question Question? @relation(fields: [questionId], references: [id])
answerId Int?
answer Answer? @relation(fields: [answerId], references: [id])
commentId Int?
comment Comment? @relation(fields: [commentId], references: [id])
userId String
user User @relation(fields: [userId], references: [id])
voteType VoteType
createdAt DateTime @default(now())
id Int @id @default(autoincrement())
questionId Int?
question Question? @relation(fields: [questionId], references: [id])
answerId Int?
answer Answer? @relation(fields: [answerId], references: [id])
commentId Int?
comment Comment? @relation(fields: [commentId], references: [id])
userId String
user User @relation(fields: [userId], references: [id])
voteType VoteType
createdAt DateTime @default(now())
@@unique([questionId, userId])
@@unique([answerId, userId])
@@unique([commentId, userId])
@@unique([questionId, userId])
@@unique([answerId, userId])
@@unique([commentId, userId])
}

enum VoteType {
UPVOTE
DOWNVOTE
}

enum PostType {
QUESTION
ANSWER
}

enum CommentType {
INTRO
DEFAULT
Expand Down
36 changes: 36 additions & 0 deletions src/app/api/auth/register/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { NextRequest, NextResponse } from 'next/server';
import db from '@/db';
import bcrypt from 'bcrypt';
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';

export async function POST(req: NextRequest) {
try {
const { name, email, password } = await req.json();

const hashedPassword = await bcrypt.hash(password, 10);
await db.user.create({
data: {
email,
name,
password: hashedPassword,
},
});

return NextResponse.json(
{ message: 'Account created sucessfully' },
{ status: 201 },
);
} catch (e) {
if (e instanceof PrismaClientKnownRequestError) {
return NextResponse.json(
{ error: 'Email already taken.' },
{ status: 400 },
);
}
console.log(e);
return NextResponse.json(
{ error: 'Failed to parse JSON input' },
{ status: 400 },
);
}
}
83 changes: 83 additions & 0 deletions src/app/api/razorpay/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { NextRequest, NextResponse } from 'next/server';
import Razorpay from 'razorpay';
import db from '@/db';
import { z } from 'zod';

const schema = z.object({
courseId: z.number(),
userId: z.string(),
});

const razorpay = new Razorpay({
key_id: process.env.RAZORPAY_KEY_ID!,
key_secret: process.env.RAZORPAY_SECRET!,
});

export async function POST(req: NextRequest) {
const reqBody = await req.json();
const body = schema.safeParse(reqBody);

if (!body.success) {
return NextResponse.json(
{
error: 'Error parsing the body of the request',
},
{
status: 422,
},
);
}

const course = await db.course.findFirst({
where: {
id: body.data.courseId,
},
});

if (!course)
return NextResponse.json({ error: 'Course Not Found' }, { status: 404 });

const receipt = await db.receipt.create({
data: {
userId: body.data.userId,
courseId: body.data.courseId,
},
});

const payment_capture = 1;
const amount = course.price;
const options = {
amount: (amount * 100).toString(),
currency: 'INR',
receipt: receipt.id,
payment_capture,
notes: {
userId: body.data.userId,
courseId: body.data.courseId,
receipt: receipt.id,
},
};

try {
const response = await razorpay.orders.create(options);

await db.receipt.update({
where: {
id: receipt.id,
},
data: {
razorpayOrderId: response.id,
},
});

return NextResponse.json({
id: response.id,
currency: response.currency,
amount: response.amount,
razorPayKey: process.env.RAZORPAY_KEY_ID,
});
} catch (err) {
console.log(err);
return NextResponse.json(err);
}
}
Loading

0 comments on commit 59c18e5

Please sign in to comment.