Skip to contentPedro Farbo
Lesson 24 / 2560 min

E-commerce Project - Part 2

E-commerce Project - Part 2

Implement cart, orders and payments.

Cart Service

typescript
// src/modules/cart/cart.service.tsexport class CartService {  async getCart(userId: string) {    const cart = await prisma.cart.findUnique({      where: { userId },      include: { items: { include: { product: true } } },    });     const subtotal = cart.items.reduce((acc, item) =>      acc + Number(item.product.price) * item.quantity, 0);     return { ...cart, subtotal };  }   async addItem(userId: string, productId: string, quantity: number) {    const product = await prisma.product.findUnique({ where: { id: productId } });    if (!product) throw new AppError('Product not found', 404);    if (product.stock < quantity) throw new AppError('Insufficient stock', 400);     const cart = await prisma.cart.findUnique({ where: { userId } });     const existingItem = await prisma.cartItem.findUnique({      where: { cartId_productId: { cartId: cart.id, productId } },    });     if (existingItem) {      await prisma.cartItem.update({        where: { id: existingItem.id },        data: { quantity: existingItem.quantity + quantity },      });    } else {      await prisma.cartItem.create({        data: { cartId: cart.id, productId, quantity },      });    }     return this.getCart(userId);  }   async removeItem(userId: string, productId: string) {    const cart = await prisma.cart.findUnique({ where: { userId } });    await prisma.cartItem.delete({      where: { cartId_productId: { cartId: cart.id, productId } },    });    return this.getCart(userId);  }}

Order Service

typescript
// src/modules/orders/order.service.tsexport class OrderService {  async create(userId: string, data: CreateOrderDTO) {    const cart = await cartService.getCart(userId);    if (!cart.items.length) throw new AppError('Cart is empty', 400);     // Validate stock    for (const item of cart.items) {      if (item.product.stock < item.quantity) {        throw new AppError(`Insufficient stock for ${item.product.name}`, 400);      }    }     // Create order with transaction    const order = await prisma.$transaction(async (tx) => {      const newOrder = await tx.order.create({        data: {          userId,          total: cart.subtotal,          items: {            create: cart.items.map(item => ({              productId: item.productId,              name: item.product.name,              price: item.product.price,              quantity: item.quantity,            })),          },        },      });       // Update stock      for (const item of cart.items) {        await tx.product.update({          where: { id: item.productId },          data: { stock: { decrement: item.quantity } },        });      }       // Clear cart      await tx.cartItem.deleteMany({ where: { cartId: cart.id } });       return newOrder;    });     await mailService.sendOrderConfirmation(user, order);     return order;  }   async findByUser(userId: string) {    return prisma.order.findMany({      where: { userId },      include: { items: true },      orderBy: { createdAt: 'desc' },    });  }}

Cart Routes

typescript
router.use(authenticate);router.get('/', controller.show);router.post('/items', controller.addItem);router.delete('/items/:productId', controller.removeItem);

Summary

  • ✅ Complete cart service
  • ✅ Order creation with transaction
  • ✅ Stock validation
  • ✅ Email notification

Next lesson: E-commerce Project - Part 3! 🚀

Enjoyed the content? Your contribution helps keep everything online and free!

PIX:0737160d-e98f-4a65-8392-5dba70e7ff3e