PaymentService.java

package dev.ianrobrien.fakebank.payments;

import dev.ianrobrien.fakebank.accounts.AccountService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import dev.ianrobrien.fakebank.accounts.InvalidAccountException;
import dev.ianrobrien.fakebank.accounts.Account;
import dev.ianrobrien.fakebank.model.InstructedAmount;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/***
 * Provides payment requests
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class PaymentService {

  private final AccountService accountService;

  /***
   * Initiates a payment with the given request
   * @param creditorAccountId the source of the amount
   * @param debtorAccountId the destination of the amount
   * @param instructedAmount the amount to be sent
   */
  @Transactional
  public void initiatePayment(
      Long creditorAccountId, Long debtorAccountId, InstructedAmount instructedAmount)
      throws InsufficientFundsException, InvalidAccountException, InvalidPaymentRequestException {
    log.info("Initiate payment from PaymentService");

    Account creditorAccount = accountService.getAccount(creditorAccountId);
    Account debtorAccount = accountService.getAccount(debtorAccountId);

    var amount = Double.parseDouble(instructedAmount.getAmount());
    validatePaymentRequest(debtorAccount, amount);

    creditorAccount.setBalance(creditorAccount.getBalance() + amount);
    debtorAccount.setBalance(debtorAccount.getBalance() - amount);

    accountService.saveAccount(creditorAccount);
    accountService.saveAccount(debtorAccount);
  }

  /***
   * Validates the payment request (can be expanded later with more business logic)
   * @param debtorAccount the account that will be debited
   * @param amount the amount to debit
   * @throws InvalidPaymentRequestException
   * @throws InsufficientFundsException
   */
  private static void validatePaymentRequest(Account debtorAccount, double amount)
      throws InvalidPaymentRequestException, InsufficientFundsException {
    log.info("Validating payment request");
    if (amount <= 0) {
      throw new InvalidPaymentRequestException();
    }
    if (amount > debtorAccount.getBalance()) {
      throw new InsufficientFundsException();
    }
  }
}