Code formatting updates (#4502)

* Editorconfig: Add space_before_self_closing setting

This was a difference between the way dotnet-format and Rider format code. See https://www.jetbrains.com/help/rider/EditorConfig_Index.html

* Editorconfig: Keep 4 spaces indentation for Swagger JSON files

They are all formatted that way, let's keep it like that.

* Apply dotnet-format, mostly white-space related changes
This commit is contained in:
d11n 2023-01-06 14:18:07 +01:00 committed by GitHub
parent 3fa28bb46d
commit d5d0be5824
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
241 changed files with 1815 additions and 1715 deletions

View File

@ -11,10 +11,14 @@ insert_final_newline = true
indent_style = space indent_style = space
indent_size = 4 indent_size = 4
charset = utf-8 charset = utf-8
space_before_self_closing = true
[*.json] [*.json]
indent_size = 2 indent_size = 2
[swagger*.json]
indent_size = 4
# C# files # C# files
[*.cs] [*.cs]
# New line preferences # New line preferences
@ -67,7 +71,7 @@ dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
dotnet_naming_style.camel_case_underscore_style.required_prefix = _ dotnet_naming_style.camel_case_underscore_style.required_prefix = _
dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
# Code style defaults # Code style defaults
dotnet_sort_system_directives_first = true dotnet_sort_system_directives_first = true

View File

@ -1,4 +1,4 @@
#nullable enable #nullable enable
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;

View File

@ -1,4 +1,4 @@
#nullable enable #nullable enable
namespace BTCPayServer.Abstractions.Contracts; namespace BTCPayServer.Abstractions.Contracts;
public interface IScopeProvider public interface IScopeProvider

View File

@ -1,4 +1,4 @@
using System; using System;
namespace BTCPayServer.Abstractions.Contracts; namespace BTCPayServer.Abstractions.Contracts;

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using NBitcoin; using NBitcoin;

View File

@ -9,7 +9,7 @@ public class AssetQuoteResult
public AssetQuoteResult() { } public AssetQuoteResult() { }
public AssetQuoteResult(string fromAsset, string toAsset,decimal bid, decimal ask) public AssetQuoteResult(string fromAsset, string toAsset, decimal bid, decimal ask)
{ {
FromAsset = fromAsset; FromAsset = fromAsset;
ToAsset = toAsset; ToAsset = toAsset;

View File

@ -2,10 +2,7 @@ namespace BTCPayServer.Abstractions.Custodians;
public class AssetBalancesUnavailableException : CustodianApiException public class AssetBalancesUnavailableException : CustodianApiException
{ {
public AssetBalancesUnavailableException(System.Exception e) : base(500, "asset-balances-unavailable", $"Cannot fetch the asset balances: {e.Message}", e) public AssetBalancesUnavailableException(System.Exception e) : base(500, "asset-balances-unavailable", $"Cannot fetch the asset balances: {e.Message}", e)
{ {
} }
} }

View File

@ -1,6 +1,7 @@
using System; using System;
namespace BTCPayServer.Abstractions.Custodians; namespace BTCPayServer.Abstractions.Custodians;
public class CustodianApiException: Exception { public class CustodianApiException : Exception
{
public int HttpStatus { get; } public int HttpStatus { get; }
public string Code { get; } public string Code { get; }
@ -9,7 +10,8 @@ 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

@ -1,9 +1,8 @@
namespace BTCPayServer.Abstractions.Custodians; namespace BTCPayServer.Abstractions.Custodians;
public class CustodianFeatureNotImplementedException: CustodianApiException public class CustodianFeatureNotImplementedException : CustodianApiException
{ {
public CustodianFeatureNotImplementedException(string message) : base(400, "not-implemented", message) public CustodianFeatureNotImplementedException(string message) : base(400, "not-implemented", message)
{ {
} }
} }

View File

@ -1,9 +1,8 @@
namespace BTCPayServer.Abstractions.Custodians; namespace BTCPayServer.Abstractions.Custodians;
public class InsufficientFundsException : CustodianApiException public class InsufficientFundsException : CustodianApiException
{ {
public InsufficientFundsException(string message) : base(400, "insufficient-funds", message) public InsufficientFundsException(string message) : base(400, "insufficient-funds", message)
{ {
} }
} }

View File

@ -4,7 +4,7 @@ public class TradeNotFoundException : CustodianApiException
{ {
private string tradeId { get; } private string tradeId { get; }
public TradeNotFoundException(string tradeId) : base(404,"trade-not-found","Could not find trade ID " + tradeId) public TradeNotFoundException(string tradeId) : base(404, "trade-not-found", "Could not find trade ID " + tradeId)
{ {
this.tradeId = tradeId; this.tradeId = tradeId;
} }

View File

@ -1,6 +1,6 @@
namespace BTCPayServer.Abstractions.Custodians; namespace BTCPayServer.Abstractions.Custodians;
public class WrongTradingPairException: CustodianApiException public class WrongTradingPairException : CustodianApiException
{ {
public const int HttpCode = 404; public const int HttpCode = 404;
public WrongTradingPairException(string fromAsset, string toAsset) : base(HttpCode, "wrong-trading-pair", $"Cannot find a trading pair for converting {fromAsset} into {toAsset}.") public WrongTradingPairException(string fromAsset, string toAsset) : base(HttpCode, "wrong-trading-pair", $"Cannot find a trading pair for converting {fromAsset} into {toAsset}.")

View File

@ -9,18 +9,16 @@ namespace BTCPayServer.Abstractions.Custodians;
public interface ICanTrade public interface ICanTrade
{ {
/** /**
* A list of tradable asset pairs, or NULL if the custodian cannot trade/convert assets. if thr asset pair contains fiat, fiat is always put last. If both assets are a cyrptocode or both are fiat, the pair is written alphabetically. Always in uppercase. Example: ["BTC/EUR","BTC/USD", "EUR/USD", "BTC/ETH",...] * A list of tradable asset pairs, or NULL if the custodian cannot trade/convert assets. if thr asset pair contains fiat, fiat is always put last. If both assets are a cyrptocode or both are fiat, the pair is written alphabetically. Always in uppercase. Example: ["BTC/EUR","BTC/USD", "EUR/USD", "BTC/ETH",...]
*/ */
public List<AssetPairData> GetTradableAssetPairs(); public List<AssetPairData> GetTradableAssetPairs();
/** /**
* Execute a market order right now. * Execute a market order right now.
*/ */
public Task<MarketTradeResult> TradeMarketAsync(string fromAsset, string toAsset, decimal qty, JObject config, CancellationToken cancellationToken); public Task<MarketTradeResult> TradeMarketAsync(string fromAsset, string toAsset, decimal qty, JObject config, CancellationToken cancellationToken);
/** /**
* Get the details about a previous market trade. * Get the details about a previous market trade.
*/ */

View File

@ -12,7 +12,7 @@ public interface ICustodian
* Get the unique code that identifies this custodian. * Get the unique code that identifies this custodian.
*/ */
string Code { get; } string Code { get; }
string Name { get; } string Name { get; }
/** /**

View File

@ -30,12 +30,12 @@ public static class GreenfieldExtensions
{ {
return controller.BadRequest(new GreenfieldAPIError(errorCode, errorMessage)); return controller.BadRequest(new GreenfieldAPIError(errorCode, errorMessage));
} }
public static IActionResult CreateAPIError(this ControllerBase controller, int httpCode, string errorCode, string errorMessage) public static IActionResult CreateAPIError(this ControllerBase controller, int httpCode, string errorCode, string errorMessage)
{ {
return controller.StatusCode(httpCode, new GreenfieldAPIError(errorCode, errorMessage)); return controller.StatusCode(httpCode, new GreenfieldAPIError(errorCode, errorMessage));
} }
public static IActionResult CreateAPIPermissionError(this ControllerBase controller, string missingPermission, string message = null) public static IActionResult CreateAPIPermissionError(this ControllerBase controller, string missingPermission, string message = null)
{ {
return controller.StatusCode(403, new GreenfieldPermissionAPIError(missingPermission, message)); return controller.StatusCode(403, new GreenfieldPermissionAPIError(missingPermission, message));

View File

@ -11,7 +11,7 @@ public static class HttpRequestExtensions
return false; return false;
return request.Host.Host.EndsWith(".onion", StringComparison.OrdinalIgnoreCase); return request.Host.Host.EndsWith(".onion", StringComparison.OrdinalIgnoreCase);
} }
public static string GetAbsoluteRoot(this HttpRequest request) public static string GetAbsoluteRoot(this HttpRequest request)
{ {
return string.Concat( return string.Concat(

View File

@ -6,7 +6,6 @@ namespace BTCPayServer.Abstractions.Extensions;
public static class StringExtensions public static class StringExtensions
{ {
public static bool IsValidFileName(this string fileName) public static bool IsValidFileName(this string fileName)
{ {
return !fileName.ToCharArray().Any(c => Path.GetInvalidFileNameChars().Contains(c) return !fileName.ToCharArray().Any(c => Path.GetInvalidFileNameChars().Contains(c)
@ -41,5 +40,4 @@ public static class StringExtensions
return str.Substring(0, str.Length - 1); return str.Substring(0, str.Length - 1);
return str; return str;
} }
} }

View File

@ -50,7 +50,7 @@ namespace BTCPayServer.Abstractions.Extensions
{ {
return IsActiveCategory(viewData, category.ToString(), id); return IsActiveCategory(viewData, category.ToString(), id);
} }
public static string IsActiveCategory(this ViewDataDictionary viewData, string category, object id = null) public static string IsActiveCategory(this ViewDataDictionary viewData, string category, object id = null)
{ {
if (!viewData.ContainsKey(ACTIVE_CATEGORY_KEY)) if (!viewData.ContainsKey(ACTIVE_CATEGORY_KEY))
@ -77,7 +77,7 @@ namespace BTCPayServer.Abstractions.Extensions
? ActivePageClass ? ActivePageClass
: null; : null;
} }
public static string IsActivePage(this ViewDataDictionary viewData, string page, string category, object id = null) public static string IsActivePage(this ViewDataDictionary viewData, string page, string category, object id = null)
{ {
if (!viewData.ContainsKey(ACTIVE_PAGE_KEY)) if (!viewData.ContainsKey(ACTIVE_PAGE_KEY))
@ -126,7 +126,7 @@ namespace BTCPayServer.Abstractions.Extensions
{ {
return $"{(int)timeSpan.TotalMinutes} minute{Plural((int)timeSpan.TotalMinutes)}"; return $"{(int)timeSpan.TotalMinutes} minute{Plural((int)timeSpan.TotalMinutes)}";
} }
return timeSpan.Days < 1 return timeSpan.Days < 1
? $"{(int)timeSpan.TotalHours} hour{Plural((int)timeSpan.TotalHours)}" ? $"{(int)timeSpan.TotalHours} hour{Plural((int)timeSpan.TotalHours)}"
: $"{(int)timeSpan.TotalDays} day{Plural((int)timeSpan.TotalDays)}"; : $"{(int)timeSpan.TotalDays} day{Plural((int)timeSpan.TotalDays)}";
} }

View File

@ -17,16 +17,16 @@ public class AlertMessage
Danger, Danger,
Info Info
} }
[JsonConverter(typeof(StringEnumConverter))] [JsonConverter(typeof(StringEnumConverter))]
public AlertMessageType Type; public AlertMessageType Type;
// The translated message to be shown to the user // The translated message to be shown to the user
public string Message; public string Message;
public AlertMessage() public AlertMessage()
{ {
} }
public AlertMessage(AlertMessageType type, string message) public AlertMessage(AlertMessageType type, string message)

View File

@ -17,7 +17,7 @@ public class Field
Label = label, Label = label,
Name = name, Name = name,
Value = value, Value = value,
OriginalValue = value, OriginalValue = value,
Required = required, Required = required,
HelpText = helpText, HelpText = helpText,
Type = type Type = type

View File

@ -22,10 +22,10 @@ public class Form
#nullable restore #nullable restore
// Messages to be shown at the top of the form indicating user feedback like "Saved successfully" or "Please change X because of Y." or a warning, etc... // Messages to be shown at the top of the form indicating user feedback like "Saved successfully" or "Please change X because of Y." or a warning, etc...
public List<AlertMessage> TopMessages { get; set; } = new(); public List<AlertMessage> TopMessages { get; set; } = new();
// Groups of fields in the form // Groups of fields in the form
public List<Field> Fields { get; set; } = new(); public List<Field> Fields { get; set; } = new();
// Are all the fields valid in the form? // Are all the fields valid in the form?
public bool IsValid() public bool IsValid()
{ {
@ -36,7 +36,7 @@ public class Form
{ {
return GetFieldByName(name, Fields, null); return GetFieldByName(name, Fields, null);
} }
private static Field GetFieldByName(string name, List<Field> fields, string prefix) private static Field GetFieldByName(string name, List<Field> fields, string prefix)
{ {
prefix ??= string.Empty; prefix ??= string.Empty;
@ -45,7 +45,7 @@ public class Form
var currentPrefix = prefix; var currentPrefix = prefix;
if (!string.IsNullOrEmpty(field.Name)) if (!string.IsNullOrEmpty(field.Name))
{ {
currentPrefix = $"{prefix}{field.Name}"; currentPrefix = $"{prefix}{field.Name}";
if (currentPrefix.Equals(name, StringComparison.InvariantCultureIgnoreCase)) if (currentPrefix.Equals(name, StringComparison.InvariantCultureIgnoreCase))
{ {
@ -60,7 +60,7 @@ public class Form
{ {
return subFieldResult; return subFieldResult;
} }
} }
return null; return null;
} }
@ -73,7 +73,7 @@ public class Form
private static List<string> GetAllNames(List<Field> fields) private static List<string> GetAllNames(List<Field> fields)
{ {
var names = new List<string>(); var names = new List<string>();
foreach (var field in fields) foreach (var field in fields)
{ {
string prefix = string.Empty; string prefix = string.Empty;
@ -85,7 +85,7 @@ public class Form
if (field.Fields.Any()) if (field.Fields.Any())
{ {
names.AddRange(GetAllNames(field.Fields).Select(s => $"{prefix}{s}" )); names.AddRange(GetAllNames(field.Fields).Select(s => $"{prefix}{s}"));
} }
} }
@ -105,7 +105,7 @@ public class Form
} }
} }
} }
public void ApplyValuesFromForm(IFormCollection form) public void ApplyValuesFromForm(IFormCollection form)
{ {
var names = GetAllNames(); var names = GetAllNames();
@ -125,7 +125,7 @@ public class Form
{ {
return GetValues(Fields); return GetValues(Fields);
} }
private static Dictionary<string, object> GetValues(List<Field> fields) private static Dictionary<string, object> GetValues(List<Field> fields)
{ {
var result = new Dictionary<string, object>(); var result = new Dictionary<string, object>();
@ -136,11 +136,12 @@ public class Form
{ {
var values = GetValues(fields); var values = GetValues(fields);
values.Remove(string.Empty, out var keylessValue); values.Remove(string.Empty, out var keylessValue);
result.TryAdd(name, values); result.TryAdd(name, values);
if (keylessValue is not Dictionary<string, object> dict) continue; if (keylessValue is not Dictionary<string, object> dict)
foreach (KeyValuePair<string,object> keyValuePair in dict) continue;
foreach (KeyValuePair<string, object> keyValuePair in dict)
{ {
result.TryAdd(keyValuePair.Key, keyValuePair.Value); result.TryAdd(keyValuePair.Key, keyValuePair.Value);
} }

View File

@ -8,7 +8,7 @@ public class AuthorizationFilterHandle
public AuthorizationHandlerContext Context { get; } public AuthorizationHandlerContext Context { get; }
public PolicyRequirement Requirement { get; } public PolicyRequirement Requirement { get; }
public HttpContext HttpContext { get; } public HttpContext HttpContext { get; }
public bool Success { get; private set; } public bool Success { get; private set; }
public AuthorizationFilterHandle( public AuthorizationFilterHandle(
AuthorizationHandlerContext context, AuthorizationHandlerContext context,

View File

@ -29,5 +29,5 @@ public class SVGUse : UrlResolutionTagHelper2
attr = _fileVersionProvider.AddFileVersionToPath(ViewContext.HttpContext.Request.PathBase, attr); attr = _fileVersionProvider.AddFileVersionToPath(ViewContext.HttpContext.Request.PathBase, attr);
output.Attributes.SetAttribute("href", attr); output.Attributes.SetAttribute("href", attr);
base.Process(context, output); base.Process(context, output);
} }
} }

View File

@ -19,7 +19,7 @@ namespace BTCPayServer.Client
method: HttpMethod.Post), token); method: HttpMethod.Post), token);
return await HandleResponse<PointOfSaleAppData>(response); return await HandleResponse<PointOfSaleAppData>(response);
} }
public virtual async Task<CrowdfundAppData> CreateCrowdfundApp(string storeId, public virtual async Task<CrowdfundAppData> CreateCrowdfundApp(string storeId,
CreateCrowdfundAppRequest request, CancellationToken token = default) CreateCrowdfundAppRequest request, CancellationToken token = default)
{ {

View File

@ -58,7 +58,7 @@ namespace BTCPayServer.Client
public virtual async Task<MarketTradeResponseData> MarketTradeCustodianAccountAsset(string storeId, string accountId, TradeRequestData request, CancellationToken token = default) public virtual async Task<MarketTradeResponseData> MarketTradeCustodianAccountAsset(string storeId, string accountId, TradeRequestData request, CancellationToken token = default)
{ {
//var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/users", null, request, HttpMethod.Post), token); //var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/users", null, request, HttpMethod.Post), token);
//return await HandleResponse<ApplicationUserData>(response); //return await HandleResponse<ApplicationUserData>(response);
var internalRequest = CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/trades/market", null, var internalRequest = CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/trades/market", null,
@ -81,8 +81,8 @@ 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> CreateWithdrawal(string storeId, string accountId, WithdrawRequestData request, CancellationToken token = default) public virtual async Task<WithdrawalResponseData> CreateWithdrawal(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);

View File

@ -17,7 +17,7 @@ namespace BTCPayServer.Client
method: HttpMethod.Get), token); method: HttpMethod.Get), token);
return await HandleResponse<LightningNodeInformationData>(response); return await HandleResponse<LightningNodeInformationData>(response);
} }
public virtual async Task<LightningNodeBalanceData> GetLightningNodeBalance(string cryptoCode, public virtual async Task<LightningNodeBalanceData> GetLightningNodeBalance(string cryptoCode,
CancellationToken token = default) CancellationToken token = default)
{ {
@ -95,7 +95,7 @@ namespace BTCPayServer.Client
method: HttpMethod.Get), token); method: HttpMethod.Get), token);
return await HandleResponse<LightningInvoiceData>(response); return await HandleResponse<LightningInvoiceData>(response);
} }
public virtual async Task<LightningInvoiceData[]> GetLightningInvoices(string cryptoCode, public virtual async Task<LightningInvoiceData[]> GetLightningInvoices(string cryptoCode,
bool? pendingOnly = null, long? offsetIndex = null, CancellationToken token = default) bool? pendingOnly = null, long? offsetIndex = null, CancellationToken token = default)
{ {

View File

@ -17,7 +17,7 @@ namespace BTCPayServer.Client
method: HttpMethod.Get), token); method: HttpMethod.Get), token);
return await HandleResponse<LightningNodeInformationData>(response); return await HandleResponse<LightningNodeInformationData>(response);
} }
public virtual async Task<LightningNodeBalanceData> GetLightningNodeBalance(string storeId, string cryptoCode, public virtual async Task<LightningNodeBalanceData> GetLightningNodeBalance(string storeId, string cryptoCode,
CancellationToken token = default) CancellationToken token = default)
{ {
@ -97,7 +97,7 @@ namespace BTCPayServer.Client
method: HttpMethod.Get), token); method: HttpMethod.Get), token);
return await HandleResponse<LightningInvoiceData>(response); return await HandleResponse<LightningInvoiceData>(response);
} }
public virtual async Task<LightningInvoiceData[]> GetLightningInvoices(string storeId, string cryptoCode, public virtual async Task<LightningInvoiceData[]> GetLightningInvoices(string storeId, string cryptoCode,
bool? pendingOnly = null, long? offsetIndex = null, CancellationToken token = default) bool? pendingOnly = null, long? offsetIndex = null, CancellationToken token = default)
{ {

View File

@ -39,7 +39,7 @@ namespace BTCPayServer.Client
parameters.Add("includeNeighbourData", v); parameters.Add("includeNeighbourData", v);
var response = var response =
await _httpClient.SendAsync( await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/objects", parameters, method:HttpMethod.Get), token); CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/objects", parameters, method: HttpMethod.Get), token);
return await HandleResponse<OnChainWalletObjectData[]>(response); return await HandleResponse<OnChainWalletObjectData[]>(response);
} }
public virtual async Task RemoveOnChainWalletObject(string storeId, string cryptoCode, OnChainWalletObjectId objectId, public virtual async Task RemoveOnChainWalletObject(string storeId, string cryptoCode, OnChainWalletObjectId objectId,
@ -47,7 +47,7 @@ namespace BTCPayServer.Client
{ {
var response = var response =
await _httpClient.SendAsync( await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/objects/{objectId.Type}/{objectId.Id}", method:HttpMethod.Delete), token); CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/objects/{objectId.Type}/{objectId.Id}", method: HttpMethod.Delete), token);
await HandleResponse(response); await HandleResponse(response);
} }
public virtual async Task<OnChainWalletObjectData> AddOrUpdateOnChainWalletObject(string storeId, string cryptoCode, AddOnChainWalletObjectRequest request, public virtual async Task<OnChainWalletObjectData> AddOrUpdateOnChainWalletObject(string storeId, string cryptoCode, AddOnChainWalletObjectRequest request,
@ -55,7 +55,7 @@ namespace BTCPayServer.Client
{ {
var response = var response =
await _httpClient.SendAsync( await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/objects", method:HttpMethod.Post, bodyPayload: request), token); CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/objects", method: HttpMethod.Post, bodyPayload: request), token);
return await HandleResponse<OnChainWalletObjectData>(response); return await HandleResponse<OnChainWalletObjectData>(response);
} }
public virtual async Task AddOrUpdateOnChainWalletLink(string storeId, string cryptoCode, public virtual async Task AddOrUpdateOnChainWalletLink(string storeId, string cryptoCode,
@ -65,7 +65,7 @@ namespace BTCPayServer.Client
{ {
var response = var response =
await _httpClient.SendAsync( await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/objects/{objectId.Type}/{objectId.Id}/links", method:HttpMethod.Post, bodyPayload: request), token); CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/objects/{objectId.Type}/{objectId.Id}/links", method: HttpMethod.Post, bodyPayload: request), token);
await HandleResponse(response); await HandleResponse(response);
} }
public virtual async Task RemoveOnChainWalletLinks(string storeId, string cryptoCode, public virtual async Task RemoveOnChainWalletLinks(string storeId, string cryptoCode,
@ -75,7 +75,7 @@ namespace BTCPayServer.Client
{ {
var response = var response =
await _httpClient.SendAsync( await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/objects/{objectId.Type}/{objectId.Id}/links/{link.Type}/{link.Id}", method:HttpMethod.Delete), token); CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/objects/{objectId.Type}/{objectId.Id}/links/{link.Type}/{link.Id}", method: HttpMethod.Delete), token);
await HandleResponse(response); await HandleResponse(response);
} }
} }

View File

@ -63,7 +63,8 @@ namespace BTCPayServer.Client
{ {
query.Add(nameof(statusFilter), statusFilter); query.Add(nameof(statusFilter), statusFilter);
} }
if (labelFilter != null) { if (labelFilter != null)
{
query.Add(nameof(labelFilter), labelFilter); query.Add(nameof(labelFilter), labelFilter);
} }
var response = var response =

View File

@ -52,17 +52,17 @@ namespace BTCPayServer.Client
{ {
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}/payouts", bodyPayload: payoutRequest, method: HttpMethod.Post), cancellationToken); var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}/payouts", bodyPayload: payoutRequest, method: HttpMethod.Post), cancellationToken);
return await HandleResponse<PayoutData>(response); return await HandleResponse<PayoutData>(response);
} }
public virtual async Task<PayoutData> GetPullPaymentPayout(string pullPaymentId, string payoutId, CancellationToken cancellationToken = default) public virtual async Task<PayoutData> GetPullPaymentPayout(string pullPaymentId, string payoutId, CancellationToken cancellationToken = default)
{ {
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}/payouts/{payoutId}", method: HttpMethod.Get), cancellationToken); var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}/payouts/{payoutId}", method: HttpMethod.Get), cancellationToken);
return await HandleResponse<PayoutData>(response); return await HandleResponse<PayoutData>(response);
} }
public virtual async Task<PayoutData> GetStorePayout(string storeId, string payoutId, CancellationToken cancellationToken = default) public virtual async Task<PayoutData> GetStorePayout(string storeId, string payoutId, CancellationToken cancellationToken = default)
{ {
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payouts/{payoutId}", method: HttpMethod.Get), cancellationToken); var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payouts/{payoutId}", method: HttpMethod.Get), cancellationToken);
return await HandleResponse<PayoutData>(response); return await HandleResponse<PayoutData>(response);
} }
public virtual async Task<PayoutData> CreatePayout(string storeId, CreatePayoutThroughStoreRequest payoutRequest, CancellationToken cancellationToken = default) public virtual async Task<PayoutData> CreatePayout(string storeId, CreatePayoutThroughStoreRequest payoutRequest, CancellationToken cancellationToken = default)
{ {
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payouts", bodyPayload: payoutRequest, method: HttpMethod.Post), cancellationToken); var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payouts", bodyPayload: payoutRequest, method: HttpMethod.Post), cancellationToken);

View File

@ -9,7 +9,7 @@ namespace BTCPayServer.Client
{ {
public partial class BTCPayServerClient public partial class BTCPayServerClient
{ {
public virtual async Task<IEnumerable<PayoutProcessorData>> GetPayoutProcessors(string storeId, public virtual async Task<IEnumerable<PayoutProcessorData>> GetPayoutProcessors(string storeId,
CancellationToken token = default) CancellationToken token = default)
{ {
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payout-processors"), token); var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payout-processors"), token);
@ -20,28 +20,28 @@ namespace BTCPayServer.Client
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payout-processors/{processor}/{paymentMethod}", null, HttpMethod.Delete), token); var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payout-processors/{processor}/{paymentMethod}", null, HttpMethod.Delete), token);
await HandleResponse(response); await HandleResponse(response);
} }
public virtual async Task<IEnumerable<LightningAutomatedPayoutSettings>> GetStoreLightningAutomatedPayoutProcessors(string storeId, string? paymentMethod = null, public virtual async Task<IEnumerable<LightningAutomatedPayoutSettings>> GetStoreLightningAutomatedPayoutProcessors(string storeId, string? paymentMethod = null,
CancellationToken token = default) CancellationToken token = default)
{ {
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payout-processors/LightningAutomatedPayoutSenderFactory{(paymentMethod is null? string.Empty: $"/{paymentMethod}")}"), token); var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payout-processors/LightningAutomatedPayoutSenderFactory{(paymentMethod is null ? string.Empty : $"/{paymentMethod}")}"), token);
return await HandleResponse<IEnumerable<LightningAutomatedPayoutSettings>>(response); return await HandleResponse<IEnumerable<LightningAutomatedPayoutSettings>>(response);
} }
public virtual async Task<LightningAutomatedPayoutSettings> UpdateStoreLightningAutomatedPayoutProcessors(string storeId, string paymentMethod,LightningAutomatedPayoutSettings request, CancellationToken token = default) public virtual async Task<LightningAutomatedPayoutSettings> UpdateStoreLightningAutomatedPayoutProcessors(string storeId, string paymentMethod, LightningAutomatedPayoutSettings request, CancellationToken token = default)
{ {
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payout-processors/LightningAutomatedPayoutSenderFactory/{paymentMethod}",null, request, HttpMethod.Put ), token); var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payout-processors/LightningAutomatedPayoutSenderFactory/{paymentMethod}", null, request, HttpMethod.Put), token);
return await HandleResponse<LightningAutomatedPayoutSettings>(response); return await HandleResponse<LightningAutomatedPayoutSettings>(response);
} }
public virtual async Task<OnChainAutomatedPayoutSettings> UpdateStoreOnChainAutomatedPayoutProcessors(string storeId, string paymentMethod,OnChainAutomatedPayoutSettings request, CancellationToken token = default) public virtual async Task<OnChainAutomatedPayoutSettings> UpdateStoreOnChainAutomatedPayoutProcessors(string storeId, string paymentMethod, OnChainAutomatedPayoutSettings request, CancellationToken token = default)
{ {
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payout-processors/OnChainAutomatedPayoutSenderFactory/{paymentMethod}",null, request, HttpMethod.Put ), token); var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payout-processors/OnChainAutomatedPayoutSenderFactory/{paymentMethod}", null, request, HttpMethod.Put), token);
return await HandleResponse<OnChainAutomatedPayoutSettings>(response); return await HandleResponse<OnChainAutomatedPayoutSettings>(response);
} }
public virtual async Task<IEnumerable<OnChainAutomatedPayoutSettings>> GetStoreOnChainAutomatedPayoutProcessors(string storeId, string? paymentMethod = null, public virtual async Task<IEnumerable<OnChainAutomatedPayoutSettings>> GetStoreOnChainAutomatedPayoutProcessors(string storeId, string? paymentMethod = null,
CancellationToken token = default) CancellationToken token = default)
{ {
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payout-processors/OnChainAutomatedPayoutSenderFactory{(paymentMethod is null? string.Empty: $"/{paymentMethod}")}"), token); var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/payout-processors/OnChainAutomatedPayoutSenderFactory{(paymentMethod is null ? string.Empty : $"/{paymentMethod}")}"), token);
return await HandleResponse<IEnumerable<OnChainAutomatedPayoutSettings>>(response); return await HandleResponse<IEnumerable<OnChainAutomatedPayoutSettings>>(response);
} }
} }

View File

@ -44,7 +44,7 @@ namespace BTCPayServer.Client
{ {
using var response = await _httpClient.SendAsync( using var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/rates/configuration/preview", bodyPayload: request, CreateHttpRequest($"api/v1/stores/{storeId}/rates/configuration/preview", bodyPayload: request,
queryPayload: new Dictionary<string, object>() {{"currencyPair", currencyPair}}, queryPayload: new Dictionary<string, object>() { { "currencyPair", currencyPair } },
method: HttpMethod.Post), method: HttpMethod.Post),
token); token);
return await HandleResponse<List<StoreRatePreviewResult>>(response); return await HandleResponse<List<StoreRatePreviewResult>>(response);

View File

@ -36,12 +36,12 @@ namespace BTCPayServer.Client
public virtual async Task<bool> LockUser(string idOrEmail, bool locked, CancellationToken token = default) public virtual async Task<bool> LockUser(string idOrEmail, bool locked, CancellationToken token = default)
{ {
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/users/{idOrEmail}/lock", null, var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/users/{idOrEmail}/lock", null,
new LockUserRequest {Locked = locked}, HttpMethod.Post), token); new LockUserRequest { Locked = locked }, HttpMethod.Post), token);
await HandleResponse(response); await HandleResponse(response);
return response.IsSuccessStatusCode; return response.IsSuccessStatusCode;
} }
public virtual async Task<ApplicationUserData[]> GetUsers( CancellationToken token = default) public virtual async Task<ApplicationUserData[]> GetUsers(CancellationToken token = default)
{ {
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/users/", null, HttpMethod.Get), token); var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/users/", null, HttpMethod.Get), token);
return await HandleResponse<ApplicationUserData[]>(response); return await HandleResponse<ApplicationUserData[]>(response);

View File

@ -8,7 +8,7 @@ public class AssetPairData
public AssetPairData() public AssetPairData()
{ {
} }
public AssetPairData(string assetBought, string assetSold, decimal minimumTradeQty) public AssetPairData(string assetBought, string assetSold, decimal minimumTradeQty)
{ {
AssetBought = assetBought; AssetBought = assetBought;
@ -25,7 +25,7 @@ public class AssetPairData
[JsonProperty] [JsonProperty]
public decimal MinimumTradeQty { set; get; } public decimal MinimumTradeQty { set; get; }
public override string ToString() public override string ToString()
{ {
return AssetBought + "/" + AssetSold; return AssetBought + "/" + AssetSold;

View File

@ -11,14 +11,14 @@ namespace BTCPayServer.Client.Models
{ {
} }
public CreateLightningInvoiceRequest(LightMoney amount, string description, TimeSpan expiry) public CreateLightningInvoiceRequest(LightMoney amount, string description, TimeSpan expiry)
{ {
Amount = amount; Amount = amount;
Description = description; Description = description;
Expiry = expiry; Expiry = expiry;
} }
[JsonConverter(typeof(JsonConverters.LightMoneyJsonConverter))] [JsonConverter(typeof(JsonConverters.LightMoneyJsonConverter))]
public LightMoney Amount { get; set; } public LightMoney Amount { get; set; }
public string Description { get; set; } public string Description { get; set; }

View File

@ -5,11 +5,11 @@ namespace BTCPayServer.Client.Models
public abstract class CustodianAccountBaseData public abstract class CustodianAccountBaseData
{ {
public string CustodianCode { get; set; } public string CustodianCode { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string StoreId { get; set; } public string StoreId { get; set; }
public JObject Config { get; set; } public JObject Config { get; set; }
} }

View File

@ -2,13 +2,13 @@ using System.Collections.Generic;
namespace BTCPayServer.Client.Models; namespace BTCPayServer.Client.Models;
public class CustodianAccountResponse: CustodianAccountData public class CustodianAccountResponse : CustodianAccountData
{ {
public IDictionary<string, decimal> AssetBalances { get; set; } public IDictionary<string, decimal> AssetBalances { get; set; }
public CustodianAccountResponse() public CustodianAccountResponse()
{ {
} }
} }

View File

@ -9,5 +9,5 @@ public class CustodianData
public Dictionary<string, AssetPairData> TradableAssetPairs { get; set; } public Dictionary<string, AssetPairData> TradableAssetPairs { get; set; }
public string[] WithdrawablePaymentMethods { get; set; } public string[] WithdrawablePaymentMethods { get; set; }
public string[] DepositablePaymentMethods { get; set; } public string[] DepositablePaymentMethods { get; set; }
} }

View File

@ -6,10 +6,10 @@ public class DepositAddressData
// * Example: P2PKH, P2SH, P2WPKH, P2TR, BOLT11, ... // * Example: P2PKH, P2SH, P2WPKH, P2TR, BOLT11, ...
// */ // */
// public string Type { get; set; } // public string Type { get; set; }
/** /**
* Format depends hugely on the type. * Format depends hugely on the type.
*/ */
public string Address { get; set; } public string Address { get; set; }
} }

View File

@ -1,4 +1,4 @@
using System; using System;
using BTCPayServer.Client.JsonConverters; using BTCPayServer.Client.JsonConverters;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -7,7 +7,7 @@ namespace BTCPayServer.Client.Models;
public class LightningAutomatedPayoutSettings public class LightningAutomatedPayoutSettings
{ {
public string PaymentMethod { get; set; } public string PaymentMethod { get; set; }
[JsonConverter(typeof(TimeSpanJsonConverter.Seconds))] [JsonConverter(typeof(TimeSpanJsonConverter.Seconds))]
public TimeSpan IntervalSeconds { get; set; } public TimeSpan IntervalSeconds { get; set; }
} }

View File

@ -9,10 +9,10 @@ namespace BTCPayServer.Client.Models
{ {
[JsonProperty("onchain")] [JsonProperty("onchain")]
public OnchainBalanceData OnchainBalance { get; set; } public OnchainBalanceData OnchainBalance { get; set; }
[JsonProperty("offchain")] [JsonProperty("offchain")]
public OffchainBalanceData OffchainBalance { get; set; } public OffchainBalanceData OffchainBalance { get; set; }
public LightningNodeBalanceData() public LightningNodeBalanceData()
{ {
} }
@ -31,7 +31,7 @@ namespace BTCPayServer.Client.Models
[JsonConverter(typeof(JsonConverters.MoneyJsonConverter))] [JsonConverter(typeof(JsonConverters.MoneyJsonConverter))]
public Money Unconfirmed { get; set; } public Money Unconfirmed { get; set; }
[JsonConverter(typeof(JsonConverters.MoneyJsonConverter))] [JsonConverter(typeof(JsonConverters.MoneyJsonConverter))]
public Money Reserved { get; set; } public Money Reserved { get; set; }
} }
@ -40,13 +40,13 @@ namespace BTCPayServer.Client.Models
{ {
[JsonConverter(typeof(LightMoneyJsonConverter))] [JsonConverter(typeof(LightMoneyJsonConverter))]
public LightMoney Opening { get; set; } public LightMoney Opening { get; set; }
[JsonConverter(typeof(LightMoneyJsonConverter))] [JsonConverter(typeof(LightMoneyJsonConverter))]
public LightMoney Local { get; set; } public LightMoney Local { get; set; }
[JsonConverter(typeof(LightMoneyJsonConverter))] [JsonConverter(typeof(LightMoneyJsonConverter))]
public LightMoney Remote { get; set; } public LightMoney Remote { get; set; }
[JsonConverter(typeof(LightMoneyJsonConverter))] [JsonConverter(typeof(LightMoneyJsonConverter))]
public LightMoney Closing { get; set; } public LightMoney Closing { get; set; }
} }

View File

@ -17,12 +17,12 @@ namespace BTCPayServer.Client.Models
[JsonProperty("BOLT11")] [JsonProperty("BOLT11")]
public string BOLT11 { get; set; } public string BOLT11 { get; set; }
public string Preimage { get; set; } public string Preimage { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))] [JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset? CreatedAt { get; set; } public DateTimeOffset? CreatedAt { get; set; }
[JsonConverter(typeof(LightMoneyJsonConverter))] [JsonConverter(typeof(LightMoneyJsonConverter))]
public LightMoney TotalAmount { get; set; } public LightMoney TotalAmount { get; set; }

View File

@ -1,4 +1,4 @@
namespace BTCPayServer.Client; namespace BTCPayServer.Client;
public class LockUserRequest public class LockUserRequest
{ {

View File

@ -16,7 +16,7 @@ public class MarketTradeResponseData
public string TradeId { get; } public string TradeId { get; }
public string AccountId { get; } public string AccountId { get; }
public string CustodianCode { get; } public string CustodianCode { get; }
public MarketTradeResponseData(string fromAsset, string toAsset, List<LedgerEntryData> ledgerEntries, string tradeId, string accountId, string custodianCode) public MarketTradeResponseData(string fromAsset, string toAsset, List<LedgerEntryData> ledgerEntries, string tradeId, string accountId, string custodianCode)

View File

@ -1,4 +1,4 @@
using System; using System;
using BTCPayServer.Client.JsonConverters; using BTCPayServer.Client.JsonConverters;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -7,9 +7,9 @@ namespace BTCPayServer.Client.Models;
public class OnChainAutomatedPayoutSettings public class OnChainAutomatedPayoutSettings
{ {
public string PaymentMethod { get; set; } public string PaymentMethod { get; set; }
[JsonConverter(typeof(TimeSpanJsonConverter.Seconds))] [JsonConverter(typeof(TimeSpanJsonConverter.Seconds))]
public TimeSpan IntervalSeconds { get; set; } public TimeSpan IntervalSeconds { get; set; }
public int? FeeBlockTarget { get; set; } public int? FeeBlockTarget { get; set; }
} }

View File

@ -11,13 +11,13 @@ namespace BTCPayServer.Client.Models
{ {
[JsonProperty("BOLT11")] [JsonProperty("BOLT11")]
public string BOLT11 { get; set; } public string BOLT11 { get; set; }
[JsonProperty(ItemConverterType = typeof(NumericStringJsonConverter))] [JsonProperty(ItemConverterType = typeof(NumericStringJsonConverter))]
public float? MaxFeePercent { get; set; } public float? MaxFeePercent { get; set; }
[JsonConverter(typeof(MoneyJsonConverter))] [JsonConverter(typeof(MoneyJsonConverter))]
public Money MaxFeeFlat { get; set; } public Money MaxFeeFlat { get; set; }
[JsonConverter(typeof(LightMoneyJsonConverter))] [JsonConverter(typeof(LightMoneyJsonConverter))]
public LightMoney Amount { get; set; } public LightMoney Amount { get; set; }

View File

@ -12,7 +12,7 @@ namespace BTCPayServer.Client.Models
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))] [JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset Created { get; set; } public DateTimeOffset Created { get; set; }
} }
public class PointOfSaleAppData : AppDataBase public class PointOfSaleAppData : AppDataBase
{ {
// We can add POS specific things here later // We can add POS specific things here later

View File

@ -1,7 +1,7 @@
namespace BTCPayServer.Client.Models; namespace BTCPayServer.Client.Models;
public class RateSource public class RateSource
{ {
public string Id { get; set; } public string Id { get; set; }
public string Name { get; set; } public string Name { get; set; }
} }

View File

@ -60,7 +60,7 @@ namespace BTCPayServer.Client.Models
public NetworkFeeMode NetworkFeeMode { get; set; } = NetworkFeeMode.Never; public NetworkFeeMode NetworkFeeMode { get; set; } = NetworkFeeMode.Never;
public bool PayJoinEnabled { get; set; } public bool PayJoinEnabled { get; set; }
public InvoiceData.ReceiptOptions Receipt { get; set; } public InvoiceData.ReceiptOptions Receipt { get; set; }

View File

@ -7,7 +7,7 @@ namespace BTCPayServer.Client.Models
/// </summary> /// </summary>
public string Id { get; set; } public string Id { get; set; }
} }
public class StoreUserData public class StoreUserData
{ {
/// <summary> /// <summary>

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace BTCPayServer.Client.Models; namespace BTCPayServer.Client.Models;
@ -7,4 +7,4 @@ public class StoreRatePreviewResult
public string CurrencyPair { get; set; } public string CurrencyPair { get; set; }
public decimal? Rate { get; set; } public decimal? Rate { get; set; }
public List<string> Errors { get; set; } public List<string> Errors { get; set; }
} }

View File

@ -1,7 +1,7 @@
namespace BTCPayServer.Client.Models; namespace BTCPayServer.Client.Models;
public class StoreRateResult public class StoreRateResult
{ {
public string CurrencyPair { get; set; } public string CurrencyPair { get; set; }
public decimal Rate { get; set; } public decimal Rate { get; set; }
} }

View File

@ -23,7 +23,7 @@ namespace BTCPayServer
internal Task ProcessTask; internal Task ProcessTask;
public async Task Process(CancellationToken cancellationToken) public async Task Process(CancellationToken cancellationToken)
{ {
retry: retry:
while (Chan.Reader.TryRead(out var item)) while (Chan.Reader.TryRead(out var item))
{ {
await item(cancellationToken); await item(cancellationToken);
@ -52,7 +52,7 @@ namespace BTCPayServer
{ {
lock (_Queues) lock (_Queues)
{ {
retry: retry:
if (stopped) if (stopped)
return; return;
Cleanup(); Cleanup();

View File

@ -67,7 +67,7 @@ namespace BTCPayServer.Data
public DbSet<WalletTransactionData> WalletTransactions { get; set; } public DbSet<WalletTransactionData> WalletTransactions { get; set; }
public DbSet<WebhookDeliveryData> WebhookDeliveries { get; set; } public DbSet<WebhookDeliveryData> WebhookDeliveries { get; set; }
public DbSet<WebhookData> Webhooks { get; set; } public DbSet<WebhookData> Webhooks { get; set; }
public DbSet<LightningAddressData> LightningAddresses{ get; set; } public DbSet<LightningAddressData> LightningAddresses { get; set; }
public DbSet<PayoutProcessorData> PayoutProcessors { get; set; } public DbSet<PayoutProcessorData> PayoutProcessors { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)

View File

@ -14,28 +14,28 @@ public class CustodianAccountData
[Required] [Required]
[MaxLength(50)] [MaxLength(50)]
public string StoreId { get; set; } public string StoreId { get; set; }
[Required] [Required]
[MaxLength(50)] [MaxLength(50)]
public string CustodianCode { get; set; } public string CustodianCode { get; set; }
[Required] [Required]
[MaxLength(50)] [MaxLength(50)]
public string Name { get; set; } public string Name { get; set; }
[JsonIgnore] [JsonIgnore]
public byte[] Blob { get; set; } public byte[] Blob { get; set; }
[JsonIgnore] [JsonIgnore]
public StoreData StoreData { get; set; } public StoreData StoreData { get; set; }
internal static void OnModelCreating(ModelBuilder builder) internal static void OnModelCreating(ModelBuilder builder)
{ {
builder.Entity<CustodianAccountData>() builder.Entity<CustodianAccountData>()
.HasOne(o => o.StoreData) .HasOne(o => o.StoreData)
.WithMany(i => i.CustodianAccounts) .WithMany(i => i.CustodianAccounts)
.HasForeignKey(i => i.StoreId).OnDelete(DeleteBehavior.Cascade); .HasForeignKey(i => i.StoreId).OnDelete(DeleteBehavior.Cascade);
builder.Entity<APIKeyData>() builder.Entity<APIKeyData>()
.HasIndex(o => o.StoreId); .HasIndex(o => o.StoreId);
} }

View File

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

View File

@ -1,4 +1,4 @@
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace BTCPayServer.Data.Data; namespace BTCPayServer.Data.Data;
@ -11,9 +11,9 @@ public class PayoutProcessorData
public StoreData Store { get; set; } public StoreData Store { get; set; }
public string PaymentMethod { get; set; } public string PaymentMethod { get; set; }
public string Processor { get; set; } public string Processor { get; set; }
public byte[] Blob { get; set; } public byte[] Blob { get; set; }
internal static void OnModelCreating(ModelBuilder builder) internal static void OnModelCreating(ModelBuilder builder)
{ {

View File

@ -11,7 +11,7 @@ public class StoreSettingData
public string Value { get; set; } public string Value { get; set; }
public StoreData Store { get; set; } public StoreData Store { get; set; }
public static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade) public static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
{ {
builder.Entity<StoreSettingData>().HasKey(data => new { data.StoreId, data.Name }); builder.Entity<StoreSettingData>().HasKey(data => new { data.StoreId, data.Name });

View File

@ -2,5 +2,5 @@ namespace BTCPayServer.Data;
public class TradeResultData public class TradeResultData
{ {
} }

View File

@ -1,8 +1,8 @@
using System; using System;
using Microsoft.EntityFrameworkCore.Migrations;
using BTCPayServer.Data; using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;

View File

@ -57,8 +57,8 @@ namespace BTCPayServer.PluginPacker
var sha256sums = new StringBuilder(); var sha256sums = new StringBuilder();
sha256sums.AppendLine( sha256sums.AppendLine(
$"{Encoders.Hex.EncodeData(Hashes.SHA256(Encoding.UTF8.GetBytes(json)))} {name}.btcpay.json"); $"{Encoders.Hex.EncodeData(Hashes.SHA256(Encoding.UTF8.GetBytes(json)))} {name}.btcpay.json");
sha256sums.AppendLine( sha256sums.AppendLine(
$"{Encoders.Hex.EncodeData(Hashes.SHA256(await File.ReadAllBytesAsync(outputFile + ".btcpay")))} {name}.btcpay"); $"{Encoders.Hex.EncodeData(Hashes.SHA256(await File.ReadAllBytesAsync(outputFile + ".btcpay")))} {name}.btcpay");
@ -68,7 +68,7 @@ namespace BTCPayServer.PluginPacker
File.Delete(sha256dirs); File.Delete(sha256dirs);
} }
await File.WriteAllTextAsync(sha256dirs, sha256sums.ToString()); await File.WriteAllTextAsync(sha256dirs, sha256sums.ToString());
// try Windows executable first, fall back to macOS/Linux PowerShell // try Windows executable first, fall back to macOS/Linux PowerShell
try try
{ {
@ -86,7 +86,7 @@ namespace BTCPayServer.PluginPacker
$"Attempted to sign hashes with gpg but maybe powershell is not installed?\n{ex.Message}"); $"Attempted to sign hashes with gpg but maybe powershell is not installed?\n{ex.Message}");
} }
} }
Console.WriteLine($"Created {outputFile}.btcpay at {directory}"); Console.WriteLine($"Created {outputFile}.btcpay at {directory}");
} }

View File

@ -27,8 +27,8 @@ namespace BTCPayServer.Rating
Url = url; Url = url;
Source = source; Source = source;
} }
public string DisplayName => public string DisplayName =>
Source switch Source switch
{ {
RateSource.Direct => Name, RateSource.Direct => Name,

View File

@ -1,4 +1,4 @@
using System.Net.Http; using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Rating; using BTCPayServer.Rating;

View File

@ -23,7 +23,7 @@ namespace BTCPayServer.Services.Rates
{ {
await new SynchronizationContextRemover(); await new SynchronizationContextRemover();
var exchangeAPI = (T) await ExchangeAPI.GetExchangeAPIAsync<T>(); var exchangeAPI = (T)await ExchangeAPI.GetExchangeAPIAsync<T>();
exchangeAPI.RequestMaker = new HttpClientRequestMaker(exchangeAPI, _httpClient, cancellationToken); exchangeAPI.RequestMaker = new HttpClientRequestMaker(exchangeAPI, _httpClient, cancellationToken);
var rates = await exchangeAPI.GetTickersAsync(); var rates = await exchangeAPI.GetTickersAsync();

View File

@ -177,11 +177,11 @@ namespace BTCPayServer.Services.Rates
var result = JsonConvert.DeserializeObject<T>(stringResult); var result = JsonConvert.DeserializeObject<T>(stringResult);
if (result is JToken json) if (result is JToken json)
{ {
if (!(json is JArray) && json["result"] is JObject {Count: > 0} pairResult) if (!(json is JArray) && json["result"] is JObject { Count: > 0 } pairResult)
{ {
return (T)(object)(pairResult); return (T)(object)(pairResult);
} }
if (!(json is JArray) && json["error"] is JArray error && error.Count != 0) if (!(json is JArray) && json["error"] is JArray error && error.Count != 0)
{ {
throw new APIException(string.Join("\n", throw new APIException(string.Join("\n",

View File

@ -7,8 +7,8 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Rating; using BTCPayServer.Rating;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Services.Rates namespace BTCPayServer.Services.Rates
{ {

View File

@ -58,14 +58,14 @@ namespace BTCPayServer.Tests
s.GoToProfile(ManageNavPages.APIKeys); s.GoToProfile(ManageNavPages.APIKeys);
s.Driver.FindElement(By.Id("AddApiKey")).Click(); s.Driver.FindElement(By.Id("AddApiKey")).Click();
Assert.Contains("btcpay.server.canmodifyserversettings", s.Driver.PageSource); Assert.Contains("btcpay.server.canmodifyserversettings", s.Driver.PageSource);
//server management should show now //server management should show now
s.Driver.SetCheckbox(By.Id("btcpay.server.canmodifyserversettings"), true); s.Driver.SetCheckbox(By.Id("btcpay.server.canmodifyserversettings"), true);
s.Driver.SetCheckbox(By.Id("btcpay.store.canmodifystoresettings"), true); s.Driver.SetCheckbox(By.Id("btcpay.store.canmodifystoresettings"), true);
s.Driver.SetCheckbox(By.Id("btcpay.user.canviewprofile"), true); s.Driver.SetCheckbox(By.Id("btcpay.user.canviewprofile"), true);
s.Driver.FindElement(By.Id("Generate")).Click(); s.Driver.FindElement(By.Id("Generate")).Click();
var superApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text; var superApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
//this api key has access to everything //this api key has access to everything
await TestApiAgainstAccessToken(superApiKey, tester, user, Policies.CanModifyServerSettings, Policies.CanModifyStoreSettings, Policies.CanViewProfile); await TestApiAgainstAccessToken(superApiKey, tester, user, Policies.CanModifyServerSettings, Policies.CanModifyStoreSettings, Policies.CanViewProfile);
@ -75,7 +75,7 @@ namespace BTCPayServer.Tests
var serverOnlyApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text; var serverOnlyApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
await TestApiAgainstAccessToken(serverOnlyApiKey, tester, user, await TestApiAgainstAccessToken(serverOnlyApiKey, tester, user,
Policies.CanModifyServerSettings); Policies.CanModifyServerSettings);
s.Driver.FindElement(By.Id("AddApiKey")).Click(); s.Driver.FindElement(By.Id("AddApiKey")).Click();
s.Driver.SetCheckbox(By.Id("btcpay.store.canmodifystoresettings"), true); s.Driver.SetCheckbox(By.Id("btcpay.store.canmodifystoresettings"), true);
s.Driver.FindElement(By.Id("Generate")).Click(); s.Driver.FindElement(By.Id("Generate")).Click();
@ -108,7 +108,7 @@ namespace BTCPayServer.Tests
await TestApiAgainstAccessToken<bool>("incorrect key", $"{TestApiPath}/me/id", await TestApiAgainstAccessToken<bool>("incorrect key", $"{TestApiPath}/me/id",
tester.PayTester.HttpClient); tester.PayTester.HttpClient);
}); });
TestLogs.LogInformation("Checking authorize screen"); TestLogs.LogInformation("Checking authorize screen");
//let's test the authorized screen now //let's test the authorized screen now
@ -124,35 +124,35 @@ namespace BTCPayServer.Tests
var callbackUrl = s.ServerUri + "postredirect-callback-test"; var callbackUrl = s.ServerUri + "postredirect-callback-test";
var authUrl = BTCPayServerClient.GenerateAuthorizeUri(s.ServerUri, var authUrl = BTCPayServerClient.GenerateAuthorizeUri(s.ServerUri,
new[] { Policies.CanModifyServerSettings }, applicationDetails: (appidentifier, new Uri(callbackUrl))).ToString(); new[] { Policies.CanModifyServerSettings }, applicationDetails: (appidentifier, new Uri(callbackUrl))).ToString();
// No upfront store selection with only server settings // No upfront store selection with only server settings
s.GoToUrl(authUrl); s.GoToUrl(authUrl);
Assert.Contains(appidentifier, s.Driver.PageSource); Assert.Contains(appidentifier, s.Driver.PageSource);
Assert.True(s.Driver.ElementDoesNotExist(By.CssSelector("select#StoreId"))); Assert.True(s.Driver.ElementDoesNotExist(By.CssSelector("select#StoreId")));
// No upfront store selection with selectiveStores being false // No upfront store selection with selectiveStores being false
authUrl = BTCPayServerClient.GenerateAuthorizeUri(s.ServerUri, authUrl = BTCPayServerClient.GenerateAuthorizeUri(s.ServerUri,
new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, selectiveStores: false, applicationDetails: (appidentifier, new Uri(callbackUrl))).ToString(); new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, selectiveStores: false, applicationDetails: (appidentifier, new Uri(callbackUrl))).ToString();
s.GoToUrl(authUrl); s.GoToUrl(authUrl);
Assert.True(s.Driver.ElementDoesNotExist(By.CssSelector("select#StoreId"))); Assert.True(s.Driver.ElementDoesNotExist(By.CssSelector("select#StoreId")));
// Now with store settings // Now with store settings
authUrl = BTCPayServerClient.GenerateAuthorizeUri(s.ServerUri, authUrl = BTCPayServerClient.GenerateAuthorizeUri(s.ServerUri,
new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, selectiveStores: true, applicationDetails: (appidentifier, new Uri(callbackUrl))).ToString(); new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, selectiveStores: true, applicationDetails: (appidentifier, new Uri(callbackUrl))).ToString();
s.GoToUrl(authUrl); s.GoToUrl(authUrl);
Assert.Contains(appidentifier, s.Driver.PageSource); Assert.Contains(appidentifier, s.Driver.PageSource);
// Select a store // Select a store
var select = new SelectElement(s.Driver.FindElement(By.Id("StoreId"))); var select = new SelectElement(s.Driver.FindElement(By.Id("StoreId")));
select.SelectByIndex(0); select.SelectByIndex(0);
s.Driver.FindElement(By.Id("continue")).Click(); s.Driver.FindElement(By.Id("continue")).Click();
Assert.Equal("hidden", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("type").ToLowerInvariant()); Assert.Equal("hidden", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("type").ToLowerInvariant());
Assert.Equal("true", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("value").ToLowerInvariant()); Assert.Equal("true", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("value").ToLowerInvariant());
Assert.Equal("hidden", s.Driver.FindElement(By.Id("btcpay.server.canmodifyserversettings")).GetAttribute("type").ToLowerInvariant()); Assert.Equal("hidden", s.Driver.FindElement(By.Id("btcpay.server.canmodifyserversettings")).GetAttribute("type").ToLowerInvariant());
Assert.Equal("true", s.Driver.FindElement(By.Id("btcpay.server.canmodifyserversettings")).GetAttribute("value").ToLowerInvariant()); Assert.Equal("true", s.Driver.FindElement(By.Id("btcpay.server.canmodifyserversettings")).GetAttribute("value").ToLowerInvariant());
Assert.DoesNotContain("change-store-mode", s.Driver.PageSource); Assert.DoesNotContain("change-store-mode", s.Driver.PageSource);
s.Driver.WaitForAndClick(By.Id("consent-yes")); s.Driver.WaitForAndClick(By.Id("consent-yes"));
Assert.Equal(callbackUrl, s.Driver.Url); Assert.Equal(callbackUrl, s.Driver.Url);
@ -192,12 +192,12 @@ namespace BTCPayServer.Tests
//if it's the same, go to the confirm page //if it's the same, go to the confirm page
s.GoToUrl(authUrl); s.GoToUrl(authUrl);
// Select the same store // Select the same store
select = new SelectElement(s.Driver.FindElement(By.Id("StoreId"))); select = new SelectElement(s.Driver.FindElement(By.Id("StoreId")));
select.SelectByIndex(0); select.SelectByIndex(0);
s.Driver.FindElement(By.Id("continue")).Click(); s.Driver.FindElement(By.Id("continue")).Click();
Assert.Contains("previously generated the API Key", s.Driver.PageSource); Assert.Contains("previously generated the API Key", s.Driver.PageSource);
s.Driver.WaitForAndClick(By.Id("continue")); s.Driver.WaitForAndClick(By.Id("continue"));
Assert.Equal(callbackUrl, s.Driver.Url); Assert.Equal(callbackUrl, s.Driver.Url);
@ -207,12 +207,12 @@ namespace BTCPayServer.Tests
new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, false, true, (appidentifier, new Uri("https://international.local/callback"))).ToString(); new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, false, true, (appidentifier, new Uri("https://international.local/callback"))).ToString();
s.GoToUrl(authUrl); s.GoToUrl(authUrl);
// Select the same store // Select the same store
select = new SelectElement(s.Driver.FindElement(By.Id("StoreId"))); select = new SelectElement(s.Driver.FindElement(By.Id("StoreId")));
select.SelectByIndex(0); select.SelectByIndex(0);
s.Driver.FindElement(By.Id("continue")).Click(); s.Driver.FindElement(By.Id("continue")).Click();
Assert.DoesNotContain("previously generated the API Key", s.Driver.PageSource); Assert.DoesNotContain("previously generated the API Key", s.Driver.PageSource);
Assert.False(s.Driver.Url.StartsWith("https://international.com/callback")); Assert.False(s.Driver.Url.StartsWith("https://international.com/callback"));
@ -230,11 +230,11 @@ namespace BTCPayServer.Tests
TestLogs.LogInformation("Generating API key"); TestLogs.LogInformation("Generating API key");
s.Driver.WaitForAndClick(By.Id("Generate")); s.Driver.WaitForAndClick(By.Id("Generate"));
var allAPIKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text; var allAPIKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
TestLogs.LogInformation($"Checking API key permissions: {allAPIKey}"); TestLogs.LogInformation($"Checking API key permissions: {allAPIKey}");
var apikeydata = await TestApiAgainstAccessToken<ApiKeyData>(allAPIKey, "api/v1/api-keys/current", tester.PayTester.HttpClient); var apikeydata = await TestApiAgainstAccessToken<ApiKeyData>(allAPIKey, "api/v1/api-keys/current", tester.PayTester.HttpClient);
Assert.Equal(checkedPermissionCount, apikeydata.Permissions.Length); Assert.Equal(checkedPermissionCount, apikeydata.Permissions.Length);
TestLogs.LogInformation("Checking empty permissions"); TestLogs.LogInformation("Checking empty permissions");
authUrl = BTCPayServerClient.GenerateAuthorizeUri(s.ServerUri, Array.Empty<string>(), false, true).ToString(); authUrl = BTCPayServerClient.GenerateAuthorizeUri(s.ServerUri, Array.Empty<string>(), false, true).ToString();
s.GoToUrl(authUrl); s.GoToUrl(authUrl);
@ -279,10 +279,10 @@ namespace BTCPayServer.Tests
var canModifyServer = Permission.Create(Policies.CanModifyServerSettings); var canModifyServer = Permission.Create(Policies.CanModifyServerSettings);
var unrestricted = Permission.Create(Policies.Unrestricted); var unrestricted = Permission.Create(Policies.Unrestricted);
var selectiveStorePermissions = permissions.Where(p => p.Scope != null && p.Policy == Policies.CanModifyStoreSettings); var selectiveStorePermissions = permissions.Where(p => p.Scope != null && p.Policy == Policies.CanModifyStoreSettings);
TestLogs.LogInformation("Testing can edit store for first user"); TestLogs.LogInformation("Testing can edit store for first user");
IEnumerable<Permission> storePermissions = selectiveStorePermissions as Permission[] ?? selectiveStorePermissions.ToArray(); IEnumerable<Permission> storePermissions = selectiveStorePermissions as Permission[] ?? selectiveStorePermissions.ToArray();
if (permissions.Contains(canModifyAllStores) || storePermissions.Any()) if (permissions.Contains(canModifyAllStores) || storePermissions.Any())
{ {
var resultStores = var resultStores =

View File

@ -16,11 +16,11 @@ namespace BTCPayServer.Tests
public class CheckoutV2Tests : UnitTestBase public class CheckoutV2Tests : UnitTestBase
{ {
private const int TestTimeout = TestUtils.TestTimeout; private const int TestTimeout = TestUtils.TestTimeout;
public CheckoutV2Tests(ITestOutputHelper helper) : base(helper) public CheckoutV2Tests(ITestOutputHelper helper) : base(helper)
{ {
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = TestTimeout)]
[Trait("Lightning", "Lightning")] [Trait("Lightning", "Lightning")]
public async Task CanConfigureCheckout() public async Task CanConfigureCheckout()
@ -41,20 +41,20 @@ 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);
// Default payment method // Default payment method
var invoiceId = s.CreateInvoice(defaultPaymentMethod: "BTC_LightningLike"); var invoiceId = s.CreateInvoice(defaultPaymentMethod: "BTC_LightningLike");
s.GoToInvoiceCheckout(invoiceId); s.GoToInvoiceCheckout(invoiceId);
// Ensure we are seeing Checkout v2 // Ensure we are seeing Checkout v2
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2")); s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
Assert.Equal(2, s.Driver.FindElements(By.CssSelector(".payment-method")).Count); Assert.Equal(2, s.Driver.FindElements(By.CssSelector(".payment-method")).Count);
Assert.Contains("Lightning", s.Driver.WaitForElement(By.CssSelector(".payment-method.active")).Text); Assert.Contains("Lightning", s.Driver.WaitForElement(By.CssSelector(".payment-method.active")).Text);
Assert.DoesNotContain("LNURL", s.Driver.PageSource); Assert.DoesNotContain("LNURL", s.Driver.PageSource);
var payUrl = s.Driver.FindElement(By.CssSelector(".btn-primary")).GetAttribute("href"); var payUrl = s.Driver.FindElement(By.CssSelector(".btn-primary")).GetAttribute("href");
Assert.StartsWith("lightning:", payUrl); Assert.StartsWith("lightning:", payUrl);
// Lightning amount in Sats // Lightning amount in Sats
Assert.Contains("BTC", s.Driver.FindElement(By.Id("AmountDue")).Text); Assert.Contains("BTC", s.Driver.FindElement(By.Id("AmountDue")).Text);
s.GoToHome(); s.GoToHome();
@ -64,7 +64,7 @@ namespace BTCPayServer.Tests
Assert.Contains("BTC Lightning settings successfully updated", s.FindAlertMessage().Text); Assert.Contains("BTC Lightning settings successfully updated", s.FindAlertMessage().Text);
s.GoToInvoiceCheckout(invoiceId); s.GoToInvoiceCheckout(invoiceId);
Assert.Contains("Sats", s.Driver.FindElement(By.Id("AmountDue")).Text); Assert.Contains("Sats", s.Driver.FindElement(By.Id("AmountDue")).Text);
// Expire // Expire
var expirySeconds = s.Driver.FindElement(By.Id("ExpirySeconds")); var expirySeconds = s.Driver.FindElement(By.Id("ExpirySeconds"));
expirySeconds.Clear(); expirySeconds.Clear();
@ -82,12 +82,12 @@ namespace BTCPayServer.Tests
}); });
Assert.True(s.Driver.ElementDoesNotExist(By.Id("ReceiptLink"))); Assert.True(s.Driver.ElementDoesNotExist(By.Id("ReceiptLink")));
Assert.Equal(storeUrl, s.Driver.FindElement(By.Id("StoreLink")).GetAttribute("href")); Assert.Equal(storeUrl, s.Driver.FindElement(By.Id("StoreLink")).GetAttribute("href"));
// Test payment // Test payment
s.GoToHome(); s.GoToHome();
invoiceId = s.CreateInvoice(); invoiceId = s.CreateInvoice();
s.GoToInvoiceCheckout(invoiceId); s.GoToInvoiceCheckout(invoiceId);
// Details // Details
s.Driver.ToggleCollapse("PaymentDetails"); s.Driver.ToggleCollapse("PaymentDetails");
var details = s.Driver.FindElement(By.CssSelector(".payment-details")); var details = s.Driver.FindElement(By.CssSelector(".payment-details"));
@ -96,7 +96,7 @@ namespace BTCPayServer.Tests
Assert.Contains("Exchange Rate", details.Text); Assert.Contains("Exchange Rate", details.Text);
Assert.Contains("Amount Due", details.Text); Assert.Contains("Amount Due", details.Text);
Assert.Contains("Recommended Fee", details.Text); Assert.Contains("Recommended Fee", details.Text);
// Pay partial amount // Pay partial amount
await Task.Delay(200); await Task.Delay(200);
var address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-destination"); var address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-destination");
@ -104,7 +104,7 @@ namespace BTCPayServer.Tests
await s.Server.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(address, Network.RegTest), await s.Server.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(address, Network.RegTest),
Money.Parse(amountFraction)); Money.Parse(amountFraction));
await s.Server.ExplorerNode.GenerateAsync(1); await s.Server.ExplorerNode.GenerateAsync(1);
// Fake Pay // Fake Pay
s.Driver.FindElement(By.Id("FakePayAmount")).FillIn(amountFraction); s.Driver.FindElement(By.Id("FakePayAmount")).FillIn(amountFraction);
s.Driver.FindElement(By.Id("FakePay")).Click(); s.Driver.FindElement(By.Id("FakePay")).Click();
@ -125,7 +125,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);
}); });
// Pay full amount // Pay full amount
var amountDue = s.Driver.FindElement(By.Id("AmountDue")).GetAttribute("data-amount-due"); var amountDue = s.Driver.FindElement(By.Id("AmountDue")).GetAttribute("data-amount-due");
s.Driver.FindElement(By.Id("FakePayAmount")).FillIn(amountDue); s.Driver.FindElement(By.Id("FakePayAmount")).FillIn(amountDue);
@ -139,21 +139,21 @@ namespace BTCPayServer.Tests
}); });
s.Driver.FindElement(By.Id("ReceiptLink")); s.Driver.FindElement(By.Id("ReceiptLink"));
Assert.Equal(storeUrl, s.Driver.FindElement(By.Id("StoreLink")).GetAttribute("href")); Assert.Equal(storeUrl, s.Driver.FindElement(By.Id("StoreLink")).GetAttribute("href"));
// BIP21 // BIP21
s.GoToHome(); s.GoToHome();
s.GoToStore(StoreNavPages.CheckoutAppearance); s.GoToStore(StoreNavPages.CheckoutAppearance);
s.Driver.SetCheckbox(By.Id("OnChainWithLnInvoiceFallback"), true); s.Driver.SetCheckbox(By.Id("OnChainWithLnInvoiceFallback"), true);
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);
invoiceId = s.CreateInvoice(); invoiceId = s.CreateInvoice();
s.GoToInvoiceCheckout(invoiceId); s.GoToInvoiceCheckout(invoiceId);
Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method"))); Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method")));
payUrl = s.Driver.FindElement(By.CssSelector(".btn-primary")).GetAttribute("href"); payUrl = s.Driver.FindElement(By.CssSelector(".btn-primary")).GetAttribute("href");
Assert.StartsWith("bitcoin:", payUrl); Assert.StartsWith("bitcoin:", payUrl);
Assert.Contains("&LIGHTNING=", payUrl); Assert.Contains("&LIGHTNING=", payUrl);
// 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");
@ -162,7 +162,7 @@ namespace BTCPayServer.Tests
payUrl = s.Driver.FindElement(By.CssSelector(".btn-primary")).GetAttribute("href"); payUrl = s.Driver.FindElement(By.CssSelector(".btn-primary")).GetAttribute("href");
Assert.StartsWith("bitcoin:", payUrl); Assert.StartsWith("bitcoin:", payUrl);
Assert.Contains("&LIGHTNING=", payUrl); Assert.Contains("&LIGHTNING=", payUrl);
// BIP21 with topup invoice (which is only available with Bitcoin onchain) // BIP21 with topup invoice (which is only available with Bitcoin onchain)
s.GoToHome(); s.GoToHome();
invoiceId = s.CreateInvoice(amount: null); invoiceId = s.CreateInvoice(amount: null);
@ -171,7 +171,7 @@ namespace BTCPayServer.Tests
payUrl = s.Driver.FindElement(By.CssSelector(".btn-primary")).GetAttribute("href"); payUrl = s.Driver.FindElement(By.CssSelector(".btn-primary")).GetAttribute("href");
Assert.StartsWith("bitcoin:", payUrl); Assert.StartsWith("bitcoin:", payUrl);
Assert.DoesNotContain("&LIGHTNING=", payUrl); Assert.DoesNotContain("&LIGHTNING=", payUrl);
// Expiry message should not show amount for topup invoice // Expiry message should not show amount for topup invoice
expirySeconds = s.Driver.FindElement(By.Id("ExpirySeconds")); expirySeconds = s.Driver.FindElement(By.Id("ExpirySeconds"));
expirySeconds.Clear(); expirySeconds.Clear();
@ -199,12 +199,12 @@ namespace BTCPayServer.Tests
s.Driver.Navigate() s.Driver.Navigate()
.GoToUrl(new Uri(s.ServerUri, $"tests/index.html?invoice={invoiceId}")); .GoToUrl(new Uri(s.ServerUri, $"tests/index.html?invoice={invoiceId}"));
s.Driver.WaitUntilAvailable(By.Name("btcpay")); s.Driver.WaitUntilAvailable(By.Name("btcpay"));
var frameElement = s.Driver.FindElement(By.Name("btcpay")); var frameElement = s.Driver.FindElement(By.Name("btcpay"));
Assert.True(frameElement.Displayed); Assert.True(frameElement.Displayed);
var iframe = s.Driver.SwitchTo().Frame(frameElement); var iframe = s.Driver.SwitchTo().Frame(frameElement);
iframe.WaitUntilAvailable(By.Id("Checkout-v2")); iframe.WaitUntilAvailable(By.Id("Checkout-v2"));
await s.Server.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(invoice await s.Server.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(invoice
.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike)) .GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike))
.GetPaymentMethodDetails().GetPaymentDestination(), Network.RegTest), .GetPaymentMethodDetails().GetPaymentDestination(), Network.RegTest),

View File

@ -138,7 +138,7 @@ retry:
el.Clear(); el.Clear();
el.SendKeys(text); el.SendKeys(text);
} }
public static void ScrollTo(this IWebDriver driver, IWebElement element) public static void ScrollTo(this IWebDriver driver, IWebElement element)
{ {
driver.ExecuteJavaScript("arguments[0].scrollIntoView();", element); driver.ExecuteJavaScript("arguments[0].scrollIntoView();", element);
@ -148,7 +148,7 @@ retry:
{ {
ScrollTo(driver, driver.FindElement(selector)); ScrollTo(driver, driver.FindElement(selector));
} }
public static void WaitUntilAvailable(this IWebDriver driver, By selector, TimeSpan? waitTime = null) public static void WaitUntilAvailable(this IWebDriver driver, By selector, TimeSpan? waitTime = null)
{ {
// Try fast path // Try fast path
@ -165,7 +165,7 @@ retry:
wait.UntilJsIsReady(); wait.UntilJsIsReady();
int retriesLeft = 4; int retriesLeft = 4;
retry: retry:
try try
{ {
var el = driver.FindElement(selector); var el = driver.FindElement(selector);
@ -176,18 +176,19 @@ retry:
catch (NoSuchElementException) when (retriesLeft > 0) catch (NoSuchElementException) when (retriesLeft > 0)
{ {
retriesLeft--; retriesLeft--;
if (waitTime != null) Thread.Sleep(waitTime.Value); if (waitTime != null)
Thread.Sleep(waitTime.Value);
goto retry; goto retry;
} }
wait.UntilJsIsReady(); wait.UntilJsIsReady();
} }
public static void WaitForAndClick(this IWebDriver driver, By selector) public static void WaitForAndClick(this IWebDriver driver, By selector)
{ {
driver.WaitUntilAvailable(selector); driver.WaitUntilAvailable(selector);
driver.FindElement(selector).Click(); driver.FindElement(selector).Click();
} }
public static bool ElementDoesNotExist(this IWebDriver driver, By selector) public static bool ElementDoesNotExist(this IWebDriver driver, By selector)
{ {
Assert.Throws<NoSuchElementException>(() => Assert.Throws<NoSuchElementException>(() =>

View File

@ -672,7 +672,7 @@ namespace BTCPayServer.Tests
Assert.Equal(3, inner.Keys.Count); Assert.Equal(3, inner.Keys.Count);
Assert.Equal(2, inner.RequiredSignatures); Assert.Equal(2, inner.RequiredSignatures);
Assert.Equal(expected, inner.ToString()); Assert.Equal(expected, inner.ToString());
// Output Descriptor // Output Descriptor
networkProvider = new BTCPayNetworkProvider(ChainName.Mainnet); networkProvider = new BTCPayNetworkProvider(ChainName.Mainnet);
parser = new DerivationSchemeParser(networkProvider.BTC); parser = new DerivationSchemeParser(networkProvider.BTC);
@ -681,7 +681,7 @@ namespace BTCPayServer.Tests
Assert.Single(rootedKeyPath); Assert.Single(rootedKeyPath);
Assert.IsType<DirectDerivationStrategy>(strategyBase); Assert.IsType<DirectDerivationStrategy>(strategyBase);
Assert.True(((DirectDerivationStrategy)strategyBase).Segwit); Assert.True(((DirectDerivationStrategy)strategyBase).Segwit);
// Failure cases // Failure cases
Assert.Throws<FormatException>(() => { parser.Parse("xpub 661MyMwAqRbcGVBsTGeNZN6QGVHmMHLdSA4FteGsRrEriu4pnVZMZWnruFFFXkMnyoBjyHndD3Qwcfz4MPzBUxjSevweNFQx7SAYZATtcDw"); }); // invalid format because of space Assert.Throws<FormatException>(() => { parser.Parse("xpub 661MyMwAqRbcGVBsTGeNZN6QGVHmMHLdSA4FteGsRrEriu4pnVZMZWnruFFFXkMnyoBjyHndD3Qwcfz4MPzBUxjSevweNFQx7SAYZATtcDw"); }); // invalid format because of space
Assert.Throws<ParsingException>(() => { parser.ParseOutputDescriptor("invalid"); }); // invalid in general Assert.Throws<ParsingException>(() => { parser.ParseOutputDescriptor("invalid"); }); // invalid in general
@ -744,7 +744,7 @@ namespace BTCPayServer.Tests
Assert.Equal("49'/0'/0'", specter.AccountKeySettings[0].AccountKeyPath.ToString()); Assert.Equal("49'/0'/0'", specter.AccountKeySettings[0].AccountKeyPath.ToString());
Assert.Equal("Specter", specter.Label); Assert.Equal("Specter", specter.Label);
Assert.Null(error); Assert.Null(error);
// Failure case // Failure case
Assert.False(DerivationSchemeSettings.TryParseFromWalletFile( Assert.False(DerivationSchemeSettings.TryParseFromWalletFile(
"{\"keystore\": {\"ckcc_xpub\": \"tpubFailure\", \"xpub\": \"tpubFailure\", \"label\": \"Failure\"}, \"wallet_type\": \"standard\"}", "{\"keystore\": {\"ckcc_xpub\": \"tpubFailure\", \"xpub\": \"tpubFailure\", \"label\": \"Failure\"}, \"wallet_type\": \"standard\"}",
@ -1077,7 +1077,8 @@ namespace BTCPayServer.Tests
{ {
MultiProcessingQueueTest t = new MultiProcessingQueueTest(); MultiProcessingQueueTest t = new MultiProcessingQueueTest();
t.Tcs = new TaskCompletionSource(); t.Tcs = new TaskCompletionSource();
q.Enqueue(queueName, async (cancellationToken) => { q.Enqueue(queueName, async (cancellationToken) =>
{
t.Started = true; t.Started = true;
try try
{ {
@ -1774,11 +1775,11 @@ namespace BTCPayServer.Tests
{ {
foreach (var policy in Policies.AllPolicies) foreach (var policy in Policies.AllPolicies)
{ {
Assert.True( UIManageController.AddApiKeyViewModel.PermissionValueItem.PermissionDescriptions.ContainsKey(policy)); Assert.True(UIManageController.AddApiKeyViewModel.PermissionValueItem.PermissionDescriptions.ContainsKey(policy));
if (Policies.IsStorePolicy(policy)) if (Policies.IsStorePolicy(policy))
{ {
Assert.True( UIManageController.AddApiKeyViewModel.PermissionValueItem.PermissionDescriptions.ContainsKey($"{policy}:")); Assert.True(UIManageController.AddApiKeyViewModel.PermissionValueItem.PermissionDescriptions.ContainsKey($"{policy}:"));
} }
} }
} }
[Fact] [Fact]
@ -1804,8 +1805,8 @@ namespace BTCPayServer.Tests
PaymentMethod = new PaymentMethodId("BTC", PaymentTypes.BTCLike) PaymentMethod = new PaymentMethodId("BTC", PaymentTypes.BTCLike)
} }
}; };
var newBlob = new Serializer(null).ToString(blob).Replace( "paymentMethod\":\"BTC\"","paymentMethod\":\"ETH_ZYC\""); var newBlob = new Serializer(null).ToString(blob).Replace("paymentMethod\":\"BTC\"", "paymentMethod\":\"ETH_ZYC\"");
Assert.Empty(StoreDataExtensions.GetStoreBlob(new StoreData() {StoreBlob = newBlob}).PaymentMethodCriteria); Assert.Empty(StoreDataExtensions.GetStoreBlob(new StoreData() { StoreBlob = newBlob }).PaymentMethodCriteria);
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -77,7 +77,7 @@ public class MockCustodian : ICustodian, ICanDeposit, ICanTrade, ICanWithdraw
public List<AssetPairData> GetTradableAssetPairs() public List<AssetPairData> GetTradableAssetPairs()
{ {
var r = new List<AssetPairData>(); var r = new List<AssetPairData>();
r.Add(new AssetPairData("BTC", "EUR", (decimal) 0.0001)); r.Add(new AssetPairData("BTC", "EUR", (decimal)0.0001));
return r; return r;
} }

View File

@ -21,7 +21,7 @@ namespace BTCPayServer.Tests
public PSBTTests(ITestOutputHelper helper) : base(helper) public PSBTTests(ITestOutputHelper helper) : base(helper)
{ {
} }
[Fact] [Fact]
[Trait("Selenium", "Selenium")] [Trait("Selenium", "Selenium")]
public async Task CanPlayWithPSBT() public async Task CanPlayWithPSBT()

View File

@ -395,9 +395,9 @@ namespace BTCPayServer.Tests
s.GoToWallet(receiverWalletId, WalletsNavPages.Transactions); s.GoToWallet(receiverWalletId, WalletsNavPages.Transactions);
Assert.Contains(invoiceId, s.Driver.PageSource); Assert.Contains(invoiceId, s.Driver.PageSource);
Assert.Contains("payjoin", s.Driver.PageSource); Assert.Contains("payjoin", s.Driver.PageSource);
//this label does not always show since input gets used //this label does not always show since input gets used
// Assert.Contains("payjoin-exposed", s.Driver.PageSource); // Assert.Contains("payjoin-exposed", s.Driver.PageSource);
}); });
} }
} }

View File

@ -46,7 +46,7 @@ namespace BTCPayServer.Tests
// Reset this using `dotnet user-secrets remove RunSeleniumInBrowser` // Reset this using `dotnet user-secrets remove RunSeleniumInBrowser`
var chromeDriverPath = config["ChromeDriverDirectory"] ?? (Server.PayTester.InContainer ? "/usr/bin" : Directory.GetCurrentDirectory()); var chromeDriverPath = config["ChromeDriverDirectory"] ?? (Server.PayTester.InContainer ? "/usr/bin" : Directory.GetCurrentDirectory());
var options = new ChromeOptions(); var options = new ChromeOptions();
if (!runInBrowser) if (!runInBrowser)
{ {
@ -284,12 +284,12 @@ namespace BTCPayServer.Tests
{ {
AddLightningNode(null, null, true); AddLightningNode(null, null, true);
} }
public void AddLightningNode(LightningConnectionType? connectionType = null, bool test = true) public void AddLightningNode(LightningConnectionType? connectionType = null, bool test = true)
{ {
AddLightningNode(null, connectionType, test); AddLightningNode(null, connectionType, test);
} }
public void AddLightningNode(string cryptoCode = null, LightningConnectionType? connectionType = null, bool test = true) public void AddLightningNode(string cryptoCode = null, LightningConnectionType? connectionType = null, bool test = true)
{ {
cryptoCode ??= "BTC"; cryptoCode ??= "BTC";
@ -405,7 +405,7 @@ namespace BTCPayServer.Tests
{ {
GoToStore(null, storeNavPage); GoToStore(null, storeNavPage);
} }
public void GoToStore(string storeId, StoreNavPages storeNavPage = StoreNavPages.General) public void GoToStore(string storeId, StoreNavPages storeNavPage = StoreNavPages.General)
{ {
if (storeId is not null) if (storeId is not null)
@ -415,7 +415,7 @@ namespace BTCPayServer.Tests
if (WalletId != null) if (WalletId != null)
WalletId = new WalletId(storeId, WalletId.CryptoCode); WalletId = new WalletId(storeId, WalletId.CryptoCode);
} }
Driver.FindElement(By.Id("StoreNav-StoreSettings")).Click(); Driver.FindElement(By.Id("StoreNav-StoreSettings")).Click();
if (storeNavPage != StoreNavPages.General) if (storeNavPage != StoreNavPages.General)
@ -434,7 +434,7 @@ namespace BTCPayServer.Tests
} }
} }
} }
public void GoToWalletSettings(string cryptoCode = "BTC") public void GoToWalletSettings(string cryptoCode = "BTC")
{ {
Driver.FindElement(By.Id($"StoreNav-Wallet{cryptoCode}")).Click(); Driver.FindElement(By.Id($"StoreNav-Wallet{cryptoCode}")).Click();
@ -555,7 +555,7 @@ namespace BTCPayServer.Tests
for (var i = 0; i < coins; i++) for (var i = 0; i < coins; i++)
{ {
bool mined = false; bool mined = false;
retry: retry:
try try
{ {
await Server.ExplorerNode.SendToAddressAsync(address, Money.Coins(denomination)); await Server.ExplorerNode.SendToAddressAsync(address, Money.Coins(denomination));

View File

@ -63,7 +63,7 @@ namespace BTCPayServer.Tests
Assert.Contains("Starting listening NBXplorer", s.Driver.PageSource); Assert.Contains("Starting listening NBXplorer", s.Driver.PageSource);
s.Driver.Quit(); s.Driver.Quit();
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = TestTimeout)]
public async Task CanUseForms() public async Task CanUseForms()
{ {
@ -72,14 +72,14 @@ namespace BTCPayServer.Tests
s.RegisterNewUser(true); s.RegisterNewUser(true);
s.CreateNewStore(); s.CreateNewStore();
s.GenerateWallet(isHotWallet: true); s.GenerateWallet(isHotWallet: true);
// Point Of Sale // Point Of Sale
s.Driver.FindElement(By.Id("StoreNav-CreateApp")).Click(); s.Driver.FindElement(By.Id("StoreNav-CreateApp")).Click();
new SelectElement(s.Driver.FindElement(By.Id("SelectedAppType"))).SelectByValue("PointOfSale"); new SelectElement(s.Driver.FindElement(By.Id("SelectedAppType"))).SelectByValue("PointOfSale");
s.Driver.FindElement(By.Id("AppName")).SendKeys(Guid.NewGuid().ToString()); s.Driver.FindElement(By.Id("AppName")).SendKeys(Guid.NewGuid().ToString());
s.Driver.FindElement(By.Id("Create")).Click(); s.Driver.FindElement(By.Id("Create")).Click();
Assert.Contains("App successfully created", s.FindAlertMessage().Text); Assert.Contains("App successfully created", s.FindAlertMessage().Text);
new SelectElement(s.Driver.FindElement(By.Id("FormId"))).SelectByValue("Email"); new SelectElement(s.Driver.FindElement(By.Id("FormId"))).SelectByValue("Email");
s.Driver.FindElement(By.Id("SaveSettings")).Click(); s.Driver.FindElement(By.Id("SaveSettings")).Click();
Assert.Contains("App updated", s.FindAlertMessage().Text); Assert.Contains("App updated", s.FindAlertMessage().Text);
@ -89,16 +89,16 @@ namespace BTCPayServer.Tests
Assert.Equal(2, windows.Count); Assert.Equal(2, windows.Count);
s.Driver.SwitchTo().Window(windows[1]); s.Driver.SwitchTo().Window(windows[1]);
s.Driver.FindElement(By.CssSelector("button[type='submit']")).Click(); s.Driver.FindElement(By.CssSelector("button[type='submit']")).Click();
Assert.Contains("Enter your email", s.Driver.PageSource); Assert.Contains("Enter your email", s.Driver.PageSource);
s.Driver.FindElement(By.Name("buyerEmail")).SendKeys("aa@aa.com"); s.Driver.FindElement(By.Name("buyerEmail")).SendKeys("aa@aa.com");
s.Driver.FindElement(By.CssSelector("input[type='submit']")).Click(); s.Driver.FindElement(By.CssSelector("input[type='submit']")).Click();
s.PayInvoice(true); s.PayInvoice(true);
var invoiceId = s.Driver.Url[(s.Driver.Url.LastIndexOf("/", StringComparison.Ordinal) + 1)..]; var invoiceId = s.Driver.Url[(s.Driver.Url.LastIndexOf("/", StringComparison.Ordinal) + 1)..];
s.GoToInvoice(invoiceId); s.GoToInvoice(invoiceId);
Assert.Contains("aa@aa.com", s.Driver.PageSource); Assert.Contains("aa@aa.com", s.Driver.PageSource);
// Payment Request // Payment Request
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();
@ -106,19 +106,19 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id("Amount")).SendKeys("700"); s.Driver.FindElement(By.Id("Amount")).SendKeys("700");
new SelectElement(s.Driver.FindElement(By.Id("FormId"))).SelectByValue("Email"); new SelectElement(s.Driver.FindElement(By.Id("FormId"))).SelectByValue("Email");
s.Driver.FindElement(By.Id("SaveButton")).Click(); s.Driver.FindElement(By.Id("SaveButton")).Click();
s.Driver.FindElement(By.XPath("//a[starts-with(@id, 'Edit-')]")).Click(); s.Driver.FindElement(By.XPath("//a[starts-with(@id, 'Edit-')]")).Click();
var editUrl = s.Driver.Url; var editUrl = s.Driver.Url;
s.Driver.FindElement(By.Id("ViewPaymentRequest")).Click(); s.Driver.FindElement(By.Id("ViewPaymentRequest")).Click();
s.Driver.FindElement(By.CssSelector("[data-test='form-button']")).Click(); s.Driver.FindElement(By.CssSelector("[data-test='form-button']")).Click();
Assert.Contains("Enter your email", s.Driver.PageSource); Assert.Contains("Enter your email", s.Driver.PageSource);
s.Driver.FindElement(By.Name("buyerEmail")).SendKeys("aa@aa.com"); s.Driver.FindElement(By.Name("buyerEmail")).SendKeys("aa@aa.com");
s.Driver.FindElement(By.CssSelector("input[type='submit']")).Click(); s.Driver.FindElement(By.CssSelector("input[type='submit']")).Click();
s.Driver.Navigate().GoToUrl(editUrl); s.Driver.Navigate().GoToUrl(editUrl);
Assert.Contains("aa@aa.com", s.Driver.PageSource); Assert.Contains("aa@aa.com", s.Driver.PageSource);
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = TestTimeout)]
public async Task CanUseCPFP() public async Task CanUseCPFP()
{ {
@ -372,7 +372,7 @@ namespace BTCPayServer.Tests
using var s = CreateSeleniumTester(); using var s = CreateSeleniumTester();
await s.StartAsync(); await s.StartAsync();
s.RegisterNewUser(true); s.RegisterNewUser(true);
// Server Emails // Server Emails
s.Driver.Navigate().GoToUrl(s.Link("/server/emails")); s.Driver.Navigate().GoToUrl(s.Link("/server/emails"));
if (s.Driver.PageSource.Contains("Configured")) if (s.Driver.PageSource.Contains("Configured"))
@ -382,21 +382,21 @@ namespace BTCPayServer.Tests
} }
CanSetupEmailCore(s); CanSetupEmailCore(s);
s.CreateNewStore(); s.CreateNewStore();
// Store Emails // Store Emails
s.GoToStore(StoreNavPages.Emails); s.GoToStore(StoreNavPages.Emails);
s.Driver.FindElement(By.Id("ConfigureEmailRules")).Click(); s.Driver.FindElement(By.Id("ConfigureEmailRules")).Click();
Assert.Contains("You need to configure email settings before this feature works", s.Driver.PageSource); Assert.Contains("You need to configure email settings before this feature works", s.Driver.PageSource);
s.GoToStore(StoreNavPages.Emails); s.GoToStore(StoreNavPages.Emails);
CanSetupEmailCore(s); CanSetupEmailCore(s);
// Store Email Rules // Store Email Rules
s.Driver.FindElement(By.Id("ConfigureEmailRules")).Click(); s.Driver.FindElement(By.Id("ConfigureEmailRules")).Click();
Assert.Contains("There are no rules yet.", s.Driver.PageSource); Assert.Contains("There are no rules yet.", s.Driver.PageSource);
Assert.DoesNotContain("id=\"SaveEmailRules\"", s.Driver.PageSource); Assert.DoesNotContain("id=\"SaveEmailRules\"", s.Driver.PageSource);
Assert.DoesNotContain("You need to configure email settings before this feature works", s.Driver.PageSource); Assert.DoesNotContain("You need to configure email settings before this feature works", s.Driver.PageSource);
s.Driver.FindElement(By.Id("CreateEmailRule")).Click(); s.Driver.FindElement(By.Id("CreateEmailRule")).Click();
var select = new SelectElement(s.Driver.FindElement(By.Id("Rules_0__Trigger"))); var select = new SelectElement(s.Driver.FindElement(By.Id("Rules_0__Trigger")));
select.SelectByText("InvoiceSettled", true); select.SelectByText("InvoiceSettled", true);
@ -456,7 +456,7 @@ namespace BTCPayServer.Tests
Assert.DoesNotContain("/server/services/dynamic-dns/pouet.hello.com/delete", s.Driver.PageSource); Assert.DoesNotContain("/server/services/dynamic-dns/pouet.hello.com/delete", s.Driver.PageSource);
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = TestTimeout)]
public async Task CanCreateInvoiceInUI() public async Task CanCreateInvoiceInUI()
{ {
@ -491,7 +491,7 @@ namespace BTCPayServer.Tests
s.Driver.FindElements(By.ClassName("changeInvoiceState"))[0].Click(); s.Driver.FindElements(By.ClassName("changeInvoiceState"))[0].Click();
TestUtils.Eventually(() => Assert.Contains("Settled (marked)", s.Driver.PageSource)); TestUtils.Eventually(() => Assert.Contains("Settled (marked)", s.Driver.PageSource));
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = TestTimeout)]
public async Task CanUseInvoiceReceipts() public async Task CanUseInvoiceReceipts()
{ {
@ -513,9 +513,9 @@ namespace BTCPayServer.Tests
s.Driver.Navigate().Refresh(); s.Driver.Navigate().Refresh();
Assert.DoesNotContain("invoice-unsettled", s.Driver.PageSource); Assert.DoesNotContain("invoice-unsettled", s.Driver.PageSource);
Assert.DoesNotContain("invoice-processing", s.Driver.PageSource); Assert.DoesNotContain("invoice-processing", s.Driver.PageSource);
}); });
Assert.Contains(s.Server.PayTester.GetService<CurrencyNameTable>().DisplayFormatCurrency(100, "USD"), Assert.Contains(s.Server.PayTester.GetService<CurrencyNameTable>().DisplayFormatCurrency(100, "USD"),
s.Driver.PageSource); s.Driver.PageSource);
Assert.Contains(i, s.Driver.PageSource); Assert.Contains(i, s.Driver.PageSource);
@ -525,7 +525,7 @@ namespace BTCPayServer.Tests
var receipturl = s.Driver.Url + "/receipt"; var receipturl = s.Driver.Url + "/receipt";
s.Driver.Navigate().GoToUrl(receipturl); s.Driver.Navigate().GoToUrl(receipturl);
s.Driver.FindElement(By.Id("invoice-unsettled")); s.Driver.FindElement(By.Id("invoice-unsettled"));
s.GoToInvoices(s.StoreId); s.GoToInvoices(s.StoreId);
s.GoToInvoiceCheckout(i); s.GoToInvoiceCheckout(i);
var checkouturi = s.Driver.Url; var checkouturi = s.Driver.Url;
@ -540,19 +540,19 @@ namespace BTCPayServer.Tests
s.Driver.Navigate().Refresh(); s.Driver.Navigate().Refresh();
Assert.DoesNotContain("invoice-unsettled", s.Driver.PageSource); Assert.DoesNotContain("invoice-unsettled", s.Driver.PageSource);
Assert.Contains("invoice-processing", s.Driver.PageSource); Assert.Contains("invoice-processing", s.Driver.PageSource);
}); });
s.GoToUrl(checkouturi); s.GoToUrl(checkouturi);
await s.Server.PayTester.InvoiceRepository.MarkInvoiceStatus(i, InvoiceStatus.Settled); await s.Server.PayTester.InvoiceRepository.MarkInvoiceStatus(i, InvoiceStatus.Settled);
TestUtils.Eventually(() => s.Driver.FindElement(By.Id("receipt-btn")).Click()); TestUtils.Eventually(() => s.Driver.FindElement(By.Id("receipt-btn")).Click());
TestUtils.Eventually(() => TestUtils.Eventually(() =>
{ {
s.Driver.Navigate().Refresh(); s.Driver.Navigate().Refresh();
Assert.DoesNotContain("invoice-unsettled", s.Driver.PageSource); Assert.DoesNotContain("invoice-unsettled", s.Driver.PageSource);
Assert.DoesNotContain("invoice-processing", s.Driver.PageSource); Assert.DoesNotContain("invoice-processing", s.Driver.PageSource);
}); });
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = TestTimeout)]
@ -571,7 +571,7 @@ namespace BTCPayServer.Tests
Assert.Contains("/stores/create", s.Driver.Url); Assert.Contains("/stores/create", s.Driver.Url);
(_, string storeId) = s.CreateNewStore(); (_, string storeId) = s.CreateNewStore();
// should redirect to store // should redirect to store
s.GoToUrl("/"); s.GoToUrl("/");
@ -623,7 +623,7 @@ namespace BTCPayServer.Tests
Assert.Contains("There are no invoices matching your criteria.", s.Driver.PageSource); Assert.Contains("There are no invoices matching your criteria.", s.Driver.PageSource);
var invoiceId = s.CreateInvoice(); var invoiceId = s.CreateInvoice();
s.FindAlertMessage(); s.FindAlertMessage();
var invoiceUrl = s.Driver.Url; var invoiceUrl = s.Driver.Url;
//let's test archiving an invoice //let's test archiving an invoice
@ -737,7 +737,7 @@ namespace BTCPayServer.Tests
s.RegisterNewUser(); s.RegisterNewUser();
s.CreateNewStore(); s.CreateNewStore();
s.AddDerivationScheme(); s.AddDerivationScheme();
s.GoToStore(StoreNavPages.Tokens); s.GoToStore(StoreNavPages.Tokens);
s.Driver.FindElement(By.Id("CreateNewToken")).Click(); s.Driver.FindElement(By.Id("CreateNewToken")).Click();
s.Driver.FindElement(By.Id("RequestPairing")).Click(); s.Driver.FindElement(By.Id("RequestPairing")).Click();
@ -781,7 +781,7 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id("SelectedAppType")).SendKeys("Point of Sale"); s.Driver.FindElement(By.Id("SelectedAppType")).SendKeys("Point of Sale");
s.Driver.FindElement(By.Id("Create")).Click(); s.Driver.FindElement(By.Id("Create")).Click();
Assert.Contains("App successfully created", s.FindAlertMessage().Text); Assert.Contains("App successfully created", s.FindAlertMessage().Text);
s.Driver.FindElement(By.CssSelector(".template-item:nth-of-type(1) .btn-primary")).Click(); s.Driver.FindElement(By.CssSelector(".template-item:nth-of-type(1) .btn-primary")).Click();
s.Driver.FindElement(By.Id("BuyButtonText")).SendKeys("Take my money"); s.Driver.FindElement(By.Id("BuyButtonText")).SendKeys("Take my money");
s.Driver.FindElement(By.Id("SaveItemChanges")).Click(); s.Driver.FindElement(By.Id("SaveItemChanges")).Click();
@ -825,7 +825,7 @@ namespace BTCPayServer.Tests
// Make sure after login, we are not redirected to the PoS // Make sure after login, we are not redirected to the PoS
Assert.DoesNotContain("Tea shop", s.Driver.PageSource); Assert.DoesNotContain("Tea shop", s.Driver.PageSource);
var prevUrl = s.Driver.Url; var prevUrl = s.Driver.Url;
// We are only if explicitly going to / // We are only if explicitly going to /
s.GoToUrl("/"); s.GoToUrl("/");
Assert.Contains("Tea shop", s.Driver.PageSource); Assert.Contains("Tea shop", s.Driver.PageSource);
@ -848,7 +848,7 @@ namespace BTCPayServer.Tests
s.LogIn(userId); s.LogIn(userId);
// Make sure after login, we are not redirected to the PoS // Make sure after login, we are not redirected to the PoS
Assert.DoesNotContain("Tea shop", s.Driver.PageSource); Assert.DoesNotContain("Tea shop", s.Driver.PageSource);
// We are only if explicitly going to / // We are only if explicitly going to /
s.GoToUrl("/"); s.GoToUrl("/");
Assert.Contains("Tea shop", s.Driver.PageSource); Assert.Contains("Tea shop", s.Driver.PageSource);
@ -868,20 +868,20 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id("SelectedAppType")).SendKeys("Crowdfund"); s.Driver.FindElement(By.Id("SelectedAppType")).SendKeys("Crowdfund");
s.Driver.FindElement(By.Id("Create")).Click(); s.Driver.FindElement(By.Id("Create")).Click();
Assert.Contains("App successfully created", s.FindAlertMessage().Text); Assert.Contains("App successfully created", s.FindAlertMessage().Text);
s.Driver.FindElement(By.Id("Title")).SendKeys("Kukkstarter"); s.Driver.FindElement(By.Id("Title")).SendKeys("Kukkstarter");
s.Driver.FindElement(By.CssSelector("div.note-editable.card-block")).SendKeys("1BTC = 1BTC"); s.Driver.FindElement(By.CssSelector("div.note-editable.card-block")).SendKeys("1BTC = 1BTC");
s.Driver.FindElement(By.Id("TargetCurrency")).Clear(); s.Driver.FindElement(By.Id("TargetCurrency")).Clear();
s.Driver.FindElement(By.Id("TargetCurrency")).SendKeys("JPY"); s.Driver.FindElement(By.Id("TargetCurrency")).SendKeys("JPY");
s.Driver.FindElement(By.Id("TargetAmount")).SendKeys("700"); s.Driver.FindElement(By.Id("TargetAmount")).SendKeys("700");
// test wrong dates // test wrong dates
s.Driver.ExecuteJavaScript("const now = new Date();document.getElementById('StartDate').value = now.toISOString();" + s.Driver.ExecuteJavaScript("const now = new Date();document.getElementById('StartDate').value = now.toISOString();" +
"const yst = new Date(now.setDate(now.getDate() -1));document.getElementById('EndDate').value = yst.toISOString()"); "const yst = new Date(now.setDate(now.getDate() -1));document.getElementById('EndDate').value = yst.toISOString()");
s.Driver.FindElement(By.Id("SaveSettings")).Click(); s.Driver.FindElement(By.Id("SaveSettings")).Click();
Assert.Contains("End date cannot be before start date", s.Driver.PageSource); Assert.Contains("End date cannot be before start date", s.Driver.PageSource);
Assert.DoesNotContain("App updated", s.Driver.PageSource); Assert.DoesNotContain("App updated", s.Driver.PageSource);
// unset end date // unset end date
s.Driver.ExecuteJavaScript("document.getElementById('EndDate').value = ''"); s.Driver.ExecuteJavaScript("document.getElementById('EndDate').value = ''");
s.Driver.FindElement(By.Id("SaveSettings")).Click(); s.Driver.FindElement(By.Id("SaveSettings")).Click();
@ -894,7 +894,7 @@ namespace BTCPayServer.Tests
Assert.Equal("currently active!", Assert.Equal("currently active!",
s.Driver.FindElement(By.CssSelector("[data-test='time-state']")).Text); s.Driver.FindElement(By.CssSelector("[data-test='time-state']")).Text);
s.Driver.Close(); s.Driver.Close();
s.Driver.SwitchTo().Window(windows[0]); s.Driver.SwitchTo().Window(windows[0]);
} }
@ -917,23 +917,23 @@ namespace BTCPayServer.Tests
Assert.Equal("USD", currencyInput.GetAttribute("value")); Assert.Equal("USD", currencyInput.GetAttribute("value"));
currencyInput.Clear(); currencyInput.Clear();
currencyInput.SendKeys("BTC"); currencyInput.SendKeys("BTC");
s.Driver.FindElement(By.Id("SaveButton")).Click(); s.Driver.FindElement(By.Id("SaveButton")).Click();
s.Driver.FindElement(By.XPath("//a[starts-with(@id, 'Edit-')]")).Click(); s.Driver.FindElement(By.XPath("//a[starts-with(@id, 'Edit-')]")).Click();
var editUrl = s.Driver.Url; var editUrl = s.Driver.Url;
s.Driver.FindElement(By.Id("ViewPaymentRequest")).Click(); s.Driver.FindElement(By.Id("ViewPaymentRequest")).Click();
var viewUrl = s.Driver.Url; var viewUrl = s.Driver.Url;
Assert.Equal("Amount due", s.Driver.FindElement(By.CssSelector("[data-test='amount-due-title']")).Text); Assert.Equal("Amount due", s.Driver.FindElement(By.CssSelector("[data-test='amount-due-title']")).Text);
Assert.Equal("Pay Invoice", s.Driver.FindElement(By.CssSelector("[data-test='pay-button']")).Text.Trim()); Assert.Equal("Pay Invoice", s.Driver.FindElement(By.CssSelector("[data-test='pay-button']")).Text.Trim());
// expire // expire
s.GoToUrl(editUrl); s.GoToUrl(editUrl);
s.Driver.ExecuteJavaScript("document.getElementById('ExpiryDate').value = '2021-01-21T21:00:00.000Z'"); s.Driver.ExecuteJavaScript("document.getElementById('ExpiryDate').value = '2021-01-21T21:00:00.000Z'");
s.Driver.FindElement(By.Id("SaveButton")).Click(); s.Driver.FindElement(By.Id("SaveButton")).Click();
s.Driver.FindElement(By.XPath("//a[starts-with(@id, 'Edit-')]")).Click(); s.Driver.FindElement(By.XPath("//a[starts-with(@id, 'Edit-')]")).Click();
s.GoToUrl(viewUrl); s.GoToUrl(viewUrl);
Assert.Equal("Expired", s.Driver.WaitForElement(By.CssSelector("[data-test='status']")).Text); Assert.Equal("Expired", s.Driver.WaitForElement(By.CssSelector("[data-test='status']")).Text);
@ -947,22 +947,22 @@ namespace BTCPayServer.Tests
s.GoToUrl(editUrl); s.GoToUrl(editUrl);
Assert.True(s.Driver.FindElement(By.Id("Amount")).Enabled); Assert.True(s.Driver.FindElement(By.Id("Amount")).Enabled);
Assert.True(s.Driver.FindElement(By.Id("Currency")).Enabled); Assert.True(s.Driver.FindElement(By.Id("Currency")).Enabled);
s.GoToUrl(viewUrl); s.GoToUrl(viewUrl);
s.Driver.AssertElementNotFound(By.CssSelector("[data-test='status']")); s.Driver.AssertElementNotFound(By.CssSelector("[data-test='status']"));
Assert.Equal("Pay Invoice", s.Driver.FindElement(By.CssSelector("[data-test='pay-button']")).Text.Trim()); Assert.Equal("Pay Invoice", s.Driver.FindElement(By.CssSelector("[data-test='pay-button']")).Text.Trim());
// test invoice creation, click with JS, because the button is inside a sticky header // test invoice creation, click with JS, because the button is inside a sticky header
s.Driver.ExecuteJavaScript("document.querySelector('[data-test=\"pay-button\"]').click()"); s.Driver.ExecuteJavaScript("document.querySelector('[data-test=\"pay-button\"]').click()");
// checkout v1 // checkout v1
s.Driver.WaitForElement(By.CssSelector("invoice")); s.Driver.WaitForElement(By.CssSelector("invoice"));
Assert.Contains("Awaiting Payment", s.Driver.PageSource); Assert.Contains("Awaiting Payment", s.Driver.PageSource);
// amount and currency should not be editable, because invoice exists // amount and currency should not be editable, because invoice exists
s.GoToUrl(editUrl); s.GoToUrl(editUrl);
Assert.False(s.Driver.FindElement(By.Id("Amount")).Enabled); Assert.False(s.Driver.FindElement(By.Id("Amount")).Enabled);
Assert.False(s.Driver.FindElement(By.Id("Currency")).Enabled); Assert.False(s.Driver.FindElement(By.Id("Currency")).Enabled);
// archive (from details page) // archive (from details page)
var payReqId = s.Driver.Url.Split('/').Last(); var payReqId = s.Driver.Url.Split('/').Last();
s.Driver.FindElement(By.Id("ArchivePaymentRequest")).Click(); s.Driver.FindElement(By.Id("ArchivePaymentRequest")).Click();
@ -971,7 +971,7 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id("SearchDropdownToggle")).Click(); s.Driver.FindElement(By.Id("SearchDropdownToggle")).Click();
s.Driver.FindElement(By.Id("SearchIncludeArchived")).Click(); s.Driver.FindElement(By.Id("SearchIncludeArchived")).Click();
Assert.Contains("Pay123", s.Driver.PageSource); Assert.Contains("Pay123", s.Driver.PageSource);
// unarchive (from list) // unarchive (from list)
s.Driver.FindElement(By.Id($"ToggleArchival-{payReqId}")).Click(); s.Driver.FindElement(By.Id($"ToggleArchival-{payReqId}")).Click();
Assert.Contains("The payment request has been unarchived", s.FindAlertMessage().Text); Assert.Contains("The payment request has been unarchived", s.FindAlertMessage().Text);
@ -1123,7 +1123,7 @@ namespace BTCPayServer.Tests
s.GoToStore(StoreNavPages.Webhooks); s.GoToStore(StoreNavPages.Webhooks);
s.Driver.FindElement(By.LinkText("Modify")).Click(); s.Driver.FindElement(By.LinkText("Modify")).Click();
var elements = s.Driver.FindElements(By.ClassName("redeliver")); var elements = s.Driver.FindElements(By.ClassName("redeliver"));
// One worked, one failed // One worked, one failed
s.Driver.FindElement(By.ClassName("fa-times")); s.Driver.FindElement(By.ClassName("fa-times"));
s.Driver.FindElement(By.ClassName("fa-check")); s.Driver.FindElement(By.ClassName("fa-check"));
@ -1199,7 +1199,7 @@ namespace BTCPayServer.Tests
Assert.True(s.Driver.ElementDoesNotExist(By.Id("GoBack"))); Assert.True(s.Driver.ElementDoesNotExist(By.Id("GoBack")));
s.Driver.FindElement(By.Id("CancelWizard")).Click(); s.Driver.FindElement(By.Id("CancelWizard")).Click();
s.Driver.FindElement(By.Id("WalletNav-Receive")).Click(); s.Driver.FindElement(By.Id("WalletNav-Receive")).Click();
//generate a receiving address //generate a receiving address
s.Driver.FindElement(By.CssSelector("button[value=generate-new-address]")).Click(); s.Driver.FindElement(By.CssSelector("button[value=generate-new-address]")).Click();
Assert.True(s.Driver.FindElement(By.CssSelector("#address-tab .qr-container")).Displayed); Assert.True(s.Driver.FindElement(By.CssSelector("#address-tab .qr-container")).Displayed);
@ -1343,7 +1343,7 @@ namespace BTCPayServer.Tests
Assert.Empty(s.Driver.FindElements(By.Id("confirm"))); Assert.Empty(s.Driver.FindElements(By.Id("confirm")));
s.Driver.FindElement(By.Id("proceed")).Click(); s.Driver.FindElement(By.Id("proceed")).Click();
Assert.Equal(settingsUri.ToString(), s.Driver.Url); Assert.Equal(settingsUri.ToString(), s.Driver.Url);
// Once more, test the cancel link of the wallet send page leads back to the previous page // Once more, test the cancel link of the wallet send page leads back to the previous page
s.Driver.FindElement(By.Id("WalletNav-Send")).Click(); s.Driver.FindElement(By.Id("WalletNav-Send")).Click();
cancelUrl = s.Driver.FindElement(By.Id("CancelWizard")).GetAttribute("href"); cancelUrl = s.Driver.FindElement(By.Id("CancelWizard")).GetAttribute("href");
@ -1352,12 +1352,12 @@ namespace BTCPayServer.Tests
Assert.True(s.Driver.ElementDoesNotExist(By.Id("GoBack"))); Assert.True(s.Driver.ElementDoesNotExist(By.Id("GoBack")));
s.Driver.FindElement(By.Id("CancelWizard")).Click(); s.Driver.FindElement(By.Id("CancelWizard")).Click();
Assert.Equal(settingsUri.ToString(), s.Driver.Url); Assert.Equal(settingsUri.ToString(), s.Driver.Url);
// Transactions list contains export and action, ensure functions are present. // Transactions list contains export and action, ensure functions are present.
s.Driver.FindElement(By.Id($"StoreNav-Wallet{cryptoCode}")).Click(); s.Driver.FindElement(By.Id($"StoreNav-Wallet{cryptoCode}")).Click();
s.Driver.FindElement(By.Id("ActionsDropdownToggle")).Click(); s.Driver.FindElement(By.Id("ActionsDropdownToggle")).Click();
s.Driver.FindElement(By.Id("BumpFee")); s.Driver.FindElement(By.Id("BumpFee"));
// JSON export // JSON export
s.Driver.FindElement(By.Id("ExportDropdownToggle")).Click(); s.Driver.FindElement(By.Id("ExportDropdownToggle")).Click();
s.Driver.FindElement(By.Id("ExportJSON")).Click(); s.Driver.FindElement(By.Id("ExportJSON")).Click();
@ -1367,7 +1367,7 @@ namespace BTCPayServer.Tests
Assert.EndsWith("export?format=json", s.Driver.Url); Assert.EndsWith("export?format=json", s.Driver.Url);
Assert.Contains("\"Amount\": \"3.00000000\"", s.Driver.PageSource); Assert.Contains("\"Amount\": \"3.00000000\"", s.Driver.PageSource);
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First()); s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First());
// CSV export // CSV export
s.Driver.FindElement(By.Id("ExportDropdownToggle")).Click(); s.Driver.FindElement(By.Id("ExportDropdownToggle")).Click();
s.Driver.FindElement(By.Id("ExportCSV")).Click(); s.Driver.FindElement(By.Id("ExportCSV")).Click();
@ -1389,7 +1389,7 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id("AccountKeys_0__MasterFingerprint")).GetAttribute("value")); s.Driver.FindElement(By.Id("AccountKeys_0__MasterFingerprint")).GetAttribute("value"));
Assert.Contains("m/84'/1'/0'", Assert.Contains("m/84'/1'/0'",
s.Driver.FindElement(By.Id("AccountKeys_0__AccountKeyPath")).GetAttribute("value")); s.Driver.FindElement(By.Id("AccountKeys_0__AccountKeyPath")).GetAttribute("value"));
// Transactions list is empty // Transactions list is empty
s.Driver.FindElement(By.Id($"StoreNav-Wallet{cryptoCode}")).Click(); s.Driver.FindElement(By.Id($"StoreNav-Wallet{cryptoCode}")).Click();
Assert.Contains("There are no transactions yet.", s.Driver.PageSource); Assert.Contains("There are no transactions yet.", s.Driver.PageSource);
@ -1605,7 +1605,7 @@ namespace BTCPayServer.Tests
newStore = s.CreateNewStore(); newStore = s.CreateNewStore();
s.AddLightningNode(); s.AddLightningNode();
//Currently an onchain wallet is required to use the Lightning payouts feature.. //Currently an onchain wallet is required to use the Lightning payouts feature..
s.GenerateWallet("BTC", "", true, true); s.GenerateWallet("BTC", "", true, true);
s.GoToStore(newStore.storeId, StoreNavPages.PullPayments); s.GoToStore(newStore.storeId, StoreNavPages.PullPayments);
@ -1680,8 +1680,8 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id($"{PayoutState.Completed}-view")).Click(); s.Driver.FindElement(By.Id($"{PayoutState.Completed}-view")).Click();
Assert.Contains(bolt, s.Driver.PageSource); Assert.Contains(bolt, s.Driver.PageSource);
} }
//auto-approve pull payments //auto-approve pull payments
@ -1703,8 +1703,8 @@ namespace BTCPayServer.Tests
Assert.Contains(PayoutState.AwaitingPayment.GetStateString(), s.Driver.PageSource); Assert.Contains(PayoutState.AwaitingPayment.GetStateString(), s.Driver.PageSource);
//lnurl-w support check //lnurl-w support check
s.GoToStore(s.StoreId,StoreNavPages.PullPayments); s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
s.Driver.FindElement(By.Id("NewPullPayment")).Click(); s.Driver.FindElement(By.Id("NewPullPayment")).Click();
s.Driver.FindElement(By.Id("Name")).SendKeys("PP1"); s.Driver.FindElement(By.Id("Name")).SendKeys("PP1");
s.Driver.SetCheckbox(By.Id("AutoApproveClaims"), true); s.Driver.SetCheckbox(By.Id("AutoApproveClaims"), true);
@ -1715,7 +1715,7 @@ namespace BTCPayServer.Tests
s.FindAlertMessage(StatusMessageModel.StatusSeverity.Success); s.FindAlertMessage(StatusMessageModel.StatusSeverity.Success);
s.Driver.FindElement(By.LinkText("View")).Click(); s.Driver.FindElement(By.LinkText("View")).Click();
s.Driver.FindElement(By.CssSelector("#lnurlwithdraw-button")).Click(); s.Driver.FindElement(By.CssSelector("#lnurlwithdraw-button")).Click();
var lnurl = new Uri(LNURL.LNURL.Parse(s.Driver.FindElement(By.Id("qr-code-data-input")).GetAttribute("value"), out _).ToString().Replace("https", "http")); var lnurl = new Uri(LNURL.LNURL.Parse(s.Driver.FindElement(By.Id("qr-code-data-input")).GetAttribute("value"), out _).ToString().Replace("https", "http"));
s.Driver.FindElement(By.CssSelector("button[data-bs-dismiss='modal']")).Click(); s.Driver.FindElement(By.CssSelector("button[data-bs-dismiss='modal']")).Click();
var info = Assert.IsType<LNURLWithdrawRequest>(await LNURL.LNURL.FetchInformation(lnurl, s.Server.PayTester.HttpClient)); var info = Assert.IsType<LNURLWithdrawRequest>(await LNURL.LNURL.FetchInformation(lnurl, s.Server.PayTester.HttpClient));
Assert.Equal(info.MaxWithdrawable, new LightMoney(0.0000001m, LightMoneyUnit.BTC)); Assert.Equal(info.MaxWithdrawable, new LightMoney(0.0000001m, LightMoneyUnit.BTC));
@ -1723,7 +1723,7 @@ namespace BTCPayServer.Tests
info = Assert.IsType<LNURLWithdrawRequest>(await LNURL.LNURL.FetchInformation(info.BalanceCheck, s.Server.PayTester.HttpClient)); info = Assert.IsType<LNURLWithdrawRequest>(await LNURL.LNURL.FetchInformation(info.BalanceCheck, s.Server.PayTester.HttpClient));
Assert.Equal(info.MaxWithdrawable, new LightMoney(0.0000001m, LightMoneyUnit.BTC)); Assert.Equal(info.MaxWithdrawable, new LightMoney(0.0000001m, LightMoneyUnit.BTC));
Assert.Equal(info.CurrentBalance, new LightMoney(0.0000001m, LightMoneyUnit.BTC)); Assert.Equal(info.CurrentBalance, new LightMoney(0.0000001m, LightMoneyUnit.BTC));
var bolt2 = (await s.Server.CustomerLightningD.CreateInvoice( var bolt2 = (await s.Server.CustomerLightningD.CreateInvoice(
new LightMoney(0.0000001m, LightMoneyUnit.BTC), new LightMoney(0.0000001m, LightMoneyUnit.BTC),
$"LNurl w payout test {DateTime.UtcNow.Ticks}", $"LNurl w payout test {DateTime.UtcNow.Ticks}",
@ -1733,12 +1733,12 @@ namespace BTCPayServer.Tests
{ {
s.Driver.Navigate().Refresh(); s.Driver.Navigate().Refresh();
Assert.Contains(bolt2.BOLT11, s.Driver.PageSource); Assert.Contains(bolt2.BOLT11, s.Driver.PageSource);
Assert.Contains(PayoutState.Completed.GetStateString(), s.Driver.PageSource); Assert.Contains(PayoutState.Completed.GetStateString(), s.Driver.PageSource);
Assert.Equal( LightningInvoiceStatus.Paid, (await s.Server.CustomerLightningD.GetInvoice(bolt2.Id)).Status ); Assert.Equal(LightningInvoiceStatus.Paid, (await s.Server.CustomerLightningD.GetInvoice(bolt2.Id)).Status);
}); });
s.GoToStore(s.StoreId,StoreNavPages.PullPayments); s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
s.Driver.FindElement(By.Id("NewPullPayment")).Click(); s.Driver.FindElement(By.Id("NewPullPayment")).Click();
s.Driver.FindElement(By.Id("Name")).SendKeys("PP1"); s.Driver.FindElement(By.Id("Name")).SendKeys("PP1");
s.Driver.SetCheckbox(By.Id("AutoApproveClaims"), false); s.Driver.SetCheckbox(By.Id("AutoApproveClaims"), false);
@ -1749,8 +1749,8 @@ namespace BTCPayServer.Tests
s.FindAlertMessage(StatusMessageModel.StatusSeverity.Success); s.FindAlertMessage(StatusMessageModel.StatusSeverity.Success);
s.Driver.FindElement(By.LinkText("View")).Click(); s.Driver.FindElement(By.LinkText("View")).Click();
s.Driver.FindElement(By.CssSelector("#lnurlwithdraw-button")).Click(); s.Driver.FindElement(By.CssSelector("#lnurlwithdraw-button")).Click();
lnurl = new Uri(LNURL.LNURL.Parse(s.Driver.FindElement(By.Id("qr-code-data-input")).GetAttribute("value"), out _).ToString().Replace("https", "http")); lnurl = new Uri(LNURL.LNURL.Parse(s.Driver.FindElement(By.Id("qr-code-data-input")).GetAttribute("value"), out _).ToString().Replace("https", "http"));
s.Driver.FindElement(By.CssSelector("button[data-bs-dismiss='modal']")).Click(); s.Driver.FindElement(By.CssSelector("button[data-bs-dismiss='modal']")).Click();
info = Assert.IsType<LNURLWithdrawRequest>(await LNURL.LNURL.FetchInformation(lnurl, s.Server.PayTester.HttpClient)); info = Assert.IsType<LNURLWithdrawRequest>(await LNURL.LNURL.FetchInformation(lnurl, s.Server.PayTester.HttpClient));
Assert.Equal(info.MaxWithdrawable, new LightMoney(0.0000001m, LightMoneyUnit.BTC)); Assert.Equal(info.MaxWithdrawable, new LightMoney(0.0000001m, LightMoneyUnit.BTC));
@ -1758,7 +1758,7 @@ namespace BTCPayServer.Tests
info = Assert.IsType<LNURLWithdrawRequest>(await LNURL.LNURL.FetchInformation(info.BalanceCheck, s.Server.PayTester.HttpClient)); info = Assert.IsType<LNURLWithdrawRequest>(await LNURL.LNURL.FetchInformation(info.BalanceCheck, s.Server.PayTester.HttpClient));
Assert.Equal(info.MaxWithdrawable, new LightMoney(0.0000001m, LightMoneyUnit.BTC)); Assert.Equal(info.MaxWithdrawable, new LightMoney(0.0000001m, LightMoneyUnit.BTC));
Assert.Equal(info.CurrentBalance, new LightMoney(0.0000001m, LightMoneyUnit.BTC)); Assert.Equal(info.CurrentBalance, new LightMoney(0.0000001m, LightMoneyUnit.BTC));
bolt2 = (await s.Server.CustomerLightningD.CreateInvoice( bolt2 = (await s.Server.CustomerLightningD.CreateInvoice(
new LightMoney(0.0000001m, LightMoneyUnit.BTC), new LightMoney(0.0000001m, LightMoneyUnit.BTC),
$"LNurl w payout test {DateTime.UtcNow.Ticks}", $"LNurl w payout test {DateTime.UtcNow.Ticks}",
@ -1768,7 +1768,7 @@ namespace BTCPayServer.Tests
{ {
s.Driver.Navigate().Refresh(); s.Driver.Navigate().Refresh();
Assert.Contains(bolt2.BOLT11, s.Driver.PageSource); Assert.Contains(bolt2.BOLT11, s.Driver.PageSource);
Assert.Contains(PayoutState.AwaitingApproval.GetStateString(), s.Driver.PageSource); Assert.Contains(PayoutState.AwaitingApproval.GetStateString(), s.Driver.PageSource);
}); });
} }
@ -1867,7 +1867,8 @@ namespace BTCPayServer.Tests
}); });
var greenfield = await s.AsTestAccount().CreateClient(); var greenfield = await s.AsTestAccount().CreateClient();
var paymentMethods = await greenfield.GetInvoicePaymentMethods(s.StoreId, i); var paymentMethods = await greenfield.GetInvoicePaymentMethods(s.StoreId, i);
Assert.Single(paymentMethods, p => { Assert.Single(paymentMethods, p =>
{
return p.AdditionalData["providedComment"].Value<string>() == "lol2"; return p.AdditionalData["providedComment"].Value<string>() == "lol2";
}); });
// Standard invoice test // Standard invoice test
@ -1977,12 +1978,12 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id("Name")).SendKeys("PP1"); s.Driver.FindElement(By.Id("Name")).SendKeys("PP1");
s.Driver.FindElement(By.Id("Amount")).Clear(); s.Driver.FindElement(By.Id("Amount")).Clear();
s.Driver.FindElement(By.Id("Amount")).SendKeys("0.0000001"); s.Driver.FindElement(By.Id("Amount")).SendKeys("0.0000001");
var currencyInput = s.Driver.FindElement(By.Id("Currency")); var currencyInput = s.Driver.FindElement(By.Id("Currency"));
Assert.Equal("USD", currencyInput.GetAttribute("value")); Assert.Equal("USD", currencyInput.GetAttribute("value"));
currencyInput.Clear(); currencyInput.Clear();
currencyInput.SendKeys("BTC"); currencyInput.SendKeys("BTC");
s.Driver.FindElement(By.Id("Create")).Click(); s.Driver.FindElement(By.Id("Create")).Click();
s.Driver.FindElement(By.LinkText("View")).Click(); s.Driver.FindElement(By.LinkText("View")).Click();
s.Driver.FindElement(By.Id("Destination")).SendKeys(lnurl); s.Driver.FindElement(By.Id("Destination")).SendKeys(lnurl);
@ -2124,7 +2125,7 @@ retry:
} }
} }
[Fact] [Fact]
[Trait("Selenium", "Selenium")] [Trait("Selenium", "Selenium")]
public async Task CanUseLNURLAuth() public async Task CanUseLNURLAuth()
@ -2138,32 +2139,32 @@ retry:
.FindElement(By.CssSelector($"option[value='{(int)Fido2Credential.CredentialType.LNURLAuth}']")).Click(); .FindElement(By.CssSelector($"option[value='{(int)Fido2Credential.CredentialType.LNURLAuth}']")).Click();
s.Driver.FindElement(By.Id("btn-add")).Click(); s.Driver.FindElement(By.Id("btn-add")).Click();
var links = s.Driver.FindElements(By.CssSelector(".tab-content a")).Select(element => element.GetAttribute("href")); var links = s.Driver.FindElements(By.CssSelector(".tab-content a")).Select(element => element.GetAttribute("href"));
Assert.Equal(2,links.Count()); Assert.Equal(2, links.Count());
Uri prevEndpoint = null; Uri prevEndpoint = null;
foreach (string link in links) foreach (string link in links)
{ {
var endpoint = LNURL.LNURL.Parse(link, out var tag); var endpoint = LNURL.LNURL.Parse(link, out var tag);
Assert.Equal("login",tag); Assert.Equal("login", tag);
if(endpoint.Scheme != "https") if (endpoint.Scheme != "https")
prevEndpoint = endpoint; prevEndpoint = endpoint;
} }
var linkingKey = new Key(); var linkingKey = new Key();
var request = Assert.IsType<LNAuthRequest>(await LNURL.LNURL.FetchInformation(prevEndpoint, null)); var request = Assert.IsType<LNAuthRequest>(await LNURL.LNURL.FetchInformation(prevEndpoint, null));
_ = await request.SendChallenge(linkingKey, new HttpClient()); _ = await request.SendChallenge(linkingKey, new HttpClient());
TestUtils.Eventually(() => s.FindAlertMessage()); TestUtils.Eventually(() => s.FindAlertMessage());
s.Logout(); s.Logout();
s.LogIn(user, "123456"); s.LogIn(user, "123456");
var section = s.Driver.FindElement(By.Id("lnurlauth-section")); var section = s.Driver.FindElement(By.Id("lnurlauth-section"));
links = section.FindElements(By.CssSelector(".tab-content a")).Select(element => element.GetAttribute("href")); links = section.FindElements(By.CssSelector(".tab-content a")).Select(element => element.GetAttribute("href"));
Assert.Equal(2,links.Count()); Assert.Equal(2, links.Count());
prevEndpoint = null; prevEndpoint = null;
foreach (string link in links) foreach (string link in links)
{ {
var endpoint = LNURL.LNURL.Parse(link, out var tag); var endpoint = LNURL.LNURL.Parse(link, out var tag);
Assert.Equal("login",tag); Assert.Equal("login", tag);
if(endpoint.Scheme != "https") if (endpoint.Scheme != "https")
prevEndpoint = endpoint; prevEndpoint = endpoint;
} }
request = Assert.IsType<LNAuthRequest>(await LNURL.LNURL.FetchInformation(prevEndpoint, null)); request = Assert.IsType<LNAuthRequest>(await LNURL.LNURL.FetchInformation(prevEndpoint, null));
@ -2173,7 +2174,7 @@ retry:
Assert.Equal(s.Driver.Url, s.ServerUri.ToString()); Assert.Equal(s.Driver.Url, s.ServerUri.ToString());
}); });
} }
private static void CanBrowseContent(SeleniumTester s) private static void CanBrowseContent(SeleniumTester s)
{ {
s.Driver.FindElement(By.ClassName("delivery-content")).Click(); s.Driver.FindElement(By.ClassName("delivery-content")).Click();

View File

@ -254,7 +254,7 @@ namespace BTCPayServer.Tests
get; get;
set; set;
} }
public string Email public string Email
{ {
get; get;

View File

@ -75,7 +75,7 @@ namespace BTCPayServer.Tests
public async Task CanQueryDirectProviders() public async Task CanQueryDirectProviders()
{ {
// TODO: Check once in a while whether or not they are working again // TODO: Check once in a while whether or not they are working again
string[] brokenShitcoinCasinos = {}; string[] brokenShitcoinCasinos = { };
var skipped = 0; var skipped = 0;
var factory = FastTests.CreateBTCPayRateFactory(); var factory = FastTests.CreateBTCPayRateFactory();
var directlySupported = factory.GetSupportedExchanges().Where(s => s.Source == RateSource.Direct) var directlySupported = factory.GetSupportedExchanges().Where(s => s.Source == RateSource.Direct)
@ -95,7 +95,7 @@ namespace BTCPayServer.Tests
skipped++; skipped++;
continue; continue;
} }
TestLogs.LogInformation($"Testing {name}"); TestLogs.LogInformation($"Testing {name}");
result.Fetcher.InvalidateCache(); result.Fetcher.InvalidateCache();
@ -175,7 +175,7 @@ namespace BTCPayServer.Tests
var p = new KrakenExchangeRateProvider(); var p = new KrakenExchangeRateProvider();
var rates = await p.GetRatesAsync(default); var rates = await p.GetRatesAsync(default);
Assert.Contains(rates, e => e.CurrencyPair == new CurrencyPair("XMR", "BTC") && e.BidAsk.Bid < 1.0m); Assert.Contains(rates, e => e.CurrencyPair == new CurrencyPair("XMR", "BTC") && e.BidAsk.Bid < 1.0m);
// Check we didn't skip too many exchanges // Check we didn't skip too many exchanges
Assert.InRange(skipped, 0, 3); Assert.InRange(skipped, 0, 3);
} }
@ -225,7 +225,7 @@ namespace BTCPayServer.Tests
{ {
var uri = new Uri(url); var uri = new Uri(url);
int retryLeft = 3; int retryLeft = 3;
retry: retry:
try try
{ {
using var request = new HttpRequestMessage(HttpMethod.Get, uri); using var request = new HttpRequestMessage(HttpMethod.Get, uri);
@ -350,7 +350,7 @@ namespace BTCPayServer.Tests
expected = (await (await client.GetAsync($"https://unpkg.com/@chenfengyuan/vue-qrcode@{version}/dist/vue-qrcode.min.js")).Content.ReadAsStringAsync()).Trim(); expected = (await (await client.GetAsync($"https://unpkg.com/@chenfengyuan/vue-qrcode@{version}/dist/vue-qrcode.min.js")).Content.ReadAsStringAsync()).Trim();
Assert.Equal(expected, actual); Assert.Equal(expected, actual);
} }
string GetFileContent(params string[] path) string GetFileContent(params string[] path)
{ {
var l = path.ToList(); var l = path.ToList();

View File

@ -189,7 +189,7 @@ namespace BTCPayServer.Tests
Assert.Equal(description, json["components"]["securitySchemes"]["API_Key"]["description"].Value<string>()); Assert.Equal(description, json["components"]["securitySchemes"]["API_Key"]["description"].Value<string>());
} }
[Fact] [Fact]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async void CanStoreArbitrarySettingsWithStore() public async void CanStoreArbitrarySettingsWithStore()
@ -199,11 +199,11 @@ namespace BTCPayServer.Tests
var user = tester.NewAccount(); var user = tester.NewAccount();
await user.GrantAccessAsync(); await user.GrantAccessAsync();
var settingsRepo = tester.PayTester.ServiceProvider.GetRequiredService<IStoreRepository>(); var settingsRepo = tester.PayTester.ServiceProvider.GetRequiredService<IStoreRepository>();
var arbValue = await settingsRepo.GetSettingAsync<string>(user.StoreId,"arbitrary"); var arbValue = await settingsRepo.GetSettingAsync<string>(user.StoreId, "arbitrary");
Assert.Null(arbValue); Assert.Null(arbValue);
await settingsRepo.UpdateSetting(user.StoreId, "arbitrary", "saved"); await settingsRepo.UpdateSetting(user.StoreId, "arbitrary", "saved");
arbValue = await settingsRepo.GetSettingAsync<string>(user.StoreId,"arbitrary"); arbValue = await settingsRepo.GetSettingAsync<string>(user.StoreId, "arbitrary");
Assert.Equal("saved", arbValue); Assert.Equal("saved", arbValue);
await settingsRepo.UpdateSetting<TestData>(user.StoreId, "arbitrary", new TestData() { Name = "hello" }); await settingsRepo.UpdateSetting<TestData>(user.StoreId, "arbitrary", new TestData() { Name = "hello" });
@ -1934,8 +1934,8 @@ namespace BTCPayServer.Tests
Assert.Contains($",orderId,{invoice.Id},", paidresult.Content); Assert.Contains($",orderId,{invoice.Id},", paidresult.Content);
Assert.Contains($",On-Chain,BTC,0.0991,0.0001,5000.0", paidresult.Content); Assert.Contains($",On-Chain,BTC,0.0991,0.0001,5000.0", paidresult.Content);
Assert.Contains($",USD,5.00", paidresult.Content); // Seems hacky but some plateform does not render this decimal the same Assert.Contains($",USD,5.00", paidresult.Content); // Seems hacky but some plateform does not render this decimal the same
Assert.Contains("0,,\"Some \"\", description\",New (paidPartial),new,paidPartial", Assert.Contains("0,,\"Some \"\", description\",New (paidPartial),new,paidPartial",
paidresult.Content); paidresult.Content);
}); });
} }
@ -2157,7 +2157,7 @@ namespace BTCPayServer.Tests
Assert.Equal("paidPartial", localInvoice.ExceptionStatus.ToString()); Assert.Equal("paidPartial", localInvoice.ExceptionStatus.ToString());
Assert.Equal(1, localInvoice.CryptoInfo[0].TxCount); Assert.Equal(1, localInvoice.CryptoInfo[0].TxCount);
Assert.NotEqual(localInvoice.BitcoinAddress, invoice.BitcoinAddress); //New address Assert.NotEqual(localInvoice.BitcoinAddress, invoice.BitcoinAddress); //New address
Assert.True(IsMapped(invoice, ctx)); Assert.True(IsMapped(invoice, ctx));
Assert.True(IsMapped(localInvoice, ctx)); Assert.True(IsMapped(localInvoice, ctx));
invoiceEntity = repo.GetInvoice(invoice.Id, true).GetAwaiter().GetResult(); invoiceEntity = repo.GetInvoice(invoice.Id, true).GetAwaiter().GetResult();
@ -2175,7 +2175,7 @@ namespace BTCPayServer.Tests
Assert.Equal(firstPayment + secondPayment, localInvoice.BtcPaid); Assert.Equal(firstPayment + secondPayment, localInvoice.BtcPaid);
Assert.Equal(Money.Zero, localInvoice.BtcDue); Assert.Equal(Money.Zero, localInvoice.BtcDue);
Assert.Equal(localInvoice.BitcoinAddress, invoiceAddress.ToString()); //no new address generated Assert.Equal(localInvoice.BitcoinAddress, invoiceAddress.ToString()); //no new address generated
Assert.True(IsMapped(localInvoice, ctx)); Assert.True(IsMapped(localInvoice, ctx));
Assert.False((bool)((JValue)localInvoice.ExceptionStatus).Value); Assert.False((bool)((JValue)localInvoice.ExceptionStatus).Value);
}); });

View File

@ -26,9 +26,11 @@ public class AppSales : ViewComponent
public async Task<IViewComponentResult> InvokeAsync(AppSalesViewModel vm) public async Task<IViewComponentResult> InvokeAsync(AppSalesViewModel vm)
{ {
if (vm.App == null) throw new ArgumentNullException(nameof(vm.App)); if (vm.App == null)
if (vm.InitialRendering) return View(vm); throw new ArgumentNullException(nameof(vm.App));
if (vm.InitialRendering)
return View(vm);
var stats = await _appService.GetSalesStats(vm.App); var stats = await _appService.GetSalesStats(vm.App);
vm.SalesCount = stats.SalesCount; vm.SalesCount = stats.SalesCount;

View File

@ -20,13 +20,15 @@ public class AppTopItems : ViewComponent
public async Task<IViewComponentResult> InvokeAsync(AppTopItemsViewModel vm) public async Task<IViewComponentResult> InvokeAsync(AppTopItemsViewModel vm)
{ {
if (vm.App == null) throw new ArgumentNullException(nameof(vm.App)); if (vm.App == null)
if (vm.InitialRendering) return View(vm); throw new ArgumentNullException(nameof(vm.App));
if (vm.InitialRendering)
return View(vm);
var entries = Enum.Parse<AppType>(vm.App.AppType) == AppType.Crowdfund var entries = Enum.Parse<AppType>(vm.App.AppType) == AppType.Crowdfund
? await _appService.GetPerkStats(vm.App) ? await _appService.GetPerkStats(vm.App)
: await _appService.GetItemStats(vm.App); : await _appService.GetItemStats(vm.App);
vm.Entries = entries.ToList(); vm.Entries = entries.ToList();
return View(vm); return View(vm);

View File

@ -76,7 +76,7 @@ namespace BTCPayServer.Components.MainNav
AppName = a.AppName, AppName = a.AppName,
AppType = Enum.Parse<AppType>(a.AppType) AppType = Enum.Parse<AppType>(a.AppType)
}).ToList(); }).ToList();
if (PoliciesSettings.Experimental) if (PoliciesSettings.Experimental)
{ {
// Custodian Accounts // Custodian Accounts

View File

@ -47,26 +47,29 @@ public class StoreLightningBalance : ViewComponent
public async Task<IViewComponentResult> InvokeAsync(StoreLightningBalanceViewModel vm) public async Task<IViewComponentResult> InvokeAsync(StoreLightningBalanceViewModel vm)
{ {
if (vm.Store == null) throw new ArgumentNullException(nameof(vm.Store)); if (vm.Store == null)
if (vm.CryptoCode == null) throw new ArgumentNullException(nameof(vm.CryptoCode)); throw new ArgumentNullException(nameof(vm.Store));
if (vm.CryptoCode == null)
throw new ArgumentNullException(nameof(vm.CryptoCode));
vm.DefaultCurrency = vm.Store.GetStoreBlob().DefaultCurrency; vm.DefaultCurrency = vm.Store.GetStoreBlob().DefaultCurrency;
vm.CurrencyData = _currencies.GetCurrencyData(vm.DefaultCurrency, true); vm.CurrencyData = _currencies.GetCurrencyData(vm.DefaultCurrency, true);
if (vm.InitialRendering) return View(vm); if (vm.InitialRendering)
return View(vm);
try try
{ {
var lightningClient = GetLightningClient(vm.Store, vm.CryptoCode); var lightningClient = GetLightningClient(vm.Store, vm.CryptoCode);
var balance = await lightningClient.GetBalance(); var balance = await lightningClient.GetBalance();
vm.Balance = balance; vm.Balance = balance;
vm.TotalOnchain = balance.OnchainBalance != null vm.TotalOnchain = balance.OnchainBalance != null
? (balance.OnchainBalance.Confirmed?? 0L) + (balance.OnchainBalance.Reserved ?? 0L) + ? (balance.OnchainBalance.Confirmed ?? 0L) + (balance.OnchainBalance.Reserved ?? 0L) +
(balance.OnchainBalance.Unconfirmed ?? 0L) (balance.OnchainBalance.Unconfirmed ?? 0L)
: null; : null;
vm.TotalOffchain = balance.OffchainBalance != null vm.TotalOffchain = balance.OffchainBalance != null
? (balance.OffchainBalance.Opening?? 0) + (balance.OffchainBalance.Local?? 0) + ? (balance.OffchainBalance.Opening ?? 0) + (balance.OffchainBalance.Local ?? 0) +
(balance.OffchainBalance.Closing?? 0) (balance.OffchainBalance.Closing ?? 0)
: null; : null;
} }
catch (NotSupportedException) catch (NotSupportedException)
@ -81,7 +84,7 @@ public class StoreLightningBalance : ViewComponent
} }
return View(vm); return View(vm);
} }
private ILightningClient GetLightningClient(StoreData store, string cryptoCode) private ILightningClient GetLightningClient(StoreData store, string cryptoCode)
{ {
var network = _networkProvider.GetNetwork<BTCPayNetwork>(cryptoCode); var network = _networkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
@ -89,9 +92,10 @@ public class StoreLightningBalance : ViewComponent
var existing = store.GetSupportedPaymentMethods(_networkProvider) var existing = store.GetSupportedPaymentMethods(_networkProvider)
.OfType<LightningSupportedPaymentMethod>() .OfType<LightningSupportedPaymentMethod>()
.FirstOrDefault(d => d.PaymentId == id); .FirstOrDefault(d => d.PaymentId == id);
if (existing == null) return null; if (existing == null)
return null;
if (existing.GetExternalLightningUrl() is {} connectionString)
if (existing.GetExternalLightningUrl() is { } connectionString)
{ {
return _lightningClientFactory.Create(connectionString, network); return _lightningClientFactory.Create(connectionString, network);
} }

View File

@ -34,10 +34,14 @@ public class StoreLightningServices : ViewComponent
public IViewComponentResult Invoke(StoreLightningServicesViewModel vm) public IViewComponentResult Invoke(StoreLightningServicesViewModel vm)
{ {
if (vm.Store == null) throw new ArgumentNullException(nameof(vm.Store)); if (vm.Store == null)
if (vm.CryptoCode == null) throw new ArgumentNullException(nameof(vm.CryptoCode)); throw new ArgumentNullException(nameof(vm.Store));
if (vm.LightningNodeType != LightningNodeType.Internal) return View(vm); if (vm.CryptoCode == null)
if (!User.IsInRole(Roles.ServerAdmin)) return View(vm); throw new ArgumentNullException(nameof(vm.CryptoCode));
if (vm.LightningNodeType != LightningNodeType.Internal)
return View(vm);
if (!User.IsInRole(Roles.ServerAdmin))
return View(vm);
var services = _externalServiceOptions.Value.ExternalServices.ToList() var services = _externalServiceOptions.Value.ExternalServices.ToList()
.Where(service => ExternalServices.LightningServiceTypes.Contains(service.Type)) .Where(service => ExternalServices.LightningServiceTypes.Contains(service.Type))
@ -62,7 +66,7 @@ public class StoreLightningServices : ViewComponent
}) })
.Select(t => t.Result) .Select(t => t.Result)
.ToList(); .ToList();
// other services // other services
foreach ((string key, Uri value) in _externalServiceOptions.Value.OtherExternalServices) foreach ((string key, Uri value) in _externalServiceOptions.Value.OtherExternalServices)
{ {

View File

@ -1,5 +1,4 @@
using System; using System;
using Dapper;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -9,6 +8,7 @@ using BTCPayServer.Data;
using BTCPayServer.Services; using BTCPayServer.Services;
using BTCPayServer.Services.Stores; using BTCPayServer.Services.Stores;
using BTCPayServer.Services.Wallets; using BTCPayServer.Services.Wallets;
using Dapper;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -41,13 +41,16 @@ public class StoreNumbers : ViewComponent
public async Task<IViewComponentResult> InvokeAsync(StoreNumbersViewModel vm) public async Task<IViewComponentResult> InvokeAsync(StoreNumbersViewModel vm)
{ {
if (vm.Store == null) throw new ArgumentNullException(nameof(vm.Store)); if (vm.Store == null)
if (vm.CryptoCode == null) throw new ArgumentNullException(nameof(vm.CryptoCode)); throw new ArgumentNullException(nameof(vm.Store));
if (vm.CryptoCode == null)
throw new ArgumentNullException(nameof(vm.CryptoCode));
vm.WalletId = new WalletId(vm.Store.Id, vm.CryptoCode); vm.WalletId = new WalletId(vm.Store.Id, vm.CryptoCode);
if (vm.InitialRendering) return View(vm); if (vm.InitialRendering)
return View(vm);
await using var ctx = _dbContextFactory.CreateContext(); await using var ctx = _dbContextFactory.CreateContext();
var payoutsCount = await ctx.Payouts var payoutsCount = await ctx.Payouts
.Where(p => p.PullPaymentData.StoreId == vm.Store.Id && !p.PullPaymentData.Archived && p.State == PayoutState.AwaitingApproval) .Where(p => p.PullPaymentData.StoreId == vm.Store.Id && !p.PullPaymentData.Archived && p.State == PayoutState.AwaitingApproval)
@ -55,7 +58,7 @@ public class StoreNumbers : ViewComponent
var refundsCount = await ctx.Invoices var refundsCount = await ctx.Invoices
.Where(i => i.StoreData.Id == vm.Store.Id && !i.Archived && i.CurrentRefundId != null) .Where(i => i.StoreData.Id == vm.Store.Id && !i.Archived && i.CurrentRefundId != null)
.CountAsync(); .CountAsync();
var derivation = vm.Store.GetDerivationSchemeSettings(_networkProvider, vm.CryptoCode); var derivation = vm.Store.GetDerivationSchemeSettings(_networkProvider, vm.CryptoCode);
int? transactionsCount = null; int? transactionsCount = null;
if (derivation != null && _nbxConnectionFactory.Available) if (derivation != null && _nbxConnectionFactory.Available)

View File

@ -35,31 +35,34 @@ public class StoreRecentInvoices : ViewComponent
public async Task<IViewComponentResult> InvokeAsync(StoreRecentInvoicesViewModel vm) public async Task<IViewComponentResult> InvokeAsync(StoreRecentInvoicesViewModel vm)
{ {
if (vm.Store == null) throw new ArgumentNullException(nameof(vm.Store)); if (vm.Store == null)
if (vm.CryptoCode == null) throw new ArgumentNullException(nameof(vm.CryptoCode)); throw new ArgumentNullException(nameof(vm.Store));
if (vm.InitialRendering) return View(vm); if (vm.CryptoCode == null)
throw new ArgumentNullException(nameof(vm.CryptoCode));
if (vm.InitialRendering)
return View(vm);
var userId = _userManager.GetUserId(UserClaimsPrincipal); var userId = _userManager.GetUserId(UserClaimsPrincipal);
var invoiceEntities = await _invoiceRepo.GetInvoices(new InvoiceQuery var invoiceEntities = await _invoiceRepo.GetInvoices(new InvoiceQuery
{ {
UserId = userId, UserId = userId,
StoreId = new [] { vm.Store.Id }, StoreId = new[] { vm.Store.Id },
IncludeArchived = false, IncludeArchived = false,
IncludeRefunds = true, IncludeRefunds = true,
Take = 5 Take = 5
}); });
vm.Invoices = (from invoice in invoiceEntities vm.Invoices = (from invoice in invoiceEntities
let state = invoice.GetInvoiceState() let state = invoice.GetInvoiceState()
select new StoreRecentInvoiceViewModel select new StoreRecentInvoiceViewModel
{ {
Date = invoice.InvoiceTime, Date = invoice.InvoiceTime,
Status = state, Status = state,
HasRefund = invoice.Refunds.Any(), HasRefund = invoice.Refunds.Any(),
InvoiceId = invoice.Id, InvoiceId = invoice.Id,
OrderId = invoice.Metadata.OrderId ?? string.Empty, OrderId = invoice.Metadata.OrderId ?? string.Empty,
AmountCurrency = _currencyNameTable.DisplayFormatCurrency(invoice.Price, invoice.Currency), AmountCurrency = _currencyNameTable.DisplayFormatCurrency(invoice.Price, invoice.Currency),
}).ToList(); }).ToList();
return View(vm); return View(vm);
} }

View File

@ -1,22 +1,22 @@
#nullable enable #nullable enable
using System; using System;
using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Abstractions.Extensions; using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Data; using BTCPayServer.Data;
using BTCPayServer.Services;
using BTCPayServer.Models.StoreViewModels; using BTCPayServer.Models.StoreViewModels;
using BTCPayServer.Services;
using BTCPayServer.Services.Stores; using BTCPayServer.Services.Stores;
using Dapper;
using BTCPayServer.Services.Wallets; using BTCPayServer.Services.Wallets;
using Dapper;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using NBXplorer.Client;
using static BTCPayServer.Components.StoreRecentTransactions.StoreRecentTransactionsViewModel;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NBitcoin; using NBitcoin;
using NBXplorer.Client;
using static BTCPayServer.Components.StoreRecentTransactions.StoreRecentTransactionsViewModel;
namespace BTCPayServer.Components.StoreRecentTransactions; namespace BTCPayServer.Components.StoreRecentTransactions;
@ -35,13 +35,16 @@ public class StoreRecentTransactions : ViewComponent
public async Task<IViewComponentResult> InvokeAsync(StoreRecentTransactionsViewModel vm) public async Task<IViewComponentResult> InvokeAsync(StoreRecentTransactionsViewModel vm)
{ {
if (vm.Store == null) throw new ArgumentNullException(nameof(vm.Store)); if (vm.Store == null)
if (vm.CryptoCode == null) throw new ArgumentNullException(nameof(vm.CryptoCode)); throw new ArgumentNullException(nameof(vm.Store));
if (vm.CryptoCode == null)
throw new ArgumentNullException(nameof(vm.CryptoCode));
vm.WalletId = new WalletId(vm.Store.Id, vm.CryptoCode); vm.WalletId = new WalletId(vm.Store.Id, vm.CryptoCode);
if (vm.InitialRendering) return View(vm); if (vm.InitialRendering)
return View(vm);
var derivationSettings = vm.Store.GetDerivationSchemeSettings(NetworkProvider, vm.CryptoCode); var derivationSettings = vm.Store.GetDerivationSchemeSettings(NetworkProvider, vm.CryptoCode);
var transactions = new List<StoreRecentTransactionViewModel>(); var transactions = new List<StoreRecentTransactionViewModel>();
if (derivationSettings?.AccountDerivation is not null) if (derivationSettings?.AccountDerivation is not null)
@ -63,7 +66,7 @@ public class StoreRecentTransactions : ViewComponent
} }
vm.Transactions = transactions; vm.Transactions = transactions;
return View(vm); return View(vm);
} }
} }

View File

@ -33,7 +33,7 @@ public class StoreWalletBalance : ViewComponent
public StoreWalletBalance( public StoreWalletBalance(
StoreRepository storeRepo, StoreRepository storeRepo,
CurrencyNameTable currencies, CurrencyNameTable currencies,
WalletHistogramService walletHistogramService, WalletHistogramService walletHistogramService,
BTCPayWalletProvider walletProvider, BTCPayWalletProvider walletProvider,
BTCPayNetworkProvider networkProvider) BTCPayNetworkProvider networkProvider)
{ {
@ -50,7 +50,7 @@ public class StoreWalletBalance : ViewComponent
var walletId = new WalletId(store.Id, cryptoCode); var walletId = new WalletId(store.Id, cryptoCode);
var data = await _walletHistogramService.GetHistogram(store, walletId, DefaultType); var data = await _walletHistogramService.GetHistogram(store, walletId, DefaultType);
var defaultCurrency = store.GetStoreBlob().DefaultCurrency; var defaultCurrency = store.GetStoreBlob().DefaultCurrency;
var vm = new StoreWalletBalanceViewModel var vm = new StoreWalletBalanceViewModel
{ {
Store = store, Store = store,
@ -69,7 +69,7 @@ public class StoreWalletBalance : ViewComponent
} }
else else
{ {
using CancellationTokenSource cts = new (TimeSpan.FromSeconds(3)); using CancellationTokenSource cts = new(TimeSpan.FromSeconds(3));
var wallet = _walletProvider.GetWallet(_networkProvider.DefaultNetwork); var wallet = _walletProvider.GetWallet(_networkProvider.DefaultNetwork);
var derivation = store.GetDerivationSchemeSettings(_networkProvider, walletId.CryptoCode); var derivation = store.GetDerivationSchemeSettings(_networkProvider, walletId.CryptoCode);
if (derivation is not null) if (derivation is not null)

View File

@ -42,7 +42,7 @@ namespace BTCPayServer.Components.WalletNav
var wallet = _walletProvider.GetWallet(network); var wallet = _walletProvider.GetWallet(network);
var derivation = store.GetDerivationSchemeSettings(_networkProvider, walletId.CryptoCode); var derivation = store.GetDerivationSchemeSettings(_networkProvider, walletId.CryptoCode);
var balance = await _walletsController.GetBalanceString(wallet, derivation?.AccountDerivation); var balance = await _walletsController.GetBalanceString(wallet, derivation?.AccountDerivation);
var vm = new WalletNavViewModel var vm = new WalletNavViewModel
{ {
WalletId = walletId, WalletId = walletId,

View File

@ -97,7 +97,7 @@ namespace BTCPayServer.Configuration
} }
} }
connectionString.CookieFilePath = null; connectionString.CookieFilePath = null;
if (serviceType == ExternalServiceTypes.RTL || serviceType == ExternalServiceTypes.Configurator || if (serviceType == ExternalServiceTypes.RTL || serviceType == ExternalServiceTypes.Configurator ||
serviceType == ExternalServiceTypes.ThunderHub || serviceType == ExternalServiceTypes.Torq) serviceType == ExternalServiceTypes.ThunderHub || serviceType == ExternalServiceTypes.Torq)
{ {

View File

@ -92,7 +92,7 @@ namespace BTCPayServer.Configuration
&& &&
o.ServiceName.Equals(serviceName, StringComparison.OrdinalIgnoreCase)); o.ServiceName.Equals(serviceName, StringComparison.OrdinalIgnoreCase));
} }
public static readonly ExternalServiceTypes[] LightningServiceTypes = public static readonly ExternalServiceTypes[] LightningServiceTypes =
{ {
ExternalServiceTypes.Spark, ExternalServiceTypes.Spark,
@ -100,7 +100,7 @@ namespace BTCPayServer.Configuration
ExternalServiceTypes.ThunderHub, ExternalServiceTypes.ThunderHub,
ExternalServiceTypes.Torq ExternalServiceTypes.Torq
}; };
public static readonly string[] LightningServiceNames = public static readonly string[] LightningServiceNames =
{ {
"Lightning Terminal" "Lightning Terminal"
@ -114,7 +114,7 @@ namespace BTCPayServer.Configuration
public ExternalConnectionString ConnectionString { get; set; } public ExternalConnectionString ConnectionString { get; set; }
public string CryptoCode { get; set; } public string CryptoCode { get; set; }
public string ServiceName { get; set; } public string ServiceName { get; set; }
public async Task<string> GetLink(Uri absoluteUriNoPathBase, ChainName networkType) public async Task<string> GetLink(Uri absoluteUriNoPathBase, ChainName networkType)
{ {
var connectionString = await ConnectionString.Expand(absoluteUriNoPathBase, Type, networkType); var connectionString = await ConnectionString.Expand(absoluteUriNoPathBase, Type, networkType);

View File

@ -25,7 +25,7 @@ namespace BTCPayServer.Controllers
[Authorize(Policy = ServerPolicies.CanGetRates.Key, AuthenticationSchemes = AuthenticationSchemes.Bitpay)] [Authorize(Policy = ServerPolicies.CanGetRates.Key, AuthenticationSchemes = AuthenticationSchemes.Bitpay)]
public class BitpayRateController : Controller public class BitpayRateController : Controller
{ {
readonly RateFetcher _rateProviderFactory; readonly RateFetcher _rateProviderFactory;
readonly BTCPayNetworkProvider _networkProvider; readonly BTCPayNetworkProvider _networkProvider;
readonly CurrencyNameTable _currencyNameTable; readonly CurrencyNameTable _currencyNameTable;
@ -67,7 +67,7 @@ namespace BTCPayServer.Controllers
public async Task<IActionResult> GetCurrencyPairRate(string baseCurrency, string currency, CancellationToken cancellationToken) public async Task<IActionResult> GetCurrencyPairRate(string baseCurrency, string currency, CancellationToken cancellationToken)
{ {
var result = await GetRates2($"{baseCurrency}_{currency}", null, cancellationToken); var result = await GetRates2($"{baseCurrency}_{currency}", null, cancellationToken);
return (result as JsonResult)?.Value is not Rate[] rates return (result as JsonResult)?.Value is not Rate[] rates
? result ? result
: Json(new DataWrapper<Rate>(rates.First())); : Json(new DataWrapper<Rate>(rates.First()));
} }
@ -152,7 +152,7 @@ namespace BTCPayServer.Controllers
[JsonProperty(PropertyName = "name")] [JsonProperty(PropertyName = "name")]
public string Name { get; set; } public string Name { get; set; }
[JsonProperty(PropertyName = "cryptoCode")] [JsonProperty(PropertyName = "cryptoCode")]
public string CryptoCode { get; set; } public string CryptoCode { get; set; }
@ -161,7 +161,7 @@ namespace BTCPayServer.Controllers
[JsonProperty(PropertyName = "code")] [JsonProperty(PropertyName = "code")]
public string Code { get; set; } public string Code { get; set; }
[JsonProperty(PropertyName = "rate")] [JsonProperty(PropertyName = "rate")]
public decimal Value { get; set; } public decimal Value { get; set; }
} }

View File

@ -3,13 +3,13 @@ using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants; using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Client; using BTCPayServer.Client;
using BTCPayServer.Client.Models; using BTCPayServer.Client.Models;
using BTCPayServer.Data; using BTCPayServer.Data;
using BTCPayServer.Services.Apps; using BTCPayServer.Services.Apps;
using BTCPayServer.Services.Rates; using BTCPayServer.Services.Rates;
using BTCPayServer.Services.Stores; using BTCPayServer.Services.Stores;
using BTCPayServer.Abstractions.Extensions;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
@ -86,7 +86,7 @@ namespace BTCPayServer.Controllers.Greenfield
{ {
return validationResult; return validationResult;
} }
var appData = new AppData var appData = new AppData
{ {
StoreDataId = storeId, StoreDataId = storeId,
@ -112,7 +112,7 @@ namespace BTCPayServer.Controllers.Greenfield
} }
var settings = app.GetSettings<PointOfSaleSettings>(); var settings = app.GetSettings<PointOfSaleSettings>();
// This is not obvious but we must have a non-null currency or else request validation may work incorrectly // This is not obvious but we must have a non-null currency or else request validation may work incorrectly
request.Currency = request.Currency ?? settings.Currency; request.Currency = request.Currency ?? settings.Currency;
@ -124,7 +124,7 @@ namespace BTCPayServer.Controllers.Greenfield
app.Name = request.AppName; app.Name = request.AppName;
app.SetSettings(ToPointOfSaleSettings(request)); app.SetSettings(ToPointOfSaleSettings(request));
await _appService.UpdateOrCreateApp(app); await _appService.UpdateOrCreateApp(app);
return Ok(ToPointOfSaleModel(app)); return Ok(ToPointOfSaleModel(app));
@ -152,7 +152,7 @@ namespace BTCPayServer.Controllers.Greenfield
{ {
return AppNotFound(); return AppNotFound();
} }
return Ok(ToModel(app)); return Ok(ToModel(app));
} }
@ -164,7 +164,7 @@ namespace BTCPayServer.Controllers.Greenfield
{ {
return AppNotFound(); return AppNotFound();
} }
await _appService.DeleteApp(app); await _appService.DeleteApp(app);
return Ok(); return Ok();
@ -281,7 +281,7 @@ namespace BTCPayServer.Controllers.Greenfield
ModelState.AddModelError(nameof(request.Template), "Invalid template"); ModelState.AddModelError(nameof(request.Template), "Invalid template");
} }
} }
if (!ModelState.IsValid) if (!ModelState.IsValid)
{ {
validationResult = this.CreateValidationError(ModelState); validationResult = this.CreateValidationError(ModelState);
@ -304,7 +304,7 @@ namespace BTCPayServer.Controllers.Greenfield
private string[]? ValidateStringArray(string[]? arr) private string[]? ValidateStringArray(string[]? arr)
{ {
if (arr == null || !arr.Any()) if (arr == null || !arr.Any())
{ {
return null; return null;
} }
@ -359,7 +359,7 @@ namespace BTCPayServer.Controllers.Greenfield
{ {
ModelState.AddModelError(nameof(request.EndDate), "End date cannot be before start date"); ModelState.AddModelError(nameof(request.EndDate), "End date cannot be before start date");
} }
if (!ModelState.IsValid) if (!ModelState.IsValid)
{ {
validationResult = this.CreateValidationError(ModelState); validationResult = this.CreateValidationError(ModelState);

View File

@ -91,7 +91,7 @@ namespace BTCPayServer.Controllers.Greenfield
var custodianAccount = await ToModel(custodianAccountData, assetBalances, cancellationToken); var custodianAccount = await ToModel(custodianAccountData, assetBalances, cancellationToken);
return Ok(custodianAccount); return Ok(custodianAccount);
} }
// [HttpGet("~/api/v1/stores/{storeId}/custodian-accounts/{accountId}/config")] // [HttpGet("~/api/v1/stores/{storeId}/custodian-accounts/{accountId}/config")]
// [Authorize(Policy = Policies.CanManageCustodianAccounts, // [Authorize(Policy = Policies.CanManageCustodianAccounts,
// AuthenticationSchemes = AuthenticationSchemes.Greenfield)] // AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
@ -276,7 +276,7 @@ namespace BTCPayServer.Controllers.Greenfield
} }
catch (CustodianApiException e) catch (CustodianApiException e)
{ {
return this.CreateAPIError(e.HttpStatus, e.Code, return this.CreateAPIError(e.HttpStatus, e.Code,
e.Message); e.Message);
} }
} }

View File

@ -11,10 +11,10 @@ using BTCPayServer.Client.Models;
using BTCPayServer.Data; using BTCPayServer.Data;
using BTCPayServer.HostedServices; using BTCPayServer.HostedServices;
using BTCPayServer.Payments; using BTCPayServer.Payments;
using BTCPayServer.Rating;
using BTCPayServer.Services; using BTCPayServer.Services;
using BTCPayServer.Services.Invoices; using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Rates; using BTCPayServer.Services.Rates;
using BTCPayServer.Rating;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -425,21 +425,22 @@ namespace BTCPayServer.Controllers.Greenfield
createPullPayment.Amount = cryptoPaid.RoundToSignificant(paymentMethodDivisibility); createPullPayment.Amount = cryptoPaid.RoundToSignificant(paymentMethodDivisibility);
createPullPayment.AutoApproveClaims = true; createPullPayment.AutoApproveClaims = true;
break; break;
case RefundVariant.CurrentRate: case RefundVariant.CurrentRate:
createPullPayment.Currency = invoicePaymentMethod.GetId().CryptoCode; createPullPayment.Currency = invoicePaymentMethod.GetId().CryptoCode;
createPullPayment.Amount = Math.Round(paidCurrency / rateResult.BidAsk.Bid, paymentMethodDivisibility); createPullPayment.Amount = Math.Round(paidCurrency / rateResult.BidAsk.Bid, paymentMethodDivisibility);
createPullPayment.AutoApproveClaims = true; createPullPayment.AutoApproveClaims = true;
break; break;
case RefundVariant.Fiat: case RefundVariant.Fiat:
createPullPayment.Currency = invoice.Currency; createPullPayment.Currency = invoice.Currency;
createPullPayment.Amount = paidCurrency; createPullPayment.Amount = paidCurrency;
createPullPayment.AutoApproveClaims = false; createPullPayment.AutoApproveClaims = false;
break; break;
case RefundVariant.Custom: case RefundVariant.Custom:
if (request.CustomAmount is null || (request.CustomAmount is decimal v && v <= 0)) { if (request.CustomAmount is null || (request.CustomAmount is decimal v && v <= 0))
{
this.ModelState.AddModelError(nameof(request.CustomAmount), "Amount must be greater than 0"); this.ModelState.AddModelError(nameof(request.CustomAmount), "Amount must be greater than 0");
} }
@ -466,14 +467,14 @@ namespace BTCPayServer.Controllers.Greenfield
createPullPayment.Amount = request.CustomAmount.Value; createPullPayment.Amount = request.CustomAmount.Value;
createPullPayment.AutoApproveClaims = paymentMethodId.CryptoCode == request.CustomCurrency; createPullPayment.AutoApproveClaims = paymentMethodId.CryptoCode == request.CustomCurrency;
break; break;
default: default:
ModelState.AddModelError(nameof(request.RefundVariant), "Please select a valid refund option"); ModelState.AddModelError(nameof(request.RefundVariant), "Please select a valid refund option");
return this.CreateValidationError(ModelState); return this.CreateValidationError(ModelState);
} }
var ppId = await _pullPaymentService.CreatePullPayment(createPullPayment); var ppId = await _pullPaymentService.CreatePullPayment(createPullPayment);
await using var ctx = _dbContextFactory.CreateContext(); await using var ctx = _dbContextFactory.CreateContext();
(await ctx.Invoices.FindAsync(new[] { invoice.Id }, cancellationToken))!.CurrentRefundId = ppId; (await ctx.Invoices.FindAsync(new[] { invoice.Id }, cancellationToken))!.CurrentRefundId = ppId;
ctx.Refunds.Add(new RefundData ctx.Refunds.Add(new RefundData
@ -599,7 +600,7 @@ namespace BTCPayServer.Controllers.Greenfield
Amount = entity.Price, Amount = entity.Price,
Type = entity.Type, Type = entity.Type,
Id = entity.Id, Id = entity.Id,
CheckoutLink = request is null? null: linkGenerator.CheckoutLink(entity.Id, request.Scheme, request.Host, request.PathBase), CheckoutLink = request is null ? null : linkGenerator.CheckoutLink(entity.Id, request.Scheme, request.Host, request.PathBase),
Status = entity.Status.ToModernStatus(), Status = entity.Status.ToModernStatus(),
AdditionalStatus = entity.ExceptionStatus, AdditionalStatus = entity.ExceptionStatus,
Currency = entity.Currency, Currency = entity.Currency,

View File

@ -40,7 +40,7 @@ namespace BTCPayServer.Controllers.Greenfield
_lightningClientFactory = lightningClientFactory; _lightningClientFactory = lightningClientFactory;
_btcPayNetworkProvider = btcPayNetworkProvider; _btcPayNetworkProvider = btcPayNetworkProvider;
} }
[Authorize(Policy = Policies.CanUseLightningNodeInStore, [Authorize(Policy = Policies.CanUseLightningNodeInStore,
AuthenticationSchemes = AuthenticationSchemes.Greenfield)] AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
[HttpGet("~/api/v1/stores/{storeId}/lightning/{cryptoCode}/info")] [HttpGet("~/api/v1/stores/{storeId}/lightning/{cryptoCode}/info")]
@ -135,13 +135,13 @@ namespace BTCPayServer.Controllers.Greenfield
{ {
throw ErrorCryptoCodeNotFound(); throw ErrorCryptoCodeNotFound();
} }
var store = HttpContext.GetStoreData(); var store = HttpContext.GetStoreData();
if (store == null) if (store == null)
{ {
throw new JsonHttpException(StoreNotFound()); throw new JsonHttpException(StoreNotFound());
} }
var id = new PaymentMethodId(cryptoCode, PaymentTypes.LightningLike); var id = new PaymentMethodId(cryptoCode, PaymentTypes.LightningLike);
var existing = store.GetSupportedPaymentMethods(_btcPayNetworkProvider) var existing = store.GetSupportedPaymentMethods(_btcPayNetworkProvider)
.OfType<LightningSupportedPaymentMethod>() .OfType<LightningSupportedPaymentMethod>()
@ -164,7 +164,7 @@ namespace BTCPayServer.Controllers.Greenfield
} }
throw ErrorLightningNodeNotConfiguredForStore(); throw ErrorLightningNodeNotConfiguredForStore();
} }
private IActionResult StoreNotFound() private IActionResult StoreNotFound()
{ {
return this.CreateAPIError(404, "store-not-found", "The store was not found"); return this.CreateAPIError(404, "store-not-found", "The store was not found");

View File

@ -23,7 +23,7 @@ namespace BTCPayServer.Controllers.Greenfield
// Do not mark handled, it is possible filters above have better errors // Do not mark handled, it is possible filters above have better errors
} }
} }
public abstract class GreenfieldLightningNodeApiController : Controller public abstract class GreenfieldLightningNodeApiController : Controller
{ {
private readonly BTCPayNetworkProvider _btcPayNetworkProvider; private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
@ -209,7 +209,7 @@ namespace BTCPayServer.Controllers.Greenfield
var lightningClient = await GetLightningClient(cryptoCode, true); var lightningClient = await GetLightningClient(cryptoCode, true);
var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode); var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
BOLT11PaymentRequest bolt11 = null; BOLT11PaymentRequest bolt11 = null;
if (string.IsNullOrEmpty(lightningInvoice.BOLT11) || if (string.IsNullOrEmpty(lightningInvoice.BOLT11) ||
!BOLT11PaymentRequest.TryParse(lightningInvoice.BOLT11, out bolt11, network.NBitcoinNetwork)) !BOLT11PaymentRequest.TryParse(lightningInvoice.BOLT11, out bolt11, network.NBitcoinNetwork))
{ {
@ -220,7 +220,7 @@ namespace BTCPayServer.Controllers.Greenfield
{ {
return this.CreateValidationError(ModelState); return this.CreateValidationError(ModelState);
} }
var param = lightningInvoice.MaxFeeFlat != null || lightningInvoice.MaxFeePercent != null var param = lightningInvoice.MaxFeeFlat != null || lightningInvoice.MaxFeePercent != null
|| lightningInvoice.Amount != null || lightningInvoice.SendTimeout != null || lightningInvoice.Amount != null || lightningInvoice.SendTimeout != null
? new PayInvoiceParams ? new PayInvoiceParams
@ -232,12 +232,12 @@ namespace BTCPayServer.Controllers.Greenfield
} }
: null; : null;
var result = await lightningClient.Pay(lightningInvoice.BOLT11, param, cancellationToken); var result = await lightningClient.Pay(lightningInvoice.BOLT11, param, cancellationToken);
if (result.Result is PayResult.Ok or PayResult.Unknown && bolt11?.PaymentHash is not null) if (result.Result is PayResult.Ok or PayResult.Unknown && bolt11?.PaymentHash is not null)
{ {
// get a new instance of the LN client, because the old one might have disposed its HTTPClient // get a new instance of the LN client, because the old one might have disposed its HTTPClient
lightningClient = await GetLightningClient(cryptoCode, true); lightningClient = await GetLightningClient(cryptoCode, true);
var paymentHash = bolt11.PaymentHash.ToString(); var paymentHash = bolt11.PaymentHash.ToString();
var payment = await lightningClient.GetPayment(paymentHash, cancellationToken); var payment = await lightningClient.GetPayment(paymentHash, cancellationToken);
var data = new LightningPaymentData var data = new LightningPaymentData
@ -253,7 +253,7 @@ namespace BTCPayServer.Controllers.Greenfield
}; };
return result.Result is PayResult.Ok ? Ok(data) : Accepted(data); return result.Result is PayResult.Ok ? Ok(data) : Accepted(data);
} }
return result.Result switch return result.Result switch
{ {
PayResult.CouldNotFindRoute => this.CreateAPIError("could-not-find-route", "Impossible to find a route to the peer"), PayResult.CouldNotFindRoute => this.CreateAPIError("could-not-find-route", "Impossible to find a route to the peer"),
@ -265,7 +265,7 @@ namespace BTCPayServer.Controllers.Greenfield
PayResult.Ok => Ok(new LightningPaymentData PayResult.Ok => Ok(new LightningPaymentData
{ {
Status = LightningPaymentStatus.Complete, Status = LightningPaymentStatus.Complete,
TotalAmount = result.Details?.TotalAmount, TotalAmount = result.Details?.TotalAmount,
FeeAmount = result.Details?.FeeAmount FeeAmount = result.Details?.FeeAmount
}), }),
_ => throw new NotSupportedException("Unsupported PayResult") _ => throw new NotSupportedException("Unsupported PayResult")
@ -308,14 +308,14 @@ namespace BTCPayServer.Controllers.Greenfield
{ {
return this.CreateValidationError(ModelState); return this.CreateValidationError(ModelState);
} }
request.Description ??= ""; request.Description ??= "";
try try
{ {
var param = new CreateInvoiceParams(request.Amount, request.Description, request.Expiry) var param = new CreateInvoiceParams(request.Amount, request.Description, request.Expiry)
{ {
PrivateRouteHints = request.PrivateRouteHints, PrivateRouteHints = request.PrivateRouteHints,
DescriptionHashOnly = request.DescriptionHashOnly DescriptionHashOnly = request.DescriptionHashOnly
}; };
var invoice = await lightningClient.CreateInvoice(param, cancellationToken); var invoice = await lightningClient.CreateInvoice(param, cancellationToken);
return Ok(ToModel(invoice)); return Ok(ToModel(invoice));

View File

@ -20,7 +20,7 @@ namespace BTCPayServer.Controllers.Greenfield
{ {
private readonly IEnumerable<IPayoutProcessorFactory> _factories; private readonly IEnumerable<IPayoutProcessorFactory> _factories;
public GreenfieldPayoutProcessorsController(IEnumerable<IPayoutProcessorFactory>factories) public GreenfieldPayoutProcessorsController(IEnumerable<IPayoutProcessorFactory> factories)
{ {
_factories = factories; _factories = factories;
} }
@ -39,5 +39,5 @@ namespace BTCPayServer.Controllers.Greenfield
} }
} }
} }

View File

@ -199,10 +199,10 @@ namespace BTCPayServer.Controllers.Greenfield
var pp = await _pullPaymentService.GetPullPayment(pullPaymentId, true); var pp = await _pullPaymentService.GetPullPayment(pullPaymentId, true);
if (pp is null) if (pp is null)
return PullPaymentNotFound(); return PullPaymentNotFound();
var payouts =await _pullPaymentService.GetPayouts(new PullPaymentHostedService.PayoutQuery() var payouts = await _pullPaymentService.GetPayouts(new PullPaymentHostedService.PayoutQuery()
{ {
PullPayments = new[] {pullPaymentId}, PullPayments = new[] { pullPaymentId },
States = GetStateFilter(includeCancelled) States = GetStateFilter(includeCancelled)
}); });
return base.Ok(payouts return base.Ok(payouts
@ -219,10 +219,11 @@ namespace BTCPayServer.Controllers.Greenfield
var payout = (await _pullPaymentService.GetPayouts(new PullPaymentHostedService.PayoutQuery() var payout = (await _pullPaymentService.GetPayouts(new PullPaymentHostedService.PayoutQuery()
{ {
PullPayments = new[] {pullPaymentId}, PayoutIds = new[] {payoutId} PullPayments = new[] { pullPaymentId },
PayoutIds = new[] { payoutId }
})).FirstOrDefault(); })).FirstOrDefault();
if (payout is null) if (payout is null)
return PayoutNotFound(); return PayoutNotFound();
return base.Ok(ToModel(payout)); return base.Ok(ToModel(payout));
@ -291,17 +292,17 @@ namespace BTCPayServer.Controllers.Greenfield
ModelState.AddModelError(nameof(request.Amount), $"Amount too small (should be at least {ppBlob.MinimumClaim})"); ModelState.AddModelError(nameof(request.Amount), $"Amount too small (should be at least {ppBlob.MinimumClaim})");
return this.CreateValidationError(ModelState); return this.CreateValidationError(ModelState);
} }
var result = await _pullPaymentService.Claim(new ClaimRequest() var result = await _pullPaymentService.Claim(new ClaimRequest()
{ {
Destination = destination.destination, Destination = destination.destination,
PullPaymentId = pullPaymentId, PullPaymentId = pullPaymentId,
Value = request.Amount, Value = request.Amount,
PaymentMethodId = paymentMethodId, PaymentMethodId = paymentMethodId,
}); });
return HandleClaimResult(result); return HandleClaimResult(result);
} }
[HttpPost("~/api/v1/stores/{storeId}/payouts")] [HttpPost("~/api/v1/stores/{storeId}/payouts")]
[Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] [Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
public async Task<IActionResult> CreatePayoutThroughStore(string storeId, CreatePayoutThroughStoreRequest request) public async Task<IActionResult> CreatePayoutThroughStore(string storeId, CreatePayoutThroughStoreRequest request)
@ -413,11 +414,11 @@ namespace BTCPayServer.Controllers.Greenfield
{ {
var payouts = await _pullPaymentService.GetPayouts(new PullPaymentHostedService.PayoutQuery() var payouts = await _pullPaymentService.GetPayouts(new PullPaymentHostedService.PayoutQuery()
{ {
Stores = new[] {storeId}, Stores = new[] { storeId },
States = GetStateFilter(includeCancelled) States = GetStateFilter(includeCancelled)
}); });
return base.Ok(payouts return base.Ok(payouts
.Select(ToModel).ToArray()); .Select(ToModel).ToArray());
} }
@ -426,7 +427,7 @@ namespace BTCPayServer.Controllers.Greenfield
[Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] [Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
public async Task<IActionResult> CancelPayout(string storeId, string payoutId) public async Task<IActionResult> CancelPayout(string storeId, string payoutId)
{ {
var res= await _pullPaymentService.Cancel(new PullPaymentHostedService.CancelRequest(new[] { payoutId }, new []{storeId})); var res = await _pullPaymentService.Cancel(new PullPaymentHostedService.CancelRequest(new[] { payoutId }, new[] { storeId }));
return MapResult(res.First().Value); return MapResult(res.First().Value);
} }
@ -530,14 +531,15 @@ namespace BTCPayServer.Controllers.Greenfield
var payout = (await _pullPaymentService.GetPayouts(new PullPaymentHostedService.PayoutQuery() var payout = (await _pullPaymentService.GetPayouts(new PullPaymentHostedService.PayoutQuery()
{ {
Stores = new[] {storeId}, PayoutIds = new[] {payoutId} Stores = new[] { storeId },
PayoutIds = new[] { payoutId }
})).FirstOrDefault(); })).FirstOrDefault();
if (payout is null) if (payout is null)
return PayoutNotFound(); return PayoutNotFound();
return base.Ok(ToModel(payout)); return base.Ok(ToModel(payout));
} }
private IActionResult MapResult(MarkPayoutRequest.PayoutPaidResult result) private IActionResult MapResult(MarkPayoutRequest.PayoutPaidResult result)
{ {
var errorMessage = MarkPayoutRequest.GetErrorMessage(result); var errorMessage = MarkPayoutRequest.GetErrorMessage(result);

View File

@ -42,9 +42,9 @@ namespace BTCPayServer.Controllers.Greenfield
await _payoutProcessorService.GetProcessors( await _payoutProcessorService.GetProcessors(
new PayoutProcessorService.PayoutProcessorQuery() new PayoutProcessorService.PayoutProcessorQuery()
{ {
Stores = new[] {storeId}, Stores = new[] { storeId },
Processors = new[] {LightningAutomatedPayoutSenderFactory.ProcessorName}, Processors = new[] { LightningAutomatedPayoutSenderFactory.ProcessorName },
PaymentMethods = paymentMethod is null ? null : new[] {paymentMethod} PaymentMethods = paymentMethod is null ? null : new[] { paymentMethod }
}); });
return Ok(configured.Select(ToModel).ToArray()); return Ok(configured.Select(ToModel).ToArray());
@ -61,7 +61,7 @@ namespace BTCPayServer.Controllers.Greenfield
private static AutomatedPayoutBlob FromModel(LightningAutomatedPayoutSettings data) private static AutomatedPayoutBlob FromModel(LightningAutomatedPayoutSettings data)
{ {
return new AutomatedPayoutBlob() {Interval = data.IntervalSeconds}; return new AutomatedPayoutBlob() { Interval = data.IntervalSeconds };
} }
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
@ -75,9 +75,9 @@ namespace BTCPayServer.Controllers.Greenfield
(await _payoutProcessorService.GetProcessors( (await _payoutProcessorService.GetProcessors(
new PayoutProcessorService.PayoutProcessorQuery() new PayoutProcessorService.PayoutProcessorQuery()
{ {
Stores = new[] {storeId}, Stores = new[] { storeId },
Processors = new[] {LightningAutomatedPayoutSenderFactory.ProcessorName}, Processors = new[] { LightningAutomatedPayoutSenderFactory.ProcessorName },
PaymentMethods = new[] {paymentMethod} PaymentMethods = new[] { paymentMethod }
})) }))
.FirstOrDefault(); .FirstOrDefault();
activeProcessor ??= new PayoutProcessorData(); activeProcessor ??= new PayoutProcessorData();
@ -88,7 +88,9 @@ namespace BTCPayServer.Controllers.Greenfield
var tcs = new TaskCompletionSource(); var tcs = new TaskCompletionSource();
_eventAggregator.Publish(new PayoutProcessorUpdated() _eventAggregator.Publish(new PayoutProcessorUpdated()
{ {
Data = activeProcessor, Id = activeProcessor.Id, Processed = tcs Data = activeProcessor,
Id = activeProcessor.Id,
Processed = tcs
}); });
await tcs.Task; await tcs.Task;
return Ok(ToModel(activeProcessor)); return Ok(ToModel(activeProcessor));

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