Пример #1
0
        private async Task <string> CheckLimitAsync(
            double cashPeriodValue,
            double transferPeriodValue,
            CashOperationLimitation limit,
            LimitationPeriod period,
            string clientId,
            string asset,
            double amount)
        {
            double currentValue = cashPeriodValue;

            if (limit.LimitationType == LimitationType.CardAndSwiftCashIn)
            {
                currentValue += transferPeriodValue;
            }
            double limitValue = (await _currencyConverter.ConvertAsync(limit.Asset, _currencyConverter.DefaultAsset, limit.Limit)).Item2;

            if (limitValue < currentValue + amount)
            {
                return(GetPeriodLimitFailMessage(period));
            }
            double antiFraudValue = await _antiFraudCollector.GetAttemptsValueAsync(
                clientId,
                asset,
                limit.LimitationType);

            if (limitValue < currentValue + amount + antiFraudValue)
            {
                var forbidDuration = limit.LimitationType == LimitationType.CardCashIn ? CashOperationsTimeoutInMinutes : _attemptRetainInMinutes;
                return($"Please wait {forbidDuration} minute(s) after previous payment attempt");
            }
            return(null);
        }
Пример #2
0
        public async Task <(double, bool)> GetCurrentAmountAsync(
            string clientId,
            string asset,
            LimitationPeriod period,
            CurrencyOperationType operationType)
        {
            (var items, bool notCached) = await _data.GetClientDataAsync(clientId, operationType);

            double   result = 0;
            DateTime now    = DateTime.UtcNow;

            foreach (var item in items)
            {
                if (item.Asset != asset)
                {
                    continue;
                }
                if (period == LimitationPeriod.Day &&
                    now.Subtract(item.DateTime).TotalHours >= 24)
                {
                    continue;
                }

                result += item.Volume;
            }
            return(result, notCached);
        }
Пример #3
0
        private string GetPeriodLimitFailMessage(LimitationPeriod period)
        {
            switch (period)
            {
            case LimitationPeriod.Day:
                return("Operation is not allowed because of Daily limitation.");

            case LimitationPeriod.Month:
                return("Operation is not allowed because of Monthly limitation.");

            default:
                throw new NotSupportedException($"Limitation period {period} is not supported");
            }
        }
Пример #4
0
        private async Task AddRemainingLimitsAsync(string clientId, LimitationPeriod period, ClientData clientData)
        {
            var result       = new List <RemainingLimitation>();
            var periodLimits = _limits.Where(l => l.Period == period);

            foreach (var periodLimit in periodLimits)
            {
                if (!string.IsNullOrWhiteSpace(periodLimit.ClientId) && clientId != periodLimit.ClientId)
                {
                    continue;
                }

                if (periodLimit.LimitationType == LimitationType.CardCashIn)
                {
                    result.Add(
                        await CalculateRemainingAsync(
                            clientData.CashOperations,
                            clientData.OperationAttempts,
                            new List <CurrencyOperationType> {
                        CurrencyOperationType.CardCashIn
                    },
                            periodLimit));
                }
                else if (periodLimit.LimitationType == LimitationType.CryptoCashOut)
                {
                    result.Add(
                        await CalculateRemainingAsync(
                            clientData.CashOperations,
                            clientData.OperationAttempts,
                            new List <CurrencyOperationType> {
                        CurrencyOperationType.CryptoCashOut
                    },
                            periodLimit));
                }
                else if (periodLimit.LimitationType == LimitationType.CardAndSwiftCashIn)
                {
                    result.Add(
                        await CalculateRemainingAsync(
                            clientData.CashOperations.Concat(clientData.CashTransferOperations),
                            clientData.OperationAttempts,
                            new List <CurrencyOperationType> {
                        CurrencyOperationType.CardCashIn, CurrencyOperationType.SwiftTransfer
                    },
                            periodLimit));
                }
            }
            clientData.RemainingLimits = result;
        }
Пример #5
0
        public async Task <ClientData> GetClientDataAsync(string clientId, LimitationPeriod period)
        {
            var result = new ClientData
            {
                CashOperations         = await _cashOperationsCollector.GetClientDataAsync(clientId, period),
                CashTransferOperations = (await _cashTransfersCollector.GetClientDataAsync(clientId, period))
                                         .Select(i => new CashOperation
                {
                    Id            = i.Id,
                    ClientId      = i.ClientId,
                    Asset         = i.Asset,
                    Volume        = i.Volume,
                    DateTime      = i.DateTime,
                    OperationType = i.OperationType,
                })
                                         .ToList(),
                OperationAttempts = await _antiFraudCollector.GetClientDataAsync(clientId),
            };

            await AddRemainingLimitsAsync(clientId, period, result);

            return(result);
        }
        public async Task <(double, bool)> GetCurrentAmountAsync(
            string clientId,
            string asset,
            LimitationPeriod period,
            CurrencyOperationType operationType,
            bool checkAllCrypto = false)
        {
            int sign;

            switch (operationType)
            {
            case CurrencyOperationType.CardCashIn:
            case CurrencyOperationType.CryptoCashIn:
            case CurrencyOperationType.SwiftTransfer:
                sign = 1;
                break;

            case CurrencyOperationType.CardCashOut:
            case CurrencyOperationType.CryptoCashOut:
            case CurrencyOperationType.SwiftTransferOut:
                sign = -1;
                break;

            default:
                throw new NotSupportedException($"Operation type {operationType} can't be mapped to CashFlowDirection!");
            }
            (var items, bool notCached) = await _data.GetClientDataAsync(clientId, operationType);

            DateTime now    = DateTime.UtcNow;
            double   result = 0;

            foreach (var item in items)
            {
                if (period == LimitationPeriod.Day &&
                    now.Subtract(item.DateTime).TotalHours >= 24)
                {
                    continue;
                }
                if (Math.Sign(item.Volume) != Math.Sign(sign))
                {
                    continue;
                }

                double amount;

                if (checkAllCrypto)
                {
                    if (!_currencyConverter.IsNotConvertible(item.Asset))
                    {
                        continue;
                    }

                    var(_, convertedAmount) = await _currencyConverter.ConvertAsync(
                        item.Asset,
                        _currencyConverter.DefaultAsset,
                        item.Volume,
                        true);

                    amount = convertedAmount;
                }
                else
                {
                    if (item.Asset != asset)
                    {
                        continue;
                    }

                    amount = item.Volume;
                }

                result += amount;
            }
            return(Math.Abs(result), notCached);
        }
        public async Task <List <CashOperation> > GetClientDataAsync(string clientId, LimitationPeriod period)
        {
            var(result, _) = await _data.GetClientDataAsync(clientId);

            if (period == LimitationPeriod.Day)
            {
                var now = DateTime.UtcNow;
                result.RemoveAll(i => now.Subtract(i.DateTime).TotalHours >= 24);
            }
            foreach (var item in result)
            {
                if (item.OperationType.HasValue)
                {
                    continue;
                }

                item.OperationType = GetOperationType(item);
            }
            return(result);
        }
Пример #8
0
        private async Task <string> DoPeriodCheckAsync(
            IEnumerable <CashOperationLimitation> limits,
            LimitationPeriod period,
            string clientId,
            string asset,
            double amount,
            CurrencyOperationType currencyOperationType,
            bool checkAllCrypto)
        {
            var periodLimits = limits.Where(l => l.Period == period).ToList();

            if (!periodLimits.Any())
            {
                return(null);
            }

            (double cashPeriodValue, bool cashOperationsNotCached) = await _cashOperationsCollector.GetCurrentAmountAsync(
                clientId,
                asset,
                period,
                currencyOperationType,
                checkAllCrypto);

            (double transferPeriodValue, bool cashTransfersNotCached) = await _cashTransfersCollector.GetCurrentAmountAsync(
                clientId,
                asset,
                period,
                currencyOperationType);

            if (cashOperationsNotCached || cashTransfersNotCached)
            {
                try
                {
                    await _limitOperationsApi.CacheClientDataAsync(clientId, currencyOperationType);
                }
                catch (Exception ex)
                {
                    _log.Error(ex, context: new { Type = "CachOp", clientId, currencyOperationType });
                }
            }

            var clientLimit = periodLimits.FirstOrDefault(l => l.ClientId == clientId);

            if (clientLimit != null)
            {
                string checkMessage = await CheckLimitAsync(
                    cashPeriodValue,
                    transferPeriodValue,
                    clientLimit,
                    period,
                    clientId,
                    asset,
                    amount);

                if (!string.IsNullOrWhiteSpace(checkMessage))
                {
                    return(checkMessage);
                }
            }
            else
            {
                foreach (var periodLimit in periodLimits)
                {
                    if (periodLimit.ClientId != null)
                    {
                        continue;
                    }

                    string checkMessage = await CheckLimitAsync(
                        cashPeriodValue,
                        transferPeriodValue,
                        periodLimit,
                        period,
                        clientId,
                        asset,
                        amount);

                    if (!string.IsNullOrWhiteSpace(checkMessage))
                    {
                        return(checkMessage);
                    }
                }
            }

            return(null);
        }