From f28eb13b0df9418b9962dbdee3416d88b6a09034 Mon Sep 17 00:00:00 2001 From: Dennis Reimann Date: Tue, 5 Oct 2021 17:37:25 +0200 Subject: [PATCH] Create separate payment settings section for stores --- .../AltcoinTests/AltcoinTests.cs | 6 +- BTCPayServer.Tests/CheckoutUITests.cs | 4 +- BTCPayServer.Tests/CrowdfundTests.cs | 2 +- BTCPayServer.Tests/PayJoinTests.cs | 4 +- BTCPayServer.Tests/TestAccount.cs | 23 ++- BTCPayServer.Tests/UnitTest1.cs | 27 ++- BTCPayServer/Controllers/StoresController.cs | 125 ++++++++++---- .../StoreViewModels/PaymentViewModel.cs | 64 +++++++ .../StoreViewModels/StoreDerivationScheme.cs | 12 ++ .../StoreViewModels/StoreLightningNode.cs | 9 + .../Models/StoreViewModels/StoreViewModel.cs | 122 +------------- .../Views/Stores/CheckoutExperience.cshtml | 2 +- BTCPayServer/Views/Stores/Payment.cshtml | 156 ++++++++++++++++++ BTCPayServer/Views/Stores/StoreNavPages.cs | 4 +- BTCPayServer/Views/Stores/_Nav.cshtml | 4 +- 15 files changed, 381 insertions(+), 183 deletions(-) create mode 100644 BTCPayServer/Models/StoreViewModels/PaymentViewModel.cs create mode 100644 BTCPayServer/Models/StoreViewModels/StoreDerivationScheme.cs create mode 100644 BTCPayServer/Models/StoreViewModels/StoreLightningNode.cs create mode 100644 BTCPayServer/Views/Stores/Payment.cshtml diff --git a/BTCPayServer.Tests/AltcoinTests/AltcoinTests.cs b/BTCPayServer.Tests/AltcoinTests/AltcoinTests.cs index 64901e284..6b127aa13 100644 --- a/BTCPayServer.Tests/AltcoinTests/AltcoinTests.cs +++ b/BTCPayServer.Tests/AltcoinTests/AltcoinTests.cs @@ -77,7 +77,7 @@ namespace BTCPayServer.Tests // Get enabled state from overview action StoreViewModel storeModel; - response = await controller.UpdateStore(); + response = controller.UpdateStore(); storeModel = (StoreViewModel)Assert.IsType(response).Model; var lnNode = storeModel.LightningNodes.Find(node => node.CryptoCode == cryptoCode); Assert.NotNull(lnNode); @@ -89,7 +89,7 @@ namespace BTCPayServer.Tests Assert.IsType(response); // Get enabled state from overview action - response = await controller.UpdateStore(); + response = controller.UpdateStore(); storeModel = (StoreViewModel)Assert.IsType(response).Model; var derivationScheme = storeModel.DerivationSchemes.Find(scheme => scheme.Crypto == cryptoCode); Assert.NotNull(derivationScheme); @@ -98,7 +98,7 @@ namespace BTCPayServer.Tests // Disable wallet response = controller.SetWalletEnabled(storeId, cryptoCode, false).GetAwaiter().GetResult(); Assert.IsType(response); - response = await controller.UpdateStore(); + response = controller.UpdateStore(); storeModel = (StoreViewModel)Assert.IsType(response).Model; derivationScheme = storeModel.DerivationSchemes.Find(scheme => scheme.Crypto == cryptoCode); Assert.NotNull(derivationScheme); diff --git a/BTCPayServer.Tests/CheckoutUITests.cs b/BTCPayServer.Tests/CheckoutUITests.cs index c9fa293ee..eec46ca20 100644 --- a/BTCPayServer.Tests/CheckoutUITests.cs +++ b/BTCPayServer.Tests/CheckoutUITests.cs @@ -137,10 +137,10 @@ namespace BTCPayServer.Tests s.RegisterNewUser(true); var store = s.CreateNewStore(); s.AddLightningNode(); - s.GoToStore(store.storeId); + s.GoToStore(store.storeId, StoreNavPages.Payment); s.Driver.SetCheckbox(By.Id("LightningAmountInSatoshi"), true); s.Driver.FindElement(By.Id("Save")).Click(); - Assert.Contains("Store successfully updated", s.FindAlertMessage().Text); + Assert.Contains("Payment settings successfully updated", s.FindAlertMessage().Text); var invoiceId = s.CreateInvoice(store.storeName, 10, "USD", "a@g.com"); s.GoToInvoiceCheckout(invoiceId); diff --git a/BTCPayServer.Tests/CrowdfundTests.cs b/BTCPayServer.Tests/CrowdfundTests.cs index ffea8b55e..28409aea8 100644 --- a/BTCPayServer.Tests/CrowdfundTests.cs +++ b/BTCPayServer.Tests/CrowdfundTests.cs @@ -165,7 +165,7 @@ namespace BTCPayServer.Tests var user = tester.NewAccount(); user.GrantAccess(); user.RegisterDerivationScheme("BTC"); - await user.ModifyStore(s => s.NetworkFeeMode = NetworkFeeMode.Never); + await user.SetNetworkFeeMode(NetworkFeeMode.Never); var apps = user.GetController(); var vm = Assert.IsType(Assert.IsType(apps.CreateApp().Result).Model); vm.Name = "test"; diff --git a/BTCPayServer.Tests/PayJoinTests.cs b/BTCPayServer.Tests/PayJoinTests.cs index 95a25704a..334d37818 100644 --- a/BTCPayServer.Tests/PayJoinTests.cs +++ b/BTCPayServer.Tests/PayJoinTests.cs @@ -570,9 +570,9 @@ namespace BTCPayServer.Tests address = (await nbx.GetUnusedAsync(bob.DerivationScheme, DerivationFeature.Deposit)).Address; tester.ExplorerNode.SendToAddress(address, Money.Coins(1.1m)); await notifications.NextEventAsync(); - await bob.ModifyStore(s => s.PayJoinEnabled = true); + await bob.ModifyPayment(p => p.PayJoinEnabled = true); var invoice = bob.BitPay.CreateInvoice( - new Invoice() { Price = 0.1m, Currency = "BTC", FullNotifications = true }); + new Invoice { Price = 0.1m, Currency = "BTC", FullNotifications = true }); var invoiceBIP21 = new BitcoinUrlBuilder(invoice.CryptoInfo.First().PaymentUrls.BIP21, tester.ExplorerClient.Network.NBitcoinNetwork); diff --git a/BTCPayServer.Tests/TestAccount.cs b/BTCPayServer.Tests/TestAccount.cs index 19c286143..e162bcfec 100644 --- a/BTCPayServer.Tests/TestAccount.cs +++ b/BTCPayServer.Tests/TestAccount.cs @@ -109,7 +109,7 @@ namespace BTCPayServer.Tests { await RegisterAsync(isAdmin); await CreateStoreAsync(); - var store = this.GetController(); + var store = GetController(); var pairingCode = BitPay.RequestClientAuthorization("test", Facade.Merchant); Assert.IsType(await store.RequestPairing(pairingCode.ToString())); await store.Pair(pairingCode.ToString(), StoreId); @@ -127,19 +127,28 @@ namespace BTCPayServer.Tests public async Task SetNetworkFeeMode(NetworkFeeMode mode) { - await ModifyStore(store => + await ModifyPayment(payment => { - store.NetworkFeeMode = mode; + payment.NetworkFeeMode = mode; }); } - public async Task ModifyStore(Action modify) + public void ModifyStore(Action modify) { var storeController = GetController(); - var response = await storeController.UpdateStore(); + var response = storeController.UpdateStore(); StoreViewModel store = (StoreViewModel)((ViewResult)response).Model; modify(store); - storeController.UpdateStore(store).GetAwaiter().GetResult(); + storeController.UpdateStore(store); + } + + public async Task ModifyPayment(Action modify) + { + var storeController = GetController(); + var response = await storeController.Payment(); + PaymentViewModel payment = (PaymentViewModel)((ViewResult)response).Model; + modify(payment); + storeController.Payment(payment).GetAwaiter().GetResult(); } public T GetController(bool setImplicitStore = true) where T : Controller @@ -190,7 +199,7 @@ namespace BTCPayServer.Tests public Task EnablePayJoin() { - return ModifyStore(s => s.PayJoinEnabled = true); + return ModifyPayment(p => p.PayJoinEnabled = true); } public GenerateWalletResponse GenerateWalletResponseV { get; set; } diff --git a/BTCPayServer.Tests/UnitTest1.cs b/BTCPayServer.Tests/UnitTest1.cs index dddaaaa1b..7d7329b86 100644 --- a/BTCPayServer.Tests/UnitTest1.cs +++ b/BTCPayServer.Tests/UnitTest1.cs @@ -818,11 +818,11 @@ namespace BTCPayServer.Tests // Set tolerance to 50% var stores = user.GetController(); - var response = await stores.UpdateStore(); - var vm = Assert.IsType(Assert.IsType(response).Model); + var response = await stores.Payment(); + var vm = Assert.IsType(Assert.IsType(response).Model); Assert.Equal(0.0, vm.PaymentTolerance); vm.PaymentTolerance = 50.0; - Assert.IsType(stores.UpdateStore(vm).Result); + Assert.IsType(stores.Payment(vm).Result); var invoice = user.BitPay.CreateInvoice( new Invoice() @@ -996,8 +996,7 @@ namespace BTCPayServer.Tests Assert.Equal(4, tor.Services.Length); } - - + [Fact(Timeout = 60 * 2 * 1000)] [Trait("Integration", "Integration")] [Trait("Lightning", "Lightning")] @@ -1012,7 +1011,7 @@ namespace BTCPayServer.Tests await user.RegisterDerivationSchemeAsync("BTC"); await user.RegisterLightningNodeAsync("BTC", LightningConnectionType.CLightning); await user.SetNetworkFeeMode(NetworkFeeMode.Never); - await user.ModifyStore(model => model.SpeedPolicy = SpeedPolicy.HighSpeed); + await user.ModifyPayment(p => p.SpeedPolicy = SpeedPolicy.HighSpeed); var invoice = await user.BitPay.CreateInvoiceAsync(new Invoice(0.0001m, "BTC")); await tester.WaitForEvent(async () => { @@ -1065,7 +1064,7 @@ namespace BTCPayServer.Tests var user = tester.NewAccount(); user.GrantAccess(true); var storeController = user.GetController(); - var storeResponse = await storeController.UpdateStore(); + var storeResponse = storeController.UpdateStore(); Assert.IsType(storeResponse); Assert.IsType(await storeController.SetupLightningNode(user.StoreId, "BTC")); @@ -1089,7 +1088,7 @@ namespace BTCPayServer.Tests new LightningNodeViewModel { ConnectionString = tester.MerchantCharge.Client.Uri.AbsoluteUri }, "save", "BTC").GetAwaiter().GetResult()); - storeResponse = await storeController.UpdateStore(); + storeResponse = storeController.UpdateStore(); var storeVm = Assert.IsType(Assert .IsType(storeResponse).Model); @@ -1205,7 +1204,7 @@ namespace BTCPayServer.Tests var acc = tester.NewAccount(); acc.GrantAccess(); acc.RegisterDerivationScheme("BTC"); - await acc.ModifyStore(s => s.SpeedPolicy = SpeedPolicy.LowSpeed); + await acc.ModifyPayment(p => p.SpeedPolicy = SpeedPolicy.LowSpeed); var invoice = acc.BitPay.CreateInvoice(new Invoice { Price = 5.0m, @@ -2032,7 +2031,7 @@ namespace BTCPayServer.Tests }); Assert.Equal(404, (int)response.StatusCode); - await user.ModifyStore(s => s.AnyoneCanCreateInvoice = true); + await user.ModifyPayment(p => p.AnyoneCanCreateInvoice = true); Logs.Tester.LogInformation("Bad store with anyone can create invoice = 403"); response = await tester.PayTester.HttpClient.SendAsync( @@ -2448,12 +2447,12 @@ namespace BTCPayServer.Tests Assert.DoesNotContain("&lightning=", paymentMethodFirst.InvoiceBitcoinUrlQR); // enable unified QR code in settings - var vm = Assert.IsType(Assert - .IsType(await user.GetController().UpdateStore()).Model + var vm = Assert.IsType(Assert + .IsType(await user.GetController().Payment()).Model ); vm.OnChainWithLnInvoiceFallback = true; Assert.IsType( - user.GetController().UpdateStore(vm).Result + user.GetController().Payment(vm).Result ); // validate that QR code now has both onchain and offchain payment urls @@ -2470,7 +2469,7 @@ namespace BTCPayServer.Tests Assert.True($"bitcoin:{paymentMethodSecond.BtcAddress.ToUpperInvariant()}" == split); // Fallback lightning invoice should be uppercase inside the QR code. - var lightningFallback = paymentMethodSecond.InvoiceBitcoinUrlQR.Split(new string[] { "&lightning=" }, StringSplitOptions.None)[1]; + var lightningFallback = paymentMethodSecond.InvoiceBitcoinUrlQR.Split(new [] { "&lightning=" }, StringSplitOptions.None)[1]; Assert.True(lightningFallback.ToUpperInvariant() == lightningFallback); } } diff --git a/BTCPayServer/Controllers/StoresController.cs b/BTCPayServer/Controllers/StoresController.cs index 9fafbc735..7d183ed2e 100644 --- a/BTCPayServer/Controllers/StoresController.cs +++ b/BTCPayServer/Controllers/StoresController.cs @@ -493,8 +493,8 @@ namespace BTCPayServer.Controllers }); } - - private void AddPaymentMethods(StoreData store, StoreBlob storeBlob, StoreViewModel vm) + private void AddPaymentMethods(StoreData store, StoreBlob storeBlob, + out List derivationSchemes, out List lightningNodes) { var excludeFilters = storeBlob.GetExcludedPaymentMethods(); var derivationByCryptoCode = @@ -509,6 +509,9 @@ namespace BTCPayServer.Controllers .Where(method => method.PaymentId.PaymentType == LightningPaymentType.Instance) .ToDictionary(c => c.CryptoCode.ToUpperInvariant()); + derivationSchemes = new List(); + lightningNodes = new List(); + foreach (var paymentMethodId in _paymentMethodHandlerDictionary.Distinct().SelectMany(handler => handler.GetSupportedPaymentMethods())) { switch (paymentMethodId.PaymentType) @@ -518,7 +521,7 @@ namespace BTCPayServer.Controllers var network = _NetworkProvider.GetNetwork(paymentMethodId.CryptoCode); var value = strategy?.ToPrettyString() ?? string.Empty; - vm.DerivationSchemes.Add(new StoreViewModel.DerivationScheme() + derivationSchemes.Add(new StoreDerivationScheme { Crypto = paymentMethodId.CryptoCode, WalletSupported = network.WalletSupported, @@ -530,12 +533,14 @@ namespace BTCPayServer.Controllers #endif }); break; + case LNURLPayPaymentType lnurlPayPaymentType: break; + case LightningPaymentType _: var lightning = lightningByCryptoCode.TryGet(paymentMethodId.CryptoCode); var isEnabled = !excludeFilters.Match(paymentMethodId) && lightning != null; - vm.LightningNodes.Add(new StoreViewModel.LightningNode + lightningNodes.Add(new StoreLightningNode { CryptoCode = paymentMethodId.CryptoCode, Address = lightning?.GetDisplayableConnectionString(), @@ -547,30 +552,92 @@ namespace BTCPayServer.Controllers } [HttpGet("{storeId}")] - public async Task UpdateStore() + public IActionResult UpdateStore() { var store = HttpContext.GetStoreData(); if (store == null) return NotFound(); var storeBlob = store.GetStoreBlob(); - var vm = new StoreViewModel(); - vm.Id = store.Id; - vm.StoreName = store.StoreName; - vm.StoreWebsite = store.StoreWebsite; - vm.DefaultCurrency = storeBlob.DefaultCurrency; - vm.NetworkFeeMode = storeBlob.NetworkFeeMode; - vm.AnyoneCanCreateInvoice = storeBlob.AnyoneCanInvoice; - vm.SpeedPolicy = store.SpeedPolicy; - vm.CanDelete = _Repo.CanDeleteStores(); - AddPaymentMethods(store, storeBlob, vm); + var vm = new StoreViewModel + { + Id = store.Id, + CanDelete = _Repo.CanDeleteStores(), + StoreName = store.StoreName, + StoreWebsite = store.StoreWebsite, + HintWallet = storeBlob.Hints.Wallet, + HintLightning = storeBlob.Hints.Lightning + }; + + AddPaymentMethods(store, storeBlob, + out var derivationSchemes, out var lightningNodes); + + vm.DerivationSchemes = derivationSchemes; + vm.LightningNodes = lightningNodes; + + return View(vm); + } + + [HttpPost("{storeId}")] + public async Task UpdateStore(StoreViewModel model, string command = null) + { + bool needUpdate = false; + if (CurrentStore.StoreName != model.StoreName) + { + needUpdate = true; + CurrentStore.StoreName = model.StoreName; + } + if (CurrentStore.StoreWebsite != model.StoreWebsite) + { + needUpdate = true; + CurrentStore.StoreWebsite = model.StoreWebsite; + } + + var blob = CurrentStore.GetStoreBlob(); + if (CurrentStore.SetStoreBlob(blob)) + { + needUpdate = true; + } + + if (needUpdate) + { + await _Repo.UpdateStore(CurrentStore); + + TempData[WellKnownTempData.SuccessMessage] = "Store successfully updated"; + } + + return RedirectToAction(nameof(UpdateStore), new + { + storeId = CurrentStore.Id + }); + } + + [HttpGet("{storeId}/payment")] + public async Task Payment() + { + var store = HttpContext.GetStoreData(); + if (store == null) + return NotFound(); + + var storeBlob = store.GetStoreBlob(); + var vm = new PaymentViewModel + { + NetworkFeeMode = storeBlob.NetworkFeeMode, + AnyoneCanCreateInvoice = storeBlob.AnyoneCanInvoice, + SpeedPolicy = store.SpeedPolicy, + PaymentTolerance = storeBlob.PaymentTolerance, + DefaultCurrency = storeBlob.DefaultCurrency + }; + + AddPaymentMethods(store, storeBlob, + out var derivationSchemes, out var lightningNodes); + + vm.DerivationSchemes = derivationSchemes; + vm.LightningNodes = lightningNodes; vm.MonitoringExpiration = (int)storeBlob.MonitoringExpiration.TotalMinutes; vm.InvoiceExpiration = (int)storeBlob.InvoiceExpiration.TotalMinutes; vm.LightningDescriptionTemplate = storeBlob.LightningDescriptionTemplate; - vm.PaymentTolerance = storeBlob.PaymentTolerance; vm.PayJoinEnabled = storeBlob.PayJoinEnabled; - vm.HintWallet = storeBlob.Hints.Wallet; - vm.HintLightning = storeBlob.Hints.Lightning; vm.LightningAmountInSatoshi = storeBlob.LightningAmountInSatoshi; vm.LightningPrivateRouteHints = storeBlob.LightningPrivateRouteHints; vm.OnChainWithLnInvoiceFallback = storeBlob.OnChainWithLnInvoiceFallback; @@ -584,12 +651,12 @@ namespace BTCPayServer.Controllers .GetSupportedPaymentMethods(_NetworkProvider) .OfType() .Any(settings => settings.Network.SupportPayJoin && settings.IsHotWallet); - + return View(vm); } - [HttpPost("{storeId}")] - public async Task UpdateStore(StoreViewModel model, string command = null) + [HttpPost("{storeId}/payment")] + public async Task Payment(PaymentViewModel model, string command = null) { bool needUpdate = false; if (CurrentStore.SpeedPolicy != model.SpeedPolicy) @@ -597,16 +664,6 @@ namespace BTCPayServer.Controllers needUpdate = true; CurrentStore.SpeedPolicy = model.SpeedPolicy; } - if (CurrentStore.StoreName != model.StoreName) - { - needUpdate = true; - CurrentStore.StoreName = model.StoreName; - } - if (CurrentStore.StoreWebsite != model.StoreWebsite) - { - needUpdate = true; - CurrentStore.StoreWebsite = model.StoreWebsite; - } var blob = CurrentStore.GetStoreBlob(); blob.DefaultCurrency = model.DefaultCurrency; @@ -633,7 +690,7 @@ namespace BTCPayServer.Controllers { await _Repo.UpdateStore(CurrentStore); - TempData[WellKnownTempData.SuccessMessage] = "Store successfully updated"; + TempData[WellKnownTempData.SuccessMessage] = "Payment settings successfully updated"; if (payjoinChanged && blob.PayJoinEnabled) { @@ -649,13 +706,13 @@ namespace BTCPayServer.Controllers TempData.SetStatusMessageModel(new StatusMessageModel() { Severity = StatusMessageModel.StatusSeverity.Warning, - Html = $"The store was updated successfully. However, payjoin will not work for {string.Join(", ", problematicPayjoinEnabledMethods)} until you configure them to be a hot wallet." + Html = $"The payment settings were updated successfully. However, payjoin will not work for {string.Join(", ", problematicPayjoinEnabledMethods)} until you configure them to be a hot wallet." }); } } } - return RedirectToAction(nameof(UpdateStore), new + return RedirectToAction(nameof(Payment), new { storeId = CurrentStore.Id }); diff --git a/BTCPayServer/Models/StoreViewModels/PaymentViewModel.cs b/BTCPayServer/Models/StoreViewModels/PaymentViewModel.cs new file mode 100644 index 000000000..06d24ca45 --- /dev/null +++ b/BTCPayServer/Models/StoreViewModels/PaymentViewModel.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using BTCPayServer.Client.Models; +using BTCPayServer.Validation; +using static BTCPayServer.Data.StoreBlob; + +namespace BTCPayServer.Models.StoreViewModels +{ + public class PaymentViewModel + { + public List DerivationSchemes { get; set; } + public List LightningNodes { get; set; } + public bool IsOnchainSetup { get; set; } + public bool IsLightningSetup { get; set; } + public bool CanUsePayJoin { get; set; } + + [Display(Name = "Allow anyone to create invoice")] + public bool AnyoneCanCreateInvoice { get; set; } + + [Display(Name = "Invoice expires if the full amount has not been paid after …")] + [Range(1, 60 * 24 * 24)] + public int InvoiceExpiration { get; set; } + + [Display(Name = "Payment invalid if transactions fails to confirm … after invoice expiration")] + [Range(10, 60 * 24 * 24)] + public int MonitoringExpiration { get; set; } + + [Display(Name = "Consider the invoice confirmed when the payment transaction …")] + public SpeedPolicy SpeedPolicy { get; set; } + + [Display(Name = "Add additional fee (network fee) to invoice …")] + public NetworkFeeMode NetworkFeeMode { get; set; } + + [Display(Name = "Description template of the lightning invoice")] + public string LightningDescriptionTemplate { get; set; } + + [Display(Name = "Enable Payjoin/P2EP")] + public bool PayJoinEnabled { get; set; } + + [Display(Name = "Show recommended fee")] + public bool ShowRecommendedFee { get; set; } + + [Display(Name = "Recommended fee confirmation target blocks")] + [Range(1, double.PositiveInfinity)] + public int RecommendedFeeBlockTarget { get; set; } + + [Display(Name = "Display Lightning payment amounts in Satoshis")] + public bool LightningAmountInSatoshi { get; set; } + + [Display(Name = "Add hop hints for private channels to the Lightning invoice")] + public bool LightningPrivateRouteHints { get; set; } + + [Display(Name = "Include Lightning invoice fallback to on-chain BIP21 payment URL")] + public bool OnChainWithLnInvoiceFallback { get; set; } + + [Display(Name = "Consider the invoice paid even if the paid amount is ... % less than expected")] + [Range(0, 100)] + public double PaymentTolerance { get; set; } + + [Display(Name = "Default currency")] + [MaxLength(10)] + public string DefaultCurrency { get; set; } + } +} diff --git a/BTCPayServer/Models/StoreViewModels/StoreDerivationScheme.cs b/BTCPayServer/Models/StoreViewModels/StoreDerivationScheme.cs new file mode 100644 index 000000000..938deae14 --- /dev/null +++ b/BTCPayServer/Models/StoreViewModels/StoreDerivationScheme.cs @@ -0,0 +1,12 @@ +namespace BTCPayServer.Models.StoreViewModels +{ + public class StoreDerivationScheme + { + public string Crypto { get; set; } + public string Value { get; set; } + public WalletId WalletId { get; set; } + public bool WalletSupported { get; set; } + public bool Enabled { get; set; } + public bool Collapsed { get; set; } + } +} diff --git a/BTCPayServer/Models/StoreViewModels/StoreLightningNode.cs b/BTCPayServer/Models/StoreViewModels/StoreLightningNode.cs new file mode 100644 index 000000000..8bb7ff33a --- /dev/null +++ b/BTCPayServer/Models/StoreViewModels/StoreLightningNode.cs @@ -0,0 +1,9 @@ +namespace BTCPayServer.Models.StoreViewModels +{ + public class StoreLightningNode + { + public string CryptoCode { get; set; } + public string Address { get; set; } + public bool Enabled { get; set; } + } +} diff --git a/BTCPayServer/Models/StoreViewModels/StoreViewModel.cs b/BTCPayServer/Models/StoreViewModels/StoreViewModel.cs index e086d61b5..3b3c54d5c 100644 --- a/BTCPayServer/Models/StoreViewModels/StoreViewModel.cs +++ b/BTCPayServer/Models/StoreViewModels/StoreViewModel.cs @@ -8,130 +8,24 @@ namespace BTCPayServer.Models.StoreViewModels { public class StoreViewModel { - public class DerivationScheme - { - public string Crypto { get; set; } - public string Value { get; set; } - public WalletId WalletId { get; set; } - public bool WalletSupported { get; set; } - public bool Enabled { get; set; } - public bool Collapsed { get; set; } - } - - public class AdditionalPaymentMethod - { - public string Provider { get; set; } - public bool Enabled { get; set; } - public string Action { get; set; } - } - public StoreViewModel() - { - - } - + public List DerivationSchemes { get; set; } + public List LightningNodes { get; set; } + public bool HintWallet { get; set; } + public bool HintLightning { get; set; } public bool CanDelete { get; set; } + [Display(Name = "Store ID")] public string Id { get; set; } + [Display(Name = "Store Name")] [Required] [MaxLength(50)] [MinLength(1)] - public string StoreName - { - get; set; - } + public string StoreName { get; set; } [Uri] [Display(Name = "Store Website")] [MaxLength(500)] - public string StoreWebsite - { - get; - set; - } - - [Display(Name = "Default currency")] - [MaxLength(10)] - public string DefaultCurrency { get; set; } - - [Display(Name = "Allow anyone to create invoice")] - public bool AnyoneCanCreateInvoice { get; set; } - - public List DerivationSchemes { get; set; } = new List(); - - [Display(Name = "Invoice expires if the full amount has not been paid after …")] - [Range(1, 60 * 24 * 24)] - public int InvoiceExpiration - { - get; - set; - } - - [Display(Name = "Payment invalid if transactions fails to confirm … after invoice expiration")] - [Range(10, 60 * 24 * 24)] - public int MonitoringExpiration - { - get; - set; - } - - [Display(Name = "Consider the invoice confirmed when the payment transaction …")] - public SpeedPolicy SpeedPolicy - { - get; set; - } - - [Display(Name = "Add additional fee (network fee) to invoice …")] - public NetworkFeeMode NetworkFeeMode - { - get; set; - } - - [Display(Name = "Description template of the lightning invoice")] - public string LightningDescriptionTemplate { get; set; } - - [Display(Name = "Enable Payjoin/P2EP")] - public bool PayJoinEnabled { get; set; } - public bool CanUsePayJoin { get; set; } - public bool IsOnchainSetup { get; set; } - public bool IsLightningSetup { get; set; } - - public bool HintWallet { get; set; } - public bool HintLightning { get; set; } - - [Display(Name = "Show recommended fee")] - public bool ShowRecommendedFee { get; set; } - - [Display(Name = "Recommended fee confirmation target blocks")] - [Range(1, double.PositiveInfinity)] - public int RecommendedFeeBlockTarget { get; set; } - - [Display(Name = "Display Lightning payment amounts in Satoshis")] - public bool LightningAmountInSatoshi { get; set; } - - [Display(Name = "Add hop hints for private channels to the Lightning invoice")] - public bool LightningPrivateRouteHints { get; set; } - - [Display(Name = "Include Lightning invoice fallback to on-chain BIP21 payment URL")] - public bool OnChainWithLnInvoiceFallback { get; set; } - - public class LightningNode - { - public string CryptoCode { get; set; } - public string Address { get; set; } - public bool Enabled { get; set; } - } - public List LightningNodes - { - get; set; - } = new List(); - - [Display(Name = "Consider the invoice paid even if the paid amount is ... % less than expected")] - [Range(0, 100)] - public double PaymentTolerance - { - get; - set; - } + public string StoreWebsite { get; set; } } } diff --git a/BTCPayServer/Views/Stores/CheckoutExperience.cshtml b/BTCPayServer/Views/Stores/CheckoutExperience.cshtml index ac0e11e64..be62b6601 100644 --- a/BTCPayServer/Views/Stores/CheckoutExperience.cshtml +++ b/BTCPayServer/Views/Stores/CheckoutExperience.cshtml @@ -12,7 +12,7 @@ {
} -

Payment

+

Invoice Settings

@if (Model.PaymentMethods.Any()) {
diff --git a/BTCPayServer/Views/Stores/Payment.cshtml b/BTCPayServer/Views/Stores/Payment.cshtml new file mode 100644 index 000000000..265b8c886 --- /dev/null +++ b/BTCPayServer/Views/Stores/Payment.cshtml @@ -0,0 +1,156 @@ +@model PaymentViewModel +@{ + Layout = "../Shared/_NavLayout.cshtml"; + ViewData.SetActivePageAndTitle(StoreNavPages.Payment, "Payment", Context.GetStoreData().StoreName); +} + +
+
+

Payment

+ @if (!ViewContext.ModelState.IsValid) + { +
+ } + @if (Model.IsOnchainSetup || Model.IsLightningSetup) + { +
+
+ + + + + +
+
+ + + + + +
+
+ + + + +
+ + minutes +
+ +
+
+ + + + +
+ + percent +
+ +
+
+ + + +
+ @if (Model.IsOnchainSetup) + { +
On-Chain
+ @if (Model.CanUsePayJoin) + { +
+
+ + + + + +
+ +
+ } +
+ + + + +
+ + minutes +
+ +
+
+ + + + + + + +
+
+ + +

Fee will be shown for BTC and LTC onchain payments only.

+
+
+ + + +
+ } + + @if (Model.IsLightningSetup) + { +
Lightning
+
+ + +
+
+ + +
+
+ + +
+
+ + + +

+ Available placeholders: + {StoreName} {ItemDescription} {OrderId} +

+
+ } + +
+ } + else + { +

+ Please configure either an on-chain wallet or Lightning node first. +

+ } +
+
+ +@section PageFootContent { + +} diff --git a/BTCPayServer/Views/Stores/StoreNavPages.cs b/BTCPayServer/Views/Stores/StoreNavPages.cs index 4fc87f5ca..fecec960e 100644 --- a/BTCPayServer/Views/Stores/StoreNavPages.cs +++ b/BTCPayServer/Views/Stores/StoreNavPages.cs @@ -2,8 +2,6 @@ namespace BTCPayServer.Views.Stores { public enum StoreNavPages { - Index, Create, Rates, Checkout, Tokens, Users, PayButton, Integrations, Wallet, Webhooks, ActivePage, - PullPayments, - Payouts + Index, Create, Rates, Payment, Checkout, Tokens, Users, PayButton, Integrations, Wallet, Webhooks, ActivePage, PullPayments, Payouts } } diff --git a/BTCPayServer/Views/Stores/_Nav.cshtml b/BTCPayServer/Views/Stores/_Nav.cshtml index f67d5e3c2..9a2ac28a4 100644 --- a/BTCPayServer/Views/Stores/_Nav.cshtml +++ b/BTCPayServer/Views/Stores/_Nav.cshtml @@ -7,7 +7,7 @@ Pay Button Integrations Webhooks - Pull payments - Payouts + Pull payments + Payouts