Compare commits
14 Commits
v1.0.2.96
...
v1.0.2.101
Author | SHA1 | Date | |
---|---|---|---|
0f1efc16f5 | |||
da8a06952c | |||
38d810cef7 | |||
393a3a2b8f | |||
aaddc580d1 | |||
957d478865 | |||
023913a852 | |||
6c51d83f61 | |||
0edaedb6ab | |||
13f21aa0d6 | |||
929a0c37bd | |||
058ccf56d0 | |||
162ac572da | |||
c7f3fdb46d |
@ -1783,6 +1783,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
factory.Providers.Clear();
|
||||
var fetch = new BackgroundFetcherRateProvider(spy);
|
||||
fetch.DoNotAutoFetchIfExpired = true;
|
||||
factory.Providers.Add("bittrex", fetch);
|
||||
fetchedRate = fetcher.FetchRate(CurrencyPair.Parse("BTC_USD"), rateRules).GetAwaiter().GetResult();
|
||||
spy.AssertHit();
|
||||
|
@ -2,7 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<Version>1.0.2.96</Version>
|
||||
<Version>1.0.2.101</Version>
|
||||
<NoWarn>NU1701,CA1816,CA1308,CA1810,CA2208</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
@ -49,6 +49,7 @@
|
||||
<PackageReference Include="NBXplorer.Client" Version="1.0.2.18" />
|
||||
<PackageReference Include="NicolasDorier.CommandLine" Version="1.0.0.2" />
|
||||
<PackageReference Include="NicolasDorier.CommandLine.Configuration" Version="1.0.0.3" />
|
||||
<PackageReference Include="NicolasDorier.RateLimits" Version="1.0.0.3" />
|
||||
<PackageReference Include="NicolasDorier.StandardConfiguration" Version="1.0.0.17" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.1.0" />
|
||||
<PackageReference Include="SSH.NET" Version="2016.1.0" />
|
||||
|
@ -18,6 +18,7 @@ using BTCPayServer.Services.Stores;
|
||||
using BTCPayServer.Logging;
|
||||
using BTCPayServer.Security;
|
||||
using System.Globalization;
|
||||
using NicolasDorier.RateLimits;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
@ -70,6 +71,7 @@ namespace BTCPayServer.Controllers
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
[RateLimitsFilter(ZoneLimits.Login, Scope = RateLimitsScope.RemoteAddress)]
|
||||
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
|
||||
{
|
||||
ViewData["ReturnUrl"] = returnUrl;
|
||||
|
@ -41,6 +41,7 @@ using System.Security.Claims;
|
||||
using BTCPayServer.Security;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using NBXplorer.DerivationStrategy;
|
||||
using NicolasDorier.RateLimits;
|
||||
|
||||
namespace BTCPayServer.Hosting
|
||||
{
|
||||
@ -156,7 +157,7 @@ namespace BTCPayServer.Hosting
|
||||
{
|
||||
var opts = provider.GetRequiredService<BTCPayServerOptions>();
|
||||
var bundle = new BundleOptions();
|
||||
bundle.UseMinifiedFiles = opts.BundleJsCss;
|
||||
bundle.UseBundles = opts.BundleJsCss;
|
||||
bundle.AppendVersion = true;
|
||||
return bundle;
|
||||
});
|
||||
@ -165,6 +166,10 @@ namespace BTCPayServer.Hosting
|
||||
{
|
||||
options.AddPolicy(CorsPolicies.All, p=>p.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin());
|
||||
});
|
||||
|
||||
var rateLimits = new RateLimitService();
|
||||
rateLimits.SetZone($"zone={ZoneLimits.Login} rate=5r/min burst=3 nodelay");
|
||||
services.AddSingleton(rateLimits);
|
||||
return services;
|
||||
}
|
||||
|
||||
|
@ -166,6 +166,7 @@ namespace BTCPayServer.Hosting
|
||||
Authorization = new[] { new NeedRole(Roles.ServerAdmin) }
|
||||
});
|
||||
app.UseWebSockets();
|
||||
app.UseStatusCodePages();
|
||||
app.UseMvc(routes =>
|
||||
{
|
||||
routes.MapRoute(
|
||||
|
@ -211,13 +211,26 @@ namespace BTCPayServer.Payments.Lightning.CLightning
|
||||
async Task<LightningNodeInformation> ILightningInvoiceClient.GetInfo(CancellationToken cancellation)
|
||||
{
|
||||
var info = await GetInfoAsync(cancellation);
|
||||
var address = info.Address.Select(a => a.Address).FirstOrDefault();
|
||||
var port = info.Port;
|
||||
return ToLightningNodeInformation(info);
|
||||
}
|
||||
|
||||
internal static LightningNodeInformation ToLightningNodeInformation(Charge.GetInfoResponse info)
|
||||
{
|
||||
var addr = info.Address.FirstOrDefault();
|
||||
if (addr == null)
|
||||
{
|
||||
addr = new Charge.GetInfoResponse.GetInfoAddress();
|
||||
addr.Address = "127.0.0.1";
|
||||
}
|
||||
if (addr.Port == 0)
|
||||
{
|
||||
addr.Port = 9735;
|
||||
}
|
||||
return new LightningNodeInformation()
|
||||
{
|
||||
NodeId = info.Id,
|
||||
P2PPort = port,
|
||||
Address = address,
|
||||
P2PPort = addr.Port,
|
||||
Address = addr.Address,
|
||||
BlockHeight = info.BlockHeight
|
||||
};
|
||||
}
|
||||
|
@ -163,15 +163,7 @@ namespace BTCPayServer.Payments.Lightning.Charge
|
||||
async Task<LightningNodeInformation> ILightningInvoiceClient.GetInfo(CancellationToken cancellation)
|
||||
{
|
||||
var info = await GetInfoAsync(cancellation);
|
||||
var address = info.Address.Select(a => a.Address).FirstOrDefault();
|
||||
var port = info.Port;
|
||||
return new LightningNodeInformation()
|
||||
{
|
||||
NodeId = info.Id,
|
||||
P2PPort = port,
|
||||
Address = address,
|
||||
BlockHeight = info.BlockHeight
|
||||
};
|
||||
return CLightning.CLightningRPCClient.ToLightningNodeInformation(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ namespace BTCPayServer.Payments.Lightning.Charge
|
||||
public int Port { get; set; }
|
||||
}
|
||||
public string Id { get; set; }
|
||||
public int Port { get; set; }
|
||||
public GetInfoAddress[] Address { get; set; }
|
||||
public string Version { get; set; }
|
||||
public int BlockHeight { get; set; }
|
||||
|
@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Logging;
|
||||
using BTCPayServer.Rating;
|
||||
|
||||
namespace BTCPayServer.Services.Rates
|
||||
@ -13,15 +15,15 @@ namespace BTCPayServer.Services.Rates
|
||||
public class LatestFetch
|
||||
{
|
||||
public ExchangeRates Latest;
|
||||
public DateTimeOffset Timestamp;
|
||||
public DateTimeOffset NextRefresh;
|
||||
public DateTimeOffset Expiration;
|
||||
public Exception Exception;
|
||||
|
||||
internal ExchangeRates GetResult()
|
||||
{
|
||||
if(Expiration < DateTimeOffset.UtcNow)
|
||||
if (Expiration <= DateTimeOffset.UtcNow)
|
||||
{
|
||||
if(Exception != null)
|
||||
if (Exception != null)
|
||||
{
|
||||
ExceptionDispatchInfo.Capture(Exception).Throw();
|
||||
}
|
||||
@ -32,14 +34,6 @@ namespace BTCPayServer.Services.Rates
|
||||
}
|
||||
return Latest;
|
||||
}
|
||||
|
||||
internal void CopyFrom(LatestFetch previous)
|
||||
{
|
||||
Latest = previous.Latest;
|
||||
Timestamp = previous.Timestamp;
|
||||
Expiration = previous.Expiration;
|
||||
Exception = previous.Exception;
|
||||
}
|
||||
}
|
||||
|
||||
IRateProvider _Inner;
|
||||
@ -50,20 +44,53 @@ namespace BTCPayServer.Services.Rates
|
||||
_Inner = inner;
|
||||
}
|
||||
|
||||
public TimeSpan RefreshRate { get; set; } = TimeSpan.FromSeconds(30);
|
||||
public TimeSpan ValidatyTime { get; set; } = TimeSpan.FromMinutes(10);
|
||||
TimeSpan _RefreshRate = TimeSpan.FromSeconds(30);
|
||||
public TimeSpan RefreshRate
|
||||
{
|
||||
get
|
||||
{
|
||||
return _RefreshRate;
|
||||
}
|
||||
set
|
||||
{
|
||||
var diff = value - _RefreshRate;
|
||||
var latest = _Latest;
|
||||
if (latest != null)
|
||||
latest.NextRefresh += diff;
|
||||
_RefreshRate = value;
|
||||
}
|
||||
}
|
||||
|
||||
TimeSpan _ValidatyTime = TimeSpan.FromMinutes(10);
|
||||
public TimeSpan ValidatyTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return _ValidatyTime;
|
||||
}
|
||||
set
|
||||
{
|
||||
var diff = value - _ValidatyTime;
|
||||
var latest = _Latest;
|
||||
if (latest != null)
|
||||
latest.Expiration += diff;
|
||||
_ValidatyTime = value;
|
||||
}
|
||||
}
|
||||
|
||||
public DateTimeOffset NextUpdate
|
||||
{
|
||||
get
|
||||
{
|
||||
var latest = _Latest;
|
||||
if (latest == null || latest.Exception != null)
|
||||
if (latest == null)
|
||||
return DateTimeOffset.UtcNow;
|
||||
return latest.Timestamp + RefreshRate;
|
||||
return latest.NextRefresh;
|
||||
}
|
||||
}
|
||||
|
||||
public bool DoNotAutoFetchIfExpired { get; set; }
|
||||
|
||||
public async Task<LatestFetch> UpdateIfNecessary()
|
||||
{
|
||||
if (NextUpdate <= DateTimeOffset.UtcNow)
|
||||
@ -81,7 +108,20 @@ namespace BTCPayServer.Services.Rates
|
||||
LatestFetch _Latest;
|
||||
public async Task<ExchangeRates> GetRatesAsync()
|
||||
{
|
||||
return (_Latest ?? (await Fetch())).GetResult();
|
||||
var latest = _Latest;
|
||||
if (!DoNotAutoFetchIfExpired && latest != null && latest.Expiration <= DateTimeOffset.UtcNow + TimeSpan.FromSeconds(1.0))
|
||||
{
|
||||
Logs.PayServer.LogWarning($"GetRatesAsync was called on {GetExchangeName()} when the rate is outdated. It should never happen, let BTCPayServer developers know about this.");
|
||||
latest = null;
|
||||
}
|
||||
return (latest ?? (await Fetch())).GetResult();
|
||||
}
|
||||
|
||||
private string GetExchangeName()
|
||||
{
|
||||
if (_Inner is IHasExchangeName exchangeName)
|
||||
return exchangeName.ExchangeName ?? "???";
|
||||
return "???";
|
||||
}
|
||||
|
||||
private async Task<LatestFetch> Fetch()
|
||||
@ -93,16 +133,22 @@ namespace BTCPayServer.Services.Rates
|
||||
var rates = await _Inner.GetRatesAsync();
|
||||
fetch.Latest = rates;
|
||||
fetch.Expiration = DateTimeOffset.UtcNow + ValidatyTime;
|
||||
fetch.NextRefresh = DateTimeOffset.UtcNow + RefreshRate;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if(previous != null)
|
||||
if (previous != null)
|
||||
{
|
||||
fetch.CopyFrom(previous);
|
||||
fetch.Latest = previous.Latest;
|
||||
fetch.Expiration = previous.Expiration;
|
||||
}
|
||||
else
|
||||
{
|
||||
fetch.Expiration = DateTimeOffset.UtcNow;
|
||||
}
|
||||
fetch.NextRefresh = DateTimeOffset.UtcNow;
|
||||
fetch.Exception = ex;
|
||||
}
|
||||
fetch.Timestamp = DateTimeOffset.UtcNow;
|
||||
_Latest = fetch;
|
||||
fetch.GetResult(); // Will throw if not valid
|
||||
return fetch;
|
||||
|
@ -9,7 +9,7 @@ using BTCPayServer.Rating;
|
||||
|
||||
namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public class BitpayRateProvider : IRateProvider
|
||||
public class BitpayRateProvider : IRateProvider, IHasExchangeName
|
||||
{
|
||||
public const string BitpayName = "bitpay";
|
||||
Bitpay _Bitpay;
|
||||
@ -20,6 +20,8 @@ namespace BTCPayServer.Services.Rates
|
||||
_Bitpay = bitpay;
|
||||
}
|
||||
|
||||
public string ExchangeName => BitpayName;
|
||||
|
||||
public async Task<ExchangeRates> GetRatesAsync()
|
||||
{
|
||||
return new ExchangeRates((await _Bitpay.GetRatesAsync().ConfigureAwait(false))
|
||||
|
@ -7,7 +7,7 @@ using Microsoft.Extensions.Caching.Memory;
|
||||
|
||||
namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public class CachedRateProvider : IRateProvider
|
||||
public class CachedRateProvider : IRateProvider, IHasExchangeName
|
||||
{
|
||||
private IRateProvider _Inner;
|
||||
private IMemoryCache _MemoryCache;
|
||||
@ -31,7 +31,7 @@ namespace BTCPayServer.Services.Rates
|
||||
}
|
||||
}
|
||||
|
||||
public string ExchangeName { get; set; }
|
||||
public string ExchangeName { get; }
|
||||
|
||||
public TimeSpan CacheSpan
|
||||
{
|
||||
|
@ -49,7 +49,7 @@ namespace BTCPayServer.Services.Rates
|
||||
Task AddHeader(HttpRequestMessage message);
|
||||
}
|
||||
|
||||
public class CoinAverageRateProvider : IRateProvider
|
||||
public class CoinAverageRateProvider : IRateProvider, IHasExchangeName
|
||||
{
|
||||
public const string CoinAverageName = "coinaverage";
|
||||
public CoinAverageRateProvider()
|
||||
@ -82,6 +82,8 @@ namespace BTCPayServer.Services.Rates
|
||||
|
||||
public ICoinAverageAuthenticator Authenticator { get; set; }
|
||||
|
||||
public string ExchangeName => Exchange ?? CoinAverageName;
|
||||
|
||||
private bool TryToBidAsk(JProperty p, out BidAsk bidAsk)
|
||||
{
|
||||
bidAsk = null;
|
||||
|
@ -10,7 +10,7 @@ using ExchangeSharp;
|
||||
|
||||
namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public class ExchangeSharpRateProvider : IRateProvider
|
||||
public class ExchangeSharpRateProvider : IRateProvider, IHasExchangeName
|
||||
{
|
||||
readonly ExchangeAPI _ExchangeAPI;
|
||||
readonly string _ExchangeName;
|
||||
@ -29,6 +29,8 @@ namespace BTCPayServer.Services.Rates
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string ExchangeName => _ExchangeName;
|
||||
|
||||
public async Task<ExchangeRates> GetRatesAsync()
|
||||
{
|
||||
await new SynchronizationContextRemover();
|
||||
|
12
BTCPayServer/Services/Rates/IHasExchangeName.cs
Normal file
12
BTCPayServer/Services/Rates/IHasExchangeName.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public interface IHasExchangeName
|
||||
{
|
||||
string ExchangeName { get; }
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ using Newtonsoft.Json.Linq;
|
||||
namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
// Make sure that only one request is sent to kraken in general
|
||||
public class KrakenExchangeRateProvider : IRateProvider
|
||||
public class KrakenExchangeRateProvider : IRateProvider, IHasExchangeName
|
||||
{
|
||||
public KrakenExchangeRateProvider()
|
||||
{
|
||||
@ -31,6 +31,9 @@ namespace BTCPayServer.Services.Rates
|
||||
_LocalClient = null;
|
||||
}
|
||||
}
|
||||
|
||||
public string ExchangeName => "kraken";
|
||||
|
||||
HttpClient _LocalClient;
|
||||
static HttpClient _Client = new HttpClient();
|
||||
|
||||
|
@ -9,11 +9,13 @@ using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public class QuadrigacxRateProvider : IRateProvider
|
||||
public class QuadrigacxRateProvider : IRateProvider, IHasExchangeName
|
||||
{
|
||||
public const string QuadrigacxName = "quadrigacx";
|
||||
static HttpClient _Client = new HttpClient();
|
||||
|
||||
public string ExchangeName => QuadrigacxName;
|
||||
|
||||
private bool TryToBidAsk(JObject p, out BidAsk v)
|
||||
{
|
||||
v = null;
|
||||
|
@ -21,17 +21,17 @@ namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
_inner = inner;
|
||||
}
|
||||
public Task<ExchangeRates> GetRatesAsync()
|
||||
public async Task<ExchangeRates> GetRatesAsync()
|
||||
{
|
||||
DateTimeOffset now = DateTimeOffset.UtcNow;
|
||||
try
|
||||
{
|
||||
return _inner.GetRatesAsync();
|
||||
return await _inner.GetRatesAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Exception = ex;
|
||||
return Task.FromResult(new ExchangeRates());
|
||||
return new ExchangeRates();
|
||||
}
|
||||
finally
|
||||
{
|
||||
@ -79,7 +79,10 @@ namespace BTCPayServer.Services.Rates
|
||||
provider.MemoryCache = cache;
|
||||
}
|
||||
if (Providers.TryGetValue(CoinAverageRateProvider.CoinAverageName, out var coinAverage) && coinAverage is BackgroundFetcherRateProvider c)
|
||||
{
|
||||
c.RefreshRate = CacheSpan;
|
||||
c.ValidatyTime = CacheSpan + TimeSpan.FromMinutes(1.0);
|
||||
}
|
||||
}
|
||||
CoinAverageSettings _CoinAverageSettings;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
@ -117,7 +120,16 @@ namespace BTCPayServer.Services.Rates
|
||||
foreach (var provider in Providers.ToArray())
|
||||
{
|
||||
var prov = new BackgroundFetcherRateProvider(Providers[provider.Key]);
|
||||
prov.RefreshRate = provider.Key == CoinAverageRateProvider.CoinAverageName ? CacheSpan : TimeSpan.FromMinutes(1.0);
|
||||
if(provider.Key == CoinAverageRateProvider.CoinAverageName)
|
||||
{
|
||||
prov.RefreshRate = CacheSpan;
|
||||
prov.ValidatyTime = CacheSpan + TimeSpan.FromMinutes(1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
prov.RefreshRate = TimeSpan.FromMinutes(1.0);
|
||||
prov.ValidatyTime = TimeSpan.FromMinutes(5.0);
|
||||
}
|
||||
Providers[provider.Key] = prov;
|
||||
}
|
||||
|
||||
|
@ -339,268 +339,4 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Obsolete? Start *@
|
||||
<div class="bp-view" id="link-expired" style="padding-top: 3.6rem;">
|
||||
<div class="manual__step-one refund-address-form" novalidate="">
|
||||
<div class="manual__step-one__header" i18n="">Link Expired</div>
|
||||
<div class="manual__step-one__instructions">
|
||||
<span class="initial-label" i18n="">Sorry, this link has expired. Please try requesting another refund by clicking the button below.</span>
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<bp-loading-button i18n="">
|
||||
<button class="action-button" style="margin-top: 15px;" type="submit">
|
||||
<span class="button-text" lcl="">Request Refund</span>
|
||||
<div class="loader-wrapper">
|
||||
<partial name="Checkout-Spinner" />
|
||||
</div>
|
||||
</button>
|
||||
</bp-loading-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bp-view confirm-contact-email-view">
|
||||
<form class="manual__step-one refund-address-form contact-email-form ng-untouched ng-pristine" novalidate="">
|
||||
<div class="manual__step-one__header" i18n="">Contact & Refund Email</div>
|
||||
<div class="manual__step-one__instructions">
|
||||
<span class="initial-label" i18n="">If there is an issue with this payment, or a refund needs to be made, we will contact you at this address.</span>
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<input bp-focus="focusEmailInput" class="bp-input email-input ng-untouched ng-pristine" disabled="disabled" name="receiptEmail" placeholder="Your email"
|
||||
style="opacity: 1;" type="email">
|
||||
<button type="submit" class="action-button" style="margin-top: 15px;">
|
||||
<span i18n="">Confirm email address</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="refund-address-form__link" id="wrong-email-button" style="color: #a9a9a9;">
|
||||
<span i18n="">Wrong email?</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="bp-view wrong-email-view" id="compromised-invoice">
|
||||
<div class="manual__step-one refund-address-form" novalidate="" style="margin-top: -1rem;">
|
||||
<div class="manual__step-one__header">
|
||||
<span i18n="">There seems to be a problem</span>
|
||||
</div>
|
||||
<div class="manual__step-one__instructions">
|
||||
<span class="initial-label" i18n="">
|
||||
This invoice was previously opened, and the address <strong class="placeholder-refundEmail">{Entered email address}</strong> was submitted as your contact email. If you entered this email, you can still safely make your payment. <br> <br>
|
||||
If you did not submit the email address, it's possible a thief falsely
|
||||
submitted this address to steal refunds. Please contact the merchant
|
||||
about this security incident, and try your payment again.
|
||||
</span>
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<a class="action-button" style="margin-top: 15px;" target="_blank" href="mailto:@Model.StoreEmail">
|
||||
<span i18n="">Contact {{srvModel.storeName}}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="refund-address-form__link">
|
||||
<span i18n="">I understand, continue to payment →</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bp-view confirm-bitcoin-address-view" id="confirm-refund-address">
|
||||
<form class="manual__step-one refund-address-form ng-untouched ng-pristine ng-valid" novalidate="" style="padding-top: 1.6rem;">
|
||||
<div><img src="~/imlegacy/mail.svg"></div>
|
||||
<div class="manual__step-one__header">
|
||||
<span i18n="">Please confirm your address</span>
|
||||
</div>
|
||||
<div class="manual__step-one__instructions">
|
||||
<span class="initial-label" i18n="">You should receive an email from us in a moment at <strong class="placeholder-refundEmail">{enterd refund email}</strong>. To ensure your refund is sent to the correct address, please confirm your bitcoin address by clicking the link in the email. </span>
|
||||
</div>
|
||||
<bp-resend-link id="resend-link">
|
||||
<div class="bp-resend__link">
|
||||
<span class="link-text">
|
||||
|
||||
<span i18n="">Resend email</span>
|
||||
|
||||
</span>
|
||||
<div class="success-text">
|
||||
|
||||
<img src="~/imlegacy/circle-check.svg">
|
||||
|
||||
<div i18n="">Email resent</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</bp-resend-link>
|
||||
</form>
|
||||
</div>
|
||||
<div class="bp-view refund-address-view" id="enter-refund-address">
|
||||
<form class="manual__step-one refund-address-form ng-untouched ng-pristine ng-invalid" name="refundAddressForm" novalidate="" style="margin-top: 28px; margin-bottom: 4rem;">
|
||||
<div class="manual__step-one__header">
|
||||
|
||||
<span i18n="">Please provide a refund address.</span>
|
||||
|
||||
</div>
|
||||
<div class="manual__step-one__instructions">
|
||||
<span class="initial-label">
|
||||
|
||||
<span i18n="">
|
||||
To send your refund of {BTC to refund} BTC,
|
||||
we’ll need a bitcoin address from your wallet. Please open your bitcoin
|
||||
wallet, copy a receiving address, and paste it below.
|
||||
</span>
|
||||
|
||||
</span>
|
||||
<span class="submission-error-label" i18n="" id="invalid-bitcoin-address">Please enter a valid bitcoin address.</span>
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<bp-refund-address name="refundAddress" ngmodel="" class="ng-untouched ng-pristine ng-invalid">
|
||||
<div class="bp-refund-address">
|
||||
<div class="bitcoin-logo">
|
||||
<div><img src="@Model.CryptoImage"></div>
|
||||
</div>
|
||||
<input class="bp-input {'not-empty': addressValue.length > 0} ng-untouched ng-pristine ng-valid" id="refund-address-input" name="refundAddress" ngclass="{'not-empty': addressValue.length > 0}">
|
||||
</div>
|
||||
</bp-refund-address>
|
||||
<bp-loading-button i18n="" id="request-refund-button">
|
||||
<button class="action-button" style="margin-top: 15px;" type="submit">
|
||||
<span class="button-text" lcl="">Request Refund</span>
|
||||
<div class="loader-wrapper">
|
||||
<partial name="Checkout-Spinner" />
|
||||
</div>
|
||||
</button>
|
||||
</bp-loading-button>
|
||||
</div>
|
||||
<div class="refund-address-form__cancel">
|
||||
<span i18n="">Cancel</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="bp-view" id="refund-pending">
|
||||
<div class="status-block">
|
||||
<div class="pending-block" style="position: relative; padding-bottom: 1.6rem;">
|
||||
<img src="~/imlegacy/refund-pending.svg">
|
||||
<div class="pending-block__header" i18n="">Processing Refund</div>
|
||||
<span>
|
||||
<span class="pending-block__message" i18n="">The amount below will be refunded to you within 1-2 business days. </span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="manual-box" style="margin-bottom: 30px;">
|
||||
<div class="manual-box__amount amount-only">
|
||||
<div class="manual-box__amount__label label">
|
||||
<span class="initial-label"> </span>
|
||||
<span class="final-label" i18n="">Amount To Be Refunded</span>
|
||||
</div>
|
||||
<div class="manual-box__amount__value">{BTC Amount} BTC</div>
|
||||
</div>
|
||||
<div class="manual-box__address">
|
||||
<div class="flipper">
|
||||
<div class="back"></div>
|
||||
<div class="front">
|
||||
<div class="manual-box__address__arrow"></div>
|
||||
<div class="manual-box__address__label label" i18n="">Will Be Refunded To</div>
|
||||
<div class="manual-box__address__wrapper">
|
||||
<div class="manual-box__address__wrapper__logo">
|
||||
<img src="~/imlegacy/bitcoin-symbol.svg">
|
||||
</div>
|
||||
<div class="manual-box__address__wrapper__value">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bp-view expired" id="low-fee">
|
||||
<div class="expired__body">
|
||||
<div class="expired__header" i18n="" style="font-weight: 400; font-size: 22px;">Payment Confirming</div>
|
||||
<div class="expired__text" i18n="">This payment was made with a low <a href="https://bitcoin.org/en/glossary/transaction-fee">bitcoin miner fee</a>, which may prevent it from being accepted by the Bitcoin network.</div>
|
||||
<div class="expired__text" i18n="">This is an issue with the configuration of your bitcoin wallet.</div>
|
||||
<div class="expired__text" i18n="">
|
||||
If the transaction
|
||||
doesn't confirm, the funds will be spendable again in your wallet.
|
||||
Depending on the wallet, this may take 48-72 hours.
|
||||
</div>
|
||||
<low-fee-timeline>
|
||||
<div class="timeline">
|
||||
<div class="timeline__item">
|
||||
<div class="timeline__item__icon timeline__item__icon--complete">
|
||||
<img src="~/imlegacy/checkmark-small.svg">
|
||||
</div>
|
||||
<div class="timeline__item__name" i18n="">Transaction created</div>
|
||||
</div>
|
||||
<div class="timeline__item">
|
||||
<div class="timeline__item__icon timeline__item__icon--pending">
|
||||
<img src="~/imlegacy/pending.svg">
|
||||
</div>
|
||||
<div class="timeline__item__name">
|
||||
<span i18n="">Transaction confirming — funds have not yet moved</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeline__item">
|
||||
<div class="timeline__item__icon"></div>
|
||||
<div class="timeline__item__name" i18n="">Payment received by {{srvModel.storeName}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</low-fee-timeline>
|
||||
</div>
|
||||
<button class="action-button" style="margin-top: .75rem;">
|
||||
<bp-done-text>
|
||||
<span i18n="">Return to {{srvModel.storeName}}</span>
|
||||
</bp-done-text>
|
||||
</button>
|
||||
</div>
|
||||
<div class="bp-view" id="refund-complete">
|
||||
<div class="status-block">
|
||||
<div class="success-block" style="opacity: 1;">
|
||||
<div class="status-icon">
|
||||
<div class="status-icon__wrapper">
|
||||
<div class="inner-wrapper">
|
||||
<div class="status-icon__wrapper__icon">
|
||||
<img src="~/imlegacy/checkmark.svg">
|
||||
</div>
|
||||
<div class="status-icon__wrapper__outline" style="height: 117px; width: 117px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="success-message">
|
||||
<span>
|
||||
<span i18n="">Refund Complete</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="manual-box">
|
||||
<div class="manual-box__amount amount-only">
|
||||
<div class="manual-box__amount__label label">
|
||||
<span class="initial-label" i18n="">Overpaid By</span>
|
||||
<span class="final-label">
|
||||
<span i18n="">Amount Refunded</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="manual-box__amount__value">{BTC amount} BTC</div>
|
||||
</div>
|
||||
<div class="manual-box__address">
|
||||
<div class="flipper flipped-initially">
|
||||
<div class="back"></div>
|
||||
<div class="front">
|
||||
<div class="manual-box__address__arrow"></div>
|
||||
<div class="manual-box__address__label label" i18n="">Refunded To</div>
|
||||
<div class="manual-box__address__wrapper">
|
||||
<div class="manual-box__address__wrapper__logo">
|
||||
<img src="~/imlegacy/bitcoin-symbol.svg">
|
||||
</div>
|
||||
<div class="manual-box__address__wrapper__value">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="action-button finished" style="margin-top: 23px;">
|
||||
<bp-done-text>
|
||||
<span>{{$t("Return to StoreName", srvModel)}}</span>
|
||||
</bp-done-text>
|
||||
</button>
|
||||
</div>
|
||||
<div class="footer-button enter-different-address-button">
|
||||
<bp-done-text>
|
||||
<span>{{$t("Return to StoreName", srvModel)}}</span>
|
||||
</bp-done-text>
|
||||
</div>
|
||||
@* Obsolete? End *@
|
||||
</div>
|
||||
|
@ -64,7 +64,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: 10px; text-align: right;">
|
||||
<div style="margin-top: 10px; text-align: center;">
|
||||
@* Not working because of nsSeparator: false, keySeparator: false,
|
||||
{{$t("nested.lang")}} >>
|
||||
*@
|
||||
@ -92,7 +92,7 @@
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
<div style="margin-top: 10px; text-align: right;" class="form-text small text-muted">
|
||||
<div style="margin-top: 10px; text-align: center;" class="form-text small text-muted">
|
||||
<span>Powered by <a target="_blank" href="https://github.com/btcpayserver/btcpayserver">BTCPay Server</a></span>
|
||||
</div>
|
||||
</div>
|
||||
|
12
BTCPayServer/ZoneLimits.cs
Normal file
12
BTCPayServer/ZoneLimits.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public class ZoneLimits
|
||||
{
|
||||
public const string Login = "btcpaylogin";
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user