Skip to content

Commit 091ce6e

Browse files
committed
fix: loop and add enums in db
1 parent e4884a7 commit 091ce6e

File tree

7 files changed

+105
-22
lines changed

7 files changed

+105
-22
lines changed

apps/api/prisma/migrations/20251030121123_add_payment_constraints/migration.sql

Whitespace-only changes.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
Warnings:
3+
4+
- Changed the type of `status` on the `Payment` table. No cast exists, the column would be dropped and recreated, which cannot be done if there is data, since the column is required.
5+
- Changed the type of `status` on the `Subscription` table. No cast exists, the column would be dropped and recreated, which cannot be done if there is data, since the column is required.
6+
7+
*/
8+
-- CreateEnum
9+
CREATE TYPE "PaymentStatus" AS ENUM ('created', 'authorized', 'captured', 'refunded', 'failed');
10+
11+
-- CreateEnum
12+
CREATE TYPE "SubscriptionStatus" AS ENUM ('created', 'authenticated', 'active', 'pending', 'halted', 'cancelled', 'completed', 'expired');
13+
14+
-- AlterTable
15+
ALTER TABLE "Payment" DROP COLUMN "status",
16+
ADD COLUMN "status" "PaymentStatus" NOT NULL;
17+
18+
-- AlterTable
19+
ALTER TABLE "Subscription" DROP COLUMN "status",
20+
ADD COLUMN "status" "SubscriptionStatus" NOT NULL,
21+
ALTER COLUMN "endDate" DROP NOT NULL;

apps/api/prisma/schema.prisma

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,25 @@ model QueryCount {
1212
total_queries BigInt
1313
}
1414

15+
enum PaymentStatus {
16+
created
17+
authorized
18+
captured
19+
refunded
20+
failed
21+
}
22+
23+
enum SubscriptionStatus {
24+
created
25+
authenticated
26+
active
27+
pending
28+
halted
29+
cancelled
30+
completed
31+
expired
32+
}
33+
1534
model User {
1635
id String @id @default(cuid())
1736
email String @unique
@@ -48,9 +67,9 @@ model Payment {
4867
subscriptionId String?
4968
razorpayPaymentId String @unique
5069
razorpayOrderId String
51-
amount Int
70+
amount Int // Amount in paise (smallest currency unit)
5271
currency String @default("INR")
53-
status String
72+
status PaymentStatus
5473
createdAt DateTime @default(now())
5574
updatedAt DateTime @updatedAt
5675
subscription Subscription? @relation(fields: [subscriptionId], references: [id])
@@ -61,9 +80,9 @@ model Subscription {
6180
id String @id @default(cuid())
6281
userId String
6382
planId String
64-
status String
83+
status SubscriptionStatus
6584
startDate DateTime @default(now())
66-
endDate DateTime
85+
endDate DateTime?
6786
autoRenew Boolean @default(true)
6887
payments Payment[]
6988
plan Plan @relation(fields: [planId], references: [id], onDelete: Cascade)

apps/api/src/clients/razorpay.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import Razorpay from "razorpay";
2+
3+
const RAZORPAY_KEY_ID = process.env.RAZORPAY_KEY_ID;
4+
const RAZORPAY_KEY_SECRET = process.env.RAZORPAY_KEY_SECRET;
5+
6+
if (!RAZORPAY_KEY_ID) {
7+
throw new Error(
8+
"RAZORPAY_KEY_ID is required but not set in environment variables. Please configure it in your .env file."
9+
);
10+
}
11+
12+
if (!RAZORPAY_KEY_SECRET) {
13+
throw new Error(
14+
"RAZORPAY_KEY_SECRET is required but not set in environment variables. Please configure it in your .env file."
15+
);
16+
}
17+
18+
export const rz_instance = new Razorpay({
19+
key_id: RAZORPAY_KEY_ID,
20+
key_secret: RAZORPAY_KEY_SECRET,
21+
});

apps/api/src/index.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import type { CorsOptions as CorsOptionsType } from "cors";
1010
import rateLimit from "express-rate-limit";
1111
import helmet from "helmet";
1212
import ipBlocker from "./middleware/ipBlock.js";
13-
import Razorpay from "razorpay";
1413
import crypto from "crypto";
1514
import { paymentService } from "./services/payment.service.js";
1615
import { verifyToken } from "./utils/auth.js";
@@ -23,11 +22,6 @@ const CORS_ORIGINS = process.env.CORS_ORIGINS
2322
? process.env.CORS_ORIGINS.split(",")
2423
: ["http://localhost:3000", "http://localhost:5000"];
2524

26-
export const rz_instance = new Razorpay({
27-
key_id: process.env.RAZORPAY_KEY_ID!,
28-
key_secret: process.env.RAZORPAY_KEY_SECRET!,
29-
});
30-
3125
// Security headers
3226
app.use(helmet());
3327
app.use(

apps/api/src/services/payment.service.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { rz_instance } from "../index.js";
1+
import { rz_instance } from "../clients/razorpay.js";
22
import crypto from "crypto";
33
import prismaModule from "../prisma.js";
44

@@ -43,7 +43,7 @@ type CreateOrderResponse = RazorpayOrderSuccess | RazorpayError;
4343
interface PaymentData {
4444
razorpayPaymentId: string;
4545
razorpayOrderId: string;
46-
amount: number;
46+
amount: number; // Amount in paise (smallest currency unit)
4747
currency: string;
4848
}
4949

@@ -154,7 +154,7 @@ export const paymentService = {
154154
userId,
155155
razorpayPaymentId: paymentData.razorpayPaymentId,
156156
razorpayOrderId: paymentData.razorpayOrderId,
157-
amount: paymentData.amount,
157+
amount: paymentData.amount, // Amount in paise (smallest currency unit)
158158
currency: paymentData.currency,
159159
status: "captured",
160160
},

apps/web/src/components/payment/PaymentFlow.tsx

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const PaymentFlow: React.FC<PaymentFlowProps> = ({
4242
buttonText = "Invest",
4343
buttonClassName,
4444
}) => {
45-
const { data: session } = useSession();
45+
const { data: session, status: sessionStatus } = useSession();
4646
const router = useRouter();
4747
const [isProcessing, setIsProcessing] = useState(false);
4848
const orderDataRef = useRef<{
@@ -89,9 +89,11 @@ const PaymentFlow: React.FC<PaymentFlowProps> = ({
8989

9090
const handlePayment = async () => {
9191
try {
92-
// Check if user is logged in
93-
if (!session) {
94-
// Redirect to login with return URL to come back to pricing
92+
if (sessionStatus === "loading") {
93+
return;
94+
}
95+
96+
if (sessionStatus === "unauthenticated" || !session) {
9597
router.push("/login?callbackUrl=/pricing");
9698
return;
9799
}
@@ -143,20 +145,46 @@ const PaymentFlow: React.FC<PaymentFlowProps> = ({
143145
};
144146

145147
await initiatePayment(options);
146-
} catch (error) {
148+
} catch (error: any) {
147149
console.warn("Failed to create order:", error);
148-
router.push("/login?callbackUrl=/pricing");
149150
setIsProcessing(false);
151+
152+
// Only redirect to login if it's an authentication error
153+
const isAuthError =
154+
error?.data?.code === "UNAUTHORIZED" ||
155+
error?.message?.includes("UNAUTHORIZED") ||
156+
error?.message?.includes("Missing or invalid authorization") ||
157+
error?.message?.includes("Invalid or expired token");
158+
159+
if (isAuthError) {
160+
router.push("/login?callbackUrl=/pricing");
161+
} else {
162+
// For other errors (network, validation, etc.), show user-friendly message
163+
alert(
164+
"Failed to create payment order. Please try again or contact support if the issue persists."
165+
);
166+
}
150167
}
151168
};
152169

170+
// Show loading state while session is being determined
171+
const isButtonDisabled =
172+
isProcessing || isLoading || sessionStatus === "loading";
173+
174+
const buttonTextDisplay =
175+
sessionStatus === "loading"
176+
? "Loading..."
177+
: isProcessing || isLoading
178+
? "Processing..."
179+
: buttonText;
180+
153181
return (
154182
<div className="flex flex-col gap-2">
155183
<PrimaryButton
156-
classname={`${buttonClassName || "w-full"} ${isProcessing || isLoading ? "opacity-50 cursor-not-allowed" : ""}`}
157-
onClick={isProcessing || isLoading ? undefined : handlePayment}
184+
classname={`${buttonClassName || "w-full"} ${isButtonDisabled ? "opacity-50 cursor-not-allowed" : ""}`}
185+
onClick={isButtonDisabled ? undefined : handlePayment}
158186
>
159-
{isProcessing || isLoading ? "Processing..." : buttonText}
187+
{buttonTextDisplay}
160188
</PrimaryButton>
161189
{error && <p className="text-sm text-red-500">Payment error: {error}</p>}
162190
</div>

0 commit comments

Comments
 (0)