btcpayserver/BTCPayServer/Controllers/BitpayRateController.cs

188 lines
8.1 KiB
C#
Raw Normal View History

2020-06-29 04:44:35 +02:00
using System;
2017-09-13 08:47:34 +02:00
using System.Collections.Generic;
2021-12-27 05:46:31 +01:00
using System.Globalization;
2017-09-13 08:47:34 +02:00
using System.Linq;
2020-06-28 10:55:27 +02:00
using System.Text;
using System.Threading;
2017-09-13 08:47:34 +02:00
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
2020-06-28 10:55:27 +02:00
using BTCPayServer.Data;
2017-09-13 08:47:34 +02:00
using BTCPayServer.Filters;
2020-06-28 10:55:27 +02:00
using BTCPayServer.Models;
using BTCPayServer.Payments;
2020-06-28 10:55:27 +02:00
using BTCPayServer.Rating;
using BTCPayServer.Security;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Rates;
using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
2020-06-28 10:55:27 +02:00
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
2017-09-13 08:47:34 +02:00
namespace BTCPayServer.Controllers
{
[EnableCors(CorsPolicies.All)]
[Authorize(Policy = ServerPolicies.CanGetRates.Key, AuthenticationSchemes = AuthenticationSchemes.Bitpay)]
2022-01-07 04:08:28 +01:00
public class BitpayRateController : Controller
{
readonly RateFetcher _rateProviderFactory;
readonly CurrencyNameTable _currencyNameTable;
private readonly DefaultRulesCollection _defaultRules;
private readonly PaymentMethodHandlerDictionary _handlers;
readonly StoreRepository _storeRepo;
private readonly InvoiceRepository _invoiceRepository;
private StoreData CurrentStore => HttpContext.GetStoreData();
2022-01-07 04:08:28 +01:00
public BitpayRateController(
RateFetcher rateProviderFactory,
StoreRepository storeRepo,
InvoiceRepository invoiceRepository,
CurrencyNameTable currencyNameTable,
DefaultRulesCollection defaultRules,
PaymentMethodHandlerDictionary handlers)
{
_rateProviderFactory = rateProviderFactory ?? throw new ArgumentNullException(nameof(rateProviderFactory));
_storeRepo = storeRepo;
_invoiceRepository = invoiceRepository;
_currencyNameTable = currencyNameTable ?? throw new ArgumentNullException(nameof(currencyNameTable));
_defaultRules = defaultRules;
_handlers = handlers;
}
2017-09-13 08:47:34 +02:00
2018-05-21 16:49:37 +02:00
[Route("rates/{baseCurrency}")]
[HttpGet]
[BitpayAPIConstraint]
public async Task<IActionResult> GetBaseCurrencyRates(string baseCurrency, string cryptoCode = null, CancellationToken cancellationToken = default)
2018-05-21 16:49:37 +02:00
{
var inv = _invoiceRepository.CreateNewInvoice(CurrentStore.Id);
inv.Currency = baseCurrency;
var ctx = new InvoiceCreationContext(CurrentStore, CurrentStore.GetStoreBlob(), inv, new Logging.InvoiceLogs(), _handlers, null);
ctx.SetLazyActivation(true);
await ctx.BeforeFetchingRates();
var currencyCodes = ctx
.PaymentMethodContexts
.SelectMany(c => c.Value.RequiredRates)
.Where(c => c.Left.Equals(baseCurrency, StringComparison.OrdinalIgnoreCase))
.Select(c => c.Right)
.ToHashSet();
2018-07-30 15:51:39 +02:00
var currencypairs = BuildCurrencyPairs(currencyCodes, baseCurrency);
2019-10-12 13:35:30 +02:00
var result = await GetRates2(currencypairs, null, cryptoCode, cancellationToken);
2018-05-21 16:49:37 +02:00
var rates = (result as JsonResult)?.Value as Rate[];
return rates == null ? result : Json(new DataWrapper<Rate[]>(rates));
2018-05-21 16:49:37 +02:00
}
[HttpGet("rates/{baseCurrency}/{currency}")]
[BitpayAPIConstraint]
public async Task<IActionResult> GetCurrencyPairRate(string baseCurrency, string currency, string cryptoCode = null, CancellationToken cancellationToken = default)
2018-05-28 10:20:18 +02:00
{
var result = await GetRates2($"{baseCurrency}_{currency}", null, cryptoCode, cancellationToken);
return (result as JsonResult)?.Value is not Rate[] rates
? result
: Json(new DataWrapper<Rate>(rates.First()));
2018-05-28 10:20:18 +02:00
}
[HttpGet("rates")]
[BitpayAPIConstraint]
public async Task<IActionResult> GetRates(string currencyPairs, string storeId = null, string cryptoCode = null, CancellationToken cancellationToken = default)
2018-01-20 04:11:24 +01:00
{
var result = await GetRates2(currencyPairs, storeId, cryptoCode, cancellationToken);
return (result as JsonResult)?.Value is not Rate[] rates
? result
: Json(new DataWrapper<Rate[]>(rates));
2018-01-20 04:11:24 +01:00
}
2019-10-12 13:35:30 +02:00
[AllowAnonymous]
[HttpGet("api/rates")]
public async Task<IActionResult> GetRates2(string currencyPairs, string storeId, string cryptoCode = null, CancellationToken cancellationToken = default)
{
var store = CurrentStore ?? await _storeRepo.FindStore(storeId);
2018-05-02 20:32:42 +02:00
if (store == null)
{
var err = Json(new BitpayErrorsModel { Error = "Store not found" });
2019-10-12 13:35:30 +02:00
err.StatusCode = 404;
return err;
}
2018-05-28 14:55:49 +02:00
if (currencyPairs == null)
{
var blob = store.GetStoreBlob();
currencyPairs = blob.GetDefaultCurrencyPairString();
if (string.IsNullOrEmpty(currencyPairs) && !string.IsNullOrWhiteSpace(cryptoCode))
{
currencyPairs = $"{blob.DefaultCurrency}_{cryptoCode}".ToUpperInvariant();
}
2018-05-28 14:55:49 +02:00
if (string.IsNullOrEmpty(currencyPairs))
{
var result = Json(new BitpayErrorsModel() { Error = "You need to setup the default currency pairs in 'Store Settings / Rates' or specify 'currencyPairs' query parameter (eg. BTC_USD,LTC_CAD)." });
2018-05-28 14:55:49 +02:00
result.StatusCode = 400;
return result;
}
}
var rules = store.GetStoreBlob().GetRateRules(_defaultRules);
var pairs = new HashSet<CurrencyPair>();
2018-05-28 14:55:49 +02:00
foreach (var currency in currencyPairs.Split(','))
2018-05-02 20:32:42 +02:00
{
2018-05-28 14:55:49 +02:00
if (!CurrencyPair.TryParse(currency, out var pair))
2018-05-02 20:32:42 +02:00
{
var result = Json(new BitpayErrorsModel() { Error = $"Currency pair {currency} uncorrectly formatted" });
result.StatusCode = 400;
return result;
}
pairs.Add(pair);
}
2018-04-15 14:18:51 +02:00
var fetching = _rateProviderFactory.FetchRates(pairs, rules, new StoreIdRateContext(storeId), cancellationToken);
2018-05-02 20:32:42 +02:00
await Task.WhenAll(fetching.Select(f => f.Value).ToArray());
return Json(pairs
.Select(r => (Pair: r, Value: fetching[r].GetAwaiter().GetResult().BidAsk?.Bid))
2018-05-02 20:32:42 +02:00
.Where(r => r.Value.HasValue)
.Select(r =>
new Rate
{
2018-05-02 20:32:42 +02:00
CryptoCode = r.Pair.Left,
Code = r.Pair.Right,
2018-05-06 15:41:38 +02:00
CurrencyPair = r.Pair.ToString(),
Name = _currencyNameTable.GetCurrencyData(r.Pair.Right, true).Name,
2018-05-02 20:32:42 +02:00
Value = r.Value.Value
2018-01-20 04:11:24 +01:00
}).Where(n => n.Name != null).ToArray());
}
2018-05-02 20:32:42 +02:00
2018-07-30 15:51:39 +02:00
private static string BuildCurrencyPairs(IEnumerable<string> currencyCodes, string baseCrypto)
{
var currencyPairsBuilder = new StringBuilder();
2018-07-30 15:51:39 +02:00
bool first = true;
foreach (var currencyCode in currencyCodes)
{
2019-10-12 13:35:30 +02:00
if (!first)
2021-10-06 05:53:41 +02:00
currencyPairsBuilder.Append(',');
2018-07-30 16:07:29 +02:00
first = false;
2021-12-27 05:46:31 +01:00
currencyPairsBuilder.Append(CultureInfo.InvariantCulture, $"{baseCrypto}_{currencyCode}");
2018-07-30 15:51:39 +02:00
}
return currencyPairsBuilder.ToString();
}
2018-05-02 20:32:42 +02:00
public class Rate
{
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
2018-05-02 20:32:42 +02:00
[JsonProperty(PropertyName = "cryptoCode")]
public string CryptoCode { get; set; }
2018-05-06 15:41:38 +02:00
[JsonProperty(PropertyName = "currencyPair")]
public string CurrencyPair { get; set; }
2018-05-06 15:41:38 +02:00
2018-05-02 20:32:42 +02:00
[JsonProperty(PropertyName = "code")]
public string Code { get; set; }
2018-05-02 20:32:42 +02:00
[JsonProperty(PropertyName = "rate")]
public decimal Value { get; set; }
2018-05-02 20:32:42 +02:00
}
}
2017-09-13 08:47:34 +02:00
}