2017-09-13 08:47:34 +02:00
|
|
|
|
using BTCPayServer.Models;
|
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using BTCPayServer.Filters;
|
2017-09-15 09:06:57 +02:00
|
|
|
|
using BTCPayServer.Services.Rates;
|
2018-01-19 10:11:43 +01:00
|
|
|
|
using BTCPayServer.Services.Stores;
|
2018-05-02 20:32:42 +02:00
|
|
|
|
using BTCPayServer.Rating;
|
|
|
|
|
using Newtonsoft.Json;
|
2018-10-15 11:11:20 +02:00
|
|
|
|
using Microsoft.AspNetCore.Authorization;
|
|
|
|
|
using BTCPayServer.Authentication;
|
2017-09-13 08:47:34 +02:00
|
|
|
|
|
|
|
|
|
namespace BTCPayServer.Controllers
|
|
|
|
|
{
|
2018-10-15 11:11:20 +02:00
|
|
|
|
[Authorize(AuthenticationSchemes = Security.Policies.BitpayAuthentication)]
|
|
|
|
|
[AllowAnonymous]
|
2017-10-27 10:53:04 +02:00
|
|
|
|
public class RateController : Controller
|
|
|
|
|
{
|
2018-08-22 09:53:40 +02:00
|
|
|
|
RateFetcher _RateProviderFactory;
|
2018-01-19 10:11:43 +01:00
|
|
|
|
BTCPayNetworkProvider _NetworkProvider;
|
2017-10-27 10:53:04 +02:00
|
|
|
|
CurrencyNameTable _CurrencyNameTable;
|
2018-01-19 10:11:43 +01:00
|
|
|
|
StoreRepository _StoreRepo;
|
2018-10-15 11:11:20 +02:00
|
|
|
|
|
|
|
|
|
public TokenRepository TokenRepository { get; }
|
|
|
|
|
|
2018-01-19 10:11:43 +01:00
|
|
|
|
public RateController(
|
2018-08-22 09:53:40 +02:00
|
|
|
|
RateFetcher rateProviderFactory,
|
2018-01-19 10:11:43 +01:00
|
|
|
|
BTCPayNetworkProvider networkProvider,
|
2018-10-15 11:11:20 +02:00
|
|
|
|
TokenRepository tokenRepository,
|
2018-01-19 10:11:43 +01:00
|
|
|
|
StoreRepository storeRepo,
|
|
|
|
|
CurrencyNameTable currencyNameTable)
|
2017-10-27 10:53:04 +02:00
|
|
|
|
{
|
2018-01-19 10:11:43 +01:00
|
|
|
|
_RateProviderFactory = rateProviderFactory ?? throw new ArgumentNullException(nameof(rateProviderFactory));
|
|
|
|
|
_NetworkProvider = networkProvider;
|
2018-10-15 11:11:20 +02:00
|
|
|
|
TokenRepository = tokenRepository;
|
2018-01-19 10:11:43 +01:00
|
|
|
|
_StoreRepo = storeRepo;
|
2017-10-27 10:53:04 +02:00
|
|
|
|
_CurrencyNameTable = currencyNameTable ?? throw new ArgumentNullException(nameof(currencyNameTable));
|
|
|
|
|
}
|
2017-09-13 08:47:34 +02:00
|
|
|
|
|
2018-05-21 16:49:37 +02:00
|
|
|
|
[Route("rates/{baseCurrency}")]
|
|
|
|
|
[HttpGet]
|
2018-07-27 06:38:54 +02:00
|
|
|
|
[BitpayAPIConstraint]
|
2018-05-28 15:30:43 +02:00
|
|
|
|
public async Task<IActionResult> GetBaseCurrencyRates(string baseCurrency, string storeId)
|
2018-05-21 16:49:37 +02:00
|
|
|
|
{
|
2018-10-15 11:11:20 +02:00
|
|
|
|
storeId = await GetStoreId(storeId);
|
2018-05-28 14:55:49 +02:00
|
|
|
|
var store = this.HttpContext.GetStoreData();
|
2018-05-28 15:30:43 +02:00
|
|
|
|
if (store == null || store.Id != storeId)
|
|
|
|
|
store = await _StoreRepo.FindStore(storeId);
|
|
|
|
|
if (store == null)
|
|
|
|
|
{
|
|
|
|
|
var err = Json(new BitpayErrorsModel() { Error = "Store not found" });
|
|
|
|
|
err.StatusCode = 404;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2018-05-21 16:49:37 +02:00
|
|
|
|
var supportedMethods = store.GetSupportedPaymentMethods(_NetworkProvider);
|
|
|
|
|
|
2018-07-27 08:41:36 +02:00
|
|
|
|
var currencyCodes = supportedMethods.Where(method => !string.IsNullOrEmpty(method.PaymentId.CryptoCode))
|
|
|
|
|
.Select(method => method.PaymentId.CryptoCode).Distinct();
|
2018-05-28 14:55:49 +02:00
|
|
|
|
|
2018-07-30 15:51:39 +02:00
|
|
|
|
var currencypairs = BuildCurrencyPairs(currencyCodes, baseCurrency);
|
|
|
|
|
|
2018-05-21 16:49:37 +02:00
|
|
|
|
var result = await GetRates2(currencypairs, store.Id);
|
|
|
|
|
var rates = (result as JsonResult)?.Value as Rate[];
|
|
|
|
|
if (rates == null)
|
|
|
|
|
return result;
|
2018-05-28 15:30:43 +02:00
|
|
|
|
return Json(new DataWrapper<Rate[]>(rates));
|
2018-05-21 16:49:37 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-28 10:20:18 +02:00
|
|
|
|
|
|
|
|
|
[Route("rates/{baseCurrency}/{currency}")]
|
|
|
|
|
[HttpGet]
|
2018-07-27 06:38:54 +02:00
|
|
|
|
[BitpayAPIConstraint]
|
2018-05-28 10:20:18 +02:00
|
|
|
|
public async Task<IActionResult> GetCurrencyPairRate(string baseCurrency, string currency, string storeId)
|
|
|
|
|
{
|
2018-10-15 11:11:20 +02:00
|
|
|
|
storeId = await GetStoreId(storeId);
|
2018-05-28 10:20:18 +02:00
|
|
|
|
var result = await GetRates2($"{baseCurrency}_{currency}", storeId);
|
|
|
|
|
var rates = (result as JsonResult)?.Value as Rate[];
|
|
|
|
|
if (rates == null)
|
|
|
|
|
return result;
|
|
|
|
|
return Json(new DataWrapper<Rate>(rates.First()));
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-27 10:53:04 +02:00
|
|
|
|
[Route("rates")]
|
|
|
|
|
[HttpGet]
|
|
|
|
|
[BitpayAPIConstraint]
|
2018-05-28 14:55:49 +02:00
|
|
|
|
public async Task<IActionResult> GetRates(string currencyPairs, string storeId)
|
2018-01-20 04:11:24 +01:00
|
|
|
|
{
|
2018-10-15 11:11:20 +02:00
|
|
|
|
storeId = await GetStoreId(storeId);
|
2018-05-02 20:32:42 +02:00
|
|
|
|
var result = await GetRates2(currencyPairs, storeId);
|
2018-05-06 15:41:38 +02:00
|
|
|
|
var rates = (result as JsonResult)?.Value as Rate[];
|
2018-05-02 20:32:42 +02:00
|
|
|
|
if (rates == null)
|
2018-01-20 04:11:24 +01:00
|
|
|
|
return result;
|
2018-05-06 15:41:38 +02:00
|
|
|
|
return Json(new DataWrapper<Rate[]>(rates));
|
2018-01-20 04:11:24 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-15 11:11:20 +02:00
|
|
|
|
private async Task<string> GetStoreId(string storeId)
|
|
|
|
|
{
|
|
|
|
|
if (storeId != null && this.HttpContext.GetStoreData()?.Id == storeId)
|
|
|
|
|
return storeId;
|
|
|
|
|
if(storeId == null)
|
|
|
|
|
{
|
|
|
|
|
var tokens = await this.TokenRepository.GetTokens(this.User.GetSIN());
|
|
|
|
|
storeId = tokens.Select(s => s.StoreId).Where(s => s != null).FirstOrDefault();
|
|
|
|
|
}
|
|
|
|
|
if (storeId == null)
|
|
|
|
|
return null;
|
|
|
|
|
var store = await _StoreRepo.FindStore(storeId);
|
|
|
|
|
if (store == null)
|
|
|
|
|
return null;
|
|
|
|
|
this.HttpContext.SetStoreData(store);
|
|
|
|
|
return storeId;
|
|
|
|
|
}
|
2018-05-21 16:49:37 +02:00
|
|
|
|
|
2018-01-20 04:11:24 +01:00
|
|
|
|
[Route("api/rates")]
|
|
|
|
|
[HttpGet]
|
2018-05-02 20:32:42 +02:00
|
|
|
|
public async Task<IActionResult> GetRates2(string currencyPairs, string storeId)
|
2017-10-27 10:53:04 +02:00
|
|
|
|
{
|
2018-10-15 11:11:20 +02:00
|
|
|
|
storeId = await GetStoreId(storeId);
|
2018-05-28 14:55:49 +02:00
|
|
|
|
if (storeId == null)
|
2018-05-02 20:32:42 +02:00
|
|
|
|
{
|
2018-05-29 17:12:07 +02:00
|
|
|
|
var result = Json(new BitpayErrorsModel() { Error = "You need to specify storeId (in your store settings)" });
|
2018-05-02 20:32:42 +02:00
|
|
|
|
result.StatusCode = 400;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2018-05-06 15:41:38 +02:00
|
|
|
|
var store = this.HttpContext.GetStoreData();
|
2018-05-28 14:55:49 +02:00
|
|
|
|
if (store == null || store.Id != storeId)
|
2018-05-06 15:41:38 +02:00
|
|
|
|
store = await _StoreRepo.FindStore(storeId);
|
2018-05-02 20:32:42 +02:00
|
|
|
|
if (store == null)
|
2018-01-19 10:11:43 +01:00
|
|
|
|
{
|
2018-05-02 20:32:42 +02:00
|
|
|
|
var result = Json(new BitpayErrorsModel() { Error = "Store not found" });
|
|
|
|
|
result.StatusCode = 404;
|
|
|
|
|
return result;
|
2018-01-19 10:11:43 +01:00
|
|
|
|
}
|
2018-05-28 14:55:49 +02:00
|
|
|
|
|
|
|
|
|
if (currencyPairs == null)
|
|
|
|
|
{
|
|
|
|
|
var supportedMethods = store.GetSupportedPaymentMethods(_NetworkProvider);
|
2018-07-30 15:45:28 +02:00
|
|
|
|
var currencyCodes = supportedMethods.Select(method => method.PaymentId.CryptoCode).Distinct();
|
2018-07-27 07:54:55 +02:00
|
|
|
|
var defaultCrypto = store.GetDefaultCrypto(_NetworkProvider);
|
2018-05-28 14:55:49 +02:00
|
|
|
|
|
2018-07-30 15:51:39 +02:00
|
|
|
|
currencyPairs = BuildCurrencyPairs(currencyCodes, defaultCrypto);
|
2018-07-27 07:54:55 +02:00
|
|
|
|
|
2018-05-28 14:55:49 +02:00
|
|
|
|
if (string.IsNullOrEmpty(currencyPairs))
|
|
|
|
|
{
|
2018-05-29 17:12:07 +02:00
|
|
|
|
var result = Json(new BitpayErrorsModel() { Error = "You need to specify currencyPairs (eg. BTC_USD,LTC_CAD)" });
|
2018-05-28 14:55:49 +02:00
|
|
|
|
result.StatusCode = 400;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-05-02 20:32:42 +02:00
|
|
|
|
var rules = store.GetStoreBlob().GetRateRules(_NetworkProvider);
|
2018-01-19 10:11:43 +01:00
|
|
|
|
|
2018-05-02 20:32:42 +02:00
|
|
|
|
HashSet<CurrencyPair> 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
|
|
|
|
|
2018-05-02 20:32:42 +02:00
|
|
|
|
var fetching = _RateProviderFactory.FetchRates(pairs, rules);
|
|
|
|
|
await Task.WhenAll(fetching.Select(f => f.Value).ToArray());
|
|
|
|
|
return Json(pairs
|
2018-07-27 11:04:41 +02:00
|
|
|
|
.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()
|
2017-10-27 10:53:04 +02:00
|
|
|
|
{
|
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(),
|
2018-05-20 16:37:18 +02:00
|
|
|
|
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());
|
2017-10-27 10:53:04 +02:00
|
|
|
|
}
|
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)
|
|
|
|
|
{
|
|
|
|
|
StringBuilder currencyPairsBuilder = new StringBuilder();
|
|
|
|
|
bool first = true;
|
|
|
|
|
foreach (var currencyCode in currencyCodes)
|
|
|
|
|
{
|
|
|
|
|
if(!first)
|
|
|
|
|
currencyPairsBuilder.Append(",");
|
2018-07-30 16:07:29 +02:00
|
|
|
|
first = false;
|
2018-07-30 15:51:39 +02:00
|
|
|
|
currencyPairsBuilder.Append($"{baseCrypto}_{currencyCode}");
|
|
|
|
|
}
|
|
|
|
|
return currencyPairsBuilder.ToString();
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-02 20:32:42 +02:00
|
|
|
|
public class Rate
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
[JsonProperty(PropertyName = "name")]
|
|
|
|
|
public string Name
|
|
|
|
|
{
|
|
|
|
|
get;
|
|
|
|
|
set;
|
|
|
|
|
}
|
|
|
|
|
[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-02 20:32:42 +02:00
|
|
|
|
[JsonProperty(PropertyName = "code")]
|
|
|
|
|
public string Code
|
|
|
|
|
{
|
|
|
|
|
get;
|
|
|
|
|
set;
|
|
|
|
|
}
|
|
|
|
|
[JsonProperty(PropertyName = "rate")]
|
|
|
|
|
public decimal Value
|
|
|
|
|
{
|
|
|
|
|
get;
|
|
|
|
|
set;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-10-27 10:53:04 +02:00
|
|
|
|
}
|
2017-09-13 08:47:34 +02:00
|
|
|
|
}
|