Esempio n. 1
0
        public async Task <IActionResult> Execute([FromBody] CashoutModel request)
        {
            try
            {
                var clientRequest = Mapper.Map <CashoutRequest>(request, opt =>
                {
                    opt.Items["MerchantId"]    = this.GetUserMerchantId();
                    opt.Items["EmployeeEmail"] = this.GetUserEmail();
                });

                CashoutResponse response = await _payInternalClient.CashoutAsync(clientRequest);

                return(Ok(Mapper.Map <CashoutResponseModel>(response)));
            }
            catch (DefaultErrorResponseException e) when(e.StatusCode == HttpStatusCode.BadRequest)
            {
                var apiException = e.InnerException as ApiException;

                if (apiException?.StatusCode == HttpStatusCode.BadRequest)
                {
                    return(BadRequest(apiException.GetContentAs <ErrorResponse>()));
                }

                _log.Error(e, null, $"request:{request.ToJson()}");

                return(BadRequest(ErrorResponse.Create(e.Message)));
            }
        }
        public async Task <OffchainClosingResponse> Cashout(CashoutData data)
        {
            var request = new CashoutModel(data.ClientPubKey, data.CashoutAddress, data.HotWalletAddress, data.AssetId, data.Amount);

            var response = await _apiClient.ApiOffchainCashoutPostAsync(request);

            return(PrepareOffchainClosingResult(response));
        }
 public static HistoryModel ToHistoryModel(this CashoutModel cashout)
 {
     return(new HistoryModel
     {
         Id = cashout.Id.ToString(),
         DateTime = cashout.Timestamp,
         Type = HistoryType.CashOut,
         Asset = cashout.AssetId,
         Amount = cashout.Volume,
         State = cashout.State,
         FeeSize = cashout.FeeSize.GetValueOrDefault(0),
         FeeAssetId = cashout.AssetId
     });
 }
        public async Task <IActionResult> Cashout([FromBody] CashoutModel model)
        {
            if (!ModelState.IsValid)
            {
                throw new ClientSideException(ExceptionType.WrongParams, JsonConvert.SerializeObject(ModelState.Errors()));
            }

            await Log("Cashout", $"Begin Process {this.GetIp()}", model);

            var amount      = BigInteger.Parse(model.Amount);
            var operationId = await _pendingOperationService.CashOut(model.Id, model.CoinAdapterAddress,
                                                                     _addressUtil.ConvertToChecksumAddress(model.FromAddress), _addressUtil.ConvertToChecksumAddress(model.ToAddress), amount, model.Sign);

            await Log("Cashout", "End Process", model, operationId);

            return(Ok(new OperationIdResponse {
                OperationId = operationId
            }));
        }
 /// <param name='operations'>
 /// The operations group for this extension method.
 /// </param>
 /// <param name='model'>
 /// </param>
 /// <param name='cancellationToken'>
 /// The cancellation token.
 /// </param>
 public static async Task <object> ApiExchangeCashoutPostAsync(this IEthereumApi operations, CashoutModel model = default(CashoutModel), CancellationToken cancellationToken = default(CancellationToken))
 {
     using (var _result = await operations.ApiExchangeCashoutPostWithHttpMessagesAsync(model, null, cancellationToken).ConfigureAwait(false))
     {
         return(_result.Body);
     }
 }
 /// <param name='operations'>
 /// The operations group for this extension method.
 /// </param>
 /// <param name='model'>
 /// </param>
 public static object ApiExchangeCashoutPost(this IEthereumApi operations, CashoutModel model = default(CashoutModel))
 {
     return(operations.ApiExchangeCashoutPostAsync(model).GetAwaiter().GetResult());
 }
 /// <param name='operations'>
 /// The operations group for this extension method.
 /// </param>
 /// <param name='model'>
 /// </param>
 /// <param name='cancellationToken'>
 /// The cancellation token.
 /// </param>
 public static async System.Threading.Tasks.Task <object> ApiOffchainCashoutPostAsync(this IBitcoinApi operations, CashoutModel model = default(CashoutModel), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))
 {
     using (var _result = await operations.ApiOffchainCashoutPostWithHttpMessagesAsync(model, null, cancellationToken).ConfigureAwait(false))
     {
         return(_result.Body);
     }
 }
 /// <param name='operations'>
 /// The operations group for this extension method.
 /// </param>
 /// <param name='model'>
 /// </param>
 public static object ApiOffchainCashoutPost(this IBitcoinApi operations, CashoutModel model = default(CashoutModel))
 {
     return(System.Threading.Tasks.Task.Factory.StartNew(s => ((IBitcoinApi)s).ApiOffchainCashoutPostAsync(model), operations, System.Threading.CancellationToken.None, System.Threading.Tasks.TaskCreationOptions.None, System.Threading.Tasks.TaskScheduler.Default).Unwrap().GetAwaiter().GetResult());
 }
        public async Task <CashoutOffchainApiResponse> CreateCashout([FromBody] CashoutModel model)
        {
            var asset = await GetAsset(model.Asset);

            return(new CashoutOffchainApiResponse(await _offchain.CreateCashout(model.ClientPubKey, model.CashoutAddress, model.Amount, asset)));
        }
        public async Task <IActionResult> Execute([FromBody] CashoutModel request)
        {
            try
            {
                CashoutResult cashoutResult = await _cashoutService.ExecuteAsync(Mapper.Map <CashoutCommand>(request));

                return(Ok(Mapper.Map <CashoutResponse>(cashoutResult)));
            }
            catch (AssetNetworkNotDefinedException e)
            {
                _log.ErrorWithDetails(e, new { e.AssetId });

                return(BadRequest(ErrorResponse.Create(e.Message)));
            }
            catch (InvalidOperationException e)
            {
                _log.ErrorWithDetails(e, request);

                return(BadRequest(ErrorResponse.Create(e.Message)));
            }
            catch (InsufficientFundsException e)
            {
                _log.ErrorWithDetails(e, new
                {
                    e.WalletAddress,
                    e.AssetId
                });

                return(BadRequest(ErrorResponse.Create(e.Message)));
            }
            catch (CashoutOperationFailedException e)
            {
                _log.ErrorWithDetails(e, new { errors = e.TransferErrors });

                return(BadRequest(ErrorResponse.Create(e.Message)));
            }
            catch (MultipleDefaultMerchantWalletsException e)
            {
                _log.ErrorWithDetails(e, new
                {
                    e.AssetId,
                    e.MerchantId,
                    e.PaymentDirection
                });

                return(BadRequest(ErrorResponse.Create(e.Message)));
            }
            catch (DefaultMerchantWalletNotFoundException e)
            {
                _log.ErrorWithDetails(e, new
                {
                    e.AssetId,
                    e.MerchantId,
                    e.PaymentDirection
                });

                return(BadRequest(ErrorResponse.Create(e.Message)));
            }
            catch (MerchantWalletOwnershipException e)
            {
                _log.ErrorWithDetails(e, new
                {
                    e.MerchantId,
                    e.WalletAddress
                });

                return(BadRequest(ErrorResponse.Create(e.Message)));
            }
            catch (CashoutHotwalletNotDefinedException e)
            {
                _log.ErrorWithDetails(e, new { e.Blockchain });

                return(BadRequest(ErrorResponse.Create(e.Message)));
            }
        }
 /// <param name='operations'>
 /// The operations group for this extension method.
 /// </param>
 /// <param name='model'>
 /// </param>
 public static object ApiOffchainCashoutPost(this IBitcoinApi operations, CashoutModel model = default(CashoutModel))
 {
     return(operations.ApiOffchainCashoutPostAsync(model).GetAwaiter().GetResult());
 }
        /// <summary>
        ///
        /// </summary>
        /// <param name="cashoutModel"></param>
        /// <returns>
        /// ValidationError - client error
        /// ArgumentValidationException - developer error
        /// </returns>
        public async Task <IReadOnlyCollection <ValidationError> > ValidateAsync(CashoutModel cashoutModel)
        {
            var errors = new List <ValidationError>(1);

            if (cashoutModel == null)
            {
                return(FieldNotValidResult("cashoutModel can't be null"));
            }

            if (string.IsNullOrEmpty(cashoutModel.AssetId))
            {
                return(FieldNotValidResult("cashoutModel.AssetId can't be null or empty"));
            }

            Asset asset;

            try
            {
                asset = await _assetsService.TryGetAssetAsync(cashoutModel.AssetId);
            }
            catch (Exception)
            {
                throw new ArgumentValidationException($"Asset with Id-{cashoutModel.AssetId} does not exists",
                                                      "assetId");
            }

            if (asset.IsDisabled)
            {
                errors.Add(ValidationError.Create(ValidationErrorType.None, $"Asset {asset.Id} is disabled"));
            }

            if (asset == null)
            {
                throw new ArgumentValidationException($"Asset with Id-{cashoutModel.AssetId} does not exists",
                                                      "assetId");
            }

            var isAddressValid = true;
            IBlockchainApiClient blockchainClient = null;

            if (asset.Id != LykkeConstants.SolarAssetId)
            {
                if (string.IsNullOrEmpty(asset.BlockchainIntegrationLayerId))
                {
                    throw new ArgumentValidationException(
                              $"Given asset Id-{cashoutModel.AssetId} is not a part of Blockchain Integration Layer",
                              "assetId");
                }

                blockchainClient = _blockchainApiClientProvider.Get(asset.BlockchainIntegrationLayerId);
            }

            if (string.IsNullOrEmpty(cashoutModel.DestinationAddress) ||
                !cashoutModel.DestinationAddress.IsValidPartitionOrRowKey() ||
                asset.Id != LykkeConstants.SolarAssetId && blockchainClient != null &&
                !await blockchainClient.IsAddressValidAsync(cashoutModel.DestinationAddress) ||
                asset.Id == LykkeConstants.SolarAssetId &&
                !SolarCoinValidation.ValidateAddress(cashoutModel.DestinationAddress)
                )
            {
                isAddressValid = false;
                errors.Add(ValidationError.Create(ValidationErrorType.AddressIsNotValid, "Address is not valid"));
            }

            if (isAddressValid)
            {
                if (asset.Id != LykkeConstants.SolarAssetId)
                {
                    var isBlocked = await _blackListService.IsBlockedWithoutAddressValidationAsync
                                    (
                        asset.BlockchainIntegrationLayerId,
                        cashoutModel.DestinationAddress
                                    );

                    if (isBlocked)
                    {
                        errors.Add(ValidationError.Create(ValidationErrorType.BlackListedAddress, "Address is in the black list"));
                    }
                }

                if (cashoutModel.Volume.HasValue && Math.Abs(cashoutModel.Volume.Value) < (decimal)asset.CashoutMinimalAmount)
                {
                    var minimalAmount = asset.CashoutMinimalAmount.GetFixedAsString(asset.Accuracy).TrimEnd('0');

                    errors.Add(ValidationError.Create(ValidationErrorType.LessThanMinCashout,
                                                      $"Please enter an amount greater than {minimalAmount}"));
                }

                if (asset.Id != LykkeConstants.SolarAssetId)
                {
                    var blockchainSettings = _blockchainSettingsProvider.Get(asset.BlockchainIntegrationLayerId);

                    if (cashoutModel.DestinationAddress == blockchainSettings.HotWalletAddress)
                    {
                        errors.Add(ValidationError.Create(ValidationErrorType.HotwalletTargetProhibited,
                                                          "Hot wallet as destitnation address prohibited"));
                    }

                    var isPublicExtensionRequired =
                        _blockchainWalletsCacheService.IsPublicExtensionRequired(asset.BlockchainIntegrationLayerId);
                    if (isPublicExtensionRequired)
                    {
                        var hotWalletParseResult = await _blockchainWalletsClient.ParseAddressAsync(
                            asset.BlockchainIntegrationLayerId,
                            blockchainSettings.HotWalletAddress);

                        var destAddressParseResult = await _blockchainWalletsClient.ParseAddressAsync(
                            asset.BlockchainIntegrationLayerId,
                            cashoutModel.DestinationAddress);

                        if (hotWalletParseResult.BaseAddress == destAddressParseResult.BaseAddress)
                        {
                            var existedClientIdAsDestination = await _blockchainWalletsClient.TryGetClientIdAsync(
                                asset.BlockchainIntegrationLayerId,
                                cashoutModel.DestinationAddress);

                            if (existedClientIdAsDestination == null)
                            {
                                errors.Add(ValidationError.Create(ValidationErrorType.DepositAddressNotFound,
                                                                  $"Deposit address {cashoutModel.DestinationAddress} not found"));
                            }
                        }

                        var forbiddenCharacterErrors = await ValidateForForbiddenCharsAsync(
                            destAddressParseResult.BaseAddress,
                            destAddressParseResult.AddressExtension,
                            asset.BlockchainIntegrationLayerId);

                        if (forbiddenCharacterErrors != null)
                        {
                            errors.AddRange(forbiddenCharacterErrors);
                        }

                        if (!string.IsNullOrEmpty(destAddressParseResult.BaseAddress))
                        {
                            if (!cashoutModel.DestinationAddress.Contains(destAddressParseResult.BaseAddress))
                            {
                                errors.Add(ValidationError.Create(ValidationErrorType.FieldIsNotValid,
                                                                  "Base Address should be part of destination address"));
                            }

                            // full address is already checked by integration,
                            // we don't need to validate it again,
                            // just ensure that base address is not black-listed
                            var isBlockedBase = await _blackListService.IsBlockedWithoutAddressValidationAsync(
                                asset.BlockchainIntegrationLayerId,
                                destAddressParseResult.BaseAddress);

                            if (isBlockedBase)
                            {
                                errors.Add(ValidationError.Create(ValidationErrorType.BlackListedAddress,
                                                                  "Base Address is in the black list"));
                            }
                        }
                    }
                }

                if (cashoutModel.ClientId.HasValue)
                {
                    var destinationClientId = await _blockchainWalletsClient.TryGetClientIdAsync
                                              (
                        asset.BlockchainIntegrationLayerId,
                        cashoutModel.DestinationAddress
                                              );

                    if (destinationClientId.HasValue && destinationClientId == cashoutModel.ClientId.Value)
                    {
                        var error = ValidationError.Create
                                    (
                            ValidationErrorType.CashoutToSelfAddress,
                            "Withdrawals to the deposit wallet owned by the customer himself prohibited"
                                    );

                        errors.Add(error);
                    }
                }
            }

            return(errors);
        }