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 ;
2020-11-17 13:46:23 +01:00
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 ;
2024-04-04 09:31:04 +02:00
using BTCPayServer.Payments ;
2020-06-28 10:55:27 +02:00
using BTCPayServer.Rating ;
using BTCPayServer.Security ;
2024-04-04 09:31:04 +02:00
using BTCPayServer.Services.Invoices ;
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-10-15 11:11:20 +02:00
using Microsoft.AspNetCore.Authorization ;
2019-01-29 10:20:18 +01:00
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
{
2019-01-29 10:20:18 +01:00
[EnableCors(CorsPolicies.All)]
2020-11-17 13:46:23 +01:00
[Authorize(Policy = ServerPolicies.CanGetRates.Key, AuthenticationSchemes = AuthenticationSchemes.Bitpay)]
2022-01-07 04:08:28 +01:00
public class BitpayRateController : Controller
2017-10-27 10:53:04 +02:00
{
2023-01-06 14:18:07 +01:00
2022-06-05 14:33:33 +02:00
readonly RateFetcher _rateProviderFactory ;
readonly CurrencyNameTable _currencyNameTable ;
2024-05-13 15:29:42 +02:00
private readonly DefaultRulesCollection _defaultRules ;
2024-04-04 09:31:04 +02:00
private readonly PaymentMethodHandlerDictionary _handlers ;
2022-06-05 14:33:33 +02:00
readonly StoreRepository _storeRepo ;
2024-04-04 09:31:04 +02:00
private readonly InvoiceRepository _invoiceRepository ;
2018-10-15 11:11:20 +02:00
2022-06-05 14:33:33 +02:00
private StoreData CurrentStore = > HttpContext . GetStoreData ( ) ;
2018-10-15 11:11:20 +02:00
2022-01-07 04:08:28 +01:00
public BitpayRateController (
2018-08-22 09:53:40 +02:00
RateFetcher rateProviderFactory ,
2018-01-19 10:11:43 +01:00
StoreRepository storeRepo ,
2024-04-04 09:31:04 +02:00
InvoiceRepository invoiceRepository ,
CurrencyNameTable currencyNameTable ,
2024-05-13 15:29:42 +02:00
DefaultRulesCollection defaultRules ,
2024-04-04 09:31:04 +02:00
PaymentMethodHandlerDictionary handlers )
2017-10-27 10:53:04 +02:00
{
2022-06-05 14:33:33 +02:00
_rateProviderFactory = rateProviderFactory ? ? throw new ArgumentNullException ( nameof ( rateProviderFactory ) ) ;
_storeRepo = storeRepo ;
2024-04-04 09:31:04 +02:00
_invoiceRepository = invoiceRepository ;
2022-06-05 14:33:33 +02:00
_currencyNameTable = currencyNameTable ? ? throw new ArgumentNullException ( nameof ( currencyNameTable ) ) ;
2024-05-13 15:29:42 +02:00
_defaultRules = defaultRules ;
2024-04-04 09:31:04 +02:00
_handlers = handlers ;
2017-10-27 10:53:04 +02:00
}
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]
2024-01-18 06:08:07 +01:00
public async Task < IActionResult > GetBaseCurrencyRates ( string baseCurrency , string cryptoCode = null , CancellationToken cancellationToken = default )
2018-05-21 16:49:37 +02:00
{
2024-04-04 09:31:04 +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
2024-01-18 06:08:07 +01: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 [ ] ;
2022-06-05 14:33:33 +02:00
return rates = = null ? result : Json ( new DataWrapper < Rate [ ] > ( rates ) ) ;
2018-05-21 16:49:37 +02:00
}
2022-06-05 14:33:33 +02:00
[HttpGet("rates/{baseCurrency}/{currency}")]
2018-07-27 06:38:54 +02:00
[BitpayAPIConstraint]
2024-01-18 06:08:07 +01:00
public async Task < IActionResult > GetCurrencyPairRate ( string baseCurrency , string currency , string cryptoCode = null , CancellationToken cancellationToken = default )
2018-05-28 10:20:18 +02:00
{
2024-01-18 06:08:07 +01:00
var result = await GetRates2 ( $"{baseCurrency}_{currency}" , null , cryptoCode , cancellationToken ) ;
2023-01-06 14:18:07 +01:00
return ( result as JsonResult ) ? . Value is not Rate [ ] rates
2022-06-05 14:33:33 +02:00
? result
: Json ( new DataWrapper < Rate > ( rates . First ( ) ) ) ;
2018-05-28 10:20:18 +02:00
}
2022-06-05 14:33:33 +02:00
[HttpGet("rates")]
2017-10-27 10:53:04 +02:00
[BitpayAPIConstraint]
2024-01-18 06:08:07 +01:00
public async Task < IActionResult > GetRates ( string currencyPairs , string storeId = null , string cryptoCode = null , CancellationToken cancellationToken = default )
2018-01-20 04:11:24 +01:00
{
2024-01-18 06:08:07 +01:00
var result = await GetRates2 ( currencyPairs , storeId , cryptoCode , cancellationToken ) ;
2022-06-05 14:33:33 +02:00
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]
2022-06-05 14:33:33 +02:00
[HttpGet("api/rates")]
2024-01-18 06:08:07 +01:00
public async Task < IActionResult > GetRates2 ( string currencyPairs , string storeId , string cryptoCode = null , CancellationToken cancellationToken = default )
2017-10-27 10:53:04 +02:00
{
2022-06-05 14:33:33 +02:00
var store = CurrentStore ? ? await _storeRepo . FindStore ( storeId ) ;
2018-05-02 20:32:42 +02:00
if ( store = = null )
2018-01-19 10:11:43 +01:00
{
2022-06-05 14:33:33 +02:00
var err = Json ( new BitpayErrorsModel { Error = "Store not found" } ) ;
2019-10-12 13:35:30 +02:00
err . StatusCode = 404 ;
return err ;
2018-01-19 10:11:43 +01:00
}
2018-05-28 14:55:49 +02:00
if ( currencyPairs = = null )
{
2024-01-18 06:08:07 +01:00
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 ) )
{
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 ;
}
}
2024-05-13 15:29:42 +02:00
var rules = store . GetStoreBlob ( ) . GetRateRules ( _defaultRules ) ;
2022-06-05 14:33:33 +02:00
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
2024-04-30 11:31:15 +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
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 = >
2022-06-05 14:33:33 +02:00
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 ( ) ,
2022-06-05 14:33:33 +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 )
{
2022-06-05 14:33:33 +02:00
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")]
2022-06-05 14:33:33 +02:00
public string Name { get ; set ; }
2023-01-06 14:18:07 +01:00
2018-05-02 20:32:42 +02:00
[JsonProperty(PropertyName = "cryptoCode")]
2022-06-05 14:33:33 +02:00
public string CryptoCode { get ; set ; }
2018-05-06 15:41:38 +02:00
[JsonProperty(PropertyName = "currencyPair")]
2022-06-05 14:33:33 +02:00
public string CurrencyPair { get ; set ; }
2018-05-06 15:41:38 +02:00
2018-05-02 20:32:42 +02:00
[JsonProperty(PropertyName = "code")]
2022-06-05 14:33:33 +02:00
public string Code { get ; set ; }
2023-01-06 14:18:07 +01:00
2018-05-02 20:32:42 +02:00
[JsonProperty(PropertyName = "rate")]
2022-06-05 14:33:33 +02:00
public decimal Value { get ; set ; }
2018-05-02 20:32:42 +02:00
}
2017-10-27 10:53:04 +02:00
}
2017-09-13 08:47:34 +02:00
}