mirror of
https://github.com/aljazceru/btcpayserver-breez-nodeless-plugin.git
synced 2025-12-18 17:14:21 +01:00
fixed send, full transaction list
This commit is contained in:
@@ -5,14 +5,14 @@
|
|||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<NoWarn>$(NoWarn);CS1591;CS0618</NoWarn>
|
<NoWarn>$(NoWarn);CS1591;CS0618;CS8073</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<!-- Plugin specific properties -->
|
<!-- Plugin specific properties -->
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Product>BreezSpark Lightning Plugin</Product>
|
<Product>BreezSpark Lightning Plugin</Product>
|
||||||
<Description>Nodeless Lightning payments powered by Breez Spark SDK</Description>
|
<Description>Nodeless Lightning payments powered by Breez Nodeless SDK (Spark)</Description>
|
||||||
<Version>1.1.0</Version>
|
<Version>0.0.4</Version>
|
||||||
<Author>Aljaz Ceru</Author>
|
<Author>Aljaz Ceru</Author>
|
||||||
<Company>Aljaz Ceru</Company>
|
<Company>Aljaz Ceru</Company>
|
||||||
<AssemblyName>BTCPayServer.Plugins.BreezSpark</AssemblyName>
|
<AssemblyName>BTCPayServer.Plugins.BreezSpark</AssemblyName>
|
||||||
@@ -40,6 +40,11 @@
|
|||||||
<EmbeddedResource Include="Resources\**" />
|
<EmbeddedResource Include="Resources\**" />
|
||||||
<ProjectReference Include="../btcpayserver/BTCPayServer/BTCPayServer.csproj" />
|
<ProjectReference Include="../btcpayserver/BTCPayServer/BTCPayServer.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="csharp\**\*.cs" />
|
||||||
|
<EmbeddedResource Remove="csharp\**\*" />
|
||||||
|
<None Include="csharp\**\*" />
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Breez.Sdk.Spark" Version="0.4.1" />
|
<PackageReference Include="Breez.Sdk.Spark" Version="0.4.1" />
|
||||||
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
|
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ using BTCPayServer.Services.Stores;
|
|||||||
using BTCPayServer.Services.Wallets;
|
using BTCPayServer.Services.Wallets;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
using NBitcoin.DataEncoders;
|
using NBitcoin.DataEncoders;
|
||||||
|
|
||||||
@@ -32,18 +33,22 @@ public class BreezSparkController : Controller
|
|||||||
private readonly BreezSparkService _breezService;
|
private readonly BreezSparkService _breezService;
|
||||||
private readonly BTCPayWalletProvider _btcWalletProvider;
|
private readonly BTCPayWalletProvider _btcWalletProvider;
|
||||||
private readonly StoreRepository _storeRepository;
|
private readonly StoreRepository _storeRepository;
|
||||||
|
private readonly ILogger<BreezSparkController> _logger;
|
||||||
|
|
||||||
public BreezSparkController(
|
public BreezSparkController(
|
||||||
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary,
|
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary,
|
||||||
BTCPayNetworkProvider btcPayNetworkProvider,
|
BTCPayNetworkProvider btcPayNetworkProvider,
|
||||||
BreezSparkService breezService,
|
BreezSparkService breezService,
|
||||||
BTCPayWalletProvider btcWalletProvider, StoreRepository storeRepository)
|
BTCPayWalletProvider btcWalletProvider,
|
||||||
|
StoreRepository storeRepository,
|
||||||
|
ILogger<BreezSparkController> logger)
|
||||||
{
|
{
|
||||||
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
|
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
|
||||||
_btcPayNetworkProvider = btcPayNetworkProvider;
|
_btcPayNetworkProvider = btcPayNetworkProvider;
|
||||||
_breezService = breezService;
|
_breezService = breezService;
|
||||||
_btcWalletProvider = btcWalletProvider;
|
_btcWalletProvider = btcWalletProvider;
|
||||||
_storeRepository = storeRepository;
|
_storeRepository = storeRepository;
|
||||||
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -176,7 +181,7 @@ public class BreezSparkController : Controller
|
|||||||
TempData["bolt11"] = response.paymentRequest;
|
TempData["bolt11"] = response.paymentRequest;
|
||||||
TempData[WellKnownTempData.SuccessMessage] = "Invoice created successfully!";
|
TempData[WellKnownTempData.SuccessMessage] = "Invoice created successfully!";
|
||||||
|
|
||||||
return RedirectToAction(nameof(Payments), new {storeId});
|
return RedirectToAction(nameof(Transactions), new {storeId});
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -203,11 +208,7 @@ public class BreezSparkController : Controller
|
|||||||
return RedirectToAction(nameof(Send), new {storeId});
|
return RedirectToAction(nameof(Send), new {storeId});
|
||||||
}
|
}
|
||||||
|
|
||||||
BigInteger? amountSats = null;
|
var amountSats = ResolveAmountSats(address, amount);
|
||||||
if (amount > 0)
|
|
||||||
{
|
|
||||||
amountSats = new BigInteger(amount.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
var prepareRequest = new PrepareSendPaymentRequest(
|
var prepareRequest = new PrepareSendPaymentRequest(
|
||||||
paymentRequest: address,
|
paymentRequest: address,
|
||||||
@@ -219,38 +220,30 @@ public class BreezSparkController : Controller
|
|||||||
if (prepareResponse.paymentMethod is SendPaymentMethod.Bolt11Invoice bolt11Method)
|
if (prepareResponse.paymentMethod is SendPaymentMethod.Bolt11Invoice bolt11Method)
|
||||||
{
|
{
|
||||||
var totalFee = bolt11Method.lightningFeeSats + (bolt11Method.sparkTransferFeeSats ?? 0);
|
var totalFee = bolt11Method.lightningFeeSats + (bolt11Method.sparkTransferFeeSats ?? 0);
|
||||||
var viewModel = new
|
var amt = amountSats ?? BigInteger.Zero;
|
||||||
{
|
ViewData["PaymentDetails"] = new PaymentDetailsDto(
|
||||||
Destination = address,
|
Destination: address,
|
||||||
Amount = amountSats ?? 0,
|
Amount: (long)amt,
|
||||||
Fee = totalFee,
|
Fee: (long)totalFee
|
||||||
PrepareResponseJson = JsonSerializer.Serialize(prepareResponse)
|
);
|
||||||
};
|
|
||||||
ViewData["PaymentDetails"] = viewModel;
|
|
||||||
}
|
}
|
||||||
else if (prepareResponse.paymentMethod is SendPaymentMethod.BitcoinAddress bitcoinMethod)
|
else if (prepareResponse.paymentMethod is SendPaymentMethod.BitcoinAddress bitcoinMethod)
|
||||||
{
|
{
|
||||||
var fees = bitcoinMethod.feeQuote;
|
var fees = bitcoinMethod.feeQuote;
|
||||||
var mediumFee = fees.speedMedium.userFeeSat + fees.speedMedium.l1BroadcastFeeSat;
|
var mediumFee = fees.speedMedium.userFeeSat + fees.speedMedium.l1BroadcastFeeSat;
|
||||||
var viewModel = new
|
ViewData["PaymentDetails"] = new PaymentDetailsDto(
|
||||||
{
|
Destination: address,
|
||||||
Destination = address,
|
Amount: (long)BigInteger.Abs(amountSats ?? BigInteger.Zero),
|
||||||
Amount = amountSats ?? 0,
|
Fee: (long)mediumFee
|
||||||
Fee = mediumFee,
|
);
|
||||||
PrepareResponseJson = JsonSerializer.Serialize(prepareResponse)
|
|
||||||
};
|
|
||||||
ViewData["PaymentDetails"] = viewModel;
|
|
||||||
}
|
}
|
||||||
else if (prepareResponse.paymentMethod is SendPaymentMethod.SparkAddress sparkMethod)
|
else if (prepareResponse.paymentMethod is SendPaymentMethod.SparkAddress sparkMethod)
|
||||||
{
|
{
|
||||||
var viewModel = new
|
ViewData["PaymentDetails"] = new PaymentDetailsDto(
|
||||||
{
|
Destination: address,
|
||||||
Destination = address,
|
Amount: (long)BigInteger.Abs(amountSats ?? BigInteger.Zero),
|
||||||
Amount = amountSats ?? 0,
|
Fee: (long)sparkMethod.fee
|
||||||
Fee = sparkMethod.fee,
|
);
|
||||||
PrepareResponseJson = JsonSerializer.Serialize(prepareResponse)
|
|
||||||
};
|
|
||||||
ViewData["PaymentDetails"] = viewModel;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -263,7 +256,7 @@ public class BreezSparkController : Controller
|
|||||||
|
|
||||||
[HttpPost("confirm-send")]
|
[HttpPost("confirm-send")]
|
||||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||||
public async Task<IActionResult> ConfirmSend(string storeId, string paymentRequest, long amount, string prepareResponseJson)
|
public async Task<IActionResult> ConfirmSend(string storeId, string paymentRequest, long amount)
|
||||||
{
|
{
|
||||||
var client = _breezService.GetClient(storeId);
|
var client = _breezService.GetClient(storeId);
|
||||||
if (client is null)
|
if (client is null)
|
||||||
@@ -273,11 +266,12 @@ public class BreezSparkController : Controller
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var prepareResponse = JsonSerializer.Deserialize<PrepareSendPaymentResponse>(prepareResponseJson);
|
// Re-run preparation to avoid polymorphic JSON deserialization issues
|
||||||
if (prepareResponse == null)
|
var amountSats = ResolveAmountSats(paymentRequest, amount);
|
||||||
{
|
var prepareResponse = await client.Sdk.PrepareSendPayment(new PrepareSendPaymentRequest(
|
||||||
throw new InvalidOperationException("Invalid payment preparation data");
|
paymentRequest: paymentRequest,
|
||||||
}
|
amount: amountSats
|
||||||
|
));
|
||||||
|
|
||||||
SendPaymentOptions? options = prepareResponse.paymentMethod switch
|
SendPaymentOptions? options = prepareResponse.paymentMethod switch
|
||||||
{
|
{
|
||||||
@@ -289,7 +283,8 @@ public class BreezSparkController : Controller
|
|||||||
confirmationSpeed: OnchainConfirmationSpeed.Medium
|
confirmationSpeed: OnchainConfirmationSpeed.Medium
|
||||||
),
|
),
|
||||||
SendPaymentMethod.SparkAddress => null,
|
SendPaymentMethod.SparkAddress => null,
|
||||||
_ => throw new NotSupportedException("Unsupported payment method")
|
SendPaymentMethod.SparkInvoice => null,
|
||||||
|
_ => null
|
||||||
};
|
};
|
||||||
|
|
||||||
var sendRequest = new SendPaymentRequest(
|
var sendRequest = new SendPaymentRequest(
|
||||||
@@ -297,14 +292,18 @@ public class BreezSparkController : Controller
|
|||||||
options: options
|
options: options
|
||||||
);
|
);
|
||||||
|
|
||||||
|
_logger.LogInformation("BreezSpark sending payment for store {StoreId} to {Destination}", storeId, paymentRequest);
|
||||||
var sendResponse = await client.Sdk.SendPayment(sendRequest);
|
var sendResponse = await client.Sdk.SendPayment(sendRequest);
|
||||||
|
_logger.LogInformation("BreezSpark send complete for store {StoreId}: payment id {PaymentId}, status {Status}",
|
||||||
|
storeId, sendResponse.payment?.id, sendResponse.payment?.status);
|
||||||
|
|
||||||
TempData[WellKnownTempData.SuccessMessage] = "Payment sent successfully!";
|
TempData[WellKnownTempData.SuccessMessage] = "Payment sent successfully!";
|
||||||
return RedirectToAction(nameof(Payments), new {storeId});
|
return RedirectToAction(nameof(Transactions), new {storeId});
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
TempData[WellKnownTempData.ErrorMessage] = $"Error sending payment: {ex.Message}";
|
TempData[WellKnownTempData.ErrorMessage] = $"Error sending payment: {ex.Message}";
|
||||||
|
_logger.LogError(ex, "BreezSpark send failed for store {StoreId}", storeId);
|
||||||
return RedirectToAction(nameof(Send), new {storeId});
|
return RedirectToAction(nameof(Send), new {storeId});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -484,9 +483,9 @@ public class BreezSparkController : Controller
|
|||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("payments")]
|
[Route("transactions")]
|
||||||
[Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
[Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||||
public async Task<IActionResult> Payments(string storeId, PaymentsViewModel viewModel)
|
public async Task<IActionResult> Transactions(string storeId, PaymentsViewModel viewModel)
|
||||||
{
|
{
|
||||||
var client = _breezService.GetClient(storeId);
|
var client = _breezService.GetClient(storeId);
|
||||||
if (client is null)
|
if (client is null)
|
||||||
@@ -495,6 +494,7 @@ public class BreezSparkController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
viewModel ??= new PaymentsViewModel();
|
viewModel ??= new PaymentsViewModel();
|
||||||
|
viewModel.Balance = await client.GetBalance();
|
||||||
var req = new ListPaymentsRequest(
|
var req = new ListPaymentsRequest(
|
||||||
typeFilter: null,
|
typeFilter: null,
|
||||||
statusFilter: null,
|
statusFilter: null,
|
||||||
@@ -506,18 +506,75 @@ public class BreezSparkController : Controller
|
|||||||
sortAscending: false
|
sortAscending: false
|
||||||
);
|
);
|
||||||
var response = await client.Sdk.ListPayments(req);
|
var response = await client.Sdk.ListPayments(req);
|
||||||
viewModel.Payments = response.payments.Select(client.NormalizePayment).ToList();
|
var normalized = new List<NormalizedPayment>();
|
||||||
|
foreach (var p in response.payments.Where(p => p != null))
|
||||||
|
{
|
||||||
|
var norm = client.NormalizePayment(p);
|
||||||
|
if (norm is not null)
|
||||||
|
{
|
||||||
|
normalized.Add(norm);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
return View(viewModel);
|
// Fallback: show raw SDK payment even if we lack invoice context
|
||||||
|
long amountSat = 0;
|
||||||
|
if (p.details is PaymentDetails.Lightning l && !string.IsNullOrEmpty(l.invoice))
|
||||||
|
{
|
||||||
|
var nbitcoinNetwork = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>("BTC")?.NBitcoinNetwork ?? NBitcoin.Network.Main;
|
||||||
|
if (BOLT11PaymentRequest.TryParse(l.invoice, out var pr, nbitcoinNetwork) && pr?.MinimumAmount is not null)
|
||||||
|
{
|
||||||
|
amountSat = (long)pr.MinimumAmount.ToUnit(LightMoneyUnit.Satoshi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long feeSat = 0;
|
||||||
|
if (p.fees != null)
|
||||||
|
{
|
||||||
|
feeSat = (long)(p.fees / 1000);
|
||||||
|
}
|
||||||
|
normalized.Add(new NormalizedPayment
|
||||||
|
{
|
||||||
|
Id = p.id ?? Guid.NewGuid().ToString("N"),
|
||||||
|
PaymentType = p.paymentType,
|
||||||
|
Status = p.status,
|
||||||
|
Timestamp = p.timestamp,
|
||||||
|
Amount = LightMoney.Satoshis(amountSat),
|
||||||
|
Fee = LightMoney.Satoshis(feeSat),
|
||||||
|
Description = p.details?.ToString() ?? "BreezSpark payment"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
viewModel.Payments = normalized;
|
||||||
|
|
||||||
|
return View("Transactions", viewModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigInteger? ResolveAmountSats(string paymentRequest, long? amount)
|
||||||
|
{
|
||||||
|
if (amount.HasValue && amount.Value > 0)
|
||||||
|
{
|
||||||
|
return new BigInteger(amount.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to derive amount from bolt11 invoice if present
|
||||||
|
var nbitcoinNetwork = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>("BTC")?.NBitcoinNetwork ?? NBitcoin.Network.Main;
|
||||||
|
if (BOLT11PaymentRequest.TryParse(paymentRequest, out var pr, nbitcoinNetwork) && pr?.MinimumAmount is not null)
|
||||||
|
{
|
||||||
|
return new BigInteger((long)pr.MinimumAmount.ToUnit(LightMoneyUnit.Satoshi));
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PaymentsViewModel : BasePagingViewModel
|
public class PaymentsViewModel : BasePagingViewModel
|
||||||
{
|
{
|
||||||
public List<NormalizedPayment> Payments { get; set; } = new();
|
public List<NormalizedPayment> Payments { get; set; } = new();
|
||||||
|
public LightningNodeBalance? Balance { get; set; }
|
||||||
public override int CurrentPageCount => Payments.Count;
|
public override int CurrentPageCount => Payments.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public record PaymentDetailsDto(string Destination, long Amount, long Fee);
|
||||||
|
|
||||||
// Helper class for swap information display in views
|
// Helper class for swap information display in views
|
||||||
public class SwapInfo
|
public class SwapInfo
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -215,10 +215,6 @@ public class BreezSparkLightningClient : ILightningClient, IDisposable
|
|||||||
|
|
||||||
return new LightningNodeBalance()
|
return new LightningNodeBalance()
|
||||||
{
|
{
|
||||||
OnchainBalance = new OnchainBalance()
|
|
||||||
{
|
|
||||||
Confirmed = Money.Satoshis((long)response.balanceSats)
|
|
||||||
},
|
|
||||||
OffchainBalance = new OffchainBalance()
|
OffchainBalance = new OffchainBalance()
|
||||||
{
|
{
|
||||||
Local = LightMoney.Satoshis((long)response.balanceSats),
|
Local = LightMoney.Satoshis((long)response.balanceSats),
|
||||||
@@ -230,10 +226,6 @@ public class BreezSparkLightningClient : ILightningClient, IDisposable
|
|||||||
{
|
{
|
||||||
return new LightningNodeBalance()
|
return new LightningNodeBalance()
|
||||||
{
|
{
|
||||||
OnchainBalance = new OnchainBalance()
|
|
||||||
{
|
|
||||||
Confirmed = Money.Zero
|
|
||||||
},
|
|
||||||
OffchainBalance = new OffchainBalance()
|
OffchainBalance = new OffchainBalance()
|
||||||
{
|
{
|
||||||
Local = LightMoney.Zero,
|
Local = LightMoney.Zero,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
@using BTCPayServer.Security
|
@using BTCPayServer.Security
|
||||||
@using BTCPayServer.Services
|
@using BTCPayServer.Services
|
||||||
@using BTCPayServer.Services.Invoices
|
@using BTCPayServer.Services.Invoices
|
||||||
|
@using BTCPayServer.Lightning
|
||||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
@using NBitcoin
|
@using NBitcoin
|
||||||
@inject BreezSparkService BreezService
|
@inject BreezSparkService BreezService
|
||||||
@@ -22,14 +23,17 @@
|
|||||||
StoreDashboardViewModel dashboardModel => dashboardModel.StoreId,
|
StoreDashboardViewModel dashboardModel => dashboardModel.StoreId,
|
||||||
_ => Context.GetImplicitStoreId()
|
_ => Context.GetImplicitStoreId()
|
||||||
};
|
};
|
||||||
var sdk = BreezService.GetClient(storeId)?.Sdk;
|
var client = BreezService.GetClient(storeId);
|
||||||
if (sdk is null)
|
var sdk = client?.Sdk;
|
||||||
|
if (client is null || sdk is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
LightningNodeBalance balance = await client.GetBalance();
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="row mb-4 mt-4">
|
<div class="row mb-4 mt-4">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<h3>BreezSpark Lightning Node Information</h3>
|
<h3>BreezSpark Lightning Balance</h3>
|
||||||
|
|
||||||
@try
|
@try
|
||||||
{
|
{
|
||||||
@@ -37,39 +41,13 @@
|
|||||||
{
|
{
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h5 class="card-title mb-0">Node Status</h5>
|
<h5 class="card-title mb-0">Balance</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p class="text-muted">
|
<h3 class="mb-0">
|
||||||
BreezSpark Lightning Node is connected and operational.
|
@((balance?.OffchainBalance?.Local ?? LightMoney.Zero).ToDecimal(LightMoneyUnit.BTC)) BTC
|
||||||
This is a simplified view for SDK v0.4.1 compatibility.
|
</h3>
|
||||||
</p>
|
<p class="text-muted mb-0">Off-chain balance (Breez Spark)</p>
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<dl class="row">
|
|
||||||
<dt class="col-sm-6">Status</dt>
|
|
||||||
<dd class="col-sm-6">
|
|
||||||
<span class="badge bg-success">Connected</span>
|
|
||||||
</dd>
|
|
||||||
|
|
||||||
<dt class="col-sm-6">Network</dt>
|
|
||||||
<dd class="col-sm-6">Bitcoin</dd>
|
|
||||||
|
|
||||||
<dt class="col-sm-6">Type</dt>
|
|
||||||
<dd class="col-sm-6">Breez Spark SDK v0.4.1</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<dl class="row">
|
|
||||||
<dt class="col-sm-6">Service</dt>
|
|
||||||
<dd class="col-sm-6">Lightning Network</dd>
|
|
||||||
|
|
||||||
<dt class="col-sm-6">Integration</dt>
|
|
||||||
<dd class="col-sm-6">BTCPay Server</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -80,13 +58,13 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<a href="@Url.Action("SwapIn", "BreezSpark", new { storeId = storeId })" class="btn btn-primary w-100 mb-2">
|
<a href="@Url.Action("Receive", "BreezSpark", new { storeId = storeId })" class="btn btn-primary w-100 mb-2">
|
||||||
<i class="bi bi-arrow-down-circle"></i> Swap In
|
<i class="bi bi-arrow-down-circle"></i> Receive
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<a href="@Url.Action("SwapOut", "BreezSpark", new { storeId = storeId })" class="btn btn-success w-100 mb-2">
|
<a href="@Url.Action("Send", "BreezSpark", new { storeId = storeId })" class="btn btn-success w-100 mb-2">
|
||||||
<i class="bi bi-arrow-up-circle"></i> Swap Out
|
<i class="bi bi-arrow-up-circle"></i> Send
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@if (ViewData["PaymentDetails"] is PaymentDetailsViewModel paymentDetails)
|
@if (ViewData["PaymentDetails"] is BTCPayServer.Plugins.BreezSpark.PaymentDetailsDto paymentDetails)
|
||||||
{
|
{
|
||||||
<div class="payment-details mb-4">
|
<div class="payment-details mb-4">
|
||||||
<h4>Payment Details</h4>
|
<h4>Payment Details</h4>
|
||||||
@@ -54,7 +54,6 @@
|
|||||||
<form method="post" asp-action="ConfirmSend" asp-route-storeId="@storeId">
|
<form method="post" asp-action="ConfirmSend" asp-route-storeId="@storeId">
|
||||||
<input type="hidden" name="paymentRequest" value="@paymentDetails.Destination" />
|
<input type="hidden" name="paymentRequest" value="@paymentDetails.Destination" />
|
||||||
<input type="hidden" name="amount" value="@paymentDetails.Amount" />
|
<input type="hidden" name="amount" value="@paymentDetails.Amount" />
|
||||||
<input type="hidden" name="prepareResponse" value="@paymentDetails.PrepareResponseJson" />
|
|
||||||
<button type="submit" class="btn btn-success">Confirm and Send</button>
|
<button type="submit" class="btn btn-success">Confirm and Send</button>
|
||||||
<a href="javascript:history.back()" class="btn btn-secondary">Cancel</a>
|
<a href="javascript:history.back()" class="btn btn-secondary">Cancel</a>
|
||||||
</form>
|
</form>
|
||||||
@@ -70,7 +69,7 @@ else
|
|||||||
<span>@ViewData["Title"]</span>
|
<span>@ViewData["Title"]</span>
|
||||||
</h3>
|
</h3>
|
||||||
<div class="d-flex gap-3 mt-3 mt-sm-0">
|
<div class="d-flex gap-3 mt-3 mt-sm-0">
|
||||||
<button type="submit" class="btn btn-primary">Prepare Payment</button>
|
<button type="submit" class="btn btn-primary">Pay</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||||
@@ -81,20 +80,12 @@ else
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="amount" class="form-label">Amount (sats)</label>
|
<label for="amount" class="form-label">Amount (sats)</label>
|
||||||
<input type="number" id="amount" name="amount" min="1" max="@max" class="form-control" placeholder="Amount in satoshis"/>
|
<input type="number" id="amount" name="amount" min="1" max="@max" class="form-control" placeholder="Leave blank if invoice has amount"/>
|
||||||
<small class="form-text text-muted">Maximum payable: @max sats</small>
|
<small class="form-text text-muted">Maximum payable: @max sats. Bolt11 amounts are auto-read; fill only for zero-amount invoices.</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
|
|
||||||
@functions {
|
@* PaymentDetails model is provided via ViewData from the controller *@
|
||||||
public class PaymentDetailsViewModel
|
|
||||||
{
|
|
||||||
public string Destination { get; set; } = string.Empty;
|
|
||||||
public long Amount { get; set; }
|
|
||||||
public long Fee { get; set; }
|
|
||||||
public string PrepareResponseJson { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,11 +5,12 @@
|
|||||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
@using BTCPayServer.Components.QRCode
|
@using BTCPayServer.Components.QRCode
|
||||||
@using BTCPayServer.Components.TruncateCenter
|
@using BTCPayServer.Components.TruncateCenter
|
||||||
|
@using BTCPayServer.Lightning
|
||||||
@model BTCPayServer.Plugins.BreezSpark.PaymentsViewModel
|
@model BTCPayServer.Plugins.BreezSpark.PaymentsViewModel
|
||||||
@{
|
@{
|
||||||
var storeId = Context.GetCurrentStoreId();
|
var storeId = Context.GetCurrentStoreId();
|
||||||
|
|
||||||
ViewData.SetActivePage("BreezSpark", "Payments", "Payments");
|
ViewData.SetActivePage("BreezSpark", "Transactions", "Transactions");
|
||||||
TempData.TryGetValue("bolt11", out var bolt11);
|
TempData.TryGetValue("bolt11", out var bolt11);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@
|
|||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="d-flex align-items-center justify-content-between mb-3">
|
<div class="d-flex align-items-center justify-content-between mb-3">
|
||||||
<h3 class="mb-0">
|
<h3 class="mb-0">
|
||||||
<span>@ViewData["Title"]</span>
|
<span>Transactions</span>
|
||||||
</h3>
|
</h3>
|
||||||
<div class="d-flex gap-3 mt-3 mt-sm-0">
|
<div class="d-flex gap-3 mt-3 mt-sm-0">
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<partial name="BreezSpark/BreezSparkPaymentsTable" model="Model.Payments"/>
|
<partial name="BreezSpark/BreezSparkTransactionsTable" model="Model.Payments"/>
|
||||||
<vc:pager view-model="Model"></vc:pager>
|
<vc:pager view-model="Model"></vc:pager>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
<a permission="@Policies.CanViewStoreSettings" asp-action="Info" asp-route-storeId="@storeId" class="nav-link @ViewData.ActivePageClass("BreezSpark", null, "Info")">Info</a>
|
<a permission="@Policies.CanViewStoreSettings" asp-action="Info" asp-route-storeId="@storeId" class="nav-link @ViewData.ActivePageClass("BreezSpark", null, "Info")">Info</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item nav-item-sub">
|
<li class="nav-item nav-item-sub">
|
||||||
<a permission="@Policies.CanViewStoreSettings" asp-action="Payments" asp-route-storeId="@storeId" class="nav-link @ViewData.ActivePageClass("BreezSpark", null, "Payments")">Payments</a>
|
<a permission="@Policies.CanViewStoreSettings" asp-action="Transactions" asp-route-storeId="@storeId" class="nav-link @ViewData.ActivePageClass("BreezSpark", null, "Transactions")">Transactions</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item nav-item-sub">
|
<li class="nav-item nav-item-sub">
|
||||||
<a permission="@Policies.CanModifyStoreSettings" asp-action="Configure" asp-route-storeId="@storeId" class="nav-link @ViewData.ActivePageClass("BreezSpark", null, "Configure")">Configuration</a>
|
<a permission="@Policies.CanModifyStoreSettings" asp-action="Configure" asp-route-storeId="@storeId" class="nav-link @ViewData.ActivePageClass("BreezSpark", null, "Configure")">Configuration</a>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
@using BTCPayServer.Security
|
@using BTCPayServer.Security
|
||||||
@model List<NormalizedPayment>
|
@model List<NormalizedPayment>
|
||||||
@{
|
@{
|
||||||
var data = Model ?? new List<NormalizedPayment>();
|
var data = (Model ?? new List<NormalizedPayment>()).Where(p => p != null).ToList();
|
||||||
var storeId = Context.GetImplicitStoreId() ?? string.Empty;
|
var storeId = Context.GetImplicitStoreId() ?? string.Empty;
|
||||||
var isDashboard = false;
|
var isDashboard = false;
|
||||||
}
|
}
|
||||||
@@ -20,20 +20,11 @@
|
|||||||
</style>
|
</style>
|
||||||
}
|
}
|
||||||
<div id="breezspark-payments" class="@(isDashboard ? "widget store-wallet-balance" : "")">
|
<div id="breezspark-payments" class="@(isDashboard ? "widget store-wallet-balance" : "")">
|
||||||
@if (isDashboard)
|
@* Dashboard widget header removed for simplicity *@
|
||||||
{
|
|
||||||
<header>
|
|
||||||
<h3>BreezSpark Payments</h3>
|
|
||||||
@if (data.Any())
|
|
||||||
{
|
|
||||||
<a asp-controller="BreezSpark" asp-action="Payments" asp-route-storeId="@storeId">View All</a>
|
|
||||||
}
|
|
||||||
</header>
|
|
||||||
}
|
|
||||||
@if (!data.Any())
|
@if (!data.Any())
|
||||||
{
|
{
|
||||||
<p class="text-secondary mt-3 mb-0">
|
<p class="text-secondary mt-3 mb-0">
|
||||||
There are no recent payments.
|
There are no recent transactions.
|
||||||
</p>
|
</p>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -54,6 +45,10 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
@foreach (var payment in data)
|
@foreach (var payment in data)
|
||||||
{
|
{
|
||||||
|
if (payment == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="smMaxWidth text-truncate">
|
<td class="smMaxWidth text-truncate">
|
||||||
|
|
||||||
Reference in New Issue
Block a user