public MakePaymentResult IsAccountValid(Account account, MakePaymentRequest makePaymentRequest) { if (account == null) { return new MakePaymentResult { Success = false } } ; if (!account.AllowedPaymentSchemes.HasFlag(AllowedPaymentSchemes.FasterPayments)) { return new MakePaymentResult { Success = false } } ; if (account.Balance < makePaymentRequest.Amount) { return new MakePaymentResult { Success = false } } ; return(new MakePaymentResult { Success = true }); } } }
public void Balance_Should_Not_Be_Deducted_After_Failed_Payment() { Account testAccount = GetTestAccount(); var dataStore = new Mock <IAccountDataStore>(); dataStore.Setup <Account>(d => d.GetAccount(It.IsAny <string>())).Returns (testAccount); dataStore.Setup(d => d.UpdateAccount(It.IsAny <Account>())).Callback((Account ac) => { testAccount = ac; }); var paymentValidationFactory = GetValidatePaymentFactory(false); var paymentService = new PaymentService(dataStore.Object, paymentValidationFactory.Object); MakePaymentRequest request = new MakePaymentRequest() { Amount = 500, PaymentScheme = PaymentScheme.Bacs }; var result = paymentService.MakePayment(request); Assert.IsFalse(result.Success); Assert.AreEqual(testAccount.Balance, 1500);//balance should stay at 1500 after failed payment no deduction }
public void Balance_Should_Be_Deducted_After_Successfull_Payment() { var testAccount = GetTestAccount(); testAccount.AllowedPaymentSchemes = AllowedPaymentSchemes.Bacs; var dataStore = new Mock <IAccountDataStore>(); dataStore.Setup <Account>(d => d.GetAccount(It.IsAny <string>())).Returns ((string accountNo) => { return(testAccount); }); dataStore.Setup(d => d.UpdateAccount(It.IsAny <Account>())).Callback((Account ac) => { testAccount = ac; }); var paymentService = new PaymentService(dataStore.Object, _validatePaymentFactory); MakePaymentRequest request = new MakePaymentRequest() { Amount = 500, PaymentScheme = PaymentScheme.Bacs }; var result = paymentService.MakePayment(request); Assert.IsTrue(result.Success); Assert.AreEqual(testAccount.Balance, 1000);//balance should be 1000 after deduction of 500 }
/// <summary> /// Makes the payment. /// </summary> /// <param name="request">The request.</param> /// <returns>MakePaymentResponse.</returns> public MakePaymentResponse MakePayment(MakePaymentRequest request) { request.Signature = CreateSHA256Hash(request.ApiSecret + request.TransactionId + request.TransactionTime + request.Amount + request.Currency + request.Installment); return(MakeApiRequest <MakePaymentRequest, MakePaymentResponse>(request, _baseApiUrl + "payments/pay", null)); }
public MakePaymentResult MakePayment(MakePaymentRequest request) { Account account = GetAccount(request.DebitorAccountNumber) ?? null; var result = new MakePaymentResult(); if (IsPaymentAllowed(request, account)) { try { account.Balance -= request.Amount; // A call to do the actual transfer should be here result.Success = true; _datastore.UpdateAccount(account); } catch (Exception e) { Console.WriteLine("An error occurred while making the payment: '{0}'", e); } } else { result.Success = false; } return(result); }
public ValidationResponse Validate(MakePaymentRequest request, DateTime asOf) { return(RunRules( CardValidationRules.NumberIsCorrectLength, CardValidationRules.NumberHasOnlyDigits, PaymentValidationRules.AmountIsGreaterThanZero, ExpiryIsInFuture )); bool ExpiryIsInFuture(MakePaymentRequest request) => asOf.Month <= request.Card.ExpiryMonth && asOf.Year <= request.Card.ExpiryYear; ValidationResponse RunRules(params Func <MakePaymentRequest, bool>[] rules) { foreach (var rule in rules) { if (!rule(request)) { return(new InvalidPayment()); } } return(new ValidPayment(request)); } }
public void Test_PaymentService_MakePayment_UnSuccess_when_Account_Is_Null() { // arrange MakePaymentRequest makePaymentRequest = new MakePaymentRequest { Amount = 50, CreditorAccountNumber = "PR2408", DebtorAccountNumber = "DC2404", PaymentDate = DateTime.Now, PaymentScheme = PaymentScheme.Bacs }; MakePaymentResult makePaymentResult = new MakePaymentResult { Success = false }; _accountDataStore.Setup(x => x.GetAccount(makePaymentRequest.DebtorAccountNumber)).Returns <Account>(null); _accountDataStoreFactory.Setup(x => x.AccountDataStore(It.IsAny <string>())).Returns(_accountDataStore.Object); _makePaymentRequestValidator.Setup(x => x.IsAccountValidToMakePayment(null, makePaymentRequest)).Returns(makePaymentResult); // act makePaymentResult = _paymentSrevice.MakePayment(makePaymentRequest); // assert Assert.AreEqual(makePaymentResult.Success, false); _accountDataStore.Verify(x => x.UpdateAccount(null), Times.Never); }
public void Setup() { _accountBalanceBefore = 100; _request = new MakePaymentRequest { DebtorAccountNumber = "ABC123", Amount = 10 }; _account = new Account { Balance = _accountBalanceBefore }; _validationResult = true; _accountDataStore = new Mock <IAccountDataStore>(); _accountDataStore.Setup(x => x.GetAccount(_request.DebtorAccountNumber)).Returns(_account); _accountDataStore.Setup(x => x.UpdateAccount(_account)).Callback <Account>(x => _accountBalanceAfter = x.Balance); _accountDataStoreFactory = new Mock <IAccountDataStoreFactory>(); _accountDataStoreFactory.Setup(x => x.GetAccountDataStore()).Returns(_accountDataStore.Object); _paymentValitadionService = new Mock <IPaymentValitadionService>(); _paymentValitadionService.Setup(x => x.ValidatePayment(_account, _request)).Returns(() => _validationResult); _paymentService = new PaymentService(_accountDataStoreFactory.Object, _paymentValitadionService.Object); }
public MakePaymentResult MakePayment(MakePaymentRequest request) { var result = new MakePaymentResult(); var dataStoreType = ConfigurationManager.AppSettings["DataStoreType"] ?? String.Empty; var accountDataStore = _accountDataStoreFactory.Create(dataStoreType); var account = accountDataStore.GetAccount(request.DebtorAccountNumber); if (account == null) { return(result); } var paymentRule = _paymentRuleFactory.Create(request.PaymentScheme); if (paymentRule.IsPaymentAllowed(account, request.Amount)) { account.Balance -= request.Amount; accountDataStore.UpdateAccount(account); //Have incorporated unit of work so account check and update can be atomic accountDataStore.Commit(); result.Success = true; } return(result); }
public void MakePayment_DebtorAccountNotFound_PaymentNotOK() { //Arrange var request = new MakePaymentRequest { Amount = 10, DebtorAccountNumber = debtorAccountNumber, CreditorAccountNumber = creditorAccountNumber, PaymentDate = DateTime.Today, PaymentScheme = Types.PaymentScheme.Bacs }; creditorAccount = new Account { AccountNumber = creditorAccountNumber, AllowedPaymentSchemes = Types.PaymentScheme.Bacs, Balance = 0, Status = AccountStatus.Live }; accountDataStoreMock .Setup(o => o.GetAccount(creditorAccountNumber)) .Returns(creditorAccount); //Act var paymentService = new PaymentService(accountRepositoryMock.Object); var result = paymentService.MakePayment(request); //Assert Assert.IsFalse(result.Success); }
public void MakePayment_CreditorAccountNotFound_PaymentNotOK() { //Arrange int DebtorAccountBalance = 10; var request = new MakePaymentRequest { Amount = 10, DebtorAccountNumber = debtorAccountNumber, CreditorAccountNumber = creditorAccountNumber, PaymentDate = DateTime.Today, PaymentScheme = Types.PaymentScheme.Bacs }; debtorAccount = new Account { AccountNumber = debtorAccountNumber, AllowedPaymentSchemes = Types.PaymentScheme.Bacs, Balance = DebtorAccountBalance, Status = AccountStatus.Live }; accountDataStoreMock .Setup(o => o.GetAccount(debtorAccountNumber)) .Returns(debtorAccount); //Act var paymentService = new PaymentService(accountRepositoryMock.Object); var result = paymentService.MakePayment(request); //Assert Assert.IsFalse(result.Success); //If we had an ILogger we could also have custom Exceptions for each bussiness case e.g. AccountNotFoundException etc //and we could verify that the logger method has been called with the correct exception for each case. }
public void WhenChapsRequestForAmmount99_Is_Made_On_ChapsAccountStatus_Disabled_WithBalance100_Then_MakePaymentResult_Fails() { //assert var request = new MakePaymentRequest() { Amount = 99, CreditorAccountNumber = "creditAccountNumber", PaymentDate = DateTime.Today, PaymentScheme = PaymentScheme.Chaps }; var account = new Account() { Balance = 100, AccountNumber = "accountNumber", AllowedPaymentSchemes = AllowedPaymentSchemes.Chaps, Status = AccountStatus.Disabled }; _accountDataStoreMock.Setup(x => x.GetAccount(It.IsAny <string>())).Returns(account); //act var makePaymentResult = _paymentService.MakePayment(request); //assert makePaymentResult.Success.Should().BeFalse(); account.Balance.Should().Be(100); _accountDataStoreMock.Verify(x => x.UpdateAccount(It.IsAny <Account>()), Times.Never); }
public void WhenBacsRequestForAmmount200_Is_Made_On_BacsAccountStatus_Live_AndWithBalance100_Then_MakePaymentResult_stillSucceeds() { var request = new MakePaymentRequest() { Amount = 200, CreditorAccountNumber = "creditAccountNumber", PaymentDate = DateTime.Today, PaymentScheme = PaymentScheme.Bacs }; var account = new Account() { Balance = 100, AccountNumber = "accountNumber", AllowedPaymentSchemes = AllowedPaymentSchemes.Bacs, Status = AccountStatus.Live }; _accountDataStoreMock.Setup(x => x.GetAccount(It.IsAny <string>())).Returns(account); var makePaymentResult = _paymentService.MakePayment(request); makePaymentResult.Success.Should().BeTrue(); account.Balance.Should().Be(-100); _accountDataStoreMock.Verify(x => x.UpdateAccount(It.IsAny <Account>()), Times.Once); }
public void WhenFasterPaymentsRequestForAmmount99_Is_Made_On_FasterPaymentsAccountStatus_InboundPaymentsOnly_AndWithBalance100_Then_MakePaymentResult_Succeeds() { var request = new MakePaymentRequest() { Amount = 99, CreditorAccountNumber = "creditAccountNumber", PaymentDate = DateTime.Today, PaymentScheme = PaymentScheme.FasterPayments }; var account = new Account() { Balance = 100, AccountNumber = "accountNumber", AllowedPaymentSchemes = AllowedPaymentSchemes.FasterPayments, Status = AccountStatus.InboundPaymentsOnly }; _accountDataStoreMock.Setup(x => x.GetAccount(It.IsAny <string>())).Returns(account); var makePaymentResult = _paymentService.MakePayment(request); makePaymentResult.Success.Should().BeTrue(); account.Balance.Should().Be(1); _accountDataStoreMock.Verify(x => x.UpdateAccount(It.IsAny <Account>()), Times.Once); }
public void Should_update_account_balance_if_validation_is_successful() { const string accountNumber = "12312312ABC"; const int initialAccountBalance = 224; const int expectedAccountBalance = 102; var request = new MakePaymentRequest { PaymentScheme = PaymentScheme.Bacs, DebtorAccountNumber = accountNumber, Amount = 122 }; var account = accountBuilder .WithAccountNumberAs(accountNumber) .WithBalanceAs(initialAccountBalance) .Build(); config.DataStoreType.Returns(DataStoreType.Account); accountDataStore.GetAccount(accountNumber).Returns(account); bacsValidator.Validate(account, request) .Returns(MakePaymentResult.Ok); var paymentService = CreateService(); paymentService.MakePayment(request); account.Balance.Should().Be(expectedAccountBalance); }
public BacsPaymentValidationShould() { _account = new Account(); _makePaymentRequest = new MakePaymentRequest(); _validator = new BacsPaymentValidation(); }
public void Setup() { _request = new MakePaymentRequest(); _account = new Account(); _validationStrategy = new FasterPaymentValidationStrategy(); }
public MakePaymentResult MakePayment(MakePaymentRequest request) { // Although the implementation of the datastores is quite straightforward, we will more likely than not, want to expand it // by making it runtime configurable, in a more dynamic fashion than an app.config file var dataStore = DataStoreService.GetAccountDataStore(); var account = dataStore.GetAccount(request.DebtorAccountNumber); var result = new MakePaymentResult { Success = RulesService.IsMovementAuthorized(account, request.PaymentScheme, request.Amount) }; if (!result.Success) { LogService.LogException( new ConstraintException("Unable to make payment, rules engine rejected the operation")); return(result); } account.Balance -= request.Amount; // Update debtor account dataStore.UpdateAccount(account); // Question pending: does the creditor account not need to have the funds deposited? // Log the successfull operation LogService.LogOperation(request, result); return(result); }
public MakePaymentResult MakePayment(MakePaymentRequest request) { var result = new MakePaymentResult { Success = false }; if (request == null) { return(result); } IAccountDataStore accountDataStore = _accountDataStoreFactory.BuildAccountDataStore(); IValidator validator = _validatorFactory.BuildValidator(request); Account account = accountDataStore.GetAccount(request.DebtorAccountNumber); if (account == null || !validator.AccountCanMakePayment(account)) { return(result); } account.Balance -= request.Amount; accountDataStore.UpdateAccount(account); result.Success = true; return(result); }
// Abstract away following private helper methods into seprate sub-classes based and // may consider into Factories and write test cases accordingly, BUT for now I decided // not to do this at these stage - kept is simple for the purpose of readability. // NOTE: The account.AllowedPaymentSchemes.HasFlag(xxx) can be refactored into Is<T>Valid() private static MakePaymentResult TakePayment(MakePaymentRequest paymentRequest, Account accountInfo) { var paymentResult = new MakePaymentResult(); // TODO: Factory pattern for each type of payment! // For now just call appropriate helper methods switch (paymentRequest.PaymentScheme) { case PaymentScheme.Bacs: BacsPaymentProcess(accountInfo, paymentResult); break; case PaymentScheme.Chaps: ChapsPaymentProcess(accountInfo, paymentResult); break; case PaymentScheme.FasterPayments: FasterPaymentProcess(paymentRequest, accountInfo, paymentResult); break; default: throw new InvalidEnumArgumentException("Invalid PaymentScheme enum"); } return(paymentResult); }
public void Test_PaymentService_MakePayment_Success() { // arrange MakePaymentRequest makePaymentRequest = new MakePaymentRequest { Amount = 50, CreditorAccountNumber = "PR2408", DebtorAccountNumber = "DC2404", PaymentDate = DateTime.Now, PaymentScheme = PaymentScheme.Bacs }; Account account = new Account { AccountNumber = "PR2408", AllowedPaymentSchemes = AllowedPaymentSchemes.Bacs | AllowedPaymentSchemes.Chaps, Balance = 250, Status = AccountStatus.Live, }; MakePaymentResult makePaymentResult = new MakePaymentResult { Success = true }; _accountDataStore.Setup(x => x.GetAccount(makePaymentRequest.DebtorAccountNumber)).Returns(account); _accountDataStoreFactory.Setup(x => x.AccountDataStore(It.IsAny <string>())).Returns(_accountDataStore.Object); _makePaymentRequestValidator.Setup(x => x.IsAccountValidToMakePayment(account, makePaymentRequest)).Returns(makePaymentResult); // act makePaymentResult = _paymentSrevice.MakePayment(makePaymentRequest); // assert Assert.AreEqual(account.Balance, 200); Assert.AreEqual(makePaymentResult.Success, true); }
public void Setup() { _account = new Account(); _makePaymentRequest = new MakePaymentRequest(); _fasterPaymentsValidator = new FasterPaymentsValidator(); }
private bool IsAllowed(Account debtorAccount, MakePaymentRequest request) { return (debtorAccount != null && request.PaymentScheme == PaymentScheme.Bacs && debtorAccount.AllowedPaymentSchemes.HasFlag(AllowedPaymentSchemes.Bacs)); }
public void TestChapsFails() { // Test if Chaps payment fails (unauthorized payment scheme) var request = new MakePaymentRequest { Amount = 5001, CreditorAccountNumber = "1", DebtorAccountNumber = "2", PaymentDate = DateTime.UtcNow, PaymentScheme = PaymentScheme.Chaps }; var result = PaymentService.MakePayment(request); Assert.IsNotNull(result); Assert.IsInstanceOf <MakePaymentResult>(result); Assert.IsFalse(result.Success); // Test if Chaps payment fails (wrong account status) request = new MakePaymentRequest { Amount = 5001, CreditorAccountNumber = "2", DebtorAccountNumber = "1", PaymentDate = DateTime.UtcNow, PaymentScheme = PaymentScheme.Chaps }; result = PaymentService.MakePayment(request); Assert.IsNotNull(result); Assert.IsInstanceOf <MakePaymentResult>(result); Assert.IsFalse(result.Success); }
public void MakePaymentRequestWithInvalidPayementScheme() { Dictionary <String, Object> debitorAccount = (Dictionary <String, Object>)context["DebitorAccount"]; Dictionary <String, Object> creditorAccount = (Dictionary <String, Object>)context["CreditorAccount"]; DateTime paymentDate = DateTime.Now; string creditorAccountNumber = creditorAccount["AccountNumber"].ToString(); string debitorAccountNumber = debitorAccount["AccountNumber"].ToString(); decimal debitAmount = (decimal)context["DebitAmount"]; // Object status = Enum.Parse(typeof(AccountStatus), debitorAccount["AccountStatus"].ToString()); PaymentScheme debitorPaymentScheme = (PaymentScheme)Enum.Parse(typeof(PaymentScheme), debitorAccount["PaymentScheme"].ToString()); //Get invalid payment scheme var values = Enum.GetValues(typeof(PaymentScheme)); foreach (var val in values) { if (!(val.ToString().Equals(debitorAccount["PaymentScheme"].ToString()))) { debitorPaymentScheme = (PaymentScheme)Enum.Parse(typeof(PaymentScheme), val.ToString()); break; } } MakePaymentRequest request = new MakePaymentRequest(creditorAccountNumber, debitorAccountNumber, debitAmount, paymentDate, debitorPaymentScheme); PaymentService paymentService = new PaymentService(); MakePaymentResult result = paymentService.MakePayment(request); context["MakePaymentResult"] = result; }
public void TestFasterPaymentsFails() { // Test if FasterPayments payment fails (insuficient balance) var request = new MakePaymentRequest { Amount = 5001, CreditorAccountNumber = "1", DebtorAccountNumber = "2", PaymentDate = DateTime.UtcNow, PaymentScheme = PaymentScheme.FasterPayments }; var result = PaymentService.MakePayment(request); Assert.IsNotNull(result); Assert.IsInstanceOf <MakePaymentResult>(result); Assert.IsFalse(result.Success); // Test if FasterPayments payment fails (unauthorized payment scheme) request = new MakePaymentRequest { Amount = 5001, CreditorAccountNumber = "1", DebtorAccountNumber = "3", PaymentDate = DateTime.UtcNow, PaymentScheme = PaymentScheme.FasterPayments }; result = PaymentService.MakePayment(request); Assert.IsNotNull(result); Assert.IsInstanceOf <MakePaymentResult>(result); Assert.IsFalse(result.Success); }
public void Balance_Should_Not_Be_Deducted_After_Failed_Payment() { Account testAccount = GetTestAccount(); testAccount.AllowedPaymentSchemes = AllowedPaymentSchemes.Bacs; var dataStore = new Mock <IAccountDataStore>(); dataStore.Setup <Account>(d => d.GetAccount(It.IsAny <string>())).Returns (testAccount); dataStore.Setup(d => d.UpdateAccount(It.IsAny <Account>())).Callback((Account ac) => { testAccount = ac; }); var paymentService = new PaymentService(dataStore.Object, _validatePaymentFactory); MakePaymentRequest request = new MakePaymentRequest() { Amount = 500, PaymentScheme = PaymentScheme.FasterPayments }; var result = paymentService.MakePayment(request); //payment should fail as account only support Bacs whereas payment request is for fasterpayments Assert.IsFalse(result.Success); Assert.AreEqual(testAccount.Balance, 1500);//balance should stay at 1500 after failed payment no deduction }
public void Should_not_update_balance_if_validation_fails() { const string accountNumber = "12312312ABC"; const int accountBalance = 224; var request = new MakePaymentRequest { PaymentScheme = PaymentScheme.Bacs, DebtorAccountNumber = accountNumber, Amount = 122 }; var account = accountBuilder .WithAccountNumberAs(accountNumber) .WithBalanceAs(accountBalance) .Build(); config.DataStoreType.Returns(DataStoreType.Account); accountDataStore.GetAccount(Arg.Any <string>()).Returns(account); bacsValidator.Validate(account, request) .Returns(MakePaymentResult.Failure); var paymentService = CreateService(); paymentService.MakePayment(request); account.Balance.Should().Be(accountBalance); accountDataStore.DidNotReceive().UpdateAccount(account); }
public void ChapsPayment_Should_Fail_When_Account_IsNot_Live() { var testAccount = GetTestAccount(); testAccount.AllowedPaymentSchemes = AllowedPaymentSchemes.Chaps; testAccount.Status = AccountStatus.Disabled; var dataStore = new Mock <IAccountDataStore>(); dataStore.Setup <Account>(d => d.GetAccount(It.IsAny <string>())).Returns ((string accountNo) => { return(testAccount); }); dataStore.Setup(d => d.UpdateAccount(It.IsAny <Account>())).Callback((Account ac) => { testAccount = ac; }); var paymentService = new PaymentService(dataStore.Object, _validatePaymentFactory); MakePaymentRequest request = new MakePaymentRequest() { Amount = 500, PaymentScheme = PaymentScheme.Chaps }; var result = paymentService.MakePayment(request); Assert.IsFalse(result.Success); Assert.AreEqual(testAccount.Balance, 1500);//balance should stay same as payment should fail }
public MakePaymentResult MakePayment(MakePaymentRequest request) { var(_, debtorAccountNumber, amount, _, paymentScheme) = request; var(accountExists, account) = _accountDataStore.GetAccount(debtorAccountNumber); if (!accountExists) { return(InvalidAccountResult(debtorAccountNumber)); } var paymentValidator = PaymentSchemeValidators[paymentScheme]; var(isValid, validationMessage) = paymentValidator.IsRequestValidForPayment(request, account); if (!isValid) { return(AccountNotAllowedForPaymentResult(validationMessage)); } var newBalance = account.Balance - amount; _accountDataStore.UpdateAccount(account with { Balance = newBalance }); return(PaymentSuccessfulResult()); }