< Summary

Class:SVETA.Api.Services.Implements.WalletPaymentService
Assembly:SVETA.Api
File(s):/opt/dev/sveta_api_build/SVETA.Api/Services/Implements/WalletPaymentService.cs
Covered lines:31
Uncovered lines:226
Coverable lines:257
Total lines:443
Line coverage:12% (31 of 257)
Covered branches:10
Total branches:142
Branch coverage:7% (10 of 142)

Metrics

MethodLine coverage Branch coverage
.ctor(...)100%100%
GetWalletInfo()0%0%
GetWalletInfo()0%0%
GetWalletFreeBalance()73.33%66.66%
CreateTransaction()0%0%
CheckSms()0%0%
RetrySms()0%0%
ConfirmTransaction()0%0%
CancelTransaction()0%0%
GetTransaction()100%100%
ChangeDealTransaction()0%0%
CheckChangeDealTransaction()0%0%
GetTransactionInfo()0%0%
GetAccountHistory()0%0%
UpdateTransactionStatus()0%0%

File(s)

/opt/dev/sveta_api_build/SVETA.Api/Services/Implements/WalletPaymentService.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using System.Threading.Tasks;
 5using AutoMapper;
 6using Microsoft.Extensions.Logging;
 7using SVETA.Api.Data.DTO.Wallet;
 8using SVETA.Api.Helpers;
 9using SVETA.Api.Helpers.Authorize;
 10using SVETA.Api.Services.Interfaces;
 11using WinSolutions.Sveta.Common;
 12using WinSolutions.Sveta.Server.Data.DataModel.Entities;
 13using WinSolutions.Sveta.Server.Data.DataModel.Extensions;
 14using WinSolutions.Sveta.Server.Data.DataModel.Kinds;
 15using WinSolutions.Sveta.Server.Services.Interfaces;
 16
 17namespace SVETA.Api.Services.Implements
 18{
 19    public class WalletPaymentService: IWalletPaymentService
 20    {
 21        private readonly ILogger<WalletPaymentService> _logger;
 22        private readonly IWalletHttpClient _walletHttpClient;
 23        private readonly IWalletService _walletService;
 24        private readonly IMovementService _movementService;
 25        private readonly IAuthenticationService _authUserService;
 26        private readonly IContragentService _contragentService;
 8627        private string vtbOk = "ok";
 8628        private string invalidSms = "sms.sms_invalidcode";
 29
 8630        public WalletPaymentService(ILogger<WalletPaymentService> logger,
 8631            IAuthenticationService authUserService,
 8632            IWalletService walletService,
 8633            IMovementService movementService,
 8634            IContragentService contragentService,
 8635            IWalletHttpClient client)
 8636        {
 8637            _logger = logger;
 8638            _walletHttpClient = client;
 8639            _walletService = walletService;
 8640            _authUserService = authUserService;
 8641            _contragentService = contragentService;
 8642            _movementService = movementService;
 8643        }
 44
 45        /// <summary>
 46        /// Возвращает информацию по кошельку
 47        /// </summary>
 48        /// <returns></returns>
 49        public async Task<WalletBalanceResponse> GetWalletInfo()
 050        {
 051            var contragent = await _contragentService.GetContragent(_authUserService.ContragentId) ??
 052                             throw new ArgumentException($"Не найден контрагент #{_authUserService.ContragentId}");
 053            if (contragent.WalletId == default)
 054                throw new ArgumentException($"Не прописан кошелек для контрагента #{contragent.Id}");
 055            var content = await _walletHttpClient.GetWalletBalance(contragent.WalletId);
 056            if (!content.Status.Equals(vtbOk, StringComparison.OrdinalIgnoreCase))
 057                throw new ArgumentException(content.Errdesc);
 058            return content.Response;
 059        }
 60
 61        /// <summary>
 62        /// Возвращает информацию по кошельку для контрагента
 63        /// </summary>
 64        /// <param name="contragentId">Контрагент</param>
 65        /// <returns></returns>
 66        /// <exception cref="ArgumentException"></exception>
 67        public async Task<WalletBalanceResponse> GetWalletInfo(long contragentId)
 068        {
 69
 070            var contragent = await _contragentService.GetContragent(contragentId) ??
 071                             throw new ArgumentException($"Не найден контрагент #{_authUserService.ContragentId}");
 072            if (contragent.WalletId == default)
 073                throw new ArgumentException($"Не прописан кошелек для контрагента #{contragent.Id}");
 074            var content = await _walletHttpClient.GetWalletBalance(contragent.WalletId);
 075            if (!content.Status.Equals(vtbOk, StringComparison.OrdinalIgnoreCase))
 076                throw new ArgumentException(content.Errdesc);
 077            return content.Response;
 078        }
 79
 80        /// <summary>
 81        /// Проверяет доступность средств в кошельке на сумму
 82        /// </summary>
 83        /// <param name="sum"></param>
 84        /// <returns></returns>
 85        public async Task<WalletBalanceFreeResponse> GetWalletFreeBalance(long contragentId = 0, decimal sum = 0)
 7386        {
 7387            contragentId = contragentId != 0 ? contragentId : _authUserService.ContragentId;
 7388            var contragent = await _contragentService.GetContragent(contragentId) ??
 7389                             throw new ArgumentException($"Не найден контрагент #{contragentId}");
 7390            if (contragent.WalletId == default)
 091                throw new ArgumentException($"Не прописан кошелек для контрагента #{contragent.Id}");
 7392            var content = await _walletHttpClient.GetFreeBalance(contragent.WalletId, sum);
 7393            if (!content.Status.Equals(vtbOk, StringComparison.OrdinalIgnoreCase))
 094            {
 095                _logger.LogError($"Ошибка получения баланса кошелька {contragent.WalletId}");
 096                throw new ArgumentException(content.Errdesc);
 97            }
 98
 7399            var mapper = new MapperConfiguration(cfg =>
 146100                cfg.CreateMap<WalletBalanceFreeVtbResponse, WalletBalanceFreeResponse>()
 146101                    .ForMember(d => d.Id, e =>
 219102                        e.MapFrom(s => s.Acc_id)))
 73103                .CreateMapper();
 73104            return mapper.Map<WalletBalanceFreeResponse>(content.Response);
 73105        }
 106
 107        /// <summary>
 108        /// Создает транзакцию в втб
 109        /// </summary>
 110        /// <param name="movementId"></param>
 111        /// <returns></returns>
 112        public async Task CreateTransaction(long movementId)
 0113        {
 0114            Movement movement = await _movementService.GetMovementForItems(movementId) ??
 0115                                throw new ArgumentException($"Не найден документ #{movementId}");
 116
 0117            if (movement.Customer.Id != _authUserService.ContragentId && !_authUserService.IsUserPlatform()) //TODO: rem
 0118                throw new ForbidException();
 119
 0120            if (movement.MovementType.Id != (long)MovementKind.Shipment)
 0121                throw new ArgumentException($"Оплата возможна только для Отрузки");
 122
 0123            if (movement.MovementStatus.Id != (long)MovementsStatus.PaymentAwaiting)
 0124                throw new ArgumentException($"Оплата возможна только в статусе Ожидание оплаты");
 125
 0126            string buyerWalletId = movement.Customer.WalletId;
 0127            string sellerWaletId = movement.Supplier.WalletId;
 128
 129            //Проверим что у всех участников прописаны кошельки в системе
 0130            var contragent = movement.Customer.WalletId == default ? movement.Customer : movement.Supplier.WalletId == d
 0131            if (contragent != null)
 0132                throw new ArgumentException($"Не прописан кошелек для контрагента #{contragent.Id}");
 133
 134            //Поищем транзакцию по этой заявке в статусе ожидание проверки смс, холдирование средств и подтверждена
 0135            long[] statuses = { (long)TransactionStatusKind.SmsWaited, (long)TransactionStatusKind.Holded, (long)Transac
 0136            foreach (var status in statuses)
 0137            {
 0138                var transaction = await _walletService.GetTransaction(movement.Id, status);
 0139                if (transaction != null)
 0140                {
 0141                    switch (transaction.Status.Id)
 142                    {
 143                        case (long)TransactionStatusKind.SmsWaited:
 0144                            {
 0145                                DateTime time = transaction.ModificationDateTime ?? transaction.CreationDateTime;
 146                                // если при добавлении 119 секунд получилось большая дата, то время жизни транзакции еще
 0147                                if (time != DateTime.MinValue && time.AddSeconds(119) > DateTime.UtcNow)
 0148                                {
 0149                                    return;
 150                                }
 151                                // код умер, да здравствует вновь запрошенный код
 0152                                await RetrySms(movementId);
 0153                                return;
 154                            }
 155                        case (long)TransactionStatusKind.Holded:
 0156                            {
 0157                                throw new ArgumentException($"Денежные средства по документу #{movement.Id} уже зарезерв
 158                            }
 159                        case (long)TransactionStatusKind.Confirmed:
 0160                            {
 0161                                throw new ArgumentException($"Денежные средства по документу #{movement.Id} уже зарезерв
 162                            }
 0163                    };
 0164                }
 0165            }
 166
 0167            var request = new WalletRequest
 0168            {
 0169                Tp = "distributor",
 0170                Model = new WalletRequestModel
 0171                {
 0172                    Buyer = buyerWalletId,
 0173                    Seller = sellerWaletId,
 0174                    Sum = movement.PrepaimentSum,
 0175                    Sum_distr = movement.Items.Sum(d => d.Price * d.Quantity),
 0176                    Orderid = movement.DocumentNumber
 0177                }
 0178            };
 0179            var content = await _walletHttpClient.CreateTransaction(movement.GUID, request);
 0180            if (!content.Status.Equals(vtbOk, StringComparison.OrdinalIgnoreCase))
 0181                throw new ArgumentException(content.Errdesc);
 0182            if (content.Response == null)
 0183                throw new ArgumentException($"Сервис ВТБК не вернул тело транзакции.");
 0184            var trStatus = await _walletService.GetTransactionStatus((long) TransactionStatusKind.SmsWaited);
 0185            await _walletService.CreateWalletTransaction(new WalletTransaction
 0186            {
 0187                Movement = movement,
 0188                Sum = request.Model.Sum,
 0189                TransactionId = content.Response.Id,
 0190                SmsId = content.Response.SmsId,
 0191                Status = trStatus
 0192            });
 0193        }
 194
 195        /// <summary>
 196        /// Совершает проверку введенной смс
 197        /// </summary>
 198        /// <param name="movementId"></param>
 199        /// <param name="code"></param>
 200        /// <returns></returns>
 201        public async Task CheckSms(long movementId, string code)
 0202        {
 0203            var transaction = (await _walletService.GetTransaction(movementId, (long)TransactionStatusKind.SmsWaited))
 0204                              ?? throw new ArgumentException($"Транзакция для #{movementId} в статусе ожидания проверки 
 0205            if (transaction.Movement.Customer.Id != _authUserService.ContragentId && !_authUserService.IsUserPlatform())
 0206                throw new ForbidException();
 0207            DateTime time = transaction.ModificationDateTime ?? transaction.CreationDateTime;
 0208            if (time.AddSeconds(119) <= DateTime.UtcNow)
 0209                throw new SvetaException($"Время ожидания смс истекло. Запросите повторное смс", (int)ErrorCode.PaymentC
 210
 0211            WalletCheckSmsRequest checkRequest = new WalletCheckSmsRequest
 0212            {
 0213                Action = "check_sms",
 0214                Id = transaction.TransactionId,
 0215                Smsid = transaction.SmsId,
 0216                Code = code
 0217            };
 218
 0219            var content = await _walletHttpClient.CheckSms(transaction.GUID, checkRequest);
 220
 0221            if (!content.Status.Equals(vtbOk, StringComparison.OrdinalIgnoreCase))
 0222            {
 0223                if (content.Err.Equals(invalidSms, StringComparison.OrdinalIgnoreCase))
 0224                {
 0225                    throw new SvetaException(content.Errdesc, (int)ErrorCode.PaymentCodeIncorrect);
 226                }
 227                else
 0228                {
 0229                    throw new SvetaException(content.Errdesc, (int)ErrorCode.PaymentOtherError);
 230                }
 231
 232            }
 233
 234            //await _movementWorker.SetNextStatus(transaction.Movement.Id, MovementStatusKeys.payment);
 0235            await UpdateTransactionStatus(transaction, (long)TransactionStatusKind.Holded);
 0236        }
 237
 238        /// <summary>
 239        /// Запрашивает повторное смс
 240        /// </summary>
 241        /// <param name="movementId"></param>
 242        /// <returns></returns>
 243        public async Task RetrySms(long movementId)
 0244        {
 0245            var transaction = (await _walletService.GetTransaction(movementId, (long)TransactionStatusKind.SmsWaited))
 0246                              ?? throw new ArgumentException($"Транзакция для #{movementId} в статусе ожидания проверки 
 0247            if (transaction.Movement.Customer.Id != _authUserService.ContragentId && !_authUserService.IsUserPlatform())
 0248                throw new ForbidException();
 0249            WalletConfirmReqDTO request = new WalletConfirmReqDTO
 0250            {
 0251                Action = "retry_sms",
 0252                Id = transaction.TransactionId
 0253            };
 0254            var content = await _walletHttpClient.RetrySms(transaction.GUID, request);
 0255            if (!content.Status.Equals(vtbOk, StringComparison.OrdinalIgnoreCase))
 0256                throw new ArgumentException(content.Errdesc);
 257
 0258            transaction.SmsId = content.Response.Rp.Smsid;
 0259            await _walletService.Update(transaction);
 0260        }
 261
 262        /// <summary>
 263        /// Подтверждает транзакцию
 264        /// </summary>
 265        /// <param name="movementId"></param>
 266        /// <returns></returns>
 267        public async Task ConfirmTransaction(long movementId)
 0268        {
 0269            var transaction = (await _walletService.GetTransaction(movementId, (long)TransactionStatusKind.Holded))
 0270                              ?? throw new ArgumentException($"Транзакция для документа #{movementId} со статусом Заморо
 271
 0272            WalletConfirmReqDTO request = new WalletConfirmReqDTO
 0273            {
 0274                Action = "confirm",
 0275                Id = transaction.TransactionId
 0276            };
 277
 0278            var content = await _walletHttpClient.ConfirmTransaction(transaction.GUID, request);
 0279            if (!content.Status.Equals(vtbOk, StringComparison.OrdinalIgnoreCase))
 0280                throw new ArgumentException(content.Errdesc);
 281
 0282            await UpdateTransactionStatus(transaction, (long)TransactionStatusKind.Confirmed);
 0283        }
 284
 285        /// <summary>
 286        /// Отменяет транзакцию
 287        /// </summary>
 288        /// <param name="movementId"></param>
 289        /// <returns></returns>
 290        public async Task CancelTransaction(long movementId)
 0291        {
 0292            var transaction = (await _walletService.GetTransaction(movementId, (long)TransactionStatusKind.Holded))
 0293                              ?? throw new ArgumentException($"Транзакция для документа #{movementId} со статусом Заморо
 0294            WalletConfirmReqDTO request = new WalletConfirmReqDTO
 0295            {
 0296                Action = "cancel",
 0297                Id = transaction.TransactionId
 0298            };
 299
 0300            var content = await _walletHttpClient.CancelTransaction(transaction.GUID, request);
 0301            if (!content.Status.Equals(vtbOk, StringComparison.OrdinalIgnoreCase))
 0302                throw new ArgumentException(content.Errdesc);
 303
 0304            await UpdateTransactionStatus(transaction, (long)TransactionStatusKind.Canceled);
 0305        }
 306
 307        public async Task<WalletTransaction> GetTransaction(long movementId, TransactionStatusKind transactionStatus) =>
 29308            await _walletService.GetTransaction(movementId, (long)transactionStatus);
 309        /// <summary>
 310        /// Изменяет сумму сделки по документу
 311        /// </summary>
 312        /// <param name="movementId"></param>
 313        /// <returns></returns>
 314        public async Task ChangeDealTransaction(long movementId)
 0315        {
 0316            Movement movement = await _movementService.GetMovementForItems(movementId) ??
 0317                                throw new ArgumentException($"Не найден документ #{movementId}");
 0318            var transaction = (await _walletService.GetTransaction(movementId, (long)TransactionStatusKind.Holded))
 0319                              ?? throw new ArgumentException($"Транзакция для документа #{movementId} со статусом Заморо
 320
 0321            WalletChangeDealRequestDto request = new WalletChangeDealRequestDto
 0322            {
 0323                Id = transaction.TransactionId,
 0324                Sum = movement.PrepaimentSum,
 0325                Sum_distr = movement.Items.Sum(d => d.Good.Prices.Actual(movement.Sender.Id).PriceNew * d.Quantity)
 0326            };
 0327            var checkDealResult = await _walletHttpClient.CheckChangeDeal(transaction.GUID, request);
 0328            if (!checkDealResult.Status.Equals(vtbOk, StringComparison.OrdinalIgnoreCase))
 0329                throw new ArgumentException(checkDealResult.Errdesc);
 0330            var content = await _walletHttpClient.ChangeDeal(transaction.GUID, request);
 0331            if (!content.Status.Equals(vtbOk, StringComparison.OrdinalIgnoreCase))
 0332                throw new ArgumentException(content.Errdesc);
 0333            transaction.Sum = movement.PrepaimentSum;
 0334            await UpdateTransactionStatus(transaction, (long)TransactionStatusKind.Holded);
 0335        }
 336
 337        public async Task CheckChangeDealTransaction(long movementId)
 0338        {
 0339            Movement movement = await _movementService.GetMovement(movementId) ??
 0340                                throw new ArgumentException($"Не найден документ #{movementId}");
 0341            var transaction = (await _walletService.GetTransaction(movement.Id, (long)TransactionStatusKind.Holded))
 0342                              ?? throw new ArgumentException($"Транзакция для документа #{movement.Id} со статусом Замор
 343
 0344            WalletChangeDealRequestDto request = new WalletChangeDealRequestDto
 0345            {
 0346                Id = transaction.TransactionId,
 0347                Sum = movement.PrepaimentSum,
 0348                Sum_distr = movement.Items.Sum(d => d.Good.Prices.Actual(movement.Sender.Id).PriceNew * d.Quantity)
 0349            };
 0350            var content = await _walletHttpClient.CheckChangeDeal(transaction.GUID, request);
 0351            if (!content.Status.Equals(vtbOk, StringComparison.OrdinalIgnoreCase))
 0352                throw new ArgumentException(content.Errdesc);
 0353        }
 354
 355        /// <summary>
 356        /// Возвращает информацию о транзакции
 357        /// </summary>
 358        /// <param name="transactionId"></param>
 359        /// <returns></returns>
 360        public async Task<WalletTransactionHistory> GetTransactionInfo(string transactionId)
 0361        {
 0362         var content = await _walletHttpClient.GetTransactionInfo(transactionId);
 0363         if (!content.Status.Equals(vtbOk, StringComparison.OrdinalIgnoreCase))
 0364             throw new ArgumentException(content.Errdesc);
 365
 0366         return content.Response;
 0367        }
 368
 369        /// <summary>
 370        ///История транзакций за период по контрагенту
 371        /// </summary>
 372        /// <remarks>author: aabelentsov</remarks>
 373        /// <param name="typeRecord">тип записей - hold/record</param>
 374        /// <param name="active">1 - активный холд (если ничего не указано - выводится все) </param>
 375        /// <param name="status">статус транзакции (если ничего не указано - выводится все)</param>
 376        /// <param name="dateFrom">начальное время периода проводок</param>
 377        /// <param name="dateTo">конечное время периода проводок</param>
 378        /// <returns></returns>
 379        public async Task<List<WalletHistoryResponse>> GetAccountHistory(string typeRecord, int active, int status, Date
 0380        {
 0381            var contragent = await _contragentService.GetContragent(_authUserService.ContragentId) ??
 0382                             throw new ArgumentException($"Не найден контрагент #{_authUserService.ContragentId}");
 0383            dateFrom = dateFrom == DateTime.MinValue ? DateTime.UtcNow.AddDays(-1) : dateFrom;
 0384            dateTo = dateTo == DateTime.MinValue ? DateTime.UtcNow.AddDays(1) : dateTo;
 385
 0386            string onlyType = typeRecord?.ToUpper() switch
 0387            {
 0388                "HOLD" => "hold",
 0389                "RECORD" => "record",
 0390                _ => null
 0391            };
 0392            var request = new WalletHistoryRequest
 0393            {
 0394                Periodbeg = dateFrom,
 0395                Periodend = dateTo,
 0396                Acc = contragent.WalletId,
 0397                Only = onlyType,
 0398                Active_hold = active == 1 ? active : (int?)null,
 0399                Status = status == 0 ? (int?)null : status,
 0400                Tp = null
 0401            };
 0402            var content = await _walletHttpClient.GetTransactionHistory(request);
 0403            if (!content.Status.Equals(vtbOk, StringComparison.OrdinalIgnoreCase))
 0404                throw new ArgumentException(content.Errdesc);
 0405            return content.Response ?? new List<WalletHistoryResponse>() { };
 0406        }
 407
 408
 409        public async Task<bool> CheckNeedFallbackMoney(long movementId)
 410        {
 411            var transactionList = await _walletService.GetTransactions(movementId);
 412            if (transactionList.TotalCount == 0) return false;
 413            var transactions = transactionList.Result;
 0414            return transactions.Any(d => d.Status.Id == (long) TransactionStatusKind.Holded);
 415        }
 416
 417        private async Task UpdateTransactionStatus(WalletTransaction transaction, long statusId)
 0418        {
 0419            var status = await _walletService.GetTransactionStatus(statusId);
 0420            transaction.Status = status;
 0421            await _walletService.Update(transaction);
 0422        }
 423    }
 424
 425    public interface IWalletPaymentService
 426    {
 427        Task CreateTransaction(long movementId);
 428        Task<WalletBalanceResponse> GetWalletInfo();
 429        Task<WalletBalanceResponse> GetWalletInfo(long contragentId);
 430        Task<WalletBalanceFreeResponse> GetWalletFreeBalance(long contragentId = 0, decimal sum = 0);
 431        Task CheckSms(long movementId, string code);
 432        Task RetrySms(long movementId);
 433        Task ConfirmTransaction(long movementId);
 434        Task CancelTransaction(long movementId);
 435        Task<WalletTransactionHistory> GetTransactionInfo(string transactionId);
 436        Task<List<WalletHistoryResponse>> GetAccountHistory(string typeRecord, int active,
 437            int status, DateTime dateFrom, DateTime dateTo);
 438        Task ChangeDealTransaction(long movementId);
 439        Task<bool> CheckNeedFallbackMoney(long movementId);
 440        Task<WalletTransaction> GetTransaction(long movementId, TransactionStatusKind transactionStatus);
 441        Task CheckChangeDealTransaction(long movementId);
 442    }
 443}