Add Bitcoin average quota

This commit is contained in:
nicolas.dorier 2018-04-18 18:23:39 +09:00
parent 73ed4003a3
commit 6936b034cb
6 changed files with 88 additions and 24 deletions

View file

@ -253,7 +253,7 @@ namespace BTCPayServer.Controllers
}; };
var expiration = TimeSpan.FromSeconds(model.ExpirationSeconds); var expiration = TimeSpan.FromSeconds(model.ExpirationSeconds);
model.TimeLeft = PrettyPrint(expiration); model.TimeLeft = expiration.PrettyPrint();
return model; return model;
} }
@ -272,17 +272,6 @@ namespace BTCPayServer.Controllers
return price.ToString("C", _CurrencyNameTable.GetCurrencyProvider(currency)) + $" ({currency})"; return price.ToString("C", _CurrencyNameTable.GetCurrencyProvider(currency)) + $" ({currency})";
} }
private string PrettyPrint(TimeSpan expiration)
{
StringBuilder builder = new StringBuilder();
if (expiration.Days >= 1)
builder.Append(expiration.Days.ToString(CultureInfo.InvariantCulture));
if (expiration.Hours >= 1)
builder.Append(expiration.Hours.ToString("00", CultureInfo.InvariantCulture));
builder.Append($"{expiration.Minutes.ToString("00", CultureInfo.InvariantCulture)}:{expiration.Seconds.ToString("00", CultureInfo.InvariantCulture)}");
return builder.ToString();
}
[HttpGet] [HttpGet]
[Route("i/{invoiceId}/status")] [Route("i/{invoiceId}/status")]
[Route("i/{invoiceId}/{paymentMethodId}/status")] [Route("i/{invoiceId}/{paymentMethodId}/status")]

View file

@ -36,13 +36,28 @@ namespace BTCPayServer.Controllers
public async Task<IActionResult> Rates() public async Task<IActionResult> Rates()
{ {
var rates = (await _SettingsRepository.GetSettingAsync<RatesSetting>()) ?? new RatesSetting(); var rates = (await _SettingsRepository.GetSettingAsync<RatesSetting>()) ?? new RatesSetting();
return View(new RatesViewModel()
var vm = new RatesViewModel()
{ {
CacheMinutes = rates.CacheInMinutes, CacheMinutes = rates.CacheInMinutes,
PrivateKey = rates.PrivateKey, PrivateKey = rates.PrivateKey,
PublicKey = rates.PublicKey PublicKey = rates.PublicKey
}); };
await FetchRateLimits(vm);
return View(vm);
}
private static async Task FetchRateLimits(RatesViewModel vm)
{
var coinAverage = GetCoinaverageService(vm, false);
if (coinAverage != null)
{
try
{
vm.RateLimits = await coinAverage.GetRateLimitsAsync();
}
catch { }
}
} }
[Route("server/rates")] [Route("server/rates")]
@ -55,27 +70,38 @@ namespace BTCPayServer.Controllers
rates.CacheInMinutes = vm.CacheMinutes; rates.CacheInMinutes = vm.CacheMinutes;
try try
{ {
var settings = new CoinAverageSettings() var service = GetCoinaverageService(vm, true);
{ if(service != null)
KeyPair = (vm.PublicKey, vm.PrivateKey) await service.TestAuthAsync();
};
if (settings.GetCoinAverageSignature() != null)
{
await new CoinAverageRateProvider("BTC")
{ Authenticator = settings }.TestAuthAsync();
}
} }
catch catch
{ {
ModelState.AddModelError(nameof(vm.PrivateKey), "Invalid API key pair"); ModelState.AddModelError(nameof(vm.PrivateKey), "Invalid API key pair");
} }
if (!ModelState.IsValid) if (!ModelState.IsValid)
{
await FetchRateLimits(vm);
return View(vm); return View(vm);
}
await _SettingsRepository.UpdateSetting(rates); await _SettingsRepository.UpdateSetting(rates);
StatusMessage = "Rate settings successfully updated"; StatusMessage = "Rate settings successfully updated";
return RedirectToAction(nameof(Rates)); return RedirectToAction(nameof(Rates));
} }
private static CoinAverageRateProvider GetCoinaverageService(RatesViewModel vm, bool withAuth)
{
var settings = new CoinAverageSettings()
{
KeyPair = (vm.PublicKey, vm.PrivateKey)
};
if (!withAuth || settings.GetCoinAverageSignature() != null)
{
return new CoinAverageRateProvider("BTC")
{ Authenticator = settings };
}
return null;
}
[Route("server/users")] [Route("server/users")]
public IActionResult ListUsers() public IActionResult ListUsers()
{ {

View file

@ -28,11 +28,22 @@ using BTCPayServer.Payments;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using BTCPayServer.Models; using BTCPayServer.Models;
using System.Security.Claims; using System.Security.Claims;
using System.Globalization;
namespace BTCPayServer namespace BTCPayServer
{ {
public static class Extensions public static class Extensions
{ {
public static string PrettyPrint(this TimeSpan expiration)
{
StringBuilder builder = new StringBuilder();
if (expiration.Days >= 1)
builder.Append(expiration.Days.ToString(CultureInfo.InvariantCulture));
if (expiration.Hours >= 1)
builder.Append(expiration.Hours.ToString("00", CultureInfo.InvariantCulture));
builder.Append($"{expiration.Minutes.ToString("00", CultureInfo.InvariantCulture)}:{expiration.Seconds.ToString("00", CultureInfo.InvariantCulture)}");
return builder.ToString();
}
public static decimal RoundUp(decimal value, int precision) public static decimal RoundUp(decimal value, int precision)
{ {
for (int i = 0; i < precision; i++) for (int i = 0; i < precision; i++)

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Services.Rates;
namespace BTCPayServer.Models.ServerViewModels namespace BTCPayServer.Models.ServerViewModels
{ {
@ -14,5 +15,6 @@ namespace BTCPayServer.Models.ServerViewModels
[Display(Name = "Cache the rates for ... minutes")] [Display(Name = "Cache the rates for ... minutes")]
[Range(0, 60)] [Range(0, 60)]
public int CacheMinutes { get; set; } public int CacheMinutes { get; set; }
public GetRateLimitsResponse RateLimits { get; internal set; }
} }
} }

View file

@ -170,6 +170,23 @@ namespace BTCPayServer.Services.Rates
resp.EnsureSuccessStatusCode(); resp.EnsureSuccessStatusCode();
} }
public async Task<GetRateLimitsResponse> GetRateLimitsAsync()
{
var request = new HttpRequestMessage(HttpMethod.Get, "https://apiv2.bitcoinaverage.com/info/ratelimits");
var auth = Authenticator;
if (auth != null)
{
await auth.AddHeader(request);
}
var resp = await _Client.SendAsync(request);
resp.EnsureSuccessStatusCode();
var jobj = JObject.Parse(await resp.Content.ReadAsStringAsync());
var response = new GetRateLimitsResponse();
response.CounterReset = TimeSpan.FromSeconds(jobj["counter_reset"].Value<int>());
response.RequestsLeft = jobj["requests_left"].Value<int>();
return response;
}
public async Task<GetExchangeTickersResponse> GetExchangeTickersAsync() public async Task<GetExchangeTickersResponse> GetExchangeTickersAsync()
{ {
var request = new HttpRequestMessage(HttpMethod.Get, "https://apiv2.bitcoinaverage.com/symbols/exchanges/ticker"); var request = new HttpRequestMessage(HttpMethod.Get, "https://apiv2.bitcoinaverage.com/symbols/exchanges/ticker");
@ -191,4 +208,10 @@ namespace BTCPayServer.Services.Rates
return response; return response;
} }
} }
public class GetRateLimitsResponse
{
public TimeSpan CounterReset { get; set; }
public int RequestsLeft { get; set; }
}
} }

View file

@ -28,7 +28,6 @@
<input asp-for="PublicKey" style="width:50%;" class="form-control" placeholder="Public key" /> <input asp-for="PublicKey" style="width:50%;" class="form-control" placeholder="Public key" />
<label class="sr-only" asp-for="PrivateKey"></label> <label class="sr-only" asp-for="PrivateKey"></label>
<input asp-for="PrivateKey" style="width:50%;" class="form-control" placeholder="Private key" /> <input asp-for="PrivateKey" style="width:50%;" class="form-control" placeholder="Private key" />
<span asp-validation-for="PrivateKey" class="text-danger"></span>
<p class="form-text text-muted">You can find the information on <a target="_blank" href="https://bitcoinaverage.com/en/apikeys">bitcoinaverage api key page</a></p> <p class="form-text text-muted">You can find the information on <a target="_blank" href="https://bitcoinaverage.com/en/apikeys">bitcoinaverage api key page</a></p>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -36,6 +35,20 @@
<input asp-for="CacheMinutes" class="form-control" /> <input asp-for="CacheMinutes" class="form-control" />
<span asp-validation-for="CacheMinutes" class="text-danger"></span> <span asp-validation-for="CacheMinutes" class="text-danger"></span>
</div> </div>
@if(Model.RateLimits != null)
{
<h5>Current Bitcoin Average Quotas:</h5>
<table class="table table-sm">
<tr>
<th>Requests left</th>
<td>@Model.RateLimits.RequestsLeft</td>
</tr>
<tr>
<th>Quota reset in</th>
<td>@Model.RateLimits.CounterReset</td>
</tr>
</table>
}
<button type="submit" class="btn btn-primary" name="command" value="Save">Save</button> <button type="submit" class="btn btn-primary" name="command" value="Save">Save</button>
</form> </form>
</div> </div>