From d1d0d378e348411d6853e0b6ddaa87c1b60de2a1 Mon Sep 17 00:00:00 2001 From: prophet7821 Date: Fri, 13 Sep 2024 20:19:25 +0530 Subject: [PATCH] app up --- .env.example | 5 -- package.json | 1 + src/app.module.ts | 7 ++ src/auth/auth.controller.ts | 9 +-- src/auth/auth.service.ts | 6 +- src/cars/cars.service.ts | 8 +-- src/main.ts | 6 +- src/order/order.controller.ts | 51 ++++++------- src/order/order.module.ts | 40 ++++++----- src/order/order.schema.ts | 70 +++++++++--------- src/order/order.service.ts | 73 ++++++------------- src/payments/payments.controller.ts | 31 ++++++-- src/payments/payments.module.ts | 2 + src/payments/payments.schema.ts | 8 +-- src/payments/payments.service.ts | 106 +++++++++++++++++----------- src/types/loginDto.dto.ts | 0 src/users/users.service.ts | 4 +- 17 files changed, 212 insertions(+), 215 deletions(-) delete mode 100644 .env.example create mode 100644 src/types/loginDto.dto.ts diff --git a/.env.example b/.env.example deleted file mode 100644 index 7f25c8a..0000000 --- a/.env.example +++ /dev/null @@ -1,5 +0,0 @@ -MONGO_URI = -STRIPE_SECRET = -STRIPE_API_VERSION = -JWT_SECRET = -JWT_EXPIRES_IN = \ No newline at end of file diff --git a/package.json b/package.json index e03ea59..60a8f7f 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@nestjs/common": "^9.0.0", "@nestjs/config": "^3.1.1", "@nestjs/core": "^9.0.0", + "@nestjs/event-emitter": "^2.0.3", "@nestjs/jwt": "^10.1.0", "@nestjs/mapped-types": "^2.0.2", "@nestjs/mongoose": "^10.0.1", diff --git a/src/app.module.ts b/src/app.module.ts index 8864aa5..40b4a18 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -8,13 +8,20 @@ import { ConfigModule, ConfigService } from "@nestjs/config"; import * as cors from "cors"; // import { OrderModule } from "./order/order.module"; import { StripeModule } from "./stripe/stripe.module"; +import { OrderModule } from "./order/order.module"; +import { EventEmitterModule } from "@nestjs/event-emitter"; @Module({ imports: [ CarsModule, AuthModule, PaymentsModule, + OrderModule, ConfigModule.forRoot(), + EventEmitterModule.forRoot({ + wildcard: true, + delimiter: ".", + }), MongooseModule.forRootAsync({ imports: [ConfigModule], useFactory: async (configService: ConfigService) => ({ diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index 668e825..a11c91f 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -9,7 +9,6 @@ import { UseInterceptors, } from "@nestjs/common"; import { AuthService } from "./auth.service"; -import { UsersService } from "../users/users.service"; import { User } from "../users/users.schema"; import MongooseClassSerializerInterceptor from "../interceptors/mongooseClassSerializer.interceptor"; import { JwtAuthGuard } from "../guards/auth.guard"; @@ -19,13 +18,9 @@ import { User as UserDecorator } from "../decorator/user.decorator"; path: "auth", version: "1", }) - @UseInterceptors(MongooseClassSerializerInterceptor(User)) export class AuthController { - constructor( - private authService: AuthService, - private usersService: UsersService - ) {} + constructor(private authService: AuthService) {} @Post("login") async login(@Body() loginData: { email: string; password: string }) { @@ -39,7 +34,7 @@ export class AuthController { @Post("signup") async signUp(@Body() userData: User) { - return await this.usersService.createUser(userData); + return await this.authService.createUser(userData); } @Get("profile") diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 7741a5c..92e8299 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -3,7 +3,7 @@ import { Injectable } from "@nestjs/common"; import { JwtService } from "@nestjs/jwt"; import { UsersService } from "../users/users.service"; import * as bcrypt from "bcryptjs"; -import { UserDocument } from "../users/users.schema"; +import { User, UserDocument } from "../users/users.schema"; @Injectable() export class AuthService { @@ -26,4 +26,8 @@ export class AuthService { token: this.jwtService.sign(payload), }; } + + async createUser(userData: User) { + return await this.userService.createUser(userData); + } } diff --git a/src/cars/cars.service.ts b/src/cars/cars.service.ts index 854ab87..556fed5 100644 --- a/src/cars/cars.service.ts +++ b/src/cars/cars.service.ts @@ -23,11 +23,11 @@ export class CarsService { // Fetches a car by its ID. async getCarById(id: string): Promise { - const car = await this.carModel.findById(id).exec(); - if (!car) { - throw new NotFoundException(`Car with ID ${id} not found.`); + try { + return await this.carModel.findById(id).exec(); + } catch (e) { + throw new NotFoundException("No Car Exists with that ID"); } - return car; } async getCarsBySearch(searchString: string, page = 0, limit = 10) { diff --git a/src/main.ts b/src/main.ts index 062bb40..afe5d87 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,9 +1,11 @@ import { NestFactory } from "@nestjs/core"; import { AppModule } from "./app.module"; -import { VersioningType } from "@nestjs/common"; +import { INestApplication, VersioningType } from "@nestjs/common"; async function bootstrap() { - const app = await NestFactory.create(AppModule); + const app = await NestFactory.create(AppModule, { + rawBody: true, + }); app.enableVersioning({ type: VersioningType.URI, }); diff --git a/src/order/order.controller.ts b/src/order/order.controller.ts index f961d79..5b4528b 100644 --- a/src/order/order.controller.ts +++ b/src/order/order.controller.ts @@ -1,31 +1,20 @@ -// import { Body, Controller, Get, Post, UseGuards } from "@nestjs/common"; -// import { JwtAuthGuard } from "../guards/auth.guard"; -// import { createOrderDto } from "../types/createOrder.dto"; -// import { User as UserDecorator } from "../decorator/user.decorator"; -// import { User } from "../users/users.schema"; -// import { OrderService } from "./order.service"; -// -// @Controller({ -// path: "order", -// version: "1", -// }) -// export class OrderController { -// constructor(private readonly orderService: OrderService) {} -// -// @Get("/getOrders") -// @UseGuards(JwtAuthGuard) -// async getOrders(@UserDecorator() user: User) { -// return this.orderService.getOrders(user); -// } -// -// @Post("/createOrder") -// @UseGuards(JwtAuthGuard) -// async create( -// @Body() createOrderDto: createOrderDto, -// @UserDecorator() user: User -// ) { -// const car = createOrderDto["car"]; -// const paymentIntent = createOrderDto["paymentIntent"]; -// return this.orderService.create(car, paymentIntent, user); -// } -// } +import { Body, Controller, Get, Post, UseGuards } from "@nestjs/common"; +import { JwtAuthGuard } from "../guards/auth.guard"; +import { User as UserDecorator } from "../decorator/user.decorator"; +import { User } from "../users/users.schema"; +import { OrderService } from "./order.service"; +import { Stripe } from "stripe"; + +@Controller({ + path: "order", + version: "1", +}) +export class OrderController { + constructor(private readonly orderService: OrderService) {} + + @Get("/getOrders") + @UseGuards(JwtAuthGuard) + async getOrders(@UserDecorator() user: User) { + return this.orderService.getOrders(user); + } +} diff --git a/src/order/order.module.ts b/src/order/order.module.ts index 03b311a..dca4326 100644 --- a/src/order/order.module.ts +++ b/src/order/order.module.ts @@ -1,19 +1,21 @@ -// //cars.module.ts -// import { Module } from "@nestjs/common"; -// import { MongooseModule } from "@nestjs/mongoose"; -// import { Order, OrderSchema } from "./order.schema"; -// import { OrderController } from "./order.controller"; -// import { CarsModule } from "../cars/cars.module"; -// import { OrderService } from "./order.service"; -// import { PaymentsModule } from "../payments/payments.module"; -// -// @Module({ -// imports: [ -// MongooseModule.forFeature([{ name: Order.name, schema: OrderSchema }]), -// CarsModule, -// PaymentsModule, -// ], -// controllers: [OrderController], -// providers: [OrderService], -// }) -// export class OrderModule {} +//cars.module.ts +import { Module } from "@nestjs/common"; +import { MongooseModule } from "@nestjs/mongoose"; +import { Order, OrderSchema } from "./order.schema"; +import { OrderController } from "./order.controller"; +import { CarsModule } from "../cars/cars.module"; +import { OrderService } from "./order.service"; +import { PaymentsModule } from "../payments/payments.module"; +import { StripeModule } from "../stripe/stripe.module"; + +@Module({ + imports: [ + MongooseModule.forFeature([{ name: Order.name, schema: OrderSchema }]), + CarsModule, + PaymentsModule, + StripeModule, + ], + controllers: [OrderController], + providers: [OrderService], +}) +export class OrderModule {} diff --git a/src/order/order.schema.ts b/src/order/order.schema.ts index 8431a7b..f685fc4 100644 --- a/src/order/order.schema.ts +++ b/src/order/order.schema.ts @@ -1,35 +1,35 @@ -// import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; -// import { HydratedDocument, Schema as mongooseSchema } from "mongoose"; -// import { Car } from "../cars/cars.schema"; -// import { User } from "../users/users.schema"; -// -// export type OrdersDocument = HydratedDocument; -// -// @Schema({ collection: "order" }) -// export class Order { -// @Prop({ type: mongooseSchema.Types.ObjectId, ref: "Car", required: true }) -// car: Car; -// -// @Prop({ type: mongooseSchema.Types.ObjectId, ref: "User", required: true }) -// user: User; -// -// @Prop({ type: String, required: true }) -// paymentIntent: string; -// -// @Prop({ type: String, required: true }) -// paymentMethod: string; -// -// @Prop({ type: Number, required: true }) -// amount: number; -// -// @Prop({ type: String, required: true }) -// currency: string; -// -// @Prop({ type: Date, default: Date.now() }) -// createdOn: Date; -// -// @Prop({ type: Date }) -// deliveredOn: Date; -// } -// -// export const OrderSchema = SchemaFactory.createForClass(Order); +import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; +import { HydratedDocument, Schema as mongooseSchema } from "mongoose"; +import { Car } from "../cars/cars.schema"; +import { User } from "../users/users.schema"; + +export type OrdersDocument = HydratedDocument; + +@Schema({ collection: "order" }) +export class Order { + @Prop({ type: mongooseSchema.Types.ObjectId, ref: "Car", required: true }) + car: Car; + + @Prop({ type: mongooseSchema.Types.ObjectId, ref: "User", required: true }) + user: User; + + @Prop({ type: String, required: true }) + paymentIntent: string; + + @Prop({ type: String, required: true }) + paymentMethod: string; + + @Prop({ type: Number, required: true }) + amount: number; + + @Prop({ type: String, required: true }) + currency: string; + + @Prop({ type: Date, default: Date.now() }) + createdOn: Date; + + @Prop({ type: Date }) + deliveredOn: Date; +} + +export const OrderSchema = SchemaFactory.createForClass(Order); diff --git a/src/order/order.service.ts b/src/order/order.service.ts index d18a21f..b98dcd8 100644 --- a/src/order/order.service.ts +++ b/src/order/order.service.ts @@ -1,53 +1,20 @@ -// import { InjectModel } from "@nestjs/mongoose"; -// import { Order } from "./order.schema"; -// import { Model } from "mongoose"; -// import { -// Injectable, -// NotAcceptableException, -// NotFoundException, -// } from "@nestjs/common"; -// import { User } from "../users/users.schema"; -// import { CarsService } from "../cars/cars.service"; -// import { PaymentsService } from "../payments/payments.service"; -// -// @Injectable() -// export class OrderService { -// constructor( -// @InjectModel(Order.name) private orderModel: Model, -// private carsService: CarsService, -// private paymentsService: PaymentsService -// ) {} -// -// async getOrders(user: User) { -// return this.orderModel.find({ user: user["_id"] }).populate("car").exec(); -// } -// -// async create(car: string, paymentIntent: string, user: User) { -// const [carData, paymentIntentData] = await Promise.all([ -// this.carsService.getCarById(car), -// this.paymentsService.retrievePaymentIntent(paymentIntent), -// ]); -// -// if (!carData || !paymentIntentData) { -// throw new NotFoundException("Car or payment intent not found"); -// } -// -// const existingOrder = await this.orderModel.findOne({ -// paymentIntent: paymentIntent, -// }); -// if (existingOrder) { -// throw new NotAcceptableException("Order already exists"); -// } -// -// const order = new this.orderModel({ -// car: car, -// user: user["_id"], -// paymentIntent: paymentIntent, -// paymentMethod: paymentIntentData["payment_method"], -// amount: paymentIntentData["amount"], -// currency: paymentIntentData["currency"], -// }); -// -// return order.save(); -// } -// } +import { InjectModel } from "@nestjs/mongoose"; +import { Order } from "./order.schema"; +import { Model } from "mongoose"; +import { Injectable } from "@nestjs/common"; +import { User } from "../users/users.schema"; +import { CarsService } from "../cars/cars.service"; +import { PaymentsService } from "../payments/payments.service"; + +@Injectable() +export class OrderService { + constructor( + @InjectModel(Order.name) private orderModel: Model, + private carsService: CarsService, + private paymentsService: PaymentsService + ) {} + + async getOrders(user: User) { + return this.orderModel.find({ user: user["_id"] }).populate("car").exec(); + } +} diff --git a/src/payments/payments.controller.ts b/src/payments/payments.controller.ts index cf492d0..d0ce259 100644 --- a/src/payments/payments.controller.ts +++ b/src/payments/payments.controller.ts @@ -2,32 +2,53 @@ import { Body, Controller, - Post, - UseGuards, Headers, ParseIntPipe, + Post, + RawBodyRequest, + Req, + UseGuards, } from "@nestjs/common"; import { PaymentsService } from "./payments.service"; import { JwtAuthGuard } from "../guards/auth.guard"; +import { InjectStripe } from "../stripe/decorators/injectStripe.decorator"; +import { User as UserDecorator } from "../decorator/user.decorator"; +import { Stripe } from "stripe"; +import { User } from "../users/users.schema"; @Controller({ path: "payments", version: "1", }) export class PaymentsController { - constructor(private readonly paymentsService: PaymentsService) {} + constructor( + private readonly paymentsService: PaymentsService, + @InjectStripe() private readonly stripeClient: Stripe + ) {} @Post("createPaymentIntent") @UseGuards(JwtAuthGuard) - createPayment( + createPaymentIntent( @Body("amount", ParseIntPipe) amount: number, @Body("currency") currency: string, + @Body("carId") carId: string, + @UserDecorator() user: User, @Headers("Idempotency-Key") idempotencyKey: string ) { return this.paymentsService.createPaymentIntent( amount, currency, - idempotencyKey + idempotencyKey, + user, + carId ); } + + @Post("webhook") + async webhook( + @Req() req: RawBodyRequest, + @Headers("stripe-signature") signature: string + ) { + this.paymentsService.handleWebhookEvent(req.rawBody, signature); + } } diff --git a/src/payments/payments.module.ts b/src/payments/payments.module.ts index 09da04d..a9a4981 100644 --- a/src/payments/payments.module.ts +++ b/src/payments/payments.module.ts @@ -5,11 +5,13 @@ import { PaymentsController } from "./payments.controller"; import { MongooseModule } from "@nestjs/mongoose"; import { Payment, PaymentSchema } from "./payments.schema"; import { StripeModule } from "../stripe/stripe.module"; +import { CarsModule } from "../cars/cars.module"; @Module({ imports: [ MongooseModule.forFeature([{ name: Payment.name, schema: PaymentSchema }]), StripeModule, + CarsModule, ], controllers: [PaymentsController], providers: [PaymentsService], diff --git a/src/payments/payments.schema.ts b/src/payments/payments.schema.ts index b2723a0..88c95e7 100644 --- a/src/payments/payments.schema.ts +++ b/src/payments/payments.schema.ts @@ -5,7 +5,7 @@ import { User } from "../users/users.schema"; export type PaymentsDocument = HydratedDocument; -@Schema({ collection: "payments" }) +@Schema({ collection: "payments", timestamps: true }) export class Payment { @Prop({ type: mongooseSchema.Types.ObjectId, ref: "Car", required: true }) car: Car; @@ -31,12 +31,6 @@ export class Payment { @Prop({ type: String, required: true }) currency: string; - @Prop({ type: Date, required: true }) - createdAt: Date; - - @Prop({ type: Date, required: true }) - updatedAt: Date; - @Prop({ type: mongooseSchema.Types.ObjectId, ref: "Payment", diff --git a/src/payments/payments.service.ts b/src/payments/payments.service.ts index 4cc5c04..e8891d3 100644 --- a/src/payments/payments.service.ts +++ b/src/payments/payments.service.ts @@ -1,21 +1,22 @@ //payments.service.ts -import { - Inject, - Injectable, - InternalServerErrorException, - NotFoundException, -} from "@nestjs/common"; +import { Injectable, NotFoundException } from "@nestjs/common"; import { Stripe } from "stripe"; import { InjectModel } from "@nestjs/mongoose"; +import * as mongoose from "mongoose"; import { Model } from "mongoose"; import { Payment } from "./payments.schema"; import { InjectStripe } from "../stripe/decorators/injectStripe.decorator"; +import { User } from "../users/users.schema"; +import { CarsService } from "../cars/cars.service"; +import { EventEmitter2, OnEvent } from "@nestjs/event-emitter"; @Injectable() export class PaymentsService { constructor( @InjectStripe() private readonly stripeClient: Stripe, - @InjectModel(Payment.name) private readonly paymentModel: Model + @InjectModel(Payment.name) private readonly paymentModel: Model, + private readonly carService: CarsService, + private readonly eventEmitter: EventEmitter2 ) {} async retrievePaymentIntent(paymentIntentId: string) { @@ -29,57 +30,76 @@ export class PaymentsService { async createPaymentIntent( amount: number, currency: string, - idempotencyKey: string + idempotencyKey: string, + user: User, + carId: string ) { try { + await this.carService.getCarById(carId); const paymentIntent = await this.stripeClient.paymentIntents.create( { amount, currency, - statement_descriptor: "Fast24", }, { idempotencyKey, } ); - // this.createPaymentLog(car, user, paymentIntent.id, "created", "A Payment Intent creation was attempted").catch(e => console.log(e)); + this.eventEmitter.emit("payment_intent.created", { + car: carId, + user: user["_id"], + paymentIntent: paymentIntent.id, + status: paymentIntent.status, + message: "Payment Intent Created", + }); - return { - client_secret: paymentIntent.client_secret, - }; + return paymentIntent; } catch (e) { - console.log(e); - throw new InternalServerErrorException("Error creating Payment Intent"); + throw e; } } - // async createPaymentLog( - // car: string, - // user: User, - // paymentIntentId: string, - // status: string, - // message: string, - // ) { - // - // const latestLog = await this.paymentModel.findOne({paymentIntent: paymentIntentId}) - // .sort({updatedAt: -1}) - // .exec(); - // const paymentIntent = await this.stripeClient.paymentIntents.retrieve(paymentIntentId); - // const payment = new this.paymentModel({ - // car: new mongoose.Types.ObjectId(car), - // user: user["_id"], - // paymentIntent: paymentIntent.id, - // paymentMethod: String(paymentIntent.payment_method), - // amount: paymentIntent.amount, - // currency: paymentIntent.currency, - // status: status, - // createdAt: new Date(paymentIntent.created * 1000), - // updatedAt: Date.now(), - // message: message, - // previousPayment: latestLog ? latestLog["_id"] : null, - // }); - // - // await payment.save(); - // } + @OnEvent("payment.*") + async createPaymentLog(payload: { + car: string; + user: User; + paymentIntentId: string; + status: string; + message: string; + }) { + const { car, user, paymentIntentId, status, message } = payload; + const latestLog = await this.paymentModel + .findOne({ paymentIntent: paymentIntentId }) + .sort({ updatedAt: -1 }) + .exec(); + const paymentIntent = await this.stripeClient.paymentIntents.retrieve( + paymentIntentId + ); + const payment = new this.paymentModel({ + car: new mongoose.Types.ObjectId(car), + user: user["_id"], + paymentIntent: paymentIntent.id, + paymentMethod: String(paymentIntent.payment_method), + amount: paymentIntent.amount, + currency: paymentIntent.currency, + status: status, + createdAt: new Date(paymentIntent.created * 1000), + updatedAt: Date.now(), + message: message, + previousPayment: latestLog ? latestLog["_id"] : null, + }); + + await payment.save(); + } + + handleWebhookEvent(rawBody: Buffer, signature: string) { + const event = this.stripeClient.webhooks.constructEvent( + rawBody, + signature, + process.env.STRIPE_WEBHOOK_SECRET + ); + + console.log(event); + } } diff --git a/src/types/loginDto.dto.ts b/src/types/loginDto.dto.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/users/users.service.ts b/src/users/users.service.ts index 2e8226d..920e6ce 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -27,9 +27,7 @@ export class UsersService { } } - throw new InternalServerErrorException( - "An error occurred while signing up" - ); + throw new InternalServerErrorException("Something went wrong!!"); } }