mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 06:24:24 +01:00
Some refactoring improving performance, and better tests for multiple currencies
This commit is contained in:
@@ -43,6 +43,8 @@ namespace BTCPayServer.Tests
|
|||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Uri LTCNBXplorerUri { get; set; }
|
||||||
public string CookieFile
|
public string CookieFile
|
||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
@@ -79,7 +81,9 @@ namespace BTCPayServer.Tests
|
|||||||
config.AppendLine($"regtest=1");
|
config.AppendLine($"regtest=1");
|
||||||
config.AppendLine($"port={Port}");
|
config.AppendLine($"port={Port}");
|
||||||
config.AppendLine($"explorer.url={NBXplorerUri.AbsoluteUri}");
|
config.AppendLine($"explorer.url={NBXplorerUri.AbsoluteUri}");
|
||||||
|
config.AppendLine($"ltc.explorer.url={LTCNBXplorerUri.AbsoluteUri}");
|
||||||
config.AppendLine($"explorer.cookiefile={CookieFile}");
|
config.AppendLine($"explorer.cookiefile={CookieFile}");
|
||||||
|
config.AppendLine($"ltc.explorer.cookiefile={CookieFile}");
|
||||||
config.AppendLine($"hdpubkey={HDPrivateKey.Neuter().ToString(Network.RegTest)}");
|
config.AppendLine($"hdpubkey={HDPrivateKey.Neuter().ToString(Network.RegTest)}");
|
||||||
if (Postgres != null)
|
if (Postgres != null)
|
||||||
config.AppendLine($"postgres=" + Postgres);
|
config.AppendLine($"postgres=" + Postgres);
|
||||||
|
|||||||
@@ -47,11 +47,17 @@ namespace BTCPayServer.Tests
|
|||||||
Directory.CreateDirectory(_Directory);
|
Directory.CreateDirectory(_Directory);
|
||||||
|
|
||||||
|
|
||||||
ExplorerNode = new RPCClient(RPCCredentialString.Parse(GetEnvironment("TESTS_RPCCONNECTION", "server=http://127.0.0.1:43782;ceiwHEbqWI83:DwubwWsoo3")), Network);
|
var network = new BTCPayNetworkProvider(Network);
|
||||||
|
ExplorerNode = new RPCClient(RPCCredentialString.Parse(GetEnvironment("TESTS_RPCCONNECTION", "server=http://127.0.0.1:43782;ceiwHEbqWI83:DwubwWsoo3")), network.GetNetwork("BTC").NBitcoinNetwork);
|
||||||
|
LTCExplorerNode = new RPCClient(RPCCredentialString.Parse(GetEnvironment("TESTS_LTCRPCCONNECTION", "server=http://127.0.0.1:43783;ceiwHEbqWI83:DwubwWsoo3")), network.GetNetwork("LTC").NBitcoinNetwork);
|
||||||
|
|
||||||
ExplorerClient = new ExplorerClient(Network, new Uri(GetEnvironment("TESTS_NBXPLORERURL", "http://127.0.0.1:32838/")));
|
ExplorerClient = new ExplorerClient(Network, new Uri(GetEnvironment("TESTS_NBXPLORERURL", "http://127.0.0.1:32838/")));
|
||||||
|
LTCExplorerClient = new ExplorerClient(Network, new Uri(GetEnvironment("TESTS_LTCNBXPLORERURL", "http://127.0.0.1:32839/")));
|
||||||
|
|
||||||
PayTester = new BTCPayServerTester(Path.Combine(_Directory, "pay"))
|
PayTester = new BTCPayServerTester(Path.Combine(_Directory, "pay"))
|
||||||
{
|
{
|
||||||
NBXplorerUri = ExplorerClient.Address,
|
NBXplorerUri = ExplorerClient.Address,
|
||||||
|
LTCNBXplorerUri = LTCExplorerClient.Address,
|
||||||
Postgres = GetEnvironment("TESTS_POSTGRES", "User ID=postgres;Host=127.0.0.1;Port=39372;Database=btcpayserver")
|
Postgres = GetEnvironment("TESTS_POSTGRES", "User ID=postgres;Host=127.0.0.1;Port=39372;Database=btcpayserver")
|
||||||
};
|
};
|
||||||
PayTester.Port = int.Parse(GetEnvironment("TESTS_PORT", Utils.FreeTcpPort().ToString()));
|
PayTester.Port = int.Parse(GetEnvironment("TESTS_PORT", Utils.FreeTcpPort().ToString()));
|
||||||
@@ -107,10 +113,16 @@ namespace BTCPayServer.Tests
|
|||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RPCClient LTCExplorerNode
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
public ExplorerClient ExplorerClient
|
public ExplorerClient ExplorerClient
|
||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
public ExplorerClient LTCExplorerClient { get; set; }
|
||||||
|
|
||||||
HttpClient _Http = new HttpClient();
|
HttpClient _Http = new HttpClient();
|
||||||
|
|
||||||
|
|||||||
@@ -71,6 +71,23 @@ namespace BTCPayServer.Tests
|
|||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RegisterDerivationScheme(string crytoCode)
|
||||||
|
{
|
||||||
|
RegisterDerivationSchemeAsync(crytoCode).GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
public async Task RegisterDerivationSchemeAsync(string crytoCode)
|
||||||
|
{
|
||||||
|
var store = parent.PayTester.GetController<StoresController>(UserId);
|
||||||
|
var networkProvider = parent.PayTester.GetService<BTCPayNetworkProvider>();
|
||||||
|
var derivation = new DerivationStrategyFactory(networkProvider.GetNetwork(crytoCode).NBitcoinNetwork).Parse(ExtKey.Neuter().ToString() + "-[legacy]");
|
||||||
|
await store.AddDerivationScheme(StoreId, new DerivationSchemeViewModel()
|
||||||
|
{
|
||||||
|
CryptoCurrency = crytoCode,
|
||||||
|
DerivationSchemeFormat = crytoCode,
|
||||||
|
DerivationScheme = derivation.ToString(),
|
||||||
|
}, "Save");
|
||||||
|
}
|
||||||
|
|
||||||
public DerivationStrategyBase DerivationScheme { get; set; }
|
public DerivationStrategyBase DerivationScheme { get; set; }
|
||||||
|
|
||||||
private async Task RegisterAsync()
|
private async Task RegisterAsync()
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ namespace BTCPayServer.Tests
|
|||||||
entity.TxFee = Money.Coins(0.1m);
|
entity.TxFee = Money.Coins(0.1m);
|
||||||
entity.Rate = 5000;
|
entity.Rate = 5000;
|
||||||
|
|
||||||
var cryptoData = entity.GetCryptoData("BTC");
|
var cryptoData = entity.GetCryptoData("BTC", null);
|
||||||
Assert.NotNull(cryptoData); // Should use legacy data to build itself
|
Assert.NotNull(cryptoData); // Should use legacy data to build itself
|
||||||
entity.Payments = new System.Collections.Generic.List<PaymentEntity>();
|
entity.Payments = new System.Collections.Generic.List<PaymentEntity>();
|
||||||
entity.ProductInformation = new ProductInformation() { Price = 5000 };
|
entity.ProductInformation = new ProductInformation() { Price = 5000 };
|
||||||
@@ -92,17 +92,17 @@ namespace BTCPayServer.Tests
|
|||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
entity.Payments = new List<PaymentEntity>();
|
entity.Payments = new List<PaymentEntity>();
|
||||||
cryptoData = entity.GetCryptoData("BTC");
|
cryptoData = entity.GetCryptoData("BTC", null);
|
||||||
accounting = cryptoData.Calculate();
|
accounting = cryptoData.Calculate();
|
||||||
Assert.Equal(Money.Coins(5.1m), accounting.Due);
|
Assert.Equal(Money.Coins(5.1m), accounting.Due);
|
||||||
|
|
||||||
cryptoData = entity.GetCryptoData("LTC");
|
cryptoData = entity.GetCryptoData("LTC", null);
|
||||||
accounting = cryptoData.Calculate();
|
accounting = cryptoData.Calculate();
|
||||||
Assert.Equal(Money.Coins(10.01m), accounting.TotalDue);
|
Assert.Equal(Money.Coins(10.01m), accounting.TotalDue);
|
||||||
|
|
||||||
entity.Payments.Add(new PaymentEntity() { CryptoCode = "BTC", Output = new TxOut(Money.Coins(1.0m), new Key()), Accounted = true });
|
entity.Payments.Add(new PaymentEntity() { CryptoCode = "BTC", Output = new TxOut(Money.Coins(1.0m), new Key()), Accounted = true });
|
||||||
|
|
||||||
cryptoData = entity.GetCryptoData("BTC");
|
cryptoData = entity.GetCryptoData("BTC", null);
|
||||||
accounting = cryptoData.Calculate();
|
accounting = cryptoData.Calculate();
|
||||||
Assert.Equal(Money.Coins(4.2m), accounting.Due);
|
Assert.Equal(Money.Coins(4.2m), accounting.Due);
|
||||||
Assert.Equal(Money.Coins(1.0m), accounting.CryptoPaid);
|
Assert.Equal(Money.Coins(1.0m), accounting.CryptoPaid);
|
||||||
@@ -110,7 +110,7 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Equal(Money.Coins(5.2m), accounting.TotalDue);
|
Assert.Equal(Money.Coins(5.2m), accounting.TotalDue);
|
||||||
Assert.Equal(2, accounting.TxCount);
|
Assert.Equal(2, accounting.TxCount);
|
||||||
|
|
||||||
cryptoData = entity.GetCryptoData("LTC");
|
cryptoData = entity.GetCryptoData("LTC", null);
|
||||||
accounting = cryptoData.Calculate();
|
accounting = cryptoData.Calculate();
|
||||||
Assert.Equal(Money.Coins(10.01m + 0.1m * 2 - 2.0m /* 8.21m */), accounting.Due);
|
Assert.Equal(Money.Coins(10.01m + 0.1m * 2 - 2.0m /* 8.21m */), accounting.Due);
|
||||||
Assert.Equal(Money.Coins(0.0m), accounting.CryptoPaid);
|
Assert.Equal(Money.Coins(0.0m), accounting.CryptoPaid);
|
||||||
@@ -120,7 +120,7 @@ namespace BTCPayServer.Tests
|
|||||||
entity.Payments.Add(new PaymentEntity() { CryptoCode = "LTC", Output = new TxOut(Money.Coins(1.0m), new Key()), Accounted = true });
|
entity.Payments.Add(new PaymentEntity() { CryptoCode = "LTC", Output = new TxOut(Money.Coins(1.0m), new Key()), Accounted = true });
|
||||||
|
|
||||||
|
|
||||||
cryptoData = entity.GetCryptoData("BTC");
|
cryptoData = entity.GetCryptoData("BTC", null);
|
||||||
accounting = cryptoData.Calculate();
|
accounting = cryptoData.Calculate();
|
||||||
Assert.Equal(Money.Coins(4.2m - 0.5m + 0.01m / 2), accounting.Due);
|
Assert.Equal(Money.Coins(4.2m - 0.5m + 0.01m / 2), accounting.Due);
|
||||||
Assert.Equal(Money.Coins(1.0m), accounting.CryptoPaid);
|
Assert.Equal(Money.Coins(1.0m), accounting.CryptoPaid);
|
||||||
@@ -128,7 +128,7 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Equal(Money.Coins(5.2m + 0.01m / 2), accounting.TotalDue); // The fee for LTC added
|
Assert.Equal(Money.Coins(5.2m + 0.01m / 2), accounting.TotalDue); // The fee for LTC added
|
||||||
Assert.Equal(2, accounting.TxCount);
|
Assert.Equal(2, accounting.TxCount);
|
||||||
|
|
||||||
cryptoData = entity.GetCryptoData("LTC");
|
cryptoData = entity.GetCryptoData("LTC", null);
|
||||||
accounting = cryptoData.Calculate();
|
accounting = cryptoData.Calculate();
|
||||||
Assert.Equal(Money.Coins(8.21m - 1.0m + 0.01m), accounting.Due);
|
Assert.Equal(Money.Coins(8.21m - 1.0m + 0.01m), accounting.Due);
|
||||||
Assert.Equal(Money.Coins(1.0m), accounting.CryptoPaid);
|
Assert.Equal(Money.Coins(1.0m), accounting.CryptoPaid);
|
||||||
@@ -139,7 +139,7 @@ namespace BTCPayServer.Tests
|
|||||||
var remaining = Money.Coins(4.2m - 0.5m + 0.01m / 2);
|
var remaining = Money.Coins(4.2m - 0.5m + 0.01m / 2);
|
||||||
entity.Payments.Add(new PaymentEntity() { CryptoCode = "BTC", Output = new TxOut(remaining, new Key()), Accounted = true });
|
entity.Payments.Add(new PaymentEntity() { CryptoCode = "BTC", Output = new TxOut(remaining, new Key()), Accounted = true });
|
||||||
|
|
||||||
cryptoData = entity.GetCryptoData("BTC");
|
cryptoData = entity.GetCryptoData("BTC", null);
|
||||||
accounting = cryptoData.Calculate();
|
accounting = cryptoData.Calculate();
|
||||||
Assert.Equal(Money.Zero, accounting.Due);
|
Assert.Equal(Money.Zero, accounting.Due);
|
||||||
Assert.Equal(Money.Coins(1.0m) + remaining, accounting.CryptoPaid);
|
Assert.Equal(Money.Coins(1.0m) + remaining, accounting.CryptoPaid);
|
||||||
@@ -148,7 +148,7 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Equal(accounting.Paid, accounting.TotalDue);
|
Assert.Equal(accounting.Paid, accounting.TotalDue);
|
||||||
Assert.Equal(2, accounting.TxCount);
|
Assert.Equal(2, accounting.TxCount);
|
||||||
|
|
||||||
cryptoData = entity.GetCryptoData("LTC");
|
cryptoData = entity.GetCryptoData("LTC", null);
|
||||||
accounting = cryptoData.Calculate();
|
accounting = cryptoData.Calculate();
|
||||||
Assert.Equal(Money.Zero, accounting.Due);
|
Assert.Equal(Money.Zero, accounting.Due);
|
||||||
Assert.Equal(Money.Coins(1.0m), accounting.CryptoPaid);
|
Assert.Equal(Money.Coins(1.0m), accounting.CryptoPaid);
|
||||||
@@ -384,6 +384,93 @@ namespace BTCPayServer.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CanPayWithTwoCurrencies()
|
||||||
|
{
|
||||||
|
using (var tester = ServerTester.Create())
|
||||||
|
{
|
||||||
|
tester.Start();
|
||||||
|
var user = tester.NewAccount();
|
||||||
|
user.GrantAccess();
|
||||||
|
|
||||||
|
// First we try payment with a merchant having only BTC
|
||||||
|
var invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||||
|
{
|
||||||
|
Price = 5000.0,
|
||||||
|
Currency = "USD",
|
||||||
|
PosData = "posData",
|
||||||
|
OrderId = "orderId",
|
||||||
|
ItemDesc = "Some description",
|
||||||
|
FullNotifications = true
|
||||||
|
}, Facade.Merchant);
|
||||||
|
|
||||||
|
var cashCow = tester.ExplorerNode;
|
||||||
|
var invoiceAddress = BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network);
|
||||||
|
var firstPayment = Money.Coins(0.04m);
|
||||||
|
cashCow.SendToAddress(invoiceAddress, firstPayment);
|
||||||
|
Eventually(() =>
|
||||||
|
{
|
||||||
|
invoice = user.BitPay.GetInvoice(invoice.Id);
|
||||||
|
Assert.True(invoice.BtcPaid == firstPayment);
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.Single(invoice.CryptoInfo); // Only BTC should be presented
|
||||||
|
|
||||||
|
var controller = tester.PayTester.GetController<InvoiceController>(null);
|
||||||
|
var checkout = (Models.InvoicingModels.PaymentModel)((JsonResult)controller.GetStatus(invoice.Id, null).GetAwaiter().GetResult()).Value;
|
||||||
|
Assert.Single(checkout.AvailableCryptos);
|
||||||
|
Assert.Equal("BTC", checkout.CryptoCode);
|
||||||
|
|
||||||
|
//////////////////////
|
||||||
|
|
||||||
|
// Retry now with LTC enabled
|
||||||
|
user.RegisterDerivationScheme("LTC");
|
||||||
|
invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||||
|
{
|
||||||
|
Price = 5000.0,
|
||||||
|
Currency = "USD",
|
||||||
|
PosData = "posData",
|
||||||
|
OrderId = "orderId",
|
||||||
|
ItemDesc = "Some description",
|
||||||
|
FullNotifications = true
|
||||||
|
}, Facade.Merchant);
|
||||||
|
|
||||||
|
cashCow = tester.ExplorerNode;
|
||||||
|
invoiceAddress = BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network);
|
||||||
|
firstPayment = Money.Coins(0.04m);
|
||||||
|
cashCow.SendToAddress(invoiceAddress, firstPayment);
|
||||||
|
Eventually(() =>
|
||||||
|
{
|
||||||
|
invoice = user.BitPay.GetInvoice(invoice.Id);
|
||||||
|
Assert.True(invoice.BtcPaid == firstPayment);
|
||||||
|
});
|
||||||
|
|
||||||
|
cashCow = tester.LTCExplorerNode;
|
||||||
|
var ltcCryptoInfo = invoice.CryptoInfo.FirstOrDefault(c => c.CryptoCode == "LTC");
|
||||||
|
Assert.NotNull(ltcCryptoInfo);
|
||||||
|
invoiceAddress = BitcoinAddress.Create(ltcCryptoInfo.Address, cashCow.Network);
|
||||||
|
var secondPayment = Money.Coins(decimal.Parse(ltcCryptoInfo.Due));
|
||||||
|
cashCow.Generate(2); // LTC is not worth a lot, so just to make sure we have money...
|
||||||
|
cashCow.SendToAddress(invoiceAddress, secondPayment);
|
||||||
|
|
||||||
|
Eventually(() =>
|
||||||
|
{
|
||||||
|
invoice = user.BitPay.GetInvoice(invoice.Id);
|
||||||
|
Assert.Equal(Money.Zero, invoice.BtcDue);
|
||||||
|
var ltcPaid = invoice.CryptoInfo.First(c => c.CryptoCode == "LTC");
|
||||||
|
Assert.Equal(Money.Zero, ltcPaid.Due);
|
||||||
|
Assert.Equal(secondPayment, ltcPaid.CryptoPaid);
|
||||||
|
Assert.Equal("paid", invoice.Status);
|
||||||
|
Assert.False((bool)((JValue)invoice.ExceptionStatus).Value);
|
||||||
|
});
|
||||||
|
|
||||||
|
controller = tester.PayTester.GetController<InvoiceController>(null);
|
||||||
|
checkout = (Models.InvoicingModels.PaymentModel)((JsonResult)controller.GetStatus(invoice.Id, "LTC").GetAwaiter().GetResult()).Value;
|
||||||
|
Assert.Equal(2, checkout.AvailableCryptos.Count);
|
||||||
|
Assert.Equal("LTC", checkout.CryptoCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void InvoiceFlowThroughDifferentStatesCorrectly()
|
public void InvoiceFlowThroughDifferentStatesCorrectly()
|
||||||
{
|
{
|
||||||
@@ -398,8 +485,6 @@ namespace BTCPayServer.Tests
|
|||||||
Currency = "USD",
|
Currency = "USD",
|
||||||
PosData = "posData",
|
PosData = "posData",
|
||||||
OrderId = "orderId",
|
OrderId = "orderId",
|
||||||
//RedirectURL = redirect + "redirect",
|
|
||||||
//NotificationURL = CallbackUri + "/notification",
|
|
||||||
ItemDesc = "Some description",
|
ItemDesc = "Some description",
|
||||||
FullNotifications = true
|
FullNotifications = true
|
||||||
}, Facade.Merchant);
|
}, Facade.Merchant);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
version: "3"
|
version: "3"
|
||||||
|
|
||||||
# Run `docker-compose up dev` for bootstrapping your development environment
|
# Run `docker-compose up dev` for bootstrapping your development environment
|
||||||
# Doing so will expose eclair API, NBXplorer, Bitcoind RPC and postgres port to the host so that tests can Run,
|
# Doing so will expose eclair API, NBXplorer, Bitcoind RPC and postgres port to the host so that tests can Run,
|
||||||
@@ -11,7 +11,8 @@ services:
|
|||||||
dockerfile: BTCPayServer.Tests/Dockerfile
|
dockerfile: BTCPayServer.Tests/Dockerfile
|
||||||
environment:
|
environment:
|
||||||
TESTS_RPCCONNECTION: server=http://bitcoind:43782;ceiwHEbqWI83:DwubwWsoo3
|
TESTS_RPCCONNECTION: server=http://bitcoind:43782;ceiwHEbqWI83:DwubwWsoo3
|
||||||
TESTS_BTCNBXPLORERURL: http://bitcoin-nbxplorer:32838/
|
TESTS_LTCRPCCONNECTION: server=http://litecoind:43782;ceiwHEbqWI83:DwubwWsoo3
|
||||||
|
TESTS_NBXPLORERURL: http://bitcoin-nbxplorer:32838/
|
||||||
TESTS_LTCNBXPLORERURL: http://litecoin-nbxplorer:32839/
|
TESTS_LTCNBXPLORERURL: http://litecoin-nbxplorer:32839/
|
||||||
TESTS_POSTGRES: User ID=postgres;Host=postgres;Port=5432;Database=btcpayserver
|
TESTS_POSTGRES: User ID=postgres;Host=postgres;Port=5432;Database=btcpayserver
|
||||||
TESTS_PORT: 80
|
TESTS_PORT: 80
|
||||||
|
|||||||
@@ -25,5 +25,9 @@ namespace BTCPayServer
|
|||||||
}
|
}
|
||||||
|
|
||||||
public string CryptoImagePath { get; set; }
|
public string CryptoImagePath { get; set; }
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return CryptoCode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
<Version>1.0.0.64</Version>
|
<Version>1.0.0.65</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Remove="Build\dockerfiles\**" />
|
<Compile Remove="Build\dockerfiles\**" />
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
<PackageReference Include="Hangfire.PostgreSql" Version="1.4.8.1" />
|
<PackageReference Include="Hangfire.PostgreSql" Version="1.4.8.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.2" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.2" />
|
||||||
<PackageReference Include="NBitcoin" Version="4.0.0.51" />
|
<PackageReference Include="NBitcoin" Version="4.0.0.51" />
|
||||||
<PackageReference Include="NBitpayClient" Version="1.0.0.14" />
|
<PackageReference Include="NBitpayClient" Version="1.0.0.16" />
|
||||||
<PackageReference Include="DBreeze" Version="1.87.0" />
|
<PackageReference Include="DBreeze" Version="1.87.0" />
|
||||||
<PackageReference Include="NBXplorer.Client" Version="1.0.0.28" />
|
<PackageReference Include="NBXplorer.Client" Version="1.0.0.28" />
|
||||||
<PackageReference Include="NicolasDorier.CommandLine" Version="1.0.0.1" />
|
<PackageReference Include="NicolasDorier.CommandLine" Version="1.0.0.1" />
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ namespace BTCPayServer.Configuration
|
|||||||
Logs.Configuration.LogInformation("Network: " + Network);
|
Logs.Configuration.LogInformation("Network: " + Network);
|
||||||
|
|
||||||
|
|
||||||
|
bool btcHandled = false;
|
||||||
foreach (var net in new BTCPayNetworkProvider(Network).GetAll())
|
foreach (var net in new BTCPayNetworkProvider(Network).GetAll())
|
||||||
{
|
{
|
||||||
var nbxplorer = NBXplorer.Configuration.NetworkInformation.GetNetworkByName(net.NBitcoinNetwork.Name);
|
var nbxplorer = NBXplorer.Configuration.NetworkInformation.GetNetworkByName(net.NBitcoinNetwork.Name);
|
||||||
@@ -54,12 +54,16 @@ namespace BTCPayServer.Configuration
|
|||||||
var cookieFile = conf.GetOrDefault<string>($"{net.CryptoCode}.explorer.cookiefile", nbxplorer.GetDefaultCookieFile());
|
var cookieFile = conf.GetOrDefault<string>($"{net.CryptoCode}.explorer.cookiefile", nbxplorer.GetDefaultCookieFile());
|
||||||
if (explorer != null)
|
if (explorer != null)
|
||||||
{
|
{
|
||||||
|
#pragma warning disable CS0618
|
||||||
|
if (net.IsBTC)
|
||||||
|
btcHandled = true;
|
||||||
|
#pragma warning restore CS0618
|
||||||
ExplorerFactories.Add(net.CryptoCode, (n) => CreateExplorerClient(n, explorer, cookieFile));
|
ExplorerFactories.Add(net.CryptoCode, (n) => CreateExplorerClient(n, explorer, cookieFile));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle legacy explorer.url and explorer.cookiefile
|
// Handle legacy explorer.url and explorer.cookiefile
|
||||||
if (ExplorerFactories.Count == 0)
|
if (!btcHandled)
|
||||||
{
|
{
|
||||||
var nbxplorer = NBXplorer.Configuration.NetworkInformation.GetNetworkByName(Network.Name); // Will get BTC info
|
var nbxplorer = NBXplorer.Configuration.NetworkInformation.GetNetworkByName(Network.Name); // Will get BTC info
|
||||||
var explorer = conf.GetOrDefault<Uri>($"explorer.url", new Uri(nbxplorer.GetDefaultExplorerUrl(), UriKind.Absolute));
|
var explorer = conf.GetOrDefault<Uri>($"explorer.url", new Uri(nbxplorer.GetDefaultExplorerUrl(), UriKind.Absolute));
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace BTCPayServer.Controllers
|
|||||||
StatusException = invoice.ExceptionStatus
|
StatusException = invoice.ExceptionStatus
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var data in invoice.GetCryptoData())
|
foreach (var data in invoice.GetCryptoData(null))
|
||||||
{
|
{
|
||||||
var cryptoInfo = dto.CryptoInfo.First(o => o.CryptoCode.Equals(data.Key, StringComparison.OrdinalIgnoreCase));
|
var cryptoInfo = dto.CryptoInfo.First(o => o.CryptoCode.Equals(data.Key, StringComparison.OrdinalIgnoreCase));
|
||||||
var accounting = data.Value.Calculate();
|
var accounting = data.Value.Calculate();
|
||||||
@@ -128,7 +128,7 @@ namespace BTCPayServer.Controllers
|
|||||||
var network = _NetworkProvider.GetNetwork(cryptoCode);
|
var network = _NetworkProvider.GetNetwork(cryptoCode);
|
||||||
if (invoice == null || network == null || !invoice.Support(network))
|
if (invoice == null || network == null || !invoice.Support(network))
|
||||||
return null;
|
return null;
|
||||||
var cryptoData = invoice.GetCryptoData(network);
|
var cryptoData = invoice.GetCryptoData(network, _NetworkProvider);
|
||||||
|
|
||||||
var dto = invoice.EntityToDTO(_NetworkProvider);
|
var dto = invoice.EntityToDTO(_NetworkProvider);
|
||||||
var cryptoInfo = dto.CryptoInfo.First(o => o.CryptoCode == network.CryptoCode);
|
var cryptoInfo = dto.CryptoInfo.First(o => o.CryptoCode == network.CryptoCode);
|
||||||
@@ -157,12 +157,15 @@ namespace BTCPayServer.Controllers
|
|||||||
Status = invoice.Status,
|
Status = invoice.Status,
|
||||||
CryptoImage = "/" + Url.Content(network.CryptoImagePath),
|
CryptoImage = "/" + Url.Content(network.CryptoImagePath),
|
||||||
NetworkFeeDescription = $"{accounting.TxCount} transaction{(accounting.TxCount > 1 ? "s" : "")} x {cryptoData.TxFee} {network.CryptoCode}",
|
NetworkFeeDescription = $"{accounting.TxCount} transaction{(accounting.TxCount > 1 ? "s" : "")} x {cryptoData.TxFee} {network.CryptoCode}",
|
||||||
AvailableCryptos = invoice.GetCryptoData().Select(kv=> new PaymentModel.AvailableCrypto()
|
AvailableCryptos = invoice.GetCryptoData(_NetworkProvider)
|
||||||
{
|
.Where(i => i.Value.Network != null)
|
||||||
CryptoCode = kv.Key,
|
.Select(kv=> new PaymentModel.AvailableCrypto()
|
||||||
CryptoImage = "/" + _NetworkProvider.GetNetwork(kv.Key).CryptoImagePath,
|
{
|
||||||
Link = Url.Action(nameof(Checkout), new { invoiceId = invoiceId, cryptoCode = kv.Key })
|
CryptoCode = kv.Key,
|
||||||
}).ToList()
|
CryptoImage = "/" + kv.Value.Network.CryptoImagePath,
|
||||||
|
Link = Url.Action(nameof(Checkout), new { invoiceId = invoiceId, cryptoCode = kv.Key })
|
||||||
|
}).Where(c => c.CryptoImage != "/")
|
||||||
|
.ToList()
|
||||||
};
|
};
|
||||||
|
|
||||||
var isMultiCurrency = invoice.GetPayments().Select(p=>p.GetCryptoCode()).Concat(new[] { network.CryptoCode }).Distinct().Count() > 1;
|
var isMultiCurrency = invoice.GetPayments().Select(p=>p.GetCryptoCode()).Concat(new[] { network.CryptoCode }).Distinct().Count() > 1;
|
||||||
|
|||||||
@@ -121,7 +121,9 @@ namespace BTCPayServer.Controllers
|
|||||||
Network: derivationStrategy.Network,
|
Network: derivationStrategy.Network,
|
||||||
RateProvider: _RateProviders.GetRateProvider(derivationStrategy.Network),
|
RateProvider: _RateProviders.GetRateProvider(derivationStrategy.Network),
|
||||||
FeeRateProvider: _FeeProviderFactory.CreateFeeProvider(derivationStrategy.Network)))
|
FeeRateProvider: _FeeProviderFactory.CreateFeeProvider(derivationStrategy.Network)))
|
||||||
.Where(_ => _.Wallet != null && _.FeeRateProvider != null && _.RateProvider != null)
|
.Where(_ => _.Wallet != null &&
|
||||||
|
_.FeeRateProvider != null &&
|
||||||
|
_.RateProvider != null)
|
||||||
.Select(_ =>
|
.Select(_ =>
|
||||||
{
|
{
|
||||||
return new
|
return new
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace BTCPayServer.Events
|
|||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
String address = ScriptPubKey.GetDestinationAddress(Network.NBitcoinNetwork)?.ToString() ?? ScriptPubKey.ToString();
|
String address = ScriptPubKey.GetDestinationAddress(Network.NBitcoinNetwork)?.ToString() ?? ScriptPubKey.ToString();
|
||||||
return $"{address} received a transaction";
|
return $"{address} received a transaction ({Network.CryptoCode})";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,11 +37,11 @@ namespace BTCPayServer
|
|||||||
return activeProvider != "Microsoft.EntityFrameworkCore.Sqlite";
|
return activeProvider != "Microsoft.EntityFrameworkCore.Sqlite";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<Dictionary<uint256, TransactionResult>> GetTransactions(this BTCPayWallet client, BTCPayNetwork network, uint256[] hashes, CancellationToken cts = default(CancellationToken))
|
public static async Task<Dictionary<uint256, TransactionResult>> GetTransactions(this BTCPayWallet client, uint256[] hashes, CancellationToken cts = default(CancellationToken))
|
||||||
{
|
{
|
||||||
hashes = hashes.Distinct().ToArray();
|
hashes = hashes.Distinct().ToArray();
|
||||||
var transactions = hashes
|
var transactions = hashes
|
||||||
.Select(async o => await client.GetTransactionAsync(network, o, cts))
|
.Select(async o => await client.GetTransactionAsync(o, cts))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
await Task.WhenAll(transactions).ConfigureAwait(false);
|
await Task.WhenAll(transactions).ConfigureAwait(false);
|
||||||
return transactions.Select(t => t.Result).Where(t => t != null).ToDictionary(o => o.Transaction.GetHash());
|
return transactions.Select(t => t.Result).Where(t => t != null).ToDictionary(o => o.Transaction.GetHash());
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ namespace BTCPayServer.HostedServices
|
|||||||
}
|
}
|
||||||
|
|
||||||
var derivationStrategies = invoice.GetDerivationStrategies(_NetworkProvider).ToArray();
|
var derivationStrategies = invoice.GetDerivationStrategies(_NetworkProvider).ToArray();
|
||||||
var payments = await GetPaymentsWithTransaction(derivationStrategies, invoice);
|
var payments = await GetPaymentsWithTransaction(null, derivationStrategies, invoice);
|
||||||
foreach (Task<NetworkCoins> coinsAsync in GetCoinsPerNetwork(context, invoice, derivationStrategies))
|
foreach (Task<NetworkCoins> coinsAsync in GetCoinsPerNetwork(context, invoice, derivationStrategies))
|
||||||
{
|
{
|
||||||
var coins = await coinsAsync;
|
var coins = await coinsAsync;
|
||||||
@@ -173,11 +173,11 @@ namespace BTCPayServer.HostedServices
|
|||||||
}
|
}
|
||||||
if (dirtyAddress)
|
if (dirtyAddress)
|
||||||
{
|
{
|
||||||
payments = await GetPaymentsWithTransaction(derivationStrategies, invoice);
|
payments = await GetPaymentsWithTransaction(payments, derivationStrategies, invoice);
|
||||||
}
|
}
|
||||||
var network = coins.Wallet.Network;
|
var network = coins.Wallet.Network;
|
||||||
var cryptoData = invoice.GetCryptoData(network);
|
var cryptoData = invoice.GetCryptoData(network, _NetworkProvider);
|
||||||
var cryptoDataAll = invoice.GetCryptoData();
|
var cryptoDataAll = invoice.GetCryptoData(_NetworkProvider);
|
||||||
var accounting = cryptoData.Calculate();
|
var accounting = cryptoData.Calculate();
|
||||||
|
|
||||||
if (invoice.Status == "new" || invoice.Status == "expired")
|
if (invoice.Status == "new" || invoice.Status == "expired")
|
||||||
@@ -222,7 +222,7 @@ namespace BTCPayServer.HostedServices
|
|||||||
|
|
||||||
if (invoice.Status == "paid")
|
if (invoice.Status == "paid")
|
||||||
{
|
{
|
||||||
var transactions = payments;
|
IEnumerable<AccountedPaymentEntity> transactions = payments;
|
||||||
if (invoice.SpeedPolicy == SpeedPolicy.HighSpeed)
|
if (invoice.SpeedPolicy == SpeedPolicy.HighSpeed)
|
||||||
{
|
{
|
||||||
transactions = transactions.Where(t => t.Confirmations >= 1 || !t.Transaction.RBF);
|
transactions = transactions.Where(t => t.Confirmations >= 1 || !t.Transaction.RBF);
|
||||||
@@ -260,7 +260,7 @@ namespace BTCPayServer.HostedServices
|
|||||||
|
|
||||||
if (invoice.Status == "confirmed")
|
if (invoice.Status == "confirmed")
|
||||||
{
|
{
|
||||||
var transactions = payments;
|
IEnumerable<AccountedPaymentEntity> transactions = payments;
|
||||||
transactions = transactions.Where(t => t.Confirmations >= 6);
|
transactions = transactions.Where(t => t.Confirmations >= 6);
|
||||||
var totalConfirmed = transactions.Select(t => t.Payment.GetValue(cryptoDataAll, cryptoData.CryptoCode)).Sum();
|
var totalConfirmed = transactions.Select(t => t.Payment.GetValue(cryptoDataAll, cryptoData.CryptoCode)).Sum();
|
||||||
if (totalConfirmed >= accounting.TotalDue)
|
if (totalConfirmed >= accounting.TotalDue)
|
||||||
@@ -292,21 +292,59 @@ namespace BTCPayServer.HostedServices
|
|||||||
.ToArray();
|
.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IEnumerable<AccountedPaymentEntity>> GetPaymentsWithTransaction(DerivationStrategy[] derivations, InvoiceEntity invoice)
|
|
||||||
|
class AccountedPaymentEntities : List<AccountedPaymentEntity>
|
||||||
|
{
|
||||||
|
public AccountedPaymentEntities(AccountedPaymentEntities existing)
|
||||||
|
{
|
||||||
|
if (existing != null)
|
||||||
|
_Transactions = existing._Transactions;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<uint256, TransactionResult> _Transactions = new Dictionary<uint256, TransactionResult>();
|
||||||
|
|
||||||
|
public void AddToCache(IEnumerable<TransactionResult> transactions)
|
||||||
|
{
|
||||||
|
foreach (var tx in transactions)
|
||||||
|
_Transactions.TryAdd(tx.Transaction.GetHash(), tx);
|
||||||
|
}
|
||||||
|
public TransactionResult GetTransaction(uint256 txId)
|
||||||
|
{
|
||||||
|
_Transactions.TryGetValue(txId, out TransactionResult result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal IEnumerable<TransactionResult> GetTransactions()
|
||||||
|
{
|
||||||
|
return _Transactions.Values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private async Task<AccountedPaymentEntities> GetPaymentsWithTransaction(AccountedPaymentEntities previous, DerivationStrategy[] derivations, InvoiceEntity invoice)
|
||||||
{
|
{
|
||||||
List<PaymentEntity> updatedPaymentEntities = new List<PaymentEntity>();
|
List<PaymentEntity> updatedPaymentEntities = new List<PaymentEntity>();
|
||||||
List<AccountedPaymentEntity> accountedPayments = new List<AccountedPaymentEntity>();
|
AccountedPaymentEntities accountedPayments = new AccountedPaymentEntities(previous);
|
||||||
foreach (var network in derivations.Select(d => d.Network))
|
foreach (var network in derivations.Select(d => d.Network))
|
||||||
{
|
{
|
||||||
var wallet = _WalletProvider.GetWallet(network);
|
var wallet = _WalletProvider.GetWallet(network);
|
||||||
if (wallet == null)
|
if (wallet == null)
|
||||||
continue;
|
continue;
|
||||||
var transactions = await wallet.GetTransactions(network, invoice.GetPayments(network).Select(t => t.Outpoint.Hash).ToArray());
|
|
||||||
var conflicts = GetConflicts(transactions.Select(t => t.Value));
|
var hashesToFetch = new HashSet<uint256>(invoice
|
||||||
|
.GetPayments(network)
|
||||||
|
.Select(t => t.Outpoint.Hash)
|
||||||
|
.Where(h => accountedPayments?.GetTransaction(h) == null)
|
||||||
|
.ToList());
|
||||||
|
|
||||||
|
|
||||||
|
if (hashesToFetch.Count > 0)
|
||||||
|
{
|
||||||
|
accountedPayments.AddToCache((await wallet.GetTransactions(hashesToFetch.ToArray())).Select(t => t.Value));
|
||||||
|
}
|
||||||
|
var conflicts = GetConflicts(accountedPayments.GetTransactions());
|
||||||
foreach (var payment in invoice.GetPayments(network))
|
foreach (var payment in invoice.GetPayments(network))
|
||||||
{
|
{
|
||||||
TransactionResult tx;
|
TransactionResult tx = accountedPayments.GetTransaction(payment.Outpoint.Hash);
|
||||||
if (!transactions.TryGetValue(payment.Outpoint.Hash, out tx))
|
if (tx == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
AccountedPaymentEntity accountedPayment = new AccountedPaymentEntity()
|
AccountedPaymentEntity accountedPayment = new AccountedPaymentEntity()
|
||||||
|
|||||||
@@ -37,81 +37,6 @@ namespace BTCPayServer.Models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class InvoiceCryptoInfo
|
|
||||||
{
|
|
||||||
[JsonProperty("cryptoCode")]
|
|
||||||
public string CryptoCode { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("rate")]
|
|
||||||
public decimal Rate { get; set; }
|
|
||||||
|
|
||||||
//"exRates":{"USD":4320.02}
|
|
||||||
[JsonProperty("exRates")]
|
|
||||||
public Dictionary<string, double> ExRates
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
//"btcPaid":"0.000000"
|
|
||||||
[JsonProperty("paid")]
|
|
||||||
public string Paid
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
//"btcPrice":"0.001157"
|
|
||||||
[JsonProperty("price")]
|
|
||||||
public string Price
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
//"btcDue":"0.001160"
|
|
||||||
/// <summary>
|
|
||||||
/// Amount of crypto remaining to pay this invoice
|
|
||||||
/// </summary>
|
|
||||||
[JsonProperty("due")]
|
|
||||||
public string Due
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonProperty("paymentUrls")]
|
|
||||||
public NBitpayClient.InvoicePaymentUrls PaymentUrls
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonProperty("address")]
|
|
||||||
public string Address { get; set; }
|
|
||||||
[JsonProperty("url")]
|
|
||||||
public string Url { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Total amount of this invoice
|
|
||||||
/// </summary>
|
|
||||||
[JsonProperty("totalDue")]
|
|
||||||
public string TotalDue { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Total amount of network fee to pay to the invoice
|
|
||||||
/// </summary>
|
|
||||||
[JsonProperty("networkFee")]
|
|
||||||
public string NetworkFee { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Number of transactions required to pay
|
|
||||||
/// </summary>
|
|
||||||
[JsonProperty("txCount")]
|
|
||||||
public int TxCount { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Total amount of the invoice paid in this crypto
|
|
||||||
/// </summary>
|
|
||||||
[JsonProperty("cryptoPaid")]
|
|
||||||
public Money CryptoPaid { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
//{"facade":"pos/invoice","data":{,}}
|
//{"facade":"pos/invoice","data":{,}}
|
||||||
public class InvoiceResponse
|
public class InvoiceResponse
|
||||||
{
|
{
|
||||||
@@ -151,7 +76,7 @@ namespace BTCPayServer.Models
|
|||||||
}
|
}
|
||||||
|
|
||||||
[JsonProperty("cryptoInfo")]
|
[JsonProperty("cryptoInfo")]
|
||||||
public List<InvoiceCryptoInfo> CryptoInfo { get; set; }
|
public List<NBitpayClient.InvoiceCryptoInfo> CryptoInfo { get; set; }
|
||||||
|
|
||||||
//"price":5
|
//"price":5
|
||||||
[JsonProperty("price")]
|
[JsonProperty("price")]
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using BTCPayServer.Models;
|
using BTCPayServer.Models;
|
||||||
using NBitpayClient;
|
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using NBitcoin.DataEncoders;
|
using NBitcoin.DataEncoders;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
@@ -234,7 +233,7 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
}
|
}
|
||||||
public List<PaymentEntity> GetPayments(string cryptoCode)
|
public List<PaymentEntity> GetPayments(string cryptoCode)
|
||||||
{
|
{
|
||||||
return Payments.Where(p=>p.CryptoCode == cryptoCode).ToList();
|
return Payments.Where(p => p.CryptoCode == cryptoCode).ToList();
|
||||||
}
|
}
|
||||||
public List<PaymentEntity> GetPayments(BTCPayNetwork network)
|
public List<PaymentEntity> GetPayments(BTCPayNetwork network)
|
||||||
{
|
{
|
||||||
@@ -323,11 +322,11 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
Flags = new Flags() { Refundable = Refundable }
|
Flags = new Flags() { Refundable = Refundable }
|
||||||
};
|
};
|
||||||
|
|
||||||
dto.CryptoInfo = new List<InvoiceCryptoInfo>();
|
dto.CryptoInfo = new List<NBitpayClient.InvoiceCryptoInfo>();
|
||||||
foreach (var info in this.GetCryptoData().Values)
|
foreach (var info in this.GetCryptoData(networkProvider).Values)
|
||||||
{
|
{
|
||||||
var accounting = info.Calculate();
|
var accounting = info.Calculate();
|
||||||
var cryptoInfo = new InvoiceCryptoInfo();
|
var cryptoInfo = new NBitpayClient.InvoiceCryptoInfo();
|
||||||
cryptoInfo.CryptoCode = info.CryptoCode;
|
cryptoInfo.CryptoCode = info.CryptoCode;
|
||||||
cryptoInfo.Rate = info.Rate;
|
cryptoInfo.Rate = info.Rate;
|
||||||
cryptoInfo.Price = Money.Coins(ProductInformation.Price / cryptoInfo.Rate).ToString();
|
cryptoInfo.Price = Money.Coins(ProductInformation.Price / cryptoInfo.Rate).ToString();
|
||||||
@@ -337,7 +336,7 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
cryptoInfo.TotalDue = accounting.TotalDue.ToString();
|
cryptoInfo.TotalDue = accounting.TotalDue.ToString();
|
||||||
cryptoInfo.NetworkFee = accounting.NetworkFee.ToString();
|
cryptoInfo.NetworkFee = accounting.NetworkFee.ToString();
|
||||||
cryptoInfo.TxCount = accounting.TxCount;
|
cryptoInfo.TxCount = accounting.TxCount;
|
||||||
cryptoInfo.CryptoPaid = accounting.CryptoPaid;
|
cryptoInfo.CryptoPaid = accounting.CryptoPaid.ToString();
|
||||||
|
|
||||||
cryptoInfo.Address = info.DepositAddress;
|
cryptoInfo.Address = info.DepositAddress;
|
||||||
cryptoInfo.ExRates = new Dictionary<string, double>
|
cryptoInfo.ExRates = new Dictionary<string, double>
|
||||||
@@ -345,12 +344,12 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
{ ProductInformation.Currency, (double)cryptoInfo.Rate }
|
{ ProductInformation.Currency, (double)cryptoInfo.Rate }
|
||||||
};
|
};
|
||||||
|
|
||||||
var scheme = networkProvider.GetNetwork(info.CryptoCode)?.UriScheme ?? "BTC";
|
var scheme = info.Network.UriScheme;
|
||||||
var cryptoSuffix = cryptoInfo.CryptoCode == "BTC" ? "" : "/" + cryptoInfo.CryptoCode;
|
var cryptoSuffix = cryptoInfo.CryptoCode == "BTC" ? "" : "/" + cryptoInfo.CryptoCode;
|
||||||
cryptoInfo.Url = ServerUrl.WithTrailingSlash() + $"invoice{cryptoSuffix}?id=" + Id;
|
cryptoInfo.Url = ServerUrl.WithTrailingSlash() + $"invoice{cryptoSuffix}?id=" + Id;
|
||||||
|
|
||||||
|
|
||||||
cryptoInfo.PaymentUrls = new InvoicePaymentUrls()
|
cryptoInfo.PaymentUrls = new NBitpayClient.InvoicePaymentUrls()
|
||||||
{
|
{
|
||||||
BIP72 = $"{scheme}:{cryptoInfo.Address}?amount={cryptoInfo.Due}&r={ServerUrl.WithTrailingSlash() + ($"i/{Id}{cryptoSuffix}")}",
|
BIP72 = $"{scheme}:{cryptoInfo.Address}?amount={cryptoInfo.Due}&r={ServerUrl.WithTrailingSlash() + ($"i/{Id}{cryptoSuffix}")}",
|
||||||
BIP72b = $"{scheme}:?r={ServerUrl.WithTrailingSlash() + ($"i/{Id}{cryptoSuffix}")}",
|
BIP72b = $"{scheme}:?r={ServerUrl.WithTrailingSlash() + ($"i/{Id}{cryptoSuffix}")}",
|
||||||
@@ -392,23 +391,23 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
|
|
||||||
internal bool Support(BTCPayNetwork network)
|
internal bool Support(BTCPayNetwork network)
|
||||||
{
|
{
|
||||||
var rates = GetCryptoData();
|
var rates = GetCryptoData(null);
|
||||||
return rates.TryGetValue(network.CryptoCode, out var data);
|
return rates.TryGetValue(network.CryptoCode, out var data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CryptoData GetCryptoData(string cryptoCode)
|
public CryptoData GetCryptoData(string cryptoCode, BTCPayNetworkProvider networkProvider)
|
||||||
{
|
{
|
||||||
GetCryptoData().TryGetValue(cryptoCode, out var data);
|
GetCryptoData(networkProvider).TryGetValue(cryptoCode, out var data);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CryptoData GetCryptoData(BTCPayNetwork network)
|
public CryptoData GetCryptoData(BTCPayNetwork network, BTCPayNetworkProvider networkProvider)
|
||||||
{
|
{
|
||||||
GetCryptoData().TryGetValue(network.CryptoCode, out var data);
|
GetCryptoData(networkProvider).TryGetValue(network.CryptoCode, out var data);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Dictionary<string, CryptoData> GetCryptoData()
|
public Dictionary<string, CryptoData> GetCryptoData(BTCPayNetworkProvider networkProvider)
|
||||||
{
|
{
|
||||||
Dictionary<string, CryptoData> rates = new Dictionary<string, CryptoData>();
|
Dictionary<string, CryptoData> rates = new Dictionary<string, CryptoData>();
|
||||||
var serializer = new Serializer(Dummy);
|
var serializer = new Serializer(Dummy);
|
||||||
@@ -416,7 +415,8 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
// Legacy
|
// Legacy
|
||||||
if (Rate != 0.0m)
|
if (Rate != 0.0m)
|
||||||
{
|
{
|
||||||
rates.TryAdd("BTC", new CryptoData() { ParentEntity = this, Rate = Rate, CryptoCode = "BTC", TxFee = TxFee, FeeRate = new FeeRate(TxFee, 100), DepositAddress = DepositAddress });
|
var btcNetwork = networkProvider?.GetNetwork("BTC");
|
||||||
|
rates.TryAdd("BTC", new CryptoData() { ParentEntity = this, Rate = Rate, CryptoCode = "BTC", TxFee = TxFee, FeeRate = new FeeRate(TxFee, 100), DepositAddress = DepositAddress, Network = btcNetwork });
|
||||||
}
|
}
|
||||||
if (CryptoData != null)
|
if (CryptoData != null)
|
||||||
{
|
{
|
||||||
@@ -425,6 +425,7 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
var r = serializer.ToObject<CryptoData>(prop.Value.ToString());
|
var r = serializer.ToObject<CryptoData>(prop.Value.ToString());
|
||||||
r.CryptoCode = prop.Name;
|
r.CryptoCode = prop.Name;
|
||||||
r.ParentEntity = this;
|
r.ParentEntity = this;
|
||||||
|
r.Network = networkProvider?.GetNetwork(r.CryptoCode);
|
||||||
rates.TryAdd(r.CryptoCode, r);
|
rates.TryAdd(r.CryptoCode, r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -433,6 +434,14 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
}
|
}
|
||||||
|
|
||||||
Network Dummy = Network.Main;
|
Network Dummy = Network.Main;
|
||||||
|
|
||||||
|
public void SetCryptoData(CryptoData cryptoData)
|
||||||
|
{
|
||||||
|
var dict = GetCryptoData(null);
|
||||||
|
dict.AddOrReplace(cryptoData.CryptoCode, cryptoData);
|
||||||
|
SetCryptoData(dict);
|
||||||
|
}
|
||||||
|
|
||||||
public void SetCryptoData(Dictionary<string, CryptoData> cryptoData)
|
public void SetCryptoData(Dictionary<string, CryptoData> cryptoData)
|
||||||
{
|
{
|
||||||
var obj = new JObject();
|
var obj = new JObject();
|
||||||
@@ -485,6 +494,8 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
{
|
{
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public InvoiceEntity ParentEntity { get; set; }
|
public InvoiceEntity ParentEntity { get; set; }
|
||||||
|
[JsonIgnore]
|
||||||
|
public BTCPayNetwork Network { get; set; }
|
||||||
[JsonProperty(PropertyName = "cryptoCode", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
[JsonProperty(PropertyName = "cryptoCode", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||||
public string CryptoCode { get; set; }
|
public string CryptoCode { get; set; }
|
||||||
[JsonProperty(PropertyName = "rate")]
|
[JsonProperty(PropertyName = "rate")]
|
||||||
@@ -498,7 +509,7 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
|
|
||||||
public CryptoDataAccounting Calculate()
|
public CryptoDataAccounting Calculate()
|
||||||
{
|
{
|
||||||
var cryptoData = ParentEntity.GetCryptoData();
|
var cryptoData = ParentEntity.GetCryptoData(null);
|
||||||
var totalDue = Money.Coins(ParentEntity.ProductInformation.Price / Rate);
|
var totalDue = Money.Coins(ParentEntity.ProductInformation.Price / Rate);
|
||||||
var paid = Money.Zero;
|
var paid = Money.Zero;
|
||||||
var cryptoPaid = Money.Zero;
|
var cryptoPaid = Money.Zero;
|
||||||
|
|||||||
@@ -125,16 +125,15 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
CustomerEmail = invoice.RefundMail
|
CustomerEmail = invoice.RefundMail
|
||||||
});
|
});
|
||||||
|
|
||||||
foreach (var cryptoData in invoice.GetCryptoData().Values)
|
foreach (var cryptoData in invoice.GetCryptoData(networkProvider).Values)
|
||||||
{
|
{
|
||||||
var network = networkProvider.GetNetwork(cryptoData.CryptoCode);
|
if (cryptoData.Network == null)
|
||||||
if (network == null)
|
|
||||||
throw new InvalidOperationException("CryptoCode unsupported");
|
throw new InvalidOperationException("CryptoCode unsupported");
|
||||||
context.AddressInvoices.Add(new AddressInvoiceData()
|
context.AddressInvoices.Add(new AddressInvoiceData()
|
||||||
{
|
{
|
||||||
InvoiceDataId = invoice.Id,
|
InvoiceDataId = invoice.Id,
|
||||||
CreatedTime = DateTimeOffset.UtcNow,
|
CreatedTime = DateTimeOffset.UtcNow,
|
||||||
}.SetHash(BitcoinAddress.Create(cryptoData.DepositAddress, network.NBitcoinNetwork).ScriptPubKey.Hash, network.CryptoCode));
|
}.SetHash(BitcoinAddress.Create(cryptoData.DepositAddress, cryptoData.Network.NBitcoinNetwork).ScriptPubKey.Hash, cryptoData.CryptoCode));
|
||||||
context.HistoricalAddressInvoices.Add(new HistoricalAddressInvoiceData()
|
context.HistoricalAddressInvoices.Add(new HistoricalAddressInvoiceData()
|
||||||
{
|
{
|
||||||
InvoiceDataId = invoice.Id,
|
InvoiceDataId = invoice.Id,
|
||||||
@@ -169,8 +168,7 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
var invoiceEntity = ToObject<InvoiceEntity>(invoice.Blob);
|
var invoiceEntity = ToObject<InvoiceEntity>(invoice.Blob);
|
||||||
var cryptoData = invoiceEntity.GetCryptoData();
|
var currencyData = invoiceEntity.GetCryptoData(network, null);
|
||||||
var currencyData = cryptoData.Where(c => c.Value.CryptoCode == network.CryptoCode).Select(f => f.Value).FirstOrDefault();
|
|
||||||
if (currencyData == null)
|
if (currencyData == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -187,7 +185,7 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
invoiceEntity.DepositAddress = currencyData.DepositAddress;
|
invoiceEntity.DepositAddress = currencyData.DepositAddress;
|
||||||
}
|
}
|
||||||
#pragma warning restore CS0618
|
#pragma warning restore CS0618
|
||||||
invoiceEntity.SetCryptoData(cryptoData);
|
invoiceEntity.SetCryptoData(currencyData);
|
||||||
invoice.Blob = ToBytes(invoiceEntity);
|
invoice.Blob = ToBytes(invoiceEntity);
|
||||||
|
|
||||||
context.AddressInvoices.Add(new AddressInvoiceData() {
|
context.AddressInvoices.Add(new AddressInvoiceData() {
|
||||||
@@ -207,7 +205,7 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
|
|
||||||
private static void MarkUnassigned(string invoiceId, InvoiceEntity entity, ApplicationDbContext context, string cryptoCode)
|
private static void MarkUnassigned(string invoiceId, InvoiceEntity entity, ApplicationDbContext context, string cryptoCode)
|
||||||
{
|
{
|
||||||
foreach (var address in entity.GetCryptoData())
|
foreach (var address in entity.GetCryptoData(null))
|
||||||
{
|
{
|
||||||
if (cryptoCode != null && cryptoCode != address.Value.CryptoCode)
|
if (cryptoCode != null && cryptoCode != address.Value.CryptoCode)
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -37,13 +37,7 @@ namespace BTCPayServer.Services.Rates
|
|||||||
return _Inner.GetRateAsync(currency);
|
return _Inner.GetRateAsync(currency);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryGetFromCache(string key, out object obj)
|
|
||||||
{
|
|
||||||
obj = _MemoryCache.Get(key);
|
|
||||||
return obj != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<ICollection<Rate>> GetRatesAsync()
|
public Task<ICollection<Rate>> GetRatesAsync()
|
||||||
{
|
{
|
||||||
return _MemoryCache.GetOrCreateAsync("GLOBAL_RATES", (ICacheEntry entry) =>
|
return _MemoryCache.GetOrCreateAsync("GLOBAL_RATES", (ICacheEntry entry) =>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using System.Threading.Tasks;
|
|||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using NBXplorer.Models;
|
using NBXplorer.Models;
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
|
||||||
namespace BTCPayServer.Services.Wallets
|
namespace BTCPayServer.Services.Wallets
|
||||||
{
|
{
|
||||||
@@ -51,6 +52,8 @@ namespace BTCPayServer.Services.Wallets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TimeSpan CacheSpan { get; private set; } = TimeSpan.FromMinutes(60);
|
||||||
|
|
||||||
public async Task<BitcoinAddress> ReserveAddressAsync(DerivationStrategyBase derivationStrategy)
|
public async Task<BitcoinAddress> ReserveAddressAsync(DerivationStrategyBase derivationStrategy)
|
||||||
{
|
{
|
||||||
var pathInfo = await _Client.GetUnusedAsync(derivationStrategy, DerivationFeature.Deposit, 0, true).ConfigureAwait(false);
|
var pathInfo = await _Client.GetUnusedAsync(derivationStrategy, DerivationFeature.Deposit, 0, true).ConfigureAwait(false);
|
||||||
@@ -62,10 +65,8 @@ namespace BTCPayServer.Services.Wallets
|
|||||||
await _Client.TrackAsync(derivationStrategy);
|
await _Client.TrackAsync(derivationStrategy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<TransactionResult> GetTransactionAsync(BTCPayNetwork network, uint256 txId, CancellationToken cancellation = default(CancellationToken))
|
public Task<TransactionResult> GetTransactionAsync(uint256 txId, CancellationToken cancellation = default(CancellationToken))
|
||||||
{
|
{
|
||||||
if (network == null)
|
|
||||||
throw new ArgumentNullException(nameof(network));
|
|
||||||
if (txId == null)
|
if (txId == null)
|
||||||
throw new ArgumentNullException(nameof(txId));
|
throw new ArgumentNullException(nameof(txId));
|
||||||
return _Client.GetTransactionAsync(txId, cancellation);
|
return _Client.GetTransactionAsync(txId, cancellation);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
|
||||||
namespace BTCPayServer.Services.Wallets
|
namespace BTCPayServer.Services.Wallets
|
||||||
{
|
{
|
||||||
@@ -29,7 +30,7 @@ namespace BTCPayServer.Services.Wallets
|
|||||||
throw new ArgumentNullException(nameof(cryptoCode));
|
throw new ArgumentNullException(nameof(cryptoCode));
|
||||||
var network = _NetworkProvider.GetNetwork(cryptoCode);
|
var network = _NetworkProvider.GetNetwork(cryptoCode);
|
||||||
var client = _Client.GetExplorerClient(cryptoCode);
|
var client = _Client.GetExplorerClient(cryptoCode);
|
||||||
if (network == null && client == null)
|
if (network == null || client == null)
|
||||||
return null;
|
return null;
|
||||||
return new BTCPayWallet(client, network);
|
return new BTCPayWallet(client, network);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user