diff --git a/BTCPayServer.Tests/UnitTest1.cs b/BTCPayServer.Tests/UnitTest1.cs index d8f9b24d1..eab823615 100644 --- a/BTCPayServer.Tests/UnitTest1.cs +++ b/BTCPayServer.Tests/UnitTest1.cs @@ -355,7 +355,7 @@ namespace BTCPayServer.Tests } } - [Fact] + [Fact(Timeout = 60 * 1000)] [Trait("Integration", "Integration")] public async Task CanSetLightningServer() { @@ -407,7 +407,7 @@ namespace BTCPayServer.Tests await ProcessLightningPayment(LightningConnectionType.Charge); } - [Fact] + [Fact(Timeout = 60 * 1000)] [Trait("Integration", "Integration")] public async Task CanSendLightningPaymentLnd() { @@ -1362,7 +1362,7 @@ namespace BTCPayServer.Tests } } - [Fact] + [Fact(Timeout = 60 * 1000)] [Trait("Integration", "Integration")] public async Task CanSetPaymentMethodLimits() { diff --git a/BTCPayServer.Tests/docker-compose.yml b/BTCPayServer.Tests/docker-compose.yml index ba03e445e..e7089172d 100644 --- a/BTCPayServer.Tests/docker-compose.yml +++ b/BTCPayServer.Tests/docker-compose.yml @@ -93,12 +93,13 @@ services: - bitcoind - litecoind + bitcoind: + restart: unless-stopped image: btcpayserver/bitcoin:0.17.0 environment: BITCOIN_NETWORK: regtest - BITCOIN_EXTRA_ARGS: | - deprecatedrpc=signrawtransaction + BITCOIN_EXTRA_ARGS: |- rpcuser=ceiwHEbqWI83 rpcpassword=DwubwWsoo3 rpcport=43782 @@ -106,9 +107,9 @@ services: whitelist=0.0.0.0/0 zmqpubrawblock=tcp://0.0.0.0:28332 zmqpubrawtx=tcp://0.0.0.0:28333 + deprecatedrpc=signrawtransaction ports: - "43782:43782" - - "28332:28332" expose: - "43782" # RPC - "39388" # P2P @@ -188,13 +189,13 @@ services: - bitcoind litecoind: + restart: unless-stopped image: nicolasdorier/docker-litecoin:0.16.3 environment: - BITCOIN_EXTRA_ARGS: | + BITCOIN_EXTRA_ARGS: |- rpcuser=ceiwHEbqWI83 rpcpassword=DwubwWsoo3 regtest=1 - server=1 rpcport=43782 port=39388 whitelist=0.0.0.0/0 @@ -221,13 +222,16 @@ services: - MYSQL_ALLOW_EMPTY_PASSWORD=yes merchant_lnd: - image: btcpayserver/lnd:0.5-beta-2 + image: btcpayserver/lnd:v0.5.1-beta restart: unless-stopped environment: LND_CHAIN: "btc" LND_ENVIRONMENT: "regtest" + LND_EXPLORERURL: "http://nbxplorer:32838/" LND_EXTRA_ARGS: | restlisten=0.0.0.0:8080 + rpclisten=127.0.0.1:10008 + rpclisten=0.0.0.0:10009 bitcoin.node=bitcoind bitcoind.rpchost=bitcoind:43782 bitcoind.zmqpubrawblock=tcp://bitcoind:28332 @@ -248,13 +252,16 @@ services: - bitcoind customer_lnd: - image: btcpayserver/lnd:0.5-beta-2 + image: btcpayserver/lnd:v0.5.1-beta restart: unless-stopped environment: LND_CHAIN: "btc" LND_ENVIRONMENT: "regtest" + LND_EXPLORERURL: "http://nbxplorer:32838/" LND_EXTRA_ARGS: | restlisten=0.0.0.0:8080 + rpclisten=127.0.0.1:10008 + rpclisten=0.0.0.0:10009 bitcoin.node=bitcoind bitcoind.rpchost=bitcoind:43782 bitcoind.zmqpubrawblock=tcp://bitcoind:28332 diff --git a/BTCPayServer.Tests/docker-entrypoint.sh b/BTCPayServer.Tests/docker-entrypoint.sh index ec8b479b5..7a7c29dcc 100755 --- a/BTCPayServer.Tests/docker-entrypoint.sh +++ b/BTCPayServer.Tests/docker-entrypoint.sh @@ -2,4 +2,4 @@ set -e dotnet test --filter Fast=Fast --no-build -dotnet test --filter Integration=Integration --no-build +dotnet test --filter Integration=Integration --no-build -v n diff --git a/BTCPayServer.Tests/xunit.runner.json b/BTCPayServer.Tests/xunit.runner.json index dc14f82b1..3b632dd96 100644 --- a/BTCPayServer.Tests/xunit.runner.json +++ b/BTCPayServer.Tests/xunit.runner.json @@ -1,3 +1,5 @@ { - "parallelizeTestCollections": false -} \ No newline at end of file + "parallelizeTestCollections": false, + "longRunningTestSeconds": 60, + "diagnosticMessages": true +} diff --git a/BTCPayServer/BTCPayNetworkProvider.Groestlcoin.cs b/BTCPayServer/BTCPayNetworkProvider.Groestlcoin.cs index ce9c7305b..ec63bdfc1 100644 --- a/BTCPayServer/BTCPayNetworkProvider.Groestlcoin.cs +++ b/BTCPayServer/BTCPayNetworkProvider.Groestlcoin.cs @@ -26,7 +26,7 @@ namespace BTCPayServer "GRS_BTC = bittrex(GRS_BTC)" }, CryptoImagePath = "imlegacy/groestlcoin.png", - LightningImagePath = "imlegacy/groestlcoin-lightning.png", + LightningImagePath = "imlegacy/groestlcoin-lightning.svg", DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType), CoinType = NetworkType == NetworkType.Mainnet ? new KeyPath("17'") : new KeyPath("1'") }); diff --git a/BTCPayServer/BTCPayServer.csproj b/BTCPayServer/BTCPayServer.csproj index 36e7d9a28..73239a3ec 100644 --- a/BTCPayServer/BTCPayServer.csproj +++ b/BTCPayServer/BTCPayServer.csproj @@ -2,7 +2,7 @@ Exe netcoreapp2.1 - 1.0.3.31 + 1.0.3.33 NU1701,CA1816,CA1308,CA1810,CA2208 @@ -33,7 +33,7 @@ - + @@ -136,6 +136,9 @@ $(IncludeRazorContentInPack) + + $(IncludeRazorContentInPack) + $(IncludeRazorContentInPack) diff --git a/BTCPayServer/Configuration/BTCPayServerOptions.cs b/BTCPayServer/Configuration/BTCPayServerOptions.cs index 99c7aea30..d657f6944 100644 --- a/BTCPayServer/Configuration/BTCPayServerOptions.cs +++ b/BTCPayServer/Configuration/BTCPayServerOptions.cs @@ -37,8 +37,8 @@ namespace BTCPayServer.Configuration { get; private set; - } - + } + public string LogFile { get; @@ -68,7 +68,7 @@ namespace BTCPayServer.Configuration public static LogEventLevel GetDebugLogLevel(IConfiguration configuration) { var raw = configuration.GetValue("debugloglevel", nameof(LogEventLevel.Debug)); - return (LogEventLevel)Enum.Parse(typeof(LogEventLevel), raw, true); + return (LogEventLevel)Enum.Parse(typeof(LogEventLevel), raw, true); } public void LoadArgs(IConfiguration conf) @@ -139,7 +139,7 @@ namespace BTCPayServer.Configuration externalLnd($"{net.CryptoCode}.external.lnd.rest", "lnd-rest"); var spark = conf.GetOrDefault($"{net.CryptoCode}.external.spark", string.Empty); - if(spark.Length != 0) + if (spark.Length != 0) { if (!SparkConnectionString.TryParse(spark, out var connectionString)) { @@ -148,14 +148,30 @@ namespace BTCPayServer.Configuration } ExternalServicesByCryptoCode.Add(net.CryptoCode, new ExternalSpark(connectionString)); } + + var charge = conf.GetOrDefault($"{net.CryptoCode}.external.charge", string.Empty); + if (charge.Length != 0) + { + if (!LightningConnectionString.TryParse(charge, false, out var chargeConnectionString, out var chargeError)) + LightningConnectionString.TryParse("type=charge;" + charge, false, out chargeConnectionString, out chargeError); + + if(chargeConnectionString == null || chargeConnectionString.ConnectionType != LightningConnectionType.Charge) + { + throw new ConfigException($"Invalid setting {net.CryptoCode}.external.charge, " + Environment.NewLine + + $"lightning charge server: 'type=charge;server=https://charge.example.com;api-token=2abdf302...'" + Environment.NewLine + + $"lightning charge server: 'type=charge;server=https://charge.example.com;cookiefilepath=/root/.charge/.cookie'" + Environment.NewLine + + chargeError ?? string.Empty); + } + ExternalServicesByCryptoCode.Add(net.CryptoCode, new ExternalCharge(chargeConnectionString)); + } } Logs.Configuration.LogInformation("Supported chains: " + String.Join(',', supportedChains.ToArray())); var services = conf.GetOrDefault("externalservices", null); - if(services != null) + if (services != null) { - foreach(var service in services.Split(new[] { ';', ',' }) + foreach (var service in services.Split(new[] { ';', ',' }) .Select(p => p.Split(':')) .Where(p => p.Length == 2) .Select(p => (Name: p[0], Link: p[1]))) diff --git a/BTCPayServer/Configuration/DefaultConfiguration.cs b/BTCPayServer/Configuration/DefaultConfiguration.cs index 9e49c5d87..6717312d7 100644 --- a/BTCPayServer/Configuration/DefaultConfiguration.cs +++ b/BTCPayServer/Configuration/DefaultConfiguration.cs @@ -50,7 +50,8 @@ namespace BTCPayServer.Configuration app.Option($"--{crypto}explorercookiefile", $"Path to the cookie file (default: {network.NBXplorerNetwork.DefaultSettings.DefaultCookieFile})", CommandOptionType.SingleValue); app.Option($"--{crypto}lightning", $"Easy configuration of lightning for the server administrator: Must be a UNIX socket of c-lightning (lightning-rpc) or URL to a charge server (default: empty)", CommandOptionType.SingleValue); app.Option($"--{crypto}externallndgrpc", $"The LND gRPC configuration BTCPay will expose to easily connect to the internal lnd wallet from Zap wallet (default: empty)", CommandOptionType.SingleValue); - app.Option($"--{crypto}externalspark", $"The connection string to spark server (default: empty)", CommandOptionType.SingleValue); + app.Option($"--{crypto}externalspark", $"Show spark information in Server settings / Server. The connection string to spark server (default: empty)", CommandOptionType.SingleValue); + app.Option($"--{crypto}externalcharge", $"Show lightning charge information in Server settings/Server. The connection string to charge server (default: empty)", CommandOptionType.SingleValue); } return app; } diff --git a/BTCPayServer/Configuration/External/ExternalCharge.cs b/BTCPayServer/Configuration/External/ExternalCharge.cs new file mode 100644 index 000000000..03b8d5c86 --- /dev/null +++ b/BTCPayServer/Configuration/External/ExternalCharge.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using BTCPayServer.Lightning; + +namespace BTCPayServer.Configuration.External +{ + public class ExternalCharge : ExternalService + { + public ExternalCharge(LightningConnectionString connectionString) + { + if (connectionString == null) + throw new ArgumentNullException(nameof(connectionString)); + ConnectionString = connectionString; + } + public LightningConnectionString ConnectionString { get; } + } +} diff --git a/BTCPayServer/Configuration/SparkConnectionString.cs b/BTCPayServer/Configuration/SparkConnectionString.cs index 636f20431..5a0188039 100644 --- a/BTCPayServer/Configuration/SparkConnectionString.cs +++ b/BTCPayServer/Configuration/SparkConnectionString.cs @@ -31,6 +31,7 @@ namespace BTCPayServer.Configuration resultTemp.Server = new Uri(kv[1], UriKind.Absolute); break; case "cookiefile": + case "cookiefilepath": if (resultTemp.CookeFile != null) return false; resultTemp.CookeFile = kv[1]; diff --git a/BTCPayServer/Controllers/Macaroons.cs b/BTCPayServer/Controllers/Macaroons.cs new file mode 100644 index 000000000..08eb369e8 --- /dev/null +++ b/BTCPayServer/Controllers/Macaroons.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace BTCPayServer.Controllers +{ + public class Macaroons + { + public class Macaroon + { + public Macaroon(byte[] bytes) + { + Bytes = bytes; + Hex = NBitcoin.DataEncoders.Encoders.Hex.EncodeData(bytes); + } + + public string Hex { get; set; } + public byte[] Bytes { get; set; } + } + public static async Task GetFromDirectoryAsync(string directoryPath) + { + if (directoryPath == null) + throw new ArgumentNullException(nameof(directoryPath)); + Macaroons macaroons = new Macaroons(); + if (!Directory.Exists(directoryPath)) + return macaroons; + foreach(var file in Directory.GetFiles(directoryPath, "*.macaroon")) + { + try + { + switch (Path.GetFileName(file)) + { + case "admin.macaroon": + macaroons.AdminMacaroon = new Macaroon(await File.ReadAllBytesAsync(file)); + break; + case "readonly.macaroon": + macaroons.ReadonlyMacaroon = new Macaroon(await File.ReadAllBytesAsync(file)); + break; + case "invoice.macaroon": + macaroons.InvoiceMacaroon = new Macaroon(await File.ReadAllBytesAsync(file)); + break; + default: + break; + } + } + catch { } + } + return macaroons; + } + public Macaroon ReadonlyMacaroon { get; set; } + + public Macaroon InvoiceMacaroon { get; set; } + public Macaroon AdminMacaroon { get; set; } + } +} diff --git a/BTCPayServer/Controllers/ServerController.cs b/BTCPayServer/Controllers/ServerController.cs index 524c957d5..3c5e404c0 100644 --- a/BTCPayServer/Controllers/ServerController.cs +++ b/BTCPayServer/Controllers/ServerController.cs @@ -449,6 +449,16 @@ namespace BTCPayServer.Controllers Index = i++, }); } + foreach (var chargeService in _Options.ExternalServicesByCryptoCode.GetServices(cryptoCode)) + { + result.LNDServices.Add(new ServicesViewModel.LNDServiceViewModel() + { + Crypto = cryptoCode, + Type = "Lightning charge server", + Action = nameof(LightningChargeServices), + Index = i++, + }); + } } foreach(var externalService in _Options.ExternalServices) { @@ -469,6 +479,45 @@ namespace BTCPayServer.Controllers return View(result); } + [Route("server/services/lightning-charge/{cryptoCode}/{index}")] + public async Task LightningChargeServices(string cryptoCode, int index, bool showQR = false) + { + if (!_dashBoard.IsFullySynched(cryptoCode, out var unusud)) + { + StatusMessage = $"Error: {cryptoCode} is not fully synched"; + return RedirectToAction(nameof(Services)); + } + var lightningCharge = _Options.ExternalServicesByCryptoCode.GetServices(cryptoCode).Select(c => c.ConnectionString).FirstOrDefault(); + if (lightningCharge == null) + { + return NotFound(); + } + + ChargeServiceViewModel vm = new ChargeServiceViewModel(); + vm.Uri = lightningCharge.ToUri(false).AbsoluteUri; + vm.APIToken = lightningCharge.Password; + try + { + if (string.IsNullOrEmpty(vm.APIToken) && lightningCharge.CookieFilePath != null) + { + if (lightningCharge.CookieFilePath != "fake") + vm.APIToken = await System.IO.File.ReadAllTextAsync(lightningCharge.CookieFilePath); + else + vm.APIToken = "fake"; + } + var builder = new UriBuilder(lightningCharge.ToUri(false)); + builder.UserName = "api-token"; + builder.Password = vm.APIToken; + vm.AuthenticatedUri = builder.ToString(); + } + catch (Exception ex) + { + StatusMessage = $"Error: {ex.Message}"; + return RedirectToAction(nameof(Services)); + } + return View(vm); + } + [Route("server/services/spark/{cryptoCode}/{index}")] public async Task SparkServices(string cryptoCode, int index, bool showQR = false) { @@ -477,7 +526,7 @@ namespace BTCPayServer.Controllers StatusMessage = $"Error: {cryptoCode} is not fully synched"; return RedirectToAction(nameof(Services)); } - var spark = _Options.ExternalServicesByCryptoCode.GetServices(cryptoCode).Skip(index).Select(c => c.ConnectionString).FirstOrDefault(); + var spark = _Options.ExternalServicesByCryptoCode.GetServices(cryptoCode).Select(c => c.ConnectionString).FirstOrDefault(); if(spark == null) { return NotFound(); @@ -504,7 +553,7 @@ namespace BTCPayServer.Controllers } [Route("server/services/lnd/{cryptoCode}/{index}")] - public IActionResult LndServices(string cryptoCode, int index, uint? nonce) + public async Task LndServices(string cryptoCode, int index, uint? nonce) { if (!_dashBoard.IsFullySynched(cryptoCode, out var unusud)) { @@ -520,6 +569,7 @@ namespace BTCPayServer.Controllers model.Host = $"{external.BaseUri.DnsSafeHost}:{external.BaseUri.Port}"; model.SSL = external.BaseUri.Scheme == "https"; model.ConnectionType = "GRPC"; + model.GRPCSSLCipherSuites = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256"; } else if(external.ConnectionType == LightningConnectionType.LndREST) { @@ -535,10 +585,10 @@ namespace BTCPayServer.Controllers { model.Macaroon = Encoders.Hex.EncodeData(external.Macaroon); } - if (external.RestrictedMacaroon != null) - { - model.RestrictedMacaroon = Encoders.Hex.EncodeData(external.RestrictedMacaroon); - } + var macaroons = external.MacaroonDirectoryPath == null ? null : await Macaroons.GetFromDirectoryAsync(external.MacaroonDirectoryPath); + model.AdminMacaroon = macaroons?.AdminMacaroon?.Hex; + model.InvoiceMacaroon = macaroons?.InvoiceMacaroon?.Hex; + model.ReadonlyMacaroon = macaroons?.ReadonlyMacaroon?.Hex; if (nonce != null) { @@ -571,38 +621,40 @@ namespace BTCPayServer.Controllers [Route("server/services/lnd/{cryptoCode}/{index}")] [HttpPost] - public IActionResult LndServicesPost(string cryptoCode, int index) + public async Task LndServicesPost(string cryptoCode, int index) { var external = GetExternalLndConnectionString(cryptoCode, index); if (external == null) return NotFound(); LightningConfigurations confs = new LightningConfigurations(); + var macaroons = external.MacaroonDirectoryPath == null ? null : await Macaroons.GetFromDirectoryAsync(external.MacaroonDirectoryPath); if (external.ConnectionType == LightningConnectionType.LndGRPC) { - LightningConfiguration conf = new LightningConfiguration(); - conf.Type = "grpc"; - conf.ChainType = _Options.NetworkType.ToString(); - conf.CryptoCode = cryptoCode; - conf.Host = external.BaseUri.DnsSafeHost; - conf.Port = external.BaseUri.Port; - conf.SSL = external.BaseUri.Scheme == "https"; - conf.Macaroon = external.Macaroon == null ? null : Encoders.Hex.EncodeData(external.Macaroon); - conf.RestrictedMacaroon = external.RestrictedMacaroon == null ? null : Encoders.Hex.EncodeData(external.RestrictedMacaroon); - conf.CertificateThumbprint = external.CertificateThumbprint == null ? null : Encoders.Hex.EncodeData(external.CertificateThumbprint); - confs.Configurations.Add(conf); + LightningConfiguration grpcConf = new LightningConfiguration(); + grpcConf.Type = "grpc"; + grpcConf.Host = external.BaseUri.DnsSafeHost; + grpcConf.Port = external.BaseUri.Port; + grpcConf.SSL = external.BaseUri.Scheme == "https"; + confs.Configurations.Add(grpcConf); } else if (external.ConnectionType == LightningConnectionType.LndREST) { var restconf = new LNDRestConfiguration(); restconf.Type = "lnd-rest"; - restconf.ChainType = _Options.NetworkType.ToString(); - restconf.CryptoCode = cryptoCode; restconf.Uri = external.BaseUri.AbsoluteUri; - restconf.Macaroon = external.Macaroon == null ? null : Encoders.Hex.EncodeData(external.Macaroon); - restconf.RestrictedMacaroon = external.RestrictedMacaroon == null ? null : Encoders.Hex.EncodeData(external.RestrictedMacaroon); - restconf.CertificateThumbprint = external.CertificateThumbprint == null ? null : Encoders.Hex.EncodeData(external.CertificateThumbprint); confs.Configurations.Add(restconf); } + else + throw new NotSupportedException(external.ConnectionType.ToString()); + var commonConf = (LNDConfiguration)confs.Configurations[confs.Configurations.Count - 1]; + commonConf.ChainType = _Options.NetworkType.ToString(); + commonConf.CryptoCode = cryptoCode; + commonConf.Macaroon = external.Macaroon == null ? null : Encoders.Hex.EncodeData(external.Macaroon); + commonConf.CertificateThumbprint = external.CertificateThumbprint == null ? null : Encoders.Hex.EncodeData(external.CertificateThumbprint); + commonConf.AdminMacaroon = macaroons?.AdminMacaroon?.Hex; + commonConf.ReadonlyMacaroon = macaroons?.ReadonlyMacaroon?.Hex; + commonConf.InvoiceMacaroon = macaroons?.InvoiceMacaroon?.Hex; + var nonce = RandomUtils.GetUInt32(); var configKey = GetConfigKey("lnd", cryptoCode, index, nonce); _LnConfigProvider.KeepConfig(configKey, confs); @@ -628,18 +680,6 @@ namespace BTCPayServer.Controllers return null; } } - if (connectionString.RestrictedMacaroonFilePath != null) - { - try - { - connectionString.RestrictedMacaroon = System.IO.File.ReadAllBytes(connectionString.RestrictedMacaroonFilePath); - } - catch - { - Logs.Configuration.LogWarning($"{cryptoCode}: The restrictedmacaroon file path of the external LND grpc config was not found ({connectionString.RestrictedMacaroonFilePath})"); - } - connectionString.RestrictedMacaroonFilePath = null; - } return connectionString; } diff --git a/BTCPayServer/Models/ServerViewModels/ChargeServiceViewModel.cs b/BTCPayServer/Models/ServerViewModels/ChargeServiceViewModel.cs new file mode 100644 index 000000000..25b8ac4f6 --- /dev/null +++ b/BTCPayServer/Models/ServerViewModels/ChargeServiceViewModel.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace BTCPayServer.Models.ServerViewModels +{ + public class ChargeServiceViewModel + { + public string Uri { get; set; } + public string APIToken { get; set; } + public string AuthenticatedUri { get; set; } + } +} diff --git a/BTCPayServer/Models/ServerViewModels/LndGrpcServicesViewModel.cs b/BTCPayServer/Models/ServerViewModels/LndGrpcServicesViewModel.cs index 33bffd72b..54f33dbd8 100644 --- a/BTCPayServer/Models/ServerViewModels/LndGrpcServicesViewModel.cs +++ b/BTCPayServer/Models/ServerViewModels/LndGrpcServicesViewModel.cs @@ -11,8 +11,12 @@ namespace BTCPayServer.Models.ServerViewModels public string Host { get; set; } public bool SSL { get; set; } public string Macaroon { get; set; } - public string RestrictedMacaroon { get; set; } + public string AdminMacaroon { get; set; } + public string ReadonlyMacaroon { get; set; } + public string InvoiceMacaroon { get; set; } public string CertificateThumbprint { get; set; } + [Display(Name = "GRPC SSL Cipher suite (GRPC_SSL_CIPHER_SUITES)")] + public string GRPCSSLCipherSuites { get; set; } public string QRCode { get; set; } public string QRCodeLink { get; set; } [Display(Name = "REST Uri")] diff --git a/BTCPayServer/Payments/Lightning/LightningListener.cs b/BTCPayServer/Payments/Lightning/LightningListener.cs index 1fef6f9c6..abf56011b 100644 --- a/BTCPayServer/Payments/Lightning/LightningListener.cs +++ b/BTCPayServer/Payments/Lightning/LightningListener.cs @@ -156,7 +156,8 @@ namespace BTCPayServer.Payments.Lightning if (notification.Id == listenedInvoice.PaymentMethodDetails.InvoiceId && notification.BOLT11 == listenedInvoice.PaymentMethodDetails.BOLT11) { - if (notification.Status == LightningInvoiceStatus.Paid && notification.PaidAt.HasValue) + if (notification.Status == LightningInvoiceStatus.Paid && + notification.PaidAt.HasValue && notification.Amount != null) { await AddPayment(network, notification, listenedInvoice); if (DoneListening(listenedInvoice)) diff --git a/BTCPayServer/Properties/launchSettings.json b/BTCPayServer/Properties/launchSettings.json index 5bfafb34c..226da05de 100644 --- a/BTCPayServer/Properties/launchSettings.json +++ b/BTCPayServer/Properties/launchSettings.json @@ -30,6 +30,7 @@ "BTCPAY_BTCEXTERNALLNDGRPC": "type=lnd-grpc;server=https://lnd:lnd@127.0.0.1:53280/;allowinsecure=true", "BTCPAY_BTCEXTERNALLNDREST": "type=lnd-rest;server=https://lnd:lnd@127.0.0.1:53280/lnd-rest/btc/;allowinsecure=true", "BTCPAY_BTCEXTERNALSPARK": "server=https://127.0.0.1:53280/spark/btc/;cookiefile=fake", + "BTCPAY_BTCEXTERNALCHARGE": "server=https://127.0.0.1:53280/spark/btc/;cookiefilepath=fake", "BTCPAY_BTCEXPLORERURL": "http://127.0.0.1:32838/", "ASPNETCORE_ENVIRONMENT": "Development", "BTCPAY_CHAINS": "btc,ltc", diff --git a/BTCPayServer/Services/LightningConfigurationProvider.cs b/BTCPayServer/Services/LightningConfigurationProvider.cs index cb7842af3..36eeff879 100644 --- a/BTCPayServer/Services/LightningConfigurationProvider.cs +++ b/BTCPayServer/Services/LightningConfigurationProvider.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using NBitcoin; +using NBitcoin.DataEncoders; namespace BTCPayServer.Services { @@ -27,9 +28,9 @@ namespace BTCPayServer.Services private void CleanExpired() { - foreach(var item in _Map) + foreach (var item in _Map) { - if(item.Value.expiration < DateTimeOffset.UtcNow) + if (item.Value.expiration < DateTimeOffset.UtcNow) { _Map.TryRemove(item.Key, out var unused); } @@ -41,26 +42,26 @@ namespace BTCPayServer.Services { public List Configurations { get; set; } = new List(); } - public class LightningConfiguration + + public class LNDConfiguration { public string ChainType { get; set; } public string Type { get; set; } public string CryptoCode { get; set; } + public string CertificateThumbprint { get; set; } + public string Macaroon { get; set; } + public string AdminMacaroon { get; set; } + public string ReadonlyMacaroon { get; set; } + public string InvoiceMacaroon { get; set; } + } + public class LightningConfiguration : LNDConfiguration + { public string Host { get; set; } public int Port { get; set; } public bool SSL { get; set; } - public string CertificateThumbprint { get; set; } - public string Macaroon { get; set; } - public string RestrictedMacaroon { get; set; } } - public class LNDRestConfiguration + public class LNDRestConfiguration : LNDConfiguration { - public string ChainType { get; set; } - public string Type { get; set; } - public string CryptoCode { get; set; } public string Uri { get; set; } - public string Macaroon { get; set; } - public string CertificateThumbprint { get; set; } - public string RestrictedMacaroon { get; set; } } } diff --git a/BTCPayServer/Views/Server/LightningChargeServices.cshtml b/BTCPayServer/Views/Server/LightningChargeServices.cshtml new file mode 100644 index 000000000..6bc4e9946 --- /dev/null +++ b/BTCPayServer/Views/Server/LightningChargeServices.cshtml @@ -0,0 +1,60 @@ +@model ChargeServiceViewModel +@{ + ViewData.SetActivePageAndTitle(ServerNavPages.Services); +} + + +

Lightning charge service

+ + +
+
+
+
+
+ +
+
+
+

+ Lightning charge is a simple API for invoicing on lightning network, you can use it with several plugins: +

    +
  • WooCommerce Lightning Gateway: A comprehensive e-commerce application that integrates with stock-management and order-tracking systems
  • +
  • Nanopos: A simple point-of-sale system for fixed-price goods
  • +
  • FileBazaar: A system for selling files such as documents, images, and videos
  • +
  • Lightning Publisher for WordPress: A patronage model for unlocking WordPress blog entries
  • +
  • Paypercall: A programmer’s toolkit for Lightning that enables micropayments for individual API calls
  • +
  • Ifpaytt: An extension of paypercall that allows web developers using IFTTT to request payments for service usage
  • +
  • Lightning Jukebox: A fun demo that reimagines a classic technology for the Lightning Network
  • +
  • Nanotip: The simple tip jar, rebuilt to issue Lightning Network invoices
  • +
+

+
+
+
+
+
+

Credentials

+ @if (Model.Uri != null) + { +
+ + +
+ } + @if (Model.APIToken != null) + { +
+ + +
+ } + @if (Model.AuthenticatedUri != null) + { +
+ + +
+ } +
+
diff --git a/BTCPayServer/Views/Server/LndServices.cshtml b/BTCPayServer/Views/Server/LndServices.cshtml index 9f547bd61..dd644359e 100644 --- a/BTCPayServer/Views/Server/LndServices.cshtml +++ b/BTCPayServer/Views/Server/LndServices.cshtml @@ -86,12 +86,33 @@ } - @if (Model.RestrictedMacaroon != null) + @if (Model.AdminMacaroon != null) { - @*
- - -
*@ +
+ + +
+ } + @if (Model.InvoiceMacaroon != null) + { +
+ + +
+ } + @if (Model.ReadonlyMacaroon != null) + { +
+ + +
+ } + @if (Model.GRPCSSLCipherSuites != null) + { +
+ + +
} @if (Model.CertificateThumbprint != null) { diff --git a/BTCPayServer/wwwroot/imlegacy/groestlcoin-lightning.png b/BTCPayServer/wwwroot/imlegacy/groestlcoin-lightning.png deleted file mode 100644 index b15107a82..000000000 Binary files a/BTCPayServer/wwwroot/imlegacy/groestlcoin-lightning.png and /dev/null differ diff --git a/BTCPayServer/wwwroot/imlegacy/groestlcoin-lightning.svg b/BTCPayServer/wwwroot/imlegacy/groestlcoin-lightning.svg new file mode 100644 index 000000000..0fae175c4 --- /dev/null +++ b/BTCPayServer/wwwroot/imlegacy/groestlcoin-lightning.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BTCPayServer/wwwroot/locales/es-ES.json b/BTCPayServer/wwwroot/locales/es-ES.json index b90d88808..a528f6afa 100644 --- a/BTCPayServer/wwwroot/locales/es-ES.json +++ b/BTCPayServer/wwwroot/locales/es-ES.json @@ -22,12 +22,12 @@ "Amount": "Cantidad", "Address": "Dirección", "Copied": "Copiado", - "ConversionTab_BodyTop": "Puedes pagar {{btcDue}} {{cryptoCode}} usando Altcoins que este comercio no soporta directamente.", + "ConversionTab_BodyTop": "Puedes pagar {{btcDue}} {{cryptoCode}} usando altcoins que este comercio no soporta directamente.", "ConversionTab_BodyDesc": "Este servicio es provisto por terceros. Ten en cuenta que no tenemos control sobre cómo estos terceros envían los fondos. La factura solo se marcará como pagada una vez se reciban los fondos en la cadena de bloques de {{cryptoCode}} .", "ConversionTab_CalculateAmount_Error": "Reintentar", "ConversionTab_LoadCurrencies_Error": "Reintentar", "ConversionTab_Lightning": "No hay proveedores de conversión disponibles para los pagos de Lightning Network.", - "ConversionTab_CurrencyList_Select_Option": "Por favor selecciona el tipo de moneda que de desea cambiar", + "ConversionTab_CurrencyList_Select_Option": "Selecciona la moneda a convertir", "Invoice expiring soon...": "La factura expira pronto...", "Invoice expired": "La factura expiró", "What happened?": "¿Qué sucedió?",