Compare commits
31 Commits
Author | SHA1 | Date | |
---|---|---|---|
786d129452 | |||
355989c278 | |||
af3dee95de | |||
70a6bd6a01 | |||
4afb0acc84 | |||
9afc143801 | |||
8e4943df65 | |||
9b3bd8343d | |||
6f07849e1d | |||
199db01eaf | |||
a3c46c8f67 | |||
66a68d6180 | |||
d41a5a65a2 | |||
d5cab938ee | |||
9dddfb65f0 | |||
6bd5976d90 | |||
b3385bf901 | |||
bba268b5e2 | |||
70c98b6901 | |||
2d3b7fea2e | |||
3bdf1c9a00 | |||
a52665ea80 | |||
3d943d49e6 | |||
6ca8ba9231 | |||
75d685ae6c | |||
7b2ef9aec2 | |||
efe666b284 | |||
ca8af5047a | |||
cdc0b0d628 | |||
87e28b70fd | |||
bca68986f3 |
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<NoWarn>NU1701,CA1816,CA1308,CA1810,CA2208</NoWarn>
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM microsoft/dotnet:2.0.6-sdk-2.1.101-stretch
|
||||
FROM microsoft/dotnet:2.1.300-rc1-sdk-alpine3.7
|
||||
WORKDIR /app
|
||||
# caches restore result by copying csproj file separately
|
||||
COPY BTCPayServer.Tests/BTCPayServer.Tests.csproj BTCPayServer.Tests/BTCPayServer.Tests.csproj
|
||||
|
@ -252,7 +252,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
entity.PaymentTolerance = 100;
|
||||
accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Coins(0), accounting.MinimumTotalDue);
|
||||
Assert.Equal(Money.Satoshis(1), accounting.MinimumTotalDue);
|
||||
|
||||
}
|
||||
|
||||
@ -276,7 +276,7 @@ namespace BTCPayServer.Tests
|
||||
var invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||
{
|
||||
Buyer = new Buyer() { email = "test@fwf.com" },
|
||||
Price = 5000.0,
|
||||
Price = 5000.0m,
|
||||
Currency = "USD",
|
||||
PosData = "posData",
|
||||
OrderId = "orderId",
|
||||
@ -308,7 +308,7 @@ namespace BTCPayServer.Tests
|
||||
var invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||
{
|
||||
Buyer = new Buyer() { email = "test@fwf.com" },
|
||||
Price = 5000.0,
|
||||
Price = 5000.0m,
|
||||
Currency = "USD",
|
||||
PosData = "posData",
|
||||
OrderId = "orderId",
|
||||
@ -440,7 +440,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
var invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||
{
|
||||
Price = 0.01,
|
||||
Price = 0.01m,
|
||||
Currency = "USD",
|
||||
PosData = "posData",
|
||||
OrderId = "orderId",
|
||||
@ -473,7 +473,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
var invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||
{
|
||||
Price = 0.01,
|
||||
Price = 0.01m,
|
||||
Currency = "USD",
|
||||
PosData = "posData",
|
||||
OrderId = "orderId",
|
||||
@ -501,7 +501,7 @@ namespace BTCPayServer.Tests
|
||||
await Task.Delay(TimeSpan.FromSeconds(RandomUtils.GetUInt32() % 5));
|
||||
var invoice = await user.BitPay.CreateInvoiceAsync(new Invoice()
|
||||
{
|
||||
Price = 0.01,
|
||||
Price = 0.01m,
|
||||
Currency = "USD",
|
||||
PosData = "posData",
|
||||
OrderId = "orderId",
|
||||
@ -554,7 +554,7 @@ namespace BTCPayServer.Tests
|
||||
acc.RegisterDerivationScheme("BTC");
|
||||
var invoice = acc.BitPay.CreateInvoice(new Invoice()
|
||||
{
|
||||
Price = 5.0,
|
||||
Price = 5.0m,
|
||||
Currency = "USD",
|
||||
PosData = "posData",
|
||||
OrderId = "orderId",
|
||||
@ -656,7 +656,7 @@ namespace BTCPayServer.Tests
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
var invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||
{
|
||||
Price = 5000.0,
|
||||
Price = 5000.0m,
|
||||
Currency = "USD"
|
||||
}, Facade.Merchant);
|
||||
var payment1 = invoice.BtcDue + Money.Coins(0.0001m);
|
||||
@ -756,7 +756,7 @@ namespace BTCPayServer.Tests
|
||||
message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Encoders.Base64.EncodeData(Encoders.ASCII.DecodeData(apiKey)));
|
||||
var invoice = new Invoice()
|
||||
{
|
||||
Price = 5000.0,
|
||||
Price = 5000.0m,
|
||||
Currency = "USD"
|
||||
};
|
||||
message.Content = new StringContent(JsonConvert.SerializeObject(invoice), Encoding.UTF8, "application/json");
|
||||
@ -798,7 +798,7 @@ namespace BTCPayServer.Tests
|
||||
storeController.Rates(vm).Wait();
|
||||
var invoice2 = user.BitPay.CreateInvoice(new Invoice()
|
||||
{
|
||||
Price = 5000.0,
|
||||
Price = 5000.0m,
|
||||
Currency = "USD",
|
||||
PosData = "posData",
|
||||
OrderId = "orderId",
|
||||
@ -822,7 +822,7 @@ namespace BTCPayServer.Tests
|
||||
// First we try payment with a merchant having only BTC
|
||||
var invoice1 = user.BitPay.CreateInvoice(new Invoice()
|
||||
{
|
||||
Price = 5000.0,
|
||||
Price = 5000.0m,
|
||||
Currency = "USD",
|
||||
PosData = "posData",
|
||||
OrderId = "orderId",
|
||||
@ -840,7 +840,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
var invoice2 = user.BitPay.CreateInvoice(new Invoice()
|
||||
{
|
||||
Price = 5000.0,
|
||||
Price = 5000.0m,
|
||||
Currency = "USD",
|
||||
PosData = "posData",
|
||||
OrderId = "orderId",
|
||||
@ -896,7 +896,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
// Despite it is called BitcoinAddress it should be LTC because BTC is not available
|
||||
Assert.Null(invoice.BitcoinAddress);
|
||||
Assert.NotEqual(1.0, invoice.Rate);
|
||||
Assert.NotEqual(1.0m, invoice.Rate);
|
||||
Assert.NotEqual(invoice.BtcDue, invoice.CryptoInfo[0].Due); // Should be BTC rate
|
||||
cashCow.SendToAddress(invoiceAddress, invoice.CryptoInfo[0].Due);
|
||||
|
||||
@ -983,7 +983,7 @@ namespace BTCPayServer.Tests
|
||||
// First we try payment with a merchant having only BTC
|
||||
var invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||
{
|
||||
Price = 5000.0,
|
||||
Price = 5000.0m,
|
||||
Currency = "USD",
|
||||
PosData = "posData",
|
||||
OrderId = "orderId",
|
||||
@ -1015,7 +1015,7 @@ namespace BTCPayServer.Tests
|
||||
user.RegisterDerivationScheme("LTC");
|
||||
invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||
{
|
||||
Price = 5000.0,
|
||||
Price = 5000.0m,
|
||||
Currency = "USD",
|
||||
PosData = "posData",
|
||||
OrderId = "orderId",
|
||||
@ -1137,7 +1137,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
var invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||
{
|
||||
Price = 1.5,
|
||||
Price = 1.5m,
|
||||
Currency = "USD",
|
||||
PosData = "posData",
|
||||
OrderId = "orderId",
|
||||
@ -1150,7 +1150,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||
{
|
||||
Price = 5.5,
|
||||
Price = 5.5m,
|
||||
Currency = "USD",
|
||||
PosData = "posData",
|
||||
OrderId = "orderId",
|
||||
@ -1199,7 +1199,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal("$5.00", vmview.Items[0].Price.Formatted);
|
||||
Assert.IsType<RedirectResult>(apps.ViewPointOfSale(appId, 0, "orange").Result);
|
||||
var invoice = user.BitPay.GetInvoices().First();
|
||||
Assert.Equal(10.00, invoice.Price);
|
||||
Assert.Equal(10.00m, invoice.Price);
|
||||
Assert.Equal("CAD", invoice.Currency);
|
||||
Assert.Equal("orange", invoice.ItemDesc);
|
||||
}
|
||||
@ -1250,7 +1250,7 @@ namespace BTCPayServer.Tests
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
var invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||
{
|
||||
Price = 5000.0,
|
||||
Price = 5000.0m,
|
||||
Currency = "USD",
|
||||
PosData = "posData",
|
||||
OrderId = "orderId",
|
||||
@ -1355,12 +1355,12 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
var localInvoice = user.BitPay.GetInvoice(invoice.Id, Facade.Merchant);
|
||||
Assert.Equal("complete", localInvoice.Status);
|
||||
Assert.NotEqual(0.0, localInvoice.Rate);
|
||||
Assert.NotEqual(0.0m, localInvoice.Rate);
|
||||
});
|
||||
|
||||
invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||
{
|
||||
Price = 5000.0,
|
||||
Price = 5000.0m,
|
||||
Currency = "USD",
|
||||
PosData = "posData",
|
||||
OrderId = "orderId",
|
||||
@ -1454,7 +1454,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
private static BTCPayRateProviderFactory CreateBTCPayRateFactory(BTCPayNetworkProvider provider)
|
||||
{
|
||||
return new BTCPayRateProviderFactory(new MemoryCacheOptions() { ExpirationScanFrequency = TimeSpan.FromSeconds(1.0) }, provider, new CoinAverageSettings());
|
||||
return new BTCPayRateProviderFactory(new MemoryCacheOptions() { ExpirationScanFrequency = TimeSpan.FromSeconds(1.0) }, provider, null, new CoinAverageSettings());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -46,7 +46,7 @@ services:
|
||||
- lightning-charged
|
||||
|
||||
nbxplorer:
|
||||
image: nicolasdorier/nbxplorer:1.0.2.3
|
||||
image: nicolasdorier/nbxplorer:1.0.2.6
|
||||
ports:
|
||||
- "32838:32838"
|
||||
expose:
|
||||
@ -89,14 +89,15 @@ services:
|
||||
- "bitcoin_datadir:/data"
|
||||
|
||||
customer_lightningd:
|
||||
image: nicolasdorier/clightning:0.0.0.12-dev
|
||||
image: nicolasdorier/clightning:0.0.0.14-dev
|
||||
environment:
|
||||
EXPOSE_TCP: "true"
|
||||
LIGHTNINGD_OPT: |
|
||||
bitcoin-datadir=/etc/bitcoin
|
||||
bitcoin-rpcconnect=bitcoind
|
||||
network=regtest
|
||||
ipaddr=customer_lightningd
|
||||
bind-addr=0.0.0.0
|
||||
announce-addr=customer_lightningd
|
||||
log-level=debug
|
||||
dev-broadcast-interval=1000
|
||||
ports:
|
||||
@ -130,13 +131,14 @@ services:
|
||||
- merchant_lightningd
|
||||
|
||||
merchant_lightningd:
|
||||
image: nicolasdorier/clightning:0.0.0.11-dev
|
||||
image: nicolasdorier/clightning:0.0.0.14-dev
|
||||
environment:
|
||||
EXPOSE_TCP: "true"
|
||||
LIGHTNINGD_OPT: |
|
||||
bitcoin-datadir=/etc/bitcoin
|
||||
bitcoin-rpcconnect=bitcoind
|
||||
ipaddr=merchant_lightningd
|
||||
bind-addr=0.0.0.0
|
||||
announce-addr=merchant_lightningd
|
||||
network=regtest
|
||||
log-level=debug
|
||||
dev-broadcast-interval=1000
|
||||
|
29
BTCPayServer/BTCPayNetworkProvider.BitcoinGold.cs
Normal file
29
BTCPayServer/BTCPayNetworkProvider.BitcoinGold.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitBitcoinGold()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("BTG");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
BlockExplorerLink = NetworkType == NetworkType.Mainnet ? "https://explorer.bitcoingold.org/insight/tx/{0}/" : "https://test-explorer.bitcoingold.org/insight/tx/{0}",
|
||||
NBitcoinNetwork = nbxplorerNetwork.NBitcoinNetwork,
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
UriScheme = "bitcoingold",
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"BTG_X = BTG_BTC * BTC_X",
|
||||
"BTG_BTC = bitfinex(BTG_BTC)",
|
||||
},
|
||||
CryptoImagePath = "imlegacy/btg-symbol.svg",
|
||||
LightningImagePath = "imlegacy/btg-symbol.svg",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == NetworkType.Mainnet ? new KeyPath("156'") : new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -48,6 +48,7 @@ namespace BTCPayServer
|
||||
InitBitcoin();
|
||||
InitLitecoin();
|
||||
InitDogecoin();
|
||||
InitBitcoinGold();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -1,8 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<Version>1.0.2.7</Version>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<Version>1.0.2.16</Version>
|
||||
<NoWarn>NU1701,CA1816,CA1308,CA1810,CA2208</NoWarn>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
@ -30,38 +30,35 @@
|
||||
<EmbeddedResource Include="Currencies.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BuildBundlerMinifier" Version="2.6.375" />
|
||||
<PackageReference Include="BuildBundlerMinifier" Version="2.7.385" />
|
||||
<PackageReference Include="DigitalRuby.ExchangeSharp" Version="0.4.1" />
|
||||
<PackageReference Include="Hangfire" Version="1.6.19" />
|
||||
<PackageReference Include="Hangfire.MemoryStorage" Version="1.5.2" />
|
||||
<PackageReference Include="Hangfire.PostgreSql" Version="1.4.8.1" />
|
||||
<PackageReference Include="Hangfire.PostgreSql" Version="1.4.8.2" />
|
||||
<PackageReference Include="LedgerWallet" Version="1.0.1.36" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="2.6.1" />
|
||||
<PackageReference Include="Meziantou.AspNetCore.BundleTagHelpers" Version="1.0.1" />
|
||||
<PackageReference Include="Meziantou.AspNetCore.BundleTagHelpers" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.0-rc1-final" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.NetCore.Analyzers" Version="2.6.0" />
|
||||
<PackageReference Include="NBitcoin" Version="4.1.1.4" />
|
||||
<PackageReference Include="NBitpayClient" Version="1.0.0.18" />
|
||||
<PackageReference Include="NBitcoin" Version="4.1.1.7" />
|
||||
<PackageReference Include="NBitpayClient" Version="1.0.0.20" />
|
||||
<PackageReference Include="DBreeze" Version="1.87.0" />
|
||||
<PackageReference Include="NBXplorer.Client" Version="1.0.2.4" />
|
||||
<PackageReference Include="NBXplorer.Client" Version="1.0.2.8" />
|
||||
<PackageReference Include="NicolasDorier.CommandLine" Version="1.0.0.1" />
|
||||
<PackageReference Include="NicolasDorier.CommandLine.Configuration" Version="1.0.0.2" />
|
||||
<PackageReference Include="NicolasDorier.StandardConfiguration" Version="1.0.0.13" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.0.1" />
|
||||
<PackageReference Include="System.ValueTuple" Version="4.4.0" />
|
||||
<PackageReference Include="System.Xml.XmlSerializer" Version="4.0.11" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.1-rc1" />
|
||||
<PackageReference Include="System.Xml.XmlSerializer" Version="4.3.0" />
|
||||
<PackageReference Include="Text.Analyzers" Version="2.6.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.2" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0-rc1-final" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version=" 2.1.0-rc1-final" PrivateAssets="All" />
|
||||
<PackageReference Include="YamlDotNet" Version="4.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
|
||||
<DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="2.0.0" />
|
||||
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -162,7 +162,8 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
[HttpPost]
|
||||
[Route("{appId}/pos")]
|
||||
public async Task<IActionResult> ViewPointOfSale(string appId, double amount, string choiceKey)
|
||||
[IgnoreAntiforgeryToken]
|
||||
public async Task<IActionResult> ViewPointOfSale(string appId, decimal amount, string choiceKey)
|
||||
{
|
||||
var app = await GetApp(appId, AppType.PointOfSale);
|
||||
if (string.IsNullOrEmpty(choiceKey) && amount <= 0)
|
||||
@ -177,7 +178,7 @@ namespace BTCPayServer.Controllers
|
||||
return RedirectToAction(nameof(ViewPointOfSale), new { appId = appId });
|
||||
}
|
||||
string title = null;
|
||||
double price = 0.0;
|
||||
var price = 0.0m;
|
||||
if (!string.IsNullOrEmpty(choiceKey))
|
||||
{
|
||||
var choices = Parse(settings.Template, settings.Currency);
|
||||
@ -185,7 +186,7 @@ namespace BTCPayServer.Controllers
|
||||
if (choice == null)
|
||||
return NotFound();
|
||||
title = choice.Title;
|
||||
price = (double)choice.Price.Value;
|
||||
price = choice.Price.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -176,24 +176,19 @@ namespace BTCPayServer.Controllers
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
{
|
||||
return await ctx.UserStore
|
||||
.Where(us => us.ApplicationUserId == userId)
|
||||
.Select(us => new
|
||||
{
|
||||
IsOwner = us.Role == StoreRoles.Owner,
|
||||
StoreId = us.StoreDataId,
|
||||
StoreName = us.StoreData.StoreName,
|
||||
Apps = us.StoreData.Apps
|
||||
})
|
||||
.SelectMany(us => us.Apps.Select(app => new ListAppsViewModel.ListAppViewModel()
|
||||
{
|
||||
IsOwner = us.IsOwner,
|
||||
AppName = app.Name,
|
||||
AppType = app.AppType,
|
||||
Id = app.Id,
|
||||
StoreId = us.StoreId,
|
||||
StoreName = us.StoreName
|
||||
}))
|
||||
.ToArrayAsync();
|
||||
.Where(us => us.ApplicationUserId == userId)
|
||||
.Join(ctx.Apps, us => us.StoreDataId, app => app.StoreDataId,
|
||||
(us, app) =>
|
||||
new ListAppsViewModel.ListAppViewModel()
|
||||
{
|
||||
IsOwner = us.Role == StoreRoles.Owner,
|
||||
StoreId = us.StoreDataId,
|
||||
StoreName = us.StoreData.StoreName,
|
||||
AppName = app.Name,
|
||||
AppType = app.AppType,
|
||||
Id = app.Id
|
||||
})
|
||||
.ToArrayAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,24 +14,5 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
return View("Home");
|
||||
}
|
||||
|
||||
public IActionResult About()
|
||||
{
|
||||
ViewData["Message"] = "Your application description page.";
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
public IActionResult Contact()
|
||||
{
|
||||
ViewData["Message"] = "Your contact page.";
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
public IActionResult Error()
|
||||
{
|
||||
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,11 +13,14 @@ using BTCPayServer.Data;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using BTCPayServer.Security;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
[EnableCors("BitpayAPI")]
|
||||
[BitpayAPIConstraint]
|
||||
[Authorize(Policies.CanUseStore.Key)]
|
||||
public class InvoiceControllerAPI : Controller
|
||||
{
|
||||
private InvoiceController _InvoiceController;
|
||||
@ -43,9 +46,10 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
[HttpGet]
|
||||
[Route("invoices/{id}")]
|
||||
[AllowAnonymous]
|
||||
public async Task<DataWrapper<InvoiceResponse>> GetInvoice(string id, string token)
|
||||
{
|
||||
var invoice = await _InvoiceRepository.GetInvoice(HttpContext.GetStoreData().Id, id);
|
||||
var invoice = await _InvoiceRepository.GetInvoice(null, id);
|
||||
if (invoice == null)
|
||||
throw new BitpayHttpException(404, "Object not found");
|
||||
var resp = invoice.EntityToDTO(_NetworkProvider);
|
||||
|
@ -51,7 +51,10 @@ namespace BTCPayServer.Controllers
|
||||
StoreLink = Url.Action(nameof(StoresController.UpdateStore), "Stores", new { storeId = store.Id }),
|
||||
Id = invoice.Id,
|
||||
Status = invoice.Status,
|
||||
TransactionSpeed = invoice.SpeedPolicy == SpeedPolicy.HighSpeed ? "high" : invoice.SpeedPolicy == SpeedPolicy.MediumSpeed ? "medium" : "low",
|
||||
TransactionSpeed = invoice.SpeedPolicy == SpeedPolicy.HighSpeed ? "high" :
|
||||
invoice.SpeedPolicy == SpeedPolicy.MediumSpeed ? "medium" :
|
||||
invoice.SpeedPolicy == SpeedPolicy.LowMediumSpeed ? "low-medium" :
|
||||
"low",
|
||||
RefundEmail = invoice.RefundMail,
|
||||
CreatedDate = invoice.InvoiceTime,
|
||||
ExpirationDate = invoice.ExpirationTime,
|
||||
@ -201,6 +204,12 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
var paymentMethodId = PaymentMethodId.Parse(paymentMethodIdStr);
|
||||
var network = _NetworkProvider.GetNetwork(paymentMethodId.CryptoCode);
|
||||
if (network == null && isDefaultCrypto)
|
||||
{
|
||||
network = _NetworkProvider.GetAll().FirstOrDefault();
|
||||
paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike);
|
||||
paymentMethodIdStr = paymentMethodId.ToString();
|
||||
}
|
||||
if (invoice == null || network == null)
|
||||
return null;
|
||||
if (!invoice.Support(paymentMethodId))
|
||||
@ -210,6 +219,7 @@ namespace BTCPayServer.Controllers
|
||||
var paymentMethodTemp = invoice.GetPaymentMethods(_NetworkProvider).First();
|
||||
network = paymentMethodTemp.Network;
|
||||
paymentMethodId = paymentMethodTemp.GetId();
|
||||
paymentMethodIdStr = paymentMethodId.ToString();
|
||||
}
|
||||
|
||||
var paymentMethod = invoice.GetPaymentMethod(paymentMethodId, _NetworkProvider);
|
||||
|
@ -277,6 +277,7 @@ namespace BTCPayServer.Controllers
|
||||
return defaultPolicy;
|
||||
var mappings = new Dictionary<string, SpeedPolicy>();
|
||||
mappings.Add("low", SpeedPolicy.LowSpeed);
|
||||
mappings.Add("low-medium", SpeedPolicy.LowMediumSpeed);
|
||||
mappings.Add("medium", SpeedPolicy.MediumSpeed);
|
||||
mappings.Add("high", SpeedPolicy.HighSpeed);
|
||||
if (!mappings.TryGetValue(transactionSpeed, out SpeedPolicy policy))
|
||||
|
@ -36,11 +36,12 @@ namespace BTCPayServer.Controllers
|
||||
[BitpayAPIConstraint]
|
||||
public async Task<IActionResult> GetRates(string currencyPairs, string storeId)
|
||||
{
|
||||
storeId = storeId ?? this.HttpContext.GetStoreData()?.Id;
|
||||
var result = await GetRates2(currencyPairs, storeId);
|
||||
var rates = (result as JsonResult)?.Value as NBitpayClient.Rate[];
|
||||
var rates = (result as JsonResult)?.Value as Rate[];
|
||||
if (rates == null)
|
||||
return result;
|
||||
return Json(new DataWrapper<NBitpayClient.Rate[]>(rates));
|
||||
return Json(new DataWrapper<Rate[]>(rates));
|
||||
}
|
||||
|
||||
[Route("api/rates")]
|
||||
@ -54,8 +55,9 @@ namespace BTCPayServer.Controllers
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
var store = await _StoreRepo.FindStore(storeId);
|
||||
var store = this.HttpContext.GetStoreData();
|
||||
if(store == null || store.Id != storeId)
|
||||
store = await _StoreRepo.FindStore(storeId);
|
||||
if (store == null)
|
||||
{
|
||||
var result = Json(new BitpayErrorsModel() { Error = "Store not found" });
|
||||
@ -86,6 +88,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
CryptoCode = r.Pair.Left,
|
||||
Code = r.Pair.Right,
|
||||
CurrencyPair = r.Pair.ToString(),
|
||||
Name = _CurrencyNameTable.GetCurrencyData(r.Pair.Right)?.Name,
|
||||
Value = r.Value.Value
|
||||
}).Where(n => n.Name != null).ToArray());
|
||||
@ -106,6 +109,14 @@ namespace BTCPayServer.Controllers
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "currencyPair")]
|
||||
public string CurrencyPair
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "code")]
|
||||
public string Code
|
||||
{
|
||||
|
@ -10,6 +10,7 @@ using BTCPayServer.Data;
|
||||
using BTCPayServer.Models.StoreViewModels;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Services;
|
||||
using LedgerWallet;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NBitcoin;
|
||||
using NBXplorer.DerivationStrategy;
|
||||
@ -264,7 +265,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
var strategy = GetDirectDerivationStrategy(store, network);
|
||||
var strategyBase = GetDerivationStrategy(store, network);
|
||||
if (strategy == null || !await hw.SupportDerivation(network, strategy))
|
||||
if (strategy == null || await hw.GetKeyPath(network, strategy) == null)
|
||||
{
|
||||
throw new Exception($"This store is not configured to use this ledger");
|
||||
}
|
||||
@ -286,11 +287,76 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
var unspentCoins = await wallet.GetUnspentCoins(strategyBase);
|
||||
var changeAddress = await change;
|
||||
var transaction = await hw.SendToAddress(strategy, unspentCoins, network,
|
||||
new[] { (destinationAddress as IDestination, amountBTC, subsctractFeesValue) },
|
||||
feeRateValue,
|
||||
changeAddress.Item1,
|
||||
changeAddress.Item2, summary.Status.BitcoinStatus.MinRelayTxFee);
|
||||
var send = new[] { (
|
||||
destination: destinationAddress as IDestination,
|
||||
amount: amountBTC,
|
||||
substractFees: subsctractFeesValue) };
|
||||
|
||||
foreach (var element in send)
|
||||
{
|
||||
if (element.destination == null)
|
||||
throw new ArgumentNullException(nameof(element.destination));
|
||||
if (element.amount == null)
|
||||
throw new ArgumentNullException(nameof(element.amount));
|
||||
if (element.amount <= Money.Zero)
|
||||
throw new ArgumentOutOfRangeException(nameof(element.amount), "The amount should be above zero");
|
||||
}
|
||||
|
||||
var foundKeyPath = await hw.GetKeyPath(network, strategy);
|
||||
if (foundKeyPath == null)
|
||||
{
|
||||
throw new HardwareWalletException($"This store is not configured to use this ledger");
|
||||
}
|
||||
|
||||
TransactionBuilder builder = new TransactionBuilder();
|
||||
builder.StandardTransactionPolicy.MinRelayTxFee = summary.Status.BitcoinStatus.MinRelayTxFee;
|
||||
builder.SetConsensusFactory(network.NBitcoinNetwork);
|
||||
builder.AddCoins(unspentCoins.Select(c => c.Coin).ToArray());
|
||||
|
||||
foreach (var element in send)
|
||||
{
|
||||
builder.Send(element.destination, element.amount);
|
||||
if (element.substractFees)
|
||||
builder.SubtractFees();
|
||||
}
|
||||
builder.SetChange(changeAddress.Item1);
|
||||
builder.SendEstimatedFees(feeRateValue);
|
||||
builder.Shuffle();
|
||||
var unsigned = builder.BuildTransaction(false);
|
||||
|
||||
var keypaths = new Dictionary<Script, KeyPath>();
|
||||
foreach (var c in unspentCoins)
|
||||
{
|
||||
keypaths.TryAdd(c.Coin.ScriptPubKey, c.KeyPath);
|
||||
}
|
||||
|
||||
var hasChange = unsigned.Outputs.Count == 2;
|
||||
var usedCoins = builder.FindSpentCoins(unsigned);
|
||||
|
||||
Dictionary<uint256, Transaction> parentTransactions = new Dictionary<uint256, Transaction>();
|
||||
|
||||
if(!strategy.Segwit)
|
||||
{
|
||||
var parentHashes = usedCoins.Select(c => c.Outpoint.Hash).ToHashSet();
|
||||
var explorer = _ExplorerProvider.GetExplorerClient(network);
|
||||
var getTransactionAsyncs = parentHashes.Select(h => (Op: explorer.GetTransactionAsync(h), Hash: h)).ToList();
|
||||
foreach(var getTransactionAsync in getTransactionAsyncs)
|
||||
{
|
||||
var tx = (await getTransactionAsync.Op);
|
||||
if(tx == null)
|
||||
throw new Exception($"Parent transaction {getTransactionAsync.Hash} not found");
|
||||
parentTransactions.Add(tx.Transaction.GetHash(), tx.Transaction);
|
||||
}
|
||||
}
|
||||
|
||||
var transaction = await hw.SignTransactionAsync(usedCoins.Select(c => new SignatureRequest
|
||||
{
|
||||
InputTransaction = parentTransactions.TryGet(c.Outpoint.Hash),
|
||||
InputCoin = c,
|
||||
KeyPath = foundKeyPath.Derive(keypaths[c.TxOut.ScriptPubKey]),
|
||||
PubKey = strategy.Root.Derive(keypaths[c.TxOut.ScriptPubKey]).PubKey
|
||||
}).ToArray(), unsigned, hasChange ? foundKeyPath.Derive(changeAddress.Item2) : null);
|
||||
|
||||
try
|
||||
{
|
||||
var broadcastResult = await wallet.BroadcastTransactionsAsync(new List<Transaction>() { transaction });
|
||||
@ -336,8 +402,6 @@ namespace BTCPayServer.Controllers
|
||||
var directStrategy = strategy as DirectDerivationStrategy;
|
||||
if (directStrategy == null)
|
||||
directStrategy = (strategy as P2SHDerivationStrategy).Inner as DirectDerivationStrategy;
|
||||
if (!directStrategy.Segwit)
|
||||
return null;
|
||||
return directStrategy;
|
||||
}
|
||||
|
||||
|
@ -646,11 +646,11 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
var stores = await _Repo.GetStoresByUserId(userId);
|
||||
model.Stores = new SelectList(stores.Where(s => s.HasClaim(Policies.CanModifyStoreSettings.Key)), nameof(StoreData.Id), nameof(StoreData.StoreName), storeId);
|
||||
}
|
||||
if (model.Stores.Count() == 0)
|
||||
{
|
||||
StatusMessage = "Error: You need to be owner of at least one store before pairing";
|
||||
return RedirectToAction(nameof(UserStoresController.ListStores), "UserStores");
|
||||
if (model.Stores.Count() == 0)
|
||||
{
|
||||
StatusMessage = "Error: You need to be owner of at least one store before pairing";
|
||||
return RedirectToAction(nameof(UserStoresController.ListStores), "UserStores");
|
||||
}
|
||||
}
|
||||
return View(model);
|
||||
}
|
||||
|
@ -41,10 +41,12 @@ namespace BTCPayServer.Data
|
||||
|
||||
public void ConfigureHangfireBuilder(IGlobalConfiguration builder)
|
||||
{
|
||||
if (_Type == DatabaseType.Sqlite)
|
||||
builder.UseMemoryStorage(); //Sql provider does not support multiple workers
|
||||
else if (_Type == DatabaseType.Postgres)
|
||||
builder.UsePostgreSqlStorage(_ConnectionString);
|
||||
builder.UseMemoryStorage();
|
||||
//We always use memory storage because of incompatibilities with the latest postgres in 2.1
|
||||
//if (_Type == DatabaseType.Sqlite)
|
||||
// builder.UseMemoryStorage(); //Sqlite provider does not support multiple workers
|
||||
//else if (_Type == DatabaseType.Postgres)
|
||||
// builder.UsePostgreSqlStorage(_ConnectionString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,21 +6,6 @@ using BTCPayServer.HostedServices;
|
||||
|
||||
namespace BTCPayServer.Events
|
||||
{
|
||||
public class NBXplorerErrorEvent
|
||||
{
|
||||
public NBXplorerErrorEvent(BTCPayNetwork network, string errorMessage)
|
||||
{
|
||||
Message = errorMessage;
|
||||
Network = network;
|
||||
}
|
||||
public string Message { get; set; }
|
||||
public BTCPayNetwork Network { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Network.CryptoCode}: NBXplorer error `{Message}`";
|
||||
}
|
||||
}
|
||||
public class NBXplorerStateChangedEvent
|
||||
{
|
||||
public NBXplorerStateChangedEvent(BTCPayNetwork network, NBXplorerState old, NBXplorerState newState)
|
||||
|
@ -41,6 +41,13 @@ namespace BTCPayServer.HostedServices
|
||||
{
|
||||
get { return _creativeStartUri; }
|
||||
}
|
||||
|
||||
public bool ShowRegister { get; set; }
|
||||
|
||||
internal void Update(PoliciesSettings data)
|
||||
{
|
||||
ShowRegister = !data.LockSubscription;
|
||||
}
|
||||
}
|
||||
|
||||
public class CssThemeManagerHostedService : BaseAsyncService
|
||||
@ -58,10 +65,19 @@ namespace BTCPayServer.HostedServices
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
CreateLoopTask(ListenForThemeChanges)
|
||||
CreateLoopTask(ListenForThemeChanges),
|
||||
CreateLoopTask(ListenForPoliciesChanges),
|
||||
};
|
||||
}
|
||||
|
||||
async Task ListenForPoliciesChanges()
|
||||
{
|
||||
await new SynchronizationContextRemover();
|
||||
var data = (await _SettingsRepository.GetSettingAsync<PoliciesSettings>()) ?? new PoliciesSettings();
|
||||
_CssThemeManager.Update(data);
|
||||
await _SettingsRepository.WaitSettingsChanged<PoliciesSettings>(Cancellation);
|
||||
}
|
||||
|
||||
async Task ListenForThemeChanges()
|
||||
{
|
||||
await new SynchronizationContextRemover();
|
||||
|
@ -207,7 +207,7 @@ namespace BTCPayServer.HostedServices
|
||||
if (btcCryptoInfo != null)
|
||||
{
|
||||
#pragma warning disable CS0618
|
||||
notification.Rate = (double)dto.Rate;
|
||||
notification.Rate = dto.Rate;
|
||||
notification.Url = dto.Url;
|
||||
notification.BTCDue = dto.BTCDue;
|
||||
notification.BTCPaid = dto.BTCPaid;
|
||||
|
@ -291,7 +291,7 @@ namespace BTCPayServer.HostedServices
|
||||
if (updateContext.Dirty)
|
||||
{
|
||||
await _InvoiceRepository.UpdateInvoiceStatus(invoice.Id, invoice.Status, invoice.ExceptionStatus);
|
||||
updateContext.Events.Add(new InvoiceDataChangedEvent(invoice));
|
||||
updateContext.Events.Insert(0, new InvoiceDataChangedEvent(invoice));
|
||||
}
|
||||
|
||||
foreach (var evt in updateContext.Events)
|
||||
|
@ -192,7 +192,7 @@ namespace BTCPayServer.HostedServices
|
||||
{
|
||||
State = NBXplorerState.NotConnected;
|
||||
status = null;
|
||||
_Aggregator.Publish(new NBXplorerErrorEvent(_Network, error));
|
||||
Logs.PayServer.LogError($"{_Network.CryptoCode}: NBXplorer error `{error}`");
|
||||
}
|
||||
|
||||
_Dashboard.Publish(_Network, State, status, error);
|
||||
|
@ -12,7 +12,6 @@ using NBitcoin;
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.IO;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using NBXplorer;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
@ -79,12 +79,13 @@ namespace BTCPayServer.Hosting
|
||||
if (!httpContext.Request.Path.HasValue)
|
||||
return false;
|
||||
|
||||
var isJson = (httpContext.Request.ContentType ?? string.Empty).StartsWith("application/json", StringComparison.OrdinalIgnoreCase);
|
||||
var path = httpContext.Request.Path.Value;
|
||||
if (
|
||||
bitpayAuth &&
|
||||
path == "/invoices" &&
|
||||
httpContext.Request.Method == "POST" &&
|
||||
(httpContext.Request.ContentType ?? string.Empty).StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
|
||||
isJson)
|
||||
return true;
|
||||
|
||||
if (
|
||||
@ -94,9 +95,9 @@ namespace BTCPayServer.Hosting
|
||||
return true;
|
||||
|
||||
if (
|
||||
bitpayAuth &&
|
||||
path.StartsWith("/invoices/", StringComparison.OrdinalIgnoreCase) &&
|
||||
httpContext.Request.Method == "GET")
|
||||
httpContext.Request.Method == "GET" &&
|
||||
(isJson || httpContext.Request.Query.ContainsKey("token")))
|
||||
return true;
|
||||
|
||||
if (path.Equals("/rates", StringComparison.OrdinalIgnoreCase) &&
|
||||
|
@ -35,7 +35,6 @@ using Hangfire.Annotations;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.ApplicationInsights.AspNetCore.Extensions;
|
||||
using Microsoft.AspNetCore.Mvc.Cors.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using System.Net;
|
||||
@ -104,10 +103,6 @@ namespace BTCPayServer.Hosting
|
||||
b.AllowAnyMethod().AllowAnyHeader().AllowAnyOrigin();
|
||||
});
|
||||
});
|
||||
services.Configure<IOptions<ApplicationInsightsServiceOptions>>(o =>
|
||||
{
|
||||
o.Value.DeveloperMode = _Env.IsDevelopment();
|
||||
});
|
||||
|
||||
// Needed to debug U2F for ledger support
|
||||
//services.Configure<KestrelServerOptions>(kestrel =>
|
||||
@ -146,12 +141,8 @@ namespace BTCPayServer.Hosting
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
app.UseBrowserLink();
|
||||
}
|
||||
|
||||
//App insight do not that by itself...
|
||||
loggerFactory.AddApplicationInsights(prov, LogLevel.Information);
|
||||
|
||||
app.UsePayServer();
|
||||
app.UseStaticFiles();
|
||||
app.UseAuthentication();
|
||||
|
@ -1,13 +1,14 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Console;
|
||||
using Microsoft.Extensions.Logging.Console.Internal;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions.Internal;
|
||||
using Microsoft.Extensions.Logging.Console;
|
||||
using Microsoft.Extensions.Logging.Console.Internal;
|
||||
|
||||
namespace BTCPayServer.Logging
|
||||
{
|
||||
@ -20,19 +21,18 @@ namespace BTCPayServer.Logging
|
||||
}
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
{
|
||||
return new CustomConsoleLogger(categoryName, (a, b) => true, false, _Processor);
|
||||
return new CustomerConsoleLogger(categoryName, (a, b) => true, null, _Processor);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A variant of ASP.NET Core ConsoleLogger which does not make new line for the category
|
||||
/// </summary>
|
||||
public class CustomConsoleLogger : ILogger
|
||||
public class CustomerConsoleLogger : ILogger
|
||||
{
|
||||
private static readonly string _loglevelPadding = ": ";
|
||||
private static readonly string _messagePadding;
|
||||
@ -47,19 +47,33 @@ namespace BTCPayServer.Logging
|
||||
[ThreadStatic]
|
||||
private static StringBuilder _logBuilder;
|
||||
|
||||
static CustomConsoleLogger()
|
||||
static CustomerConsoleLogger()
|
||||
{
|
||||
var logLevelString = GetLogLevelString(LogLevel.Information);
|
||||
_messagePadding = new string(' ', logLevelString.Length + _loglevelPadding.Length);
|
||||
_newLineWithMessagePadding = Environment.NewLine + _messagePadding;
|
||||
}
|
||||
|
||||
public CustomConsoleLogger(string name, Func<string, LogLevel, bool> filter, bool includeScopes, ConsoleLoggerProcessor loggerProcessor)
|
||||
public CustomerConsoleLogger(string name, Func<string, LogLevel, bool> filter, bool includeScopes)
|
||||
: this(name, filter, includeScopes ? new LoggerExternalScopeProvider() : null, new ConsoleLoggerProcessor())
|
||||
{
|
||||
Name = name ?? throw new ArgumentNullException(nameof(name));
|
||||
Filter = filter ?? ((category, logLevel) => true);
|
||||
IncludeScopes = includeScopes;
|
||||
}
|
||||
|
||||
internal CustomerConsoleLogger(string name, Func<string, LogLevel, bool> filter, IExternalScopeProvider scopeProvider)
|
||||
: this(name, filter, scopeProvider, new ConsoleLoggerProcessor())
|
||||
{
|
||||
}
|
||||
|
||||
internal CustomerConsoleLogger(string name, Func<string, LogLevel, bool> filter, IExternalScopeProvider scopeProvider, ConsoleLoggerProcessor loggerProcessor)
|
||||
{
|
||||
if (name == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
Name = name;
|
||||
Filter = filter ?? ((category, logLevel) => true);
|
||||
ScopeProvider = scopeProvider;
|
||||
_queueProcessor = loggerProcessor;
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
@ -80,7 +94,12 @@ namespace BTCPayServer.Logging
|
||||
}
|
||||
set
|
||||
{
|
||||
_queueProcessor.Console = value ?? throw new ArgumentNullException(nameof(value));
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
_queueProcessor.Console = value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,13 +111,13 @@ namespace BTCPayServer.Logging
|
||||
}
|
||||
set
|
||||
{
|
||||
_filter = value ?? throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
}
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
public bool IncludeScopes
|
||||
{
|
||||
get; set;
|
||||
_filter = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string Name
|
||||
@ -106,6 +125,16 @@ namespace BTCPayServer.Logging
|
||||
get;
|
||||
}
|
||||
|
||||
internal IExternalScopeProvider ScopeProvider
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public bool DisableColors
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
if (!IsEnabled(logLevel))
|
||||
@ -154,10 +183,7 @@ namespace BTCPayServer.Logging
|
||||
while (lenAfter++ < 18)
|
||||
logBuilder.Append(" ");
|
||||
// scope information
|
||||
if (IncludeScopes)
|
||||
{
|
||||
GetScopeInformation(logBuilder);
|
||||
}
|
||||
GetScopeInformation(logBuilder);
|
||||
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
{
|
||||
@ -202,18 +228,15 @@ namespace BTCPayServer.Logging
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
if (logLevel == LogLevel.None)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Filter(Name, logLevel);
|
||||
}
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
{
|
||||
if (state == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(state));
|
||||
}
|
||||
|
||||
return ConsoleLogScope.Push(Name, state);
|
||||
}
|
||||
public IDisposable BeginScope<TState>(TState state) => ScopeProvider?.Push(state) ?? NullScope.Instance;
|
||||
|
||||
private static string GetLogLevelString(LogLevel logLevel)
|
||||
{
|
||||
@ -238,6 +261,11 @@ namespace BTCPayServer.Logging
|
||||
|
||||
private ConsoleColors GetLogLevelConsoleColors(LogLevel logLevel)
|
||||
{
|
||||
if (DisableColors)
|
||||
{
|
||||
return new ConsoleColors(null, null);
|
||||
}
|
||||
|
||||
// We must explicitly set the background color if we are setting the foreground color,
|
||||
// since just setting one can look bad on the users console.
|
||||
switch (logLevel)
|
||||
@ -259,30 +287,25 @@ namespace BTCPayServer.Logging
|
||||
}
|
||||
}
|
||||
|
||||
private void GetScopeInformation(StringBuilder builder)
|
||||
private void GetScopeInformation(StringBuilder stringBuilder)
|
||||
{
|
||||
var current = ConsoleLogScope.Current;
|
||||
string scopeLog = string.Empty;
|
||||
var length = builder.Length;
|
||||
|
||||
while (current != null)
|
||||
var scopeProvider = ScopeProvider;
|
||||
if (scopeProvider != null)
|
||||
{
|
||||
if (length == builder.Length)
|
||||
{
|
||||
scopeLog = $"=> {current}";
|
||||
}
|
||||
else
|
||||
{
|
||||
scopeLog = $"=> {current} ";
|
||||
}
|
||||
var initialLength = stringBuilder.Length;
|
||||
|
||||
builder.Insert(length, scopeLog);
|
||||
current = current.Parent;
|
||||
}
|
||||
if (builder.Length > length)
|
||||
{
|
||||
builder.Insert(length, _messagePadding);
|
||||
builder.AppendLine();
|
||||
scopeProvider.ForEachScope((scope, state) =>
|
||||
{
|
||||
var (builder, length) = state;
|
||||
var first = length == builder.Length;
|
||||
builder.Append(first ? "=> " : " => ").Append(scope);
|
||||
}, (stringBuilder, initialLength));
|
||||
|
||||
if (stringBuilder.Length > initialLength)
|
||||
{
|
||||
stringBuilder.Insert(initialLength, _messagePadding);
|
||||
stringBuilder.AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -333,9 +356,9 @@ namespace BTCPayServer.Logging
|
||||
// Start Console message queue processor
|
||||
_outputTask = Task.Factory.StartNew(
|
||||
ProcessLogQueue,
|
||||
this,
|
||||
default(CancellationToken),
|
||||
TaskCreationOptions.LongRunning, TaskScheduler.Default);
|
||||
state: this,
|
||||
cancellationToken: default(CancellationToken),
|
||||
creationOptions: TaskCreationOptions.LongRunning, scheduler: TaskScheduler.Default);
|
||||
}
|
||||
|
||||
public virtual void EnqueueMessage(LogMessageEntry message)
|
||||
|
@ -12,10 +12,10 @@ namespace BTCPayServer.Migrations
|
||||
name: "AspNetRoles",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||
ConcurrencyStamp = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Name = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
|
||||
NormalizedName = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true)
|
||||
Id = table.Column<string>(nullable: false),
|
||||
ConcurrencyStamp = table.Column<string>(nullable: true),
|
||||
Name = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedName = table.Column<string>(maxLength: 256, nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -26,21 +26,21 @@ namespace BTCPayServer.Migrations
|
||||
name: "AspNetUsers",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||
AccessFailedCount = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
ConcurrencyStamp = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Email = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
|
||||
Id = table.Column<string>(nullable: false),
|
||||
AccessFailedCount = table.Column<int>(nullable: false),
|
||||
ConcurrencyStamp = table.Column<string>(nullable: true),
|
||||
Email = table.Column<string>(maxLength: 256, nullable: true),
|
||||
EmailConfirmed = table.Column<bool>(nullable: false),
|
||||
LockoutEnabled = table.Column<bool>(nullable: false),
|
||||
LockoutEnd = table.Column<DateTimeOffset>(nullable: true),
|
||||
NormalizedEmail = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
|
||||
NormalizedUserName = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
|
||||
PasswordHash = table.Column<string>(type: "TEXT", nullable: true),
|
||||
PhoneNumber = table.Column<string>(type: "TEXT", nullable: true),
|
||||
NormalizedEmail = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedUserName = table.Column<string>(maxLength: 256, nullable: true),
|
||||
PasswordHash = table.Column<string>(nullable: true),
|
||||
PhoneNumber = table.Column<string>(nullable: true),
|
||||
PhoneNumberConfirmed = table.Column<bool>(nullable: false),
|
||||
SecurityStamp = table.Column<string>(type: "TEXT", nullable: true),
|
||||
SecurityStamp = table.Column<string>(nullable: true),
|
||||
TwoFactorEnabled = table.Column<bool>(nullable: false),
|
||||
UserName = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true)
|
||||
UserName = table.Column<string>(maxLength: 256, nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -51,12 +51,12 @@ namespace BTCPayServer.Migrations
|
||||
name: "Stores",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||
DerivationStrategy = table.Column<string>(type: "TEXT", nullable: true),
|
||||
SpeedPolicy = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Id = table.Column<string>(nullable: false),
|
||||
DerivationStrategy = table.Column<string>(nullable: true),
|
||||
SpeedPolicy = table.Column<int>(nullable: false),
|
||||
StoreCertificate = table.Column<byte[]>(nullable: true),
|
||||
StoreName = table.Column<string>(type: "TEXT", nullable: true),
|
||||
StoreWebsite = table.Column<string>(type: "TEXT", nullable: true)
|
||||
StoreName = table.Column<string>(nullable: true),
|
||||
StoreWebsite = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -67,11 +67,11 @@ namespace BTCPayServer.Migrations
|
||||
name: "AspNetRoleClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
ClaimType = table.Column<string>(type: "TEXT", nullable: true),
|
||||
ClaimValue = table.Column<string>(type: "TEXT", nullable: true),
|
||||
RoleId = table.Column<string>(type: "TEXT", nullable: false)
|
||||
ClaimType = table.Column<string>(nullable: true),
|
||||
ClaimValue = table.Column<string>(nullable: true),
|
||||
RoleId = table.Column<string>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -88,11 +88,11 @@ namespace BTCPayServer.Migrations
|
||||
name: "AspNetUserClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
ClaimType = table.Column<string>(type: "TEXT", nullable: true),
|
||||
ClaimValue = table.Column<string>(type: "TEXT", nullable: true),
|
||||
UserId = table.Column<string>(type: "TEXT", nullable: false)
|
||||
ClaimType = table.Column<string>(nullable: true),
|
||||
ClaimValue = table.Column<string>(nullable: true),
|
||||
UserId = table.Column<string>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -109,10 +109,10 @@ namespace BTCPayServer.Migrations
|
||||
name: "AspNetUserLogins",
|
||||
columns: table => new
|
||||
{
|
||||
LoginProvider = table.Column<string>(type: "TEXT", nullable: false),
|
||||
ProviderKey = table.Column<string>(type: "TEXT", nullable: false),
|
||||
ProviderDisplayName = table.Column<string>(type: "TEXT", nullable: true),
|
||||
UserId = table.Column<string>(type: "TEXT", nullable: false)
|
||||
LoginProvider = table.Column<string>(nullable: false),
|
||||
ProviderKey = table.Column<string>(nullable: false),
|
||||
ProviderDisplayName = table.Column<string>(nullable: true),
|
||||
UserId = table.Column<string>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -129,8 +129,8 @@ namespace BTCPayServer.Migrations
|
||||
name: "AspNetUserRoles",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(type: "TEXT", nullable: false),
|
||||
RoleId = table.Column<string>(type: "TEXT", nullable: false)
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
RoleId = table.Column<string>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -153,10 +153,10 @@ namespace BTCPayServer.Migrations
|
||||
name: "AspNetUserTokens",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(type: "TEXT", nullable: false),
|
||||
LoginProvider = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Name = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Value = table.Column<string>(type: "TEXT", nullable: true)
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
LoginProvider = table.Column<string>(nullable: false),
|
||||
Name = table.Column<string>(nullable: false),
|
||||
Value = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -173,15 +173,15 @@ namespace BTCPayServer.Migrations
|
||||
name: "Invoices",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Id = table.Column<string>(nullable: false),
|
||||
Blob = table.Column<byte[]>(nullable: true),
|
||||
Created = table.Column<DateTimeOffset>(nullable: false),
|
||||
CustomerEmail = table.Column<string>(type: "TEXT", nullable: true),
|
||||
ExceptionStatus = table.Column<string>(type: "TEXT", nullable: true),
|
||||
ItemCode = table.Column<string>(type: "TEXT", nullable: true),
|
||||
OrderId = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Status = table.Column<string>(type: "TEXT", nullable: true),
|
||||
StoreDataId = table.Column<string>(type: "TEXT", nullable: true)
|
||||
CustomerEmail = table.Column<string>(nullable: true),
|
||||
ExceptionStatus = table.Column<string>(nullable: true),
|
||||
ItemCode = table.Column<string>(nullable: true),
|
||||
OrderId = table.Column<string>(nullable: true),
|
||||
Status = table.Column<string>(nullable: true),
|
||||
StoreDataId = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -198,9 +198,9 @@ namespace BTCPayServer.Migrations
|
||||
name: "UserStore",
|
||||
columns: table => new
|
||||
{
|
||||
ApplicationUserId = table.Column<string>(type: "TEXT", nullable: false),
|
||||
StoreDataId = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Role = table.Column<string>(type: "TEXT", nullable: true)
|
||||
ApplicationUserId = table.Column<string>(nullable: false),
|
||||
StoreDataId = table.Column<string>(nullable: false),
|
||||
Role = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -223,9 +223,9 @@ namespace BTCPayServer.Migrations
|
||||
name: "Payments",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Id = table.Column<string>(nullable: false),
|
||||
Blob = table.Column<byte[]>(nullable: true),
|
||||
InvoiceDataId = table.Column<string>(type: "TEXT", nullable: true)
|
||||
InvoiceDataId = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -242,9 +242,9 @@ namespace BTCPayServer.Migrations
|
||||
name: "RefundAddresses",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Id = table.Column<string>(nullable: false),
|
||||
Blob = table.Column<byte[]>(nullable: true),
|
||||
InvoiceDataId = table.Column<string>(type: "TEXT", nullable: true)
|
||||
InvoiceDataId = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
|
@ -12,8 +12,8 @@ namespace BTCPayServer.Migrations
|
||||
name: "Settings",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Value = table.Column<string>(type: "TEXT", nullable: true)
|
||||
Id = table.Column<string>(nullable: false),
|
||||
Value = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
|
@ -12,8 +12,8 @@ namespace BTCPayServer.Migrations
|
||||
name: "AddressInvoices",
|
||||
columns: table => new
|
||||
{
|
||||
Address = table.Column<string>(type: "TEXT", nullable: false),
|
||||
InvoiceDataId = table.Column<string>(type: "TEXT", nullable: true)
|
||||
Address = table.Column<string>(nullable: false),
|
||||
InvoiceDataId = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
|
@ -12,13 +12,13 @@ namespace BTCPayServer.Migrations
|
||||
name: "PairedSINData",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Facade = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Label = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Name = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Id = table.Column<string>(nullable: false),
|
||||
Facade = table.Column<string>(nullable: true),
|
||||
Label = table.Column<string>(nullable: true),
|
||||
Name = table.Column<string>(nullable: true),
|
||||
PairingTime = table.Column<DateTimeOffset>(nullable: false),
|
||||
SIN = table.Column<string>(type: "TEXT", nullable: true),
|
||||
StoreDataId = table.Column<string>(type: "TEXT", nullable: true)
|
||||
SIN = table.Column<string>(nullable: true),
|
||||
StoreDataId = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -29,15 +29,15 @@ namespace BTCPayServer.Migrations
|
||||
name: "PairingCodes",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Id = table.Column<string>(nullable: false),
|
||||
DateCreated = table.Column<DateTime>(nullable: false),
|
||||
Expiration = table.Column<DateTimeOffset>(nullable: false),
|
||||
Facade = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Label = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Name = table.Column<string>(type: "TEXT", nullable: true),
|
||||
SIN = table.Column<string>(type: "TEXT", nullable: true),
|
||||
StoreDataId = table.Column<string>(type: "TEXT", nullable: true),
|
||||
TokenValue = table.Column<string>(type: "TEXT", nullable: true)
|
||||
Facade = table.Column<string>(nullable: true),
|
||||
Label = table.Column<string>(nullable: true),
|
||||
Name = table.Column<string>(nullable: true),
|
||||
SIN = table.Column<string>(nullable: true),
|
||||
StoreDataId = table.Column<string>(nullable: true),
|
||||
TokenValue = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
|
@ -22,7 +22,7 @@ namespace BTCPayServer.Migrations
|
||||
name: "PendingInvoices",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "TEXT", nullable: false)
|
||||
Id = table.Column<string>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
|
@ -17,8 +17,8 @@ namespace BTCPayServer.Migrations
|
||||
name: "HistoricalAddressInvoices",
|
||||
columns: table => new
|
||||
{
|
||||
InvoiceDataId = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Address = table.Column<string>(type: "TEXT", nullable: false),
|
||||
InvoiceDataId = table.Column<string>(nullable: false),
|
||||
Address = table.Column<string>(nullable: false),
|
||||
Assigned = table.Column<DateTimeOffset>(nullable: false),
|
||||
UnAssigned = table.Column<DateTimeOffset>(nullable: true)
|
||||
},
|
||||
|
@ -1,11 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace BTCPayServer.Models
|
||||
{
|
||||
public class ErrorViewModel
|
||||
{
|
||||
public string RequestId { get; set; }
|
||||
|
||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
}
|
||||
}
|
@ -79,7 +79,7 @@ namespace BTCPayServer.Models
|
||||
|
||||
//"price":5
|
||||
[JsonProperty("price")]
|
||||
public double Price
|
||||
public decimal Price
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
@ -94,7 +94,7 @@ namespace BTCPayServer.Models
|
||||
//"exRates":{"USD":4320.02}
|
||||
[JsonProperty("exRates")]
|
||||
[Obsolete("Use CryptoInfo.ExRates instead")]
|
||||
public Dictionary<string, double> ExRates
|
||||
public Dictionary<string, decimal> ExRates
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ namespace BTCPayServer.Models.InvoicingModels
|
||||
Currency = "USD";
|
||||
}
|
||||
[Required]
|
||||
public double? Amount
|
||||
public decimal? Amount
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
@ -68,6 +68,10 @@ namespace BTCPayServer.Payments.Bitcoin
|
||||
{
|
||||
return ConfirmationCount >= 1;
|
||||
}
|
||||
else if (speedPolicy == SpeedPolicy.LowMediumSpeed)
|
||||
{
|
||||
return ConfirmationCount >= 2;
|
||||
}
|
||||
else if (speedPolicy == SpeedPolicy.LowSpeed)
|
||||
{
|
||||
return ConfirmationCount >= 6;
|
||||
|
@ -28,7 +28,7 @@ namespace BTCPayServer.Payments.Bitcoin
|
||||
{
|
||||
EventAggregator _Aggregator;
|
||||
ExplorerClientProvider _ExplorerClients;
|
||||
IApplicationLifetime _Lifetime;
|
||||
Microsoft.Extensions.Hosting.IApplicationLifetime _Lifetime;
|
||||
InvoiceRepository _InvoiceRepository;
|
||||
private TaskCompletionSource<bool> _RunningTask;
|
||||
private CancellationTokenSource _Cts;
|
||||
@ -39,7 +39,7 @@ namespace BTCPayServer.Payments.Bitcoin
|
||||
BTCPayWalletProvider wallets,
|
||||
InvoiceRepository invoiceRepository,
|
||||
BTCPayNetworkProvider networkProvider,
|
||||
EventAggregator aggregator, IApplicationLifetime lifetime)
|
||||
EventAggregator aggregator, Microsoft.Extensions.Hosting.IApplicationLifetime lifetime)
|
||||
{
|
||||
PollInterval = TimeSpan.FromMinutes(1.0);
|
||||
_Wallets = wallets;
|
||||
|
@ -36,17 +36,25 @@ namespace BTCPayServer.Payments.Lightning
|
||||
expiry = TimeSpan.FromSeconds(1);
|
||||
|
||||
LightningInvoice lightningInvoice = null;
|
||||
try
|
||||
|
||||
string description = storeBlob.LightningDescriptionTemplate;
|
||||
description = description.Replace("{StoreName}", store.StoreName ?? "", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("{ItemDescription}", invoice.ProductInformation.ItemDesc ?? "", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("{OrderId}", invoice.OrderId ?? "", StringComparison.OrdinalIgnoreCase);
|
||||
using (var cts = new CancellationTokenSource(5000))
|
||||
{
|
||||
string description = storeBlob.LightningDescriptionTemplate;
|
||||
description = description.Replace("{StoreName}", store.StoreName ?? "", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("{ItemDescription}", invoice.ProductInformation.ItemDesc ?? "", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("{OrderId}", invoice.OrderId ?? "", StringComparison.OrdinalIgnoreCase);
|
||||
lightningInvoice = await client.CreateInvoice(new LightMoney(due, LightMoneyUnit.BTC), description, expiry);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new PaymentMethodUnavailableException($"Impossible to create lightning invoice ({ex.Message})", ex);
|
||||
try
|
||||
{
|
||||
lightningInvoice = await client.CreateInvoice(new LightMoney(due, LightMoneyUnit.BTC), description, expiry, cts.Token);
|
||||
}
|
||||
catch (OperationCanceledException) when (cts.IsCancellationRequested)
|
||||
{
|
||||
throw new PaymentMethodUnavailableException($"The lightning node did not replied in a timely maner");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new PaymentMethodUnavailableException($"Impossible to create lightning invoice ({ex.Message})", ex);
|
||||
}
|
||||
}
|
||||
var nodeInfo = await test;
|
||||
return new LightningLikePaymentMethodDetails()
|
||||
@ -62,34 +70,36 @@ namespace BTCPayServer.Payments.Lightning
|
||||
if (!_Dashboard.IsFullySynched(network.CryptoCode, out var summary))
|
||||
throw new PaymentMethodUnavailableException($"Full node not available");
|
||||
|
||||
var cts = new CancellationTokenSource(5000);
|
||||
var client = _LightningClientFactory.CreateClient(supportedPaymentMethod, network);
|
||||
LightningNodeInformation info = null;
|
||||
try
|
||||
using (var cts = new CancellationTokenSource(5000))
|
||||
{
|
||||
info = await client.GetInfo(cts.Token);
|
||||
}
|
||||
catch (OperationCanceledException) when (cts.IsCancellationRequested)
|
||||
{
|
||||
throw new PaymentMethodUnavailableException($"The lightning node did not replied in a timely maner");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new PaymentMethodUnavailableException($"Error while connecting to the API ({ex.Message})");
|
||||
}
|
||||
var client = _LightningClientFactory.CreateClient(supportedPaymentMethod, network);
|
||||
LightningNodeInformation info = null;
|
||||
try
|
||||
{
|
||||
info = await client.GetInfo(cts.Token);
|
||||
}
|
||||
catch (OperationCanceledException) when (cts.IsCancellationRequested)
|
||||
{
|
||||
throw new PaymentMethodUnavailableException($"The lightning node did not replied in a timely maner");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new PaymentMethodUnavailableException($"Error while connecting to the API ({ex.Message})");
|
||||
}
|
||||
|
||||
if (info.Address == null)
|
||||
{
|
||||
throw new PaymentMethodUnavailableException($"No lightning node public address has been configured");
|
||||
}
|
||||
if (info.Address == null)
|
||||
{
|
||||
throw new PaymentMethodUnavailableException($"No lightning node public address has been configured");
|
||||
}
|
||||
|
||||
var blocksGap = Math.Abs(info.BlockHeight - summary.Status.ChainHeight);
|
||||
if (blocksGap > 10)
|
||||
{
|
||||
throw new PaymentMethodUnavailableException($"The lightning is not synched ({blocksGap} blocks)");
|
||||
}
|
||||
var blocksGap = Math.Abs(info.BlockHeight - summary.Status.ChainHeight);
|
||||
if (blocksGap > 10)
|
||||
{
|
||||
throw new PaymentMethodUnavailableException($"The lightning is not synched ({blocksGap} blocks)");
|
||||
}
|
||||
|
||||
return new NodeInfo(info.NodeId, info.Address, info.P2PPort);
|
||||
return new NodeInfo(info.NodeId, info.Address, info.P2PPort);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task TestConnection(NodeInfo nodeInfo, CancellationToken cancellation)
|
||||
|
@ -40,7 +40,6 @@ namespace BTCPayServer
|
||||
.UseIISIntegration()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseConfiguration(conf)
|
||||
.UseApplicationInsights()
|
||||
.ConfigureLogging(l =>
|
||||
{
|
||||
l.AddFilter("Microsoft", LogLevel.Error);
|
||||
|
@ -417,10 +417,21 @@ namespace BTCPayServer.Rating
|
||||
|
||||
public RateRule(RateRules parent, CurrencyPair currencyPair, SyntaxNode candidate)
|
||||
{
|
||||
_CurrencyPair = currencyPair;
|
||||
flatten = new FlattenExpressionRewriter(parent, currencyPair);
|
||||
this.expression = flatten.Visit(candidate);
|
||||
}
|
||||
|
||||
|
||||
private readonly CurrencyPair _CurrencyPair;
|
||||
public CurrencyPair CurrencyPair
|
||||
{
|
||||
get
|
||||
{
|
||||
return _CurrencyPair;
|
||||
}
|
||||
}
|
||||
|
||||
public ExchangeRates ExchangeRates
|
||||
{
|
||||
get
|
||||
|
@ -79,13 +79,13 @@ namespace BTCPayServer.Security
|
||||
if (storeId != null)
|
||||
{
|
||||
var identity = ((ClaimsIdentity)context.HttpContext.User.Identity);
|
||||
identity.AddClaim(new Claim(Claims.OwnStore, storeId));
|
||||
identity.AddClaim(new Claim(Policies.CanUseStore.Key, storeId));
|
||||
var store = await _StoreRepository.FindStore(storeId);
|
||||
context.HttpContext.SetStoreData(store);
|
||||
}
|
||||
else if (failedAuth)
|
||||
{
|
||||
throw new BitpayHttpException(401, "Can't access to store");
|
||||
throw new BitpayHttpException(401, "Invalid credentials");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -118,18 +118,7 @@ namespace BTCPayServer.Services
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> SupportDerivation(BTCPayNetwork network, DirectDerivationStrategy strategy)
|
||||
{
|
||||
if (network == null)
|
||||
throw new ArgumentNullException(nameof(network));
|
||||
if (strategy == null)
|
||||
throw new ArgumentNullException(nameof(strategy));
|
||||
if (!strategy.Segwit)
|
||||
return false;
|
||||
return await GetKeyPath(_Ledger, network, strategy) != null;
|
||||
}
|
||||
|
||||
private static async Task<KeyPath> GetKeyPath(LedgerClient ledger, BTCPayNetwork network, DirectDerivationStrategy directStrategy)
|
||||
public async Task<KeyPath> GetKeyPath(BTCPayNetwork network, DirectDerivationStrategy directStrategy)
|
||||
{
|
||||
List<KeyPath> derivations = new List<KeyPath>();
|
||||
if(network.NBitcoinNetwork.Consensus.SupportSegwit)
|
||||
@ -143,7 +132,7 @@ namespace BTCPayServer.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
var extpubkey = await GetExtPubKey(ledger, network, account, true);
|
||||
var extpubkey = await GetExtPubKey(_Ledger, network, account, true);
|
||||
if (directStrategy.Root.PubKey == extpubkey.ExtPubKey.PubKey)
|
||||
{
|
||||
foundKeyPath = account;
|
||||
@ -159,79 +148,12 @@ namespace BTCPayServer.Services
|
||||
return foundKeyPath;
|
||||
}
|
||||
|
||||
public async Task<Transaction> SendToAddress(DirectDerivationStrategy strategy,
|
||||
ReceivedCoin[] coins, BTCPayNetwork network,
|
||||
(IDestination destination, Money amount, bool substractFees)[] send,
|
||||
FeeRate feeRate,
|
||||
IDestination changeAddress,
|
||||
KeyPath changeKeyPath,
|
||||
FeeRate minTxRelayFee)
|
||||
public async Task<Transaction> SignTransactionAsync(SignatureRequest[] signatureRequests,
|
||||
Transaction unsigned,
|
||||
KeyPath changeKeyPath)
|
||||
{
|
||||
if (strategy == null)
|
||||
throw new ArgumentNullException(nameof(strategy));
|
||||
if (network == null)
|
||||
throw new ArgumentNullException(nameof(network));
|
||||
if (feeRate == null)
|
||||
throw new ArgumentNullException(nameof(feeRate));
|
||||
if (changeAddress == null)
|
||||
throw new ArgumentNullException(nameof(changeAddress));
|
||||
if (feeRate.FeePerK <= Money.Zero)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(feeRate), "The fee rate should be above zero");
|
||||
}
|
||||
|
||||
foreach (var element in send)
|
||||
{
|
||||
if (element.destination == null)
|
||||
throw new ArgumentNullException(nameof(element.destination));
|
||||
if (element.amount == null)
|
||||
throw new ArgumentNullException(nameof(element.amount));
|
||||
if (element.amount <= Money.Zero)
|
||||
throw new ArgumentOutOfRangeException(nameof(element.amount), "The amount should be above zero");
|
||||
}
|
||||
|
||||
var foundKeyPath = await GetKeyPath(Ledger, network, strategy);
|
||||
|
||||
if (foundKeyPath == null)
|
||||
{
|
||||
throw new HardwareWalletException($"This store is not configured to use this ledger");
|
||||
}
|
||||
|
||||
TransactionBuilder builder = new TransactionBuilder();
|
||||
builder.StandardTransactionPolicy.MinRelayTxFee = minTxRelayFee;
|
||||
builder.SetConsensusFactory(network.NBitcoinNetwork);
|
||||
builder.AddCoins(coins.Select(c=>c.Coin).ToArray());
|
||||
|
||||
foreach (var element in send)
|
||||
{
|
||||
builder.Send(element.destination, element.amount);
|
||||
if (element.substractFees)
|
||||
builder.SubtractFees();
|
||||
}
|
||||
builder.SetChange(changeAddress);
|
||||
builder.SendEstimatedFees(feeRate);
|
||||
builder.Shuffle();
|
||||
var unsigned = builder.BuildTransaction(false);
|
||||
|
||||
var keypaths = new Dictionary<Script, KeyPath>();
|
||||
foreach(var c in coins)
|
||||
{
|
||||
keypaths.TryAdd(c.Coin.ScriptPubKey, c.KeyPath);
|
||||
}
|
||||
|
||||
var hasChange = unsigned.Outputs.Count == 2;
|
||||
var usedCoins = builder.FindSpentCoins(unsigned);
|
||||
_Transport.Timeout = TimeSpan.FromMinutes(5);
|
||||
var fullySigned = await Ledger.SignTransactionAsync(
|
||||
usedCoins.Select(c => new SignatureRequest
|
||||
{
|
||||
InputCoin = c,
|
||||
KeyPath = foundKeyPath.Derive(keypaths[c.TxOut.ScriptPubKey]),
|
||||
PubKey = strategy.Root.Derive(keypaths[c.TxOut.ScriptPubKey]).PubKey
|
||||
}).ToArray(),
|
||||
unsigned,
|
||||
hasChange ? foundKeyPath.Derive(changeKeyPath) : null);
|
||||
return fullySigned;
|
||||
return await Ledger.SignTransactionAsync(signatureRequests, unsigned, changeKeyPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,7 +100,8 @@ namespace BTCPayServer.Services.Invoices
|
||||
{
|
||||
HighSpeed = 0,
|
||||
MediumSpeed = 1,
|
||||
LowSpeed = 2
|
||||
LowSpeed = 2,
|
||||
LowMediumSpeed = 3
|
||||
}
|
||||
public class InvoiceEntity
|
||||
{
|
||||
@ -357,9 +358,9 @@ namespace BTCPayServer.Services.Invoices
|
||||
cryptoInfo.CryptoPaid = accounting.CryptoPaid.ToString();
|
||||
|
||||
cryptoInfo.Address = info.GetPaymentMethodDetails()?.GetPaymentDestination();
|
||||
cryptoInfo.ExRates = new Dictionary<string, double>
|
||||
cryptoInfo.ExRates = new Dictionary<string, decimal>
|
||||
{
|
||||
{ ProductInformation.Currency, (double)cryptoInfo.Rate }
|
||||
{ ProductInformation.Currency, cryptoInfo.Rate }
|
||||
};
|
||||
var paymentId = info.GetId();
|
||||
var scheme = info.Network.UriScheme;
|
||||
@ -676,7 +677,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
accounting.Due = Money.Max(accounting.TotalDue - accounting.Paid, Money.Zero);
|
||||
accounting.DueUncapped = accounting.TotalDue - accounting.Paid;
|
||||
accounting.NetworkFee = accounting.TotalDue - totalDueNoNetworkCost;
|
||||
accounting.MinimumTotalDue = Money.Satoshis(accounting.TotalDue.Satoshi * (1.0m - ((decimal)ParentEntity.PaymentTolerance / 100.0m)));
|
||||
accounting.MinimumTotalDue = Money.Max(Money.Satoshis(1), Money.Satoshis(accounting.TotalDue.Satoshi * (1.0m - ((decimal)ParentEntity.PaymentTolerance / 100.0m))));
|
||||
return accounting;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
@ -8,12 +9,14 @@ namespace BTCPayServer.Services
|
||||
{
|
||||
public class PoliciesSettings
|
||||
{
|
||||
[Display(Name = "Requires a confirmation mail for registering")]
|
||||
public bool RequiresConfirmedEmail
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
||||
[Display(Name = "Disable registration")]
|
||||
public bool LockSubscription { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ namespace BTCPayServer.Services.Rates
|
||||
}
|
||||
IMemoryCache _Cache;
|
||||
private IOptions<MemoryCacheOptions> _CacheOptions;
|
||||
|
||||
CurrencyNameTable _CurrencyTable;
|
||||
public IMemoryCache Cache
|
||||
{
|
||||
get
|
||||
@ -46,10 +46,12 @@ namespace BTCPayServer.Services.Rates
|
||||
CoinAverageSettings _CoinAverageSettings;
|
||||
public BTCPayRateProviderFactory(IOptions<MemoryCacheOptions> cacheOptions,
|
||||
BTCPayNetworkProvider btcpayNetworkProvider,
|
||||
CurrencyNameTable currencyTable,
|
||||
CoinAverageSettings coinAverageSettings)
|
||||
{
|
||||
if (cacheOptions == null)
|
||||
throw new ArgumentNullException(nameof(cacheOptions));
|
||||
_CurrencyTable = currencyTable;
|
||||
_CoinAverageSettings = coinAverageSettings;
|
||||
_Cache = new MemoryCache(cacheOptions);
|
||||
_CacheOptions = cacheOptions;
|
||||
@ -161,6 +163,13 @@ namespace BTCPayServer.Services.Rates
|
||||
}
|
||||
rateRule.Reevaluate();
|
||||
result.Value = rateRule.Value;
|
||||
|
||||
var currencyData = _CurrencyTable?.GetCurrencyData(rateRule.CurrencyPair.Right);
|
||||
if(currencyData != null && result.Value.HasValue)
|
||||
{
|
||||
result.Value = decimal.Round(result.Value.Value, currencyData.Divisibility, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
|
||||
result.Errors = rateRule.Errors;
|
||||
result.EvaluatedRule = rateRule.ToString(true);
|
||||
result.Rule = rateRule.ToString(false);
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-center">
|
||||
@Html.Partial("_StatusMessage", "Thank you for confirming your email.")
|
||||
<partial name="_StatusMessage" for="@("Thank you for confirming your email.")" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-center">
|
||||
@Html.Partial("_StatusMessage", TempData["StatusMessage"])
|
||||
<partial name="_StatusMessage" for="@TempData["StatusMessage"]" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
@ -7,7 +7,7 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-center">
|
||||
@Html.Partial("_StatusMessage", TempData["StatusMessage"])
|
||||
<partial name="_StatusMessage" for="@TempData["StatusMessage"]" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-center">
|
||||
@Html.Partial("_StatusMessage", TempData["TempDataProperty-StatusMessage"])
|
||||
<partial name="_StatusMessage" for="@TempData["TempDataProperty-StatusMessage"]" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-center">
|
||||
@Html.Partial("_StatusMessage", TempData["TempDataProperty-StatusMessage"])
|
||||
<partial name="_StatusMessage" for="@TempData["TempDataProperty-StatusMessage"]" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
@ -1,4 +1,6 @@
|
||||
@model ViewPointOfSaleViewModel
|
||||
@inject BTCPayServer.HostedServices.CssThemeManager themeManager
|
||||
|
||||
@model ViewPointOfSaleViewModel
|
||||
@{
|
||||
ViewData["Title"] = Model.Title;
|
||||
Layout = null;
|
||||
@ -11,13 +13,13 @@
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<link rel="stylesheet" href="~/vendor/bootstrap4/css/bootstrap.css" />
|
||||
<link href="@this.Context.Request.GetAbsoluteUri(themeManager.BootstrapUri)" rel="stylesheet" />
|
||||
</head>
|
||||
<body class="h-100">
|
||||
<div class="container d-flex h-100">
|
||||
<div class="justify-content-center align-self-center text-center mx-auto" style="margin: auto;">
|
||||
<h1 class="mb-4">@Model.Title</h1>
|
||||
<form method="post">
|
||||
<form method="post" asp-antiforgery="false">
|
||||
<div class="row">
|
||||
@for(int i = 0; i < Model.Items.Length; i++)
|
||||
{
|
||||
@ -34,7 +36,7 @@
|
||||
{
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-4 offset-md-4 col-sm-6 offset-sm-3">
|
||||
<form method="post" data-buy>
|
||||
<form method="post" asp-antiforgery="false" data-buy>
|
||||
<div class="input-group">
|
||||
<input class="form-control" type="number" min="0" step="@Model.Step" name="amount" placeholder="amount"><div class="input-group-append">
|
||||
<button class="btn btn-primary" type="submit">Pay</button>
|
||||
|
@ -16,7 +16,7 @@
|
||||
<div class="timer-row">
|
||||
<div class="timer-row__progress-bar" style="width: 0%;"></div>
|
||||
<div class="timer-row__spinner">
|
||||
@Html.Partial("Checkout-Spinner")
|
||||
<partial name="Checkout-Spinner" />
|
||||
</div>
|
||||
<div class="timer-row__message">
|
||||
<span v-if="srvModel.status === 'expired' || srvModel.status === 'invalid'">
|
||||
@ -51,7 +51,7 @@
|
||||
}
|
||||
</div>
|
||||
<div class="payment__spinner">
|
||||
@Html.Partial("Checkout-Spinner")
|
||||
<partial name="Checkout-Spinner" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -151,7 +151,7 @@
|
||||
<button type="submit" class="action-button" style="margin-top: 15px;">
|
||||
<span class="button-text">{{$t("Continue")}}</span>
|
||||
<div class="loader-wrapper">
|
||||
@Html.Partial("Checkout-Spinner")
|
||||
<partial name="Checkout-Spinner" />
|
||||
</div>
|
||||
</button>
|
||||
</bp-loading-button>
|
||||
@ -323,7 +323,7 @@
|
||||
<button class="action-button" style="margin-top: 15px;" type="submit">
|
||||
<span class="button-text" lcl="">Request Refund</span>
|
||||
<div class="loader-wrapper">
|
||||
@Html.Partial("Checkout-Spinner")
|
||||
<partial name="Checkout-Spinner" />
|
||||
</div>
|
||||
</button>
|
||||
</bp-loading-button>
|
||||
@ -430,7 +430,7 @@
|
||||
<button class="action-button" style="margin-top: 15px;" type="submit">
|
||||
<span class="button-text" lcl="">Request Refund</span>
|
||||
<div class="loader-wrapper">
|
||||
@Html.Partial("Checkout-Spinner")
|
||||
<partial name="Checkout-Spinner" />
|
||||
</div>
|
||||
</button>
|
||||
</bp-loading-button>
|
||||
|
@ -57,7 +57,7 @@
|
||||
<div class="modal-content long">
|
||||
<div class="content">
|
||||
<div class="invoice">
|
||||
@Html.Partial("Checkout-Body")
|
||||
<partial name="Checkout-Body" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -28,7 +28,7 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-center">
|
||||
@Html.Partial("_StatusMessage", Model.StatusMessage)
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-center">
|
||||
@Html.Partial("_StatusMessage", Model.StatusMessage)
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
<li><code>unusual:(true|false)</code> for filtering invoices which might requires merchant attention (those invalid or with an exceptionstatus)</li>
|
||||
</ul>
|
||||
<p>
|
||||
If you want two confirmed and complete invoices, duplicate the filter: <code>status:confirmed status:complete</code>.
|
||||
If you want all confirmed and complete invoices, you can duplicate a filter <code>status:confirmed status:complete</code>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -4,7 +4,7 @@
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
@Html.Partial("_StatusMessage", Model.StatusMessage)
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<form method="post">
|
||||
|
@ -3,7 +3,7 @@
|
||||
ViewData.SetActivePageAndTitle(ManageNavPages.ExternalLogins, "Manage your external logins");
|
||||
}
|
||||
|
||||
@Html.Partial("_StatusMessage", Model.StatusMessage)
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
@if (Model.CurrentLogins?.Count > 0)
|
||||
{
|
||||
<h4>Registered Logins</h4>
|
||||
|
@ -4,7 +4,7 @@
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
@Html.Partial("_StatusMessage", Model.StatusMessage)
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
|
@ -4,7 +4,7 @@
|
||||
}
|
||||
|
||||
<h4>Set your password</h4>
|
||||
@Html.Partial("_StatusMessage", Model.StatusMessage)
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
<p class="text-info">
|
||||
You do not have a local username/password for this site. Add a local
|
||||
account so you can log in without an external login.
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
@Html.Partial("_StatusMessage", Model.StatusMessage)
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
|
||||
|
||||
<div class="row">
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
@Html.Partial("_StatusMessage", Model.StatusMessage)
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
|
||||
|
||||
<table class="table table-sm table-responsive-md">
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
@Html.Partial("_StatusMessage", TempData["StatusMessage"])
|
||||
<partial name="_StatusMessage" for="@TempData["StatusMessage"]" />
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
@Html.Partial("_StatusMessage", TempData["TempDataProperty-StatusMessage"])
|
||||
<partial name="_StatusMessage" for="@TempData["TempDataProperty-StatusMessage"]" />
|
||||
|
||||
|
||||
<div class="row">
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
@Html.Partial("_StatusMessage", TempData["StatusMessage"])
|
||||
<partial name="_StatusMessage" for="@TempData["StatusMessage"]" />
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
|
||||
<h4>Modify User - @Model.Email</h4>
|
||||
@Html.Partial("_StatusMessage", Model.StatusMessage)
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
|
||||
|
||||
<div class="row">
|
||||
|
@ -1,22 +0,0 @@
|
||||
@model ErrorViewModel
|
||||
@{
|
||||
ViewData["Title"] = "Error";
|
||||
}
|
||||
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
|
||||
@if(Model.ShowRequestId)
|
||||
{
|
||||
<p>
|
||||
<strong>Request ID:</strong> <code>@Model.RequestId</code>
|
||||
</p>
|
||||
}
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<p>
|
||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Development environment should not be enabled in deployed applications</strong>, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>, and restarting the application.
|
||||
</p>
|
@ -31,7 +31,7 @@
|
||||
<body id="page-top">
|
||||
|
||||
@{
|
||||
if (ViewBag.AlwaysShrinkNavBar == null)
|
||||
if(ViewBag.AlwaysShrinkNavBar == null)
|
||||
{
|
||||
ViewBag.AlwaysShrinkNavBar = true;
|
||||
}
|
||||
@ -43,7 +43,7 @@
|
||||
<div class="container">
|
||||
<a class="navbar-brand js-scroll-trigger" href="~/">
|
||||
<img src="~/img/logo.png" height="45">
|
||||
@if (env.NetworkType != NBitcoin.NetworkType.Mainnet)
|
||||
@if(env.NetworkType != NBitcoin.NetworkType.Mainnet)
|
||||
{
|
||||
<span class="badge badge-warning" style="font-size:10px;">@env.NetworkType.ToString()</span>
|
||||
}
|
||||
@ -53,9 +53,9 @@
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarResponsive">
|
||||
<ul class="navbar-nav ml-auto">
|
||||
@if (SignInManager.IsSignedIn(User))
|
||||
@if(SignInManager.IsSignedIn(User))
|
||||
{
|
||||
@if (User.IsInRole(Roles.ServerAdmin))
|
||||
@if(User.IsInRole(Roles.ServerAdmin))
|
||||
{
|
||||
<li class="nav-item"><a asp-area="" asp-controller="Server" asp-action="ListUsers" class="nav-link js-scroll-trigger">Server settings</a></li>
|
||||
}
|
||||
@ -70,7 +70,10 @@
|
||||
</li>}
|
||||
else
|
||||
{
|
||||
<li class="nav-item"><a asp-area="" asp-controller="Account" asp-action="Register" class="nav-link js-scroll-trigger">Register</a></li>
|
||||
@if(themeManager.ShowRegister)
|
||||
{
|
||||
<li class="nav-item"><a asp-area="" asp-controller="Account" asp-action="Register" class="nav-link js-scroll-trigger">Register</a></li>
|
||||
}
|
||||
<li class="nav-item"><a asp-area="" asp-controller="Account" asp-action="Login" class="nav-link js-scroll-trigger">Log in</a></li>}
|
||||
|
||||
</ul>
|
||||
@ -84,9 +87,9 @@
|
||||
<div class="container text-right">@env.ToString()</div>
|
||||
</footer>
|
||||
|
||||
@if (!dashboard.IsFullySynched())
|
||||
@if(!dashboard.IsFullySynched())
|
||||
{
|
||||
@Html.Partial("SyncModal")
|
||||
<partial name="SyncModal" />
|
||||
}
|
||||
|
||||
@RenderSection("Scripts", required: false)
|
||||
|
@ -5,7 +5,7 @@
|
||||
ViewData.AddActivePage(BTCPayServer.Views.Stores.StoreNavPages.Index);
|
||||
}
|
||||
|
||||
@Html.Partial("_StatusMessage", Model.StatusMessage)
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
|
||||
<div class="row">
|
||||
|
@ -6,7 +6,7 @@
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
@Html.Partial("_StatusMessage", Model.StatusMessage)
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
|
@ -6,7 +6,7 @@
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
@Html.Partial("_StatusMessage", TempData["TempDataProperty-StatusMessage"])
|
||||
<partial name="_StatusMessage" for="@TempData["TempDataProperty-StatusMessage"]" />
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
|
@ -5,7 +5,7 @@
|
||||
ViewData.AddActivePage(StoreNavPages.Tokens);
|
||||
}
|
||||
|
||||
@Html.Partial("_StatusMessage", Model.StatusMessage)
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
<h4>Access token</h4>
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
|
@ -6,7 +6,7 @@
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
@Html.Partial("_StatusMessage", TempData["TempDataProperty-StatusMessage"])
|
||||
<partial name="_StatusMessage" for="@TempData["TempDataProperty-StatusMessage"]" />
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
|
@ -6,7 +6,7 @@
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
@Html.Partial("_StatusMessage", TempData["TempDataProperty-StatusMessage"])
|
||||
<partial name="_StatusMessage" for="@TempData["TempDataProperty-StatusMessage"]" />
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
|
@ -6,7 +6,7 @@
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
@Html.Partial("_StatusMessage", TempData["TempDataProperty-StatusMessage"])
|
||||
<partial name="_StatusMessage" for="@TempData["TempDataProperty-StatusMessage"]" />
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
@ -54,6 +54,7 @@
|
||||
<select asp-for="SpeedPolicy" class="form-control">
|
||||
<option value="0">Is unconfirmed</option>
|
||||
<option value="1">Has at least 1 confirmation</option>
|
||||
<option value="3">Has at least 2 confirmations</option>
|
||||
<option value="2">Has at least 6 confirmations</option>
|
||||
</select>
|
||||
<span asp-validation-for="SpeedPolicy" class="text-danger"></span>
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-center">
|
||||
@Html.Partial("_StatusMessage", TempData["TempDataProperty-StatusMessage"])
|
||||
<partial name="_StatusMessage" for="@TempData["TempDataProperty-StatusMessage"]" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
60
BTCPayServer/wwwroot/imlegacy/btg-symbol.svg
Normal file
60
BTCPayServer/wwwroot/imlegacy/btg-symbol.svg
Normal file
@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 600 600" style="enable-background:new 0 0 600 600;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{fill:url(#SVGID_1_);}
|
||||
.st2{fill:#132365;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<circle class="st0" cx="300" cy="300" r="223.4"/>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="163.539" y1="93.4842" x2="469.1968" y2="556.0573">
|
||||
<stop offset="5.128205e-03" style="stop-color:#F1D200"/>
|
||||
<stop offset="1" style="stop-color:#D27D00"/>
|
||||
</linearGradient>
|
||||
<path class="st1" d="M300,45C159.2,45,45,159.2,45,300s114.2,255,255,255s255-114.2,255-255S440.8,45,300,45z M300,496.3
|
||||
c-108.4,0-196.3-87.9-196.3-196.3S191.6,103.7,300,103.7S496.3,191.6,496.3,300S408.4,496.3,300,496.3z"/>
|
||||
<g>
|
||||
<g>
|
||||
<path id="XMLID_145_" class="st2" d="M215,387.2l9.8-19.6h117.7c16.2,0,29.4-13.2,29.4-29.4v0c0-16.2-13.2-29.4-29.4-29.4h-98.1
|
||||
v-19.6h98.1c27.1,0,49,22,49,49v0c0,27.1-22,49-49,49H215z"/>
|
||||
<path class="st2" d="M342.5,392.6H206.2l15.3-30.5h121.1c13.2,0,24-10.8,24-24c0-13.2-10.8-24-24-24H239v-30.5h103.5
|
||||
c30,0,54.5,24.4,54.5,54.5C397,368.2,372.5,392.6,342.5,392.6z M223.8,381.7h118.7c24,0,43.6-19.6,43.6-43.6
|
||||
c0-24-19.6-43.6-43.6-43.6h-92.6v8.7h92.6c19.2,0,34.9,15.6,34.9,34.9c0,19.2-15.6,34.9-34.9,34.9H228.2L223.8,381.7z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path id="XMLID_144_" class="st2" d="M215,209.6l9.8,19.6h104.6c16.2,0,29.4,13.2,29.4,29.4v0c0,16.2-13.2,29.4-29.4,29.4h-75.2
|
||||
v19.6h75.2c27.1,0,49-22,49-49v0c0-27.1-22-49-49-49H215z"/>
|
||||
<path class="st2" d="M329.4,313.1h-80.6v-30.5h80.6c13.2,0,24-10.8,24-24c0-13.2-10.8-24-24-24h-108l-15.3-30.5h123.2
|
||||
c30,0,54.5,24.4,54.5,54.5C383.9,288.6,359.5,313.1,329.4,313.1z M259.7,302.2h69.7c24,0,43.6-19.6,43.6-43.6
|
||||
S353.5,215,329.4,215H223.8l4.4,8.7h101.2c19.2,0,34.9,15.6,34.9,34.9s-15.6,34.9-34.9,34.9h-69.7V302.2z"/>
|
||||
</g>
|
||||
<g>
|
||||
<rect id="XMLID_143_" x="244.4" y="210.6" class="st2" width="19.6" height="176.5"/>
|
||||
<path class="st2" d="M269.5,392.6H239V205.2h30.5V392.6z M249.9,381.7h8.7V216.1h-8.7V381.7z"/>
|
||||
</g>
|
||||
<g>
|
||||
<rect id="XMLID_142_" x="268.4" y="180.1" class="st2" width="19.6" height="39.2"/>
|
||||
<path class="st2" d="M290.7,222.1h-25.1v-44.7h25.1V222.1z M271.1,216.6h14.2v-33.8h-14.2V216.6z"/>
|
||||
</g>
|
||||
<g>
|
||||
<rect id="XMLID_141_" x="310.9" y="180.1" class="st2" width="19.6" height="39.2"/>
|
||||
<path class="st2" d="M333.2,222.1h-25.1v-44.7h25.1V222.1z M313.6,216.6h14.2v-33.8h-14.2V216.6z"/>
|
||||
</g>
|
||||
<g>
|
||||
<rect id="XMLID_140_" x="268.4" y="376.3" class="st2" width="19.6" height="39.2"/>
|
||||
<path class="st2" d="M290.7,418.2h-25.1v-44.7h25.1V418.2z M271.1,412.8h14.2V379h-14.2V412.8z"/>
|
||||
</g>
|
||||
<g>
|
||||
<rect id="XMLID_139_" x="310.9" y="376.3" class="st2" width="19.6" height="39.2"/>
|
||||
<path class="st2" d="M333.2,418.2h-25.1v-44.7h25.1V418.2z M313.6,412.8h14.2V379h-14.2V412.8z"/>
|
||||
</g>
|
||||
</g>
|
||||
<path class="st2" d="M300,473.8c-95.8,0-173.8-77.9-173.8-173.8c0-95.8,77.9-173.8,173.8-173.8c95.8,0,173.8,77.9,173.8,173.8
|
||||
C473.8,395.8,395.8,473.8,300,473.8z M300,143.6c-86.2,0-156.4,70.2-156.4,156.4S213.8,456.4,300,456.4S456.4,386.2,456.4,300
|
||||
S386.2,143.6,300,143.6z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.3 KiB |
11
Dockerfile
11
Dockerfile
@ -1,4 +1,4 @@
|
||||
FROM microsoft/aspnetcore-build:2.0.6-2.1.101-stretch AS builder
|
||||
FROM microsoft/dotnet:2.1.300-rc1-sdk-alpine3.7 AS builder
|
||||
WORKDIR /source
|
||||
COPY BTCPayServer/BTCPayServer.csproj BTCPayServer.csproj
|
||||
# Cache some dependencies
|
||||
@ -6,7 +6,14 @@ RUN dotnet restore
|
||||
COPY BTCPayServer/. .
|
||||
RUN dotnet publish --output /app/ --configuration Release
|
||||
|
||||
FROM microsoft/aspnetcore:2.0.6-stretch
|
||||
FROM microsoft/dotnet:2.1.0-rc1-aspnetcore-runtime-alpine3.7
|
||||
|
||||
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT false
|
||||
RUN apk add --no-cache icu-libs
|
||||
|
||||
ENV LC_ALL en_US.UTF-8
|
||||
ENV LANG en_US.UTF-8
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN mkdir /datadir
|
||||
|
@ -30,7 +30,7 @@ You can also checkout [The Merchants Guide to accepting Bitcoin directly with no
|
||||
|
||||
While the documentation advise using docker-compose, you may want to build yourself outside of development purpose.
|
||||
|
||||
First install .NET Core SDK as specified by [Microsoft website](https://www.microsoft.com/net/download).
|
||||
First install .NET Core SDK 2.1 as specified by [Microsoft website](https://www.microsoft.com/net/download/dotnet-core/sdk-2.1.300-rc1).
|
||||
|
||||
On Powershell:
|
||||
```
|
||||
|
Reference in New Issue
Block a user