Compare commits
1 Commits
fix-array-
...
fixpluginp
Author | SHA1 | Date | |
---|---|---|---|
|
11154ff19d |
12
.github/ISSUE_TEMPLATE/config.yml
vendored
12
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,14 +1,8 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: 💡 Request a feature
|
||||
url: https://github.com/btcpayserver/btcpayserver/discussions/categories/ideas-feature-requests
|
||||
about: Submit a feature request or vote on ideas posted by others. Features with most upvotes become roadmap candidates
|
||||
- name: 🧑💻 Ask a technical question
|
||||
url: https://github.com/btcpayserver/btcpayserver/discussions/new?category=technical-support
|
||||
about: If you're experiencing a technical problem post it to our community support forum
|
||||
- name: 🔌 Report a problem with a plugin
|
||||
url: https://github.com/btcpayserver/btcpayserver/discussions/new?category=plugins-integrations
|
||||
about: Experiencing a problem with a third-party plugin? Post it here and we will tag their developers to assist
|
||||
- name: 🚀 Discussions
|
||||
url: https://github.com/btcpayserver/btcpayserver/discussions
|
||||
about: Technical discussions, questions and feature requests
|
||||
- name: 📝 Official Documentation
|
||||
url: https://docs.btcpayserver.org
|
||||
about: Check our documentation for answers to common questions
|
||||
|
21
.run/Pack Test Extension.run.xml
Normal file
21
.run/Pack Test Extension.run.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Pack Test Plugin" type="DotNetProject" factoryName=".NET Project" singleton="false">
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/BTCPayServer.PluginPacker/bin/Debug/netcoreapp3.1/BTCPayServer.PluginPacker.dll" />
|
||||
<option name="PROGRAM_PARAMETERS" value="../../../../BTCPayServer.Plugins.Test\bin\Debug\netcoreapp3.1 BTCPayServer.Plugins.Test "../../../../Packed Plugins"" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/BTCPayServer.PluginPacker/bin/Debug/netcoreapp3.1" />
|
||||
<option name="PASS_PARENT_ENVS" value="1" />
|
||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||
<option name="USE_MONO" value="0" />
|
||||
<option name="RUNTIME_ARGUMENTS" value="" />
|
||||
<option name="PROJECT_PATH" value="$PROJECT_DIR$/BTCPayServer.PluginPacker/BTCPayServer.PluginPacker.csproj" />
|
||||
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||
<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" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -19,8 +19,6 @@ namespace BTCPayServer.Abstractions.Contracts
|
||||
public class NotificationViewModel
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Identifier { get; set; }
|
||||
public string Type { get; set; }
|
||||
public DateTimeOffset Created { get; set; }
|
||||
public string Body { get; set; }
|
||||
public string ActionLink { get; set; }
|
||||
|
@@ -5,8 +5,4 @@ public class AssetBalancesUnavailableException : CustodianApiException
|
||||
public AssetBalancesUnavailableException(System.Exception e) : base(500, "asset-balances-unavailable", $"Cannot fetch the asset balances: {e.Message}", e)
|
||||
{
|
||||
}
|
||||
|
||||
public AssetBalancesUnavailableException(string errorMsg) : base(500, "asset-balances-unavailable", $"Cannot fetch the asset balances: {errorMsg}")
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@@ -10,7 +10,7 @@ public class CustodianApiException : Exception
|
||||
HttpStatus = httpStatus;
|
||||
Code = code;
|
||||
}
|
||||
|
||||
|
||||
public CustodianApiException(int httpStatus, string code, string message) : this(httpStatus, code, message, null)
|
||||
{
|
||||
}
|
||||
|
@@ -1,28 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.JsonConverters;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Custodians.Client;
|
||||
|
||||
public class SimulateWithdrawalResult
|
||||
{
|
||||
public string PaymentMethod { get; }
|
||||
public string Asset { get; }
|
||||
public decimal MinQty { get; }
|
||||
public decimal MaxQty { get; }
|
||||
|
||||
public List<LedgerEntryData> LedgerEntries { get; }
|
||||
|
||||
// Fee can be NULL if unknown.
|
||||
public decimal? Fee { get; }
|
||||
|
||||
public SimulateWithdrawalResult(string paymentMethod, string asset, List<LedgerEntryData> ledgerEntries,
|
||||
decimal minQty, decimal maxQty)
|
||||
{
|
||||
PaymentMethod = paymentMethod;
|
||||
Asset = asset;
|
||||
LedgerEntries = ledgerEntries;
|
||||
MinQty = minQty;
|
||||
MaxQty = maxQty;
|
||||
}
|
||||
}
|
@@ -5,14 +5,9 @@ using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for custodians that can move funds to the store wallet.
|
||||
/// </summary>
|
||||
public interface ICanWithdraw
|
||||
{
|
||||
public Task<WithdrawResult> WithdrawToStoreWalletAsync(string paymentMethod, decimal amount, JObject config, CancellationToken cancellationToken);
|
||||
|
||||
public Task<SimulateWithdrawalResult> SimulateWithdrawalAsync(string paymentMethod, decimal qty, JObject config, CancellationToken cancellationToken);
|
||||
public Task<WithdrawResult> WithdrawAsync(string paymentMethod, decimal amount, JObject config, CancellationToken cancellationToken);
|
||||
|
||||
public Task<WithdrawResult> GetWithdrawalInfoAsync(string paymentMethod, string withdrawalId, JObject config, CancellationToken cancellationToken);
|
||||
|
||||
|
@@ -1,4 +1,3 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -21,6 +20,7 @@ public interface ICustodian
|
||||
*/
|
||||
Task<Dictionary<string, decimal>> GetAssetBalancesAsync(JObject config, CancellationToken cancellationToken);
|
||||
|
||||
public Task<Form.Form> GetConfigForm(JObject config, CancellationToken cancellationToken = default);
|
||||
public Task<Form.Form> GetConfigForm(JObject config, string locale,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
}
|
||||
|
@@ -7,10 +7,6 @@ namespace BTCPayServer.Abstractions.Extensions;
|
||||
|
||||
public static class GreenfieldExtensions
|
||||
{
|
||||
public static IActionResult UserNotFound(this ControllerBase ctrl)
|
||||
{
|
||||
return ctrl.CreateAPIError(404, "user-not-found", "The user was not found");
|
||||
}
|
||||
public static IActionResult CreateValidationError(this ControllerBase controller, ModelStateDictionary modelState)
|
||||
{
|
||||
return controller.UnprocessableEntity(modelState.ToGreenfieldValidationError());
|
||||
|
@@ -12,7 +12,7 @@ public class Field
|
||||
{
|
||||
public static Field Create(string label, string name, string value, bool required, string helpText, string type = "text")
|
||||
{
|
||||
return new Field
|
||||
return new Field()
|
||||
{
|
||||
Label = label,
|
||||
Name = name,
|
||||
@@ -26,14 +26,14 @@ public class Field
|
||||
// The name of the HTML5 node. Should be used as the key for the posted data.
|
||||
public string Name;
|
||||
|
||||
public bool Constant;
|
||||
public bool Hidden;
|
||||
|
||||
// HTML5 compatible type string like "text", "textarea", "email", "password", etc.
|
||||
// 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" };
|
||||
return new Field() { Type = "fieldset" };
|
||||
}
|
||||
|
||||
// The value field is what is currently in the DB or what the user entered, but possibly not saved yet due to validation errors.
|
||||
@@ -55,7 +55,7 @@ public class Field
|
||||
public List<Field> Fields { get; set; } = new();
|
||||
|
||||
// The field is considered "valid" if there are no validation errors
|
||||
public List<string> ValidationErrors = new();
|
||||
public List<string> ValidationErrors = new List<string>();
|
||||
|
||||
public virtual bool IsValid()
|
||||
{
|
||||
|
@@ -1,11 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Npgsql.Internal.TypeHandlers.GeometricHandlers;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Form;
|
||||
|
||||
@@ -22,7 +20,6 @@ public class Form
|
||||
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();
|
||||
|
||||
@@ -32,110 +29,129 @@ public class Form
|
||||
// Are all the fields valid in the form?
|
||||
public bool IsValid()
|
||||
{
|
||||
if (TopMessages?.Any(t => t.Type == AlertMessage.AlertMessageType.Danger) is true)
|
||||
return false;
|
||||
return Fields.Select(f => f.IsValid()).All(o => o);
|
||||
}
|
||||
|
||||
public Field GetFieldByFullName(string fullName)
|
||||
public Field GetFieldByName(string name)
|
||||
{
|
||||
foreach (var f in GetAllFields())
|
||||
return GetFieldByName(name, Fields, null);
|
||||
}
|
||||
|
||||
private static Field GetFieldByName(string name, List<Field> fields, string prefix)
|
||||
{
|
||||
prefix ??= string.Empty;
|
||||
foreach (var field in fields)
|
||||
{
|
||||
if (f.FullName == fullName)
|
||||
return f.Field;
|
||||
var currentPrefix = prefix;
|
||||
if (!string.IsNullOrEmpty(field.Name))
|
||||
{
|
||||
|
||||
currentPrefix = $"{prefix}{field.Name}";
|
||||
if (currentPrefix.Equals(name, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return field;
|
||||
}
|
||||
|
||||
currentPrefix += "_";
|
||||
}
|
||||
|
||||
var subFieldResult = GetFieldByName(name, field.Fields, currentPrefix);
|
||||
if (subFieldResult is not null)
|
||||
{
|
||||
return subFieldResult;
|
||||
}
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public IEnumerable<(string FullName, List<string> Path, Field Field)> GetAllFields()
|
||||
public List<string> GetAllNames()
|
||||
{
|
||||
HashSet<string> nameReturned = new();
|
||||
foreach (var f in GetAllFieldsCore(new List<string>(), Fields))
|
||||
{
|
||||
var fullName = string.Join('_', f.Path.Where(s => !string.IsNullOrEmpty(s)));
|
||||
if (!nameReturned.Add(fullName))
|
||||
continue;
|
||||
yield return (fullName, f.Path, f.Field);
|
||||
}
|
||||
return GetAllNames(Fields);
|
||||
}
|
||||
|
||||
public bool ValidateFieldNames(out List<string> errors)
|
||||
private static List<string> GetAllNames(List<Field> fields)
|
||||
{
|
||||
errors = new List<string>();
|
||||
HashSet<string> nameReturned = new();
|
||||
foreach (var f in GetAllFieldsCore(new List<string>(), Fields))
|
||||
{
|
||||
var fullName = string.Join('_', f.Path.Where(s => !string.IsNullOrEmpty(s)));
|
||||
if (!nameReturned.Add(fullName))
|
||||
{
|
||||
errors.Add($"Form contains duplicate field names '{fullName}'");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return errors.Count == 0;
|
||||
}
|
||||
var names = new List<string>();
|
||||
|
||||
IEnumerable<(List<string> Path, Field Field)> GetAllFieldsCore(List<string> path, List<Field> fields)
|
||||
{
|
||||
foreach (var field in fields)
|
||||
{
|
||||
List<string> thisPath = new(path.Count + 1);
|
||||
thisPath.AddRange(path);
|
||||
string prefix = string.Empty;
|
||||
if (!string.IsNullOrEmpty(field.Name))
|
||||
{
|
||||
thisPath.Add(field.Name);
|
||||
yield return (thisPath, field);
|
||||
names.Add(field.Name);
|
||||
prefix = $"{field.Name}_";
|
||||
}
|
||||
|
||||
foreach (var child in field.Fields)
|
||||
if (field.Fields.Any())
|
||||
{
|
||||
if (field.Constant)
|
||||
child.Constant = true;
|
||||
foreach (var descendant in GetAllFieldsCore(thisPath, field.Fields))
|
||||
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)
|
||||
{
|
||||
yield return descendant;
|
||||
result.TryAdd(keyValuePair.Key, keyValuePair.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyValuesFromForm(IEnumerable<KeyValuePair<string, StringValues>> form)
|
||||
{
|
||||
var values = form.GroupBy(f => f.Key, f => f.Value).ToDictionary(g => g.Key, g => g.First());
|
||||
foreach (var f in GetAllFields())
|
||||
{
|
||||
if (f.Field.Constant || !values.TryGetValue(f.FullName, out var val))
|
||||
continue;
|
||||
|
||||
f.Field.Value = val;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetValues(JObject values)
|
||||
{
|
||||
var fields = GetAllFields().ToDictionary(k => k.FullName, k => k.Field);
|
||||
SetValues(fields, new List<string>(), values);
|
||||
}
|
||||
|
||||
private void SetValues(Dictionary<string, Field> fields, List<string> path, JObject values)
|
||||
{
|
||||
foreach (var prop in values.Properties())
|
||||
{
|
||||
List<string> propPath = new List<string>(path.Count + 1);
|
||||
propPath.AddRange(path);
|
||||
propPath.Add(prop.Name);
|
||||
if (prop.Value.Type == JTokenType.Object)
|
||||
else
|
||||
{
|
||||
SetValues(fields, propPath, (JObject)prop.Value);
|
||||
}
|
||||
else if (prop.Value.Type == JTokenType.String)
|
||||
{
|
||||
var fullName = string.Join('_', propPath.Where(s => !string.IsNullOrEmpty(s)));
|
||||
if (fields.TryGetValue(fullName, out var f) && !f.Constant)
|
||||
f.Value = prop.Value.Value<string>();
|
||||
result.TryAdd(name, field.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -114,11 +114,6 @@ namespace BTCPayServer.Security
|
||||
_Policies.Add(policy);
|
||||
}
|
||||
|
||||
public void UnsafeEval()
|
||||
{
|
||||
Add("script-src", "'unsafe-eval'");
|
||||
}
|
||||
|
||||
public IEnumerable<ConsentSecurityPolicy> Rules => _Policies;
|
||||
public bool HasRules => _Policies.Count != 0;
|
||||
|
||||
|
@@ -23,19 +23,10 @@ public class SVGUse : UrlResolutionTagHelper2
|
||||
{
|
||||
_fileVersionProvider = fileVersionProvider;
|
||||
}
|
||||
|
||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
var attr = output.Attributes["href"].Value.ToString();
|
||||
var symbolIndex = attr!.IndexOf("#", StringComparison.InvariantCulture);
|
||||
var start = attr.IndexOf("~", StringComparison.InvariantCulture) + 1;
|
||||
var length = (symbolIndex != -1 ? symbolIndex : attr.Length) - start;
|
||||
var filePath = attr.Substring(start, length);
|
||||
if (!string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
var versioned = _fileVersionProvider.AddFileVersionToPath(ViewContext.HttpContext.Request.PathBase, filePath);
|
||||
attr = attr.Replace(filePath, versioned);
|
||||
}
|
||||
attr = _fileVersionProvider.AddFileVersionToPath(ViewContext.HttpContext.Request.PathBase, attr);
|
||||
output.Attributes.SetAttribute("href", attr);
|
||||
base.Process(context, output);
|
||||
}
|
||||
|
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -23,15 +22,6 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<ApiKeyData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<ApiKeyData> CreateAPIKey(string userId, CreateApiKeyRequest request, CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/users/{userId}/api-keys",
|
||||
bodyPayload: request, method: HttpMethod.Post), token);
|
||||
return await HandleResponse<ApiKeyData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task RevokeCurrentAPIKeyInfo(CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/api-keys/current", null, HttpMethod.Delete), token);
|
||||
@@ -45,14 +35,5 @@ namespace BTCPayServer.Client
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/api-keys/{apikey}", null, HttpMethod.Delete), token);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
public virtual async Task RevokeAPIKey(string userId, string apikey, CancellationToken token = default)
|
||||
{
|
||||
if (apikey == null)
|
||||
throw new ArgumentNullException(nameof(apikey));
|
||||
if (userId is null)
|
||||
throw new ArgumentNullException(nameof(userId));
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/users/{userId}/api-keys/{apikey}", null, HttpMethod.Delete), token);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -52,44 +52,6 @@ namespace BTCPayServer.Client
|
||||
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)
|
||||
{
|
||||
if (appId == null)
|
||||
|
@@ -50,7 +50,7 @@ namespace BTCPayServer.Client
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public virtual async Task<DepositAddressData> GetCustodianAccountDepositAddress(string storeId, string accountId, string paymentMethod, CancellationToken token = default)
|
||||
public virtual async Task<DepositAddressData> GetDepositAddress(string storeId, string accountId, string paymentMethod, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/addresses/{paymentMethod}"), token);
|
||||
return await HandleResponse<DepositAddressData>(response);
|
||||
@@ -58,6 +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,
|
||||
@@ -66,13 +67,13 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<MarketTradeResponseData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<MarketTradeResponseData> GetCustodianAccountTradeInfo(string storeId, string accountId, string tradeId, CancellationToken token = default)
|
||||
public virtual async Task<MarketTradeResponseData> GetTradeInfo(string storeId, string accountId, string tradeId, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/trades/{tradeId}", method: HttpMethod.Get), token);
|
||||
return await HandleResponse<MarketTradeResponseData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<TradeQuoteResponseData> GetCustodianAccountTradeQuote(string storeId, string accountId, string fromAsset, string toAsset, CancellationToken token = default)
|
||||
public virtual async Task<TradeQuoteResponseData> GetTradeQuote(string storeId, string accountId, string fromAsset, string toAsset, CancellationToken token = default)
|
||||
{
|
||||
var queryPayload = new Dictionary<string, object>();
|
||||
queryPayload.Add("fromAsset", fromAsset);
|
||||
@@ -81,19 +82,13 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<TradeQuoteResponseData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<WithdrawalResponseData> CreateCustodianAccountWithdrawal(string storeId, string accountId, WithdrawRequestData request, CancellationToken token = default)
|
||||
public virtual async Task<WithdrawalResponseData> 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);
|
||||
}
|
||||
|
||||
public virtual async Task<WithdrawalSimulationResponseData> SimulateCustodianAccountWithdrawal(string storeId, string accountId, WithdrawRequestData request, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/withdrawals/simulation", bodyPayload: request, method: HttpMethod.Post), token);
|
||||
return await HandleResponse<WithdrawalSimulationResponseData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<WithdrawalResponseData> GetCustodianAccountWithdrawalInfo(string storeId, string accountId, string paymentMethod, string withdrawalId, CancellationToken token = default)
|
||||
public virtual async Task<WithdrawalResponseData> GetWithdrawalInfo(string storeId, string accountId, string paymentMethod, string withdrawalId, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/withdrawals/{paymentMethod}/{withdrawalId}", method: HttpMethod.Get), token);
|
||||
return await HandleResponse<WithdrawalResponseData>(response);
|
||||
|
@@ -114,24 +114,6 @@ namespace BTCPayServer.Client
|
||||
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)
|
||||
{
|
||||
|
@@ -116,24 +116,6 @@ namespace BTCPayServer.Client
|
||||
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)
|
||||
{
|
||||
|
@@ -97,15 +97,5 @@ namespace BTCPayServer.Client
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -37,7 +37,7 @@ 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)
|
||||
@@ -47,18 +47,7 @@ namespace BTCPayServer.Client
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -30,9 +30,9 @@ namespace BTCPayServer.JsonConverters
|
||||
case JTokenType.Integer:
|
||||
case JTokenType.String:
|
||||
if (objectType == typeof(decimal) || objectType == typeof(decimal?))
|
||||
return decimal.Parse(token.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture);
|
||||
return decimal.Parse(token.ToString(), CultureInfo.InvariantCulture);
|
||||
if (objectType == typeof(double) || objectType == typeof(double?))
|
||||
return double.Parse(token.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture);
|
||||
return double.Parse(token.ToString(), CultureInfo.InvariantCulture);
|
||||
throw new JsonSerializationException("Unexpected object type: " + objectType);
|
||||
case JTokenType.Null when objectType == typeof(decimal?) || objectType == typeof(double?):
|
||||
return null;
|
||||
|
@@ -1,36 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Lightning;
|
||||
using NBitcoin.JsonConverters;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Client.JsonConverters
|
||||
{
|
||||
public class TradeQuantityJsonConverter : JsonConverter<TradeQuantity>
|
||||
{
|
||||
public override TradeQuantity ReadJson(JsonReader reader, Type objectType, TradeQuantity existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||
{
|
||||
JToken token = JToken.Load(reader);
|
||||
switch (token.Type)
|
||||
{
|
||||
case JTokenType.Float:
|
||||
case JTokenType.Integer:
|
||||
case JTokenType.String:
|
||||
if (TradeQuantity.TryParse(token.ToString(), out var q))
|
||||
return q;
|
||||
break;
|
||||
case JTokenType.Null:
|
||||
return null;
|
||||
}
|
||||
throw new JsonObjectException("Invalid TradeQuantity, expected string. Expected: \"1.50\" or \"50%\"", reader);
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, TradeQuantity value, JsonSerializer serializer)
|
||||
{
|
||||
if (value is not null)
|
||||
writer.WriteValue(value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
@@ -86,7 +86,6 @@ namespace BTCPayServer.Client.Models
|
||||
public bool? RequiresRefundEmail { get; set; } = null;
|
||||
public string DefaultLanguage { get; set; }
|
||||
public CheckoutType? CheckoutType { get; set; }
|
||||
public bool? LazyPaymentMethods { get; set; }
|
||||
}
|
||||
}
|
||||
public class InvoiceData : InvoiceDataBase
|
||||
|
@@ -3,6 +3,7 @@ namespace BTCPayServer.Client.Models
|
||||
public class LNURLPayPaymentMethodBaseData
|
||||
{
|
||||
public bool UseBech32Scheme { get; set; }
|
||||
public bool EnableForStandardInvoices { get; set; }
|
||||
public bool LUD12Enabled { get; set; }
|
||||
|
||||
public LNURLPayPaymentMethodBaseData()
|
||||
|
@@ -16,11 +16,12 @@ namespace BTCPayServer.Client.Models
|
||||
{
|
||||
}
|
||||
|
||||
public LNURLPayPaymentMethodData(string cryptoCode, bool enabled, bool useBech32Scheme)
|
||||
public LNURLPayPaymentMethodData(string cryptoCode, bool enabled, bool useBech32Scheme, bool enableForStandardInvoices)
|
||||
{
|
||||
Enabled = enabled;
|
||||
CryptoCode = cryptoCode;
|
||||
UseBech32Scheme = useBech32Scheme;
|
||||
EnableForStandardInvoices = enableForStandardInvoices;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,3 @@
|
||||
using BTCPayServer.JsonConverters;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
@@ -7,7 +6,6 @@ namespace BTCPayServer.Client.Models;
|
||||
public class LedgerEntryData
|
||||
{
|
||||
public string Asset { get; }
|
||||
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||
public decimal Qty { get; }
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
|
@@ -3,8 +3,8 @@ namespace BTCPayServer.Client.Models;
|
||||
public class LightningAddressData
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string CurrencyCode { get; set; }
|
||||
public string? CurrencyCode { get; set; }
|
||||
public decimal? Min { get; set; }
|
||||
public decimal? Max { get; set; }
|
||||
|
||||
|
||||
}
|
||||
|
@@ -16,7 +16,7 @@ namespace BTCPayServer.Client.Models
|
||||
|
||||
[JsonProperty("BOLT11")]
|
||||
public string BOLT11 { get; set; }
|
||||
|
||||
|
||||
public string PaymentHash { get; set; }
|
||||
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
@@ -24,7 +24,7 @@ namespace BTCPayServer.Client.Models
|
||||
|
||||
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
||||
public DateTimeOffset? PaidAt { get; set; }
|
||||
|
||||
|
||||
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
||||
public DateTimeOffset ExpiresAt { get; set; }
|
||||
|
||||
|
@@ -4,6 +4,7 @@ namespace BTCPayServer.Client.Models
|
||||
{
|
||||
|
||||
public string ConnectionString { get; set; }
|
||||
public bool DisableBOLT11PaymentOption { get; set; }
|
||||
public LightningNetworkPaymentMethodBaseData()
|
||||
{
|
||||
|
||||
|
@@ -16,12 +16,13 @@ namespace BTCPayServer.Client.Models
|
||||
{
|
||||
}
|
||||
|
||||
public LightningNetworkPaymentMethodData(string cryptoCode, string connectionString, bool enabled, string paymentMethod)
|
||||
public LightningNetworkPaymentMethodData(string cryptoCode, string connectionString, bool enabled, string paymentMethod, bool disableBOLT11PaymentOption)
|
||||
{
|
||||
Enabled = enabled;
|
||||
CryptoCode = cryptoCode;
|
||||
ConnectionString = connectionString;
|
||||
PaymentMethod = paymentMethod;
|
||||
DisableBOLT11PaymentOption = disableBOLT11PaymentOption;
|
||||
}
|
||||
|
||||
public string PaymentMethod { get; set; }
|
||||
|
@@ -6,8 +6,6 @@ namespace BTCPayServer.Client.Models
|
||||
public class NotificationData
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Identifier { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string Body { get; set; }
|
||||
public bool Seen { get; set; }
|
||||
public Uri Link { get; set; }
|
||||
|
@@ -1,13 +0,0 @@
|
||||
using BTCPayServer.JsonConverters;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Client.Models;
|
||||
|
||||
public class PaymentMethodCriteriaData
|
||||
{
|
||||
public string PaymentMethod { get; set; }
|
||||
public string CurrencyCode { get; set; }
|
||||
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||
public decimal Amount { get; set; }
|
||||
public bool Above { get; set; }
|
||||
}
|
@@ -15,53 +15,11 @@ namespace BTCPayServer.Client.Models
|
||||
|
||||
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; }
|
||||
// We can add POS specific things here later
|
||||
}
|
||||
|
||||
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 Crowdfund 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; }
|
||||
}
|
||||
}
|
@@ -63,9 +63,6 @@ namespace BTCPayServer.Client.Models
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public NetworkFeeMode NetworkFeeMode { get; set; } = NetworkFeeMode.Never;
|
||||
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public List<PaymentMethodCriteriaData> PaymentMethodCriteria { get; set; }
|
||||
|
||||
public bool PayJoinEnabled { get; set; }
|
||||
|
||||
public InvoiceData.ReceiptOptions Receipt { get; set; }
|
||||
|
10
BTCPayServer.Client/Models/StoreRatePreviewResult.cs
Normal file
10
BTCPayServer.Client/Models/StoreRatePreviewResult.cs
Normal file
@@ -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;
|
||||
|
||||
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,13 +1,8 @@
|
||||
using BTCPayServer.JsonConverters;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Client.Models;
|
||||
|
||||
public class TradeQuoteResponseData
|
||||
{
|
||||
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||
public decimal Bid { get; }
|
||||
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||
public decimal Ask { get; }
|
||||
public string ToAsset { get; }
|
||||
public string FromAsset { get; }
|
||||
|
@@ -1,11 +1,8 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Client.Models;
|
||||
|
||||
public class TradeRequestData
|
||||
{
|
||||
public string FromAsset { set; get; }
|
||||
public string ToAsset { set; get; }
|
||||
[JsonConverter(typeof(JsonConverters.TradeQuantityJsonConverter))]
|
||||
public TradeQuantity Qty { set; get; }
|
||||
public string Qty { set; get; }
|
||||
}
|
||||
|
@@ -1,85 +1,13 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Net.Http.Headers;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Client.Models;
|
||||
|
||||
public class WithdrawRequestData
|
||||
{
|
||||
public string PaymentMethod { set; get; }
|
||||
[JsonConverter(typeof(JsonConverters.TradeQuantityJsonConverter))]
|
||||
public TradeQuantity Qty { set; get; }
|
||||
public decimal Qty { set; get; }
|
||||
|
||||
public WithdrawRequestData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public WithdrawRequestData(string paymentMethod, TradeQuantity qty)
|
||||
public WithdrawRequestData(string paymentMethod, decimal qty)
|
||||
{
|
||||
PaymentMethod = paymentMethod;
|
||||
Qty = qty;
|
||||
}
|
||||
}
|
||||
|
||||
#nullable enable
|
||||
public record TradeQuantity
|
||||
{
|
||||
public TradeQuantity(decimal value, ValueType type)
|
||||
{
|
||||
Type = type;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public enum ValueType
|
||||
{
|
||||
Exact,
|
||||
Percent
|
||||
}
|
||||
|
||||
public ValueType Type { get; }
|
||||
public decimal Value { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (Type == ValueType.Exact)
|
||||
return Value.ToString(CultureInfo.InvariantCulture);
|
||||
else
|
||||
return Value.ToString(CultureInfo.InvariantCulture) + "%";
|
||||
}
|
||||
public static TradeQuantity Parse(string str)
|
||||
{
|
||||
if (!TryParse(str, out var r))
|
||||
throw new FormatException("Invalid TradeQuantity");
|
||||
return r;
|
||||
}
|
||||
public static bool TryParse(string str, [MaybeNullWhen(false)] out TradeQuantity quantity)
|
||||
{
|
||||
if (str is null)
|
||||
throw new ArgumentNullException(nameof(str));
|
||||
quantity = null;
|
||||
str = str.Trim();
|
||||
str = str.Replace(" ", "");
|
||||
if (str.Length == 0)
|
||||
return false;
|
||||
if (str[^1] == '%')
|
||||
{
|
||||
if (!decimal.TryParse(str[..^1], NumberStyles.Any, CultureInfo.InvariantCulture, out var r))
|
||||
return false;
|
||||
if (r < 0.0m)
|
||||
return false;
|
||||
quantity = new TradeQuantity(r, TradeQuantity.ValueType.Percent);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!decimal.TryParse(str, NumberStyles.Any, CultureInfo.InvariantCulture, out var r))
|
||||
return false;
|
||||
if (r < 0.0m)
|
||||
return false;
|
||||
quantity = new TradeQuantity(r, TradeQuantity.ValueType.Exact);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@@ -1,22 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BTCPayServer.Client.Models;
|
||||
|
||||
public abstract class WithdrawalBaseResponseData
|
||||
{
|
||||
public string Asset { get; }
|
||||
public string PaymentMethod { get; }
|
||||
public List<LedgerEntryData> LedgerEntries { get; }
|
||||
public string AccountId { get; }
|
||||
public string CustodianCode { get; }
|
||||
|
||||
public WithdrawalBaseResponseData(string paymentMethod, string asset, List<LedgerEntryData> ledgerEntries, string accountId,
|
||||
string custodianCode)
|
||||
{
|
||||
PaymentMethod = paymentMethod;
|
||||
Asset = asset;
|
||||
LedgerEntries = ledgerEntries;
|
||||
AccountId = accountId;
|
||||
CustodianCode = custodianCode;
|
||||
}
|
||||
}
|
@@ -5,13 +5,18 @@ using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace BTCPayServer.Client.Models;
|
||||
|
||||
public class WithdrawalResponseData : WithdrawalBaseResponseData
|
||||
public class WithdrawalResponseData
|
||||
{
|
||||
public string Asset { get; }
|
||||
public string PaymentMethod { get; }
|
||||
public List<LedgerEntryData> LedgerEntries { get; }
|
||||
public string WithdrawalId { get; }
|
||||
public string AccountId { get; }
|
||||
public string CustodianCode { get; }
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public WithdrawalStatus Status { get; }
|
||||
|
||||
public string WithdrawalId { get; }
|
||||
public DateTimeOffset CreatedTime { get; }
|
||||
|
||||
public string TransactionId { get; }
|
||||
@@ -19,10 +24,14 @@ public class WithdrawalResponseData : WithdrawalBaseResponseData
|
||||
public string TargetAddress { get; }
|
||||
|
||||
public WithdrawalResponseData(string paymentMethod, string asset, List<LedgerEntryData> ledgerEntries, string withdrawalId, string accountId,
|
||||
string custodianCode, WithdrawalStatus status, DateTimeOffset createdTime, string targetAddress, string transactionId) : base(paymentMethod, asset, ledgerEntries, accountId,
|
||||
custodianCode)
|
||||
string custodianCode, WithdrawalStatus status, DateTimeOffset createdTime, string targetAddress, string transactionId)
|
||||
{
|
||||
PaymentMethod = paymentMethod;
|
||||
Asset = asset;
|
||||
LedgerEntries = ledgerEntries;
|
||||
WithdrawalId = withdrawalId;
|
||||
AccountId = accountId;
|
||||
CustodianCode = custodianCode;
|
||||
TargetAddress = targetAddress;
|
||||
TransactionId = transactionId;
|
||||
Status = status;
|
||||
|
@@ -1,21 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using BTCPayServer.JsonConverters;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Client.Models;
|
||||
|
||||
public class WithdrawalSimulationResponseData : WithdrawalBaseResponseData
|
||||
{
|
||||
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||
public decimal? MinQty { get; set; }
|
||||
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||
public decimal? MaxQty { get; set; }
|
||||
|
||||
public WithdrawalSimulationResponseData(string paymentMethod, string asset, string accountId,
|
||||
string custodianCode, List<LedgerEntryData> ledgerEntries, decimal? minQty, decimal? maxQty) : base(paymentMethod,
|
||||
asset, ledgerEntries, accountId, custodianCode)
|
||||
{
|
||||
MinQty = minQty;
|
||||
MaxQty = maxQty;
|
||||
}
|
||||
}
|
@@ -28,11 +28,8 @@ namespace BTCPayServer.Client
|
||||
public const string CanViewNotificationsForUser = "btcpay.user.canviewnotificationsforuser";
|
||||
public const string CanViewUsers = "btcpay.server.canviewusers";
|
||||
public const string CanCreateUser = "btcpay.server.cancreateuser";
|
||||
public const string CanManageUsers = "btcpay.server.canmanageusers";
|
||||
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";
|
||||
@@ -67,14 +64,11 @@ namespace BTCPayServer.Client
|
||||
yield return CanViewLightningInvoiceInStore;
|
||||
yield return CanCreateLightningInvoiceInStore;
|
||||
yield return CanManagePullPayments;
|
||||
yield return CanCreatePullPayments;
|
||||
yield return CanCreateNonApprovedPullPayments;
|
||||
yield return CanViewCustodianAccounts;
|
||||
yield return CanManageCustodianAccounts;
|
||||
yield return CanDepositToCustodianAccounts;
|
||||
yield return CanWithdrawFromCustodianAccounts;
|
||||
yield return CanTradeCustodianAccount;
|
||||
yield return CanManageUsers;
|
||||
}
|
||||
}
|
||||
public static bool IsValidPolicy(string policy)
|
||||
@@ -98,45 +92,9 @@ namespace BTCPayServer.Client
|
||||
{
|
||||
return policy.StartsWith("btcpay.plugin", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
public static bool IsUserPolicy(string policy)
|
||||
{
|
||||
return policy.StartsWith("btcpay.user", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
public class PermissionSet
|
||||
{
|
||||
public PermissionSet() : this(Array.Empty<Permission>())
|
||||
{
|
||||
|
||||
}
|
||||
public PermissionSet(Permission[] permissions)
|
||||
{
|
||||
Permissions = permissions;
|
||||
}
|
||||
|
||||
public Permission[] Permissions { get; }
|
||||
|
||||
public bool Contains(Permission requestedPermission)
|
||||
{
|
||||
return Permissions.Any(p => p.Contains(requestedPermission));
|
||||
}
|
||||
public bool Contains(string permission, string store)
|
||||
{
|
||||
if (permission is null)
|
||||
throw new ArgumentNullException(nameof(permission));
|
||||
if (store is null)
|
||||
throw new ArgumentNullException(nameof(store));
|
||||
return Contains(Permission.Create(permission, store));
|
||||
}
|
||||
}
|
||||
public class Permission
|
||||
{
|
||||
static Permission()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
public static Permission Create(string policy, string scope = null)
|
||||
{
|
||||
if (TryCreatePermission(policy, scope, out var r))
|
||||
@@ -152,7 +110,7 @@ namespace BTCPayServer.Client
|
||||
policy = policy.Trim().ToLowerInvariant();
|
||||
if (!Policies.IsValidPolicy(policy))
|
||||
return false;
|
||||
if (!string.IsNullOrEmpty(scope) && !Policies.IsStorePolicy(policy))
|
||||
if (scope != null && !Policies.IsStorePolicy(policy))
|
||||
return false;
|
||||
permission = new Permission(policy, scope);
|
||||
return true;
|
||||
@@ -205,7 +163,7 @@ namespace BTCPayServer.Client
|
||||
}
|
||||
if (!Policies.IsStorePolicy(subpermission.Policy))
|
||||
return true;
|
||||
return Scope == null || subpermission.Scope == Scope;
|
||||
return Scope == null || subpermission.Scope == this.Scope;
|
||||
}
|
||||
|
||||
public static IEnumerable<Permission> ToPermissions(string[] permissions)
|
||||
@@ -221,61 +179,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,
|
||||
Policies.CanUseLightningNodeInStore);
|
||||
|
||||
PolicyHasChild(Policies.CanManageUsers, Policies.CanCreateUser);
|
||||
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,
|
||||
Policies.CanManageUsers);
|
||||
PolicyHasChild(Policies.CanUseInternalLightningNode, Policies.CanCreateLightningInvoiceInternalNode, Policies.CanViewLightningInvoiceInternalNode);
|
||||
PolicyHasChild(Policies.CanManageCustodianAccounts, Policies.CanViewCustodianAccounts);
|
||||
PolicyHasChild(Policies.CanModifyInvoices, Policies.CanViewInvoices, Policies.CanCreateInvoice, Policies.CanCreateLightningInvoiceInStore);
|
||||
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.CanViewLightningInvoiceInternalNode when this.Policy == Policies.CanUseInternalLightningNode:
|
||||
case Policies.CanCreateLightningInvoiceInStore when this.Policy == Policies.CanUseLightningNodeInStore:
|
||||
case Policies.CanViewLightningInvoiceInStore 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,17 +218,23 @@ namespace BTCPayServer.Client
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Scope != null ? $"{Policy}:{Scope}" : Policy;
|
||||
if (Scope != null)
|
||||
{
|
||||
return $"{Policy}:{Scope}";
|
||||
}
|
||||
return Policy;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
Permission item = obj as Permission;
|
||||
return item != null && ToString().Equals(item.ToString());
|
||||
if (item == null)
|
||||
return false;
|
||||
return ToString().Equals(item.ToString());
|
||||
}
|
||||
public static bool operator ==(Permission a, Permission b)
|
||||
{
|
||||
if (ReferenceEquals(a, b))
|
||||
if (System.Object.ReferenceEquals(a, b))
|
||||
return true;
|
||||
if (((object)a == null) || ((object)b == null))
|
||||
return false;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Data.Data;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
@@ -30,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; }
|
||||
@@ -74,7 +69,6 @@ namespace BTCPayServer.Data
|
||||
public DbSet<WebhookData> Webhooks { get; set; }
|
||||
public DbSet<LightningAddressData> LightningAddresses { get; set; }
|
||||
public DbSet<PayoutProcessorData> PayoutProcessors { get; set; }
|
||||
public DbSet<FormData> Forms { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
@@ -89,23 +83,23 @@ namespace BTCPayServer.Data
|
||||
|
||||
// some of the data models don't have OnModelCreating for now, commenting them
|
||||
|
||||
ApplicationUser.OnModelCreating(builder, Database);
|
||||
ApplicationUser.OnModelCreating(builder);
|
||||
AddressInvoiceData.OnModelCreating(builder);
|
||||
APIKeyData.OnModelCreating(builder, Database);
|
||||
APIKeyData.OnModelCreating(builder);
|
||||
AppData.OnModelCreating(builder);
|
||||
CustodianAccountData.OnModelCreating(builder, Database);
|
||||
CustodianAccountData.OnModelCreating(builder);
|
||||
//StoredFile.OnModelCreating(builder);
|
||||
InvoiceEventData.OnModelCreating(builder);
|
||||
InvoiceSearchData.OnModelCreating(builder);
|
||||
InvoiceWebhookDeliveryData.OnModelCreating(builder);
|
||||
InvoiceData.OnModelCreating(builder, Database);
|
||||
NotificationData.OnModelCreating(builder, Database);
|
||||
InvoiceData.OnModelCreating(builder);
|
||||
NotificationData.OnModelCreating(builder);
|
||||
//OffchainTransactionData.OnModelCreating(builder);
|
||||
BTCPayServer.Data.PairedSINData.OnModelCreating(builder);
|
||||
PairingCodeData.OnModelCreating(builder);
|
||||
//PayjoinLock.OnModelCreating(builder);
|
||||
PaymentRequestData.OnModelCreating(builder, Database);
|
||||
PaymentData.OnModelCreating(builder, Database);
|
||||
PaymentRequestData.OnModelCreating(builder);
|
||||
PaymentData.OnModelCreating(builder);
|
||||
PayoutData.OnModelCreating(builder);
|
||||
PendingInvoiceData.OnModelCreating(builder);
|
||||
//PlannedTransaction.OnModelCreating(builder);
|
||||
@@ -116,7 +110,7 @@ namespace BTCPayServer.Data
|
||||
StoreWebhookData.OnModelCreating(builder);
|
||||
StoreData.OnModelCreating(builder, Database);
|
||||
U2FDevice.OnModelCreating(builder);
|
||||
Fido2Credential.OnModelCreating(builder, Database);
|
||||
Fido2Credential.OnModelCreating(builder);
|
||||
BTCPayServer.Data.UserStore.OnModelCreating(builder);
|
||||
//WalletData.OnModelCreating(builder);
|
||||
WalletObjectData.OnModelCreating(builder, Database);
|
||||
@@ -124,11 +118,10 @@ namespace BTCPayServer.Data
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
WalletTransactionData.OnModelCreating(builder);
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
WebhookDeliveryData.OnModelCreating(builder, Database);
|
||||
LightningAddressData.OnModelCreating(builder, Database);
|
||||
PayoutProcessorData.OnModelCreating(builder, Database);
|
||||
WebhookData.OnModelCreating(builder, Database);
|
||||
FormData.OnModelCreating(builder, Database);
|
||||
WebhookDeliveryData.OnModelCreating(builder);
|
||||
LightningAddressData.OnModelCreating(builder);
|
||||
PayoutProcessorData.OnModelCreating(builder);
|
||||
//WebhookData.OnModelCreating(builder);
|
||||
|
||||
|
||||
if (Database.IsSqlite() && !_designTime)
|
||||
|
@@ -1,11 +1,9 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public class APIKeyData : IHasBlob<APIKeyBlob>
|
||||
public class APIKeyData
|
||||
{
|
||||
[MaxLength(50)]
|
||||
public string Id { get; set; }
|
||||
@@ -18,15 +16,13 @@ namespace BTCPayServer.Data
|
||||
|
||||
public APIKeyType Type { get; set; } = APIKeyType.Legacy;
|
||||
|
||||
[Obsolete("Use Blob2 instead")]
|
||||
public byte[] Blob { get; set; }
|
||||
public string Blob2 { get; set; }
|
||||
public StoreData StoreData { get; set; }
|
||||
public ApplicationUser User { get; set; }
|
||||
public string Label { get; set; }
|
||||
|
||||
|
||||
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
||||
internal static void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
builder.Entity<APIKeyData>()
|
||||
.HasOne(o => o.StoreData)
|
||||
@@ -40,13 +36,6 @@ namespace BTCPayServer.Data
|
||||
|
||||
builder.Entity<APIKeyData>()
|
||||
.HasIndex(o => o.StoreId);
|
||||
|
||||
if (databaseFacade.IsNpgsql())
|
||||
{
|
||||
builder.Entity<APIKeyData>()
|
||||
.Property(o => o.Blob2)
|
||||
.HasColumnType("JSONB");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -2,13 +2,11 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
// Add profile data for application users by adding properties to the ApplicationUser class
|
||||
public class ApplicationUser : IdentityUser, IHasBlob<UserBlob>
|
||||
public class ApplicationUser : IdentityUser
|
||||
{
|
||||
public bool RequiresEmailConfirmation { get; set; }
|
||||
public List<StoredFile> StoredFiles { get; set; }
|
||||
@@ -22,28 +20,15 @@ namespace BTCPayServer.Data
|
||||
public List<UserStore> UserStores { get; set; }
|
||||
public List<Fido2Credential> Fido2Credentials { get; set; }
|
||||
|
||||
[Obsolete("Use Blob2 instead")]
|
||||
public byte[] Blob { get; set; }
|
||||
public string Blob2 { get; set; }
|
||||
|
||||
public List<IdentityUserRole<string>> UserRoles { get; set; }
|
||||
|
||||
public static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
||||
public static void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
builder.Entity<ApplicationUser>()
|
||||
.HasMany<IdentityUserRole<string>>(user => user.UserRoles)
|
||||
.WithOne().HasForeignKey(role => role.UserId);
|
||||
if (databaseFacade.IsNpgsql())
|
||||
{
|
||||
builder.Entity<ApplicationUser>()
|
||||
.Property(o => o.Blob2)
|
||||
.HasColumnType("JSONB");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class UserBlob
|
||||
{
|
||||
public bool ShowInvoiceStatusChangeHint { get; set; }
|
||||
}
|
||||
}
|
||||
|
@@ -1,13 +1,11 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Data;
|
||||
|
||||
public class CustodianAccountData : IHasBlob<JObject>
|
||||
public class CustodianAccountData
|
||||
{
|
||||
[Required]
|
||||
[MaxLength(50)]
|
||||
@@ -26,29 +24,19 @@ public class CustodianAccountData : IHasBlob<JObject>
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
[Obsolete("Use Blob2 instead")]
|
||||
public byte[] Blob { get; set; }
|
||||
[JsonIgnore]
|
||||
public string Blob2 { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public StoreData StoreData { get; set; }
|
||||
|
||||
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
||||
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<CustodianAccountData>()
|
||||
builder.Entity<APIKeyData>()
|
||||
.HasIndex(o => o.StoreId);
|
||||
|
||||
if (databaseFacade.IsNpgsql())
|
||||
{
|
||||
builder.Entity<CustodianAccountData>()
|
||||
.Property(o => o.Blob2)
|
||||
.HasColumnType("JSONB");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,11 +2,10 @@ using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public class Fido2Credential : IHasBlobUntyped
|
||||
public class Fido2Credential
|
||||
{
|
||||
public string Name { get; set; }
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
@@ -15,7 +14,6 @@ namespace BTCPayServer.Data
|
||||
public string ApplicationUserId { get; set; }
|
||||
|
||||
public byte[] Blob { get; set; }
|
||||
public string Blob2 { get; set; }
|
||||
public CredentialType Type { get; set; }
|
||||
public enum CredentialType
|
||||
{
|
||||
@@ -24,18 +22,12 @@ namespace BTCPayServer.Data
|
||||
[Display(Name = "Lightning node (LNURL Auth)")]
|
||||
LNURLAuth
|
||||
}
|
||||
public static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
||||
public static void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
builder.Entity<Fido2Credential>()
|
||||
.HasOne(o => o.ApplicationUser)
|
||||
.WithMany(i => i.Fido2Credentials)
|
||||
.HasForeignKey(i => i.ApplicationUserId).OnDelete(DeleteBehavior.Cascade);
|
||||
if (databaseFacade.IsNpgsql())
|
||||
{
|
||||
builder.Entity<Fido2Credential>()
|
||||
.Property(o => o.Blob2)
|
||||
.HasColumnType("JSONB");
|
||||
}
|
||||
}
|
||||
|
||||
public ApplicationUser ApplicationUser { get; set; }
|
||||
|
@@ -2,30 +2,11 @@ using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
namespace BTCPayServer.Data;
|
||||
namespace BTCPayServer.Data.Data;
|
||||
|
||||
public class FormData
|
||||
{
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public string Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string StoreId { get; set; }
|
||||
public StoreData Store { get; set; }
|
||||
public string Config { get; set; }
|
||||
public bool Public { get; set; }
|
||||
|
||||
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
||||
{
|
||||
builder.Entity<FormData>()
|
||||
.HasOne(o => o.Store)
|
||||
.WithMany(o => o.Forms).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.Entity<FormData>().HasIndex(o => o.StoreId);
|
||||
|
||||
if (databaseFacade.IsNpgsql())
|
||||
{
|
||||
builder.Entity<FormData>()
|
||||
.Property(o => o.Config)
|
||||
.HasColumnType("JSONB");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,28 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public interface IHasBlob<T>
|
||||
{
|
||||
[Obsolete("Use Blob2 instead")]
|
||||
byte[] Blob { get; set; }
|
||||
string Blob2 { get; set; }
|
||||
}
|
||||
public interface IHasBlob
|
||||
{
|
||||
[Obsolete("Use Blob2 instead")]
|
||||
byte[] Blob { get; set; }
|
||||
string Blob2 { get; set; }
|
||||
public Type Type { get; set; }
|
||||
}
|
||||
public interface IHasBlobUntyped
|
||||
{
|
||||
[Obsolete("Use Blob2 instead")]
|
||||
byte[] Blob { get; set; }
|
||||
string Blob2 { get; set; }
|
||||
}
|
||||
}
|
@@ -2,11 +2,10 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public class InvoiceData : IHasBlobUntyped
|
||||
public class InvoiceData
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
@@ -17,9 +16,7 @@ namespace BTCPayServer.Data
|
||||
public List<PaymentData> Payments { get; set; }
|
||||
public List<InvoiceEventData> Events { get; set; }
|
||||
|
||||
[Obsolete("Use Blob2 instead")]
|
||||
public byte[] Blob { get; set; }
|
||||
public string Blob2 { get; set; }
|
||||
public string ItemCode { get; set; }
|
||||
public string OrderId { get; set; }
|
||||
public string Status { get; set; }
|
||||
@@ -35,7 +32,7 @@ namespace BTCPayServer.Data
|
||||
public RefundData CurrentRefund { get; set; }
|
||||
|
||||
|
||||
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
||||
internal static void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
builder.Entity<InvoiceData>()
|
||||
.HasOne(o => o.StoreData)
|
||||
@@ -45,13 +42,6 @@ namespace BTCPayServer.Data
|
||||
builder.Entity<InvoiceData>()
|
||||
.HasOne(o => o.CurrentRefund);
|
||||
builder.Entity<InvoiceData>().HasIndex(o => o.Created);
|
||||
|
||||
if (databaseFacade.IsNpgsql())
|
||||
{
|
||||
builder.Entity<InvoiceData>()
|
||||
.Property(o => o.Blob2)
|
||||
.HasColumnType("JSONB");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,22 +1,17 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Data;
|
||||
|
||||
public class LightningAddressData : IHasBlob<LightningAddressDataBlob>
|
||||
public class LightningAddressData
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string StoreDataId { get; set; }
|
||||
[Obsolete("Use Blob2 instead")]
|
||||
public byte[] Blob { get; set; }
|
||||
public string Blob2 { get; set; }
|
||||
|
||||
public StoreData Store { get; set; }
|
||||
|
||||
|
||||
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
||||
internal static void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
builder.Entity<LightningAddressData>()
|
||||
.HasOne(o => o.Store)
|
||||
@@ -25,12 +20,6 @@ public class LightningAddressData : IHasBlob<LightningAddressDataBlob>
|
||||
.IsRequired()
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
builder.Entity<LightningAddressData>().HasKey(o => o.Username);
|
||||
if (databaseFacade.IsNpgsql())
|
||||
{
|
||||
builder.Entity<LightningAddressData>()
|
||||
.Property(o => o.Blob2)
|
||||
.HasColumnType("JSONB");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +28,4 @@ public class LightningAddressDataBlob
|
||||
public string CurrencyCode { get; set; }
|
||||
public decimal? Min { get; set; }
|
||||
public decimal? Max { get; set; }
|
||||
|
||||
public JObject InvoiceMetadata { get; set; }
|
||||
}
|
||||
|
@@ -1,12 +1,10 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public class NotificationData : IHasBlobUntyped
|
||||
public class NotificationData
|
||||
{
|
||||
[MaxLength(36)]
|
||||
public string Id { get; set; }
|
||||
@@ -19,23 +17,15 @@ namespace BTCPayServer.Data
|
||||
[Required]
|
||||
public string NotificationType { get; set; }
|
||||
public bool Seen { get; set; }
|
||||
[Obsolete("Use Blob2 instead")]
|
||||
public byte[] Blob { get; set; }
|
||||
public string Blob2 { get; set; }
|
||||
|
||||
|
||||
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
||||
internal static void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
builder.Entity<NotificationData>()
|
||||
.HasOne(o => o.ApplicationUser)
|
||||
.WithMany(n => n.Notifications)
|
||||
.HasForeignKey(k => k.ApplicationUserId).OnDelete(DeleteBehavior.Cascade);
|
||||
if (databaseFacade.IsNpgsql())
|
||||
{
|
||||
builder.Entity<NotificationData>()
|
||||
.Property(o => o.Blob2)
|
||||
.HasColumnType("JSONB");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,34 +1,24 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public class PaymentData : IHasBlobUntyped
|
||||
public class PaymentData
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string InvoiceDataId { get; set; }
|
||||
public InvoiceData InvoiceData { get; set; }
|
||||
[Obsolete("Use Blob2 instead")]
|
||||
|
||||
public byte[] Blob { get; set; }
|
||||
public string Blob2 { get; set; }
|
||||
public string Type { get; set; }
|
||||
public bool Accounted { get; set; }
|
||||
|
||||
|
||||
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
||||
internal static void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
builder.Entity<PaymentData>()
|
||||
.HasOne(o => o.InvoiceData)
|
||||
.WithMany(i => i.Payments).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.Entity<PaymentData>()
|
||||
.HasIndex(o => o.InvoiceDataId);
|
||||
if (databaseFacade.IsNpgsql())
|
||||
{
|
||||
builder.Entity<PaymentData>()
|
||||
.Property(o => o.Blob2)
|
||||
.HasColumnType("JSONB");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,10 +1,9 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public class PaymentRequestData : IHasBlobUntyped
|
||||
public class PaymentRequestData
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public DateTimeOffset Created { get; set; }
|
||||
@@ -15,12 +14,10 @@ namespace BTCPayServer.Data
|
||||
|
||||
public Client.Models.PaymentRequestData.PaymentRequestStatus Status { get; set; }
|
||||
|
||||
[Obsolete("Use Blob2 instead")]
|
||||
public byte[] Blob { get; set; }
|
||||
public string Blob2 { get; set; }
|
||||
|
||||
|
||||
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
||||
internal static void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
builder.Entity<PaymentRequestData>()
|
||||
.HasOne(o => o.StoreData)
|
||||
@@ -31,13 +28,6 @@ namespace BTCPayServer.Data
|
||||
.HasDefaultValue(new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero));
|
||||
builder.Entity<PaymentRequestData>()
|
||||
.HasIndex(o => o.Status);
|
||||
|
||||
if (databaseFacade.IsNpgsql())
|
||||
{
|
||||
builder.Entity<PaymentRequestData>()
|
||||
.Property(o => o.Blob2)
|
||||
.HasColumnType("JSONB");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,15 +1,9 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
namespace BTCPayServer.Data;
|
||||
namespace BTCPayServer.Data.Data;
|
||||
|
||||
public class AutomatedPayoutBlob
|
||||
{
|
||||
public TimeSpan Interval { get; set; } = TimeSpan.FromHours(1);
|
||||
}
|
||||
public class PayoutProcessorData : IHasBlobUntyped
|
||||
public class PayoutProcessorData
|
||||
{
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public string Id { get; set; }
|
||||
@@ -18,22 +12,14 @@ public class PayoutProcessorData : IHasBlobUntyped
|
||||
public string PaymentMethod { get; set; }
|
||||
public string Processor { get; set; }
|
||||
|
||||
[Obsolete("Use Blob2 instead")]
|
||||
public byte[] Blob { get; set; }
|
||||
public string Blob2 { get; set; }
|
||||
|
||||
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
||||
internal static void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
|
||||
builder.Entity<PayoutProcessorData>()
|
||||
.HasOne(o => o.Store)
|
||||
.WithMany(data => data.PayoutProcessors).OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
if (databaseFacade.IsNpgsql())
|
||||
{
|
||||
builder.Entity<PayoutProcessorData>()
|
||||
.Property(o => o.Blob2)
|
||||
.HasColumnType("JSONB");
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
@@ -1,13 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Data.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using PayoutProcessorData = BTCPayServer.Data.PayoutProcessorData;
|
||||
using PayoutProcessorData = BTCPayServer.Data.Data.PayoutProcessorData;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
@@ -27,6 +25,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; }
|
||||
@@ -51,7 +50,6 @@ namespace BTCPayServer.Data
|
||||
public IEnumerable<PayoutData> Payouts { get; set; }
|
||||
public IEnumerable<CustodianAccountData> CustodianAccounts { get; set; }
|
||||
public IEnumerable<StoreSettingData> Settings { get; set; }
|
||||
public IEnumerable<FormData> Forms { get; set; }
|
||||
|
||||
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
||||
{
|
||||
@@ -60,20 +58,6 @@ namespace BTCPayServer.Data
|
||||
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)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -12,13 +12,6 @@ namespace BTCPayServer.Data
|
||||
{
|
||||
public class Types
|
||||
{
|
||||
public static readonly HashSet<string> AllTypes;
|
||||
static Types()
|
||||
{
|
||||
AllTypes = typeof(Types).GetFields()
|
||||
.Where(f => f.FieldType == typeof(string))
|
||||
.Select(f => (string)f.GetValue(null)).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
public const string Label = "label";
|
||||
public const string Tx = "tx";
|
||||
public const string Payjoin = "payjoin";
|
||||
|
@@ -1,29 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public class WebhookData : IHasBlobUntyped
|
||||
public class WebhookData
|
||||
{
|
||||
[Key]
|
||||
[MaxLength(25)]
|
||||
public string Id { get; set; }
|
||||
[Obsolete("Use Blob2 instead")]
|
||||
[Required]
|
||||
public byte[] Blob { get; set; }
|
||||
public string Blob2 { get; set; }
|
||||
public List<WebhookDeliveryData> Deliveries { get; set; }
|
||||
|
||||
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
||||
{
|
||||
if (databaseFacade.IsNpgsql())
|
||||
{
|
||||
builder.Entity<WebhookData>()
|
||||
.Property(o => o.Blob2)
|
||||
.HasColumnType("JSONB");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,10 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public class WebhookDeliveryData : IHasBlobUntyped
|
||||
public class WebhookDeliveryData
|
||||
{
|
||||
[Key]
|
||||
[MaxLength(25)]
|
||||
@@ -17,24 +16,17 @@ namespace BTCPayServer.Data
|
||||
|
||||
[Required]
|
||||
public DateTimeOffset Timestamp { get; set; }
|
||||
[Obsolete("Use Blob2 instead")]
|
||||
|
||||
[Required]
|
||||
public byte[] Blob { get; set; }
|
||||
public string Blob2 { get; set; }
|
||||
|
||||
|
||||
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
||||
internal static void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
builder.Entity<WebhookDeliveryData>()
|
||||
.HasOne(o => o.Webhook)
|
||||
.WithMany(a => a.Deliveries).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.Entity<WebhookDeliveryData>().HasIndex(o => o.WebhookId);
|
||||
|
||||
if (databaseFacade.IsNpgsql())
|
||||
{
|
||||
builder.Entity<WebhookDeliveryData>()
|
||||
.Property(o => o.Blob2)
|
||||
.HasColumnType("JSONB");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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,15 +17,13 @@ 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 =>
|
||||
@@ -37,17 +35,15 @@ namespace BTCPayServer.Migrations
|
||||
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),
|
||||
AType = table.Column<string>(type: "TEXT", nullable: false),
|
||||
AId = table.Column<string>(type: "TEXT", nullable: false),
|
||||
BType = table.Column<string>(type: "TEXT", nullable: false),
|
||||
BId = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Data = table.Column<string>(type: migrationBuilder.IsNpgsql() ? "JSONB" : "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,54 +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("20230125085242_AddForms")]
|
||||
public partial class AddForms : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
int? maxlength = migrationBuilder.IsMySql() ? 255 : null;
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Forms",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "TEXT", nullable: false, maxLength: maxlength),
|
||||
Name = table.Column<string>(type: "TEXT", nullable: true, maxLength: maxlength),
|
||||
StoreId = table.Column<string>(type: "TEXT", nullable: true, maxLength: maxlength),
|
||||
Config = table.Column<string>(type: migrationBuilder.IsNpgsql() ? "JSONB" : "TEXT", nullable: true),
|
||||
Public = table.Column<bool>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Forms", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Forms_Stores_StoreId",
|
||||
column: x => x.StoreId,
|
||||
principalTable: "Stores",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Forms_StoreId",
|
||||
table: "Forms",
|
||||
column: "StoreId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Forms");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,150 +0,0 @@
|
||||
using System;
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BTCPayServer.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20230130040047_blob2")]
|
||||
public partial class blob2 : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
var type = migrationBuilder.IsNpgsql() ? "JSONB" : "TEXT";
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Blob2",
|
||||
table: "Webhooks",
|
||||
type: type,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Blob2",
|
||||
table: "WebhookDeliveries",
|
||||
type: type,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Blob2",
|
||||
table: "PaymentRequests",
|
||||
type: type,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Blob2",
|
||||
table: "Notifications",
|
||||
type: type,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Blob2",
|
||||
table: "LightningAddresses",
|
||||
type: type,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Blob2",
|
||||
table: "Fido2Credentials",
|
||||
type: type,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Blob2",
|
||||
table: "AspNetUsers",
|
||||
type: type,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Blob2",
|
||||
table: "ApiKeys",
|
||||
type: type,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Blob2",
|
||||
table: "Invoices",
|
||||
type: type,
|
||||
nullable: true);
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Blob2",
|
||||
table: "Payments",
|
||||
type: type,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Blob2",
|
||||
table: "PayoutProcessors",
|
||||
type: type,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Blob2",
|
||||
table: "CustodianAccount",
|
||||
type: type,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Type",
|
||||
table: "Payments",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Blob2",
|
||||
table: "Webhooks");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Blob2",
|
||||
table: "WebhookDeliveries");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Blob2",
|
||||
table: "PaymentRequests");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Blob2",
|
||||
table: "Notifications");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Blob2",
|
||||
table: "LightningAddresses");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Blob2",
|
||||
table: "Fido2Credentials");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Blob2",
|
||||
table: "AspNetUsers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Blob2",
|
||||
table: "ApiKeys");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Blob2",
|
||||
table: "Invoices");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Blob2",
|
||||
table: "Payments");
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Blob2",
|
||||
table: "PayoutProcessors");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Blob2",
|
||||
table: "CustodianAccount");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Type",
|
||||
table: "Payments");
|
||||
}
|
||||
}
|
||||
}
|
@@ -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("20230130062447_jsonb2")]
|
||||
public partial class jsonb2 : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder.IsNpgsql())
|
||||
{
|
||||
migrationBuilder.Sql("ALTER TABLE \"Stores\" ALTER COLUMN \"DerivationStrategies\" TYPE JSONB USING \"DerivationStrategies\"::JSONB");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// Not supported
|
||||
}
|
||||
}
|
||||
}
|
@@ -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("20230315062447_fixmaxlength")]
|
||||
public partial class fixmaxlength : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder.IsNpgsql())
|
||||
{
|
||||
migrationBuilder.Sql("ALTER TABLE \"InvoiceSearches\" ALTER COLUMN \"Value\" TYPE TEXT USING \"Value\"::TEXT;");
|
||||
migrationBuilder.Sql("ALTER TABLE \"Invoices\" ALTER COLUMN \"OrderId\" TYPE TEXT USING \"OrderId\"::TEXT;");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// Not supported
|
||||
}
|
||||
}
|
||||
}
|
@@ -45,9 +45,6 @@ namespace BTCPayServer.Migrations
|
||||
b.Property<byte[]>("Blob")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<string>("Blob2")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Label")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -112,9 +109,6 @@ namespace BTCPayServer.Migrations
|
||||
b.Property<byte[]>("Blob")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<string>("Blob2")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("TEXT");
|
||||
@@ -189,9 +183,6 @@ namespace BTCPayServer.Migrations
|
||||
b.Property<byte[]>("Blob")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<string>("Blob2")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustodianCode")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
@@ -214,32 +205,7 @@ namespace BTCPayServer.Migrations
|
||||
b.ToTable("CustodianAccount");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.FormData", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Config")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Public")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("StoreId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StoreId");
|
||||
|
||||
b.ToTable("Forms");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.PayoutProcessorData", b =>
|
||||
modelBuilder.Entity("BTCPayServer.Data.Data.PayoutProcessorData", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -276,9 +242,6 @@ namespace BTCPayServer.Migrations
|
||||
b.Property<byte[]>("Blob")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<string>("Blob2")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -303,9 +266,6 @@ namespace BTCPayServer.Migrations
|
||||
b.Property<byte[]>("Blob")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<string>("Blob2")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("Created")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -416,9 +376,6 @@ namespace BTCPayServer.Migrations
|
||||
b.Property<byte[]>("Blob")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<string>("Blob2")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("StoreDataId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
@@ -444,9 +401,6 @@ namespace BTCPayServer.Migrations
|
||||
b.Property<byte[]>("Blob")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<string>("Blob2")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("Created")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -558,15 +512,9 @@ namespace BTCPayServer.Migrations
|
||||
b.Property<byte[]>("Blob")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<string>("Blob2")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("InvoiceDataId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InvoiceDataId");
|
||||
@@ -585,9 +533,6 @@ namespace BTCPayServer.Migrations
|
||||
b.Property<byte[]>("Blob")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<string>("Blob2")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("Created")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
@@ -760,8 +705,8 @@ namespace BTCPayServer.Migrations
|
||||
b.Property<int>("SpeedPolicy")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("StoreBlob")
|
||||
.HasColumnType("TEXT");
|
||||
b.Property<byte[]>("StoreBlob")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<byte[]>("StoreCertificate")
|
||||
.HasColumnType("BLOB");
|
||||
@@ -975,11 +920,9 @@ namespace BTCPayServer.Migrations
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<byte[]>("Blob")
|
||||
.IsRequired()
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<string>("Blob2")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Webhooks");
|
||||
@@ -992,11 +935,9 @@ namespace BTCPayServer.Migrations
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<byte[]>("Blob")
|
||||
.IsRequired()
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<string>("Blob2")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("Timestamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -1188,17 +1129,7 @@ namespace BTCPayServer.Migrations
|
||||
b.Navigation("StoreData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.FormData", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.StoreData", "Store")
|
||||
.WithMany("Forms")
|
||||
.HasForeignKey("StoreId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.Navigation("Store");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.PayoutProcessorData", b =>
|
||||
modelBuilder.Entity("BTCPayServer.Data.Data.PayoutProcessorData", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.StoreData", "Store")
|
||||
.WithMany("PayoutProcessors")
|
||||
@@ -1588,8 +1519,6 @@ namespace BTCPayServer.Migrations
|
||||
|
||||
b.Navigation("CustodianAccounts");
|
||||
|
||||
b.Navigation("Forms");
|
||||
|
||||
b.Navigation("Invoices");
|
||||
|
||||
b.Navigation("LightningAddresses");
|
||||
|
@@ -28,7 +28,7 @@ namespace BTCPayServer.PluginPacker
|
||||
var name = args[1];
|
||||
var outputDir = Path.Combine(args[2], name);
|
||||
var outputFile = Path.Combine(outputDir, name);
|
||||
var rootDLLPath = Path.GetFullPath(Path.Combine(directory, name + ".dll"));
|
||||
var rootDLLPath = Path.Combine(directory, name + ".dll");
|
||||
if (!File.Exists(rootDLLPath))
|
||||
{
|
||||
throw new Exception($"{rootDLLPath} could not be found");
|
||||
|
@@ -12,15 +12,17 @@ namespace BTCPayServer.Rating
|
||||
public string Name { get; }
|
||||
public string Url { get; }
|
||||
public string Id { get; }
|
||||
public string SourceId { get; }
|
||||
public RateSource Source { get; }
|
||||
|
||||
public AvailableRateProvider(string id, string name, string url) : this(id, name, url, RateSource.Direct)
|
||||
public AvailableRateProvider(string id, string name, string url) : this(id, id, name, url, RateSource.Direct)
|
||||
{
|
||||
|
||||
}
|
||||
public AvailableRateProvider(string id, string name, string url, RateSource source)
|
||||
public AvailableRateProvider(string id, string sourceId, string name, string url, RateSource source)
|
||||
{
|
||||
Id = id;
|
||||
SourceId = sourceId;
|
||||
Name = name;
|
||||
Url = url;
|
||||
Source = source;
|
||||
|
@@ -1305,7 +1305,7 @@
|
||||
"name":"Satoshis",
|
||||
"code":"SATS",
|
||||
"divisibility":0,
|
||||
"symbol":"sats",
|
||||
"symbol":"Sats",
|
||||
"crypto":true
|
||||
},
|
||||
{
|
||||
|
@@ -5,6 +5,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using BTCPayServer.Rating;
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
@@ -27,6 +28,14 @@ namespace BTCPayServer.Services.Rates
|
||||
}
|
||||
|
||||
static readonly Dictionary<string, IFormatProvider> _CurrencyProviders = new Dictionary<string, IFormatProvider>();
|
||||
public string FormatCurrency(string price, string currency)
|
||||
{
|
||||
return FormatCurrency(decimal.Parse(price, CultureInfo.InvariantCulture), currency);
|
||||
}
|
||||
public string FormatCurrency(decimal price, string currency)
|
||||
{
|
||||
return price.ToString("C", GetCurrencyProvider(currency));
|
||||
}
|
||||
|
||||
public NumberFormatInfo GetNumberFormatInfo(string currency, bool useFallback)
|
||||
{
|
||||
@@ -47,7 +56,6 @@ namespace BTCPayServer.Services.Rates
|
||||
currencyInfo.CurrencySymbol = currency;
|
||||
return currencyInfo;
|
||||
}
|
||||
|
||||
public NumberFormatInfo GetNumberFormatInfo(string currency)
|
||||
{
|
||||
var curr = GetCurrencyProvider(currency);
|
||||
@@ -57,7 +65,6 @@ namespace BTCPayServer.Services.Rates
|
||||
return ni;
|
||||
return null;
|
||||
}
|
||||
|
||||
public IFormatProvider GetCurrencyProvider(string currency)
|
||||
{
|
||||
lock (_CurrencyProviders)
|
||||
@@ -97,6 +104,30 @@ namespace BTCPayServer.Services.Rates
|
||||
currencyProviders.TryAdd(code, number);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Format a currency like "0.004 $ (USD)", round to significant divisibility
|
||||
/// </summary>
|
||||
/// <param name="value">The value</param>
|
||||
/// <param name="currency">Currency code</param>
|
||||
/// <returns></returns>
|
||||
public string DisplayFormatCurrency(decimal value, string currency)
|
||||
{
|
||||
var provider = GetNumberFormatInfo(currency, true);
|
||||
var currencyData = GetCurrencyData(currency, true);
|
||||
var divisibility = currencyData.Divisibility;
|
||||
value = value.RoundToSignificant(ref divisibility);
|
||||
if (divisibility != provider.CurrencyDecimalDigits)
|
||||
{
|
||||
provider = (NumberFormatInfo)provider.Clone();
|
||||
provider.CurrencyDecimalDigits = divisibility;
|
||||
}
|
||||
|
||||
if (currencyData.Crypto)
|
||||
return value.ToString("C", provider);
|
||||
else
|
||||
return value.ToString("C", provider) + $" ({currency})";
|
||||
}
|
||||
|
||||
readonly Dictionary<string, CurrencyData> _Currencies;
|
||||
|
||||
static CurrencyData[] LoadCurrency()
|
||||
|
@@ -15,8 +15,6 @@ namespace BTCPayServer.Services.Rates
|
||||
_httpClient = httpClient ?? new HttpClient();
|
||||
}
|
||||
|
||||
public RateSourceInfo RateSourceInfo => new("argoneum", "Argoneum", "https://rates.argoneum.net/rates");
|
||||
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
// Example result: AGM to BTC rate: {"agm":5000000.000000}
|
||||
|
@@ -19,9 +19,6 @@ namespace BTCPayServer.Rating.Providers
|
||||
public decimal? ask { get; set; }
|
||||
}
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public RateSourceInfo RateSourceInfo => new RateSourceInfo("btcturk", "BtcTurk", "https://api.btcturk.com/api/v2/ticker");
|
||||
|
||||
public BtcTurkRateProvider(HttpClient httpClient)
|
||||
{
|
||||
_httpClient = httpClient ?? new HttpClient();
|
||||
|
@@ -222,8 +222,6 @@ namespace BTCPayServer.Services.Rates
|
||||
}
|
||||
}
|
||||
|
||||
public RateSourceInfo RateSourceInfo => _Inner.RateSourceInfo;
|
||||
|
||||
private async Task<LatestFetch> Fetch(CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
@@ -11,9 +11,6 @@ namespace BTCPayServer.Services.Rates
|
||||
public class BitbankRateProvider : IRateProvider
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public RateSourceInfo RateSourceInfo => new("bitbank", "Bitbank", "https://public.bitbank.cc/tickers");
|
||||
|
||||
public BitbankRateProvider(HttpClient httpClient)
|
||||
{
|
||||
_httpClient = httpClient ?? new HttpClient();
|
||||
|
@@ -15,8 +15,6 @@ namespace BTCPayServer.Services.Rates
|
||||
_httpClient = httpClient ?? new HttpClient();
|
||||
}
|
||||
|
||||
public RateSourceInfo RateSourceInfo => new RateSourceInfo("bitflyer", "Bitflyer", "https://api.bitflyer.com/v1/ticker");
|
||||
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _httpClient.GetAsync("https://api.bitflyer.jp/v1/ticker", cancellationToken);
|
||||
|
@@ -15,8 +15,6 @@ namespace BTCPayServer.Services.Rates
|
||||
_httpClient = httpClient ?? new HttpClient();
|
||||
}
|
||||
|
||||
public RateSourceInfo RateSourceInfo => new("bitpay", "Bitpay", "https://bitpay.com/rates");
|
||||
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _httpClient.GetAsync("https://bitpay.com/rates", cancellationToken);
|
||||
|
@@ -14,8 +14,6 @@ public class BudaRateProvider : IRateProvider
|
||||
_httpClient = httpClient ?? new HttpClient();
|
||||
}
|
||||
|
||||
public RateSourceInfo RateSourceInfo => new RateSourceInfo("buda", "Buda", "https://www.buda.com/api/v2/markets/btc-clp/ticker");
|
||||
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _httpClient.GetAsync("https://www.buda.com/api/v2/markets/btc-clp/ticker", cancellationToken);
|
||||
|
@@ -14,8 +14,6 @@ namespace BTCPayServer.Services.Rates
|
||||
_httpClient = httpClient ?? new HttpClient();
|
||||
}
|
||||
|
||||
public RateSourceInfo RateSourceInfo => new RateSourceInfo("bylls", "Bylls", "https://bylls.com/api/price?from_currency=BTC&to_currency=CAD");
|
||||
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _httpClient.GetAsync("https://bylls.com/api/price?from_currency=BTC&to_currency=CAD", cancellationToken);
|
||||
|
File diff suppressed because one or more lines are too long
@@ -11,7 +11,6 @@ namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public class CryptoMarketExchangeRateProvider : IRateProvider
|
||||
{
|
||||
public RateSourceInfo RateSourceInfo => new("cryptomarket", "CryptoMarket", "https://api.exchange.cryptomkt.com/api/3/public/ticker/");
|
||||
private readonly HttpClient _httpClient;
|
||||
public CryptoMarketExchangeRateProvider(HttpClient httpClient)
|
||||
{
|
||||
|
@@ -40,9 +40,6 @@ namespace BTCPayServer.Services.Rates
|
||||
|
||||
// ExchangeSymbolToGlobalSymbol throws exception which would kill perf
|
||||
readonly ConcurrentDictionary<string, string> notFoundSymbols = new ConcurrentDictionary<string, string>();
|
||||
|
||||
public RateSourceInfo RateSourceInfo { get; set; }
|
||||
|
||||
private async Task<PairRate> CreateExchangeRate(T exchangeAPI, KeyValuePair<string, ExchangeTicker> ticker)
|
||||
{
|
||||
if (notFoundSymbols.TryGetValue(ticker.Key, out _))
|
||||
|
37
BTCPayServer.Rating/Providers/FallbackRateProvider.cs
Normal file
37
BTCPayServer.Rating/Providers/FallbackRateProvider.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Rating;
|
||||
|
||||
namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public class FallbackRateProvider : IRateProvider
|
||||
{
|
||||
readonly IRateProvider[] _Providers;
|
||||
public FallbackRateProvider(IRateProvider[] providers)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(providers);
|
||||
_Providers = providers;
|
||||
}
|
||||
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
foreach (var p in _Providers)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await p.GetRatesAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch when (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex) { Exceptions.Add(ex); }
|
||||
}
|
||||
return Array.Empty<PairRate>();
|
||||
}
|
||||
|
||||
public List<Exception> Exceptions { get; set; } = new List<Exception>();
|
||||
}
|
||||
}
|
@@ -12,7 +12,6 @@ namespace BTCPayServer.Rating
|
||||
{
|
||||
public class HitBTCRateProvider : IRateProvider
|
||||
{
|
||||
public RateSourceInfo RateSourceInfo => new("hitbtc", "HitBTC", "https://api.hitbtc.com/api/2/public/ticker");
|
||||
private readonly HttpClient _httpClient;
|
||||
public HitBTCRateProvider(HttpClient httpClient)
|
||||
{
|
||||
|
@@ -6,7 +6,6 @@ namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public interface IRateProvider
|
||||
{
|
||||
RateSourceInfo RateSourceInfo { get; }
|
||||
Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
@@ -16,7 +16,7 @@ namespace BTCPayServer.Services.Rates
|
||||
// Make sure that only one request is sent to kraken in general
|
||||
public class KrakenExchangeRateProvider : IRateProvider
|
||||
{
|
||||
public RateSourceInfo RateSourceInfo => new("kraken", "Kraken", "https://api.kraken.com/0/public/Ticker?pair=ATOMETH,ATOMEUR,ATOMUSD,ATOMXBT,BATETH,BATEUR,BATUSD,BATXBT,BCHEUR,BCHUSD,BCHXBT,DAIEUR,DAIUSD,DAIUSDT,DASHEUR,DASHUSD,DASHXBT,EOSETH,EOSXBT,ETHCHF,ETHDAI,ETHUSDC,ETHUSDT,GNOETH,GNOXBT,ICXETH,ICXEUR,ICXUSD,ICXXBT,LINKETH,LINKEUR,LINKUSD,LINKXBT,LSKETH,LSKEUR,LSKUSD,LSKXBT,NANOETH,NANOEUR,NANOUSD,NANOXBT,OMGETH,OMGEUR,OMGUSD,OMGXBT,PAXGETH,PAXGEUR,PAXGUSD,PAXGXBT,SCETH,SCEUR,SCUSD,SCXBT,USDCEUR,USDCUSD,USDCUSDT,USDTCAD,USDTEUR,USDTGBP,USDTZUSD,WAVESETH,WAVESEUR,WAVESUSD,WAVESXBT,XBTCHF,XBTDAI,XBTUSDC,XBTUSDT,XDGEUR,XDGUSD,XETCXETH,XETCXXBT,XETCZEUR,XETCZUSD,XETHXXBT,XETHZCAD,XETHZEUR,XETHZGBP,XETHZJPY,XETHZUSD,XLTCXXBT,XLTCZEUR,XLTCZUSD,XMLNXETH,XMLNXXBT,XMLNZEUR,XMLNZUSD,XREPXETH,XREPXXBT,XREPZEUR,XXBTZCAD,XXBTZEUR,XXBTZGBP,XXBTZJPY,XXBTZUSD,XXDGXXBT,XXLMXXBT,XXMRXXBT,XXMRZEUR,XXMRZUSD,XXRPXXBT,XXRPZEUR,XXRPZUSD,XZECXXBT,XZECZEUR,XZECZUSD");
|
||||
|
||||
public HttpClient HttpClient
|
||||
{
|
||||
get
|
||||
|
@@ -19,9 +19,6 @@ namespace BTCPayServer.Services.Rates
|
||||
return _Instance;
|
||||
}
|
||||
}
|
||||
|
||||
public RateSourceInfo RateSourceInfo => new RateSourceInfo("NULL", "NULL", "https://NULL.NULL");
|
||||
|
||||
public Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(Array.Empty<PairRate>());
|
||||
|
@@ -13,7 +13,6 @@ namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public class RipioExchangeProvider : IRateProvider
|
||||
{
|
||||
public RateSourceInfo RateSourceInfo => new("ripio", "Ripio", "https://api.exchange.ripio.com/api/v1/rate/all/");
|
||||
private readonly HttpClient _httpClient;
|
||||
public RipioExchangeProvider(HttpClient httpClient)
|
||||
{
|
||||
|
@@ -14,7 +14,6 @@ namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public class YadioRateProvider : IRateProvider
|
||||
{
|
||||
public RateSourceInfo RateSourceInfo => new("yadio", "Yadio", "https://api.yadio.io/exrates/BTC");
|
||||
private readonly HttpClient _httpClient;
|
||||
public YadioRateProvider(HttpClient httpClient)
|
||||
{
|
||||
|
@@ -1,21 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer.Rating
|
||||
{
|
||||
public class RateSourceInfo
|
||||
{
|
||||
public RateSourceInfo(string id, string displayName, string url)
|
||||
{
|
||||
Id = id;
|
||||
DisplayName = displayName;
|
||||
Url = url;
|
||||
}
|
||||
public string Id { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
public string Url { get; set; }
|
||||
}
|
||||
}
|
@@ -2,13 +2,11 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Rating;
|
||||
using BTCPayServer.Rating.Providers;
|
||||
using ExchangeSharp;
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Services.Rates
|
||||
@@ -17,7 +15,6 @@ namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
class WrapperRateProvider : IRateProvider
|
||||
{
|
||||
public RateSourceInfo RateSourceInfo => _inner.RateSourceInfo;
|
||||
private readonly IRateProvider _inner;
|
||||
public Exception Exception { get; private set; }
|
||||
public TimeSpan Latency { get; set; }
|
||||
@@ -50,49 +47,148 @@ namespace BTCPayServer.Services.Rates
|
||||
public ExchangeException Exception { get; internal set; }
|
||||
public string Exchange { get; internal set; }
|
||||
}
|
||||
public RateProviderFactory(IHttpClientFactory httpClientFactory, IEnumerable<IRateProvider> rateProviders)
|
||||
public RateProviderFactory(IHttpClientFactory httpClientFactory)
|
||||
{
|
||||
_httpClientFactory = httpClientFactory;
|
||||
foreach (var prov in rateProviders)
|
||||
{
|
||||
Providers.Add(prov.RateSourceInfo.Id, prov);
|
||||
}
|
||||
InitExchanges();
|
||||
}
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
public Dictionary<string, IRateProvider> Providers { get; } = new Dictionary<string, IRateProvider>();
|
||||
private readonly Dictionary<string, IRateProvider> _DirectProviders = new Dictionary<string, IRateProvider>();
|
||||
public Dictionary<string, IRateProvider> Providers
|
||||
{
|
||||
get
|
||||
{
|
||||
return _DirectProviders;
|
||||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<AvailableRateProvider> GetDirectlySupportedExchanges()
|
||||
{
|
||||
yield return new AvailableRateProvider("binance", "Binance", "https://api.binance.com/api/v1/ticker/24hr");
|
||||
yield return new AvailableRateProvider("bittrex", "Bittrex", "https://bittrex.com/api/v1.1/public/getmarketsummaries");
|
||||
yield return new AvailableRateProvider("poloniex", "Poloniex", "https://poloniex.com/public?command=returnTicker");
|
||||
yield return new AvailableRateProvider("hitbtc", "HitBTC", "https://api.hitbtc.com/api/2/public/ticker");
|
||||
yield return new AvailableRateProvider("ndax", "NDAX", "https://ndax.io/api/returnTicker");
|
||||
|
||||
yield return new AvailableRateProvider("coingecko", "CoinGecko", "https://api.coingecko.com/api/v3/exchange_rates");
|
||||
yield return new AvailableRateProvider("kraken", "Kraken", "https://api.kraken.com/0/public/Ticker?pair=ATOMETH,ATOMEUR,ATOMUSD,ATOMXBT,BATETH,BATEUR,BATUSD,BATXBT,BCHEUR,BCHUSD,BCHXBT,DAIEUR,DAIUSD,DAIUSDT,DASHEUR,DASHUSD,DASHXBT,EOSETH,EOSXBT,ETHCHF,ETHDAI,ETHUSDC,ETHUSDT,GNOETH,GNOXBT,ICXETH,ICXEUR,ICXUSD,ICXXBT,LINKETH,LINKEUR,LINKUSD,LINKXBT,LSKETH,LSKEUR,LSKUSD,LSKXBT,NANOETH,NANOEUR,NANOUSD,NANOXBT,OMGETH,OMGEUR,OMGUSD,OMGXBT,PAXGETH,PAXGEUR,PAXGUSD,PAXGXBT,SCETH,SCEUR,SCUSD,SCXBT,USDCEUR,USDCUSD,USDCUSDT,USDTCAD,USDTEUR,USDTGBP,USDTZUSD,WAVESETH,WAVESEUR,WAVESUSD,WAVESXBT,XBTCHF,XBTDAI,XBTUSDC,XBTUSDT,XDGEUR,XDGUSD,XETCXETH,XETCXXBT,XETCZEUR,XETCZUSD,XETHXXBT,XETHZCAD,XETHZEUR,XETHZGBP,XETHZJPY,XETHZUSD,XLTCXXBT,XLTCZEUR,XLTCZUSD,XMLNXETH,XMLNXXBT,XMLNZEUR,XMLNZUSD,XREPXETH,XREPXXBT,XREPZEUR,XXBTZCAD,XXBTZEUR,XXBTZGBP,XXBTZJPY,XXBTZUSD,XXDGXXBT,XXLMXXBT,XXMRXXBT,XXMRZEUR,XXMRZUSD,XXRPXXBT,XXRPZEUR,XXRPZUSD,XZECXXBT,XZECZEUR,XZECZUSD");
|
||||
yield return new AvailableRateProvider("bylls", "Bylls", "https://bylls.com/api/price?from_currency=BTC&to_currency=CAD");
|
||||
yield return new AvailableRateProvider("buda", "Buda", "https://www.buda.com/api/v2/markets/btc-clp/ticker");
|
||||
yield return new AvailableRateProvider("bitbank", "Bitbank", "https://public.bitbank.cc/tickers");
|
||||
yield return new AvailableRateProvider("bitflyer", "Bitflyer", "https://api.bitflyer.com/v1/ticker");
|
||||
yield return new AvailableRateProvider("bitpay", "Bitpay", "https://bitpay.com/rates");
|
||||
yield return new AvailableRateProvider("ripio", "Ripio", "https://api.exchange.ripio.com/api/v1/rate/all/");
|
||||
yield return new AvailableRateProvider("cryptomarket", "CryptoMarket", "https://api.exchange.cryptomkt.com/api/3/public/ticker/");
|
||||
yield return new AvailableRateProvider("btcturk", "BtcTurk", "https://api.btcturk.com/api/v2/ticker");
|
||||
|
||||
yield return new AvailableRateProvider("bitfinex", "Bitfinex", "https://api.bitfinex.com/v2/tickers?symbols=tBTCUSD,tLTCUSD,tLTCBTC,tETHUSD,tETHBTC,tETCBTC,tETCUSD,tRRTUSD,tRRTBTC,tZECUSD,tZECBTC,tXMRUSD,tXMRBTC,tDSHUSD,tDSHBTC,tBTCEUR,tBTCJPY,tXRPUSD,tXRPBTC,tIOTUSD,tIOTBTC,tIOTETH,tEOSUSD,tEOSBTC,tEOSETH,tSANUSD,tSANBTC,tSANETH,tOMGUSD,tOMGBTC,tOMGETH,tNEOUSD,tNEOBTC,tNEOETH,tETPUSD,tETPBTC,tETPETH,tQTMUSD,tQTMBTC,tQTMETH,tAVTUSD,tAVTBTC,tAVTETH,tEDOUSD,tEDOBTC,tEDOETH,tBTGUSD,tBTGBTC,tDATUSD,tDATBTC,tDATETH,tQSHUSD,tQSHBTC,tQSHETH,tYYWUSD,tYYWBTC,tYYWETH,tGNTUSD,tGNTBTC,tGNTETH,tSNTUSD,tSNTBTC,tSNTETH,tIOTEUR,tBATUSD,tBATBTC,tBATETH,tMNAUSD,tMNABTC,tMNAETH,tFUNUSD,tFUNBTC,tFUNETH,tZRXUSD,tZRXBTC,tZRXETH,tTNBUSD,tTNBBTC,tTNBETH,tSPKUSD,tSPKBTC,tSPKETH,tTRXUSD,tTRXBTC,tTRXETH,tRCNUSD,tRCNBTC,tRCNETH,tRLCUSD,tRLCBTC,tRLCETH,tAIDUSD,tAIDBTC,tAIDETH,tSNGUSD,tSNGBTC,tSNGETH,tREPUSD,tREPBTC,tREPETH,tELFUSD,tELFBTC,tELFETH,tNECUSD,tNECBTC,tNECETH,tBTCGBP,tETHEUR,tETHJPY,tETHGBP,tNEOEUR,tNEOJPY,tNEOGBP,tEOSEUR,tEOSJPY,tEOSGBP,tIOTJPY,tIOTGBP,tIOSUSD,tIOSBTC,tIOSETH,tAIOUSD,tAIOBTC,tAIOETH,tREQUSD,tREQBTC,tREQETH,tRDNUSD,tRDNBTC,tRDNETH,tLRCUSD,tLRCBTC,tLRCETH,tWAXUSD,tWAXBTC,tWAXETH,tDAIUSD,tDAIBTC,tDAIETH,tAGIUSD,tAGIBTC,tAGIETH,tBFTUSD,tBFTBTC,tBFTETH,tMTNUSD,tMTNBTC,tMTNETH,tODEUSD,tODEBTC,tODEETH,tANTUSD,tANTBTC,tANTETH,tDTHUSD,tDTHBTC,tDTHETH,tMITUSD,tMITBTC,tMITETH,tSTJUSD,tSTJBTC,tSTJETH,tXLMUSD,tXLMEUR,tXLMJPY,tXLMGBP,tXLMBTC,tXLMETH,tXVGUSD,tXVGEUR,tXVGJPY,tXVGGBP,tXVGBTC,tXVGETH,tBCIUSD,tBCIBTC,tMKRUSD,tMKRBTC,tMKRETH,tKNCUSD,tKNCBTC,tKNCETH,tPOAUSD,tPOABTC,tPOAETH,tEVTUSD,tLYMUSD,tLYMBTC,tLYMETH,tUTKUSD,tUTKBTC,tUTKETH,tVEEUSD,tVEEBTC,tVEEETH,tDADUSD,tDADBTC,tDADETH,tORSUSD,tORSBTC,tORSETH,tAUCUSD,tAUCBTC,tAUCETH,tPOYUSD,tPOYBTC,tPOYETH,tFSNUSD,tFSNBTC,tFSNETH,tCBTUSD,tCBTBTC,tCBTETH,tZCNUSD,tZCNBTC,tZCNETH,tSENUSD,tSENBTC,tSENETH,tNCAUSD,tNCABTC,tNCAETH,tCNDUSD,tCNDBTC,tCNDETH,tCTXUSD,tCTXBTC,tCTXETH,tPAIUSD,tPAIBTC,tSEEUSD,tSEEBTC,tSEEETH,tESSUSD,tESSBTC,tESSETH,tATMUSD,tATMBTC,tATMETH,tHOTUSD,tHOTBTC,tHOTETH,tDTAUSD,tDTABTC,tDTAETH,tIQXUSD,tIQXBTC,tIQXEOS,tWPRUSD,tWPRBTC,tWPRETH,tZILUSD,tZILBTC,tZILETH,tBNTUSD,tBNTBTC,tBNTETH,tABSUSD,tABSETH,tXRAUSD,tXRAETH,tMANUSD,tMANETH,tBBNUSD,tBBNETH,tNIOUSD,tNIOETH,tDGXUSD,tDGXETH,tVETUSD,tVETBTC,tVETETH,tUTNUSD,tUTNETH,tTKNUSD,tTKNETH,tGOTUSD,tGOTEUR,tGOTETH,tXTZUSD,tXTZBTC,tCNNUSD,tCNNETH,tBOXUSD,tBOXETH,tTRXEUR,tTRXGBP,tTRXJPY,tMGOUSD,tMGOETH,tRTEUSD,tRTEETH,tYGGUSD,tYGGETH,tMLNUSD,tMLNETH,tWTCUSD,tWTCETH,tCSXUSD,tCSXETH,tOMNUSD,tOMNBTC,tINTUSD,tINTETH,tDRNUSD,tDRNETH,tPNKUSD,tPNKETH,tDGBUSD,tDGBBTC,tBSVUSD,tBSVBTC,tBABUSD,tBABBTC,tWLOUSD,tWLOXLM,tVLDUSD,tVLDETH,tENJUSD,tENJETH,tONLUSD,tONLETH,tRBTUSD,tRBTBTC,tUSTUSD,tEUTEUR,tEUTUSD,tGSDUSD,tUDCUSD,tTSDUSD,tPAXUSD,tRIFUSD,tRIFBTC,tPASUSD,tPASETH,tVSYUSD,tVSYBTC,tZRXDAI,tMKRDAI,tOMGDAI,tBTTUSD,tBTTBTC,tBTCUST,tETHUST,tCLOUSD,tCLOBTC,tIMPUSD,tIMPETH,tLTCUST,tEOSUST,tBABUST,tSCRUSD,tSCRETH,tGNOUSD,tGNOETH,tGENUSD,tGENETH,tATOUSD,tATOBTC,tATOETH,tWBTUSD,tXCHUSD,tEUSUSD,tWBTETH,tXCHETH,tEUSETH,tLEOUSD,tLEOBTC,tLEOUST,tLEOEOS,tLEOETH,tASTUSD,tASTETH,tFOAUSD,tFOAETH,tUFRUSD,tUFRETH,tZBTUSD,tZBTUST,tOKBUSD,tUSKUSD,tGTXUSD,tKANUSD,tOKBUST,tOKBETH,tOKBBTC,tUSKUST,tUSKETH,tUSKBTC,tUSKEOS,tGTXUST,tKANUST,tAMPUSD,tALGUSD,tALGBTC,tALGUST,tBTCXCH,tSWMUSD,tSWMETH,tTRIUSD,tTRIETH,tLOOUSD,tLOOETH,tAMPUST,tDUSK:USD,tDUSK:BTC,tUOSUSD,tUOSBTC,tRRBUSD,tRRBUST,tDTXUSD,tDTXUST,tAMPBTC,tFTTUSD,tFTTUST,tPAXUST,tUDCUST,tTSDUST,tBTC:CNHT,tUST:CNHT,tCNH:CNHT,tCHZUSD,tCHZUST,tBTCF0:USTF0,tETHF0:USTF0");
|
||||
yield return new AvailableRateProvider("okex", "OKEx", "https://www.okex.com/api/futures/v3/instruments/ticker");
|
||||
yield return new AvailableRateProvider("coinbasepro", "Coinbase Pro", "https://api.pro.coinbase.com/products");
|
||||
|
||||
yield return new AvailableRateProvider("argoneum", "Argoneum", "https://rates.argoneum.net/rates");
|
||||
yield return new AvailableRateProvider("yadio", "Yadio", "https://api.yadio.io/exrates/BTC");
|
||||
}
|
||||
void InitExchanges()
|
||||
{
|
||||
// We need to be careful to only add exchanges which OnGetTickers implementation make only 1 request
|
||||
AddExchangeSharpProviders<ExchangeBinanceAPI>("binance");
|
||||
AddExchangeSharpProviders<ExchangeBittrexAPI>("bittrex");
|
||||
AddExchangeSharpProviders<ExchangePoloniexAPI>("poloniex");
|
||||
AddExchangeSharpProviders<ExchangeNDAXAPI>("ndax");
|
||||
|
||||
// Handmade providers
|
||||
Providers.Add("hitbtc", new HitBTCRateProvider(_httpClientFactory?.CreateClient("EXCHANGE_HITBTC")));
|
||||
Providers.Add("coingecko", new CoinGeckoRateProvider(_httpClientFactory));
|
||||
Providers.Add("kraken", new KrakenExchangeRateProvider() { HttpClient = _httpClientFactory?.CreateClient("EXCHANGE_KRAKEN") });
|
||||
Providers.Add("bylls", new ByllsRateProvider(_httpClientFactory?.CreateClient("EXCHANGE_BYLLS")));
|
||||
Providers.Add("buda", new BudaRateProvider(_httpClientFactory?.CreateClient("EXCHANGE_BUDA")));
|
||||
Providers.Add("bitbank", new BitbankRateProvider(_httpClientFactory?.CreateClient("EXCHANGE_BITBANK")));
|
||||
Providers.Add("bitpay", new BitpayRateProvider(_httpClientFactory?.CreateClient("EXCHANGE_BITPAY")));
|
||||
Providers.Add("ripio", new RipioExchangeProvider(_httpClientFactory?.CreateClient("EXCHANGE_RIPIO")));
|
||||
Providers.Add("cryptomarket", new CryptoMarketExchangeRateProvider(_httpClientFactory?.CreateClient("EXCHANGE_CRYPTOMARKET")));
|
||||
Providers.Add("bitflyer", new BitflyerRateProvider(_httpClientFactory?.CreateClient("EXCHANGE_BITFLYER")));
|
||||
Providers.Add("yadio", new YadioRateProvider(_httpClientFactory?.CreateClient("EXCHANGE_YADIO")));
|
||||
Providers.Add("btcturk", new BtcTurkRateProvider(_httpClientFactory?.CreateClient("EXCHANGE_BTCTURK")));
|
||||
// Providers.Add("argoneum", new ArgoneumRateProvider(_httpClientFactory?.CreateClient("EXCHANGE_ARGONEUM")));
|
||||
|
||||
|
||||
// Backward compatibility: coinaverage should be using coingecko to prevent stores from breaking
|
||||
Providers.Add("coinaverage", new CoinGeckoRateProvider(_httpClientFactory));
|
||||
|
||||
AddExchangeSharpProviders<ExchangeBitfinexAPI>("bitfinex");
|
||||
AddExchangeSharpProviders<ExchangeOKExAPI>("okex");
|
||||
AddExchangeSharpProviders<ExchangeCoinbaseAPI>("coinbasepro");
|
||||
// Those exchanges make too many requests, exchange sharp do not parallelize so it is too slow...
|
||||
//AddExchangeSharpProviders<ExchangeGeminiAPI>("gemini");
|
||||
//AddExchangeSharpProviders<ExchangeBitstampAPI>("bitstamp");
|
||||
//AddExchangeSharpProviders<ExchangeBitMEXAPI>("bitmex");
|
||||
|
||||
foreach (var provider in Providers.ToArray())
|
||||
{
|
||||
var prov = new BackgroundFetcherRateProvider(Providers[provider.Key]);
|
||||
prov.RefreshRate = TimeSpan.FromMinutes(1.0);
|
||||
prov.ValidatyTime = TimeSpan.FromMinutes(5.0);
|
||||
Providers[provider.Key] = prov;
|
||||
var rsi = provider.Value.RateSourceInfo;
|
||||
AvailableRateProviders.Add(new(rsi.Id, rsi.DisplayName, rsi.Url));
|
||||
}
|
||||
Providers["gdax"] = Providers["coinbasepro"];
|
||||
|
||||
foreach (var supportedExchange in CoinGeckoRateProvider.SupportedExchanges.Values)
|
||||
foreach (var supportedExchange in GetCoinGeckoSupportedExchanges())
|
||||
{
|
||||
if (!Providers.ContainsKey(supportedExchange.Id) && supportedExchange.Id != CoinGeckoRateProvider.CoinGeckoName)
|
||||
{
|
||||
var coingecko = new CoinGeckoRateProvider(_httpClientFactory)
|
||||
{
|
||||
UnderlyingExchange = supportedExchange.Id
|
||||
UnderlyingExchange = supportedExchange.SourceId
|
||||
};
|
||||
var bgFetcher = new BackgroundFetcherRateProvider(coingecko);
|
||||
bgFetcher.RefreshRate = TimeSpan.FromMinutes(1.0);
|
||||
bgFetcher.ValidatyTime = TimeSpan.FromMinutes(5.0);
|
||||
Providers.Add(supportedExchange.Id, bgFetcher);
|
||||
var rsi = coingecko.RateSourceInfo;
|
||||
AvailableRateProviders.Add(new(rsi.Id, rsi.DisplayName, rsi.Url, RateSource.Coingecko));
|
||||
}
|
||||
}
|
||||
AvailableRateProviders.Sort((a, b) => StringComparer.Ordinal.Compare(a.DisplayName, b.DisplayName));
|
||||
}
|
||||
|
||||
public List<AvailableRateProvider> AvailableRateProviders { get; } = new List<AvailableRateProvider>();
|
||||
private IRateProvider AddExchangeSharpProviders<T>(string providerName) where T : ExchangeAPI
|
||||
{
|
||||
var provider = new ExchangeSharpRateProvider<T>(_httpClientFactory.CreateClient($"EXCHANGE_{providerName}".ToUpperInvariant()));
|
||||
Providers.Add(providerName, provider);
|
||||
return provider;
|
||||
}
|
||||
|
||||
IEnumerable<AvailableRateProvider> _AvailableRateProviders = null;
|
||||
public IEnumerable<AvailableRateProvider> GetSupportedExchanges()
|
||||
{
|
||||
if (_AvailableRateProviders == null)
|
||||
{
|
||||
var availableProviders = new Dictionary<string, AvailableRateProvider>();
|
||||
foreach (var exchange in GetDirectlySupportedExchanges())
|
||||
{
|
||||
availableProviders.Add(exchange.Id, exchange);
|
||||
}
|
||||
foreach (var exchange in GetCoinGeckoSupportedExchanges())
|
||||
{
|
||||
availableProviders.TryAdd(exchange.Id, exchange);
|
||||
}
|
||||
_AvailableRateProviders = availableProviders.Values.OrderBy(o => o.Name).ToArray();
|
||||
}
|
||||
return _AvailableRateProviders;
|
||||
}
|
||||
|
||||
internal IEnumerable<AvailableRateProvider> GetCoinGeckoSupportedExchanges()
|
||||
{
|
||||
return JArray.Parse(CoinGeckoRateProvider.SupportedExchanges).Select(token =>
|
||||
new AvailableRateProvider(Normalize(token["id"].ToString().ToLowerInvariant()), token["id"].ToString().ToLowerInvariant(), token["name"].ToString(),
|
||||
$"https://api.coingecko.com/api/v3/exchanges/{token["id"]}/tickers", RateSource.Coingecko));
|
||||
}
|
||||
|
||||
private string Normalize(string name)
|
||||
{
|
||||
if (name == "oasis_trade")
|
||||
return "oasisdex";
|
||||
if (name == "gdax")
|
||||
return "coinbasepro";
|
||||
return name;
|
||||
}
|
||||
|
||||
public async Task<QueryRateResult> QueryRates(string exchangeName, CancellationToken cancellationToken)
|
||||
{
|
||||
|
@@ -11,7 +11,6 @@ using BTCPayServer.Models.AppViewModels;
|
||||
using BTCPayServer.Models.StoreViewModels;
|
||||
using BTCPayServer.Models.WalletViewModels;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Plugins.PointOfSale;
|
||||
using BTCPayServer.Plugins.PointOfSale.Controllers;
|
||||
using BTCPayServer.Plugins.PointOfSale.Models;
|
||||
using BTCPayServer.Services.Apps;
|
||||
@@ -51,7 +50,6 @@ namespace BTCPayServer.Tests
|
||||
user.RegisterDerivationScheme(cryptoCode);
|
||||
user.RegisterDerivationScheme("LTC");
|
||||
user.RegisterLightningNode(cryptoCode, LightningConnectionType.CLightning);
|
||||
user.SetLNUrl("BTC", false);
|
||||
var btcNetwork = tester.PayTester.Networks.GetNetwork<BTCPayNetwork>(cryptoCode);
|
||||
var invoice = await user.BitPay.CreateInvoiceAsync(
|
||||
new Invoice
|
||||
@@ -388,7 +386,7 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.Id("BOLT11Expiration")).SendKeys("5" + Keys.Enter);
|
||||
s.GoToInvoice(invoice.Id);
|
||||
s.Driver.FindElement(By.Id("IssueRefund")).Click();
|
||||
|
||||
|
||||
if (multiCurrency)
|
||||
{
|
||||
s.Driver.WaitUntilAvailable(By.Id("RefundForm"), TimeSpan.FromSeconds(1));
|
||||
@@ -398,21 +396,21 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
|
||||
s.Driver.WaitUntilAvailable(By.Id("RefundForm"), TimeSpan.FromSeconds(1));
|
||||
Assert.Contains("5,500.00 USD", s.Driver.PageSource); // Should propose reimburse in fiat
|
||||
Assert.Contains("1.10000000 BTC", s.Driver.PageSource); // Should propose reimburse in BTC at the rate of before
|
||||
Assert.Contains("2.20000000 BTC", s.Driver.PageSource); // Should propose reimburse in BTC at the current rate
|
||||
Assert.Contains("$5,500.00", s.Driver.PageSource); // Should propose reimburse in fiat
|
||||
Assert.Contains("1.10000000 ₿", s.Driver.PageSource); // Should propose reimburse in BTC at the rate of before
|
||||
Assert.Contains("2.20000000 ₿", s.Driver.PageSource); // Should propose reimburse in BTC at the current rate
|
||||
s.Driver.WaitForAndClick(By.Id(rateSelection));
|
||||
s.Driver.FindElement(By.Id("ok")).Click();
|
||||
|
||||
|
||||
s.Driver.WaitUntilAvailable(By.Id("Destination"), TimeSpan.FromSeconds(1));
|
||||
Assert.Contains("pull-payments", s.Driver.Url);
|
||||
if (rateSelection == "FiatOption")
|
||||
Assert.Contains("5,500.00 USD", s.Driver.PageSource);
|
||||
Assert.Contains("$5,500.00", s.Driver.PageSource);
|
||||
if (rateSelection == "CurrentOption")
|
||||
Assert.Contains("2.20000000 BTC", s.Driver.PageSource);
|
||||
Assert.Contains("2.20000000 ₿", s.Driver.PageSource);
|
||||
if (rateSelection == "RateThenOption")
|
||||
Assert.Contains("1.10000000 BTC", s.Driver.PageSource);
|
||||
|
||||
Assert.Contains("1.10000000 ₿", s.Driver.PageSource);
|
||||
|
||||
s.GoToInvoice(invoice.Id);
|
||||
s.Driver.FindElement(By.Id("IssueRefund")).Click();
|
||||
s.Driver.WaitUntilAvailable(By.Id("Destination"), TimeSpan.FromSeconds(1));
|
||||
@@ -586,7 +584,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.True(invoice.SupportedTransactionCurrencies["LTC"].Enabled);
|
||||
Assert.True(invoice.PaymentSubtotals.ContainsKey("LTC"));
|
||||
Assert.True(invoice.PaymentTotals.ContainsKey("LTC"));
|
||||
|
||||
|
||||
// Check if we can disable LTC
|
||||
invoice = await user.BitPay.CreateInvoiceAsync(
|
||||
new Invoice
|
||||
@@ -624,11 +622,10 @@ namespace BTCPayServer.Tests
|
||||
var apps = user.GetController<UIAppsController>();
|
||||
var pos = user.GetController<UIPointOfSaleController>();
|
||||
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp(user.StoreId)).Model);
|
||||
var appType = PointOfSaleAppType.AppType;
|
||||
var appType = AppType.PointOfSale.ToString();
|
||||
vm.AppName = "test";
|
||||
vm.SelectedAppType = appType;
|
||||
var redirect = Assert.IsType<RedirectResult>(apps.CreateApp(user.StoreId, vm).Result);
|
||||
Assert.EndsWith("/settings/pos", redirect.Url);
|
||||
Assert.IsType<RedirectToActionResult>(apps.CreateApp(user.StoreId, vm).Result);
|
||||
var appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model);
|
||||
var app = appList.Apps[0];
|
||||
var appData = new AppData { Id = app.Id, StoreDataId = app.StoreId, Name = app.AppName, AppType = appType };
|
||||
@@ -683,7 +680,7 @@ donation:
|
||||
var appleInvoice = invoices.SingleOrDefault(invoice => invoice.ItemCode.Equals("apple"));
|
||||
Assert.NotNull(appleInvoice);
|
||||
Assert.Equal("good apple", appleInvoice.ItemDesc);
|
||||
|
||||
|
||||
// testing custom amount
|
||||
var action = Assert.IsType<RedirectToActionResult>(publicApps
|
||||
.ViewPointOfSale(app.Id, PosViewType.Cart, 6.6m, null, null, null, null, "donation").Result);
|
||||
@@ -738,7 +735,7 @@ donation:
|
||||
Assert.Equal(test.ExpectedDivisibility, vmview.CurrencyInfo.Divisibility);
|
||||
Assert.Equal(test.ExpectedSymbolSpace, vmview.CurrencyInfo.SymbolSpace);
|
||||
}
|
||||
|
||||
|
||||
//test inventory related features
|
||||
vmpos = await pos.UpdatePointOfSale(app.Id).AssertViewModelAsync<UpdatePointOfSaleViewModel>();
|
||||
vmpos.Title = "hello";
|
||||
@@ -759,7 +756,7 @@ noninventoryitem:
|
||||
.ViewPointOfSale(app.Id, PosViewType.Cart, 1, null, null, null, null, "inventoryitem").Result);
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
|
||||
//we already bought all available stock so this should fail
|
||||
await Task.Delay(100);
|
||||
Assert.IsType<RedirectToActionResult>(publicApps
|
||||
@@ -822,13 +819,13 @@ normal:
|
||||
normalInvoice.CryptoInfo,
|
||||
s => PaymentTypes.BTCLike.ToString() == s.PaymentType && new[] { "BTC", "LTC" }.Contains(
|
||||
s.CryptoCode));
|
||||
|
||||
|
||||
//test topup option
|
||||
vmpos.Template = @"
|
||||
a:
|
||||
price: 1000.0
|
||||
title: good apple
|
||||
|
||||
|
||||
b:
|
||||
price: 10.0
|
||||
custom: false
|
||||
@@ -846,7 +843,7 @@ f:
|
||||
g:
|
||||
custom: topup
|
||||
";
|
||||
|
||||
|
||||
Assert.IsType<RedirectToActionResult>(pos.UpdatePointOfSale(app.Id, vmpos).Result);
|
||||
vmpos = await pos.UpdatePointOfSale(app.Id).AssertViewModelAsync<UpdatePointOfSaleViewModel>();
|
||||
Assert.DoesNotContain("custom", vmpos.Template);
|
||||
@@ -858,7 +855,7 @@ g:
|
||||
Assert.Contains(items, item => item.Id == "e" && item.Price.Type == ViewPointOfSaleViewModel.Item.ItemPrice.ItemPriceType.Minimum);
|
||||
Assert.Contains(items, item => item.Id == "f" && item.Price.Type == ViewPointOfSaleViewModel.Item.ItemPrice.ItemPriceType.Topup);
|
||||
Assert.Contains(items, item => item.Id == "g" && item.Price.Type == ViewPointOfSaleViewModel.Item.ItemPrice.ItemPriceType.Topup);
|
||||
|
||||
|
||||
Assert.IsType<RedirectToActionResult>(publicApps
|
||||
.ViewPointOfSale(app.Id, PosViewType.Static, null, null, null, null, null, "g").Result);
|
||||
invoices = user.BitPay.GetInvoices();
|
||||
|
@@ -286,7 +286,7 @@ namespace BTCPayServer.Tests
|
||||
if (permissions.Contains(canModifyAllStores) || storePermissions.Any())
|
||||
{
|
||||
var resultStores =
|
||||
await TestApiAgainstAccessToken<Client.Models.StoreData[]>(accessToken, $"{TestApiPath}/me/stores",
|
||||
await TestApiAgainstAccessToken<StoreData[]>(accessToken, $"{TestApiPath}/me/stores",
|
||||
tester.PayTester.HttpClient);
|
||||
|
||||
foreach (var selectiveStorePermission in storePermissions)
|
||||
|
@@ -23,7 +23,7 @@
|
||||
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.14" />
|
||||
<PackageReference Include="Selenium.Support" Version="4.1.1" />
|
||||
<PackageReference Include="Selenium.WebDriver" Version="4.1.1" />
|
||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="112.0.5615.4900" />
|
||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="108.0.5359.7100" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using BTCPayServer.Views.Stores;
|
||||
@@ -28,7 +27,6 @@ namespace BTCPayServer.Tests
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser();
|
||||
s.CreateNewStore();
|
||||
s.EnableCheckout(CheckoutType.V1);
|
||||
s.AddDerivationScheme();
|
||||
s.GoToStore(StoreNavPages.CheckoutAppearance);
|
||||
s.Driver.FindElement(By.Id("RequiresRefundEmail")).Click();
|
||||
@@ -74,7 +72,6 @@ namespace BTCPayServer.Tests
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser();
|
||||
s.CreateNewStore();
|
||||
s.EnableCheckout(CheckoutType.V1);
|
||||
s.AddDerivationScheme();
|
||||
|
||||
// Now create an invoice that requires a refund email
|
||||
@@ -127,7 +124,6 @@ namespace BTCPayServer.Tests
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser();
|
||||
s.CreateNewStore();
|
||||
s.EnableCheckout(CheckoutType.V1);
|
||||
s.AddDerivationScheme();
|
||||
|
||||
var invoiceId = s.CreateInvoice();
|
||||
@@ -158,13 +154,13 @@ namespace BTCPayServer.Tests
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser(true);
|
||||
s.CreateNewStore();
|
||||
s.EnableCheckout(CheckoutType.V1);
|
||||
s.AddLightningNode();
|
||||
s.AddDerivationScheme();
|
||||
|
||||
var invoiceId = s.CreateInvoice(defaultPaymentMethod: "BTC_LightningLike");
|
||||
s.GoToInvoiceCheckout(invoiceId);
|
||||
Assert.Equal("Bitcoin (Lightning)", s.Driver.FindElement(By.ClassName("payment__currencies")).Text);
|
||||
|
||||
Assert.Equal("Bitcoin (Lightning) (BTC)", s.Driver.FindElement(By.ClassName("payment__currencies")).Text);
|
||||
s.Driver.Quit();
|
||||
}
|
||||
|
||||
@@ -178,7 +174,6 @@ namespace BTCPayServer.Tests
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser(true);
|
||||
s.CreateNewStore();
|
||||
s.EnableCheckout(CheckoutType.V1);
|
||||
s.AddLightningNode();
|
||||
s.GoToLightningSettings();
|
||||
s.Driver.SetCheckbox(By.Id("LightningAmountInSatoshi"), true);
|
||||
@@ -187,7 +182,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
var invoiceId = s.CreateInvoice(10, "USD", "a@g.com");
|
||||
s.GoToInvoiceCheckout(invoiceId);
|
||||
Assert.Contains("sats", s.Driver.FindElement(By.ClassName("buyerTotalLine")).Text);
|
||||
Assert.Contains("Sats", s.Driver.FindElement(By.ClassName("payment__currencies_noborder")).Text);
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
@@ -198,7 +193,6 @@ namespace BTCPayServer.Tests
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser();
|
||||
s.CreateNewStore();
|
||||
s.EnableCheckout(CheckoutType.V1);
|
||||
s.GoToStore();
|
||||
s.AddDerivationScheme();
|
||||
var invoiceId = s.CreateInvoice(0.001m, "BTC", "a@x.com");
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user