public async Task <ActionResult <ProcessPaymentResult> > ProcessOrderPayments(string orderId, string paymentId, [SwaggerOptional] BankCardInfo bankCardInfo) { var order = await _customerOrderService.GetByIdAsync(orderId, CustomerOrderResponseGroup.Full.ToString()); if (order == null) { var searchCriteria = AbstractTypeFactory <CustomerOrderSearchCriteria> .TryCreateInstance(); searchCriteria.Number = orderId; searchCriteria.ResponseGroup = CustomerOrderResponseGroup.Full.ToString(); var orders = await _searchService.SearchCustomerOrdersAsync(searchCriteria); order = orders.Results.FirstOrDefault(); } if (order == null) { throw new InvalidOperationException($"Cannot find order with ID {orderId}"); } var payment = order.InPayments.FirstOrDefault(x => x.Id == paymentId); if (payment == null) { throw new InvalidOperationException($"Cannot find payment with ID {paymentId}"); } var store = await _storeService.GetByIdAsync(order.StoreId); var paymentMethod = store.PaymentMethods.FirstOrDefault(x => x.Code == payment.GatewayCode); if (paymentMethod == null) { throw new InvalidOperationException($"Cannot find payment method with code {payment.GatewayCode}"); } var context = new ProcessPaymentEvaluationContext { OrderId = order.Id, PaymentId = payment.Id, //TODO //Store = store, //BankCardInfo = bankCardInfo }; var result = paymentMethod.ProcessPayment(context); if (result.OuterId != null) { payment.OuterId = result.OuterId; } await _customerOrderService.SaveChangesAsync(new[] { order }); return(Ok(result)); }
public virtual async Task SaveSubscriptionsAsync(Subscription[] subscriptions) { var pkMap = new PrimaryKeyResolvingMap(); var changedEntries = new List <GenericChangedEntry <Subscription> >(); using (var repository = _subscriptionRepositoryFactory()) { var existEntities = await repository.GetSubscriptionsByIdsAsync(subscriptions.Where(x => !x.IsTransient()).Select(x => x.Id).ToArray()); foreach (var subscription in subscriptions) { //Generate numbers for new subscriptions if (string.IsNullOrEmpty(subscription.Number)) { var store = await _storeService.GetByIdAsync(subscription.StoreId, StoreResponseGroup.StoreInfo.ToString()); var numberTemplate = store.Settings.GetSettingValue(ModuleConstants.Settings.General.NewNumberTemplate.Name, ModuleConstants.Settings.General.NewNumberTemplate.DefaultValue.ToString()); subscription.Number = _uniqueNumberGenerator.GenerateNumber(numberTemplate); } //Save subscription order prototype with same as subscription Number if (subscription.CustomerOrderPrototype != null) { subscription.CustomerOrderPrototype.Number = subscription.Number; subscription.CustomerOrderPrototype.IsPrototype = true; await _customerOrderService.SaveChangesAsync(new[] { subscription.CustomerOrderPrototype }); } var originalEntity = existEntities.FirstOrDefault(x => x.Id == subscription.Id); var modifiedEntity = AbstractTypeFactory <SubscriptionEntity> .TryCreateInstance().FromModel(subscription, pkMap); if (originalEntity != null) { changedEntries.Add(new GenericChangedEntry <Subscription>(subscription, originalEntity.ToModel(AbstractTypeFactory <Subscription> .TryCreateInstance()), EntryState.Modified)); modifiedEntity.Patch(originalEntity); //force the subscription.ModifiedDate update, because the subscription object may not have any changes in its properties originalEntity.ModifiedDate = DateTime.UtcNow; } else { repository.Add(modifiedEntity); changedEntries.Add(new GenericChangedEntry <Subscription>(subscription, EntryState.Added)); } } //Raise domain events await _eventPublisher.Publish(new SubscriptionChangingEvent(changedEntries)); await repository.UnitOfWork.CommitAsync(); pkMap.ResolvePrimaryKeys(); ClearCacheFor(subscriptions); await _eventPublisher.Publish(new SubscriptionChangedEvent(changedEntries)); } }
public async Task <bool> Handle(CancelOrderPaymentCommand request, CancellationToken cancellationToken) { var orderAggregate = await _customerOrderAggregateRepository.GetOrderByIdAsync(request.Payment.OrderId); orderAggregate.CancelOrderPayment(request.Payment); await _customerOrderService.SaveChangesAsync(new[] { orderAggregate.Order }); return(true); }
public async Task <CustomerOrderAggregate> Handle(AddOrUpdateOrderPaymentCommand request, CancellationToken cancellationToken) { var orderAggregate = await _customerOrderAggregateRepository.GetOrderByIdAsync(request.OrderId); var paymentId = request.Payment.Id?.Value; var payment = orderAggregate.Order.InPayments.FirstOrDefault(s => paymentId != null && s.Id == paymentId); payment = request.Payment.MapTo(payment); var criteria = new PaymentMethodsSearchCriteria { IsActive = true, StoreId = orderAggregate.Order.StoreId, }; if (!string.IsNullOrEmpty(payment.GatewayCode)) { criteria.Codes = new List <string> { payment.GatewayCode }; } var result = await _paymentMethodsSearchService.SearchAsync(criteria); await orderAggregate.AddPaymentAsync(payment, result.Results); if (!request.Payment.DynamicProperties.IsNullOrEmpty()) { await orderAggregate.UpdatePaymentDynamicProperties(paymentId, request.Payment.DynamicProperties); } await _customerOrderService.SaveChangesAsync(new[] { orderAggregate.Order }); return(orderAggregate); }
protected virtual async Task HandleOrderChangesAsync(CustomerOrder customerOrder) { //Prevent creating subscription for customer orders with other operation type (it is need for preventing to handling subscription prototype and recurring order creations) if (!customerOrder.IsPrototype && string.IsNullOrEmpty(customerOrder.SubscriptionId)) { try { var subscription = await _subscriptionBuilder.TryCreateSubscriptionFromOrderAsync(customerOrder); if (subscription != null) { await _subscriptionBuilder.TakeSubscription(subscription).ActualizeAsync(); await _subscriptionService.SaveSubscriptionsAsync(new[] { subscription }); //Link subscription with customer order customerOrder.SubscriptionId = subscription.Id; customerOrder.SubscriptionNumber = subscription.Number; //Save order changes await _customerOrderService.SaveChangesAsync(new[] { customerOrder }); } } catch (Exception ex) { throw new CreateSubscriptionException(ex); } } }
protected virtual async Task TryToCancelOrder(GenericChangedEntry <CustomerOrder> changedEntry) { var store = await _storeService.GetByIdAsync(changedEntry.NewEntry.StoreId); //Try to load payment methods for payments foreach (var payment in changedEntry.NewEntry.InPayments) { payment.PaymentMethod = store.PaymentMethods.FirstOrDefault(p => p.Code.EqualsInvariant(payment.GatewayCode)); } var toCancelPayments = new List <PaymentIn>(); var isOrderCancelled = !changedEntry.OldEntry.IsCancelled && changedEntry.NewEntry.IsCancelled; if (isOrderCancelled) { toCancelPayments = changedEntry.NewEntry.InPayments?.ToList(); } else { foreach (var canceledPayment in changedEntry.NewEntry?.InPayments.Where(x => x.IsCancelled) ?? Enumerable.Empty <PaymentIn>()) { var oldSamePayment = changedEntry.OldEntry?.InPayments.FirstOrDefault(x => x == canceledPayment); if (oldSamePayment != null && !oldSamePayment.IsCancelled) { toCancelPayments.Add(canceledPayment); } } } TryToCancelOrderPayments(toCancelPayments); if (!toCancelPayments.IsNullOrEmpty()) { await _orderService.SaveChangesAsync(new[] { changedEntry.NewEntry }); } }
public virtual async Task <CustomerOrder> PlaceCustomerOrderFromCartAsync(ShoppingCart cart) { var customerOrder = ConvertCartToOrder(cart); await _customerOrderService.SaveChangesAsync(new[] { customerOrder }); return(customerOrder); }
public async Task Process() { var criteria = new SubscriptionSearchCriteria { Statuses = new[] { SubscriptionStatus.Active, SubscriptionStatus.PastDue, SubscriptionStatus.Trialing, SubscriptionStatus.Unpaid }.Select(x => x.ToString()).ToArray(), Take = 0, }; var result = await _subscriptionSearchService.SearchSubscriptionsAsync(criteria); var batchSize = 20; for (var i = 0; i < result.TotalCount; i += batchSize) { criteria.Skip = i; criteria.Take = batchSize; result = await _subscriptionSearchService.SearchSubscriptionsAsync(criteria); var subscriptions = await _subscriptionService.GetByIdsAsync(result.Results.Select(x => x.Id).ToArray()); foreach (var subscription in subscriptions) { var subscriptionBuilder = await _subscriptionBuilder.TakeSubscription(subscription).ActualizeAsync(); var newOrder = await subscriptionBuilder.TryToCreateRecurrentOrderAsync(); if (newOrder != null) { await _customerOrderService.SaveChangesAsync(new[] { newOrder }); } } } }
public async Task SaveChangesAsync_CreateNewOrder() { //Arrange var order = GetTestOrder($"order{DateTime.Now:O}"); //Act await _customerOrderService.SaveChangesAsync(new[] { order }); order = await _customerOrderService.GetByIdAsync(order.Id); //Assert Assert.Equal(PaymentStatus.Pending, order.InPayments.First().PaymentStatus); Assert.NotNull(order); Assert.Equal(PaymentStatus.Pending, order.InPayments.First().PaymentStatus); Assert.NotNull(order.InPayments.First().Status); }
public async Task ImportAsync(Stream inputStream, ExportImportOptions options, Action <ExportImportProgressInfo> progressCallback, ICancellationToken cancellationToken) { //TDODO: Use AbstractTypeFactory for deserialization of the derived types cancellationToken.ThrowIfCancellationRequested(); var progressInfo = new ExportImportProgressInfo(); var orderTotalCount = 0; using (var streamReader = new StreamReader(inputStream)) using (var reader = new JsonTextReader(streamReader)) { while (reader.Read()) { if (reader.TokenType == JsonToken.PropertyName) { if (reader.Value.ToString() == "OrderTotalCount") { orderTotalCount = reader.ReadAsInt32() ?? 0; } else if (reader.Value.ToString() == "CustomerOrders") { reader.Read(); if (reader.TokenType == JsonToken.StartArray) { reader.Read(); var orders = new List <CustomerOrder>(); var orderCount = 0; while (reader.TokenType != JsonToken.EndArray) { var order = _serializer.Deserialize <CustomerOrder>(reader); orders.Add(order); orderCount++; reader.Read(); } for (var i = 0; i < orderCount; i += _batchSize) { await _customerOrderService.SaveChangesAsync(orders.Skip(i).Take(_batchSize).ToArray()); if (orderCount > 0) { progressInfo.Description = $"{ i } of { orderCount } orders have been imported"; } else { progressInfo.Description = $"{ i } orders have been imported"; } progressCallback(progressInfo); } } } } } } }
public virtual async Task <bool> Handle(ChangeOrderStatusCommand request, CancellationToken cancellationToken) { var orderAggregate = await _customerOrderAggregateRepository.GetOrderByIdAsync(request.OrderId); orderAggregate.ChangeOrderStatus(request.Status); await _customerOrderService.SaveChangesAsync(new[] { orderAggregate.Order }); return(true); }
public async Task SaveChangesAsync_CreateNewOrder() { //Arrange var order = GetTestOrder($"order{DateTime.Now:O}"); var cacheKey = CacheKey.With(_customerOrderService.GetType(), "GetByIdsAsync", string.Join("-", order.Id), null); _platformMemoryCacheMock.Setup(pmc => pmc.CreateEntry(cacheKey)).Returns(_cacheEntryMock.Object); //Act await _customerOrderService.SaveChangesAsync(new[] { order }); order = await _customerOrderService.GetByIdAsync(order.Id); //Assert Assert.Equal(PaymentStatus.Pending, order.InPayments.First().PaymentStatus); Assert.NotNull(order); Assert.Equal(PaymentStatus.Pending, order.InPayments.First().PaymentStatus); Assert.NotNull(order.InPayments.First().Status); }
public async Task <IActionResult> CreateReccurentOrderForSubscription([FromBody] Subscription subscription) { var subscriptionBuilder = await _subscriptionBuilder.TakeSubscription(subscription).ActualizeAsync(); var order = await subscriptionBuilder.TryToCreateRecurrentOrderAsync(forceCreation : true); await _customerOrderService.SaveChangesAsync(new[] { order }); return(Ok(order)); }
public virtual async Task <CustomerOrderAggregate> Handle(UpdateOrderShipmentDynamicPropertiesCommand request, CancellationToken cancellationToken) { var orderAggregate = await _customerOrderAggregateRepository.GetOrderByIdAsync(request.OrderId); await orderAggregate.UpdateShipmentDynamicProperties(request.ShipmentId, request.DynamicProperties); await _customerOrderService.SaveChangesAsync(new[] { orderAggregate.Order }); return(orderAggregate); }
protected virtual async Task TryToCancelOrders(CustomerOrder changedOrder, CustomerOrder oldOrder) { var store = await _storeService.GetByIdAsync(changedOrder.StoreId, StoreResponseGroup.StoreInfo.ToString()); //Try to load payment methods for payments var gatewayCodes = changedOrder.InPayments.Select(x => x.GatewayCode).ToArray(); var paymentMethods = await GetPaymentMethodsAsync(store.Id, gatewayCodes); foreach (var payment in changedOrder.InPayments) { payment.PaymentMethod = paymentMethods.FirstOrDefault(x => x.Code == payment.GatewayCode); } var toCancelPayments = new List <PaymentIn>(); var isOrderCancelled = !oldOrder.IsCancelled && changedOrder.IsCancelled; if (isOrderCancelled) { toCancelPayments = changedOrder.InPayments?.ToList(); } else { foreach (var canceledPayment in changedOrder?.InPayments.Where(x => x.IsCancelled)) { var oldSamePayment = oldOrder?.InPayments.FirstOrDefault(x => x == canceledPayment); if (oldSamePayment != null && !oldSamePayment.IsCancelled) { toCancelPayments.Add(canceledPayment); } } } TryToCancelOrderPayments(toCancelPayments, changedOrder); if (!toCancelPayments.IsNullOrEmpty()) { await _orderService.SaveChangesAsync(new[] { changedOrder }); } }
public virtual async Task <ProcessPaymentRequestResult> Handle(ProcessOrderPaymentCommand request, CancellationToken cancellationToken) { var orderAggregate = await _customerOrderAggregateRepository.GetOrderByIdAsync(request.OrderId); if (orderAggregate == null) { throw new OperationCanceledException($"Can't find an order with ID: {request.OrderId}"); } var store = await _storeService.GetByIdAsync(orderAggregate.Order.StoreId, StoreResponseGroup.StoreInfo.ToString()); var processPaymentRequest = new ProcessPaymentRequest { OrderId = orderAggregate.Order.Id, PaymentId = request.PaymentId, StoreId = orderAggregate.Order.StoreId, Store = store, BankCardInfo = request.BankCardInfo }; var result = orderAggregate.ProcessOrderPayment(processPaymentRequest); await _customerOrderService.SaveChangesAsync(new[] { orderAggregate.Order }); return(result); }
protected virtual async Task DoBulkActionsWithOrderAggregate(PaymentIn[] payments, Action <CustomerOrder, PaymentIn> action) { if (payments.Any(x => string.IsNullOrEmpty(x.OrderId))) { throw new OperationCanceledException($"{ nameof(PaymentIn.OrderId) } must be set."); } var oderIds = payments.Select(x => x.OrderId).Distinct().ToArray(); if (oderIds.Any()) { var ordersAggregates = await _customerOrderService.GetByIdsAsync(oderIds); foreach (var payment in payments) { var orderAggregateRoot = ordersAggregates.FirstOrDefault(x => x.Id == payment.OrderId); if (orderAggregateRoot != null) { orderAggregateRoot.InPayments.Remove(payment); orderAggregateRoot.InPayments.Add(payment); } } await _customerOrderService.SaveChangesAsync(ordersAggregates); } }
[Consumes("application/json", new[] { "application/json-patch+json" })] // It's a trick that allows ASP.NET infrastructure to select this action with body and ProcessOrderPaymentsWithoutBankCardInfo if no body public async Task <ActionResult <ProcessPaymentRequestResult> > ProcessOrderPayments([FromRoute] string orderId, [FromRoute] string paymentId, [FromBody] BankCardInfo bankCardInfo) { var order = await _customerOrderService.GetByIdAsync(orderId, CustomerOrderResponseGroup.Full.ToString()); if (order == null) { var searchCriteria = AbstractTypeFactory <CustomerOrderSearchCriteria> .TryCreateInstance(); searchCriteria.Number = orderId; searchCriteria.ResponseGroup = CustomerOrderResponseGroup.Full.ToString(); var orders = await _searchService.SearchCustomerOrdersAsync(searchCriteria); order = orders.Results.FirstOrDefault(); } if (order == null) { throw new InvalidOperationException($"Cannot find order with ID {orderId}"); } var authorizationResult = await _authorizationService.AuthorizeAsync(User, order, new OrderAuthorizationRequirement(ModuleConstants.Security.Permissions.Update)); if (!authorizationResult.Succeeded) { return(Unauthorized()); } var inPayment = order.InPayments.FirstOrDefault(x => x.Id == paymentId); if (inPayment == null) { throw new InvalidOperationException($"Cannot find payment with ID {paymentId}"); } if (inPayment.PaymentMethod == null) { throw new InvalidOperationException($"Cannot find payment method with code {inPayment.GatewayCode}"); } var store = await _storeService.GetByIdAsync(order.StoreId, StoreResponseGroup.StoreInfo.ToString()); if (store == null) { throw new InvalidOperationException($"Cannot find store with ID {order.StoreId}"); } var request = new ProcessPaymentRequest { OrderId = order.Id, Order = order, PaymentId = inPayment.Id, Payment = inPayment, StoreId = order.StoreId, Store = store, BankCardInfo = bankCardInfo }; var result = inPayment.PaymentMethod.ProcessPayment(request); if (result.OuterId != null) { inPayment.OuterId = result.OuterId; } // Exclusive status set for DefaultManualPaymentMethod, otherwise the order be stuck in the "New" state. // Current payment flow suggests payment processing by payment method code, // but, unfortunately, there is no way to do set status directly in DefaultManualPaymentMethod, because it will produce cyclical references between order and payment modules. // One day, we should change the flow to commonly divide payment and order processing, but now it isn't. if (inPayment.PaymentMethod is DefaultManualPaymentMethod) { order.Status = result.NewPaymentStatus.ToString(); } await _customerOrderService.SaveChangesAsync(new[] { order }); return(Ok(result)); }
[Consumes("application/json", new[] { "application/json-patch+json" })] // It's a trick that allows ASP.NET infrastructure to select this action with body and ProcessOrderPaymentsWithoutBankCardInfo if no body public async Task <ActionResult <ProcessPaymentRequestResult> > ProcessOrderPayments([FromRoute] string orderId, [FromRoute] string paymentId, [FromBody] BankCardInfo bankCardInfo) { var order = await _customerOrderService.GetByIdAsync(orderId, CustomerOrderResponseGroup.Full.ToString()); if (order == null) { var searchCriteria = AbstractTypeFactory <CustomerOrderSearchCriteria> .TryCreateInstance(); searchCriteria.Number = orderId; searchCriteria.ResponseGroup = CustomerOrderResponseGroup.Full.ToString(); var orders = await _searchService.SearchCustomerOrdersAsync(searchCriteria); order = orders.Results.FirstOrDefault(); } if (order == null) { throw new InvalidOperationException($"Cannot find order with ID {orderId}"); } var authorizationResult = await _authorizationService.AuthorizeAsync(User, order, new OrderAuthorizationRequirement(ModuleConstants.Security.Permissions.Update)); if (!authorizationResult.Succeeded) { return(Unauthorized()); } var inPayment = order.InPayments.FirstOrDefault(x => x.Id == paymentId); if (inPayment == null) { throw new InvalidOperationException($"Cannot find payment with ID {paymentId}"); } if (inPayment.PaymentMethod == null) { throw new InvalidOperationException($"Cannot find payment method with code {inPayment.GatewayCode}"); } var store = await _storeService.GetByIdAsync(order.StoreId, StoreResponseGroup.StoreInfo.ToString()); if (store == null) { throw new InvalidOperationException($"Cannot find store with ID {order.StoreId}"); } var request = new ProcessPaymentRequest { OrderId = order.Id, Order = order, PaymentId = inPayment.Id, Payment = inPayment, StoreId = order.StoreId, Store = store, BankCardInfo = bankCardInfo }; var result = inPayment.PaymentMethod.ProcessPayment(request); if (result.OuterId != null) { inPayment.OuterId = result.OuterId; } await _customerOrderService.SaveChangesAsync(new[] { order }); return(Ok(result)); }
public async Task DoImportAsync(Stream inputStream, Action <ExportImportProgressInfo> progressCallback, ICancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var progressInfo = new ExportImportProgressInfo(); using (var streamReader = new StreamReader(inputStream)) using (var reader = new JsonTextReader(streamReader)) { while (reader.Read()) { if (reader.TokenType == JsonToken.PropertyName) { if (reader.Value.ToString() == "CustomerOrders") { await reader.DeserializeJsonArrayWithPagingAsync <CustomerOrder>(_jsonSerializer, _batchSize, items => _customerOrderService.SaveChangesAsync(items.ToArray()), processedCount => { progressInfo.Description = $"{ processedCount } orders have been imported"; progressCallback(progressInfo); }, cancellationToken); } } } } }