Add payment creation endpoint and first validation criteria
* Giver account * Iban validation with openiban.com
This commit is contained in:
@@ -20,9 +20,7 @@ public class AuthConfig {
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.httpBasic().and()
|
||||
.sessionManagement(sessionManagement -> {
|
||||
sessionManagement.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
|
||||
})
|
||||
.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED))
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
@@ -13,4 +13,9 @@ public class BankingServiceConfig {
|
||||
public ModelMapper modelMapper() {
|
||||
return new ModelMapper();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
|
||||
return restTemplateBuilder.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,37 @@
|
||||
package net.kapcake.bankingservice.controllers;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import net.kapcake.bankingservice.exceptions.PaymentValidationException;
|
||||
import net.kapcake.bankingservice.model.domain.PaymentDTO;
|
||||
import net.kapcake.bankingservice.model.domain.BankAccountDTO;
|
||||
import net.kapcake.bankingservice.model.entities.BankAccount;
|
||||
import net.kapcake.bankingservice.model.entities.Payment;
|
||||
import net.kapcake.bankingservice.repositories.BankAccountRepository;
|
||||
import net.kapcake.bankingservice.security.UserDetailsImpl;
|
||||
import net.kapcake.bankingservice.services.AccountService;
|
||||
import net.kapcake.bankingservice.services.PaymentService;
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@RestController
|
||||
public class BankingServiceController {
|
||||
private final BankAccountRepository bankAccountRepository;
|
||||
private final AccountService accountService;
|
||||
private final PaymentService paymentService;
|
||||
private final ModelMapper modelMapper;
|
||||
|
||||
public BankingServiceController(AccountService accountService) {
|
||||
public BankingServiceController(BankAccountRepository bankAccountRepository, AccountService accountService, PaymentService paymentService, ModelMapper modelMapper) {
|
||||
this.bankAccountRepository = bankAccountRepository;
|
||||
this.accountService = accountService;
|
||||
this.paymentService = paymentService;
|
||||
this.modelMapper = modelMapper;
|
||||
}
|
||||
|
||||
@@ -27,4 +40,29 @@ public class BankingServiceController {
|
||||
return accountService.getAccounts(authenticatedUser.user()).stream()
|
||||
.map(account -> modelMapper.map(account, BankAccountDTO.class)).toList();
|
||||
}
|
||||
|
||||
@PostMapping("/payment")
|
||||
public PaymentDTO createPayment(@AuthenticationPrincipal UserDetailsImpl authenticatedUser, @RequestBody @Valid PaymentDTO paymentDTO, BindingResult bindingResult) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
throw new PaymentValidationException("Payment request invalid: " + bindingResult.getAllErrors());
|
||||
}
|
||||
Payment payment = mapToEntity(paymentDTO);
|
||||
Payment createdPayment = paymentService.createPayment(authenticatedUser.user(), payment);
|
||||
return mapToDTO(createdPayment);
|
||||
}
|
||||
|
||||
private PaymentDTO mapToDTO(Payment payment) {
|
||||
PaymentDTO paymentDTO = modelMapper.map(payment, PaymentDTO.class);
|
||||
return paymentDTO;
|
||||
}
|
||||
|
||||
private Payment mapToEntity(PaymentDTO paymentDTO) {
|
||||
Payment payment = modelMapper.map(paymentDTO, Payment.class);
|
||||
Optional<BankAccount> bankAccountOptional = bankAccountRepository.findById(paymentDTO.getGiverAccount());
|
||||
if (bankAccountOptional.isEmpty()) {
|
||||
throw new PaymentValidationException("Payment request invalid: Giver account does not exist.");
|
||||
}
|
||||
payment.setGiverAccount(bankAccountOptional.get());
|
||||
return payment;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package net.kapcake.bankingservice.controllers;
|
||||
|
||||
import net.kapcake.bankingservice.exceptions.PaymentValidationException;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
|
||||
|
||||
@ControllerAdvice
|
||||
public class BankingServiceExceptionHandler extends ResponseEntityExceptionHandler {
|
||||
@ExceptionHandler({PaymentValidationException.class})
|
||||
protected ResponseEntity<Object> handlePaymentValidationException(PaymentValidationException exception, WebRequest request) {
|
||||
return this.handleExceptionInternal(exception, exception.getMessage(),
|
||||
new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package net.kapcake.bankingservice.exceptions;
|
||||
|
||||
public class PaymentValidationException extends IllegalArgumentException {
|
||||
public PaymentValidationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
public PaymentValidationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package net.kapcake.bankingservice.model.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record IbanValidationResponse(boolean valid, List<String> messages, String iban) {
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package net.kapcake.bankingservice.services;
|
||||
|
||||
import net.kapcake.bankingservice.exceptions.PaymentValidationException;
|
||||
import net.kapcake.bankingservice.model.domain.IbanValidationResponse;
|
||||
import net.kapcake.bankingservice.model.entities.Payment;
|
||||
import net.kapcake.bankingservice.model.entities.User;
|
||||
import net.kapcake.bankingservice.repositories.BankAccountRepository;
|
||||
import net.kapcake.bankingservice.repositories.PaymentRepository;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@Service
|
||||
public class PaymentService {
|
||||
private final PaymentRepository paymentRepository;
|
||||
private final BankAccountRepository bankAccountRepository;
|
||||
private final RestTemplate restTemplate;
|
||||
private final String validationUrl;
|
||||
|
||||
public PaymentService(PaymentRepository paymentRepository,
|
||||
BankAccountRepository bankAccountRepository,
|
||||
RestTemplate restTemplate,
|
||||
@Value("${banking-service.iban-validation.url}") String validationUrl) {
|
||||
this.paymentRepository = paymentRepository;
|
||||
this.bankAccountRepository = bankAccountRepository;
|
||||
this.restTemplate = restTemplate;
|
||||
this.validationUrl = validationUrl;
|
||||
}
|
||||
|
||||
public Payment createPayment(User user, Payment payment) {
|
||||
validatePayment(user, payment);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void validatePayment(User user, Payment payment) {
|
||||
if (user.getBankAccounts().stream().noneMatch(bankAccount -> bankAccount.getId().equals(payment.getGiverAccount().getId()))) {
|
||||
throw new PaymentValidationException("Giver account not owned by authenticated user.");
|
||||
}
|
||||
IbanValidationResponse validationResponse = restTemplate.getForObject(validationUrl + payment.getBeneficiaryAccountNumber(), IbanValidationResponse.class);
|
||||
if (!validationResponse.valid()) {
|
||||
throw new PaymentValidationException("Beneficiary account not valid: " + validationResponse.messages());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,4 +6,8 @@ spring:
|
||||
password: bankPassword
|
||||
jpa:
|
||||
database-platform: org.hibernate.dialect.H2Dialect
|
||||
defer-datasource-initialization: true
|
||||
defer-datasource-initialization: true
|
||||
|
||||
banking-service:
|
||||
iban-validation:
|
||||
url: https://openiban.com/validate/
|
||||
Reference in New Issue
Block a user