using System; using System.Threading; using System.Threading.Tasks; using BTCPayServer.Models; using BTCPayServer.Payments.Changelly; using BTCPayServer.Rating; using BTCPayServer.Services.Rates; using Microsoft.AspNetCore.Mvc; using BTCPayServer.Data; namespace BTCPayServer.Controllers { [Route("[controller]/{storeId}")] public class ChangellyController : Controller { private readonly ChangellyClientProvider _changellyClientProvider; private readonly BTCPayNetworkProvider _btcPayNetworkProvider; private readonly RateFetcher _RateProviderFactory; public ChangellyController(ChangellyClientProvider changellyClientProvider, BTCPayNetworkProvider btcPayNetworkProvider, RateFetcher rateProviderFactory) { _RateProviderFactory = rateProviderFactory ?? throw new ArgumentNullException(nameof(rateProviderFactory)); _changellyClientProvider = changellyClientProvider; _btcPayNetworkProvider = btcPayNetworkProvider; } [HttpGet] [Route("currencies")] public async Task GetCurrencyList(string storeId) { try { var client = await TryGetChangellyClient(storeId); return Ok(await client.GetCurrenciesFull()); } catch (Exception e) { return BadRequest(new BitpayErrorModel() { Error = e.Message }); } } [HttpGet] [Route("calculate")] public async Task CalculateAmount(string storeId, string fromCurrency, string toCurrency, decimal toCurrencyAmount, CancellationToken cancellationToken) { try { var client = await TryGetChangellyClient(storeId); if (fromCurrency.Equals("usd", StringComparison.InvariantCultureIgnoreCase) || fromCurrency.Equals("eur", StringComparison.InvariantCultureIgnoreCase)) { return await HandleCalculateFiatAmount(fromCurrency, toCurrency, toCurrencyAmount, cancellationToken); } var callCounter = 0; var baseRate = await client.GetExchangeAmount(fromCurrency, toCurrency, 1); var currentAmount = ChangellyCalculationHelper.ComputeBaseAmount(baseRate, toCurrencyAmount); while (true) { if (callCounter > 10) { BadRequest(); } var computedAmount = await client.GetExchangeAmount(fromCurrency, toCurrency, currentAmount); callCounter++; if (computedAmount < toCurrencyAmount) { currentAmount = ChangellyCalculationHelper.ComputeCorrectAmount(currentAmount, computedAmount, toCurrencyAmount); } else { return Ok(currentAmount); } } } catch (Exception e) { return BadRequest(new BitpayErrorModel() { Error = e.Message }); } } private async Task TryGetChangellyClient(string storeId) { var store = IsTest? null: HttpContext.GetStoreData(); storeId = storeId ?? store?.Id; return await _changellyClientProvider.TryGetChangellyClient(storeId, store); } private async Task HandleCalculateFiatAmount(string fromCurrency, string toCurrency, decimal toCurrencyAmount, CancellationToken cancellationToken) { var store = HttpContext.GetStoreData(); var rules = store.GetStoreBlob().GetRateRules(_btcPayNetworkProvider); var rate = await _RateProviderFactory.FetchRate(new CurrencyPair(toCurrency, fromCurrency), rules, cancellationToken); if (rate.BidAsk == null) return BadRequest(); var flatRate = rate.BidAsk.Center; return Ok(flatRate * toCurrencyAmount); } public bool IsTest { get; set; } = false; } }