Skip to content

Commit 98375b3

Browse files
committed
seller onboard setup completed
1 parent 7a078cd commit 98375b3

11 files changed

Lines changed: 445 additions & 14 deletions

File tree

.env

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@ SMTP_USER= "srihari6281@gmail.com"
1010
SMTP_PASS= "npva kjya qgig limk"
1111

1212
JWT_ACCESS_SECRET = "86b9c4ad75ca4fdcd7f0802f02726b8d09d0a11cc0bc25aebca6ede3a03fe8e5"
13-
JWT_REFRESH_SECRET = "e8765102d2990ab6883d7637d70a14d4b39800b11ebf2896daee122e3376caf8"
13+
JWT_REFRESH_SECRET = "e8765102d2990ab6883d7637d70a14d4b39800b11ebf2896daee122e3376caf8"
14+
15+
STRIPE_SECRET_KEY = "sk_test_51PXzbSSGnmKVDMGjgzYASGjq7ktUKr0moWbKGzm9cB1EzYl196t4LRvgU9jrI2IE5JhpLKQos2lEHPAIdQGJMGVq00VNk2qv95"

apps/auth-service/src/controller/auth.controller.ts

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ import { AuthenticationError, ValidationError } from "@packages/error-handler";
1313
import bcrypt from "bcryptjs";
1414
import jwt from "jsonwebtoken";
1515
import { setCookie } from "../utils/cookies/setCookie";
16+
import { Stripe } from "stripe";
17+
18+
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
19+
apiVersion: "2025-12-15.clover",
20+
});
1621

1722
//new user registration
1823
export const userRegistration = async (
@@ -364,3 +369,126 @@ export const createShop = async (
364369
return next(error);
365370
}
366371
};
372+
373+
//create a stripe connect link
374+
375+
export const createStripeConnectLink = async (
376+
req: Request,
377+
res: Response,
378+
next: NextFunction
379+
) => {
380+
try {
381+
const { sellerId } = req.body;
382+
console.log(req.body, "req.body");
383+
if (!sellerId) {
384+
return next(new ValidationError("Seller ID is required"));
385+
}
386+
const seller = await prisma.sellers.findUnique({
387+
where: { id: sellerId },
388+
});
389+
if (!seller) {
390+
return next(new ValidationError("Seller not found"));
391+
}
392+
393+
//create stripe connect account
394+
const account = await stripe.accounts.create({
395+
type: "express",
396+
country: "IN",
397+
email: seller.email,
398+
business_type: "individual",
399+
capabilities: {
400+
card_payments: { requested: true },
401+
transfers: { requested: true },
402+
},
403+
});
404+
await prisma.sellers.update({
405+
where: { id: sellerId },
406+
data: { stripeId: account.id },
407+
});
408+
409+
const accountLink = await stripe.accountLinks.create({
410+
account: account.id,
411+
refresh_url: `http://localhost:3000/success`,
412+
return_url: `http://localhost:3000/success`,
413+
type: "account_onboarding",
414+
});
415+
416+
res.status(200).json({
417+
success: true,
418+
url: accountLink.url,
419+
});
420+
} catch (error) {
421+
return next(error);
422+
}
423+
};
424+
425+
//Seller Login
426+
427+
export const loginSeller = async (
428+
req: Request,
429+
res: Response,
430+
next: NextFunction
431+
) => {
432+
try {
433+
const { email, password } = req.body;
434+
if (!email || !password) {
435+
return next(new ValidationError("Email and password are required"));
436+
}
437+
const seller = await prisma.sellers.findUnique({
438+
where: { email },
439+
});
440+
if (!seller) {
441+
return next(new AuthenticationError("Seller not found"));
442+
}
443+
//verify password
444+
const isMatch = await bcrypt.compare(password, seller.password!);
445+
if (!isMatch) {
446+
return next(new AuthenticationError("Invalid credentials"));
447+
}
448+
//Generate Access Token and Refresh Token
449+
const accessToken = jwt.sign(
450+
{ id: seller.id, role: "seller" },
451+
process.env.JWT_ACCESS_SECRET! as string,
452+
{ expiresIn: "15m" }
453+
);
454+
const refreshToken = jwt.sign(
455+
{ id: seller.id, role: "seller" },
456+
process.env.JWT_REFRESH_SECRET! as string,
457+
{ expiresIn: "7d" }
458+
);
459+
460+
//store refresh token and access token in httpOnly cookies
461+
setCookie(res, "sellerAccessToken", accessToken);
462+
setCookie(res, "sellerRefreshToken", refreshToken);
463+
464+
res.status(200).json({
465+
success: true,
466+
message: "Login successful",
467+
468+
user: {
469+
id: seller.id,
470+
name: seller.name,
471+
email: seller.email,
472+
},
473+
});
474+
} catch (error) {
475+
return next(error);
476+
}
477+
};
478+
479+
//get loggedin seller info
480+
export const getSeller = async (
481+
req: any,
482+
res: Response,
483+
next: NextFunction
484+
) => {
485+
try {
486+
const seller = req.seller;
487+
res.status(201).json({
488+
success: true,
489+
seller,
490+
});
491+
} catch (error) {
492+
next(error);
493+
}
494+
};

apps/auth-service/src/routes/auth.router.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import express, { Router } from "express";
22
import {
33
createShop,
4+
createStripeConnectLink,
5+
getSeller,
46
getUser,
7+
loginSeller,
58
loginUser,
69
refreshToken,
710
registerSeller,
@@ -13,6 +16,7 @@ import {
1316
verifyUserOtp,
1417
} from "../controller/auth.controller";
1518
import isAuthenticated from "@packages/middleware/isAuthenticated";
19+
import { isSeller } from "@packages/middleware/authorizeRoles";
1620

1721
const router: Router = express.Router();
1822

@@ -29,5 +33,8 @@ router.post("/verify-forgot-password-user", verifyUserForgotPassword);
2933
router.post("/seller-registration", registerSeller);
3034
router.post("/verify-seller", verifySellerOtp);
3135
router.post("/create-shop", createShop);
36+
router.post("/create-stripe-link", createStripeConnectLink);
37+
router.post("/login-seller", loginSeller);
38+
router.get("/logged-in-seller", isAuthenticated, isSeller, getSeller);
3239

3340
export default router;

apps/seller-ui/src/app/(routes)/signup/page.tsx

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { Eye, EyeOff } from "lucide-react";
77
import { useMutation } from "@tanstack/react-query";
88
import axios, { AxiosError } from "axios";
99
import countries from "apps/seller-ui/src/utils/countries";
10+
import CreateShop from "../../shared/modules/auth/create-shop";
11+
import StripeLogo from "apps/seller-ui/src/assets/svgs/stripe-logo";
1012

1113
// type FormData = {
1214
// name: string;
@@ -17,7 +19,7 @@ import countries from "apps/seller-ui/src/utils/countries";
1719
const Signup = () => {
1820
const [passwordVisible, setpasswordVisible] = useState(false);
1921
// const [serverError, setServerError] = useState<string | null>(null);
20-
const [activeStep, setActiveStep] = useState(1);
22+
const [activeStep, setActiveStep] = useState(3);
2123
const [timer, setTimer] = useState(60);
2224
const [showOtp, setShowOtp] = useState(false);
2325
const [canResend, setCanResend] = useState(true);
@@ -26,8 +28,6 @@ const Signup = () => {
2628
const [sellerId, setSellerId] = useState("");
2729
const inputRefs = useRef<(HTMLInputElement | null)[]>([]);
2830

29-
const router = useRouter();
30-
3131
const {
3232
register,
3333
handleSubmit,
@@ -77,7 +77,7 @@ const Signup = () => {
7777
return response.data;
7878
},
7979
onSuccess: (data) => {
80-
setSellerId(data?.sellerId);
80+
setSellerId(data?.seller.id);
8181
setActiveStep(2);
8282
},
8383
});
@@ -115,6 +115,21 @@ const Signup = () => {
115115
}
116116
};
117117

118+
const connectStripe = async () => {
119+
try {
120+
console.log(sellerId, "SellerId");
121+
const response = await axios.post(
122+
`${process.env.NEXT_PUBLIC_SERVER_URI}/api/create-stripe-link`,
123+
sellerId
124+
);
125+
if (response.data.url) {
126+
window.location.href = response.data.url;
127+
}
128+
} catch (error) {
129+
console.error("Error connecting to Stripe:", error);
130+
}
131+
};
132+
118133
return (
119134
<div className="w-full flex flex-col items-center pt-10 min-h-screen">
120135
{/* {stepper} */}
@@ -324,6 +339,21 @@ const Signup = () => {
324339
)}
325340
</>
326341
)}
342+
{activeStep === 2 && (
343+
<CreateShop sellerId={sellerId} setActiveStep={setActiveStep} />
344+
)}
345+
{activeStep === 3 && (
346+
<div className="text-center">
347+
<h3 className="text-2xl font-semibold">Withdraw Method</h3>
348+
<br />
349+
<button
350+
onClick={connectStripe}
351+
className="w-full m-auto flex items-center justify-center gap-3 text-lg bg-[#334155] text-white py-2 rounded-lg"
352+
>
353+
Connect <StripeLogo />
354+
</button>
355+
</div>
356+
)}
327357
</div>
328358
</div>
329359
);

0 commit comments

Comments
 (0)