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()
|
.anyRequest().authenticated()
|
||||||
)
|
)
|
||||||
.httpBasic().and()
|
.httpBasic().and()
|
||||||
.sessionManagement(sessionManagement -> {
|
.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED))
|
||||||
sessionManagement.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
|
|
||||||
})
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,4 +13,9 @@ public class BankingServiceConfig {
|
|||||||
public ModelMapper modelMapper() {
|
public ModelMapper modelMapper() {
|
||||||
return new ModelMapper();
|
return new ModelMapper();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
|
||||||
|
return restTemplateBuilder.build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,37 @@
|
|||||||
package net.kapcake.bankingservice.controllers;
|
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.domain.BankAccountDTO;
|
||||||
import net.kapcake.bankingservice.model.entities.BankAccount;
|
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.security.UserDetailsImpl;
|
||||||
import net.kapcake.bankingservice.services.AccountService;
|
import net.kapcake.bankingservice.services.AccountService;
|
||||||
|
import net.kapcake.bankingservice.services.PaymentService;
|
||||||
import org.modelmapper.ModelMapper;
|
import org.modelmapper.ModelMapper;
|
||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
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.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class BankingServiceController {
|
public class BankingServiceController {
|
||||||
|
private final BankAccountRepository bankAccountRepository;
|
||||||
private final AccountService accountService;
|
private final AccountService accountService;
|
||||||
|
private final PaymentService paymentService;
|
||||||
private final ModelMapper modelMapper;
|
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.accountService = accountService;
|
||||||
|
this.paymentService = paymentService;
|
||||||
this.modelMapper = modelMapper;
|
this.modelMapper = modelMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,4 +40,29 @@ public class BankingServiceController {
|
|||||||
return accountService.getAccounts(authenticatedUser.user()).stream()
|
return accountService.getAccounts(authenticatedUser.user()).stream()
|
||||||
.map(account -> modelMapper.map(account, BankAccountDTO.class)).toList();
|
.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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,3 +7,7 @@ spring:
|
|||||||
jpa:
|
jpa:
|
||||||
database-platform: org.hibernate.dialect.H2Dialect
|
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