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 ;
2019-01-29 10:20:18 +01:00
using Microsoft.AspNetCore.Cors ;
2019-03-05 09:09:17 +01:00
using System.Threading ;
2019-08-29 17:24:42 +02:00
using BTCPayServer.Data ;
2019-10-12 13:35:30 +02:00
using BTCPayServer.Security ;
2019-10-18 17:54:20 +02:00
using BTCPayServer.Security.Bitpay ;
2017-09-13 08:47:34 +02:00
namespace BTCPayServer.Controllers
{
2019-01-29 10:20:18 +01:00
[EnableCors(CorsPolicies.All)]
2020-03-20 05:44:02 +01:00
[Authorize(Policy = ServerPolicies.CanGetRates.Key, AuthenticationSchemes = Security.AuthenticationSchemes.Bitpay)]
2017-10-27 10:53:04 +02:00
public class RateController : Controller
{
2019-10-12 13:35:30 +02:00
public StoreData CurrentStore
{
get
{
return HttpContext . GetStoreData ( ) ;
}
}
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]
2019-10-12 13:35:30 +02:00
public async Task < IActionResult > GetBaseCurrencyRates ( string baseCurrency , CancellationToken cancellationToken )
2018-05-21 16:49:37 +02:00
{
2019-10-12 13:35:30 +02:00
var supportedMethods = CurrentStore . GetSupportedPaymentMethods ( _NetworkProvider ) ;
2018-05-21 16:49:37 +02:00
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 ) ;
2019-10-12 13:35:30 +02:00
var result = await GetRates2 ( currencypairs , null , cancellationToken ) ;
2018-05-21 16:49:37 +02:00
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]
2019-10-12 13:35:30 +02:00
public async Task < IActionResult > GetCurrencyPairRate ( string baseCurrency , string currency , CancellationToken cancellationToken )
2018-05-28 10:20:18 +02:00
{
2019-10-12 13:35:30 +02:00
var result = await GetRates2 ( $"{baseCurrency}_{currency}" , null , cancellationToken ) ;
2018-05-28 10:20:18 +02:00
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]
2019-11-15 10:20:10 +01:00
public async Task < IActionResult > GetRates ( string currencyPairs , string storeId = null , CancellationToken cancellationToken = default )
2018-01-20 04:11:24 +01:00
{
2019-11-15 10:10:14 +01:00
var result = await GetRates2 ( currencyPairs , storeId , cancellationToken ) ;
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
}
[Route("api/rates")]
[HttpGet]
2019-10-12 13:35:30 +02:00
[AllowAnonymous]
2019-03-05 09:09:17 +01:00
public async Task < IActionResult > GetRates2 ( string currencyPairs , string storeId , CancellationToken cancellationToken )
2017-10-27 10:53:04 +02:00
{
2019-10-12 13:35:30 +02:00
var store = this . CurrentStore ? ? await this . _StoreRepo . FindStore ( storeId ) ;
2018-05-02 20:32:42 +02:00
if ( store = = null )
2018-01-19 10:11:43 +01:00
{
2019-10-12 13:35:30 +02:00
var err = Json ( new BitpayErrorsModel ( ) { Error = "Store not found" } ) ;
err . StatusCode = 404 ;
return err ;
2018-01-19 10:11:43 +01:00
}
2018-05-28 14:55:49 +02:00
if ( currencyPairs = = null )
{
2019-03-11 10:39:21 +01:00
currencyPairs = store . GetStoreBlob ( ) . GetDefaultCurrencyPairString ( ) ;
2018-05-28 14:55:49 +02:00
if ( string . IsNullOrEmpty ( currencyPairs ) )
{
2019-03-11 10:39:21 +01:00
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 ;
}
}
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
2019-03-05 09:09:17 +01:00
var fetching = _RateProviderFactory . FetchRates ( pairs , rules , cancellationToken ) ;
2018-05-02 20:32:42 +02:00
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 )
{
2019-10-12 13:35:30 +02:00
if ( ! first )
2018-07-30 15:51:39 +02:00
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
}