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