From 44f82633dcd657ebe404706aae709f2ef1f74621 Mon Sep 17 00:00:00 2001 From: yuskithedeveloper Date: Mon, 8 Sep 2025 10:47:41 -0500 Subject: [PATCH 01/15] Moved logic from OrderModuleController.PostProcessPayment to CustomerOrderService.PostProcessPaymentAsync --- .../Services/ICustomerOrderService.cs | 4 + ...PostProcessPaymentRequestNotValidResult.cs | 10 ++ .../Services/CustomerOrderService.cs | 98 ++++++++++++++++++- .../Controllers/Api/OrderModuleController.cs | 71 ++------------ ...ustomerOrderServiceImplIntegrationTests.cs | 16 +++ .../CustomerOrderServiceUnitTests.cs | 13 ++- 6 files changed, 148 insertions(+), 64 deletions(-) create mode 100644 src/VirtoCommerce.OrdersModule.Data/Model/PostProcessPaymentRequestNotValidResult.cs diff --git a/src/VirtoCommerce.OrdersModule.Core/Services/ICustomerOrderService.cs b/src/VirtoCommerce.OrdersModule.Core/Services/ICustomerOrderService.cs index f303e7324..7abac03d0 100644 --- a/src/VirtoCommerce.OrdersModule.Core/Services/ICustomerOrderService.cs +++ b/src/VirtoCommerce.OrdersModule.Core/Services/ICustomerOrderService.cs @@ -1,9 +1,13 @@ +using System.Collections.Specialized; +using System.Threading.Tasks; using VirtoCommerce.OrdersModule.Core.Model; +using VirtoCommerce.PaymentModule.Model.Requests; using VirtoCommerce.Platform.Core.GenericCrud; namespace VirtoCommerce.OrdersModule.Core.Services { public interface ICustomerOrderService : IOuterEntityService { + Task PostProcessPaymentAsync(NameValueCollection paymentParameters); } } diff --git a/src/VirtoCommerce.OrdersModule.Data/Model/PostProcessPaymentRequestNotValidResult.cs b/src/VirtoCommerce.OrdersModule.Data/Model/PostProcessPaymentRequestNotValidResult.cs new file mode 100644 index 000000000..f16552ce4 --- /dev/null +++ b/src/VirtoCommerce.OrdersModule.Data/Model/PostProcessPaymentRequestNotValidResult.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using FluentValidation.Results; +using VirtoCommerce.PaymentModule.Model.Requests; + +namespace VirtoCommerce.OrdersModule.Data.Model; + +public class PostProcessPaymentRequestNotValidResult : PostProcessPaymentRequestResult +{ + public IList Errors { get; set; } +} diff --git a/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderService.cs b/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderService.cs index b789b055b..1888b0c0f 100644 --- a/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderService.cs +++ b/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderService.cs @@ -1,18 +1,24 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.Linq; using System.Threading.Tasks; +using FluentValidation; +using FluentValidation.Results; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using VirtoCommerce.AssetsModule.Core.Assets; using VirtoCommerce.CoreModule.Core.Common; +using VirtoCommerce.OrdersModule.Core; using VirtoCommerce.OrdersModule.Core.Events; using VirtoCommerce.OrdersModule.Core.Model; +using VirtoCommerce.OrdersModule.Core.Model.Search; using VirtoCommerce.OrdersModule.Core.Services; using VirtoCommerce.OrdersModule.Data.Model; using VirtoCommerce.OrdersModule.Data.Repositories; using VirtoCommerce.PaymentModule.Core.Model.Search; using VirtoCommerce.PaymentModule.Core.Services; +using VirtoCommerce.PaymentModule.Model.Requests; using VirtoCommerce.Platform.Caching; using VirtoCommerce.Platform.Core.Caching; using VirtoCommerce.Platform.Core.Common; @@ -38,6 +44,9 @@ public class CustomerOrderService : OuterEntityService _customerOrderValidator; + private readonly ISettingsManager _settingsManager; public CustomerOrderService( Func repositoryFactory, @@ -48,7 +57,10 @@ public CustomerOrderService( ICustomerOrderTotalsCalculator totalsCalculator, IShippingMethodsSearchService shippingMethodsSearchService, IPaymentMethodsSearchService paymentMethodSearchService, - IBlobUrlResolver blobUrlResolver) + IBlobUrlResolver blobUrlResolver, + ICustomerOrderSearchService searchService, + IValidator customerOrderValidator, + ISettingsManager settingsManager) : base(repositoryFactory, platformMemoryCache, eventPublisher) { _repositoryFactory = repositoryFactory; @@ -60,6 +72,9 @@ public CustomerOrderService( _shippingMethodsSearchService = shippingMethodsSearchService; _paymentMethodSearchService = paymentMethodSearchService; _blobUrlResolver = blobUrlResolver; + _searchService = searchService; + _customerOrderValidator = customerOrderValidator; + _settingsManager = settingsManager; } public override async Task SaveChangesAsync(IList models) @@ -311,5 +326,86 @@ private void ResolveFileUrls(CustomerOrder order) file.Url = file.Url.StartsWith("/api") ? file.Url : _blobUrlResolver.GetAbsoluteUrl(file.Url); } } + + public virtual async Task PostProcessPaymentAsync(NameValueCollection paymentParameters) + { + var orderId = paymentParameters.Get("orderid"); + if (string.IsNullOrEmpty(orderId)) + { + throw new InvalidOperationException("the 'orderid' parameter must be passed"); + } + + //some payment method require customer number to be passed and returned. First search customer order by number + var searchCriteria = AbstractTypeFactory.TryCreateInstance(); + searchCriteria.Number = orderId; + searchCriteria.ResponseGroup = CustomerOrderResponseGroup.Full.ToString(); + //if order not found by order number search by order id + var orders = await _searchService.SearchAsync(searchCriteria); + var customerOrder = orders.Results.FirstOrDefault() ?? await this.GetByIdAsync(orderId, CustomerOrderResponseGroup.Full.ToString()); + + if (customerOrder == null) + { + throw new InvalidOperationException($"Cannot find order with ID {orderId}"); + } + + var store = await _storeService.GetByIdAsync(customerOrder.StoreId, StoreResponseGroup.StoreInfo.ToString()); + if (store == null) + { + throw new InvalidOperationException($"Cannot find store with ID {customerOrder.StoreId}"); + } + + var paymentMethodCode = paymentParameters.Get("code"); + + //Need to use concrete payment method if it code passed otherwise use all order payment methods + foreach (var inPayment in customerOrder.InPayments.Where(x => x.PaymentMethod != null && (string.IsNullOrEmpty(paymentMethodCode) || x.GatewayCode.EqualsIgnoreCase(paymentMethodCode)))) + { + //Each payment method must check that these parameters are addressed to it + var result = inPayment.PaymentMethod.ValidatePostProcessRequest(paymentParameters); + if (result.IsSuccess) + { + + var request = new PostProcessPaymentRequest + { + OrderId = customerOrder.Id, + Order = customerOrder, + PaymentId = inPayment.Id, + Payment = inPayment, + StoreId = customerOrder.StoreId, + Store = store, + OuterId = result.OuterId, + Parameters = paymentParameters + }; + var retVal = inPayment.PaymentMethod.PostProcessPayment(request); + if (retVal != null) + { + var validationResult = await ValidateAsync(customerOrder); + if (!validationResult.IsValid) + { + return new PostProcessPaymentRequestNotValidResult() + { + Errors = validationResult.Errors, + ErrorMessage = string.Join(" ", validationResult.Errors.Select(x => x.ErrorMessage)) + }; + } + await this.SaveChangesAsync(new[] { customerOrder }); + + // order Number is required + retVal.OrderId = customerOrder.Number; + } + return retVal; + } + } + return new PostProcessPaymentRequestResult { ErrorMessage = "Payment method not found" }; + } + + private async Task ValidateAsync(CustomerOrder customerOrder) + { + if (await _settingsManager.GetValueAsync(ModuleConstants.Settings.General.CustomerOrderValidation)) + { + return await _customerOrderValidator.ValidateAsync(customerOrder); + } + + return new ValidationResult(); + } } } diff --git a/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs b/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs index be4fe58b2..ab79f23fb 100644 --- a/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs +++ b/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs @@ -30,6 +30,7 @@ using VirtoCommerce.OrdersModule.Data.Authorization; using VirtoCommerce.OrdersModule.Data.Caching; using VirtoCommerce.OrdersModule.Data.Extensions; +using VirtoCommerce.OrdersModule.Data.Model; using VirtoCommerce.OrdersModule.Web.Model; using VirtoCommerce.PaymentModule.Core.Model; using VirtoCommerce.PaymentModule.Data; @@ -526,73 +527,19 @@ public async Task> PostProcessPaym { parameters.Add(param.Key, param.Value); } - var orderId = parameters.Get("orderid"); - if (string.IsNullOrEmpty(orderId)) - { - throw new InvalidOperationException("the 'orderid' parameter must be passed"); - } - - //some payment method require customer number to be passed and returned. First search customer order by number - var searchCriteria = AbstractTypeFactory.TryCreateInstance(); - searchCriteria.Number = orderId; - searchCriteria.ResponseGroup = CustomerOrderResponseGroup.Full.ToString(); - //if order not found by order number search by order id - var orders = await searchService.SearchAsync(searchCriteria); - var customerOrder = orders.Results.FirstOrDefault() ?? await customerOrderService.GetByIdAsync(orderId, CustomerOrderResponseGroup.Full.ToString()); - if (customerOrder == null) - { - throw new InvalidOperationException($"Cannot find order with ID {orderId}"); - } + var result = await customerOrderService.PostProcessPaymentAsync(parameters); - var store = await storeService.GetByIdAsync(customerOrder.StoreId, StoreResponseGroup.StoreInfo.ToString()); - if (store == null) + if (result is PostProcessPaymentRequestNotValidResult notValidResult) { - throw new InvalidOperationException($"Cannot find store with ID {customerOrder.StoreId}"); - } - - var paymentMethodCode = parameters.Get("code"); - - //Need to use concrete payment method if it code passed otherwise use all order payment methods - foreach (var inPayment in customerOrder.InPayments.Where(x => x.PaymentMethod != null && (string.IsNullOrEmpty(paymentMethodCode) || x.GatewayCode.EqualsIgnoreCase(paymentMethodCode)))) - { - //Each payment method must check that these parameters are addressed to it - var result = inPayment.PaymentMethod.ValidatePostProcessRequest(parameters); - if (result.IsSuccess) + return BadRequest(new { - - var request = new PostProcessPaymentRequest - { - OrderId = customerOrder.Id, - Order = customerOrder, - PaymentId = inPayment.Id, - Payment = inPayment, - StoreId = customerOrder.StoreId, - Store = store, - OuterId = result.OuterId, - Parameters = parameters - }; - var retVal = inPayment.PaymentMethod.PostProcessPayment(request); - if (retVal != null) - { - var validationResult = await ValidateAsync(customerOrder); - if (!validationResult.IsValid) - { - return BadRequest(new - { - Message = string.Join(" ", validationResult.Errors.Select(x => x.ErrorMessage)), - validationResult.Errors - }); - } - await customerOrderService.SaveChangesAsync(new[] { customerOrder }); - - // order Number is required - retVal.OrderId = customerOrder.Number; - } - return Ok(retVal); - } + Message = notValidResult.ErrorMessage, + notValidResult.Errors + }); } - return Ok(new PostProcessPaymentRequestResult { ErrorMessage = "Payment method not found" }); + + return Ok(result); } [HttpGet] diff --git a/tests/VirtoCommerce.OrdersModule.Tests/CustomerOrderServiceImplIntegrationTests.cs b/tests/VirtoCommerce.OrdersModule.Tests/CustomerOrderServiceImplIntegrationTests.cs index 979cf7d89..5b02c88c0 100644 --- a/tests/VirtoCommerce.OrdersModule.Tests/CustomerOrderServiceImplIntegrationTests.cs +++ b/tests/VirtoCommerce.OrdersModule.Tests/CustomerOrderServiceImplIntegrationTests.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using FluentValidation; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection; @@ -10,6 +11,7 @@ using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; using Moq; +using VirtoCommerce.AssetsModule.Core.Assets; using VirtoCommerce.CoreModule.Core.Common; using VirtoCommerce.OrdersModule.Core.Model; using VirtoCommerce.OrdersModule.Core.Model.Search; @@ -27,6 +29,7 @@ using VirtoCommerce.Platform.Core.DynamicProperties; using VirtoCommerce.Platform.Core.Events; using VirtoCommerce.Platform.Core.GenericCrud; +using VirtoCommerce.Platform.Core.Settings; using VirtoCommerce.ShippingModule.Core.Model.Search; using VirtoCommerce.ShippingModule.Core.Services; using VirtoCommerce.StoreModule.Core.Services; @@ -54,6 +57,10 @@ public class CustomerOrderServiceImplIntegrationTests private readonly ICustomerOrderSearchService _customerOrderSearchService; private readonly Mock> _logMock; private readonly Mock> _logEventMock; + private readonly Mock _blobUrlResolver; + private readonly Mock _searchServiceMock; + private readonly Mock> _customerOrderValidatorMock; + private readonly Mock _settingsManagerMock; public CustomerOrderServiceImplIntegrationTests() { @@ -72,6 +79,11 @@ public CustomerOrderServiceImplIntegrationTests() _changeLogServiceMock = new Mock(); _logMock = new Mock>(); _logEventMock = new Mock>(); + _blobUrlResolver = new Mock(); + _searchServiceMock = new Mock(); + _customerOrderValidatorMock = new Mock>(); + _settingsManagerMock = new Mock(); + var cachingOptions = new OptionsWrapper(new CachingOptions { CacheEnabled = true }); var memoryCache = new MemoryCache(new MemoryCacheOptions() { @@ -97,6 +109,10 @@ public CustomerOrderServiceImplIntegrationTests() container.AddSingleton(x => _platformMemoryCache); container.AddSingleton(x => _changeLogServiceMock.Object); container.AddSingleton(x => _logEventMock.Object); + container.AddSingleton(x => _blobUrlResolver.Object); + container.AddSingleton(x => _searchServiceMock.Object); + container.AddSingleton(x => _customerOrderValidatorMock.Object); + container.AddSingleton(x => _settingsManagerMock.Object); container.AddOptions(); var serviceProvider = container.BuildServiceProvider(); diff --git a/tests/VirtoCommerce.OrdersModule.Tests/CustomerOrderServiceUnitTests.cs b/tests/VirtoCommerce.OrdersModule.Tests/CustomerOrderServiceUnitTests.cs index 69209c2db..3a4010d3a 100644 --- a/tests/VirtoCommerce.OrdersModule.Tests/CustomerOrderServiceUnitTests.cs +++ b/tests/VirtoCommerce.OrdersModule.Tests/CustomerOrderServiceUnitTests.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using FluentValidation; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -21,6 +22,7 @@ using VirtoCommerce.Platform.Core.Common; using VirtoCommerce.Platform.Core.Domain; using VirtoCommerce.Platform.Core.Events; +using VirtoCommerce.Platform.Core.Settings; using VirtoCommerce.ShippingModule.Core.Model.Search; using VirtoCommerce.ShippingModule.Core.Services; using VirtoCommerce.StoreModule.Core.Services; @@ -40,6 +42,9 @@ public class CustomerOrderServiceUnitTests private readonly Mock _shippingMethodsSearchServiceMock; private readonly Mock _paymentMethodsSearchServiceMock; private readonly Mock _blobUrlResolver; + private readonly Mock _searchServiceMock; + private readonly Mock> _customerOrderValidatorMock; + private readonly Mock _settingsManagerMock; public CustomerOrderServiceUnitTests() { @@ -52,6 +57,9 @@ public CustomerOrderServiceUnitTests() _shippingMethodsSearchServiceMock = new Mock(); _paymentMethodsSearchServiceMock = new Mock(); _blobUrlResolver = new Mock(); + _searchServiceMock = new Mock(); + _customerOrderValidatorMock = new Mock>(); + _settingsManagerMock = new Mock(); } [Fact] @@ -181,7 +189,10 @@ private CustomerOrderService GetCustomerOrderService(IPlatformMemoryCache platfo _customerOrderTotalsCalculatorMock.Object, _shippingMethodsSearchServiceMock.Object, _paymentMethodsSearchServiceMock.Object, - _blobUrlResolver.Object); + _blobUrlResolver.Object, + _searchServiceMock.Object, + _customerOrderValidatorMock.Object, + _settingsManagerMock.Object); } } } From a5525efad5e3a733b9f81a8804e3261812ea111f Mon Sep 17 00:00:00 2001 From: yuskithedeveloper Date: Mon, 8 Sep 2025 11:42:47 -0500 Subject: [PATCH 02/15] Extracted OrderId and MethodCode parameters reading --- .../Services/CustomerOrderService.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderService.cs b/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderService.cs index 1888b0c0f..bebd45dec 100644 --- a/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderService.cs +++ b/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderService.cs @@ -329,10 +329,12 @@ private void ResolveFileUrls(CustomerOrder order) public virtual async Task PostProcessPaymentAsync(NameValueCollection paymentParameters) { - var orderId = paymentParameters.Get("orderid"); + var orderId = GetOrderId(paymentParameters); + var paymentMethodCode = GetPaymentMethodCode(paymentParameters); + if (string.IsNullOrEmpty(orderId)) { - throw new InvalidOperationException("the 'orderid' parameter must be passed"); + throw new InvalidOperationException("The 'orderid' parameter must be passed"); } //some payment method require customer number to be passed and returned. First search customer order by number @@ -354,8 +356,6 @@ public virtual async Task PostProcessPaymentAsy throw new InvalidOperationException($"Cannot find store with ID {customerOrder.StoreId}"); } - var paymentMethodCode = paymentParameters.Get("code"); - //Need to use concrete payment method if it code passed otherwise use all order payment methods foreach (var inPayment in customerOrder.InPayments.Where(x => x.PaymentMethod != null && (string.IsNullOrEmpty(paymentMethodCode) || x.GatewayCode.EqualsIgnoreCase(paymentMethodCode)))) { @@ -398,6 +398,16 @@ public virtual async Task PostProcessPaymentAsy return new PostProcessPaymentRequestResult { ErrorMessage = "Payment method not found" }; } + protected virtual string GetOrderId(NameValueCollection paymentParameters) + { + return paymentParameters.Get("orderid"); + } + + protected virtual string GetPaymentMethodCode(NameValueCollection paymentParameters) + { + return paymentParameters.Get("code"); + } + private async Task ValidateAsync(CustomerOrder customerOrder) { if (await _settingsManager.GetValueAsync(ModuleConstants.Settings.General.CustomerOrderValidation)) From bd8662f6ce03cfddff4dccda1063b3cbef61b620 Mon Sep 17 00:00:00 2001 From: yuskithedeveloper Date: Mon, 8 Sep 2025 14:02:02 -0500 Subject: [PATCH 03/15] Extracted service --- .../Services/ICustomerOrderPaymentService.cs | 11 ++ .../Services/ICustomerOrderService.cs | 4 - .../Services/CustomerOrderPaymentService.cs | 119 ++++++++++++++++++ .../Services/CustomerOrderService.cs | 107 +--------------- .../Controllers/Api/OrderModuleController.cs | 38 +++++- src/VirtoCommerce.OrdersModule.Web/Module.cs | 1 + ...ustomerOrderServiceImplIntegrationTests.cs | 12 -- .../CustomerOrderServiceUnitTests.cs | 13 +- 8 files changed, 169 insertions(+), 136 deletions(-) create mode 100644 src/VirtoCommerce.OrdersModule.Core/Services/ICustomerOrderPaymentService.cs create mode 100644 src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs diff --git a/src/VirtoCommerce.OrdersModule.Core/Services/ICustomerOrderPaymentService.cs b/src/VirtoCommerce.OrdersModule.Core/Services/ICustomerOrderPaymentService.cs new file mode 100644 index 000000000..ccfc7d157 --- /dev/null +++ b/src/VirtoCommerce.OrdersModule.Core/Services/ICustomerOrderPaymentService.cs @@ -0,0 +1,11 @@ +using System.Collections.Specialized; +using System.Threading.Tasks; +using VirtoCommerce.PaymentModule.Model.Requests; + +namespace VirtoCommerce.OrdersModule.Core.Services +{ + public interface ICustomerOrderPaymentService + { + Task PostProcessPaymentAsync(NameValueCollection paymentParameters); + } +} diff --git a/src/VirtoCommerce.OrdersModule.Core/Services/ICustomerOrderService.cs b/src/VirtoCommerce.OrdersModule.Core/Services/ICustomerOrderService.cs index 7abac03d0..f303e7324 100644 --- a/src/VirtoCommerce.OrdersModule.Core/Services/ICustomerOrderService.cs +++ b/src/VirtoCommerce.OrdersModule.Core/Services/ICustomerOrderService.cs @@ -1,13 +1,9 @@ -using System.Collections.Specialized; -using System.Threading.Tasks; using VirtoCommerce.OrdersModule.Core.Model; -using VirtoCommerce.PaymentModule.Model.Requests; using VirtoCommerce.Platform.Core.GenericCrud; namespace VirtoCommerce.OrdersModule.Core.Services { public interface ICustomerOrderService : IOuterEntityService { - Task PostProcessPaymentAsync(NameValueCollection paymentParameters); } } diff --git a/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs b/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs new file mode 100644 index 000000000..442745f46 --- /dev/null +++ b/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Specialized; +using System.Linq; +using System.Threading.Tasks; +using FluentValidation; +using FluentValidation.Results; +using VirtoCommerce.OrdersModule.Core; +using VirtoCommerce.OrdersModule.Core.Model; +using VirtoCommerce.OrdersModule.Core.Model.Search; +using VirtoCommerce.OrdersModule.Core.Services; +using VirtoCommerce.OrdersModule.Data.Model; +using VirtoCommerce.PaymentModule.Model.Requests; +using VirtoCommerce.Platform.Core.Common; +using VirtoCommerce.Platform.Core.Settings; +using VirtoCommerce.StoreModule.Core.Model; +using VirtoCommerce.StoreModule.Core.Services; + +namespace VirtoCommerce.OrdersModule.Data.Services +{ + public class CustomerOrderPaymentService( + IStoreService storeService, + ICustomerOrderService customerOrderService, + ICustomerOrderSearchService customerOrderSearchService, + IValidator customerOrderValidator, + ISettingsManager settingsManager) + : ICustomerOrderPaymentService + { + public virtual async Task PostProcessPaymentAsync(NameValueCollection paymentParameters) + { + var orderId = GetOrderId(paymentParameters); + var paymentMethodCode = GetPaymentMethodCode(paymentParameters); + + if (string.IsNullOrEmpty(orderId)) + { + throw new InvalidOperationException("The 'orderid' parameter must be passed"); + } + + //some payment method require customer number to be passed and returned. First search customer order by number + var searchCriteria = AbstractTypeFactory.TryCreateInstance(); + searchCriteria.Number = orderId; + searchCriteria.ResponseGroup = CustomerOrderResponseGroup.Full.ToString(); + //if order not found by order number search by order id + var orders = await customerOrderSearchService.SearchAsync(searchCriteria); + var customerOrder = orders.Results.FirstOrDefault() ?? await customerOrderService.GetByIdAsync(orderId, CustomerOrderResponseGroup.Full.ToString()); + + if (customerOrder == null) + { + throw new InvalidOperationException($"Cannot find order with ID {orderId}"); + } + + var store = await storeService.GetByIdAsync(customerOrder.StoreId, StoreResponseGroup.StoreInfo.ToString()); + if (store == null) + { + throw new InvalidOperationException($"Cannot find store with ID {customerOrder.StoreId}"); + } + + //Need to use concrete payment method if it code passed otherwise use all order payment methods + foreach (var inPayment in customerOrder.InPayments.Where(x => x.PaymentMethod != null && (string.IsNullOrEmpty(paymentMethodCode) || x.GatewayCode.EqualsIgnoreCase(paymentMethodCode)))) + { + //Each payment method must check that these parameters are addressed to it + var result = inPayment.PaymentMethod.ValidatePostProcessRequest(paymentParameters); + if (result.IsSuccess) + { + + var request = new PostProcessPaymentRequest + { + OrderId = customerOrder.Id, + Order = customerOrder, + PaymentId = inPayment.Id, + Payment = inPayment, + StoreId = customerOrder.StoreId, + Store = store, + OuterId = result.OuterId, + Parameters = paymentParameters + }; + var retVal = inPayment.PaymentMethod.PostProcessPayment(request); + if (retVal != null) + { + var validationResult = await ValidateAsync(customerOrder); + if (!validationResult.IsValid) + { + return new PostProcessPaymentRequestNotValidResult() + { + Errors = validationResult.Errors, + ErrorMessage = string.Join(" ", validationResult.Errors.Select(x => x.ErrorMessage)) + }; + } + await customerOrderService.SaveChangesAsync(new[] { customerOrder }); + + // order Number is required + retVal.OrderId = customerOrder.Number; + } + return retVal; + } + } + return new PostProcessPaymentRequestResult { ErrorMessage = "Payment method not found" }; + } + + protected virtual string GetOrderId(NameValueCollection paymentParameters) + { + return paymentParameters.Get("orderid"); + } + + protected virtual string GetPaymentMethodCode(NameValueCollection paymentParameters) + { + return paymentParameters.Get("code"); + } + + protected virtual async Task ValidateAsync(CustomerOrder customerOrder) + { + if (await settingsManager.GetValueAsync(ModuleConstants.Settings.General.CustomerOrderValidation)) + { + return await customerOrderValidator.ValidateAsync(customerOrder); + } + + return new ValidationResult(); + } + } +} diff --git a/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderService.cs b/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderService.cs index bebd45dec..f291963b6 100644 --- a/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderService.cs +++ b/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderService.cs @@ -1,24 +1,19 @@ using System; using System.Collections.Generic; -using System.Collections.Specialized; using System.Linq; using System.Threading.Tasks; using FluentValidation; -using FluentValidation.Results; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using VirtoCommerce.AssetsModule.Core.Assets; using VirtoCommerce.CoreModule.Core.Common; -using VirtoCommerce.OrdersModule.Core; using VirtoCommerce.OrdersModule.Core.Events; using VirtoCommerce.OrdersModule.Core.Model; -using VirtoCommerce.OrdersModule.Core.Model.Search; using VirtoCommerce.OrdersModule.Core.Services; using VirtoCommerce.OrdersModule.Data.Model; using VirtoCommerce.OrdersModule.Data.Repositories; using VirtoCommerce.PaymentModule.Core.Model.Search; using VirtoCommerce.PaymentModule.Core.Services; -using VirtoCommerce.PaymentModule.Model.Requests; using VirtoCommerce.Platform.Caching; using VirtoCommerce.Platform.Core.Caching; using VirtoCommerce.Platform.Core.Common; @@ -44,9 +39,6 @@ public class CustomerOrderService : OuterEntityService _customerOrderValidator; - private readonly ISettingsManager _settingsManager; public CustomerOrderService( Func repositoryFactory, @@ -57,10 +49,7 @@ public CustomerOrderService( ICustomerOrderTotalsCalculator totalsCalculator, IShippingMethodsSearchService shippingMethodsSearchService, IPaymentMethodsSearchService paymentMethodSearchService, - IBlobUrlResolver blobUrlResolver, - ICustomerOrderSearchService searchService, - IValidator customerOrderValidator, - ISettingsManager settingsManager) + IBlobUrlResolver blobUrlResolver) : base(repositoryFactory, platformMemoryCache, eventPublisher) { _repositoryFactory = repositoryFactory; @@ -72,9 +61,6 @@ public CustomerOrderService( _shippingMethodsSearchService = shippingMethodsSearchService; _paymentMethodSearchService = paymentMethodSearchService; _blobUrlResolver = blobUrlResolver; - _searchService = searchService; - _customerOrderValidator = customerOrderValidator; - _settingsManager = settingsManager; } public override async Task SaveChangesAsync(IList models) @@ -326,96 +312,5 @@ private void ResolveFileUrls(CustomerOrder order) file.Url = file.Url.StartsWith("/api") ? file.Url : _blobUrlResolver.GetAbsoluteUrl(file.Url); } } - - public virtual async Task PostProcessPaymentAsync(NameValueCollection paymentParameters) - { - var orderId = GetOrderId(paymentParameters); - var paymentMethodCode = GetPaymentMethodCode(paymentParameters); - - if (string.IsNullOrEmpty(orderId)) - { - throw new InvalidOperationException("The 'orderid' parameter must be passed"); - } - - //some payment method require customer number to be passed and returned. First search customer order by number - var searchCriteria = AbstractTypeFactory.TryCreateInstance(); - searchCriteria.Number = orderId; - searchCriteria.ResponseGroup = CustomerOrderResponseGroup.Full.ToString(); - //if order not found by order number search by order id - var orders = await _searchService.SearchAsync(searchCriteria); - var customerOrder = orders.Results.FirstOrDefault() ?? await this.GetByIdAsync(orderId, CustomerOrderResponseGroup.Full.ToString()); - - if (customerOrder == null) - { - throw new InvalidOperationException($"Cannot find order with ID {orderId}"); - } - - var store = await _storeService.GetByIdAsync(customerOrder.StoreId, StoreResponseGroup.StoreInfo.ToString()); - if (store == null) - { - throw new InvalidOperationException($"Cannot find store with ID {customerOrder.StoreId}"); - } - - //Need to use concrete payment method if it code passed otherwise use all order payment methods - foreach (var inPayment in customerOrder.InPayments.Where(x => x.PaymentMethod != null && (string.IsNullOrEmpty(paymentMethodCode) || x.GatewayCode.EqualsIgnoreCase(paymentMethodCode)))) - { - //Each payment method must check that these parameters are addressed to it - var result = inPayment.PaymentMethod.ValidatePostProcessRequest(paymentParameters); - if (result.IsSuccess) - { - - var request = new PostProcessPaymentRequest - { - OrderId = customerOrder.Id, - Order = customerOrder, - PaymentId = inPayment.Id, - Payment = inPayment, - StoreId = customerOrder.StoreId, - Store = store, - OuterId = result.OuterId, - Parameters = paymentParameters - }; - var retVal = inPayment.PaymentMethod.PostProcessPayment(request); - if (retVal != null) - { - var validationResult = await ValidateAsync(customerOrder); - if (!validationResult.IsValid) - { - return new PostProcessPaymentRequestNotValidResult() - { - Errors = validationResult.Errors, - ErrorMessage = string.Join(" ", validationResult.Errors.Select(x => x.ErrorMessage)) - }; - } - await this.SaveChangesAsync(new[] { customerOrder }); - - // order Number is required - retVal.OrderId = customerOrder.Number; - } - return retVal; - } - } - return new PostProcessPaymentRequestResult { ErrorMessage = "Payment method not found" }; - } - - protected virtual string GetOrderId(NameValueCollection paymentParameters) - { - return paymentParameters.Get("orderid"); - } - - protected virtual string GetPaymentMethodCode(NameValueCollection paymentParameters) - { - return paymentParameters.Get("code"); - } - - private async Task ValidateAsync(CustomerOrder customerOrder) - { - if (await _settingsManager.GetValueAsync(ModuleConstants.Settings.General.CustomerOrderValidation)) - { - return await _customerOrderValidator.ValidateAsync(customerOrder); - } - - return new ValidationResult(); - } } } diff --git a/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs b/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs index ab79f23fb..b921bd5d3 100644 --- a/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs +++ b/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs @@ -69,7 +69,8 @@ public class OrderModuleController( IOptions htmlToPdfOptions, IOptions outputJsonSerializerSettings, IValidator customerOrderValidator, - ISettingsManager settingsManager) + ISettingsManager settingsManager, + ICustomerOrderPaymentService customerOrderPaymentService) : Controller { /// @@ -528,7 +529,7 @@ public async Task> PostProcessPaym parameters.Add(param.Key, param.Value); } - var result = await customerOrderService.PostProcessPaymentAsync(parameters); + var result = await customerOrderPaymentService.PostProcessPaymentAsync(parameters); if (result is PostProcessPaymentRequestNotValidResult notValidResult) { @@ -542,6 +543,39 @@ public async Task> PostProcessPaym return Ok(result); } + /// + /// Payment callback operation used by external payment services to inform post process payment in our system + /// + [HttpPost] + [Route("~/api/paymentcallback-raw")] + public async Task> PostProcessPaymentRaw() + { + var parameters = await PopulatePaymentCallbackParameters(); + + var result = await customerOrderPaymentService.PostProcessPaymentAsync(parameters); + + if (result is PostProcessPaymentRequestNotValidResult notValidResult) + { + return BadRequest(new + { + Message = notValidResult.ErrorMessage, + notValidResult.Errors + }); + } + + return Ok(result); + } + + private async Task PopulatePaymentCallbackParameters() + { + var result = new NameValueCollection(); + using (var reader = new System.IO.StreamReader(Request.Body, System.Text.Encoding.UTF8, true, 1024, true)) + { + string requestBody = await reader.ReadToEndAsync(); + } + return result; + } + [HttpGet] [Route("invoice/{orderNumber}")] [SwaggerFileResponse] diff --git a/src/VirtoCommerce.OrdersModule.Web/Module.cs b/src/VirtoCommerce.OrdersModule.Web/Module.cs index 02d6ed2b9..b88c58822 100644 --- a/src/VirtoCommerce.OrdersModule.Web/Module.cs +++ b/src/VirtoCommerce.OrdersModule.Web/Module.cs @@ -92,6 +92,7 @@ public void Initialize(IServiceCollection serviceCollection) serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); + serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); diff --git a/tests/VirtoCommerce.OrdersModule.Tests/CustomerOrderServiceImplIntegrationTests.cs b/tests/VirtoCommerce.OrdersModule.Tests/CustomerOrderServiceImplIntegrationTests.cs index 5b02c88c0..31ac27dc6 100644 --- a/tests/VirtoCommerce.OrdersModule.Tests/CustomerOrderServiceImplIntegrationTests.cs +++ b/tests/VirtoCommerce.OrdersModule.Tests/CustomerOrderServiceImplIntegrationTests.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using FluentValidation; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection; @@ -29,7 +28,6 @@ using VirtoCommerce.Platform.Core.DynamicProperties; using VirtoCommerce.Platform.Core.Events; using VirtoCommerce.Platform.Core.GenericCrud; -using VirtoCommerce.Platform.Core.Settings; using VirtoCommerce.ShippingModule.Core.Model.Search; using VirtoCommerce.ShippingModule.Core.Services; using VirtoCommerce.StoreModule.Core.Services; @@ -58,9 +56,6 @@ public class CustomerOrderServiceImplIntegrationTests private readonly Mock> _logMock; private readonly Mock> _logEventMock; private readonly Mock _blobUrlResolver; - private readonly Mock _searchServiceMock; - private readonly Mock> _customerOrderValidatorMock; - private readonly Mock _settingsManagerMock; public CustomerOrderServiceImplIntegrationTests() { @@ -80,10 +75,6 @@ public CustomerOrderServiceImplIntegrationTests() _logMock = new Mock>(); _logEventMock = new Mock>(); _blobUrlResolver = new Mock(); - _searchServiceMock = new Mock(); - _customerOrderValidatorMock = new Mock>(); - _settingsManagerMock = new Mock(); - var cachingOptions = new OptionsWrapper(new CachingOptions { CacheEnabled = true }); var memoryCache = new MemoryCache(new MemoryCacheOptions() { @@ -110,9 +101,6 @@ public CustomerOrderServiceImplIntegrationTests() container.AddSingleton(x => _changeLogServiceMock.Object); container.AddSingleton(x => _logEventMock.Object); container.AddSingleton(x => _blobUrlResolver.Object); - container.AddSingleton(x => _searchServiceMock.Object); - container.AddSingleton(x => _customerOrderValidatorMock.Object); - container.AddSingleton(x => _settingsManagerMock.Object); container.AddOptions(); var serviceProvider = container.BuildServiceProvider(); diff --git a/tests/VirtoCommerce.OrdersModule.Tests/CustomerOrderServiceUnitTests.cs b/tests/VirtoCommerce.OrdersModule.Tests/CustomerOrderServiceUnitTests.cs index 3a4010d3a..69209c2db 100644 --- a/tests/VirtoCommerce.OrdersModule.Tests/CustomerOrderServiceUnitTests.cs +++ b/tests/VirtoCommerce.OrdersModule.Tests/CustomerOrderServiceUnitTests.cs @@ -2,7 +2,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using FluentValidation; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -22,7 +21,6 @@ using VirtoCommerce.Platform.Core.Common; using VirtoCommerce.Platform.Core.Domain; using VirtoCommerce.Platform.Core.Events; -using VirtoCommerce.Platform.Core.Settings; using VirtoCommerce.ShippingModule.Core.Model.Search; using VirtoCommerce.ShippingModule.Core.Services; using VirtoCommerce.StoreModule.Core.Services; @@ -42,9 +40,6 @@ public class CustomerOrderServiceUnitTests private readonly Mock _shippingMethodsSearchServiceMock; private readonly Mock _paymentMethodsSearchServiceMock; private readonly Mock _blobUrlResolver; - private readonly Mock _searchServiceMock; - private readonly Mock> _customerOrderValidatorMock; - private readonly Mock _settingsManagerMock; public CustomerOrderServiceUnitTests() { @@ -57,9 +52,6 @@ public CustomerOrderServiceUnitTests() _shippingMethodsSearchServiceMock = new Mock(); _paymentMethodsSearchServiceMock = new Mock(); _blobUrlResolver = new Mock(); - _searchServiceMock = new Mock(); - _customerOrderValidatorMock = new Mock>(); - _settingsManagerMock = new Mock(); } [Fact] @@ -189,10 +181,7 @@ private CustomerOrderService GetCustomerOrderService(IPlatformMemoryCache platfo _customerOrderTotalsCalculatorMock.Object, _shippingMethodsSearchServiceMock.Object, _paymentMethodsSearchServiceMock.Object, - _blobUrlResolver.Object, - _searchServiceMock.Object, - _customerOrderValidatorMock.Object, - _settingsManagerMock.Object); + _blobUrlResolver.Object); } } } From 16eaf90da6b5d063600f721690bb4179d18a9766 Mon Sep 17 00:00:00 2001 From: yuskithedeveloper Date: Mon, 8 Sep 2025 14:37:21 -0500 Subject: [PATCH 04/15] Extracted request convertor --- .../Model/PaymentParameters.cs | 10 ++++++ .../Services/ICustomerOrderPaymentService.cs | 4 +-- .../Services/CustomerOrderPaymentService.cs | 30 ++++++----------- .../Controllers/Api/OrderModuleController.cs | 17 ++++------ .../Converters/IPaymentParametersConverter.cs | 10 ++++++ .../PaymentParametersDefaultConverter.cs | 32 +++++++++++++++++++ src/VirtoCommerce.OrdersModule.Web/Module.cs | 2 ++ 7 files changed, 71 insertions(+), 34 deletions(-) create mode 100644 src/VirtoCommerce.OrdersModule.Core/Model/PaymentParameters.cs create mode 100644 src/VirtoCommerce.OrdersModule.Web/Converters/IPaymentParametersConverter.cs create mode 100644 src/VirtoCommerce.OrdersModule.Web/Converters/PaymentParametersDefaultConverter.cs diff --git a/src/VirtoCommerce.OrdersModule.Core/Model/PaymentParameters.cs b/src/VirtoCommerce.OrdersModule.Core/Model/PaymentParameters.cs new file mode 100644 index 000000000..6af2447be --- /dev/null +++ b/src/VirtoCommerce.OrdersModule.Core/Model/PaymentParameters.cs @@ -0,0 +1,10 @@ +using System.Collections.Specialized; + +namespace VirtoCommerce.OrdersModule.Core.Model; + +public class PaymentParameters +{ + public string OrderId { get; set; } + public string PaymentMethodCode { get; set; } + public NameValueCollection Parameters { get; set; } +} diff --git a/src/VirtoCommerce.OrdersModule.Core/Services/ICustomerOrderPaymentService.cs b/src/VirtoCommerce.OrdersModule.Core/Services/ICustomerOrderPaymentService.cs index ccfc7d157..8e026948d 100644 --- a/src/VirtoCommerce.OrdersModule.Core/Services/ICustomerOrderPaymentService.cs +++ b/src/VirtoCommerce.OrdersModule.Core/Services/ICustomerOrderPaymentService.cs @@ -1,11 +1,11 @@ -using System.Collections.Specialized; using System.Threading.Tasks; +using VirtoCommerce.OrdersModule.Core.Model; using VirtoCommerce.PaymentModule.Model.Requests; namespace VirtoCommerce.OrdersModule.Core.Services { public interface ICustomerOrderPaymentService { - Task PostProcessPaymentAsync(NameValueCollection paymentParameters); + Task PostProcessPaymentAsync(PaymentParameters paymentParameters); } } diff --git a/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs b/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs index 442745f46..6f75afc55 100644 --- a/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs +++ b/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Specialized; using System.Linq; using System.Threading.Tasks; using FluentValidation; @@ -25,27 +24,26 @@ public class CustomerOrderPaymentService( ISettingsManager settingsManager) : ICustomerOrderPaymentService { - public virtual async Task PostProcessPaymentAsync(NameValueCollection paymentParameters) + public virtual async Task PostProcessPaymentAsync(PaymentParameters paymentParameters) { - var orderId = GetOrderId(paymentParameters); - var paymentMethodCode = GetPaymentMethodCode(paymentParameters); + ArgumentNullException.ThrowIfNull(paymentParameters); - if (string.IsNullOrEmpty(orderId)) + if (string.IsNullOrEmpty(paymentParameters.OrderId)) { throw new InvalidOperationException("The 'orderid' parameter must be passed"); } //some payment method require customer number to be passed and returned. First search customer order by number var searchCriteria = AbstractTypeFactory.TryCreateInstance(); - searchCriteria.Number = orderId; + searchCriteria.Number = paymentParameters.OrderId; searchCriteria.ResponseGroup = CustomerOrderResponseGroup.Full.ToString(); //if order not found by order number search by order id var orders = await customerOrderSearchService.SearchAsync(searchCriteria); - var customerOrder = orders.Results.FirstOrDefault() ?? await customerOrderService.GetByIdAsync(orderId, CustomerOrderResponseGroup.Full.ToString()); + var customerOrder = orders.Results.FirstOrDefault() ?? await customerOrderService.GetByIdAsync(paymentParameters.OrderId, CustomerOrderResponseGroup.Full.ToString()); if (customerOrder == null) { - throw new InvalidOperationException($"Cannot find order with ID {orderId}"); + throw new InvalidOperationException($"Cannot find order with ID {paymentParameters.OrderId}"); } var store = await storeService.GetByIdAsync(customerOrder.StoreId, StoreResponseGroup.StoreInfo.ToString()); @@ -55,10 +53,10 @@ public virtual async Task PostProcessPaymentAsy } //Need to use concrete payment method if it code passed otherwise use all order payment methods - foreach (var inPayment in customerOrder.InPayments.Where(x => x.PaymentMethod != null && (string.IsNullOrEmpty(paymentMethodCode) || x.GatewayCode.EqualsIgnoreCase(paymentMethodCode)))) + foreach (var inPayment in customerOrder.InPayments.Where(x => x.PaymentMethod != null && (string.IsNullOrEmpty(paymentParameters.PaymentMethodCode) || x.GatewayCode.EqualsIgnoreCase(paymentParameters.PaymentMethodCode)))) { //Each payment method must check that these parameters are addressed to it - var result = inPayment.PaymentMethod.ValidatePostProcessRequest(paymentParameters); + var result = inPayment.PaymentMethod.ValidatePostProcessRequest(paymentParameters.Parameters); if (result.IsSuccess) { @@ -71,7 +69,7 @@ public virtual async Task PostProcessPaymentAsy StoreId = customerOrder.StoreId, Store = store, OuterId = result.OuterId, - Parameters = paymentParameters + Parameters = paymentParameters.Parameters }; var retVal = inPayment.PaymentMethod.PostProcessPayment(request); if (retVal != null) @@ -96,16 +94,6 @@ public virtual async Task PostProcessPaymentAsy return new PostProcessPaymentRequestResult { ErrorMessage = "Payment method not found" }; } - protected virtual string GetOrderId(NameValueCollection paymentParameters) - { - return paymentParameters.Get("orderid"); - } - - protected virtual string GetPaymentMethodCode(NameValueCollection paymentParameters) - { - return paymentParameters.Get("code"); - } - protected virtual async Task ValidateAsync(CustomerOrder customerOrder) { if (await settingsManager.GetValueAsync(ModuleConstants.Settings.General.CustomerOrderValidation)) diff --git a/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs b/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs index b921bd5d3..17ebed069 100644 --- a/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs +++ b/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Specialized; using System.Linq; using System.Threading.Tasks; using DinkToPdf; @@ -31,6 +30,7 @@ using VirtoCommerce.OrdersModule.Data.Caching; using VirtoCommerce.OrdersModule.Data.Extensions; using VirtoCommerce.OrdersModule.Data.Model; +using VirtoCommerce.OrdersModule.Web.Converters; using VirtoCommerce.OrdersModule.Web.Model; using VirtoCommerce.PaymentModule.Core.Model; using VirtoCommerce.PaymentModule.Data; @@ -70,6 +70,7 @@ public class OrderModuleController( IOptions outputJsonSerializerSettings, IValidator customerOrderValidator, ISettingsManager settingsManager, + IPaymentParametersConverter paymentParametersConverter, ICustomerOrderPaymentService customerOrderPaymentService) : Controller { @@ -523,11 +524,7 @@ public async Task> GetDashboardStatistic [Route("~/api/paymentcallback")] public async Task> PostProcessPayment([FromBody] PaymentCallbackParameters callback) { - var parameters = new NameValueCollection(); - foreach (var param in callback?.Parameters ?? Array.Empty()) - { - parameters.Add(param.Key, param.Value); - } + var parameters = paymentParametersConverter.GetPaymentParameters(callback); var result = await customerOrderPaymentService.PostProcessPaymentAsync(parameters); @@ -550,7 +547,7 @@ public async Task> PostProcessPaym [Route("~/api/paymentcallback-raw")] public async Task> PostProcessPaymentRaw() { - var parameters = await PopulatePaymentCallbackParameters(); + var parameters = paymentParametersConverter.GetPaymentParameters(await GetRequestBody(), Request.QueryString.Value); var result = await customerOrderPaymentService.PostProcessPaymentAsync(parameters); @@ -566,14 +563,12 @@ public async Task> PostProcessPaym return Ok(result); } - private async Task PopulatePaymentCallbackParameters() + private async Task GetRequestBody() { - var result = new NameValueCollection(); using (var reader = new System.IO.StreamReader(Request.Body, System.Text.Encoding.UTF8, true, 1024, true)) { - string requestBody = await reader.ReadToEndAsync(); + return await reader.ReadToEndAsync(); } - return result; } [HttpGet] diff --git a/src/VirtoCommerce.OrdersModule.Web/Converters/IPaymentParametersConverter.cs b/src/VirtoCommerce.OrdersModule.Web/Converters/IPaymentParametersConverter.cs new file mode 100644 index 000000000..12fc8f5f5 --- /dev/null +++ b/src/VirtoCommerce.OrdersModule.Web/Converters/IPaymentParametersConverter.cs @@ -0,0 +1,10 @@ +using VirtoCommerce.OrdersModule.Core.Model; +using VirtoCommerce.OrdersModule.Web.Model; + +namespace VirtoCommerce.OrdersModule.Web.Converters; + +public interface IPaymentParametersConverter +{ + PaymentParameters GetPaymentParameters(PaymentCallbackParameters request); + PaymentParameters GetPaymentParameters(string requestBody, string requestQuery); +} diff --git a/src/VirtoCommerce.OrdersModule.Web/Converters/PaymentParametersDefaultConverter.cs b/src/VirtoCommerce.OrdersModule.Web/Converters/PaymentParametersDefaultConverter.cs new file mode 100644 index 000000000..caf63f6ef --- /dev/null +++ b/src/VirtoCommerce.OrdersModule.Web/Converters/PaymentParametersDefaultConverter.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Specialized; +using Newtonsoft.Json; +using VirtoCommerce.OrdersModule.Core.Model; +using VirtoCommerce.OrdersModule.Web.Model; + +namespace VirtoCommerce.OrdersModule.Web.Converters; + +public class PaymentParametersDefaultConverter : IPaymentParametersConverter +{ + public virtual PaymentParameters GetPaymentParameters(PaymentCallbackParameters request) + { + var result = new PaymentParameters(); + + result.Parameters = new NameValueCollection(); + foreach (var parameter in request?.Parameters ?? Array.Empty()) + { + result.Parameters.Add(parameter.Key, parameter.Value); + } + + result.OrderId = result.Parameters.Get("orderid"); + result.PaymentMethodCode = result.Parameters.Get("code"); + + return result; + } + + public virtual PaymentParameters GetPaymentParameters(string requestBody, string requestQuery) + { + var paymentCallbackParameters = JsonConvert.DeserializeObject(requestBody); + return GetPaymentParameters(paymentCallbackParameters); + } +} diff --git a/src/VirtoCommerce.OrdersModule.Web/Module.cs b/src/VirtoCommerce.OrdersModule.Web/Module.cs index b88c58822..adfd536f4 100644 --- a/src/VirtoCommerce.OrdersModule.Web/Module.cs +++ b/src/VirtoCommerce.OrdersModule.Web/Module.cs @@ -30,6 +30,7 @@ using VirtoCommerce.OrdersModule.Data.Services; using VirtoCommerce.OrdersModule.Data.SqlServer; using VirtoCommerce.OrdersModule.Web.Authorization; +using VirtoCommerce.OrdersModule.Web.Converters; using VirtoCommerce.OrdersModule.Web.Extensions; using VirtoCommerce.OrdersModule.Web.JsonConverters; using VirtoCommerce.Platform.Core.Common; @@ -102,6 +103,7 @@ public void Initialize(IServiceCollection serviceCollection) serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); + serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); From 7b37c406d425f39595b31d6cb5d1848dd941b474 Mon Sep 17 00:00:00 2001 From: yuskithedeveloper Date: Mon, 8 Sep 2025 14:38:37 -0500 Subject: [PATCH 05/15] Not used using --- .../Services/CustomerOrderService.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderService.cs b/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderService.cs index f291963b6..b789b055b 100644 --- a/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderService.cs +++ b/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderService.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using FluentValidation; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using VirtoCommerce.AssetsModule.Core.Assets; From 1de02138b1448d47a17c8cd62b48c42478809c37 Mon Sep 17 00:00:00 2001 From: yuskithedeveloper Date: Tue, 9 Sep 2025 07:16:27 -0500 Subject: [PATCH 06/15] Failed test test --- .../VirtoCommerce.OrdersModule.Tests/CancelPaymentOrderTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/VirtoCommerce.OrdersModule.Tests/CancelPaymentOrderTests.cs b/tests/VirtoCommerce.OrdersModule.Tests/CancelPaymentOrderTests.cs index c3c13c43d..6c865a182 100644 --- a/tests/VirtoCommerce.OrdersModule.Tests/CancelPaymentOrderTests.cs +++ b/tests/VirtoCommerce.OrdersModule.Tests/CancelPaymentOrderTests.cs @@ -17,6 +17,7 @@ public class CancelPaymentOrderTests [Fact] public void GetJobArgumentsForChangedEntry_PaymentsCancelledStateChanged() { + Assert.Fail("FAILED TEST"); // Arrange var paymentId = Guid.NewGuid().ToString(); var oldPayment = new PaymentIn From 84617de82544c34bb7b83e0f6a069c52b3e27575 Mon Sep 17 00:00:00 2001 From: yuskithedeveloper Date: Tue, 9 Sep 2025 07:20:20 -0500 Subject: [PATCH 07/15] Failed test test --- .../VirtoCommerce.OrdersModule.Tests/CancelPaymentOrderTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/VirtoCommerce.OrdersModule.Tests/CancelPaymentOrderTests.cs b/tests/VirtoCommerce.OrdersModule.Tests/CancelPaymentOrderTests.cs index 6c865a182..c3c13c43d 100644 --- a/tests/VirtoCommerce.OrdersModule.Tests/CancelPaymentOrderTests.cs +++ b/tests/VirtoCommerce.OrdersModule.Tests/CancelPaymentOrderTests.cs @@ -17,7 +17,6 @@ public class CancelPaymentOrderTests [Fact] public void GetJobArgumentsForChangedEntry_PaymentsCancelledStateChanged() { - Assert.Fail("FAILED TEST"); // Arrange var paymentId = Guid.NewGuid().ToString(); var oldPayment = new PaymentIn From 8d7f7d4a72554767707561b5fc3cbb3c8fb1c78c Mon Sep 17 00:00:00 2001 From: yuskithedeveloper Date: Tue, 9 Sep 2025 07:40:38 -0500 Subject: [PATCH 08/15] Migration test --- .../MigrationTests.cs | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 tests/VirtoCommerce.OrdersModule.Tests/MigrationTests.cs diff --git a/tests/VirtoCommerce.OrdersModule.Tests/MigrationTests.cs b/tests/VirtoCommerce.OrdersModule.Tests/MigrationTests.cs new file mode 100644 index 000000000..5e18d459b --- /dev/null +++ b/tests/VirtoCommerce.OrdersModule.Tests/MigrationTests.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.EntityFrameworkCore.Migrations; +using Xunit; + +namespace VirtoCommerce.OrdersModule.Tests; + +[Trait("Category", "CI")] +public class MigrationTests +{ + [Fact] + public void CheckMigrationConsistency() + { + DateTime after = new DateTime(2025, 1, 1); + + var mySqlMigrations = GetMigrationTypes(typeof(VirtoCommerce.OrdersModule.Data.MySql.MySqlDataAssemblyMarker).Assembly, after); + var postgreMigrations = GetMigrationTypes(typeof(VirtoCommerce.OrdersModule.Data.PostgreSql.PostgreSqlDataAssemblyMarker).Assembly, after); + var sqlServerMigrations = GetMigrationTypes(typeof(VirtoCommerce.OrdersModule.Data.SqlServer.SqlServerDataAssemblyMarker).Assembly, after); + + Assert.True(mySqlMigrations.Count == postgreMigrations.Count, $"MySql migrations count {mySqlMigrations.Count} doesn't match PostgreSQL migrations count {postgreMigrations.Count}"); + Assert.True(sqlServerMigrations.Count == postgreMigrations.Count, $"SqlServer migrations count {sqlServerMigrations.Count} doesn't match PostgreSQL migrations count {postgreMigrations.Count}"); + Assert.True(sqlServerMigrations.Count == mySqlMigrations.Count, $"SqlServer migrations count {sqlServerMigrations.Count} doesn't match MySql migrations count {mySqlMigrations.Count}"); + } + + private IList GetMigrationTypes(Assembly assembly, DateTime? after = null) + { + var result = assembly.GetTypes() + .Where(x => typeof(Migration).IsAssignableFrom(x)); + + if (after != null) + { + result = result.Where(x => GetMigrationDate(x) >= after).ToList(); + } + + return result.ToList(); + } + + private DateTime? GetMigrationDate(Type migrationType) + { + var migrationAttribute = migrationType.GetCustomAttribute(); + + if (migrationAttribute == null || migrationAttribute.Id == null) + { + return null; + } + + if (DateTime.TryParseExact(migrationAttribute.Id.Split('_')[0], "yyyyMMddHHmmss", null, System.Globalization.DateTimeStyles.None, out var migrationDate)) + { + return migrationDate; + } + + return null; + } +} From 483080d3716d4d0f8c653dfb811e461e0091013f Mon Sep 17 00:00:00 2001 From: yuskithedeveloper Date: Tue, 9 Sep 2025 08:58:53 -0500 Subject: [PATCH 09/15] Convertor and models moved to Core project --- .../Model/KeyValuePair.cs | 2 +- .../Model/PaymentCallbackParameters.cs | 2 +- .../Services/IPaymentParametersConverter.cs | 10 ++++++ .../PaymentParametersDefaultConverter.cs | 33 +++++++++++++++++++ .../Controllers/Api/OrderModuleController.cs | 2 -- .../Converters/IPaymentParametersConverter.cs | 10 ------ .../PaymentParametersDefaultConverter.cs | 32 ------------------ src/VirtoCommerce.OrdersModule.Web/Module.cs | 1 - .../VirtoCommerce.OrdersModule.Web.csproj | 1 + 9 files changed, 46 insertions(+), 47 deletions(-) rename src/{VirtoCommerce.OrdersModule.Web => VirtoCommerce.OrdersModule.Core}/Model/KeyValuePair.cs (72%) rename src/{VirtoCommerce.OrdersModule.Web => VirtoCommerce.OrdersModule.Core}/Model/PaymentCallbackParameters.cs (70%) create mode 100644 src/VirtoCommerce.OrdersModule.Core/Services/IPaymentParametersConverter.cs create mode 100644 src/VirtoCommerce.OrdersModule.Data/Services/PaymentParametersDefaultConverter.cs delete mode 100644 src/VirtoCommerce.OrdersModule.Web/Converters/IPaymentParametersConverter.cs delete mode 100644 src/VirtoCommerce.OrdersModule.Web/Converters/PaymentParametersDefaultConverter.cs diff --git a/src/VirtoCommerce.OrdersModule.Web/Model/KeyValuePair.cs b/src/VirtoCommerce.OrdersModule.Core/Model/KeyValuePair.cs similarity index 72% rename from src/VirtoCommerce.OrdersModule.Web/Model/KeyValuePair.cs rename to src/VirtoCommerce.OrdersModule.Core/Model/KeyValuePair.cs index da59db2eb..6e207517a 100644 --- a/src/VirtoCommerce.OrdersModule.Web/Model/KeyValuePair.cs +++ b/src/VirtoCommerce.OrdersModule.Core/Model/KeyValuePair.cs @@ -1,4 +1,4 @@ -namespace VirtoCommerce.OrdersModule.Web.Model +namespace VirtoCommerce.OrdersModule.Core.Model { public class KeyValuePair { diff --git a/src/VirtoCommerce.OrdersModule.Web/Model/PaymentCallbackParameters.cs b/src/VirtoCommerce.OrdersModule.Core/Model/PaymentCallbackParameters.cs similarity index 70% rename from src/VirtoCommerce.OrdersModule.Web/Model/PaymentCallbackParameters.cs rename to src/VirtoCommerce.OrdersModule.Core/Model/PaymentCallbackParameters.cs index 88b3d6ee3..cfe127355 100644 --- a/src/VirtoCommerce.OrdersModule.Web/Model/PaymentCallbackParameters.cs +++ b/src/VirtoCommerce.OrdersModule.Core/Model/PaymentCallbackParameters.cs @@ -1,4 +1,4 @@ -namespace VirtoCommerce.OrdersModule.Web.Model +namespace VirtoCommerce.OrdersModule.Core.Model { public class PaymentCallbackParameters { diff --git a/src/VirtoCommerce.OrdersModule.Core/Services/IPaymentParametersConverter.cs b/src/VirtoCommerce.OrdersModule.Core/Services/IPaymentParametersConverter.cs new file mode 100644 index 000000000..40f66fdf6 --- /dev/null +++ b/src/VirtoCommerce.OrdersModule.Core/Services/IPaymentParametersConverter.cs @@ -0,0 +1,10 @@ +using VirtoCommerce.OrdersModule.Core.Model; + +namespace VirtoCommerce.OrdersModule.Core.Services +{ + public interface IPaymentParametersConverter + { + PaymentParameters GetPaymentParameters(PaymentCallbackParameters request); + PaymentParameters GetPaymentParameters(string requestBody, string requestQuery); + } +} diff --git a/src/VirtoCommerce.OrdersModule.Data/Services/PaymentParametersDefaultConverter.cs b/src/VirtoCommerce.OrdersModule.Data/Services/PaymentParametersDefaultConverter.cs new file mode 100644 index 000000000..0a456e7b4 --- /dev/null +++ b/src/VirtoCommerce.OrdersModule.Data/Services/PaymentParametersDefaultConverter.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Specialized; +using Newtonsoft.Json; +using VirtoCommerce.OrdersModule.Core.Model; +using VirtoCommerce.OrdersModule.Core.Services; + +namespace VirtoCommerce.OrdersModule.Data.Services +{ + public class PaymentParametersDefaultConverter : IPaymentParametersConverter + { + public virtual PaymentParameters GetPaymentParameters(PaymentCallbackParameters request) + { + var result = new PaymentParameters(); + + result.Parameters = new NameValueCollection(); + foreach (var parameter in request?.Parameters ?? Array.Empty()) + { + result.Parameters.Add(parameter.Key, parameter.Value); + } + + result.OrderId = result.Parameters.Get("orderid"); + result.PaymentMethodCode = result.Parameters.Get("code"); + + return result; + } + + public virtual PaymentParameters GetPaymentParameters(string requestBody, string requestQuery) + { + var paymentCallbackParameters = JsonConvert.DeserializeObject(requestBody); + return GetPaymentParameters(paymentCallbackParameters); + } + } +} diff --git a/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs b/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs index 17ebed069..2c1d3dfe0 100644 --- a/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs +++ b/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs @@ -30,8 +30,6 @@ using VirtoCommerce.OrdersModule.Data.Caching; using VirtoCommerce.OrdersModule.Data.Extensions; using VirtoCommerce.OrdersModule.Data.Model; -using VirtoCommerce.OrdersModule.Web.Converters; -using VirtoCommerce.OrdersModule.Web.Model; using VirtoCommerce.PaymentModule.Core.Model; using VirtoCommerce.PaymentModule.Data; using VirtoCommerce.PaymentModule.Model.Requests; diff --git a/src/VirtoCommerce.OrdersModule.Web/Converters/IPaymentParametersConverter.cs b/src/VirtoCommerce.OrdersModule.Web/Converters/IPaymentParametersConverter.cs deleted file mode 100644 index 12fc8f5f5..000000000 --- a/src/VirtoCommerce.OrdersModule.Web/Converters/IPaymentParametersConverter.cs +++ /dev/null @@ -1,10 +0,0 @@ -using VirtoCommerce.OrdersModule.Core.Model; -using VirtoCommerce.OrdersModule.Web.Model; - -namespace VirtoCommerce.OrdersModule.Web.Converters; - -public interface IPaymentParametersConverter -{ - PaymentParameters GetPaymentParameters(PaymentCallbackParameters request); - PaymentParameters GetPaymentParameters(string requestBody, string requestQuery); -} diff --git a/src/VirtoCommerce.OrdersModule.Web/Converters/PaymentParametersDefaultConverter.cs b/src/VirtoCommerce.OrdersModule.Web/Converters/PaymentParametersDefaultConverter.cs deleted file mode 100644 index caf63f6ef..000000000 --- a/src/VirtoCommerce.OrdersModule.Web/Converters/PaymentParametersDefaultConverter.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Specialized; -using Newtonsoft.Json; -using VirtoCommerce.OrdersModule.Core.Model; -using VirtoCommerce.OrdersModule.Web.Model; - -namespace VirtoCommerce.OrdersModule.Web.Converters; - -public class PaymentParametersDefaultConverter : IPaymentParametersConverter -{ - public virtual PaymentParameters GetPaymentParameters(PaymentCallbackParameters request) - { - var result = new PaymentParameters(); - - result.Parameters = new NameValueCollection(); - foreach (var parameter in request?.Parameters ?? Array.Empty()) - { - result.Parameters.Add(parameter.Key, parameter.Value); - } - - result.OrderId = result.Parameters.Get("orderid"); - result.PaymentMethodCode = result.Parameters.Get("code"); - - return result; - } - - public virtual PaymentParameters GetPaymentParameters(string requestBody, string requestQuery) - { - var paymentCallbackParameters = JsonConvert.DeserializeObject(requestBody); - return GetPaymentParameters(paymentCallbackParameters); - } -} diff --git a/src/VirtoCommerce.OrdersModule.Web/Module.cs b/src/VirtoCommerce.OrdersModule.Web/Module.cs index adfd536f4..0f30e2ff2 100644 --- a/src/VirtoCommerce.OrdersModule.Web/Module.cs +++ b/src/VirtoCommerce.OrdersModule.Web/Module.cs @@ -30,7 +30,6 @@ using VirtoCommerce.OrdersModule.Data.Services; using VirtoCommerce.OrdersModule.Data.SqlServer; using VirtoCommerce.OrdersModule.Web.Authorization; -using VirtoCommerce.OrdersModule.Web.Converters; using VirtoCommerce.OrdersModule.Web.Extensions; using VirtoCommerce.OrdersModule.Web.JsonConverters; using VirtoCommerce.Platform.Core.Common; diff --git a/src/VirtoCommerce.OrdersModule.Web/VirtoCommerce.OrdersModule.Web.csproj b/src/VirtoCommerce.OrdersModule.Web/VirtoCommerce.OrdersModule.Web.csproj index 5f8ace3a2..ea1bb8c57 100644 --- a/src/VirtoCommerce.OrdersModule.Web/VirtoCommerce.OrdersModule.Web.csproj +++ b/src/VirtoCommerce.OrdersModule.Web/VirtoCommerce.OrdersModule.Web.csproj @@ -34,6 +34,7 @@ + From e29dfdd925fd9dd2f0b0d321f4e856376f5438e8 Mon Sep 17 00:00:00 2001 From: yuskithedeveloper Date: Tue, 9 Sep 2025 11:18:55 -0500 Subject: [PATCH 10/15] Response conversion --- ...nverter.cs => IPaymentRequestConverter.cs} | 5 +++- ...r.cs => PaymentRequestDefaultConverter.cs} | 25 ++++++++++++++++- .../Controllers/Api/OrderModuleController.cs | 27 ++++++++----------- src/VirtoCommerce.OrdersModule.Web/Module.cs | 2 +- 4 files changed, 40 insertions(+), 19 deletions(-) rename src/VirtoCommerce.OrdersModule.Core/Services/{IPaymentParametersConverter.cs => IPaymentRequestConverter.cs} (55%) rename src/VirtoCommerce.OrdersModule.Data/Services/{PaymentParametersDefaultConverter.cs => PaymentRequestDefaultConverter.cs} (57%) diff --git a/src/VirtoCommerce.OrdersModule.Core/Services/IPaymentParametersConverter.cs b/src/VirtoCommerce.OrdersModule.Core/Services/IPaymentRequestConverter.cs similarity index 55% rename from src/VirtoCommerce.OrdersModule.Core/Services/IPaymentParametersConverter.cs rename to src/VirtoCommerce.OrdersModule.Core/Services/IPaymentRequestConverter.cs index 40f66fdf6..c3c7b263f 100644 --- a/src/VirtoCommerce.OrdersModule.Core/Services/IPaymentParametersConverter.cs +++ b/src/VirtoCommerce.OrdersModule.Core/Services/IPaymentRequestConverter.cs @@ -1,10 +1,13 @@ using VirtoCommerce.OrdersModule.Core.Model; +using VirtoCommerce.PaymentModule.Model.Requests; namespace VirtoCommerce.OrdersModule.Core.Services { - public interface IPaymentParametersConverter + public interface IPaymentRequestConverter { PaymentParameters GetPaymentParameters(PaymentCallbackParameters request); PaymentParameters GetPaymentParameters(string requestBody, string requestQuery); + bool IsFailure(PostProcessPaymentRequestResult result); + object GetResponse(PostProcessPaymentRequestResult result); } } diff --git a/src/VirtoCommerce.OrdersModule.Data/Services/PaymentParametersDefaultConverter.cs b/src/VirtoCommerce.OrdersModule.Data/Services/PaymentRequestDefaultConverter.cs similarity index 57% rename from src/VirtoCommerce.OrdersModule.Data/Services/PaymentParametersDefaultConverter.cs rename to src/VirtoCommerce.OrdersModule.Data/Services/PaymentRequestDefaultConverter.cs index 0a456e7b4..d81b119ef 100644 --- a/src/VirtoCommerce.OrdersModule.Data/Services/PaymentParametersDefaultConverter.cs +++ b/src/VirtoCommerce.OrdersModule.Data/Services/PaymentRequestDefaultConverter.cs @@ -3,10 +3,12 @@ using Newtonsoft.Json; using VirtoCommerce.OrdersModule.Core.Model; using VirtoCommerce.OrdersModule.Core.Services; +using VirtoCommerce.OrdersModule.Data.Model; +using VirtoCommerce.PaymentModule.Model.Requests; namespace VirtoCommerce.OrdersModule.Data.Services { - public class PaymentParametersDefaultConverter : IPaymentParametersConverter + public class PaymentRequestDefaultConverter : IPaymentRequestConverter { public virtual PaymentParameters GetPaymentParameters(PaymentCallbackParameters request) { @@ -29,5 +31,26 @@ public virtual PaymentParameters GetPaymentParameters(string requestBody, string var paymentCallbackParameters = JsonConvert.DeserializeObject(requestBody); return GetPaymentParameters(paymentCallbackParameters); } + + public virtual bool IsFailure(PostProcessPaymentRequestResult result) + { + return result is PostProcessPaymentRequestNotValidResult; + } + + public virtual object GetResponse(PostProcessPaymentRequestResult result) + { + if (IsFailure(result)) + { + return new + { + Message = (result as PostProcessPaymentRequestNotValidResult)?.ErrorMessage, + Errors = (result as PostProcessPaymentRequestNotValidResult)?.Errors + }; + } + else + { + return result; + } + } } } diff --git a/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs b/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs index 2c1d3dfe0..94f8a045a 100644 --- a/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs +++ b/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs @@ -29,7 +29,6 @@ using VirtoCommerce.OrdersModule.Data.Authorization; using VirtoCommerce.OrdersModule.Data.Caching; using VirtoCommerce.OrdersModule.Data.Extensions; -using VirtoCommerce.OrdersModule.Data.Model; using VirtoCommerce.PaymentModule.Core.Model; using VirtoCommerce.PaymentModule.Data; using VirtoCommerce.PaymentModule.Model.Requests; @@ -68,7 +67,7 @@ public class OrderModuleController( IOptions outputJsonSerializerSettings, IValidator customerOrderValidator, ISettingsManager settingsManager, - IPaymentParametersConverter paymentParametersConverter, + IPaymentRequestConverter paymentParametersConverter, ICustomerOrderPaymentService customerOrderPaymentService) : Controller { @@ -526,16 +525,14 @@ public async Task> PostProcessPaym var result = await customerOrderPaymentService.PostProcessPaymentAsync(parameters); - if (result is PostProcessPaymentRequestNotValidResult notValidResult) + object response = paymentParametersConverter.GetResponse(result); + + if (paymentParametersConverter.IsFailure(result)) { - return BadRequest(new - { - Message = notValidResult.ErrorMessage, - notValidResult.Errors - }); + return BadRequest(response); } - return Ok(result); + return Ok(response); } /// @@ -549,16 +546,14 @@ public async Task> PostProcessPaym var result = await customerOrderPaymentService.PostProcessPaymentAsync(parameters); - if (result is PostProcessPaymentRequestNotValidResult notValidResult) + object response = paymentParametersConverter.GetResponse(result); + + if (paymentParametersConverter.IsFailure(result)) { - return BadRequest(new - { - Message = notValidResult.ErrorMessage, - notValidResult.Errors - }); + return BadRequest(response); } - return Ok(result); + return Ok(response); } private async Task GetRequestBody() diff --git a/src/VirtoCommerce.OrdersModule.Web/Module.cs b/src/VirtoCommerce.OrdersModule.Web/Module.cs index 0f30e2ff2..5e72a5d72 100644 --- a/src/VirtoCommerce.OrdersModule.Web/Module.cs +++ b/src/VirtoCommerce.OrdersModule.Web/Module.cs @@ -102,7 +102,7 @@ public void Initialize(IServiceCollection serviceCollection) serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); - serviceCollection.AddTransient(); + serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); From b1e6b8d371d04b16a3ee34164ca1f837587981b5 Mon Sep 17 00:00:00 2001 From: yuskithedeveloper Date: Tue, 9 Sep 2025 11:23:46 -0500 Subject: [PATCH 11/15] Minor refactoring --- .../Services/CustomerOrderPaymentService.cs | 9 ++++++++- .../Services/PaymentRequestDefaultConverter.cs | 7 +++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs b/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs index 6f75afc55..d8dcb01ce 100644 --- a/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs +++ b/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs @@ -37,8 +37,10 @@ public virtual async Task PostProcessPaymentAsy var searchCriteria = AbstractTypeFactory.TryCreateInstance(); searchCriteria.Number = paymentParameters.OrderId; searchCriteria.ResponseGroup = CustomerOrderResponseGroup.Full.ToString(); + //if order not found by order number search by order id var orders = await customerOrderSearchService.SearchAsync(searchCriteria); + var customerOrder = orders.Results.FirstOrDefault() ?? await customerOrderService.GetByIdAsync(paymentParameters.OrderId, CustomerOrderResponseGroup.Full.ToString()); if (customerOrder == null) @@ -47,6 +49,7 @@ public virtual async Task PostProcessPaymentAsy } var store = await storeService.GetByIdAsync(customerOrder.StoreId, StoreResponseGroup.StoreInfo.ToString()); + if (store == null) { throw new InvalidOperationException($"Cannot find store with ID {customerOrder.StoreId}"); @@ -57,9 +60,9 @@ public virtual async Task PostProcessPaymentAsy { //Each payment method must check that these parameters are addressed to it var result = inPayment.PaymentMethod.ValidatePostProcessRequest(paymentParameters.Parameters); + if (result.IsSuccess) { - var request = new PostProcessPaymentRequest { OrderId = customerOrder.Id, @@ -71,7 +74,9 @@ public virtual async Task PostProcessPaymentAsy OuterId = result.OuterId, Parameters = paymentParameters.Parameters }; + var retVal = inPayment.PaymentMethod.PostProcessPayment(request); + if (retVal != null) { var validationResult = await ValidateAsync(customerOrder); @@ -88,9 +93,11 @@ public virtual async Task PostProcessPaymentAsy // order Number is required retVal.OrderId = customerOrder.Number; } + return retVal; } } + return new PostProcessPaymentRequestResult { ErrorMessage = "Payment method not found" }; } diff --git a/src/VirtoCommerce.OrdersModule.Data/Services/PaymentRequestDefaultConverter.cs b/src/VirtoCommerce.OrdersModule.Data/Services/PaymentRequestDefaultConverter.cs index d81b119ef..d2e6aa246 100644 --- a/src/VirtoCommerce.OrdersModule.Data/Services/PaymentRequestDefaultConverter.cs +++ b/src/VirtoCommerce.OrdersModule.Data/Services/PaymentRequestDefaultConverter.cs @@ -15,6 +15,7 @@ public virtual PaymentParameters GetPaymentParameters(PaymentCallbackParameters var result = new PaymentParameters(); result.Parameters = new NameValueCollection(); + foreach (var parameter in request?.Parameters ?? Array.Empty()) { result.Parameters.Add(parameter.Key, parameter.Value); @@ -47,10 +48,8 @@ public virtual object GetResponse(PostProcessPaymentRequestResult result) Errors = (result as PostProcessPaymentRequestNotValidResult)?.Errors }; } - else - { - return result; - } + + return result; } } } From 162383ca272d7eea56b821afae2d0d2b2cdaef77 Mon Sep 17 00:00:00 2001 From: yuskithedeveloper Date: Thu, 11 Sep 2025 08:46:59 -0500 Subject: [PATCH 12/15] Refactoring --- .../Services/CustomerOrderPaymentService.cs | 68 ++++++++++--------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs b/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs index d8dcb01ce..23b5337f4 100644 --- a/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs +++ b/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs @@ -59,43 +59,49 @@ public virtual async Task PostProcessPaymentAsy foreach (var inPayment in customerOrder.InPayments.Where(x => x.PaymentMethod != null && (string.IsNullOrEmpty(paymentParameters.PaymentMethodCode) || x.GatewayCode.EqualsIgnoreCase(paymentParameters.PaymentMethodCode)))) { //Each payment method must check that these parameters are addressed to it - var result = inPayment.PaymentMethod.ValidatePostProcessRequest(paymentParameters.Parameters); + var paymentMethodValidationResult = inPayment.PaymentMethod.ValidatePostProcessRequest(paymentParameters.Parameters); - if (result.IsSuccess) + if (!paymentMethodValidationResult.IsSuccess) { - var request = new PostProcessPaymentRequest - { - OrderId = customerOrder.Id, - Order = customerOrder, - PaymentId = inPayment.Id, - Payment = inPayment, - StoreId = customerOrder.StoreId, - Store = store, - OuterId = result.OuterId, - Parameters = paymentParameters.Parameters - }; + continue; + } + + var paymentMethodRequest = new PostProcessPaymentRequest + { + OrderId = customerOrder.Id, + Order = customerOrder, + PaymentId = inPayment.Id, + Payment = inPayment, + StoreId = customerOrder.StoreId, + Store = store, + OuterId = paymentMethodValidationResult.OuterId, + Parameters = paymentParameters.Parameters + }; + + var paymentMethodPostProcessResult = inPayment.PaymentMethod.PostProcessPayment(paymentMethodRequest); + + if (paymentMethodPostProcessResult == null) + { + continue; + } - var retVal = inPayment.PaymentMethod.PostProcessPayment(request); + var customerOrderValidationResult = await ValidateAsync(customerOrder); - if (retVal != null) + if (!customerOrderValidationResult.IsValid) + { + return new PostProcessPaymentRequestNotValidResult() { - var validationResult = await ValidateAsync(customerOrder); - if (!validationResult.IsValid) - { - return new PostProcessPaymentRequestNotValidResult() - { - Errors = validationResult.Errors, - ErrorMessage = string.Join(" ", validationResult.Errors.Select(x => x.ErrorMessage)) - }; - } - await customerOrderService.SaveChangesAsync(new[] { customerOrder }); - - // order Number is required - retVal.OrderId = customerOrder.Number; - } - - return retVal; + Errors = customerOrderValidationResult.Errors, + ErrorMessage = string.Join(" ", customerOrderValidationResult.Errors.Select(x => x.ErrorMessage)) + }; } + + await customerOrderService.SaveChangesAsync(new[] { customerOrder }); + + // order Number is required + paymentMethodPostProcessResult.OrderId = customerOrder.Number; + + return paymentMethodPostProcessResult; } return new PostProcessPaymentRequestResult { ErrorMessage = "Payment method not found" }; From 15157d6a7d9425edbb152aa99bc03b15530d75e2 Mon Sep 17 00:00:00 2001 From: yuskithedeveloper Date: Thu, 11 Sep 2025 09:04:12 -0500 Subject: [PATCH 13/15] Refactoring --- .../Services/CustomerOrderPaymentService.cs | 51 +++++++++++-------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs b/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs index 23b5337f4..ac7454db0 100644 --- a/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs +++ b/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using FluentValidation; @@ -28,39 +29,23 @@ public virtual async Task PostProcessPaymentAsy { ArgumentNullException.ThrowIfNull(paymentParameters); - if (string.IsNullOrEmpty(paymentParameters.OrderId)) - { - throw new InvalidOperationException("The 'orderid' parameter must be passed"); - } - - //some payment method require customer number to be passed and returned. First search customer order by number - var searchCriteria = AbstractTypeFactory.TryCreateInstance(); - searchCriteria.Number = paymentParameters.OrderId; - searchCriteria.ResponseGroup = CustomerOrderResponseGroup.Full.ToString(); - - //if order not found by order number search by order id - var orders = await customerOrderSearchService.SearchAsync(searchCriteria); - - var customerOrder = orders.Results.FirstOrDefault() ?? await customerOrderService.GetByIdAsync(paymentParameters.OrderId, CustomerOrderResponseGroup.Full.ToString()); - + var customerOrder = await GetCustomerOrder(paymentParameters); if (customerOrder == null) { throw new InvalidOperationException($"Cannot find order with ID {paymentParameters.OrderId}"); } var store = await storeService.GetByIdAsync(customerOrder.StoreId, StoreResponseGroup.StoreInfo.ToString()); - if (store == null) { throw new InvalidOperationException($"Cannot find store with ID {customerOrder.StoreId}"); } - //Need to use concrete payment method if it code passed otherwise use all order payment methods - foreach (var inPayment in customerOrder.InPayments.Where(x => x.PaymentMethod != null && (string.IsNullOrEmpty(paymentParameters.PaymentMethodCode) || x.GatewayCode.EqualsIgnoreCase(paymentParameters.PaymentMethodCode)))) + var inPayments = GetInPayments(customerOrder, paymentParameters); + foreach (var inPayment in inPayments) { //Each payment method must check that these parameters are addressed to it var paymentMethodValidationResult = inPayment.PaymentMethod.ValidatePostProcessRequest(paymentParameters.Parameters); - if (!paymentMethodValidationResult.IsSuccess) { continue; @@ -79,14 +64,12 @@ public virtual async Task PostProcessPaymentAsy }; var paymentMethodPostProcessResult = inPayment.PaymentMethod.PostProcessPayment(paymentMethodRequest); - if (paymentMethodPostProcessResult == null) { continue; } var customerOrderValidationResult = await ValidateAsync(customerOrder); - if (!customerOrderValidationResult.IsValid) { return new PostProcessPaymentRequestNotValidResult() @@ -107,6 +90,32 @@ public virtual async Task PostProcessPaymentAsy return new PostProcessPaymentRequestResult { ErrorMessage = "Payment method not found" }; } + protected virtual async Task GetCustomerOrder(PaymentParameters paymentParameters) + { + if (string.IsNullOrEmpty(paymentParameters.OrderId)) + { + throw new InvalidOperationException("The 'orderid' parameter must be passed"); + } + + //some payment method require customer number to be passed and returned. First search customer order by number + var searchCriteria = AbstractTypeFactory.TryCreateInstance(); + searchCriteria.Number = paymentParameters.OrderId; + searchCriteria.ResponseGroup = CustomerOrderResponseGroup.Full.ToString(); + + //if order not found by order number search by order id + var orders = await customerOrderSearchService.SearchAsync(searchCriteria); + + return orders.Results.FirstOrDefault() ?? await customerOrderService.GetByIdAsync(paymentParameters.OrderId, CustomerOrderResponseGroup.Full.ToString()); + } + + protected virtual IList GetInPayments(CustomerOrder customerOrder, PaymentParameters paymentParameters) + { + //Need to use concrete payment method if it code passed otherwise use all order payment methods + return customerOrder.InPayments + .Where(x => x.PaymentMethod != null && (string.IsNullOrEmpty(paymentParameters.PaymentMethodCode) || x.GatewayCode.EqualsIgnoreCase(paymentParameters.PaymentMethodCode))) + .ToList(); + } + protected virtual async Task ValidateAsync(CustomerOrder customerOrder) { if (await settingsManager.GetValueAsync(ModuleConstants.Settings.General.CustomerOrderValidation)) From d65a25a2bdff7df37a63484abb7afe155ea5a148 Mon Sep 17 00:00:00 2001 From: Artem Dudarev Date: Mon, 15 Sep 2025 19:06:02 +0200 Subject: [PATCH 14/15] Refactoring --- .../Model/PaymentParameters.cs | 2 +- .../Services/IPaymentRequestConverter.cs | 14 +- .../Services/CustomerOrderPaymentService.cs | 166 +++++++++--------- .../PaymentRequestDefaultConverter.cs | 65 +++---- .../Controllers/Api/OrderModuleController.cs | 38 ++-- .../VirtoCommerce.OrdersModule.Web.csproj | 1 - .../MigrationTests.cs | 14 +- 7 files changed, 141 insertions(+), 159 deletions(-) diff --git a/src/VirtoCommerce.OrdersModule.Core/Model/PaymentParameters.cs b/src/VirtoCommerce.OrdersModule.Core/Model/PaymentParameters.cs index 6af2447be..2f9bcb6fe 100644 --- a/src/VirtoCommerce.OrdersModule.Core/Model/PaymentParameters.cs +++ b/src/VirtoCommerce.OrdersModule.Core/Model/PaymentParameters.cs @@ -6,5 +6,5 @@ public class PaymentParameters { public string OrderId { get; set; } public string PaymentMethodCode { get; set; } - public NameValueCollection Parameters { get; set; } + public NameValueCollection Parameters { get; set; } = new(); } diff --git a/src/VirtoCommerce.OrdersModule.Core/Services/IPaymentRequestConverter.cs b/src/VirtoCommerce.OrdersModule.Core/Services/IPaymentRequestConverter.cs index c3c7b263f..bfaf4807c 100644 --- a/src/VirtoCommerce.OrdersModule.Core/Services/IPaymentRequestConverter.cs +++ b/src/VirtoCommerce.OrdersModule.Core/Services/IPaymentRequestConverter.cs @@ -1,13 +1,11 @@ using VirtoCommerce.OrdersModule.Core.Model; using VirtoCommerce.PaymentModule.Model.Requests; -namespace VirtoCommerce.OrdersModule.Core.Services +namespace VirtoCommerce.OrdersModule.Core.Services; + +public interface IPaymentRequestConverter { - public interface IPaymentRequestConverter - { - PaymentParameters GetPaymentParameters(PaymentCallbackParameters request); - PaymentParameters GetPaymentParameters(string requestBody, string requestQuery); - bool IsFailure(PostProcessPaymentRequestResult result); - object GetResponse(PostProcessPaymentRequestResult result); - } + PaymentParameters GetPaymentParameters(PaymentCallbackParameters request); + PaymentParameters GetPaymentParameters(string requestBody, string requestQuery); + (object, bool) GetResponse(PostProcessPaymentRequestResult result); } diff --git a/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs b/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs index ac7454db0..b7dba2b77 100644 --- a/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs +++ b/src/VirtoCommerce.OrdersModule.Data/Services/CustomerOrderPaymentService.cs @@ -15,115 +15,115 @@ using VirtoCommerce.StoreModule.Core.Model; using VirtoCommerce.StoreModule.Core.Services; -namespace VirtoCommerce.OrdersModule.Data.Services +namespace VirtoCommerce.OrdersModule.Data.Services; + +public class CustomerOrderPaymentService( + IStoreService storeService, + ICustomerOrderService customerOrderService, + ICustomerOrderSearchService customerOrderSearchService, + IValidator customerOrderValidator, + ISettingsManager settingsManager) + : ICustomerOrderPaymentService { - public class CustomerOrderPaymentService( - IStoreService storeService, - ICustomerOrderService customerOrderService, - ICustomerOrderSearchService customerOrderSearchService, - IValidator customerOrderValidator, - ISettingsManager settingsManager) - : ICustomerOrderPaymentService + public virtual async Task PostProcessPaymentAsync(PaymentParameters paymentParameters) { - public virtual async Task PostProcessPaymentAsync(PaymentParameters paymentParameters) + ArgumentNullException.ThrowIfNull(paymentParameters); + + var customerOrder = await GetCustomerOrder(paymentParameters); + if (customerOrder == null) + { + throw new InvalidOperationException($"Cannot find order with ID {paymentParameters.OrderId}"); + } + + var store = await storeService.GetByIdAsync(customerOrder.StoreId, nameof(StoreResponseGroup.StoreInfo)); + if (store == null) { - ArgumentNullException.ThrowIfNull(paymentParameters); + throw new InvalidOperationException($"Cannot find store with ID {customerOrder.StoreId}"); + } + + var inPayments = GetInPayments(customerOrder, paymentParameters); - var customerOrder = await GetCustomerOrder(paymentParameters); - if (customerOrder == null) + foreach (var inPayment in inPayments) + { + // Each payment method must check that these parameters are addressed to it + var paymentMethodValidationResult = inPayment.PaymentMethod.ValidatePostProcessRequest(paymentParameters.Parameters); + if (!paymentMethodValidationResult.IsSuccess) { - throw new InvalidOperationException($"Cannot find order with ID {paymentParameters.OrderId}"); + continue; } - var store = await storeService.GetByIdAsync(customerOrder.StoreId, StoreResponseGroup.StoreInfo.ToString()); - if (store == null) + var paymentMethodRequest = new PostProcessPaymentRequest + { + OrderId = customerOrder.Id, + Order = customerOrder, + PaymentId = inPayment.Id, + Payment = inPayment, + StoreId = customerOrder.StoreId, + Store = store, + OuterId = paymentMethodValidationResult.OuterId, + Parameters = paymentParameters.Parameters, + }; + + var paymentMethodPostProcessResult = inPayment.PaymentMethod.PostProcessPayment(paymentMethodRequest); + if (paymentMethodPostProcessResult == null) { - throw new InvalidOperationException($"Cannot find store with ID {customerOrder.StoreId}"); + continue; } - var inPayments = GetInPayments(customerOrder, paymentParameters); - foreach (var inPayment in inPayments) + var customerOrderValidationResult = await ValidateAsync(customerOrder); + if (!customerOrderValidationResult.IsValid) { - //Each payment method must check that these parameters are addressed to it - var paymentMethodValidationResult = inPayment.PaymentMethod.ValidatePostProcessRequest(paymentParameters.Parameters); - if (!paymentMethodValidationResult.IsSuccess) + return new PostProcessPaymentRequestNotValidResult { - continue; - } - - var paymentMethodRequest = new PostProcessPaymentRequest - { - OrderId = customerOrder.Id, - Order = customerOrder, - PaymentId = inPayment.Id, - Payment = inPayment, - StoreId = customerOrder.StoreId, - Store = store, - OuterId = paymentMethodValidationResult.OuterId, - Parameters = paymentParameters.Parameters + Errors = customerOrderValidationResult.Errors, + ErrorMessage = string.Join(" ", customerOrderValidationResult.Errors.Select(x => x.ErrorMessage)), }; + } - var paymentMethodPostProcessResult = inPayment.PaymentMethod.PostProcessPayment(paymentMethodRequest); - if (paymentMethodPostProcessResult == null) - { - continue; - } - - var customerOrderValidationResult = await ValidateAsync(customerOrder); - if (!customerOrderValidationResult.IsValid) - { - return new PostProcessPaymentRequestNotValidResult() - { - Errors = customerOrderValidationResult.Errors, - ErrorMessage = string.Join(" ", customerOrderValidationResult.Errors.Select(x => x.ErrorMessage)) - }; - } + await customerOrderService.SaveChangesAsync([customerOrder]); - await customerOrderService.SaveChangesAsync(new[] { customerOrder }); + // Order number is required + paymentMethodPostProcessResult.OrderId = customerOrder.Number; - // order Number is required - paymentMethodPostProcessResult.OrderId = customerOrder.Number; + return paymentMethodPostProcessResult; + } - return paymentMethodPostProcessResult; - } + return new PostProcessPaymentRequestResult { ErrorMessage = "Payment method not found" }; + } - return new PostProcessPaymentRequestResult { ErrorMessage = "Payment method not found" }; + protected virtual async Task GetCustomerOrder(PaymentParameters paymentParameters) + { + if (string.IsNullOrEmpty(paymentParameters.OrderId)) + { + throw new InvalidOperationException("The 'orderid' parameter must be passed"); } - protected virtual async Task GetCustomerOrder(PaymentParameters paymentParameters) - { - if (string.IsNullOrEmpty(paymentParameters.OrderId)) - { - throw new InvalidOperationException("The 'orderid' parameter must be passed"); - } + // Some payment method require customer number to be passed and returned. First search customer order by number + var searchCriteria = AbstractTypeFactory.TryCreateInstance(); + searchCriteria.Number = paymentParameters.OrderId; + searchCriteria.ResponseGroup = nameof(CustomerOrderResponseGroup.Full); - //some payment method require customer number to be passed and returned. First search customer order by number - var searchCriteria = AbstractTypeFactory.TryCreateInstance(); - searchCriteria.Number = paymentParameters.OrderId; - searchCriteria.ResponseGroup = CustomerOrderResponseGroup.Full.ToString(); + // If order is not found by order number, search by order id + var orders = await customerOrderSearchService.SearchAsync(searchCriteria); - //if order not found by order number search by order id - var orders = await customerOrderSearchService.SearchAsync(searchCriteria); + return orders.Results.FirstOrDefault() ?? await customerOrderService.GetByIdAsync(paymentParameters.OrderId, nameof(CustomerOrderResponseGroup.Full)); + } - return orders.Results.FirstOrDefault() ?? await customerOrderService.GetByIdAsync(paymentParameters.OrderId, CustomerOrderResponseGroup.Full.ToString()); - } + protected virtual IList GetInPayments(CustomerOrder customerOrder, PaymentParameters paymentParameters) + { + // Need to use concrete payment method if its code has been passed, otherwise use all order payment methods + return customerOrder.InPayments + .Where(x => x.PaymentMethod != null && (string.IsNullOrEmpty(paymentParameters.PaymentMethodCode) || x.GatewayCode.EqualsIgnoreCase(paymentParameters.PaymentMethodCode))) + .ToList(); + } - protected virtual IList GetInPayments(CustomerOrder customerOrder, PaymentParameters paymentParameters) + protected virtual async Task ValidateAsync(CustomerOrder customerOrder) + { + if (await settingsManager.GetValueAsync(ModuleConstants.Settings.General.CustomerOrderValidation)) { - //Need to use concrete payment method if it code passed otherwise use all order payment methods - return customerOrder.InPayments - .Where(x => x.PaymentMethod != null && (string.IsNullOrEmpty(paymentParameters.PaymentMethodCode) || x.GatewayCode.EqualsIgnoreCase(paymentParameters.PaymentMethodCode))) - .ToList(); + return await customerOrderValidator.ValidateAsync(customerOrder); } - protected virtual async Task ValidateAsync(CustomerOrder customerOrder) - { - if (await settingsManager.GetValueAsync(ModuleConstants.Settings.General.CustomerOrderValidation)) - { - return await customerOrderValidator.ValidateAsync(customerOrder); - } - - return new ValidationResult(); - } + return new ValidationResult(); } } diff --git a/src/VirtoCommerce.OrdersModule.Data/Services/PaymentRequestDefaultConverter.cs b/src/VirtoCommerce.OrdersModule.Data/Services/PaymentRequestDefaultConverter.cs index d2e6aa246..8494d37d5 100644 --- a/src/VirtoCommerce.OrdersModule.Data/Services/PaymentRequestDefaultConverter.cs +++ b/src/VirtoCommerce.OrdersModule.Data/Services/PaymentRequestDefaultConverter.cs @@ -1,55 +1,48 @@ -using System; -using System.Collections.Specialized; using Newtonsoft.Json; using VirtoCommerce.OrdersModule.Core.Model; using VirtoCommerce.OrdersModule.Core.Services; using VirtoCommerce.OrdersModule.Data.Model; using VirtoCommerce.PaymentModule.Model.Requests; +using VirtoCommerce.Platform.Core.Common; -namespace VirtoCommerce.OrdersModule.Data.Services +namespace VirtoCommerce.OrdersModule.Data.Services; + +public class PaymentRequestDefaultConverter : IPaymentRequestConverter { - public class PaymentRequestDefaultConverter : IPaymentRequestConverter + public virtual PaymentParameters GetPaymentParameters(PaymentCallbackParameters request) { - public virtual PaymentParameters GetPaymentParameters(PaymentCallbackParameters request) - { - var result = new PaymentParameters(); + var result = AbstractTypeFactory.TryCreateInstance(); - result.Parameters = new NameValueCollection(); + foreach (var parameter in request?.Parameters ?? []) + { + result.Parameters.Add(parameter.Key, parameter.Value); + } - foreach (var parameter in request?.Parameters ?? Array.Empty()) - { - result.Parameters.Add(parameter.Key, parameter.Value); - } + result.OrderId = result.Parameters.Get("orderid"); + result.PaymentMethodCode = result.Parameters.Get("code"); - result.OrderId = result.Parameters.Get("orderid"); - result.PaymentMethodCode = result.Parameters.Get("code"); + return result; + } - return result; - } + public virtual PaymentParameters GetPaymentParameters(string requestBody, string requestQuery) + { + var paymentCallbackParameters = JsonConvert.DeserializeObject(requestBody); + return GetPaymentParameters(paymentCallbackParameters); + } - public virtual PaymentParameters GetPaymentParameters(string requestBody, string requestQuery) + public virtual (object, bool) GetResponse(PostProcessPaymentRequestResult result) + { + if (result is PostProcessPaymentRequestNotValidResult notValidResult) { - var paymentCallbackParameters = JsonConvert.DeserializeObject(requestBody); - return GetPaymentParameters(paymentCallbackParameters); - } + var response = new + { + Message = notValidResult.ErrorMessage, + Errors = notValidResult.Errors, + }; - public virtual bool IsFailure(PostProcessPaymentRequestResult result) - { - return result is PostProcessPaymentRequestNotValidResult; + return (response, false); } - public virtual object GetResponse(PostProcessPaymentRequestResult result) - { - if (IsFailure(result)) - { - return new - { - Message = (result as PostProcessPaymentRequestNotValidResult)?.ErrorMessage, - Errors = (result as PostProcessPaymentRequestNotValidResult)?.Errors - }; - } - - return result; - } + return (result, true); } } diff --git a/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs b/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs index 94f8a045a..144d31f12 100644 --- a/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs +++ b/src/VirtoCommerce.OrdersModule.Web/Controllers/Api/OrderModuleController.cs @@ -1,5 +1,7 @@ using System; +using System.IO; using System.Linq; +using System.Text; using System.Threading.Tasks; using DinkToPdf; using DinkToPdf.Contracts; @@ -67,7 +69,7 @@ public class OrderModuleController( IOptions outputJsonSerializerSettings, IValidator customerOrderValidator, ISettingsManager settingsManager, - IPaymentRequestConverter paymentParametersConverter, + IPaymentRequestConverter paymentRequestConverter, ICustomerOrderPaymentService customerOrderPaymentService) : Controller { @@ -521,18 +523,14 @@ public async Task> GetDashboardStatistic [Route("~/api/paymentcallback")] public async Task> PostProcessPayment([FromBody] PaymentCallbackParameters callback) { - var parameters = paymentParametersConverter.GetPaymentParameters(callback); - + var parameters = paymentRequestConverter.GetPaymentParameters(callback); var result = await customerOrderPaymentService.PostProcessPaymentAsync(parameters); - object response = paymentParametersConverter.GetResponse(result); + var (response, succeeded) = paymentRequestConverter.GetResponse(result); - if (paymentParametersConverter.IsFailure(result)) - { - return BadRequest(response); - } - - return Ok(response); + return succeeded + ? Ok(response) + : BadRequest(response); } /// @@ -542,26 +540,20 @@ public async Task> PostProcessPaym [Route("~/api/paymentcallback-raw")] public async Task> PostProcessPaymentRaw() { - var parameters = paymentParametersConverter.GetPaymentParameters(await GetRequestBody(), Request.QueryString.Value); - + var parameters = paymentRequestConverter.GetPaymentParameters(await GetRequestBody(), Request.QueryString.Value); var result = await customerOrderPaymentService.PostProcessPaymentAsync(parameters); - object response = paymentParametersConverter.GetResponse(result); + var (response, succeeded) = paymentRequestConverter.GetResponse(result); - if (paymentParametersConverter.IsFailure(result)) - { - return BadRequest(response); - } - - return Ok(response); + return succeeded + ? Ok(response) + : BadRequest(response); } private async Task GetRequestBody() { - using (var reader = new System.IO.StreamReader(Request.Body, System.Text.Encoding.UTF8, true, 1024, true)) - { - return await reader.ReadToEndAsync(); - } + using var reader = new StreamReader(Request.Body, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, 1024, leaveOpen: true); + return await reader.ReadToEndAsync(); } [HttpGet] diff --git a/src/VirtoCommerce.OrdersModule.Web/VirtoCommerce.OrdersModule.Web.csproj b/src/VirtoCommerce.OrdersModule.Web/VirtoCommerce.OrdersModule.Web.csproj index ea1bb8c57..5f8ace3a2 100644 --- a/src/VirtoCommerce.OrdersModule.Web/VirtoCommerce.OrdersModule.Web.csproj +++ b/src/VirtoCommerce.OrdersModule.Web/VirtoCommerce.OrdersModule.Web.csproj @@ -34,7 +34,6 @@ - diff --git a/tests/VirtoCommerce.OrdersModule.Tests/MigrationTests.cs b/tests/VirtoCommerce.OrdersModule.Tests/MigrationTests.cs index 5e18d459b..3262f4529 100644 --- a/tests/VirtoCommerce.OrdersModule.Tests/MigrationTests.cs +++ b/tests/VirtoCommerce.OrdersModule.Tests/MigrationTests.cs @@ -13,18 +13,18 @@ public class MigrationTests [Fact] public void CheckMigrationConsistency() { - DateTime after = new DateTime(2025, 1, 1); + var after = new DateTime(2025, 1, 1); - var mySqlMigrations = GetMigrationTypes(typeof(VirtoCommerce.OrdersModule.Data.MySql.MySqlDataAssemblyMarker).Assembly, after); - var postgreMigrations = GetMigrationTypes(typeof(VirtoCommerce.OrdersModule.Data.PostgreSql.PostgreSqlDataAssemblyMarker).Assembly, after); - var sqlServerMigrations = GetMigrationTypes(typeof(VirtoCommerce.OrdersModule.Data.SqlServer.SqlServerDataAssemblyMarker).Assembly, after); + var mySqlMigrations = GetMigrationTypes(typeof(Data.MySql.MySqlDataAssemblyMarker).Assembly, after); + var postgreMigrations = GetMigrationTypes(typeof(Data.PostgreSql.PostgreSqlDataAssemblyMarker).Assembly, after); + var sqlServerMigrations = GetMigrationTypes(typeof(Data.SqlServer.SqlServerDataAssemblyMarker).Assembly, after); Assert.True(mySqlMigrations.Count == postgreMigrations.Count, $"MySql migrations count {mySqlMigrations.Count} doesn't match PostgreSQL migrations count {postgreMigrations.Count}"); Assert.True(sqlServerMigrations.Count == postgreMigrations.Count, $"SqlServer migrations count {sqlServerMigrations.Count} doesn't match PostgreSQL migrations count {postgreMigrations.Count}"); Assert.True(sqlServerMigrations.Count == mySqlMigrations.Count, $"SqlServer migrations count {sqlServerMigrations.Count} doesn't match MySql migrations count {mySqlMigrations.Count}"); } - private IList GetMigrationTypes(Assembly assembly, DateTime? after = null) + private static IList GetMigrationTypes(Assembly assembly, DateTime? after = null) { var result = assembly.GetTypes() .Where(x => typeof(Migration).IsAssignableFrom(x)); @@ -37,11 +37,11 @@ private IList GetMigrationTypes(Assembly assembly, DateTime? after = null) return result.ToList(); } - private DateTime? GetMigrationDate(Type migrationType) + private static DateTime? GetMigrationDate(Type migrationType) { var migrationAttribute = migrationType.GetCustomAttribute(); - if (migrationAttribute == null || migrationAttribute.Id == null) + if (migrationAttribute?.Id == null) { return null; } From 1febbfed5659d35287c566283d397bc95e6f5d0b Mon Sep 17 00:00:00 2001 From: yuskithedeveloper Date: Mon, 15 Sep 2025 12:11:56 -0500 Subject: [PATCH 15/15] Test removed --- .../MigrationTests.cs | 56 ------------------- 1 file changed, 56 deletions(-) delete mode 100644 tests/VirtoCommerce.OrdersModule.Tests/MigrationTests.cs diff --git a/tests/VirtoCommerce.OrdersModule.Tests/MigrationTests.cs b/tests/VirtoCommerce.OrdersModule.Tests/MigrationTests.cs deleted file mode 100644 index 3262f4529..000000000 --- a/tests/VirtoCommerce.OrdersModule.Tests/MigrationTests.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Microsoft.EntityFrameworkCore.Migrations; -using Xunit; - -namespace VirtoCommerce.OrdersModule.Tests; - -[Trait("Category", "CI")] -public class MigrationTests -{ - [Fact] - public void CheckMigrationConsistency() - { - var after = new DateTime(2025, 1, 1); - - var mySqlMigrations = GetMigrationTypes(typeof(Data.MySql.MySqlDataAssemblyMarker).Assembly, after); - var postgreMigrations = GetMigrationTypes(typeof(Data.PostgreSql.PostgreSqlDataAssemblyMarker).Assembly, after); - var sqlServerMigrations = GetMigrationTypes(typeof(Data.SqlServer.SqlServerDataAssemblyMarker).Assembly, after); - - Assert.True(mySqlMigrations.Count == postgreMigrations.Count, $"MySql migrations count {mySqlMigrations.Count} doesn't match PostgreSQL migrations count {postgreMigrations.Count}"); - Assert.True(sqlServerMigrations.Count == postgreMigrations.Count, $"SqlServer migrations count {sqlServerMigrations.Count} doesn't match PostgreSQL migrations count {postgreMigrations.Count}"); - Assert.True(sqlServerMigrations.Count == mySqlMigrations.Count, $"SqlServer migrations count {sqlServerMigrations.Count} doesn't match MySql migrations count {mySqlMigrations.Count}"); - } - - private static IList GetMigrationTypes(Assembly assembly, DateTime? after = null) - { - var result = assembly.GetTypes() - .Where(x => typeof(Migration).IsAssignableFrom(x)); - - if (after != null) - { - result = result.Where(x => GetMigrationDate(x) >= after).ToList(); - } - - return result.ToList(); - } - - private static DateTime? GetMigrationDate(Type migrationType) - { - var migrationAttribute = migrationType.GetCustomAttribute(); - - if (migrationAttribute?.Id == null) - { - return null; - } - - if (DateTime.TryParseExact(migrationAttribute.Id.Split('_')[0], "yyyyMMddHHmmss", null, System.Globalization.DateTimeStyles.None, out var migrationDate)) - { - return migrationDate; - } - - return null; - } -}