Compare commits

..

1 Commits

587 changed files with 20307 additions and 18436 deletions
.circleci
.editorconfig.gitignore
.run
BTCPayServer.Abstractions
BTCPayServer.Client
BTCPayServer.Common
BTCPayServer.Data
BTCPayServer.PluginPacker
BTCPayServer.Plugins.Test
BTCPayServer.Rating
BTCPayServer.Tests
BTCPayServer
BTCPayServer.csprojBufferizedFormFile.cs
Components
Configuration
Controllers
BitpayRateController.cs
GreenField
LightningAddressService.csLnurlAuthService.csUIAccountController.csUIAppsController.Dashboard.csUIAppsController.csUICustodianAccountsController.csUIHomeController.csUIInvoiceController.Testing.csUIInvoiceController.UI.csUIInvoiceController.csUILNURLAuthController.csUILNURLController.csUIManageController.APIKeys.csUIManageController.csUIPaymentRequestController.csUIPullPaymentController.csUIServerController.Plugins.csUIServerController.Storage.csUIServerController.Users.csUIServerController.csUIStorePullPaymentsController.PullPayments.csUIStoresController.Dashboard.csUIStoresController.Email.csUIStoresController.Integrations.csUIStoresController.LightningLike.csUIStoresController.Onchain.csUIStoresController.csUIUserStoresController.csUIWalletsController.PSBT.csUIWalletsController.cs
Data
DerivationSchemeParser.csDerivationSchemeSettings.csExtensions.cs
Extensions
Fido2
FileTypeDetector.cs
Filters
Forms
GitCommitAttribute.cs
HostedServices
Hosting
Models
PaymentRequest
Payments
PayoutProcessors
Plugins
Program.cs
Properties
Roles.cs
Security/GreenField
Services
Storage
TagHelpers
Views
Shared
UIAccount
UIApps
UICustodianAccounts
UIError
UIForms
UIHome
UIInvoice
UILNURL
UIManage
UIPaymentRequest
UIPayoutProcessors
UIPublicLightningNodeInfo
UIPullPayment
UIServer
UIShopify
UIStorePullPayments
UIStores
UIUserStores
UIWallets
wwwroot
Build
Changelog.mdLICENSE
Plugins
BTCPayServer.Plugins.Custodians.FakeCustodian
BTCPayServer.Plugins.Test/Views/Shared
README.mdamd64.Dockerfilearm32v7.Dockerfilearm64v8.Dockerfilebtcpayserver.sln
docs

@ -41,10 +41,9 @@ jobs:
- run:
command: |
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
GIT_COMMIT=$(git rev-parse HEAD)
#
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --pull -t $DOCKERHUB_REPO:$LATEST_TAG-amd64 -f amd64.Dockerfile .
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --pull --build-arg CONFIGURATION_NAME=Altcoins-Release -t $DOCKERHUB_REPO:$LATEST_TAG-altcoins-amd64 -f amd64.Dockerfile .
sudo docker build --pull -t $DOCKERHUB_REPO:$LATEST_TAG-amd64 -f amd64.Dockerfile .
sudo docker build --pull --build-arg CONFIGURATION_NAME=Altcoins-Release -t $DOCKERHUB_REPO:$LATEST_TAG-altcoins-amd64 -f amd64.Dockerfile .
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-amd64
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-altcoins-amd64
@ -58,10 +57,9 @@ jobs:
command: |
sudo docker run --rm --privileged multiarch/qemu-user-static:register --reset
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
GIT_COMMIT=$(git rev-parse HEAD)
#
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --pull -t $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 -f arm32v7.Dockerfile .
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --pull --build-arg CONFIGURATION_NAME=Altcoins-Release -t $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm32v7 -f arm32v7.Dockerfile .
sudo docker build --pull -t $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 -f arm32v7.Dockerfile .
sudo docker build --pull --build-arg CONFIGURATION_NAME=Altcoins-Release -t $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm32v7 -f arm32v7.Dockerfile .
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-arm32v7
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm32v7
@ -75,10 +73,9 @@ jobs:
command: |
sudo docker run --rm --privileged multiarch/qemu-user-static:register --reset
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
GIT_COMMIT=$(git rev-parse HEAD)
#
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --pull -t $DOCKERHUB_REPO:$LATEST_TAG-arm64v8 -f arm64v8.Dockerfile .
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --build-arg CONFIGURATION_NAME=Altcoins-Release --pull -t $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm64v8 -f arm64v8.Dockerfile .
sudo docker build --pull -t $DOCKERHUB_REPO:$LATEST_TAG-arm64v8 -f arm64v8.Dockerfile .
sudo docker build --build-arg CONFIGURATION_NAME=Altcoins-Release --pull -t $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm64v8 -f arm64v8.Dockerfile .
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-arm64v8
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm64v8

@ -11,14 +11,10 @@ insert_final_newline = true
indent_style = space
indent_size = 4
charset = utf-8
space_before_self_closing = true
[*.json]
[launchSettings.json]
indent_size = 2
[swagger*.json]
indent_size = 4
# C# files
[*.cs]
# New line preferences
@ -71,7 +67,7 @@ dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
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.capitalization = camel_case
dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
# Code style defaults
dotnet_sort_system_directives_first = true

1
.gitignore vendored

@ -298,4 +298,3 @@ Packed Plugins
Plugins/packed
BTCPayServer/wwwroot/swagger/v1/openapi.json
BTCPayServer/appsettings.dev.json

@ -14,8 +14,8 @@
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" />
<method v="2">
<option name="Build" default="false" projectName="BTCPayServer.Plugins.Test" projectPath="C:\Git\btcpayserver\Plugins\BTCPayServer.Plugins.Test\BTCPayServer.Plugins.Test.csproj" />
<option name="Build" default="false" projectName="BTCPayServer.Plugins.Test" projectPath="C:\Git\btcpayserver\BTCPayServer.Plugins.Test\BTCPayServer.Plugins.Test.csproj" />
<option name="Build" />
</method>
</configuration>
</component>
</component>

@ -1,18 +0,0 @@
using Newtonsoft.Json;
namespace BTCPayServer.Abstractions
{
public class CamelCaseSerializerSettings
{
static CamelCaseSerializerSettings()
{
Settings = new JsonSerializerSettings()
{
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
};
Serializer = JsonSerializer.Create(Settings);
}
public static readonly JsonSerializerSettings Settings;
public static readonly JsonSerializer Serializer;
}
}

@ -1,5 +1,3 @@
using System.IO;
namespace BTCPayServer.Configuration
{
public class DataDirectories
@ -9,12 +7,5 @@ namespace BTCPayServer.Configuration
public string TempStorageDir { get; set; }
public string StorageDir { get; set; }
public string TempDir { get; set; }
public string ToDatadirFullPath(string path)
{
if (Path.IsPathRooted(path))
return path;
return Path.Combine(DataDir, path);
}
}
}

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

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

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

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

@ -9,7 +9,7 @@ public class 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;
ToAsset = toAsset;

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

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

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

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

@ -4,7 +4,7 @@ public class TradeNotFoundException : CustodianApiException
{
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;
}

@ -1,6 +1,6 @@
namespace BTCPayServer.Abstractions.Custodians;
public class WrongTradingPairException : CustodianApiException
public class WrongTradingPairException: CustodianApiException
{
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}.")

@ -9,16 +9,18 @@ namespace BTCPayServer.Abstractions.Custodians;
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",...]
*/
public List<AssetPairData> GetTradableAssetPairs();
/**
* Execute a market order right now.
*/
public Task<MarketTradeResult> TradeMarketAsync(string fromAsset, string toAsset, decimal qty, JObject config, CancellationToken cancellationToken);
/**
* Get the details about a previous market trade.
*/

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

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

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

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

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

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

@ -1,64 +1,35 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Abstractions.Form;
public class Field
public abstract class Field
{
public static Field Create(string label, string name, string value, bool required, string helpText, string type = "text")
{
return new Field()
{
Label = label,
Name = name,
Value = value,
OriginalValue = value,
Required = required,
HelpText = helpText,
Type = type
};
}
// The name of the HTML5 node. Should be used as the key for the posted data.
public string Name;
public bool Hidden;
// HTML5 compatible type string like "text", "textarea", "email", "password", etc. Each type is a class and may contain more fields (i.e. "select" would have options).
public string Type;
public static Field CreateFieldset()
{
return new Field() { Type = "fieldset" };
}
// The name of the HTML5 node. Should be used as the key for the posted data.
public string Name;
// The translated label of the field.
public string Label;
// The value field is what is currently in the DB or what the user entered, but possibly not saved yet due to validation errors.
// If this is the first the user sees the form, then value and original value are the same. Value changes as the user starts interacting with the form.
public string Value;
public bool Required;
// The translated label of the field.
public string Label;
// The original value is the value that is currently saved in the backend. A "reset" button can be used to revert back to this. Should only be set from the constructor.
public string OriginalValue;
// A useful note shown below the field or via a tooltip / info icon. Should be translated for the user.
public string HelpText;
[JsonExtensionData] public IDictionary<string, JToken> AdditionalData { get; set; }
public List<Field> Fields { get; set; } = new();
// The field is considered "valid" if there are no validation errors
public List<string> ValidationErrors = new List<string>();
public virtual bool IsValid()
public bool Required = false;
public bool IsValid()
{
return ValidationErrors.Count == 0 && Fields.All(field => field.IsValid());
return ValidationErrors.Count == 0;
}
}

@ -0,0 +1,14 @@
using System.Collections.Generic;
namespace BTCPayServer.Abstractions.Form;
public class Fieldset
{
public Fieldset()
{
this.Fields = new List<Field>();
}
public string Label { get; set; }
public List<Field> Fields { get; set; }
}

@ -1,157 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Abstractions.Form;
public class Form
{
#nullable enable
public static Form Parse(string str)
{
ArgumentNullException.ThrowIfNull(str);
return JObject.Parse(str).ToObject<Form>(CamelCaseSerializerSettings.Serializer) ?? throw new InvalidOperationException("Impossible to deserialize Form");
}
public override string ToString()
{
return JObject.FromObject(this, CamelCaseSerializerSettings.Serializer).ToString(Newtonsoft.Json.Formatting.Indented);
}
#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...
public List<AlertMessage> TopMessages { get; set; } = new();
// Groups of fields in the form
public List<Field> Fields { get; set; } = new();
public List<Fieldset> Fieldsets { get; set; } = new();
// Are all the fields valid in the form?
public bool IsValid()
{
return Fields.Select(f => f.IsValid()).All(o => o);
foreach (var fieldset in Fieldsets)
{
foreach (var field in fieldset.Fields)
{
if (!field.IsValid())
{
return false;
}
}
}
return true;
}
public Field GetFieldByName(string name)
{
return GetFieldByName(name, Fields, null);
}
private static Field GetFieldByName(string name, List<Field> fields, string prefix)
{
prefix ??= string.Empty;
foreach (var field in fields)
foreach (var fieldset in Fieldsets)
{
var currentPrefix = prefix;
if (!string.IsNullOrEmpty(field.Name))
foreach (var field in fieldset.Fields)
{
currentPrefix = $"{prefix}{field.Name}";
if (currentPrefix.Equals(name, StringComparison.InvariantCultureIgnoreCase))
if (name.Equals(field.Name))
{
return field;
}
currentPrefix += "_";
}
var subFieldResult = GetFieldByName(name, field.Fields, currentPrefix);
if (subFieldResult is not null)
{
return subFieldResult;
}
}
return null;
}
public List<string> GetAllNames()
{
return GetAllNames(Fields);
}
private static List<string> GetAllNames(List<Field> fields)
{
var names = new List<string>();
foreach (var field in fields)
foreach (var fieldset in Fieldsets)
{
string prefix = string.Empty;
if (!string.IsNullOrEmpty(field.Name))
foreach (var field in fieldset.Fields)
{
names.Add(field.Name);
prefix = $"{field.Name}_";
}
if (field.Fields.Any())
{
names.AddRange(GetAllNames(field.Fields).Select(s => $"{prefix}{s}"));
}
}
return names;
}
public void ApplyValuesFromOtherForm(Form form)
{
foreach (var fieldset in Fields)
{
foreach (var field in fieldset.Fields)
{
field.Value = form
.GetFieldByName(
$"{(string.IsNullOrEmpty(fieldset.Name) ? string.Empty : fieldset.Name + "_")}{field.Name}")
?.Value;
}
}
}
public void ApplyValuesFromForm(IFormCollection form)
{
var names = GetAllNames();
foreach (var name in names)
{
var field = GetFieldByName(name);
if (field is null || !form.TryGetValue(name, out var val))
{
continue;
}
field.Value = val;
}
}
public Dictionary<string, object> GetValues()
{
return GetValues(Fields);
}
private static Dictionary<string, object> GetValues(List<Field> fields)
{
var result = new Dictionary<string, object>();
foreach (Field field in fields)
{
var name = field.Name ?? string.Empty;
if (field.Fields.Any())
{
var values = GetValues(fields);
values.Remove(string.Empty, out var keylessValue);
result.TryAdd(name, values);
if (keylessValue is not Dictionary<string, object> dict)
continue;
foreach (KeyValuePair<string, object> keyValuePair in dict)
{
result.TryAdd(keyValuePair.Key, keyValuePair.Value);
}
}
else
{
result.TryAdd(name, field.Value);
}
}
return result;
}
}

@ -0,0 +1,19 @@
namespace BTCPayServer.Abstractions.Form;
public class TextField : Field
{
public TextField(string label, string name, string value, bool required, string helpText)
{
this.Label = label;
this.Name = name;
this.Value = value;
this.OriginalValue = value;
this.Required = required;
this.HelpText = helpText;
this.Type = "text";
}
// TODO JSON parsing from string to objects again probably won't work out of the box because of the different field types.
}

@ -8,52 +8,18 @@ namespace BTCPayServer.Abstractions.Models
{
public abstract class BaseBTCPayServerPlugin : IBTCPayServerPlugin
{
public virtual string Identifier
{
get
{
return GetType().GetTypeInfo().Assembly.GetName().Name;
}
}
public virtual string Name
{
get
{
return GetType().GetTypeInfo().Assembly
.GetCustomAttribute<AssemblyProductAttribute>()?
.Product ?? "???";
}
}
public abstract string Identifier { get; }
public abstract string Name { get; }
public virtual Version Version
{
get
{
return GetVersion(GetType().GetTypeInfo().Assembly
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?
.InformationalVersion) ??
Assembly.GetAssembly(GetType())?.GetName()?.Version ??
new Version(1, 0, 0, 0);
return Assembly.GetAssembly(GetType())?.GetName().Version ?? new Version(1, 0, 0, 0);
}
}
private static Version GetVersion(string informationalVersion)
{
if (informationalVersion is null)
return null;
Version.TryParse(informationalVersion, out var r);
return r;
}
public virtual string Description
{
get
{
return GetType().GetTypeInfo().Assembly
.GetCustomAttribute<AssemblyDescriptionAttribute>()?
.Description ?? string.Empty;
}
}
public abstract string Description { get; }
public bool SystemPlugin { get; set; }
public virtual IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } = Array.Empty<IBTCPayServerPlugin.PluginDependency>();

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

@ -1,12 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Razor.TagHelpers;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
@ -15,7 +8,7 @@ namespace BTCPayServer.Abstractions.TagHelpers;
// Make sure that <svg><use href=/ are correctly working if rootpath is present
[HtmlTargetElement("use", Attributes = "href")]
public class SVGUse : UrlResolutionTagHelper2
public class SVGUse : UrlResolutionTagHelper
{
private readonly IFileVersionProvider _fileVersionProvider;
@ -28,6 +21,5 @@ public class SVGUse : UrlResolutionTagHelper2
var attr = output.Attributes["href"].Value.ToString();
attr = _fileVersionProvider.AddFileVersionToPath(ViewContext.HttpContext.Request.PathBase, attr);
output.Attributes.SetAttribute("href", attr);
base.Process(context, output);
}
}

@ -1,314 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Razor.TagHelpers;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace BTCPayServer.Abstractions.TagHelpers
{
// A copy of https://github.com/dotnet/aspnetcore/blob/39f0e0b8f40b4754418f81aef0de58a9204a1fe5/src/Mvc/Mvc.Razor/src/TagHelpers/UrlResolutionTagHelper.cs
// slightly modified to also work on use tag.
public class UrlResolutionTagHelper2 : TagHelper
{
// Valid whitespace characters defined by the HTML5 spec.
private static readonly char[] ValidAttributeWhitespaceChars =
new[] { '\t', '\n', '\u000C', '\r', ' ' };
private static readonly Dictionary<string, string[]> ElementAttributeLookups =
new(StringComparer.OrdinalIgnoreCase)
{
{ "use", new[] { "href" } },
{ "a", new[] { "href" } },
{ "applet", new[] { "archive" } },
{ "area", new[] { "href" } },
{ "audio", new[] { "src" } },
{ "base", new[] { "href" } },
{ "blockquote", new[] { "cite" } },
{ "button", new[] { "formaction" } },
{ "del", new[] { "cite" } },
{ "embed", new[] { "src" } },
{ "form", new[] { "action" } },
{ "html", new[] { "manifest" } },
{ "iframe", new[] { "src" } },
{ "img", new[] { "src", "srcset" } },
{ "input", new[] { "src", "formaction" } },
{ "ins", new[] { "cite" } },
{ "link", new[] { "href" } },
{ "menuitem", new[] { "icon" } },
{ "object", new[] { "archive", "data" } },
{ "q", new[] { "cite" } },
{ "script", new[] { "src" } },
{ "source", new[] { "src", "srcset" } },
{ "track", new[] { "src" } },
{ "video", new[] { "poster", "src" } },
};
/// <summary>
/// Creates a new <see cref="UrlResolutionTagHelper"/>.
/// </summary>
/// <param name="urlHelperFactory">The <see cref="IUrlHelperFactory"/>.</param>
/// <param name="htmlEncoder">The <see cref="HtmlEncoder"/>.</param>
public UrlResolutionTagHelper2(IUrlHelperFactory urlHelperFactory, HtmlEncoder htmlEncoder)
{
UrlHelperFactory = urlHelperFactory;
HtmlEncoder = htmlEncoder;
}
/// <inheritdoc />
public override int Order => -1000 - 999;
/// <summary>
/// The <see cref="IUrlHelperFactory"/>.
/// </summary>
protected IUrlHelperFactory UrlHelperFactory { get; }
/// <summary>
/// The <see cref="HtmlEncoder"/>.
/// </summary>
protected HtmlEncoder HtmlEncoder { get; }
/// <summary>
/// The <see cref="ViewContext"/>.
/// </summary>
[HtmlAttributeNotBound]
[ViewContext]
public ViewContext ViewContext { get; set; } = default!;
/// <inheritdoc />
public override void Process(TagHelperContext context, TagHelperOutput output)
{
ArgumentNullException.ThrowIfNull(context);
ArgumentNullException.ThrowIfNull(output);
if (output.TagName == null)
{
return;
}
if (ElementAttributeLookups.TryGetValue(output.TagName, out var attributeNames))
{
for (var i = 0; i < attributeNames.Length; i++)
{
ProcessUrlAttribute(attributeNames[i], output);
}
}
// itemid can be present on any HTML element.
ProcessUrlAttribute("itemid", output);
}
/// <summary>
/// Resolves and updates URL values starting with '~/' (relative to the application's 'webroot' setting) for
/// <paramref name="output"/>'s <see cref="TagHelperOutput.Attributes"/> whose
/// <see cref="TagHelperAttribute.Name"/> is <paramref name="attributeName"/>.
/// </summary>
/// <param name="attributeName">The attribute name used to lookup values to resolve.</param>
/// <param name="output">The <see cref="TagHelperOutput"/>.</param>
protected void ProcessUrlAttribute(string attributeName, TagHelperOutput output)
{
ArgumentNullException.ThrowIfNull(attributeName);
ArgumentNullException.ThrowIfNull(output);
var attributes = output.Attributes;
// Read interface .Count once rather than per iteration
var attributesCount = attributes.Count;
for (var i = 0; i < attributesCount; i++)
{
var attribute = attributes[i];
if (!string.Equals(attribute.Name, attributeName, StringComparison.OrdinalIgnoreCase))
{
continue;
}
if (attribute.Value is string stringValue)
{
if (TryResolveUrl(stringValue, resolvedUrl: out string? resolvedUrl))
{
attributes[i] = new TagHelperAttribute(
attribute.Name,
resolvedUrl,
attribute.ValueStyle);
}
}
else
{
if (attribute.Value is IHtmlContent htmlContent)
{
var htmlString = htmlContent as HtmlString;
if (htmlString != null)
{
// No need for a StringWriter in this case.
stringValue = htmlString.ToString();
}
else
{
using var writer = new StringWriter();
htmlContent.WriteTo(writer, HtmlEncoder);
stringValue = writer.ToString();
}
if (TryResolveUrl(stringValue, resolvedUrl: out IHtmlContent? resolvedUrl))
{
attributes[i] = new TagHelperAttribute(
attribute.Name,
resolvedUrl,
attribute.ValueStyle);
}
else if (htmlString == null)
{
// Not a ~/ URL. Just avoid re-encoding the attribute value later.
attributes[i] = new TagHelperAttribute(
attribute.Name,
new HtmlString(stringValue),
attribute.ValueStyle);
}
}
}
}
}
/// <summary>
/// Tries to resolve the given <paramref name="url"/> value relative to the application's 'webroot' setting.
/// </summary>
/// <param name="url">The URL to resolve.</param>
/// <param name="resolvedUrl">Absolute URL beginning with the application's virtual root. <c>null</c> if
/// <paramref name="url"/> could not be resolved.</param>
/// <returns><c>true</c> if the <paramref name="url"/> could be resolved; <c>false</c> otherwise.</returns>
protected bool TryResolveUrl(string url, out string? resolvedUrl)
{
resolvedUrl = null;
var start = FindRelativeStart(url);
if (start == -1)
{
return false;
}
var trimmedUrl = CreateTrimmedString(url, start);
var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext);
resolvedUrl = urlHelper.Content(trimmedUrl);
return true;
}
/// <summary>
/// Tries to resolve the given <paramref name="url"/> value relative to the application's 'webroot' setting.
/// </summary>
/// <param name="url">The URL to resolve.</param>
/// <param name="resolvedUrl">
/// Absolute URL beginning with the application's virtual root. <c>null</c> if <paramref name="url"/> could
/// not be resolved.
/// </param>
/// <returns><c>true</c> if the <paramref name="url"/> could be resolved; <c>false</c> otherwise.</returns>
protected bool TryResolveUrl(string url, [NotNullWhen(true)] out IHtmlContent? resolvedUrl)
{
resolvedUrl = null;
var start = FindRelativeStart(url);
if (start == -1)
{
return false;
}
var trimmedUrl = CreateTrimmedString(url, start);
var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext);
var appRelativeUrl = urlHelper.Content(trimmedUrl);
var postTildeSlashUrlValue = trimmedUrl.Substring(2);
if (!appRelativeUrl.EndsWith(postTildeSlashUrlValue, StringComparison.Ordinal))
{
throw new InvalidOperationException();
}
resolvedUrl = new EncodeFirstSegmentContent(
appRelativeUrl,
appRelativeUrl.Length - postTildeSlashUrlValue.Length,
postTildeSlashUrlValue);
return true;
}
private static int FindRelativeStart(string url)
{
if (url == null || url.Length < 2)
{
return -1;
}
var maxTestLength = url.Length - 2;
var start = 0;
for (; start < url.Length; start++)
{
if (start > maxTestLength)
{
return -1;
}
if (!IsCharWhitespace(url[start]))
{
break;
}
}
// Before doing more work, ensure that the URL we're looking at is app-relative.
if (url[start] != '~' || url[start + 1] != '/')
{
return -1;
}
return start;
}
private static string CreateTrimmedString(string input, int start)
{
var end = input.Length - 1;
for (; end >= start; end--)
{
if (!IsCharWhitespace(input[end]))
{
break;
}
}
var len = end - start + 1;
// Substring returns same string if start == 0 && len == Length
return input.Substring(start, len);
}
private static bool IsCharWhitespace(char ch)
{
return ValidAttributeWhitespaceChars.AsSpan().IndexOf(ch) != -1;
}
private sealed class EncodeFirstSegmentContent : IHtmlContent
{
private readonly string _firstSegment;
private readonly int _firstSegmentLength;
private readonly string _secondSegment;
public EncodeFirstSegmentContent(string firstSegment, int firstSegmentLength, string secondSegment)
{
_firstSegment = firstSegment;
_firstSegmentLength = firstSegmentLength;
_secondSegment = secondSegment;
}
public void WriteTo(TextWriter writer, HtmlEncoder encoder)
{
encoder.Encode(writer, _firstSegment, 0, _firstSegmentLength);
writer.Write(_secondSegment);
}
}
}
}

@ -14,7 +14,7 @@
<RepositoryType>git</RepositoryType>
</PropertyGroup>
<PropertyGroup>
<Version Condition=" '$(Version)' == '' ">1.7.2</Version>
<Version Condition=" '$(Version)' == '' ">1.7.0</Version>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<PublishRepositoryUrl>true</PublishRepositoryUrl>
@ -28,8 +28,8 @@
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.3.21" />
<PackageReference Include="NBitcoin" Version="7.0.24" />
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.3.15" />
<PackageReference Include="NBitcoin" Version="7.0.14" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
<ItemGroup>

@ -20,17 +20,6 @@ namespace BTCPayServer.Client
return await HandleResponse<PointOfSaleAppData>(response);
}
public virtual async Task<CrowdfundAppData> CreateCrowdfundApp(string storeId,
CreateCrowdfundAppRequest request, CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/apps/crowdfund", bodyPayload: request,
method: HttpMethod.Post), token);
return await HandleResponse<CrowdfundAppData>(response);
}
public virtual async Task<PointOfSaleAppData> UpdatePointOfSaleApp(string appId,
CreatePointOfSaleAppRequest request, CancellationToken token = default)
{
@ -51,44 +40,6 @@ namespace BTCPayServer.Client
method: HttpMethod.Get), token);
return await HandleResponse<AppDataBase>(response);
}
public virtual async Task<AppDataBase[]> GetAllApps(string storeId, CancellationToken token = default)
{
if (storeId == null)
throw new ArgumentNullException(nameof(storeId));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/apps",
method: HttpMethod.Get), token);
return await HandleResponse<AppDataBase[]>(response);
}
public virtual async Task<AppDataBase[]> GetAllApps(CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/apps",
method: HttpMethod.Get), token);
return await HandleResponse<AppDataBase[]>(response);
}
public virtual async Task<PointOfSaleAppData> GetPosApp(string appId, CancellationToken token = default)
{
if (appId == null)
throw new ArgumentNullException(nameof(appId));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/apps/pos/{appId}",
method: HttpMethod.Get), token);
return await HandleResponse<PointOfSaleAppData>(response);
}
public virtual async Task<CrowdfundAppData> GetCrowdfundApp(string appId, CancellationToken token = default)
{
if (appId == null)
throw new ArgumentNullException(nameof(appId));
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/apps/crowdfund/{appId}",
method: HttpMethod.Get), token);
return await HandleResponse<CrowdfundAppData>(response);
}
public virtual async Task DeleteApp(string appId, CancellationToken token = default)
{

@ -58,7 +58,7 @@ namespace BTCPayServer.Client
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);
//return await HandleResponse<ApplicationUserData>(response);
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);
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);
return await HandleResponse<WithdrawalResponseData>(response);

@ -128,18 +128,5 @@ namespace BTCPayServer.Client
method: HttpMethod.Post), token);
await HandleResponse(response);
}
public virtual async Task<PullPaymentData> RefundInvoice(
string storeId,
string invoiceId,
RefundInvoiceRequest request,
CancellationToken token = default
)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}/refund", bodyPayload: request,
method: HttpMethod.Post), token);
return await HandleResponse<PullPaymentData>(response);
}
}
}

@ -17,7 +17,7 @@ namespace BTCPayServer.Client
method: HttpMethod.Get), token);
return await HandleResponse<LightningNodeInformationData>(response);
}
public virtual async Task<LightningNodeBalanceData> GetLightningNodeBalance(string cryptoCode,
CancellationToken token = default)
{
@ -95,7 +95,7 @@ namespace BTCPayServer.Client
method: HttpMethod.Get), token);
return await HandleResponse<LightningInvoiceData>(response);
}
public virtual async Task<LightningInvoiceData[]> GetLightningInvoices(string cryptoCode,
bool? pendingOnly = null, long? offsetIndex = null, CancellationToken token = default)
{
@ -113,24 +113,6 @@ namespace BTCPayServer.Client
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/invoices", queryPayload), token);
return await HandleResponse<LightningInvoiceData[]>(response);
}
public virtual async Task<LightningPaymentData[]> GetLightningPayments(string cryptoCode,
bool? includePending = null, long? offsetIndex = null, CancellationToken token = default)
{
var queryPayload = new Dictionary<string, object>();
if (includePending is bool v)
{
queryPayload.Add("includePending", v.ToString());
}
if (offsetIndex is > 0)
{
queryPayload.Add("offsetIndex", offsetIndex);
}
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/payments", queryPayload), token);
return await HandleResponse<LightningPaymentData[]>(response);
}
public virtual async Task<LightningInvoiceData> CreateLightningInvoice(string cryptoCode, CreateLightningInvoiceRequest request,
CancellationToken token = default)

@ -17,7 +17,7 @@ namespace BTCPayServer.Client
method: HttpMethod.Get), token);
return await HandleResponse<LightningNodeInformationData>(response);
}
public virtual async Task<LightningNodeBalanceData> GetLightningNodeBalance(string storeId, string cryptoCode,
CancellationToken token = default)
{
@ -97,7 +97,7 @@ namespace BTCPayServer.Client
method: HttpMethod.Get), token);
return await HandleResponse<LightningInvoiceData>(response);
}
public virtual async Task<LightningInvoiceData[]> GetLightningInvoices(string storeId, string cryptoCode,
bool? pendingOnly = null, long? offsetIndex = null, CancellationToken token = default)
{
@ -115,24 +115,6 @@ namespace BTCPayServer.Client
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/invoices", queryPayload), token);
return await HandleResponse<LightningInvoiceData[]>(response);
}
public virtual async Task<LightningPaymentData[]> GetLightningPayments(string storeId, string cryptoCode,
bool? includePending = null, long? offsetIndex = null, CancellationToken token = default)
{
var queryPayload = new Dictionary<string, object>();
if (includePending is bool v)
{
queryPayload.Add("includePending", v.ToString());
}
if (offsetIndex is > 0)
{
queryPayload.Add("offsetIndex", offsetIndex);
}
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/payments", queryPayload), token);
return await HandleResponse<LightningPaymentData[]>(response);
}
public virtual async Task<LightningInvoiceData> CreateLightningInvoice(string storeId, string cryptoCode,
CreateLightningInvoiceRequest request, CancellationToken token = default)

@ -1,48 +0,0 @@
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
{
public partial class BTCPayServerClient
{
public virtual async Task<LightningAddressData[]> GetStoreLightningAddresses(string storeId,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning-addresses",
method: HttpMethod.Get), token);
return await HandleResponse<LightningAddressData[]>(response);
}
public virtual async Task<LightningAddressData> GetStoreLightningAddress(string storeId, string username,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning-addresses/{username}",
method: HttpMethod.Get), token);
return await HandleResponse<LightningAddressData>(response);
}
public virtual async Task RemoveStoreLightningAddress(string storeId, string username,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning-addresses/{username}",
method: HttpMethod.Delete), token);
await HandleResponse(response);
}
public virtual async Task<LightningAddressData> AddOrUpdateStoreLightningAddress(string storeId,
string username, LightningAddressData data,
CancellationToken token = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/lightning-addresses/{username}",
method: HttpMethod.Post, bodyPayload: data), token);
return await HandleResponse<LightningAddressData>(response);
}
}
}

@ -1,82 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
using NBitcoin;
namespace BTCPayServer.Client
{
public partial class BTCPayServerClient
{
public virtual async Task<OnChainWalletObjectData> GetOnChainWalletObject(string storeId, string cryptoCode, OnChainWalletObjectId objectId, bool? includeNeighbourData = null, CancellationToken token = default)
{
Dictionary<string, object> parameters = new Dictionary<string, object>();
if (includeNeighbourData is bool v)
parameters.Add("includeNeighbourData", v);
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/objects/{objectId.Type}/{objectId.Id}", parameters, method: HttpMethod.Get), token);
try
{
return await HandleResponse<OnChainWalletObjectData>(response);
}
catch (GreenfieldAPIException err) when (err.APIError.Code == "wallet-object-not-found")
{
return null;
}
}
public virtual async Task<OnChainWalletObjectData[]> GetOnChainWalletObjects(string storeId, string cryptoCode, GetWalletObjectsRequest query = null, CancellationToken token = default)
{
Dictionary<string, object> parameters = new Dictionary<string, object>();
if (query?.Type is string s)
parameters.Add("type", s);
if (query?.Ids is string[] ids)
parameters.Add("ids", ids);
if (query?.IncludeNeighbourData is bool v)
parameters.Add("includeNeighbourData", v);
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/objects", parameters, method: HttpMethod.Get), token);
return await HandleResponse<OnChainWalletObjectData[]>(response);
}
public virtual async Task RemoveOnChainWalletObject(string storeId, string cryptoCode, OnChainWalletObjectId objectId,
CancellationToken token = default)
{
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/objects/{objectId.Type}/{objectId.Id}", method: HttpMethod.Delete), token);
await HandleResponse(response);
}
public virtual async Task<OnChainWalletObjectData> AddOrUpdateOnChainWalletObject(string storeId, string cryptoCode, AddOnChainWalletObjectRequest request,
CancellationToken token = default)
{
var response =
await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/objects", method: HttpMethod.Post, bodyPayload: request), token);
return await HandleResponse<OnChainWalletObjectData>(response);
}
public virtual async Task AddOrUpdateOnChainWalletLink(string storeId, string cryptoCode,
OnChainWalletObjectId objectId,
AddOnChainWalletObjectLinkRequest request = null,
CancellationToken token = default)
{
var response =
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);
await HandleResponse(response);
}
public virtual async Task RemoveOnChainWalletLinks(string storeId, string cryptoCode,
OnChainWalletObjectId objectId,
OnChainWalletObjectId link,
CancellationToken token = default)
{
var response =
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);
await HandleResponse(response);
}
}
}

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

@ -52,17 +52,7 @@ namespace BTCPayServer.Client
{
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);
}
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);
return await HandleResponse<PayoutData>(response);
}
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);
return await HandleResponse<PayoutData>(response);
}
}
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);
@ -79,7 +69,7 @@ namespace BTCPayServer.Client
return await HandleResponse<PayoutData>(response);
}
public virtual async Task MarkPayoutPaid(string storeId, string payoutId,
public async Task MarkPayoutPaid(string storeId, string payoutId,
CancellationToken cancellationToken = default)
{
var response = await _httpClient.SendAsync(
@ -88,24 +78,5 @@ namespace BTCPayServer.Client
method: HttpMethod.Post), cancellationToken);
await HandleResponse(response);
}
public virtual async Task MarkPayout(string storeId, string payoutId, MarkPayoutRequest request,
CancellationToken cancellationToken = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest(
$"api/v1/stores/{HttpUtility.UrlEncode(storeId)}/payouts/{HttpUtility.UrlEncode(payoutId)}/mark",
method: HttpMethod.Post, bodyPayload: request), cancellationToken);
await HandleResponse(response);
}
public virtual async Task<PullPaymentLNURL> GetPullPaymentLNURL(string pullPaymentId,
CancellationToken cancellationToken = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest(
$"/api/v1/pull-payments/{pullPaymentId}/lnurl",
method: HttpMethod.Get), cancellationToken);
return await HandleResponse<PullPaymentLNURL>(response);
}
}
}

@ -9,7 +9,7 @@ namespace BTCPayServer.Client
{
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)
{
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);
await HandleResponse(response);
}
public virtual async Task<IEnumerable<LightningAutomatedPayoutSettings>> GetStoreLightningAutomatedPayoutProcessors(string storeId, string? paymentMethod = null,
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);
}
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);
}
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);
}
public virtual async Task<IEnumerable<OnChainAutomatedPayoutSettings>> GetStoreOnChainAutomatedPayoutProcessors(string storeId, string? paymentMethod = null,
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);
}
}

@ -37,28 +37,17 @@ namespace BTCPayServer.Client
return await HandleResponse<StoreRateConfiguration>(response);
}
public virtual async Task<List<StoreRateResult>> PreviewUpdateStoreRateConfiguration(string storeId,
public virtual async Task<List<StoreRatePreviewResult>> PreviewUpdateStoreRateConfiguration(string storeId,
StoreRateConfiguration request,
string[] currencyPair,
CancellationToken token = default)
{
using var response = await _httpClient.SendAsync(
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),
token);
return await HandleResponse<List<StoreRateResult>>(response);
}
public virtual async Task<List<StoreRateResult>> GetStoreRates(string storeId, string[] currencyPair,
CancellationToken token = default)
{
using var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/rates",
queryPayload: new Dictionary<string, object>() { { "currencyPair", currencyPair } },
method: HttpMethod.Get),
token);
return await HandleResponse<List<StoreRateResult>>(response);
return await HandleResponse<List<StoreRatePreviewResult>>(response);
}
}
}

@ -33,15 +33,14 @@ namespace BTCPayServer.Client
return await HandleResponse<ApplicationUserData>(response);
}
public virtual async Task<bool> LockUser(string idOrEmail, bool locked, CancellationToken token = default)
public virtual async Task LockUser(string idOrEmail, bool locked, CancellationToken token = default)
{
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);
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);
return await HandleResponse<ApplicationUserData[]>(response);

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

@ -1,4 +1,3 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
@ -37,48 +36,8 @@ namespace BTCPayServer.Client.Models
public string RedirectUrl { get; set; } = null;
public bool? RedirectAutomatically { get; set; } = null;
public bool? RequiresRefundEmail { get; set; } = null;
public string FormId { get; set; } = null;
public string CheckoutFormId { get; set; } = null;
public string EmbeddedCSS { get; set; } = null;
public CheckoutType? CheckoutType { get; set; } = null;
}
public enum CrowdfundResetEvery
{
Never,
Hour,
Day,
Month,
Year
}
public class CreateCrowdfundAppRequest : CreateAppRequest
{
public string Title { get; set; } = null;
public bool? Enabled { get; set; } = null;
public bool? EnforceTargetAmount { get; set; } = null;
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset? StartDate { get; set; } = null;
public string TargetCurrency { get; set; } = null;
public string Description { get; set; } = null;
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset? EndDate { get; set; } = null;
public decimal? TargetAmount { get; set; } = null;
public string CustomCSSLink { get; set; } = null;
public string MainImageUrl { get; set; } = null;
public string EmbeddedCSS { get; set; } = null;
public string NotificationUrl { get; set; } = null;
public string Tagline { get; set; } = null;
public string PerksTemplate { get; set; } = null;
public bool? SoundsEnabled { get; set; } = null;
public string DisqusShortname { get; set; } = null;
public bool? AnimationsEnabled { get; set; } = null;
public int? ResetEveryAmount { get; set; } = null;
[JsonConverter(typeof(StringEnumConverter))]
public CrowdfundResetEvery ResetEvery { get; set; } = CrowdfundResetEvery.Never;
public bool? DisplayPerksValue { get; set; } = null;
public bool? DisplayPerksRanking { get; set; } = null;
public bool? SortPerksByPopularity { get; set; } = null;
public string[] Sounds { get; set; } = null;
public string[] AnimationColors { get; set; } = null;
}
}

@ -11,18 +11,19 @@ namespace BTCPayServer.Client.Models
{
}
public CreateLightningInvoiceRequest(LightMoney amount, string description, TimeSpan expiry)
{
Amount = amount;
Description = description;
Expiry = expiry;
}
[JsonConverter(typeof(JsonConverters.LightMoneyJsonConverter))]
public LightMoney Amount { get; set; }
public string Description { get; set; }
public bool DescriptionHashOnly { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.UInt256JsonConverter))]
public uint256 DescriptionHash { get; set; }
[JsonConverter(typeof(JsonConverters.TimeSpanJsonConverter.Seconds))]
public TimeSpan Expiry { get; set; }
public bool PrivateRouteHints { get; set; }

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

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

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

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

@ -85,6 +85,8 @@ namespace BTCPayServer.Client.Models
public bool? RedirectAutomatically { get; set; }
public bool? RequiresRefundEmail { get; set; } = null;
public string DefaultLanguage { get; set; }
[JsonProperty("checkoutFormId")]
public string CheckoutFormId { get; set; }
public CheckoutType? CheckoutType { get; set; }
}
}

@ -1,10 +0,0 @@
namespace BTCPayServer.Client.Models;
public class LightningAddressData
{
public string Username { get; set; }
public string CurrencyCode { get; set; }
public decimal? Min { get; set; }
public decimal? Max { get; set; }
}

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

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

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

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

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

@ -1,14 +0,0 @@
#nullable enable
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Client.Models;
public class MarkPayoutRequest
{
[JsonConverter(typeof(StringEnumConverter))]
public PayoutState State { get; set; } = PayoutState.Completed;
public JObject? PaymentProof { get; set; }
}

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

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

@ -4,85 +4,9 @@ using BTCPayServer.JsonConverters;
using NBitcoin;
using NBitcoin.JsonConverters;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Client.Models
{
public class OnChainWalletObjectId
{
public OnChainWalletObjectId()
{
}
public OnChainWalletObjectId(string type, string id)
{
Type = type;
Id = id;
}
public string Type { get; set; }
public string Id { get; set; }
}
public class AddOnChainWalletObjectLinkRequest : OnChainWalletObjectId
{
public AddOnChainWalletObjectLinkRequest()
{
}
public AddOnChainWalletObjectLinkRequest(string objectType, string objectId) : base(objectType, objectId)
{
}
public JObject Data { get; set; }
}
public class GetWalletObjectsRequest
{
public string Type { get; set; }
public string[] Ids { get; set; }
public bool? IncludeNeighbourData { get; set; }
}
public class AddOnChainWalletObjectRequest : OnChainWalletObjectId
{
public AddOnChainWalletObjectRequest()
{
}
public AddOnChainWalletObjectRequest(string objectType, string objectId) : base(objectType, objectId)
{
}
public JObject Data { get; set; }
}
public class OnChainWalletObjectData : OnChainWalletObjectId
{
public OnChainWalletObjectData()
{
}
public OnChainWalletObjectData(string type, string id) : base(type, id)
{
}
public class OnChainWalletObjectLink : OnChainWalletObjectId
{
public OnChainWalletObjectLink()
{
}
public OnChainWalletObjectLink(string type, string id) : base(type, id)
{
}
public JObject LinkData { get; set; }
public JObject ObjectData { get; set; }
}
public JObject Data { get; set; }
public OnChainWalletObjectLink[] Links { get; set; }
}
public class OnChainWalletTransactionData
{
[JsonConverter(typeof(UInt256JsonConverter))]

@ -11,7 +11,7 @@ namespace BTCPayServer.Client.Models
{
[JsonConverter(typeof(NodeUriJsonConverter))]
[JsonProperty("nodeURI")]
public BTCPayServer.Lightning.NodeInfo NodeURI { get; set; }
public NodeInfo NodeURI { get; set; }
[JsonConverter(typeof(MoneyJsonConverter))]
public Money ChannelAmount { get; set; }

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

@ -24,9 +24,5 @@ namespace BTCPayServer.Client.Models
[JsonExtensionData]
public IDictionary<string, JToken> AdditionalData { get; set; }
public string FormId { get; set; }
public JObject FormResponse { get; set; }
}
}

@ -12,6 +12,7 @@ namespace BTCPayServer.Client.Models
public DateTimeOffset CreatedTime { get; set; }
public string Id { get; set; }
public bool Archived { get; set; }
public enum PaymentRequestStatus
{
Pending = 0,

@ -2,7 +2,6 @@ using System;
using BTCPayServer.JsonConverters;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Client.Models
{
@ -30,6 +29,5 @@ namespace BTCPayServer.Client.Models
[JsonConverter(typeof(StringEnumConverter))]
public PayoutState State { get; set; }
public int Revision { get; set; }
public JObject PaymentProof { get; set; }
}
}

@ -12,56 +12,9 @@ namespace BTCPayServer.Client.Models
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset Created { get; set; }
}
public class PointOfSaleAppData : AppDataBase
{
public string Title { get; set; }
public string DefaultView { get; set; }
public bool ShowCustomAmount { get; set; }
public bool ShowDiscount { get; set; }
public bool EnableTips { get; set; }
public string Currency { get; set; }
public object Items { get; set; }
public string FixedAmountPayButtonText { get; set; }
public string CustomAmountPayButtonText { get; set; }
public string TipText { get; set; }
public string CustomCSSLink { get; set; }
public string NotificationUrl { get; set; }
public string RedirectUrl { get; set; }
public string Description { get; set; }
public string EmbeddedCSS { get; set; }
public bool? RedirectAutomatically { get; set; }
public bool? RequiresRefundEmail { get; set; }
}
public class CrowdfundAppData : AppDataBase
{
public string Title { get; set; }
public bool Enabled { get; set; }
public bool EnforceTargetAmount { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset? StartDate { get; set; }
public string TargetCurrency { get; set; }
public string Description { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset? EndDate { get; set; }
public decimal? TargetAmount { get; set; }
public string CustomCSSLink { get; set; }
public string MainImageUrl { get; set; }
public string EmbeddedCSS { get; set; }
public string NotificationUrl { get; set; }
public string Tagline { get; set; }
public object Perks { get; set; }
public bool DisqusEnabled { get; set; }
public string DisqusShortname { get; set; }
public bool SoundsEnabled { get; set; }
public bool AnimationsEnabled { get; set; }
public int ResetEveryAmount { get; set; }
public string ResetEvery { get; set; }
public bool DisplayPerksValue { get; set; }
public bool DisplayPerksRanking { get; set; }
public bool SortPerksByPopularity { get; set; }
public string[] Sounds { get; set; }
public string[] AnimationColors { get; set; }
// We can add POS specific things here later
}
}

@ -1,8 +0,0 @@
namespace BTCPayServer.Client.Models
{
public class PullPaymentLNURL
{
public string LNURLBech32 { get; set; }
public string LNURLUri { get; set; }
}
}

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

@ -1,27 +0,0 @@
#nullable enable
using BTCPayServer.JsonConverters;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace BTCPayServer.Client.Models
{
public enum RefundVariant
{
RateThen,
CurrentRate,
Fiat,
Custom
}
public class RefundInvoiceRequest
{
public string? Name { get; set; } = null;
public string? PaymentMethod { get; set; }
public string? Description { get; set; } = null;
[JsonConverter(typeof(StringEnumConverter))]
public RefundVariant? RefundVariant { get; set; }
[JsonConverter(typeof(NumericStringJsonConverter))]
public decimal? CustomAmount { get; set; }
public string? CustomCurrency { get; set; }
}
}

@ -20,10 +20,6 @@ namespace BTCPayServer.Client.Models
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public TimeSpan InvoiceExpiration { get; set; } = TimeSpan.FromMinutes(15);
[JsonConverter(typeof(TimeSpanJsonConverter.Seconds))]
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public TimeSpan DisplayExpirationTimer { get; set; } = TimeSpan.FromMinutes(5);
[JsonConverter(typeof(TimeSpanJsonConverter.Seconds))]
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public TimeSpan MonitoringExpiration { get; set; } = TimeSpan.FromMinutes(60);
@ -35,6 +31,8 @@ namespace BTCPayServer.Client.Models
public bool AnyoneCanCreateInvoice { get; set; }
public string DefaultCurrency { get; set; }
public bool RequiresRefundEmail { get; set; }
public string CheckoutFormId { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public CheckoutType CheckoutType { get; set; }
public bool LightningAmountInSatoshi { get; set; }
@ -64,7 +62,7 @@ namespace BTCPayServer.Client.Models
public NetworkFeeMode NetworkFeeMode { get; set; } = NetworkFeeMode.Never;
public bool PayJoinEnabled { get; set; }
public InvoiceData.ReceiptOptions Receipt { get; set; }

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

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

@ -1,13 +1,7 @@
using System.Collections.Generic;
using BTCPayServer.JsonConverters;
using Newtonsoft.Json;
namespace BTCPayServer.Client.Models;
namespace BTCPayServer.Client.Models;
public class StoreRateResult
{
public string CurrencyPair { get; set; }
[JsonConverter(typeof(NumericStringJsonConverter))]
public decimal? Rate { get; set; }
public List<string> Errors { get; set; }
}
public decimal Rate { get; set; }
}

@ -1,5 +1,8 @@
using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
namespace BTCPayServer.Client.Models
{
@ -16,7 +19,6 @@ namespace BTCPayServer.Client.Models
[JsonProperty(Order = 1)] public string StoreId { get; set; }
[JsonProperty(Order = 2)] public string InvoiceId { get; set; }
[JsonProperty(Order = 3)] public JObject Metadata { get; set; }
}
public class WebhookInvoiceSettledEvent : WebhookInvoiceEvent

@ -6,9 +6,7 @@ namespace BTCPayServer.Client
{
public class Policies
{
public const string CanViewLightningInvoiceInternalNode = "btcpay.server.canviewlightninginvoiceinternalnode";
public const string CanCreateLightningInvoiceInternalNode = "btcpay.server.cancreatelightninginvoiceinternalnode";
public const string CanViewLightningInvoiceInStore = "btcpay.store.canviewlightninginvoice";
public const string CanCreateLightningInvoiceInStore = "btcpay.store.cancreatelightninginvoice";
public const string CanUseInternalLightningNode = "btcpay.server.canuseinternallightningnode";
public const string CanUseLightningNodeInStore = "btcpay.store.canuselightningnode";
@ -30,8 +28,6 @@ namespace BTCPayServer.Client
public const string CanCreateUser = "btcpay.server.cancreateuser";
public const string CanDeleteUser = "btcpay.user.candeleteuser";
public const string CanManagePullPayments = "btcpay.store.canmanagepullpayments";
public const string CanCreatePullPayments = "btcpay.store.cancreatepullpayments";
public const string CanCreateNonApprovedPullPayments = "btcpay.store.cancreatenonapprovedpullpayments";
public const string CanViewCustodianAccounts = "btcpay.store.canviewcustodianaccounts";
public const string CanManageCustodianAccounts = "btcpay.store.canmanagecustodianaccounts";
public const string CanDepositToCustodianAccounts = "btcpay.store.candeposittocustodianaccount";
@ -60,14 +56,10 @@ namespace BTCPayServer.Client
yield return CanViewNotificationsForUser;
yield return Unrestricted;
yield return CanUseInternalLightningNode;
yield return CanViewLightningInvoiceInternalNode;
yield return CanCreateLightningInvoiceInternalNode;
yield return CanUseLightningNodeInStore;
yield return CanViewLightningInvoiceInStore;
yield return CanCreateLightningInvoiceInStore;
yield return CanManagePullPayments;
yield return CanCreatePullPayments;
yield return CanCreateNonApprovedPullPayments;
yield return CanViewCustodianAccounts;
yield return CanManageCustodianAccounts;
yield return CanDepositToCustodianAccounts;
@ -99,11 +91,6 @@ namespace BTCPayServer.Client
}
public class Permission
{
static Permission()
{
Init();
}
public static Permission Create(string policy, string scope = null)
{
if (TryCreatePermission(policy, scope, out var r))
@ -188,55 +175,37 @@ namespace BTCPayServer.Client
private bool ContainsPolicy(string subpolicy)
{
return ContainsPolicy(Policy, subpolicy);
}
private static bool ContainsPolicy(string policy, string subpolicy)
{
if (policy == Policies.Unrestricted)
if (this.Policy == Policies.Unrestricted)
return true;
if (policy == subpolicy)
if (this.Policy == subpolicy)
return true;
if (!PolicyMap.TryGetValue(policy, out var subPolicies)) return false;
return subPolicies.Contains(subpolicy) || subPolicies.Any(s => ContainsPolicy(s, subpolicy));
}
private static Dictionary<string, HashSet<string>> PolicyMap = new();
private static void Init()
{
PolicyHasChild(Policies.CanModifyStoreSettings,
Policies.CanManageCustodianAccounts, Policies.CanManagePullPayments, Policies.CanModifyInvoices, Policies.CanViewStoreSettings, Policies.CanModifyStoreWebhooks, Policies.CanModifyPaymentRequests );
PolicyHasChild(Policies.CanManagePullPayments, Policies.CanCreatePullPayments );
PolicyHasChild(Policies.CanCreatePullPayments, Policies.CanCreateNonApprovedPullPayments );
PolicyHasChild(Policies.CanModifyPaymentRequests, Policies.CanViewPaymentRequests );
PolicyHasChild(Policies.CanModifyProfile, Policies.CanViewProfile );
PolicyHasChild(Policies.CanUseLightningNodeInStore, Policies.CanViewLightningInvoiceInStore, Policies.CanCreateLightningInvoiceInStore );
PolicyHasChild(Policies.CanManageNotificationsForUser, Policies.CanViewNotificationsForUser );
PolicyHasChild(Policies.CanModifyServerSettings, Policies.CanUseInternalLightningNode );
PolicyHasChild(Policies.CanUseInternalLightningNode, Policies.CanCreateLightningInvoiceInternalNode,Policies.CanViewLightningInvoiceInternalNode );
PolicyHasChild(Policies.CanManageCustodianAccounts, Policies.CanViewCustodianAccounts );
PolicyHasChild(Policies.CanModifyInvoices, Policies.CanViewInvoices, Policies.CanCreateInvoice );
PolicyHasChild(Policies.CanViewStoreSettings, Policies.CanViewInvoices, Policies.CanViewPaymentRequests );
}
private static void PolicyHasChild(string policy, params string[] subPolicies)
{
if (PolicyMap.TryGetValue(policy, out var existingSubPolicies))
switch (subpolicy)
{
foreach (string subPolicy in subPolicies)
{
existingSubPolicies.Add(subPolicy);
}
}
else
{
PolicyMap.Add(policy,subPolicies.ToHashSet());
case Policies.CanViewInvoices when this.Policy == Policies.CanModifyStoreSettings:
case Policies.CanViewInvoices when this.Policy == Policies.CanModifyInvoices:
case Policies.CanModifyStoreWebhooks when this.Policy == Policies.CanModifyStoreSettings:
case Policies.CanViewInvoices when this.Policy == Policies.CanViewStoreSettings:
case Policies.CanViewStoreSettings when this.Policy == Policies.CanModifyStoreSettings:
case Policies.CanCreateInvoice when this.Policy == Policies.CanModifyStoreSettings:
case Policies.CanModifyInvoices when this.Policy == Policies.CanModifyStoreSettings:
case Policies.CanViewProfile when this.Policy == Policies.CanModifyProfile:
case Policies.CanModifyPaymentRequests when this.Policy == Policies.CanModifyStoreSettings:
case Policies.CanViewPaymentRequests when this.Policy == Policies.CanModifyStoreSettings:
case Policies.CanManagePullPayments when this.Policy == Policies.CanModifyStoreSettings:
case Policies.CanViewPaymentRequests when this.Policy == Policies.CanViewStoreSettings:
case Policies.CanViewPaymentRequests when this.Policy == Policies.CanModifyPaymentRequests:
case Policies.CanCreateLightningInvoiceInternalNode when this.Policy == Policies.CanUseInternalLightningNode:
case Policies.CanCreateLightningInvoiceInStore when this.Policy == Policies.CanUseLightningNodeInStore:
case Policies.CanViewNotificationsForUser when this.Policy == Policies.CanManageNotificationsForUser:
case Policies.CanUseInternalLightningNode when this.Policy == Policies.CanModifyServerSettings:
case Policies.CanViewCustodianAccounts when this.Policy == Policies.CanManageCustodianAccounts:
case Policies.CanViewCustodianAccounts when this.Policy == Policies.CanModifyStoreSettings:
case Policies.CanManageCustodianAccounts when this.Policy == Policies.CanModifyStoreSettings:
return true;
default:
return false;
}
}
public string Scope { get; }
public string Policy { get; }

@ -137,7 +137,6 @@ namespace BTCPayServer
public string CryptoImagePath { get; set; }
public string[] DefaultRateRules { get; set; } = Array.Empty<string>();
public override string ToString()
{
return CryptoCode;

@ -4,8 +4,8 @@
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="NBXplorer.Client" Version="4.2.3" />
<PackageReference Include="NicolasDorier.StandardConfiguration" Version="2.0.1" />
<PackageReference Include="NBXplorer.Client" Version="4.2.1" />
<PackageReference Include="NicolasDorier.StandardConfiguration" Version="1.0.0.18" />
</ItemGroup>
<ItemGroup Condition="'$(Altcoins)' != 'true'">
<Compile Remove="Altcoins\**\*.cs"></Compile>

@ -180,7 +180,7 @@ namespace BTCPayServer.Logging
logBuilder.Append(": ");
var lenAfter = logBuilder.ToString().Length;
while (lenAfter++ < 18)
logBuilder.Append(' ');
logBuilder.Append(" ");
// scope information
GetScopeInformation(logBuilder);

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

@ -1,6 +1,5 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Data.Data;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
@ -31,12 +30,7 @@ namespace BTCPayServer.Data
{
_designTime = designTime;
}
#nullable enable
public async Task<string?> GetMigrationState()
{
return (await Settings.FromSqlRaw("SELECT \"Id\", \"Value\" FROM \"Settings\" WHERE \"Id\"='MigrationData'").AsNoTracking().FirstOrDefaultAsync())?.Value;
}
#nullable restore
public DbSet<AddressInvoiceData> AddressInvoices { get; set; }
public DbSet<APIKeyData> ApiKeys { get; set; }
public DbSet<AppData> Apps { get; set; }
@ -73,7 +67,7 @@ namespace BTCPayServer.Data
public DbSet<WalletTransactionData> WalletTransactions { get; set; }
public DbSet<WebhookDeliveryData> WebhookDeliveries { 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; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
@ -111,10 +105,10 @@ namespace BTCPayServer.Data
//PlannedTransaction.OnModelCreating(builder);
PullPaymentData.OnModelCreating(builder);
RefundData.OnModelCreating(builder);
SettingData.OnModelCreating(builder, Database);
//SettingData.OnModelCreating(builder);
StoreSettingData.OnModelCreating(builder, Database);
StoreWebhookData.OnModelCreating(builder);
StoreData.OnModelCreating(builder, Database);
//StoreData.OnModelCreating(builder);
U2FDevice.OnModelCreating(builder);
Fido2Credential.OnModelCreating(builder);
BTCPayServer.Data.UserStore.OnModelCreating(builder);

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

@ -1,12 +0,0 @@
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace BTCPayServer.Data.Data;
public class FormData
{
public string Id { get; set; }
public string Name { get; set; }
public string Config { get; set; }
}

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

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

@ -1,6 +1,3 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace BTCPayServer.Data
{
public class SettingData
@ -8,15 +5,5 @@ namespace BTCPayServer.Data
public string Id { get; set; }
public string Value { get; set; }
public static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
{
if (databaseFacade.IsNpgsql())
{
builder.Entity<SettingData>()
.Property(o => o.Value)
.HasColumnType("JSONB");
}
}
}
}

@ -1,12 +1,8 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text;
using BTCPayServer.Client.Models;
using BTCPayServer.Data.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using PayoutProcessorData = BTCPayServer.Data.Data.PayoutProcessorData;
namespace BTCPayServer.Data
@ -27,6 +23,7 @@ namespace BTCPayServer.Data
[Obsolete("Use GetDerivationStrategies instead")]
public string DerivationStrategy { get; set; }
[Obsolete("Use GetDerivationStrategies instead")]
public string DerivationStrategies { get; set; }
public string StoreName { get; set; }
@ -39,7 +36,7 @@ namespace BTCPayServer.Data
[NotMapped] public string Role { get; set; }
public string StoreBlob { get; set; }
public byte[] StoreBlob { get; set; }
[Obsolete("Use GetDefaultPaymentId instead")]
public string DefaultCrypto { get; set; }
@ -51,29 +48,5 @@ namespace BTCPayServer.Data
public IEnumerable<PayoutData> Payouts { get; set; }
public IEnumerable<CustodianAccountData> CustodianAccounts { get; set; }
public IEnumerable<StoreSettingData> Settings { get; set; }
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
{
if (databaseFacade.IsNpgsql())
{
builder.Entity<StoreData>()
.Property(o => o.StoreBlob)
.HasColumnType("JSONB");
builder.Entity<StoreData>()
.Property(o => o.DerivationStrategies)
.HasColumnType("JSONB");
}
else if (databaseFacade.IsMySql())
{
builder.Entity<StoreData>()
.Property(o => o.StoreBlob)
.HasConversion(new ValueConverter<string, byte[]>
(
convertToProviderExpression: (str) => Encoding.UTF8.GetBytes(str),
convertFromProviderExpression: (bytes) => Encoding.UTF8.GetString(bytes)
));
}
}
}
}

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

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

@ -14,53 +14,14 @@ namespace BTCPayServer.Data
{
public const string Label = "label";
public const string Tx = "tx";
public const string Payjoin = "payjoin";
public const string Invoice = "invoice";
public const string PaymentRequest = "payment-request";
public const string App = "app";
public const string PayjoinExposed = "pj-exposed";
public const string Payout = "payout";
public const string PullPayment = "pull-payment";
public const string Address = "address";
public const string Utxo = "utxo";
}
public string WalletId { get; set; }
public string Type { get; set; }
public string Id { get; set; }
public string Data { get; set; }
public List<WalletObjectLinkData> Bs { get; set; }
public List<WalletObjectLinkData> As { get; set; }
public IEnumerable<(string type, string id, string linkdata, string objectdata)> GetLinks()
{
if (Bs is not null)
foreach (var c in Bs)
{
yield return (c.BType, c.BId, c.Data, c.B?.Data);
}
if (As is not null)
foreach (var c in As)
{
yield return (c.AType, c.AId, c.Data, c.A?.Data);
}
}
public IEnumerable<WalletObjectData> GetNeighbours()
{
if (Bs != null)
foreach (var c in Bs)
{
if (c.B != null)
yield return c.B;
}
if (As != null)
foreach (var c in As)
{
if (c.A != null)
yield return c.A;
}
}
public List<WalletObjectLinkData> ChildLinks { get; set; }
public List<WalletObjectLinkData> ParentLinks { get; set; }
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
{
@ -71,12 +32,6 @@ namespace BTCPayServer.Data
o.Type,
o.Id,
});
builder.Entity<WalletObjectData>().HasIndex(o =>
new
{
o.Type,
o.Id
});
if (databaseFacade.IsNpgsql())
{

@ -11,14 +11,14 @@ namespace BTCPayServer.Data
public class WalletObjectLinkData
{
public string WalletId { get; set; }
public string AType { get; set; }
public string AId { get; set; }
public string BType { get; set; }
public string BId { get; set; }
public string ParentType { get; set; }
public string ParentId { get; set; }
public string ChildType { get; set; }
public string ChildId { get; set; }
public string Data { get; set; }
public WalletObjectData A { get; set; }
public WalletObjectData B { get; set; }
public WalletObjectData Parent { get; set; }
public WalletObjectData Child { get; set; }
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
{
@ -26,28 +26,28 @@ namespace BTCPayServer.Data
new
{
o.WalletId,
o.AType,
o.AId,
o.BType,
o.BId,
o.ParentType,
o.ParentId,
o.ChildType,
o.ChildId,
});
builder.Entity<WalletObjectLinkData>().HasIndex(o => new
{
o.WalletId,
o.BType,
o.BId,
o.ChildType,
o.ChildId,
});
builder.Entity<WalletObjectLinkData>()
.HasOne(o => o.A)
.WithMany(o => o.Bs)
.HasForeignKey(o => new { o.WalletId, o.AType, o.AId })
.HasOne(o => o.Parent)
.WithMany(o => o.ChildLinks)
.HasForeignKey(o => new { o.WalletId, o.ParentType, o.ParentId })
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<WalletObjectLinkData>()
.HasOne(o => o.B)
.WithMany(o => o.As)
.HasForeignKey(o => new { o.WalletId, o.BType, o.BId })
.HasOne(o => o.Child)
.WithMany(o => o.ParentLinks)
.HasForeignKey(o => new { o.WalletId, o.ChildType, o.ChildId })
.OnDelete(DeleteBehavior.Cascade);
if (databaseFacade.IsNpgsql())

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

@ -1,4 +1,4 @@
// <auto-generated />
// <auto-generated />
using System;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore;
@ -17,21 +17,20 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.AddColumn<string>(
name: "StoreDataId",
table: "Payouts",
nullable: true,
maxLength: maxLength);
type: "TEXT",
nullable: true);
migrationBuilder.CreateTable(
name: "PayoutProcessors",
columns: table => new
{
Id = table.Column<string>(nullable: false, maxLength: maxLength),
StoreId = table.Column<string>(nullable: true, maxLength: maxLength),
PaymentMethod = table.Column<string>(nullable: true),
Processor = table.Column<string>(nullable: true),
Id = table.Column<string>(type: "TEXT", nullable: false),
StoreId = table.Column<string>(type: "TEXT", nullable: true),
PaymentMethod = table.Column<string>(type: "TEXT", nullable: true),
Processor = table.Column<string>(type: "TEXT", nullable: true),
Blob = table.Column<byte[]>(nullable: true)
},
constraints: table =>

@ -1,4 +1,4 @@
// <auto-generated />
// <auto-generated />
using System;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore;
@ -17,13 +17,12 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
migrationBuilder.CreateTable(
name: "LightningAddresses",
columns: table => new
{
Username = table.Column<string>(nullable: false, maxLength: maxLength),
StoreDataId = table.Column<string>(nullable: false, maxLength: maxLength),
Username = table.Column<string>(type: "TEXT", nullable: false),
StoreDataId = table.Column<string>(type: "TEXT", nullable: false),
Blob = table.Column<byte[]>( nullable: true)
},
constraints: table =>

@ -14,13 +14,12 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxlength = migrationBuilder.IsMySql() ? 255 : null;
migrationBuilder.CreateTable(
name: "StoreSettings",
columns: table => new
{
Name = table.Column<string>(nullable: false, maxLength: maxlength),
StoreId = table.Column<string>(nullable: false, maxLength: maxlength),
Name = table.Column<string>(type: "TEXT", nullable: false),
StoreId = table.Column<string>(type: "TEXT", nullable: false),
Value = table.Column<string>(type: migrationBuilder.IsNpgsql() ? "JSONB" : "TEXT", nullable: true)
},
constraints: table =>

@ -17,60 +17,52 @@ namespace BTCPayServer.Migrations
{
protected override void Up(MigrationBuilder migrationBuilder)
{
int? maxlength = migrationBuilder.IsMySql() ? 255 : null;
migrationBuilder.CreateTable(
name: "WalletObjects",
columns: table => new
{
WalletId = table.Column<string>(nullable: false, maxLength: maxlength),
Type = table.Column<string>(nullable: false, maxLength: maxlength),
Id = table.Column<string>(nullable: false, maxLength: maxlength),
WalletId = table.Column<string>(type: "TEXT", nullable: false),
Type = table.Column<string>(type: "TEXT", nullable: false),
Id = table.Column<string>(type: "TEXT", nullable: false),
Data = table.Column<string>(type: migrationBuilder.IsNpgsql() ? "JSONB" : "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_WalletObjects", x => new { x.WalletId, x.Type, x.Id });
});
migrationBuilder.CreateIndex(
name: "IX_WalletObjects_Type_Id",
table: "WalletObjects",
columns: new[] { "Type", "Id" });
maxlength = migrationBuilder.IsMySql() ? 100 : null;
migrationBuilder.CreateTable(
name: "WalletObjectLinks",
columns: table => new
{
WalletId = table.Column<string>(nullable: false, maxLength: maxlength),
AType = table.Column<string>(nullable: false, maxLength: maxlength),
AId = table.Column<string>(nullable: false, maxLength: maxlength),
BType = table.Column<string>(nullable: false, maxLength: maxlength),
BId = table.Column<string>(nullable: false, maxLength: maxlength),
WalletId = table.Column<string>(type: "TEXT", nullable: false),
ParentType = table.Column<string>(type: "TEXT", nullable: false),
ParentId = table.Column<string>(type: "TEXT", nullable: false),
ChildType = table.Column<string>(type: "TEXT", nullable: false),
ChildId = table.Column<string>(type: "TEXT", nullable: false),
Data = table.Column<string>(type: migrationBuilder.IsNpgsql() ? "JSONB" : "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_WalletObjectLinks", x => new { x.WalletId, x.AType, x.AId, x.BType, x.BId });
table.PrimaryKey("PK_WalletObjectLinks", x => new { x.WalletId, x.ParentType, x.ParentId, x.ChildType, x.ChildId });
table.ForeignKey(
name: "FK_WalletObjectLinks_WalletObjects_WalletId_BType_BId",
columns: x => new { x.WalletId, x.BType, x.BId },
name: "FK_WalletObjectLinks_WalletObjects_WalletId_ChildType_ChildId",
columns: x => new { x.WalletId, x.ChildType, x.ChildId },
principalTable: "WalletObjects",
principalColumns: new[] { "WalletId", "Type", "Id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_WalletObjectLinks_WalletObjects_WalletId_AType_AId",
columns: x => new { x.WalletId, x.AType, x.AId },
name: "FK_WalletObjectLinks_WalletObjects_WalletId_ParentType_ParentId",
columns: x => new { x.WalletId, x.ParentType, x.ParentId },
principalTable: "WalletObjects",
principalColumns: new[] { "WalletId", "Type", "Id" },
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_WalletObjectLinks_WalletId_BType_BId",
name: "IX_WalletObjectLinks_WalletId_ChildType_ChildId",
table: "WalletObjectLinks",
columns: new[] { "WalletId", "BType", "BId" });
columns: new[] { "WalletId", "ChildType", "ChildId" });
}
protected override void Down(MigrationBuilder migrationBuilder)

@ -1,31 +0,0 @@
using System;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20221128062447_jsonb")]
public partial class jsonb : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
if (migrationBuilder.IsNpgsql())
{
migrationBuilder.Sql("ALTER TABLE \"Settings\" ALTER COLUMN \"Value\" TYPE JSONB USING \"Value\"::JSONB");
migrationBuilder.Sql("ALTER TABLE \"Stores\" ALTER COLUMN \"StoreBlob\" TYPE JSONB USING regexp_replace(convert_from(\"StoreBlob\",'UTF8'), '\\\\u0000', '', 'g')::JSONB");
}
}
protected override void Down(MigrationBuilder migrationBuilder)
{
// Not supported
}
}
}

@ -1,30 +0,0 @@
using System;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20230123062447_migrateoldratesource")]
public partial class migrateoldratesource : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
if (migrationBuilder.IsNpgsql())
{
migrationBuilder.Sql("UPDATE \"Stores\" SET \"StoreBlob\"=jsonb_set(\"StoreBlob\", \'{preferredExchange}\', \'{\"oasis_trade\": \"oasisdev\", \"gdax\":\"coinbasepro\", \"coinaverage\":\"coingecko\"}\'::jsonb->(\"StoreBlob\"->>\'preferredExchange\')) WHERE \"StoreBlob\"->>\'preferredExchange\' = ANY (ARRAY[\'oasis_trade\', \'gdax\', \'coinaverage\']);");
}
}
protected override void Down(MigrationBuilder migrationBuilder)
{
// Not supported
}
}
}

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