mirror of
https://github.com/aljazceru/BTCPayServerPlugins.git
synced 2025-12-17 07:34:24 +01:00
wip
This commit is contained in:
@@ -49,6 +49,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.Prism"
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.FileSeller", "Plugins\BTCPayServer.Plugins.FileSeller\BTCPayServer.Plugins.FileSeller.csproj", "{F2D81B6A-E1EA-4900-BF9A-924D2CA951DD}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.FileSeller", "Plugins\BTCPayServer.Plugins.FileSeller\BTCPayServer.Plugins.FileSeller.csproj", "{F2D81B6A-E1EA-4900-BF9A-924D2CA951DD}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.Breez", "Plugins\BTCPayServer.Plugins.Breez\BTCPayServer.Plugins.Breez.csproj", "{5934F898-00B1-4781-BD18-04DF8685BC76}"
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.DynamicReports", "Plugins\BTCPayServer.Plugins.DynamicReports\BTCPayServer.Plugins.DynamicReports.csproj", "{BCB4E68D-089F-481E-A3AE-FC9CED6AA34D}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.DynamicReports", "Plugins\BTCPayServer.Plugins.DynamicReports\BTCPayServer.Plugins.DynamicReports.csproj", "{BCB4E68D-089F-481E-A3AE-FC9CED6AA34D}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.Bringin", "Plugins\BTCPayServer.Plugins.Bringin\BTCPayServer.Plugins.Bringin.csproj", "{D4AFEC95-64D4-4FC4-9AE4-B82F4C6D6E29}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.Bringin", "Plugins\BTCPayServer.Plugins.Bringin\BTCPayServer.Plugins.Bringin.csproj", "{D4AFEC95-64D4-4FC4-9AE4-B82F4C6D6E29}"
|
||||||
@@ -253,6 +254,14 @@ Global
|
|||||||
{D4AFEC95-64D4-4FC4-9AE4-B82F4C6D6E29}.Altcoins-Debug|Any CPU.Build.0 = Debug|Any CPU
|
{D4AFEC95-64D4-4FC4-9AE4-B82F4C6D6E29}.Altcoins-Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{D4AFEC95-64D4-4FC4-9AE4-B82F4C6D6E29}.Altcoins-Release|Any CPU.ActiveCfg = Debug|Any CPU
|
{D4AFEC95-64D4-4FC4-9AE4-B82F4C6D6E29}.Altcoins-Release|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{D4AFEC95-64D4-4FC4-9AE4-B82F4C6D6E29}.Altcoins-Release|Any CPU.Build.0 = Debug|Any CPU
|
{D4AFEC95-64D4-4FC4-9AE4-B82F4C6D6E29}.Altcoins-Release|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{5934F898-00B1-4781-BD18-04DF8685BC76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{5934F898-00B1-4781-BD18-04DF8685BC76}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{5934F898-00B1-4781-BD18-04DF8685BC76}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{5934F898-00B1-4781-BD18-04DF8685BC76}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{5934F898-00B1-4781-BD18-04DF8685BC76}.Altcoins-Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{5934F898-00B1-4781-BD18-04DF8685BC76}.Altcoins-Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{5934F898-00B1-4781-BD18-04DF8685BC76}.Altcoins-Release|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{5934F898-00B1-4781-BD18-04DF8685BC76}.Altcoins-Release|Any CPU.Build.0 = Debug|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{B19C9F52-DC47-466D-8B5C-2D202B7B003F} = {9E04ECE9-E304-4FF2-9CBC-83256E6C6962}
|
{B19C9F52-DC47-466D-8B5C-2D202B7B003F} = {9E04ECE9-E304-4FF2-9CBC-83256E6C6962}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<LangVersion>10</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- Plugin specific properties -->
|
||||||
|
<PropertyGroup>
|
||||||
|
<Product>Breez / Greenlight</Product>
|
||||||
|
<Description>Lightwight lightning baby!</Description>
|
||||||
|
<Version>1.0.0</Version>
|
||||||
|
</PropertyGroup>
|
||||||
|
<!-- Plugin development properties -->
|
||||||
|
<PropertyGroup>
|
||||||
|
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
|
||||||
|
<PreserveCompilationContext>false</PreserveCompilationContext>
|
||||||
|
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- This will make sure that referencing BTCPayServer doesn't put any artifact in the published directory -->
|
||||||
|
<ItemDefinitionGroup>
|
||||||
|
<ProjectReference>
|
||||||
|
<Properties>StaticWebAssetsEnabled=false</Properties>
|
||||||
|
<Private>false</Private>
|
||||||
|
<ExcludeAssets>runtime;native;build;buildTransitive;contentFiles</ExcludeAssets>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="Resources\**" />
|
||||||
|
<ProjectReference Include="..\..\submodules\btcpayserver\BTCPayServer\BTCPayServer.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Resources" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Breez.Sdk" Version="0.2.7" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
|
|
||||||
59
Plugins/BTCPayServer.Plugins.Breez/BreezController.cs
Normal file
59
Plugins/BTCPayServer.Plugins.Breez/BreezController.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Abstractions.Constants;
|
||||||
|
using BTCPayServer.Client;
|
||||||
|
using BTCPayServer.Plugins.FixedFloat;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Plugins.Breez;
|
||||||
|
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||||
|
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||||
|
[Route("plugins/{storeId}/Breez")]
|
||||||
|
public class BreezController : Controller
|
||||||
|
{
|
||||||
|
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
|
||||||
|
private readonly BreezService _breezService;
|
||||||
|
|
||||||
|
public BreezController(BTCPayNetworkProvider btcPayNetworkProvider, BreezService breezService)
|
||||||
|
{
|
||||||
|
_btcPayNetworkProvider = btcPayNetworkProvider;
|
||||||
|
_breezService = breezService;
|
||||||
|
}
|
||||||
|
[HttpGet("")]
|
||||||
|
public async Task<IActionResult> Index(string storeId)
|
||||||
|
{
|
||||||
|
return View(await _breezService.Get(storeId));
|
||||||
|
|
||||||
|
}
|
||||||
|
[HttpPost("")]
|
||||||
|
public async Task<IActionResult> Index(string storeId, string command, BreezSettings settings)
|
||||||
|
{
|
||||||
|
if (command == "clear")
|
||||||
|
{
|
||||||
|
await _breezService.Set(storeId, null);
|
||||||
|
TempData[WellKnownTempData.SuccessMessage] = "Settings cleared successfully";
|
||||||
|
return RedirectToAction(nameof(Index), new {storeId});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command == "save")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _breezService.Set(storeId, settings);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
TempData[WellKnownTempData.ErrorMessage] = $"Couldnt use provided settings: {e.Message}";
|
||||||
|
return View(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
TempData[WellKnownTempData.SuccessMessage] = "Settings saved successfully";
|
||||||
|
return RedirectToAction(nameof(Index), new {storeId});
|
||||||
|
}
|
||||||
|
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
281
Plugins/BTCPayServer.Plugins.Breez/BreezLightningClient.cs
Normal file
281
Plugins/BTCPayServer.Plugins.Breez/BreezLightningClient.cs
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Breez.Sdk;
|
||||||
|
using NBitcoin;
|
||||||
|
using Network = Breez.Sdk.Network;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Lightning.Breez;
|
||||||
|
|
||||||
|
|
||||||
|
public class BreezLightningClient: ILightningClient, IDisposable, EventListener
|
||||||
|
{
|
||||||
|
private readonly NBitcoin.Network _network;
|
||||||
|
|
||||||
|
public BreezLightningClient(string inviteCode, string apiKey, string workingDir, NBitcoin.Network network,
|
||||||
|
string mnemonic)
|
||||||
|
{
|
||||||
|
_network = network;
|
||||||
|
var nodeConfig = new NodeConfig.Greenlight(
|
||||||
|
new GreenlightNodeConfig(null, inviteCode)
|
||||||
|
);
|
||||||
|
var config = BreezSdkMethods.DefaultConfig(
|
||||||
|
EnvironmentType.PRODUCTION,
|
||||||
|
apiKey,
|
||||||
|
nodeConfig
|
||||||
|
) with {
|
||||||
|
workingDir= workingDir,
|
||||||
|
network = network == NBitcoin.Network.Main ? Network.BITCOIN : network == NBitcoin.Network.TestNet ? Network.TESTNET: network == NBitcoin.Network.RegTest? Network.REGTEST: Network.SIGNET
|
||||||
|
};
|
||||||
|
var seed = BreezSdkMethods.MnemonicToSeed(mnemonic);
|
||||||
|
Sdk = BreezSdkMethods.Connect(config, seed, this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockingBreezServices Sdk { get; }
|
||||||
|
|
||||||
|
public event EventHandler<BreezEvent> EventReceived;
|
||||||
|
public void OnEvent(BreezEvent e)
|
||||||
|
{
|
||||||
|
EventReceived?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<LightningInvoice> GetInvoice(string invoiceId, CancellationToken cancellation = default)
|
||||||
|
{
|
||||||
|
return GetInvoice(uint256.Parse(invoiceId), cancellation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LightningPayment ToLightningPayment(Payment payment)
|
||||||
|
{
|
||||||
|
if (payment?.details is not PaymentDetails.Ln lnPaymentDetails)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new LightningPayment()
|
||||||
|
{
|
||||||
|
Amount = LightMoney.MilliSatoshis(payment.amountMsat),
|
||||||
|
Id = lnPaymentDetails.data.paymentHash,
|
||||||
|
Preimage = lnPaymentDetails.data.paymentPreimage,
|
||||||
|
PaymentHash = lnPaymentDetails.data.paymentHash,
|
||||||
|
BOLT11 = lnPaymentDetails.data.bolt11,
|
||||||
|
Status = payment.status switch
|
||||||
|
{
|
||||||
|
PaymentStatus.FAILED => LightningPaymentStatus.Failed,
|
||||||
|
PaymentStatus.COMPLETE => LightningPaymentStatus.Complete,
|
||||||
|
PaymentStatus.PENDING => LightningPaymentStatus.Pending,
|
||||||
|
_ => throw new ArgumentOutOfRangeException()
|
||||||
|
},
|
||||||
|
CreatedAt = DateTimeOffset.FromUnixTimeMilliseconds(payment.paymentTime),
|
||||||
|
Fee = LightMoney.MilliSatoshis(payment.feeMsat),
|
||||||
|
AmountSent = LightMoney.MilliSatoshis(payment.amountMsat)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
private LightningInvoice FromPayment(Payment p)
|
||||||
|
{
|
||||||
|
if (p?.details is not PaymentDetails.Ln lnPaymentDetails)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var bolt11 = BOLT11PaymentRequest.Parse(lnPaymentDetails.data.bolt11, _network);
|
||||||
|
|
||||||
|
return new LightningInvoice()
|
||||||
|
{
|
||||||
|
Amount = LightMoney.MilliSatoshis(p.amountMsat),
|
||||||
|
Id = lnPaymentDetails.data.paymentHash,
|
||||||
|
Preimage = lnPaymentDetails.data.paymentPreimage,
|
||||||
|
PaymentHash = lnPaymentDetails.data.paymentHash,
|
||||||
|
BOLT11 = lnPaymentDetails.data.bolt11,
|
||||||
|
Status = p.status switch
|
||||||
|
{
|
||||||
|
PaymentStatus.FAILED => LightningInvoiceStatus.Expired,
|
||||||
|
PaymentStatus.COMPLETE => LightningInvoiceStatus.Paid,
|
||||||
|
_ => LightningInvoiceStatus.Unpaid
|
||||||
|
},
|
||||||
|
PaidAt = DateTimeOffset.FromUnixTimeMilliseconds(p.paymentTime),
|
||||||
|
ExpiresAt = bolt11.ExpiryDate
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LightningInvoice> GetInvoice(uint256 paymentHash, CancellationToken cancellation = default)
|
||||||
|
{
|
||||||
|
var p = Sdk.PaymentByHash(paymentHash.ToString()!);
|
||||||
|
return FromPayment(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LightningInvoice[]> ListInvoices(CancellationToken cancellation = default)
|
||||||
|
{
|
||||||
|
|
||||||
|
return await ListInvoices(null, cancellation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LightningInvoice[]> ListInvoices(ListInvoicesParams request, CancellationToken cancellation = default)
|
||||||
|
{
|
||||||
|
return Sdk.ListPayments(new ListPaymentsRequest(PaymentTypeFilter.RECEIVED, null, null, request?.PendingOnly is not true, (uint?) request?.OffsetIndex, null))
|
||||||
|
.Select(FromPayment).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LightningPayment> GetPayment(string paymentHash, CancellationToken cancellation = default)
|
||||||
|
{
|
||||||
|
return ToLightningPayment(Sdk.PaymentByHash(paymentHash));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LightningPayment[]> ListPayments(CancellationToken cancellation = default)
|
||||||
|
{
|
||||||
|
return await ListPayments(null, cancellation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LightningPayment[]> ListPayments(ListPaymentsParams request, CancellationToken cancellation = default)
|
||||||
|
{
|
||||||
|
return Sdk.ListPayments(new ListPaymentsRequest(PaymentTypeFilter.RECEIVED, null, null, null, (uint?) request?.OffsetIndex, null))
|
||||||
|
.Select(ToLightningPayment).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<LightningInvoice> CreateInvoice(LightMoney amount, string description, TimeSpan expiry, CancellationToken cancellation = default)
|
||||||
|
{
|
||||||
|
|
||||||
|
var expiryS =expiry == TimeSpan.Zero? (uint?) null: Math.Max(0, (uint)expiry.TotalSeconds);
|
||||||
|
var p = Sdk.ReceivePayment(new ReceivePaymentRequest((ulong)amount.MilliSatoshi, description, null, null, false,expiryS ));
|
||||||
|
return await GetInvoice(p.lnInvoice.paymentHash, cancellation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LightningInvoice> CreateInvoice(CreateInvoiceParams createInvoiceRequest, CancellationToken cancellation = default)
|
||||||
|
{
|
||||||
|
var expiryS =createInvoiceRequest.Expiry == TimeSpan.Zero? (uint?) null: Math.Max(0, (uint)createInvoiceRequest.Expiry.TotalSeconds);
|
||||||
|
var p = Sdk.ReceivePayment(new ReceivePaymentRequest((ulong)createInvoiceRequest.Amount.MilliSatoshi, (createInvoiceRequest.Description??createInvoiceRequest.DescriptionHash.ToString())!, null, null, createInvoiceRequest.DescriptionHashOnly,expiryS ));
|
||||||
|
return await GetInvoice(p.lnInvoice.paymentHash, cancellation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ILightningInvoiceListener> Listen(CancellationToken cancellation = default)
|
||||||
|
{
|
||||||
|
return new BreezInvoiceListener(this, cancellation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LightningNodeInformation> GetInfo(CancellationToken cancellation = default)
|
||||||
|
{
|
||||||
|
|
||||||
|
var ni = Sdk.NodeInfo();
|
||||||
|
return new LightningNodeInformation()
|
||||||
|
{
|
||||||
|
PeersCount = ni.connectedPeers.Count,
|
||||||
|
Alias = $"greenlight {ni.id}",
|
||||||
|
NodeInfoList = {new NodeInfo(new PubKey(ni.id), "blockstrean.com", 69)},//we have to fake this as btcpay currently requires this to enable the payment method
|
||||||
|
BlockHeight = (int)ni.blockHeight
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LightningNodeBalance> GetBalance(CancellationToken cancellation = default)
|
||||||
|
{
|
||||||
|
var ni = Sdk.NodeInfo();
|
||||||
|
return new LightningNodeBalance()
|
||||||
|
{
|
||||||
|
OnchainBalance =
|
||||||
|
new OnchainBalance()
|
||||||
|
{
|
||||||
|
Confirmed = Money.Coins(LightMoney.MilliSatoshis(ni.onchainBalanceMsat)
|
||||||
|
.ToUnit(LightMoneyUnit.BTC))
|
||||||
|
},
|
||||||
|
OffchainBalance = new OffchainBalance()
|
||||||
|
{
|
||||||
|
Local = LightMoney.MilliSatoshis(ni.channelsBalanceMsat),
|
||||||
|
Remote = LightMoney.MilliSatoshis(ni.inboundLiquidityMsats),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PayResponse> Pay(PayInvoiceParams payParams, CancellationToken cancellation = default)
|
||||||
|
{
|
||||||
|
return await Pay(null, payParams, cancellation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PayResponse> Pay(string bolt11, PayInvoiceParams payParams, CancellationToken cancellation = default)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PayResponse> Pay(string bolt11, CancellationToken cancellation = default)
|
||||||
|
{
|
||||||
|
return await Pay(bolt11, null, cancellation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<OpenChannelResponse> OpenChannel(OpenChannelRequest openChannelRequest, CancellationToken cancellation = default)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<BitcoinAddress> GetDepositAddress(CancellationToken cancellation = default)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ConnectionResult> ConnectTo(NodeInfo nodeInfo, CancellationToken cancellation = default)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CancelInvoice(string invoiceId, CancellationToken cancellation = default)
|
||||||
|
{
|
||||||
|
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LightningChannel[]> ListChannels(CancellationToken cancellation = default)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Sdk.Dispose();
|
||||||
|
Sdk.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BreezInvoiceListener: ILightningInvoiceListener
|
||||||
|
{
|
||||||
|
private readonly BreezLightningClient _breezLightningClient;
|
||||||
|
private readonly CancellationToken _cancellationToken;
|
||||||
|
|
||||||
|
public BreezInvoiceListener(BreezLightningClient breezLightningClient, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_breezLightningClient = breezLightningClient;
|
||||||
|
_cancellationToken = cancellationToken;
|
||||||
|
|
||||||
|
breezLightningClient.EventReceived += BreezLightningClientOnEventReceived;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly ConcurrentQueue<Task<LightningInvoice>> _invoices = new();
|
||||||
|
|
||||||
|
private void BreezLightningClientOnEventReceived(object sender, BreezEvent e)
|
||||||
|
{
|
||||||
|
if (e is BreezEvent.InvoicePaid pre)
|
||||||
|
{
|
||||||
|
_invoices.Enqueue(_breezLightningClient.GetInvoice(pre.details.paymentHash, _cancellationToken));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_breezLightningClient.EventReceived -= BreezLightningClientOnEventReceived;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LightningInvoice> WaitInvoice(CancellationToken cancellation)
|
||||||
|
{
|
||||||
|
while(cancellation.IsCancellationRequested is not true)
|
||||||
|
{
|
||||||
|
if (_invoices.TryDequeue(out var task))
|
||||||
|
{
|
||||||
|
return await task.WithCancellation(cancellation);
|
||||||
|
}
|
||||||
|
await Task.Delay(100, cancellation);
|
||||||
|
}
|
||||||
|
cancellation.ThrowIfCancellationRequested();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using BTCPayServer.Plugins.FixedFloat;
|
||||||
|
using NBitcoin;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Lightning.Breez;
|
||||||
|
|
||||||
|
public class BreezLightningConnectionStringHandler : ILightningConnectionStringHandler
|
||||||
|
{
|
||||||
|
private readonly BreezService _breezService;
|
||||||
|
|
||||||
|
public BreezLightningConnectionStringHandler(BreezService breezService)
|
||||||
|
{
|
||||||
|
_breezService = breezService;
|
||||||
|
}
|
||||||
|
public ILightningClient Create(string connectionString, Network network, out string error)
|
||||||
|
{
|
||||||
|
var kv = LightningConnectionStringHelper.ExtractValues(connectionString, out var type);
|
||||||
|
if (type != "breez")
|
||||||
|
{
|
||||||
|
error = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!kv.TryGetValue("store", out var storeId))
|
||||||
|
{
|
||||||
|
error = $"The key 'store' is mandatory for breez connection strings";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = null;
|
||||||
|
return _breezService.GetClient(storeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
149
Plugins/BTCPayServer.Plugins.Breez/Program.cs
Normal file
149
Plugins/BTCPayServer.Plugins.Breez/Program.cs
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
#nullable enable
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Abstractions.Contracts;
|
||||||
|
using BTCPayServer.Abstractions.Models;
|
||||||
|
using BTCPayServer.Abstractions.Services;
|
||||||
|
using BTCPayServer.Configuration;
|
||||||
|
using BTCPayServer.Lightning;
|
||||||
|
using BTCPayServer.Lightning.Breez;
|
||||||
|
using BTCPayServer.Services.Stores;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using NBitcoin;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Plugins.FixedFloat
|
||||||
|
{
|
||||||
|
public class BreezSettings
|
||||||
|
{
|
||||||
|
public string InviteCode { get; set; }
|
||||||
|
public string Mnemonic { get; set; }
|
||||||
|
public string ApiKey { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BreezService:IHostedService
|
||||||
|
{
|
||||||
|
private readonly StoreRepository _storeRepository;
|
||||||
|
private readonly IOptions<DataDirectories> _dataDirectories;
|
||||||
|
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private Dictionary<string, BreezSettings> _settings;
|
||||||
|
private Dictionary<string, BreezLightningClient> _clients = new();
|
||||||
|
|
||||||
|
public BreezService(StoreRepository storeRepository,
|
||||||
|
IOptions<DataDirectories> dataDirectories,
|
||||||
|
BTCPayNetworkProvider btcPayNetworkProvider,
|
||||||
|
ILogger<BreezService> logger)
|
||||||
|
{
|
||||||
|
_storeRepository = storeRepository;
|
||||||
|
_dataDirectories = dataDirectories;
|
||||||
|
_btcPayNetworkProvider = btcPayNetworkProvider;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetWorkDir()
|
||||||
|
{
|
||||||
|
var dir = _dataDirectories.Value.DataDir;
|
||||||
|
return Path.Combine(dir, "Plugins", "Breez");
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskCompletionSource tcs = new();
|
||||||
|
public async Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_settings = (await _storeRepository.GetSettingsAsync<BreezSettings>("Breez")).Where(pair => pair.Value is not null).ToDictionary(pair => pair.Key, pair => pair.Value!);
|
||||||
|
tcs.TrySetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<BreezSettings?> Get(string storeId)
|
||||||
|
{
|
||||||
|
await tcs.Task;
|
||||||
|
_settings.TryGetValue(storeId, out var settings);
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<BreezLightningClient?> Handle(string? storeId, BreezSettings? settings)
|
||||||
|
{
|
||||||
|
if (settings is null)
|
||||||
|
{
|
||||||
|
if (storeId is not null && _clients.Remove(storeId, out var client))
|
||||||
|
{
|
||||||
|
client.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var client = new BreezLightningClient(settings.InviteCode, settings.ApiKey, GetWorkDir(),
|
||||||
|
_btcPayNetworkProvider.BTC.NBitcoinNetwork, settings.Mnemonic);
|
||||||
|
if (storeId is not null)
|
||||||
|
{
|
||||||
|
_clients.AddOrReplace(storeId, client);
|
||||||
|
}
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.LogError(e, "Could not create Breez client");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Set(string storeId, BreezSettings? settings)
|
||||||
|
{
|
||||||
|
|
||||||
|
var result = await Handle(storeId, settings);
|
||||||
|
await _storeRepository.UpdateSetting(storeId, "Breez", settings!);
|
||||||
|
if (settings is null)
|
||||||
|
{
|
||||||
|
_settings.Remove(storeId);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if(result is not null )
|
||||||
|
{
|
||||||
|
_settings.AddOrReplace(storeId, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_clients.Values.ToList().ForEach(c => c.Dispose());
|
||||||
|
}
|
||||||
|
|
||||||
|
public BreezLightningClient? GetClient(string storeId)
|
||||||
|
{
|
||||||
|
_clients.TryGetValue(storeId, out var client);
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BreezPlugin : BaseBTCPayServerPlugin
|
||||||
|
{
|
||||||
|
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
|
||||||
|
{
|
||||||
|
new() {Identifier = nameof(BTCPayServer), Condition = ">=1.12.0"}
|
||||||
|
};
|
||||||
|
|
||||||
|
public override void Execute(IServiceCollection applicationBuilder)
|
||||||
|
{
|
||||||
|
applicationBuilder.AddSingleton<BreezService>();
|
||||||
|
applicationBuilder.AddSingleton<IHostedService>(provider => provider.GetRequiredService<BreezService>());
|
||||||
|
applicationBuilder.AddSingleton<BreezLightningConnectionStringHandler>();
|
||||||
|
applicationBuilder.AddSingleton<ILightningConnectionStringHandler>(provider => provider.GetRequiredService<BreezLightningConnectionStringHandler>());
|
||||||
|
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("Breez/BreezNav",
|
||||||
|
"store-integrations-nav"));
|
||||||
|
base.Execute(applicationBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
53
Plugins/BTCPayServer.Plugins.Breez/Views/Breez/Index.cshtml
Normal file
53
Plugins/BTCPayServer.Plugins.Breez/Views/Breez/Index.cshtml
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
@using BTCPayServer
|
||||||
|
@using BTCPayServer.Abstractions.Extensions
|
||||||
|
@using BTCPayServer.Plugins.FixedFloat
|
||||||
|
@model BreezSettings?
|
||||||
|
@inject BreezService BreezService
|
||||||
|
@{
|
||||||
|
ViewData.SetActivePage("Breez", "Breez", "Breez");
|
||||||
|
var storeId = Context.GetCurrentStoreId();
|
||||||
|
}
|
||||||
|
|
||||||
|
<partial name="_StatusMessage"/>
|
||||||
|
|
||||||
|
<h2 class="mb-4">@ViewData["Title"]</h2>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-10">
|
||||||
|
<form method="post">
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Mnemonic" class="form-label">Mnemonic</label>
|
||||||
|
<input asp-for="Mnemonic" class="form-control"/>
|
||||||
|
<span asp-validation-for="Mnemonic" class="text-danger"></span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="ApiKey" class="form-label">ApiKey</label>
|
||||||
|
<input asp-for="ApiKey" class="form-control"/>
|
||||||
|
<span asp-validation-for="ApiKey" class="text-danger"></span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="InviteCode" class="form-label">InviteCode</label>
|
||||||
|
<input asp-for="InviteCode" class="form-control"/>
|
||||||
|
<span asp-validation-for="InviteCode" class="text-danger"></span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<button name="command" type="submit" value="save" class="btn btn-primary">Submit</button>
|
||||||
|
@if (await BreezService.Get(storeId) is not null)
|
||||||
|
{
|
||||||
|
<button name="command" type="submit" value="clear" class="btn btn-danger">Clear</button>
|
||||||
|
|
||||||
|
var client = BreezService.GetClient(storeId);
|
||||||
|
if (client is not null)
|
||||||
|
{
|
||||||
|
<pre>
|
||||||
|
@Json.Serialize(client.Sdk.NodeInfo())
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
@using BTCPayServer.Abstractions.Contracts
|
||||||
|
@using BTCPayServer.Abstractions.Extensions
|
||||||
|
@using BTCPayServer.Client
|
||||||
|
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
|
@inject IScopeProvider ScopeProvider
|
||||||
|
@{
|
||||||
|
var storeId = ScopeProvider.GetCurrentStoreId();
|
||||||
|
}
|
||||||
|
@if (!string.IsNullOrEmpty(storeId))
|
||||||
|
{
|
||||||
|
<li class="nav-item">
|
||||||
|
<a permission="@Policies.CanModifyStoreSettings" asp-controller="Breez" asp-action="Index" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez")" id="Nav-Breez">
|
||||||
|
|
||||||
|
<svg style="width: 15px; margin-left: 3px; margin-right: 7px;" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<g id="favicon-64-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="Group" transform="translate(3.000000, 2.000000)" fill-rule="nonzero">
|
||||||
|
<path d="M35.4723926,39.8214286 C35.4723926,37.9541367 33.9340883,36.4285714 32.0236136,36.4285714 L10.3260796,36.4285714 C8.42801059,36.4285714 6.87730061,37.9419322 6.87730061,39.8214286 C6.87730061,41.6887205 8.41560491,43.2142857 10.3260796,43.2142857 L32.0236136,43.2142857 C33.9216827,43.2020812 35.4723926,41.6887205 35.4723926,39.8214286 Z" id="Shape" fill="currentColor"></path>
|
||||||
|
<path d="M49.7395693,25.9068425 C51.3370507,23.7180544 52.2855553,21.0346249 52.2855553,18.1162407 C52.2855553,10.7460841 46.257559,4.77328937 38.819286,4.77328937 L36.9721981,4.77328937 L36.9721981,3.32646331 C36.9721981,1.48392415 35.462079,0 33.614991,0 C31.7554228,0 30.257784,1.49629019 30.257784,3.32646331 L30.257784,4.77328937 L26.8381753,4.77328937 L26.8381753,4.7856554 L24.6790793,4.7856554 C22.8819126,4.7856554 21.4341951,6.23248145 21.4341951,8.0008244 C21.4341951,9.78153339 22.894393,11.2159934 24.6790793,11.2159934 L26.8381753,11.2159934 L26.8381753,11.2283594 L38.7693647,11.2283594 C42.5009815,11.2283594 45.5212199,14.2209398 45.5212199,17.9183842 L45.5212199,17.9307502 C45.5212199,21.5292663 42.6507454,24.5342127 39.0439319,24.6084089 C38.3824747,24.6207749 36.3856229,24.6084089 36.3856229,24.6084089 C36.3731426,24.6084089 36.3481819,24.6084089 36.3357016,24.6084089 L36.310741,24.6084089 C34.3263695,24.657873 32.7413684,26.3520198 32.9535339,28.3800495 C33.1407387,30.1483924 34.7382202,31.4468261 36.5353868,31.4468261 L43.3496435,31.4468261 C44.0485417,31.4468261 44.7599201,31.4962902 45.4338576,31.6694147 C49.0905925,32.6215993 51.7988227,35.9109646 51.7988227,39.8433636 C51.7988227,44.5053586 47.9798437,48.2893652 43.2747616,48.2893652 L22.7071881,48.2893652 L18.8382878,48.2893652 C17.0161605,48.2893652 15.3937184,49.6125309 15.2189939,51.4056059 C15.0193087,53.4583677 16.6542311,55.1896125 18.6885239,55.1896125 L30.257784,55.1896125 L30.257784,56.6735367 C30.257784,58.5160758 31.7679031,60 33.614991,60 C35.4745593,60 36.9721981,58.5037098 36.9721981,56.6735367 L36.9721981,55.1896125 L39.8925939,55.1896125 C39.8925939,55.1896125 39.8925939,55.1896125 39.9050742,55.1896125 L43.5742894,55.1896125 C52.0983505,55.1896125 58.9999695,48.3388293 58.9999695,39.9051937 C59.0124498,33.6479802 55.1934708,28.2563891 49.7395693,25.9068425 Z" id="Shape" fill="currentColor"></path>
|
||||||
|
<path d="M22.4417178,28.0357143 C22.4417178,26.1684224 20.8948734,24.6428571 18.9737925,24.6428571 L3.46792526,24.6428571 C1.55931891,24.6428571 0,26.1562179 0,28.0357143 C0,29.9030062 1.54684436,31.4285714 3.46792526,31.4285714 L18.9737925,31.4285714 C20.8948734,31.4285714 22.4417178,29.9030062 22.4417178,28.0357143 Z" id="Shape" fill="currentColor"></path>
|
||||||
|
<path d="M15.7746737,21.4285714 L31.280541,21.4285714 C33.1891473,21.4285714 34.7484663,19.9152107 34.7484663,18.0357143 C34.7484663,16.1684224 33.2016219,14.6428571 31.280541,14.6428571 L15.7746737,14.6428571 C13.8660674,14.6428571 12.3067485,16.1562179 12.3067485,18.0357143 C12.3067485,19.9030062 13.8535928,21.4285714 15.7746737,21.4285714 Z" id="Shape" fill="currentColor"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
<span>Breez</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
@addTagHelper *, BTCPayServer.Abstractions
|
||||||
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
Reference in New Issue
Block a user