Simplify extension of payments extensions

This commit is contained in:
nicolas.dorier 2024-10-07 18:19:19 +09:00
parent e1bfc04451
commit 34b2cca492
No known key found for this signature in database
GPG Key ID: 6618763EF09186FE
15 changed files with 55 additions and 126 deletions

View File

@ -3264,6 +3264,7 @@ namespace BTCPayServer.Tests
public async Task CanUseLNAddress()
{
using var s = CreateSeleniumTester();
s.Server.DeleteStore = false;
s.Server.ActivateLightning();
await s.StartAsync();
await s.Server.EnsureChannelsSetup();
@ -3416,7 +3417,13 @@ namespace BTCPayServer.Tests
var succ = JsonConvert.DeserializeObject<LNURLPayRequest.LNURLPayRequestCallbackResponse>(str);
Assert.NotNull(succ.Pr);
Assert.Equal(new LightMoney(2001), BOLT11PaymentRequest.Parse(succ.Pr, Network.RegTest).MinimumAmount);
await s.Server.CustomerLightningD.Pay(succ.Pr);
}
// Can we find our comment and address in the payment list?
s.GoToInvoices();
var source = s.Driver.PageSource;
Assert.Contains(lnUsername, source);
}
[Fact]

View File

@ -609,9 +609,7 @@ retry:
var methods = await client.GetInvoicePaymentMethods(StoreId, invoiceId);
var method = methods.First(m => m.PaymentMethodId == $"{cryptoCode}-LN");
var bolt11 = method.Destination;
TestLogs.LogInformation("PAYING");
await parent.CustomerLightningD.Pay(bolt11);
TestLogs.LogInformation("PAID");
await WaitInvoicePaid(invoiceId);
}

View File

@ -68,7 +68,7 @@ public class StoreRecentInvoices : ViewComponent
Details = new InvoiceDetailsModel
{
Archived = invoice.Archived,
Payments = invoice.GetPayments(false)
Payments = invoice.GetPayments(false)
}
}).ToList();

View File

@ -128,6 +128,7 @@ namespace BTCPayServer.Controllers
StoreLink = Url.Action(nameof(UIStoresController.GeneralSettings), "UIStores", new { storeId = store.Id }),
PaymentRequestLink = Url.Action(nameof(UIPaymentRequestController.ViewPaymentRequest), "UIPaymentRequest", new { payReqId = invoice.Metadata.PaymentRequestId }),
Id = invoice.Id,
Entity = invoice,
State = invoiceState,
TransactionSpeed = invoice.SpeedPolicy == SpeedPolicy.HighSpeed ? "high" :
invoice.SpeedPolicy == SpeedPolicy.MediumSpeed ? "medium" :
@ -554,6 +555,7 @@ namespace BTCPayServer.Controllers
{
Archived = invoice.Archived,
Payments = invoice.GetPayments(false),
Entity = invoice,
CryptoPayments = invoice.GetPaymentPrompts().Select(
data =>
{

View File

@ -63,7 +63,6 @@ namespace BTCPayServer.Controllers
private readonly IAuthorizationService _authorizationService;
private readonly TransactionLinkProviders _transactionLinkProviders;
private readonly Dictionary<PaymentMethodId, IPaymentModelExtension> _paymentModelExtensions;
private readonly PaymentMethodViewProvider _viewProvider;
private readonly PrettyNameProvider _prettyName;
private readonly AppService _appService;
private readonly IFileService _fileService;
@ -99,7 +98,6 @@ namespace BTCPayServer.Controllers
IAuthorizationService authorizationService,
TransactionLinkProviders transactionLinkProviders,
Dictionary<PaymentMethodId, IPaymentModelExtension> paymentModelExtensions,
PaymentMethodViewProvider viewProvider,
PrettyNameProvider prettyName)
{
_displayFormatter = displayFormatter;
@ -124,7 +122,6 @@ namespace BTCPayServer.Controllers
_authorizationService = authorizationService;
_transactionLinkProviders = transactionLinkProviders;
_paymentModelExtensions = paymentModelExtensions;
_viewProvider = viewProvider;
_prettyName = prettyName;
_fileService = fileService;
_uriResolver = uriResolver;

View File

@ -292,10 +292,9 @@ namespace BTCPayServer
}
public static IServiceCollection AddUIExtension(this IServiceCollection services, string key, string partialView)
public static IServiceCollection AddUIExtension(this IServiceCollection services, string location, string partialViewName)
{
services.AddSingleton<IUIExtension>(new UIExtension(partialView,
key));
services.AddSingleton<IUIExtension>(new UIExtension(partialViewName, location));
return services;
}
public static IServiceCollection AddReportProvider<T>(this IServiceCollection services)

View File

@ -158,8 +158,7 @@ namespace BTCPayServer.Hosting
//
AddOnchainWalletParsers(services);
services.AddSingleton<IUIExtension>(new UIExtension("Bitcoin/ViewBitcoinLikePaymentData", "store-invoices-payments"));
services.AddSingleton<IUIExtension>(new UIExtension("Lightning/ViewLightningLikePaymentData", "store-invoices-payments"));
services.AddStartupTask<BlockExplorerLinkStartupTask>();
services.AddStartupTask<LoadCurrencyNameTableStartupTask>();
@ -367,6 +366,8 @@ namespace BTCPayServer.Hosting
services.AddUIExtension("checkout-end", "Bitcoin/BitcoinLikeMethodCheckout");
services.AddUIExtension("checkout-end", "Lightning/LightningLikeMethodCheckout");
services.AddUIExtension("store-invoices-payments", "Bitcoin/ViewBitcoinLikePaymentData");
services.AddUIExtension("store-invoices-payments", "Lightning/ViewLightningLikePaymentData");
services.AddSingleton<Services.NBXplorerConnectionFactory>();
services.AddSingleton<IHostedService, Services.NBXplorerConnectionFactory>(o => o.GetRequiredService<Services.NBXplorerConnectionFactory>());
@ -407,7 +408,6 @@ o.GetRequiredService<IEnumerable<IPaymentLinkExtension>>().ToDictionary(o => o.P
services.AddSingleton<IHostedService, LightningPendingPayoutListener>();
services.AddSingleton<PaymentMethodHandlerDictionary>();
services.AddSingleton<PaymentMethodViewProvider>();
services.AddSingleton<PayoutMethodHandlerDictionary>();
@ -668,8 +668,6 @@ o.GetRequiredService<IEnumerable<IPaymentLinkExtension>>().ToDictionary(o => o.P
(IPaymentLinkExtension)ActivatorUtilities.CreateInstance(provider, typeof(LNURLPayPaymentLinkExtension), new object[] { network, pmi }));
services.AddSingleton<IPaymentModelExtension>(provider =>
(IPaymentModelExtension)ActivatorUtilities.CreateInstance(provider, typeof(LNURLPayPaymentModelExtension), new object[] { network, pmi }));
services.AddSingleton<IPaymentMethodViewExtension>(provider =>
(IPaymentMethodViewExtension)ActivatorUtilities.CreateInstance(provider, typeof(LNURLPaymentMethodViewExtension), new object[] { pmi }));
services.AddSingleton<IPaymentMethodBitpayAPIExtension>(provider =>
(IPaymentMethodBitpayAPIExtension)ActivatorUtilities.CreateInstance(provider, typeof(LNURLPayPaymentMethodBitpayAPIExtension), new object[] { pmi }));
}

View File

@ -136,5 +136,6 @@ namespace BTCPayServer.Models.InvoicingModels
public bool HasRefund { get; set; }
public bool StillDue { get; set; }
public bool HasRates { get; set; }
public InvoiceEntity Entity { get; internal set; }
}
}

View File

@ -1,59 +0,0 @@
#nullable enable
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using BTCPayServer.Services.Invoices;
namespace BTCPayServer.Payments
{
public interface IPaymentMethodViewExtension
{
PaymentMethodId PaymentMethodId { get; }
void RegisterViews(PaymentMethodViewContext context);
}
public record ViewViewModel(object View, object ViewModel);
public class PaymentMethodViewProvider
{
private readonly Dictionary<PaymentMethodId, IPaymentMethodViewExtension> _extensions;
private readonly PaymentMethodHandlerDictionary _handlers;
public PaymentMethodViewProvider(
IEnumerable<IPaymentMethodViewExtension> extensions,
PaymentMethodHandlerDictionary handlers)
{
_extensions = extensions.ToDictionary(o => o.PaymentMethodId, o => o);
_handlers = handlers;
}
public ViewViewModel? TryGetViewViewModel(PaymentPrompt paymentPrompt, string key)
{
if (!_extensions.TryGetValue(paymentPrompt.PaymentMethodId, out var extension))
return null;
if (!_handlers.TryGetValue(paymentPrompt.PaymentMethodId, out var handler) || paymentPrompt.Details is null)
return null;
var ctx = new PaymentMethodViewContext()
{
Details = handler.ParsePaymentPromptDetails(paymentPrompt.Details)
};
extension.RegisterViews(ctx);
object? view = null;
if (!ctx._Views.TryGetValue(key, out view))
return null;
return new ViewViewModel(view, handler.ParsePaymentPromptDetails(paymentPrompt.Details));
}
}
public class PaymentMethodViewContext
{
internal Dictionary<string, object> _Views = new Dictionary<string, object>();
public object? Details { get; internal set; }
public void RegisterPaymentMethodDetails(string partialName)
{
_Views.Add("AdditionalPaymentMethodDetails", partialName);
}
public void Register(string key, object value)
{
_Views.Add(key, value);
}
}
}

View File

@ -1,19 +0,0 @@
namespace BTCPayServer.Payments.LNURLPay
{
public class LNURLPaymentMethodViewExtension : IPaymentMethodViewExtension
{
public LNURLPaymentMethodViewExtension(PaymentMethodId paymentMethodId)
{
PaymentMethodId = paymentMethodId;
}
public PaymentMethodId PaymentMethodId { get; }
public void RegisterViews(PaymentMethodViewContext context)
{
var details = context.Details;
if (details is not LNURLPayPaymentMethodDetails d)
return;
context.RegisterPaymentMethodDetails("LNURL/AdditionalPaymentMethodDetails");
}
}
}

View File

@ -21,9 +21,9 @@ using Newtonsoft.Json.Linq;
namespace BTCPayServer.Payments.Lightning
{
public interface ILightningPaymentHandler : IHasNetwork
public interface ILightningPaymentHandler : IHasNetwork, IPaymentMethodHandler
{
LightningPaymentData ParsePaymentDetails(JToken details);
new LightningPaymentData ParsePaymentDetails(JToken details);
}
public class LightningLikePaymentHandler : IPaymentMethodHandler, ILightningPaymentHandler
{

View File

@ -7,10 +7,10 @@
@inject DisplayFormatter DisplayFormatter
@inject TransactionLinkProviders TransactionLinkProviders
@inject PaymentMethodHandlerDictionary handlers
@model IEnumerable<BTCPayServer.Services.Invoices.PaymentEntity>
@model InvoiceDetailsModel
@{
PayjoinInformation payjoinInformation = null;
var payments = Model
var payments = Model.Payments
.Select(payment =>
{
if (!handlers.TryGetValue(payment.PaymentMethodId, out var h) || h is not BitcoinLikePaymentHandler handler)

View File

@ -1,18 +0,0 @@
@model BTCPayServer.Payments.LNURLPayPaymentMethodDetails
@if (!string.IsNullOrEmpty(Model.ProvidedComment))
{
<tr>
<td colspan="100% bg-tile">
LNURL Comment: @Model.ProvidedComment
</td>
</tr>
}
@if (!string.IsNullOrEmpty(Model.ConsumedLightningAddress))
{
<tr>
<td colspan="100% bg-tile">
Lightning address used: @Model.ConsumedLightningAddress
</td>
</tr>
}

View File

@ -7,16 +7,31 @@
@inject DisplayFormatter DisplayFormatter
@inject PaymentMethodHandlerDictionary handlers
@inject PrettyNameProvider prettyName
@model IEnumerable<BTCPayServer.Services.Invoices.PaymentEntity>
@model InvoiceDetailsModel
@{
string providedComment = null;
string consumedLightningAddress = null;
var payments = Model
.Payments
.Select(payment =>
{
if (handlers.TryGet(payment.PaymentMethodId) is not ILightningPaymentHandler handler)
return null;
var offChainPaymentData = handler.ParsePaymentDetails(payment.Details);
if (handler.ParsePaymentPromptDetails(Model.Entity.GetPaymentPrompt(payment.PaymentMethodId)?.Details) is LNURLPayPaymentMethodDetails lnurlPrompt)
{
if (lnurlPrompt.ConsumedLightningAddress is string consumed)
{
consumedLightningAddress = consumed;
}
if (lnurlPrompt.ProvidedComment is string comment)
{
providedComment = comment;
}
}
return new OffChainPaymentViewModel
{
Type = prettyName.PrettyName(payment.PaymentMethodId),
@ -25,8 +40,8 @@
Amount = DisplayFormatter.Currency(payment.Value, handler.Network.CryptoCode, divisibility: payment.Divisibility)
};
})
.Where(model => model != null)
.ToList();
.Where(model => model != null)
.ToList();
}
@if (payments.Any())
@ -37,7 +52,7 @@
<table class="table table-hover mb-0">
<thead>
<tr>
<th class="w-75px">Type</th>
<th class="w-175px">Type</th>
<th class="w-175px">Destination</th>
<th class="text-nowrap">Payment Proof</th>
<th class="w-150px text-end">Paid</th>
@ -61,6 +76,19 @@
}
</tbody>
</table>
@if (!string.IsNullOrEmpty(providedComment))
{
<div>
<b>LNURL Comment</b>: @providedComment
</div>
}
@if (!string.IsNullOrEmpty(consumedLightningAddress))
{
<div>
<b>Lightning address used</b>: @consumedLightningAddress
</div>
}
</div>
</section>
}

View File

@ -1,5 +1,4 @@
@using BTCPayServer.Payments
@inject PaymentMethodViewProvider paymentMethodViewProvider
@model (InvoiceDetailsModel Invoice, bool ShowAddress)
@{
var invoice = Model.Invoice;
@ -87,14 +86,10 @@
}
</td>
</tr>
var vvm = paymentMethodViewProvider.TryGetViewViewModel(payment.PaymentMethodRaw, "AdditionalPaymentMethodDetails");;
if (vvm != null)
{
<partial name="@((string)vvm.View)" model="@vvm.ViewModel" />
}
<vc:ui-extension-point location="invoice-payments-list" />
}
</tbody>
</table>
</div>
<vc:ui-extension-point location="store-invoices-payments" model="@invoice.Payments" />
<vc:ui-extension-point location="store-invoices-payments" model="@invoice" />