From 82620ee32767d8a52376a588606eb6dc51a014b7 Mon Sep 17 00:00:00 2001 From: d11n Date: Mon, 30 Sep 2024 12:13:51 +0200 Subject: [PATCH] Move wallet payment settings back to store settings (#6251) Intermediate solution, until we implement these settings on the payment method level. Closes #6237. --- BTCPayServer.Tests/TestAccount.cs | 14 ++- BTCPayServer.Tests/UnitTest1.cs | 14 +-- .../Controllers/UIStoresController.Onchain.cs | 90 ++++--------------- .../UIStoresController.Settings.cs | 21 ++++- .../GeneralSettingsViewModel.cs | 14 +++ .../WalletSettingsViewModel.cs | 15 ---- .../Views/UIStores/GeneralSettings.cshtml | 57 ++++++++++-- .../Views/UIStores/WalletSettings.cshtml | 73 +++------------ 8 files changed, 130 insertions(+), 168 deletions(-) diff --git a/BTCPayServer.Tests/TestAccount.cs b/BTCPayServer.Tests/TestAccount.cs index a26f33f90..3a3b7e8a6 100644 --- a/BTCPayServer.Tests/TestAccount.cs +++ b/BTCPayServer.Tests/TestAccount.cs @@ -146,19 +146,27 @@ namespace BTCPayServer.Tests public async Task ModifyPayment(Action modify) { var storeController = GetController(); - var response = await storeController.GeneralSettings(); - GeneralSettingsViewModel settings = (GeneralSettingsViewModel)((ViewResult)response).Model; + var response = await storeController.GeneralSettings(StoreId); + GeneralSettingsViewModel settings = (GeneralSettingsViewModel)((ViewResult)response).Model!; modify(settings); await storeController.GeneralSettings(settings); } + public async Task ModifyGeneralSettings(Action modify) + { + var storeController = GetController(); + var response = await storeController.GeneralSettings(StoreId); + GeneralSettingsViewModel settings = (GeneralSettingsViewModel)((ViewResult)response).Model!; + modify(settings); + storeController.GeneralSettings(settings).GetAwaiter().GetResult(); + } + public async Task ModifyOnchainPaymentSettings(Action modify) { var storeController = GetController(); var response = await storeController.WalletSettings(StoreId, "BTC"); WalletSettingsViewModel walletSettings = (WalletSettingsViewModel)((ViewResult)response).Model; modify(walletSettings); - storeController.UpdatePaymentSettings(walletSettings).GetAwaiter().GetResult(); storeController.UpdateWalletSettings(walletSettings).GetAwaiter().GetResult(); } diff --git a/BTCPayServer.Tests/UnitTest1.cs b/BTCPayServer.Tests/UnitTest1.cs index 812cca065..6aa32a1ca 100644 --- a/BTCPayServer.Tests/UnitTest1.cs +++ b/BTCPayServer.Tests/UnitTest1.cs @@ -303,7 +303,7 @@ namespace BTCPayServer.Tests // Set tolerance to 50% var stores = user.GetController(); - var response = await stores.GeneralSettings(); + var response = await stores.GeneralSettings(user.StoreId); var vm = Assert.IsType(Assert.IsType(response).Model); Assert.Equal(0.0, vm.PaymentTolerance); vm.PaymentTolerance = 50.0; @@ -385,7 +385,7 @@ namespace BTCPayServer.Tests await user.RegisterDerivationSchemeAsync("BTC"); await user.RegisterLightningNodeAsync("BTC", LightningConnectionType.CLightning); await user.SetNetworkFeeMode(NetworkFeeMode.Never); - await user.ModifyOnchainPaymentSettings(p => p.SpeedPolicy = SpeedPolicy.HighSpeed); + await user.ModifyGeneralSettings(p => p.SpeedPolicy = SpeedPolicy.HighSpeed); var invoice = await user.BitPay.CreateInvoiceAsync(new Invoice(0.0001m, "BTC")); await tester.WaitForEvent(async () => { @@ -445,7 +445,7 @@ namespace BTCPayServer.Tests var user = tester.NewAccount(); await user.GrantAccessAsync(true); var storeController = user.GetController(); - var storeResponse = await storeController.GeneralSettings(); + var storeResponse = await storeController.GeneralSettings(user.StoreId); Assert.IsType(storeResponse); Assert.IsType(storeController.SetupLightningNode(user.StoreId, "BTC")); @@ -568,10 +568,10 @@ namespace BTCPayServer.Tests using var tester = CreateServerTester(); await tester.StartAsync(); var acc = tester.NewAccount(); - acc.GrantAccess(); + await acc.GrantAccessAsync(); acc.RegisterDerivationScheme("BTC"); - await acc.ModifyOnchainPaymentSettings(p => p.SpeedPolicy = SpeedPolicy.LowSpeed); - var invoice = acc.BitPay.CreateInvoice(new Invoice + await acc.ModifyGeneralSettings(p => p.SpeedPolicy = SpeedPolicy.LowSpeed); + var invoice = await acc.BitPay.CreateInvoiceAsync(new Invoice { Price = 5.0m, Currency = "USD", @@ -2619,7 +2619,7 @@ namespace BTCPayServer.Tests } var controller = tester.PayTester.GetController(user.UserId, user.StoreId); - var vm = await controller.GeneralSettings().AssertViewModelAsync(); + var vm = await controller.GeneralSettings(user.StoreId).AssertViewModelAsync(); Assert.Equal(tester.PayTester.ServerUriWithIP + "LocalStorage/8f890691-87f9-4c65-80e5-3b7ffaa3551f-store.png", vm.LogoUrl); Assert.Equal(tester.PayTester.ServerUriWithIP + "LocalStorage/2a51c49a-9d54-4013-80a2-3f6e69d08523-store.css", vm.CssUrl); diff --git a/BTCPayServer/Controllers/UIStoresController.Onchain.cs b/BTCPayServer/Controllers/UIStoresController.Onchain.cs index c98e857de..04bac86e1 100644 --- a/BTCPayServer/Controllers/UIStoresController.Onchain.cs +++ b/BTCPayServer/Controllers/UIStoresController.Onchain.cs @@ -437,15 +437,10 @@ public partial class UIStoresController }).ToList(), Config = ProtectString(JToken.FromObject(derivation, handler.Serializer).ToString()), PayJoinEnabled = storeBlob.PayJoinEnabled, - MonitoringExpiration = (int)storeBlob.MonitoringExpiration.TotalMinutes, - SpeedPolicy = store.SpeedPolicy, - ShowRecommendedFee = storeBlob.ShowRecommendedFee, - RecommendedFeeBlockTarget = storeBlob.RecommendedFeeBlockTarget, + CanUsePayJoin = canUseHotWallet && network.SupportPayJoin && derivation.IsHotWallet, CanUseHotWallet = canUseHotWallet, CanUseRPCImport = rpcImport, - CanUsePayJoin = canUseHotWallet && network.SupportPayJoin && derivation.IsHotWallet, - StoreName = store.StoreName, - + StoreName = store.StoreName }; ViewData["ReplaceDescription"] = WalletReplaceWarning(derivation.IsHotWallet); @@ -473,15 +468,14 @@ public partial class UIStoresController var storeBlob = store.GetStoreBlob(); var excludeFilters = storeBlob.GetExcludedPaymentMethods(); var currentlyEnabled = !excludeFilters.Match(handler.PaymentMethodId); - bool enabledChanged = currentlyEnabled != vm.Enabled; - bool needUpdate = enabledChanged; + var enabledChanged = currentlyEnabled != vm.Enabled; + var payjoinChanged = storeBlob.PayJoinEnabled != vm.PayJoinEnabled; + var needUpdate = enabledChanged || payjoinChanged; string errorMessage = null; - if (enabledChanged) - { - storeBlob.SetExcluded(handler.PaymentMethodId, !vm.Enabled); - store.SetStoreBlob(storeBlob); - } + if (enabledChanged) storeBlob.SetExcluded(handler.PaymentMethodId, !vm.Enabled); + if (payjoinChanged && network.SupportPayJoin) storeBlob.PayJoinEnabled = vm.PayJoinEnabled; + if (needUpdate) store.SetStoreBlob(storeBlob); if (derivation.Label != vm.Label) { @@ -552,6 +546,15 @@ public partial class UIStoresController _eventAggregator.Publish(new WalletChangedEvent { WalletId = new WalletId(vm.StoreId, vm.CryptoCode) }); successMessage += $" {vm.CryptoCode} on-chain payments are now {(vm.Enabled ? "enabled" : "disabled")} for this store."; } + + if (payjoinChanged && storeBlob.PayJoinEnabled && network.SupportPayJoin) + { + var config = store.GetPaymentMethodConfig(PaymentTypes.CHAIN.GetPaymentMethodId(network.CryptoCode), _handlers); + if (config?.IsHotWallet is not true) + { + successMessage += " However, PayJoin will not work, as this isn't a hot wallet."; + } + } TempData[WellKnownTempData.SuccessMessage] = successMessage; } @@ -564,65 +567,6 @@ public partial class UIStoresController return RedirectToAction(nameof(WalletSettings), new { vm.StoreId, vm.CryptoCode }); } - [HttpPost("{storeId}/onchain/{cryptoCode}/settings/payment")] - [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)] - public async Task UpdatePaymentSettings(WalletSettingsViewModel vm) - { - var checkResult = IsAvailable(vm.CryptoCode, out var store, out var network); - if (checkResult != null) - { - return checkResult; - } - - var derivation = GetExistingDerivationStrategy(vm.CryptoCode, store); - if (derivation == null) - { - return NotFound(); - } - - bool needUpdate = false; - var blob = store.GetStoreBlob(); - var payjoinChanged = blob.PayJoinEnabled != vm.PayJoinEnabled; - blob.MonitoringExpiration = TimeSpan.FromMinutes(vm.MonitoringExpiration); - blob.ShowRecommendedFee = vm.ShowRecommendedFee; - blob.RecommendedFeeBlockTarget = vm.RecommendedFeeBlockTarget; - blob.PayJoinEnabled = vm.PayJoinEnabled; - - if (store.SetStoreBlob(blob)) - { - needUpdate = true; - } - - if (store.SpeedPolicy != vm.SpeedPolicy) - { - store.SpeedPolicy = vm.SpeedPolicy; - needUpdate = true; - } - - if (needUpdate) - { - await _storeRepo.UpdateStore(store); - - TempData[WellKnownTempData.SuccessMessage] = "Payment settings successfully updated"; - - if (payjoinChanged && blob.PayJoinEnabled && network.SupportPayJoin) - { - var config = store.GetPaymentMethodConfig(PaymentTypes.CHAIN.GetPaymentMethodId(network.CryptoCode), _handlers); - if (config?.IsHotWallet is not true) - { - TempData.Remove(WellKnownTempData.SuccessMessage); - TempData.SetStatusMessageModel(new StatusMessageModel - { - Severity = StatusMessageModel.StatusSeverity.Warning, - Html = "The payment settings were updated successfully. However, PayJoin will not work, as this isn't a hot wallet." - }); - } - } - } - - return RedirectToAction(nameof(WalletSettings), new { vm.StoreId, vm.CryptoCode }); - } - [HttpGet("{storeId}/onchain/{cryptoCode}/seed")] [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)] public async Task WalletSeed(string storeId, string cryptoCode, CancellationToken cancellationToken = default) diff --git a/BTCPayServer/Controllers/UIStoresController.Settings.cs b/BTCPayServer/Controllers/UIStoresController.Settings.cs index 307e92196..48d98296f 100644 --- a/BTCPayServer/Controllers/UIStoresController.Settings.cs +++ b/BTCPayServer/Controllers/UIStoresController.Settings.cs @@ -20,11 +20,11 @@ namespace BTCPayServer.Controllers; public partial class UIStoresController { [HttpGet("{storeId}/settings")] - public async Task GeneralSettings() + public async Task GeneralSettings(string storeId) { var store = HttpContext.GetStoreData(); - if (store == null) - return NotFound(); + if (store == null) return NotFound(); + var storeBlob = store.GetStoreBlob(); var vm = new GeneralSettingsViewModel { @@ -41,7 +41,11 @@ public partial class UIStoresController InvoiceExpiration = (int)storeBlob.InvoiceExpiration.TotalMinutes, DefaultCurrency = storeBlob.DefaultCurrency, BOLT11Expiration = (long)storeBlob.RefundBOLT11Expiration.TotalDays, - Archived = store.Archived + Archived = store.Archived, + MonitoringExpiration = (int)storeBlob.MonitoringExpiration.TotalMinutes, + SpeedPolicy = store.SpeedPolicy, + ShowRecommendedFee = storeBlob.ShowRecommendedFee, + RecommendedFeeBlockTarget = storeBlob.RecommendedFeeBlockTarget }; return View(vm); @@ -67,13 +71,22 @@ public partial class UIStoresController CurrentStore.StoreWebsite = model.StoreWebsite; } + if (CurrentStore.SpeedPolicy != model.SpeedPolicy) + { + CurrentStore.SpeedPolicy = model.SpeedPolicy; + needUpdate = true; + } + var blob = CurrentStore.GetStoreBlob(); blob.AnyoneCanInvoice = model.AnyoneCanCreateInvoice; blob.NetworkFeeMode = model.NetworkFeeMode; blob.PaymentTolerance = model.PaymentTolerance; blob.DefaultCurrency = model.DefaultCurrency; + blob.ShowRecommendedFee = model.ShowRecommendedFee; + blob.RecommendedFeeBlockTarget = model.RecommendedFeeBlockTarget; blob.InvoiceExpiration = TimeSpan.FromMinutes(model.InvoiceExpiration); blob.RefundBOLT11Expiration = TimeSpan.FromDays(model.BOLT11Expiration); + blob.MonitoringExpiration = TimeSpan.FromMinutes(model.MonitoringExpiration); if (!string.IsNullOrEmpty(model.BrandColor) && !ColorPalette.IsValid(model.BrandColor)) { ModelState.AddModelError(nameof(model.BrandColor), "The brand color needs to be a valid hex color code"); diff --git a/BTCPayServer/Models/StoreViewModels/GeneralSettingsViewModel.cs b/BTCPayServer/Models/StoreViewModels/GeneralSettingsViewModel.cs index da16a5618..bc84908fc 100644 --- a/BTCPayServer/Models/StoreViewModels/GeneralSettingsViewModel.cs +++ b/BTCPayServer/Models/StoreViewModels/GeneralSettingsViewModel.cs @@ -59,5 +59,19 @@ namespace BTCPayServer.Models.StoreViewModels [Display(Name = "Minimum acceptable expiration time for BOLT11 for refunds")] [Range(0, 365 * 10)] public long BOLT11Expiration { 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 = "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 settled when the payment transaction …")] + public SpeedPolicy SpeedPolicy { get; set; } } } diff --git a/BTCPayServer/Models/StoreViewModels/WalletSettingsViewModel.cs b/BTCPayServer/Models/StoreViewModels/WalletSettingsViewModel.cs index d146cf175..6bed0295d 100644 --- a/BTCPayServer/Models/StoreViewModels/WalletSettingsViewModel.cs +++ b/BTCPayServer/Models/StoreViewModels/WalletSettingsViewModel.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using BTCPayServer.Client.Models; using Newtonsoft.Json; namespace BTCPayServer.Models.StoreViewModels @@ -16,20 +15,6 @@ namespace BTCPayServer.Models.StoreViewModels [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 = "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 settled when the payment transaction …")] - public SpeedPolicy SpeedPolicy { get; set; } - public string Label { get; set; } public string DerivationSchemeInput { get; set; } diff --git a/BTCPayServer/Views/UIStores/GeneralSettings.cshtml b/BTCPayServer/Views/UIStores/GeneralSettings.cshtml index f11e4cf72..7e924a669 100644 --- a/BTCPayServer/Views/UIStores/GeneralSettings.cshtml +++ b/BTCPayServer/Views/UIStores/GeneralSettings.cshtml @@ -122,13 +122,6 @@ -
- - - - - -
+
+ + + + +
+ + minutes +
+ +
+
+ + + + + + + +
+
+ + + + + +
+
+ +
+ +
Fee will be shown for BTC and LTC onchain payments only.
+
+
+
+ +
+ + blocks +
+ +
diff --git a/BTCPayServer/Views/UIStores/WalletSettings.cshtml b/BTCPayServer/Views/UIStores/WalletSettings.cshtml index 23f272332..a46b15803 100644 --- a/BTCPayServer/Views/UIStores/WalletSettings.cshtml +++ b/BTCPayServer/Views/UIStores/WalletSettings.cshtml @@ -77,8 +77,21 @@ - + + @if (Model.CanUsePayJoin) + { +
+
+ + + + + +
+ +
+ }
@@ -143,64 +156,6 @@ -

Payment

-
- @if (Model.CanUsePayJoin) - { -
-
- - - - - -
- -
- } -
- - - - -
- - minutes -
- -
-
- - - - - - - -
-
- -
- -
Fee will be shown for BTC and LTC onchain payments only.
-
-
-
- - - -
- -
-

Labels

Manage labels