Move checkout registration to the UI Extension

This commit is contained in:
nicolas.dorier 2024-10-07 17:38:02 +09:00
parent ef0ba7b0c4
commit e1bfc04451
No known key found for this signature in database
GPG Key ID: 6618763EF09186FE
21 changed files with 31 additions and 100 deletions

View File

@ -941,19 +941,9 @@ namespace BTCPayServer.Controllers
extension.ModifyPaymentModel(new PaymentModelContext(model, store, storeBlob, invoice, Url, kv, h, paymentMethodId == kv.PaymentMethodId));
}
}
model.UISettings = _viewProvider.TryGetViewViewModel(prompt, "CheckoutUI")?.View as CheckoutUIPaymentMethodSettings;
model.PaymentMethodId = paymentMethodId.ToString();
model.OrderAmountFiat = OrderAmountFromInvoice(model.CryptoCode, invoice, DisplayFormatter.CurrencyFormat.Symbol);
foreach (var paymentPrompt in invoice.GetPaymentPrompts())
{
var vvm = _viewProvider.TryGetViewViewModel(paymentPrompt, "CheckoutUI");
if (vvm?.View is CheckoutUIPaymentMethodSettings { ExtensionPartial: { } partial })
{
model.ExtensionPartials.Add(partial);
}
}
if (storeBlob.PlaySoundOnPayment)
{
model.PaymentSoundUrl = storeBlob.PaymentSoundUrl is null

View File

@ -14,6 +14,8 @@ using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Abstractions.Services;
using BTCPayServer.BIP78.Sender;
using BTCPayServer.Configuration;
using BTCPayServer.Data;
@ -289,6 +291,13 @@ namespace BTCPayServer
}
}
public static IServiceCollection AddUIExtension(this IServiceCollection services, string key, string partialView)
{
services.AddSingleton<IUIExtension>(new UIExtension(partialView,
key));
return services;
}
public static IServiceCollection AddReportProvider<T>(this IServiceCollection services)
where T : ReportProvider
{

View File

@ -365,6 +365,9 @@ namespace BTCPayServer.Hosting
o.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(DerivationStrategyBase)));
});
services.AddUIExtension("checkout-end", "Bitcoin/BitcoinLikeMethodCheckout");
services.AddUIExtension("checkout-end", "Lightning/LightningLikeMethodCheckout");
services.AddSingleton<Services.NBXplorerConnectionFactory>();
services.AddSingleton<IHostedService, Services.NBXplorerConnectionFactory>(o => o.GetRequiredService<Services.NBXplorerConnectionFactory>());
services.AddSingleton<HostedServices.CheckConfigurationHostedService>();
@ -631,8 +634,6 @@ o.GetRequiredService<IEnumerable<IPaymentLinkExtension>>().ToDictionary(o => o.P
(BitcoinPaymentModelExtension)ActivatorUtilities.CreateInstance(provider, typeof(BitcoinPaymentModelExtension), new object[] { network, pmi }));
services.AddSingleton<IPaymentMethodBitpayAPIExtension>(provider =>
(IPaymentMethodBitpayAPIExtension)ActivatorUtilities.CreateInstance(provider, typeof(BitcoinPaymentMethodBitpayAPIExtension), new object[] { pmi }));
services.AddSingleton<IPaymentMethodViewExtension>(provider =>
(IPaymentMethodViewExtension)ActivatorUtilities.CreateInstance(provider, typeof(BitcoinPaymentMethodViewExtension), new object[] { pmi }));
if (!network.ReadonlyWallet && network.WalletSupported)
{
@ -652,8 +653,6 @@ o.GetRequiredService<IEnumerable<IPaymentLinkExtension>>().ToDictionary(o => o.P
(IPaymentLinkExtension)ActivatorUtilities.CreateInstance(provider, typeof(LightningPaymentLinkExtension), new object[] { network, pmi }));
services.AddSingleton<IPaymentModelExtension>(provider =>
(IPaymentModelExtension)ActivatorUtilities.CreateInstance(provider, typeof(LightningPaymentModelExtension), new object[] { network, pmi }));
services.AddSingleton<IPaymentMethodViewExtension>(provider =>
(IPaymentMethodViewExtension)ActivatorUtilities.CreateInstance(provider, typeof(LightningPaymentMethodViewExtension), new object[] { pmi }));
services.AddSingleton<IPaymentMethodBitpayAPIExtension>(provider =>
(IPaymentMethodBitpayAPIExtension)ActivatorUtilities.CreateInstance(provider, typeof(LightningPaymentMethodBitpayAPIExtension), new object[] { pmi }));
var payoutMethodId = PayoutTypes.LN.GetPayoutMethodId(network.CryptoCode);

View File

@ -9,7 +9,7 @@ namespace BTCPayServer.Models.InvoicingModels
{
public class PaymentModel
{
public CheckoutUIPaymentMethodSettings UISettings;
public string CheckoutBodyComponentName { get; set; }
public class AvailableCrypto
{
[JsonConverter(typeof(PaymentMethodIdJsonConverter))]
@ -80,9 +80,6 @@ namespace BTCPayServer.Models.InvoicingModels
public string ReceiptLink { get; set; }
public int? RequiredConfirmations { get; set; }
public long? ReceivedConfirmations { get; set; }
[JsonIgnore]
public HashSet<string> ExtensionPartials { get; } = new HashSet<string>();
[JsonExtensionData]
public Dictionary<string, JToken> AdditionalData { get; set; } = new();
}

View File

@ -1,19 +0,0 @@
namespace BTCPayServer.Payments.Bitcoin
{
public class BitcoinPaymentMethodViewExtension : IPaymentMethodViewExtension
{
public BitcoinPaymentMethodViewExtension(PaymentMethodId paymentMethodId)
{
PaymentMethodId = paymentMethodId;
}
public PaymentMethodId PaymentMethodId { get; }
public void RegisterViews(PaymentMethodViewContext context)
{
context.RegisterCheckoutUI(new CheckoutUIPaymentMethodSettings
{
ExtensionPartial = "Bitcoin/BitcoinLikeMethodCheckout",
});
}
}
}

View File

@ -14,6 +14,7 @@ namespace BTCPayServer.Payments.Bitcoin
{
public class BitcoinPaymentModelExtension : IPaymentModelExtension
{
public const string CheckoutBodyComponentName = "BitcoinCheckoutBody";
private readonly PaymentMethodHandlerDictionary _handlers;
private readonly BTCPayNetwork _Network;
private readonly DisplayFormatter _displayFormatter;
@ -50,6 +51,7 @@ namespace BTCPayServer.Payments.Bitcoin
return;
var prompt = context.Prompt;
var details = handler.ParsePaymentPromptDetails(prompt.Details);
context.Model.CheckoutBodyComponentName = CheckoutBodyComponentName;
context.Model.ShowRecommendedFee = context.StoreBlob.ShowRecommendedFee;
context.Model.FeeRate = details.RecommendedFeeRate.SatoshiPerByte;

View File

@ -51,17 +51,9 @@ namespace BTCPayServer.Payments
{
_Views.Add("AdditionalPaymentMethodDetails", partialName);
}
public void RegisterCheckoutUI(CheckoutUIPaymentMethodSettings settings)
{
_Views.Add("CheckoutUI", settings);
}
public void Register(string key, object value)
{
_Views.Add(key, value);
}
}
public class CheckoutUIPaymentMethodSettings
{
public string? ExtensionPartial { get; set; }
}
}

View File

@ -48,6 +48,7 @@ namespace BTCPayServer.Payments.LNURLPay
context.Model.InvoiceBitcoinUrl = lnurl;
context.Model.InvoiceBitcoinUrlQR = lnurl.ToUpperInvariant().Replace(UriScheme.ToUpperInvariant(), UriScheme);
}
context.Model.CheckoutBodyComponentName = LightningPaymentModelExtension.CheckoutBodyComponentName;
context.Model.PeerInfo = handler.ParsePaymentPromptDetails(context.Prompt.Details).NodeInfo;
if (context.StoreBlob.LightningAmountInSatoshi && context.Model.CryptoCode == "BTC")
{

View File

@ -14,10 +14,6 @@ namespace BTCPayServer.Payments.LNURLPay
if (details is not LNURLPayPaymentMethodDetails d)
return;
context.RegisterPaymentMethodDetails("LNURL/AdditionalPaymentMethodDetails");
context.RegisterCheckoutUI(new CheckoutUIPaymentMethodSettings()
{
ExtensionPartial = "Lightning/LightningLikeMethodCheckout"
});
}
}
}

View File

@ -1,19 +0,0 @@
namespace BTCPayServer.Payments.Lightning
{
public class LightningPaymentMethodViewExtension : IPaymentMethodViewExtension
{
public LightningPaymentMethodViewExtension(PaymentMethodId paymentMethodId)
{
PaymentMethodId = paymentMethodId;
}
public PaymentMethodId PaymentMethodId { get; }
public void RegisterViews(PaymentMethodViewContext context)
{
context.RegisterCheckoutUI(new CheckoutUIPaymentMethodSettings()
{
ExtensionPartial = "Lightning/LightningLikeMethodCheckout"
});
}
}
}

View File

@ -11,6 +11,7 @@ namespace BTCPayServer.Payments.Lightning
{
public class LightningPaymentModelExtension : IPaymentModelExtension
{
public const string CheckoutBodyComponentName = "LightningCheckoutBody";
private readonly DisplayFormatter _displayFormatter;
IPaymentLinkExtension _PaymentLinkExtension;
public LightningPaymentModelExtension(
@ -44,6 +45,7 @@ namespace BTCPayServer.Payments.Lightning
var paymentPrompt = context.InvoiceEntity.GetPaymentPrompt(PaymentMethodId);
if (paymentPrompt is null)
return;
context.Model.CheckoutBodyComponentName = CheckoutBodyComponentName;
context.Model.InvoiceBitcoinUrl = _PaymentLinkExtension.GetPaymentLink(context.Prompt, context.UrlHelper);
if (context.Model.InvoiceBitcoinUrl is not null)
context.Model.InvoiceBitcoinUrlQR = $"lightning:{context.Model.InvoiceBitcoinUrl.ToUpperInvariant()?.Substring("LIGHTNING:".Length)}";

View File

@ -42,8 +42,6 @@ public partial class AltcoinsPlugin
var pmi = PaymentTypes.CHAIN.GetPaymentMethodId("XMR");
services.AddBTCPayNetwork(network)
.AddTransactionLinkProvider(pmi, new SimpleTransactionLinkProvider(blockExplorerLink));
services.AddSingleton<IPaymentMethodViewExtension>(provider =>
(IPaymentMethodViewExtension)ActivatorUtilities.CreateInstance(provider, typeof(BitcoinPaymentMethodViewExtension), new object[] { pmi }));
services.AddSingleton(provider =>

View File

@ -43,8 +43,6 @@ public partial class AltcoinsPlugin
var pmi = PaymentTypes.CHAIN.GetPaymentMethodId("ZEC");
services.AddBTCPayNetwork(network)
.AddTransactionLinkProvider(pmi, new SimpleTransactionLinkProvider(blockExplorerLink));
services.AddSingleton<IPaymentMethodViewExtension>(provider =>
(IPaymentMethodViewExtension)ActivatorUtilities.CreateInstance(provider, typeof(BitcoinPaymentMethodViewExtension), new object[] { pmi }));
services.AddSingleton(provider =>

View File

@ -108,14 +108,6 @@ namespace BTCPayServer.Services.Altcoins.Monero.Payments
return details.ToObject<MoneroLikeOnChainPaymentMethodDetails>(Serializer);
}
public CheckoutUIPaymentMethodSettings GetCheckoutUISettings()
{
return new CheckoutUIPaymentMethodSettings
{
ExtensionPartial = "Bitcoin/BitcoinLikeMethodCheckout"
};
}
public MoneroLikePaymentData ParsePaymentDetails(JToken details)
{
return details.ToObject<MoneroLikePaymentData>(Serializer) ?? throw new FormatException($"Invalid {nameof(MoneroLikePaymentMethodHandler)}");

View File

@ -36,6 +36,7 @@ namespace BTCPayServer.Services.Altcoins.Monero.Payments
{
if (context is not { IsSelected: true, Handler: MoneroLikePaymentMethodHandler handler })
return;
context.Model.CheckoutBodyComponentName = BitcoinPaymentModelExtension.CheckoutBodyComponentName;
if (context.Model.Activated)
{
var details = context.InvoiceEntity.GetPayments(true)

View File

@ -102,14 +102,6 @@ namespace BTCPayServer.Services.Altcoins.Zcash.Payments
public long AccountIndex { get; internal set; }
}
public CheckoutUIPaymentMethodSettings GetCheckoutUISettings()
{
return new CheckoutUIPaymentMethodSettings
{
ExtensionPartial = "Bitcoin/BitcoinLikeMethodCheckout"
};
}
public ZcashLikePaymentData ParsePaymentDetails(JToken details)
{
return details.ToObject<ZcashLikePaymentData>(Serializer) ?? throw new FormatException($"Invalid {nameof(ZcashLikePaymentData)}");

View File

@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using BTCPayServer.Payments;
using BTCPayServer.Payments.Bitcoin;
using BTCPayServer.Services.Altcoins.Monero.Payments;
using BTCPayServer.Services.Altcoins.Zcash.Services;
using BTCPayServer.Services.Invoices;
@ -35,7 +36,8 @@ namespace BTCPayServer.Services.Altcoins.Zcash.Payments
{
if (context is not { IsSelected: true, Handler: ZcashLikePaymentMethodHandler handler })
return;
if (context.Model.Activated)
context.Model.CheckoutBodyComponentName = BitcoinPaymentModelExtension.CheckoutBodyComponentName;
if (context.Model.Activated)
{
var details = context.InvoiceEntity.GetPayments(true)
.Select(p => p.GetDetails<ZcashLikePaymentData>(handler))

View File

@ -1,9 +1,10 @@
@using BTCPayServer.BIP78.Sender
@using BTCPayServer.Components.TruncateCenter
@using BTCPayServer.Abstractions.TagHelpers
@using BTCPayServer.Payments.Bitcoin
@model BTCPayServer.Models.InvoicingModels.PaymentModel
<template id="checkout-template">
<template id="@BitcoinPaymentModelExtension.CheckoutBodyComponentName">
@await Component.InvokeAsync("UiExtensionPoint", new {location = "checkout-bitcoin-pre-content", model = Model})
<div class="payment-box">
<div v-if="model.invoiceBitcoinUrlQR" class="qr-container" :data-qr-value="model.invoiceBitcoinUrlQR" :data-clipboard="model.invoiceBitcoinUrl" data-clipboard-confirm-element="#Address_@Model.PaymentMethodId [data-clipboard]">
@ -31,9 +32,9 @@
</template>
<script>
Vue.component('checkout-template', {
Vue.component(@Safe.Json(BitcoinPaymentModelExtension.CheckoutBodyComponentName), {
props: ['model', 'nfcSupported', 'nfcScanning', 'nfcErrorMessage'],
template: "#checkout-template",
template: @Safe.Json("#" + BitcoinPaymentModelExtension.CheckoutBodyComponentName),
components: {
qrcode: VueQrcode
},

View File

@ -1,6 +1,7 @@
@using BTCPayServer.Payments.Lightning
@model BTCPayServer.Models.InvoicingModels.PaymentModel
<template id="checkout-template">
<template id="@LightningPaymentModelExtension.CheckoutBodyComponentName">
<div class="payment-box">
@await Component.InvokeAsync("UiExtensionPoint" , new { location="checkout-lightning-pre-content", model = Model})
<div v-if="model.invoiceBitcoinUrlQR" class="qr-container" :data-qr-value="model.invoiceBitcoinUrlQR" :data-clipboard="model.invoiceBitcoinUrl" data-clipboard-confirm-element="#Lightning_@Model.PaymentMethodId [data-clipboard]">
@ -23,9 +24,9 @@
</template>
<script>
Vue.component('checkout-template', {
Vue.component(@Safe.Json(LightningPaymentModelExtension.CheckoutBodyComponentName), {
props: ['model', 'nfcSupported', 'nfcScanning', 'nfcErrorMessage'],
template: "#checkout-template",
template: @Safe.Json("#" + LightningPaymentModelExtension.CheckoutBodyComponentName),
components: {
qrcode: VueQrcode
},

View File

@ -316,10 +316,6 @@
@if (Env.CheatMode)
{
<partial name="Checkout-Cheating" model="@Model" />
}
@foreach (var extensionPartial in Model.ExtensionPartials)
{
<partial name="@extensionPartial" model="@Model" />
}
@await Component.InvokeAsync("UiExtensionPoint", new { location = "checkout-payment", model = Model })
@await Component.InvokeAsync("UiExtensionPoint", new { location = "checkout-end", model = Model })

View File

@ -177,7 +177,7 @@ function initApp() {
paymentMethodComponent() {
return this.isPluginPaymentMethod
? `${this.pmId}Checkout`
: this.srvModel.activated && 'checkout-template';
: this.srvModel.activated && this.srvModel.checkoutBodyComponentName;
},
isPluginPaymentMethod () {
return !this.paymentMethodIds.includes(this.pmId);