diff --git a/src/modules/cart/controllers/cart.controller.ts b/src/modules/cart/controllers/cart.controller.ts index 70156d9..6bb1509 100644 --- a/src/modules/cart/controllers/cart.controller.ts +++ b/src/modules/cart/controllers/cart.controller.ts @@ -4,6 +4,7 @@ import { AddItemToCartDto } from '../dto/add-item.dto'; import { UpdateItemQuantityDto } from '../dto/update-item.dto'; import { ApplyCouponDto } from '../dto/apply-coupon.dto'; import { SetAddressDto } from '../dto/set-address.dto'; +import { SetPaymentMethodDto } from '../dto/set-payment-method.dto'; import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth, ApiBody, ApiParam } from '@nestjs/swagger'; import { AuthGuard } from '../../auth/guards/auth.guard'; import { UserId } from 'src/common/decorators/user-id.decorator'; @@ -84,4 +85,18 @@ export class CartController { async setAddress(@UserId() userId: string, @RestId() restaurantId: string, @Body() setAddressDto: SetAddressDto) { return this.cartService.setAddress(userId, restaurantId, setAddressDto); } + + @Patch('payment-method') + @ApiOperation({ summary: 'Set payment method for cart' }) + @ApiBody({ type: SetPaymentMethodDto }) + @ApiResponse({ status: 200, description: 'Payment method set successfully' }) + @ApiResponse({ status: 400, description: 'Bad request (e.g., payment method is not active)' }) + @ApiResponse({ status: 404, description: 'Payment method not found for restaurant' }) + async setPaymentMethod( + @UserId() userId: string, + @RestId() restaurantId: string, + @Body() setPaymentMethodDto: SetPaymentMethodDto, + ) { + return this.cartService.setPaymentMethod(userId, restaurantId, setPaymentMethodDto); + } } diff --git a/src/modules/cart/dto/set-payment-method.dto.ts b/src/modules/cart/dto/set-payment-method.dto.ts new file mode 100644 index 0000000..2b4d262 --- /dev/null +++ b/src/modules/cart/dto/set-payment-method.dto.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; + +export class SetPaymentMethodDto { + @ApiProperty({ description: 'Payment method ID', example: '01ARZ3NDEKTSV4RRFFQ69G5FAV' }) + @IsNotEmpty() + @IsString() + paymentMethodId!: string; +} + diff --git a/src/modules/cart/interfaces/cart.interface.ts b/src/modules/cart/interfaces/cart.interface.ts index 602fc67..a431a8f 100644 --- a/src/modules/cart/interfaces/cart.interface.ts +++ b/src/modules/cart/interfaces/cart.interface.ts @@ -21,6 +21,8 @@ export interface Cart { items: CartItem[]; coupon?: CartCoupon; addressId?: string; + paymentMethodId?: string; + paymentMethodFee: number; totalPrice: number; totalDiscount: number; couponDiscount: number; diff --git a/src/modules/cart/providers/cart.service.ts b/src/modules/cart/providers/cart.service.ts index 70c4816..2af7fcf 100644 --- a/src/modules/cart/providers/cart.service.ts +++ b/src/modules/cart/providers/cart.service.ts @@ -8,10 +8,11 @@ import { AddItemToCartDto } from '../dto/add-item.dto'; import { UpdateItemQuantityDto } from '../dto/update-item.dto'; import { ApplyCouponDto } from '../dto/apply-coupon.dto'; import { SetAddressDto } from '../dto/set-address.dto'; +import { SetPaymentMethodDto } from '../dto/set-payment-method.dto'; import { UserAddress } from '../../users/entities/user-address.entity'; +import { RestaurantPaymentMethod } from '../../payments/entities/restaurant-payment-method.entity'; import { Cart, CartItem, CartCoupon } from '../interfaces/cart.interface'; - @Injectable() export class CartService { private readonly CART_TTL = 1200; // 20 minutes in seconds @@ -44,6 +45,7 @@ export class CartService { restaurantId, restaurantName: restaurant.name, items: [], + paymentMethodFee: 0, totalPrice: 0, totalDiscount: 0, couponDiscount: 0, @@ -279,6 +281,52 @@ export class CartService { return cart; } + /** + * Set payment method for cart + */ + async setPaymentMethod( + userId: string, + restaurantId: string, + setPaymentMethodDto: SetPaymentMethodDto, + ): Promise { + const cart = await this.findOne(userId, restaurantId); + + // Find and validate payment method belongs to restaurant + const restaurantPaymentMethod = await this.em.findOne( + RestaurantPaymentMethod, + { + restaurant: { id: restaurantId }, + paymentMethod: { id: setPaymentMethodDto.paymentMethodId }, + }, + { populate: ['restaurant', 'paymentMethod'] }, + ); + + if (!restaurantPaymentMethod) { + throw new NotFoundException( + `Payment method with ID ${setPaymentMethodDto.paymentMethodId} not found for restaurant ${restaurantId}`, + ); + } + + // Verify payment method is active + if (!restaurantPaymentMethod.isActive) { + throw new BadRequestException('Payment method is not active for this restaurant'); + } + + // Verify the payment method itself is active + if (!restaurantPaymentMethod.paymentMethod.isActive) { + throw new BadRequestException('Payment method is not active'); + } + + cart.paymentMethodId = setPaymentMethodDto.paymentMethodId; + cart.paymentMethodFee = Number(restaurantPaymentMethod.price) || 0; + + // Recalculate cart totals to include payment method fee + await this.recalculateCartTotals(cart); + await this.saveCart(cart); + + return cart; + } + /** * Recalculate cart totals (including coupon discount and VAT) */ @@ -323,7 +371,22 @@ export class CartService { } cart.vatAmount = vatAmount; - cart.finalPrice = priceAfterDiscounts + vatAmount; + + // Get payment method fee if payment method is set + let paymentMethodFee = 0; + if (cart.paymentMethodId) { + const restaurantPaymentMethod = await this.em.findOne(RestaurantPaymentMethod, { + restaurant: { id: cart.restaurantId }, + paymentMethod: { id: cart.paymentMethodId }, + }); + if (restaurantPaymentMethod) { + paymentMethodFee = Number(restaurantPaymentMethod.price) || 0; + } + } + cart.paymentMethodFee = paymentMethodFee; + + // Final price includes items, discounts, VAT, and payment method fee + cart.finalPrice = priceAfterDiscounts + vatAmount + paymentMethodFee; cart.totalItems = totalItems; cart.updatedAt = new Date().toISOString(); } diff --git a/src/modules/payments/entities/restaurant-payment-method.entity.ts b/src/modules/payments/entities/restaurant-payment-method.entity.ts index e226fd5..b7d3822 100644 --- a/src/modules/payments/entities/restaurant-payment-method.entity.ts +++ b/src/modules/payments/entities/restaurant-payment-method.entity.ts @@ -16,6 +16,9 @@ export class RestaurantPaymentMethod extends BaseEntity { @Property({ nullable: true }) merchantId?: string; + @Property({ type: 'decimal', precision: 10, scale: 2, default: 0 }) + price: number = 0; + @Property({ type: 'boolean', default: true }) isActive: boolean = true; }