Пример #1
0
        public OrdersDataContract.CreateOrderResponse Create(OrdersDataContract.CreateOrderRequest request)
        {
            var response = new OrdersDataContract.CreateOrderResponse();

            try
            {
                var app = _appRepository.Get(request.Order.App.AppId.Value);
                if (app == null)
                {
                    response.Exception = new ResourceNotFoundException("App not found.");
                    return(response);
                }

                var customer = _userRepository.Get(request.User.UserId.Value);
                if (customer == null)
                {
                    response.Exception = new ResourceNotFoundException("Customer not found.");
                    return(response);
                }

                var creditCard          = default(CreditCard);
                var creditCardRequested = request.Order.PaymentInfo.CreditCard;

                if (creditCardRequested.CreditCardId.HasValue)
                {
                    creditCard = customer.GetCreditCard(creditCardRequested.CreditCardId);
                    if (creditCard == null)
                    {
                        response.Exception = new NotAllowedOperationException("Credit card does not belong to this customer");
                        return(response);
                    }
                }

                var order = new Order(customer, app);

                _orderRepository.Add(order);
                _unitOfWork.Commit();

                var processPaymentHandler = new ProcessPaymentHandler()
                {
                    OrderId        = order.OrderId,
                    InstantBuyKey  = creditCard?.InstantBuyKey,
                    Brand          = (Streaming.Handlers.CreditCardBrand)creditCardRequested?.Brand,
                    Number         = creditCardRequested?.Number,
                    ExpMonth       = creditCardRequested?.ExpMonth,
                    ExpYear        = creditCardRequested?.ExpYear,
                    HolderName     = creditCardRequested?.HolderName,
                    SecurityCode   = creditCardRequested?.SecurityCode,
                    SaveCreditCard = request.Order.PaymentInfo?.SaveCreditCard
                };

                _paymentProducer.Produce(processPaymentHandler.Serialize());
            }
            catch (Exception ex)
            {
                response.Exception = new InvalidResourceOperationException(ex.Message);;
            }

            return(response);
        }
Пример #2
0
        public async void Handle_ShouldProcessPayment()
        {
            var accountsRepo = new Mock <IAccountRepository>();

            var account = new Account(Guid.NewGuid(), 100);

            account.AddPaymentRequest(new DateTime(2020, 3, 1), 40);

            accountsRepo.Setup(a => a.GetAsync(account.Id)).ReturnsAsync(account);

            var paymentRepo = new Mock <IPaymentRepository>();

            var payment = account.PaymentRequests.First();

            paymentRepo.Setup(p => p.GetAsync(payment.Id)).ReturnsAsync(payment);
            paymentRepo.Setup(p => p.UnitOfWork.SaveEntitiesAsync(It.IsAny <CancellationToken>())).ReturnsAsync(true);

            var reason  = "Processed";
            var command = new ProcessPaymentCommand(account.Id, payment.Id);

            var handler = new ProcessPaymentHandler(accountsRepo.Object, paymentRepo.Object);
            await handler.Handle(command, new CancellationToken());

            Assert.Equal(PaymentStatus.Processed, payment.Status);
            Assert.Equal(reason, payment.Reason);
        }
Пример #3
0
 public Arrangements(ICallBankApi callBankApi,
                     IServiceProvider serviceProvider,
                     IPublishEndpoint publishEndpoint,
                     ConsumeContext <ProcessPayment> consumeContext,
                     List <IEvent> firedEvents)
 {
     this.callBankApi = callBankApi;
     ConsumeContext   = consumeContext;
     PublishEndpoint  = publishEndpoint;
     FiredEvents      = firedEvents;
     SUT = new ProcessPaymentHandler(serviceProvider);
 }
 public Arrangements(IAcquiringBank bank,
                     IServiceProvider serviceProvider,
                     IPublishEndpoint publishEndpoint,
                     ConsumeContext <ProcessPayment> consumeContext,
                     List <IEvent> firedEvents)
 {
     this.bank       = bank;
     ConsumeContext  = consumeContext;
     PublishEndpoint = publishEndpoint;
     FiredEvents     = firedEvents;
     SUT             = new ProcessPaymentHandler(serviceProvider);
 }
Пример #5
0
        public void GivenADuplicatePaymentRequest_ShouldNotTalkToBank_AndShouldThrowBadRequestException()
        {
            // arrange
            _paymentHistoryRepo.IsDuplicate(Arg.Any <string>()).Returns(true);

            var handler        = new ProcessPaymentHandler(_paymentHistoryRepo, _bankApi);
            var paymentRequest = BuildValidTestPaymentRequest();

            // act
            var ex = Assert.Throws <BadRequestException>(() => handler.Handle(paymentRequest));

            // assert
            Assert.Equal("Duplicate request.", ex.Message);
        }
Пример #6
0
        public void GivenAPaymentRequestThatSucceeds_ThenRequestLogged_AndLoggedDetailsShowFailure()
        {
            // arrange
            var paymentRequest = BuildValidTestPaymentRequest();

            _bankApi.MakePayment(paymentRequest).Returns(false);

            var handler = new ProcessPaymentHandler(_paymentHistoryRepo, _bankApi);

            // act
            handler.Handle(paymentRequest);

            // assert
            _paymentHistoryRepo.Received(1).LogPayment(Arg.Is <LoggedPaymentRequest>(x => Equals(x.Success, false)));
        }
Пример #7
0
        public void GivenAPaymentRequest_IfRejectedByBank_ShouldReturnUnsuccessful()
        {
            // arrange
            var paymentRequest = BuildValidTestPaymentRequest();

            _bankApi.MakePayment(paymentRequest).Returns(false);

            var handler = new ProcessPaymentHandler(_paymentHistoryRepo, _bankApi);

            // act
            var result = handler.Handle(paymentRequest);

            // assert
            Assert.False(result);
        }
        public async Task Returns_Error_Response_When_Cache_Already_Contains_Key()
        {
            const string  CARD_NUMBER = "4111111111111111";
            const string  CVV         = "123";
            const string  CURRENCY    = "GBP";
            const string  OWNER       = "owner";
            const decimal AMOUNT      = .1m;
            var           EXPIRY      = new MonthYear {
                Year = 2021, Month = 01
            };

            var testNow     = new DateTime(2021, 02, 01);
            var nowProvider = new NowProvider(testNow);

            var request = new ProcessPaymentRequest
            {
                CardNumber = CARD_NUMBER,
                Amount     = AMOUNT,
                Currency   = CURRENCY,
                CVV        = CVV,
                Expiry     = EXPIRY,
                Owner      = OWNER
            };

            var acqBankMock     = new Mock <IAcquiringBank>();
            var memoryCacheMock = new Mock <IMemoryCache>();

            memoryCacheMock
            .Setup(mock => mock.TryGetValue(It.IsAny <string>(), out It.Ref <object> .IsAny))
            .Returns(true);

            using var context = Setup.CreateContext();

            var handler = new ProcessPaymentHandler(
                acqBankMock.Object,
                nowProvider,
                memoryCacheMock.Object,
                _logger,
                _cachingOptions,
                context);

            var result = (await handler.Handle(request, CancellationToken.None)).ErrorOrDefault;

            Assert.AreEqual(HttpStatusCode.TooManyRequests, result.StatusCode);
        }
        public static async Task <IActionResult> Status([HttpTrigger(
                                                             AuthorizationLevel.Function,
                                                             "POST",
                                                             Route = "fakeBank/payment/")]
                                                        HttpRequestMessage request)
        {
            // Auth checks would be done here against or at Api Gateway level to ensure
            // person making request has access to do so for the merchant.

            var bankApi            = new FakeBankApi();
            var paymentHistoryRepo = new PaymentHistoryRepo();
            var handler            = new ProcessPaymentHandler(paymentHistoryRepo, bankApi);

            try
            {
                var paymentRequest = DeserializeRequestFromBody(await request.Content.ReadAsStringAsync());

                var validation = new ProcessPaymentValidator().Validate(paymentRequest);
                if (!validation.IsValid)
                {
                    return(new BadRequestObjectResult(JsonConvert.SerializeObject(validation.Errors)));
                }

                var isCompleted = handler.Handle(paymentRequest);

                if (isCompleted)
                {
                    return(new OkObjectResult(paymentRequest));
                }

                return(new BadRequestObjectResult("Payment Request Unsuccessful"));
            }
            catch (BadRequestException ex)
            {
                return(new BadRequestObjectResult(ex.Message));
            }
        }
        public async Task Adds_Key_To_Cache()
        {
            const string  GENERATED_REQUEST_KEY = "1v9ESIpTOJ1czY9SZW5oWf2DiRWrNOB4kHdPFODIYjI=";
            const int     RETURNED_PAYMENT_ID   = 1;
            const string  CARD_NUMBER           = "4111111111111111";
            const string  CVV      = "123";
            const string  CURRENCY = "GBP";
            const string  OWNER    = "owner";
            const decimal AMOUNT   = .1m;
            var           EXPIRY   = new MonthYear {
                Year = 2021, Month = 01
            };

            var testNow     = new DateTime(2021, 02, 01);
            var nowProvider = new NowProvider(testNow);

            var request = new ProcessPaymentRequest
            {
                CardNumber = CARD_NUMBER,
                Amount     = AMOUNT,
                Currency   = CURRENCY,
                CVV        = CVV,
                Expiry     = EXPIRY,
                Owner      = OWNER
            };

            var acqBankMock = new Mock <IAcquiringBank>();

            acqBankMock
            .Setup(mock => mock.SendPayment(It.IsAny <AcquiringBankRequest>()))
            .ReturnsAsync(new AcquiringBankResponse
            {
                Status    = AcquiringBankResponseStatus.Success,
                PaymentId = RETURNED_PAYMENT_ID
            });

            var memoryCacheMock = new Mock <IMemoryCache>();
            var cacheEntryMock  = new Mock <ICacheEntry>();

            memoryCacheMock
            .Setup(mock => mock.TryGetValue(It.IsAny <string>(), out It.Ref <object> .IsAny))
            .Returns(false);

            memoryCacheMock
            .Setup(mock => mock.CreateEntry(It.IsAny <object>()))
            .Returns(cacheEntryMock.Object);

            using var context = Setup.CreateContext();

            var handler = new ProcessPaymentHandler(
                acqBankMock.Object,
                nowProvider,
                memoryCacheMock.Object,
                _logger,
                _cachingOptions,
                context);
            var result = await handler.Handle(request, CancellationToken.None);

            memoryCacheMock.Verify(
                mock => mock.CreateEntry(GENERATED_REQUEST_KEY),
                Times.Once);
        }
        public async Task Stores_Masked_Card_Details()
        {
            const int     RETURNED_PAYMENT_ID = 1;
            const string  CARD_NUMBER         = "4111111111111111";
            const string  CVV      = "123";
            const string  CURRENCY = "GBP";
            const string  OWNER    = "owner";
            const decimal AMOUNT   = .1m;
            var           EXPIRY   = new MonthYear {
                Year = 2021, Month = 01
            };

            var testNow     = new DateTime(2021, 02, 01);
            var nowProvider = new NowProvider(testNow);

            var request = new ProcessPaymentRequest
            {
                CardNumber = CARD_NUMBER,
                Amount     = AMOUNT,
                Currency   = CURRENCY,
                CVV        = CVV,
                Expiry     = EXPIRY,
                Owner      = OWNER
            };

            var acqBankMock = new Mock <IAcquiringBank>();

            acqBankMock
            .Setup(mock => mock.SendPayment(It.IsAny <AcquiringBankRequest>()))
            .ReturnsAsync(new AcquiringBankResponse
            {
                Status    = AcquiringBankResponseStatus.Success,
                PaymentId = RETURNED_PAYMENT_ID
            });

            var memoryCacheMock = new Mock <IMemoryCache>();
            var cacheEntryMock  = new Mock <ICacheEntry>();

            memoryCacheMock
            .Setup(mock => mock.TryGetValue(It.IsAny <string>(), out It.Ref <object> .IsAny))
            .Returns(false);

            memoryCacheMock
            .Setup(mock => mock.CreateEntry(It.IsAny <object>()))
            .Returns(cacheEntryMock.Object);

            using var context = Setup.CreateContext();

            var handler = new ProcessPaymentHandler(
                acqBankMock.Object,
                nowProvider,
                memoryCacheMock.Object,
                _logger,
                _cachingOptions,
                context);
            var result = (await handler.Handle(request, CancellationToken.None)).SuccessOrDefault;

            Assert.IsTrue(result.Success);

            // assert that a record is in the db for this payment id,
            var foundPayment = context.ProcessedPayments.Find(result.PaymentId);

            // should be masked
            Assert.AreEqual("************1111", foundPayment.CardNumber);
            Assert.AreEqual("***", foundPayment.CVV);
            Assert.AreEqual(AMOUNT, foundPayment.Amount);
            Assert.AreEqual(CURRENCY, foundPayment.Currency);
            Assert.AreEqual(OWNER, foundPayment.Owner);

            Assert.AreEqual(EXPIRY.Year, foundPayment.Expiry.Year);
            Assert.AreEqual(EXPIRY.Month, foundPayment.Expiry.Month);
        }
        public async Task Unsuccesful_Bank_Result_Does_Not_Throw()
        {
            const int     RETURNED_PAYMENT_ID = 1;
            const string  CARD_NUMBER         = "4111111111111111";
            const string  CVV      = "123";
            const string  CURRENCY = "GBP";
            const string  OWNER    = "owner";
            const decimal AMOUNT   = .1m;
            var           EXPIRY   = new MonthYear {
                Year = 2021, Month = 01
            };

            var testNow     = new DateTime(2021, 02, 01);
            var nowProvider = new NowProvider(testNow);

            var request = new ProcessPaymentRequest
            {
                CardNumber = CARD_NUMBER,
                Amount     = AMOUNT,
                Currency   = CURRENCY,
                CVV        = CVV,
                Expiry     = EXPIRY,
                Owner      = OWNER
            };

            using var context = Setup.CreateContext();

            var acqBankMock = new Mock <IAcquiringBank>();

            acqBankMock
            .Setup(mock => mock.SendPayment(It.IsAny <AcquiringBankRequest>()))
            .ReturnsAsync(new AcquiringBankResponse
            {
                Status    = AcquiringBankResponseStatus.Acquiring_Bank_Unreachable,
                PaymentId = RETURNED_PAYMENT_ID
            });

            var memoryCacheMock = new Mock <IMemoryCache>();
            var cacheEntryMock  = new Mock <ICacheEntry>();

            memoryCacheMock
            .Setup(mock => mock.TryGetValue(It.IsAny <string>(), out It.Ref <object> .IsAny))
            .Returns(false);

            memoryCacheMock
            .Setup(mock => mock.CreateEntry(It.IsAny <object>()))
            .Returns(cacheEntryMock.Object);

            // use mock of acqbank to ensure we get a failure here
            var handler = new ProcessPaymentHandler(
                acqBankMock.Object,
                nowProvider,
                memoryCacheMock.Object,
                _logger,
                _cachingOptions,
                context);
            var result = (await handler.Handle(request, CancellationToken.None)).SuccessOrDefault;

            // ensure payment is still saved in failed state
            var foundPayment = context.ProcessedPayments.Find(result.PaymentId);

            Assert.AreEqual(RETURNED_PAYMENT_ID, result.PaymentId);
            Assert.IsFalse(result.Success);

            Assert.AreEqual("************1111", foundPayment.CardNumber);
            Assert.AreEqual("***", foundPayment.CVV);
            Assert.AreEqual(AMOUNT, foundPayment.Amount);
            Assert.AreEqual(CURRENCY, foundPayment.Currency);
            Assert.AreEqual(OWNER, foundPayment.Owner);

            Assert.AreEqual(EXPIRY.Year, foundPayment.Expiry.Year);
            Assert.AreEqual(EXPIRY.Month, foundPayment.Expiry.Month);
        }