Skip to content

Commit

Permalink
[OVERALL FIXING] Fixed some issues related to Payment and Rental (#33)
Browse files Browse the repository at this point in the history
* adjusted payment service

* added check data method

* reset default data

* fixed the problem with correct calculating of amount

* added scheduler service class and made minor adjustments

* reset default properties

* resolved conflict

* added cron param

* implemented beta scheduling

* updated from remote branch

* adjusted payment and scheduler

* fixed overall issues and adjusted payment process

* fixed mistakes
  • Loading branch information
ivan0dyatlyukkk authored Oct 5, 2023
1 parent 70cb59b commit a2bf40a
Show file tree
Hide file tree
Showing 22 changed files with 287 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import com.project.carsharingapp.dto.payment.CreatePaymentSessionRequestDto;
import com.project.carsharingapp.dto.payment.PaymentResponseDto;
import com.project.carsharingapp.model.Payment;
import com.project.carsharingapp.service.PaymentService;
import com.project.carsharingapp.service.payment.PaymentService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
Expand Down Expand Up @@ -49,6 +49,7 @@ public PaymentResponseDto create(
@GetMapping("/success")
@Operation(summary = "Redirect endpoint in case of successful payment")
public String redirectToSuccessPage(@RequestParam String sessionId) {

paymentService.updateStatus(sessionId, Payment.Status.PAID);
return SUCCESS_ENDPOINT_MESSAGE;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,26 +46,38 @@ protected ResponseEntity<Object> handleRegistrationException(
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}

@ExceptionHandler(RuntimeException.class)
protected ResponseEntity<Object> handleAllErrors(
RuntimeException exception) {
@ExceptionHandler(EntityNotFoundException.class)
protected ResponseEntity<Object> handleEntityNotFoundException(
EntityNotFoundException exception) {
ErrorResponseDto response = new ErrorResponseDto(
LocalDateTime.now(),
HttpStatus.INTERNAL_SERVER_ERROR,
HttpStatus.NOT_FOUND,
new String[]{exception.getMessage()}
);
return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
}

@ExceptionHandler(EntityNotFoundException.class)
protected ResponseEntity<Object> handleAllErrors(
EntityNotFoundException exception) {
@ExceptionHandler(NotValidPaymentProcessException.class)
protected ResponseEntity<Object> handleNotValidPaymentProcessException(
NotValidPaymentProcessException exception
) {
ErrorResponseDto response = new ErrorResponseDto(
LocalDateTime.now(),
HttpStatus.NOT_FOUND,
HttpStatus.BAD_REQUEST,
new String[]{exception.getMessage()}
);
return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}

@ExceptionHandler(RuntimeException.class)
protected ResponseEntity<Object> handleRuntimeException(
RuntimeException exception) {
ErrorResponseDto response = new ErrorResponseDto(
LocalDateTime.now(),
HttpStatus.INTERNAL_SERVER_ERROR,
new String[]{exception.getMessage()}
);
return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
}

private String getErrorMessage(ObjectError e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.project.carsharingapp.exception;

public class NotValidPaymentProcessException extends RuntimeException {
public NotValidPaymentProcessException(String message) {
super(message);
}
}
3 changes: 3 additions & 0 deletions src/main/java/com/project/carsharingapp/model/Payment.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import java.time.Instant;
import lombok.Getter;
import lombok.Setter;

Expand All @@ -38,6 +39,8 @@ public class Payment {
private String sessionId;
@Column(nullable = false)
private BigDecimal amount;
@Column(name = "expired_time", nullable = false)
private Instant expiredTime;

public enum Status {
PENDING,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
package com.project.carsharingapp.repository;

import com.project.carsharingapp.model.Payment;
import java.util.List;
import java.util.Optional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

public interface PaymentRepository extends JpaRepository<Payment, Long> {
@Query("FROM Payment p LEFT JOIN FETCH p.rental")
Page<Payment> findAll(Pageable pageable);

@Query("FROM Payment p LEFT JOIN FETCH p.rental")
List<Payment> findAll();

Optional<Payment> findBySessionId(String sessionId);

Optional<Payment> findByRentalId(Long rentalId);

boolean existsByRentalIdAndStatus(Long rentalId, Payment.Status status);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@
import org.springframework.stereotype.Repository;

@Repository

public interface RentalRepository extends JpaRepository<Rental, Long>,
JpaSpecificationExecutor<Rental> {
@Query("FROM Rental r LEFT JOIN FETCH r.car "
+ "LEFT JOIN FETCH r.user WHERE r.id = :id")
Optional<Rental> findById(Long id);

List<Rental> findAllByUserId(Long userId);

@Query("FROM Rental r LEFT JOIN FETCH r.user "
+ "LEFT JOIN FETCH r.car "
+ " WHERE r.user.id = :userId AND r.isActive = :isActive")
Expand All @@ -25,4 +26,9 @@ public interface RentalRepository extends JpaRepository<Rental, Long>,
+ "LEFT JOIN FETCH r.car "
+ " WHERE r.user.id = :userId AND r.isActive = :isActive")
Optional<Rental> findByUserIdAndActiveStatus(Long userId, boolean isActive);

@Query("FROM Rental r LEFT JOIN FETCH r.user "
+ "LEFT JOIN FETCH r.car "
+ " WHERE r.user.id = :userId AND r.id = :rentalId")
Optional<Rental> findByUserIdAndRentalId(Long userId, Long rentalId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import java.math.BigDecimal;

public interface PaymentAmountHandler {
Long getPaymentAmount(BigDecimal dailyFee, int numberOfDays);
BigDecimal getPaymentAmount(BigDecimal dailyFee, int numberOfDays);

boolean isApplicable(Payment.Type type);
}
10 changes: 10 additions & 0 deletions src/main/java/com/project/carsharingapp/service/RentalService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.project.carsharingapp.dto.rental.CreateRentalRequestDto;
import com.project.carsharingapp.dto.rental.RentalDto;
import com.project.carsharingapp.model.Rental;
import com.project.carsharingapp.model.User;
import java.util.List;
import org.springframework.data.domain.Pageable;
import org.springframework.security.core.Authentication;
Expand All @@ -11,7 +13,15 @@ public interface RentalService {

List<RentalDto> getByUserIdAndActiveStatus(Pageable pageable, Long userId, Boolean isActive);

Rental getByUserIdAndActiveStatus(Long userId, boolean isActive);

List<RentalDto> getAllByUserIdAndActiveStatus(Long userId, boolean isActive);

Rental getByUserAndId(User user, Long id);

RentalDto getById(Long id);

RentalDto setActualReturnDay(Authentication authentication);

List<RentalDto> getAllOverdueRentals();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.project.carsharingapp.service;

public interface SchedulerService {
void checkOverdueRentals();

void checkExpiredPaymentSessions();
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.project.carsharingapp.dto.user.UserResponseDto;
import com.project.carsharingapp.exception.RegistrationException;
import com.project.carsharingapp.model.User;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

@Service
Expand All @@ -21,4 +22,8 @@ UserRegisterResponseDto register(UserRegistrationRequestDto registrationRequestD
UserResponseDto getById(Long id);

User getByEmail(String email);

User getByAuthentication(Authentication auth);

User getUserByRentalId(Long rentalId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
Expand Down Expand Up @@ -47,16 +48,40 @@ public RentalDto add(CreateRentalRequestDto requestDto, Authentication authentic
}

@Override
public List<RentalDto> getByUserIdAndActiveStatus(Pageable pageable,
Long userId,
Boolean isActive) {
public Rental getByUserIdAndActiveStatus(Long userId, boolean isActive) {
return rentalRepository.findByUserIdAndActiveStatus(userId, isActive).orElseThrow(
() -> new EntityNotFoundException("Can't find a rental by user id: " + userId));
}

@Override
public List<RentalDto> getByUserIdAndActiveStatus(
Pageable pageable,
Long userId,
Boolean isActive
) {
Specification<Rental> rentalSpecification = getSpecification(userId, isActive);
return rentalRepository.findAll(rentalSpecification, pageable)
.stream()
.map(rentalMapper::toDto)
.toList();
}

@Override
public List<RentalDto> getAllByUserIdAndActiveStatus(Long userId, boolean isActive) {
return rentalRepository.findAllByUserIdAndActiveStatus(userId, isActive)
.stream()
.map(rentalMapper::toDto)
.collect(Collectors.toList());
}

@Override
public Rental getByUserAndId(User user, Long id) {
return rentalRepository.findByUserIdAndRentalId(user.getId(), id).orElseThrow(
() -> new EntityNotFoundException("Can't find a rental with id: " + id
+ " for the user")
);
}

@Override
public RentalDto getById(Long id) {
Rental rental = rentalRepository.findById(id).orElseThrow(
Expand All @@ -68,25 +93,30 @@ public RentalDto getById(Long id) {
@Override
public RentalDto setActualReturnDay(Authentication authentication) {
User user = getUser(authentication.getName());
Rental rental1 = rentalRepository.findByUserIdAndActiveStatus(
user.getId(), true)
.orElseThrow(() -> new EntityNotFoundException(" Active rental "
+ "not found by id: " + user.getId()));
sendNotification(rental1, authentication);
return rentalRepository.findByUserIdAndActiveStatus(user.getId(), true)
.map(rental -> {
isCorrectReturnDate(rental);
rental.setActualReturnDate(LocalDateTime.now());
rental.setActive(false);
rentalRepository.save(rental);
increaseCarInventory(rental.getCar().getId());
sendNotification(rental, authentication);
return rentalMapper.toDto(rental);
})
.orElseThrow(() -> new EntityNotFoundException(" Active rental "
+ "not found by id: " + user.getId()));
}

private static void isCorrectReturnDate(Rental rental) {
if (rental.getRentalDate().isAfter(LocalDateTime.now())) {
throw new RuntimeException("The car can't be returned"
+ " before rental date");
}
@Override
public List<RentalDto> getAllOverdueRentals() {
return rentalRepository.findAll().stream()
.filter(rental -> rental.getReturnDate().isBefore(LocalDateTime.now())
&& rental.isActive())
.map(rentalMapper::toDto)
.collect(Collectors.toList());
}

private User getUser(String email) {
Expand All @@ -104,6 +134,11 @@ private Car getCar(Long carId) {
private void decreaseCarInventory(Long carId) {
Car car = getCar(carId);
Integer existedInventory = car.getInventory();

if (existedInventory < 0) {
throw new RuntimeException("Can't make a rental because there are not enough cars");
}

car.setInventory(existedInventory - 1);
carRepository.save(car);
}
Expand All @@ -115,6 +150,10 @@ private void increaseCarInventory(Long carId) {
carRepository.save(car);
}

private boolean isValidActualReturnDate(Rental rental, LocalDateTime actualReturnData) {
return rental.getRentalDate().isBefore(actualReturnData);
}

private Specification<Rental> getSpecification(Long userId, Boolean isActive) {
return (root, query, criteriaBuilder) -> {
Predicate userPredicate = (userId != null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.project.carsharingapp.service.impl;

import com.project.carsharingapp.model.Payment;
import com.project.carsharingapp.model.User;
import com.project.carsharingapp.repository.PaymentRepository;
import com.project.carsharingapp.repository.UserRepository;
import com.project.carsharingapp.service.NotificationService;
import com.project.carsharingapp.service.RentalService;
import com.project.carsharingapp.service.SchedulerService;
import com.project.carsharingapp.service.payment.PaymentService;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class SchedulerServiceImp implements SchedulerService {
private static final String OVERDUE_RENTAL_MESSAGE = "Your rental is expected to be "
+ "overdue! Rental id: ";
private final RentalService rentalService;
private final PaymentService paymentService;
private final UserRepository userRepository;
private final PaymentRepository paymentRepository;
private final NotificationService notificationService;

@Override
@Scheduled(cron = "0/1 0 * * * MON-FRI")
public void checkOverdueRentals() {
rentalService.getAllOverdueRentals()
.forEach(
(rentalDto) -> {
User user = userRepository.findUserById(rentalDto.getUserId()).get();
notificationService.sendMessage(user.getTelegramChatId(),
OVERDUE_RENTAL_MESSAGE + rentalDto.getId());
}
);
}

@Override
@Scheduled(cron = "0 * * * * *")
public void checkExpiredPaymentSessions() {
List<Payment> allExpiredPayments = paymentService.getAllExpiredPayments();
allExpiredPayments.forEach(payment -> payment.setStatus(Payment.Status.EXPIRED));
paymentRepository.saveAll(allExpiredPayments);
}
}
Loading

0 comments on commit a2bf40a

Please sign in to comment.