diff --git a/BTCPayServer/Controllers/UIInvoiceController.UI.cs b/BTCPayServer/Controllers/UIInvoiceController.UI.cs index ae594d3cf..6e2e4cc32 100644 --- a/BTCPayServer/Controllers/UIInvoiceController.UI.cs +++ b/BTCPayServer/Controllers/UIInvoiceController.UI.cs @@ -922,7 +922,6 @@ namespace BTCPayServer.Controllers Status = invoice.Status.ToString(), // The Tweak is part of the PaymentMethodFee, but let's not show it in the UI as it's negligible. NetworkFee = prompt.PaymentMethodFee - prompt.TweakFee, - IsMultiCurrency = invoice.GetPayments(false).Select(p => p.PaymentMethodId).Concat(new[] { prompt.PaymentMethodId }).Distinct().Count() > 1, StoreId = store.Id, AvailableCryptos = invoice.GetPaymentPrompts() .Select(kv => @@ -930,8 +929,9 @@ namespace BTCPayServer.Controllers var handler = _handlers[kv.PaymentMethodId]; return new PaymentModel.AvailableCrypto { + Handler = handler, Displayed = displayedPaymentMethods.Contains(kv.PaymentMethodId), - PaymentMethodId = kv.PaymentMethodId.ToString(), + PaymentMethodId = kv.PaymentMethodId, CryptoCode = kv.Currency, PaymentMethodName = _prettyName.PrettyName(kv.PaymentMethodId), IsLightning = handler is ILightningPaymentHandler, @@ -947,8 +947,15 @@ namespace BTCPayServer.Controllers .OrderByDescending(a => a.CryptoCode == _NetworkProvider.DefaultNetwork.CryptoCode).ThenBy(a => a.PaymentMethodName).ThenBy(a => a.IsLightning ? 1 : 0) .ToList() }; - if (_paymentModelExtensions.TryGetValue(paymentMethodId, out var extension)) - extension.ModifyPaymentModel(new PaymentModelContext(model, store, storeBlob, invoice, Url, prompt, handler)); + + foreach (var kv in invoice.GetPaymentPrompts()) + { + if (_paymentModelExtensions.TryGetValue(kv.PaymentMethodId, out var extension) && + _handlers.TryGetValue(kv.PaymentMethodId, out var h)) + { + 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); diff --git a/BTCPayServer/Models/InvoicingModels/PaymentModel.cs b/BTCPayServer/Models/InvoicingModels/PaymentModel.cs index 5518ffd67..15898ae72 100644 --- a/BTCPayServer/Models/InvoicingModels/PaymentModel.cs +++ b/BTCPayServer/Models/InvoicingModels/PaymentModel.cs @@ -1,6 +1,9 @@ using System.Collections.Generic; using BTCPayServer.Client.Models; +using BTCPayServer.JsonConverters; using BTCPayServer.Payments; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace BTCPayServer.Models.InvoicingModels { @@ -9,13 +12,19 @@ namespace BTCPayServer.Models.InvoicingModels public CheckoutUIPaymentMethodSettings UISettings; public class AvailableCrypto { - public string PaymentMethodId { get; set; } + [JsonConverter(typeof(PaymentMethodIdJsonConverter))] + public PaymentMethodId PaymentMethodId { get; set; } public string CryptoImage { get; set; } public string Link { get; set; } public string PaymentMethodName { get; set; } public bool IsLightning { get; set; } public string CryptoCode { get; set; } public bool Displayed { get; set; } + [JsonIgnore] + public IPaymentMethodHandler Handler { get; internal set; } + + [JsonExtensionData] + public Dictionary AdditionalData { get; set; } = new(); } public StoreBrandingViewModel StoreBranding { get; set; } public string PaymentSoundUrl { get; set; } @@ -58,7 +67,6 @@ namespace BTCPayServer.Models.InvoicingModels public string OrderId { get; set; } public decimal NetworkFee { get; set; } - public bool IsMultiCurrency { get; set; } public int MaxTimeMinutes { get; set; } public string PaymentMethodId { get; set; } public string PaymentMethodName { get; set; } @@ -73,6 +81,9 @@ namespace BTCPayServer.Models.InvoicingModels public int? RequiredConfirmations { get; set; } public long? ReceivedConfirmations { get; set; } + [JsonIgnore] public HashSet ExtensionPartials { get; } = new HashSet(); + [JsonExtensionData] + public Dictionary AdditionalData { get; set; } = new(); } } diff --git a/BTCPayServer/Payments/Bitcoin/BitcoinPaymentModelExtension.cs b/BTCPayServer/Payments/Bitcoin/BitcoinPaymentModelExtension.cs index 1b22e46e3..273cccd01 100644 --- a/BTCPayServer/Payments/Bitcoin/BitcoinPaymentModelExtension.cs +++ b/BTCPayServer/Payments/Bitcoin/BitcoinPaymentModelExtension.cs @@ -46,9 +46,9 @@ namespace BTCPayServer.Payments.Bitcoin public PaymentMethodId PaymentMethodId { get; } public void ModifyPaymentModel(PaymentModelContext context) { - var prompt = context.Prompt; - if (!_handlers.TryGetValue(PaymentMethodId, out var o) || o is not BitcoinLikePaymentHandler handler) + if (context is not { IsSelected: true, Handler: BitcoinLikePaymentHandler handler}) return; + var prompt = context.Prompt; var details = handler.ParsePaymentPromptDetails(prompt.Details); context.Model.ShowRecommendedFee = context.StoreBlob.ShowRecommendedFee; context.Model.FeeRate = details.RecommendedFeeRate.SatoshiPerByte; diff --git a/BTCPayServer/Payments/IPaymentModelExtension.cs b/BTCPayServer/Payments/IPaymentModelExtension.cs index 3881396e7..a50755bbe 100644 --- a/BTCPayServer/Payments/IPaymentModelExtension.cs +++ b/BTCPayServer/Payments/IPaymentModelExtension.cs @@ -11,7 +11,8 @@ namespace BTCPayServer.Payments InvoiceEntity InvoiceEntity, IUrlHelper UrlHelper, PaymentPrompt Prompt, - IPaymentMethodHandler Handler); + IPaymentMethodHandler Handler, + bool IsSelected); public interface IPaymentModelExtension { public PaymentMethodId PaymentMethodId { get; } diff --git a/BTCPayServer/Payments/LNURLPay/LNURLPayPaymentModelExtension.cs b/BTCPayServer/Payments/LNURLPay/LNURLPayPaymentModelExtension.cs index b9abe91f3..14b19dfd2 100644 --- a/BTCPayServer/Payments/LNURLPay/LNURLPayPaymentModelExtension.cs +++ b/BTCPayServer/Payments/LNURLPay/LNURLPayPaymentModelExtension.cs @@ -39,6 +39,8 @@ namespace BTCPayServer.Payments.LNURLPay private const string UriScheme = "lightning:"; public void ModifyPaymentModel(PaymentModelContext context) { + if (context is not { IsSelected: true, Handler: LNURLPayPaymentHandler handler }) + return; var lnurl = paymentLinkExtension.GetPaymentLink(context.Prompt, context.UrlHelper); if (lnurl is not null) { diff --git a/BTCPayServer/Payments/Lightning/LightningPaymentModelExtension.cs b/BTCPayServer/Payments/Lightning/LightningPaymentModelExtension.cs index 7a47ae7c1..7f1573625 100644 --- a/BTCPayServer/Payments/Lightning/LightningPaymentModelExtension.cs +++ b/BTCPayServer/Payments/Lightning/LightningPaymentModelExtension.cs @@ -39,7 +39,7 @@ namespace BTCPayServer.Payments.Lightning public string Badge => "⚡"; public void ModifyPaymentModel(PaymentModelContext context) { - if (!Handlers.TryGetValue(PaymentMethodId, out var o) || o is not LightningLikePaymentHandler handler) + if (context is not { IsSelected: true, Handler: LightningLikePaymentHandler handler }) return; var paymentPrompt = context.InvoiceEntity.GetPaymentPrompt(PaymentMethodId); if (paymentPrompt is null) diff --git a/BTCPayServer/Services/Altcoins/Monero/Payments/MoneroPaymentModelExtension.cs b/BTCPayServer/Services/Altcoins/Monero/Payments/MoneroPaymentModelExtension.cs index 6af2fbdcc..092cdb105 100644 --- a/BTCPayServer/Services/Altcoins/Monero/Payments/MoneroPaymentModelExtension.cs +++ b/BTCPayServer/Services/Altcoins/Monero/Payments/MoneroPaymentModelExtension.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using BTCPayServer.Payments; using BTCPayServer.Payments.Bitcoin; +using BTCPayServer.Payments.Lightning; using BTCPayServer.Services.Altcoins.Monero.Services; using BTCPayServer.Services.Invoices; @@ -33,20 +34,20 @@ namespace BTCPayServer.Services.Altcoins.Monero.Payments public void ModifyPaymentModel(PaymentModelContext context) { + if (context is not { IsSelected: true, Handler: MoneroLikePaymentMethodHandler handler }) + return; if (context.Model.Activated) { - if (_handlers.TryGetValue(PaymentMethodId, out var handler)) + var details = context.InvoiceEntity.GetPayments(true) + .Select(p => p.GetDetails(handler)) + .Where(p => p is not null) + .FirstOrDefault(); + if (details is not null) { - var details = context.InvoiceEntity.GetPayments(true) - .Select(p => p.GetDetails(handler)) - .Where(p => p is not null) - .FirstOrDefault(); - if (details is not null) - { - context.Model.ReceivedConfirmations = details.ConfirmationCount; - context.Model.RequiredConfirmations = (int)MoneroListener.ConfirmationsRequired(details, context.InvoiceEntity.SpeedPolicy); - } + context.Model.ReceivedConfirmations = details.ConfirmationCount; + context.Model.RequiredConfirmations = (int)MoneroListener.ConfirmationsRequired(details, context.InvoiceEntity.SpeedPolicy); } + context.Model.InvoiceBitcoinUrl = paymentLinkExtension.GetPaymentLink(context.Prompt, context.UrlHelper); context.Model.InvoiceBitcoinUrlQR = context.Model.InvoiceBitcoinUrl; } diff --git a/BTCPayServer/Services/Altcoins/Zcash/Payments/ZcashPaymentModelExtension.cs b/BTCPayServer/Services/Altcoins/Zcash/Payments/ZcashPaymentModelExtension.cs index f0fb35265..47a0527cd 100644 --- a/BTCPayServer/Services/Altcoins/Zcash/Payments/ZcashPaymentModelExtension.cs +++ b/BTCPayServer/Services/Altcoins/Zcash/Payments/ZcashPaymentModelExtension.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using BTCPayServer.Payments; +using BTCPayServer.Services.Altcoins.Monero.Payments; using BTCPayServer.Services.Altcoins.Zcash.Services; using BTCPayServer.Services.Invoices; @@ -32,19 +33,18 @@ namespace BTCPayServer.Services.Altcoins.Zcash.Payments public void ModifyPaymentModel(PaymentModelContext context) { + if (context is not { IsSelected: true, Handler: ZcashLikePaymentMethodHandler handler }) + return; if (context.Model.Activated) { - if (_handlers.TryGetValue(PaymentMethodId, out var handler)) + var details = context.InvoiceEntity.GetPayments(true) + .Select(p => p.GetDetails(handler)) + .Where(p => p is not null) + .FirstOrDefault(); + if (details is not null) { - var details = context.InvoiceEntity.GetPayments(true) - .Select(p => p.GetDetails(handler)) - .Where(p => p is not null) - .FirstOrDefault(); - if (details is not null) - { - context.Model.ReceivedConfirmations = details.ConfirmationCount; - context.Model.RequiredConfirmations = (int)ZcashListener.ConfirmationsRequired(context.InvoiceEntity.SpeedPolicy); - } + context.Model.ReceivedConfirmations = details.ConfirmationCount; + context.Model.RequiredConfirmations = (int)ZcashListener.ConfirmationsRequired(context.InvoiceEntity.SpeedPolicy); } context.Model.InvoiceBitcoinUrl = paymentLinkExtension.GetPaymentLink(context.Prompt, context.UrlHelper); context.Model.InvoiceBitcoinUrlQR = context.Model.InvoiceBitcoinUrl;