Compare commits

...

31 Commits

Author SHA1 Message Date
786d129452 Make sure to not freeze if ligthning does not respond 2018-05-12 00:14:39 +09:00
355989c278 bump 2018-05-11 23:34:42 +09:00
af3dee95de round up rates sent back by the RateProviderFactory 2018-05-11 23:31:50 +09:00
70a6bd6a01 bump 2018-05-11 22:42:29 +09:00
4afb0acc84 does not generate antiforgery 2018-05-11 22:41:11 +09:00
9afc143801 Use decimals and fix invoices 2018-05-11 22:38:31 +09:00
8e4943df65 low-medium speed policy 2018-05-11 22:12:45 +09:00
9b3bd8343d bump nuget packages 2018-05-11 21:33:46 +09:00
6f07849e1d Use policies security for controlling access to bitpay api 2018-05-11 17:16:18 +09:00
199db01eaf No need of authentication for GetInvoice API (#166) 2018-05-11 17:05:08 +09:00
a3c46c8f67 Use hangfire in-memory provider until the postgres binding to hangfire get updated. 2018-05-11 15:06:11 +09:00
66a68d6180 Rename LockSubscription, remove the link if not available 2018-05-10 16:02:49 +09:00
d41a5a65a2 Update posgres and clightning in tests 2018-05-10 11:56:46 +09:00
d5cab938ee bump 2018-05-09 14:10:06 +09:00
9dddfb65f0 Add globalization to the alpine package 2018-05-09 14:09:41 +09:00
6bd5976d90 Update SQLite package 2018-05-08 19:17:06 +09:00
b3385bf901 update tests image 2018-05-08 18:09:12 +09:00
bba268b5e2 Upgrade to .NET Core 2.1 2018-05-08 17:57:53 +09:00
70c98b6901 use the theme manager for ViewPointOfSale 2018-05-08 00:19:28 +09:00
2d3b7fea2e update packages 2018-05-07 23:02:55 +09:00
3bdf1c9a00 Merge pull request #156 from Vutov/master
Added BTG Support
2018-05-07 14:16:31 +09:00
a52665ea80 bump 2018-05-07 12:29:56 +09:00
3d943d49e6 Make sure if the default crypto is no available, we don't get error 404 2018-05-07 12:25:50 +09:00
6ca8ba9231 Allow signing on non segwit transactions via the ledger 2018-05-07 12:17:46 +09:00
75d685ae6c fix grammar 2018-05-07 00:24:23 +09:00
7b2ef9aec2 Remove unused Error.cshtml 2018-05-06 22:49:00 +09:00
efe666b284 Fix call to Rates via bitpay API 2018-05-06 22:41:38 +09:00
ca8af5047a Changed DefaultRateRules 2018-05-06 14:59:49 +03:00
cdc0b0d628 Fix crash when creating a token 2018-05-06 19:03:30 +09:00
87e28b70fd cap MinimumTotalDue to 1 satoshi 2018-05-06 13:55:03 +09:00
bca68986f3 Added BTG Support 2018-05-05 23:06:09 +03:00
78 changed files with 591 additions and 479 deletions

View File

@ -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>

View File

@ -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

View File

@ -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]

View File

@ -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

View 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'")
});
}
}
}

View File

@ -48,6 +48,7 @@ namespace BTCPayServer
InitBitcoin();
InitLitecoin();
InitDogecoin();
InitBitcoinGold();
}
/// <summary>

View File

@ -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>

View File

@ -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
{

View File

@ -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();
}
}

View File

@ -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 });
}
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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))

View File

@ -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
{

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}
}
}

View File

@ -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)

View File

@ -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();

View File

@ -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;

View File

@ -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)

View File

@ -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);

View File

@ -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;

View File

@ -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) &&

View File

@ -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();

View File

@ -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)

View File

@ -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 =>
{

View File

@ -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 =>
{

View File

@ -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 =>
{

View File

@ -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 =>
{

View File

@ -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 =>
{

View File

@ -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)
},

View File

@ -1,11 +0,0 @@
using System;
namespace BTCPayServer.Models
{
public class ErrorViewModel
{
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}
}

View File

@ -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;
}

View File

@ -14,7 +14,7 @@ namespace BTCPayServer.Models.InvoicingModels
Currency = "USD";
}
[Required]
public double? Amount
public decimal? Amount
{
get; set;
}

View File

@ -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;

View File

@ -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;

View File

@ -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)

View File

@ -40,7 +40,6 @@ namespace BTCPayServer
.UseIISIntegration()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseConfiguration(conf)
.UseApplicationInsights()
.ConfigureLogging(l =>
{
l.AddFilter("Microsoft", LogLevel.Error);

View File

@ -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

View File

@ -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");
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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; }
}
}

View File

@ -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);

View File

@ -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>

View File

@ -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">

View File

@ -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">

View File

@ -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>

View File

@ -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">

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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">

View File

@ -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">

View File

@ -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>

View File

@ -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">

View File

@ -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.

View File

@ -5,7 +5,7 @@
<h4>@ViewData["Title"]</h4>
@Html.Partial("_StatusMessage", Model.StatusMessage)
<partial name="_StatusMessage" for="StatusMessage" />
<div class="row">

View File

@ -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">

View File

@ -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>

View File

@ -5,7 +5,7 @@
<h4>@ViewData["Title"]</h4>
@Html.Partial("_StatusMessage", TempData["TempDataProperty-StatusMessage"])
<partial name="_StatusMessage" for="@TempData["TempDataProperty-StatusMessage"]" />
<div class="row">

View File

@ -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>

View File

@ -5,7 +5,7 @@
<h4>Modify User - @Model.Email</h4>
@Html.Partial("_StatusMessage", Model.StatusMessage)
<partial name="_StatusMessage" for="StatusMessage" />
<div class="row">

View File

@ -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>

View File

@ -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)

View File

@ -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">

View File

@ -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>

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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>

View File

@ -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>

View 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

View File

@ -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

View File

@ -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:
```