Compare commits

..

7 Commits

Author SHA1 Message Date
c1fe473cc7 remove from state when unreserved 2021-04-09 14:25:49 +02:00
d3923da8e4 pr changes 2021-04-08 13:41:29 +02:00
eac4d54466 add to docs 2021-04-08 13:41:29 +02:00
717836e9e0 style better 2021-04-08 13:41:29 +02:00
8d1ec4b5c0 show bip21 and additional work 2021-04-08 13:41:29 +02:00
1ee5c82b8b wip 2021-04-08 13:41:29 +02:00
c775c3cc78 Allow Payjoin for wallet receive addresses 2021-04-08 13:41:29 +02:00
170 changed files with 2077 additions and 1660 deletions

View File

@ -9,5 +9,7 @@ namespace BTCPayServer.Client.Models
public string Address { get; set; }
[JsonConverter(typeof(KeyPathJsonConverter))]
public KeyPath KeyPath { get; set; }
public string PaymentLink { get; set; }
}
}

View File

@ -38,8 +38,8 @@ namespace BTCPayServer
//precision 0: 10 = 0.00000010
//precision 2: 10 = 0.00001000
//precision 8: 10 = 10
var money = new Money(cryptoInfoDue.ToDecimal(MoneyUnit.BTC) / decimal.Parse("1".PadRight(1 + 8 - Divisibility, '0')), MoneyUnit.BTC);
return $"{base.GenerateBIP21(cryptoInfoAddress, money)}&assetid={AssetId}";
var money = cryptoInfoDue is null? null: new Money(cryptoInfoDue.ToDecimal(MoneyUnit.BTC) / decimal.Parse("1".PadRight(1 + 8 - Divisibility, '0')), MoneyUnit.BTC);
return $"{base.GenerateBIP21(cryptoInfoAddress, money)}{(money is null? "?": "&")}assetid={AssetId}";
}
}
}

View File

@ -123,7 +123,7 @@ namespace BTCPayServer
public virtual string GenerateBIP21(string cryptoInfoAddress, Money cryptoInfoDue)
{
return $"{UriScheme}:{cryptoInfoAddress}?amount={cryptoInfoDue.ToString(false, true)}";
return $"{UriScheme}:{cryptoInfoAddress}{(cryptoInfoDue is null? string.Empty: $"?amount={cryptoInfoDue.ToString(false, true)}")}";
}
public virtual List<TransactionInformation> FilterValidTransactions(List<TransactionInformation> transactionInformationSet)

View File

@ -225,7 +225,7 @@ namespace BTCPayServer.Tests
public void ClickOnAllSideMenus()
{
var links = Driver.FindElements(By.CssSelector(".nav .nav-link")).Select(c => c.GetAttribute("href")).ToList();
var links = Driver.FindElements(By.CssSelector(".nav-pills .nav-link")).Select(c => c.GetAttribute("href")).ToList();
Driver.AssertNoError();
Assert.NotEmpty(links);
foreach (var l in links)

View File

@ -240,7 +240,7 @@ namespace BTCPayServer.Tests
Assert.True(valid);
}
}
[Fact]
[Trait("Integration", "Integration")]
public async Task EnsureSwaggerPermissionsDocumented()
@ -252,7 +252,7 @@ namespace BTCPayServer.Tests
var description =
"BTCPay Server supports authenticating and authorizing users through an API Key that is generated by them. Send the API Key as a header value to Authorization with the format: `token {token}`. For a smoother experience, you can generate a url that redirects users to an API key creation screen.\n\n The following permissions are available to the context of the user creating the API Key:\n\n#OTHERPERMISSIONS#\n\nThe following permissions are available if the user is an administrator:\n\n#SERVERPERMISSIONS#\n\nThe following permissions applies to all stores of the user, you can limit to a specific store with the following format: `btcpay.store.cancreateinvoice:6HSHAEU4iYWtjxtyRs9KyPjM9GAQp8kw2T9VWbGG1FnZ`:\n\n#STOREPERMISSIONS#\n\nNote that API Keys only limits permission of a user and can never expand it. If an API Key has the permission `btcpay.server.canmodifyserversettings` but that the user account creating this API Key is not administrator, the API Key will not be able to modify the server settings.\n";
var storePolicies =
ManageController.AddApiKeyViewModel.PermissionValueItem.PermissionDescriptions.Where(pair =>
Policies.IsStorePolicy(pair.Key) && !pair.Key.EndsWith(":", StringComparison.InvariantCulture));
@ -270,7 +270,7 @@ namespace BTCPayServer.Tests
.Replace("#STOREPERMISSIONS#",
string.Join("\n", storePolicies.Select(pair => $"* `{pair.Key}`: {pair.Value.Title}")));
Logs.Tester.LogInformation(description);
var sresp = Assert
.IsType<JsonResult>(await tester.PayTester.GetController<HomeController>(acc.UserId, acc.StoreId)
.Swagger()).Value.ToJson();
@ -668,7 +668,7 @@ namespace BTCPayServer.Tests
AssertPayoutLabel(info);
}
[Fact]
[Trait("Fast", "Fast")]
@ -905,7 +905,7 @@ namespace BTCPayServer.Tests
{
await tester.ExplorerNode.SendToAddressAsync(
BitcoinAddress.Create(invoice.BitcoinAddress, Network.RegTest), Money.Coins(0.00005m));
}, e => e.InvoiceId == invoice.Id && e.PaymentMethodId.PaymentType == LightningPaymentType.Instance);
}, e => e.InvoiceId == invoice.Id && e.PaymentMethodId.PaymentType == LightningPaymentType.Instance );
await tester.ExplorerNode.GenerateAsync(1);
await Task.Delay(100); // wait a bit for payment to process before fetching new invoice
var newInvoice = await user.BitPay.GetInvoiceAsync(invoice.Id);
@ -2321,8 +2321,6 @@ namespace BTCPayServer.Tests
{("[1,2,3]", new Dictionary<string, object>() {{string.Empty, "[1,2,3]"}})},
{("{ \"key\": \"value\"}", new Dictionary<string, object>() {{"key", "value"}})},
{("{ \"key\": true}", new Dictionary<string, object>() {{"key", "True"}})},
// Duplicate keys should not crash things
{("{ \"key\": true, \"key\": true}", new Dictionary<string, object>() {{"key", "True"}})},
{
("{ invalidjson file here}",
new Dictionary<string, object>() {{String.Empty, "{ invalidjson file here}"}})
@ -2332,17 +2330,6 @@ namespace BTCPayServer.Tests
testCases.ForEach(tuple =>
{
Assert.Equal(tuple.expectedOutput, InvoiceController.PosDataParser.ParsePosData(tuple.input));
JObject jobj = null;
try
{
jobj = JObject.Parse(tuple.input);
}
catch
{
}
// If the posData is directly a jobject, it should parse the same as if it was a string with json encoded
if (jobj != null)
Assert.Equal(tuple.expectedOutput, InvoiceController.PosDataParser.ParsePosData(jobj));
});
}
@ -3142,7 +3129,7 @@ namespace BTCPayServer.Tests
("\\test.com", false),
("te\\st.com", false)
};
foreach (var t in tests)
foreach(var t in tests)
{
Assert.Equal(t.Item2, t.Item1.IsValidFileName());
}
@ -3452,7 +3439,7 @@ namespace BTCPayServer.Tests
var acc = tester.NewAccount();
await acc.GrantAccessAsync(true);
await acc.CreateStoreAsync();
// Test if legacy DerivationStrategy column is converted to DerivationStrategies
var store = await tester.PayTester.StoreRepository.FindStore(acc.StoreId);
var xpub = "tpubDDmH1briYfZcTDMEc7uMEA5hinzjUTzR9yMC1drxTMeiWyw1VyCqTuzBke6df2sqbfw9QG6wbgTLF5yLjcXsZNaXvJMZLwNEwyvmiFWcLav";
@ -3467,7 +3454,7 @@ namespace BTCPayServer.Tests
Assert.Equal(derivation, v.AccountOriginal.ToString());
Assert.Equal(xpub, v.SigningKey.ToString());
Assert.Equal(xpub, v.GetSigningAccountKeySettings().AccountKey.ToString());
await acc.RegisterLightningNodeAsync("BTC", LightningConnectionType.CLightning, true);
store = await tester.PayTester.StoreRepository.FindStore(acc.StoreId);
var lnMethod = store.GetSupportedPaymentMethods(tester.NetworkProvider).OfType<LightningSupportedPaymentMethod>().First();
@ -3614,43 +3601,43 @@ namespace BTCPayServer.Tests
var settings = tester.PayTester.GetService<SettingsRepository>();
var emailSenderFactory = tester.PayTester.GetService<EmailSenderFactory>();
Assert.Null(await Assert.IsType<ServerEmailSender>(emailSenderFactory.GetEmailSender()).GetEmailSettings());
Assert.Null(await Assert.IsType<StoreEmailSender>(emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings());
await settings.UpdateSetting(new PoliciesSettings() { DisableStoresToUseServerEmailSettings = false });
await settings.UpdateSetting(new EmailSettings()
{
From = "admin@admin.com",
Login = "admin@admin.com",
Password = "admin@admin.com",
Port = 1234,
Server = "admin.com",
EnableSSL = true
From = "admin@admin.com",
Login = "admin@admin.com",
Password = "admin@admin.com",
Port = 1234,
Server = "admin.com",
EnableSSL = true
});
Assert.Equal("admin@admin.com", (await Assert.IsType<ServerEmailSender>(emailSenderFactory.GetEmailSender()).GetEmailSettings()).Login);
Assert.Equal("admin@admin.com", (await Assert.IsType<StoreEmailSender>(emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings()).Login);
Assert.Equal("admin@admin.com",(await Assert.IsType<ServerEmailSender>(emailSenderFactory.GetEmailSender()).GetEmailSettings()).Login);
Assert.Equal("admin@admin.com",(await Assert.IsType<StoreEmailSender>(emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings()).Login);
await settings.UpdateSetting(new PoliciesSettings() { DisableStoresToUseServerEmailSettings = true });
Assert.Equal("admin@admin.com", (await Assert.IsType<ServerEmailSender>(emailSenderFactory.GetEmailSender()).GetEmailSettings()).Login);
Assert.Equal("admin@admin.com",(await Assert.IsType<ServerEmailSender>(emailSenderFactory.GetEmailSender()).GetEmailSettings()).Login);
Assert.Null(await Assert.IsType<StoreEmailSender>(emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings());
Assert.IsType<RedirectToActionResult>(await acc.GetController<StoresController>().Emails(acc.StoreId, new EmailsViewModel(new EmailSettings()
{
From = "store@store.com",
From = "store@store.com",
Login = "store@store.com",
Password = "store@store.com",
Port = 1234,
Server = "store.com",
EnableSSL = true
}), ""));
Assert.Equal("store@store.com", (await Assert.IsType<StoreEmailSender>(emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings()).Login);
Assert.Equal("store@store.com",(await Assert.IsType<StoreEmailSender>(emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings()).Login);
}
}
}
}

View File

@ -16,22 +16,23 @@ namespace BTCPayServer.Controllers
public string StoreId { get; set; }
public override string ToString()
{
return string.Empty;
return String.Empty;
}
}
[HttpGet("{appId}/settings/crowdfund")]
[HttpGet]
[Route("{appId}/settings/crowdfund")]
public async Task<IActionResult> UpdateCrowdfund(string appId)
{
var app = await GetOwnedApp(appId, AppType.Crowdfund);
if (app == null)
return NotFound();
var settings = app.GetSettings<CrowdfundSettings>();
var vm = new UpdateCrowdfundViewModel
var vm = new UpdateCrowdfundViewModel()
{
Title = settings.Title,
StoreId = app.StoreDataId,
StoreName = app.StoreData?.StoreName,
Enabled = settings.Enabled,
EnforceTargetAmount = settings.EnforceTargetAmount,
StartDate = settings.StartDate,

View File

@ -97,11 +97,10 @@ namespace BTCPayServer.Controllers
settings.DefaultView = settings.EnableShoppingCart ? PosViewType.Cart : settings.DefaultView;
settings.EnableShoppingCart = false;
var vm = new UpdatePointOfSaleViewModel
var vm = new UpdatePointOfSaleViewModel()
{
Id = appId,
StoreId = app.StoreDataId,
StoreName = app.StoreData?.StoreName,
Title = settings.Title,
DefaultView = settings.DefaultView,
ShowCustomAmount = settings.ShowCustomAmount,
@ -184,7 +183,7 @@ namespace BTCPayServer.Controllers
var app = await GetOwnedApp(appId, AppType.PointOfSale);
if (app == null)
return NotFound();
app.SetSettings(new PointOfSaleSettings
app.SetSettings(new PointOfSaleSettings()
{
Title = vm.Title,
DefaultView = vm.DefaultView,

View File

@ -11,6 +11,7 @@ using BTCPayServer.Client.Models;
using BTCPayServer.Data;
using BTCPayServer.HostedServices;
using BTCPayServer.Models.WalletViewModels;
using BTCPayServer.Payments.PayJoin;
using BTCPayServer.Payments.PayJoin.Sender;
using BTCPayServer.Services;
using BTCPayServer.Services.Wallets;
@ -122,9 +123,18 @@ namespace BTCPayServer.Controllers.GreenField
{
return BadRequest();
}
var bip21 = network.GenerateBIP21(kpi.Address.ToString(), null);
var allowedPayjoin = derivationScheme.IsHotWallet && Store.GetStoreBlob().PayJoinEnabled;
if (allowedPayjoin)
{
bip21 +=
$"?{PayjoinClient.BIP21EndpointKey}={Request.GetAbsoluteUri(Url.Action(nameof(PayJoinEndpointController.Submit), "PayJoinEndpoint", new {cryptoCode}))}";
}
return Ok(new OnChainWalletAddressData()
{
Address = kpi.Address.ToString(),
PaymentLink = bip21,
KeyPath = kpi.KeyPath
});
}
@ -323,7 +333,7 @@ namespace BTCPayServer.Controllers.GreenField
request.AddModelError(transactionRequest => transactionRequest.Destinations[index],
"Amount must be specified or destination must be a BIP21 payment link, and greater than 0", this);
}
if (request.ProceedWithPayjoin && bip21?.UnknowParameters?.ContainsKey("pj") is true)
if (request.ProceedWithPayjoin && bip21?.UnknowParameters?.ContainsKey(PayjoinClient.BIP21EndpointKey) is true)
{
payjoinOutputIndex = index;
}

View File

@ -120,7 +120,7 @@ namespace BTCPayServer.Controllers
ExpirationDate = invoice.ExpirationTime,
MonitoringDate = invoice.MonitoringExpiration,
Fiat = _CurrencyNameTable.DisplayFormatCurrency(invoice.Price, invoice.Currency),
TaxIncluded = _CurrencyNameTable.DisplayFormatCurrency(invoice.Metadata.TaxIncluded.AsDecimal() ?? 0.0m, invoice.Currency),
TaxIncluded = _CurrencyNameTable.DisplayFormatCurrency(invoice.Metadata.TaxIncluded ?? 0.0m, invoice.Currency),
NotificationUrl = invoice.NotificationURL?.AbsoluteUri,
RedirectUrl = invoice.RedirectURL?.AbsoluteUri,
TypedMetadata = invoice.Metadata,
@ -539,7 +539,7 @@ namespace BTCPayServer.Controllers
Activated = paymentMethodDetails.Activated,
CryptoCode = network.CryptoCode,
RootPath = this.Request.PathBase.Value.WithTrailingSlash(),
OrderId = invoice.Metadata.OrderId.AsString(),
OrderId = invoice.Metadata.OrderId,
InvoiceId = invoice.Id,
DefaultLang = lang ?? invoice.DefaultLanguage ?? storeBlob.DefaultLang ?? "en",
CustomCSSLink = storeBlob.CustomCSS,
@ -556,7 +556,7 @@ namespace BTCPayServer.Controllers
ExpirationSeconds = Math.Max(0, (int)(invoice.ExpirationTime - DateTimeOffset.UtcNow).TotalSeconds),
MaxTimeSeconds = (int)(invoice.ExpirationTime - invoice.InvoiceTime).TotalSeconds,
MaxTimeMinutes = (int)(invoice.ExpirationTime - invoice.InvoiceTime).TotalMinutes,
ItemDesc = invoice.Metadata.ItemDesc.AsString(),
ItemDesc = invoice.Metadata.ItemDesc,
Rate = ExchangeRate(paymentMethod),
MerchantRefLink = invoice.RedirectURL?.AbsoluteUri ?? "/",
RedirectAutomatically = invoice.RedirectAutomatically,
@ -725,7 +725,7 @@ namespace BTCPayServer.Controllers
ShowCheckout = invoice.Status == InvoiceStatusLegacy.New,
Date = invoice.InvoiceTime,
InvoiceId = invoice.Id,
OrderId = invoice.Metadata.OrderId.AsString() ?? string.Empty,
OrderId = invoice.Metadata.OrderId ?? string.Empty,
RedirectUrl = invoice.RedirectURL?.AbsoluteUri ?? string.Empty,
AmountCurrency = _CurrencyNameTable.DisplayFormatCurrency(invoice.Price, invoice.Currency),
CanMarkInvalid = state.CanMarkInvalid(),
@ -900,55 +900,42 @@ namespace BTCPayServer.Controllers
public class PosDataParser
{
public static Dictionary<string, object> ParsePosData(JToken posData)
public static Dictionary<string, object> ParsePosData(string posData)
{
var result = new Dictionary<string, object>();
if (posData is null)
if (string.IsNullOrEmpty(posData))
{
return result;
}
JObject posDataJObj = posData as JObject;
if (posDataJObj is null)
try
{
if (posData.AsString() is string posDataStr)
var jObject = JObject.Parse(posData);
foreach (var item in jObject)
{
if (string.IsNullOrEmpty(posDataStr))
return result;
try
switch (item.Value.Type)
{
posDataJObj = JObject.Parse(posDataStr);
}
catch
{
result.TryAdd(string.Empty, posDataStr);
case JTokenType.Array:
var items = item.Value.AsEnumerable().ToList();
for (var i = 0; i < items.Count; i++)
{
result.Add($"{item.Key}[{i}]", ParsePosData(items[i].ToString()));
}
break;
case JTokenType.Object:
result.Add(item.Key, ParsePosData(item.Value.ToString()));
break;
default:
result.Add(item.Key, item.Value.ToString());
break;
}
}
}
if (posDataJObj is null)
return result;
foreach (var item in posDataJObj)
catch
{
switch (item.Value.Type)
{
case JTokenType.Array:
var items = item.Value.AsEnumerable().ToList();
for (var i = 0; i < items.Count; i++)
{
result.TryAdd($"{item.Key}[{i}]", ParsePosData(items[i]));
}
break;
case JTokenType.Object:
result.TryAdd(item.Key, ParsePosData(item.Value));
break;
default:
result.TryAdd(item.Key, item.Value.ToString());
break;
}
result.Add(string.Empty, posData);
}
return result;
}

View File

@ -196,9 +196,9 @@ namespace BTCPayServer.Controllers
if (entity.Metadata.BuyerEmail != null)
{
if (!EmailValidator.IsEmail(entity.Metadata.BuyerEmail.AsString()))
if (!EmailValidator.IsEmail(entity.Metadata.BuyerEmail))
throw new BitpayHttpException(400, "Invalid email");
entity.RefundMail = entity.Metadata.BuyerEmail.AsString();
entity.RefundMail = entity.Metadata.BuyerEmail;
}
entity.Status = InvoiceStatusLegacy.New;
HashSet<CurrencyPair> currencyPairsToFetch = new HashSet<CurrencyPair>();

View File

@ -29,10 +29,7 @@ namespace BTCPayServer.Controllers
var usersQuery = _UserManager.Users;
if (!string.IsNullOrWhiteSpace(model.SearchTerm))
{
#pragma warning disable CA1307 // Specify StringComparison
// This is Entity query, can't pass string comparison without crashing
usersQuery = usersQuery.Where(u => u.Email.Contains(model.SearchTerm));
#pragma warning restore CA1307 // Specify StringComparison
}
if (sortOrder != null)

View File

@ -857,12 +857,6 @@ namespace BTCPayServer.Controllers
if (string.IsNullOrWhiteSpace(userId))
return Challenge(AuthenticationSchemes.Cookie);
var storeId = CurrentStore?.Id;
if (storeId != null)
{
var store = await _Repo.FindStore(storeId, userId);
if (store != null)
HttpContext.SetStoreData(store);
}
var model = new CreateTokenViewModel();
ViewBag.HidePublicKey = true;
ViewBag.ShowStores = true;
@ -919,14 +913,6 @@ namespace BTCPayServer.Controllers
return Challenge(AuthenticationSchemes.Cookie);
if (pairingCode == null)
return NotFound();
if (selectedStore != null)
{
var store = await _Repo.FindStore(selectedStore, userId);
if (store == null)
return NotFound();
HttpContext.SetStoreData(store);
ViewBag.ShowStores = false;
}
var pairing = await _TokenRepository.GetPairingAsync(pairingCode);
if (pairing == null)
{
@ -936,7 +922,7 @@ namespace BTCPayServer.Controllers
else
{
var stores = await _Repo.GetStoresByUserId(userId);
return View(new PairingModel
return View(new PairingModel()
{
Id = pairing.Id,
Label = pairing.Label,
@ -995,6 +981,8 @@ namespace BTCPayServer.Controllers
return _UserManager.GetUserId(User);
}
// TODO: Need to have talk about how architect default currency implementation
// For now we have also hardcoded USD for Store creation and then Invoice creation
const string DEFAULT_CURRENCY = "USD";

View File

@ -27,6 +27,7 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using NBitcoin;
using BTCPayServer.BIP78.Sender;
using BTCPayServer.Payments.PayJoin;
using NBitcoin.DataEncoders;
using NBXplorer;
using NBXplorer.DerivationStrategy;
@ -341,8 +342,6 @@ namespace BTCPayServer.Controllers
model.Transactions = model.Transactions.OrderByDescending(t => t.Timestamp).Skip(skip).Take(count).ToList();
}
model.CryptoCode = walletId.CryptoCode;
return View(model);
}
@ -364,13 +363,20 @@ namespace BTCPayServer.Controllers
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId?.CryptoCode);
if (network == null)
return NotFound();
var address = _walletReceiveService.Get(walletId)?.Address;
var allowedPayjoin = paymentMethod.IsHotWallet && CurrentStore.GetStoreBlob().PayJoinEnabled;
var bip21 = address is null ? null : network.GenerateBIP21(address.ToString(), null);
if (allowedPayjoin)
{
bip21 +=
$"?{PayjoinClient.BIP21EndpointKey}={Request.GetAbsoluteUri(Url.Action(nameof(PayJoinEndpointController.Submit), "PayJoinEndpoint", new {walletId.CryptoCode}))}";
}
return View(new WalletReceiveViewModel()
{
CryptoCode = walletId.CryptoCode,
Address = address?.ToString(),
CryptoImage = GetImage(paymentMethod.PaymentId, network)
CryptoImage = GetImage(paymentMethod.PaymentId, network),
PaymentLink = bip21
});
}
@ -724,7 +730,7 @@ namespace BTCPayServer.Controllers
{
new WalletSendModel.TransactionOutput()
{
Amount = uriBuilder.Amount.ToDecimal(MoneyUnit.BTC),
Amount = uriBuilder.Amount?.ToDecimal(MoneyUnit.BTC),
DestinationAddress = uriBuilder.Address.ToString(),
SubtractFeesFromOutput = false
}
@ -1179,6 +1185,7 @@ namespace BTCPayServer.Controllers
public string CryptoImage { get; set; }
public string CryptoCode { get; set; }
public string Address { get; set; }
public string PaymentLink { get; set; }
}

View File

@ -34,7 +34,6 @@ using NBitpayClient;
using NBXplorer.DerivationStrategy;
using NBXplorer.Models;
using Newtonsoft.Json.Linq;
using System.Numerics;
namespace BTCPayServer
{
@ -428,42 +427,6 @@ namespace BTCPayServer
{
return ctx.Items.TryGet("BTCPAY.STOREDATA") as StoreData;
}
// https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Linq/JToken.cs#L1235
private static readonly JTokenType[] StringTypes = new[] { JTokenType.Date, JTokenType.Integer, JTokenType.Float, JTokenType.String, JTokenType.Comment, JTokenType.Raw, JTokenType.Boolean, JTokenType.Bytes, JTokenType.Guid, JTokenType.TimeSpan, JTokenType.Uri };
public static string AsString(this JToken tok)
{
if (tok is JValue v && StringTypes.Contains(v.Type))
{
if (v.Value is null)
return null;
if (v.Value is byte[] bytes)
return Convert.ToBase64String(bytes);
if (v.Value is BigInteger bigint)
return bigint.ToString(CultureInfo.InvariantCulture);
return Convert.ToString(v.Value, CultureInfo.InvariantCulture);
}
return null;
}
// https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Linq/JToken.cs#L1108
private static readonly JTokenType[] NumberTypes = new[] { JTokenType.Integer, JTokenType.Float, JTokenType.String, JTokenType.Comment, JTokenType.Raw, JTokenType.Boolean };
public static decimal? AsDecimal(this JToken tok)
{
if (tok is JValue v && NumberTypes.Contains(v.Type))
{
if (v.Value is null)
return null;
if (v.Value is BigInteger bigint)
return (decimal)bigint;
return Convert.ToDecimal(v.Value, CultureInfo.InvariantCulture);
}
return null;
}
public static void SetStoreData(this HttpContext ctx, StoreData storeData)
{
ctx.Items["BTCPAY.STOREDATA"] = storeData;

View File

@ -35,7 +35,7 @@ namespace BTCPayServer
{
var negative = sats < 0;
var amt = sats.ToString(CultureInfo.InvariantCulture)
.Replace("-", "", StringComparison.OrdinalIgnoreCase)
.Replace("-", "", StringComparison.InvariantCulture)
.PadLeft(divisibility, '0');
amt = amt.Length == divisibility ? $"0.{amt}" : amt.Insert(amt.Length - divisibility, ".");
return decimal.Parse($"{(negative? "-": string.Empty)}{amt}", CultureInfo.InvariantCulture);

View File

@ -104,8 +104,8 @@ namespace BTCPayServer.HostedServices
return;
}
if ((!string.IsNullOrEmpty(invoiceEvent.Invoice.Metadata.ItemCode.AsString()) ||
AppService.TryParsePosCartItems(invoiceEvent.Invoice.Metadata.PosData.AsString(), out cartItems)))
if ((!string.IsNullOrEmpty(invoiceEvent.Invoice.Metadata.ItemCode) ||
AppService.TryParsePosCartItems(invoiceEvent.Invoice.Metadata.PosData, out cartItems)))
{
var appIds = AppService.GetAppInternalTags(invoiceEvent.Invoice);
@ -115,9 +115,9 @@ namespace BTCPayServer.HostedServices
}
var items = cartItems ?? new Dictionary<string, int>();
if (!string.IsNullOrEmpty(invoiceEvent.Invoice.Metadata.ItemCode.AsString()))
if (!string.IsNullOrEmpty(invoiceEvent.Invoice.Metadata.ItemCode))
{
items.TryAdd(invoiceEvent.Invoice.Metadata.ItemCode.AsString(), 1);
items.TryAdd(invoiceEvent.Invoice.Metadata.ItemCode, 1);
}
_eventAggregator.Publish(new UpdateAppInventory()

View File

@ -89,9 +89,9 @@ namespace BTCPayServer.HostedServices
textSearch.Add(invoice.Id);
textSearch.Add(invoice.InvoiceTime.ToString(CultureInfo.InvariantCulture));
textSearch.Add(invoice.Price.ToString(CultureInfo.InvariantCulture));
textSearch.Add(invoice.Metadata.OrderId.AsString());
textSearch.Add(invoice.Metadata.OrderId);
textSearch.Add(invoice.StoreId);
textSearch.Add(invoice.Metadata.BuyerEmail.AsString());
textSearch.Add(invoice.Metadata.BuyerEmail);
//
textSearch.Add(invoice.RefundMail);
// TODO: Are there more things to cache? PaymentData?

View File

@ -1,36 +1,23 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.NetworkInformation;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using Amazon.Runtime.Internal;
using Amazon.S3.Model;
using BTCPayServer.Client.Models;
using BTCPayServer.Data;
using BTCPayServer.Events;
using BTCPayServer.Logging;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Stores;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using NBitcoin;
using NBitcoin.DataEncoders;
using NBitcoin.Secp256k1;
using NBitpayClient;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using Org.BouncyCastle.Ocsp;
using TwentyTwenty.Storage;
namespace BTCPayServer.HostedServices
{

View File

@ -9,8 +9,6 @@ namespace BTCPayServer.Models.AppViewModels
public class UpdateCrowdfundViewModel
{
public string StoreId { get; set; }
public string StoreName { get; set; }
[Required]
[MaxLength(30)]
public string Title { get; set; }
@ -97,7 +95,12 @@ namespace BTCPayServer.Models.AppViewModels
[Display(Name = "Colors to rotate between with animation when a payment is made. First color is the default background. One color per line. Can be any valid css color value.")]
public string AnimationColors { get; set; }
// NOTE: Improve validation if needed
public bool ModelWithMinimumData => Description != null && Title != null && TargetCurrency != null;
public bool ModelWithMinimumData
{
get { return Description != null && Title != null && TargetCurrency != null; }
}
}
}

View File

@ -9,8 +9,6 @@ namespace BTCPayServer.Models.AppViewModels
public class UpdatePointOfSaleViewModel
{
public string StoreId { get; set; }
public string StoreName { get; set; }
[Required]
[MaxLength(30)]
public string Title { get; set; }

View File

@ -19,6 +19,5 @@ namespace BTCPayServer.Models.WalletViewModels
}
public HashSet<ColoredLabel> Labels { get; set; } = new HashSet<ColoredLabel>();
public List<TransactionViewModel> Transactions { get; set; } = new List<TransactionViewModel>();
public string CryptoCode { get; set; }
}
}

View File

@ -84,8 +84,8 @@ namespace BTCPayServer.Payments.Lightning
string description = storeBlob.LightningDescriptionTemplate;
description = description.Replace("{StoreName}", store.StoreName ?? "", StringComparison.OrdinalIgnoreCase)
.Replace("{ItemDescription}", invoice.Metadata.ItemDesc.AsString() ?? "", StringComparison.OrdinalIgnoreCase)
.Replace("{OrderId}", invoice.Metadata.OrderId.AsString() ?? "", StringComparison.OrdinalIgnoreCase);
.Replace("{ItemDescription}", invoice.Metadata.ItemDesc ?? "", StringComparison.OrdinalIgnoreCase)
.Replace("{OrderId}", invoice.Metadata.OrderId ?? "", StringComparison.OrdinalIgnoreCase);
using (var cts = new CancellationTokenSource(LIGHTNING_TIMEOUT))
{
try

View File

@ -89,6 +89,8 @@ namespace BTCPayServer.Payments.PayJoin
private readonly NBXplorerDashboard _dashboard;
private readonly DelayedTransactionBroadcaster _broadcaster;
private readonly BTCPayServerEnvironment _env;
private readonly WalletReceiveService _walletReceiveService;
private readonly StoreRepository _storeRepository;
public PayJoinEndpointController(BTCPayNetworkProvider btcPayNetworkProvider,
InvoiceRepository invoiceRepository, ExplorerClientProvider explorerClientProvider,
@ -97,7 +99,9 @@ namespace BTCPayServer.Payments.PayJoin
EventAggregator eventAggregator,
NBXplorerDashboard dashboard,
DelayedTransactionBroadcaster broadcaster,
BTCPayServerEnvironment env)
BTCPayServerEnvironment env,
WalletReceiveService walletReceiveService,
StoreRepository storeRepository)
{
_btcPayNetworkProvider = btcPayNetworkProvider;
_invoiceRepository = invoiceRepository;
@ -108,6 +112,8 @@ namespace BTCPayServer.Payments.PayJoin
_dashboard = dashboard;
_broadcaster = broadcaster;
_env = env;
_walletReceiveService = walletReceiveService;
_storeRepository = storeRepository;
}
[HttpPost("")]
@ -238,17 +244,34 @@ namespace BTCPayServer.Payments.PayJoin
KeyPath paymentAddressIndex = null;
InvoiceEntity invoice = null;
DerivationSchemeSettings derivationSchemeSettings = null;
WalletId walletId = null;
foreach (var output in psbt.Outputs)
{
var key = output.ScriptPubKey.Hash + "#" + network.CryptoCode.ToUpperInvariant();
invoice = (await _invoiceRepository.GetInvoicesFromAddresses(new[] { key })).FirstOrDefault();
if (invoice is null)
continue;
derivationSchemeSettings = invoice.GetSupportedPaymentMethod<DerivationSchemeSettings>(paymentMethodId)
.SingleOrDefault();
var walletReceiveMatch =
_walletReceiveService.GetByScriptPubKey(network.CryptoCode, output.ScriptPubKey);
if (walletReceiveMatch is null)
{
var key = output.ScriptPubKey.Hash + "#" + network.CryptoCode.ToUpperInvariant();
invoice = (await _invoiceRepository.GetInvoicesFromAddresses(new[] {key})).FirstOrDefault();
if (invoice is null)
continue;
derivationSchemeSettings = invoice
.GetSupportedPaymentMethod<DerivationSchemeSettings>(paymentMethodId)
.SingleOrDefault();
walletId = new WalletId(invoice.StoreId, network.CryptoCode.ToUpperInvariant());
}
else
{
var store = await _storeRepository.FindStore(walletReceiveMatch.Item1.StoreId);
derivationSchemeSettings = store.GetDerivationSchemeSettings(_btcPayNetworkProvider,
walletReceiveMatch.Item1.CryptoCode);
walletId = walletReceiveMatch.Item1;
}
if (derivationSchemeSettings is null)
continue;
var receiverInputsType = derivationSchemeSettings.AccountDerivation.ScriptPubKeyType();
if (receiverInputsType == ScriptPubKeyType.Legacy)
{
@ -259,24 +282,39 @@ namespace BTCPayServer.Payments.PayJoin
{
return CreatePayjoinErrorAndLog(503, PayjoinReceiverWellknownErrors.Unavailable, "We do not have any UTXO available for making a payjoin with the sender's inputs type");
}
var paymentMethod = invoice.GetPaymentMethod(paymentMethodId);
var paymentDetails =
paymentMethod.GetPaymentMethodDetails() as Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod;
if (paymentDetails is null || !paymentDetails.PayjoinEnabled || !paymentDetails.Activated)
continue;
if (invoice.GetAllBitcoinPaymentData().Any())
if (walletReceiveMatch is null)
{
ctx.DoNotBroadcast();
return UnprocessableEntity(CreatePayjoinError("already-paid",
$"The invoice this PSBT is paying has already been partially or completely paid"));
var paymentMethod = invoice.GetPaymentMethod(paymentMethodId);
var paymentDetails =
paymentMethod.GetPaymentMethodDetails() as Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod;
if (paymentDetails is null || !paymentDetails.PayjoinEnabled)
continue;
paidSomething = true;
due = paymentMethod.Calculate().TotalDue - output.Value;
if (due > Money.Zero)
{
break;
}
paymentAddress = paymentDetails.GetDepositAddress(network.NBitcoinNetwork);
paymentAddressIndex = paymentDetails.KeyPath;
if (invoice.GetAllBitcoinPaymentData().Any())
{
ctx.DoNotBroadcast();
return UnprocessableEntity(CreatePayjoinError("already-paid",
$"The invoice this PSBT is paying has already been partially or completely paid"));
}
}
else
{
paidSomething = true;
due = Money.Zero;
paymentAddress = walletReceiveMatch.Item2.Address;
paymentAddressIndex = walletReceiveMatch.Item2.KeyPath;
}
paidSomething = true;
due = paymentMethod.Calculate().TotalDue - output.Value;
if (due > Money.Zero)
{
break;
}
if (!await _payJoinRepository.TryLockInputs(ctx.OriginalTransaction.Inputs.Select(i => i.PrevOut).ToArray()))
{
@ -299,8 +337,6 @@ namespace BTCPayServer.Payments.PayJoin
}
ctx.LockedUTXOs = selectedUTXOs.Select(u => u.Key).ToArray();
originalPaymentOutput = output;
paymentAddress = paymentDetails.GetDepositAddress(network.NBitcoinNetwork);
paymentAddressIndex = paymentDetails.KeyPath;
break;
}
@ -448,22 +484,27 @@ namespace BTCPayServer.Payments.PayJoin
CoinjoinValue = originalPaymentValue - ourFeeContribution,
ContributedOutPoints = selectedUTXOs.Select(o => o.Key).ToArray()
};
var payment = await _invoiceRepository.AddPayment(invoice.Id, DateTimeOffset.UtcNow, originalPaymentData, network, true);
if (payment is null)
if (invoice != null)
{
return UnprocessableEntity(CreatePayjoinError("already-paid",
$"The original transaction has already been accounted"));
var payment = await _invoiceRepository.AddPayment(invoice.Id, DateTimeOffset.UtcNow, originalPaymentData, network, true);
if (payment is null)
{
return UnprocessableEntity(CreatePayjoinError("already-paid",
$"The original transaction has already been accounted"));
}
_eventAggregator.Publish(new InvoiceEvent(invoice,InvoiceEvent.ReceivedPayment) { Payment = payment });
}
await _btcPayWalletProvider.GetWallet(network).SaveOffchainTransactionAsync(ctx.OriginalTransaction);
_eventAggregator.Publish(new InvoiceEvent(invoice,InvoiceEvent.ReceivedPayment) { Payment = payment });
_eventAggregator.Publish(new UpdateTransactionLabel()
{
WalletId = new WalletId(invoice.StoreId, network.CryptoCode),
WalletId = walletId,
TransactionLabels = selectedUTXOs.GroupBy(pair => pair.Key.Hash).Select(utxo =>
new KeyValuePair<uint256, List<(string color, Label label)>>(utxo.Key,
new List<(string color, Label label)>()
{
UpdateTransactionLabel.PayjoinExposedLabelTemplate(invoice.Id)
UpdateTransactionLabel.PayjoinExposedLabelTemplate(invoice?.Id)
}))
.ToDictionary(pair => pair.Key, pair => pair.Value)
});

View File

@ -28,7 +28,7 @@ namespace BTCPayServer.Plugins
public static bool IsExceptionByPlugin(Exception exception)
{
return _pluginAssemblies.Any(assembly => assembly.FullName.Contains(exception.Source, StringComparison.OrdinalIgnoreCase));
return _pluginAssemblies.Any(assembly => assembly.FullName.Contains(exception.Source));
}
public static IMvcBuilder AddPlugins(this IMvcBuilder mvcBuilder, IServiceCollection serviceCollection,
IConfiguration config, ILoggerFactory loggerFactory)

View File

@ -98,8 +98,8 @@ namespace BTCPayServer.Services.Apps
var currentPayments = GetContributionsByPaymentMethodId(settings.TargetCurrency, completeInvoices, !settings.EnforceTargetAmount);
var perkCount = paidInvoices
.Where(entity => !string.IsNullOrEmpty(entity.Metadata.ItemCode.AsString()))
.GroupBy(entity => entity.Metadata.ItemCode.AsString())
.Where(entity => !string.IsNullOrEmpty(entity.Metadata.ItemCode))
.GroupBy(entity => entity.Metadata.ItemCode)
.ToDictionary(entities => entities.Key, entities => entities.Count());
var perks = Parse(settings.PerksTemplate, settings.TargetCurrency);

View File

@ -87,7 +87,7 @@ namespace BTCPayServer.Services.Invoices.Export
// while looking just at export you could sum Paid and assume merchant "received payments"
NetworkFee = payment.NetworkFee.ToString(CultureInfo.InvariantCulture),
InvoiceDue = Math.Round(invoiceDue, currency.NumberDecimalDigits),
OrderId = invoice.Metadata.OrderId.AsString() ?? string.Empty,
OrderId = invoice.Metadata.OrderId ?? string.Empty,
StoreId = invoice.StoreId,
InvoiceId = invoice.Id,
InvoiceCreatedDate = invoice.InvoiceTime.UtcDateTime,
@ -98,11 +98,11 @@ namespace BTCPayServer.Services.Invoices.Export
InvoiceStatus = invoice.StatusString,
InvoiceExceptionStatus = invoice.ExceptionStatusString,
#pragma warning restore CS0618 // Type or member is obsolete
InvoiceItemCode = invoice.Metadata.ItemCode.AsString(),
InvoiceItemDesc = invoice.Metadata.ItemDesc.AsString(),
InvoiceItemCode = invoice.Metadata.ItemCode,
InvoiceItemDesc = invoice.Metadata.ItemDesc,
InvoicePrice = invoice.Price,
InvoiceCurrency = invoice.Currency,
BuyerEmail = invoice.Metadata.BuyerEmail.AsString()
BuyerEmail = invoice.Metadata.BuyerEmail
};
exportList.Add(target);

View File

@ -30,39 +30,67 @@ namespace BTCPayServer.Services.Invoices
seria.ContractResolver = new CamelCasePropertyNamesContractResolver();
MetadataSerializer = seria;
}
public JToken OrderId { get; set; }
public string OrderId { get; set; }
[JsonProperty(PropertyName = "buyerName")]
public JToken BuyerName { get; set; }
public string BuyerName { get; set; }
[JsonProperty(PropertyName = "buyerEmail")]
public JToken BuyerEmail { get; set; }
public string BuyerEmail { get; set; }
[JsonProperty(PropertyName = "buyerCountry")]
public JToken BuyerCountry { get; set; }
public string BuyerCountry { get; set; }
[JsonProperty(PropertyName = "buyerZip")]
public JToken BuyerZip { get; set; }
public string BuyerZip { get; set; }
[JsonProperty(PropertyName = "buyerState")]
public JToken BuyerState { get; set; }
public string BuyerState { get; set; }
[JsonProperty(PropertyName = "buyerCity")]
public JToken BuyerCity { get; set; }
public string BuyerCity { get; set; }
[JsonProperty(PropertyName = "buyerAddress2")]
public JToken BuyerAddress2 { get; set; }
public string BuyerAddress2 { get; set; }
[JsonProperty(PropertyName = "buyerAddress1")]
public JToken BuyerAddress1 { get; set; }
public string BuyerAddress1 { get; set; }
[JsonProperty(PropertyName = "buyerPhone")]
public JToken BuyerPhone { get; set; }
public string BuyerPhone { get; set; }
[JsonProperty(PropertyName = "itemDesc")]
public JToken ItemDesc { get; set; }
public string ItemDesc { get; set; }
[JsonProperty(PropertyName = "itemCode")]
public JToken ItemCode { get; set; }
public string ItemCode { get; set; }
[JsonProperty(PropertyName = "physical")]
public JToken Physical { get; set; }
public bool? Physical { get; set; }
[JsonProperty(PropertyName = "taxIncluded", DefaultValueHandling = DefaultValueHandling.Ignore)]
public JToken TaxIncluded { get; set; }
public decimal? TaxIncluded { get; set; }
[JsonIgnore]
public string PosData
{
get
{
return PosRawData?.ToString();
}
set
{
if (value is null)
{
PosRawData = JValue.CreateNull();
}
else
{
try
{
PosRawData = JToken.Parse(value);
}
catch (Exception )
{
PosRawData = JToken.FromObject(value);
}
}
}
}
[JsonProperty(PropertyName = "posData")]
public JToken PosData { get; set; }
public JToken PosRawData { get; set; }
[JsonExtensionData]
public IDictionary<string, JToken> AdditionalData { get; set; }
@ -255,11 +283,9 @@ namespace BTCPayServer.Services.Invoices
private Uri FillPlaceholdersUri(string v)
{
v ??= string.Empty;
string orderId = Metadata.OrderId.AsString();
v = v.Replace("{OrderId}", orderId is null ? string.Empty : System.Web.HttpUtility.UrlEncode(orderId), StringComparison.OrdinalIgnoreCase);
v = v.Replace("{InvoiceId}", System.Web.HttpUtility.UrlEncode(Id), StringComparison.OrdinalIgnoreCase);
if (Uri.TryCreate(v, UriKind.Absolute, out var uri) && (uri.Scheme == "http" || uri.Scheme == "https"))
var uriStr = (v ?? string.Empty).Replace("{OrderId}", System.Web.HttpUtility.UrlEncode(Metadata.OrderId) ?? "", StringComparison.OrdinalIgnoreCase)
.Replace("{InvoiceId}", System.Web.HttpUtility.UrlEncode(Id) ?? "", StringComparison.OrdinalIgnoreCase);
if (Uri.TryCreate(uriStr, UriKind.Absolute, out var uri) && (uri.Scheme == "http" || uri.Scheme == "https"))
return uri;
return null;
}
@ -304,8 +330,8 @@ namespace BTCPayServer.Services.Invoices
{
Id = Id,
StoreId = StoreId,
OrderId = Metadata.OrderId.AsString(),
PosData = Metadata.PosData.AsString(),
OrderId = Metadata.OrderId,
PosData = Metadata.PosData,
CurrentTime = DateTimeOffset.UtcNow,
InvoiceTime = InvoiceTime,
ExpirationTime = ExpirationTime,
@ -423,9 +449,9 @@ namespace BTCPayServer.Services.Invoices
//dto.AmountPaid dto.MinerFees & dto.TransactionCurrency are not supported by btcpayserver as we have multi currency payment support per invoice
dto.ItemCode = Metadata.ItemCode.AsString();
dto.ItemDesc = Metadata.ItemDesc.AsString();
dto.TaxIncluded = Metadata.TaxIncluded.AsDecimal() ?? 0m;
dto.ItemCode = Metadata.ItemCode;
dto.ItemDesc = Metadata.ItemDesc;
dto.TaxIncluded = Metadata.TaxIncluded ?? 0m;
dto.Price = Price;
dto.Currency = Currency;
dto.Buyer = new JObject();
@ -437,7 +463,7 @@ namespace BTCPayServer.Services.Invoices
dto.Buyer.Add(new JProperty("postalCode", Metadata.BuyerZip));
dto.Buyer.Add(new JProperty("country", Metadata.BuyerCountry));
dto.Buyer.Add(new JProperty("phone", Metadata.BuyerPhone));
dto.Buyer.Add(new JProperty("email", string.IsNullOrWhiteSpace(Metadata.BuyerEmail.AsString()) ? RefundMail : Metadata.BuyerEmail));
dto.Buyer.Add(new JProperty("email", string.IsNullOrWhiteSpace(Metadata.BuyerEmail) ? RefundMail : Metadata.BuyerEmail));
dto.Token = Encoders.Base58.EncodeData(RandomUtils.GetBytes(16)); //No idea what it is useful for
dto.Guid = Guid.NewGuid().ToString();

View File

@ -166,11 +166,11 @@ namespace BTCPayServer.Services.Invoices
Id = invoice.Id,
Created = invoice.InvoiceTime,
Blob = ToBytes(invoice, null),
OrderId = invoice.Metadata.OrderId.AsString(),
OrderId = invoice.Metadata.OrderId,
#pragma warning disable CS0618 // Type or member is obsolete
Status = invoice.StatusString,
#pragma warning restore CS0618 // Type or member is obsolete
ItemCode = invoice.Metadata.ItemCode.AsString(),
ItemCode = invoice.Metadata.ItemCode,
CustomerEmail = invoice.RefundMail,
Archived = false
};
@ -207,9 +207,9 @@ namespace BTCPayServer.Services.Invoices
textSearch.Add(invoice.Id);
textSearch.Add(invoice.InvoiceTime.ToString(CultureInfo.InvariantCulture));
textSearch.Add(invoice.Price.ToString(CultureInfo.InvariantCulture));
textSearch.Add(invoice.Metadata.OrderId.AsString());
textSearch.Add(invoice.Metadata.OrderId);
textSearch.Add(invoice.StoreId);
textSearch.Add(invoice.Metadata.BuyerEmail.AsString());
textSearch.Add(invoice.Metadata.BuyerEmail);
AddToTextSearch(context, invoiceData, textSearch.ToArray());
await context.SaveChangesAsync().ConfigureAwait(false);
@ -597,7 +597,7 @@ namespace BTCPayServer.Services.Invoices
{
entity.Events = invoice.Events.OrderBy(c => c.Timestamp).ToList();
}
if (!string.IsNullOrEmpty(entity.RefundMail) && string.IsNullOrEmpty(entity.Metadata.BuyerEmail.AsString()))
if (!string.IsNullOrEmpty(entity.RefundMail) && string.IsNullOrEmpty(entity.Metadata.BuyerEmail))
{
entity.Metadata.BuyerEmail = entity.RefundMail;
}

View File

@ -10,7 +10,7 @@ namespace BTCPayServer.Services.Labels
{
static void FixLegacy(JObject jObj, ReferenceLabel refLabel)
{
if (refLabel.Reference is null)
if (refLabel.Reference is null && jObj.ContainsKey("id"))
refLabel.Reference = jObj["id"].Value<string>();
FixLegacy(jObj, (Label)refLabel);
}

View File

@ -52,6 +52,7 @@ namespace BTCPayServer.Services.Wallets
}
await explorerClient.CancelReservationAsync(kpi.DerivationStrategy, new[] {kpi.KeyPath});
Remove(walletId);
return kpi.Address.ToString();
}
@ -128,5 +129,19 @@ namespace BTCPayServer.Services.Wallets
_leases.Dispose();
return Task.CompletedTask;
}
public Tuple<WalletId, KeyPathInformation>? GetByScriptPubKey(string cryptoCode,Script script)
{
var match = _walletReceiveState.Where(pair =>
pair.Key.CryptoCode.Equals(cryptoCode, StringComparison.InvariantCulture) &&
pair.Value.ScriptPubKey == script);
if (match.Any())
{
var f =match.First();
return new Tuple<WalletId, KeyPathInformation>(f.Key, f.Value);
}
return null;
}
}
}

View File

@ -5,8 +5,14 @@
<section>
<div class="container">
<partial name="_StatusMessage" />
@if (TempData.HasStatusMessage())
{
<div class="row">
<div class="col-lg-12 text-center">
<partial name="_StatusMessage" />
</div>
</div>
}
<div class="row justify-content-center">
<div class="col-md-6 section-heading">
<h2>@ViewData["Title"]</h2>
@ -33,7 +39,6 @@
</div>
</div>
</section>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -7,7 +7,7 @@
}
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
}
<div class="row justify-content-center mb-2">
@ -18,7 +18,10 @@
<h1 class="h2 mb-3">Welcome to your BTCPay&nbsp;Server</h1>
<partial name="_StatusMessage" />
@if (TempData.HasStatusMessage())
{
<partial name="_StatusMessage"/>
}
</div>
</div>

View File

@ -43,5 +43,5 @@
</section>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -33,5 +33,5 @@
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -6,7 +6,7 @@
}
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
}
<div class="row justify-content-center mb-2">
@ -22,7 +22,10 @@
<span class="d-sm-block">It is secure, private, censorship-resistant and free.</span>
</p>
<partial name="_StatusMessage" />
@if (TempData.HasStatusMessage())
{
<partial name="_StatusMessage"/>
}
</div>
</div>

View File

@ -36,7 +36,6 @@
</div>
</div>
</section>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,11 +1,12 @@
@model BTCPayServer.Models.AccountViewModels.SetPasswordViewModel
@{
ViewData["Title"] = "Reset password";
Layout = "_LayoutSimple";
}
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
}
<div class="row justify-content-center mb-2">
@ -16,7 +17,10 @@
<h1 class="h2 mb-3">Welcome to your BTCPay&nbsp;Server</h1>
<partial name="_StatusMessage" />
@if (TempData.HasStatusMessage())
{
<partial name="_StatusMessage"/>
}
</div>
</div>

View File

@ -2,6 +2,6 @@ namespace BTCPayServer.Views.Apps
{
public enum AppsNavPages
{
Index, Create, Update
Index, Create
}
}

View File

@ -1,13 +1,15 @@
@model CreateAppViewModel
@{
ViewData.SetActivePageAndTitle(AppsNavPages.Create, "Create a new app");
ViewData["Title"] = "Create a new app";
}
<section>
<div class="container">
<partial name="_StatusMessage" />
<h2 class="mb-4">@ViewData["Title"]</h2>
<div class="row">
<div class="col-lg-12 section-heading">
<h2>@ViewData["Title"]</h2>
<hr class="primary">
</div>
</div>
<div class="row">
<div class="col-lg-12">
<form asp-action="CreateApp">
@ -26,10 +28,10 @@
<select asp-for="SelectedStore" asp-items="Model.Stores" class="form-control"></select>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" id="Create"/>
<a asp-action="ListApps" class="text-muted ml-3">Back to list</a>
<input type="submit" value="Create" class="btn btn-primary" id="Create" />
</div>
</form>
<a asp-action="ListApps">Back to the app list</a>
</div>
</div>
</div>

View File

@ -1,6 +1,6 @@
@model ListAppsViewModel
@{
ViewData.SetActivePageAndTitle(AppsNavPages.Index, "Apps");
ViewData["Title"] = "Apps";
var storeNameSortOrder = (string)ViewData["StoreNameSortOrder"];
var appNameSortOrder = (string)ViewData["AppNameSortOrder"];
var appTypeSortOrder = (string)ViewData["AppTypeSortOrder"];
@ -10,20 +10,27 @@
<section>
<div class="container">
<partial name="_StatusMessage" />
<div class="d-sm-flex align-items-center justify-content-between mb-2">
<h2 class="mb-0">
@ViewData["PageTitle"]
<small>
<a href="https://docs.btcpayserver.org/Apps/" target="_blank">
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
</a>
</small>
</h2>
<a asp-action="CreateApp" class="btn btn-primary mt-3 mt-sm-0" role="button" id="CreateNewApp"><span class="fa fa-plus"></span> Create a new app</a>
@if (TempData.HasStatusMessage())
{
<div class="row">
<div class="col-lg-12 text-center">
<partial name="_StatusMessage" />
</div>
</div>
}
<div class="row">
<div class="col-lg-12 section-heading">
<h2>@ViewData["Title"]</h2>
<hr class="primary">
<p>Create and manage apps.</p>
</div>
</div>
<div class="row button-row mb-3">
<div class="col-lg-12">
<a asp-action="CreateApp" class="btn btn-primary" role="button" id="CreateNewApp"><span class="fa fa-plus"></span> Create a new app</a>
<a href="https://docs.btcpayserver.org/Apps/" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
</div>
</div>
<div class="row">
<div class="col-lg-12">
@if (Model.Apps.Any())

View File

@ -2,14 +2,25 @@
@using System.Globalization
@model UpdateCrowdfundViewModel
@{
ViewData.SetActivePageAndTitle(AppsNavPages.Update, "Update Crowdfund", Model.StoreName);
ViewData["Title"] = "Update Crowdfund";
}
<section>
<div class="container">
<partial name="_StatusMessage" />
<h2 class="mb-4">@ViewData["PageTitle"]</h2>
<div class="row">
<div class="col-lg-12 section-heading">
<h2>@ViewData["Title"]</h2>
<hr class="primary">
</div>
</div>
@if (TempData.HasStatusMessage())
{
<div class="row">
<div class="col-lg-12 text-center">
<partial name="_StatusMessage" />
</div>
</div>
}
<div class="row">
<div class="col-lg-12">
<form method="post">
@ -81,9 +92,7 @@
</div>
<span asp-validation-for="EndDate" class="text-danger"></span>
</div>
<partial name="TemplateEditor" model="@(nameof(Model.PerksTemplate), "Perks")" />
<partial name="TemplateEditor" model="@(nameof(Model.PerksTemplate), "Perks" )"/>
<div class="form-group">
<label asp-for="PerksTemplate" class="control-label"></label>
<textarea asp-for="PerksTemplate" rows="10" cols="40" class="js-product-template form-control"></textarea>
@ -91,9 +100,7 @@
</div>
<div class="form-group">
<label asp-for="CustomCSSLink" class="control-label"></label>
<a href="https://docs.btcpayserver.org/Theme/#2-bootstrap-themes" target="_blank">
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
</a>
<a href="https://docs.btcpayserver.org/Theme/#2-bootstrap-themes" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
<input asp-for="CustomCSSLink" class="form-control" />
<span asp-validation-for="CustomCSSLink" class="text-danger"></span>
</div>

View File

@ -2,14 +2,24 @@
@addTagHelper *, BundlerMinifier.TagHelpers
@model UpdatePointOfSaleViewModel
@{
ViewData.SetActivePageAndTitle(AppsNavPages.Update, "Update Point of Sale", Model.StoreName);
ViewData["Title"] = "Update Point of Sale";
}
<section>
<div class="container">
<partial name="_StatusMessage" />
<h2 class="mb-4">@ViewData["PageTitle"]</h2>
<div class="row">
<div class="col-lg-12 section-heading">
<h2>@ViewData["Title"]</h2>
<hr class="primary">
</div>
</div>
@if (TempData.HasStatusMessage())
{
<div class="row">
<div class="col-lg-12 text-center">
<partial name="_StatusMessage" />
</div>
</div>
}
<div class="row">
<div class="col-lg-12">
<form method="post">
@ -69,15 +79,11 @@
</div>
<div class="form-group">
<label asp-for="CustomCSSLink" class="control-label"></label>
<a href="https://docs.btcpayserver.org/Theme/#2-bootstrap-themes" target="_blank">
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
</a>
<a href="https://docs.btcpayserver.org/Theme/#2-bootstrap-themes" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
<input asp-for="CustomCSSLink" class="form-control" />
<span asp-validation-for="CustomCSSLink" class="text-danger"></span>
</div>
<partial name="TemplateEditor" model="@(nameof(Model.Template), "Products")" />
<div class="form-group">
<label asp-for="Template" class="control-label"></label>
<textarea asp-for="Template" rows="10" cols="40" class="js-product-template form-control"></textarea>

View File

@ -1,2 +1 @@
@using BTCPayServer.Views.Apps
@using BTCPayServer.Models.AppViewModels
@using BTCPayServer.Models.AppViewModels

View File

@ -1,6 +1,6 @@
@using BTCPayServer.Views
@using BTCPayServer.Views.Apps
@{
ViewBag.CategoryTitle = "Apps";
ViewBag.MainTitle = "Manage app";
ViewData.SetActiveCategory(typeof(AppsNavPages));
}

View File

@ -202,8 +202,10 @@
<div id="content">
<div class="p-2 p-sm-4">
<div class="row">
<partial name="_StatusMessage" />
@if (this.TempData.HasStatusMessage())
{
<partial name="_StatusMessage" />
}
<div class="col-sm-4 col-lg-2 order-sm-last text-right mb-2">
<a class="js-cart btn btn-lg btn-outline-primary" href="#">
<i class="fa fa-shopping-basket"></i>&nbsp;

View File

@ -5,12 +5,12 @@
@if (Context.Request.Query.ContainsKey("simple"))
{
<partial name="/Views/AppsPublic/PointOfSale/MinimalLight.cshtml" model="Model" />
@await Html.PartialAsync("/Views/AppsPublic/PointOfSale/MinimalLight.cshtml", Model)
}
else
{
<noscript>
<partial name="/Views/AppsPublic/PointOfSale/MinimalLight.cshtml" model="Model" />
@await Html.PartialAsync("/Views/AppsPublic/PointOfSale/MinimalLight.cshtml", Model)
</noscript>
<partial name="/Views/AppsPublic/PointOfSale/VueLight.cshtml" model="Model" />
@await Html.PartialAsync("/Views/AppsPublic/PointOfSale/VueLight.cshtml", Model)
}

View File

@ -6,8 +6,10 @@
<div class="container d-flex h-100">
<div class="justify-content-center align-self-center text-center mx-auto px-2 py-3 w-100 m-auto">
<partial name="_StatusMessage" />
@if (TempData.HasStatusMessage())
{
<partial name="_StatusMessage" />
}
<h1 class="mb-4">@Model.Title</h1>
@if (!string.IsNullOrEmpty(Model.Description))
{

View File

@ -40,12 +40,12 @@
<body>
@if (Context.Request.Query.ContainsKey("simple"))
{
<partial name="/Views/AppsPublic/Crowdfund/MinimalCrowdfund.cshtml" model="Model" />
@await Html.PartialAsync("/Views/AppsPublic/Crowdfund/MinimalCrowdfund.cshtml", Model)
}
else
{
<noscript>
<partial name="/Views/AppsPublic/Crowdfund/MinimalCrowdfund.cshtml" model="Model" />
@await Html.PartialAsync("/Views/AppsPublic/Crowdfund/MinimalCrowdfund.cshtml", Model)
<style>
/* Hide the below canvas or else user will be staring "Loading..." text when JS is disabled */
@ -60,7 +60,8 @@
{
<canvas id="fireworks"></canvas>
}
<partial name="/Views/AppsPublic/Crowdfund/VueCrowdfund.cshtml" model="Model" />
@await Html.PartialAsync("/Views/AppsPublic/Crowdfund/VueCrowdfund.cshtml", Model)
}
</body>
</html>

View File

@ -1,20 +1,29 @@
@using BTCPayServer.Views.Stores
@model BTCPayServer.Services.Altcoins.Ethereum.UI.EditEthereumPaymentMethodViewModel
@{
Layout = "../Shared/_NavLayout.cshtml";
ViewData["NavPartialName"] = "../Stores/_Nav";
ViewData.SetActivePageAndTitle(StoreNavPages.ActivePage, $"{Context.GetRouteValue("cryptoCode")} Settings", Context.GetStoreData().StoreName);
ViewData.SetActivePageAndTitle(StoreNavPages.ActivePage, $"{this.Context.GetRouteValue("cryptoCode")} Settings");
}
<partial name="_StatusMessage"/>
<div class="row">
<div class="col-md-6">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
<div class="row">
<div class="col-md-8">
<div class="alert alert-warning">DO NOT USE THE WALLET TO ACCEPT PAYMENTS OUTSIDE OF THIS STORE.<br/>If you spend funds received on invoices which have not been marked complete yet, the invoice will be marked as unpaid.
</div>
<div asp-validation-summary="All" class="text-danger"></div>
<form method="post" asp-action="GetStoreEthereumLikePaymentMethod"
asp-route-storeId="@Context.GetRouteValue("storeId")"
asp-route-cryptoCode="@Context.GetRouteValue("cryptoCode")"
asp-route-storeId="@this.Context.GetRouteValue("storeId")"
asp-route-cryptoCode="@this.Context.GetRouteValue("cryptoCode")"
class="mt-4" enctype="multipart/form-data">
<input type="hidden" asp-for="OriginalIndex"/>
@ -76,8 +85,8 @@
<button type="submit" class="btn btn-primary" id="SaveButton">Save</button>
<a class="btn btn-secondary" asp-action="GetStoreEthereumLikePaymentMethods"
asp-route-storeId="@Context.GetRouteValue("storeId")"
asp-route-cryptoCode="@Context.GetRouteValue("cryptoCode")"
asp-route-storeId="@this.Context.GetRouteValue("storeId")"
asp-route-cryptoCode="@this.Context.GetRouteValue("cryptoCode")"
asp-controller="EthereumLikeStore">
Back to list
</a>

View File

@ -5,13 +5,22 @@
@inject BTCPayNetworkProvider BTCPayNetworkProvider;
@{
Layout = "../Shared/_NavLayout.cshtml";
ViewData.SetActivePageAndTitle(StoreNavPages.ActivePage, "Ethereum Settings", Context.GetStoreData().StoreName);
ViewData.SetActivePageAndTitle(StoreNavPages.ActivePage, "Ethereum Settings");
ViewData["NavPartialName"] = "../Stores/_Nav";
}
<partial name="_StatusMessage"/>
<div class="row">
<div class="col-md-6">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
<div class="row">
<div class="col-md-8">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<table class="table table-sm table-responsive-md">
<thead>
@ -40,10 +49,11 @@
</td>
<td class="text-right">
<a id="Modify@(item.CryptoCode)" asp-action="GetStoreEthereumLikePaymentMethod"
asp-route-storeId="@Context.GetRouteValue("storeId")"
asp-route-storeId="@this.Context.GetRouteValue("storeId")"
asp-route-cryptoCode="@item.CryptoCode">
Modify
</a>
</td>
</tr>
}
@ -57,6 +67,7 @@
var chains = BTCPayNetworkProvider.GetAll().OfType<EthereumBTCPayNetwork>().Select(network => network.ChainId).Distinct();
foreach (var chain in chains)
{
<a asp-action="UpdateChainConfig" asp-controller="EthereumConfig" asp-route-chainId="@chain">Configure Web3 for chain @chain</a>
}
}

View File

@ -5,7 +5,7 @@
}
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
}
<style>

View File

@ -1,6 +1,6 @@
@model BTCPayServer.Models.InvoicingModels.CreateInvoiceModel
@{
ViewData.SetActivePageAndTitle(InvoiceNavPages.Create, "Create an invoice");
ViewData["Title"] = "Create an invoice";
}
<script>
$(function() {
@ -12,10 +12,12 @@
</script>
<section>
<div class="container">
<partial name="_StatusMessage" />
<h2 class="mb-4">@ViewData["Title"]</h2>
<div class="row">
<div class="col-lg-12 section-heading">
<h2>@ViewData["Title"]</h2>
<hr class="primary">
</div>
</div>
<div class="row">
<div class="col-lg-12">
<form asp-action="CreateInvoice" method="post" id="create-invoice-form">
@ -67,9 +69,9 @@
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" id="Create" />
<a asp-action="ListInvoices" class="text-muted ml-3">Back to list</a>
</div>
</form>
<a asp-action="ListInvoices">Back to List</a>
</div>
</div>
</div>

View File

@ -13,7 +13,14 @@
<section class="invoice-details">
<div class="container">
<partial name="_StatusMessage" />
@if (TempData.HasStatusMessage())
{
<div class="row">
<div class="col-lg-12 text-center">
<partial name="_StatusMessage" />
</div>
</div>
}
<div class="row mb-4">
<h2 class="col-xs-12 col-lg-9 mb-4 mb-lg-0">@ViewData["Title"]</h2>
@ -150,14 +157,14 @@
{
<h3 class="mb-3">Product Information</h3>
<table class="table table-sm table-responsive-md removetopborder">
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemCode.AsString()))
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemCode))
{
<tr>
<th>Item code</th>
<td>@Model.TypedMetadata.ItemCode</td>
</tr>
}
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemDesc.AsString()))
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemDesc))
{
<tr>
<th>Item Description</th>
@ -183,14 +190,14 @@
<div class="col-md-6 mb-4">
<h3 class="mb-3">Product information</h3>
<table class="table table-sm table-responsive-md removetopborder">
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemCode.AsString()))
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemCode))
{
<tr>
<th>Item code</th>
<td>@Model.TypedMetadata.ItemCode</td>
</tr>
}
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemDesc.AsString()))
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemDesc))
{
<tr>
<th>Item Description</th>

View File

@ -1,6 +1,7 @@
@using BTCPayServer.Payments
@model InvoicesModel
@{
ViewData.SetActivePageAndTitle(InvoiceNavPages.Index, "Invoices");
ViewData["Title"] = "Invoices";
var storeIds = string.Join("", Model.StoreIds.Select(storeId => $",storeid:{storeId}"));
}
@section HeadScripts {
@ -10,25 +11,31 @@
@Html.HiddenFor(a => a.Count)
<section>
<div class="container">
<partial name="_StatusMessage" />
@if (TempData.HasStatusMessage())
{
<div class="row">
<div class="col-lg-12 text-center">
<partial name="_StatusMessage" />
</div>
</div>
}
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h2 class="mb-0">
@ViewData["Title"]
<small>
<a href="https://docs.btcpayserver.org/PaymentRequests/" class="ml-1" target="_blank">
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
</a>
</small>
</h2>
<a id="CreateNewInvoice" asp-action="CreateInvoice" class="btn btn-primary mt-3 mt-sm-0">
<span class="fa fa-plus"></span>
Create an invoice
</a>
<div class="row">
<div class="col-lg-12 section-heading">
<h2>@ViewData["Title"]</h2>
<hr class="primary">
<p>Create, search or pay an invoice.</p>
</div>
</div>
<div class="row">
<div class="col-12 col-lg-6 mb-5 mb-lg-2 ml-auto">
<div class="col-12 col-sm-4 col-lg-6 mb-3">
<a id="CreateNewInvoice" asp-action="CreateInvoice" class="btn btn-primary mb-1">
<span class="fa fa-plus"></span>
Create an invoice
</a>
</div>
<div class="col-12 col-sm-8 col-lg-6 mb-3">
<form asp-action="ListInvoices" method="get">
<input type="hidden" asp-for="Count"/>
<input asp-for="TimezoneOffset" type="hidden"/>
@ -177,7 +184,7 @@
@if (Model.Total > 0)
{
<form method="post" id="MassAction" asp-action="MassAction" class="mt-lg-n5">
<form method="post" id="MassAction" asp-action="MassAction" class="mt-3">
<span class="mr-2">
<button class="btn btn-secondary dropdown-toggle mb-1" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Actions

View File

@ -1,2 +1 @@
@using BTCPayServer.Services.Invoices
@using BTCPayServer.Views.Invoice

View File

@ -4,7 +4,7 @@
ViewData.SetActivePageAndTitle(ManageNavPages.APIKeys, "Manage your API Keys");
}
<partial name="_StatusMessage" />
<partial name="_StatusMessage"/>
<p>
The <a asp-controller="Home" asp-action="SwaggerDocs" target="_blank">BTCPay Server Greenfield API</a> offers programmatic access to your instance. You can manage your BTCPay

View File

@ -7,11 +7,11 @@
ViewData.SetActivePageAndTitle(ManageNavPages.APIKeys, "Add API Key");
}
<partial name="_StatusMessage" />
<h2 class="mb-4">@ViewData["PageTitle"]</h2>
<p>Generate a new api key to use BTCPay through its API.</p>
<h4>@ViewData["Title"]</h4>
<partial name="_StatusMessage"/>
<p>
Generate a new api key to use BTCPay through its API.
</p>
<form method="post" asp-action="AddApiKey">
<div asp-validation-summary="All" class="text-danger"></div>
@ -109,7 +109,7 @@
</form>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
<style>
.remove-btn {

View File

@ -4,6 +4,7 @@
}
<form asp-action="AddU2FDevice" method="post" id="registerForm" class="hidden">
<input type="hidden" asp-for="AppId"/>
<input type="hidden" asp-for="Version"/>
<input type="hidden" asp-for="Challenge"/>
@ -11,6 +12,7 @@
<input type="hidden" asp-for="DeviceResponse"/>
</form>
<div class="card">
<div class="card-header">
<h4 class="mb-0">

View File

@ -5,12 +5,11 @@
@{
Layout = "_Layout";
ViewData["Title"] = $"Authorize {Model.ApplicationName ?? "Application"}";
ViewData["Title"] = $"Authorize {(Model.ApplicationName ?? "Application")}";
var permissions = Permission.ToPermissions(Model.Permissions.Split(';')).GroupBy(permission => permission.Policy);
}
<partial name="_StatusMessage" />
<partial name="_StatusMessage"/>
<form method="post" asp-action="AuthorizeAPIKey">
<input type="hidden" asp-for="RedirectUrl" value="@Model.RedirectUrl"/>
<input type="hidden" asp-for="Permissions" value="@Model.Permissions"/>

View File

@ -5,26 +5,31 @@
<partial name="_StatusMessage" />
@if (!this.ViewContext.ModelState.IsValid)
{
<div class="row">
<div class="col-md-6">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
}
<div class="row">
<div class="col-md-6">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All" class="text-danger"></div>
}
<form method="post">
<div class="form-group">
<label asp-for="OldPassword"></label>
<input asp-for="OldPassword" class="form-control"/>
<input asp-for="OldPassword" class="form-control" />
<span asp-validation-for="OldPassword" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="NewPassword"></label>
<input asp-for="NewPassword" class="form-control"/>
<input asp-for="NewPassword" class="form-control" />
<span asp-validation-for="NewPassword" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword"></label>
<input asp-for="ConfirmPassword" class="form-control"/>
<input asp-for="ConfirmPassword" class="form-control" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary" id="UpdatePassword">Update password</button>
@ -33,5 +38,5 @@
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -12,7 +12,7 @@
<partial name="Header" />
</head>
<body class="bg-light">
<div class="modal-dialog modal-dialog-centered min-vh-100">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title w-100 text-center">@ViewData["Title"]</h4>

View File

@ -56,7 +56,7 @@
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
<script type="text/javascript" src="~/js/qrcode.js" asp-append-version="true"></script>
<script type="text/javascript">
new QRCode(document.getElementById("qrCode"),

View File

@ -3,28 +3,32 @@
ViewData.SetActivePageAndTitle(ManageNavPages.Index, "Profile");
}
<partial name="_StatusMessage" />)
<partial name="_StatusMessage" />
@if (!this.ViewContext.ModelState.IsValid)
{
<div class="row">
<div class="col-md-6">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
}
<form method="post">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All" class="text-danger"></div>
}
<div class="form-row">
<div class="col-md-6 mb-3">
<label asp-for="Username"></label>
<input asp-for="Username" class="form-control" disabled/>
<input asp-for="Username" class="form-control" disabled />
</div>
</div>
<div class="form-row">
<div class="col-md-6 mb-3">
<label asp-for="Email"></label>
<input asp-for="Email" class="form-control"/>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="col-md-6 mb-3 d-flex align-items-end">
@if (Model.IsEmailConfirmed)
@if(Model.IsEmailConfirmed)
{
<span class="badge badge-success p-2 my-1">
<span class="fa fa-check"></span>
@ -41,5 +45,5 @@
</form>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -5,8 +5,7 @@
ViewData.SetActivePageAndTitle(ManageNavPages.Notifications, "Notification preferences");
}
<partial name="_StatusMessage" />
<partial name="_StatusMessage"/>
<form method="post" asp-action="NotificationSettings">
@if (Model.All)
{

View File

@ -3,10 +3,8 @@
ViewData.SetActivePageAndTitle(ManageNavPages.ChangePassword, "Set password");
}
<partial name="_StatusMessage" />
<h4>Set your password</h4>
<partial name="_StatusMessage" />
<p class="text-info">
You do not have a local username/password for this site. Add a local
account so you can log in without an external login.
@ -31,5 +29,5 @@
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -83,5 +83,5 @@
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,6 +1,6 @@
@inject SignInManager<ApplicationUser> SignInManager
<div class="nav flex-column mb-4">
<div class="nav flex-column nav-pills mb-4">
<a id="@ManageNavPages.Index.ToString()" class="nav-link @ViewData.IsActivePage(ManageNavPages.Index)" asp-action="Index">Profile</a>
<a id="@ManageNavPages.ChangePassword.ToString()" class="nav-link @ViewData.IsActivePage(ManageNavPages.ChangePassword)" asp-action="ChangePassword">Password</a>
<a id="@ManageNavPages.TwoFactorAuthentication.ToString()" class="nav-link @ViewData.IsActivePage(ManageNavPages.TwoFactorAuthentication)" asp-action="TwoFactorAuthentication">Two-factor authentication</a>

View File

@ -2,6 +2,6 @@
@using BTCPayServer.Views.Manage
@{
Layout = "../Shared/_NavLayout.cshtml";
ViewBag.CategoryTitle = "Account";
ViewBag.MainTitle = "Manage your account";
ViewData.SetActiveCategory(typeof(ManageNavPages));
}

View File

@ -4,13 +4,21 @@
@{
Layout = "../Shared/_NavLayout.cshtml";
ViewData["NavPartialName"] = "../Stores/_Nav";
ViewData.SetActivePageAndTitle(StoreNavPages.ActivePage, $"{Model.CryptoCode} Settings");
}
<partial name="_StatusMessage" />
<div class="row">
<div class="col-md-6">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
<div class="row">
<div class="col-md-8">
<div asp-validation-summary="All" class="text-danger"></div>
@if (Model.Summary != null)
{
<div class="card">
@ -23,11 +31,11 @@
</div>
}
@if (!Model.WalletFileFound || Model.Summary.WalletHeight == default)
@if (!Model.WalletFileFound || Model.Summary.WalletHeight == default(long))
{
<form method="post" asp-action="GetStoreMoneroLikePaymentMethod"
asp-route-storeId="@Context.GetRouteValue("storeId")"
asp-route-cryptoCode="@Context.GetRouteValue("cryptoCode")"
asp-route-storeId="@this.Context.GetRouteValue("storeId")"
asp-route-cryptoCode="@this.Context.GetRouteValue("cryptoCode")"
class="mt-4" enctype="multipart/form-data">
<div class="card my-2">
@ -54,12 +62,12 @@
</form>
}
<form method="post" asp-action="GetStoreMoneroLikePaymentMethod"
asp-route-storeId="@Context.GetRouteValue("storeId")"
asp-route-cryptoCode="@Context.GetRouteValue("cryptoCode")"
asp-route-storeId="@this.Context.GetRouteValue("storeId")"
asp-route-cryptoCode="@this.Context.GetRouteValue("cryptoCode")"
class="mt-4" enctype="multipart/form-data">
<input type="hidden" asp-for="CryptoCode"/>
@if (!Model.WalletFileFound || Model.Summary.WalletHeight == default)
@if (!Model.WalletFileFound || Model.Summary.WalletHeight == default(long))
{
<input type="hidden" asp-for="AccountIndex"/>
}
@ -82,7 +90,7 @@
<div class="input-group my-3">
<input type="text" class="form-control" placeholder="New account label" asp-for="NewAccountLabel">
<div class="input-group-append">
<button name="command" value="add-account" class="btn btn-secondary" type="submit">Add account</button>
<button name="command" value="add-account" class="btn btn-secondary"type="submit">Add account</button>
</div>
</div>
@ -99,8 +107,9 @@
<button type="submit" class="btn btn-primary" id="SaveButton">Save</button>
<a class="btn btn-secondary" asp-action="GetStoreMoneroLikePaymentMethods"
asp-route-storeId="@Context.GetRouteValue("storeId")"
asp-route-cryptoCode="@Context.GetRouteValue("cryptoCode")"
asp-route-storeId="@this.Context.GetRouteValue("storeId")"
asp-route-cryptoCode="@this.Context.GetRouteValue("cryptoCode")"
asp-controller="MoneroLikeStore">
Back to list
</a>

View File

@ -3,13 +3,22 @@
@{
Layout = "../Shared/_NavLayout.cshtml";
ViewData.SetActivePageAndTitle(StoreNavPages.ActivePage, "Monero Settings");
ViewData["NavPartialName"] = "../Stores/_Nav";
}
<partial name="_StatusMessage"/>
<div class="row">
<div class="col-md-6">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
<div class="row">
<div class="col-md-8">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<table class="table table-sm table-responsive-md">
<thead>
@ -38,7 +47,7 @@
</td>
<td class="text-right">
<a id="Modify" asp-action="GetStoreMoneroLikePaymentMethod"
asp-route-storeId="@Context.GetRouteValue("storeId")"
asp-route-storeId="@this.Context.GetRouteValue("storeId")"
asp-route-cryptoCode="@item.CryptoCode">
Modify
</a>

View File

@ -5,8 +5,14 @@
<section>
<div class="container">
<partial name="_StatusMessage" />
@if (TempData.HasStatusMessage())
{
<div class="row">
<div class="col-lg-12 text-center">
<partial name="_StatusMessage"/>
</div>
</div>
}
<div class="row">
<div class="col-lg-12 section-heading">
<h2>@ViewData["Title"]</h2>

View File

@ -3,14 +3,24 @@
@model BTCPayServer.Models.PaymentRequestViewModels.UpdatePaymentRequestViewModel
@addTagHelper *, BundlerMinifier.TagHelpers
@{
ViewData.SetActivePageAndTitle(PaymentRequestsNavPages.Create, (string.IsNullOrEmpty(Model.Id) ? "Create" : "Edit") + " Payment Request");
ViewData["Title"] = (string.IsNullOrEmpty(Model.Id) ? "Create" : "Edit") + " Payment Request";
}
<section>
<div class="container">
<partial name="_StatusMessage" />
<h2 class="mb-4">@ViewData["Title"]</h2>
<div class="row">
<div class="col-lg-12 section-heading">
<h2>@ViewData["Title"]</h2>
<hr class="primary">
</div>
</div>
@if (TempData.HasStatusMessage())
{
<div class="row">
<div class="col-lg-12 text-center">
<partial name="_StatusMessage" />
</div>
</div>
}
<div class="row">
<div class="col-lg-12">
<form method="post" action="@Url.Action("EditPaymentRequest", "PaymentRequest", new { id = Model.Id}, Context.Request.Scheme)">
@ -76,7 +86,7 @@
<div class="form-group">
<label asp-for="CustomCSSLink" class="control-label"></label>
<a href="https://docs.btcpayserver.org/Theme/#2-bootstrap-themes" target="_blank">
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
<span class="fa fa-question-circle-o" title="More information..."></span>
</a>
<input asp-for="CustomCSSLink" class="form-control" />
<span asp-validation-for="CustomCSSLink" class="text-danger"></span>
@ -90,25 +100,28 @@
<button type="submit" class="btn btn-primary" id="SaveButton">Save</button>
@if (!string.IsNullOrEmpty(Model.Id))
{
<a class="btn btn-secondary" target="_blank" asp-action="ViewPaymentRequest" asp-route-id="@Context.GetRouteValue("id")" id="@Model.Id" name="ViewAppButton">View</a>
<a class="btn btn-secondary" target="_blank" asp-action="ViewPaymentRequest" asp-route-id="@this.Context.GetRouteValue("id")" id="@Model.Id" name="ViewAppButton">View</a>
<a class="btn btn-secondary"
target="_blank"
asp-action="ListInvoices"
asp-controller="Invoice"
asp-route-searchterm="@($"orderid:{PaymentRequestRepository.GetOrderIdForPaymentRequest(Model.Id)}")">Invoices</a>
<a class="btn btn-secondary" asp-route-id="@Context.GetRouteValue("id")" asp-action="ClonePaymentRequest" id="@Model.Id">Clone</a>
<a class="btn btn-secondary" asp-route-id="@this.Context.GetRouteValue("id")" asp-action="ClonePaymentRequest" id="@Model.Id">Clone</a>
@if (!Model.Archived)
{
<a class="btn btn-secondary" data-toggle="tooltip" title="Archive this payment request so that it does not appear in the payment request list by default" asp-route-id="@Context.GetRouteValue("id")" asp-controller="PaymentRequest" asp-action="TogglePaymentRequestArchival">Archive</a>
<a class="btn btn-secondary" data-toggle="tooltip" title="Archive this payment request so that it does not appear in the payment request list by default" asp-route-id="@this.Context.GetRouteValue("id")" asp-controller="PaymentRequest" asp-action="TogglePaymentRequestArchival">Archive</a>
}
else
{
<a class="btn btn-secondary" data-toggle="tooltip" title="Unarchive this payment request" asp-route-id="@Context.GetRouteValue("id")" asp-controller="PaymentRequest" asp-action="TogglePaymentRequestArchival">Unarchive</a>
<a class="btn btn-secondary" data-toggle="tooltip" title="Unarchive this payment request" asp-route-id="@this.Context.GetRouteValue("id")" asp-controller="PaymentRequest" asp-action="TogglePaymentRequestArchival">Unarchive</a>
}
}
<a asp-action="GetPaymentRequests" class="text-muted ml-3">Back to list</a>
</div>
</form>
<a asp-action="GetPaymentRequests">Back to List</a>
</div>
</div>
</div>

View File

@ -6,25 +6,35 @@
}
<section>
<div class="container">
<partial name="_StatusMessage" />
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h2 class="mb-0">
@ViewData["Title"]
<small>
@if (TempData.HasStatusMessage())
{
<div class="row">
<div class="col-lg-12 text-center">
<partial name="_StatusMessage" />
</div>
</div>
}
<div class="row">
<div class="col-lg-12 section-heading">
<h2>Payment Requests</h2>
<hr class="primary">
<p>
Create, search or pay a payment request.
<a href="https://docs.btcpayserver.org/PaymentRequests/" class="ml-1" target="_blank">
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
</a>
</small>
</h2>
<a asp-action="EditPaymentRequest" class="btn btn-primary mt-3 mt-sm-0" role="button" id="CreatePaymentRequest">
<span class="fa fa-plus"></span>
Create a payment request
</a>
</p>
</div>
</div>
<div class="row">
<div class="col-12 col-lg-6 mb-3 ml-auto">
<div class="col-12 col-md-4 col-lg-6 mb-3">
<a asp-action="EditPaymentRequest" class="btn btn-primary" role="button" id="CreatePaymentRequest">
<span class="fa fa-plus"></span>
Create a payment request
</a>
</div>
<div class="col-12 col-md-8 col-lg-6 mb-3">
<form asp-action="GetPaymentRequests" method="get">
<input type="hidden" asp-for="Count"/>
<input type="hidden" asp-for="TimezoneOffset" />

View File

@ -1,6 +1,7 @@
@using BTCPayServer.Services.Invoices
@using BTCPayServer.Client.Models
@model BTCPayServer.Models.PaymentRequestViewModels.ViewPaymentRequestViewModel
@addTagHelper *, BundlerMinifier.TagHelpers
@inject BTCPayServer.Services.BTCPayServerEnvironment env
@inject BTCPayServer.HostedServices.CssThemeManager themeManager
@ -196,7 +197,7 @@
<main class="flex-grow-1 py-4">
<div class="container">
<partial name="_StatusMessage" model="@(new ViewDataDictionary(ViewData){ { "Margin", "mb-4" } })" />
@await Html.PartialAsync("_StatusMessage", new ViewDataDictionary(ViewData){ { "Margin", "mb-4" } })
<div class="row">
<div class="col col-12 col-lg-6 mb-4">
<div class="jumbotron h-100 m-0 p-sm-5">

View File

@ -1 +0,0 @@
@using BTCPayServer.Views.PaymentRequest

View File

@ -76,7 +76,7 @@
<main class="flex-grow-1 py-4">
<div class="container">
<partial name="_StatusMessage" model="@(new ViewDataDictionary(ViewData){ { "Margin", "mb-4" } })" />
@await Html.PartialAsync("_StatusMessage", new ViewDataDictionary(ViewData){ { "Margin", "mb-4" } })
@if (!ViewContext.ModelState.IsValid)
{
@Html.ValidationSummary(string.Empty, new { @class = "alert alert-danger mb-4 pb-0 text-center" })

View File

@ -1,29 +1,45 @@
@model LndServicesViewModel
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Services, $"C-Lightning {Model.ConnectionType}");
ViewData.SetActivePageAndTitle(ServerNavPages.Services);
}
<h2 class="mb-4">@ViewData["Title"]</h2>
<h4>C-Lightning @Model.ConnectionType</h4>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-md-6">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
<div class="row">
<div class="col-md-8">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All" class="text-danger"></div>
}
<div class="form-group">
<p>
<span>BTCPay exposes Clightning-Rest's service for outside consumption, you will find connection information here.<br /></span>
</p>
</div>
<h5>Compatible wallets</h5>
<div class="form-group">
<h5>Compatible wallets</h5>
</div>
<div>
<a href="https://github.com/ZeusLN/zeus" target="_blank" class="d-inline-block mr-3 mb-3 text-center">
<img src="~/img/zeus.jpg" width="100" height="100" asp-append-version="true" alt="Zeus" />
<div class="mt-2">Zeus</div>
</a>
<div class="row">
<div class="col-lg-3 ml-auto text-center">
<a href="https://github.com/ZeusLN/zeus" target="_blank">
<img src="~/img/zeus.jpg" height="100" asp-append-version="true" />
</a>
<p><a href="https://github.com/ZeusLN/zeus" target="_blank">Zeus</a></p>
</div>
<div class="col-lg-3 mr-auto text-center">
</div>
<div class="col-lg-3 mr-auto text-center">
</div>
<div class="col-lg-3 mr-auto text-center">
</div>
</div>
<div class="form-group">
@ -131,9 +147,9 @@
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
@if (Model.QRCode != null)
@if(Model.QRCode != null)
{
<script type="text/javascript" src="~/js/qrcode.js" asp-append-version="true"></script>
<script type="text/javascript">

View File

@ -1,9 +1,10 @@
@model LightningWalletServices
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Services, "BTCPay Server Configurator");
ViewData.SetActivePageAndTitle(ServerNavPages.Services);
}
<h2 class="mb-4">@ViewData["Title"]</h2>
<h4>BTCPay Server Configurator</h4>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-md-6">
@ -27,5 +28,6 @@
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -2,16 +2,18 @@
@model BTCPayServer.Controllers.ServerController.CreateTemporaryFileUrlViewModel
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Services, $"Create temporary file link");
}
<h2 class="mb-4">@ViewData["Title"]</h2>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-lg-6">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
<div class="row">
<div class="col-lg-6">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All" class="text-danger"></div>
}
<form method="post">
<div class="form-group">
<label asp-for="IsDownload"></label>
@ -22,12 +24,12 @@
<label asp-for="TimeAmount" class="control-label"></label>
<div class="input-group">
<input type="number" asp-for="TimeAmount" class="form-control">
<input type="number" asp-for="TimeAmount" class="form-control">
<div class="input-group-append">
<select asp-for="TimeType" asp-items="@Html.GetEnumSelectList<ServerController.CreateTemporaryFileUrlViewModel.TmpFileTimeType>()" class="custom-select"></select>
<select asp-for="TimeType" asp-items="@Html.GetEnumSelectList< ServerController.CreateTemporaryFileUrlViewModel.TmpFileTimeType>()" class="custom-select"></select>
</div>
</div>
<span asp-validation-for="TimeAmount" class="text-danger"></span>
<span asp-validation-for="TimeType" class="text-danger"></span>
</div>
@ -37,5 +39,5 @@
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,9 +1,9 @@
@model BTCPayServer.Controllers.RegisterFromAdminViewModel
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Users, "Create account");
ViewData.SetActivePageAndTitle(ServerNavPages.Users, $"Users - Create account");
}
<h2 class="mb-4">@ViewData["Title"]</h2>
<partial name="_StatusMessage"/>
<div class="row">
<div class="col-md-6">

View File

@ -1,22 +1,27 @@
@model BTCPayServer.Models.ServerViewModels.DynamicDnsViewModel
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Services, "Dynamic DNS Service");
ViewData.SetActivePageAndTitle(ServerNavPages.Services);
}
<h2 class="mb-4">@ViewData["PageTitle"]</h2>
<h4>Dynamic DNS Service</h4>
<partial name="_StatusMessage" />
@if (!this.ViewContext.ModelState.IsValid)
{
<div class="row">
<div class="col-md-8">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
}
<div class="row">
<div class="col-md-8">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All" class="text-danger"></div>
}
<form method="post">
<div class="form-group">
<input type="hidden" asp-for="Modify"/>
<input type="hidden" asp-for="Modify" />
<div class="form-group">
<label asp-for="Settings.ServiceUrl"></label>
<input id="ServiceUrl" asp-for="Settings.ServiceUrl" class="form-control" placeholder="Url"/>
<input id="ServiceUrl" asp-for="Settings.ServiceUrl" class="form-control" placeholder="Url" />
<p class="form-text text-muted">
Well-known Dynamic DNS providers are:
@for (int i = 0; i < Model.KnownServices.Length; i++)
@ -29,7 +34,7 @@
<label asp-for="Settings.Hostname"></label>
@if (Model.Modify)
{
<input asp-for="Settings.Hostname" class="form-control" readonly placeholder="Hostname"/>
<input asp-for="Settings.Hostname" class="form-control" readonly placeholder="Hostname" />
<p class="form-text text-muted">
<span>The DNS record has been refreshed: </span>
@if (Model.LastUpdated != null)
@ -40,23 +45,23 @@
}
else
{
<input asp-for="Settings.Hostname" class="form-control" placeholder="Hostname"/>
<input asp-for="Settings.Hostname" class="form-control" placeholder="Hostname" />
<span asp-validation-for="Settings.Hostname" class="text-danger"></span>
}
</div>
<div class="form-group">
<label asp-for="Settings.Login"></label>
<input asp-for="Settings.Login" class="form-control" placeholder="Login"/>
<input asp-for="Settings.Login" class="form-control" placeholder="Login" />
</div>
<div class="form-group">
<label asp-for="Settings.Password"></label>
<input asp-for="Settings.Password" class="form-control" placeholder="Password"/>
<input asp-for="Settings.Password" class="form-control" placeholder="Password" />
</div>
@if (Model.Modify)
{
<div class="form-group">
<label asp-for="Settings.Enabled"></label>
<input asp-for="Settings.Enabled" class="form-check-inline" type="checkbox"/>
<input asp-for="Settings.Enabled" class="form-check-inline" type="checkbox" />
</div>
}
<button name="command" class="btn btn-primary" type="submit" value="Save">Save</button>

View File

@ -1,9 +1,11 @@
@model BTCPayServer.Models.ServerViewModels.DynamicDnsViewModel[]
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Services, "Dynamic DNS Settings");
ViewData.SetActivePageAndTitle(ServerNavPages.Services);
}
<h2 class="mb-4">@ViewData["PageTitle"]</h2>
<h4>Dynamic DNS Settings</h4>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-md-8">

View File

@ -1,20 +1,21 @@
@model BTCPayServer.Storage.Services.Providers.AmazonS3Storage.Configuration.AmazonS3StorageConfiguration
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Services, "Amazon S3 Storage");
ViewData.SetActivePageAndTitle(ServerNavPages.Services, $"Storage - Amazon S3");
}
<h2 class="mb-4">@ViewData["PageTitle"]</h2>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-lg-6">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
<div class="row">
<div class="col-lg-6">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All" class="text-danger"></div>
}
<form method="post">
<div class="form-group">
<label asp-for="ContainerName"></label>
<input class="form-control" asp-for="ContainerName"/>
<input class="form-control" asp-for="ContainerName" />
<span asp-validation-for="ContainerName" class="text-danger"></span>
</div>
<div class="form-group">
@ -44,16 +45,16 @@
</div>
<div class="form-group">
<label asp-for="ChunkedUploadThreshold"></label>
<input class="form-control" type="number" asp-for="ChunkedUploadThreshold"/>
<input class="form-control" type="number" asp-for="ChunkedUploadThreshold"/>
<span asp-validation-for="ChunkedUploadThreshold" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary" name="command" value="Save">Save</button>
<a asp-action="Storage" asp-route-forceChoice="true">Change Storage provider</a>
<a asp-action="Storage" asp-route-forceChoice="true" >Change Storage provider</a>
</form>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,20 +1,21 @@
@model BTCPayServer.Storage.Services.Providers.AzureBlobStorage.Configuration.AzureBlobStorageConfiguration
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Services, "Azure Blob Storage");
ViewData.SetActivePageAndTitle(ServerNavPages.Services, $"Storage - Azure Blob Storage");
}
<h2 class="mb-4">@ViewData["PageTitle"]</h2>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-lg-6">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
<div class="row">
<div class="col-lg-6">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All" class="text-danger"></div>
}
<form method="post">
<div class="form-group">
<label asp-for="ContainerName"></label>
<input class="form-control" asp-for="ContainerName"/>
<input class="form-control" asp-for="ContainerName" />
<span asp-validation-for="ContainerName" class="text-danger"></span>
</div>
<div class="form-group">
@ -24,11 +25,11 @@
</div>
<button type="submit" class="btn btn-primary" name="command" value="Save">Save</button>
<a asp-action="Storage" asp-route-forceChoice="true">Change Storage provider</a>
<a asp-action="Storage" asp-route-forceChoice="true" >Change Storage provider</a>
</form>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,21 +1,21 @@
@model BTCPayServer.Storage.Services.Providers.FileSystemStorage.Configuration.FileSystemStorageConfiguration
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Services, $"Local Filesystem Storage");
ViewData.SetActivePageAndTitle(ServerNavPages.Services, $"Storage - Local Filesystem");
}
<h2 class="mb-4">@ViewData["PageTitle"]</h2>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-lg-6">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
<p>Any uploaded files are being saved on the same machine that hosts BTCPay; please pay attention to your storage space.</p>
<div class="row">
<div class="col-lg-6">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All" class="text-danger"></div>
}
<a asp-action="Storage" asp-route-forceChoice="true">Change Storage provider</a>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,20 +1,20 @@
@model BTCPayServer.Storage.Services.Providers.GoogleCloudStorage.Configuration.GoogleCloudStorageConfiguration
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Services, "Google Cloud Storage");
ViewData.SetActivePageAndTitle(ServerNavPages.Services, $"Storage - Google Cloud Storage");
}
<h2 class="mb-4">@ViewData["PageTitle"]</h2>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-lg-6">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
<div class="row">
<div class="col-lg-6">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All" class="text-danger"></div>
}
<form method="post">
<div class="form-group">
<label asp-for="ContainerName"></label>
<input class="form-control" asp-for="ContainerName"/>
<input class="form-control" asp-for="ContainerName" />
<span asp-validation-for="ContainerName" class="text-danger"></span>
</div>
<div class="form-group">
@ -34,11 +34,11 @@
</div>
<button type="submit" class="btn btn-primary" name="command" value="Save">Save</button>
<a asp-action="Storage" asp-route-forceChoice="true">Change Storage provider</a>
<a asp-action="Storage" asp-route-forceChoice="true" >Change Storage provider</a>
</form>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -3,9 +3,5 @@
ViewData.SetActivePageAndTitle(ServerNavPages.Emails);
}
<partial name="EmailsBody" model="Model" />
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}
<partial name="EmailsBody" model="@Model"/>

View File

@ -1,16 +1,16 @@
@model ViewFilesViewModel
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Files, "File Storage");
ViewData.SetActivePageAndTitle(ServerNavPages.Files);
}
<h2 class="mb-4">@ViewData["PageTitle"]</h2>
<p>
Change your <a asp-action="Services" asp-route-returnurl="@ViewData["ReturnUrl"]">external storage service</a> provider.
<a href="https://docs.btcpayserver.org/FAQ/FAQ-ServerSettings/#how-to-upload-files-to-btcpay" target="_blank">
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
</a>
</p>
<partial name="_StatusMessage" />
<h4>File Storage</h4>
<div class="form-group">
<span>Change your <a asp-action="Services" asp-route-returnurl="@ViewData["ReturnUrl"]">external storage service</a> provider</span>
<a href="https://docs.btcpayserver.org/FAQ/FAQ-ServerSettings/#how-to-upload-files-to-btcpay" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
</div>
@if (Model.Files.Any())
{
@ -81,16 +81,23 @@ else
@if (Model.StorageConfigured)
{
<form asp-action="CreateFile" method="post" enctype="multipart/form-data">
<h4 class="mt-5 mb-3">Upload File</h4>
<div class="card">
<form asp-action="CreateFile" method="post" enctype="multipart/form-data">
<div class="custom-file mb-3">
<input type="file" class="custom-file-input" name="file" id="file" required>
<label class="custom-file-label" for="customFile">Choose file</label>
</div>
<div class="card-body">
<h3 class="header">Upload File</h3>
<button class="btn btn-primary" role="button"><span class="fa fa-plus"></span> Upload file</button>
</form>
<div class="custom-file">
<input type="file" class="custom-file-input" name="file" id="file" required>
<label class="custom-file-label" for="customFile">Choose file</label>
</div>
</div>
<div class="card-footer">
<button class="btn btn-primary" role="button"><span class="fa fa-plus"></span> Upload file</button>
</div>
</form>
</div>
@section Scripts {
<script>

View File

@ -1,28 +1,34 @@
@model ChargeServiceViewModel
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Services, "Lightning charge service");
ViewData.SetActivePageAndTitle(ServerNavPages.Services);
}
<h2 class="mb-4">@ViewData["PageTitle"]</h2>
<h4>Lightning charge service</h4>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-md-6">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
<div class="row">
<div class="col-md-8">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All" class="text-danger"></div>
}
<div class="form-group">
<p>Lightning charge is a simple API for invoicing on lightning network, you can use it with several plugins:</p>
<ul>
<li><a href="https://github.com/ElementsProject/woocommerce-gateway-lightning" target="_blank">WooCommerce Lightning Gateway</a>: A comprehensive e-commerce application that integrates with stock-management and order-tracking systems</li>
<li><a href="https://github.com/ElementsProject/nanopos" target="_blank">Nanopos</a>: A simple point-of-sale system for fixed-price goods</li>
<li><a href="https://github.com/ElementsProject/filebazaar" target="_blank">FileBazaar</a>: A system for selling files such as documents, images, and videos</li>
<li><a href="https://github.com/ElementsProject/wordpress-lightning-publisher" target="_blank">Lightning Publisher for WordPress</a>: A patronage model for unlocking WordPress blog entries</li>
<li><a href="https://github.com/ElementsProject/paypercall" target="_blank">Paypercall</a>: A programmers toolkit for Lightning that enables micropayments for individual API calls</li>
<li><a href="https://github.com/ElementsProject/ifpaytt" target="_blank">Ifpaytt</a>: An extension of paypercall that allows web developers using IFTTT to request payments for service usage</li>
<li><a href="https://github.com/ElementsProject/lightning-jukebox" target="_blank">Lightning Jukebox</a>: A fun demo that reimagines a classic technology for the Lightning Network</li>
<li><a href="https://github.com/ElementsProject/nanotip" target="_blank">Nanotip</a>: The simple tip jar, rebuilt to issue Lightning Network invoices</li>
</ul>
<p>
<span>Lightning charge is a simple API for invoicing on lightning network, you can use it with several plugins:</span>
<ul>
<li><a href="https://github.com/ElementsProject/woocommerce-gateway-lightning" target="_blank">WooCommerce Lightning Gateway</a>: A comprehensive e-commerce application that integrates with stock-management and order-tracking systems</li>
<li><a href="https://github.com/ElementsProject/nanopos" target="_blank">Nanopos</a>: A simple point-of-sale system for fixed-price goods</li>
<li><a href="https://github.com/ElementsProject/filebazaar" target="_blank">FileBazaar</a>: A system for selling files such as documents, images, and videos</li>
<li><a href="https://github.com/ElementsProject/wordpress-lightning-publisher" target="_blank">Lightning Publisher for WordPress</a>: A patronage model for unlocking WordPress blog entries</li>
<li><a href="https://github.com/ElementsProject/paypercall" target="_blank">Paypercall</a>: A programmers toolkit for Lightning that enables micropayments for individual API calls</li>
<li><a href="https://github.com/ElementsProject/ifpaytt" target="_blank">Ifpaytt</a>: An extension of paypercall that allows web developers using IFTTT to request payments for service usage</li>
<li><a href="https://github.com/ElementsProject/lightning-jukebox" target="_blank">Lightning Jukebox</a>: A fun demo that reimagines a classic technology for the Lightning Network</li>
<li><a href="https://github.com/ElementsProject/nanotip" target="_blank">Nanotip</a>: The simple tip jar, rebuilt to issue Lightning Network invoices</li>
</ul>
</p>
</div>
</div>
</div>

View File

@ -1,9 +1,11 @@
@model LightningWalletServices
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Services, Model.WalletName);
ViewData.SetActivePageAndTitle(ServerNavPages.Services);
}
<h2 class="mb-4">@ViewData["PageTitle"]</h2>
<h4>@Model.WalletName</h4>
<partial name="_StatusMessage" />
@if (Model.ShowQR)
{
@ -17,22 +19,25 @@
}
<div class="row">
<div class="col-md-6">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
<div class="row">
<div class="col-md-8">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All" class="text-danger"></div>
}
<div class="form-group">
<h5>Browser connection</h5>
<p>
<span>You can go to @Model.WalletName from your browser by <a href="@Model.ServiceLink">clicking here</a><br/></span>
<span>You can go to @Model.WalletName from your browser by <a href="@Model.ServiceLink">clicking here</a><br /></span>
</p>
</div>
<div class="form-group">
<h5>QR Code connection</h5>
<p>
<span>You can use QR Code to connect to your @Model.WalletName from your mobile.<br/></span>
<span>You can use QR Code to connect to your @Model.WalletName from your mobile.<br /></span>
</p>
</div>
<div class="form-group">
@ -40,7 +45,7 @@
{
<div class="form-group">
<form method="get">
<input type="hidden" asp-for="ShowQR" value="true"/>
<input type="hidden" asp-for="ShowQR" value="true" />
<button type="submit" class="btn btn-primary">Show Confidential QR Code</button>
</form>
</div>
@ -57,7 +62,7 @@
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
@if (Model.ShowQR)
{

View File

@ -88,6 +88,8 @@
}
}
<partial name="_StatusMessage"/>
<style>
.version-switch .nav-link { display: inline; }
.version-switch .nav-link.active { display: none; }
@ -116,7 +118,7 @@
@if (Model.Installed.Any())
{
<h2 class="mb-4">Installed Plugins</h2>
<h3 class="mb-3">Installed Plugins</h3>
<div class="row mb-4">
@foreach (var plugin in Model.Installed)
{
@ -233,10 +235,9 @@
}
</div>
}
@if (availableAndNotInstalled.Any())
{
<h2 class="mb-4">Available Plugins</h2>
<h3 class="mb-3">Available Plugins</h3>
<div class="row mb-4">
@foreach (var pluginT in availableAndNotInstalled)
{
@ -249,7 +250,7 @@
@plugin.Version
@if (pluginT.Item2)
{
<div class="badge badge-light ml-2" data-toggle="tooltip" title="This plugin has been recommended to be installed by your deployment method." class="text-nowrap">Recommended <span class="fa fa-question-circle-o text-secondary"></span></div>
<div class="badge badge-light ml-2" data-toggle="tooltip" title="This plugin has been recommended to be installed by your deployment method." class="text-nowrap">Recommended <span class="fa fa-question-circle-o"></span></div>
}
</h5>
<p class="card-text">@plugin.Description</p>

View File

@ -23,68 +23,79 @@
var sortByAsc = "Sort by ascending...";
}
<div class="d-flex align-items-center justify-content-between mb-4">
<h2 class="mb-0">@ViewData["Title"]</h2>
<a asp-action="CreateUser" class="btn btn-primary" role="button" id="CreateUser">
<span class="fa fa-plus"></span> Add User
</a>
</div>
<form
asp-action="ListUsers"
asp-route-sortOrder="@(userEmailSortOrder)"
>
<div class="input-group">
<input asp-for="SearchTerm" class="form-control" placeholder="Search by email..." />
<div class="input-group-append">
<button type="submit" class="btn btn-secondary" title="Search by email">
<span class="fa fa-search"></span> Search
</button>
</div>
<partial name="_StatusMessage"/>
<div class="row button-row">
<div class="col-12 col-sm-4 col-lg-6 mb-3">
<a asp-action="CreateUser" class="btn btn-primary" role="button" id="CreateUser">
<span class="fa fa-plus"></span> Add User
</a>
</div>
<span asp-validation-for="SearchTerm" class="text-danger"></span>
</form>
<table class="table table-sm">
<thead>
<tr>
<th>
<a
asp-action="ListUsers"
asp-route-sortOrder="@(nextUserEmailSortOrder ?? "asc")"
class="text-nowrap"
title="@(nextUserEmailSortOrder == "desc" ? sortByAsc : sortByDesc)"
>
Email
<span class="fa @(sortIconClass)" />
</a>
</th>
<th>Created</th>
<th>Verified</th>
<th class="text-right">Actions</th>
</tr>
</thead>
<tbody>
@foreach (var user in Model.Users)
{
<tr>
<td>@user.Email</td>
<td>@user.Created?.ToBrowserDate()</td>
<td class="text-center">
@if (user.Verified)
{
<span class="text-success fa fa-check"></span>
}
else
{
<span class="text-danger fa fa-times"></span>
}
</td>
<td class="text-right">
<a asp-action="User" asp-route-userId="@user.Id">Edit</a> <span> - </span> <a asp-action="DeleteUser" asp-route-userId="@user.Id">Remove</a>
</td>
</tr>
}
</tbody>
</table>
<div class="col-12 col-sm-8 col-lg-6 mb-3">
<form
asp-action="ListUsers"
asp-route-sortOrder="@(userEmailSortOrder)"
>
<div class="input-group">
<input asp-for="SearchTerm" class="form-control" placeholder="Search by email..." />
<div class="input-group-append">
<button type="submit" class="btn btn-secondary" title="Search by email">
<span class="fa fa-search"></span> Search
</button>
</div>
</div>
<span asp-validation-for="SearchTerm" class="text-danger"></span>
</form>
</div>
</div>
<vc:pager view-model="Model"></vc:pager>
<div class="row">
<div class="col-lg-9 col-xl-8">
<table class="table table-sm">
<thead>
<tr>
<th>
<a
asp-action="ListUsers"
asp-route-sortOrder="@(nextUserEmailSortOrder ?? "asc")"
class="text-nowrap"
title="@(nextUserEmailSortOrder == "desc" ? sortByAsc : sortByDesc)"
>
Email
<span class="fa @(sortIconClass)" />
</a>
</th>
<th>Created</th>
<th>Verified</th>
<th class="text-right">Actions</th>
</tr>
</thead>
<tbody>
@foreach (var user in Model.Users)
{
<tr>
<td>@user.Email</td>
<td>@user.Created?.ToBrowserDate()</td>
<td class="text-center">
@if (user.Verified)
{
<span class="text-success fa fa-check"></span>
}
else
{
<span class="text-danger fa fa-times"></span>
}
</td>
<td class="text-right">
<a asp-action="User" asp-route-userId="@user.Id">Edit</a> <span> - </span> <a asp-action="DeleteUser" asp-route-userId="@user.Id">Remove</a>
</td>
</tr>
}
</tbody>
</table>
<vc:pager view-model="Model"></vc:pager>
</div>
</div>

View File

@ -1,14 +1,23 @@
@model LndSeedBackupViewModel
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Services, "LND Seed Backup");
ViewData.SetActivePageAndTitle(ServerNavPages.Services);
}
<h2 class="mb-4">@ViewData["Title"]</h2>
<h4>LND Seed Backup</h4>
@if (TempData.HasStatusMessage())
{
<div class="row">
<div class="col-md-8 text-center">
<partial name="_StatusMessage" />
</div>
</div>
}
@if (Model.IsWalletUnlockPresent)
{
<div class="row">
<div class="col-lg-8">
<div class="col-md-8">
<div class="form-group">
<p>The LND seed backup is useful to recover funds of your LND wallet in case of a corruption of your server.</p>
<p>The recovering process is documented by LND on <a href="https://github.com/lightningnetwork/lnd/blob/master/docs/recovery.md">this page</a>.</p>

View File

@ -1,150 +1,189 @@
@model LndServicesViewModel
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Services, $"LND {Model.ConnectionType}");
ViewData.SetActivePageAndTitle(ServerNavPages.Services);
}
<h4>LND @Model.ConnectionType</h4>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-md-6">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
<div class="row">
<div class="col-md-8">
<h2 class="mb-4">@ViewData["Title"]</h2>
<p>
BTCPay exposes LND's @Model.ConnectionType service for outside consumption, you will find connection information here.
</p>
<h4 class="mb-3">Compatible wallets</h4>
<div>
@if (Model.Uri == null) // if GRPC
{
<a href="https://www.pebble.indiesquare.me/" target="_blank" class="d-inline-block mr-3 text-center">
<img src="~/img/pebblewallet.jpg" width="100" height="100" asp-append-version="true" alt="Pebble" />
<div class="mt-2">Pebble</div>
</a>
<a href="https://zaphq.io/" target="_blank" class="d-inline-block mr-3 text-center">
<img src="~/img/zapwallet.jpg" width="100" height="100" asp-append-version="true" alt="Zap" />
<div class="mt-2">Zap</div>
</a>
}
else
{
<a href="https://lightningjoule.com/" target="_blank" class="d-inline-block mr-3 mb-3 text-center">
<img src="~/img/joule.png" width="100" height="100" asp-append-version="true" alt="Joule" />
<div class="mt-2">Joule</div>
</a>
<a href="https://github.com/ZeusLN/zeus" target="_blank" class="d-inline-block mr-3 mb-3 text-center">
<img src="~/img/zeus.jpg" width="100" height="100" asp-append-version="true" alt="Zeus" />
<div class="mt-2">Zeus</div>
</a>
}
<div class="form-group">
<p>
<span>BTCPay exposes LND's @Model.ConnectionType service for outside consumption, you will find connection information here.<br /></span>
</p>
</div>
<h4 class="mt-5 mb-3">QR Code connection</h4>
<p>
You can use this QR Code to connect external software to your LND instance.<br/>
This QR Code is only valid for 10 minutes.
</p>
@if (Model.QRCode == null)
<div class="form-group">
<h5>Compatible wallets</h5>
</div>
@if (Model.Uri == null) // if GRPC
{
<div class="form-group">
<form method="post">
<button type="submit" class="btn btn-primary">Show QR Code</button>
</form>
<div class="row">
<div class="col-lg-3 ml-auto text-center">
<a href="https://www.pebble.indiesquare.me/" target="_blank">
<img src="~/img/pebblewallet.jpg" height="100" asp-append-version="true" />
</a>
<p><a href="https://www.pebble.indiesquare.me/" target="_blank">Pebble</a></p>
</div>
<div class="col-lg-3 ml-auto text-center">
<a href="https://zaphq.io/" target="_blank">
<img src="~/img/zapwallet.jpg" height="100" asp-append-version="true" />
</a>
<p><a href="https://zaphq.io/" target="_blank">Zap</a></p>
</div>
<div class="col-lg-3 mr-auto text-center">
</div>
<div class="col-lg-3 mr-auto text-center">
</div>
</div>
}
else
{
<div class="form-group">
<div id="qrCode"></div>
<div id="qrCodeData" data-url="@Model.QRCode"></div>
</div>
<p>See QR Code information by clicking <a href="#detailsQR" data-toggle="collapse">here</a>.</p>
<div id="detailsQR" class="collapse">
<div class="form-group">
<label>QR Code data</label>
<input asp-for="QRCode" readonly class="form-control"/>
<div class="row">
<div class="col-lg-3 ml-auto text-center">
<a href="https://lightningjoule.com/" target="_blank">
<img src="~/img/joule.png" height="100" asp-append-version="true" />
</a>
<p><a href="https://lightningjoule.com/" target="_blank">Joule</a></p>
</div>
<div class="form-group">
Click <a href="@Model.QRCodeLink" target="_blank">here</a> to open the configuration file.
<div class="col-lg-3 ml-auto text-center">
<a href="https://github.com/ZeusLN/zeus" target="_blank">
<img src="~/img/zeus.jpg" height="100" asp-append-version="true" />
</a>
<p><a href="https://github.com/ZeusLN/zeus" target="_blank">Zeus</a></p>
</div>
<div class="col-lg-3 mr-auto text-center">
</div>
<div class="col-lg-3 mr-auto text-center">
</div>
</div>
}
<h4 class="mt-5 mb-3">More details</h4>
<p>Alternatively, you can see the settings by clicking <a href="#details" data-toggle="collapse">here</a>.</p>
<div id="details" class="collapse">
@if (Model.Uri == null)
<div class="form-group">
<h5>QR Code connection</h5>
<p>
<span>You can use this QR Code to connect external software to your LND instance.<br /></span>
<span>This QR Code is only valid for 10 minutes</span>
</p>
</div>
<div class="form-group">
@if (Model.QRCode == null)
{
<div class="form-group">
<label asp-for="Host"></label>
<input asp-for="Host" readonly class="form-control"/>
</div>
<div class="form-group">
<label asp-for="SSL"></label>
<input asp-for="SSL" disabled type="checkbox" class="form-check-inline"/>
<form method="post">
<button type="submit" class="btn btn-primary">Show QR Code</button>
</form>
</div>
}
else
{
<div class="form-group">
<label asp-for="Uri"></label>
<input asp-for="Uri" readonly class="form-control"/>
</div>
}
@if (Model.Macaroon != null)
{
<div class="form-group">
<label asp-for="Macaroon"></label>
<input asp-for="Macaroon" readonly class="form-control"/>
</div>
}
@if (Model.AdminMacaroon != null)
{
<div class="form-group">
<label asp-for="AdminMacaroon"></label>
<input asp-for="AdminMacaroon" readonly class="form-control"/>
</div>
}
@if (Model.InvoiceMacaroon != null)
{
<div class="form-group">
<label asp-for="InvoiceMacaroon"></label>
<input asp-for="InvoiceMacaroon" readonly class="form-control"/>
</div>
}
@if (Model.ReadonlyMacaroon != null)
{
<div class="form-group">
<label asp-for="ReadonlyMacaroon"></label>
<input asp-for="ReadonlyMacaroon" readonly class="form-control"/>
</div>
}
@if (Model.GRPCSSLCipherSuites != null)
{
<div class="form-group">
<label asp-for="GRPCSSLCipherSuites"></label>
<input asp-for="GRPCSSLCipherSuites" readonly class="form-control"/>
</div>
}
@if (Model.CertificateThumbprint != null)
{
<div class="form-group">
<label asp-for="CertificateThumbprint"></label>
<input asp-for="CertificateThumbprint" readonly class="form-control"/>
<div id="qrCode"></div>
<div id="qrCodeData" data-url="@Model.QRCode"></div>
</div>
<p>See QR Code information by clicking <a href="#detailsQR" data-toggle="collapse">here</a></p>
<div id="detailsQR" class="collapse">
<div class="form-group">
<label>QR Code data</label>
<input asp-for="QRCode" readonly class="form-control" />
</div>
<div class="form-group">
Click <a href="@Model.QRCodeLink" target="_blank">here</a> to open the configuration file.
</div>
</div>
}
<div class="form-group">
<h5>More details...</h5>
<p>Alternatively, you can see the settings by clicking <a href="#details" data-toggle="collapse">here</a></p>
</div>
<div id="details" class="collapse">
@if (Model.Uri == null)
{
<div class="form-group">
<label asp-for="Host"></label>
<input asp-for="Host" readonly class="form-control" />
</div>
<div class="form-group">
<label asp-for="SSL"></label>
<input asp-for="SSL" disabled type="checkbox" class="form-check-inline" />
</div>
}
else
{
<div class="form-group">
<label asp-for="Uri"></label>
<input asp-for="Uri" readonly class="form-control" />
</div>
}
@if (Model.Macaroon != null)
{
<div class="form-group">
<label asp-for="Macaroon"></label>
<input asp-for="Macaroon" readonly class="form-control" />
</div>
}
@if (Model.AdminMacaroon != null)
{
<div class="form-group">
<label asp-for="AdminMacaroon"></label>
<input asp-for="AdminMacaroon" readonly class="form-control" />
</div>
}
@if (Model.InvoiceMacaroon != null)
{
<div class="form-group">
<label asp-for="InvoiceMacaroon"></label>
<input asp-for="InvoiceMacaroon" readonly class="form-control" />
</div>
}
@if (Model.ReadonlyMacaroon != null)
{
<div class="form-group">
<label asp-for="ReadonlyMacaroon"></label>
<input asp-for="ReadonlyMacaroon" readonly class="form-control" />
</div>
}
@if (Model.GRPCSSLCipherSuites != null)
{
<div class="form-group">
<label asp-for="GRPCSSLCipherSuites"></label>
<input asp-for="GRPCSSLCipherSuites" readonly class="form-control" />
</div>
}
@if (Model.CertificateThumbprint != null)
{
<div class="form-group">
<label asp-for="CertificateThumbprint"></label>
<input asp-for="CertificateThumbprint" readonly class="form-control" />
</div>
}
</div>
</div>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
@if(Model.QRCode != null)
{
<script type="text/javascript" src="~/js/qrcode.js" asp-append-version="true"></script>
<script type="text/javascript">
new QRCode(document.getElementById("qrCode"), {
new QRCode(document.getElementById("qrCode"),
{
text: @Safe.Json(Model.QRCode),
width: 200,
height: 200,

View File

@ -1,9 +1,9 @@
@model BTCPayServer.Models.ServerViewModels.LogsViewModel
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Logs, "Logs");
ViewData.SetActivePageAndTitle(ServerNavPages.Logs);
}
<h2 class="mb-4">@ViewData["PageTitle"]</h2>
<partial name="_StatusMessage" />
<ul class="list-unstyled">
@foreach (var file in Model.LogFiles)
@ -41,5 +41,5 @@
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,15 +1,17 @@
@model BTCPayServer.Models.ServerViewModels.MaintenanceViewModel
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Maintenance, "Maintenance");
ViewData.SetActivePageAndTitle(ServerNavPages.Maintenance);
}
<h2 class="mb-4">@ViewData["PageTitle"]</h2>
<partial name="_StatusMessage" />
<form method="post">
<div class="row mb-5">
<div class="col-lg-9 col-xl-8">
<h4 class="mb-3">Change domain name</h4>
<p>You can change the domain name of your server by following <a href="https://docs.btcpayserver.org/ChangeDomain" target="_blank">this guide</a>.</p>
<div class="form-group">
<h5>Change domain name</h5>
<span>You can change the domain name of your server by following <a href="https://docs.btcpayserver.org/ChangeDomain" target="_blank">this guide</a></span>
</div>
<div class="form-group">
<div class="form-inline">
@ -20,25 +22,28 @@
</div>
<span asp-validation-for="DNSDomain" class="text-danger"></span>
</div>
<h4 class="mt-5 mb-3">Update</h4>
<p class="text-secondary">Update to the latest version of BTCPay server.</p>
</div>
</div>
<div class="row mb-5">
<div class="col-lg-9 col-xl-8">
<h5>Update</h5>
<p class="text-secondary mb-2">Update to the latest version of BTCPay server.</p>
<div class="form-group">
<div class="input-group">
<button name="command" type="submit" class="btn btn-primary" value="update" disabled="@(Model.CanUseSSH ? null : "disabled")">Update</button>
</div>
</div>
<h4 class="mt-5 mb-3">Restart</h4>
<p class="text-secondary">Restart BTCPay server and related services.</p>
<h5 class="mt-5">Restart</h5>
<p class="text-secondary mb-2">Restart BTCPay server and related services.</p>
<div class="form-group">
<div class="input-group">
<button name="command" type="submit" class="btn btn-primary" value="restart" disabled="@(Model.CanUseSSH ? null : "disabled")">Restart</button>
</div>
</div>
<h4 class="mt-5 mb-3">Clean</h4>
<p class="text-secondary">Delete unused docker images present on your system.</p>
<h5 class="mt-5">Clean</h5>
<p class="text-secondary mb-2">Delete unused docker images present on your system.</p>
<div class="form-group">
<div class="input-group">
<button name="command" type="submit" class="btn btn-secondary" value="clean" disabled="@(Model.CanUseSSH ? null : "disabled")">Clean</button>
@ -49,5 +54,5 @@
</form>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,9 +1,11 @@
@model LightningWalletServices
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Services, Model.WalletName);
ViewData.SetActivePageAndTitle(ServerNavPages.Services);
}
<h2 class="mb-4">@ViewData["PageTitle"]</h2>
<h4>@Model.WalletName</h4>
<partial name="_StatusMessage" />
@if (Model.ShowQR)
{
@ -16,31 +18,49 @@
</div>
}
<div class="row">
<div class="col-md-6">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
<div class="row">
<div class="col-md-8">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All" class="text-danger"></div>
}
<h4 class="mb-3">Full node connection</h4>
<p>This page exposes information to connect remotely to your full node via the P2P protocol.</p>
<h4 class="mb-3">Compatible wallets</h4>
<div>
<a href="https://play.google.com/store/apps/details?id=com.greenaddress.greenbits_android_wallet" target="_blank" class="d-inline-block mr-3 mb-3 text-center">
<img src="~/img/GreenWallet.png" width="100" height="100" asp-append-version="true" alt="Blockstream Green" />
<div class="mt-2">Blockstream Green</div>
</a>
<a href="https://www.wasabiwallet.io/" target="_blank" class="d-inline-block mr-3 mb-3 text-center">
<img src="~/img/wasabi.png" width="100" height="100" asp-append-version="true" alt="Wasabi Wallet" />
<div class="mt-2">Wasabi Wallet</div>
</a>
<div class="form-group">
<h5>Full node connection</h5>
<p>
<span>This page exposes information to connect remotely to your full node via the P2P protocol.</span>
</p>
</div>
<div class="form-group">
<h5>Compatible wallets</h5>
</div>
<div class="row">
<div class="col-lg-3 ml-auto text-center">
<a href="https://play.google.com/store/apps/details?id=com.greenaddress.greenbits_android_wallet" target="_blank">
<img src="~/img/GreenWallet.png" height="100" asp-append-version="true" />
</a>
<p><a href="https://play.google.com/store/apps/details?id=com.greenaddress.greenbits_android_wallet" target="_blank">Blockstream Green Wallet</a></p>
</div>
<div class="col-lg-3 mr-auto text-center">
<a href="https://www.wasabiwallet.io/" target="_blank">
<img src="~/img/wasabi.png" height="100" asp-append-version="true" />
</a>
<p><a href="https://www.wasabiwallet.io/" target="_blank">Wasabi Wallet</a> <a href="https://www.reddit.com/r/WasabiWallet/comments/aqlyia/how_to_connect_wasabi_wallet_to_my_own_full/" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a></p>
</div>
<div class="col-lg-3 mr-auto text-center">
<h4 class="mt-5 mb-3">QR Code connection</h4>
<p>You can use QR Code to connect to @Model.WalletName with compatible wallets.</p>
</div>
<div class="col-lg-3 mr-auto text-center">
</div>
</div>
<div class="form-group">
<h5>QR Code connection</h5>
<p>
<span>You can use QR Code to connect to @Model.WalletName with compatible wallets.<br /></span>
</p>
</div>
<div class="form-group">
@if (!Model.ShowQR)
{
@ -70,7 +90,7 @@
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
@if (Model.ShowQR)
{

View File

@ -6,7 +6,7 @@
ViewData.SetActivePageAndTitle(ServerNavPages.Policies);
}
<h2 class="mb-4">@ViewData["Title"]</h2>
<partial name="_StatusMessage"/>
@if (!ViewContext.ModelState.IsValid)
{
@ -14,22 +14,18 @@
}
<form method="post">
<div class="form-group mb-5">
<h4 class="mb-3">Existing User Settings</h4>
<div class="form-group mb-4">
<h5>Existing User Settings</h5>
<div class="form-check my-1">
<input asp-for="AllowLightningInternalNodeForAll" type="checkbox" class="form-check-input"/>
<label asp-for="AllowLightningInternalNodeForAll" class="form-check-label"></label>
<a href="https://docs.btcpayserver.org/FAQ/FAQ-LightningNetwork/#how-many-users-can-use-lightning-network-in-btcpay" target="_blank">\
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
</a>
<a href="https://docs.btcpayserver.org/FAQ/FAQ-LightningNetwork/#how-many-users-can-use-lightning-network-in-btcpay" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
<span asp-validation-for="AllowLightningInternalNodeForAll" class="text-danger"></span>
</div>
<div class="form-check my-1">
<input asp-for="AllowHotWalletForAll" type="checkbox" class="form-check-input"/>
<label asp-for="AllowHotWalletForAll" class="form-check-label"></label>
<a href="https://docs.btcpayserver.org/CreateWallet/#requirements-to-create-wallets" target="_blank">
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
</a>
<a href="https://docs.btcpayserver.org/CreateWallet/#requirements-to-create-wallets" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
<span asp-validation-for="AllowHotWalletForAll" class="text-danger"></span>
</div>
<div class="form-check my-1">
@ -39,8 +35,8 @@
</div>
</div>
<div class="form-group mb-5">
<h4 class="mb-3">New User Settings</h4>
<div class="form-group mb-4">
<h5>New User Settings</h5>
<div class="form-check my-1">
@{
var emailSettings = (await _SettingsRepository.GetSettingAsync<EmailSettings>()) ?? new EmailSettings();
@ -50,9 +46,7 @@
}
<input asp-for="RequiresConfirmedEmail" type="checkbox" class="form-check-input" disabled="@(isEmailConfigured ? null : "disabled")"/>
<label asp-for="RequiresConfirmedEmail" class="form-check-label"></label>
<a href="https://docs.btcpayserver.org/FAQ/FAQ-ServerSettings/#how-to-allow-registration-on-my-btcpay-server" target="_blank">
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
</a>
<a href="https://docs.btcpayserver.org/FAQ/FAQ-ServerSettings/#how-to-allow-registration-on-my-btcpay-server" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
<span asp-validation-for="RequiresConfirmedEmail" class="text-danger"></span>
@if (!isEmailConfigured)
{
@ -74,28 +68,24 @@
</div>
</div>
<div class="form-group mb-5">
<h4 class="mb-3">Notification Settings</h4>
<div class="form-group mb-4">
<h5>Notification Settings</h5>
<div class="form-check my-1">
<input asp-for="DisableInstantNotifications" type="checkbox" class="form-check-input"/>
<label asp-for="DisableInstantNotifications" class="form-check-label"></label>
<a href="https://docs.btcpayserver.org/Notifications/#notifications" target="_blank">
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
</a>
<a href="https://docs.btcpayserver.org/Notifications/#notifications" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
<span asp-validation-for="DisableInstantNotifications" class="text-danger"></span>
</div>
<div class="form-check my-1">
<input asp-for="DisableStoresToUseServerEmailSettings" type="checkbox" class="form-check-input"/>
<label asp-for="DisableStoresToUseServerEmailSettings" class="form-check-label"></label>
<a href="https://docs.btcpayserver.org/Notifications/#server-emails" target="_blank">
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
</a>
<a href="https://docs.btcpayserver.org/Notifications/#server-emails" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
<span asp-validation-for="DisableStoresToUseServerEmailSettings" class="text-danger"></span>
</div>
</div>
<div class="form-group mb-5">
<h4 class="mb-3">Maintenance Settings</h4>
<div class="form-group mb-4">
<h5>Maintenance Settings</h5>
@if (ViewBag.UpdateUrlPresent)
{
<div class="form-check my-1">
@ -107,14 +97,12 @@
<div class="form-check my-1">
<input asp-for="DiscourageSearchEngines" type="checkbox" class="form-check-input"/>
<label asp-for="DiscourageSearchEngines" class="form-check-label"></label>
<a href="https://docs.btcpayserver.org/FAQ/FAQ-ServerSettings/#how-to-hide-my-btcpay-server-from-search-engines" target="_blank">
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
</a>
<a href="https://docs.btcpayserver.org/FAQ/FAQ-ServerSettings/#how-to-hide-my-btcpay-server-from-search-engines" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
<span asp-validation-for="DiscourageSearchEngines" class="text-danger"></span>
</div>
</div>
<h4 class="mb-3">Customization Settings</h4>
<h5>Customization Settings</h5>
<div class="form-group">
<label asp-for="RootAppId"></label>

View File

@ -1,9 +1,11 @@
@model LightningWalletServices
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Services, Model.WalletName);
ViewData.SetActivePageAndTitle(ServerNavPages.Services);
}
<h2 class="mb-4">@ViewData["PageTitle"]</h2>
<h4>@Model.WalletName</h4>
<partial name="_StatusMessage" />
@if (Model.ShowQR)
{
@ -16,37 +18,55 @@
</div>
}
<div class="row">
<div class="col-md-6">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
<div class="row">
<div class="col-md-8">
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All" class="text-danger"></div>
}
<h4 class="mb-3">Full node connection</h4>
<p>This page exposes information to connect remotely to your full node via the RPC protocol.</p>
<h4 class="mb-3">Compatible wallets</h4>
<div>
<a href="https://apps.apple.com/us/app/fully-noded/id1436425586" target="_blank" class="d-inline-block mr-3 text-center">
<img src="~/img/fullynoded.png" width="100" height="100" asp-append-version="true" alt="Fully Noded" />
<div class="mt-2">Fully Noded</div>
</a>
<a href="https://github.com/cryptoadvance/specter-desktop" target="_blank" class="d-inline-block mr-3 text-center">
<img src="~/img/specter.png" width="100" height="100" asp-append-version="true" alt="Specter Desktop" />
<div class="mt-2">Specter Desktop</div>
</a>
<div class="form-group">
<h5>Full node connection</h5>
<p>
<span>This page exposes information to connect remotely to your full node via the RPC protocol.</span>
</p>
</div>
<div class="form-group">
<h5>Compatible wallets</h5>
</div>
<div class="row">
<div class="col-lg-3 ml-auto text-center">
<a href="https://apps.apple.com/us/app/fully-noded/id1436425586" target="_blank">
<img src="~/img/fullynoded.png" height="100" asp-append-version="true" />
</a>
<p><a href="https://apps.apple.com/us/app/fully-noded/id1436425586" target="_blank">Fully Noded</a></p>
</div>
<div class="col-lg-3 mr-auto text-center">
<a href="https://github.com/cryptoadvance/specter-desktop" target="_blank">
<img src="~/img/Specter.png" height="100" asp-append-version="true" />
</a>
<p><a href="https://github.com/cryptoadvance/specter-desktop" target="_blank">Specter</a></p>
</div>
<div class="col-lg-3 mr-auto text-center">
<h4 class="mt-5 mb-3">QR Code connection</h4>
<p>You can use QR Code to connect to @Model.WalletName with compatible wallets.</p>
</div>
<div class="col-lg-3 mr-auto text-center">
</div>
</div>
<div class="form-group">
<h5>QR Code connection</h5>
<p>
<span>You can use QR Code to connect to @Model.WalletName with compatible wallets.<br /></span>
</p>
</div>
<div class="form-group">
@if (!Model.ShowQR)
{
<div class="form-group">
<form method="get">
<input type="hidden" asp-for="ShowQR" value="true"/>
<input type="hidden" asp-for="ShowQR" value="true" />
<button type="submit" class="btn btn-primary">Show Confidential QR Code</button>
</form>
</div>
@ -61,7 +81,7 @@
<div id="detailsQR" class="collapse">
<div class="form-group">
<label>QR Code data</label>
<input asp-for="ServiceLink" readonly class="form-control"/>
<input asp-for="ServiceLink" readonly class="form-control" />
</div>
</div>
}
@ -70,7 +90,7 @@
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
@await Html.PartialAsync("_ValidationScriptsPartial")
@if (Model.ShowQR)
{

View File

@ -1,11 +1,19 @@
@model BTCPayServer.Models.ServerViewModels.SSHServiceViewModel
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Services, "SSH settings");
ViewData.SetActivePageAndTitle(ServerNavPages.Services);
}
<h2 class="mb-4">@ViewData["PageTitle"]</h2>
<h4>SSH settings</h4>
<partial name="_StatusMessage" />
<div class="row">
<div class="col-md-6">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
<div class="row">
<div class="col-md-8">
<div class="form-group">
<p>
@ -13,10 +21,6 @@
</p>
</div>
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="All" class="text-danger"></div>
}
<div class="form-group">
<div class="form-group">

Some files were not shown because too many files have changed in this diff Show More