mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 14:34:23 +01:00
Restructure store and payment settings (#2995)
Co-authored-by: Kukks <evilkukka@gmail.com>
This commit is contained in:
@@ -26,6 +26,7 @@ using Newtonsoft.Json.Linq;
|
||||
using OpenQA.Selenium;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using WalletSettingsViewModel = BTCPayServer.Models.StoreViewModels.WalletSettingsViewModel;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
{
|
||||
@@ -76,10 +77,10 @@ namespace BTCPayServer.Tests
|
||||
Assert.IsType<RedirectToActionResult>(response);
|
||||
|
||||
// Get enabled state from overview action
|
||||
StoreViewModel storeModel;
|
||||
response = controller.UpdateStore();
|
||||
storeModel = (StoreViewModel)Assert.IsType<ViewResult>(response).Model;
|
||||
var lnNode = storeModel.LightningNodes.Find(node => node.CryptoCode == cryptoCode);
|
||||
PaymentMethodsViewModel paymentMethodsModel;
|
||||
response = controller.PaymentMethods();
|
||||
paymentMethodsModel = (PaymentMethodsViewModel)Assert.IsType<ViewResult>(response).Model;
|
||||
var lnNode = paymentMethodsModel.LightningNodes.Find(node => node.CryptoCode == cryptoCode);
|
||||
Assert.NotNull(lnNode);
|
||||
Assert.False(lnNode.Enabled);
|
||||
|
||||
@@ -89,18 +90,18 @@ namespace BTCPayServer.Tests
|
||||
Assert.IsType<ViewResult>(response);
|
||||
|
||||
// Get enabled state from overview action
|
||||
response = controller.UpdateStore();
|
||||
storeModel = (StoreViewModel)Assert.IsType<ViewResult>(response).Model;
|
||||
var derivationScheme = storeModel.DerivationSchemes.Find(scheme => scheme.Crypto == cryptoCode);
|
||||
response = controller.PaymentMethods();
|
||||
paymentMethodsModel = (PaymentMethodsViewModel)Assert.IsType<ViewResult>(response).Model;
|
||||
var derivationScheme = paymentMethodsModel.DerivationSchemes.Find(scheme => scheme.Crypto == cryptoCode);
|
||||
Assert.NotNull(derivationScheme);
|
||||
Assert.True(derivationScheme.Enabled);
|
||||
|
||||
// Disable wallet
|
||||
response = controller.SetWalletEnabled(storeId, cryptoCode, false).GetAwaiter().GetResult();
|
||||
Assert.IsType<RedirectToActionResult>(response);
|
||||
response = controller.UpdateStore();
|
||||
storeModel = (StoreViewModel)Assert.IsType<ViewResult>(response).Model;
|
||||
derivationScheme = storeModel.DerivationSchemes.Find(scheme => scheme.Crypto == cryptoCode);
|
||||
response = controller.PaymentMethods();
|
||||
paymentMethodsModel = (PaymentMethodsViewModel)Assert.IsType<ViewResult>(response).Model;
|
||||
derivationScheme = paymentMethodsModel.DerivationSchemes.Find(scheme => scheme.Crypto == cryptoCode);
|
||||
Assert.NotNull(derivationScheme);
|
||||
Assert.False(derivationScheme.Enabled);
|
||||
|
||||
@@ -138,9 +139,9 @@ namespace BTCPayServer.Tests
|
||||
Assert.True(setupVm.Confirmation);
|
||||
response = await controller.UpdateWallet(setupVm);
|
||||
Assert.IsType<RedirectToActionResult>(response);
|
||||
response = await controller.ModifyWallet(new WalletSetupViewModel { StoreId = storeId, CryptoCode = cryptoCode });
|
||||
setupVm = (WalletSetupViewModel)Assert.IsType<ViewResult>(response).Model;
|
||||
Assert.Equal("CoboVault", setupVm.Source);
|
||||
response = await controller.WalletSettings(storeId, cryptoCode);
|
||||
var settingsVm = (WalletSettingsViewModel)Assert.IsType<ViewResult>(response).Model;
|
||||
Assert.Equal("CoboVault", settingsVm.Source);
|
||||
|
||||
// wasabi wallet file
|
||||
content = "{\r\n \"EncryptedSecret\": \"6PYWBQ1zsukowsnTNA57UUx791aBuJusm7E4egXUmF5WGw3tcdG3cmTL57\",\r\n \"ChainCode\": \"waSIVbn8HaoovoQg/0t8IS1+ZCxGsJRGFT21i06nWnc=\",\r\n \"MasterFingerprint\": \"7a7563b5\",\r\n \"ExtPubKey\": \"xpub6CEqRFZ7yZxCFXuEWZBAdnC8bdvu9SRHevaoU2SsW9ZmKhrCShmbpGZWwaR15hdLURf8hg47g4TpPGaqEU8hw5LEJCE35AUhne67XNyFGBk\",\r\n \"PasswordVerified\": false,\r\n \"MinGapLimit\": 21,\r\n \"AccountKeyPath\": \"84'/0'/0'\",\r\n \"BlockchainState\": {\r\n \"Network\": \"RegTest\",\r\n \"Height\": \"0\"\r\n },\r\n \"HdPubKeys\": []\r\n}";
|
||||
@@ -149,9 +150,9 @@ namespace BTCPayServer.Tests
|
||||
Assert.True(setupVm.Confirmation);
|
||||
response = await controller.UpdateWallet(setupVm);
|
||||
Assert.IsType<RedirectToActionResult>(response);
|
||||
response = await controller.ModifyWallet(new WalletSetupViewModel { StoreId = storeId, CryptoCode = cryptoCode });
|
||||
setupVm = (WalletSetupViewModel)Assert.IsType<ViewResult>(response).Model;
|
||||
Assert.Equal("WasabiFile", setupVm.Source);
|
||||
response = await controller.WalletSettings(storeId, cryptoCode);
|
||||
settingsVm = (WalletSettingsViewModel)Assert.IsType<ViewResult>(response).Model;
|
||||
Assert.Equal("WasabiFile", settingsVm.Source);
|
||||
|
||||
// Can we upload coldcard settings? (Should fail, we are giving a mainnet file to a testnet network)
|
||||
content = "{\"keystore\": {\"ckcc_xpub\": \"xpub661MyMwAqRbcGVBsTGeNZN6QGVHmMHLdSA4FteGsRrEriu4pnVZMZWnruFFFXkMnyoBjyHndD3Qwcfz4MPzBUxjSevweNFQx7SAYZATtcDw\", \"xpub\": \"ypub6WWc2gWwHbdnAAyJDnR4SPL1phRh7REqrPBfZeizaQ1EmTshieRXJC3Z5YoU4wkcdKHEjQGkh6AYEzCQC1Kz3DNaWSwdc1pc8416hAjzqyD\", \"label\": \"Coldcard Import 0x60d1af8b\", \"ckcc_xfp\": 1624354699, \"type\": \"hardware\", \"hw_type\": \"coldcard\", \"derivation\": \"m/49'/0'/0'\"}, \"wallet_type\": \"standard\", \"use_encryption\": false, \"seed_version\": 17}";
|
||||
@@ -166,9 +167,9 @@ namespace BTCPayServer.Tests
|
||||
Assert.True(setupVm.Confirmation);
|
||||
response = await controller.UpdateWallet(setupVm);
|
||||
Assert.IsType<RedirectToActionResult>(response);
|
||||
response = await controller.ModifyWallet(new WalletSetupViewModel { StoreId = storeId, CryptoCode = cryptoCode });
|
||||
setupVm = (WalletSetupViewModel)Assert.IsType<ViewResult>(response).Model;
|
||||
Assert.Equal("ElectrumFile", setupVm.Source);
|
||||
response = await controller.WalletSettings(storeId, cryptoCode);
|
||||
settingsVm = (WalletSettingsViewModel)Assert.IsType<ViewResult>(response).Model;
|
||||
Assert.Equal("ElectrumFile", settingsVm.Source);
|
||||
|
||||
// Now let's check that no data has been lost in the process
|
||||
var store = tester.PayTester.StoreRepository.FindStore(storeId).GetAwaiter().GetResult();
|
||||
|
||||
@@ -5,7 +5,6 @@ using BTCPayServer.Tests.Logging;
|
||||
using BTCPayServer.Views.Stores;
|
||||
using NBitcoin;
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Support.UI;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
@@ -32,7 +31,7 @@ namespace BTCPayServer.Tests
|
||||
s.RegisterNewUser();
|
||||
var store = s.CreateNewStore();
|
||||
s.AddDerivationScheme("BTC");
|
||||
s.GoToStore(store.storeId, StoreNavPages.Checkout);
|
||||
s.GoToStore(store.storeId, StoreNavPages.CheckoutAppearance);
|
||||
s.Driver.FindElement(By.Id("RequiresRefundEmail")).Click();
|
||||
s.Driver.FindElement(By.Name("command")).Click();
|
||||
|
||||
@@ -195,14 +194,15 @@ namespace BTCPayServer.Tests
|
||||
await s.StartAsync();
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser(true);
|
||||
var store = s.CreateNewStore();
|
||||
(string storeName, string storeId) = s.CreateNewStore();
|
||||
s.AddLightningNode();
|
||||
s.GoToStore(store.storeId, StoreNavPages.Payment);
|
||||
s.GoToStore(storeId);
|
||||
s.Driver.FindElement(By.Id("Modify-LightningBTC")).Click();
|
||||
s.Driver.SetCheckbox(By.Id("LightningAmountInSatoshi"), true);
|
||||
s.Driver.FindElement(By.Id("Save")).Click();
|
||||
Assert.Contains("Payment settings successfully updated", s.FindAlertMessage().Text);
|
||||
s.Driver.FindElement(By.Id("save")).Click();
|
||||
Assert.Contains("BTC Lightning settings successfully updated", s.FindAlertMessage().Text);
|
||||
|
||||
var invoiceId = s.CreateInvoice(store.storeName, 10, "USD", "a@g.com");
|
||||
var invoiceId = s.CreateInvoice(storeName, 10, "USD", "a@g.com");
|
||||
s.GoToInvoiceCheckout(invoiceId);
|
||||
Assert.Contains("Sats", s.Driver.FindElement(By.ClassName("payment__currencies_noborder")).Text);
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
await user.SetNetworkFeeMode(NetworkFeeMode.Never);
|
||||
var apps = user.GetController<AppsController>();
|
||||
|
||||
@@ -201,7 +201,7 @@ namespace BTCPayServer.Tests
|
||||
var receiverUser = tester.NewAccount();
|
||||
receiverUser.GrantAccess(true);
|
||||
receiverUser.RegisterDerivationScheme("BTC", receiverAddressType, true);
|
||||
await receiverUser.EnablePayJoin();
|
||||
await receiverUser.ModifyWalletSettings(p => p.PayJoinEnabled = true);
|
||||
var receiverCoin = await receiverUser.ReceiveUTXO(Money.Satoshis(810), network);
|
||||
|
||||
string errorCode = receiverAddressType == senderAddressType ? null : "unavailable|any UTXO available";
|
||||
@@ -290,9 +290,10 @@ namespace BTCPayServer.Tests
|
||||
|
||||
foreach (var format in new []{ScriptPubKeyType.Segwit, ScriptPubKeyType.SegwitP2SH})
|
||||
{
|
||||
var cryptoCode = "BTC";
|
||||
var receiver = s.CreateNewStore();
|
||||
var receiverSeed = s.GenerateWallet("BTC", "", true, true, format);
|
||||
var receiverWalletId = new WalletId(receiver.storeId, "BTC");
|
||||
var receiverSeed = s.GenerateWallet(cryptoCode, "", true, true, format);
|
||||
var receiverWalletId = new WalletId(receiver.storeId, cryptoCode);
|
||||
|
||||
//payjoin is enabled by default.
|
||||
var invoiceId = s.CreateInvoice(receiver.storeName);
|
||||
@@ -302,12 +303,13 @@ namespace BTCPayServer.Tests
|
||||
Assert.Contains($"{PayjoinClient.BIP21EndpointKey}=", bip21);
|
||||
|
||||
s.GoToHome();
|
||||
s.GoToStore(receiver.storeId, StoreNavPages.Payment);
|
||||
s.GoToStore(receiver.storeId);
|
||||
s.Driver.FindElement(By.Id($"Modify{cryptoCode}")).Click();
|
||||
Assert.True(s.Driver.FindElement(By.Id("PayJoinEnabled")).Selected);
|
||||
|
||||
var sender = s.CreateNewStore();
|
||||
var senderSeed = s.GenerateWallet("BTC", "", true, true, format);
|
||||
var senderWalletId = new WalletId(sender.storeId, "BTC");
|
||||
var senderSeed = s.GenerateWallet(cryptoCode, "", true, true, format);
|
||||
var senderWalletId = new WalletId(sender.storeId, cryptoCode);
|
||||
await s.Server.ExplorerNode.GenerateAsync(1);
|
||||
await s.FundStoreWallet(senderWalletId);
|
||||
|
||||
@@ -571,7 +573,7 @@ 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.ModifyPayment(p => p.PayJoinEnabled = true);
|
||||
await bob.ModifyWalletSettings(p => p.PayJoinEnabled = true);
|
||||
var invoice = bob.BitPay.CreateInvoice(
|
||||
new Invoice { Price = 0.1m, Currency = "BTC", FullNotifications = true });
|
||||
var invoiceBIP21 = new BitcoinUrlBuilder(invoice.CryptoInfo.First().PaymentUrls.BIP21,
|
||||
@@ -660,7 +662,7 @@ namespace BTCPayServer.Tests
|
||||
var receiverUser = tester.NewAccount();
|
||||
receiverUser.GrantAccess(true);
|
||||
receiverUser.RegisterDerivationScheme("BTC", ScriptPubKeyType.Segwit, true);
|
||||
await receiverUser.EnablePayJoin();
|
||||
await receiverUser.ModifyWalletSettings(p => p.PayJoinEnabled = true);
|
||||
var receiverCoin = await receiverUser.ReceiveUTXO(Money.Satoshis(810), network);
|
||||
string lastInvoiceId = null;
|
||||
|
||||
@@ -857,7 +859,7 @@ retry:
|
||||
receiverUser.GrantAccess(true);
|
||||
receiverUser.RegisterDerivationScheme("BTC", ScriptPubKeyType.Segwit, true);
|
||||
|
||||
await receiverUser.EnablePayJoin();
|
||||
await receiverUser.ModifyWalletSettings(p => p.PayJoinEnabled = true);
|
||||
// payjoin is enabled, with a segwit wallet, and the keys are available in nbxplorer
|
||||
invoice = receiverUser.BitPay.CreateInvoice(
|
||||
new Invoice() { Price = 0.02m, Currency = "BTC", FullNotifications = true });
|
||||
|
||||
@@ -3,11 +3,11 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Models;
|
||||
using BTCPayServer.Lightning;
|
||||
using BTCPayServer.Lightning.CLightning;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using BTCPayServer.Views.Manage;
|
||||
using BTCPayServer.Views.Server;
|
||||
@@ -16,10 +16,8 @@ using BTCPayServer.Views.Wallets;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using NBitcoin;
|
||||
using BTCPayServer.BIP78.Sender;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Chrome;
|
||||
using OpenQA.Selenium.Support.Extensions;
|
||||
using Xunit;
|
||||
using OpenQA.Selenium.Support.UI;
|
||||
|
||||
@@ -90,6 +88,7 @@ namespace BTCPayServer.Tests
|
||||
GoToRegister();
|
||||
Driver.AssertNoError();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use this ServerUri when trying to browse with selenium
|
||||
/// Because for some reason, the selenium container can't resolve the tests container domain name
|
||||
@@ -139,7 +138,9 @@ namespace BTCPayServer.Tests
|
||||
var name = "Store" + RandomUtils.GetUInt64();
|
||||
Driver.WaitForElement(By.Id("Name")).SendKeys(name);
|
||||
Driver.WaitForElement(By.Id("Create")).Click();
|
||||
Driver.FindElement(By.Id($"Nav-{StoreNavPages.GeneralSettings.ToString()}")).Click();
|
||||
var storeId = Driver.WaitForElement(By.Id("Id")).GetAttribute("value");
|
||||
Driver.FindElement(By.Id($"Nav-{StoreNavPages.PaymentMethods.ToString()}")).Click();
|
||||
if (keepId)
|
||||
StoreId = storeId;
|
||||
return (name, storeId);
|
||||
@@ -215,10 +216,15 @@ namespace BTCPayServer.Tests
|
||||
FindAlertMessage();
|
||||
}
|
||||
|
||||
public void AddLightningNode(string cryptoCode = "BTC", LightningConnectionType? connectionType = null, Action beforeEnable = null, bool test = true)
|
||||
public void AddLightningNode(string cryptoCode = "BTC", LightningConnectionType? connectionType = null, bool test = true)
|
||||
{
|
||||
Driver.FindElement(By.Id($"Modify-Lightning{cryptoCode}")).Click();
|
||||
|
||||
if (Driver.PageSource.Contains("id=\"SetupLightningNodeLink\""))
|
||||
{
|
||||
Driver.FindElement(By.Id($"SetupLightningNodeLink")).Click();
|
||||
}
|
||||
|
||||
var connectionString = connectionType switch
|
||||
{
|
||||
LightningConnectionType.Charge =>
|
||||
@@ -246,12 +252,8 @@ namespace BTCPayServer.Tests
|
||||
Assert.Contains("Connection to the Lightning node successful.", FindAlertMessage().Text);
|
||||
}
|
||||
}
|
||||
beforeEnable?.Invoke();
|
||||
|
||||
Driver.FindElement(By.Id("save")).Click();
|
||||
//soemtimes selenium slows down and misses a beat
|
||||
if(FindAlertMessage().Text == "Connection to the Lightning node successful.")
|
||||
Driver.FindElement(By.Id("save")).Click();
|
||||
Assert.Contains($"{cryptoCode} Lightning node updated.", FindAlertMessage().Text);
|
||||
|
||||
var enabled = Driver.FindElement(By.Id($"{cryptoCode}LightningEnabled"));
|
||||
@@ -320,15 +322,15 @@ namespace BTCPayServer.Tests
|
||||
Driver.FindElement(By.Id("Stores")).Click();
|
||||
}
|
||||
|
||||
public void GoToStore(string storeId, StoreNavPages storeNavPage = StoreNavPages.Index)
|
||||
public void GoToStore(string storeId, StoreNavPages storeNavPage = StoreNavPages.PaymentMethods)
|
||||
{
|
||||
GoToHome();
|
||||
Driver.WaitForAndClick(By.Id("Stores"));
|
||||
Driver.FindElement(By.Id($"update-store-{storeId}")).Click();
|
||||
|
||||
if (storeNavPage != StoreNavPages.Index)
|
||||
if (storeNavPage != StoreNavPages.PaymentMethods)
|
||||
{
|
||||
Driver.FindElement(By.Id(storeNavPage.ToString())).Click();
|
||||
Driver.FindElement(By.Id($"Nav-{storeNavPage.ToString()}")).Click();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -471,7 +471,7 @@ namespace BTCPayServer.Tests
|
||||
s.CreateNewStore();
|
||||
s.AddDerivationScheme();
|
||||
|
||||
s.Driver.FindElement(By.Id("Tokens")).Click();
|
||||
s.Driver.FindElement(By.Id("Nav-Tokens")).Click();
|
||||
s.Driver.FindElement(By.Id("CreateNewToken")).Click();
|
||||
s.Driver.FindElement(By.Id("RequestPairing")).Click();
|
||||
var pairingCode = AssertUrlHasPairingCode(s);
|
||||
@@ -782,9 +782,7 @@ namespace BTCPayServer.Tests
|
||||
server.Done();
|
||||
|
||||
Logs.Tester.LogInformation("Let's see if we can delete store with some webhooks inside");
|
||||
s.GoToStore(storeId);
|
||||
|
||||
s.Driver.ToggleCollapse("danger-zone");
|
||||
s.GoToStore(storeId, StoreNavPages.GeneralSettings);
|
||||
s.Driver.FindElement(By.Id("delete-store")).Click();
|
||||
s.Driver.WaitForElement(By.Id("ConfirmContinue")).Click();
|
||||
s.FindAlertMessage();
|
||||
@@ -1103,7 +1101,6 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.Id("NotificationsDropdownToggle")).Click();
|
||||
s.Driver.FindElement(By.CssSelector("#notificationsForm button")).Click();
|
||||
|
||||
|
||||
var newStore = s.CreateNewStore();
|
||||
s.GenerateWallet("BTC", "", true, true);
|
||||
var newWalletId = new WalletId(newStore.storeId, "BTC");
|
||||
@@ -1144,7 +1141,6 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.Id($"{PayoutState.InProgress}-view")).Click();
|
||||
Assert.Contains(tx.ToString(), s.Driver.PageSource);
|
||||
|
||||
|
||||
//lightning tests
|
||||
newStore = s.CreateNewStore();
|
||||
s.AddLightningNode("BTC");
|
||||
@@ -1158,7 +1154,6 @@ namespace BTCPayServer.Tests
|
||||
var paymentMethodOptions = s.Driver.FindElements(By.CssSelector("#PaymentMethods option"));
|
||||
Assert.Equal(2, paymentMethodOptions.Count);
|
||||
|
||||
|
||||
s.Driver.FindElement(By.Id("Name")).SendKeys("Lightning Test");
|
||||
s.Driver.FindElement(By.Id("Amount")).Clear();
|
||||
s.Driver.FindElement(By.Id("Amount")).SendKeys("0.00001");
|
||||
@@ -1208,11 +1203,9 @@ namespace BTCPayServer.Tests
|
||||
s.GoToStore(newStore.storeId, StoreNavPages.Payouts);
|
||||
s.Driver.FindElement(By.Id($"{new PaymentMethodId("BTC", PaymentTypes.LightningLike )}-view")).Click();
|
||||
|
||||
|
||||
s.Driver.FindElement(By.Id($"{PayoutState.Completed}-view")).Click();
|
||||
if (!s.Driver.PageSource.Contains(bolt))
|
||||
{
|
||||
|
||||
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingPayment}-view")).Click();
|
||||
Assert.Contains(bolt, s.Driver.PageSource);
|
||||
|
||||
@@ -1234,27 +1227,27 @@ namespace BTCPayServer.Tests
|
||||
using var s = SeleniumTester.Create();
|
||||
s.Server.ActivateLightning();
|
||||
await s.StartAsync();
|
||||
|
||||
await s.Server.EnsureChannelsSetup();
|
||||
await BTCPayServer.Lightning.Tests.ConnectChannels.ConnectAll(s.Server.ExplorerNode,
|
||||
var cryptoCode = "BTC";
|
||||
await Lightning.Tests.ConnectChannels.ConnectAll(s.Server.ExplorerNode,
|
||||
new[] { s.Server.MerchantLightningD },
|
||||
new[] { s.Server.MerchantLnd.Client });
|
||||
s.RegisterNewUser(true);
|
||||
var store = s.CreateNewStore();
|
||||
var network = s.Server.NetworkProvider.GetNetwork<BTCPayNetwork>("BTC").NBitcoinNetwork;
|
||||
var network = s.Server.NetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode).NBitcoinNetwork;
|
||||
s.GoToStore(store.storeId);
|
||||
s.AddLightningNode("BTC", LightningConnectionType.CLightning, () =>
|
||||
{
|
||||
//lnurl is false by default
|
||||
s.AddLightningNode(cryptoCode, LightningConnectionType.CLightning);
|
||||
s.Driver.FindElement(By.Id($"Modify-Lightning{cryptoCode}")).Click();
|
||||
// LNURL is false by default
|
||||
Assert.False(s.Driver.FindElement(By.Id("LNURLEnabled")).Selected);
|
||||
//lnurl settings are not expanded when lnurl is disabled
|
||||
// LNURL settings are not expanded when LNURL is disabled
|
||||
Assert.DoesNotContain("show", s.Driver.FindElement(By.Id("LNURLSettings")).GetAttribute("class"));
|
||||
s.Driver.SetCheckbox(By.Id("LNURLEnabled"), true);
|
||||
s.Driver.WaitForAndClick(By.Id("save"));
|
||||
Assert.Contains($"{cryptoCode} Lightning settings successfully updated", s.FindAlertMessage().Text);
|
||||
|
||||
|
||||
});
|
||||
//topup Invoice test
|
||||
var i = s.CreateInvoice(store.storeName, null, "BTC");
|
||||
// Topup Invoice test
|
||||
var i = s.CreateInvoice(store.storeName, null, cryptoCode);
|
||||
s.GoToInvoiceCheckout(i);
|
||||
s.Driver.FindElement(By.Id("copy-tab")).Click();
|
||||
var lnurl = s.Driver.FindElement(By.CssSelector("input.checkoutTextbox")).GetAttribute("value");
|
||||
@@ -1272,7 +1265,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(new LightMoney(0.000002m, LightMoneyUnit.BTC), lnurlResponse2.GetPaymentRequest(network).MinimumAmount);
|
||||
await Assert.ThrowsAnyAsync<LightningRPCException>(async () =>
|
||||
{
|
||||
//the initial bolt was cancelled
|
||||
// Initial bolt was cancelled
|
||||
await s.Server.CustomerLightningD.Pay(lnurlResponse.Pr);
|
||||
});
|
||||
|
||||
@@ -1283,30 +1276,28 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(InvoiceStatusLegacy.Complete, inv.Status);
|
||||
});
|
||||
|
||||
//standard invoice test
|
||||
// Standard invoice test
|
||||
s.GoToHome();
|
||||
i = s.CreateInvoice(store.storeName, 0.0000001m, "BTC");
|
||||
i = s.CreateInvoice(store.storeName, 0.0000001m, cryptoCode);
|
||||
s.GoToInvoiceCheckout(i);
|
||||
s.Driver.FindElement(By.ClassName("payment__currencies")).Click();
|
||||
//bolt 11 is also available for standard invoices
|
||||
// BOLT11 is also available for standard invoices
|
||||
Assert.Equal(2, s.Driver.FindElements(By.CssSelector(".vex.vex-theme-btcpay .vex-content .vexmenu li.vexmenuitem")).Count);
|
||||
s.Driver.FindElement(By.CssSelector(".vex.vex-theme-btcpay .vex-content .vexmenu li.vexmenuitem")).Click();
|
||||
s.Driver.FindElement(By.Id("copy-tab")).Click();
|
||||
lnurl = s.Driver.FindElement(By.CssSelector("input.checkoutTextbox")).GetAttribute("value");
|
||||
parsed = LNURL.LNURL.Parse(lnurl, out tag);
|
||||
fetchedReuqest = Assert.IsType<LNURL.LNURLPayRequest>(await LNURL.LNURL.FetchInformation(parsed, new HttpClient()));
|
||||
fetchedReuqest = Assert.IsType<LNURLPayRequest>(await LNURL.LNURL.FetchInformation(parsed, new HttpClient()));
|
||||
Assert.Equal(0.0000001m, fetchedReuqest.MaxSendable.ToDecimal(LightMoneyUnit.BTC));
|
||||
Assert.Equal(0.0000001m, fetchedReuqest.MinSendable.ToDecimal(LightMoneyUnit.BTC));
|
||||
|
||||
await Assert.ThrowsAsync<HttpRequestException>(async () =>
|
||||
{
|
||||
|
||||
await fetchedReuqest.SendRequest(new LightMoney(0.0000002m, LightMoneyUnit.BTC),
|
||||
network, new HttpClient());
|
||||
});
|
||||
await Assert.ThrowsAsync<HttpRequestException>(async () =>
|
||||
{
|
||||
|
||||
await fetchedReuqest.SendRequest(new LightMoney(0.00000005m, LightMoneyUnit.BTC),
|
||||
network, new HttpClient());
|
||||
});
|
||||
@@ -1315,39 +1306,46 @@ namespace BTCPayServer.Tests
|
||||
network, new HttpClient());
|
||||
lnurlResponse2 = await fetchedReuqest.SendRequest(new LightMoney(0.0000001m, LightMoneyUnit.BTC),
|
||||
network, new HttpClient());
|
||||
//invoice amounts do no change so the paymnet request is not regenerated
|
||||
|
||||
// Invoice amounts do no change so the payment request is not regenerated
|
||||
Assert.Equal(lnurlResponse.Pr,lnurlResponse2.Pr);
|
||||
await s.Server.CustomerLightningD.Pay(lnurlResponse.Pr);
|
||||
Assert.Equal(new LightMoney(0.0000001m, LightMoneyUnit.BTC), lnurlResponse2.GetPaymentRequest(network).MinimumAmount);
|
||||
|
||||
|
||||
|
||||
s.GoToStore(s.StoreId);
|
||||
s.AddLightningNode("BTC", LightningConnectionType.CLightning, () =>
|
||||
{
|
||||
|
||||
s.Driver.FindElement(By.Id($"Modify-Lightning{cryptoCode}")).Click();
|
||||
// LNURL is enabled and settings are expanded
|
||||
Assert.True(s.Driver.FindElement(By.Id("LNURLEnabled")).Selected);
|
||||
Assert.Contains("show", s.Driver.FindElement(By.Id("LNURLSettings")).GetAttribute("class"));
|
||||
s.Driver.SetCheckbox(By.Id("LNURLStandardInvoiceEnabled"), false);
|
||||
}, false);
|
||||
i = s.CreateInvoice(store.storeName, 0.000001m, "BTC" );
|
||||
s.Driver.FindElement(By.Id("save")).Click();
|
||||
Assert.Contains($"{cryptoCode} Lightning settings successfully updated", s.FindAlertMessage().Text);
|
||||
|
||||
i = s.CreateInvoice(store.storeName, 0.000001m, cryptoCode);
|
||||
s.GoToInvoiceCheckout(i);
|
||||
s.Driver.FindElement(By.ClassName("payment__currencies_noborder"));
|
||||
|
||||
s.GoToHome();
|
||||
i = s.CreateInvoice(store.storeName, null, "BTC");
|
||||
i = s.CreateInvoice(store.storeName, null, cryptoCode);
|
||||
s.GoToInvoiceCheckout(i);
|
||||
s.Driver.FindElement(By.ClassName("payment__currencies_noborder"));
|
||||
|
||||
s.GoToStore(s.StoreId);
|
||||
s.AddLightningNode("BTC", LightningConnectionType.CLightning, () =>
|
||||
{
|
||||
|
||||
s.Driver.FindElement(By.Id($"Modify-Lightning{cryptoCode}")).Click();
|
||||
s.Driver.SetCheckbox(By.Id("LNURLBech32Mode"), false);
|
||||
s.Driver.SetCheckbox(By.Id("LNURLStandardInvoiceEnabled"), true);
|
||||
s.Driver.SetCheckbox(By.Id("DisableBolt11PaymentMethod"), true);
|
||||
}, false);
|
||||
s.CreateInvoice(store.storeName, 0.0000001m, "BTC","",null, expectedSeverity: StatusMessageModel.StatusSeverity.Error);
|
||||
s.Driver.FindElement(By.Id("save")).Click();
|
||||
Assert.Contains($"{cryptoCode} Lightning settings successfully updated", s.FindAlertMessage().Text);
|
||||
|
||||
// Ensure the toggles are set correctly
|
||||
s.Driver.FindElement(By.Id($"Modify-Lightning{cryptoCode}")).Click();
|
||||
Assert.True(s.Driver.FindElement(By.Id("DisableBolt11PaymentMethod")).Selected);
|
||||
Assert.True(s.Driver.FindElement(By.Id("LNURLStandardInvoiceEnabled")).Selected);
|
||||
Assert.False(s.Driver.FindElement(By.Id("LNURLBech32Mode")).Selected);
|
||||
s.CreateInvoice(store.storeName, 0.0000001m, cryptoCode,"",null, expectedSeverity: StatusMessageModel.StatusSeverity.Error);
|
||||
|
||||
i = s.CreateInvoice(store.storeName, null, "BTC");
|
||||
i = s.CreateInvoice(store.storeName, null, cryptoCode);
|
||||
s.GoToInvoiceCheckout(i);
|
||||
s.Driver.FindElement(By.ClassName("payment__currencies_noborder"));
|
||||
s.Driver.FindElement(By.Id("copy-tab")).Click();
|
||||
@@ -1355,33 +1353,29 @@ namespace BTCPayServer.Tests
|
||||
Assert.StartsWith("lnurlp", lnurl);
|
||||
LNURL.LNURL.Parse(lnurl, out tag);
|
||||
|
||||
|
||||
s.GoToHome();
|
||||
var newStore = s.CreateNewStore(false);
|
||||
s.AddLightningNode("BTC", LightningConnectionType.LndREST, () =>
|
||||
{
|
||||
s.AddLightningNode(cryptoCode, LightningConnectionType.LndREST, false);
|
||||
s.Driver.FindElement(By.Id($"Modify-Lightning{cryptoCode}")).Click();
|
||||
s.Driver.SetCheckbox(By.Id("LNURLEnabled"), true);
|
||||
s.Driver.SetCheckbox(By.Id("DisableBolt11PaymentMethod"), true);
|
||||
}, false);
|
||||
var invForPP = s.CreateInvoice(newStore.storeName, 0.0000001m, "BTC");
|
||||
s.Driver.FindElement(By.Id("save")).Click();
|
||||
Assert.Contains($"{cryptoCode} Lightning settings successfully updated", s.FindAlertMessage().Text);
|
||||
var invForPP = s.CreateInvoice(newStore.storeName, 0.0000001m, cryptoCode);
|
||||
s.GoToInvoiceCheckout(invForPP);
|
||||
s.Driver.FindElement(By.Id("copy-tab")).Click();
|
||||
lnurl = s.Driver.FindElement(By.CssSelector("input.checkoutTextbox")).GetAttribute("value");
|
||||
parsed = LNURL.LNURL.Parse(lnurl, out tag);
|
||||
|
||||
|
||||
|
||||
//check that pull payment has lightning option
|
||||
// Check that pull payment has lightning option
|
||||
s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
|
||||
s.Driver.FindElement(By.Id("NewPullPayment")).Click();
|
||||
Assert.Equal(new PaymentMethodId("BTC", PaymentTypes.LightningLike),PaymentMethodId.Parse(Assert.Single( s.Driver.FindElement(By.Id("PaymentMethods")).FindElements(By.TagName("option"))).GetAttribute("value")));
|
||||
Assert.Equal(new PaymentMethodId(cryptoCode, PaymentTypes.LightningLike),PaymentMethodId.Parse(Assert.Single(s.Driver.FindElement(By.Id("PaymentMethods")).FindElements(By.TagName("option"))).GetAttribute("value")));
|
||||
s.Driver.FindElement(By.Id("Name")).SendKeys("PP1");
|
||||
s.Driver.FindElement(By.Id("Amount")).Clear();
|
||||
s.Driver.FindElement(By.Id("Amount")).SendKeys("0.0000001");;
|
||||
s.Driver.FindElement(By.Id("Create")).Click();
|
||||
s.Driver.FindElement(By.LinkText("View")).Click();
|
||||
|
||||
|
||||
s.Driver.FindElement(By.Id("Destination")).SendKeys(lnurl);
|
||||
|
||||
var pullPaymentId = s.Driver.Url.Split('/').Last();
|
||||
@@ -1389,7 +1383,6 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.Id("ClaimedAmount")).SendKeys("0.0000001" + Keys.Enter);
|
||||
s.FindAlertMessage();
|
||||
|
||||
|
||||
s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
|
||||
var payouts = s.Driver.FindElements(By.ClassName("pp-payout"));
|
||||
payouts[0].Click();
|
||||
@@ -1412,7 +1405,6 @@ namespace BTCPayServer.Tests
|
||||
var payoutsData = await ctx.Payouts.Where(p => p.PullPaymentDataId == pullPaymentId).ToListAsync();
|
||||
Assert.True(payoutsData.All(p => p.State == PayoutState.Completed));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private static void CanBrowseContent(SeleniumTester s)
|
||||
|
||||
@@ -133,13 +133,22 @@ namespace BTCPayServer.Tests
|
||||
});
|
||||
}
|
||||
|
||||
public async Task ModifyPayment(Action<PaymentViewModel> modify)
|
||||
public async Task ModifyPayment(Action<PaymentMethodsViewModel> modify)
|
||||
{
|
||||
var storeController = GetController<StoresController>();
|
||||
var response = await storeController.Payment();
|
||||
PaymentViewModel payment = (PaymentViewModel)((ViewResult)response).Model;
|
||||
modify(payment);
|
||||
await storeController.Payment(payment);
|
||||
var response = storeController.PaymentMethods();
|
||||
PaymentMethodsViewModel paymentMethods = (PaymentMethodsViewModel)((ViewResult)response).Model;
|
||||
modify(paymentMethods);
|
||||
await storeController.PaymentMethods(paymentMethods);
|
||||
}
|
||||
|
||||
public async Task ModifyWalletSettings(Action<WalletSettingsViewModel> modify)
|
||||
{
|
||||
var storeController = GetController<StoresController>();
|
||||
var response = await storeController.WalletSettings(StoreId, "BTC");
|
||||
WalletSettingsViewModel walletSettings = (WalletSettingsViewModel)((ViewResult)response).Model;
|
||||
modify(walletSettings);
|
||||
storeController.UpdateWalletSettings(walletSettings).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public T GetController<T>(bool setImplicitStore = true) where T : Controller
|
||||
@@ -154,8 +163,8 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
await RegisterAsync();
|
||||
}
|
||||
var store = this.GetController<UserStoresController>();
|
||||
await store.CreateStore(new CreateStoreViewModel() { Name = "Test Store" });
|
||||
var store = GetController<UserStoresController>();
|
||||
await store.CreateStore(new CreateStoreViewModel { Name = "Test Store" });
|
||||
StoreId = store.CreatedStoreId;
|
||||
parent.Stores.Add(StoreId);
|
||||
}
|
||||
@@ -188,19 +197,11 @@ namespace BTCPayServer.Tests
|
||||
return new WalletId(StoreId, cryptoCode);
|
||||
}
|
||||
|
||||
public Task EnablePayJoin()
|
||||
{
|
||||
return ModifyPayment(p => p.PayJoinEnabled = true);
|
||||
}
|
||||
|
||||
public GenerateWalletResponse GenerateWalletResponseV { get; set; }
|
||||
|
||||
public DerivationStrategyBase DerivationScheme
|
||||
{
|
||||
get
|
||||
{
|
||||
return GenerateWalletResponseV.DerivationScheme;
|
||||
}
|
||||
get => GenerateWalletResponseV.DerivationScheme;
|
||||
}
|
||||
|
||||
private async Task RegisterAsync(bool isAdmin = false)
|
||||
@@ -240,15 +241,15 @@ namespace BTCPayServer.Tests
|
||||
|
||||
public bool IsAdmin { get; internal set; }
|
||||
|
||||
public void RegisterLightningNode(string cryptoCode, LightningConnectionType connectionType, bool isMerchant = true, Action<LightningNodeViewModel> setViewModel = null)
|
||||
public void RegisterLightningNode(string cryptoCode, LightningConnectionType connectionType, bool isMerchant = true)
|
||||
{
|
||||
RegisterLightningNodeAsync(cryptoCode, connectionType, isMerchant, setViewModel: setViewModel).GetAwaiter().GetResult();
|
||||
RegisterLightningNodeAsync(cryptoCode, connectionType, isMerchant).GetAwaiter().GetResult();
|
||||
}
|
||||
public Task RegisterLightningNodeAsync(string cryptoCode, bool isMerchant = true, string storeId = null, Action<LightningNodeViewModel> setViewModel = null)
|
||||
public Task RegisterLightningNodeAsync(string cryptoCode, bool isMerchant = true, string storeId = null)
|
||||
{
|
||||
return RegisterLightningNodeAsync(cryptoCode, null, isMerchant, storeId, setViewModel);
|
||||
return RegisterLightningNodeAsync(cryptoCode, null, isMerchant, storeId);
|
||||
}
|
||||
public async Task RegisterLightningNodeAsync(string cryptoCode, LightningConnectionType? connectionType, bool isMerchant = true, string storeId = null, Action<LightningNodeViewModel> setViewModel = null)
|
||||
public async Task RegisterLightningNodeAsync(string cryptoCode, LightningConnectionType? connectionType, bool isMerchant = true, string storeId = null)
|
||||
{
|
||||
var storeController = GetController<StoresController>();
|
||||
|
||||
@@ -256,8 +257,6 @@ namespace BTCPayServer.Tests
|
||||
var nodeType = connectionString == LightningSupportedPaymentMethod.InternalNode ? LightningNodeType.Internal : LightningNodeType.Custom;
|
||||
|
||||
var vm = new LightningNodeViewModel { ConnectionString = connectionString, LightningNodeType = nodeType, SkipPortTest = true };
|
||||
if (setViewModel != null)
|
||||
setViewModel(vm);
|
||||
await storeController.SetupLightningNode(storeId ?? StoreId,
|
||||
vm, "save", cryptoCode);
|
||||
if (storeController.ModelState.ErrorCount != 0)
|
||||
|
||||
@@ -51,8 +51,6 @@ using ExchangeSharp;
|
||||
using Fido2NetLib;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Configuration.Memory;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NBitcoin;
|
||||
@@ -819,11 +817,11 @@ namespace BTCPayServer.Tests
|
||||
|
||||
// Set tolerance to 50%
|
||||
var stores = user.GetController<StoresController>();
|
||||
var response = await stores.Payment();
|
||||
var vm = Assert.IsType<PaymentViewModel>(Assert.IsType<ViewResult>(response).Model);
|
||||
var response = stores.PaymentMethods();
|
||||
var vm = Assert.IsType<PaymentMethodsViewModel>(Assert.IsType<ViewResult>(response).Model);
|
||||
Assert.Equal(0.0, vm.PaymentTolerance);
|
||||
vm.PaymentTolerance = 50.0;
|
||||
Assert.IsType<RedirectToActionResult>(stores.Payment(vm).Result);
|
||||
Assert.IsType<RedirectToActionResult>(stores.PaymentMethods(vm).Result);
|
||||
|
||||
var invoice = user.BitPay.CreateInvoice(
|
||||
new Invoice()
|
||||
@@ -1012,7 +1010,7 @@ namespace BTCPayServer.Tests
|
||||
await user.RegisterDerivationSchemeAsync("BTC");
|
||||
await user.RegisterLightningNodeAsync("BTC", LightningConnectionType.CLightning);
|
||||
await user.SetNetworkFeeMode(NetworkFeeMode.Never);
|
||||
await user.ModifyPayment(p => p.SpeedPolicy = SpeedPolicy.HighSpeed);
|
||||
await user.ModifyWalletSettings(p => p.SpeedPolicy = SpeedPolicy.HighSpeed);
|
||||
var invoice = await user.BitPay.CreateInvoiceAsync(new Invoice(0.0001m, "BTC"));
|
||||
await tester.WaitForEvent<InvoiceNewPaymentDetailsEvent>(async () =>
|
||||
{
|
||||
@@ -1073,7 +1071,7 @@ namespace BTCPayServer.Tests
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess(true);
|
||||
var storeController = user.GetController<StoresController>();
|
||||
var storeResponse = storeController.UpdateStore();
|
||||
var storeResponse = storeController.PaymentMethods();
|
||||
Assert.IsType<ViewResult>(storeResponse);
|
||||
Assert.IsType<ViewResult>(await storeController.SetupLightningNode(user.StoreId, "BTC"));
|
||||
|
||||
@@ -1097,9 +1095,9 @@ namespace BTCPayServer.Tests
|
||||
new LightningNodeViewModel { ConnectionString = tester.MerchantCharge.Client.Uri.AbsoluteUri },
|
||||
"save", "BTC").GetAwaiter().GetResult());
|
||||
|
||||
storeResponse = storeController.UpdateStore();
|
||||
storeResponse = storeController.PaymentMethods();
|
||||
var storeVm =
|
||||
Assert.IsType<StoreViewModel>(Assert
|
||||
Assert.IsType<PaymentMethodsViewModel>(Assert
|
||||
.IsType<ViewResult>(storeResponse).Model);
|
||||
Assert.Single(storeVm.LightningNodes.Where(l => !string.IsNullOrEmpty(l.Address)));
|
||||
}
|
||||
@@ -1213,7 +1211,7 @@ namespace BTCPayServer.Tests
|
||||
var acc = tester.NewAccount();
|
||||
acc.GrantAccess();
|
||||
acc.RegisterDerivationScheme("BTC");
|
||||
await acc.ModifyPayment(p => p.SpeedPolicy = SpeedPolicy.LowSpeed);
|
||||
await acc.ModifyWalletSettings(p => p.SpeedPolicy = SpeedPolicy.LowSpeed);
|
||||
var invoice = acc.BitPay.CreateInvoice(new Invoice
|
||||
{
|
||||
Price = 5.0m,
|
||||
@@ -1628,7 +1626,7 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
await user.SetNetworkFeeMode(NetworkFeeMode.Always);
|
||||
var invoice =
|
||||
@@ -1732,10 +1730,9 @@ namespace BTCPayServer.Tests
|
||||
Assert.False(i.GetPayments(false).First().Accounted);
|
||||
});
|
||||
|
||||
Logs.Tester.LogInformation(
|
||||
$"Let's test if we can RBF a normal payment without adding fees to the invoice");
|
||||
Logs.Tester.LogInformation("Let's test if we can RBF a normal payment without adding fees to the invoice");
|
||||
await user.SetNetworkFeeMode(NetworkFeeMode.MultiplePaymentsOnly);
|
||||
invoice = user.BitPay.CreateInvoice(new Invoice() { Price = 5000.0m, Currency = "USD" }, Facade.Merchant);
|
||||
invoice = user.BitPay.CreateInvoice(new Invoice { Price = 5000.0m, Currency = "USD" }, Facade.Merchant);
|
||||
payment1 = invoice.BtcDue;
|
||||
tx1 = new uint256(tester.ExplorerNode.SendCommand("sendtoaddress", new object[]
|
||||
{
|
||||
@@ -2312,7 +2309,7 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess(true);
|
||||
await user.GrantAccessAsync(true);
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
await user.ModifyPayment(s =>
|
||||
{
|
||||
@@ -2363,18 +2360,18 @@ namespace BTCPayServer.Tests
|
||||
var btcMethod = new PaymentMethodId("BTC", PaymentTypes.BTCLike).ToString();
|
||||
|
||||
// We allow BTC and LN, but not BTC under 5 USD, so only LN should be in the invoice
|
||||
var vm = Assert.IsType<CheckoutExperienceViewModel>(Assert
|
||||
.IsType<ViewResult>(user.GetController<StoresController>().CheckoutExperience()).Model);
|
||||
Assert.Equal(3, vm.PaymentMethodCriteria.Count);
|
||||
var vm = Assert.IsType<CheckoutAppearanceViewModel>(Assert
|
||||
.IsType<ViewResult>(user.GetController<StoresController>().CheckoutAppearance()).Model);
|
||||
Assert.Equal(2, vm.PaymentMethodCriteria.Count);
|
||||
var criteria = Assert.Single(vm.PaymentMethodCriteria.Where(m => m.PaymentMethod == btcMethod.ToString()));
|
||||
Assert.Equal(new PaymentMethodId("BTC", BitcoinPaymentType.Instance).ToString(), criteria.PaymentMethod);
|
||||
criteria.Value = "5 USD";
|
||||
criteria.Type = PaymentMethodCriteriaViewModel.CriteriaType.GreaterThan;
|
||||
Assert.IsType<RedirectToActionResult>(user.GetController<StoresController>().CheckoutExperience(vm)
|
||||
Assert.IsType<RedirectToActionResult>(user.GetController<StoresController>().CheckoutAppearance(vm)
|
||||
.Result);
|
||||
|
||||
var invoice = user.BitPay.CreateInvoice(
|
||||
new Invoice()
|
||||
new Invoice
|
||||
{
|
||||
Price = 4.5m,
|
||||
Currency = "USD",
|
||||
@@ -2390,8 +2387,8 @@ namespace BTCPayServer.Tests
|
||||
// Let's replicate https://github.com/btcpayserver/btcpayserver/issues/2963
|
||||
// We allow BTC for more than 5 USD, and LN for less than 150. The default is LN, so the default
|
||||
// payment method should be LN.
|
||||
vm = Assert.IsType<CheckoutExperienceViewModel>(Assert
|
||||
.IsType<ViewResult>(user.GetController<StoresController>().CheckoutExperience()).Model);
|
||||
vm = Assert.IsType<CheckoutAppearanceViewModel>(Assert
|
||||
.IsType<ViewResult>(user.GetController<StoresController>().CheckoutAppearance()).Model);
|
||||
vm.DefaultPaymentMethod = lnMethod;
|
||||
criteria = vm.PaymentMethodCriteria.First();
|
||||
criteria.Value = "150 USD";
|
||||
@@ -2399,7 +2396,7 @@ namespace BTCPayServer.Tests
|
||||
criteria = vm.PaymentMethodCriteria.Skip(1).First();
|
||||
criteria.Value = "5 USD";
|
||||
criteria.Type = PaymentMethodCriteriaViewModel.CriteriaType.GreaterThan;
|
||||
Assert.IsType<RedirectToActionResult>(user.GetController<StoresController>().CheckoutExperience(vm)
|
||||
Assert.IsType<RedirectToActionResult>(user.GetController<StoresController>().CheckoutAppearance(vm)
|
||||
.Result);
|
||||
invoice = user.BitPay.CreateInvoice(
|
||||
new Invoice()
|
||||
@@ -2416,7 +2413,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
// If we change store's default, it should change the checkout's default
|
||||
vm.DefaultPaymentMethod = btcMethod;
|
||||
Assert.IsType<RedirectToActionResult>(user.GetController<StoresController>().CheckoutExperience(vm)
|
||||
Assert.IsType<RedirectToActionResult>(user.GetController<StoresController>().CheckoutAppearance(vm)
|
||||
.Result);
|
||||
checkout = (await user.GetController<InvoiceController>().Checkout(invoice.Id)).AssertViewModel<PaymentModel>();
|
||||
Assert.Equal(btcMethod, checkout.PaymentMethodId);
|
||||
@@ -2433,12 +2430,13 @@ namespace BTCPayServer.Tests
|
||||
await tester.StartAsync();
|
||||
await tester.EnsureChannelsSetup();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess(true);
|
||||
user.RegisterDerivationScheme("BTC", ScriptPubKeyType.Segwit);
|
||||
user.RegisterLightningNode("BTC", LightningConnectionType.CLightning);
|
||||
var cryptoCode = "BTC";
|
||||
await user.GrantAccessAsync(true);
|
||||
user.RegisterDerivationScheme(cryptoCode, ScriptPubKeyType.Segwit);
|
||||
user.RegisterLightningNode(cryptoCode, LightningConnectionType.CLightning);
|
||||
|
||||
var invoice = user.BitPay.CreateInvoice(
|
||||
new Invoice()
|
||||
new Invoice
|
||||
{
|
||||
Price = 5.5m,
|
||||
Currency = "USD",
|
||||
@@ -2456,12 +2454,12 @@ namespace BTCPayServer.Tests
|
||||
Assert.DoesNotContain("&lightning=", paymentMethodFirst.InvoiceBitcoinUrlQR);
|
||||
|
||||
// enable unified QR code in settings
|
||||
var vm = Assert.IsType<PaymentViewModel>(Assert
|
||||
.IsType<ViewResult>(await user.GetController<StoresController>().Payment()).Model
|
||||
var vm = Assert.IsType<LightningSettingsViewModel>(Assert
|
||||
.IsType<ViewResult>(await user.GetController<StoresController>().LightningSettings(user.StoreId, cryptoCode)).Model
|
||||
);
|
||||
vm.OnChainWithLnInvoiceFallback = true;
|
||||
Assert.IsType<RedirectToActionResult>(
|
||||
user.GetController<StoresController>().Payment(vm).Result
|
||||
user.GetController<StoresController>().LightningSettings(vm).Result
|
||||
);
|
||||
|
||||
// validate that QR code now has both onchain and offchain payment urls
|
||||
@@ -2494,14 +2492,15 @@ namespace BTCPayServer.Tests
|
||||
await tester.StartAsync();
|
||||
await tester.EnsureChannelsSetup();
|
||||
var user = tester.NewAccount();
|
||||
var cryptoCode = "BTC";
|
||||
user.GrantAccess(true);
|
||||
user.RegisterLightningNode("BTC", LightningConnectionType.Charge);
|
||||
var vm = user.GetController<StoresController>().CheckoutExperience().AssertViewModel<CheckoutExperienceViewModel>();
|
||||
user.RegisterLightningNode(cryptoCode, LightningConnectionType.Charge);
|
||||
var vm = user.GetController<StoresController>().CheckoutAppearance().AssertViewModel<CheckoutAppearanceViewModel>();
|
||||
var criteria = Assert.Single(vm.PaymentMethodCriteria);
|
||||
Assert.Equal(new PaymentMethodId("BTC", LightningPaymentType.Instance).ToString(), criteria.PaymentMethod);
|
||||
Assert.Equal(new PaymentMethodId(cryptoCode, LightningPaymentType.Instance).ToString(), criteria.PaymentMethod);
|
||||
criteria.Value = "2 USD";
|
||||
criteria.Type = PaymentMethodCriteriaViewModel.CriteriaType.LessThan;
|
||||
Assert.IsType<RedirectToActionResult>(user.GetController<StoresController>().CheckoutExperience(vm)
|
||||
Assert.IsType<RedirectToActionResult>(user.GetController<StoresController>().CheckoutAppearance(vm)
|
||||
.Result);
|
||||
|
||||
var invoice = user.BitPay.CreateInvoice(
|
||||
@@ -2515,15 +2514,15 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(PaymentTypes.LightningLike.ToString(), invoice.CryptoInfo[0].PaymentType);
|
||||
|
||||
// Activating LNUrl, we should still have only 1 payment criteria that can be set.
|
||||
user.RegisterLightningNode("BTC", LightningConnectionType.Charge, setViewModel: vm =>
|
||||
{
|
||||
vm.LNURLEnabled = true;
|
||||
vm.LNURLStandardInvoiceEnabled = true;
|
||||
});
|
||||
vm = user.GetController<StoresController>().CheckoutExperience().AssertViewModel<CheckoutExperienceViewModel>();
|
||||
user.RegisterLightningNode(cryptoCode, LightningConnectionType.Charge);
|
||||
var lnSettingsVm = await user.GetController<StoresController>().LightningSettings(user.StoreId, cryptoCode).AssertViewModelAsync<LightningSettingsViewModel>();
|
||||
lnSettingsVm.LNURLEnabled = true;
|
||||
lnSettingsVm.LNURLStandardInvoiceEnabled = true;
|
||||
Assert.IsType<RedirectToActionResult>(user.GetController<StoresController>().LightningSettings(lnSettingsVm).Result);
|
||||
vm = user.GetController<StoresController>().CheckoutAppearance().AssertViewModel<CheckoutAppearanceViewModel>();
|
||||
criteria = Assert.Single(vm.PaymentMethodCriteria);
|
||||
Assert.Equal(new PaymentMethodId("BTC", LightningPaymentType.Instance).ToString(), criteria.PaymentMethod);
|
||||
Assert.IsType<RedirectToActionResult>(user.GetController<StoresController>().CheckoutExperience(vm).Result);
|
||||
Assert.Equal(new PaymentMethodId(cryptoCode, LightningPaymentType.Instance).ToString(), criteria.PaymentMethod);
|
||||
Assert.IsType<RedirectToActionResult>(user.GetController<StoresController>().CheckoutAppearance(vm).Result);
|
||||
|
||||
// However, creating an invoice should show LNURL
|
||||
invoice = user.BitPay.CreateInvoice(
|
||||
@@ -2733,11 +2732,11 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
await user.SetNetworkFeeMode(NetworkFeeMode.Always);
|
||||
var invoice = user.BitPay.CreateInvoice(
|
||||
new Invoice()
|
||||
new Invoice
|
||||
{
|
||||
Price = 10,
|
||||
Currency = "USD",
|
||||
@@ -2813,7 +2812,7 @@ namespace BTCPayServer.Tests
|
||||
Logs.Tester.LogInformation($"Trying with {nameof(networkFeeMode)}={networkFeeMode}");
|
||||
await user.SetNetworkFeeMode(networkFeeMode);
|
||||
var invoice = user.BitPay.CreateInvoice(
|
||||
new Invoice()
|
||||
new Invoice
|
||||
{
|
||||
Price = 10,
|
||||
Currency = "USD",
|
||||
@@ -2896,11 +2895,11 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
await user.SetNetworkFeeMode(NetworkFeeMode.Always);
|
||||
var invoice = user.BitPay.CreateInvoice(
|
||||
new Invoice()
|
||||
new Invoice
|
||||
{
|
||||
Price = 500,
|
||||
Currency = "USD",
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
StoreId = store.Id,
|
||||
StoreName = store.StoreName,
|
||||
StoreLink = Url.Action(nameof(StoresController.UpdateStore), "Stores", new { storeId = store.Id }),
|
||||
StoreLink = Url.Action(nameof(StoresController.PaymentMethods), "Stores", new { storeId = store.Id }),
|
||||
PaymentRequestLink = Url.Action(nameof(PaymentRequestController.ViewPaymentRequest), "PaymentRequest", new { id = invoice.Metadata.PaymentRequestId }),
|
||||
Id = invoice.Id,
|
||||
State = invoiceState.ToString(),
|
||||
@@ -847,7 +847,7 @@ namespace BTCPayServer.Controllers
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Html = $"To create an invoice, you need to <a href='{Url.Action(nameof(StoresController.UpdateStore), "Stores", new { storeId = store.Id })}' class='alert-link'>set up your wallet</a> first",
|
||||
Html = $"To create an invoice, you need to <a href='{Url.Action(nameof(StoresController.PaymentMethods), "Stores", new { storeId = store.Id })}' class='alert-link'>set up your wallet</a> first",
|
||||
AllowDismiss = false
|
||||
});
|
||||
return View(model);
|
||||
|
||||
@@ -60,8 +60,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
paymentMethod = new LightningSupportedPaymentMethod
|
||||
{
|
||||
CryptoCode = paymentMethodId.CryptoCode,
|
||||
DisableBOLT11PaymentOption = vm.LNURLEnabled && vm.LNURLStandardInvoiceEnabled && vm.DisableBolt11PaymentMethod
|
||||
CryptoCode = paymentMethodId.CryptoCode
|
||||
};
|
||||
paymentMethod.SetInternalNode();
|
||||
}
|
||||
@@ -90,11 +89,9 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
paymentMethod = new LightningSupportedPaymentMethod
|
||||
{
|
||||
CryptoCode = paymentMethodId.CryptoCode,
|
||||
DisableBOLT11PaymentOption = vm.LNURLEnabled && vm.LNURLStandardInvoiceEnabled && vm.DisableBolt11PaymentMethod
|
||||
CryptoCode = paymentMethodId.CryptoCode
|
||||
};
|
||||
paymentMethod.SetLightningUrl(connectionString);
|
||||
|
||||
}
|
||||
|
||||
switch (command)
|
||||
@@ -104,23 +101,12 @@ namespace BTCPayServer.Controllers
|
||||
storeBlob.Hints.Lightning = false;
|
||||
|
||||
var lnurl = new PaymentMethodId(vm.CryptoCode, PaymentTypes.LNURLPay);
|
||||
storeBlob.SetExcluded(lnurl, !vm.LNURLEnabled);
|
||||
store.SetSupportedPaymentMethod(new LNURLPaySupportedPaymentMethod()
|
||||
{
|
||||
CryptoCode = vm.CryptoCode,
|
||||
EnableForStandardInvoices = vm.LNURLStandardInvoiceEnabled,
|
||||
UseBech32Scheme = vm.LNURLBech32Mode,
|
||||
LUD12Enabled = vm.LUD12Enabled
|
||||
});
|
||||
|
||||
store.SetStoreBlob(storeBlob);
|
||||
store.SetSupportedPaymentMethod(paymentMethodId, paymentMethod);
|
||||
|
||||
|
||||
|
||||
await _Repo.UpdateStore(store);
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"{network.CryptoCode} Lightning node updated.";
|
||||
return RedirectToAction(nameof(UpdateStore), new { storeId });
|
||||
return RedirectToAction(nameof(PaymentMethods), new { storeId });
|
||||
|
||||
case "test":
|
||||
var handler = _ServiceProvider.GetRequiredService<LightningLikePaymentHandler>();
|
||||
@@ -146,6 +132,116 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{storeId}/lightning/{cryptoCode}/settings")]
|
||||
public async Task<IActionResult> LightningSettings(string storeId, string cryptoCode)
|
||||
{
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
return NotFound();
|
||||
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
var vm = new LightningSettingsViewModel
|
||||
{
|
||||
CryptoCode = cryptoCode,
|
||||
StoreId = storeId,
|
||||
LightningDescriptionTemplate = storeBlob.LightningDescriptionTemplate,
|
||||
LightningAmountInSatoshi = storeBlob.LightningAmountInSatoshi,
|
||||
LightningPrivateRouteHints = storeBlob.LightningPrivateRouteHints,
|
||||
OnChainWithLnInvoiceFallback = storeBlob.OnChainWithLnInvoiceFallback
|
||||
};
|
||||
await SetExistingValues(store, vm);
|
||||
|
||||
var lightning = GetExistingLightningSupportedPaymentMethod(vm.CryptoCode, store);
|
||||
var lnSet = lightning != null;
|
||||
if (lnSet)
|
||||
{
|
||||
vm.DisableBolt11PaymentMethod = lightning.DisableBOLT11PaymentOption;
|
||||
}
|
||||
|
||||
var lnurl = GetExistingLNURLSupportedPaymentMethod(vm.CryptoCode, store);
|
||||
if (lnurl != null)
|
||||
{
|
||||
vm.LNURLEnabled = !store.GetStoreBlob().GetExcludedPaymentMethods().Match(lnurl.PaymentId);
|
||||
vm.LNURLBech32Mode = lnurl.UseBech32Scheme;
|
||||
vm.LNURLStandardInvoiceEnabled = lnurl.EnableForStandardInvoices;
|
||||
vm.LUD12Enabled = lnurl.LUD12Enabled;
|
||||
vm.DisableBolt11PaymentMethod =
|
||||
vm.LNURLEnabled && vm.LNURLStandardInvoiceEnabled && vm.DisableBolt11PaymentMethod;
|
||||
}
|
||||
else
|
||||
{
|
||||
//disable by default for now
|
||||
//vm.LNURLEnabled = !lnSet;
|
||||
vm.DisableBolt11PaymentMethod = false;
|
||||
}
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
[HttpPost("{storeId}/lightning/{cryptoCode}/settings")]
|
||||
public async Task<IActionResult> LightningSettings(LightningSettingsViewModel vm)
|
||||
{
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
return NotFound();
|
||||
|
||||
var network = vm.CryptoCode == null ? null : _ExplorerProvider.GetNetwork(vm.CryptoCode);
|
||||
if (network == null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(vm.CryptoCode), "Invalid network");
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
var needUpdate = false;
|
||||
var blob = store.GetStoreBlob();
|
||||
blob.LightningDescriptionTemplate = vm.LightningDescriptionTemplate ?? string.Empty;
|
||||
blob.LightningAmountInSatoshi = vm.LightningAmountInSatoshi;
|
||||
blob.LightningPrivateRouteHints = vm.LightningPrivateRouteHints;
|
||||
blob.OnChainWithLnInvoiceFallback = vm.OnChainWithLnInvoiceFallback;
|
||||
var disableBolt11PaymentMethod =
|
||||
vm.LNURLEnabled && vm.LNURLStandardInvoiceEnabled && vm.DisableBolt11PaymentMethod;
|
||||
var lnurlId = new PaymentMethodId(vm.CryptoCode, PaymentTypes.LNURLPay);
|
||||
blob.SetExcluded(lnurlId, !vm.LNURLEnabled);
|
||||
var lightning = GetExistingLightningSupportedPaymentMethod(vm.CryptoCode, store);
|
||||
if (lightning.DisableBOLT11PaymentOption != disableBolt11PaymentMethod)
|
||||
{
|
||||
needUpdate = true;
|
||||
lightning.DisableBOLT11PaymentOption = disableBolt11PaymentMethod;
|
||||
store.SetSupportedPaymentMethod(lightning);
|
||||
}
|
||||
|
||||
var lnurl = GetExistingLNURLSupportedPaymentMethod(vm.CryptoCode, store);
|
||||
if (lnurl != null && (
|
||||
lnurl.EnableForStandardInvoices != vm.LNURLStandardInvoiceEnabled ||
|
||||
lnurl.UseBech32Scheme != vm.LNURLBech32Mode ||
|
||||
lnurl.LUD12Enabled != vm.LUD12Enabled))
|
||||
{
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
store.SetSupportedPaymentMethod(new LNURLPaySupportedPaymentMethod
|
||||
{
|
||||
CryptoCode = vm.CryptoCode,
|
||||
EnableForStandardInvoices = vm.LNURLStandardInvoiceEnabled,
|
||||
UseBech32Scheme = vm.LNURLBech32Mode,
|
||||
LUD12Enabled = vm.LUD12Enabled
|
||||
});
|
||||
|
||||
if (store.SetStoreBlob(blob))
|
||||
{
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
if (needUpdate)
|
||||
{
|
||||
await _Repo.UpdateStore(store);
|
||||
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"{network.CryptoCode} Lightning settings successfully updated";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(PaymentMethods), new { vm.StoreId });
|
||||
}
|
||||
|
||||
[HttpPost("{storeId}/lightning/{cryptoCode}/status")]
|
||||
public async Task<IActionResult> SetLightningNodeEnabled(string storeId, string cryptoCode, bool enabled)
|
||||
{
|
||||
@@ -168,7 +264,7 @@ namespace BTCPayServer.Controllers
|
||||
await _Repo.UpdateStore(store);
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"{network.CryptoCode} Lightning payments are now {(enabled ? "enabled" : "disabled")} for this store.";
|
||||
|
||||
return RedirectToAction(nameof(UpdateStore), new { storeId });
|
||||
return RedirectToAction(nameof(PaymentMethods), new { storeId });
|
||||
}
|
||||
|
||||
private async Task<bool> CanUseInternalLightning()
|
||||
@@ -186,28 +282,11 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
vm.LightningNodeType = lightning.IsInternalNode ? LightningNodeType.Internal : LightningNodeType.Custom;
|
||||
vm.ConnectionString = lightning.GetDisplayableConnectionString();
|
||||
|
||||
vm.DisableBolt11PaymentMethod = lightning.DisableBOLT11PaymentOption;
|
||||
}
|
||||
else
|
||||
{
|
||||
vm.LightningNodeType = vm.CanUseInternalNode ? LightningNodeType.Internal : LightningNodeType.Custom;
|
||||
}
|
||||
|
||||
var lnurl = GetExistingLNURLSupportedPaymentMethod(vm.CryptoCode, store);
|
||||
if (lnurl != null)
|
||||
{
|
||||
vm.LNURLEnabled = !store.GetStoreBlob().GetExcludedPaymentMethods().Match(lnurl.PaymentId);
|
||||
vm.LNURLBech32Mode = lnurl.UseBech32Scheme;
|
||||
vm.LNURLStandardInvoiceEnabled = lnurl.EnableForStandardInvoices;
|
||||
vm.LUD12Enabled = lnurl.LUD12Enabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
//disable by default for now
|
||||
//vm.LNURLEnabled = !lnSet;
|
||||
vm.DisableBolt11PaymentMethod = false;
|
||||
}
|
||||
}
|
||||
|
||||
private LightningSupportedPaymentMethod GetExistingLightningSupportedPaymentMethod(string cryptoCode, StoreData store)
|
||||
|
||||
@@ -177,7 +177,7 @@ namespace BTCPayServer.Controllers
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"Wallet settings for {network.CryptoCode} have been updated.";
|
||||
|
||||
// This is success case when derivation scheme is added to the store
|
||||
return RedirectToAction(nameof(UpdateStore), new { storeId = vm.StoreId });
|
||||
return RedirectToAction(nameof(PaymentMethods), new { storeId = vm.StoreId });
|
||||
}
|
||||
return ConfirmAddresses(vm, strategy);
|
||||
}
|
||||
@@ -363,35 +363,50 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"Wallet settings for {network.CryptoCode} have been updated.";
|
||||
|
||||
return RedirectToAction(nameof(UpdateStore), new { storeId });
|
||||
return RedirectToAction(nameof(PaymentMethods), new { storeId });
|
||||
}
|
||||
|
||||
[HttpGet("{storeId}/onchain/{cryptoCode}/modify")]
|
||||
public async Task<IActionResult> ModifyWallet(WalletSetupViewModel vm)
|
||||
[HttpGet("{storeId}/onchain/{cryptoCode}/settings")]
|
||||
public async Task<IActionResult> WalletSettings(string storeId, string cryptoCode)
|
||||
{
|
||||
var checkResult = IsAvailable(vm.CryptoCode, out var store, out var network);
|
||||
var checkResult = IsAvailable(cryptoCode, out var store, out var network);
|
||||
if (checkResult != null)
|
||||
{
|
||||
return checkResult;
|
||||
}
|
||||
|
||||
var derivation = GetExistingDerivationStrategy(vm.CryptoCode, store);
|
||||
var derivation = GetExistingDerivationStrategy(cryptoCode, store);
|
||||
if (derivation == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var (hotWallet, rpcImport) = await CanUseHotWallet();
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
(bool canUseHotWallet, bool rpcImport) = await CanUseHotWallet();
|
||||
|
||||
vm.CanUseHotWallet = hotWallet;
|
||||
vm.CanUseRPCImport = rpcImport;
|
||||
vm.Network = network;
|
||||
vm.Source = derivation.Source;
|
||||
vm.RootFingerprint = derivation.GetSigningAccountKeySettings().RootFingerprint.ToString();
|
||||
vm.DerivationScheme = derivation.AccountDerivation.ToString();
|
||||
vm.KeyPath = derivation.GetSigningAccountKeySettings().AccountKeyPath?.ToString();
|
||||
vm.Config = ProtectString(derivation.ToJson());
|
||||
vm.IsHotWallet = derivation.IsHotWallet;
|
||||
var vm = new WalletSettingsViewModel
|
||||
{
|
||||
StoreId = storeId,
|
||||
CryptoCode = cryptoCode,
|
||||
Network = network,
|
||||
Source = derivation.Source,
|
||||
RootFingerprint = derivation.GetSigningAccountKeySettings().RootFingerprint.ToString(),
|
||||
DerivationScheme = derivation.AccountDerivation.ToString(),
|
||||
KeyPath = derivation.GetSigningAccountKeySettings().AccountKeyPath?.ToString(),
|
||||
Config = ProtectString(derivation.ToJson()),
|
||||
IsHotWallet = derivation.IsHotWallet,
|
||||
PayJoinEnabled = storeBlob.PayJoinEnabled,
|
||||
MonitoringExpiration = (int)storeBlob.MonitoringExpiration.TotalMinutes,
|
||||
SpeedPolicy = store.SpeedPolicy,
|
||||
ShowRecommendedFee = storeBlob.ShowRecommendedFee,
|
||||
RecommendedFeeBlockTarget = storeBlob.RecommendedFeeBlockTarget,
|
||||
CanUseHotWallet = canUseHotWallet,
|
||||
CanUseRPCImport = rpcImport,
|
||||
CanUsePayJoin = canUseHotWallet && store
|
||||
.GetSupportedPaymentMethods(_NetworkProvider)
|
||||
.OfType<DerivationSchemeSettings>()
|
||||
.Any(settings => settings.Network.SupportPayJoin && settings.IsHotWallet)
|
||||
};
|
||||
|
||||
ViewData["ReplaceDescription"] = WalletReplaceWarning(derivation.IsHotWallet);
|
||||
ViewData["RemoveDescription"] = WalletRemoveWarning(derivation.IsHotWallet, network.CryptoCode);
|
||||
@@ -399,6 +414,62 @@ namespace BTCPayServer.Controllers
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
[HttpPost("{storeId}/onchain/{cryptoCode}/settings")]
|
||||
public async Task<IActionResult> UpdateWalletSettings(WalletSettingsViewModel vm)
|
||||
{
|
||||
bool needUpdate = false;
|
||||
if (CurrentStore.SpeedPolicy != vm.SpeedPolicy)
|
||||
{
|
||||
needUpdate = true;
|
||||
CurrentStore.SpeedPolicy = vm.SpeedPolicy;
|
||||
}
|
||||
|
||||
var blob = CurrentStore.GetStoreBlob();
|
||||
|
||||
blob.MonitoringExpiration = TimeSpan.FromMinutes(vm.MonitoringExpiration);
|
||||
blob.ShowRecommendedFee = vm.ShowRecommendedFee;
|
||||
blob.RecommendedFeeBlockTarget = vm.RecommendedFeeBlockTarget;
|
||||
|
||||
var payjoinChanged = blob.PayJoinEnabled != vm.PayJoinEnabled;
|
||||
blob.PayJoinEnabled = vm.PayJoinEnabled;
|
||||
if (CurrentStore.SetStoreBlob(blob))
|
||||
{
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
if (needUpdate)
|
||||
{
|
||||
await _Repo.UpdateStore(CurrentStore);
|
||||
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Payment settings successfully updated";
|
||||
|
||||
if (payjoinChanged && blob.PayJoinEnabled)
|
||||
{
|
||||
var problematicPayjoinEnabledMethods = CurrentStore.GetSupportedPaymentMethods(_NetworkProvider)
|
||||
.OfType<DerivationSchemeSettings>()
|
||||
.Where(settings => settings.Network.SupportPayJoin && !settings.IsHotWallet)
|
||||
.Select(settings => settings.PaymentId.CryptoCode)
|
||||
.ToArray();
|
||||
|
||||
if (problematicPayjoinEnabledMethods.Any())
|
||||
{
|
||||
TempData.Remove(WellKnownTempData.SuccessMessage);
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Warning,
|
||||
Html = $"The payment settings were updated successfully. However, payjoin will not work for {string.Join(", ", problematicPayjoinEnabledMethods)} until you configure them to be a <a href='https://docs.btcpayserver.org/HotWallet/' class='alert-link' target='_blank'>hot wallet</a>."
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(WalletSettings), new
|
||||
{
|
||||
storeId = vm.StoreId,
|
||||
cryptoCode = vm.CryptoCode
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("{storeId}/onchain/{cryptoCode}/replace")]
|
||||
public ActionResult ReplaceWallet(string storeId, string cryptoCode)
|
||||
{
|
||||
@@ -488,7 +559,7 @@ namespace BTCPayServer.Controllers
|
||||
TempData[WellKnownTempData.SuccessMessage] =
|
||||
$"{network.CryptoCode} on-chain payments are now {(enabled ? "enabled" : "disabled")} for this store.";
|
||||
|
||||
return RedirectToAction(nameof(UpdateStore), new { storeId });
|
||||
return RedirectToAction(nameof(PaymentMethods), new { storeId });
|
||||
}
|
||||
|
||||
[HttpPost("{storeId}/onchain/{cryptoCode}/delete")]
|
||||
@@ -515,7 +586,7 @@ namespace BTCPayServer.Controllers
|
||||
TempData[WellKnownTempData.SuccessMessage] =
|
||||
$"On-Chain payment for {network.CryptoCode} has been removed.";
|
||||
|
||||
return RedirectToAction(nameof(UpdateStore), new { storeId });
|
||||
return RedirectToAction(nameof(PaymentMethods), new { storeId });
|
||||
}
|
||||
|
||||
private IActionResult ConfirmAddresses(WalletSetupViewModel vm, DerivationSchemeSettings strategy)
|
||||
|
||||
@@ -367,10 +367,10 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
|
||||
[HttpGet("{storeId}/checkout")]
|
||||
public IActionResult CheckoutExperience()
|
||||
public IActionResult CheckoutAppearance()
|
||||
{
|
||||
var storeBlob = CurrentStore.GetStoreBlob();
|
||||
var vm = new CheckoutExperienceViewModel();
|
||||
var vm = new CheckoutAppearanceViewModel();
|
||||
SetCryptoCurrencies(vm, CurrentStore);
|
||||
vm.PaymentMethodCriteria = CurrentStore.GetSupportedPaymentMethods(_NetworkProvider)
|
||||
.Where(s => !storeBlob.GetExcludedPaymentMethods().Match(s.PaymentId))
|
||||
@@ -414,14 +414,14 @@ namespace BTCPayServer.Controllers
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
void SetCryptoCurrencies(CheckoutExperienceViewModel vm, Data.StoreData storeData)
|
||||
void SetCryptoCurrencies(CheckoutAppearanceViewModel vm, Data.StoreData storeData)
|
||||
{
|
||||
var enabled = storeData.GetEnabledPaymentIds(_NetworkProvider);
|
||||
var defaultPaymentId = storeData.GetDefaultPaymentId();
|
||||
var defaultChoice = defaultPaymentId is PaymentMethodId ? defaultPaymentId.FindNearest(enabled) : null;
|
||||
var choices = enabled
|
||||
.Select(o =>
|
||||
new CheckoutExperienceViewModel.Format()
|
||||
new CheckoutAppearanceViewModel.Format()
|
||||
{
|
||||
Name = o.ToPrettyString(),
|
||||
Value = o.ToString(),
|
||||
@@ -434,7 +434,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
[HttpPost]
|
||||
[Route("{storeId}/checkout")]
|
||||
public async Task<IActionResult> CheckoutExperience(CheckoutExperienceViewModel model)
|
||||
public async Task<IActionResult> CheckoutAppearance(CheckoutAppearanceViewModel model)
|
||||
{
|
||||
bool needUpdate = false;
|
||||
var blob = CurrentStore.GetStoreBlob();
|
||||
@@ -514,7 +514,7 @@ namespace BTCPayServer.Controllers
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Store successfully updated";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(CheckoutExperience), new
|
||||
return RedirectToAction(nameof(CheckoutAppearance), new
|
||||
{
|
||||
storeId = CurrentStore.Id
|
||||
});
|
||||
@@ -579,21 +579,23 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
|
||||
[HttpGet("{storeId}")]
|
||||
public IActionResult UpdateStore()
|
||||
public IActionResult PaymentMethods()
|
||||
{
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
return NotFound();
|
||||
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
var vm = new StoreViewModel
|
||||
var vm = new PaymentMethodsViewModel
|
||||
{
|
||||
Id = store.Id,
|
||||
CanDelete = _Repo.CanDeleteStores(),
|
||||
StoreName = store.StoreName,
|
||||
StoreWebsite = store.StoreWebsite,
|
||||
HintWallet = storeBlob.Hints.Wallet,
|
||||
HintLightning = storeBlob.Hints.Lightning
|
||||
HintLightning = storeBlob.Hints.Lightning,
|
||||
NetworkFeeMode = storeBlob.NetworkFeeMode,
|
||||
AnyoneCanCreateInvoice = storeBlob.AnyoneCanInvoice,
|
||||
PaymentTolerance = storeBlob.PaymentTolerance,
|
||||
InvoiceExpiration = (int)storeBlob.InvoiceExpiration.TotalMinutes,
|
||||
DefaultCurrency = storeBlob.DefaultCurrency
|
||||
};
|
||||
|
||||
AddPaymentMethods(store, storeBlob,
|
||||
@@ -606,108 +608,16 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
|
||||
[HttpPost("{storeId}")]
|
||||
public async Task<IActionResult> UpdateStore(StoreViewModel model, string command = null)
|
||||
public async Task<IActionResult> PaymentMethods(PaymentMethodsViewModel 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<IActionResult> 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.PayJoinEnabled = storeBlob.PayJoinEnabled;
|
||||
vm.LightningAmountInSatoshi = storeBlob.LightningAmountInSatoshi;
|
||||
vm.LightningPrivateRouteHints = storeBlob.LightningPrivateRouteHints;
|
||||
vm.OnChainWithLnInvoiceFallback = storeBlob.OnChainWithLnInvoiceFallback;
|
||||
vm.ShowRecommendedFee = storeBlob.ShowRecommendedFee;
|
||||
vm.RecommendedFeeBlockTarget = storeBlob.RecommendedFeeBlockTarget;
|
||||
vm.IsOnchainSetup = vm.DerivationSchemes.Any(scheme => !string.IsNullOrWhiteSpace(scheme.Value));
|
||||
vm.IsLightningSetup = vm.LightningNodes.Any(scheme => !string.IsNullOrWhiteSpace(scheme.Address));
|
||||
|
||||
(bool canUseHotWallet, _) = await CanUseHotWallet();
|
||||
vm.CanUsePayJoin = canUseHotWallet && store
|
||||
.GetSupportedPaymentMethods(_NetworkProvider)
|
||||
.OfType<DerivationSchemeSettings>()
|
||||
.Any(settings => settings.Network.SupportPayJoin && settings.IsHotWallet);
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
[HttpPost("{storeId}/payment")]
|
||||
public async Task<IActionResult> Payment(PaymentViewModel model, string command = null)
|
||||
{
|
||||
bool needUpdate = false;
|
||||
if (CurrentStore.SpeedPolicy != model.SpeedPolicy)
|
||||
{
|
||||
needUpdate = true;
|
||||
CurrentStore.SpeedPolicy = model.SpeedPolicy;
|
||||
}
|
||||
|
||||
var blob = CurrentStore.GetStoreBlob();
|
||||
blob.DefaultCurrency = model.DefaultCurrency;
|
||||
blob.AnyoneCanInvoice = model.AnyoneCanCreateInvoice;
|
||||
blob.NetworkFeeMode = model.NetworkFeeMode;
|
||||
blob.MonitoringExpiration = TimeSpan.FromMinutes(model.MonitoringExpiration);
|
||||
blob.InvoiceExpiration = TimeSpan.FromMinutes(model.InvoiceExpiration);
|
||||
blob.LightningDescriptionTemplate = model.LightningDescriptionTemplate ?? string.Empty;
|
||||
blob.PaymentTolerance = model.PaymentTolerance;
|
||||
blob.LightningAmountInSatoshi = model.LightningAmountInSatoshi;
|
||||
blob.LightningPrivateRouteHints = model.LightningPrivateRouteHints;
|
||||
blob.OnChainWithLnInvoiceFallback = model.OnChainWithLnInvoiceFallback;
|
||||
blob.ShowRecommendedFee = model.ShowRecommendedFee;
|
||||
blob.RecommendedFeeBlockTarget = model.RecommendedFeeBlockTarget;
|
||||
blob.DefaultCurrency = model.DefaultCurrency;
|
||||
blob.InvoiceExpiration = TimeSpan.FromMinutes(model.InvoiceExpiration);
|
||||
|
||||
var payjoinChanged = blob.PayJoinEnabled != model.PayJoinEnabled;
|
||||
blob.PayJoinEnabled = model.PayJoinEnabled;
|
||||
if (CurrentStore.SetStoreBlob(blob))
|
||||
{
|
||||
needUpdate = true;
|
||||
@@ -718,28 +628,56 @@ namespace BTCPayServer.Controllers
|
||||
await _Repo.UpdateStore(CurrentStore);
|
||||
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Payment settings successfully updated";
|
||||
}
|
||||
|
||||
if (payjoinChanged && blob.PayJoinEnabled)
|
||||
return RedirectToAction(nameof(PaymentMethods), new
|
||||
{
|
||||
var problematicPayjoinEnabledMethods = CurrentStore.GetSupportedPaymentMethods(_NetworkProvider)
|
||||
.OfType<DerivationSchemeSettings>()
|
||||
.Where(settings => settings.Network.SupportPayJoin && !settings.IsHotWallet)
|
||||
.Select(settings => settings.PaymentId.CryptoCode)
|
||||
.ToArray();
|
||||
|
||||
if (problematicPayjoinEnabledMethods.Any())
|
||||
{
|
||||
TempData.Remove(WellKnownTempData.SuccessMessage);
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Warning,
|
||||
Html = $"The payment settings were updated successfully. However, payjoin will not work for {string.Join(", ", problematicPayjoinEnabledMethods)} until you configure them to be a <a href='https://docs.btcpayserver.org/HotWallet/' class='alert-link' target='_blank'>hot wallet</a>."
|
||||
storeId = CurrentStore.Id
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{storeId}/settings")]
|
||||
public IActionResult GeneralSettings()
|
||||
{
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
return NotFound();
|
||||
|
||||
var vm = new GeneralSettingsViewModel
|
||||
{
|
||||
Id = store.Id,
|
||||
StoreName = store.StoreName,
|
||||
StoreWebsite = store.StoreWebsite,
|
||||
CanDelete = _Repo.CanDeleteStores()
|
||||
};
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Payment), new
|
||||
[HttpPost("{storeId}/settings")]
|
||||
public async Task<IActionResult> GeneralSettings(GeneralSettingsViewModel 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;
|
||||
}
|
||||
|
||||
if (needUpdate)
|
||||
{
|
||||
await _Repo.UpdateStore(CurrentStore);
|
||||
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Store successfully updated";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(GeneralSettings), new
|
||||
{
|
||||
storeId = CurrentStore.Id
|
||||
});
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace BTCPayServer.Controllers
|
||||
var store = await _Repo.CreateStore(GetUserId(), vm.Name);
|
||||
CreatedStoreId = store.Id;
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Store successfully created";
|
||||
return RedirectToAction(nameof(StoresController.UpdateStore), "Stores", new
|
||||
return RedirectToAction(nameof(StoresController.PaymentMethods), "Stores", new
|
||||
{
|
||||
storeId = store.Id
|
||||
});
|
||||
|
||||
@@ -32,6 +32,7 @@ using NBXplorer.DerivationStrategy;
|
||||
using NBXplorer.Models;
|
||||
using Newtonsoft.Json;
|
||||
using StoreData = BTCPayServer.Data.StoreData;
|
||||
using WalletSettingsViewModel = BTCPayServer.Models.WalletViewModels.WalletSettingsViewModel;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
namespace BTCPayServer.Models.StoreViewModels
|
||||
{
|
||||
public class CheckoutExperienceViewModel
|
||||
public class CheckoutAppearanceViewModel
|
||||
{
|
||||
public class Format
|
||||
{
|
||||
@@ -1,18 +1,10 @@
|
||||
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 StoreViewModel
|
||||
public class GeneralSettingsViewModel
|
||||
{
|
||||
public List<StoreDerivationScheme> DerivationSchemes { get; set; }
|
||||
public List<StoreLightningNode> 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; }
|
||||
@@ -27,5 +19,7 @@ namespace BTCPayServer.Models.StoreViewModels
|
||||
[Display(Name = "Store Website")]
|
||||
[MaxLength(500)]
|
||||
public string StoreWebsite { get; set; }
|
||||
|
||||
public bool CanDelete { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -10,28 +10,14 @@ namespace BTCPayServer.Models.StoreViewModels
|
||||
|
||||
public class LightningNodeViewModel
|
||||
{
|
||||
[Display(Name = "Enable LNURL")]
|
||||
public bool LNURLEnabled { get; set; }
|
||||
|
||||
[Display(Name = "LNURL Classic Mode")]
|
||||
public bool LNURLBech32Mode { get; set; } = true;
|
||||
|
||||
[Display(Name = "LNURL enabled for standard invoices")]
|
||||
public bool LNURLStandardInvoiceEnabled { get; set; } = true;
|
||||
|
||||
[Display(Name = "Allow payee to pass a comment")]
|
||||
public bool LUD12Enabled { get; set; }
|
||||
|
||||
[Display(Name = "Do not offer BOLT11 for standard invoices")]
|
||||
public bool DisableBolt11PaymentMethod { get; set; }
|
||||
public LightningNodeType LightningNodeType { get; set; }
|
||||
|
||||
[Display(Name = "Connection string")]
|
||||
public string ConnectionString { get; set; }
|
||||
public string StoreId { get; set; }
|
||||
public string CryptoCode { get; set; }
|
||||
public bool CanUseInternalNode { get; set; }
|
||||
public bool SkipPortTest { get; set; }
|
||||
public bool Enabled { get; set; } = true;
|
||||
public string StoreId { get; set; }
|
||||
|
||||
[Display(Name = "Connection string")]
|
||||
public string ConnectionString { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace BTCPayServer.Models.StoreViewModels
|
||||
{
|
||||
public class LightningSettingsViewModel : LightningNodeViewModel
|
||||
{
|
||||
// Payment
|
||||
[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 = "Description template of the lightning invoice")]
|
||||
public string LightningDescriptionTemplate { get; set; }
|
||||
|
||||
// LNURL
|
||||
[Display(Name = "Enable LNURL")]
|
||||
public bool LNURLEnabled { get; set; }
|
||||
|
||||
[Display(Name = "LNURL Classic Mode")]
|
||||
public bool LNURLBech32Mode { get; set; } = true;
|
||||
|
||||
[Display(Name = "LNURL enabled for standard invoices")]
|
||||
public bool LNURLStandardInvoiceEnabled { get; set; } = true;
|
||||
|
||||
[Display(Name = "Allow payee to pass a comment")]
|
||||
public bool LUD12Enabled { get; set; }
|
||||
|
||||
[Display(Name = "Do not offer BOLT11 for standard invoices")]
|
||||
public bool DisableBolt11PaymentMethod { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Models.StoreViewModels
|
||||
{
|
||||
public class PaymentMethodsViewModel
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public List<StoreDerivationScheme> DerivationSchemes { get; set; }
|
||||
public List<StoreLightningNode> LightningNodes { get; set; }
|
||||
public bool HintWallet { get; set; }
|
||||
public bool HintLightning { 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 = "Add additional fee (network fee) to invoice …")]
|
||||
public NetworkFeeMode NetworkFeeMode { 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; }
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
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<StoreDerivationScheme> DerivationSchemes { get; set; }
|
||||
public List<StoreLightningNode> 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; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Models.StoreViewModels
|
||||
{
|
||||
public class WalletSettingsViewModel : DerivationSchemeViewModel
|
||||
{
|
||||
public string StoreId { get; set; }
|
||||
public bool IsHotWallet { get; set; }
|
||||
public bool CanUsePayJoin { 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 = "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; }
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@
|
||||
<td>
|
||||
@if (app.IsOwner)
|
||||
{
|
||||
<span><a asp-action="UpdateStore" asp-controller="Stores" asp-route-storeId="@app.StoreId">@app.StoreName</a></span>
|
||||
<span><a asp-action="PaymentMethods" asp-controller="Stores" asp-route-storeId="@app.StoreId">@app.StoreName</a></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@using BTCPayServer.Payments
|
||||
@model CheckoutExperienceViewModel
|
||||
@model CheckoutAppearanceViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.Checkout, "Checkout experience", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.CheckoutAppearance, "Checkout experience", Context.GetStoreData().StoreName);
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
@@ -1,7 +1,7 @@
|
||||
@model BTCPayServer.Models.ServerViewModels.EmailsViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.Index, "Email Settings", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.GeneralSettings, "Email Settings", Context.GetStoreData().StoreName);
|
||||
}
|
||||
|
||||
<partial name="EmailsBody" model="Model" />
|
||||
|
||||
71
BTCPayServer/Views/Stores/GeneralSettings.cshtml
Normal file
71
BTCPayServer/Views/Stores/GeneralSettings.cshtml
Normal file
@@ -0,0 +1,71 @@
|
||||
@model GeneralSettingsViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.GeneralSettings, "General Settings", Context.GetStoreData().StoreName);
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
@if (!ViewContext.ModelState.IsValid)
|
||||
{
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
}
|
||||
<form method="post">
|
||||
<h4 class="mb-3">General</h4>
|
||||
<div class="form-group">
|
||||
<label asp-for="Id" class="form-label"></label>
|
||||
<input asp-for="Id" readonly class="form-control" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="StoreName" class="form-label"></label>
|
||||
<input asp-for="StoreName" class="form-control" />
|
||||
<span asp-validation-for="StoreName" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="StoreWebsite" class="form-label"></label>
|
||||
<input asp-for="StoreWebsite" class="form-control" />
|
||||
<span asp-validation-for="StoreWebsite" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<button name="command" type="submit" class="btn btn-primary" value="Save" id="Save">Save Store Settings</button>
|
||||
</form>
|
||||
|
||||
<h4 class="mt-5 mb-3">Services</h4>
|
||||
<div class="table-responsive-md">
|
||||
<table class="table table-hover mt-1 mb-5">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Service</th>
|
||||
<th class="text-end w-100px">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
Email
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<a asp-action="Emails" asp-route-storeId="@Context.GetRouteValue("storeId")">
|
||||
Setup
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@if (Model.CanDelete)
|
||||
{
|
||||
<h4 class="mt-5 mb-3">Other actions</h4>
|
||||
<div id="danger-zone">
|
||||
<a id="delete-store" class="btn btn-outline-danger mb-5" asp-action="DeleteStore" asp-route-storeId="@Model.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The store <strong>@Model.StoreName</strong> will be permanently deleted. This action will also delete all invoices, apps and data associated with the store.">Delete this store</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<partial name="_Confirm" model="@(new ConfirmModel("Delete store", "The store will be permanently deleted. This action will also delete all invoices, apps and data associated with the store.", "Delete"))" />
|
||||
|
||||
@section PageFootContent {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
var isHotWallet = Model.Method == WalletSetupMethod.HotWallet;
|
||||
var type = isHotWallet ? "Hot" : "Watch-Only";
|
||||
Layout = "_LayoutWalletSetup";
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.Wallet, $"Create {Model.CryptoCode} {type} Wallet", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.PaymentMethods, $"Create {Model.CryptoCode} {type} Wallet", Context.GetStoreData().StoreName);
|
||||
ViewData.Add(nameof(Model.CanUseHotWallet), Model.CanUseHotWallet);
|
||||
ViewData.Add(nameof(Model.CanUseRPCImport), Model.CanUseRPCImport);
|
||||
ViewData.Add(nameof(Model.IsTaprootActivated), Model.IsTaprootActivated);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
Layout = "_LayoutWalletSetup";
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.Wallet, $"Generate {Model.CryptoCode} Wallet", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.PaymentMethods, $"Generate {Model.CryptoCode} Wallet", Context.GetStoreData().StoreName);
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
Layout = "_LayoutWalletSetup";
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.Wallet, "Confirm addresses", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.PaymentMethods, "Confirm addresses", Context.GetStoreData().StoreName);
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
Layout = "_LayoutWalletSetup";
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.Wallet, "Import your wallet file", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.PaymentMethods, "Import your wallet file", Context.GetStoreData().StoreName);
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
Layout = "_LayoutWalletSetup";
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.Wallet, "Connect your hardware wallet", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.PaymentMethods, "Connect your hardware wallet", Context.GetStoreData().StoreName);
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
Layout = "_LayoutWalletSetup";
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.Wallet, "Scan QR code", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.PaymentMethods, "Scan QR code", Context.GetStoreData().StoreName);
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
Layout = "_LayoutWalletSetup";
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.Wallet, "Enter the wallet seed", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.PaymentMethods, "Enter the wallet seed", Context.GetStoreData().StoreName);
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
Layout = "_LayoutWalletSetup";
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.Wallet, "Enter your extended public key", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.PaymentMethods, "Enter your extended public key", Context.GetStoreData().StoreName);
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
Layout = "_LayoutWalletSetup";
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.Wallet, $"Import {Model.CryptoCode} Wallet", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.PaymentMethods, $"Import {Model.CryptoCode} Wallet", Context.GetStoreData().StoreName);
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
|
||||
104
BTCPayServer/Views/Stores/LightningSettings.cshtml
Normal file
104
BTCPayServer/Views/Stores/LightningSettings.cshtml
Normal file
@@ -0,0 +1,104 @@
|
||||
@model LightningSettingsViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.PaymentMethods, $"{Model.CryptoCode} Lightning Settings", Context.GetStoreData().StoreName);
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-10 col-xl-9">
|
||||
<div class="mb-5">
|
||||
<h4 class="mb-3">@ViewData["Title"]</h4>
|
||||
<table class="table table-borderless table-responsive-md">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="w-150px">Node Type</th>
|
||||
<td class="text-break">
|
||||
<span class="me-3">@Model.LightningNodeType</span>
|
||||
<a asp-controller="Stores" asp-action="SetupLightningNode" asp-route-storeId="@Model.StoreId" asp-route-cryptoCode="@Model.CryptoCode" id="SetupLightningNodeLink" class="text-secondary">
|
||||
Change connection
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@if (Model.LightningNodeType != LightningNodeType.Internal)
|
||||
{
|
||||
<tr>
|
||||
<th>Connection String</th>
|
||||
<td>@Model.ConnectionString</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<form method="post" class="mt-n2 text-center">
|
||||
<div class="text-start">
|
||||
<h4 class="mt-5 mb-3">Payment</h4>
|
||||
<div class="form-check my-1">
|
||||
<input asp-for="LightningAmountInSatoshi" type="checkbox" class="form-check-input"/>
|
||||
<label asp-for="LightningAmountInSatoshi" class="form-check-label"></label>
|
||||
</div>
|
||||
<div class="form-check my-1">
|
||||
<input asp-for="LightningPrivateRouteHints" type="checkbox" class="form-check-input"/>
|
||||
<label asp-for="LightningPrivateRouteHints" class="form-check-label"></label>
|
||||
</div>
|
||||
<div class="form-check my-1">
|
||||
<input asp-for="OnChainWithLnInvoiceFallback" type="checkbox" class="form-check-input"/>
|
||||
<label asp-for="OnChainWithLnInvoiceFallback" class="form-check-label"></label>
|
||||
</div>
|
||||
<div class="form-group mt-3">
|
||||
<label asp-for="LightningDescriptionTemplate" class="form-label"></label>
|
||||
<input asp-for="LightningDescriptionTemplate" class="form-control"/>
|
||||
<span asp-validation-for="LightningDescriptionTemplate" class="text-danger"></span>
|
||||
<p class="form-text text-muted">
|
||||
Available placeholders:
|
||||
<code>{StoreName} {ItemDescription} {OrderId}</code>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h4 class="mt-5 mb-3">LNURL</h4>
|
||||
<div class="d-flex align-items-center">
|
||||
<input asp-for="LNURLEnabled" type="checkbox" class="btcpay-toggle me-2" data-bs-toggle="collapse" data-bs-target="#LNURLSettings" aria-expanded="@Model.LNURLEnabled" aria-controls="LNURLSettings"/>
|
||||
<label asp-for="LNURLEnabled" class="form-label mb-0 me-1"></label>
|
||||
</div>
|
||||
<div class="collapse @(Model.LNURLEnabled ? "show" : "")" id="LNURLSettings">
|
||||
<div class="form-group">
|
||||
<div class="d-flex align-items-center pt-4">
|
||||
<input type="checkbox" asp-for="LNURLBech32Mode" class="btcpay-toggle me-2"/>
|
||||
<label asp-for="LNURLBech32Mode" class="form-label mb-0 me-1"></label>
|
||||
<span asp-validation-for="LNURLBech32Mode" class="text-danger"></span>
|
||||
</div>
|
||||
<p class="form-text text-muted mb-0 ms-5">For wallet compatibility: Bech32 encoded (classic) vs. cleartext URL (upcoming)</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="d-flex align-items-center">
|
||||
<input type="checkbox" asp-for="LNURLStandardInvoiceEnabled" class="btcpay-toggle me-2"/>
|
||||
<label asp-for="LNURLStandardInvoiceEnabled" class="form-label mb-0 me-1"></label>
|
||||
</div>
|
||||
<p class="form-text text-muted mb-0 ms-5">Required for Lightning Address, the pay button and apps.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="d-flex align-items-center">
|
||||
<input type="checkbox" asp-for="DisableBolt11PaymentMethod" class="btcpay-toggle me-2"/>
|
||||
<label asp-for="DisableBolt11PaymentMethod" class="form-label mb-0 me-1"></label>
|
||||
</div>
|
||||
<p class="form-text text-muted mb-0 ms-5">Performance: Turn it off if users should pay only via LNURL.</p>
|
||||
</div>
|
||||
<div class="form-group mb-0 pb-2">
|
||||
<div class="d-flex align-items-center">
|
||||
<input type="checkbox" asp-for="LUD12Enabled" class="btcpay-toggle me-2"/>
|
||||
<label asp-for="LUD12Enabled" class="form-label mb-0 me-1"></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-start mt-4">
|
||||
<button id="save" name="command" type="submit" value="save" class="btn btn-primary me-2">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section PageFootContent {
|
||||
<partial name="_ValidationScriptsPartial"/>
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
@model WalletSetupViewModel
|
||||
@{
|
||||
Layout = "_LayoutWalletSetup";
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.Wallet, $"Modify {Model.CryptoCode} Wallet");
|
||||
}
|
||||
|
||||
<header class="text-center">
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<p class="lead text-secondary mt-3">Change your current wallet settings</p>
|
||||
</header>
|
||||
<br>
|
||||
|
||||
<div class="mt-5 position-relative">
|
||||
<h3 class="my-4">Current settings</h3>
|
||||
|
||||
<form method="post">
|
||||
<input asp-for="Config" type="hidden" />
|
||||
<input asp-for="DerivationScheme" type="hidden" />
|
||||
|
||||
<table class="table table-hover table-borderless table-responsive-md">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="w-150px">Derivation Scheme</th>
|
||||
<td class="text-break">@Model.DerivationScheme</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Root Fingerprint</th>
|
||||
<td>@Model.RootFingerprint</td>
|
||||
</tr>
|
||||
@if (!string.IsNullOrEmpty(Model.KeyPath))
|
||||
{
|
||||
<tr>
|
||||
<th>KeyPath</th>
|
||||
<td>@Model.KeyPath</td>
|
||||
</tr>
|
||||
}
|
||||
<tr>
|
||||
<th>Source</th>
|
||||
<td>@Model.Source</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<td>@(Model.IsHotWallet ? "Hot wallet" : "Watch-only wallet")</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
<br>
|
||||
<form method="get" asp-controller="Stores" asp-action="DeleteWallet" asp-route-storeId="@Model.StoreId" asp-route-cryptoCode="@Model.CryptoCode" class="mt-5">
|
||||
<a asp-controller="Stores" asp-action="ReplaceWallet" asp-route-storeId="@Model.StoreId" asp-route-cryptoCode="@Model.CryptoCode"
|
||||
id="ChangeWalletLink"
|
||||
class="btn btn-secondary me-2"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#ConfirmModal"
|
||||
data-title="Replace @Model.CryptoCode wallet"
|
||||
data-description="@ViewData["ReplaceDescription"]"
|
||||
data-confirm="Setup new wallet"
|
||||
data-confirm-input="REPLACE">
|
||||
Replace wallet
|
||||
</a>
|
||||
<button type="submit" class="btn btn-danger" id="Delete"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#ConfirmModal"
|
||||
data-title="Remove @Model.CryptoCode wallet"
|
||||
data-description="@ViewData["RemoveDescription"]"
|
||||
data-confirm="Remove"
|
||||
data-confirm-input="REMOVE">Remove wallet</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<partial name="_Confirm" model="@(new ConfirmModel($"{Model.CryptoCode} wallet", "Change", "Update"))" />
|
||||
|
||||
@section PageFootContent {
|
||||
<script>
|
||||
const deleteButton = document.getElementById('Delete')
|
||||
deleteButton.addEventListener('click', event => {
|
||||
event.preventDefault()
|
||||
});
|
||||
</script>
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
@model PaymentViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.Payment, "Payment", Context.GetStoreData().StoreName);
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-10 col-xl-9">
|
||||
<h4 class="mb-3">Payment</h4>
|
||||
@if (!ViewContext.ModelState.IsValid)
|
||||
{
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
}
|
||||
@if (Model.IsOnchainSetup || Model.IsLightningSetup)
|
||||
{
|
||||
<form method="post">
|
||||
<div class="form-group">
|
||||
<label asp-for="DefaultCurrency" class="form-label"></label>
|
||||
<input asp-for="DefaultCurrency" class="form-control" />
|
||||
<span asp-validation-for="DefaultCurrency" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group d-flex align-items-center">
|
||||
<input asp-for="AnyoneCanCreateInvoice" type="checkbox" class="btcpay-toggle me-2"/>
|
||||
<label asp-for="AnyoneCanCreateInvoice" class="form-label mb-0 me-1"></label>
|
||||
<a href="https://docs.btcpayserver.org/FAQ/Stores/#allow-anyone-to-create-invoice" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="form-group mt-4">
|
||||
<label asp-for="NetworkFeeMode" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/FAQ/Stores/#add-network-fee-to-invoice-vary-with-mining-fees" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
<select asp-for="NetworkFeeMode" class="form-select">
|
||||
<option value="MultiplePaymentsOnly">... only if the customer makes more than one payment for the invoice</option>
|
||||
<option value="Always">... on every payment</option>
|
||||
<option value="Never">Never add network fee</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="InvoiceExpiration" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/FAQ/Stores/#invoice-expires-if-the-full-amount-has-not-been-paid-after-minutes" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
<div class="input-group">
|
||||
<input asp-for="InvoiceExpiration" class="form-control" style="max-width:10ch;"/>
|
||||
<span class="input-group-text">minutes</span>
|
||||
</div>
|
||||
<span asp-validation-for="InvoiceExpiration" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="PaymentTolerance" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/FAQ/Stores/#consider-the-invoice-paid-even-if-the-paid-amount-is-less-than-expected" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
<div class="input-group">
|
||||
<input asp-for="PaymentTolerance" class="form-control" style="max-width:10ch;"/>
|
||||
<span class="input-group-text">percent</span>
|
||||
</div>
|
||||
<span asp-validation-for="PaymentTolerance" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="DefaultCurrency" class="form-label"></label>
|
||||
<input asp-for="DefaultCurrency" class="form-control" />
|
||||
<span asp-validation-for="DefaultCurrency" class="text-danger"></span>
|
||||
</div>
|
||||
@if (Model.IsOnchainSetup)
|
||||
{
|
||||
<h5 class="mt-5 mb-3">On-Chain</h5>
|
||||
@if (Model.CanUsePayJoin)
|
||||
{
|
||||
<div class="form-group">
|
||||
<div class="d-flex align-items-center">
|
||||
<input asp-for="PayJoinEnabled" type="checkbox" class="btcpay-toggle me-2"/>
|
||||
<label asp-for="PayJoinEnabled" class="form-label mb-0 me-1"></label>
|
||||
<a href="https://docs.btcpayserver.org/Payjoin/" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
</div>
|
||||
<span asp-validation-for="PayJoinEnabled" class="text-danger"></span>
|
||||
</div>
|
||||
}
|
||||
<div class="form-group">
|
||||
<label asp-for="MonitoringExpiration" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/FAQ/Stores/#payment-invalid-if-transactions-fails-to-confirm-minutes-after-invoice-expiration" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
<div class="input-group">
|
||||
<input asp-for="MonitoringExpiration" class="form-control" style="max-width:10ch;"/>
|
||||
<span class="input-group-text">minutes</span>
|
||||
</div>
|
||||
<span asp-validation-for="MonitoringExpiration" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="SpeedPolicy" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/FAQ/Stores/#consider-the-invoice-confirmed-when-the-payment-transaction" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
<select asp-for="SpeedPolicy" class="form-select w-auto" onchange="document.getElementById('unconfirmed-warning').hidden = this.value !== '0';">
|
||||
<option value="0">Is unconfirmed</option>
|
||||
<option value="1">Has at least 1 confirmation</option>
|
||||
<option value="3">Has at least 2 confirmations</option>
|
||||
<option value="2">Has at least 6 confirmations</option>
|
||||
</select>
|
||||
<div class="alert alert-warning my-2" hidden="@(Model.SpeedPolicy != 0)" id="unconfirmed-warning" role="alert">
|
||||
Choosing to accept an unconfirmed invoice can lead to double-spending and is strongly discouraged.
|
||||
</div>
|
||||
<span asp-validation-for="SpeedPolicy" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-check my-1">
|
||||
<input asp-for="ShowRecommendedFee" type="checkbox" class="form-check-input"/>
|
||||
<label asp-for="ShowRecommendedFee" class="form-check-label"></label>
|
||||
<p class="form-text text-muted mb-0">Fee will be shown for BTC and LTC onchain payments only.</p>
|
||||
</div>
|
||||
<div class="form-group mt-2 mb-4">
|
||||
<label asp-for="RecommendedFeeBlockTarget" class="form-label"></label>
|
||||
<input asp-for="RecommendedFeeBlockTarget" class="form-control" style="width:8ch" min="1" />
|
||||
<span asp-validation-for="RecommendedFeeBlockTarget" class="text-danger"></span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (Model.IsLightningSetup)
|
||||
{
|
||||
<h5 class="mt-5 mb-3">Lightning</h5>
|
||||
<div class="form-check my-1">
|
||||
<input asp-for="LightningAmountInSatoshi" type="checkbox" class="form-check-input"/>
|
||||
<label asp-for="LightningAmountInSatoshi" class="form-check-label"></label>
|
||||
</div>
|
||||
<div class="form-check my-1">
|
||||
<input asp-for="LightningPrivateRouteHints" type="checkbox" class="form-check-input"/>
|
||||
<label asp-for="LightningPrivateRouteHints" class="form-check-label"></label>
|
||||
</div>
|
||||
<div class="form-check my-1">
|
||||
<input asp-for="OnChainWithLnInvoiceFallback" type="checkbox" class="form-check-input"/>
|
||||
<label asp-for="OnChainWithLnInvoiceFallback" class="form-check-label"></label>
|
||||
</div>
|
||||
<div class="form-group mt-3">
|
||||
<label asp-for="LightningDescriptionTemplate" class="form-label"></label>
|
||||
<input asp-for="LightningDescriptionTemplate" class="form-control"/>
|
||||
<span asp-validation-for="LightningDescriptionTemplate" class="text-danger"></span>
|
||||
<p class="form-text text-muted">
|
||||
Available placeholders:
|
||||
<code>{StoreName} {ItemDescription} {OrderId}</code>
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
<button name="command" type="submit" class="btn btn-primary" value="Save" id="Save">Save Payment Settings</button>
|
||||
</form>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-secondary mt-3">
|
||||
Please configure either an on-chain wallet or Lightning node first.
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section PageFootContent {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
@using System.Text.RegularExpressions
|
||||
@model StoreViewModel
|
||||
@model PaymentMethodsViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.Index, "General Settings", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.PaymentMethods, "Wallets", Context.GetStoreData().StoreName);
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
@@ -49,7 +49,7 @@
|
||||
asp-route-cryptoCode="@scheme.Crypto"
|
||||
asp-route-storeId="@Model.Id"
|
||||
class="d-flex align-items-center"
|
||||
style="min-width:7.65rem">
|
||||
style="min-width:7rem">
|
||||
<button type="submit" class="btcpay-toggle me-2 @if (scheme.Enabled) { @("btcpay-toggle--active") }" name="Enabled" value="@(scheme.Enabled ? "false" : "true")" id="@($"{scheme.Crypto}WalletEnabled")">@(scheme.Enabled ? "Disable" : "Enable")</button>
|
||||
@if (scheme.Enabled)
|
||||
{
|
||||
@@ -64,9 +64,9 @@
|
||||
</span>
|
||||
}
|
||||
</form>
|
||||
<span class="text-light ms-3 me-2">|</span>
|
||||
<a asp-action="ModifyWallet" asp-route-cryptoCode="@scheme.Crypto" asp-route-storeId="@Model.Id" id="@($"Modify{scheme.Crypto}")" class="btn btn-link px-1 py-1 fw-semibold">
|
||||
Modify
|
||||
<span class="text-light mx-2">|</span>
|
||||
<a asp-action="WalletSettings" asp-route-cryptoCode="@scheme.Crypto" asp-route-storeId="@Model.Id" id="@($"Modify{scheme.Crypto}")" class="btn btn-link px-1 py-1 fw-semibold">
|
||||
Settings
|
||||
</a>
|
||||
}
|
||||
else
|
||||
@@ -139,7 +139,7 @@
|
||||
asp-route-cryptoCode="@scheme.CryptoCode"
|
||||
asp-route-storeId="@Model.Id"
|
||||
class="d-flex align-items-center"
|
||||
style="min-width:7.65rem">
|
||||
style="min-width:7rem">
|
||||
<button type="submit" class="btcpay-toggle me-2 @if (scheme.Enabled) { @("btcpay-toggle--active") }" name="Enabled" value="@(scheme.Enabled ? "false" : "true")" id="@($"{scheme.CryptoCode}LightningEnabled")">@(scheme.Enabled ? "Disable" : "Enable")e</button>
|
||||
@if (scheme.Enabled)
|
||||
{
|
||||
@@ -154,11 +154,17 @@
|
||||
</span>
|
||||
}
|
||||
</form>
|
||||
<span class="text-light ms-3 me-2">|</span>
|
||||
}
|
||||
<a asp-action="SetupLightningNode" asp-route-cryptoCode="@scheme.CryptoCode" asp-route-storeId="@Model.Id" id="@($"Modify-Lightning{scheme.CryptoCode}")" class="btn btn-@(isSetUp ? "link px-1" : "primary btn-sm ms-4 px-3") py-1 fw-semibold">
|
||||
@(isSetUp ? "Modify" : "Setup")
|
||||
<span class="text-light mx-2">|</span>
|
||||
<a asp-action="LightningSettings" asp-route-cryptoCode="@scheme.CryptoCode" asp-route-storeId="@Model.Id" id="@($"Modify-Lightning{scheme.CryptoCode}")" class="btn btn-link px-1 py-1 fw-semibold">
|
||||
Settings
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a asp-action="SetupLightningNode" asp-route-cryptoCode="@scheme.CryptoCode" asp-route-storeId="@Model.Id" id="@($"Modify-Lightning{scheme.CryptoCode}")" class="btn btn-primary btn-sm ms-4 px-3 py-1 fw-semibold">
|
||||
Setup
|
||||
</a>
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
@@ -167,64 +173,58 @@
|
||||
</div>
|
||||
|
||||
<form method="post">
|
||||
<h4 class="mb-3">General</h4>
|
||||
<h4 class="mb-3">Payment</h4>
|
||||
<div class="form-group">
|
||||
<label asp-for="Id" class="form-label"></label>
|
||||
<input asp-for="Id" readonly class="form-control" />
|
||||
<label asp-for="DefaultCurrency" class="form-label"></label>
|
||||
<input asp-for="DefaultCurrency" class="form-control" style="max-width:10ch;" />
|
||||
<span asp-validation-for="DefaultCurrency" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="StoreName" class="form-label"></label>
|
||||
<input asp-for="StoreName" class="form-control" />
|
||||
<span asp-validation-for="StoreName" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="StoreWebsite" class="form-label"></label>
|
||||
<input asp-for="StoreWebsite" class="form-control" />
|
||||
<span asp-validation-for="StoreWebsite" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<button name="command" type="submit" class="btn btn-primary" value="Save" id="Save">Save Store Settings</button>
|
||||
</form>
|
||||
|
||||
<h4 class="mt-5 mb-3">Services</h4>
|
||||
<div class="table-responsive-md">
|
||||
<table class="table table-hover mt-1 mb-5">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Service</th>
|
||||
<th class="text-end w-100px">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
Email
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<a asp-action="Emails" asp-route-storeId="@Context.GetRouteValue("storeId")">
|
||||
Setup
|
||||
<div class="form-group d-flex align-items-center">
|
||||
<input asp-for="AnyoneCanCreateInvoice" type="checkbox" class="btcpay-toggle me-2"/>
|
||||
<label asp-for="AnyoneCanCreateInvoice" class="form-label mb-0 me-1"></label>
|
||||
<a href="https://docs.btcpayserver.org/FAQ/Stores/#allow-anyone-to-create-invoice" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="form-group mt-4">
|
||||
<label asp-for="NetworkFeeMode" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/FAQ/Stores/#add-network-fee-to-invoice-vary-with-mining-fees" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
<select asp-for="NetworkFeeMode" class="form-select">
|
||||
<option value="MultiplePaymentsOnly">... only if the customer makes more than one payment for the invoice</option>
|
||||
<option value="Always">... on every payment</option>
|
||||
<option value="Never">Never add network fee</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="InvoiceExpiration" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/FAQ/Stores/#invoice-expires-if-the-full-amount-has-not-been-paid-after-minutes" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
<div class="input-group">
|
||||
<input asp-for="InvoiceExpiration" class="form-control" style="max-width:10ch;"/>
|
||||
<span class="input-group-text">minutes</span>
|
||||
</div>
|
||||
<span asp-validation-for="InvoiceExpiration" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="PaymentTolerance" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/FAQ/Stores/#consider-the-invoice-paid-even-if-the-paid-amount-is-less-than-expected" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
<div class="input-group">
|
||||
<input asp-for="PaymentTolerance" class="form-control" style="max-width:10ch;"/>
|
||||
<span class="input-group-text">percent</span>
|
||||
</div>
|
||||
<span asp-validation-for="PaymentTolerance" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
@if (Model.CanDelete)
|
||||
{
|
||||
<h4 class="mt-5 mb-3">Other actions</h4>
|
||||
<button id="danger-zone-expander" class="btn btn-link text-secondary mb-3 p-0" type="button" data-bs-toggle="collapse" data-bs-target="#danger-zone">
|
||||
See more actions
|
||||
</button>
|
||||
<div id="danger-zone" class="collapse">
|
||||
<a id="delete-store" class="btn btn-outline-danger mb-5" asp-action="DeleteStore" asp-route-storeId="@Model.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The store <strong>@Model.StoreName</strong> will be permanently deleted. This action will also delete all invoices, apps and data associated with the store.">Delete this store</a>
|
||||
</div>
|
||||
}
|
||||
<button name="command" type="submit" class="btn btn-primary px-4" value="Save" id="Save">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<partial name="_Confirm" model="@(new ConfirmModel("Delete store", "The store will be permanently deleted. This action will also delete all invoices, apps and data associated with the store.", "Delete"))" />
|
||||
|
||||
@section PageFootContent {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
@model LightningNodeViewModel
|
||||
@{
|
||||
Layout = "_LayoutWalletSetup.cshtml";
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.Index, "Connect to a Lightning node", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.PaymentMethods, "Connect to a Lightning node", Context.GetStoreData().StoreName);
|
||||
}
|
||||
|
||||
<header class="text-center">
|
||||
@@ -179,44 +179,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-start">
|
||||
<div class="d-flex align-items-center">
|
||||
<input asp-for="LNURLEnabled" type="checkbox" class="btcpay-toggle me-2" data-bs-toggle="collapse" data-bs-target="#LNURLSettings" aria-expanded="@Model.LNURLEnabled" aria-controls="LNURLSettings"/>
|
||||
<label asp-for="LNURLEnabled" class="form-label mb-0 me-1"></label>
|
||||
</div>
|
||||
<div class="collapse @(Model.LNURLEnabled ? "show" : "")" id="LNURLSettings">
|
||||
<h5 class="mb-1" style="padding-top:var(--btcpay-space-l)">LNURL settings</h5>
|
||||
<div class="form-group">
|
||||
<div class="d-flex align-items-center pt-3">
|
||||
<input type="checkbox" asp-for="LNURLBech32Mode" class="btcpay-toggle me-2"/>
|
||||
<label asp-for="LNURLBech32Mode" class="form-label mb-0 me-1"></label>
|
||||
<span asp-validation-for="LNURLBech32Mode" class="text-danger"></span>
|
||||
</div>
|
||||
<p class="form-text text-muted mb-0 ms-5">For wallet compatibility: Bech32 encoded (classic) vs. cleartext URL (upcoming)</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="d-flex align-items-center">
|
||||
<input type="checkbox" asp-for="LNURLStandardInvoiceEnabled" class="btcpay-toggle me-2"/>
|
||||
<label asp-for="LNURLStandardInvoiceEnabled" class="form-label mb-0 me-1"></label>
|
||||
</div>
|
||||
<p class="form-text text-muted mb-0 ms-5">Required for Lightning Address, the pay button and apps.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="d-flex align-items-center">
|
||||
<input type="checkbox" asp-for="DisableBolt11PaymentMethod" class="btcpay-toggle me-2"/>
|
||||
<label asp-for="DisableBolt11PaymentMethod" class="form-label mb-0 me-1"></label>
|
||||
</div>
|
||||
<p class="form-text text-muted mb-0 ms-5">Performance: Turn it off if users should pay only via LNURL.</p>
|
||||
</div>
|
||||
<div class="form-group mb-0 pb-2">
|
||||
<div class="d-flex align-items-center">
|
||||
<input type="checkbox" asp-for="LUD12Enabled" class="btcpay-toggle me-2"/>
|
||||
<label asp-for="LUD12Enabled" class="form-label mb-0 me-1"></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-start mt-4">
|
||||
<button id="save" name="command" type="submit" value="save" class="btn btn-primary me-2">Save</button>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@model WalletSetupViewModel
|
||||
@{
|
||||
Layout = "_LayoutWalletSetup";
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.Wallet, $"Setup {Model.CryptoCode} Wallet", Context.GetStoreData().StoreName);
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.PaymentMethods, $"Setup {Model.CryptoCode} Wallet", Context.GetStoreData().StoreName);
|
||||
}
|
||||
|
||||
<h1 class="text-center">Let's get started</h1>
|
||||
|
||||
@@ -2,6 +2,6 @@ namespace BTCPayServer.Views.Stores
|
||||
{
|
||||
public enum StoreNavPages
|
||||
{
|
||||
Index, Create, Rates, Payment, Checkout, Tokens, Users, PayButton, Integrations, Wallet, Webhooks, ActivePage, PullPayments, Payouts
|
||||
Index, Create, Rates, PaymentMethods, CheckoutAppearance, GeneralSettings, Tokens, Users, PayButton, Integrations, Webhooks, ActivePage, PullPayments, Payouts
|
||||
}
|
||||
}
|
||||
|
||||
124
BTCPayServer/Views/Stores/WalletSettings.cshtml
Normal file
124
BTCPayServer/Views/Stores/WalletSettings.cshtml
Normal file
@@ -0,0 +1,124 @@
|
||||
@model WalletSettingsViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.PaymentMethods, $"{Model.CryptoCode} Wallet Settings", Context.GetStoreData().StoreName);
|
||||
}
|
||||
<div class="row">
|
||||
<div class="col-lg-10 col-xl-9">
|
||||
<div class="mb-5">
|
||||
<h4 class="mb-3">@ViewData["Title"]</h4>
|
||||
<table class="table table-borderless table-responsive-md">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<td class="d-flex flex-wrap align-items-center">
|
||||
<span title="@Model.Source" data-bs-toggle="tooltip" class="me-3">@(Model.IsHotWallet ? "Hot wallet" : "Watch-only wallet")</span>
|
||||
<a asp-controller="Stores" asp-action="ReplaceWallet" asp-route-storeId="@Model.StoreId" asp-route-cryptoCode="@Model.CryptoCode"
|
||||
id="ChangeWalletLink"
|
||||
class="text-secondary me-3"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#ConfirmModal"
|
||||
data-title="Replace @Model.CryptoCode wallet"
|
||||
data-description="@ViewData["ReplaceDescription"]"
|
||||
data-confirm="Setup new wallet"
|
||||
data-confirm-input="REPLACE">
|
||||
Replace wallet
|
||||
</a>
|
||||
<form method="get" asp-controller="Stores" asp-action="DeleteWallet" asp-route-storeId="@Model.StoreId" asp-route-cryptoCode="@Model.CryptoCode" class="d-inline">
|
||||
<button type="submit" class="btn btn-link text-secondary text-start p-0" id="Delete"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#ConfirmModal"
|
||||
data-title="Remove @Model.CryptoCode wallet"
|
||||
data-description="@ViewData["RemoveDescription"]"
|
||||
data-confirm="Remove"
|
||||
data-confirm-input="REMOVE">Remove wallet</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="w-150px">Derivation Scheme</th>
|
||||
<td class="text-break">@Model.DerivationScheme</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Root Fingerprint</th>
|
||||
<td>@Model.RootFingerprint</td>
|
||||
</tr>
|
||||
@if (!string.IsNullOrEmpty(Model.KeyPath))
|
||||
{
|
||||
<tr>
|
||||
<th>KeyPath</th>
|
||||
<td>@Model.KeyPath</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h4 class="mt-5 mb-3">Payment</h4>
|
||||
<form method="post" asp-action="UpdateWalletSettings" asp-route-storeId="@Model.StoreId" asp-route-cryptoCode="@Model.CryptoCode">
|
||||
@if (Model.CanUsePayJoin)
|
||||
{
|
||||
<div class="form-group">
|
||||
<div class="d-flex align-items-center">
|
||||
<input asp-for="PayJoinEnabled" type="checkbox" class="btcpay-toggle me-2"/>
|
||||
<label asp-for="PayJoinEnabled" class="form-label mb-0 me-1"></label>
|
||||
<a href="https://docs.btcpayserver.org/Payjoin/" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
</div>
|
||||
<span asp-validation-for="PayJoinEnabled" class="text-danger"></span>
|
||||
</div>
|
||||
}
|
||||
<div class="form-group">
|
||||
<label asp-for="MonitoringExpiration" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/FAQ/Stores/#payment-invalid-if-transactions-fails-to-confirm-minutes-after-invoice-expiration" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
<div class="input-group">
|
||||
<input asp-for="MonitoringExpiration" class="form-control" style="max-width:10ch;"/>
|
||||
<span class="input-group-text">minutes</span>
|
||||
</div>
|
||||
<span asp-validation-for="MonitoringExpiration" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="SpeedPolicy" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/FAQ/Stores/#consider-the-invoice-confirmed-when-the-payment-transaction" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
<select asp-for="SpeedPolicy" class="form-select w-auto" onchange="document.getElementById('unconfirmed-warning').hidden = this.value !== '0';">
|
||||
<option value="0">Is unconfirmed</option>
|
||||
<option value="1">Has at least 1 confirmation</option>
|
||||
<option value="3">Has at least 2 confirmations</option>
|
||||
<option value="2">Has at least 6 confirmations</option>
|
||||
</select>
|
||||
<div class="alert alert-warning my-2" hidden="@(Model.SpeedPolicy != 0)" id="unconfirmed-warning" role="alert">
|
||||
Choosing to accept an unconfirmed invoice can lead to double-spending and is strongly discouraged.
|
||||
</div>
|
||||
<span asp-validation-for="SpeedPolicy" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-check my-1">
|
||||
<input asp-for="ShowRecommendedFee" type="checkbox" class="form-check-input"/>
|
||||
<label asp-for="ShowRecommendedFee" class="form-check-label"></label>
|
||||
<p class="form-text text-muted mb-0">Fee will be shown for BTC and LTC onchain payments only.</p>
|
||||
</div>
|
||||
<div class="form-group mt-2 mb-4">
|
||||
<label asp-for="RecommendedFeeBlockTarget" class="form-label"></label>
|
||||
<input asp-for="RecommendedFeeBlockTarget" class="form-control" style="width:8ch" min="1" />
|
||||
<span asp-validation-for="RecommendedFeeBlockTarget" class="text-danger"></span>
|
||||
</div>
|
||||
<button name="command" type="submit" class="btn btn-primary" value="Save" id="Save">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<partial name="_Confirm" model="@(new ConfirmModel($"{Model.CryptoCode} wallet", "Change", "Update"))" />
|
||||
|
||||
@section PageFootContent {
|
||||
<script>
|
||||
const deleteButton = document.getElementById('Delete')
|
||||
deleteButton.addEventListener('click', event => {
|
||||
event.preventDefault()
|
||||
});
|
||||
</script>
|
||||
<partial name="_ValidationScriptsPartial"/>
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
@section Navbar {
|
||||
@await RenderSectionAsync("Navbar", false)
|
||||
|
||||
<a asp-controller="Stores" asp-action="UpdateStore" asp-route-storeId="@Context.GetRouteValue("storeId")" class="cancel">
|
||||
<a asp-controller="Stores" asp-action="PaymentMethods" asp-route-storeId="@Context.GetRouteValue("storeId")" class="cancel">
|
||||
<vc:icon symbol="close" />
|
||||
</a>
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<nav id="sideNav" class="nav flex-column mb-4">
|
||||
<a id="@(nameof(StoreNavPages.Index))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Index)" asp-controller="Stores" asp-action="UpdateStore" asp-route-storeId="@Context.GetRouteValue("storeId")">General settings</a>
|
||||
<a id="@(nameof(StoreNavPages.Rates))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Rates)" asp-controller="Stores" asp-action="Rates" asp-route-storeId="@Context.GetRouteValue("storeId")">Rates</a>
|
||||
<a id="@(nameof(StoreNavPages.Payment))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Payment)" asp-controller="Stores" asp-action="Payment" asp-route-storeId="@Context.GetRouteValue("storeId")">Payment</a>
|
||||
<a id="@(nameof(StoreNavPages.Checkout))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Checkout)" asp-controller="Stores" asp-action="CheckoutExperience" asp-route-storeId="@Context.GetRouteValue("storeId")">Checkout experience</a>
|
||||
<a id="@(nameof(StoreNavPages.Tokens))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Tokens)" asp-controller="Stores" asp-action="ListTokens" asp-route-storeId="@Context.GetRouteValue("storeId")">Access Tokens</a>
|
||||
<a id="@(nameof(StoreNavPages.Users))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Users)" asp-controller="Stores" asp-action="StoreUsers" asp-route-storeId="@Context.GetRouteValue("storeId")">Users</a>
|
||||
<a id="@(nameof(StoreNavPages.PayButton))" class="nav-link @ViewData.IsActivePage(StoreNavPages.PayButton)" asp-controller="Stores" asp-action="PayButton" asp-route-storeId="@Context.GetRouteValue("storeId")">Pay Button</a>
|
||||
<a id="@(nameof(StoreNavPages.Integrations))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Integrations)" asp-controller="Stores" asp-action="Integrations" asp-route-storeId="@Context.GetRouteValue("storeId")">Integrations</a>
|
||||
<a id="@(nameof(StoreNavPages.Webhooks))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Webhooks)" asp-controller="Stores" asp-action="Webhooks" asp-route-storeId="@Context.GetRouteValue("storeId")">Webhooks</a>
|
||||
<a id="@(nameof(StoreNavPages.PullPayments))" class="nav-link @ViewData.IsActivePage(StoreNavPages.PullPayments)" asp-action="PullPayments" asp-controller="StorePullPayments" asp-route-storeId="@Context.GetRouteValue("storeId")">Pull payments</a>
|
||||
<a id="@(nameof(StoreNavPages.Payouts))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Payouts)" asp-action="Payouts" asp-controller="StorePullPayments" asp-route-storeId="@Context.GetRouteValue("storeId")">Payouts</a>
|
||||
<a id="Nav-@(nameof(StoreNavPages.PaymentMethods))" class="nav-link @ViewData.IsActivePage(StoreNavPages.PaymentMethods)" asp-controller="Stores" asp-action="PaymentMethods" asp-route-storeId="@Context.GetRouteValue("storeId")">Payment Methods</a>
|
||||
<a id="Nav-@(nameof(StoreNavPages.Rates))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Rates)" asp-controller="Stores" asp-action="Rates" asp-route-storeId="@Context.GetRouteValue("storeId")">Rates</a>
|
||||
<a id="Nav-@(nameof(StoreNavPages.CheckoutAppearance))" class="nav-link @ViewData.IsActivePage(StoreNavPages.CheckoutAppearance)" asp-controller="Stores" asp-action="CheckoutAppearance" asp-route-storeId="@Context.GetRouteValue("storeId")">Checkout Appearance</a>
|
||||
<a id="Nav-@(nameof(StoreNavPages.GeneralSettings))" class="nav-link @ViewData.IsActivePage(StoreNavPages.GeneralSettings)" asp-controller="Stores" asp-action="GeneralSettings" asp-route-storeId="@Context.GetRouteValue("storeId")">General Settings</a>
|
||||
<a id="Nav-@(nameof(StoreNavPages.Tokens))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Tokens)" asp-controller="Stores" asp-action="ListTokens" asp-route-storeId="@Context.GetRouteValue("storeId")">Access Tokens</a>
|
||||
<a id="Nav-@(nameof(StoreNavPages.Users))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Users)" asp-controller="Stores" asp-action="StoreUsers" asp-route-storeId="@Context.GetRouteValue("storeId")">Users</a>
|
||||
<a id="Nav-@(nameof(StoreNavPages.PayButton))" class="nav-link @ViewData.IsActivePage(StoreNavPages.PayButton)" asp-controller="Stores" asp-action="PayButton" asp-route-storeId="@Context.GetRouteValue("storeId")">Pay Button</a>
|
||||
<a id="Nav-@(nameof(StoreNavPages.Integrations))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Integrations)" asp-controller="Stores" asp-action="Integrations" asp-route-storeId="@Context.GetRouteValue("storeId")">Integrations</a>
|
||||
<a id="Nav-@(nameof(StoreNavPages.Webhooks))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Webhooks)" asp-controller="Stores" asp-action="Webhooks" asp-route-storeId="@Context.GetRouteValue("storeId")">Webhooks</a>
|
||||
<a id="Nav-@(nameof(StoreNavPages.PullPayments))" class="nav-link @ViewData.IsActivePage(StoreNavPages.PullPayments)" asp-action="PullPayments" asp-controller="StorePullPayments" asp-route-storeId="@Context.GetRouteValue("storeId")">Pull Payments</a>
|
||||
<a id="Nav-@(nameof(StoreNavPages.Payouts))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Payouts)" asp-action="Payouts" asp-controller="StorePullPayments" asp-route-storeId="@Context.GetRouteValue("storeId")">Payouts</a>
|
||||
<vc:ui-extension-point location="store-nav" model="@Model" />
|
||||
</nav>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<td>
|
||||
@if (store.IsOwner)
|
||||
{
|
||||
<a asp-action="UpdateStore" asp-controller="Stores" asp-route-storeId="@store.Id">@store.Name</a>
|
||||
<a asp-action="PaymentMethods" asp-controller="Stores" asp-route-storeId="@store.Id">@store.Name</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -74,7 +74,7 @@
|
||||
<a asp-action="ListInvoices" asp-controller="Invoice" asp-route-searchTerm="storeid:@store.Id">Invoices</a><span> - </span>
|
||||
@if (store.IsOwner)
|
||||
{
|
||||
<a asp-action="UpdateStore" asp-controller="Stores" asp-route-storeId="@store.Id" id="update-store-@store.Id">Settings</a><span> - </span>
|
||||
<a asp-action="PaymentMethods" asp-controller="Stores" asp-route-storeId="@store.Id" id="update-store-@store.Id">Settings</a><span> - </span>
|
||||
}
|
||||
<a asp-action="DeleteStore" asp-controller="Stores" asp-route-storeId="@store.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The store <strong>@store.Name</strong> will be permanently deleted. This action will also delete all invoices, apps and data associated with the store." data-confirm-input="DELETE">Delete</a>
|
||||
</td>
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
<tr>
|
||||
@if (wallet.IsOwner)
|
||||
{
|
||||
<td><a asp-action="UpdateStore" asp-controller="Stores" asp-route-storeId="@wallet.StoreId">@wallet.StoreName</a></td>
|
||||
<td><a asp-action="PaymentMethods" asp-controller="Stores" asp-route-storeId="@wallet.StoreId">@wallet.StoreName</a></td>
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using Newtonsoft.Json
|
||||
@using System.Text
|
||||
@using NBitcoin.DataEncoders
|
||||
@model WalletSettingsViewModel
|
||||
@model BTCPayServer.Models.WalletViewModels.WalletSettingsViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePageAndTitle(WalletsNavPages.Settings, "Wallet settings", Context.GetStoreData().StoreName);
|
||||
|
||||
Reference in New Issue
Block a user