Dotnet reformat

This commit is contained in:
nicolas.dorier
2023-04-10 11:07:03 +09:00
parent f598c70a4f
commit 7546ef7a8e
110 changed files with 518 additions and 486 deletions

View File

@@ -10,7 +10,7 @@ public class CustodianApiException : Exception
HttpStatus = httpStatus; HttpStatus = httpStatus;
Code = code; Code = code;
} }
public CustodianApiException(int httpStatus, string code, string message) : this(httpStatus, code, message, null) public CustodianApiException(int httpStatus, string code, string message) : this(httpStatus, code, message, null)
{ {
} }

View File

@@ -52,10 +52,10 @@ public class Field
public string HelpText; public string HelpText;
[JsonExtensionData] public IDictionary<string, JToken> AdditionalData { get; set; } [JsonExtensionData] public IDictionary<string, JToken> AdditionalData { get; set; }
public List<Field> Fields { get; set; } = new (); public List<Field> Fields { get; set; } = new();
// The field is considered "valid" if there are no validation errors // The field is considered "valid" if there are no validation errors
public List<string> ValidationErrors = new (); public List<string> ValidationErrors = new();
public virtual bool IsValid() public virtual bool IsValid()
{ {

View File

@@ -23,12 +23,12 @@ public class SVGUse : UrlResolutionTagHelper2
{ {
_fileVersionProvider = fileVersionProvider; _fileVersionProvider = fileVersionProvider;
} }
public override void Process(TagHelperContext context, TagHelperOutput output) public override void Process(TagHelperContext context, TagHelperOutput output)
{ {
var attr = output.Attributes["href"].Value.ToString(); var attr = output.Attributes["href"].Value.ToString();
var symbolIndex = attr!.IndexOf("#", StringComparison.InvariantCulture); var symbolIndex = attr!.IndexOf("#", StringComparison.InvariantCulture);
var start = attr.IndexOf("~", StringComparison.InvariantCulture) + 1; var start = attr.IndexOf("~", StringComparison.InvariantCulture) + 1;
var length = (symbolIndex != -1 ? symbolIndex : attr.Length) - start; var length = (symbolIndex != -1 ? symbolIndex : attr.Length) - start;
var filePath = attr.Substring(start, length); var filePath = attr.Substring(start, length);
if (!string.IsNullOrEmpty(filePath)) if (!string.IsNullOrEmpty(filePath))

View File

@@ -51,7 +51,7 @@ namespace BTCPayServer.Client
method: HttpMethod.Get), token); method: HttpMethod.Get), token);
return await HandleResponse<AppDataBase>(response); return await HandleResponse<AppDataBase>(response);
} }
public virtual async Task<AppDataBase[]> GetAllApps(string storeId, CancellationToken token = default) public virtual async Task<AppDataBase[]> GetAllApps(string storeId, CancellationToken token = default)
{ {
if (storeId == null) if (storeId == null)

View File

@@ -80,13 +80,13 @@ namespace BTCPayServer.Client
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/trades/quote", queryPayload), token); var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/trades/quote", queryPayload), token);
return await HandleResponse<TradeQuoteResponseData>(response); return await HandleResponse<TradeQuoteResponseData>(response);
} }
public virtual async Task<WithdrawalResponseData> CreateCustodianAccountWithdrawal(string storeId, string accountId, WithdrawRequestData request, CancellationToken token = default) public virtual async Task<WithdrawalResponseData> CreateCustodianAccountWithdrawal(string storeId, string accountId, WithdrawRequestData request, CancellationToken token = default)
{ {
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/withdrawals", bodyPayload: request, method: HttpMethod.Post), token); var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/withdrawals", bodyPayload: request, method: HttpMethod.Post), token);
return await HandleResponse<WithdrawalResponseData>(response); return await HandleResponse<WithdrawalResponseData>(response);
} }
public virtual async Task<WithdrawalSimulationResponseData> SimulateCustodianAccountWithdrawal(string storeId, string accountId, WithdrawRequestData request, CancellationToken token = default) public virtual async Task<WithdrawalSimulationResponseData> SimulateCustodianAccountWithdrawal(string storeId, string accountId, WithdrawRequestData request, CancellationToken token = default)
{ {
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/withdrawals/simulation", bodyPayload: request, method: HttpMethod.Post), token); var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/withdrawals/simulation", bodyPayload: request, method: HttpMethod.Post), token);

View File

@@ -113,7 +113,7 @@ namespace BTCPayServer.Client
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/invoices", queryPayload), token); CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/invoices", queryPayload), token);
return await HandleResponse<LightningInvoiceData[]>(response); return await HandleResponse<LightningInvoiceData[]>(response);
} }
public virtual async Task<LightningPaymentData[]> GetLightningPayments(string cryptoCode, public virtual async Task<LightningPaymentData[]> GetLightningPayments(string cryptoCode,
bool? includePending = null, long? offsetIndex = null, CancellationToken token = default) bool? includePending = null, long? offsetIndex = null, CancellationToken token = default)
{ {

View File

@@ -115,7 +115,7 @@ namespace BTCPayServer.Client
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/invoices", queryPayload), token); CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/invoices", queryPayload), token);
return await HandleResponse<LightningInvoiceData[]>(response); return await HandleResponse<LightningInvoiceData[]>(response);
} }
public virtual async Task<LightningPaymentData[]> GetLightningPayments(string storeId, string cryptoCode, public virtual async Task<LightningPaymentData[]> GetLightningPayments(string storeId, string cryptoCode,
bool? includePending = null, long? offsetIndex = null, CancellationToken token = default) bool? includePending = null, long? offsetIndex = null, CancellationToken token = default)
{ {

View File

@@ -54,7 +54,7 @@ namespace BTCPayServer.Client
CancellationToken token = default) CancellationToken token = default)
{ {
using var response = await _httpClient.SendAsync( using var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/rates", CreateHttpRequest($"api/v1/stores/{storeId}/rates",
queryPayload: new Dictionary<string, object>() { { "currencyPair", currencyPair } }, queryPayload: new Dictionary<string, object>() { { "currencyPair", currencyPair } },
method: HttpMethod.Get), method: HttpMethod.Get),
token); token);

View File

@@ -6,5 +6,5 @@ public class LightningAddressData
public string CurrencyCode { get; set; } public string CurrencyCode { get; set; }
public decimal? Min { get; set; } public decimal? Min { get; set; }
public decimal? Max { get; set; } public decimal? Max { get; set; }
} }

View File

@@ -16,7 +16,7 @@ namespace BTCPayServer.Client.Models
[JsonProperty("BOLT11")] [JsonProperty("BOLT11")]
public string BOLT11 { get; set; } public string BOLT11 { get; set; }
public string PaymentHash { get; set; } public string PaymentHash { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
@@ -24,7 +24,7 @@ namespace BTCPayServer.Client.Models
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))] [JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset? PaidAt { get; set; } public DateTimeOffset? PaidAt { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))] [JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset ExpiresAt { get; set; } public DateTimeOffset ExpiresAt { get; set; }

View File

@@ -1,8 +1,8 @@
using System;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
using System;
using Newtonsoft.Json;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using Newtonsoft.Json;
namespace BTCPayServer.Client.Models; namespace BTCPayServer.Client.Models;
@@ -14,9 +14,9 @@ public class WithdrawRequestData
public WithdrawRequestData() public WithdrawRequestData()
{ {
} }
public WithdrawRequestData(string paymentMethod, TradeQuantity qty) public WithdrawRequestData(string paymentMethod, TradeQuantity qty)
{ {
PaymentMethod = paymentMethod; PaymentMethod = paymentMethod;

View File

@@ -7,7 +7,7 @@ namespace BTCPayServer.Client.Models;
public class WithdrawalResponseData : WithdrawalBaseResponseData public class WithdrawalResponseData : WithdrawalBaseResponseData
{ {
[JsonConverter(typeof(StringEnumConverter))] [JsonConverter(typeof(StringEnumConverter))]
public WithdrawalStatus Status { get; } public WithdrawalStatus Status { get; }

View File

@@ -124,14 +124,14 @@ namespace BTCPayServer.Data
#pragma warning disable CS0612 // Type or member is obsolete #pragma warning disable CS0612 // Type or member is obsolete
WalletTransactionData.OnModelCreating(builder); WalletTransactionData.OnModelCreating(builder);
#pragma warning restore CS0612 // Type or member is obsolete #pragma warning restore CS0612 // Type or member is obsolete
WebhookDeliveryData.OnModelCreating(builder, Database); WebhookDeliveryData.OnModelCreating(builder, Database);
LightningAddressData.OnModelCreating(builder, Database); LightningAddressData.OnModelCreating(builder, Database);
PayoutProcessorData.OnModelCreating(builder, Database); PayoutProcessorData.OnModelCreating(builder, Database);
WebhookData.OnModelCreating(builder, Database); WebhookData.OnModelCreating(builder, Database);
FormData.OnModelCreating(builder, Database); FormData.OnModelCreating(builder, Database);
if (Database.IsSqlite() && !_designTime) if (Database.IsSqlite() && !_designTime)
{ {
// SQLite does not have proper support for DateTimeOffset via Entity Framework Core, see the limitations // SQLite does not have proper support for DateTimeOffset via Entity Framework Core, see the limitations
// here: https://docs.microsoft.com/en-us/ef/core/providers/sqlite/limitations#query-limitations // here: https://docs.microsoft.com/en-us/ef/core/providers/sqlite/limitations#query-limitations

View File

@@ -13,14 +13,14 @@ public class FormData
public StoreData Store { get; set; } public StoreData Store { get; set; }
public string Config { get; set; } public string Config { get; set; }
public bool Public { get; set; } public bool Public { get; set; }
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade) internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
{ {
builder.Entity<FormData>() builder.Entity<FormData>()
.HasOne(o => o.Store) .HasOne(o => o.Store)
.WithMany(o => o.Forms).OnDelete(DeleteBehavior.Cascade); .WithMany(o => o.Forms).OnDelete(DeleteBehavior.Cascade);
builder.Entity<FormData>().HasIndex(o => o.StoreId); builder.Entity<FormData>().HasIndex(o => o.StoreId);
if (databaseFacade.IsNpgsql()) if (databaseFacade.IsNpgsql())
{ {
builder.Entity<FormData>() builder.Entity<FormData>()

View File

@@ -39,6 +39,6 @@ public class LightningAddressDataBlob
public string CurrencyCode { get; set; } public string CurrencyCode { get; set; }
public decimal? Min { get; set; } public decimal? Min { get; set; }
public decimal? Max { get; set; } public decimal? Max { get; set; }
public JObject InvoiceMetadata { get; set; } public JObject InvoiceMetadata { get; set; }
} }

View File

@@ -24,8 +24,8 @@ namespace BTCPayServer.Data
public string Blob2 { get; set; } public string Blob2 { get; set; }
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade) internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
{ {
builder.Entity<NotificationData>() builder.Entity<NotificationData>()
.HasOne(o => o.ApplicationUser) .HasOne(o => o.ApplicationUser)
.WithMany(n => n.Notifications) .WithMany(n => n.Notifications)

View File

@@ -1,4 +1,4 @@
using System; using System;
using BTCPayServer.Data; using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;

File diff suppressed because one or more lines are too long

View File

@@ -20,7 +20,7 @@ namespace BTCPayServer.Services.Rates
} }
} }
public RateSourceInfo RateSourceInfo => new RateSourceInfo("NULL","NULL", "https://NULL.NULL"); public RateSourceInfo RateSourceInfo => new RateSourceInfo("NULL", "NULL", "https://NULL.NULL");
public Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken) public Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
{ {

View File

@@ -89,7 +89,7 @@ namespace BTCPayServer.Services.Rates
AvailableRateProviders.Add(new(rsi.Id, rsi.DisplayName, rsi.Url, RateSource.Coingecko)); AvailableRateProviders.Add(new(rsi.Id, rsi.DisplayName, rsi.Url, RateSource.Coingecko));
} }
} }
AvailableRateProviders.Sort((a,b) => StringComparer.Ordinal.Compare(a.DisplayName, b.DisplayName)); AvailableRateProviders.Sort((a, b) => StringComparer.Ordinal.Compare(a.DisplayName, b.DisplayName));
} }
public List<AvailableRateProvider> AvailableRateProviders { get; } = new List<AvailableRateProvider>(); public List<AvailableRateProvider> AvailableRateProviders { get; } = new List<AvailableRateProvider>();

View File

@@ -44,7 +44,7 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id("StoreWebsite")).SendKeys(storeUrl); s.Driver.FindElement(By.Id("StoreWebsite")).SendKeys(storeUrl);
s.Driver.FindElement(By.Id("Save")).Click(); s.Driver.FindElement(By.Id("Save")).Click();
Assert.Contains("Store successfully updated", s.FindAlertMessage().Text); Assert.Contains("Store successfully updated", s.FindAlertMessage().Text);
// Enable LNURL, which we will need for (non-)presence checks throughout this test // Enable LNURL, which we will need for (non-)presence checks throughout this test
s.GoToHome(); s.GoToHome();
s.GoToLightningSettings(); s.GoToLightningSettings();
@@ -79,7 +79,7 @@ namespace BTCPayServer.Tests
Assert.Equal(address, copyAddress); Assert.Equal(address, copyAddress);
Assert.Equal($"bitcoin:{address.ToUpperInvariant()}", qrValue); Assert.Equal($"bitcoin:{address.ToUpperInvariant()}", qrValue);
s.Driver.ElementDoesNotExist(By.Id("Lightning_BTC")); s.Driver.ElementDoesNotExist(By.Id("Lightning_BTC"));
// Details should show exchange rate // Details should show exchange rate
s.Driver.ToggleCollapse("PaymentDetails"); s.Driver.ToggleCollapse("PaymentDetails");
s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-TotalPrice")); s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-TotalPrice"));
@@ -87,7 +87,7 @@ namespace BTCPayServer.Tests
s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-AmountDue")); s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-AmountDue"));
Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
Assert.Contains("sat/byte", s.Driver.FindElement(By.Id("PaymentDetails-RecommendedFee")).Text); Assert.Contains("sat/byte", s.Driver.FindElement(By.Id("PaymentDetails-RecommendedFee")).Text);
// Switch to LNURL // Switch to LNURL
s.Driver.FindElement(By.CssSelector(".payment-method:nth-child(2)")).Click(); s.Driver.FindElement(By.CssSelector(".payment-method:nth-child(2)")).Click();
TestUtils.Eventually(() => TestUtils.Eventually(() =>
@@ -125,7 +125,7 @@ namespace BTCPayServer.Tests
s.GoToInvoiceCheckout(invoiceId); s.GoToInvoiceCheckout(invoiceId);
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2")); s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
Assert.Contains("sats", s.Driver.FindElement(By.Id("AmountDue")).Text); Assert.Contains("sats", s.Driver.FindElement(By.Id("AmountDue")).Text);
// Details should not show exchange rate // Details should not show exchange rate
s.Driver.ToggleCollapse("PaymentDetails"); s.Driver.ToggleCollapse("PaymentDetails");
s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-ExchangeRate")); s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-ExchangeRate"));
@@ -206,7 +206,7 @@ namespace BTCPayServer.Tests
Assert.Contains("Mined 1 block", Assert.Contains("Mined 1 block",
s.Driver.WaitForElement(By.Id("CheatSuccessMessage")).Text); s.Driver.WaitForElement(By.Id("CheatSuccessMessage")).Text);
}); });
// Settled // Settled
TestUtils.Eventually(() => TestUtils.Eventually(() =>
{ {
@@ -244,7 +244,7 @@ namespace BTCPayServer.Tests
Assert.StartsWith("lnbcrt", copyAddressLightning); Assert.StartsWith("lnbcrt", copyAddressLightning);
Assert.StartsWith($"bitcoin:{address.ToUpperInvariant()}?amount=", qrValue); Assert.StartsWith($"bitcoin:{address.ToUpperInvariant()}?amount=", qrValue);
Assert.Contains("&lightning=LNBCRT", qrValue); Assert.Contains("&lightning=LNBCRT", qrValue);
// Check details // Check details
s.Driver.ToggleCollapse("PaymentDetails"); s.Driver.ToggleCollapse("PaymentDetails");
Assert.Contains("1 BTC = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); Assert.Contains("1 BTC = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
@@ -252,7 +252,7 @@ namespace BTCPayServer.Tests
Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text); Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text);
Assert.Contains("BTC", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text); Assert.Contains("BTC", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text);
Assert.Contains("BTC", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text); Assert.Contains("BTC", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text);
// Switch to amount displayed in sats // Switch to amount displayed in sats
s.GoToHome(); s.GoToHome();
s.GoToStore(StoreNavPages.CheckoutAppearance); s.GoToStore(StoreNavPages.CheckoutAppearance);
@@ -262,7 +262,7 @@ namespace BTCPayServer.Tests
s.GoToInvoiceCheckout(invoiceId); s.GoToInvoiceCheckout(invoiceId);
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2")); s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
Assert.Contains("sats", s.Driver.FindElement(By.Id("AmountDue")).Text); Assert.Contains("sats", s.Driver.FindElement(By.Id("AmountDue")).Text);
// Check details // Check details
s.Driver.ToggleCollapse("PaymentDetails"); s.Driver.ToggleCollapse("PaymentDetails");
Assert.Contains("1 sat = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); Assert.Contains("1 sat = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
@@ -270,7 +270,7 @@ namespace BTCPayServer.Tests
Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text); Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text);
Assert.Contains("sats", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text); Assert.Contains("sats", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text);
Assert.Contains("sats", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text); Assert.Contains("sats", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text);
// BIP21 with LN as default payment method // BIP21 with LN as default payment method
s.GoToHome(); s.GoToHome();
invoiceId = s.CreateInvoice(defaultPaymentMethod: "BTC_LightningLike"); invoiceId = s.CreateInvoice(defaultPaymentMethod: "BTC_LightningLike");
@@ -280,7 +280,7 @@ namespace BTCPayServer.Tests
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href"); payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
Assert.StartsWith("bitcoin:", payUrl); Assert.StartsWith("bitcoin:", payUrl);
Assert.Contains("&lightning=lnbcrt", payUrl); Assert.Contains("&lightning=lnbcrt", payUrl);
// Check details // Check details
s.Driver.ToggleCollapse("PaymentDetails"); s.Driver.ToggleCollapse("PaymentDetails");
Assert.Contains("1 sat = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); Assert.Contains("1 sat = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
@@ -294,7 +294,7 @@ namespace BTCPayServer.Tests
s.GoToLightningSettings(); s.GoToLightningSettings();
Assert.True(s.Driver.FindElement(By.Id("LNURLEnabled")).Selected); Assert.True(s.Driver.FindElement(By.Id("LNURLEnabled")).Selected);
Assert.True(s.Driver.FindElement(By.Id("LNURLStandardInvoiceEnabled")).Selected); Assert.True(s.Driver.FindElement(By.Id("LNURLStandardInvoiceEnabled")).Selected);
// BIP21 with top-up invoice // BIP21 with top-up invoice
invoiceId = s.CreateInvoice(amount: null); invoiceId = s.CreateInvoice(amount: null);
s.GoToInvoiceCheckout(invoiceId); s.GoToInvoiceCheckout(invoiceId);
@@ -312,7 +312,7 @@ namespace BTCPayServer.Tests
Assert.Equal(address, copyAddressOnchain); Assert.Equal(address, copyAddressOnchain);
Assert.StartsWith("lnurl", copyAddressLightning); Assert.StartsWith("lnurl", copyAddressLightning);
Assert.StartsWith($"bitcoin:{address.ToUpperInvariant()}?lightning=LNURL", qrValue); Assert.StartsWith($"bitcoin:{address.ToUpperInvariant()}?lightning=LNURL", qrValue);
// Check details // Check details
s.Driver.ToggleCollapse("PaymentDetails"); s.Driver.ToggleCollapse("PaymentDetails");
Assert.Contains("1 sat = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); Assert.Contains("1 sat = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
@@ -331,7 +331,7 @@ namespace BTCPayServer.Tests
Assert.Contains("This invoice will expire in", paymentInfo.Text); Assert.Contains("This invoice will expire in", paymentInfo.Text);
Assert.Contains("00:0", paymentInfo.Text); Assert.Contains("00:0", paymentInfo.Text);
Assert.DoesNotContain("Please send", paymentInfo.Text); Assert.DoesNotContain("Please send", paymentInfo.Text);
// Configure countdown timer // Configure countdown timer
s.GoToHome(); s.GoToHome();
invoiceId = s.CreateInvoice(); invoiceId = s.CreateInvoice();
@@ -343,13 +343,13 @@ namespace BTCPayServer.Tests
displayExpirationTimer.SendKeys("10"); displayExpirationTimer.SendKeys("10");
s.Driver.FindElement(By.Id("Save")).Click(); s.Driver.FindElement(By.Id("Save")).Click();
Assert.Contains("Store successfully updated", s.FindAlertMessage().Text); Assert.Contains("Store successfully updated", s.FindAlertMessage().Text);
s.GoToInvoiceCheckout(invoiceId); s.GoToInvoiceCheckout(invoiceId);
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2")); s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
paymentInfo = s.Driver.FindElement(By.Id("PaymentInfo")); paymentInfo = s.Driver.FindElement(By.Id("PaymentInfo"));
Assert.False(paymentInfo.Displayed); Assert.False(paymentInfo.Displayed);
Assert.DoesNotContain("This invoice will expire in", paymentInfo.Text); Assert.DoesNotContain("This invoice will expire in", paymentInfo.Text);
expirySeconds = s.Driver.FindElement(By.Id("ExpirySeconds")); expirySeconds = s.Driver.FindElement(By.Id("ExpirySeconds"));
expirySeconds.Clear(); expirySeconds.Clear();
expirySeconds.SendKeys("599"); expirySeconds.SendKeys("599");
@@ -359,7 +359,7 @@ namespace BTCPayServer.Tests
Assert.True(paymentInfo.Displayed); Assert.True(paymentInfo.Displayed);
Assert.Contains("This invoice will expire in", paymentInfo.Text); Assert.Contains("This invoice will expire in", paymentInfo.Text);
Assert.Contains("09:5", paymentInfo.Text); Assert.Contains("09:5", paymentInfo.Text);
// Disable LNURL again // Disable LNURL again
s.GoToHome(); s.GoToHome();
s.GoToLightningSettings(); s.GoToLightningSettings();
@@ -378,7 +378,7 @@ namespace BTCPayServer.Tests
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href"); payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
Assert.StartsWith("bitcoin:", payUrl); Assert.StartsWith("bitcoin:", payUrl);
Assert.Contains("&lightning=lnbcrt", payUrl); Assert.Contains("&lightning=lnbcrt", payUrl);
// Language Switch // Language Switch
var languageSelect = new SelectElement(s.Driver.FindElement(By.Id("DefaultLang"))); var languageSelect = new SelectElement(s.Driver.FindElement(By.Id("DefaultLang")));
Assert.Equal("English", languageSelect.SelectedOption.Text); Assert.Equal("English", languageSelect.SelectedOption.Text);
@@ -387,7 +387,7 @@ namespace BTCPayServer.Tests
languageSelect.SelectByText("Deutsch"); languageSelect.SelectByText("Deutsch");
Assert.Equal("Details anzeigen", s.Driver.FindElement(By.Id("DetailsToggle")).Text); Assert.Equal("Details anzeigen", s.Driver.FindElement(By.Id("DetailsToggle")).Text);
Assert.Contains("lang=de", s.Driver.Url); Assert.Contains("lang=de", s.Driver.Url);
s.Driver.Navigate().Refresh(); s.Driver.Navigate().Refresh();
languageSelect = new SelectElement(s.Driver.FindElement(By.Id("DefaultLang"))); languageSelect = new SelectElement(s.Driver.FindElement(By.Id("DefaultLang")));
Assert.Equal("Deutsch", languageSelect.SelectedOption.Text); Assert.Equal("Deutsch", languageSelect.SelectedOption.Text);

View File

@@ -626,7 +626,7 @@ namespace BTCPayServer.Tests
[Fact] [Fact]
public void RoundupCurrenciesCorrectly() public void RoundupCurrenciesCorrectly()
{ {
DisplayFormatter displayFormatter = new (CurrencyNameTable.Instance); DisplayFormatter displayFormatter = new(CurrencyNameTable.Instance);
foreach (var test in new[] foreach (var test in new[]
{ {
(0.0005m, "0.0005 USD", "USD"), (0.001m, "0.001 USD", "USD"), (0.01m, "0.01 USD", "USD"), (0.0005m, "0.0005 USD", "USD"), (0.001m, "0.001 USD", "USD"), (0.01m, "0.01 USD", "USD"),
@@ -766,7 +766,7 @@ namespace BTCPayServer.Tests
var root = new Mnemonic( var root = new Mnemonic(
"usage fever hen zero slide mammal silent heavy donate budget pulse say brain thank sausage brand craft about save attract muffin advance illegal cabbage") "usage fever hen zero slide mammal silent heavy donate budget pulse say brain thank sausage brand craft about save attract muffin advance illegal cabbage")
.DeriveExtKey(); .DeriveExtKey();
// xpub // xpub
var tpub = "tpubD6NzVbkrYhZ4YHNiuTdTmHRmbcPRLfqgyneZFCL1mkzkUBjXriQShxTh9HL34FK2mhieasJVk9EzJrUfkFqRNQBjiXgx3n5BhPkxKBoFmaS"; var tpub = "tpubD6NzVbkrYhZ4YHNiuTdTmHRmbcPRLfqgyneZFCL1mkzkUBjXriQShxTh9HL34FK2mhieasJVk9EzJrUfkFqRNQBjiXgx3n5BhPkxKBoFmaS";
Assert.True(DerivationSchemeSettings.TryParseFromWalletFile(tpub, testnet, out var settings, out var error)); Assert.True(DerivationSchemeSettings.TryParseFromWalletFile(tpub, testnet, out var settings, out var error));

View File

@@ -116,7 +116,7 @@ public class FormTests : UnitTestBase
break; break;
} }
} }
form = new Form() form = new Form()
{ {
Fields = new List<Field> Fields = new List<Field>
@@ -143,7 +143,7 @@ public class FormTests : UnitTestBase
{"invoice_item3", new StringValues("updated")}, {"invoice_item3", new StringValues("updated")},
{"invoice_test", new StringValues("updated")} {"invoice_test", new StringValues("updated")}
})); }));
foreach (var f in form.GetAllFields()) foreach (var f in form.GetAllFields())
{ {
var field = f.Field; var field = f.Field;
@@ -185,7 +185,7 @@ public class FormTests : UnitTestBase
form.SetValues(obj); form.SetValues(obj);
obj = service.GetValues(form); obj = service.GetValues(form);
Assert.Null(obj["test"].Value<string>()); Assert.Null(obj["test"].Value<string>());
form.SetValues(new JObject{ ["test"] = "hello" }); form.SetValues(new JObject { ["test"] = "hello" });
obj = service.GetValues(form); obj = service.GetValues(form);
Assert.Equal("hello", obj["test"].Value<string>()); Assert.Equal("hello", obj["test"].Value<string>());
} }

View File

@@ -302,7 +302,8 @@ namespace BTCPayServer.Tests
{ {
await client.GetApp("some random ID lol"); await client.GetApp("some random ID lol");
}); });
await AssertHttpError(404, async () => { await AssertHttpError(404, async () =>
{
await client.GetPosApp("some random ID lol"); await client.GetPosApp("some random ID lol");
}); });
@@ -451,7 +452,7 @@ namespace BTCPayServer.Tests
// Test creating a crowdfund app // Test creating a crowdfund app
var app = await client.CreateCrowdfundApp( var app = await client.CreateCrowdfundApp(
user.StoreId, user.StoreId,
new CreateCrowdfundAppRequest() new CreateCrowdfundAppRequest()
{ {
AppName = "test app from API", AppName = "test app from API",
Title = "test app title" Title = "test app title"
@@ -462,10 +463,12 @@ namespace BTCPayServer.Tests
Assert.Equal("Crowdfund", app.AppType); Assert.Equal("Crowdfund", app.AppType);
// Make sure we return a 404 if we try to get an app that doesn't exist // Make sure we return a 404 if we try to get an app that doesn't exist
await AssertHttpError(404, async () => { await AssertHttpError(404, async () =>
{
await client.GetApp("some random ID lol"); await client.GetApp("some random ID lol");
}); });
await AssertHttpError(404, async () => { await AssertHttpError(404, async () =>
{
await client.GetCrowdfundApp("some random ID lol"); await client.GetCrowdfundApp("some random ID lol");
}); });
@@ -488,7 +491,8 @@ namespace BTCPayServer.Tests
// Test deleting the newly created app // Test deleting the newly created app
await client.DeleteApp(retrievedApp.Id); await client.DeleteApp(retrievedApp.Id);
await AssertHttpError(404, async () => { await AssertHttpError(404, async () =>
{
await client.GetApp(retrievedApp.Id); await client.GetApp(retrievedApp.Id);
}); });
} }
@@ -512,8 +516,8 @@ namespace BTCPayServer.Tests
} }
); );
var crowdfundApp = await client.CreateCrowdfundApp(user.StoreId, new CreateCrowdfundAppRequest() { AppName = "test app from API" }); var crowdfundApp = await client.CreateCrowdfundApp(user.StoreId, new CreateCrowdfundAppRequest() { AppName = "test app from API" });
// Create another store and one app on it so we can get all apps from all stores for the user below // Create another store and one app on it so we can get all apps from all stores for the user below
var newStore = await client.CreateStore(new CreateStoreRequest() { Name = "A" }); var newStore = await client.CreateStore(new CreateStoreRequest() { Name = "A" });
var newApp = await client.CreateCrowdfundApp(newStore.Id, new CreateCrowdfundAppRequest() { AppName = "new app" }); var newApp = await client.CreateCrowdfundApp(newStore.Id, new CreateCrowdfundAppRequest() { AppName = "new app" });
@@ -544,7 +548,7 @@ namespace BTCPayServer.Tests
Assert.Equal(crowdfundApp.Name, apps[1].Name); Assert.Equal(crowdfundApp.Name, apps[1].Name);
Assert.Equal(crowdfundApp.StoreId, apps[1].StoreId); Assert.Equal(crowdfundApp.StoreId, apps[1].StoreId);
Assert.Equal(crowdfundApp.AppType, apps[1].AppType); Assert.Equal(crowdfundApp.AppType, apps[1].AppType);
Assert.Equal(newApp.Name, apps[2].Name); Assert.Equal(newApp.Name, apps[2].Name);
Assert.Equal(newApp.StoreId, apps[2].StoreId); Assert.Equal(newApp.StoreId, apps[2].StoreId);
Assert.Equal(newApp.AppType, apps[2].AppType); Assert.Equal(newApp.AppType, apps[2].AppType);
@@ -1066,7 +1070,7 @@ namespace BTCPayServer.Tests
var lnrURLs = await unauthenticated.GetPullPaymentLNURL(test4.Id); var lnrURLs = await unauthenticated.GetPullPaymentLNURL(test4.Id);
Assert.IsType<string>(lnrURLs.LNURLBech32); Assert.IsType<string>(lnrURLs.LNURLBech32);
Assert.IsType<string>(lnrURLs.LNURLUri); Assert.IsType<string>(lnrURLs.LNURLUri);
//permission test around auto approved pps and payouts //permission test around auto approved pps and payouts
var nonApproved = await acc.CreateClient(Policies.CanCreateNonApprovedPullPayments); var nonApproved = await acc.CreateClient(Policies.CanCreateNonApprovedPullPayments);
var approved = await acc.CreateClient(Policies.CanCreatePullPayments); var approved = await acc.CreateClient(Policies.CanCreatePullPayments);
@@ -1091,7 +1095,7 @@ namespace BTCPayServer.Tests
Destination = new Key().GetAddress(ScriptPubKeyType.TaprootBIP86, Network.RegTest).ToString() Destination = new Key().GetAddress(ScriptPubKeyType.TaprootBIP86, Network.RegTest).ToString()
}); });
}); });
var pullPayment = await approved.CreatePullPayment(acc.StoreId, new CreatePullPaymentRequest() var pullPayment = await approved.CreatePullPayment(acc.StoreId, new CreatePullPaymentRequest()
{ {
Amount = 100, Amount = 100,
@@ -1100,7 +1104,7 @@ namespace BTCPayServer.Tests
PaymentMethods = new[] { "BTC" }, PaymentMethods = new[] { "BTC" },
AutoApproveClaims = true AutoApproveClaims = true
}); });
var p = await approved.CreatePayout(acc.StoreId, new CreatePayoutThroughStoreRequest() var p = await approved.CreatePayout(acc.StoreId, new CreatePayoutThroughStoreRequest()
{ {
Amount = 100, Amount = 100,
@@ -1256,7 +1260,10 @@ namespace BTCPayServer.Tests
//update store //update store
Assert.Empty(newStore.PaymentMethodCriteria); Assert.Empty(newStore.PaymentMethodCriteria);
await client.GenerateOnChainWallet(newStore.Id, "BTC", new GenerateOnChainWalletRequest()); await client.GenerateOnChainWallet(newStore.Id, "BTC", new GenerateOnChainWalletRequest());
var updatedStore = await client.UpdateStore(newStore.Id, new UpdateStoreRequest() { Name = "B", PaymentMethodCriteria = new List<PaymentMethodCriteriaData>() var updatedStore = await client.UpdateStore(newStore.Id, new UpdateStoreRequest()
{
Name = "B",
PaymentMethodCriteria = new List<PaymentMethodCriteriaData>()
{ {
new() new()
{ {
@@ -1265,7 +1272,8 @@ namespace BTCPayServer.Tests
PaymentMethod = "BTC", PaymentMethod = "BTC",
CurrencyCode = "USD" CurrencyCode = "USD"
} }
}}); }
});
Assert.Equal("B", updatedStore.Name); Assert.Equal("B", updatedStore.Name);
var s = (await client.GetStore(newStore.Id)); var s = (await client.GetStore(newStore.Id));
Assert.Equal("B", s.Name); Assert.Equal("B", s.Name);
@@ -1275,9 +1283,9 @@ namespace BTCPayServer.Tests
Assert.True(pmc.Above); Assert.True(pmc.Above);
Assert.Equal("BTC", pmc.PaymentMethod); Assert.Equal("BTC", pmc.PaymentMethod);
Assert.Equal("USD", pmc.CurrencyCode); Assert.Equal("USD", pmc.CurrencyCode);
updatedStore = await client.UpdateStore(newStore.Id, new UpdateStoreRequest() { Name = "B"}); updatedStore = await client.UpdateStore(newStore.Id, new UpdateStoreRequest() { Name = "B" });
Assert.Empty(newStore.PaymentMethodCriteria); Assert.Empty(newStore.PaymentMethodCriteria);
//list stores //list stores
var stores = await client.GetStores(); var stores = await client.GetStores();
var storeIds = stores.Select(data => data.Id); var storeIds = stores.Select(data => data.Id);
@@ -2331,10 +2339,10 @@ namespace BTCPayServer.Tests
Assert.NotNull(merchantInvoice.Id); Assert.NotNull(merchantInvoice.Id);
Assert.NotNull(merchantInvoice.PaymentHash); Assert.NotNull(merchantInvoice.PaymentHash);
Assert.Equal(merchantInvoice.Id, merchantInvoice.PaymentHash); Assert.Equal(merchantInvoice.Id, merchantInvoice.PaymentHash);
// The default client is using charge, so we should not be able to query channels // The default client is using charge, so we should not be able to query channels
var chargeClient = await user.CreateClient(Policies.CanUseInternalLightningNode); var chargeClient = await user.CreateClient(Policies.CanUseInternalLightningNode);
var info = await chargeClient.GetLightningNodeInfo("BTC"); var info = await chargeClient.GetLightningNodeInfo("BTC");
Assert.Single(info.NodeURIs); Assert.Single(info.NodeURIs);
Assert.NotEqual(0, info.BlockHeight); Assert.NotEqual(0, info.BlockHeight);
@@ -2403,7 +2411,7 @@ namespace BTCPayServer.Tests
Assert.NotNull(payResponse.FeeAmount); Assert.NotNull(payResponse.FeeAmount);
Assert.NotNull(payResponse.TotalAmount); Assert.NotNull(payResponse.TotalAmount);
Assert.NotNull(payResponse.PaymentHash); Assert.NotNull(payResponse.PaymentHash);
// check the get invoice response // check the get invoice response
var merchInvoice = await merchantClient.GetLightningInvoice(merchant.StoreId, "BTC", merchantInvoice.Id); var merchInvoice = await merchantClient.GetLightningInvoice(merchant.StoreId, "BTC", merchantInvoice.Id);
Assert.NotNull(merchInvoice); Assert.NotNull(merchInvoice);
@@ -2442,7 +2450,7 @@ namespace BTCPayServer.Tests
// Amount received might be bigger because of internal implementation shit from lightning // Amount received might be bigger because of internal implementation shit from lightning
Assert.True(LightMoney.Satoshis(1000) <= invoice.AmountReceived); Assert.True(LightMoney.Satoshis(1000) <= invoice.AmountReceived);
// check payments list for store node // check payments list for store node
var payments = await client.GetLightningPayments(user.StoreId, "BTC"); var payments = await client.GetLightningPayments(user.StoreId, "BTC");
Assert.NotEmpty(payments); Assert.NotEmpty(payments);
@@ -2488,7 +2496,7 @@ namespace BTCPayServer.Tests
var user = tester.NewAccount(); var user = tester.NewAccount();
await user.GrantAccessAsync(true); await user.GrantAccessAsync(true);
user.RegisterLightningNode("BTC", LightningConnectionType.CLightning); user.RegisterLightningNode("BTC", LightningConnectionType.CLightning);
var client = await user.CreateClient(Policies.Unrestricted); var client = await user.CreateClient(Policies.Unrestricted);
var invoice = await client.CreateInvoice(user.StoreId, var invoice = await client.CreateInvoice(user.StoreId,
new CreateInvoiceRequest new CreateInvoiceRequest
@@ -2503,12 +2511,12 @@ namespace BTCPayServer.Tests
}); });
var pm = Assert.Single(await client.GetInvoicePaymentMethods(user.StoreId, invoice.Id)); var pm = Assert.Single(await client.GetInvoicePaymentMethods(user.StoreId, invoice.Id));
Assert.False(pm.AdditionalData.HasValues); Assert.False(pm.AdditionalData.HasValues);
var resp = await tester.CustomerLightningD.Pay(pm.Destination); var resp = await tester.CustomerLightningD.Pay(pm.Destination);
Assert.Equal(PayResult.Ok, resp.Result); Assert.Equal(PayResult.Ok, resp.Result);
Assert.NotNull(resp.Details.PaymentHash); Assert.NotNull(resp.Details.PaymentHash);
Assert.NotNull(resp.Details.Preimage); Assert.NotNull(resp.Details.Preimage);
pm = Assert.Single(await client.GetInvoicePaymentMethods(user.StoreId, invoice.Id)); pm = Assert.Single(await client.GetInvoicePaymentMethods(user.StoreId, invoice.Id));
Assert.True(pm.AdditionalData.HasValues); Assert.True(pm.AdditionalData.HasValues);
Assert.Equal(resp.Details.PaymentHash.ToString(), pm.AdditionalData.GetValue("paymentHash")); Assert.Equal(resp.Details.PaymentHash.ToString(), pm.AdditionalData.GetValue("paymentHash"));
@@ -3193,7 +3201,7 @@ namespace BTCPayServer.Tests
} }
[Fact(Timeout =TestTimeout)] [Fact(Timeout = TestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task StoreLightningAddressesAPITests() public async Task StoreLightningAddressesAPITests()
{ {
@@ -3205,14 +3213,14 @@ namespace BTCPayServer.Tests
var store = await adminClient.GetStore(admin.StoreId); var store = await adminClient.GetStore(admin.StoreId);
Assert.Empty(await adminClient.GetStorePaymentMethods(store.Id)); Assert.Empty(await adminClient.GetStorePaymentMethods(store.Id));
var store2 = (await adminClient.CreateStore(new CreateStoreRequest() {Name = "test2"})).Id; var store2 = (await adminClient.CreateStore(new CreateStoreRequest() { Name = "test2" })).Id;
var address1 = Guid.NewGuid().ToString("n").Substring(0, 8); var address1 = Guid.NewGuid().ToString("n").Substring(0, 8);
var address2 = Guid.NewGuid().ToString("n").Substring(0, 8); var address2 = Guid.NewGuid().ToString("n").Substring(0, 8);
Assert.Empty(await adminClient.GetStoreLightningAddresses(store.Id)); Assert.Empty(await adminClient.GetStoreLightningAddresses(store.Id));
Assert.Empty(await adminClient.GetStoreLightningAddresses(store2)); Assert.Empty(await adminClient.GetStoreLightningAddresses(store2));
await adminClient.AddOrUpdateStoreLightningAddress(store.Id, address1, new LightningAddressData()); await adminClient.AddOrUpdateStoreLightningAddress(store.Id, address1, new LightningAddressData());
await adminClient.AddOrUpdateStoreLightningAddress(store.Id, address1, new LightningAddressData() await adminClient.AddOrUpdateStoreLightningAddress(store.Id, address1, new LightningAddressData()
{ {
Max = 1 Max = 1
@@ -3221,8 +3229,8 @@ namespace BTCPayServer.Tests
{ {
await adminClient.AddOrUpdateStoreLightningAddress(store2, address1, new LightningAddressData()); await adminClient.AddOrUpdateStoreLightningAddress(store2, address1, new LightningAddressData());
}); });
Assert.Equal(1,Assert.Single(await adminClient.GetStoreLightningAddresses(store.Id)).Max); Assert.Equal(1, Assert.Single(await adminClient.GetStoreLightningAddresses(store.Id)).Max);
Assert.Empty(await adminClient.GetStoreLightningAddresses(store2)); Assert.Empty(await adminClient.GetStoreLightningAddresses(store2));
await adminClient.AddOrUpdateStoreLightningAddress(store2, address2, new LightningAddressData()); await adminClient.AddOrUpdateStoreLightningAddress(store2, address2, new LightningAddressData());
@@ -3233,8 +3241,8 @@ namespace BTCPayServer.Tests
await adminClient.RemoveStoreLightningAddress(store2, address1); await adminClient.RemoveStoreLightningAddress(store2, address1);
}); });
await adminClient.RemoveStoreLightningAddress(store2, address2); await adminClient.RemoveStoreLightningAddress(store2, address2);
Assert.Empty(await adminClient.GetStoreLightningAddresses(store2)); Assert.Empty(await adminClient.GetStoreLightningAddresses(store2));
} }
[Fact(Timeout = 60 * 2 * 1000)] [Fact(Timeout = 60 * 2 * 1000)]
@@ -3706,7 +3714,7 @@ namespace BTCPayServer.Tests
Assert.Equal(0.9m, Assert.Equal(0.9m,
Assert.Single(await clientBasic.GetStoreRates(user.StoreId, new[] { "BTC_XYZ" })).Rate); Assert.Single(await clientBasic.GetStoreRates(user.StoreId, new[] { "BTC_XYZ" })).Rate);
config = await clientBasic.GetStoreRateConfiguration(user.StoreId); config = await clientBasic.GetStoreRateConfiguration(user.StoreId);
Assert.NotNull(config); Assert.NotNull(config);
Assert.NotNull(config.EffectiveScript); Assert.NotNull(config.EffectiveScript);
@@ -3940,7 +3948,7 @@ clientBasic.PreviewUpdateStoreRateConfiguration(user.StoreId, new StoreRateConfi
var withdrawalClient = await admin.CreateClient(Policies.CanWithdrawFromCustodianAccounts); var withdrawalClient = await admin.CreateClient(Policies.CanWithdrawFromCustodianAccounts);
var depositClient = await admin.CreateClient(Policies.CanDepositToCustodianAccounts); var depositClient = await admin.CreateClient(Policies.CanDepositToCustodianAccounts);
var tradeClient = await admin.CreateClient(Policies.CanTradeCustodianAccount); var tradeClient = await admin.CreateClient(Policies.CanTradeCustodianAccount);
var store = await adminClient.GetStore(admin.StoreId); var store = await adminClient.GetStore(admin.StoreId);
var storeId = store.Id; var storeId = store.Id;
@@ -3981,19 +3989,19 @@ clientBasic.PreviewUpdateStoreRateConfiguration(user.StoreId, new StoreRateConfi
// Test: GetDepositAddress, unauth // Test: GetDepositAddress, unauth
await AssertHttpError(401, async () => await unauthClient.GetCustodianAccountDepositAddress(storeId, accountId, MockCustodian.DepositPaymentMethod)); await AssertHttpError(401, async () => await unauthClient.GetCustodianAccountDepositAddress(storeId, accountId, MockCustodian.DepositPaymentMethod));
// Test: GetDepositAddress, auth, but wrong permission // Test: GetDepositAddress, auth, but wrong permission
await AssertHttpError(403, async () => await managerClient.GetCustodianAccountDepositAddress(storeId, accountId, MockCustodian.DepositPaymentMethod)); await AssertHttpError(403, async () => await managerClient.GetCustodianAccountDepositAddress(storeId, accountId, MockCustodian.DepositPaymentMethod));
// Test: GetDepositAddress, wrong payment method // Test: GetDepositAddress, wrong payment method
await AssertApiError( 400, "unsupported-payment-method", async () => await depositClient.GetCustodianAccountDepositAddress(storeId, accountId, "WRONG-PaymentMethod")); await AssertApiError(400, "unsupported-payment-method", async () => await depositClient.GetCustodianAccountDepositAddress(storeId, accountId, "WRONG-PaymentMethod"));
// Test: GetDepositAddress, wrong store ID // Test: GetDepositAddress, wrong store ID
await AssertHttpError(403, async () => await depositClient.GetCustodianAccountDepositAddress("WRONG-STORE", accountId, MockCustodian.DepositPaymentMethod)); await AssertHttpError(403, async () => await depositClient.GetCustodianAccountDepositAddress("WRONG-STORE", accountId, MockCustodian.DepositPaymentMethod));
// Test: GetDepositAddress, wrong account ID // Test: GetDepositAddress, wrong account ID
await AssertHttpError(404, async () => await depositClient.GetCustodianAccountDepositAddress(storeId, "WRONG-ACCOUNT-ID", MockCustodian.DepositPaymentMethod)); await AssertHttpError(404, async () => await depositClient.GetCustodianAccountDepositAddress(storeId, "WRONG-ACCOUNT-ID", MockCustodian.DepositPaymentMethod));
// Test: GetDepositAddress, correct payment method // Test: GetDepositAddress, correct payment method
var depositAddress = await depositClient.GetCustodianAccountDepositAddress(storeId, accountId, MockCustodian.DepositPaymentMethod); var depositAddress = await depositClient.GetCustodianAccountDepositAddress(storeId, accountId, MockCustodian.DepositPaymentMethod);
Assert.NotNull(depositAddress); Assert.NotNull(depositAddress);
@@ -4001,7 +4009,7 @@ clientBasic.PreviewUpdateStoreRateConfiguration(user.StoreId, new StoreRateConfi
// Test: Trade, unauth // Test: Trade, unauth
var tradeRequest = new TradeRequestData { FromAsset = MockCustodian.TradeFromAsset, ToAsset = MockCustodian.TradeToAsset, Qty = new TradeQuantity(MockCustodian.TradeQtyBought, TradeQuantity.ValueType.Exact)}; var tradeRequest = new TradeRequestData { FromAsset = MockCustodian.TradeFromAsset, ToAsset = MockCustodian.TradeToAsset, Qty = new TradeQuantity(MockCustodian.TradeQtyBought, TradeQuantity.ValueType.Exact) };
await AssertHttpError(401, async () => await unauthClient.MarketTradeCustodianAccountAsset(storeId, accountId, tradeRequest)); await AssertHttpError(401, async () => await unauthClient.MarketTradeCustodianAccountAsset(storeId, accountId, tradeRequest));
// Test: Trade, auth, but wrong permission // Test: Trade, auth, but wrong permission
@@ -4049,11 +4057,11 @@ clientBasic.PreviewUpdateStoreRateConfiguration(user.StoreId, new StoreRateConfi
// Test: GetTradeQuote, unauth // Test: GetTradeQuote, unauth
await AssertHttpError(401, async () => await unauthClient.GetCustodianAccountTradeQuote(storeId, accountId, MockCustodian.TradeFromAsset, MockCustodian.TradeToAsset)); await AssertHttpError(401, async () => await unauthClient.GetCustodianAccountTradeQuote(storeId, accountId, MockCustodian.TradeFromAsset, MockCustodian.TradeToAsset));
// Test: GetTradeQuote, auth, but wrong permission // Test: GetTradeQuote, auth, but wrong permission
await AssertHttpError(403, async () => await managerClient.GetCustodianAccountTradeQuote(storeId, accountId, MockCustodian.TradeFromAsset, MockCustodian.TradeToAsset)); await AssertHttpError(403, async () => await managerClient.GetCustodianAccountTradeQuote(storeId, accountId, MockCustodian.TradeFromAsset, MockCustodian.TradeToAsset));
// Test: GetTradeQuote, auth, correct permission // Test: GetTradeQuote, auth, correct permission
var tradeQuote = await tradeClient.GetCustodianAccountTradeQuote(storeId, accountId, MockCustodian.TradeFromAsset, MockCustodian.TradeToAsset); var tradeQuote = await tradeClient.GetCustodianAccountTradeQuote(storeId, accountId, MockCustodian.TradeFromAsset, MockCustodian.TradeToAsset);
Assert.NotNull(tradeQuote); Assert.NotNull(tradeQuote);
@@ -4063,28 +4071,28 @@ clientBasic.PreviewUpdateStoreRateConfiguration(user.StoreId, new StoreRateConfi
Assert.Equal(MockCustodian.BtcPriceInEuro, tradeQuote.Ask); Assert.Equal(MockCustodian.BtcPriceInEuro, tradeQuote.Ask);
// Test: GetTradeQuote, SATS // Test: GetTradeQuote, SATS
await AssertApiError(400, "use-asset-synonym", async () => await tradeClient.GetCustodianAccountTradeQuote(storeId, accountId, MockCustodian.TradeFromAsset, "SATS")); await AssertApiError(400, "use-asset-synonym", async () => await tradeClient.GetCustodianAccountTradeQuote(storeId, accountId, MockCustodian.TradeFromAsset, "SATS"));
// Test: GetTradeQuote, wrong asset // Test: GetTradeQuote, wrong asset
await AssertHttpError(404, async () => await tradeClient.GetCustodianAccountTradeQuote(storeId, accountId, "WRONG-ASSET", MockCustodian.TradeToAsset)); await AssertHttpError(404, async () => await tradeClient.GetCustodianAccountTradeQuote(storeId, accountId, "WRONG-ASSET", MockCustodian.TradeToAsset));
await AssertHttpError(404, async () => await tradeClient.GetCustodianAccountTradeQuote(storeId, accountId, MockCustodian.TradeFromAsset , "WRONG-ASSET")); await AssertHttpError(404, async () => await tradeClient.GetCustodianAccountTradeQuote(storeId, accountId, MockCustodian.TradeFromAsset, "WRONG-ASSET"));
// Test: wrong account ID // Test: wrong account ID
await AssertHttpError(404, async () => await tradeClient.GetCustodianAccountTradeQuote(storeId, "WRONG-ACCOUNT-ID", MockCustodian.TradeFromAsset, MockCustodian.TradeToAsset)); await AssertHttpError(404, async () => await tradeClient.GetCustodianAccountTradeQuote(storeId, "WRONG-ACCOUNT-ID", MockCustodian.TradeFromAsset, MockCustodian.TradeToAsset));
// Test: wrong store ID // Test: wrong store ID
await AssertHttpError(403, async () => await tradeClient.GetCustodianAccountTradeQuote("WRONG-STORE-ID", accountId, MockCustodian.TradeFromAsset, MockCustodian.TradeToAsset)); await AssertHttpError(403, async () => await tradeClient.GetCustodianAccountTradeQuote("WRONG-STORE-ID", accountId, MockCustodian.TradeFromAsset, MockCustodian.TradeToAsset));
// Test: GetTradeInfo, unauth // Test: GetTradeInfo, unauth
await AssertHttpError(401, async () => await unauthClient.GetCustodianAccountTradeInfo(storeId, accountId, MockCustodian.TradeId)); await AssertHttpError(401, async () => await unauthClient.GetCustodianAccountTradeInfo(storeId, accountId, MockCustodian.TradeId));
// Test: GetTradeInfo, auth, but wrong permission // Test: GetTradeInfo, auth, but wrong permission
await AssertHttpError(403, async () => await managerClient.GetCustodianAccountTradeInfo(storeId, accountId, MockCustodian.TradeId)); await AssertHttpError(403, async () => await managerClient.GetCustodianAccountTradeInfo(storeId, accountId, MockCustodian.TradeId));
// Test: GetTradeInfo, auth, correct permission // Test: GetTradeInfo, auth, correct permission
var tradeResult = await tradeClient.GetCustodianAccountTradeInfo(storeId, accountId, MockCustodian.TradeId); var tradeResult = await tradeClient.GetCustodianAccountTradeInfo(storeId, accountId, MockCustodian.TradeId);
Assert.NotNull(tradeResult); Assert.NotNull(tradeResult);
@@ -4107,36 +4115,36 @@ clientBasic.PreviewUpdateStoreRateConfiguration(user.StoreId, new StoreRateConfi
// Test: GetTradeInfo, wrong trade ID // Test: GetTradeInfo, wrong trade ID
await AssertHttpError(404, async () => await tradeClient.GetCustodianAccountTradeInfo(storeId, accountId, "WRONG-TRADE-ID")); await AssertHttpError(404, async () => await tradeClient.GetCustodianAccountTradeInfo(storeId, accountId, "WRONG-TRADE-ID"));
// Test: wrong account ID // Test: wrong account ID
await AssertHttpError(404, async () => await tradeClient.GetCustodianAccountTradeInfo(storeId, "WRONG-ACCOUNT-ID", MockCustodian.TradeId)); await AssertHttpError(404, async () => await tradeClient.GetCustodianAccountTradeInfo(storeId, "WRONG-ACCOUNT-ID", MockCustodian.TradeId));
// Test: wrong store ID // Test: wrong store ID
await AssertHttpError(403, async () => await tradeClient.GetCustodianAccountTradeInfo("WRONG-STORE-ID", accountId, MockCustodian.TradeId)); await AssertHttpError(403, async () => await tradeClient.GetCustodianAccountTradeInfo("WRONG-STORE-ID", accountId, MockCustodian.TradeId));
var qty = new TradeQuantity(MockCustodian.WithdrawalAmount, TradeQuantity.ValueType.Exact); var qty = new TradeQuantity(MockCustodian.WithdrawalAmount, TradeQuantity.ValueType.Exact);
// Test: SimulateWithdrawal, unauth // Test: SimulateWithdrawal, unauth
var simulateWithdrawalRequest = new WithdrawRequestData(MockCustodian.WithdrawalPaymentMethod, qty); var simulateWithdrawalRequest = new WithdrawRequestData(MockCustodian.WithdrawalPaymentMethod, qty);
await AssertHttpError(401, async () => await unauthClient.SimulateCustodianAccountWithdrawal(storeId, accountId, simulateWithdrawalRequest)); await AssertHttpError(401, async () => await unauthClient.SimulateCustodianAccountWithdrawal(storeId, accountId, simulateWithdrawalRequest));
// Test: SimulateWithdrawal, auth, but wrong permission // Test: SimulateWithdrawal, auth, but wrong permission
await AssertHttpError(403, async () => await managerClient.SimulateCustodianAccountWithdrawal(storeId, accountId, simulateWithdrawalRequest)); await AssertHttpError(403, async () => await managerClient.SimulateCustodianAccountWithdrawal(storeId, accountId, simulateWithdrawalRequest));
// Test: SimulateWithdrawal, correct payment method, correct amount // Test: SimulateWithdrawal, correct payment method, correct amount
var simulateWithdrawResponse = await withdrawalClient.SimulateCustodianAccountWithdrawal(storeId, accountId, simulateWithdrawalRequest); var simulateWithdrawResponse = await withdrawalClient.SimulateCustodianAccountWithdrawal(storeId, accountId, simulateWithdrawalRequest);
AssertMockWithdrawal(simulateWithdrawResponse, custodianAccountData); AssertMockWithdrawal(simulateWithdrawResponse, custodianAccountData);
// Test: SimulateWithdrawal, wrong payment method // Test: SimulateWithdrawal, wrong payment method
var wrongPaymentMethodSimulateWithdrawalRequest = new WithdrawRequestData("WRONG-PAYMENT-METHOD", qty); var wrongPaymentMethodSimulateWithdrawalRequest = new WithdrawRequestData("WRONG-PAYMENT-METHOD", qty);
await AssertApiError( 400, "unsupported-payment-method", async () => await withdrawalClient.SimulateCustodianAccountWithdrawal(storeId, accountId, wrongPaymentMethodSimulateWithdrawalRequest)); await AssertApiError(400, "unsupported-payment-method", async () => await withdrawalClient.SimulateCustodianAccountWithdrawal(storeId, accountId, wrongPaymentMethodSimulateWithdrawalRequest));
// Test: SimulateWithdrawal, wrong account ID // Test: SimulateWithdrawal, wrong account ID
await AssertHttpError(404, async () => await withdrawalClient.SimulateCustodianAccountWithdrawal(storeId, "WRONG-ACCOUNT-ID", simulateWithdrawalRequest)); await AssertHttpError(404, async () => await withdrawalClient.SimulateCustodianAccountWithdrawal(storeId, "WRONG-ACCOUNT-ID", simulateWithdrawalRequest));
// Test: SimulateWithdrawal, wrong store ID // Test: SimulateWithdrawal, wrong store ID
// TODO it is wierd that 403 is considered normal, but it is like this for all calls where the store is wrong... I'd have preferred a 404 error, because the store cannot be found. // TODO it is wierd that 403 is considered normal, but it is like this for all calls where the store is wrong... I'd have preferred a 404 error, because the store cannot be found.
await AssertHttpError(403, async () => await withdrawalClient.SimulateCustodianAccountWithdrawal( "WRONG-STORE-ID",accountId, simulateWithdrawalRequest)); await AssertHttpError(403, async () => await withdrawalClient.SimulateCustodianAccountWithdrawal("WRONG-STORE-ID", accountId, simulateWithdrawalRequest));
// Test: SimulateWithdrawal, correct payment method, wrong amount // Test: SimulateWithdrawal, correct payment method, wrong amount
var wrongAmountSimulateWithdrawalRequest = new WithdrawRequestData(MockCustodian.WithdrawalPaymentMethod, TradeQuantity.Parse("0.666")); var wrongAmountSimulateWithdrawalRequest = new WithdrawRequestData(MockCustodian.WithdrawalPaymentMethod, TradeQuantity.Parse("0.666"));
await AssertHttpError(400, async () => await withdrawalClient.SimulateCustodianAccountWithdrawal(storeId, accountId, wrongAmountSimulateWithdrawalRequest)); await AssertHttpError(400, async () => await withdrawalClient.SimulateCustodianAccountWithdrawal(storeId, accountId, wrongAmountSimulateWithdrawalRequest));
@@ -4145,53 +4153,53 @@ clientBasic.PreviewUpdateStoreRateConfiguration(user.StoreId, new StoreRateConfi
var createWithdrawalRequest = new WithdrawRequestData(MockCustodian.WithdrawalPaymentMethod, qty); var createWithdrawalRequest = new WithdrawRequestData(MockCustodian.WithdrawalPaymentMethod, qty);
var createWithdrawalRequestPercentage = new WithdrawRequestData(MockCustodian.WithdrawalPaymentMethod, qty); var createWithdrawalRequestPercentage = new WithdrawRequestData(MockCustodian.WithdrawalPaymentMethod, qty);
await AssertHttpError(401, async () => await unauthClient.CreateCustodianAccountWithdrawal(storeId, accountId, createWithdrawalRequest)); await AssertHttpError(401, async () => await unauthClient.CreateCustodianAccountWithdrawal(storeId, accountId, createWithdrawalRequest));
// Test: CreateWithdrawal, auth, but wrong permission // Test: CreateWithdrawal, auth, but wrong permission
await AssertHttpError(403, async () => await managerClient.CreateCustodianAccountWithdrawal(storeId, accountId, createWithdrawalRequest)); await AssertHttpError(403, async () => await managerClient.CreateCustodianAccountWithdrawal(storeId, accountId, createWithdrawalRequest));
// Test: CreateWithdrawal, correct payment method, correct amount // Test: CreateWithdrawal, correct payment method, correct amount
var withdrawResponse = await withdrawalClient.CreateCustodianAccountWithdrawal(storeId, accountId, createWithdrawalRequest); var withdrawResponse = await withdrawalClient.CreateCustodianAccountWithdrawal(storeId, accountId, createWithdrawalRequest);
AssertMockWithdrawal(withdrawResponse, custodianAccountData); AssertMockWithdrawal(withdrawResponse, custodianAccountData);
// Test: CreateWithdrawal, correct payment method, correct amount, but as a percentage // Test: CreateWithdrawal, correct payment method, correct amount, but as a percentage
var withdrawWithPercentageResponse = await withdrawalClient.CreateCustodianAccountWithdrawal(storeId, accountId, createWithdrawalRequestPercentage); var withdrawWithPercentageResponse = await withdrawalClient.CreateCustodianAccountWithdrawal(storeId, accountId, createWithdrawalRequestPercentage);
AssertMockWithdrawal(withdrawWithPercentageResponse, custodianAccountData); AssertMockWithdrawal(withdrawWithPercentageResponse, custodianAccountData);
// Test: CreateWithdrawal, wrong payment method // Test: CreateWithdrawal, wrong payment method
var wrongPaymentMethodCreateWithdrawalRequest = new WithdrawRequestData("WRONG-PAYMENT-METHOD", qty); var wrongPaymentMethodCreateWithdrawalRequest = new WithdrawRequestData("WRONG-PAYMENT-METHOD", qty);
await AssertApiError( 400, "unsupported-payment-method", async () => await withdrawalClient.CreateCustodianAccountWithdrawal(storeId, accountId, wrongPaymentMethodCreateWithdrawalRequest)); await AssertApiError(400, "unsupported-payment-method", async () => await withdrawalClient.CreateCustodianAccountWithdrawal(storeId, accountId, wrongPaymentMethodCreateWithdrawalRequest));
// Test: CreateWithdrawal, wrong account ID // Test: CreateWithdrawal, wrong account ID
await AssertHttpError(404, async () => await withdrawalClient.CreateCustodianAccountWithdrawal(storeId, "WRONG-ACCOUNT-ID", createWithdrawalRequest)); await AssertHttpError(404, async () => await withdrawalClient.CreateCustodianAccountWithdrawal(storeId, "WRONG-ACCOUNT-ID", createWithdrawalRequest));
// Test: CreateWithdrawal, wrong store ID // Test: CreateWithdrawal, wrong store ID
// TODO it is wierd that 403 is considered normal, but it is like this for all calls where the store is wrong... I'd have preferred a 404 error, because the store cannot be found. // TODO it is wierd that 403 is considered normal, but it is like this for all calls where the store is wrong... I'd have preferred a 404 error, because the store cannot be found.
await AssertHttpError(403, async () => await withdrawalClient.CreateCustodianAccountWithdrawal( "WRONG-STORE-ID",accountId, createWithdrawalRequest)); await AssertHttpError(403, async () => await withdrawalClient.CreateCustodianAccountWithdrawal("WRONG-STORE-ID", accountId, createWithdrawalRequest));
// Test: CreateWithdrawal, correct payment method, wrong amount // Test: CreateWithdrawal, correct payment method, wrong amount
var wrongAmountCreateWithdrawalRequest = new WithdrawRequestData(MockCustodian.WithdrawalPaymentMethod, TradeQuantity.Parse("0.666")); var wrongAmountCreateWithdrawalRequest = new WithdrawRequestData(MockCustodian.WithdrawalPaymentMethod, TradeQuantity.Parse("0.666"));
await AssertHttpError(400, async () => await withdrawalClient.CreateCustodianAccountWithdrawal(storeId, accountId, wrongAmountCreateWithdrawalRequest)); await AssertHttpError(400, async () => await withdrawalClient.CreateCustodianAccountWithdrawal(storeId, accountId, wrongAmountCreateWithdrawalRequest));
// Test: GetWithdrawalInfo, unauth // Test: GetWithdrawalInfo, unauth
await AssertHttpError(401, async () => await unauthClient.GetCustodianAccountWithdrawalInfo(storeId, accountId, MockCustodian.WithdrawalPaymentMethod, MockCustodian.WithdrawalId)); await AssertHttpError(401, async () => await unauthClient.GetCustodianAccountWithdrawalInfo(storeId, accountId, MockCustodian.WithdrawalPaymentMethod, MockCustodian.WithdrawalId));
// Test: GetWithdrawalInfo, auth, but wrong permission // Test: GetWithdrawalInfo, auth, but wrong permission
await AssertHttpError(403, async () => await managerClient.GetCustodianAccountWithdrawalInfo(storeId, accountId, MockCustodian.WithdrawalPaymentMethod, MockCustodian.WithdrawalId)); await AssertHttpError(403, async () => await managerClient.GetCustodianAccountWithdrawalInfo(storeId, accountId, MockCustodian.WithdrawalPaymentMethod, MockCustodian.WithdrawalId));
// Test: GetWithdrawalInfo, auth, correct permission // Test: GetWithdrawalInfo, auth, correct permission
var withdrawalInfo = await withdrawalClient.GetCustodianAccountWithdrawalInfo(storeId, accountId, MockCustodian.WithdrawalPaymentMethod, MockCustodian.WithdrawalId); var withdrawalInfo = await withdrawalClient.GetCustodianAccountWithdrawalInfo(storeId, accountId, MockCustodian.WithdrawalPaymentMethod, MockCustodian.WithdrawalId);
AssertMockWithdrawal(withdrawalInfo, custodianAccountData); AssertMockWithdrawal(withdrawalInfo, custodianAccountData);
// Test: GetWithdrawalInfo, wrong withdrawal ID // Test: GetWithdrawalInfo, wrong withdrawal ID
await AssertHttpError(404, async () => await withdrawalClient.GetCustodianAccountWithdrawalInfo(storeId, accountId, MockCustodian.WithdrawalPaymentMethod, "WRONG-WITHDRAWAL-ID")); await AssertHttpError(404, async () => await withdrawalClient.GetCustodianAccountWithdrawalInfo(storeId, accountId, MockCustodian.WithdrawalPaymentMethod, "WRONG-WITHDRAWAL-ID"));
// Test: wrong account ID // Test: wrong account ID
await AssertHttpError(404, async () => await withdrawalClient.GetCustodianAccountWithdrawalInfo(storeId, "WRONG-ACCOUNT-ID", MockCustodian.WithdrawalPaymentMethod, MockCustodian.WithdrawalId)); await AssertHttpError(404, async () => await withdrawalClient.GetCustodianAccountWithdrawalInfo(storeId, "WRONG-ACCOUNT-ID", MockCustodian.WithdrawalPaymentMethod, MockCustodian.WithdrawalId));
// Test: wrong store ID // Test: wrong store ID
// TODO shouldn't this be 404? I cannot change this without bigger impact, as it would affect all API endpoints that are store centered // TODO shouldn't this be 404? I cannot change this without bigger impact, as it would affect all API endpoints that are store centered
await AssertHttpError(403, async () => await withdrawalClient.GetCustodianAccountWithdrawalInfo("WRONG-STORE-ID", accountId, MockCustodian.WithdrawalPaymentMethod, MockCustodian.WithdrawalId)); await AssertHttpError(403, async () => await withdrawalClient.GetCustodianAccountWithdrawalInfo("WRONG-STORE-ID", accountId, MockCustodian.WithdrawalPaymentMethod, MockCustodian.WithdrawalId));
// TODO assert API error codes, not just status codes by using AssertCustodianApiError() // TODO assert API error codes, not just status codes by using AssertCustodianApiError()
// TODO also test withdrawals for the various "Status" (Queued, Complete, Failed) // TODO also test withdrawals for the various "Status" (Queued, Complete, Failed)
// TODO create a mock custodian with only ICustodian // TODO create a mock custodian with only ICustodian

View File

@@ -139,7 +139,7 @@ public class MockCustodian : ICustodian, ICanDeposit, ICanTrade, ICanWithdraw
var r = new WithdrawResult(WithdrawalPaymentMethod, WithdrawalAsset, ledgerEntries, WithdrawalId, WithdrawalStatus, createdTime, WithdrawalTargetAddress, WithdrawalTransactionId); var r = new WithdrawResult(WithdrawalPaymentMethod, WithdrawalAsset, ledgerEntries, WithdrawalId, WithdrawalStatus, createdTime, WithdrawalTargetAddress, WithdrawalTransactionId);
return r; return r;
} }
private SimulateWithdrawalResult CreateWithdrawSimulationResult() private SimulateWithdrawalResult CreateWithdrawSimulationResult()
{ {
var ledgerEntries = new List<LedgerEntryData>(); var ledgerEntries = new List<LedgerEntryData>();
@@ -153,7 +153,7 @@ public class MockCustodian : ICustodian, ICanDeposit, ICanTrade, ICanWithdraw
{ {
if (paymentMethod == WithdrawalPaymentMethod) if (paymentMethod == WithdrawalPaymentMethod)
{ {
if (amount.ToString(CultureInfo.InvariantCulture).Equals(""+WithdrawalAmount, StringComparison.InvariantCulture) || WithdrawalAmountPercentage.Equals(amount)) if (amount.ToString(CultureInfo.InvariantCulture).Equals("" + WithdrawalAmount, StringComparison.InvariantCulture) || WithdrawalAmountPercentage.Equals(amount))
{ {
return Task.FromResult(CreateWithdrawResult()); return Task.FromResult(CreateWithdrawResult());
} }

View File

@@ -87,7 +87,7 @@ namespace BTCPayServer.Tests
Driver.AssertNoError(); Driver.AssertNoError();
} }
public void PayInvoice(bool mine = false, decimal? amount= null) public void PayInvoice(bool mine = false, decimal? amount = null)
{ {
if (amount is not null) if (amount is not null)

View File

@@ -148,7 +148,7 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.LinkText("Remove")).Click(); s.Driver.FindElement(By.LinkText("Remove")).Click();
s.Driver.WaitForElement(By.Id("ConfirmInput")).SendKeys("DELETE"); s.Driver.WaitForElement(By.Id("ConfirmInput")).SendKeys("DELETE");
s.Driver.FindElement(By.Id("ConfirmContinue")).Click(); s.Driver.FindElement(By.Id("ConfirmContinue")).Click();
Assert.DoesNotContain("Custom Form 1", s.Driver.PageSource); Assert.DoesNotContain("Custom Form 1", s.Driver.PageSource);
s.Driver.FindElement(By.Id("CreateForm")).Click(); s.Driver.FindElement(By.Id("CreateForm")).Click();
s.Driver.FindElement(By.Name("Name")).SendKeys("Custom Form 2"); s.Driver.FindElement(By.Name("Name")).SendKeys("Custom Form 2");
@@ -163,23 +163,23 @@ namespace BTCPayServer.Tests
formurl = s.Driver.Url; formurl = s.Driver.Url;
result = await s.Server.PayTester.HttpClient.GetAsync(formurl); result = await s.Server.PayTester.HttpClient.GetAsync(formurl);
Assert.NotEqual(HttpStatusCode.NotFound, result.StatusCode); Assert.NotEqual(HttpStatusCode.NotFound, result.StatusCode);
s.GoToHome(); s.GoToHome();
s.GoToStore(StoreNavPages.Forms); s.GoToStore(StoreNavPages.Forms);
Assert.Contains("Custom Form 2", s.Driver.PageSource); Assert.Contains("Custom Form 2", s.Driver.PageSource);
s.Driver.FindElement(By.LinkText("Custom Form 2")).Click(); s.Driver.FindElement(By.LinkText("Custom Form 2")).Click();
s.Driver.FindElement(By.Name("Name")).Clear(); s.Driver.FindElement(By.Name("Name")).Clear();
s.Driver.FindElement(By.Name("Name")).SendKeys("Custom Form 3"); s.Driver.FindElement(By.Name("Name")).SendKeys("Custom Form 3");
s.Driver.FindElement(By.Id("SaveButton")).Click(); s.Driver.FindElement(By.Id("SaveButton")).Click();
s.GoToStore(StoreNavPages.Forms); s.GoToStore(StoreNavPages.Forms);
Assert.Contains("Custom Form 3", s.Driver.PageSource); Assert.Contains("Custom Form 3", s.Driver.PageSource);
s.Driver.FindElement(By.Id("StoreNav-PaymentRequests")).Click(); s.Driver.FindElement(By.Id("StoreNav-PaymentRequests")).Click();
s.Driver.FindElement(By.Id("CreatePaymentRequest")).Click(); s.Driver.FindElement(By.Id("CreatePaymentRequest")).Click();
Assert.Equal(4, new SelectElement(s.Driver.FindElement(By.Id("FormId"))).Options.Count); Assert.Equal(4, new SelectElement(s.Driver.FindElement(By.Id("FormId"))).Options.Count);
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = TestTimeout)]
@@ -1377,9 +1377,9 @@ namespace BTCPayServer.Tests
await Task.Delay(500); await Task.Delay(500);
s.Driver.WaitForElement(By.CssSelector("div.label-manager input")).SendKeys("label2" + Keys.Enter); s.Driver.WaitForElement(By.CssSelector("div.label-manager input")).SendKeys("label2" + Keys.Enter);
}); });
TestUtils.Eventually(() => TestUtils.Eventually(() =>
{ {
s.Driver.Navigate().Refresh(); s.Driver.Navigate().Refresh();
Assert.NotNull(s.Driver.FindElement(By.CssSelector("[data-value='test-label']"))); Assert.NotNull(s.Driver.FindElement(By.CssSelector("[data-value='test-label']")));
}); });
@@ -1401,10 +1401,10 @@ namespace BTCPayServer.Tests
s.Driver.WaitForElement(By.CssSelector("[data-value='test-label']")).Click(); s.Driver.WaitForElement(By.CssSelector("[data-value='test-label']")).Click();
await Task.Delay(500); await Task.Delay(500);
s.Driver.ExecuteJavaScript("document.querySelector('[data-value=\"test-label\"]').nextSibling.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Delete', keyCode: 46}));"); s.Driver.ExecuteJavaScript("document.querySelector('[data-value=\"test-label\"]').nextSibling.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Delete', keyCode: 46}));");
}); });
TestUtils.Eventually(() => TestUtils.Eventually(() =>
{ {
s.Driver.Navigate().Refresh(); s.Driver.Navigate().Refresh();
Assert.DoesNotContain("test-label", s.Driver.PageSource); Assert.DoesNotContain("test-label", s.Driver.PageSource);
}); });

View File

@@ -1609,7 +1609,7 @@ namespace BTCPayServer.Tests
// Check correct casing: Addresses in payment URI need to be … // Check correct casing: Addresses in payment URI need to be …
// - lowercase in link version // - lowercase in link version
// - uppercase in QR version // - uppercase in QR version
// Standard for all uppercase characters in QR codes is still not implemented in all wallets // Standard for all uppercase characters in QR codes is still not implemented in all wallets
// But we're proceeding with BECH32 being uppercase // But we're proceeding with BECH32 being uppercase
Assert.Equal($"bitcoin:{paymentMethodUnified.BtcAddress}", paymentMethodUnified.InvoiceBitcoinUrl.Split('?')[0]); Assert.Equal($"bitcoin:{paymentMethodUnified.BtcAddress}", paymentMethodUnified.InvoiceBitcoinUrl.Split('?')[0]);

View File

@@ -61,34 +61,34 @@ namespace BTCPayServer.Tests
return description; return description;
} }
// /// <summary> // /// <summary>
// /// This will take the translations from v1 or v2 // /// This will take the translations from v1 or v2
// /// and upload them to transifex if not found // /// and upload them to transifex if not found
// /// </summary> // /// </summary>
// [FactWithSecret("TransifexAPIToken")] // [FactWithSecret("TransifexAPIToken")]
// [Trait("Utilities", "Utilities")] // [Trait("Utilities", "Utilities")]
//#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously //#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
// public async Task UpdateTransifex() // public async Task UpdateTransifex()
// { // {
// // DO NOT RUN IT, THIS WILL ERASE THE CURRENT TRANSIFEX TRANSLATIONS // // DO NOT RUN IT, THIS WILL ERASE THE CURRENT TRANSIFEX TRANSLATIONS
// var client = GetTransifexClient(); // var client = GetTransifexClient();
// var translations = JsonTranslation.GetTranslations(TranslationFolder.CheckoutV2); // var translations = JsonTranslation.GetTranslations(TranslationFolder.CheckoutV2);
// var enTranslations = translations["en"]; // var enTranslations = translations["en"];
// translations.Remove("en"); // translations.Remove("en");
// foreach (var t in translations) // foreach (var t in translations)
// { // {
// foreach (var w in t.Value.Words.ToArray()) // foreach (var w in t.Value.Words.ToArray())
// { // {
// if (t.Value.Words[w.Key] == null) // if (t.Value.Words[w.Key] == null)
// t.Value.Words[w.Key] = enTranslations.Words[w.Key]; // t.Value.Words[w.Key] = enTranslations.Words[w.Key];
// } // }
// t.Value.Words.Remove("code"); // t.Value.Words.Remove("code");
// t.Value.Words.Remove("NOTICE_WARN"); // t.Value.Words.Remove("NOTICE_WARN");
// } // }
// await client.UpdateTranslations(translations); // await client.UpdateTranslations(translations);
// } // }
//#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously //#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
@@ -147,7 +147,7 @@ namespace BTCPayServer.Tests
public async Task AutoTranslateChatGPT() public async Task AutoTranslateChatGPT()
{ {
var file = TranslationFolder.CheckoutV2; var file = TranslationFolder.CheckoutV2;
using var driver = new ChromeDriver(new ChromeOptions() using var driver = new ChromeDriver(new ChromeOptions()
{ {
DebuggerAddress = "127.0.0.1:9222" DebuggerAddress = "127.0.0.1:9222"
@@ -547,7 +547,7 @@ retry:
public string FullPath { get; set; } public string FullPath { get; set; }
public string TransifexProject { get; set; } public string TransifexProject { get; set; }
public string TransifexResource { get; private set; } public string TransifexResource { get; private set; }
public void Save() public void Save()
@@ -577,7 +577,7 @@ retry:
} }
} }
public void Translate(Dictionary<string,string> sourceTranslations) public void Translate(Dictionary<string, string> sourceTranslations)
{ {
foreach (var o in sourceTranslations) foreach (var o in sourceTranslations)
if (o.Value != null) if (o.Value != null)

View File

@@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Http;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace BTCPayServer namespace BTCPayServer
{ {

View File

@@ -59,7 +59,7 @@ namespace BTCPayServer
return Labels[num % Labels.Length]; return Labels[num % Labels.Length];
} }
} }
/// https://gist.github.com/zihotki/09fc41d52981fb6f93a81ebf20b35cd5 /// https://gist.github.com/zihotki/09fc41d52981fb6f93a81ebf20b35cd5
/// <summary> /// <summary>
/// Creates color with corrected brightness. /// Creates color with corrected brightness.
@@ -92,7 +92,7 @@ namespace BTCPayServer
return Color.FromArgb(color.A, (int)red, (int)green, (int)blue); return Color.FromArgb(color.A, (int)red, (int)green, (int)blue);
} }
public string AdjustBrightness(string html, float correctionFactor) public string AdjustBrightness(string html, float correctionFactor)
{ {
var color = AdjustBrightness(ColorTranslator.FromHtml(html), correctionFactor); var color = AdjustBrightness(ColorTranslator.FromHtml(html), correctionFactor);

View File

@@ -41,7 +41,7 @@ public class AppSales : ViewComponent
}; };
if (vm.InitialRendering) if (vm.InitialRendering)
return View(vm); return View(vm);
var app = HttpContext.GetAppData(); var app = HttpContext.GetAppData();
var stats = await _appService.GetSalesStats(app); var stats = await _appService.GetSalesStats(app);
vm.SalesCount = stats.SalesCount; vm.SalesCount = stats.SalesCount;

View File

@@ -13,7 +13,7 @@ namespace BTCPayServer.Components.LabelManager
{ {
ExcludeTypes = excludeTypes, ExcludeTypes = excludeTypes,
WalletObjectId = walletObjectId, WalletObjectId = walletObjectId,
SelectedLabels = selectedLabels?? Array.Empty<string>(), SelectedLabels = selectedLabels ?? Array.Empty<string>(),
DisplayInline = displayInline, DisplayInline = displayInline,
RichLabelInfo = richLabelInfo, RichLabelInfo = richLabelInfo,
AutoUpdate = autoUpdate, AutoUpdate = autoUpdate,
@@ -25,7 +25,7 @@ namespace BTCPayServer.Components.LabelManager
public class RichLabelInfo public class RichLabelInfo
{ {
public string Link { get; set; } public string Link { get; set; }
public string Tooltip { get; set; } public string Tooltip { get; set; }
} }
} }

View File

@@ -9,8 +9,8 @@ namespace BTCPayServer.Components.QRCode
{ {
public class QRCode : ViewComponent public class QRCode : ViewComponent
{ {
private static QRCodeGenerator _qrGenerator = new (); private static QRCodeGenerator _qrGenerator = new();
public IViewComponentResult Invoke(string data) public IViewComponentResult Invoke(string data)
{ {
var qrCodeData = _qrGenerator.CreateQrCode(data, QRCodeGenerator.ECCLevel.Q); var qrCodeData = _qrGenerator.CreateQrCode(data, QRCodeGenerator.ECCLevel.Q);

View File

@@ -59,7 +59,7 @@ public class StoreRecentTransactions : ViewComponent
var network = derivationSettings.Network; var network = derivationSettings.Network;
var wallet = _walletProvider.GetWallet(network); var wallet = _walletProvider.GetWallet(network);
var allTransactions = await wallet.FetchTransactionHistory(derivationSettings.AccountDerivation, 0, 5, TimeSpan.FromDays(31.0)); var allTransactions = await wallet.FetchTransactionHistory(derivationSettings.AccountDerivation, 0, 5, TimeSpan.FromDays(31.0));
var walletTransactionsInfo = await _walletRepository.GetWalletTransactionsInfo( vm.WalletId , allTransactions.Select(t => t.TransactionId.ToString()).ToArray()); var walletTransactionsInfo = await _walletRepository.GetWalletTransactionsInfo(vm.WalletId, allTransactions.Select(t => t.TransactionId.ToString()).ToArray());
transactions = allTransactions transactions = allTransactions
.Select(tx => .Select(tx =>

View File

@@ -137,7 +137,7 @@ namespace BTCPayServer.Configuration
foreach (var n in new BTCPayNetworkProvider(networkType).GetAll().OfType<BTCPayNetwork>()) foreach (var n in new BTCPayNetworkProvider(networkType).GetAll().OfType<BTCPayNetwork>())
{ {
builder.AppendLine(CultureInfo.InvariantCulture, $"#{n.CryptoCode}.explorer.url={n.NBXplorerNetwork.DefaultSettings.DefaultUrl}"); builder.AppendLine(CultureInfo.InvariantCulture, $"#{n.CryptoCode}.explorer.url={n.NBXplorerNetwork.DefaultSettings.DefaultUrl}");
builder.AppendLine(CultureInfo.InvariantCulture, $"#{n.CryptoCode}.explorer.cookiefile={ n.NBXplorerNetwork.DefaultSettings.DefaultCookieFile}"); builder.AppendLine(CultureInfo.InvariantCulture, $"#{n.CryptoCode}.explorer.cookiefile={n.NBXplorerNetwork.DefaultSettings.DefaultCookieFile}");
builder.AppendLine(CultureInfo.InvariantCulture, $"#{n.CryptoCode}.blockexplorerlink=https://mempool.space/tx/{{0}}"); builder.AppendLine(CultureInfo.InvariantCulture, $"#{n.CryptoCode}.blockexplorerlink=https://mempool.space/tx/{{0}}");
if (n.SupportLightning) if (n.SupportLightning)
{ {

View File

@@ -189,7 +189,7 @@ namespace BTCPayServer.Controllers.Greenfield
{ {
return AppNotFound(); return AppNotFound();
} }
return Ok(ToPointOfSaleModel(app)); return Ok(ToPointOfSaleModel(app));
} }
@@ -202,7 +202,7 @@ namespace BTCPayServer.Controllers.Greenfield
{ {
return AppNotFound(); return AppNotFound();
} }
return Ok(ToCrowdfundModel(app)); return Ok(ToCrowdfundModel(app));
} }
@@ -267,7 +267,7 @@ namespace BTCPayServer.Controllers.Greenfield
return new PointOfSaleSettings() return new PointOfSaleSettings()
{ {
Title = request.Title, Title = request.Title,
DefaultView = (PosViewType) request.DefaultView, DefaultView = (PosViewType)request.DefaultView,
ShowCustomAmount = request.ShowCustomAmount, ShowCustomAmount = request.ShowCustomAmount,
ShowDiscount = request.ShowDiscount, ShowDiscount = request.ShowDiscount,
EnableTips = request.EnableTips, EnableTips = request.EnableTips,
@@ -331,10 +331,10 @@ namespace BTCPayServer.Controllers.Greenfield
Currency = settings.Currency, Currency = settings.Currency,
Items = JsonConvert.DeserializeObject( Items = JsonConvert.DeserializeObject(
JsonConvert.SerializeObject( JsonConvert.SerializeObject(
_appService.Parse(settings.Template, settings.Currency), _appService.Parse(settings.Template, settings.Currency),
new JsonSerializerSettings new JsonSerializerSettings
{ {
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
} }
) )
), ),
@@ -406,10 +406,10 @@ namespace BTCPayServer.Controllers.Greenfield
Tagline = settings.Tagline, Tagline = settings.Tagline,
Perks = JsonConvert.DeserializeObject( Perks = JsonConvert.DeserializeObject(
JsonConvert.SerializeObject( JsonConvert.SerializeObject(
_appService.Parse(settings.PerksTemplate, settings.TargetCurrency), _appService.Parse(settings.PerksTemplate, settings.TargetCurrency),
new JsonSerializerSettings new JsonSerializerSettings
{ {
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
} }
) )
), ),

View File

@@ -362,10 +362,10 @@ namespace BTCPayServer.Controllers.Greenfield
{ {
return UnsupportedAsset(asset, ex.Message); return UnsupportedAsset(asset, ex.Message);
} }
var simulateWithdrawResult = var simulateWithdrawResult =
await withdrawableCustodian.SimulateWithdrawalAsync(request.PaymentMethod, qty, custodianAccount.GetBlob(), cancellationToken); await withdrawableCustodian.SimulateWithdrawalAsync(request.PaymentMethod, qty, custodianAccount.GetBlob(), cancellationToken);
var result = new WithdrawalSimulationResponseData(simulateWithdrawResult.PaymentMethod, simulateWithdrawResult.Asset, var result = new WithdrawalSimulationResponseData(simulateWithdrawResult.PaymentMethod, simulateWithdrawResult.Asset,
accountId, custodian.Code, simulateWithdrawResult.LedgerEntries, simulateWithdrawResult.MinQty, simulateWithdrawResult.MaxQty); accountId, custodian.Code, simulateWithdrawResult.LedgerEntries, simulateWithdrawResult.MinQty, simulateWithdrawResult.MaxQty);
return Ok(result); return Ok(result);
} }

View File

@@ -11,8 +11,8 @@ using BTCPayServer.Security;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using StoreData = BTCPayServer.Data.StoreData;
using PayoutProcessorData = BTCPayServer.Client.Models.PayoutProcessorData; using PayoutProcessorData = BTCPayServer.Client.Models.PayoutProcessorData;
using StoreData = BTCPayServer.Data.StoreData;
namespace BTCPayServer.Controllers.Greenfield namespace BTCPayServer.Controllers.Greenfield
{ {

View File

@@ -264,7 +264,8 @@ namespace BTCPayServer.Controllers.Greenfield
pullPaymentId = pullPaymentId pullPaymentId = pullPaymentId
}, Request.Scheme, Request.Host.ToString())!); }, Request.Scheme, Request.Host.ToString())!);
return base.Ok(new PullPaymentLNURL() { return base.Ok(new PullPaymentLNURL()
{
LNURLBech32 = LNURL.LNURL.EncodeUri(lnurlEndpoint, "withdrawRequest", true).ToString(), LNURLBech32 = LNURL.LNURL.EncodeUri(lnurlEndpoint, "withdrawRequest", true).ToString(),
LNURLUri = LNURL.LNURL.EncodeUri(lnurlEndpoint, "withdrawRequest", false).ToString() LNURLUri = LNURL.LNURL.EncodeUri(lnurlEndpoint, "withdrawRequest", false).ToString()
}); });
@@ -359,7 +360,7 @@ namespace BTCPayServer.Controllers.Greenfield
return this.CreateAPIPermissionError(Policies.CanCreatePullPayments); return this.CreateAPIPermissionError(Policies.CanCreatePullPayments);
} }
} }
if (request is null || !PaymentMethodId.TryParse(request?.PaymentMethod, out var paymentMethodId)) if (request is null || !PaymentMethodId.TryParse(request?.PaymentMethod, out var paymentMethodId))
{ {
ModelState.AddModelError(nameof(request.PaymentMethod), "Invalid payment method"); ModelState.AddModelError(nameof(request.PaymentMethod), "Invalid payment method");

View File

@@ -33,7 +33,10 @@ namespace BTCPayServer.Controllers.Greenfield
return new LightningAddressData(); return new LightningAddressData();
return new LightningAddressData() return new LightningAddressData()
{ {
Username = data.Username, Max = blob.Max, Min = blob.Min, CurrencyCode = blob.CurrencyCode Username = data.Username,
Max = blob.Max,
Min = blob.Min,
CurrencyCode = blob.CurrencyCode
}; };
} }
@@ -41,7 +44,7 @@ namespace BTCPayServer.Controllers.Greenfield
[HttpGet("~/api/v1/stores/{storeId}/lightning-addresses")] [HttpGet("~/api/v1/stores/{storeId}/lightning-addresses")]
public async Task<IActionResult> GetStoreLightningAddresses(string storeId) public async Task<IActionResult> GetStoreLightningAddresses(string storeId)
{ {
return Ok((await _lightningAddressService.Get(new LightningAddressQuery() {StoreIds = new[] {storeId}})) return Ok((await _lightningAddressService.Get(new LightningAddressQuery() { StoreIds = new[] { storeId } }))
.Select(ToModel).ToArray()); .Select(ToModel).ToArray());
} }
@@ -64,7 +67,8 @@ namespace BTCPayServer.Controllers.Greenfield
{ {
var res = await _lightningAddressService.Get(new LightningAddressQuery() var res = await _lightningAddressService.Get(new LightningAddressQuery()
{ {
Usernames = new[] {username}, StoreIds = new[] {storeId}, Usernames = new[] { username },
StoreIds = new[] { storeId },
}); });
return res?.Any() is true ? Ok(ToModel(res.First())) : this.CreateAPIError(404, "lightning-address-not-found", "The lightning address was not present."); return res?.Any() is true ? Ok(ToModel(res.First())) : this.CreateAPIError(404, "lightning-address-not-found", "The lightning address was not present.");
} }
@@ -79,17 +83,17 @@ namespace BTCPayServer.Controllers.Greenfield
ModelState.AddModelError(nameof(data.Min), "Minimum must be greater than 0 if provided."); ModelState.AddModelError(nameof(data.Min), "Minimum must be greater than 0 if provided.");
return this.CreateValidationError(ModelState); return this.CreateValidationError(ModelState);
} }
if (await _lightningAddressService.Set(new Data.LightningAddressData() if (await _lightningAddressService.Set(new Data.LightningAddressData()
{ {
StoreDataId = storeId, StoreDataId = storeId,
Username = username Username = username
}.SetBlob(new LightningAddressDataBlob() }.SetBlob(new LightningAddressDataBlob()
{ {
Max = data.Max, Max = data.Max,
Min = data.Min, Min = data.Min,
CurrencyCode = data.CurrencyCode CurrencyCode = data.CurrencyCode
}))) })))
{ {
return await GetStoreLightningAddress(storeId, username); return await GetStoreLightningAddress(storeId, username);
} }

View File

@@ -111,7 +111,7 @@ namespace BTCPayServer.Controllers.GreenField
{ {
parsedCurrencyPairs = blob.DefaultCurrencyPairs.ToHashSet(); parsedCurrencyPairs = blob.DefaultCurrencyPairs.ToHashSet();
} }
ValidateAndSanitizeConfiguration(configuration, blob); ValidateAndSanitizeConfiguration(configuration, blob);
if (!ModelState.IsValid) if (!ModelState.IsValid)
return this.CreateValidationError(ModelState); return this.CreateValidationError(ModelState);

View File

@@ -32,7 +32,7 @@ namespace BTCPayServer.Controllers.GreenField
_rateProviderFactory = rateProviderFactory; _rateProviderFactory = rateProviderFactory;
_btcPayNetworkProvider = btcPayNetworkProvider; _btcPayNetworkProvider = btcPayNetworkProvider;
} }
[HttpGet("")] [HttpGet("")]
[Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] [Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
public async Task<IActionResult> GetStoreRates([FromQuery] string[]? currencyPair) public async Task<IActionResult> GetStoreRates([FromQuery] string[]? currencyPair)
@@ -59,7 +59,7 @@ namespace BTCPayServer.Controllers.GreenField
{ {
parsedCurrencyPairs = blob.DefaultCurrencyPairs.ToHashSet(); parsedCurrencyPairs = blob.DefaultCurrencyPairs.ToHashSet();
} }
var rules = blob.GetRateRules(_btcPayNetworkProvider); var rules = blob.GetRateRules(_btcPayNetworkProvider);

View File

@@ -149,13 +149,13 @@ namespace BTCPayServer.Controllers.Greenfield
LightningDescriptionTemplate = storeBlob.LightningDescriptionTemplate, LightningDescriptionTemplate = storeBlob.LightningDescriptionTemplate,
PaymentTolerance = storeBlob.PaymentTolerance, PaymentTolerance = storeBlob.PaymentTolerance,
PayJoinEnabled = storeBlob.PayJoinEnabled, PayJoinEnabled = storeBlob.PayJoinEnabled,
PaymentMethodCriteria = storeBlob.PaymentMethodCriteria?.Where(criteria => criteria.Value is not null)?.Select(criteria => new PaymentMethodCriteriaData() PaymentMethodCriteria = storeBlob.PaymentMethodCriteria?.Where(criteria => criteria.Value is not null)?.Select(criteria => new PaymentMethodCriteriaData()
{ {
Above = criteria.Above, Above = criteria.Above,
Amount = criteria.Value.Value, Amount = criteria.Value.Value,
CurrencyCode = criteria.Value.Currency, CurrencyCode = criteria.Value.Currency,
PaymentMethod = criteria.PaymentMethod.ToStringNormalized() PaymentMethod = criteria.PaymentMethod.ToStringNormalized()
})?.ToList()?? new List<PaymentMethodCriteriaData>() })?.ToList() ?? new List<PaymentMethodCriteriaData>()
}; };
} }
@@ -249,7 +249,8 @@ namespace BTCPayServer.Controllers.Greenfield
if (string.IsNullOrEmpty(pmc.CurrencyCode)) if (string.IsNullOrEmpty(pmc.CurrencyCode))
{ {
request.AddModelError(data => data.PaymentMethodCriteria[index].CurrencyCode, "CurrencyCode is required", this); request.AddModelError(data => data.PaymentMethodCriteria[index].CurrencyCode, "CurrencyCode is required", this);
}else if (CurrencyNameTable.Instance.GetCurrencyData(pmc.CurrencyCode, false) is null) }
else if (CurrencyNameTable.Instance.GetCurrencyData(pmc.CurrencyCode, false) is null)
{ {
request.AddModelError(data => data.PaymentMethodCriteria[index].CurrencyCode, "CurrencyCode is invalid", this); request.AddModelError(data => data.PaymentMethodCriteria[index].CurrencyCode, "CurrencyCode is invalid", this);
} }

View File

@@ -30,11 +30,11 @@ using LightningAddressData = BTCPayServer.Client.Models.LightningAddressData;
using NotificationData = BTCPayServer.Client.Models.NotificationData; using NotificationData = BTCPayServer.Client.Models.NotificationData;
using PaymentRequestData = BTCPayServer.Client.Models.PaymentRequestData; using PaymentRequestData = BTCPayServer.Client.Models.PaymentRequestData;
using PayoutData = BTCPayServer.Client.Models.PayoutData; using PayoutData = BTCPayServer.Client.Models.PayoutData;
using PayoutProcessorData = BTCPayServer.Client.Models.PayoutProcessorData;
using PullPaymentData = BTCPayServer.Client.Models.PullPaymentData; using PullPaymentData = BTCPayServer.Client.Models.PullPaymentData;
using StoreData = BTCPayServer.Client.Models.StoreData; using StoreData = BTCPayServer.Client.Models.StoreData;
using StoreWebhookData = BTCPayServer.Client.Models.StoreWebhookData; using StoreWebhookData = BTCPayServer.Client.Models.StoreWebhookData;
using WebhookDeliveryData = BTCPayServer.Client.Models.WebhookDeliveryData; using WebhookDeliveryData = BTCPayServer.Client.Models.WebhookDeliveryData;
using PayoutProcessorData = BTCPayServer.Client.Models.PayoutProcessorData;
namespace BTCPayServer.Controllers.Greenfield namespace BTCPayServer.Controllers.Greenfield
{ {
@@ -223,14 +223,14 @@ namespace BTCPayServer.Controllers.Greenfield
return GetFromActionResult<MarketTradeResponseData>( return GetFromActionResult<MarketTradeResponseData>(
await GetController<GreenfieldCustodianAccountController>().MarketTradeCustodianAccountAsset(storeId, accountId, request, cancellationToken)); await GetController<GreenfieldCustodianAccountController>().MarketTradeCustodianAccountAsset(storeId, accountId, request, cancellationToken));
} }
public override async Task<WithdrawalSimulationResponseData> SimulateCustodianAccountWithdrawal(string storeId, string accountId, public override async Task<WithdrawalSimulationResponseData> SimulateCustodianAccountWithdrawal(string storeId, string accountId,
WithdrawRequestData request, CancellationToken cancellationToken = default) WithdrawRequestData request, CancellationToken cancellationToken = default)
{ {
return GetFromActionResult<WithdrawalSimulationResponseData>( return GetFromActionResult<WithdrawalSimulationResponseData>(
await GetController<GreenfieldCustodianAccountController>().SimulateWithdrawal(storeId, accountId, request, cancellationToken)); await GetController<GreenfieldCustodianAccountController>().SimulateWithdrawal(storeId, accountId, request, cancellationToken));
} }
public override async Task<WithdrawalResponseData> CreateCustodianAccountWithdrawal(string storeId, string accountId, public override async Task<WithdrawalResponseData> CreateCustodianAccountWithdrawal(string storeId, string accountId,
WithdrawRequestData request, CancellationToken cancellationToken = default) WithdrawRequestData request, CancellationToken cancellationToken = default)
{ {
@@ -1238,7 +1238,7 @@ namespace BTCPayServer.Controllers.Greenfield
return Task.FromResult(GetFromActionResult<StoreRateConfiguration>(GetController<GreenfieldStoreRateConfigurationController>().GetStoreRateConfiguration())); return Task.FromResult(GetFromActionResult<StoreRateConfiguration>(GetController<GreenfieldStoreRateConfigurationController>().GetStoreRateConfiguration()));
} }
public override async Task<List<StoreRateResult>> GetStoreRates (string storeId, public override async Task<List<StoreRateResult>> GetStoreRates(string storeId,
string[] currencyPair, CancellationToken token = default) string[] currencyPair, CancellationToken token = default)
{ {
return GetFromActionResult<List<StoreRateResult>>(await GetController<GreenfieldStoreRatesController>().GetStoreRates(currencyPair)); return GetFromActionResult<List<StoreRateResult>>(await GetController<GreenfieldStoreRatesController>().GetStoreRates(currencyPair));

View File

@@ -62,7 +62,7 @@ public class LightningAddressService
{ {
data.Username = NormalizeUsername(data.Username); data.Username = NormalizeUsername(data.Username);
await using var context = _applicationDbContextFactory.CreateContext(); await using var context = _applicationDbContextFactory.CreateContext();
var result = (await GetCore(context, new LightningAddressQuery() { Usernames = new[] { data.Username} })) var result = (await GetCore(context, new LightningAddressQuery() { Usernames = new[] { data.Username } }))
.FirstOrDefault(); .FirstOrDefault();
if (result is not null) if (result is not null)
{ {

View File

@@ -88,7 +88,7 @@ namespace BTCPayServer.Controllers
[HttpGet("/cheat/permissions")] [HttpGet("/cheat/permissions")]
[HttpGet("/cheat/permissions/stores/{storeId}")] [HttpGet("/cheat/permissions/stores/{storeId}")]
[CheatModeRoute] [CheatModeRoute]
public async Task<IActionResult> CheatPermissions([FromServices]IAuthorizationService authorizationService, string storeId = null) public async Task<IActionResult> CheatPermissions([FromServices] IAuthorizationService authorizationService, string storeId = null)
{ {
var vm = new CheatPermissionsViewModel(); var vm = new CheatPermissionsViewModel();
vm.StoreId = storeId; vm.StoreId = storeId;
@@ -790,7 +790,7 @@ namespace BTCPayServer.Controllers
if (matchedDomainMapping is not null) if (matchedDomainMapping is not null)
return RedirectToAction(nameof(UIHomeController.Home), "UIHome"); return RedirectToAction(nameof(UIHomeController.Home), "UIHome");
} }
return RedirectToAction(nameof(UIHomeController.Index), "UIHome"); return RedirectToAction(nameof(UIHomeController.Index), "UIHome");
} }

View File

@@ -54,7 +54,7 @@ namespace BTCPayServer.Controllers
var app = await _appService.GetApp(appId, null); var app = await _appService.GetApp(appId, null);
if (app is null) if (app is null)
return NotFound(); return NotFound();
var res = await _appService.ViewLink(app); var res = await _appService.ViewLink(app);
if (res is null) if (res is null)
{ {
@@ -150,11 +150,11 @@ namespace BTCPayServer.Controllers
var defaultCurrency = await GetStoreDefaultCurrentIfEmpty(appData.StoreDataId, null); var defaultCurrency = await GetStoreDefaultCurrentIfEmpty(appData.StoreDataId, null);
await _appService.SetDefaultSettings(appData, defaultCurrency); await _appService.SetDefaultSettings(appData, defaultCurrency);
await _appService.UpdateOrCreateApp(appData); await _appService.UpdateOrCreateApp(appData);
TempData[WellKnownTempData.SuccessMessage] = "App successfully created"; TempData[WellKnownTempData.SuccessMessage] = "App successfully created";
CreatedAppId = appData.Id; CreatedAppId = appData.Id;
var url = await type.ConfigureLink(appData); var url = await type.ConfigureLink(appData);
return Redirect(url); return Redirect(url);
} }

View File

@@ -123,7 +123,7 @@ namespace BTCPayServer.Controllers
var metaData = PosDataParser.ParsePosData(invoice.Metadata.ToJObject()); var metaData = PosDataParser.ParsePosData(invoice.Metadata.ToJObject());
var additionalData = metaData var additionalData = metaData
.Where(dict => !InvoiceAdditionalDataExclude.Contains(dict.Key)) .Where(dict => !InvoiceAdditionalDataExclude.Contains(dict.Key))
.ToDictionary(dict=> dict.Key, dict=> dict.Value); .ToDictionary(dict => dict.Key, dict => dict.Value);
var model = new InvoiceDetailsModel var model = new InvoiceDetailsModel
{ {
StoreId = store.Id, StoreId = store.Id,
@@ -201,12 +201,12 @@ namespace BTCPayServer.Controllers
CssFileId = storeBlob.CssFileId, CssFileId = storeBlob.CssFileId,
ReceiptOptions = receipt ReceiptOptions = receipt
}; };
if (i.Status.ToModernStatus() != InvoiceStatus.Settled) if (i.Status.ToModernStatus() != InvoiceStatus.Settled)
{ {
return View(vm); return View(vm);
} }
JToken? receiptData = null; JToken? receiptData = null;
i.Metadata?.AdditionalData?.TryGetValue("receiptData", out receiptData); i.Metadata?.AdditionalData?.TryGetValue("receiptData", out receiptData);
@@ -897,22 +897,22 @@ namespace BTCPayServer.Controllers
{ {
var currency = invoiceEntity.Currency; var currency = invoiceEntity.Currency;
var crypto = cryptoCode.ToUpperInvariant(); // uppercase to make comparison easier, might be "sats" var crypto = cryptoCode.ToUpperInvariant(); // uppercase to make comparison easier, might be "sats"
// if invoice source currency is the same as currently display currency, no need for "order amount from invoice" // if invoice source currency is the same as currently display currency, no need for "order amount from invoice"
if (crypto == currency || (crypto == "SATS" && currency == "BTC") || (crypto == "BTC" && currency == "SATS")) if (crypto == currency || (crypto == "SATS" && currency == "BTC") || (crypto == "BTC" && currency == "SATS"))
return null; return null;
return _displayFormatter.Currency(invoiceEntity.Price, currency, format); return _displayFormatter.Currency(invoiceEntity.Price, currency, format);
} }
private string? ExchangeRate(string cryptoCode, PaymentMethod paymentMethod, DisplayFormatter.CurrencyFormat format = DisplayFormatter.CurrencyFormat.Code) private string? ExchangeRate(string cryptoCode, PaymentMethod paymentMethod, DisplayFormatter.CurrencyFormat format = DisplayFormatter.CurrencyFormat.Code)
{ {
var currency = paymentMethod.ParentEntity.Currency; var currency = paymentMethod.ParentEntity.Currency;
var crypto = cryptoCode.ToUpperInvariant(); // uppercase to make comparison easier, might be "sats" var crypto = cryptoCode.ToUpperInvariant(); // uppercase to make comparison easier, might be "sats"
if (crypto == currency || (crypto == "SATS" && currency == "BTC") || (crypto == "BTC" && currency == "SATS")) if (crypto == currency || (crypto == "SATS" && currency == "BTC") || (crypto == "BTC" && currency == "SATS"))
return null; return null;
return _displayFormatter.Currency(paymentMethod.Rate, currency, format); return _displayFormatter.Currency(paymentMethod.Rate, currency, format);
} }

View File

@@ -192,7 +192,7 @@ namespace BTCPayServer
case PayResult.Error: case PayResult.Error:
default: default:
await _pullPaymentHostedService.Cancel( await _pullPaymentHostedService.Cancel(
new PullPaymentHostedService.CancelRequest(new [] new PullPaymentHostedService.CancelRequest(new[]
{ claimResponse.PayoutData.Id }, null)); { claimResponse.PayoutData.Id }, null));
return BadRequest(new LNUrlStatusResponse return BadRequest(new LNUrlStatusResponse
@@ -305,7 +305,7 @@ namespace BTCPayServer
}; };
var invoiceMetadata = new InvoiceMetadata(); var invoiceMetadata = new InvoiceMetadata();
invoiceMetadata.OrderId =AppService.GetAppOrderId(app); invoiceMetadata.OrderId = AppService.GetAppOrderId(app);
if (item != null) if (item != null)
{ {
invoiceMetadata.ItemCode = item.Id; invoiceMetadata.ItemCode = item.Id;
@@ -355,8 +355,8 @@ namespace BTCPayServer
public string InvoiceMetadata { get; set; } public string InvoiceMetadata { get; set; }
} }
public ConcurrentDictionary<string, LightningAddressItem> Items { get; } = new (); public ConcurrentDictionary<string, LightningAddressItem> Items { get; } = new();
public ConcurrentDictionary<string, string[]> StoreToItemMap { get; } = new (); public ConcurrentDictionary<string, string[]> StoreToItemMap { get; } = new();
public override string ToString() public override string ToString()
{ {
@@ -466,7 +466,7 @@ namespace BTCPayServer
lnurlRequest ??= new LNURLPayRequest(); lnurlRequest ??= new LNURLPayRequest();
lnUrlMetadata ??= new Dictionary<string, string>(); lnUrlMetadata ??= new Dictionary<string, string>();
if (lnUrlMetadata?.TryGetValue("text/identifier", out var lnAddress) is true && lnAddress is string) if (lnUrlMetadata?.TryGetValue("text/identifier", out var lnAddress) is true && lnAddress is not null)
{ {
var pm = i.GetPaymentMethod(pmi); var pm = i.GetPaymentMethod(pmi);
var paymentMethodDetails = (LNURLPayPaymentMethodDetails)pm.GetPaymentMethodDetails(); var paymentMethodDetails = (LNURLPayPaymentMethodDetails)pm.GetPaymentMethodDetails();
@@ -642,7 +642,7 @@ namespace BTCPayServer
{ {
var expiry = i.ExpirationTime.ToUniversalTime() - DateTimeOffset.UtcNow; var expiry = i.ExpirationTime.ToUniversalTime() - DateTimeOffset.UtcNow;
var metadata = JsonConvert.SerializeObject(lnurlPayRequest.Metadata); var metadata = JsonConvert.SerializeObject(lnurlPayRequest.Metadata);
var description = (await _pluginHookService.ApplyFilter("modify-lnurlp-description", metadata)) as string; var description = (await _pluginHookService.ApplyFilter("modify-lnurlp-description", metadata)) as string;
if (description is null) if (description is null)
return NotFound(); return NotFound();
@@ -704,7 +704,7 @@ namespace BTCPayServer
Reason = "Invoice not in a valid payable state" Reason = "Invoice not in a valid payable state"
}); });
} }
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)] [Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)] [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
[HttpGet("~/stores/{storeId}/plugins/lightning-address")] [HttpGet("~/stores/{storeId}/plugins/lightning-address")]
@@ -759,11 +759,11 @@ namespace BTCPayServer
} }
JObject metadata = null; JObject metadata = null;
if (!string.IsNullOrEmpty(vm.Add.InvoiceMetadata) ) if (!string.IsNullOrEmpty(vm.Add.InvoiceMetadata))
{ {
try try
{ {
metadata = JObject.Parse(vm.Add.InvoiceMetadata); metadata = JObject.Parse(vm.Add.InvoiceMetadata);
} }
catch (Exception e) catch (Exception e)
{ {

View File

@@ -198,14 +198,14 @@ namespace BTCPayServer.Controllers
{ {
return NotFound(); return NotFound();
} }
var storeBlob = store.GetStoreBlob(); var storeBlob = store.GetStoreBlob();
vm.StoreName = store.StoreName; vm.StoreName = store.StoreName;
vm.BrandColor = storeBlob.BrandColor; vm.BrandColor = storeBlob.BrandColor;
vm.LogoFileId = storeBlob.LogoFileId; vm.LogoFileId = storeBlob.LogoFileId;
vm.CssFileId = storeBlob.CssFileId; vm.CssFileId = storeBlob.CssFileId;
vm.HubPath = PaymentRequestHub.GetHubPath(Request); vm.HubPath = PaymentRequestHub.GetHubPath(Request);
return View(vm); return View(vm);
} }
@@ -224,14 +224,14 @@ namespace BTCPayServer.Controllers
var prBlob = result.GetBlob(); var prBlob = result.GetBlob();
if (prBlob.FormResponse is not null) if (prBlob.FormResponse is not null)
{ {
return RedirectToAction("PayPaymentRequest", new {payReqId}); return RedirectToAction("PayPaymentRequest", new { payReqId });
} }
var prFormId = prBlob.FormId; var prFormId = prBlob.FormId;
var formData = await FormDataService.GetForm(prFormId); var formData = await FormDataService.GetForm(prFormId);
if (formData is null) if (formData is null)
{ {
return RedirectToAction("PayPaymentRequest", new {payReqId}); return RedirectToAction("PayPaymentRequest", new { payReqId });
} }
var form = Form.Parse(formData.Config); var form = Form.Parse(formData.Config);
@@ -239,11 +239,11 @@ namespace BTCPayServer.Controllers
{ {
form.ApplyValuesFromForm(Request.Form); form.ApplyValuesFromForm(Request.Form);
if (FormDataService.Validate(form, ModelState)) if (FormDataService.Validate(form, ModelState))
{ {
prBlob.FormResponse = FormDataService.GetValues(form); prBlob.FormResponse = FormDataService.GetValues(form);
result.SetBlob(prBlob); result.SetBlob(prBlob);
await _PaymentRequestRepository.CreateOrUpdatePaymentRequest(result); await _PaymentRequestRepository.CreateOrUpdatePaymentRequest(result);
return RedirectToAction("PayPaymentRequest", new {payReqId}); return RedirectToAction("PayPaymentRequest", new { payReqId });
} }
} }
viewModel.FormName = formData.Name; viewModel.FormName = formData.Name;
@@ -283,7 +283,7 @@ namespace BTCPayServer.Controllers
var formData = await FormDataService.GetForm(result.FormId); var formData = await FormDataService.GetForm(result.FormId);
if (formData is not null) if (formData is not null)
{ {
return RedirectToAction("ViewPaymentRequestForm", new {payReqId}); return RedirectToAction("ViewPaymentRequestForm", new { payReqId });
} }
} }

View File

@@ -63,7 +63,7 @@ namespace BTCPayServer.Controllers
var store = await _storeRepository.FindStore(pp.StoreId); var store = await _storeRepository.FindStore(pp.StoreId);
if (store is null) if (store is null)
return NotFound(); return NotFound();
var storeBlob = store.GetStoreBlob(); var storeBlob = store.GetStoreBlob();
var payouts = (await ctx.Payouts.GetPayoutInPeriod(pp) var payouts = (await ctx.Payouts.GetPayoutInPeriod(pp)
.OrderByDescending(o => o.Date) .OrderByDescending(o => o.Date)

View File

@@ -424,7 +424,7 @@ namespace BTCPayServer.Controllers
{ {
var types = _AppService.GetAvailableAppTypes(); var types = _AppService.GetAvailableAppTypes();
var apps = (await _AppService.GetAllApps(null, true)) var apps = (await _AppService.GetAllApps(null, true))
.Select(a => .Select(a =>
new SelectListItem($"{types[a.AppType]} - {a.AppName} - {a.StoreName}", a.Id)).ToList(); new SelectListItem($"{types[a.AppType]} - {a.AppName} - {a.StoreName}", a.Id)).ToList();
apps.Insert(0, new SelectListItem("(None)", null)); apps.Insert(0, new SelectListItem("(None)", null));
return apps; return apps;

View File

@@ -455,9 +455,9 @@ namespace BTCPayServer.Controllers
{ {
IncludeArchived = false, IncludeArchived = false,
IncludeStoreData = true, IncludeStoreData = true,
Stores = new[] {storeId}, Stores = new[] { storeId },
PayoutIds = payoutIds, PayoutIds = payoutIds,
PaymentMethods = new[] {paymentMethodId.ToString()} PaymentMethods = new[] { paymentMethodId.ToString() }
}, ctx, cancellationToken); }, ctx, cancellationToken);
} }

View File

@@ -730,7 +730,7 @@ namespace BTCPayServer.Controllers
{ {
await _fileService.RemoveFile(blob.CssFileId, userId); await _fileService.RemoveFile(blob.CssFileId, userId);
} }
// add new CSS file // add new CSS file
try try
{ {

View File

@@ -267,7 +267,7 @@ namespace BTCPayServer.Controllers
ModelState.Remove(nameof(vm.PSBT)); ModelState.Remove(nameof(vm.PSBT));
ModelState.Remove(nameof(vm.FileName)); ModelState.Remove(nameof(vm.FileName));
ModelState.Remove(nameof(vm.UploadedPSBTFile)); ModelState.Remove(nameof(vm.UploadedPSBTFile));
await FetchTransactionDetails(walletId,derivationSchemeSettings, vm, network); await FetchTransactionDetails(walletId, derivationSchemeSettings, vm, network);
return View("WalletPSBTDecoded", vm); return View("WalletPSBTDecoded", vm);
case "save-psbt": case "save-psbt":
@@ -386,15 +386,15 @@ namespace BTCPayServer.Controllers
inputVm.BalanceChange = ValueToString(balanceChange2, network); inputVm.BalanceChange = ValueToString(balanceChange2, network);
inputVm.Positive = balanceChange2 >= Money.Zero; inputVm.Positive = balanceChange2 >= Money.Zero;
inputVm.Index = (int)input.Index; inputVm.Index = (int)input.Index;
var walletObjectIds = new List<ObjectTypeId>(); var walletObjectIds = new List<ObjectTypeId>();
walletObjectIds.Add(new ObjectTypeId(WalletObjectData.Types.Utxo, input.PrevOut.ToString())); walletObjectIds.Add(new ObjectTypeId(WalletObjectData.Types.Utxo, input.PrevOut.ToString()));
walletObjectIds.Add(new ObjectTypeId(WalletObjectData.Types.Tx, input.PrevOut.Hash.ToString())); walletObjectIds.Add(new ObjectTypeId(WalletObjectData.Types.Tx, input.PrevOut.Hash.ToString()));
var address = txOut?.ScriptPubKey.GetDestinationAddress(network.NBitcoinNetwork)?.ToString(); var address = txOut?.ScriptPubKey.GetDestinationAddress(network.NBitcoinNetwork)?.ToString();
if(address != null) if (address != null)
walletObjectIds.Add(new ObjectTypeId(WalletObjectData.Types.Address, address)); walletObjectIds.Add(new ObjectTypeId(WalletObjectData.Types.Address, address));
inputToObjects.Add(input.Index, walletObjectIds.ToArray()); inputToObjects.Add(input.Index, walletObjectIds.ToArray());
} }
vm.Destinations = new List<WalletPSBTReadyViewModel.DestinationViewModel>(); vm.Destinations = new List<WalletPSBTReadyViewModel.DestinationViewModel>();
foreach (var output in psbtObject.Outputs) foreach (var output in psbtObject.Outputs)
@@ -409,9 +409,9 @@ namespace BTCPayServer.Controllers
dest.Positive = balanceChange2 >= Money.Zero; dest.Positive = balanceChange2 >= Money.Zero;
dest.Destination = output.ScriptPubKey.GetDestinationAddress(network.NBitcoinNetwork)?.ToString() ?? output.ScriptPubKey.ToString(); dest.Destination = output.ScriptPubKey.GetDestinationAddress(network.NBitcoinNetwork)?.ToString() ?? output.ScriptPubKey.ToString();
var address = output.ScriptPubKey.GetDestinationAddress(network.NBitcoinNetwork)?.ToString(); var address = output.ScriptPubKey.GetDestinationAddress(network.NBitcoinNetwork)?.ToString();
if(address != null) if (address != null)
outputToObjects.Add(dest.Destination, new ObjectTypeId(WalletObjectData.Types.Address, address)); outputToObjects.Add(dest.Destination, new ObjectTypeId(WalletObjectData.Types.Address, address));
} }
if (psbtObject.TryGetFee(out var fee)) if (psbtObject.TryGetFee(out var fee))
@@ -442,13 +442,14 @@ namespace BTCPayServer.Controllers
.DistinctBy(id => $"{id.Type}:{id.Id}").ToArray(); .DistinctBy(id => $"{id.Type}:{id.Id}").ToArray();
var labelInfo = await WalletRepository.GetWalletTransactionsInfo(walletId, combinedTypeIds); var labelInfo = await WalletRepository.GetWalletTransactionsInfo(walletId, combinedTypeIds);
foreach (KeyValuePair<uint,ObjectTypeId[]> inputToObject in inputToObjects) foreach (KeyValuePair<uint, ObjectTypeId[]> inputToObject in inputToObjects)
{ {
var keys = inputToObject.Value.Select(id => id.Id).ToArray(); var keys = inputToObject.Value.Select(id => id.Id).ToArray();
WalletTransactionInfo ix = null; WalletTransactionInfo ix = null;
foreach (var key in keys) foreach (var key in keys)
{ {
if (!labelInfo.TryGetValue(key, out var i)) continue; if (!labelInfo.TryGetValue(key, out var i))
continue;
if (ix is null) if (ix is null)
{ {
ix = i; ix = i;
@@ -458,20 +459,22 @@ namespace BTCPayServer.Controllers
ix.Merge(i); ix.Merge(i);
} }
} }
if (ix is null) continue; if (ix is null)
continue;
var labels = _labelService.CreateTransactionTagModels(ix, Request); var labels = _labelService.CreateTransactionTagModels(ix, Request);
var input = vm.Inputs.First(model => model.Index == inputToObject.Key); var input = vm.Inputs.First(model => model.Index == inputToObject.Key);
input.Labels = labels; input.Labels = labels;
} }
foreach (var outputToObject in outputToObjects) foreach (var outputToObject in outputToObjects)
{ {
if (!labelInfo.TryGetValue(outputToObject.Value.Id, out var ix)) continue; if (!labelInfo.TryGetValue(outputToObject.Value.Id, out var ix))
continue;
var labels = _labelService.CreateTransactionTagModels(ix, Request); var labels = _labelService.CreateTransactionTagModels(ix, Request);
var destination = vm.Destinations.First(model => model.Destination == outputToObject.Key); var destination = vm.Destinations.First(model => model.Destination == outputToObject.Key);
destination.Labels = labels; destination.Labels = labels;
} }
} }
[HttpPost("{walletId}/psbt/ready")] [HttpPost("{walletId}/psbt/ready")]
@@ -491,7 +494,7 @@ namespace BTCPayServer.Controllers
if (derivationSchemeSettings == null) if (derivationSchemeSettings == null)
return NotFound(); return NotFound();
await FetchTransactionDetails(walletId,derivationSchemeSettings, vm, network); await FetchTransactionDetails(walletId, derivationSchemeSettings, vm, network);
switch (command) switch (command)
{ {
@@ -622,7 +625,7 @@ namespace BTCPayServer.Controllers
BackUrl = vm.BackUrl BackUrl = vm.BackUrl
}); });
case "decode": case "decode":
await FetchTransactionDetails(walletId,derivationSchemeSettings, vm, network); await FetchTransactionDetails(walletId, derivationSchemeSettings, vm, network);
return View("WalletPSBTDecoded", vm); return View("WalletPSBTDecoded", vm);
default: default:
vm.Errors.Add("Unknown command"); vm.Errors.Add("Unknown command");

View File

@@ -340,7 +340,7 @@ namespace BTCPayServer.Controllers
CryptoImage = GetImage(paymentMethod.PaymentId, network), CryptoImage = GetImage(paymentMethod.PaymentId, network),
PaymentLink = bip21.ToString(), PaymentLink = bip21.ToString(),
ReturnUrl = returnUrl ?? HttpContext.Request.GetTypedHeaders().Referer?.AbsolutePath, ReturnUrl = returnUrl ?? HttpContext.Request.GetTypedHeaders().Referer?.AbsolutePath,
SelectedLabels = labels?? Array.Empty<string>() SelectedLabels = labels ?? Array.Empty<string>()
}); });
} }
@@ -733,14 +733,14 @@ namespace BTCPayServer.Controllers
{ {
var labels = transactionOutput.Labels.Where(s => !string.IsNullOrWhiteSpace(s)).ToArray(); var labels = transactionOutput.Labels.Where(s => !string.IsNullOrWhiteSpace(s)).ToArray();
var walletObjectAddress = new WalletObjectId(walletId, WalletObjectData.Types.Address, transactionOutput.DestinationAddress.ToLowerInvariant()); var walletObjectAddress = new WalletObjectId(walletId, WalletObjectData.Types.Address, transactionOutput.DestinationAddress.ToLowerInvariant());
var obj = await WalletRepository.GetWalletObject(walletObjectAddress); var obj = await WalletRepository.GetWalletObject(walletObjectAddress);
if (obj is null) if (obj is null)
{ {
await WalletRepository.EnsureWalletObject(walletObjectAddress); await WalletRepository.EnsureWalletObject(walletObjectAddress);
} }
await WalletRepository.AddWalletObjectLabels(walletObjectAddress, labels); await WalletRepository.AddWalletObjectLabels(walletObjectAddress, labels);
} }
var derivationScheme = GetDerivationSchemeSettings(walletId); var derivationScheme = GetDerivationSchemeSettings(walletId);
if (derivationScheme is null) if (derivationScheme is null)
return NotFound(); return NotFound();
@@ -787,10 +787,10 @@ namespace BTCPayServer.Controllers
switch (response.Result) switch (response.Result)
{ {
case ClaimRequest.ClaimResult.Duplicate: case ClaimRequest.ClaimResult.Duplicate:
errorMessage += $"{claimRequest.Value} to {claimRequest.Destination.ToString() } - address reuse<br/>"; errorMessage += $"{claimRequest.Value} to {claimRequest.Destination.ToString()} - address reuse<br/>";
break; break;
case ClaimRequest.ClaimResult.AmountTooLow: case ClaimRequest.ClaimResult.AmountTooLow:
errorMessage += $"{claimRequest.Value} to {claimRequest.Destination.ToString() } - amount too low<br/>"; errorMessage += $"{claimRequest.Value} to {claimRequest.Destination.ToString()} - amount too low<br/>";
break; break;
} }
} }
@@ -919,8 +919,8 @@ namespace BTCPayServer.Controllers
ModelState.Clear(); ModelState.Clear();
if (address is not null) if (address is not null)
{ {
var addressLabels = await WalletRepository.GetWalletLabels(new WalletObjectId(walletId, WalletObjectData.Types.Address, address.ToString())); var addressLabels = await WalletRepository.GetWalletLabels(new WalletObjectId(walletId, WalletObjectData.Types.Address, address.ToString()));
vm.Outputs.Last().Labels = addressLabels.Select(tuple => tuple.Label).ToArray(); vm.Outputs.Last().Labels = addressLabels.Select(tuple => tuple.Label).ToArray();
} }
} }
@@ -1350,14 +1350,14 @@ namespace BTCPayServer.Controllers
Response.Headers.Add("X-Content-Type-Options", "nosniff"); Response.Headers.Add("X-Content-Type-Options", "nosniff");
return Content(res, mimeType); return Content(res, mimeType);
} }
public class UpdateLabelsRequest public class UpdateLabelsRequest
{ {
public string? Id { get; set; } public string? Id { get; set; }
public string? Type { get; set; } public string? Type { get; set; }
public string[]? Labels { get; set; } public string[]? Labels { get; set; }
} }
[HttpPost("{walletId}/update-labels")] [HttpPost("{walletId}/update-labels")]
[IgnoreAntiforgeryToken] [IgnoreAntiforgeryToken]
public async Task<IActionResult> UpdateLabels( public async Task<IActionResult> UpdateLabels(
@@ -1366,23 +1366,23 @@ namespace BTCPayServer.Controllers
{ {
if (string.IsNullOrEmpty(request.Type) || string.IsNullOrEmpty(request.Id) || request.Labels is null) if (string.IsNullOrEmpty(request.Type) || string.IsNullOrEmpty(request.Id) || request.Labels is null)
return BadRequest(); return BadRequest();
var objid = new WalletObjectId(walletId, request.Type, request.Id); var objid = new WalletObjectId(walletId, request.Type, request.Id);
var obj = await WalletRepository.GetWalletObject(objid); var obj = await WalletRepository.GetWalletObject(objid);
if (obj is null) if (obj is null)
{ {
await WalletRepository.EnsureWalletObject(objid); await WalletRepository.EnsureWalletObject(objid);
} }
else else
{ {
var currentLabels = obj.GetNeighbours().Where(data => data.Type == WalletObjectData.Types.Label).ToArray(); var currentLabels = obj.GetNeighbours().Where(data => data.Type == WalletObjectData.Types.Label).ToArray();
var toRemove = currentLabels.Where(data => !request.Labels.Contains(data.Id)).Select(data => data.Id).ToArray(); var toRemove = currentLabels.Where(data => !request.Labels.Contains(data.Id)).Select(data => data.Id).ToArray();
await WalletRepository.RemoveWalletObjectLabels(objid, toRemove); await WalletRepository.RemoveWalletObjectLabels(objid, toRemove);
} }
await WalletRepository.AddWalletObjectLabels(objid, request.Labels); await WalletRepository.AddWalletObjectLabels(objid, request.Labels);
return Ok(); return Ok();
} }
[HttpGet("{walletId}/labels")] [HttpGet("{walletId}/labels")]
[IgnoreAntiforgeryToken] [IgnoreAntiforgeryToken]
public async Task<IActionResult> GetLabels( public async Task<IActionResult> GetLabels(
@@ -1399,11 +1399,11 @@ namespace BTCPayServer.Controllers
: await WalletRepository.GetWalletLabels(walletObjectId); : await WalletRepository.GetWalletLabels(walletObjectId);
return Ok(labels return Ok(labels
.Where(l => !excludeTypes || !WalletObjectData.Types.AllTypes.Contains(l.Label)) .Where(l => !excludeTypes || !WalletObjectData.Types.AllTypes.Contains(l.Label))
.Select(tuple => new .Select(tuple => new
{ {
label = tuple.Label, label = tuple.Label,
color = tuple.Color, color = tuple.Color,
textColor = ColorPalette.Default.TextColor(tuple.Color) textColor = ColorPalette.Default.TextColor(tuple.Color)
})); }));
} }

View File

@@ -12,7 +12,7 @@ namespace BTCPayServer.Data
{ {
public static class IHasBlobExtensions public static class IHasBlobExtensions
{ {
static readonly JsonSerializerSettings DefaultSerializer; static readonly JsonSerializerSettings DefaultSerializer;
static IHasBlobExtensions() static IHasBlobExtensions()
{ {
DefaultSerializer = new JsonSerializerSettings() DefaultSerializer = new JsonSerializerSettings()
@@ -30,7 +30,7 @@ namespace BTCPayServer.Data
this.data = data; this.data = data;
} }
[Obsolete("Use Blob2 instead")] [Obsolete("Use Blob2 instead")]
public byte[] Blob { get { return data.Blob; } set { data.Blob = value; } } public byte[] Blob { get { return data.Blob; } set { data.Blob = value; } }
public string Blob2 { get { return data.Blob2; } set { data.Blob2 = value; } } public string Blob2 { get { return data.Blob2; } set { data.Blob2 = value; } }
} }
class HasBlobWrapper : IHasBlob class HasBlobWrapper : IHasBlob

View File

@@ -34,7 +34,7 @@ namespace BTCPayServer.Data
} }
else else
{ {
var entity = invoiceData.HasTypedBlob<InvoiceEntity>().GetBlob(); var entity = invoiceData.HasTypedBlob<InvoiceEntity>().GetBlob();
entity.Networks = networks; entity.Networks = networks;
return entity; return entity;
} }

View File

@@ -70,7 +70,7 @@ public class BitcoinLikePayoutHandler : IPayoutHandler
var explorerClient = _explorerClientProvider.GetExplorerClient(network); var explorerClient = _explorerClientProvider.GetExplorerClient(network);
if (claimRequest.Destination is IBitcoinLikeClaimDestination bitcoinLikeClaimDestination) if (claimRequest.Destination is IBitcoinLikeClaimDestination bitcoinLikeClaimDestination)
{ {
await explorerClient.TrackAsync(TrackedSource.Create(bitcoinLikeClaimDestination.Address)); await explorerClient.TrackAsync(TrackedSource.Create(bitcoinLikeClaimDestination.Address));
await WalletRepository.AddWalletTransactionAttachment( await WalletRepository.AddWalletTransactionAttachment(
new WalletId(claimRequest.StoreId, claimRequest.PaymentMethodId.CryptoCode), new WalletId(claimRequest.StoreId, claimRequest.PaymentMethodId.CryptoCode),
@@ -210,11 +210,13 @@ public class BitcoinLikePayoutHandler : IPayoutHandler
await using (var context = _dbContextFactory.CreateContext()) await using (var context = _dbContextFactory.CreateContext())
{ {
var payouts = (await PullPaymentHostedService.GetPayouts(new PullPaymentHostedService.PayoutQuery() var payouts = (await PullPaymentHostedService.GetPayouts(new PullPaymentHostedService.PayoutQuery()
{ {
States = new[] {PayoutState.AwaitingPayment}, Stores = new[] {storeId}, PayoutIds = payoutIds States = new[] { PayoutState.AwaitingPayment },
}, context)).Where(data => Stores = new[] { storeId },
PaymentMethodId.TryParse(data.PaymentMethodId, out var paymentMethodId) && PayoutIds = payoutIds
CanHandle(paymentMethodId)) }, context)).Where(data =>
PaymentMethodId.TryParse(data.PaymentMethodId, out var paymentMethodId) &&
CanHandle(paymentMethodId))
.Select(data => (data, ParseProof(data) as PayoutTransactionOnChainBlob)).Where(tuple => tuple.Item2 != null && tuple.Item2.TransactionId != null && tuple.Item2.Accounted == false); .Select(data => (data, ParseProof(data) as PayoutTransactionOnChainBlob)).Where(tuple => tuple.Item2 != null && tuple.Item2.TransactionId != null && tuple.Item2.Accounted == false);
foreach (var valueTuple in payouts) foreach (var valueTuple in payouts)
{ {
@@ -230,14 +232,16 @@ public class BitcoinLikePayoutHandler : IPayoutHandler
Severity = StatusMessageModel.StatusSeverity.Success Severity = StatusMessageModel.StatusSeverity.Success
}; };
case "reject-payment": case "reject-payment":
await using (var context = _dbContextFactory.CreateContext()) await using (var context = _dbContextFactory.CreateContext())
{ {
var payouts = (await PullPaymentHostedService.GetPayouts(new PullPaymentHostedService.PayoutQuery() var payouts = (await PullPaymentHostedService.GetPayouts(new PullPaymentHostedService.PayoutQuery()
{ {
States = new[] {PayoutState.AwaitingPayment}, Stores = new[] {storeId}, PayoutIds = payoutIds States = new[] { PayoutState.AwaitingPayment },
}, context)).Where(data => Stores = new[] { storeId },
PaymentMethodId.TryParse(data.PaymentMethodId, out var paymentMethodId) && PayoutIds = payoutIds
CanHandle(paymentMethodId)) }, context)).Where(data =>
PaymentMethodId.TryParse(data.PaymentMethodId, out var paymentMethodId) &&
CanHandle(paymentMethodId))
.Select(data => (data, ParseProof(data) as PayoutTransactionOnChainBlob)).Where(tuple => tuple.Item2 != null && tuple.Item2.TransactionId != null && tuple.Item2.Accounted == true); .Select(data => (data, ParseProof(data) as PayoutTransactionOnChainBlob)).Where(tuple => tuple.Item2 != null && tuple.Item2.TransactionId != null && tuple.Item2.Accounted == true);
foreach (var valueTuple in payouts) foreach (var valueTuple in payouts)
{ {

View File

@@ -225,11 +225,11 @@ namespace BTCPayServer.Data
[DefaultValue(true)] [DefaultValue(true)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
public bool ShowPayInWalletButton { get; set; } = true; public bool ShowPayInWalletButton { get; set; } = true;
[DefaultValue(true)] [DefaultValue(true)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
public bool ShowStoreHeader { get; set; } = true; public bool ShowStoreHeader { get; set; } = true;
[DefaultValue(true)] [DefaultValue(true)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
public bool CelebratePayment { get; set; } = true; public bool CelebratePayment { get; set; } = true;

View File

@@ -186,12 +186,12 @@ namespace BTCPayServer.Data
{ {
return IsPaymentTypeEnabled(storeData, networks, cryptoCode, LNURLPayPaymentType.Instance); return IsPaymentTypeEnabled(storeData, networks, cryptoCode, LNURLPayPaymentType.Instance);
} }
private static bool IsPaymentTypeEnabled(this StoreData storeData, BTCPayNetworkProvider networks, string cryptoCode, PaymentType paymentType) private static bool IsPaymentTypeEnabled(this StoreData storeData, BTCPayNetworkProvider networks, string cryptoCode, PaymentType paymentType)
{ {
var paymentMethods = storeData.GetSupportedPaymentMethods(networks); var paymentMethods = storeData.GetSupportedPaymentMethods(networks);
var excludeFilters = storeData.GetStoreBlob().GetExcludedPaymentMethods(); var excludeFilters = storeData.GetStoreBlob().GetExcludedPaymentMethods();
return paymentMethods.Any(method => return paymentMethods.Any(method =>
method.PaymentId.CryptoCode == cryptoCode && method.PaymentId.CryptoCode == cryptoCode &&
method.PaymentId.PaymentType == paymentType && method.PaymentId.PaymentType == paymentType &&
!excludeFilters.Match(method.PaymentId)); !excludeFilters.Match(method.PaymentId));

View File

@@ -49,7 +49,7 @@ namespace BTCPayServer
{ {
return AccountDerivation is null ? null : DBUtils.nbxv1_get_wallet_id(Network.CryptoCode, AccountDerivation.ToString()); return AccountDerivation is null ? null : DBUtils.nbxv1_get_wallet_id(Network.CryptoCode, AccountDerivation.ToString());
} }
private static bool TryParseXpub(string xpub, DerivationSchemeParser derivationSchemeParser, ref DerivationSchemeSettings derivationSchemeSettings, ref string error, bool electrum = true) private static bool TryParseXpub(string xpub, DerivationSchemeParser derivationSchemeParser, ref DerivationSchemeSettings derivationSchemeSettings, ref string error, bool electrum = true)
{ {
if (!electrum) if (!electrum)
@@ -87,9 +87,12 @@ namespace BTCPayServer
var match = derivationRegex.Match(xpub.Trim()); var match = derivationRegex.Match(xpub.Trim());
if (match.Success) if (match.Success)
{ {
if (!string.IsNullOrEmpty(match.Groups[1].Value)) rootFingerprint = HDFingerprint.Parse(match.Groups[1].Value); if (!string.IsNullOrEmpty(match.Groups[1].Value))
if (!string.IsNullOrEmpty(match.Groups[2].Value)) accountKeyPath = KeyPath.Parse(match.Groups[2].Value); rootFingerprint = HDFingerprint.Parse(match.Groups[1].Value);
if (!string.IsNullOrEmpty(match.Groups[3].Value)) xpub = match.Groups[3].Value; if (!string.IsNullOrEmpty(match.Groups[2].Value))
accountKeyPath = KeyPath.Parse(match.Groups[2].Value);
if (!string.IsNullOrEmpty(match.Groups[3].Value))
xpub = match.Groups[3].Value;
} }
derivationSchemeSettings.AccountOriginal = xpub.Trim(); derivationSchemeSettings.AccountOriginal = xpub.Trim();
derivationSchemeSettings.AccountDerivation = electrum ? derivationSchemeParser.ParseElectrum(derivationSchemeSettings.AccountOriginal) : derivationSchemeParser.Parse(derivationSchemeSettings.AccountOriginal); derivationSchemeSettings.AccountDerivation = electrum ? derivationSchemeParser.ParseElectrum(derivationSchemeSettings.AccountOriginal) : derivationSchemeParser.Parse(derivationSchemeSettings.AccountOriginal);

View File

@@ -41,13 +41,13 @@ namespace BTCPayServer
{ {
pattern = pattern.Replace(" ", ""); pattern = pattern.Replace(" ", "");
int[] res = new int[pattern.Length / 2]; int[] res = new int[pattern.Length / 2];
for (int i = 0; i < pattern.Length; i+=2) for (int i = 0; i < pattern.Length; i += 2)
{ {
var b = pattern[i..(i + 2)]; var b = pattern[i..(i + 2)];
if (b == "XX") if (b == "XX")
res[i/2] = -1; res[i / 2] = -1;
else else
res[i/2] = byte.Parse(b, System.Globalization.NumberStyles.HexNumber); res[i / 2] = byte.Parse(b, System.Globalization.NumberStyles.HexNumber);
} }
return res; return res;
} }

View File

@@ -13,12 +13,12 @@ namespace BTCPayServer.Filters
public DomainMappingConstraintAttribute() public DomainMappingConstraintAttribute()
{ {
} }
public DomainMappingConstraintAttribute(string appType) public DomainMappingConstraintAttribute(string appType)
{ {
AppType = appType; AppType = appType;
} }
public int Order => 100; public int Order => 100;
private string AppType { get; } private string AppType { get; }
@@ -33,21 +33,22 @@ namespace BTCPayServer.Filters
{ {
var appId = (string)context.RouteContext.RouteData.Values["appId"]; var appId = (string)context.RouteContext.RouteData.Values["appId"];
var matchedDomainMapping = mapping.FirstOrDefault(item => item.AppId == appId); var matchedDomainMapping = mapping.FirstOrDefault(item => item.AppId == appId);
// App is accessed via path, redirect to canonical domain // App is accessed via path, redirect to canonical domain
var req = context.RouteContext.HttpContext.Request; var req = context.RouteContext.HttpContext.Request;
if (matchedDomainMapping != null && req.Method != "POST" && !req.HasFormContentType) if (matchedDomainMapping != null && req.Method != "POST" && !req.HasFormContentType)
{ {
var uri = new UriBuilder(req.Scheme, matchedDomainMapping.Domain); var uri = new UriBuilder(req.Scheme, matchedDomainMapping.Domain);
if (req.Host.Port.HasValue) uri.Port = req.Host.Port.Value; if (req.Host.Port.HasValue)
uri.Port = req.Host.Port.Value;
context.RouteContext.HttpContext.Response.Redirect(uri.ToString()); context.RouteContext.HttpContext.Response.Redirect(uri.ToString());
return true; return true;
} }
} }
if (hasDomainMapping) if (hasDomainMapping)
{ {
var matchedDomainMapping = mapping.FirstOrDefault(item => var matchedDomainMapping = mapping.FirstOrDefault(item =>
item.Domain.Equals(context.RouteContext.HttpContext.Request.Host.Host, item.Domain.Equals(context.RouteContext.HttpContext.Request.Host.Host,
StringComparison.InvariantCultureIgnoreCase)); StringComparison.InvariantCultureIgnoreCase));
if (matchedDomainMapping != null) if (matchedDomainMapping != null)

View File

@@ -16,8 +16,8 @@ public static class FormDataExtensions
serviceCollection.AddSingleton<IFormComponentProvider, HtmlSelectFormProvider>(); serviceCollection.AddSingleton<IFormComponentProvider, HtmlSelectFormProvider>();
serviceCollection.AddSingleton<IFormComponentProvider, FieldValueMirror>(); serviceCollection.AddSingleton<IFormComponentProvider, FieldValueMirror>();
} }
public static JObject Deserialize(this FormData form) public static JObject Deserialize(this FormData form)
{ {
return JsonConvert.DeserializeObject<JObject>(form.Config); return JsonConvert.DeserializeObject<JObject>(form.Config);
} }

View File

@@ -22,7 +22,7 @@ public class FormDataService
private readonly FormComponentProviders _formProviders; private readonly FormComponentProviders _formProviders;
public FormDataService( public FormDataService(
ApplicationDbContextFactory applicationDbContextFactory, ApplicationDbContextFactory applicationDbContextFactory,
FormComponentProviders formProviders) FormComponentProviders formProviders)
{ {
_applicationDbContextFactory = applicationDbContextFactory; _applicationDbContextFactory = applicationDbContextFactory;
@@ -47,16 +47,16 @@ public class FormDataService
Field.Create("State", "buyerState", null, false, null), Field.Create("State", "buyerState", null, false, null),
new SelectField() new SelectField()
{ {
Name = "buyerCountry", Name = "buyerCountry",
Label = "Country", Label = "Country",
Required = true, Required = true,
Type = "select", Type = "select",
Options = "Afghanistan, Albania, Algeria, Andorra, Angola, Antigua and Barbuda, Argentina, Armenia, Australia, Austria, Azerbaijan, The Bahamas, Bahrain, Bangladesh, Barbados, Belarus, Belgium, Belize, Benin, Bhutan, Bolivia, Bosnia and Herzegovina, Botswana, Brazil, Brunei, Bulgaria, Burkina Faso, Burundi, Cabo Verde, Cambodia, Cameroon, Canada, Central African Republic (CAR), Chad, Chile, China, Colombia, Comoros, Democratic Republic of the Congo, Republic of the Congo, Costa Rica, Cote d'Ivoire, Croatia, Cuba, Cyprus, Czech Republic, Denmark, Djibouti, Dominica, Dominican Republic, Ecuador, Egypt, El Salvador, Equatorial Guinea, Eritrea, Estonia, Eswatini (formerly Swaziland), Ethiopia, Fiji, Finland, France, Gabon, The Gambia, Georgia, Germany, Ghana, Greece, Grenada, Guatemala, Guinea, Guinea-Bissau, Guyana, Haiti, Honduras, Hungary, Iceland, India, Indonesia, Iran, Iraq, Ireland, Israel, Italy, Jamaica, Japan, Jordan, Kazakhstan, Kenya, Kiribati, Kosovo, Kuwait, Kyrgyzstan, Laos, Latvia, Lebanon, Lesotho, Liberia, Libya, Liechtenstein, Lithuania, Luxembourg, Madagascar, Malawi, Malaysia, Maldives, Mali, Malta, Marshall Islands, Mauritania, Mauritius, Mexico, Micronesia, Moldova, Monaco, Mongolia, Montenegro, Morocco, Mozambique, Myanmar (formerly Burma), Namibia, Nauru, Nepal, Netherlands, New Zealand, Nicaragua, Niger, Nigeria, North Korea, North Macedonia (formerly Macedonia), Norway, Oman, Pakistan, Palau, Palestine, Panama, Papua New Guinea, Paraguay, Peru, Philippines, Poland, Portugal, Qatar, Romania, Russia, Rwanda, Saint Kitts and Nevis, Saint Lucia, Saint Vincent and the Grenadines, Samoa, San Marino, Sao Tome and Principe, Saudi Arabia, Senegal, Serbia, Seychelles, Sierra Leone, Singapore, Slovakia, Slovenia, Solomon Islands, Somalia, South Africa, South Korea, South Sudan, Spain, Sri Lanka, Sudan, Suriname, Sweden, Switzerland, Syria, Taiwan, Tajikistan, Tanzania, Thailand, Timor-Leste (formerly East Timor), Togo, Tonga, Trinidad and Tobago, Tunisia, Turkey, Turkmenistan, Tuvalu, Uganda, Ukraine, United Arab Emirates (UAE), United Kingdom (UK), United States of America (USA), Uruguay, Uzbekistan, Vanuatu, Vatican City (Holy See), Venezuela, Vietnam, Yemen, Zambia, Zimbabwe.".Split(',').Select(s => new SelectListItem(s,s)).ToList() Options = "Afghanistan, Albania, Algeria, Andorra, Angola, Antigua and Barbuda, Argentina, Armenia, Australia, Austria, Azerbaijan, The Bahamas, Bahrain, Bangladesh, Barbados, Belarus, Belgium, Belize, Benin, Bhutan, Bolivia, Bosnia and Herzegovina, Botswana, Brazil, Brunei, Bulgaria, Burkina Faso, Burundi, Cabo Verde, Cambodia, Cameroon, Canada, Central African Republic (CAR), Chad, Chile, China, Colombia, Comoros, Democratic Republic of the Congo, Republic of the Congo, Costa Rica, Cote d'Ivoire, Croatia, Cuba, Cyprus, Czech Republic, Denmark, Djibouti, Dominica, Dominican Republic, Ecuador, Egypt, El Salvador, Equatorial Guinea, Eritrea, Estonia, Eswatini (formerly Swaziland), Ethiopia, Fiji, Finland, France, Gabon, The Gambia, Georgia, Germany, Ghana, Greece, Grenada, Guatemala, Guinea, Guinea-Bissau, Guyana, Haiti, Honduras, Hungary, Iceland, India, Indonesia, Iran, Iraq, Ireland, Israel, Italy, Jamaica, Japan, Jordan, Kazakhstan, Kenya, Kiribati, Kosovo, Kuwait, Kyrgyzstan, Laos, Latvia, Lebanon, Lesotho, Liberia, Libya, Liechtenstein, Lithuania, Luxembourg, Madagascar, Malawi, Malaysia, Maldives, Mali, Malta, Marshall Islands, Mauritania, Mauritius, Mexico, Micronesia, Moldova, Monaco, Mongolia, Montenegro, Morocco, Mozambique, Myanmar (formerly Burma), Namibia, Nauru, Nepal, Netherlands, New Zealand, Nicaragua, Niger, Nigeria, North Korea, North Macedonia (formerly Macedonia), Norway, Oman, Pakistan, Palau, Palestine, Panama, Papua New Guinea, Paraguay, Peru, Philippines, Poland, Portugal, Qatar, Romania, Russia, Rwanda, Saint Kitts and Nevis, Saint Lucia, Saint Vincent and the Grenadines, Samoa, San Marino, Sao Tome and Principe, Saudi Arabia, Senegal, Serbia, Seychelles, Sierra Leone, Singapore, Slovakia, Slovenia, Solomon Islands, Somalia, South Africa, South Korea, South Sudan, Spain, Sri Lanka, Sudan, Suriname, Sweden, Switzerland, Syria, Taiwan, Tajikistan, Tanzania, Thailand, Timor-Leste (formerly East Timor), Togo, Tonga, Trinidad and Tobago, Tunisia, Turkey, Turkmenistan, Tuvalu, Uganda, Ukraine, United Arab Emirates (UAE), United Kingdom (UK), United States of America (USA), Uruguay, Uzbekistan, Vanuatu, Vatican City (Holy See), Venezuela, Vietnam, Yemen, Zambia, Zimbabwe.".Split(',').Select(s => new SelectListItem(s,s)).ToList()
} }
} }
}; };
private static readonly Dictionary<string, (string selectText, string name, Form form)> _hardcodedOptions = new() private static readonly Dictionary<string, (string selectText, string name, Form form)> _hardcodedOptions = new()
{ {
{"", ("Do not request any information", null, null)!}, {"", ("Do not request any information", null, null)!},
@@ -64,13 +64,13 @@ public class FormDataService
{"Address", ("Request shipping address", "Provide your address", StaticFormAddress)}, {"Address", ("Request shipping address", "Provide your address", StaticFormAddress)},
}; };
public async Task<SelectList> GetSelect(string storeId ,string selectedFormId) public async Task<SelectList> GetSelect(string storeId, string selectedFormId)
{ {
var forms = await GetForms(storeId); var forms = await GetForms(storeId);
return new SelectList(_hardcodedOptions.Select(pair => new SelectListItem(pair.Value.selectText, pair.Key, selectedFormId == pair.Key)).Concat(forms.Select(data => new SelectListItem(data.Name, data.Id, data.Id == selectedFormId))), return new SelectList(_hardcodedOptions.Select(pair => new SelectListItem(pair.Value.selectText, pair.Key, selectedFormId == pair.Key)).Concat(forms.Select(data => new SelectListItem(data.Name, data.Id, data.Id == selectedFormId))),
nameof(SelectListItem.Value), nameof(SelectListItem.Text)); nameof(SelectListItem.Value), nameof(SelectListItem.Text));
} }
public async Task<List<FormData>> GetForms(string storeId) public async Task<List<FormData>> GetForms(string storeId)
{ {
ArgumentNullException.ThrowIfNull(storeId); ArgumentNullException.ThrowIfNull(storeId);
@@ -129,7 +129,7 @@ public class FormDataService
{ {
return _formProviders.Validate(form, modelState); return _formProviders.Validate(form, modelState);
} }
public bool IsFormSchemaValid(string schema, [MaybeNullWhen(false)] out Form form, [MaybeNullWhen(false)] out string error) public bool IsFormSchemaValid(string schema, [MaybeNullWhen(false)] out Form form, [MaybeNullWhen(false)] out string error)
{ {
error = null; error = null;
@@ -144,7 +144,7 @@ public class FormDataService
} }
catch (Exception ex) catch (Exception ex)
{ {
error = $"Form config was invalid: {ex.Message}"; error = $"Form config was invalid: {ex.Message}";
} }
return error is null && form is not null; return error is null && form is not null;
} }
@@ -177,7 +177,7 @@ public class FormDataService
public JObject GetValues(Form form) public JObject GetValues(Form form)
{ {
var r = new JObject(); var r = new JObject();
foreach (var f in form.GetAllFields()) foreach (var f in form.GetAllFields())
{ {
var node = r; var node = r;

View File

@@ -12,7 +12,7 @@ public class FieldValueMirror : IFormComponentProvider
{ {
if (form.GetFieldByFullName(field.Value) is null) if (form.GetFieldByFullName(field.Value) is null)
{ {
field.ValidationErrors = new List<string>() {$"{field.Name} requires {field.Value} to be present"}; field.ValidationErrors = new List<string>() { $"{field.Name} requires {field.Value} to be present" };
} }
} }

View File

@@ -24,7 +24,7 @@ public class HtmlSelectFormProvider : FormComponentProviderBase
} }
} }
public class SelectField: Field public class SelectField : Field
{ {
public List<SelectListItem> Options { get; set; } public List<SelectListItem> Options { get; set; }
} }

View File

@@ -8,7 +8,7 @@ public class ModifyForm
[DisplayName("Form configuration (JSON)")] [DisplayName("Form configuration (JSON)")]
public string FormConfig { get; set; } public string FormConfig { get; set; }
[DisplayName("Allow form for public use")] [DisplayName("Allow form for public use")]
public bool Public { get; set; } public bool Public { get; set; }
} }

View File

@@ -49,7 +49,7 @@ public class UIFormsController : Controller
[HttpGet("~/stores/{storeId}/forms/new")] [HttpGet("~/stores/{storeId}/forms/new")]
public IActionResult Create(string storeId) public IActionResult Create(string storeId)
{ {
var vm = new ModifyForm {FormConfig = new Form().ToString()}; var vm = new ModifyForm { FormConfig = new Form().ToString() };
return View("Modify", vm); return View("Modify", vm);
} }
@@ -57,10 +57,11 @@ public class UIFormsController : Controller
public async Task<IActionResult> Modify(string storeId, string id) public async Task<IActionResult> Modify(string storeId, string id)
{ {
var form = await _formDataService.GetForm(storeId, id); var form = await _formDataService.GetForm(storeId, id);
if (form is null) return NotFound(); if (form is null)
return NotFound();
var config = Form.Parse(form.Config); var config = Form.Parse(form.Config);
return View(new ModifyForm {Name = form.Name, FormConfig = config.ToString(), Public = form.Public}); return View(new ModifyForm { Name = form.Name, FormConfig = config.ToString(), Public = form.Public });
} }
[HttpPost("~/stores/{storeId}/forms/modify/{id?}")] [HttpPost("~/stores/{storeId}/forms/modify/{id?}")]
@@ -76,7 +77,7 @@ public class UIFormsController : Controller
if (!_formDataService.IsFormSchemaValid(modifyForm.FormConfig, out var form, out var error)) if (!_formDataService.IsFormSchemaValid(modifyForm.FormConfig, out var form, out var error))
{ {
ModelState.AddModelError(nameof(modifyForm.FormConfig), ModelState.AddModelError(nameof(modifyForm.FormConfig),
$"Form config was invalid: {error})"); $"Form config was invalid: {error})");
} }
@@ -84,7 +85,7 @@ public class UIFormsController : Controller
{ {
modifyForm.FormConfig = form.ToString(); modifyForm.FormConfig = form.ToString();
} }
if (!ModelState.IsValid) if (!ModelState.IsValid)
{ {
@@ -95,7 +96,11 @@ public class UIFormsController : Controller
{ {
var formData = new FormData var formData = new FormData
{ {
Id = id, StoreId = storeId, Name = modifyForm.Name, Config = modifyForm.FormConfig,Public = modifyForm.Public Id = id,
StoreId = storeId,
Name = modifyForm.Name,
Config = modifyForm.FormConfig,
Public = modifyForm.Public
}; };
var isNew = id is null; var isNew = id is null;
await _formDataService.AddOrUpdateForm(formData); await _formDataService.AddOrUpdateForm(formData);
@@ -106,7 +111,7 @@ public class UIFormsController : Controller
}); });
if (isNew) if (isNew)
{ {
return RedirectToAction("Modify", new {storeId, id = formData.Id}); return RedirectToAction("Modify", new { storeId, id = formData.Id });
} }
} }
catch (Exception e) catch (Exception e)
@@ -123,9 +128,10 @@ public class UIFormsController : Controller
await _formDataService.RemoveForm(id, storeId); await _formDataService.RemoveForm(id, storeId);
TempData.SetStatusMessageModel(new StatusMessageModel TempData.SetStatusMessageModel(new StatusMessageModel
{ {
Severity = StatusMessageModel.StatusSeverity.Success, Message = "Form removed" Severity = StatusMessageModel.StatusSeverity.Success,
Message = "Form removed"
}); });
return RedirectToAction("FormsList", new {storeId}); return RedirectToAction("FormsList", new { storeId });
} }
[AllowAnonymous] [AllowAnonymous]
@@ -154,7 +160,7 @@ public class UIFormsController : Controller
form.ApplyValuesFromForm(Request.Query); form.ApplyValuesFromForm(Request.Query);
var store = formData.Store ?? await _storeRepository.FindStore(formData.StoreId); var store = formData.Store ?? await _storeRepository.FindStore(formData.StoreId);
var storeBlob = store?.GetStoreBlob(); var storeBlob = store?.GetStoreBlob();
return View("View", new FormViewModel return View("View", new FormViewModel
{ {
FormName = formData.Name, FormName = formData.Name,
@@ -187,7 +193,7 @@ public class UIFormsController : Controller
if (!Request.HasFormContentType) if (!Request.HasFormContentType)
return await GetFormView(formData); return await GetFormView(formData);
var form = Form.Parse(formData.Config); var form = Form.Parse(formData.Config);
form.ApplyValuesFromForm(Request.Form); form.ApplyValuesFromForm(Request.Form);
@@ -202,6 +208,6 @@ public class UIFormsController : Controller
var request = _formDataService.GenerateInvoiceParametersFromForm(form); var request = _formDataService.GenerateInvoiceParametersFromForm(form);
var inv = await invoiceController.CreateInvoiceCoreRaw(request, store, Request.GetAbsoluteRoot()); var inv = await invoiceController.CreateInvoiceCoreRaw(request, store, Request.GetAbsoluteRoot());
return RedirectToAction("Checkout", "UIInvoice", new {invoiceId = inv.Id}); return RedirectToAction("Checkout", "UIInvoice", new { invoiceId = inv.Id });
} }
} }

View File

@@ -120,7 +120,7 @@ namespace BTCPayServer.HostedServices
o.Period = create.Period is TimeSpan period ? (long?)period.TotalSeconds : null; o.Period = create.Period is TimeSpan period ? (long?)period.TotalSeconds : null;
o.Id = Encoders.Base58.EncodeData(RandomUtils.GetBytes(20)); o.Id = Encoders.Base58.EncodeData(RandomUtils.GetBytes(20));
o.StoreId = create.StoreId; o.StoreId = create.StoreId;
o.SetBlob(new PullPaymentBlob() o.SetBlob(new PullPaymentBlob()
{ {
Name = create.Name ?? string.Empty, Name = create.Name ?? string.Empty,
@@ -203,7 +203,7 @@ namespace BTCPayServer.HostedServices
{ {
query = query.Include(data => data.StoreData); query = query.Include(data => data.StoreData);
} }
if (payoutQuery.IncludePullPaymentData || !payoutQuery.IncludeArchived) if (payoutQuery.IncludePullPaymentData || !payoutQuery.IncludeArchived)
{ {
query = query.Include(data => data.PullPaymentData); query = query.Include(data => data.PullPaymentData);

View File

@@ -28,7 +28,7 @@ public class StoreEmailRuleProcessorSender : EventHostedServiceBase
public StoreEmailRuleProcessorSender(StoreRepository storeRepository, EventAggregator eventAggregator, public StoreEmailRuleProcessorSender(StoreRepository storeRepository, EventAggregator eventAggregator,
ILogger<InvoiceEventSaverService> logger, ILogger<InvoiceEventSaverService> logger,
EmailSenderFactory emailSenderFactory, EmailSenderFactory emailSenderFactory,
LinkGenerator linkGenerator, LinkGenerator linkGenerator,
CurrencyNameTable currencyNameTable) : base( CurrencyNameTable currencyNameTable) : base(
eventAggregator, logger) eventAggregator, logger)
{ {

View File

@@ -78,21 +78,21 @@ namespace BTCPayServer.HostedServices
{ {
await _walletRepository.EnsureWalletObjectLink(txWalletObject, walletObjectData.Key); await _walletRepository.EnsureWalletObjectLink(txWalletObject, walletObjectData.Key);
//if the object is an address, we also link the labels to the tx //if the object is an address, we also link the labels to the tx
if(walletObjectData.Value.Type == WalletObjectData.Types.Address) if (walletObjectData.Value.Type == WalletObjectData.Types.Address)
{ {
var neighbours = walletObjectData.Value.GetNeighbours().ToArray(); var neighbours = walletObjectData.Value.GetNeighbours().ToArray();
var labels = neighbours var labels = neighbours
.Where(data => data.Type == WalletObjectData.Types.Label).Select(data => .Where(data => data.Type == WalletObjectData.Types.Label).Select(data =>
new WalletObjectId(walletObjectDatas.Key, data.Type, data.Id)); new WalletObjectId(walletObjectDatas.Key, data.Type, data.Id));
foreach (var label in labels) foreach (var label in labels)
{ {
await _walletRepository.EnsureWalletObjectLink(label, txWalletObject); await _walletRepository.EnsureWalletObjectLink(label, txWalletObject);
var attachments = neighbours.Where(data => data.Type == label.Id); var attachments = neighbours.Where(data => data.Type == label.Id);
foreach (var attachment in attachments) foreach (var attachment in attachments)
{ {
await _walletRepository.EnsureWalletObjectLink(new WalletObjectId(walletObjectDatas.Key, attachment.Type, attachment.Id), txWalletObject); await _walletRepository.EnsureWalletObjectLink(new WalletObjectId(walletObjectDatas.Key, attachment.Type, attachment.Id), txWalletObject);
} }
} }
} }
} }
} }

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Configuration.Provider;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
@@ -26,6 +27,8 @@ using BTCPayServer.Payments.Lightning;
using BTCPayServer.Payments.PayJoin; using BTCPayServer.Payments.PayJoin;
using BTCPayServer.PayoutProcessors; using BTCPayServer.PayoutProcessors;
using BTCPayServer.Plugins; using BTCPayServer.Plugins;
using BTCPayServer.Rating;
using BTCPayServer.Rating.Providers;
using BTCPayServer.Security; using BTCPayServer.Security;
using BTCPayServer.Security.Bitpay; using BTCPayServer.Security.Bitpay;
using BTCPayServer.Security.Greenfield; using BTCPayServer.Security.Greenfield;
@@ -42,6 +45,7 @@ using BTCPayServer.Services.PaymentRequests;
using BTCPayServer.Services.Rates; using BTCPayServer.Services.Rates;
using BTCPayServer.Services.Stores; using BTCPayServer.Services.Stores;
using BTCPayServer.Services.Wallets; using BTCPayServer.Services.Wallets;
using ExchangeSharp;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@@ -61,10 +65,6 @@ using NBXplorer.DerivationStrategy;
using Newtonsoft.Json; using Newtonsoft.Json;
using NicolasDorier.RateLimits; using NicolasDorier.RateLimits;
using Serilog; using Serilog;
using ExchangeSharp;
using BTCPayServer.Rating;
using System.Configuration.Provider;
using BTCPayServer.Rating.Providers;
#if ALTCOINS #if ALTCOINS
using BTCPayServer.Services.Altcoins.Monero; using BTCPayServer.Services.Altcoins.Monero;
using BTCPayServer.Services.Altcoins.Zcash; using BTCPayServer.Services.Altcoins.Zcash;

View File

@@ -11,12 +11,12 @@ namespace BTCPayServer.Models.AppViewModels
public CreateAppViewModel() public CreateAppViewModel()
{ {
} }
public CreateAppViewModel(AppService appService) public CreateAppViewModel(AppService appService)
{ {
SetApps(appService); SetApps(appService);
} }
[Required] [Required]
[MaxLength(50)] [MaxLength(50)]
[MinLength(1)] [MinLength(1)]
@@ -37,7 +37,7 @@ namespace BTCPayServer.Models.AppViewModels
var defaultAppType = PointOfSaleAppType.AppType; var defaultAppType = PointOfSaleAppType.AppType;
var choices = appService.GetAvailableAppTypes().Select(pair => var choices = appService.GetAvailableAppTypes().Select(pair =>
new SelectListItem(pair.Value, pair.Key, pair.Key == defaultAppType)); new SelectListItem(pair.Value, pair.Key, pair.Key == defaultAppType));
var chosen = choices.FirstOrDefault(f => f.Value == defaultAppType) ?? choices.FirstOrDefault(); var chosen = choices.FirstOrDefault(f => f.Value == defaultAppType) ?? choices.FirstOrDefault();
AppTypes = new SelectList(choices, nameof(chosen.Value), nameof(chosen.Text), chosen); AppTypes = new SelectList(choices, nameof(chosen.Value), nameof(chosen.Text), chosen);
SelectedAppType = chosen.Value; SelectedAppType = chosen.Value;

View File

@@ -14,7 +14,7 @@ public class AssetBalanceInfo
public string FormattedFiatValue { get; set; } public string FormattedFiatValue { get; set; }
public decimal? FiatValue { get; set; } public decimal? FiatValue { get; set; }
public Dictionary<string, AssetPairData> TradableAssetPairs { get; set; } public Dictionary<string, AssetPairData> TradableAssetPairs { get; set; }
public List<string> WithdrawablePaymentMethods { get; set; } = new(); public List<string> WithdrawablePaymentMethods { get; set; } = new();
public string FormattedBid { get; set; } public string FormattedBid { get; set; }
public string FormattedAsk { get; set; } public string FormattedAsk { get; set; }

View File

@@ -8,6 +8,6 @@ namespace BTCPayServer.Models.CustodianAccountViewModels
public CustodianAccountData CustodianAccount { get; set; } public CustodianAccountData CustodianAccount { get; set; }
public Form ConfigForm { get; set; } public Form ConfigForm { get; set; }
public string Config { get; set; } public string Config { get; set; }
} }
} }

View File

@@ -5,5 +5,5 @@ namespace BTCPayServer.Models.CustodianAccountViewModels;
public class TradePrepareViewModel : AssetQuoteResult public class TradePrepareViewModel : AssetQuoteResult
{ {
public decimal MaxQty { get; set; } public decimal MaxQty { get; set; }
} }

View File

@@ -9,7 +9,7 @@ namespace BTCPayServer.Models
public string FormUrl { get; set; } public string FormUrl { get; set; }
public bool AllowExternal { get; set; } public bool AllowExternal { get; set; }
public MultiValueDictionary<string, string> FormParameters { get; set; } = new (); public MultiValueDictionary<string, string> FormParameters { get; set; } = new();
public Dictionary<string, string> RouteParameters { get; set; } = new (); public Dictionary<string, string> RouteParameters { get; set; } = new();
} }
} }

View File

@@ -28,7 +28,7 @@ namespace BTCPayServer.Models.StoreViewModels
[Display(Name = "Show \"Pay in wallet\" button")] [Display(Name = "Show \"Pay in wallet\" button")]
public bool ShowPayInWalletButton { get; set; } public bool ShowPayInWalletButton { get; set; }
[Display(Name = "Show the store header")] [Display(Name = "Show the store header")]
public bool ShowStoreHeader { get; set; } public bool ShowStoreHeader { get; set; }

View File

@@ -14,10 +14,10 @@ namespace BTCPayServer.Models.WalletViewModels
public string Link { get; set; } public string Link { get; set; }
public bool Positive { get; set; } public bool Positive { get; set; }
public string Balance { get; set; } public string Balance { get; set; }
public HashSet<TransactionTagModel> Tags { get; set; } = new (); public HashSet<TransactionTagModel> Tags { get; set; } = new();
} }
public HashSet<(string Text, string Color, string TextColor)> Labels { get; set; } = new (); public HashSet<(string Text, string Color, string TextColor)> Labels { get; set; } = new();
public List<TransactionViewModel> Transactions { get; set; } = new (); public List<TransactionViewModel> Transactions { get; set; } = new();
public override int CurrentPageCount => Transactions.Count; public override int CurrentPageCount => Transactions.Count;
public string CryptoCode { get; set; } public string CryptoCode { get; set; }
} }

View File

@@ -76,7 +76,7 @@ namespace BTCPayServer.Payments.Bitcoin
if (lightningInfo is not null && !string.IsNullOrEmpty(lightningInfo.PaymentUrls?.BOLT11)) if (lightningInfo is not null && !string.IsNullOrEmpty(lightningInfo.PaymentUrls?.BOLT11))
{ {
lightningFallback = lightningInfo.PaymentUrls.BOLT11; lightningFallback = lightningInfo.PaymentUrls.BOLT11;
} }
else else
{ {
var lnurlInfo = invoiceResponse.CryptoInfo.FirstOrDefault(a => var lnurlInfo = invoiceResponse.CryptoInfo.FirstOrDefault(a =>
@@ -84,7 +84,7 @@ namespace BTCPayServer.Payments.Bitcoin
if (lnurlInfo is not null) if (lnurlInfo is not null)
{ {
lightningFallback = lnurlInfo.PaymentUrls?.AdditionalData["LNURLP"].ToObject<string>(); lightningFallback = lnurlInfo.PaymentUrls?.AdditionalData["LNURLP"].ToObject<string>();
// This seems to be an edge case in the Selenium tests, in which the LNURLP isn't populated. // This seems to be an edge case in the Selenium tests, in which the LNURLP isn't populated.
// I have come across it only in the tests and this is supposed to make them happy. // I have come across it only in the tests and this is supposed to make them happy.
if (string.IsNullOrEmpty(lightningFallback)) if (string.IsNullOrEmpty(lightningFallback))
@@ -122,7 +122,7 @@ namespace BTCPayServer.Payments.Bitcoin
var delimiterUrl = model.InvoiceBitcoinUrl.Contains("?") ? "&" : "?"; var delimiterUrl = model.InvoiceBitcoinUrl.Contains("?") ? "&" : "?";
model.InvoiceBitcoinUrl += $"{delimiterUrl}{lightningFallback}"; model.InvoiceBitcoinUrl += $"{delimiterUrl}{lightningFallback}";
// model.InvoiceBitcoinUrl: bitcoin:bcrt1qxp2qa5dhn7?amount=0.00044007&lightning=lnbcrt440070n1... // model.InvoiceBitcoinUrl: bitcoin:bcrt1qxp2qa5dhn7?amount=0.00044007&lightning=lnbcrt440070n1...
var delimiterUrlQR = model.InvoiceBitcoinUrlQR.Contains("?") ? "&" : "?"; var delimiterUrlQR = model.InvoiceBitcoinUrlQR.Contains("?") ? "&" : "?";
model.InvoiceBitcoinUrlQR += $"{delimiterUrlQR}{lightningFallback.ToUpperInvariant().Replace("LIGHTNING=", "lightning=", StringComparison.OrdinalIgnoreCase)}"; model.InvoiceBitcoinUrlQR += $"{delimiterUrlQR}{lightningFallback.ToUpperInvariant().Replace("LIGHTNING=", "lightning=", StringComparison.OrdinalIgnoreCase)}";
// model.InvoiceBitcoinUrlQR: bitcoin:bcrt1qxp2qa5dhn7?amount=0.00044007&lightning=LNBCRT4400... // model.InvoiceBitcoinUrlQR: bitcoin:bcrt1qxp2qa5dhn7?amount=0.00044007&lightning=LNBCRT4400...
@@ -140,7 +140,7 @@ namespace BTCPayServer.Payments.Bitcoin
{ {
model.InvoiceBitcoinUrl = model.InvoiceBitcoinUrlQR = string.Empty; model.InvoiceBitcoinUrl = model.InvoiceBitcoinUrlQR = string.Empty;
} }
if (model.Activated && amountInSats) if (model.Activated && amountInSats)
{ {
base.PreparePaymentModelForAmountInSats(model, paymentMethod, _displayFormatter); base.PreparePaymentModelForAmountInSats(model, paymentMethod, _displayFormatter);

View File

@@ -100,11 +100,11 @@ namespace BTCPayServer.Payments
{ {
return null; return null;
} }
public virtual void PreparePaymentModelForAmountInSats(PaymentModel model, IPaymentMethod paymentMethod, DisplayFormatter displayFormatter) public virtual void PreparePaymentModelForAmountInSats(PaymentModel model, IPaymentMethod paymentMethod, DisplayFormatter displayFormatter)
{ {
var satoshiCulture = new CultureInfo(CultureInfo.InvariantCulture.Name) var satoshiCulture = new CultureInfo(CultureInfo.InvariantCulture.Name)
{ {
NumberFormat = { NumberGroupSeparator = " " } NumberFormat = { NumberGroupSeparator = " " }
}; };
model.CryptoCode = "sats"; model.CryptoCode = "sats";

View File

@@ -41,7 +41,7 @@ namespace BTCPayServer.Payments.Lightning
public override PaymentType PaymentType => PaymentTypes.LightningLike; public override PaymentType PaymentType => PaymentTypes.LightningLike;
private const string UriScheme = "lightning:"; private const string UriScheme = "lightning:";
public IOptions<LightningNetworkOptions> Options { get; } public IOptions<LightningNetworkOptions> Options { get; }
public override async Task<IPaymentMethodDetails> CreatePaymentMethodDetails( public override async Task<IPaymentMethodDetails> CreatePaymentMethodDetails(
@@ -121,13 +121,13 @@ namespace BTCPayServer.Payments.Lightning
var network = _networkProvider.GetNetwork<BTCPayNetwork>(model.CryptoCode); var network = _networkProvider.GetNetwork<BTCPayNetwork>(model.CryptoCode);
var cryptoInfo = invoiceResponse.CryptoInfo.First(o => o.GetpaymentMethodId() == paymentMethodId); var cryptoInfo = invoiceResponse.CryptoInfo.First(o => o.GetpaymentMethodId() == paymentMethodId);
var lnurl = cryptoInfo.PaymentUrls?.AdditionalData["LNURLP"].ToObject<string>(); var lnurl = cryptoInfo.PaymentUrls?.AdditionalData["LNURLP"].ToObject<string>();
model.PaymentMethodName = GetPaymentMethodName(network); model.PaymentMethodName = GetPaymentMethodName(network);
model.BtcAddress = lnurl?.Replace(UriScheme, ""); model.BtcAddress = lnurl?.Replace(UriScheme, "");
model.InvoiceBitcoinUrl = lnurl; model.InvoiceBitcoinUrl = lnurl;
model.InvoiceBitcoinUrlQR = lnurl?.ToUpperInvariant().Replace(UriScheme.ToUpperInvariant(), UriScheme); model.InvoiceBitcoinUrlQR = lnurl?.ToUpperInvariant().Replace(UriScheme.ToUpperInvariant(), UriScheme);
model.PeerInfo = ((LNURLPayPaymentMethodDetails)paymentMethod.GetPaymentMethodDetails()).NodeInfo; model.PeerInfo = ((LNURLPayPaymentMethodDetails)paymentMethod.GetPaymentMethodDetails()).NodeInfo;
if (storeBlob.LightningAmountInSatoshi && model.CryptoCode == "BTC") if (storeBlob.LightningAmountInSatoshi && model.CryptoCode == "BTC")
{ {
base.PreparePaymentModelForAmountInSats(model, paymentMethod, _displayFormatter); base.PreparePaymentModelForAmountInSats(model, paymentMethod, _displayFormatter);

View File

@@ -11,19 +11,19 @@ namespace BTCPayServer.Payments.Lightning
{ {
[JsonIgnore] [JsonIgnore]
public BTCPayNetworkBase Network { get; set; } public BTCPayNetworkBase Network { get; set; }
[JsonConverter(typeof(LightMoneyJsonConverter))] [JsonConverter(typeof(LightMoneyJsonConverter))]
public LightMoney Amount { get; set; } public LightMoney Amount { get; set; }
public string BOLT11 { get; set; } public string BOLT11 { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.UInt256JsonConverter))] [JsonConverter(typeof(NBitcoin.JsonConverters.UInt256JsonConverter))]
public uint256 PaymentHash { get; set; } public uint256 PaymentHash { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.UInt256JsonConverter))] [JsonConverter(typeof(NBitcoin.JsonConverters.UInt256JsonConverter))]
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public uint256 Preimage { get; set; } public uint256 Preimage { get; set; }
public string PaymentType { get; set; } public string PaymentType { get; set; }
public string GetDestination() public string GetDestination()

View File

@@ -212,12 +212,12 @@ namespace BTCPayServer.Payments.Lightning
var paymentMethodId = paymentMethod.GetId(); var paymentMethodId = paymentMethod.GetId();
var cryptoInfo = invoiceResponse.CryptoInfo.First(o => o.GetpaymentMethodId() == paymentMethodId); var cryptoInfo = invoiceResponse.CryptoInfo.First(o => o.GetpaymentMethodId() == paymentMethodId);
var network = _networkProvider.GetNetwork<BTCPayNetwork>(model.CryptoCode); var network = _networkProvider.GetNetwork<BTCPayNetwork>(model.CryptoCode);
model.PaymentMethodName = GetPaymentMethodName(network); model.PaymentMethodName = GetPaymentMethodName(network);
model.InvoiceBitcoinUrl = cryptoInfo.PaymentUrls?.BOLT11; model.InvoiceBitcoinUrl = cryptoInfo.PaymentUrls?.BOLT11;
model.InvoiceBitcoinUrlQR = $"lightning:{cryptoInfo.PaymentUrls?.BOLT11?.ToUpperInvariant()?.Substring("LIGHTNING:".Length)}"; model.InvoiceBitcoinUrlQR = $"lightning:{cryptoInfo.PaymentUrls?.BOLT11?.ToUpperInvariant()?.Substring("LIGHTNING:".Length)}";
model.PeerInfo = ((LightningLikePaymentMethodDetails)paymentMethod.GetPaymentMethodDetails()).NodeInfo; model.PeerInfo = ((LightningLikePaymentMethodDetails)paymentMethod.GetPaymentMethodDetails()).NodeInfo;
if (storeBlob.LightningAmountInSatoshi && model.CryptoCode == "BTC") if (storeBlob.LightningAmountInSatoshi && model.CryptoCode == "BTC")
{ {
base.PreparePaymentModelForAmountInSats(model, paymentMethod, _displayFormatter); base.PreparePaymentModelForAmountInSats(model, paymentMethod, _displayFormatter);

View File

@@ -62,9 +62,9 @@ public abstract class BaseAutomatedPayoutProcessor<T> : BaseAsyncService where T
var blob = GetBlob(_PayoutProcesserSettings); var blob = GetBlob(_PayoutProcesserSettings);
if (paymentMethod is not null) if (paymentMethod is not null)
{ {
// Allow plugins to do something before the automatic payouts are executed // Allow plugins to do something before the automatic payouts are executed
await _pluginHookService.ApplyFilter("before-automated-payout-processing", await _pluginHookService.ApplyFilter("before-automated-payout-processing",
new BeforePayoutFilterData(store, paymentMethod)); new BeforePayoutFilterData(store, paymentMethod));
await using var context = _applicationDbContextFactory.CreateContext(); await using var context = _applicationDbContextFactory.CreateContext();
@@ -80,9 +80,9 @@ public abstract class BaseAutomatedPayoutProcessor<T> : BaseAsyncService where T
Logs.PayServer.LogInformation($"{payouts.Count} found to process. Starting (and after will sleep for {blob.Interval})"); Logs.PayServer.LogInformation($"{payouts.Count} found to process. Starting (and after will sleep for {blob.Interval})");
await Process(paymentMethod, payouts); await Process(paymentMethod, payouts);
await context.SaveChangesAsync(); await context.SaveChangesAsync();
// Allow plugins do to something after automatic payout processing // Allow plugins do to something after automatic payout processing
await _pluginHookService.ApplyFilter("after-automated-payout-processing", await _pluginHookService.ApplyFilter("after-automated-payout-processing",
new AfterPayoutFilterData(store, paymentMethod, payouts)); new AfterPayoutFilterData(store, paymentMethod, payouts));
} }
} }

View File

@@ -403,7 +403,7 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers
{ {
return null; return null;
} }
var info = (ViewCrowdfundViewModel) await _app.GetInfo(app); var info = (ViewCrowdfundViewModel)await _app.GetInfo(app);
info.HubPath = AppHub.GetHubPath(Request); info.HubPath = AppHub.GetHubPath(Request);
info.SimpleDisplay = Request.Query.ContainsKey("simple"); info.SimpleDisplay = Request.Query.ContainsKey("simple");
return info; return info;

View File

@@ -33,12 +33,12 @@ namespace BTCPayServer.Plugins.Crowdfund
services.AddSingleton<IUIExtension>(new UIExtension("Crowdfund/NavExtension", "header-nav")); services.AddSingleton<IUIExtension>(new UIExtension("Crowdfund/NavExtension", "header-nav"));
services.AddSingleton<CrowdfundAppType>(); services.AddSingleton<CrowdfundAppType>();
services.AddSingleton<AppBaseType, CrowdfundAppType>(); services.AddSingleton<AppBaseType, CrowdfundAppType>();
base.Execute(services); base.Execute(services);
} }
} }
public class CrowdfundAppType: AppBaseType, IHasSaleStatsAppType, IHasItemStatsAppType public class CrowdfundAppType : AppBaseType, IHasSaleStatsAppType, IHasItemStatsAppType
{ {
private readonly LinkGenerator _linkGenerator; private readonly LinkGenerator _linkGenerator;
private readonly IOptions<BTCPayServerOptions> _options; private readonly IOptions<BTCPayServerOptions> _options;
@@ -146,7 +146,7 @@ namespace BTCPayServer.Plugins.Crowdfund
} }
} }
var invoices = await AppService.GetInvoicesForApp(_invoiceRepository,appData, lastResetDate); var invoices = await AppService.GetInvoicesForApp(_invoiceRepository, appData, lastResetDate);
var completeInvoices = invoices.Where(IsComplete).ToArray(); var completeInvoices = invoices.Where(IsComplete).ToArray();
var pendingInvoices = invoices.Where(IsPending).ToArray(); var pendingInvoices = invoices.Where(IsPending).ToArray();
var paidInvoices = invoices.Where(IsPaid).ToArray(); var paidInvoices = invoices.Where(IsPaid).ToArray();
@@ -256,7 +256,7 @@ namespace BTCPayServer.Plugins.Crowdfund
public override Task<string> ViewLink(AppData app) public override Task<string> ViewLink(AppData app)
{ {
return Task.FromResult(_linkGenerator.GetPathByAction(nameof(UICrowdfundController.ViewCrowdfund), return Task.FromResult(_linkGenerator.GetPathByAction(nameof(UICrowdfundController.ViewCrowdfund),
"UICrowdfund", new {appId = app.Id}, _options.Value.RootPath)!); "UICrowdfund", new { appId = app.Id }, _options.Value.RootPath)!);
} }
private static bool IsPaid(InvoiceEntity entity) private static bool IsPaid(InvoiceEntity entity)

View File

@@ -60,8 +60,8 @@ namespace BTCPayServer.Plugins.Crowdfund.Models
public DateTime? LastResetDate { get; set; } public DateTime? LastResetDate { get; set; }
public DateTime? NextResetDate { get; set; } public DateTime? NextResetDate { get; set; }
} }
public bool Started => !StartDate.HasValue || DateTime.UtcNow > StartDate; public bool Started => !StartDate.HasValue || DateTime.UtcNow > StartDate;

View File

@@ -120,7 +120,7 @@ public class FakeCustodian : ICustodian, ICanDeposit, ICanWithdraw, ICanTrade
if (ValidWithdrawalPaymentMethod.Equals(paymentMethod)) if (ValidWithdrawalPaymentMethod.Equals(paymentMethod))
{ {
LedgerEntryData ledgerEntryWithdrawal = new(ValidAsset, -amount, LedgerEntryData.LedgerEntryType.Withdrawal); LedgerEntryData ledgerEntryWithdrawal = new(ValidAsset, -amount, LedgerEntryData.LedgerEntryType.Withdrawal);
LedgerEntryData ledgerEntryFee = new(ValidAsset, - _btcWithdrawalFee, LedgerEntryData.LedgerEntryType.Fee); LedgerEntryData ledgerEntryFee = new(ValidAsset, -_btcWithdrawalFee, LedgerEntryData.LedgerEntryType.Fee);
List<LedgerEntryData> ledgerEntries = new(); List<LedgerEntryData> ledgerEntries = new();
ledgerEntries.Add(ledgerEntryWithdrawal); ledgerEntries.Add(ledgerEntryWithdrawal);
ledgerEntries.Add(ledgerEntryFee); ledgerEntries.Add(ledgerEntryFee);

View File

@@ -94,7 +94,7 @@ namespace BTCPayServer.Plugins.NFC
var details = ex.InnerException?.Message ?? ex.Message; var details = ex.InnerException?.Message ?? ex.Message;
return BadRequest($"Could not fetch info from LNURL-Withdraw: {details}"); return BadRequest($"Could not fetch info from LNURL-Withdraw: {details}");
} }
if (info?.Callback is null) if (info?.Callback is null)
{ {
return BadRequest("Could not fetch info from LNURL-Withdraw"); return BadRequest("Could not fetch info from LNURL-Withdraw");
@@ -127,7 +127,7 @@ namespace BTCPayServer.Plugins.NFC
{ {
due = new LightMoney(lnPaymentMethod.Calculate().Due); due = new LightMoney(lnPaymentMethod.Calculate().Due);
} }
if (info.MinWithdrawable > due || due > info.MaxWithdrawable) if (info.MinWithdrawable > due || due > info.MaxWithdrawable)
{ {
return BadRequest("Invoice amount is not payable with the LNURL allowed amounts."); return BadRequest("Invoice amount is not payable with the LNURL allowed amounts.");

View File

@@ -7,18 +7,18 @@ namespace BTCPayServer.Plugins.NFC
{ {
public class NFCPlugin : BaseBTCPayServerPlugin public class NFCPlugin : BaseBTCPayServerPlugin
{ {
public override string Identifier => "BTCPayServer.Plugins.NFC"; public override string Identifier => "BTCPayServer.Plugins.NFC";
public override string Name => "NFC"; public override string Name => "NFC";
public override string Description => "Allows you to support contactless card payments over NFC and LNURL Withdraw!"; public override string Description => "Allows you to support contactless card payments over NFC and LNURL Withdraw!";
public override void Execute(IServiceCollection applicationBuilder) public override void Execute(IServiceCollection applicationBuilder)
{ {
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("NFC/CheckoutEnd", applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("NFC/CheckoutEnd",
"checkout-end")); "checkout-end"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("NFC/LNURLNFCPostContent", applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("NFC/LNURLNFCPostContent",
"checkout-lightning-post-content")); "checkout-lightning-post-content"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("NFC/CheckoutEnd", applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("NFC/CheckoutEnd",
"checkout-v2-end")); "checkout-v2-end"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("NFC/LNURLNFCPostContent-v2", applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("NFC/LNURLNFCPostContent-v2",

View File

@@ -59,7 +59,7 @@ namespace BTCPayServer.Plugins
public async Task<PublishedVersion[]> GetPublishedVersions(string btcpayVersion, bool includePreRelease) public async Task<PublishedVersion[]> GetPublishedVersions(string btcpayVersion, bool includePreRelease)
{ {
var queryString = $"?includePreRelease={includePreRelease}"; var queryString = $"?includePreRelease={includePreRelease}";
if(btcpayVersion is not null) if (btcpayVersion is not null)
queryString += $"&btcpayVersion={btcpayVersion}&"; queryString += $"&btcpayVersion={btcpayVersion}&";
var result = await httpClient.GetStringAsync($"api/v1/plugins{queryString}"); var result = await httpClient.GetStringAsync($"api/v1/plugins{queryString}");
return JsonConvert.DeserializeObject<PublishedVersion[]>(result, serializerSettings) ?? throw new InvalidOperationException(); return JsonConvert.DeserializeObject<PublishedVersion[]>(result, serializerSettings) ?? throw new InvalidOperationException();

View File

@@ -98,7 +98,7 @@ namespace BTCPayServer.Plugins
// Formatted either as "<PLUGIN_IDENTIFIER>::<PathToDll>" or "<PathToDll>" // Formatted either as "<PLUGIN_IDENTIFIER>::<PathToDll>" or "<PathToDll>"
var idx = plugin.IndexOf("::"); var idx = plugin.IndexOf("::");
if (idx != -1) if (idx != -1)
pluginsToLoad.Add((plugin[0..idx], plugin[(idx+1)..])); pluginsToLoad.Add((plugin[0..idx], plugin[(idx + 1)..]));
else else
pluginsToLoad.Add((Path.GetFileNameWithoutExtension(plugin), plugin)); pluginsToLoad.Add((Path.GetFileNameWithoutExtension(plugin), plugin));
} }
@@ -198,7 +198,7 @@ namespace BTCPayServer.Plugins
if (ordersByPlugin.TryAdd(p.PluginIdentifier, order)) if (ordersByPlugin.TryAdd(p.PluginIdentifier, order))
order++; order++;
} }
pluginsToLoad.Sort((a,b) => ordersByPlugin[a.PluginIdentifier] - ordersByPlugin[b.PluginIdentifier]); pluginsToLoad.Sort((a, b) => ordersByPlugin[a.PluginIdentifier] - ordersByPlugin[b.PluginIdentifier]);
} }
public static void UsePlugins(this IApplicationBuilder applicationBuilder) public static void UsePlugins(this IApplicationBuilder applicationBuilder)

View File

@@ -200,7 +200,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
price = amount; price = amount;
title = settings.Title; title = settings.Title;
//if cart IS enabled and we detect posdata that matches the cart system's, check inventory for the items //if cart IS enabled and we detect posdata that matches the cart system's, check inventory for the items
if (currentView == PosViewType.Cart && if (currentView == PosViewType.Cart &&
AppService.TryParsePosCartItems(jposData, out cartItems)) AppService.TryParsePosCartItems(jposData, out cartItems))
{ {
@@ -242,7 +242,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
var store = await _appService.GetStore(app); var store = await _appService.GetStore(app);
var posFormId = settings.FormId; var posFormId = settings.FormId;
var formData = await FormDataService.GetForm(posFormId); var formData = await FormDataService.GetForm(posFormId);
JObject formResponseJObject = null; JObject formResponseJObject = null;
switch (formData) switch (formData)
{ {
@@ -308,7 +308,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
var receiptData = new JObject(); var receiptData = new JObject();
if (choice is not null) if (choice is not null)
{ {
receiptData = JObject.FromObject(new Dictionary<string, string>() receiptData = JObject.FromObject(new Dictionary<string, string>()
{ {
{"Title", choice.Title}, {"Description", choice.Description}, {"Title", choice.Title}, {"Description", choice.Description},
}); });
@@ -337,19 +337,20 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
if (appPosData.DiscountAmount > 0) if (appPosData.DiscountAmount > 0)
{ {
receiptData.Add("Discount", receiptData.Add("Discount",
$"{_displayFormatter.Currency(appPosData.DiscountAmount, settings.Currency, DisplayFormatter.CurrencyFormat.Symbol) } {(appPosData.DiscountPercentage > 0 ? $"({appPosData.DiscountPercentage}%)" : string.Empty)}"); $"{_displayFormatter.Currency(appPosData.DiscountAmount, settings.Currency, DisplayFormatter.CurrencyFormat.Symbol)} {(appPosData.DiscountPercentage > 0 ? $"({appPosData.DiscountPercentage}%)" : string.Empty)}");
} }
if (appPosData.Tip > 0) if (appPosData.Tip > 0)
{ {
receiptData.Add("Tip", receiptData.Add("Tip",
$"{_displayFormatter.Currency(appPosData.Tip, settings.Currency, DisplayFormatter.CurrencyFormat.Symbol) }"); $"{_displayFormatter.Currency(appPosData.Tip, settings.Currency, DisplayFormatter.CurrencyFormat.Symbol)}");
} }
} }
entity.Metadata.SetAdditionalData("receiptData", receiptData); entity.Metadata.SetAdditionalData("receiptData", receiptData);
if (formResponseJObject is null) return; if (formResponseJObject is null)
return;
var meta = entity.Metadata.ToJObject(); var meta = entity.Metadata.ToJObject();
meta.Merge(formResponseJObject); meta.Merge(formResponseJObject);
entity.Metadata = InvoiceMetadata.FromJObject(meta); entity.Metadata = InvoiceMetadata.FromJObject(meta);
@@ -388,14 +389,14 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
var app = await _appService.GetApp(appId, PointOfSaleAppType.AppType); var app = await _appService.GetApp(appId, PointOfSaleAppType.AppType);
if (app == null) if (app == null)
return NotFound(); return NotFound();
var settings = app.GetSettings<PointOfSaleSettings>(); var settings = app.GetSettings<PointOfSaleSettings>();
var formData = await FormDataService.GetForm(settings.FormId); var formData = await FormDataService.GetForm(settings.FormId);
if (formData is null) if (formData is null)
{ {
return RedirectToAction(nameof(ViewPointOfSale), new { appId, viewType }); return RedirectToAction(nameof(ViewPointOfSale), new { appId, viewType });
} }
var prefix = Encoders.Base58.EncodeData(RandomUtils.GetBytes(16)) + "_"; var prefix = Encoders.Base58.EncodeData(RandomUtils.GetBytes(16)) + "_";
var formParameters = Request.Form var formParameters = Request.Form
.Where(pair => pair.Key != "__RequestVerificationToken") .Where(pair => pair.Key != "__RequestVerificationToken")
@@ -422,7 +423,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
{ {
vm.RouteParameters.Add("viewType", viewType.Value.ToString()); vm.RouteParameters.Add("viewType", viewType.Value.ToString());
} }
return View("Views/UIForms/View", vm); return View("Views/UIForms/View", vm);
} }
@@ -434,7 +435,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
var app = await _appService.GetApp(appId, PointOfSaleAppType.AppType); var app = await _appService.GetApp(appId, PointOfSaleAppType.AppType);
if (app == null) if (app == null)
return NotFound(); return NotFound();
var settings = app.GetSettings<PointOfSaleSettings>(); var settings = app.GetSettings<PointOfSaleSettings>();
var formData = await FormDataService.GetForm(settings.FormId); var formData = await FormDataService.GetForm(settings.FormId);
if (formData is null) if (formData is null)
@@ -447,16 +448,16 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
.Where(pair => pair.Key.StartsWith(viewModel.FormParameterPrefix)) .Where(pair => pair.Key.StartsWith(viewModel.FormParameterPrefix))
.ToDictionary(pair => pair.Key.Replace(viewModel.FormParameterPrefix, string.Empty), pair => pair.Value) .ToDictionary(pair => pair.Key.Replace(viewModel.FormParameterPrefix, string.Empty), pair => pair.Value)
.ToMultiValueDictionary(p => p.Key, p => p.Value.ToString()); .ToMultiValueDictionary(p => p.Key, p => p.Value.ToString());
if (Request is { Method: "POST", HasFormContentType: true }) if (Request is { Method: "POST", HasFormContentType: true })
{ {
form.ApplyValuesFromForm(Request.Form.Where(pair => formFieldNames.Contains(pair.Key))); form.ApplyValuesFromForm(Request.Form.Where(pair => formFieldNames.Contains(pair.Key)));
if (FormDataService.Validate(form, ModelState)) if (FormDataService.Validate(form, ModelState))
{ {
var controller = nameof(UIPointOfSaleController).TrimEnd("Controller", StringComparison.InvariantCulture); var controller = nameof(UIPointOfSaleController).TrimEnd("Controller", StringComparison.InvariantCulture);
var redirectUrl = var redirectUrl =
Request.GetAbsoluteUri(Url.Action(nameof(ViewPointOfSale), controller, new {appId, viewType})); Request.GetAbsoluteUri(Url.Action(nameof(ViewPointOfSale), controller, new { appId, viewType }));
formParameters.Add("formResponse", FormDataService.GetValues(form).ToString()); formParameters.Add("formResponse", FormDataService.GetValues(form).ToString());
return View("PostRedirect", new PostRedirectViewModel return View("PostRedirect", new PostRedirectViewModel
{ {

View File

@@ -33,7 +33,7 @@ namespace BTCPayServer.Plugins.PointOfSale
base.Execute(services); base.Execute(services);
} }
} }
public enum PosViewType public enum PosViewType
{ {
[Display(Name = "Product list")] [Display(Name = "Product list")]
@@ -46,7 +46,7 @@ namespace BTCPayServer.Plugins.PointOfSale
Print Print
} }
public class PointOfSaleAppType: AppBaseType, IHasSaleStatsAppType, IHasItemStatsAppType public class PointOfSaleAppType : AppBaseType, IHasSaleStatsAppType, IHasItemStatsAppType
{ {
private readonly LinkGenerator _linkGenerator; private readonly LinkGenerator _linkGenerator;
private readonly IOptions<BTCPayServerOptions> _btcPayServerOptions; private readonly IOptions<BTCPayServerOptions> _btcPayServerOptions;

View File

@@ -1,5 +1,5 @@
using System.Collections.Generic;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace BTCPayServer namespace BTCPayServer

View File

@@ -48,7 +48,7 @@ namespace BTCPayServer.Services.Apps
StoreRepository storeRepository, StoreRepository storeRepository,
HtmlSanitizer htmlSanitizer) HtmlSanitizer htmlSanitizer)
{ {
_appTypes = apps.ToDictionary(a => a.Type, a=> a); _appTypes = apps.ToDictionary(a => a.Type, a => a);
_ContextFactory = contextFactory; _ContextFactory = contextFactory;
_InvoiceRepository = invoiceRepository; _InvoiceRepository = invoiceRepository;
_Currencies = currencies; _Currencies = currencies;
@@ -83,8 +83,8 @@ namespace BTCPayServer.Services.Apps
{ {
if (GetAppType(appData.AppType) is not IHasItemStatsAppType salesType) if (GetAppType(appData.AppType) is not IHasItemStatsAppType salesType)
throw new InvalidOperationException("This app isn't a SalesAppBaseType"); throw new InvalidOperationException("This app isn't a SalesAppBaseType");
var paidInvoices = await GetInvoicesForApp(_InvoiceRepository,appData, var paidInvoices = await GetInvoicesForApp(_InvoiceRepository, appData,
null, new [] null, new[]
{ {
InvoiceState.ToString(InvoiceStatusLegacy.Paid), InvoiceState.ToString(InvoiceStatusLegacy.Paid),
InvoiceState.ToString(InvoiceStatusLegacy.Confirmed), InvoiceState.ToString(InvoiceStatusLegacy.Confirmed),
@@ -94,7 +94,7 @@ namespace BTCPayServer.Services.Apps
} }
public static Task<SalesStats> GetSalesStatswithPOSItems(ViewPointOfSaleViewModel.Item[] items, public static Task<SalesStats> GetSalesStatswithPOSItems(ViewPointOfSaleViewModel.Item[] items,
InvoiceEntity[] paidInvoices, int numberOfDays) InvoiceEntity[] paidInvoices, int numberOfDays)
{ {
var series = paidInvoices var series = paidInvoices
.Aggregate(new List<InvoiceStatsItem>(), AggregateInvoiceEntitiesForStats(items)) .Aggregate(new List<InvoiceStatsItem>(), AggregateInvoiceEntitiesForStats(items))
@@ -126,19 +126,19 @@ namespace BTCPayServer.Services.Apps
Series = series.OrderBy(i => i.Label) Series = series.OrderBy(i => i.Label)
}); });
} }
public async Task<SalesStats> GetSalesStats(AppData app, int numberOfDays = 7) public async Task<SalesStats> GetSalesStats(AppData app, int numberOfDays = 7)
{ {
if (GetAppType(app.AppType) is not IHasSaleStatsAppType salesType) if (GetAppType(app.AppType) is not IHasSaleStatsAppType salesType)
throw new InvalidOperationException("This app isn't a SalesAppBaseType"); throw new InvalidOperationException("This app isn't a SalesAppBaseType");
var paidInvoices = await GetInvoicesForApp(_InvoiceRepository, app, DateTimeOffset.UtcNow - TimeSpan.FromDays(numberOfDays), var paidInvoices = await GetInvoicesForApp(_InvoiceRepository, app, DateTimeOffset.UtcNow - TimeSpan.FromDays(numberOfDays),
new [] new[]
{ {
InvoiceState.ToString(InvoiceStatusLegacy.Paid), InvoiceState.ToString(InvoiceStatusLegacy.Paid),
InvoiceState.ToString(InvoiceStatusLegacy.Confirmed), InvoiceState.ToString(InvoiceStatusLegacy.Confirmed),
InvoiceState.ToString(InvoiceStatusLegacy.Complete) InvoiceState.ToString(InvoiceStatusLegacy.Complete)
}); });
return await salesType.GetSalesStats(app, paidInvoices, numberOfDays); return await salesType.GetSalesStats(app, paidInvoices, numberOfDays);
} }
@@ -195,7 +195,7 @@ namespace BTCPayServer.Services.Apps
return res; return res;
}; };
} }
public static string GetAppOrderId(AppData app) => GetAppOrderId(app.AppType, app.Id); public static string GetAppOrderId(AppData app) => GetAppOrderId(app.AppType, app.Id);
public static string GetAppOrderId(string appType, string appId) => public static string GetAppOrderId(string appType, string appId) =>
appType switch appType switch
@@ -211,13 +211,13 @@ namespace BTCPayServer.Services.Apps
return invoice.GetInternalTags("APP#"); return invoice.GetInternalTags("APP#");
} }
public static async Task<InvoiceEntity[]> GetInvoicesForApp(InvoiceRepository invoiceRepository, AppData appData, DateTimeOffset? startDate = null, string[]? status = null) public static async Task<InvoiceEntity[]> GetInvoicesForApp(InvoiceRepository invoiceRepository, AppData appData, DateTimeOffset? startDate = null, string[]? status = null)
{ {
var invoices = await invoiceRepository.GetInvoices(new InvoiceQuery var invoices = await invoiceRepository.GetInvoices(new InvoiceQuery
{ {
StoreId = new[] { appData.StoreDataId }, StoreId = new[] { appData.StoreDataId },
OrderId = appData.TagAllInvoices ? null : new[] { GetAppOrderId(appData) }, OrderId = appData.TagAllInvoices ? null : new[] { GetAppOrderId(appData) },
Status = status?? new[]{ Status = status ?? new[]{
InvoiceState.ToString(InvoiceStatusLegacy.New), InvoiceState.ToString(InvoiceStatusLegacy.New),
InvoiceState.ToString(InvoiceStatusLegacy.Paid), InvoiceState.ToString(InvoiceStatusLegacy.Paid),
InvoiceState.ToString(InvoiceStatusLegacy.Confirmed), InvoiceState.ToString(InvoiceStatusLegacy.Confirmed),
@@ -270,7 +270,7 @@ namespace BTCPayServer.Services.Apps
}) })
.OrderBy(b => b.Created) .OrderBy(b => b.Created)
.ToArrayAsync(); .ToArrayAsync();
// allowNoUser can lead to apps being included twice, unify them with distinct // allowNoUser can lead to apps being included twice, unify them with distinct
if (allowNoUser) if (allowNoUser)
{ {
@@ -295,7 +295,7 @@ namespace BTCPayServer.Services.Apps
string posViewStyle = (settings.EnableShoppingCart ? PosViewType.Cart : settings.DefaultView).ToString(); string posViewStyle = (settings.EnableShoppingCart ? PosViewType.Cart : settings.DefaultView).ToString();
style = typeof(PosViewType).DisplayName(posViewStyle); style = typeof(PosViewType).DisplayName(posViewStyle);
break; break;
default: default:
style = string.Empty; style = string.Empty;
break; break;
@@ -386,7 +386,7 @@ namespace BTCPayServer.Services.Apps
return serializer.Serialize(mappingNode); return serializer.Serialize(mappingNode);
} }
public ViewPointOfSaleViewModel.Item[] Parse( string template, string currency) public ViewPointOfSaleViewModel.Item[] Parse(string template, string currency)
{ {
return Parse(_HtmlSanitizer, _displayFormatter, template, currency); return Parse(_HtmlSanitizer, _displayFormatter, template, currency);
} }
@@ -401,7 +401,7 @@ namespace BTCPayServer.Services.Apps
if (string.IsNullOrWhiteSpace(template)) if (string.IsNullOrWhiteSpace(template))
return Array.Empty<ViewPointOfSaleViewModel.Item>(); return Array.Empty<ViewPointOfSaleViewModel.Item>();
using var input = new StringReader(template); using var input = new StringReader(template);
YamlStream stream = new (); YamlStream stream = new();
stream.Load(input); stream.Load(input);
var root = (YamlMappingNode)stream.Documents[0].RootNode; var root = (YamlMappingNode)stream.Documents[0].RootNode;
return root return root
@@ -410,7 +410,7 @@ namespace BTCPayServer.Services.Apps
.Where(kv => kv.Value != null) .Where(kv => kv.Value != null)
.Select(c => .Select(c =>
{ {
ViewPointOfSaleViewModel.Item.ItemPrice price = new (); ViewPointOfSaleViewModel.Item.ItemPrice price = new();
var pValue = c.GetDetail("price")?.FirstOrDefault(); var pValue = c.GetDetail("price")?.FirstOrDefault();
switch (c.GetDetailString("custom") ?? c.GetDetailString("price_type")?.ToLowerInvariant()) switch (c.GetDetailString("custom") ?? c.GetDetailString("price_type")?.ToLowerInvariant())

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