/// <summary>
        /// Load active payment methods
        /// </summary>
        /// <param name="customer">Filter payment methods by customer and apply payment method restrictions; null to load all records</param>
        /// <param name="cart">Filter payment methods by cart amount; null to load all records</param>
        /// <param name="storeId">Filter payment methods by store identifier; pass 0 to load all records</param>
        /// <param name="types">Filter payment methods by payment method types</param>
        /// <param name="provideFallbackMethod">Provide a fallback payment method if none is active</param>
        /// <returns>Payment methods</returns>
        public virtual IEnumerable<Provider<IPaymentMethod>> LoadActivePaymentMethods(
			Customer customer = null,
			IList<OrganizedShoppingCartItem> cart = null,
			int storeId = 0,
			PaymentMethodType[] types = null,
			bool provideFallbackMethod = true)
        {
            IList<IPaymentMethodFilter> allFilters = null;
            IEnumerable<Provider<IPaymentMethod>> allProviders = null;

            var filterRequest = new PaymentFilterRequest
            {
                Cart = cart,
                StoreId = storeId,
                Customer = customer
            };

            if (types != null && types.Any())
                allProviders = LoadAllPaymentMethods(storeId).Where(x => types.Contains(x.Value.PaymentMethodType));
            else
                allProviders = LoadAllPaymentMethods(storeId);

            var activeProviders = allProviders
                .Where(p =>
                {
                    if (!p.Value.IsActive || !_paymentSettings.ActivePaymentMethodSystemNames.Contains(p.Metadata.SystemName, StringComparer.InvariantCultureIgnoreCase))
                        return false;

                    // payment method filtering
                    if (allFilters == null)
                        allFilters = GetAllPaymentMethodFilters();

                    filterRequest.PaymentMethod = p;

                    if (allFilters.Any(x => x.IsExcluded(filterRequest)))
                        return false;

                    return true;
                });

            if (!activeProviders.Any() && provideFallbackMethod)
            {
                var fallbackMethod = allProviders.FirstOrDefault(x => x.IsPaymentMethodActive(_paymentSettings));

                if (fallbackMethod == null)
                    fallbackMethod = allProviders.FirstOrDefault();

                if (fallbackMethod != null)
                {
                    return new Provider<IPaymentMethod>[] { fallbackMethod };
                }
                else
                {
                    if (DataSettings.DatabaseIsInstalled())
                        throw new SmartException(T("Payment.OneActiveMethodProviderRequired"));
                }
            }

            return activeProviders;
        }
        /// <summary>
        /// Load active payment methods
        /// </summary>
        /// <param name="customer">Filter payment methods by customer and apply payment method restrictions; null to load all records</param>
        /// <param name="cart">Filter payment methods by cart amount; null to load all records</param>
        /// <param name="storeId">Filter payment methods by store identifier; pass 0 to load all records</param>
        /// <param name="types">Filter payment methods by payment method types</param>
        /// <param name="provideFallbackMethod">Provide a fallback payment method if none is active</param>
        /// <returns>Payment methods</returns>
        public virtual IEnumerable<Provider<IPaymentMethod>> LoadActivePaymentMethods(
			Customer customer = null,
			IList<OrganizedShoppingCartItem> cart = null,
			int storeId = 0,
			PaymentMethodType[] types = null,
			bool provideFallbackMethod = true)
        {
            List<int> customerRoleIds = null;
            int? selectedShippingMethodId = null;
            decimal? orderSubTotal = null;
            decimal? orderTotal = null;
            IList<PaymentMethod> allMethods = null;
            IEnumerable<Provider<IPaymentMethod>> allProviders = null;

            if (types != null && types.Any())
                allProviders = LoadAllPaymentMethods(storeId).Where(x => types.Contains(x.Value.PaymentMethodType));
            else
                allProviders = LoadAllPaymentMethods(storeId);

            var activeProviders = allProviders
                .Where(p =>
                {
                    if (!p.Value.IsActive || !_paymentSettings.ActivePaymentMethodSystemNames.Contains(p.Metadata.SystemName, StringComparer.InvariantCultureIgnoreCase))
                        return false;

                    if (customer != null)
                    {
                        if (allMethods == null)
                            allMethods = GetAllPaymentMethods();

                        var method = allMethods.FirstOrDefault(x => x.PaymentMethodSystemName.IsCaseInsensitiveEqual(p.Metadata.SystemName));
                        if (method != null)
                        {
                            // method restricted by customer role id?
                            var excludedRoleIds = method.ExcludedCustomerRoleIds.ToIntArray();
                            if (excludedRoleIds.Any())
                            {
                                if (customerRoleIds == null)
                                    customerRoleIds = customer.CustomerRoles.Where(r => r.Active).Select(r => r.Id).ToList();

                                if (customerRoleIds != null && !customerRoleIds.Except(excludedRoleIds).Any())
                                    return false;
                            }

                            // method restricted by selected shipping method?
                            var excludedShippingMethodIds = method.ExcludedShippingMethodIds.ToIntArray();
                            if (excludedShippingMethodIds.Any())
                            {
                                if (!selectedShippingMethodId.HasValue)
                                {
                                    var selectedShipping = customer.GetAttribute<ShippingOption>(SystemCustomerAttributeNames.SelectedShippingOption, storeId);
                                    selectedShippingMethodId = (selectedShipping == null ? 0 : selectedShipping.ShippingMethodId);
                                }

                                if ((selectedShippingMethodId ?? 0) != 0 && excludedShippingMethodIds.Contains(selectedShippingMethodId.Value))
                                    return false;
                            }

                            // method restricted by country of selected billing or shipping address?
                            var excludedCountryIds = method.ExcludedCountryIds.ToIntArray();
                            if (excludedCountryIds.Any())
                            {
                                int countryId = 0;
                                if (method.CountryExclusionContext == CountryRestrictionContextType.ShippingAddress)
                                    countryId = (customer.ShippingAddress != null ? (customer.ShippingAddress.CountryId ?? 0) : 0);
                                else
                                    countryId = (customer.BillingAddress != null ? (customer.BillingAddress.CountryId ?? 0) : 0);

                                if (countryId != 0 && excludedCountryIds.Contains(countryId))
                                    return false;
                            }

                            // method restricted by min\max order amount?
                            if ((method.MinimumOrderAmount.HasValue || method.MaximumOrderAmount.HasValue) && cart != null)
                            {
                                decimal compareAmount = decimal.Zero;

                                if (method.AmountRestrictionContext == AmountRestrictionContextType.SubtotalAmount)
                                {
                                    if (!orderSubTotal.HasValue)
                                    {
                                        decimal orderSubTotalDiscountAmountBase = decimal.Zero;
                                        Discount orderSubTotalAppliedDiscount = null;
                                        decimal subTotalWithoutDiscountBase = decimal.Zero;
                                        decimal subTotalWithDiscountBase = decimal.Zero;

                                        _orderTotalCalculationService.GetShoppingCartSubTotal(cart, out orderSubTotalDiscountAmountBase, out orderSubTotalAppliedDiscount,
                                            out subTotalWithoutDiscountBase, out subTotalWithDiscountBase);

                                        orderSubTotal = _currencyService.ConvertFromPrimaryStoreCurrency(subTotalWithoutDiscountBase, _services.WorkContext.WorkingCurrency);
                                    }

                                    compareAmount = orderSubTotal.Value;
                                }
                                else if (method.AmountRestrictionContext == AmountRestrictionContextType.TotalAmount)
                                {
                                    if (!orderTotal.HasValue)
                                    {
                                        orderTotal = _orderTotalCalculationService.GetShoppingCartTotal(cart) ?? decimal.Zero;

                                        orderTotal = _currencyService.ConvertFromPrimaryStoreCurrency(orderTotal.Value, _services.WorkContext.WorkingCurrency);
                                    }

                                    compareAmount = orderTotal.Value;
                                }

                                if (method.MinimumOrderAmount.HasValue && compareAmount < method.MinimumOrderAmount.Value)
                                    return false;

                                if (method.MaximumOrderAmount.HasValue && compareAmount > method.MaximumOrderAmount.Value)
                                    return false;
                            }
                        }
                    }
                    return true;
                });

            if (!activeProviders.Any() && provideFallbackMethod)
            {
                var fallbackMethod = allProviders.FirstOrDefault(x => x.IsPaymentMethodActive(_paymentSettings));

                if (fallbackMethod == null)
                    fallbackMethod = allProviders.FirstOrDefault();

                if (fallbackMethod != null)
                {
                    return new Provider<IPaymentMethod>[] { fallbackMethod };
                }
                else
                {
                    if (DataSettings.DatabaseIsInstalled())
                        throw Error.Application("At least one payment method provider is required to be active.");
                }
            }

            return activeProviders;
        }