Webiant Logo Webiant Logo
  1. No results found.

    Try your search with a different keyword or use * as a wildcard.

OmnisendService.cs

using Newtonsoft.Json;
using Nop.Core;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Directory;
using Nop.Core.Domain.Messages;
using Nop.Core.Domain.Orders;
using Nop.Data;
using Nop.Plugin.Misc.Omnisend.DTO;
using Nop.Services.Catalog;
using Nop.Services.Configuration;
using Nop.Services.Customers;
using Nop.Services.Directory;
using Nop.Services.Orders;
using Nop.Services.Tax;

namespace Nop.Plugin.Misc.Omnisend.Services;

/// 
/// Represents the main plugin service class
/// 
public class OmnisendService
{
    #region Fields

    private readonly ICategoryService _categoryService;
    private readonly ICountryService _countryService;
    private readonly ICustomerService _customerService;
    private readonly IOrderService _orderService;
    private readonly IOrderTotalCalculationService _orderTotalCalculationService;
    private readonly IProductAttributeService _productAttributeService;
    private readonly IProductService _productService;
    private readonly IRepository _countryRepository;
    private readonly IRepository _customerRepository;
    private readonly IRepository _newsLetterSubscriptionRepository;
    private readonly IRepository _stateProvinceRepository;
    private readonly ISettingService _settingService;
    private readonly IShoppingCartService _shoppingCartService;
    private readonly IStateProvinceService _stateProvinceService;
    private readonly IStoreContext _storeContext;
    private readonly ITaxService _taxService;
    private readonly IWebHelper _webHelper;
    private readonly IWorkContext _workContext;
    private readonly OmnisendCustomerService _omnisendCustomerService;
    private readonly OmnisendHelper _omnisendHelper;
    private readonly OmnisendHttpClient _omnisendHttpClient;
    private readonly OmnisendSettings _omnisendSettings;

    #endregion

    #region Ctor

    public OmnisendService(ICategoryService categoryService,
        ICountryService countryService,
        ICustomerService customerService,
        IOrderService orderService,
        IOrderTotalCalculationService orderTotalCalculationService,
        IProductAttributeService productAttributeService,
        IProductService productService,
        IRepository countryRepository,
        IRepository customerRepository,
        IRepository newsLetterSubscriptionRepository,
        IRepository stateProvinceRepository,
        ISettingService settingService,
        IShoppingCartService shoppingCartService,
        IStateProvinceService stateProvinceService,
        IStoreContext storeContext,
        ITaxService taxService,
        IWebHelper webHelper,
        IWorkContext workContext,
        OmnisendCustomerService omnisendCustomerService,
        OmnisendHelper omnisendHelper,
        OmnisendHttpClient omnisendHttpClient,
        OmnisendSettings omnisendSettings)
    {
        _categoryService = categoryService;
        _countryService = countryService;
        _customerService = customerService;
        _orderService = orderService;
        _orderTotalCalculationService = orderTotalCalculationService;
        _productAttributeService = productAttributeService;
        _productService = productService;
        _countryRepository = countryRepository;
        _customerRepository = customerRepository;
        _newsLetterSubscriptionRepository = newsLetterSubscriptionRepository;
        _stateProvinceRepository = stateProvinceRepository;
        _settingService = settingService;
        _shoppingCartService = shoppingCartService;
        _stateProvinceService = stateProvinceService;
        _storeContext = storeContext;
        _taxService = taxService;
        _webHelper = webHelper;
        _workContext = workContext;
        _omnisendCustomerService = omnisendCustomerService;
        _omnisendHelper = omnisendHelper;
        _omnisendHttpClient = omnisendHttpClient;
        _omnisendSettings = omnisendSettings;
    }

    #endregion

    #region Utilities

    private async Task FillCustomerInfoAsync(BaseContactInfoDto dto, Customer customer)
    {
        if (customer == null)
            return;

        dto.FirstName = customer.FirstName;
        dto.LastName = customer.LastName;

        var country = await _countryService.GetCountryByIdAsync(customer.CountryId);

        if (country != null)
        {
            dto.Country = country.Name;
            dto.CountryCode = country.TwoLetterIsoCode;
        }

        var state = await _stateProvinceService.GetStateProvinceByIdAsync(customer.StateProvinceId);

        if (state != null)
            dto.State = state.Name;

        dto.City = customer.City;
        dto.Address = customer.StreetAddress;
        dto.PostalCode = customer.ZipPostalCode;
        dto.Gender = customer.Gender?.ToLower() ?? "f";
        dto.BirthDate = customer.DateOfBirth?.ToString("yyyy-MM-dd");
    }

    private async Task OrderItemToDtoAsync(OrderItem orderItem)
    {
        var product = await _productService.GetProductByIdAsync(orderItem.ProductId);

        var (sku, variantId) = await _omnisendHelper.GetSkuAndVariantIdAsync(product, orderItem.AttributesXml);

        var dto = new OrderDto.OrderItemDto
        {
            ProductId = orderItem.ProductId.ToString(),
            Sku = sku,
            VariantId = variantId.ToString(),
            Title = product.Name,
            Quantity = orderItem.Quantity,
            Price = orderItem.PriceInclTax.ToCents()
        };

        return dto;
    }

    private async Task ProductToDtoAsync(Product product)
    {
        async Task> getProductCategories()
        {
            var productCategories = await _categoryService.GetProductCategoriesByProductIdAsync(product.Id);

            return productCategories.Select(pc => pc.CategoryId.ToString()).ToList();
        }

        async Task> getProductCombinations()
        {
            return await _productAttributeService.GetAllProductAttributeCombinationsAsync(product.Id);
        }

        var combinations = await getProductCombinations();

        async Task getProductStatus(ProductAttributeCombination productAttributeCombination = null)
        {
            var status = "notAvailable";

            if (!product.Published || product.Deleted)
                return status;

            int stockQuantity;

            switch (product.ManageInventoryMethod)
            {
                case ManageInventoryMethod.ManageStock:
                    stockQuantity = await _productService.GetTotalStockQuantityAsync(product);

                    if (stockQuantity > 0 || product.BackorderMode == BackorderMode.AllowQtyBelow0)
                        status = "inStock";
                    else
                        status = "outOfStock";

                    break;
                case ManageInventoryMethod.ManageStockByAttributes:
                    if (productAttributeCombination == null)
                        return combinations.Any(c => c.StockQuantity > 0 || c.AllowOutOfStockOrders) ? "inStock" : "outOfStock";

                    stockQuantity = productAttributeCombination.StockQuantity;

                    if (stockQuantity > 0 || productAttributeCombination.AllowOutOfStockOrders)
                        status = "inStock";
                    else
                        status = "outOfStock";

                    break;
                case ManageInventoryMethod.DontManageStock:
                    status = "inStock";
                    break;
            }

            return status;
        }

        var dto = new ProductDto
        {
            ProductId = product.Id.ToString(),
            Title = product.Name,
            Status = await getProductStatus(),
            Description = product.ShortDescription,
            Currency = await _omnisendHelper.GetPrimaryStoreCurrencyCodeAsync(),
            ProductUrl = await _omnisendHelper.GetProductUrlAsync(product),
            Images = new List
            {
                await _omnisendHelper.GetProductPictureUrlAsync(product),
            },
            CreatedAt = product.CreatedOnUtc.ToDtoString(),
            UpdatedAt = product.UpdatedOnUtc.ToDtoString(),
            CategoryIDs = await getProductCategories(),
            Variants = new List
            {
                new()
                {
                    VariantId = product.Id.ToString(),
                    Title = product.Name,
                    Sku = product.Sku,
                    Status = await getProductStatus(),
                    Price = product.Price.ToCents()
                }
            }
        };

        if (combinations.Any())
            dto.Variants.AddRange(await combinations.SelectAwait(async c => new ProductDto.Variant
            {
                VariantId = c.Id.ToString(),
                Title = product.Name,
                Sku = c.Sku,
                Status = await getProductStatus(c),
                Price = (c.OverriddenPrice ?? product.Price).ToCents()
            }).ToListAsync());

        return dto;
    }

    private async Task OrderToDtoAsync(Order order)
    {
        var customer = await _customerService.GetCustomerByIdAsync(order.CustomerId);
        var email = await _omnisendCustomerService.GetEmailAsync(customer, order.BillingAddressId);
        if (string.IsNullOrEmpty(email))
            return null;

        var dto = new OrderDto
        {
            OrderId = order.OrderGuid.ToString(),
            Email = email,
            Currency = await _omnisendHelper.GetPrimaryStoreCurrencyCodeAsync(),
            OrderSum = order.OrderTotal.ToCents(),
            SubTotalSum = order.OrderSubtotalInclTax.ToCents(),
            DiscountSum = order.OrderDiscount.ToCents(),
            TaxSum = order.OrderTax.ToCents(),
            ShippingSum = order.OrderShippingInclTax.ToCents(),
            CreatedAt = order.CreatedOnUtc.ToDtoString(),
            Products = await (await _orderService.GetOrderItemsAsync(order.Id)).SelectAwait(async oi => await OrderItemToDtoAsync(oi)).ToListAsync()
        };

        return dto;
    }

    private async Task ShoppingCartItemToDtoAsync(ShoppingCartItem shoppingCartItem, Customer customer)
    {
        var product = await _productService.GetProductByIdAsync(shoppingCartItem.ProductId);

        var (sku, variantId) = await _omnisendHelper.GetSkuAndVariantIdAsync(product, shoppingCartItem.AttributesXml);

        var (scSubTotal, _, _, _) = await _shoppingCartService.GetSubTotalAsync(shoppingCartItem, true);

        var dto = new CartItemDto
        {
            CartProductId = shoppingCartItem.Id.ToString(),
            ProductId = shoppingCartItem.ProductId.ToString(),
            Sku = sku,
            VariantId = variantId.ToString(),
            Title = product.Name,
            Quantity = shoppingCartItem.Quantity,
            Currency = await _omnisendHelper.GetPrimaryStoreCurrencyCodeAsync(),
            Price = (await _taxService.GetProductPriceAsync(product, scSubTotal, true, customer)).price.ToCents()
        };

        return dto;
    }

    private CategoryDto CategoryToDto(Category category)
    {
        return new CategoryDto
        {
            CategoryId = category.Id.ToString(),
            Title = category.Name,
            CreatedAt = category.CreatedOnUtc.ToDtoString()
        };
    }

    private async Task GetCartDtoAsync(IList cart)
    {
        if (!cart.Any())
            return null;

        var customerId = cart.First().CustomerId;
        var customer = await _customerService.GetCustomerByIdAsync(customerId);

        var items = await cart
            .SelectAwait(async ci => await ShoppingCartItemToDtoAsync(ci, customer))
            .ToListAsync();

        var cartSum = (await _orderTotalCalculationService.GetShoppingCartTotalAsync(cart)).shoppingCartTotal
            ?.ToCents() ?? 0;

        var email = await _omnisendCustomerService.GetEmailAsync(customer);
        if (string.IsNullOrEmpty(email))
            return null;

        var cartId = await _omnisendCustomerService.GetCartIdAsync(customer);

        return new CartDto
        {
            Currency = await _omnisendHelper.GetPrimaryStoreCurrencyCodeAsync(),
            Email = email,
            CartId = cartId,
            CartSum = cartSum,
            Products = items,
            CartRecoveryUrl = _omnisendCustomerService.GetAbandonedCheckoutUrl(cartId)
        };
    }

    private async Task CreateOrderAsync(Order order)
    {
        var orderDto = await OrderToDtoAsync(order);
        if (orderDto is null)
            return;

        var data = JsonConvert.SerializeObject(orderDto);
        await _omnisendHttpClient.PerformRequestAsync(OmnisendDefaults.OrdersApiUrl, data, HttpMethod.Post);
    }

    private async Task CreateCartAsync(IList cart)
    {
        var cartDto = await GetCartDtoAsync(cart);
        if (cartDto is null)
            return;

        var data = JsonConvert.SerializeObject(cartDto);
        await _omnisendHttpClient.PerformRequestAsync(OmnisendDefaults.CartsApiUrl, data, HttpMethod.Post);
    }

    private async Task UpdateCartAsync(Customer customer, ShoppingCartItem newItem)
    {
        var item = await ShoppingCartItemToDtoAsync(newItem, customer);
        if (item is null)
            return;

        var data = JsonConvert.SerializeObject(item);
        await _omnisendHttpClient.PerformRequestAsync($"{OmnisendDefaults.CartsApiUrl}/{await _omnisendCustomerService.GetCartIdAsync(customer)}/{OmnisendDefaults.ProductsEndpoint}", data, HttpMethod.Post);
    }

    private async Task CanSendRequestAsync(Customer customer)
    {
        return !string.IsNullOrEmpty(
            await _omnisendCustomerService.GetEmailAsync(customer, customer.BillingAddressId));
    }

    /// 
    /// Prepare newsletter subscribers to sync
    /// 
    /// Store identifier
    /// Page index
    /// Page size
    /// Specifies whether to send a welcome message
    /// Newsletter subscription to filter
    /// Inactive status
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the list of subscriber data
    /// 
    private async Task> PrepareNewsletterSubscribersAsync(int storeId,
        int pageIndex, int pageSize,
        bool sendWelcomeMessage = false, NewsLetterSubscription subscriber = null, string inactiveStatus = "nonSubscribed")
    {
        //get contacts of newsletter subscribers
        var subscriptions = (subscriber == null ? _newsLetterSubscriptionRepository.Table : _newsLetterSubscriptionRepository.Table.Where(nlsr => nlsr.Id.Equals(subscriber.Id)))
            .Where(subscription => subscription.StoreId == storeId)
            .OrderBy(subscription => subscription.Id)
            .Skip(pageIndex * pageSize)
            .Take(pageSize);

        var contacts = from item in subscriptions
            join c in _customerRepository.Table on item.Email equals c.Email
                into temp
            from c in temp.DefaultIfEmpty()
            where c == null || (c.Active && !c.Deleted)
            select new { subscription = item, customer = c };

        var contactsWithCountry = from item in contacts
            join cr in _countryRepository.Table on item.customer.CountryId equals cr.Id
                into temp
            from cr in temp.DefaultIfEmpty()
            select new { item.customer, item.subscription, country = cr };

        var contactsWithState = from item in contactsWithCountry
            join sp in _stateProvinceRepository.Table on item.customer.StateProvinceId equals sp.Id
                into temp
            from sp in temp.DefaultIfEmpty()
            select new
            {
                item.subscription,
                item.customer.FirstName,
                item.customer.LastName,
                CountryName = item.country.Name,
                CountryTwoLetterIsoCode = item.country.TwoLetterIsoCode,
                StateProvinceName = sp.Name,
                item.customer.City,
                item.customer.StreetAddress,
                item.customer.ZipPostalCode,
                item.customer.Gender,
                item.customer.DateOfBirth
            };

        var subscribers = (await contactsWithState.ToListAsync()).Select(item =>
        {
            var dto = new CreateContactRequest(item.subscription, inactiveStatus, sendWelcomeMessage)
            {
                FirstName = item.FirstName,
                LastName = item.LastName,
                City = item.City,
                Address = item.StreetAddress,
                PostalCode = item.ZipPostalCode,
                Gender = item.Gender?.ToLower(),
                BirthDate = item.DateOfBirth?.ToString("yyyy-MM-dd")
            };

            if (!string.IsNullOrEmpty(item.CountryName))
                dto.Country = item.CountryName;

            if (!string.IsNullOrEmpty(item.CountryTwoLetterIsoCode))
                dto.CountryCode = item.CountryTwoLetterIsoCode;

            if (!string.IsNullOrEmpty(item.StateProvinceName))
                dto.State = item.StateProvinceName;

            return (IBatchSupport)dto;
        }).ToList();

        return subscribers;
    }

    #endregion

    #region Methods

    #region Sync methods

    /// 
    /// Synchronize contacts
    /// 
    public async Task SyncContactsAsync()
    {
        var store = await _storeContext.GetCurrentStoreAsync();
        var su = await PrepareNewsletterSubscribersAsync(store.Id, 0, _omnisendSettings.PageSize);

        if (su.Count >= OmnisendDefaults.MinCountToUseBatch)
        {
            var page = 0;

            while (true)
            {
                var data = JsonConvert.SerializeObject(new BatchRequest
                {
                    Endpoint = OmnisendDefaults.ContactsEndpoint,
                    Items = su
                });

                var rez = await _omnisendHttpClient.PerformRequestAsync(OmnisendDefaults.BatchesApiUrl, data, HttpMethod.Post);

                var bathId = JsonConvert.DeserializeAnonymousType(rez, new { batchID = "" })?.batchID;

                if (bathId != null)
                {
                    _omnisendSettings.BatchesIds.Add(bathId);
                    await _settingService.SaveSettingAsync(_omnisendSettings);
                }

                page++;

                su = await PrepareNewsletterSubscribersAsync(store.Id, page, _omnisendSettings.PageSize);

                if (!su.Any())
                    break;
            }
        }
        else
            foreach (var newsLetterSubscription in su)
                await UpdateOrCreateContactAsync(newsLetterSubscription as CreateContactRequest);
    }

    /// 
    /// Synchronize categories
    /// 
    public async Task SyncCategoriesAsync()
    {
        var categories = await _categoryService.GetAllCategoriesAsync(null, pageSize: _omnisendSettings.PageSize);

        if (categories.TotalCount >= OmnisendDefaults.MinCountToUseBatch || categories.TotalCount > _omnisendSettings.PageSize)
        {
            var page = 0;

            while (page < categories.TotalPages)
            {
                var data = JsonConvert.SerializeObject(new BatchRequest
                {
                    Endpoint = OmnisendDefaults.CategoriesEndpoint,
                    Items = categories.Select(category => CategoryToDto(category) as IBatchSupport).ToList()
                });

                var rez = await _omnisendHttpClient.PerformRequestAsync(OmnisendDefaults.BatchesApiUrl, data, HttpMethod.Post);

                var bathId = JsonConvert.DeserializeAnonymousType(rez, new { batchID = "" })?.batchID;

                if (bathId != null)
                {
                    _omnisendSettings.BatchesIds.Add(bathId);
                    await _settingService.SaveSettingAsync(_omnisendSettings);
                }

                page++;

                categories = await _categoryService.GetAllCategoriesAsync(null, pageIndex: page, pageSize: _omnisendSettings.PageSize);
            }
        }
        else
            foreach (var category in categories)
            {
                var data = JsonConvert.SerializeObject(CategoryToDto(category));
                await _omnisendHttpClient.PerformRequestAsync(OmnisendDefaults.CategoriesApiUrl, data, HttpMethod.Post);
            }
    }

    /// 
    /// Synchronize products
    /// 
    public async Task SyncProductsAsync()
    {
        var products = await _productService.SearchProductsAsync(pageSize: _omnisendSettings.PageSize);

        if (products.TotalCount >= OmnisendDefaults.MinCountToUseBatch || products.TotalCount > _omnisendSettings.PageSize)
        {
            var page = 0;

            while (page < products.TotalPages)
            {
                var data = JsonConvert.SerializeObject(new BatchRequest
                {
                    Endpoint = OmnisendDefaults.ProductsEndpoint,
                    Items = await products.SelectAwait(async product => await ProductToDtoAsync(product) as IBatchSupport).ToListAsync()
                });

                var rez = await _omnisendHttpClient.PerformRequestAsync(OmnisendDefaults.BatchesApiUrl, data, HttpMethod.Post);

                var bathId = JsonConvert.DeserializeAnonymousType(rez, new { batchID = "" })?.batchID;

                if (bathId != null)
                {
                    _omnisendSettings.BatchesIds.Add(bathId);
                    await _settingService.SaveSettingAsync(_omnisendSettings);
                }

                page++;

                products = await _productService.SearchProductsAsync(pageIndex: page, pageSize: _omnisendSettings.PageSize);
            }
        }
        else
            foreach (var product in products)
                await AddNewProductAsync(product);
    }

    /// 
    /// Synchronize orders
    /// 
    public async Task SyncOrdersAsync()
    {
        var orders = await _orderService.SearchOrdersAsync(pageSize: _omnisendSettings.PageSize);

        if (orders.TotalCount >= OmnisendDefaults.MinCountToUseBatch || orders.TotalCount > _omnisendSettings.PageSize)
        {
            var page = 0;

            while (page < orders.TotalPages)
            {
                var data = JsonConvert.SerializeObject(new BatchRequest
                {
                    Endpoint = OmnisendDefaults.OrdersEndpoint,
                    Items = await orders.SelectAwait(async order => await OrderToDtoAsync(order) as IBatchSupport).ToListAsync()
                });

                var rez = await _omnisendHttpClient.PerformRequestAsync(OmnisendDefaults.BatchesApiUrl, data, HttpMethod.Post);

                var bathId = JsonConvert.DeserializeAnonymousType(rez, new { batchID = "" })?.batchID;

                if (bathId != null)
                {
                    _omnisendSettings.BatchesIds.Add(bathId);
                    await _settingService.SaveSettingAsync(_omnisendSettings);
                }

                page++;

                orders = await _orderService.SearchOrdersAsync(pageIndex: page, pageSize: _omnisendSettings.PageSize);
            }
        }
        else
            foreach (var order in orders)
                await CreateOrderAsync(order);
    }

    /// 
    /// Synchronize shopping carts
    /// 
    public async Task SyncCartsAsync()
    {
        var store = await _storeContext.GetCurrentStoreAsync();
        var customers = await _customerService.GetCustomersWithShoppingCartsAsync(ShoppingCartType.ShoppingCart, store.Id);
        foreach (var customer in customers)
        {
            var cart = await _shoppingCartService.GetShoppingCartAsync(customer, ShoppingCartType.ShoppingCart, store.Id);
            await CreateCartAsync(cart);
        }
    }

    #endregion

    #region Configuration

    /// 
    /// Gets the brand identifier 
    /// 
    /// API key to send request
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the brand identifier or null
    /// 
    public async Task GetBrandIdAsync(string apiKey)
    {
        _omnisendHttpClient.ApiKey = apiKey;

        var result = await _omnisendHttpClient.PerformRequestAsync(OmnisendDefaults.AccountsApiUrl);

        return result?.BrandId;
    }

    /// 
    /// Registers the site on the omnisend service
    /// 
    public async Task SendCustomerDataAsync()
    {
        var site = _webHelper.GetStoreLocation();

        var data = JsonConvert.SerializeObject(new
        {
            website = site,
            platform = OmnisendDefaults.IntegrationOrigin,
            version = OmnisendDefaults.IntegrationVersion
        });

        await _omnisendHttpClient.PerformRequestAsync(OmnisendDefaults.AccountsApiUrl, data, HttpMethod.Post);
    }

    /// 
    /// Gets information about batch
    /// 
    /// Batch identifier
    public async ValueTask GetBatchInfoAsync(string bathId)
    {
        var url = OmnisendDefaults.BatchesApiUrl + $"/{bathId}";

        var result = await _omnisendHttpClient.PerformRequestAsync(url, httpMethod: HttpMethod.Get);

        return result;
    }

    #endregion

    #region Contacts

    /// 
    /// Gets the contacts information
    /// 
    /// Contact identifier
    public async Task GetContactInfoAsync(string contactId)
    {
        var url = $"{OmnisendDefaults.ContactsApiUrl}/{contactId}";

        var res = await _omnisendHttpClient.PerformRequestAsync(url, httpMethod: HttpMethod.Get);

        return res;
    }

    /// 
    /// Update or create contact information
    /// 
    /// Create contact request
    public async Task UpdateOrCreateContactAsync(CreateContactRequest request)
    {
        var email = request.Identifiers.First().Id;
        var exists = !string.IsNullOrEmpty(await _omnisendCustomerService.GetContactIdAsync(email));

        if (!exists)
        {
            var data = JsonConvert.SerializeObject(request);
            await _omnisendHttpClient.PerformRequestAsync(OmnisendDefaults.ContactsApiUrl, data, HttpMethod.Post);
        }
        else
        {
            var url = $"{OmnisendDefaults.ContactsApiUrl}?email={email}";
            var data = JsonConvert.SerializeObject(new { identifiers = new[] { request.Identifiers.First() } });

            await _omnisendHttpClient.PerformRequestAsync(url, data, HttpMethod.Patch);
        }
    }

    /// 
    /// Update or create contact information
    /// 
    /// Newsletter subscription
    /// Specifies whether to send a welcome message
    public async Task UpdateOrCreateContactAsync(NewsLetterSubscription subscription, bool sendWelcomeMessage = false)
    {
        var su = await PrepareNewsletterSubscribersAsync(subscription.StoreId,
            0, _omnisendSettings.PageSize,
            sendWelcomeMessage, subscription, "unsubscribed");

        if (su.FirstOrDefault() is not CreateContactRequest request)
            return;

        await UpdateOrCreateContactAsync(request);
    }

    /// 
    /// Updates contact information by customer data
    /// 
    /// Customer
    public async Task UpdateContactAsync(Customer customer)
    {
        var contactId = await _omnisendCustomerService.GetContactIdAsync(customer.Email);

        if (string.IsNullOrEmpty(contactId))
            return;

        var url = $"{OmnisendDefaults.ContactsApiUrl}/{contactId}";

        var dto = new BaseContactInfoDto();

        await FillCustomerInfoAsync(dto, customer);

        var data = JsonConvert.SerializeObject(dto);

        await _omnisendHttpClient.PerformRequestAsync(url, data, HttpMethod.Patch);
    }

    #endregion

    #region Products

    /// 
    /// Adds new product
    /// 
    /// Product to add
    public async Task AddNewProductAsync(Product product)
    {
        var data = JsonConvert.SerializeObject(await ProductToDtoAsync(product));
        await _omnisendHttpClient.PerformRequestAsync(OmnisendDefaults.ProductsApiUrl, data, HttpMethod.Post);
    }

    /// 
    /// Updates the product
    /// 
    /// Product identifier to update
    public async Task UpdateProductAsync(int productId)
    {
        var product = await _productService.GetProductByIdAsync(productId);

        await CreateOrUpdateProductAsync(product);
    }

    /// 
    /// Updates the product
    /// 
    /// Product to update
    public async Task CreateOrUpdateProductAsync(Product product)
    {
        var result = await _omnisendHttpClient.PerformRequestAsync($"{OmnisendDefaults.ProductsApiUrl}/{product.Id}", httpMethod: HttpMethod.Get);
        if (string.IsNullOrEmpty(result))
            await AddNewProductAsync(product);
        else
        {
            var data = JsonConvert.SerializeObject(await ProductToDtoAsync(product));
            await _omnisendHttpClient.PerformRequestAsync($"{OmnisendDefaults.ProductsApiUrl}/{product.Id}", data, HttpMethod.Put);
        }
    }

    #endregion

    #region Shopping cart

    /// 
    /// Restore the abandoned shopping cart
    /// 
    /// Cart identifier
    public async Task RestoreShoppingCartAsync(string cartId)
    {
        var res = await _omnisendHttpClient.PerformRequestAsync($"{OmnisendDefaults.CartsApiUrl}/{cartId}", httpMethod: HttpMethod.Get);

        if (string.IsNullOrEmpty(res))
            return;

        var restoredCart = JsonConvert.DeserializeObject(res)
            ?? throw new NopException("Cart data can't be loaded");

        var customer = await _workContext.GetCurrentCustomerAsync();
        var store = await _storeContext.GetCurrentStoreAsync();
        var cart = await _shoppingCartService.GetShoppingCartAsync(customer, ShoppingCartType.ShoppingCart, store.Id);

        foreach (var cartProduct in restoredCart.Products)
        {
            var combination = await _productAttributeService.GetProductAttributeCombinationBySkuAsync(cartProduct.Sku);
            var product = combination == null ? await _productService.GetProductBySkuAsync(cartProduct.Sku) : await _productService.GetProductByIdAsync(combination.ProductId);

            if (product == null)
            {
                if (!int.TryParse(cartProduct.VariantId, out var variantId))
                    continue;

                product = await _productService.GetProductByIdAsync(variantId);

                if (product == null)
                    continue;
            }

            var shoppingCartItem = cart.FirstOrDefault(item => item.ProductId.ToString() == cartProduct.ProductId &&
                item.Quantity == cartProduct.Quantity &&
                item.AttributesXml == combination?.AttributesXml);
            if (shoppingCartItem is not null)
                continue;

            await _shoppingCartService.AddToCartAsync(customer, product, ShoppingCartType.ShoppingCart, store.Id,
                combination?.AttributesXml, quantity: cartProduct.Quantity);
        }
    }

    /// 
    /// Adds new item to the shopping cart
    /// 
    /// Shopping cart item
    public async Task AddShoppingCartItemAsync(ShoppingCartItem shoppingCartItem)
    {
        var customer = await _customerService.GetCustomerByIdAsync(shoppingCartItem.CustomerId);

        if (!await CanSendRequestAsync(customer))
            return;

        var cart = await _shoppingCartService.GetShoppingCartAsync(customer, ShoppingCartType.ShoppingCart, shoppingCartItem.StoreId);

        if (cart.Count == 1)
            await CreateCartAsync(cart);
        else
            await UpdateCartAsync(customer, shoppingCartItem);
    }

    /// 
    /// Updates the shopping cart item
    /// 
    /// Shopping cart item
    public async Task EditShoppingCartItemAsync(ShoppingCartItem shoppingCartItem)
    {
        var customer = await _customerService.GetCustomerByIdAsync(shoppingCartItem.CustomerId);

        if (!await CanSendRequestAsync(customer))
            return;

        var item = await ShoppingCartItemToDtoAsync(shoppingCartItem, customer);

        var data = JsonConvert.SerializeObject(item);

        await _omnisendHttpClient.PerformRequestAsync($"{OmnisendDefaults.CartsApiUrl}/{await _omnisendCustomerService.GetCartIdAsync(customer)}/{OmnisendDefaults.ProductsEndpoint}/{item.CartProductId}", data, HttpMethod.Put);
    }

    /// 
    /// Deletes item from shopping cart
    /// 
    /// Shopping cart item
    public async Task DeleteShoppingCartItemAsync(ShoppingCartItem shoppingCartItem)
    {
        var customer = await _customerService.GetCustomerByIdAsync(shoppingCartItem.CustomerId);

        if (!await CanSendRequestAsync(customer))
            return;

        var cart = await _shoppingCartService.GetShoppingCartAsync(customer, ShoppingCartType.ShoppingCart, shoppingCartItem.StoreId);

        //var sendRequest = await _omnisendCustomerService.IsNeedToSendDeleteShoppingCartEventAsync(customer);

        //if (sendRequest)
        //    await _omnisendHttpClient.PerformRequestAsync($"{OmnisendDefaults.CartsApiUrl}/{await _omnisendCustomerService.GetCartIdAsync(customer)}/{OmnisendDefaults.ProductsEndpoint}/{shoppingCartItem.Id}", httpMethod: HttpMethod.Delete);

        if (!cart.Any())
        {
            //if (sendRequest)
            //    await _omnisendHttpClient.PerformRequestAsync($"{OmnisendDefaults.CartsApiUrl}/{await _omnisendCustomerService.GetCartIdAsync(customer)}", httpMethod: HttpMethod.Delete);

            await _omnisendCustomerService.DeleteCurrentCustomerShoppingCartIdAsync(customer);
        }
    }

    #endregion

    #region Orders

    /// 
    /// Sends the new order to the omnisend service
    /// 
    /// Order
    public async Task PlaceOrderAsync(Order order)
    {
        var customer = await _customerService.GetCustomerByIdAsync(order.CustomerId);

        if (!await CanSendRequestAsync(customer))
            return;

        //await CreateOrderAsync(order);

        await _omnisendCustomerService.DeleteStoredCustomerShoppingCartIdAsync(customer);
    }

    /// 
    /// Updates the order information
    /// 
    /// Order
    public async Task UpdateOrderAsync(Order order)
    {
        var customer = await _customerService.GetCustomerByIdAsync(order.CustomerId);

        if (!await CanSendRequestAsync(customer))
            return;

        var item = await OrderToDtoAsync(order);
        if (item is null)
            return;

        var data = JsonConvert.SerializeObject(item);
        await _omnisendHttpClient.PerformRequestAsync($"{OmnisendDefaults.OrdersApiUrl}/{item.OrderId}", data, HttpMethod.Put);
    }

    /// 
    /// Store the CartId during order placing
    /// 
    /// Order item
    /// 
    public async Task OrderItemAddedAsync(OrderItem entity)
    {
        var customer = await _workContext.GetCurrentCustomerAsync();
        var store = await _storeContext.GetCurrentStoreAsync();
        var cart = await _shoppingCartService.GetShoppingCartAsync(customer, ShoppingCartType.ShoppingCart, store.Id);

        if (cart.Any(sci =>
                sci.ProductId == entity.ProductId &&
                sci.AttributesXml.Equals(entity.AttributesXml, StringComparison.InvariantCultureIgnoreCase) &&
                sci.Quantity == entity.Quantity))
            await _omnisendCustomerService.StoreCartIdAsync(customer);
    }

    #endregion


    #endregion
}