Compare commits
87 Commits
v1.12.4
...
fweoinqrnt
Author | SHA1 | Date | |
---|---|---|---|
913ff62f2d | |||
6cc1751924 | |||
69b589a401 | |||
db73b1f268 | |||
9ac0e982d6 | |||
cb25c225e9 | |||
5b31d4de20 | |||
14f8c73b08 | |||
529075f64c | |||
dba102e74f | |||
0f3f8b6bf9 | |||
83028b9b73 | |||
1fe766cb16 | |||
6b45eb0d3d | |||
6b0087ab69 | |||
e60fd8d6ab | |||
88a1d83323 | |||
e21a8df0f3 | |||
93f37b506b | |||
fca3480e37 | |||
966547db54 | |||
09dbe44bca | |||
b7ce6b7400 | |||
78f169cd24 | |||
d0e11f1ec4 | |||
912a706de9 | |||
9b5c8a8254 | |||
e5adc630af | |||
c56c6401d6 | |||
0e64df3bbf | |||
e497903bf4 | |||
f1ff913cbe | |||
3a00d32ce0 | |||
f0f698f411 | |||
22c6468a5d | |||
a60072a431 | |||
dcc6f17c9c | |||
15ce148b99 | |||
3b73d5a5cb | |||
1fd3054006 | |||
2db1434929 | |||
a171671fe5 | |||
9160a1d71e | |||
a896560a3c | |||
e43b4ed540 | |||
8b446e2791 | |||
d72b0e4cee | |||
22996ea21e | |||
d55770cc16 | |||
5c98ca180a | |||
10bb75ce0e | |||
b9e3686fcf | |||
4ae1046571 | |||
147c6c4548 | |||
354338180b | |||
5939e19f72 | |||
f72a6df55a | |||
9c95b98f3a | |||
55a8ba0905 | |||
04037b3d2d | |||
4943c84655 | |||
42a8160768 | |||
33d3a25928 | |||
c2acff81c6 | |||
214d4b0c3f | |||
bd4cf61c2b | |||
b592ee2fed | |||
c57e1cca25 | |||
335f345ce3 | |||
b7be93c569 | |||
cd01a7b727 | |||
b96e73a002 | |||
0bf22ddf29 | |||
1c4dc382a8 | |||
71c5566f2b | |||
6621859567 | |||
6437967e60 | |||
c5a926c50c | |||
85ab691b68 | |||
4d3e0ab599 | |||
02663a149e | |||
a8fdc4798d | |||
6290b0f3bf | |||
411e0334d0 | |||
b174977bc7 | |||
95bf60c252 | |||
f9a43b537f |
1
.gitignore
vendored
1
.gitignore
vendored
@ -300,3 +300,4 @@ Plugins/packed
|
||||
BTCPayServer/wwwroot/swagger/v1/openapi.json
|
||||
BTCPayServer/appsettings.dev.json
|
||||
BTCPayServer.Tests/monero_wallet
|
||||
/BTCPayServer.Tests/NewBlocks.bat
|
||||
|
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@ -10,7 +10,7 @@
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/BTCPayServer/bin/Debug/net6.0/BTCPayServer.dll",
|
||||
"program": "${workspaceFolder}/BTCPayServer/bin/Debug/net8.0/BTCPayServer.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/BTCPayServer",
|
||||
"stopAtEntry": false,
|
||||
|
@ -31,7 +31,7 @@
|
||||
<None Include="icon.png" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="HtmlSanitizer" Version="8.0.723" />
|
||||
<PackageReference Include="HtmlSanitizer" Version="8.0.838" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.1" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Data.Common;
|
||||
using BTCPayServer.Abstractions.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
|
@ -1,19 +0,0 @@
|
||||
namespace BTCPayServer.Abstractions.Custodians.Client;
|
||||
|
||||
public class AssetQuoteResult
|
||||
{
|
||||
public string FromAsset { get; set; }
|
||||
public string ToAsset { get; set; }
|
||||
public decimal Bid { get; set; }
|
||||
public decimal Ask { get; set; }
|
||||
|
||||
public AssetQuoteResult() { }
|
||||
|
||||
public AssetQuoteResult(string fromAsset, string toAsset, decimal bid, decimal ask)
|
||||
{
|
||||
FromAsset = fromAsset;
|
||||
ToAsset = toAsset;
|
||||
Bid = bid;
|
||||
Ask = ask;
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class AssetBalancesUnavailableException : CustodianApiException
|
||||
{
|
||||
public AssetBalancesUnavailableException(System.Exception e) : base(500, "asset-balances-unavailable", $"Cannot fetch the asset balances: {e.Message}", e)
|
||||
{
|
||||
}
|
||||
|
||||
public AssetBalancesUnavailableException(string errorMsg) : base(500, "asset-balances-unavailable", $"Cannot fetch the asset balances: {errorMsg}")
|
||||
{
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class AssetQuoteUnavailableException : CustodianApiException
|
||||
{
|
||||
public AssetPairData AssetPair { get; }
|
||||
|
||||
public AssetQuoteUnavailableException(AssetPairData assetPair) : base(400, "asset-price-unavailable", "Cannot find a quote for pair " + assetPair)
|
||||
{
|
||||
this.AssetPair = assetPair;
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class BadConfigException : CustodianApiException
|
||||
{
|
||||
public string[] BadConfigKeys { get; set; }
|
||||
|
||||
public BadConfigException(string[] badConfigKeys) : base(500, "bad-custodian-account-config", "Wrong config values: " + String.Join(", ", badConfigKeys))
|
||||
{
|
||||
this.BadConfigKeys = badConfigKeys;
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class CannotWithdrawException : CustodianApiException
|
||||
|
||||
{
|
||||
public CannotWithdrawException(ICustodian custodian, string paymentMethod, string message) : base(403, "cannot-withdraw", message)
|
||||
{
|
||||
}
|
||||
|
||||
public CannotWithdrawException(ICustodian custodian, string paymentMethod, string targetAddress, CustodianApiException originalException) : base(403, "cannot-withdraw", $"{custodian.Name} cannot withdraw {paymentMethod} to '{targetAddress}': {originalException.Message}")
|
||||
{
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
using System;
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
public class CustodianApiException : Exception
|
||||
{
|
||||
public int HttpStatus { get; }
|
||||
public string Code { get; }
|
||||
|
||||
public CustodianApiException(int httpStatus, string code, string message, System.Exception ex) : base(message, ex)
|
||||
{
|
||||
HttpStatus = httpStatus;
|
||||
Code = code;
|
||||
}
|
||||
|
||||
public CustodianApiException(int httpStatus, string code, string message) : this(httpStatus, code, message, null)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +0,0 @@
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class CustodianFeatureNotImplementedException : CustodianApiException
|
||||
{
|
||||
public CustodianFeatureNotImplementedException(string message) : base(400, "not-implemented", message)
|
||||
{
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class DepositsUnavailableException : CustodianApiException
|
||||
{
|
||||
public DepositsUnavailableException(string message) : base(404, "deposits-unavailable", message)
|
||||
{
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class InsufficientFundsException : CustodianApiException
|
||||
{
|
||||
public InsufficientFundsException(string message) : base(400, "insufficient-funds", message)
|
||||
{
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class InvalidWithdrawalTargetException : CustodianApiException
|
||||
|
||||
{
|
||||
public InvalidWithdrawalTargetException(ICustodian custodian, string paymentMethod, string targetAddress, CustodianApiException originalException) : base(403, "invalid-withdrawal-target", $"{custodian.Name} cannot withdraw {paymentMethod} to '{targetAddress}': {originalException.Message}")
|
||||
{
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class PermissionDeniedCustodianApiException : CustodianApiException
|
||||
|
||||
{
|
||||
public PermissionDeniedCustodianApiException(ICustodian custodian) : base(403, "custodian-api-permission-denied", $"{custodian.Name}'s API reported that you don't have permission.")
|
||||
{
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class TradeNotFoundException : CustodianApiException
|
||||
{
|
||||
private string tradeId { get; }
|
||||
|
||||
public TradeNotFoundException(string tradeId) : base(404, "trade-not-found", "Could not find trade ID " + tradeId)
|
||||
{
|
||||
this.tradeId = tradeId;
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class WithdrawalNotFoundException : CustodianApiException
|
||||
{
|
||||
private string WithdrawalId { get; }
|
||||
|
||||
public WithdrawalNotFoundException(string withdrawalId) : base(404, "withdrawal-not-found", $"Could not find withdrawal ID {withdrawalId}.")
|
||||
{
|
||||
WithdrawalId = withdrawalId;
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class WrongTradingPairException : CustodianApiException
|
||||
{
|
||||
public const int HttpCode = 404;
|
||||
public WrongTradingPairException(string fromAsset, string toAsset) : base(HttpCode, "wrong-trading-pair", $"Cannot find a trading pair for converting {fromAsset} into {toAsset}.")
|
||||
{
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Custodians.Client;
|
||||
|
||||
/**
|
||||
* The result of a market trade. Used as a return type for custodians implementing ICanTrade
|
||||
*/
|
||||
public class MarketTradeResult
|
||||
{
|
||||
public string FromAsset { get; }
|
||||
public string ToAsset { get; }
|
||||
/**
|
||||
* The ledger entries that show the balances that were affected by the trade.
|
||||
*/
|
||||
public List<LedgerEntryData> LedgerEntries { get; }
|
||||
/**
|
||||
* The unique ID of the trade that was executed.
|
||||
*/
|
||||
public string TradeId { get; }
|
||||
|
||||
public MarketTradeResult(string fromAsset, string toAsset, List<LedgerEntryData> ledgerEntries, string tradeId)
|
||||
{
|
||||
this.FromAsset = fromAsset;
|
||||
this.ToAsset = toAsset;
|
||||
this.LedgerEntries = ledgerEntries;
|
||||
this.TradeId = tradeId;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Custodians.Client;
|
||||
|
||||
public class WithdrawResult
|
||||
{
|
||||
public string PaymentMethod { get; }
|
||||
public string Asset { get; set; }
|
||||
public List<LedgerEntryData> LedgerEntries { get; }
|
||||
public string WithdrawalId { get; }
|
||||
public WithdrawalResponseData.WithdrawalStatus Status { get; }
|
||||
public DateTimeOffset CreatedTime { get; }
|
||||
public string TargetAddress { get; }
|
||||
public string TransactionId { get; }
|
||||
|
||||
public WithdrawResult(string paymentMethod, string asset, List<LedgerEntryData> ledgerEntries, string withdrawalId, WithdrawalResponseData.WithdrawalStatus status, DateTimeOffset createdTime, string targetAddress, string transactionId)
|
||||
{
|
||||
PaymentMethod = paymentMethod;
|
||||
Asset = asset;
|
||||
LedgerEntries = ledgerEntries;
|
||||
WithdrawalId = withdrawalId;
|
||||
CreatedTime = createdTime;
|
||||
Status = status;
|
||||
TargetAddress = targetAddress;
|
||||
TransactionId = transactionId;
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public interface ICanDeposit
|
||||
{
|
||||
/**
|
||||
* Get the address where we can deposit for the chosen payment method (crypto code + network).
|
||||
* The result can be a string in different formats like a bitcoin address or even a LN invoice.
|
||||
*/
|
||||
public Task<DepositAddressData> GetDepositAddressAsync(string paymentMethod, JObject config, CancellationToken cancellationToken);
|
||||
|
||||
public string[] GetDepositablePaymentMethods();
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Custodians.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public interface ICanTrade
|
||||
{
|
||||
/**
|
||||
* A list of tradable asset pairs, or NULL if the custodian cannot trade/convert assets. if thr asset pair contains fiat, fiat is always put last. If both assets are a cyrptocode or both are fiat, the pair is written alphabetically. Always in uppercase. Example: ["BTC/EUR","BTC/USD", "EUR/USD", "BTC/ETH",...]
|
||||
*/
|
||||
public List<AssetPairData> GetTradableAssetPairs();
|
||||
|
||||
/**
|
||||
* Execute a market order right now.
|
||||
*/
|
||||
public Task<MarketTradeResult> TradeMarketAsync(string fromAsset, string toAsset, decimal qty, JObject config, CancellationToken cancellationToken);
|
||||
|
||||
/**
|
||||
* Get the details about a previous market trade.
|
||||
*/
|
||||
public Task<MarketTradeResult> GetTradeInfoAsync(string tradeId, JObject config, CancellationToken cancellationToken);
|
||||
|
||||
public Task<AssetQuoteResult> GetQuoteForAssetAsync(string fromAsset, string toAsset, JObject config, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,20 +0,0 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Custodians.Client;
|
||||
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> GetWithdrawalInfoAsync(string paymentMethod, string withdrawalId, JObject config, CancellationToken cancellationToken);
|
||||
|
||||
public string[] GetWithdrawablePaymentMethods();
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public interface ICustodian
|
||||
{
|
||||
/**
|
||||
* Get the unique code that identifies this custodian.
|
||||
*/
|
||||
string Code { get; }
|
||||
|
||||
string Name { get; }
|
||||
|
||||
/**
|
||||
* Get a list of assets and their qty in custody.
|
||||
*/
|
||||
Task<Dictionary<string, decimal>> GetAssetBalancesAsync(JObject config, CancellationToken cancellationToken);
|
||||
|
||||
public Task<Form.Form> GetConfigForm(JObject config, CancellationToken cancellationToken = default);
|
||||
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Extensions;
|
||||
|
||||
public static class CustodianExtensions
|
||||
{
|
||||
public static ICustodian? GetCustodianByCode(this IEnumerable<ICustodian> custodians, string code)
|
||||
{
|
||||
return custodians.FirstOrDefault(custodian => custodian.Code == code);
|
||||
}
|
||||
}
|
@ -101,6 +101,14 @@ namespace BTCPayServer.Abstractions.Extensions
|
||||
return categoryAndPageMatch && idMatch ? ActivePageClass : null;
|
||||
}
|
||||
|
||||
public static HtmlString ToBrowserDate(this DateTimeOffset date, string netFormat, string jsDateFormat = "short", string jsTimeFormat = "short")
|
||||
{
|
||||
var dateTime = date.ToString("o", CultureInfo.InvariantCulture);
|
||||
var displayDate = date.ToString(netFormat, CultureInfo.InvariantCulture);
|
||||
var tooltip = dateTime.Replace("T", " ");
|
||||
return new HtmlString($"<time datetime=\"{dateTime}\" data-date-style=\"{jsDateFormat}\" data-time-style=\"{jsTimeFormat}\" data-initial=\"localized\" data-bs-toggle=\"tooltip\" data-bs-title=\"{tooltip}\">{displayDate}</time>");
|
||||
}
|
||||
|
||||
public static HtmlString ToBrowserDate(this DateTimeOffset date, DateDisplayFormat format = DateDisplayFormat.Localized)
|
||||
{
|
||||
var relative = date.ToTimeAgo();
|
||||
|
@ -0,0 +1,35 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
|
||||
namespace BTCPayServer.Abstractions.TagHelpers;
|
||||
|
||||
[HtmlTargetElement("form", Attributes = "[permissioned]")]
|
||||
public partial class PermissionedFormTagHelper(
|
||||
IAuthorizationService authorizationService,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
: TagHelper
|
||||
{
|
||||
public string Permissioned { get; set; }
|
||||
public string PermissionResource { get; set; }
|
||||
|
||||
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
if (httpContextAccessor.HttpContext is null || string.IsNullOrEmpty(Permissioned))
|
||||
return;
|
||||
|
||||
var res = await authorizationService.AuthorizeAsync(httpContextAccessor.HttpContext.User,
|
||||
PermissionResource, Permissioned);
|
||||
if (!res.Succeeded)
|
||||
{
|
||||
var content = await output.GetChildContentAsync();
|
||||
var html = SubmitButtonRegex().Replace(content.GetContent(), "");
|
||||
output.Content.SetHtmlContent($"<fieldset disabled>{html}</fieldset>");
|
||||
}
|
||||
}
|
||||
|
||||
[GeneratedRegex("<(button|input).*?type=\"submit\".*?>.*?</\\1>")]
|
||||
private static partial Regex SubmitButtonRegex();
|
||||
}
|
@ -16,7 +16,7 @@
|
||||
<Platforms>AnyCPU</Platforms>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<Version Condition=" '$(Version)' == '' ">1.7.3</Version>
|
||||
<Version Condition=" '$(Version)' == '' ">1.7.4</Version>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
|
@ -1,102 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<IEnumerable<CustodianAccountData>> GetCustodianAccounts(string storeId, bool includeAssetBalances = false, CancellationToken token = default)
|
||||
{
|
||||
var queryPayload = new Dictionary<string, object>();
|
||||
if (includeAssetBalances)
|
||||
{
|
||||
queryPayload.Add("assetBalances", "true");
|
||||
}
|
||||
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts", queryPayload), token);
|
||||
return await HandleResponse<IEnumerable<CustodianAccountData>>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<CustodianAccountResponse> GetCustodianAccount(string storeId, string accountId, bool includeAssetBalances = false, CancellationToken token = default)
|
||||
{
|
||||
var queryPayload = new Dictionary<string, object>();
|
||||
if (includeAssetBalances)
|
||||
{
|
||||
queryPayload.Add("assetBalances", "true");
|
||||
}
|
||||
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}", queryPayload), token);
|
||||
return await HandleResponse<CustodianAccountResponse>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<CustodianAccountData> CreateCustodianAccount(string storeId, CreateCustodianAccountRequest request, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts", bodyPayload: request, method: HttpMethod.Post), token);
|
||||
return await HandleResponse<CustodianAccountData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<CustodianAccountData> UpdateCustodianAccount(string storeId, string accountId, CreateCustodianAccountRequest request, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}", bodyPayload: request, method: HttpMethod.Put), token);
|
||||
return await HandleResponse<CustodianAccountData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task DeleteCustodianAccount(string storeId, string accountId, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}", method: HttpMethod.Delete), token);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public virtual async Task<DepositAddressData> GetCustodianAccountDepositAddress(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);
|
||||
}
|
||||
|
||||
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,
|
||||
request, HttpMethod.Post);
|
||||
var response = await _httpClient.SendAsync(internalRequest, token);
|
||||
return await HandleResponse<MarketTradeResponseData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<MarketTradeResponseData> GetCustodianAccountTradeInfo(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)
|
||||
{
|
||||
var queryPayload = new Dictionary<string, object>();
|
||||
queryPayload.Add("fromAsset", fromAsset);
|
||||
queryPayload.Add("toAsset", toAsset);
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/trades/quote", queryPayload), token);
|
||||
return await HandleResponse<TradeQuoteResponseData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<WithdrawalResponseData> CreateCustodianAccountWithdrawal(string storeId, string accountId, WithdrawRequestData request, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/withdrawals", bodyPayload: request, method: HttpMethod.Post), token);
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<IEnumerable<CustodianData>> GetCustodians(CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/custodians"), token);
|
||||
return await HandleResponse<IEnumerable<CustodianData>>(response);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<IEnumerable<LNURLPayPaymentMethodData>>
|
||||
GetStoreLNURLPayPaymentMethods(string storeId, bool? enabled = null,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var query = new Dictionary<string, object>();
|
||||
if (enabled != null)
|
||||
{
|
||||
query.Add(nameof(enabled), enabled);
|
||||
}
|
||||
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LNURLPay",
|
||||
query), token);
|
||||
return await HandleResponse<IEnumerable<LNURLPayPaymentMethodData>>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LNURLPayPaymentMethodData> GetStoreLNURLPayPaymentMethod(
|
||||
string storeId,
|
||||
string cryptoCode, CancellationToken token = default)
|
||||
{
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LNURLPay/{cryptoCode}"), token);
|
||||
return await HandleResponse<LNURLPayPaymentMethodData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task RemoveStoreLNURLPayPaymentMethod(string storeId,
|
||||
string cryptoCode, CancellationToken token = default)
|
||||
{
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LNURLPay/{cryptoCode}",
|
||||
method: HttpMethod.Delete), token);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LNURLPayPaymentMethodData> UpdateStoreLNURLPayPaymentMethod(
|
||||
string storeId,
|
||||
string cryptoCode, LNURLPayPaymentMethodData paymentMethod,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LNURLPay/{cryptoCode}",
|
||||
bodyPayload: paymentMethod, method: HttpMethod.Put), token);
|
||||
return await HandleResponse<LNURLPayPaymentMethodData>(response);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<IEnumerable<LightningNetworkPaymentMethodData>>
|
||||
GetStoreLightningNetworkPaymentMethods(string storeId, bool? enabled = null,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var query = new Dictionary<string, object>();
|
||||
if (enabled != null)
|
||||
{
|
||||
query.Add(nameof(enabled), enabled);
|
||||
}
|
||||
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LightningNetwork",
|
||||
query), token);
|
||||
return await HandleResponse<IEnumerable<LightningNetworkPaymentMethodData>>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LightningNetworkPaymentMethodData> GetStoreLightningNetworkPaymentMethod(
|
||||
string storeId,
|
||||
string cryptoCode, CancellationToken token = default)
|
||||
{
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}"), token);
|
||||
return await HandleResponse<LightningNetworkPaymentMethodData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task RemoveStoreLightningNetworkPaymentMethod(string storeId,
|
||||
string cryptoCode, CancellationToken token = default)
|
||||
{
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}",
|
||||
method: HttpMethod.Delete), token);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LightningNetworkPaymentMethodData> UpdateStoreLightningNetworkPaymentMethod(
|
||||
string storeId,
|
||||
string cryptoCode, UpdateLightningNetworkPaymentMethodRequest paymentMethod,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}",
|
||||
bodyPayload: paymentMethod, method: HttpMethod.Put), token);
|
||||
return await HandleResponse<LightningNetworkPaymentMethodData>(response);
|
||||
}
|
||||
}
|
||||
}
|
@ -3,92 +3,47 @@ using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<IEnumerable<OnChainPaymentMethodData>> GetStoreOnChainPaymentMethods(string storeId,
|
||||
bool? enabled = null,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var query = new Dictionary<string, object>();
|
||||
if (enabled != null)
|
||||
{
|
||||
query.Add(nameof(enabled), enabled);
|
||||
}
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<OnChainPaymentMethodPreviewResultData>
|
||||
PreviewProposedStoreOnChainPaymentMethodAddresses(
|
||||
string storeId, string paymentMethodId, string derivationScheme, int offset = 0,
|
||||
int amount = 10,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/{paymentMethodId}/preview",
|
||||
bodyPayload: new UpdatePaymentMethodRequest() { Config = JValue.CreateString(derivationScheme) },
|
||||
queryPayload: new Dictionary<string, object>() { { "offset", offset }, { "amount", amount } },
|
||||
method: HttpMethod.Post), token);
|
||||
return await HandleResponse<OnChainPaymentMethodPreviewResultData>(response);
|
||||
}
|
||||
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain",
|
||||
query), token);
|
||||
return await HandleResponse<IEnumerable<OnChainPaymentMethodData>>(response);
|
||||
}
|
||||
public virtual async Task<OnChainPaymentMethodPreviewResultData> PreviewStoreOnChainPaymentMethodAddresses(
|
||||
string storeId, string paymentMethodId, int offset = 0, int amount = 10,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/{paymentMethodId}/preview",
|
||||
queryPayload: new Dictionary<string, object>() { { "offset", offset }, { "amount", amount } },
|
||||
method: HttpMethod.Get), token);
|
||||
return await HandleResponse<OnChainPaymentMethodPreviewResultData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<OnChainPaymentMethodData> GetStoreOnChainPaymentMethod(string storeId,
|
||||
string cryptoCode, CancellationToken token = default)
|
||||
{
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}"), token);
|
||||
return await HandleResponse<OnChainPaymentMethodData>(response);
|
||||
}
|
||||
public virtual async Task<GenerateOnChainWalletResponse> GenerateOnChainWallet(string storeId,
|
||||
string paymentMethodId, GenerateOnChainWalletRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/{paymentMethodId}/generate",
|
||||
bodyPayload: request,
|
||||
method: HttpMethod.Post), token);
|
||||
return await HandleResponse<GenerateOnChainWalletResponse>(response);
|
||||
}
|
||||
|
||||
public virtual async Task RemoveStoreOnChainPaymentMethod(string storeId,
|
||||
string cryptoCode, CancellationToken token = default)
|
||||
{
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}",
|
||||
method: HttpMethod.Delete), token);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public virtual async Task<OnChainPaymentMethodData> UpdateStoreOnChainPaymentMethod(string storeId,
|
||||
string cryptoCode, UpdateOnChainPaymentMethodRequest paymentMethod,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}",
|
||||
bodyPayload: paymentMethod, method: HttpMethod.Put), token);
|
||||
return await HandleResponse<OnChainPaymentMethodData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<OnChainPaymentMethodPreviewResultData>
|
||||
PreviewProposedStoreOnChainPaymentMethodAddresses(
|
||||
string storeId, string cryptoCode, UpdateOnChainPaymentMethodRequest paymentMethod, int offset = 0,
|
||||
int amount = 10,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/preview",
|
||||
bodyPayload: paymentMethod,
|
||||
queryPayload: new Dictionary<string, object>() { { "offset", offset }, { "amount", amount } },
|
||||
method: HttpMethod.Post), token);
|
||||
return await HandleResponse<OnChainPaymentMethodPreviewResultData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<OnChainPaymentMethodPreviewResultData> PreviewStoreOnChainPaymentMethodAddresses(
|
||||
string storeId, string cryptoCode, int offset = 0, int amount = 10,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/preview",
|
||||
queryPayload: new Dictionary<string, object>() { { "offset", offset }, { "amount", amount } },
|
||||
method: HttpMethod.Get), token);
|
||||
return await HandleResponse<OnChainPaymentMethodPreviewResultData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<OnChainPaymentMethodDataWithSensitiveData> GenerateOnChainWallet(string storeId,
|
||||
string cryptoCode, GenerateOnChainWalletRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/generate",
|
||||
bodyPayload: request,
|
||||
method: HttpMethod.Post), token);
|
||||
return await HandleResponse<OnChainPaymentMethodDataWithSensitiveData>(response);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
@ -7,21 +8,60 @@ namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<Dictionary<string, GenericPaymentMethodData>> GetStorePaymentMethods(string storeId,
|
||||
bool? enabled = null,
|
||||
public virtual async Task<GenericPaymentMethodData> UpdateStorePaymentMethod(
|
||||
string storeId,
|
||||
string paymentMethodId,
|
||||
UpdatePaymentMethodRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/{paymentMethodId}", bodyPayload: request, method: HttpMethod.Put),
|
||||
token);
|
||||
return await HandleResponse<GenericPaymentMethodData>(response);
|
||||
}
|
||||
public virtual async Task RemoveStorePaymentMethod(string storeId, string paymentMethodId)
|
||||
{
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/{paymentMethodId}", method: HttpMethod.Delete),
|
||||
CancellationToken.None);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public virtual async Task<GenericPaymentMethodData> GetStorePaymentMethod(string storeId,
|
||||
string paymentMethodId, bool? includeConfig = null, CancellationToken token = default)
|
||||
{
|
||||
var query = new Dictionary<string, object>();
|
||||
if (enabled != null)
|
||||
if (includeConfig != null)
|
||||
{
|
||||
query.Add(nameof(enabled), enabled);
|
||||
query.Add(nameof(includeConfig), includeConfig);
|
||||
}
|
||||
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/{paymentMethodId}",
|
||||
query), token);
|
||||
return await HandleResponse<GenericPaymentMethodData>(response);
|
||||
}
|
||||
public virtual async Task<GenericPaymentMethodData[]> GetStorePaymentMethods(string storeId,
|
||||
bool? onlyEnabled = null, bool? includeConfig = null, CancellationToken token = default)
|
||||
{
|
||||
var query = new Dictionary<string, object>();
|
||||
if (onlyEnabled != null)
|
||||
{
|
||||
query.Add(nameof(onlyEnabled), onlyEnabled);
|
||||
}
|
||||
if (includeConfig != null)
|
||||
{
|
||||
query.Add(nameof(includeConfig), includeConfig);
|
||||
}
|
||||
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods",
|
||||
query), token);
|
||||
return await HandleResponse<Dictionary<string, GenericPaymentMethodData>>(response);
|
||||
return await HandleResponse<GenericPaymentMethodData[]>(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,14 @@ namespace BTCPayServer.Client
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
|
||||
public virtual async Task<bool> ApproveUser(string idOrEmail, bool approved, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/users/{idOrEmail}/approve", null,
|
||||
new ApproveUserRequest { Approved = approved }, HttpMethod.Post), token);
|
||||
await HandleResponse(response);
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
|
||||
public virtual async Task<ApplicationUserData[]> GetUsers(CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/users/", null, HttpMethod.Get), token);
|
||||
|
@ -0,0 +1,43 @@
|
||||
using NBitcoin;
|
||||
using NBitcoin.DataEncoders;
|
||||
using NBitcoin.JsonConverters;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace BTCPayServer.Client.JsonConverters
|
||||
{
|
||||
public class SaneOutpointJsonConverter : JsonConverter
|
||||
{
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return typeof(OutPoint).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.TokenType == JsonToken.Null)
|
||||
return null;
|
||||
|
||||
if (reader.TokenType != JsonToken.String)
|
||||
throw new JsonObjectException($"Unexpected json token type, expected is {JsonToken.String} and actual is {reader.TokenType}", reader);
|
||||
try
|
||||
{
|
||||
if (!OutPoint.TryParse((string)reader.Value, out var outpoint))
|
||||
throw new JsonObjectException("Invalid bitcoin object of type OutPoint", reader);
|
||||
return outpoint;
|
||||
}
|
||||
catch (EndOfStreamException)
|
||||
{
|
||||
}
|
||||
throw new JsonObjectException("Invalid bitcoin object of type OutPoint", reader);
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
if (value is { })
|
||||
writer.WriteValue(value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
@ -25,6 +25,16 @@ namespace BTCPayServer.Client.Models
|
||||
/// </summary>
|
||||
public bool RequiresEmailConfirmation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the user was approved by an admin
|
||||
/// </summary>
|
||||
public bool Approved { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// whether the user needed approval on account creation
|
||||
/// </summary>
|
||||
public bool RequiresApproval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// the roles of the user
|
||||
/// </summary>
|
||||
|
6
BTCPayServer.Client/Models/ApproveUserRequest.cs
Normal file
6
BTCPayServer.Client/Models/ApproveUserRequest.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace BTCPayServer.Client;
|
||||
|
||||
public class ApproveUserRequest
|
||||
{
|
||||
public bool Approved { get; set; }
|
||||
}
|
@ -26,6 +26,7 @@ namespace BTCPayServer.Client.Models
|
||||
public string Template { get; set; } = null;
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public PosViewType DefaultView { get; set; }
|
||||
public bool ShowItems { get; set; } = false;
|
||||
public bool ShowCustomAmount { get; set; } = false;
|
||||
public bool ShowDiscount { get; set; } = false;
|
||||
public bool ShowSearch { get; set; } = true;
|
||||
|
@ -1,12 +0,0 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class CreateCustodianAccountRequest
|
||||
{
|
||||
public string CustodianCode { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
public JObject Config { get; set; }
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using BTCPayServer.Client.JsonConverters;
|
||||
using BTCPayServer.JsonConverters;
|
||||
using NBitcoin;
|
||||
using NBitcoin.JsonConverters;
|
||||
@ -21,7 +22,7 @@ namespace BTCPayServer.Client.Models
|
||||
public bool ProceedWithPayjoin { get; set; } = true;
|
||||
public bool ProceedWithBroadcast { get; set; } = true;
|
||||
public bool NoChange { get; set; } = false;
|
||||
[JsonProperty(ItemConverterType = typeof(OutpointJsonConverter))]
|
||||
[JsonProperty(ItemConverterType = typeof(SaneOutpointJsonConverter))]
|
||||
public List<OutPoint> SelectedInputs { get; set; } = null;
|
||||
public List<CreateOnChainTransactionRequestDestination> Destinations { get; set; }
|
||||
[JsonProperty("rbf")]
|
||||
|
@ -1,16 +0,0 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public abstract class CustodianAccountBaseData
|
||||
{
|
||||
public string CustodianCode { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string StoreId { get; set; }
|
||||
|
||||
public JObject Config { get; set; }
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class CustodianAccountData : CustodianAccountBaseData
|
||||
{
|
||||
public string Id { get; set; }
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BTCPayServer.Client.Models;
|
||||
|
||||
public class CustodianAccountResponse : CustodianAccountData
|
||||
{
|
||||
public IDictionary<string, decimal> AssetBalances { get; set; }
|
||||
|
||||
public CustodianAccountResponse()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BTCPayServer.Client.Models;
|
||||
|
||||
public class CustodianData
|
||||
{
|
||||
public string Code { get; set; }
|
||||
public string Name { get; set; }
|
||||
public Dictionary<string, AssetPairData> TradableAssetPairs { get; set; }
|
||||
public string[] WithdrawablePaymentMethods { get; set; }
|
||||
public string[] DepositablePaymentMethods { get; set; }
|
||||
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
namespace BTCPayServer.Client.Models;
|
||||
|
||||
public class DepositAddressData
|
||||
{
|
||||
// /**
|
||||
// * Example: P2PKH, P2SH, P2WPKH, P2TR, BOLT11, ...
|
||||
// */
|
||||
// public string Type { get; set; }
|
||||
|
||||
/**
|
||||
* Format depends hugely on the type.
|
||||
*/
|
||||
public string Address { get; set; }
|
||||
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Client.Models;
|
||||
|
||||
public class EmailSettingsData
|
||||
@ -26,4 +28,11 @@ public class EmailSettingsData
|
||||
get; set;
|
||||
}
|
||||
public bool DisableCertificateCheck { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public bool EnabledCertificateCheck
|
||||
{
|
||||
get => !DisableCertificateCheck;
|
||||
set { DisableCertificateCheck = !value; }
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using BTCPayServer.Client.JsonConverters;
|
||||
using BTCPayServer.Client.Models;
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Client
|
||||
{
|
||||
@ -22,4 +25,16 @@ namespace BTCPayServer.Client
|
||||
public bool ImportKeysToRPC { get; set; }
|
||||
public bool SavePrivateKeys { get; set; }
|
||||
}
|
||||
public class GenerateOnChainWalletResponse : GenericPaymentMethodData
|
||||
{
|
||||
public class ConfigData
|
||||
{
|
||||
public string AccountDerivation { get; set; }
|
||||
[JsonExtensionData]
|
||||
IDictionary<string, JToken> AdditionalData { get; set; }
|
||||
}
|
||||
[JsonConverter(typeof(MnemonicJsonConverter))]
|
||||
public Mnemonic Mnemonic { get; set; }
|
||||
public new ConfigData Config { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,22 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class GenericPaymentMethodData
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
public object Data { get; set; }
|
||||
public string CryptoCode { get; set; }
|
||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
public JToken Config { get; set; }
|
||||
public string PaymentMethodId { get; set; }
|
||||
}
|
||||
public class UpdatePaymentMethodRequest
|
||||
{
|
||||
public UpdatePaymentMethodRequest()
|
||||
{
|
||||
|
||||
}
|
||||
public bool? Enabled { get; set; }
|
||||
public JToken Config { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -29,13 +29,12 @@ namespace BTCPayServer.Client.Models
|
||||
public decimal Amount { get; set; }
|
||||
|
||||
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||
public decimal NetworkFee { get; set; }
|
||||
public decimal PaymentMethodFee { get; set; }
|
||||
|
||||
public List<Payment> Payments { get; set; }
|
||||
public string PaymentMethod { get; set; }
|
||||
|
||||
public string CryptoCode { get; set; }
|
||||
public JObject AdditionalData { get; set; }
|
||||
public string PaymentMethodId { get; set; }
|
||||
public JToken AdditionalData { get; set; }
|
||||
public string Currency { get; set; }
|
||||
|
||||
public class Payment
|
||||
{
|
||||
|
@ -1,17 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class LNURLPayPaymentMethodBaseData
|
||||
{
|
||||
public bool UseBech32Scheme { get; set; }
|
||||
|
||||
[JsonProperty("lud12Enabled")]
|
||||
public bool LUD12Enabled { get; set; }
|
||||
|
||||
public LNURLPayPaymentMethodBaseData()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class LNURLPayPaymentMethodData : LNURLPayPaymentMethodBaseData
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the payment method is enabled
|
||||
/// </summary>
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Crypto code of the payment method
|
||||
/// </summary>
|
||||
public string CryptoCode { get; set; }
|
||||
|
||||
public LNURLPayPaymentMethodData()
|
||||
{
|
||||
}
|
||||
|
||||
public LNURLPayPaymentMethodData(string cryptoCode, bool enabled, bool useBech32Scheme, bool lud12Enabled)
|
||||
{
|
||||
Enabled = enabled;
|
||||
CryptoCode = cryptoCode;
|
||||
UseBech32Scheme = useBech32Scheme;
|
||||
LUD12Enabled = lud12Enabled;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class LightningNetworkPaymentMethodBaseData
|
||||
{
|
||||
|
||||
public string ConnectionString { get; set; }
|
||||
public LightningNetworkPaymentMethodBaseData()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class LightningNetworkPaymentMethodData : LightningNetworkPaymentMethodBaseData
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the payment method is enabled
|
||||
/// </summary>
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Crypto code of the payment method
|
||||
/// </summary>
|
||||
public string CryptoCode { get; set; }
|
||||
|
||||
public LightningNetworkPaymentMethodData()
|
||||
{
|
||||
}
|
||||
|
||||
public LightningNetworkPaymentMethodData(string cryptoCode, string connectionString, bool enabled, string paymentMethod)
|
||||
{
|
||||
Enabled = enabled;
|
||||
CryptoCode = cryptoCode;
|
||||
ConnectionString = connectionString;
|
||||
PaymentMethod = paymentMethod;
|
||||
}
|
||||
|
||||
public string PaymentMethod { get; set; }
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BTCPayServer.Client.Models;
|
||||
|
||||
public class MarketTradeResponseData
|
||||
{
|
||||
public string FromAsset { get; }
|
||||
public string ToAsset { get; }
|
||||
/**
|
||||
* The ledger entries that show the balances that were affected by the trade.
|
||||
*/
|
||||
public List<LedgerEntryData> LedgerEntries { get; }
|
||||
/**
|
||||
* The unique ID of the trade that was executed.
|
||||
*/
|
||||
public string TradeId { get; }
|
||||
|
||||
public string AccountId { get; }
|
||||
|
||||
public string CustodianCode { get; }
|
||||
|
||||
public MarketTradeResponseData(string fromAsset, string toAsset, List<LedgerEntryData> ledgerEntries, string tradeId, string accountId, string custodianCode)
|
||||
{
|
||||
FromAsset = fromAsset;
|
||||
ToAsset = toAsset;
|
||||
LedgerEntries = ledgerEntries;
|
||||
TradeId = tradeId;
|
||||
AccountId = accountId;
|
||||
CustodianCode = custodianCode;
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class OnChainPaymentMethodBaseData
|
||||
{
|
||||
/// <summary>
|
||||
/// The derivation scheme
|
||||
/// </summary>
|
||||
public string DerivationScheme { get; set; }
|
||||
|
||||
public string Label { get; set; }
|
||||
|
||||
[JsonConverter(typeof(NBitcoin.JsonConverters.KeyPathJsonConverter))]
|
||||
public RootedKeyPath AccountKeyPath { get; set; }
|
||||
|
||||
public OnChainPaymentMethodBaseData()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class OnChainPaymentMethodDataPreview : OnChainPaymentMethodBaseData
|
||||
{
|
||||
/// <summary>
|
||||
/// Crypto code of the payment method
|
||||
/// </summary>
|
||||
public string CryptoCode { get; set; }
|
||||
|
||||
public OnChainPaymentMethodDataPreview()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public OnChainPaymentMethodDataPreview(string cryptoCode, string derivationScheme, string label, RootedKeyPath accountKeyPath)
|
||||
{
|
||||
Label = label;
|
||||
AccountKeyPath = accountKeyPath;
|
||||
CryptoCode = cryptoCode;
|
||||
DerivationScheme = derivationScheme;
|
||||
}
|
||||
}
|
||||
|
||||
public class OnChainPaymentMethodData : OnChainPaymentMethodDataPreview
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the payment method is enabled
|
||||
/// </summary>
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
public string PaymentMethod { get; set; }
|
||||
|
||||
public OnChainPaymentMethodData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public OnChainPaymentMethodData(string cryptoCode, string derivationScheme, bool enabled, string label, RootedKeyPath accountKeyPath, string paymentMethod) :
|
||||
base(cryptoCode, derivationScheme, label, accountKeyPath)
|
||||
{
|
||||
Enabled = enabled;
|
||||
PaymentMethod = paymentMethod;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
using BTCPayServer.Client.JsonConverters;
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class OnChainPaymentMethodDataWithSensitiveData : OnChainPaymentMethodData
|
||||
{
|
||||
public OnChainPaymentMethodDataWithSensitiveData()
|
||||
{
|
||||
}
|
||||
|
||||
public OnChainPaymentMethodDataWithSensitiveData(string cryptoCode, string derivationScheme, bool enabled,
|
||||
string label, RootedKeyPath accountKeyPath, Mnemonic mnemonic, string paymentMethod) : base(cryptoCode, derivationScheme, enabled,
|
||||
label, accountKeyPath, paymentMethod)
|
||||
{
|
||||
Mnemonic = mnemonic;
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(MnemonicJsonConverter))]
|
||||
public Mnemonic Mnemonic { get; set; }
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using BTCPayServer.Client.JsonConverters;
|
||||
using BTCPayServer.JsonConverters;
|
||||
using NBitcoin;
|
||||
using NBitcoin.JsonConverters;
|
||||
@ -12,7 +13,7 @@ namespace BTCPayServer.Client.Models
|
||||
public string Comment { get; set; }
|
||||
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||
public decimal Amount { get; set; }
|
||||
[JsonConverter(typeof(OutpointJsonConverter))]
|
||||
[JsonConverter(typeof(SaneOutpointJsonConverter))]
|
||||
public OutPoint Outpoint { get; set; }
|
||||
public string Link { get; set; }
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
|
@ -19,6 +19,7 @@ namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string DefaultView { get; set; }
|
||||
public bool ShowItems { get; set; }
|
||||
public bool ShowCustomAmount { get; set; }
|
||||
public bool ShowDiscount { get; set; }
|
||||
public bool ShowSearch { get; set; }
|
||||
|
@ -4,6 +4,7 @@ using System.Text;
|
||||
using NBitcoin.JsonConverters;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
@ -14,11 +15,15 @@ namespace BTCPayServer.Client.Models
|
||||
}
|
||||
public class RegisterBoltcardRequest
|
||||
{
|
||||
[JsonProperty("LNURLW")]
|
||||
public string LNURLW { get; set; }
|
||||
[JsonConverter(typeof(HexJsonConverter))]
|
||||
[JsonProperty("UID")]
|
||||
public byte[] UID { get; set; }
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public OnExistingBehavior? OnExisting { get; set; }
|
||||
[JsonExtensionData]
|
||||
public IDictionary<string, JToken> AdditionalData { get; set; } = new Dictionary<string, JToken>();
|
||||
}
|
||||
public class RegisterBoltcardResponse
|
||||
{
|
||||
|
@ -1,22 +0,0 @@
|
||||
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; }
|
||||
|
||||
public TradeQuoteResponseData(string fromAsset, string toAsset, decimal bid, decimal ask)
|
||||
{
|
||||
FromAsset = fromAsset;
|
||||
ToAsset = toAsset;
|
||||
Bid = bid;
|
||||
Ask = ask;
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
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; }
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class UpdateLightningNetworkPaymentMethodRequest : LightningNetworkPaymentMethodBaseData
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the payment method is enabled
|
||||
/// </summary>
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
public UpdateLightningNetworkPaymentMethodRequest()
|
||||
{
|
||||
}
|
||||
|
||||
public UpdateLightningNetworkPaymentMethodRequest(string connectionString, bool enabled)
|
||||
{
|
||||
Enabled = enabled;
|
||||
ConnectionString = connectionString;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class UpdateOnChainPaymentMethodRequest : OnChainPaymentMethodBaseData
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the payment method is enabled
|
||||
/// </summary>
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
public UpdateOnChainPaymentMethodRequest()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public UpdateOnChainPaymentMethodRequest(bool enabled, string derivationScheme, string label, RootedKeyPath accountKeyPath)
|
||||
{
|
||||
Enabled = enabled;
|
||||
Label = label;
|
||||
AccountKeyPath = accountKeyPath;
|
||||
DerivationScheme = derivationScheme;
|
||||
}
|
||||
}
|
||||
}
|
@ -7,11 +7,11 @@ namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class WebhookPayoutEvent : StoreWebhookEvent
|
||||
{
|
||||
public WebhookPayoutEvent(string evtType, string storeId)
|
||||
public WebhookPayoutEvent(string type, string storeId)
|
||||
{
|
||||
if (!evtType.StartsWith("payout", StringComparison.InvariantCultureIgnoreCase))
|
||||
throw new ArgumentException("Invalid event type", nameof(evtType));
|
||||
Type = evtType;
|
||||
if (!type.StartsWith("payout", StringComparison.InvariantCultureIgnoreCase))
|
||||
throw new ArgumentException("Invalid event type", nameof(type));
|
||||
Type = type;
|
||||
StoreId = storeId;
|
||||
}
|
||||
|
||||
@ -21,11 +21,11 @@ namespace BTCPayServer.Client.Models
|
||||
}
|
||||
public class WebhookPaymentRequestEvent : StoreWebhookEvent
|
||||
{
|
||||
public WebhookPaymentRequestEvent(string evtType, string storeId)
|
||||
public WebhookPaymentRequestEvent(string type, string storeId)
|
||||
{
|
||||
if (!evtType.StartsWith("paymentrequest", StringComparison.InvariantCultureIgnoreCase))
|
||||
throw new ArgumentException("Invalid event type", nameof(evtType));
|
||||
Type = evtType;
|
||||
if (!type.StartsWith("paymentrequest", StringComparison.InvariantCultureIgnoreCase))
|
||||
throw new ArgumentException("Invalid event type", nameof(type));
|
||||
Type = type;
|
||||
StoreId = storeId;
|
||||
}
|
||||
|
||||
@ -91,7 +91,7 @@ namespace BTCPayServer.Client.Models
|
||||
}
|
||||
|
||||
public bool AfterExpiration { get; set; }
|
||||
public string PaymentMethod { get; set; }
|
||||
public string PaymentMethodId { get; set; }
|
||||
public InvoicePaymentMethodDataModel.Payment Payment { get; set; }
|
||||
}
|
||||
|
||||
|
@ -1,85 +0,0 @@
|
||||
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 WithdrawRequestData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public WithdrawRequestData(string paymentMethod, TradeQuantity 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;
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace BTCPayServer.Client.Models;
|
||||
|
||||
public class WithdrawalResponseData : WithdrawalBaseResponseData
|
||||
{
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public WithdrawalStatus Status { get; }
|
||||
|
||||
public string WithdrawalId { get; }
|
||||
public DateTimeOffset CreatedTime { get; }
|
||||
|
||||
public string TransactionId { get; }
|
||||
|
||||
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)
|
||||
{
|
||||
WithdrawalId = withdrawalId;
|
||||
TargetAddress = targetAddress;
|
||||
TransactionId = transactionId;
|
||||
Status = status;
|
||||
CreatedTime = createdTime;
|
||||
}
|
||||
|
||||
|
||||
public enum WithdrawalStatus
|
||||
{
|
||||
Unknown = 0,
|
||||
Queued = 1,
|
||||
Complete = 2,
|
||||
Failed = 3
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -40,11 +40,6 @@ namespace BTCPayServer.Client
|
||||
public const string CanCreatePullPayments = "btcpay.store.cancreatepullpayments";
|
||||
public const string CanViewPullPayments = "btcpay.store.canviewpullpayments";
|
||||
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";
|
||||
public const string CanWithdrawFromCustodianAccounts = "btcpay.store.canwithdrawfromcustodianaccount";
|
||||
public const string CanTradeCustodianAccount = "btcpay.store.cantradecustodianaccount";
|
||||
public const string Unrestricted = "unrestricted";
|
||||
public static IEnumerable<string> AllPolicies
|
||||
{
|
||||
@ -79,11 +74,6 @@ namespace BTCPayServer.Client
|
||||
yield return CanCreatePullPayments;
|
||||
yield return CanViewPullPayments;
|
||||
yield return CanCreateNonApprovedPullPayments;
|
||||
yield return CanViewCustodianAccounts;
|
||||
yield return CanManageCustodianAccounts;
|
||||
yield return CanDepositToCustodianAccounts;
|
||||
yield return CanWithdrawFromCustodianAccounts;
|
||||
yield return CanTradeCustodianAccount;
|
||||
yield return CanManageUsers;
|
||||
yield return CanManagePayouts;
|
||||
yield return CanViewPayouts;
|
||||
@ -254,7 +244,6 @@ namespace BTCPayServer.Client
|
||||
{
|
||||
var policyMap = new Dictionary<string, HashSet<string>>();
|
||||
PolicyHasChild(policyMap, Policies.CanModifyStoreSettings,
|
||||
Policies.CanManageCustodianAccounts,
|
||||
Policies.CanManagePullPayments,
|
||||
Policies.CanModifyInvoices,
|
||||
Policies.CanViewStoreSettings,
|
||||
@ -275,7 +264,6 @@ namespace BTCPayServer.Client
|
||||
Policies.CanUseInternalLightningNode,
|
||||
Policies.CanManageUsers);
|
||||
PolicyHasChild(policyMap, Policies.CanUseInternalLightningNode, Policies.CanCreateLightningInvoiceInternalNode, Policies.CanViewLightningInvoiceInternalNode);
|
||||
PolicyHasChild(policyMap, Policies.CanManageCustodianAccounts, Policies.CanViewCustodianAccounts);
|
||||
PolicyHasChild(policyMap, Policies.CanModifyInvoices, Policies.CanViewInvoices, Policies.CanCreateInvoice, Policies.CanCreateLightningInvoiceInStore);
|
||||
PolicyHasChild(policyMap, Policies.CanViewStoreSettings, Policies.CanViewInvoices, Policies.CanViewPaymentRequests, Policies.CanViewReports, Policies.CanViewPullPayments, Policies.CanViewPayouts);
|
||||
PolicyHasChild(policyMap, Policies.CanManagePayouts, Policies.CanViewPayouts);
|
||||
|
@ -130,6 +130,11 @@ namespace BTCPayServer
|
||||
{
|
||||
return transactionInformationSet;
|
||||
}
|
||||
|
||||
public string GetTrackedDestination(Script scriptPubKey)
|
||||
{
|
||||
return scriptPubKey.Hash.ToString() + "#" + CryptoCode.ToUpperInvariant();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class BTCPayNetworkBase
|
||||
|
@ -39,7 +39,6 @@ namespace BTCPayServer.Data
|
||||
public DbSet<AddressInvoiceData> AddressInvoices { get; set; }
|
||||
public DbSet<APIKeyData> ApiKeys { get; set; }
|
||||
public DbSet<AppData> Apps { get; set; }
|
||||
public DbSet<CustodianAccountData> CustodianAccount { get; set; }
|
||||
public DbSet<StoredFile> Files { get; set; }
|
||||
public DbSet<InvoiceEventData> InvoiceEvents { get; set; }
|
||||
public DbSet<InvoiceSearchData> InvoiceSearches { get; set; }
|
||||
@ -94,7 +93,6 @@ namespace BTCPayServer.Data
|
||||
AddressInvoiceData.OnModelCreating(builder);
|
||||
APIKeyData.OnModelCreating(builder, Database);
|
||||
AppData.OnModelCreating(builder, Database);
|
||||
CustodianAccountData.OnModelCreating(builder, Database);
|
||||
//StoredFile.OnModelCreating(builder);
|
||||
InvoiceEventData.OnModelCreating(builder);
|
||||
InvoiceSearchData.OnModelCreating(builder);
|
||||
@ -107,10 +105,10 @@ namespace BTCPayServer.Data
|
||||
//PayjoinLock.OnModelCreating(builder);
|
||||
PaymentRequestData.OnModelCreating(builder, Database);
|
||||
PaymentData.OnModelCreating(builder, Database);
|
||||
PayoutData.OnModelCreating(builder);
|
||||
PayoutData.OnModelCreating(builder, Database);
|
||||
PendingInvoiceData.OnModelCreating(builder);
|
||||
//PlannedTransaction.OnModelCreating(builder);
|
||||
PullPaymentData.OnModelCreating(builder);
|
||||
PullPaymentData.OnModelCreating(builder, Database);
|
||||
RefundData.OnModelCreating(builder);
|
||||
SettingData.OnModelCreating(builder, Database);
|
||||
StoreSettingData.OnModelCreating(builder, Database);
|
||||
|
@ -17,6 +17,7 @@ namespace BTCPayServer.Data
|
||||
public override ApplicationDbContext CreateContext()
|
||||
{
|
||||
var builder = new DbContextOptionsBuilder<ApplicationDbContext>();
|
||||
builder.AddInterceptors(Data.InvoiceData.MigrationInterceptor.Instance);
|
||||
ConfigureBuilder(builder);
|
||||
return new ApplicationDbContext(builder.Options);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.1" />
|
||||
<PackageReference Include="NBitcoin.Altcoins" Version="3.0.23" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BTCPayServer.Abstractions\BTCPayServer.Abstractions.csproj" />
|
||||
|
@ -6,11 +6,6 @@ namespace BTCPayServer.Data
|
||||
{
|
||||
public class AddressInvoiceData
|
||||
{
|
||||
/// <summary>
|
||||
/// Some crypto currencies share same address prefix
|
||||
/// For not having exceptions thrown by two address on different network, we suffix by "#CRYPTOCODE"
|
||||
/// </summary>
|
||||
[Obsolete("Use GetHash instead")]
|
||||
public string Address { get; set; }
|
||||
public InvoiceData InvoiceData { get; set; }
|
||||
public string InvoiceDataId { get; set; }
|
||||
|
@ -11,6 +11,8 @@ namespace BTCPayServer.Data
|
||||
public class ApplicationUser : IdentityUser, IHasBlob<UserBlob>
|
||||
{
|
||||
public bool RequiresEmailConfirmation { get; set; }
|
||||
public bool RequiresApproval { get; set; }
|
||||
public bool Approved { get; set; }
|
||||
public List<StoredFile> StoredFiles { get; set; }
|
||||
[Obsolete("U2F support has been replace with FIDO2")]
|
||||
public List<U2FDevice> U2FDevices { get; set; }
|
||||
|
@ -1,54 +0,0 @@
|
||||
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>
|
||||
{
|
||||
[Required]
|
||||
[MaxLength(50)]
|
||||
public string Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(50)]
|
||||
public string StoreId { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(50)]
|
||||
public string CustodianCode { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(50)]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
[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)
|
||||
{
|
||||
builder.Entity<CustodianAccountData>()
|
||||
.HasOne(o => o.StoreData)
|
||||
.WithMany(i => i.CustodianAccounts)
|
||||
.HasForeignKey(i => i.StoreId).OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.Entity<CustodianAccountData>()
|
||||
.HasIndex(o => o.StoreId);
|
||||
|
||||
if (databaseFacade.IsNpgsql())
|
||||
{
|
||||
builder.Entity<CustodianAccountData>()
|
||||
.Property(o => o.Blob2)
|
||||
.HasColumnType("JSONB");
|
||||
}
|
||||
}
|
||||
}
|
370
BTCPayServer.Data/Data/InvoiceData.Migration.cs
Normal file
370
BTCPayServer.Data/Data/InvoiceData.Migration.cs
Normal file
@ -0,0 +1,370 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.IO.Compression;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Linq;
|
||||
using System.Globalization;
|
||||
using Newtonsoft.Json;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using BTCPayServer.Migrations;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public partial class InvoiceData
|
||||
{
|
||||
/// <summary>
|
||||
/// We have a migration running in the background that will migrate the data from the old blob to the new blob
|
||||
/// Meanwhile, we need to make sure that invoices which haven't been migrated yet are migrated on the fly.
|
||||
/// </summary>
|
||||
public class MigrationInterceptor : IMaterializationInterceptor
|
||||
{
|
||||
public static readonly MigrationInterceptor Instance = new MigrationInterceptor();
|
||||
public object InitializedInstance(MaterializationInterceptionData materializationData, object entity)
|
||||
{
|
||||
if (entity is InvoiceData invoiceData && invoiceData.Currency is null)
|
||||
{
|
||||
invoiceData.Migrate();
|
||||
}
|
||||
else if (entity is PaymentData paymentData && paymentData.Currency is null)
|
||||
{
|
||||
paymentData.Migrate();
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
static HashSet<string> superflousProperties = new HashSet<string>()
|
||||
{
|
||||
"availableAddressHashes",
|
||||
"events",
|
||||
"refunds",
|
||||
"paidAmount",
|
||||
"historicalAddresses",
|
||||
"refundable",
|
||||
"status",
|
||||
"exceptionStatus",
|
||||
"storeId",
|
||||
"id",
|
||||
"txFee",
|
||||
"refundMail",
|
||||
"rate",
|
||||
"depositAddress",
|
||||
"currency",
|
||||
"price",
|
||||
"payments",
|
||||
"orderId",
|
||||
"buyerInformation",
|
||||
"productInformation",
|
||||
"derivationStrategy",
|
||||
"archived",
|
||||
"isUnderPaid",
|
||||
"requiresRefundEmail",
|
||||
"invoiceTime"
|
||||
};
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
public void Migrate()
|
||||
{
|
||||
if (Currency is not null)
|
||||
return;
|
||||
if (Blob is not (null or { Length: 0 }))
|
||||
{
|
||||
Blob2 = MigrationExtensions.Unzip(Blob);
|
||||
Blob = null;
|
||||
}
|
||||
var blob = JObject.Parse(Blob2);
|
||||
if (blob["cryptoData"]?["BTC"] is not (null or { Type: JTokenType.Null }))
|
||||
{
|
||||
blob.Move(["rate"], ["cryptoData", "BTC", "rate"]);
|
||||
blob.Move(["txFee"], ["cryptoData", "BTC", "txFee"]);
|
||||
}
|
||||
blob.Move(["customerEmail"], ["metadata", "buyerEmail"]);
|
||||
foreach (var prop in (blob["cryptoData"] as JObject)?.Properties()?.ToList() ?? [])
|
||||
{
|
||||
// We should only change data for onchain
|
||||
if (prop.Name.Contains('_', StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (prop.Value is JObject pm)
|
||||
{
|
||||
pm.Remove("depositAddress");
|
||||
pm.Remove("feeRate");
|
||||
pm.Remove("txFee");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (prop.Value is JObject o)
|
||||
{
|
||||
o.ConvertNumberToString("rate");
|
||||
if (o["paymentMethod"] is JObject pm)
|
||||
{
|
||||
if (pm["networkFeeRate"] is null)
|
||||
pm["networkFeeRate"] = o["feeRate"] ?? 0.0m;
|
||||
if (pm["networkFeeMode"] is JValue { Type: JTokenType.Integer, Value: 0 or 0L })
|
||||
pm.Remove("networkFeeMode");
|
||||
if (pm["networkFeeMode"] is JValue { Type: JTokenType.Integer, Value: 2 or 2L })
|
||||
pm["networkFeeRate"] = 0.0m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var metadata = blob.Property("metadata")?.Value as JObject;
|
||||
if (metadata is null)
|
||||
{
|
||||
metadata = new JObject();
|
||||
blob.Add("metadata", metadata);
|
||||
}
|
||||
foreach (var prop in (blob["buyerInformation"] as JObject)?.Properties()?.ToList() ?? [])
|
||||
{
|
||||
if (prop.Value?.Value<string>() is not null)
|
||||
blob.Move(["buyerInformation", prop.Name], ["metadata", prop.Name]);
|
||||
}
|
||||
foreach (var prop in (blob["productInformation"] as JObject)?.Properties()?.ToList() ?? [])
|
||||
{
|
||||
if (prop.Name is "price" or "currency")
|
||||
blob.Move(["productInformation", prop.Name], [prop.Name]);
|
||||
else if (prop.Value?.Value<string>() is not null)
|
||||
blob.Move(["productInformation", prop.Name], ["metadata", prop.Name]);
|
||||
}
|
||||
blob.Move(["orderId"], ["metadata", "orderId"]);
|
||||
foreach (string prop in new string[] { "posData", "checkoutType", "defaultLanguage", "notificationEmail", "notificationURL", "storeSupportUrl", "redirectURL" })
|
||||
{
|
||||
blob.RemoveIfNull(prop);
|
||||
}
|
||||
blob.RemoveIfValue<bool>("fullNotifications", false);
|
||||
if (blob["receiptOptions"] is JObject receiptOptions)
|
||||
{
|
||||
foreach (string prop in new string[] { "showQR", "enabled", "showPayments" })
|
||||
{
|
||||
receiptOptions.RemoveIfNull(prop);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if (blob.Property("paymentTolerance") is JProperty { Value: { Type: JTokenType.Float } pv } prop)
|
||||
{
|
||||
if (pv.Value<decimal>() == 0.0m)
|
||||
prop.Remove();
|
||||
}
|
||||
}
|
||||
|
||||
var posData = blob.Move(["posData"], ["metadata", "posData"]);
|
||||
if (posData is not null && posData.Value?.Type is JTokenType.String)
|
||||
{
|
||||
try
|
||||
{
|
||||
posData.Value = JObject.Parse(posData.Value<string>());
|
||||
}
|
||||
catch
|
||||
{
|
||||
posData.Remove();
|
||||
}
|
||||
}
|
||||
if (posData?.Type is JTokenType.Null)
|
||||
posData.Remove();
|
||||
|
||||
if (blob["derivationStrategies"] is JValue { Type: JTokenType.String } v)
|
||||
blob["derivationStrategies"] = JObject.Parse(v.Value<string>());
|
||||
if (blob["derivationStrategies"] is JObject derivations)
|
||||
{
|
||||
foreach (var prop in derivations.Properties().ToList())
|
||||
{
|
||||
// We should only change data for onchain
|
||||
if (prop.Name.Contains('_', StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
if (prop.Value is JValue
|
||||
{
|
||||
Type: JTokenType.String,
|
||||
Value: String { Length: > 0 } val
|
||||
})
|
||||
{
|
||||
if (val[0] == '{')
|
||||
derivations[prop.Name] = JObject.Parse(val);
|
||||
else
|
||||
{
|
||||
if (val.Contains('-', StringComparison.OrdinalIgnoreCase))
|
||||
derivations[prop.Name] = new JObject() { ["accountDerivation"] = val };
|
||||
else
|
||||
derivations[prop.Name] = null;
|
||||
}
|
||||
}
|
||||
if (prop.Value is JObject derivation)
|
||||
{
|
||||
derivations[prop.Name] = derivation["accountDerivation"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (blob["derivationStrategies"] is null && blob["derivationStrategy"] is not null)
|
||||
{
|
||||
// If it's NBX derivation strategy, keep it. Else just give up, it might be Electrum format and we shouldn't support
|
||||
// that anymore in the backend for long...
|
||||
if (blob["derivationStrategy"]?.Value<string>().Contains('-', StringComparison.OrdinalIgnoreCase) is true)
|
||||
blob.Move(["derivationStrategy"], ["derivationStrategies", "BTC"]);
|
||||
else
|
||||
{
|
||||
blob.Remove("derivationStrategy");
|
||||
blob.Add("derivationStrategies", new JObject() { ["BTC"] = null });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (blob["type"]?.Value<string>() is "Standard")
|
||||
blob.Remove("type");
|
||||
foreach (var prop in new string[] { "extendedNotifications", "lazyPaymentMethods", "lazyPaymentMethods", "redirectAutomatically" })
|
||||
{
|
||||
if (blob[prop]?.Value<bool>() is false)
|
||||
blob.Remove(prop);
|
||||
}
|
||||
|
||||
blob.ConvertNumberToString("price");
|
||||
Currency = blob["currency"].Value<string>();
|
||||
var isTopup = blob["type"]?.Value<string>() is "TopUp";
|
||||
var amount = decimal.Parse(blob["price"].Value<string>(), CultureInfo.InvariantCulture);
|
||||
Amount = isTopup && amount == 0 ? null : decimal.Parse(blob["price"].Value<string>(), CultureInfo.InvariantCulture);
|
||||
CustomerEmail = null;
|
||||
foreach (var prop in superflousProperties)
|
||||
blob.Property(prop)?.Remove();
|
||||
if (blob["speedPolicy"] is JValue { Type: JTokenType.Integer, Value: 0 or 0L })
|
||||
blob.Remove("speedPolicy");
|
||||
blob.TryAdd("internalTags", new JArray());
|
||||
blob.TryAdd("receiptOptions", new JObject());
|
||||
|
||||
foreach (var prop in ((JObject)blob["cryptoData"]).Properties())
|
||||
{
|
||||
if (prop.Name.EndsWith("_LightningLike", StringComparison.OrdinalIgnoreCase) ||
|
||||
prop.Name.EndsWith("_LNURLPAY", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (prop.Value["paymentMethod"]?["PaymentHash"] is JObject)
|
||||
prop.Value["paymentMethod"]["PaymentHash"] = JValue.CreateNull();
|
||||
if (prop.Value["paymentMethod"]?["Preimage"] is JObject)
|
||||
prop.Value["paymentMethod"]["Preimage"] = JValue.CreateNull();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var prop in ((JObject)blob["cryptoData"]).Properties())
|
||||
{
|
||||
var crypto = prop.Name.Split(['_', '-']).First();
|
||||
if (blob.Move(["cryptoData", prop.Name, "rate"], ["rates", crypto]) is not null)
|
||||
((JObject)blob["rates"]).ConvertNumberToString(crypto);
|
||||
}
|
||||
blob.Move(["cryptoData"], ["prompts"]);
|
||||
var prompts = ((JObject)blob["prompts"]);
|
||||
foreach (var prop in prompts.Properties().ToList())
|
||||
{
|
||||
((JObject)blob["prompts"]).RenameProperty(prop.Name, MigrationExtensions.MigratePaymentMethodId(prop.Name));
|
||||
}
|
||||
blob["derivationStrategies"] = blob["derivationStrategies"] ?? new JObject();
|
||||
foreach (var prop in ((JObject)blob["derivationStrategies"]).Properties().ToList())
|
||||
{
|
||||
((JObject)blob["derivationStrategies"]).RenameProperty(prop.Name, MigrationExtensions.MigratePaymentMethodId(prop.Name));
|
||||
}
|
||||
|
||||
foreach (var prop in prompts.Properties())
|
||||
{
|
||||
var prompt = prop.Value as JObject;
|
||||
if (prompt is null)
|
||||
continue;
|
||||
prompt["currency"] = prop.Name.Split('-').First();
|
||||
prompt.RemoveIfNull("depositAddress");
|
||||
prompt.RemoveIfNull("txFee");
|
||||
prompt.RemoveIfNull("feeRate");
|
||||
|
||||
prompt.RenameProperty("depositAddress", "destination");
|
||||
prompt.RenameProperty("txFee", "paymentMethodFee");
|
||||
|
||||
var divisibility = MigrationExtensions.GetDivisibility(prop.Name);
|
||||
prompt.Add("divisibility", divisibility);
|
||||
if (prompt["paymentMethodFee"] is { Type: JTokenType.Integer } paymentMethodFee)
|
||||
{
|
||||
prompt["paymentMethodFee"] = ((decimal)paymentMethodFee.Value<long>() / (decimal)Math.Pow(10, divisibility)).ToString(CultureInfo.InvariantCulture);
|
||||
prompt.RemoveIfValue<string>("paymentMethodFee", "0");
|
||||
}
|
||||
prompt.Move(["paymentMethod"], ["details"]);
|
||||
prompt.Move(["feeRate"], ["details", "recommendedFeeRate"]);
|
||||
prompt.Move(["details", "networkFeeRate"], ["details", "paymentMethodFeeRate"]);
|
||||
prompt.Move(["details", "networkFeeMode"], ["details", "feeMode"]);
|
||||
if ((prompt["details"]?["Activated"])?.Value<bool>() is bool activated)
|
||||
{
|
||||
((JObject)prompt["details"]).Remove("Activated");
|
||||
prompt["inactive"] = !activated;
|
||||
prompt.RemoveIfValue<bool>("inactive", false);
|
||||
}
|
||||
if ((prompt["details"]?["activated"])?.Value<bool>() is bool activated2)
|
||||
{
|
||||
((JObject)prompt["details"]).Remove("activated");
|
||||
prompt["inactive"] = !activated2;
|
||||
prompt.RemoveIfValue<bool>("inactive", false);
|
||||
}
|
||||
var details = prompt["details"] as JObject ?? new JObject();
|
||||
details.RemoveIfValue<bool>("payjoinEnabled", false);
|
||||
details.RemoveIfNull("feeMode");
|
||||
if (details["feeMode"] is not (null or { Type: JTokenType.Null }))
|
||||
{
|
||||
details["feeMode"] = details["feeMode"].Value<int>() switch
|
||||
{
|
||||
1 => "Always",
|
||||
2 => "Never",
|
||||
_ => null
|
||||
};
|
||||
details.RemoveIfNull("feeMode");
|
||||
}
|
||||
|
||||
details.RemoveIfNull("BOLT11");
|
||||
details.RemoveIfNull("address");
|
||||
details.RemoveIfNull("Address");
|
||||
prompt.Move(["details", "BOLT11"], ["destination"]);
|
||||
prompt.Move(["details", "address"], ["destination"]);
|
||||
prompt.Move(["details", "Address"], ["destination"]);
|
||||
prompt.RenameProperty("Address", "destination");
|
||||
prompt.RenameProperty("BOLT11", "destination");
|
||||
|
||||
details.Remove("LightningSupportedPaymentMethod");
|
||||
foreach (var o in detailsRemoveDefault)
|
||||
details.RemoveIfNull(o);
|
||||
details.RemoveIfValue<decimal>("recommendedFeeRate", 0.0m);
|
||||
details.RemoveIfValue<decimal>("paymentMethodFeeRate", 0.0m);
|
||||
if (prop.Name.EndsWith("-CHAIN"))
|
||||
blob.Move(["derivationStrategies", prop.Name], ["prompts", prop.Name, "details", "accountDerivation"]);
|
||||
|
||||
var camel = new CamelCaseNamingStrategy();
|
||||
foreach (var p in details.Properties().ToList())
|
||||
{
|
||||
var camelName = camel.GetPropertyName(p.Name, false);
|
||||
if (camelName != p.Name)
|
||||
details.RenameProperty(p.Name, camelName);
|
||||
}
|
||||
}
|
||||
|
||||
if (blob["defaultPaymentMethod"] is not (null or { Type : JTokenType.Null }))
|
||||
blob["defaultPaymentMethod"] = MigrationExtensions.MigratePaymentMethodId(blob["defaultPaymentMethod"].Value<string>());
|
||||
blob.Remove("derivationStrategies");
|
||||
|
||||
blob["version"] = 3;
|
||||
Blob2 = blob.ToString(Formatting.None);
|
||||
}
|
||||
static string[] detailsRemoveDefault =
|
||||
[
|
||||
"paymentMethodFeeRate",
|
||||
"keyPath",
|
||||
"BOLT11",
|
||||
"NodeInfo",
|
||||
"Preimage",
|
||||
"InvoiceId",
|
||||
"PaymentHash",
|
||||
"ProvidedComment",
|
||||
"GeneratedBoltAmount",
|
||||
"ConsumedLightningAddress",
|
||||
"PayRequest"
|
||||
];
|
||||
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
}
|
@ -1,16 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public class InvoiceData : IHasBlobUntyped
|
||||
public partial class InvoiceData : IHasBlobUntyped
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Currency { get; set; }
|
||||
public decimal? Amount { get; set; }
|
||||
public string StoreDataId { get; set; }
|
||||
public StoreData StoreData { get; set; }
|
||||
|
||||
@ -25,6 +25,7 @@ namespace BTCPayServer.Data
|
||||
public string OrderId { get; set; }
|
||||
public string Status { get; set; }
|
||||
public string ExceptionStatus { get; set; }
|
||||
[Obsolete("Unused")]
|
||||
public string CustomerEmail { get; set; }
|
||||
public List<AddressInvoiceData> AddressInvoices { get; set; }
|
||||
public bool Archived { get; set; }
|
||||
@ -43,12 +44,14 @@ namespace BTCPayServer.Data
|
||||
builder.Entity<InvoiceData>().HasIndex(o => o.StoreDataId);
|
||||
builder.Entity<InvoiceData>().HasIndex(o => o.OrderId);
|
||||
builder.Entity<InvoiceData>().HasIndex(o => o.Created);
|
||||
|
||||
if (databaseFacade.IsNpgsql())
|
||||
{
|
||||
builder.Entity<InvoiceData>()
|
||||
.Property(o => o.Blob2)
|
||||
.HasColumnType("JSONB");
|
||||
builder.Entity<InvoiceData>()
|
||||
.Property(o => o.Amount)
|
||||
.HasColumnType("NUMERIC");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,6 @@ namespace BTCPayServer.Data
|
||||
{
|
||||
return Severity switch
|
||||
{
|
||||
EventSeverity.Info => "info",
|
||||
EventSeverity.Error => "danger",
|
||||
EventSeverity.Success => "success",
|
||||
EventSeverity.Warning => "warning",
|
||||
|
151
BTCPayServer.Data/Data/MigrationExtensions.cs
Normal file
151
BTCPayServer.Data/Data/MigrationExtensions.cs
Normal file
@ -0,0 +1,151 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO.Compression;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public static class MigrationExtensions
|
||||
{
|
||||
public static JProperty? Move(this JObject blob, string[] pathFrom, string[] pathTo)
|
||||
{
|
||||
var from = GetProperty(blob, pathFrom, false);
|
||||
if (from is null)
|
||||
return null;
|
||||
var to = GetProperty(blob, pathTo, true);
|
||||
to!.Value = from.Value;
|
||||
from.Remove();
|
||||
return to;
|
||||
}
|
||||
|
||||
public static void RenameProperty(this JObject o, string oldName, string newName)
|
||||
{
|
||||
var p = o.Property(oldName);
|
||||
if (p is null)
|
||||
return;
|
||||
RenameProperty(ref p, newName);
|
||||
}
|
||||
public static void RenameProperty(ref JProperty ls, string newName)
|
||||
{
|
||||
if (ls.Name != newName)
|
||||
{
|
||||
var parent = ls.Parent;
|
||||
ls.Remove();
|
||||
ls = new JProperty(newName, ls.Value);
|
||||
parent!.Add(ls);
|
||||
}
|
||||
}
|
||||
|
||||
public static JProperty? GetProperty(this JObject blob, string[] pathFrom, bool createIfNotExists)
|
||||
{
|
||||
var current = blob;
|
||||
for (int i = 0; i < pathFrom.Length - 1; i++)
|
||||
{
|
||||
if (current.TryGetValue(pathFrom[i], out var value) && value is JObject jObject)
|
||||
{
|
||||
current = jObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!createIfNotExists)
|
||||
return null;
|
||||
JProperty? prop = null;
|
||||
for (int ii = i; ii < pathFrom.Length; ii++)
|
||||
{
|
||||
var newProp = new JProperty(pathFrom[ii], new JObject());
|
||||
if (prop is null)
|
||||
current.Add(newProp);
|
||||
else
|
||||
prop.Value = new JObject(newProp);
|
||||
prop = newProp;
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
var result = current.Property(pathFrom[pathFrom.Length - 1]);
|
||||
if (result is null && createIfNotExists)
|
||||
{
|
||||
result = new JProperty(pathFrom[pathFrom.Length - 1], null as object);
|
||||
current.Add(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
public static CamelCaseNamingStrategy Camel = new CamelCaseNamingStrategy();
|
||||
public static void RemoveIfNull(this JObject blob, string propName)
|
||||
{
|
||||
if (blob.Property(propName)?.Value.Type is JTokenType.Null)
|
||||
blob.Remove(propName);
|
||||
}
|
||||
public static void RemoveIfValue<T>(this JObject conf, string propName, T v)
|
||||
{
|
||||
var p = conf.Property(propName);
|
||||
if (p is null)
|
||||
return;
|
||||
if (p.Value is JValue { Type: JTokenType.Null })
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(default, v))
|
||||
p.Remove();
|
||||
}
|
||||
else if (p.Value is JValue jv)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(jv.Value<T>(), v))
|
||||
{
|
||||
p.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ConvertNumberToString(this JObject o, string prop)
|
||||
{
|
||||
if (o[prop]?.Type is JTokenType.Float)
|
||||
o[prop] = o[prop]!.Value<decimal>().ToString(CultureInfo.InvariantCulture);
|
||||
if (o[prop]?.Type is JTokenType.Integer)
|
||||
o[prop] = o[prop]!.Value<long>().ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
public static string Unzip(byte[] bytes)
|
||||
{
|
||||
MemoryStream ms = new MemoryStream(bytes);
|
||||
using GZipStream gzip = new GZipStream(ms, CompressionMode.Decompress);
|
||||
StreamReader reader = new StreamReader(gzip, Encoding.UTF8);
|
||||
var unzipped = reader.ReadToEnd();
|
||||
return unzipped;
|
||||
}
|
||||
|
||||
public static int GetDivisibility(string paymentMethodId)
|
||||
{
|
||||
var splitted = paymentMethodId.Split('-');
|
||||
return (CryptoCode: splitted[0], Type: splitted[1]) switch
|
||||
{
|
||||
{ Type: "LN" } or { Type: "LNURL" } => 11,
|
||||
{ Type: "CHAIN", CryptoCode: var code } when code == "XMR" => 12,
|
||||
{ Type: "CHAIN" } => 8,
|
||||
_ => 8
|
||||
};
|
||||
}
|
||||
|
||||
public static string MigratePaymentMethodId(string paymentMethodId)
|
||||
{
|
||||
var splitted = paymentMethodId.Split(new[] { '_', '-' });
|
||||
if (splitted is [var cryptoCode, var paymentType])
|
||||
{
|
||||
return paymentType switch
|
||||
{
|
||||
"BTCLike" => $"{cryptoCode}-CHAIN",
|
||||
"LightningLike" or "LightningNetwork" => $"{cryptoCode}-LN",
|
||||
"LNURLPAY" => $"{cryptoCode}-LNURL",
|
||||
_ => throw new NotSupportedException("Unknown payment type " + paymentType)
|
||||
};
|
||||
}
|
||||
if (splitted.Length == 1)
|
||||
return $"{splitted[0]}-CHAIN";
|
||||
throw new NotSupportedException("Unknown payment id " + paymentMethodId);
|
||||
}
|
||||
}
|
||||
}
|
179
BTCPayServer.Data/Data/PaymentData.Migration.cs
Normal file
179
BTCPayServer.Data/Data/PaymentData.Migration.cs
Normal file
@ -0,0 +1,179 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Migrations;
|
||||
using NBitcoin;
|
||||
using NBitcoin.Altcoins;
|
||||
using NBitcoin.DataEncoders;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public partial class PaymentData
|
||||
{
|
||||
public void Migrate()
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
if (Currency is not null)
|
||||
return;
|
||||
if (Blob is not (null or { Length: 0 }))
|
||||
{
|
||||
Blob2 = MigrationExtensions.Unzip(Blob);
|
||||
Blob = null;
|
||||
}
|
||||
var blob = JObject.Parse(Blob2);
|
||||
if (blob["cryptoPaymentDataType"] is null)
|
||||
blob["cryptoPaymentDataType"] = "BTCLike";
|
||||
if (blob["cryptoCode"] is null)
|
||||
blob["cryptoCode"] = "BTC";
|
||||
|
||||
if (blob["receivedTime"] is null)
|
||||
blob.Move(["receivedTimeMs"], ["receivedTime"]);
|
||||
else
|
||||
{
|
||||
// Convert number of seconds to number of milliseconds
|
||||
var timeSeconds = (ulong)(long)blob["receivedTime"].Value<long>();
|
||||
var date = NBitcoin.Utils.UnixTimeToDateTime(timeSeconds);
|
||||
blob["receivedTime"] = DateTimeToMilliUnixTime(date.UtcDateTime);
|
||||
}
|
||||
|
||||
var cryptoCode = blob["cryptoCode"].Value<string>();
|
||||
Type = cryptoCode + "_" + blob["cryptoPaymentDataType"].Value<string>();
|
||||
Type = MigrationExtensions.MigratePaymentMethodId(Type);
|
||||
var divisibility = MigrationExtensions.GetDivisibility(Type);
|
||||
Currency = blob["cryptoCode"].Value<string>();
|
||||
blob.Remove("cryptoCode");
|
||||
blob.Remove("cryptoPaymentDataType");
|
||||
|
||||
JObject cryptoData;
|
||||
if (blob["cryptoPaymentData"] is null)
|
||||
{
|
||||
cryptoData = new JObject();
|
||||
blob["cryptoPaymentData"] = cryptoData;
|
||||
cryptoData["RBF"] = true;
|
||||
cryptoData["confirmationCount"] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
cryptoData = JObject.Parse(blob["cryptoPaymentData"].Value<string>());
|
||||
foreach (var prop in cryptoData.Properties().ToList())
|
||||
{
|
||||
if (prop.Name is "rbf")
|
||||
cryptoData.RenameProperty("rbf", "RBF");
|
||||
else if (prop.Name is "bolT11")
|
||||
cryptoData.RenameProperty("bolT11", "BOLT11");
|
||||
else
|
||||
cryptoData.RenameProperty(prop.Name, MigrationExtensions.Camel.GetPropertyName(prop.Name, false));
|
||||
}
|
||||
}
|
||||
blob.Remove("cryptoPaymentData");
|
||||
cryptoData["outpoint"] = blob["outpoint"];
|
||||
if (blob["output"] is not (null or { Type: JTokenType.Null }))
|
||||
{
|
||||
// Old versions didn't track addresses, so we take it from output.
|
||||
// We don't know the network for sure but better having something than nothing in destination.
|
||||
// If signet/testnet crash we don't really care anyway.
|
||||
// Also, only LTC was supported at this time.
|
||||
Network network = (cryptoCode switch { "LTC" => (INetworkSet)Litecoin.Instance, _ => Bitcoin.Instance }).Mainnet;
|
||||
var txout = network.Consensus.ConsensusFactory.CreateTxOut();
|
||||
txout.ReadWrite(Encoders.Hex.DecodeData(blob["output"].Value<string>()), network);
|
||||
cryptoData["value"] = txout.Value.Satoshi;
|
||||
blob["destination"] = txout.ScriptPubKey.GetDestinationAddress(network)?.ToString();
|
||||
}
|
||||
blob.Remove("output");
|
||||
blob.Remove("outpoint");
|
||||
// Convert from sats to btc
|
||||
if (cryptoData["value"] is not (null or { Type: JTokenType.Null }))
|
||||
{
|
||||
var v = cryptoData["value"].Value<long>();
|
||||
Amount = (decimal)v / (decimal)Money.COIN;
|
||||
cryptoData.Remove("value");
|
||||
|
||||
blob["paymentMethodFee"] = blob["networkFee"];
|
||||
blob.RemoveIfValue<decimal>("paymentMethodFee", 0.0m);
|
||||
blob.ConvertNumberToString("paymentMethodFee");
|
||||
blob.Remove("networkFee");
|
||||
blob.RemoveIfNull("paymentMethodFee");
|
||||
}
|
||||
// Convert from millisats to btc
|
||||
else if (cryptoData["amount"] is not (null or { Type: JTokenType.Null }))
|
||||
{
|
||||
var v = cryptoData["amount"].Value<long>();
|
||||
Amount = (decimal)v / (decimal)Math.Pow(10.0, divisibility);
|
||||
cryptoData.Remove("amount");
|
||||
}
|
||||
if (cryptoData["address"] is not (null or { Type: JTokenType.Null }))
|
||||
{
|
||||
blob["destination"] = cryptoData["address"];
|
||||
cryptoData.Remove("address");
|
||||
}
|
||||
if (cryptoData["BOLT11"] is not (null or { Type: JTokenType.Null }))
|
||||
{
|
||||
blob["destination"] = cryptoData["BOLT11"];
|
||||
cryptoData.Remove("BOLT11");
|
||||
}
|
||||
if (cryptoData["outpoint"] is not (null or { Type: JTokenType.Null }))
|
||||
{
|
||||
// Convert to format txid-n
|
||||
cryptoData["outpoint"] = OutPoint.Parse(cryptoData["outpoint"].Value<string>()).ToString();
|
||||
}
|
||||
if (Accounted is false)
|
||||
Status = PaymentStatus.Unaccounted;
|
||||
else if (cryptoData["confirmationCount"] is { Type: JTokenType.Integer })
|
||||
{
|
||||
var confirmationCount = cryptoData["confirmationCount"].Value<int>();
|
||||
// Technically, we should use the invoice's speed policy, however it's not on our
|
||||
// scope and is good enough for majority of cases.
|
||||
Status = confirmationCount > 0 ? PaymentStatus.Settled : PaymentStatus.Processing;
|
||||
if (cryptoData["LockTime"] is { Type: JTokenType.Integer })
|
||||
{
|
||||
var lockTime = cryptoData["LockTime"].Value<int>();
|
||||
if (confirmationCount < lockTime)
|
||||
Status = PaymentStatus.Processing;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Status = PaymentStatus.Settled;
|
||||
}
|
||||
Created = MilliUnixTimeToDateTime(blob["receivedTime"].Value<long>());
|
||||
cryptoData.RemoveIfValue<bool>("rbf", false);
|
||||
cryptoData.Remove("legacy");
|
||||
cryptoData.Remove("networkFee");
|
||||
cryptoData.Remove("paymentType");
|
||||
cryptoData.RemoveIfNull("outpoint");
|
||||
cryptoData.RemoveIfValue<bool>("RBF", false);
|
||||
|
||||
blob.Remove("receivedTime");
|
||||
blob.Remove("accounted");
|
||||
blob.Remove("networkFee");
|
||||
blob["details"] = cryptoData;
|
||||
blob["divisibility"] = divisibility;
|
||||
blob["version"] = 2;
|
||||
Blob2 = blob.ToString(Formatting.None);
|
||||
Accounted = null;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
|
||||
static readonly DateTimeOffset unixRef = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
|
||||
public static long DateTimeToMilliUnixTime(in DateTime time)
|
||||
{
|
||||
var date = ((DateTimeOffset)time).ToUniversalTime();
|
||||
long v = (long)(date - unixRef).TotalMilliseconds;
|
||||
if (v < 0)
|
||||
throw new FormatException("Invalid datetime (less than 1/1/1970)");
|
||||
return v;
|
||||
}
|
||||
public static DateTimeOffset MilliUnixTimeToDateTime(long value)
|
||||
{
|
||||
var v = value;
|
||||
if (v < 0)
|
||||
throw new FormatException("Invalid datetime (less than 1/1/1970)");
|
||||
return unixRef + TimeSpan.FromMilliseconds(v);
|
||||
}
|
||||
}
|
||||
}
|
@ -4,17 +4,32 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public class PaymentData : IHasBlobUntyped
|
||||
public enum PaymentStatus
|
||||
{
|
||||
Processing,
|
||||
Settled,
|
||||
Unaccounted
|
||||
}
|
||||
public partial class PaymentData : IHasBlobUntyped
|
||||
{
|
||||
/// <summary>
|
||||
/// The date of creation of the payment
|
||||
/// Note that while it is a nullable field, our migration
|
||||
/// process ensure it is populated.
|
||||
/// </summary>
|
||||
public DateTimeOffset? Created { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string InvoiceDataId { get; set; }
|
||||
public string Currency { get; set; }
|
||||
public decimal? Amount { 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; }
|
||||
|
||||
[Obsolete("Use Status instead")]
|
||||
public bool? Accounted { get; set; }
|
||||
public PaymentStatus? Status { get; set; }
|
||||
|
||||
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
||||
{
|
||||
@ -23,11 +38,17 @@ namespace BTCPayServer.Data
|
||||
.WithMany(i => i.Payments).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.Entity<PaymentData>()
|
||||
.HasIndex(o => o.InvoiceDataId);
|
||||
builder.Entity<PaymentData>()
|
||||
.Property(o => o.Status)
|
||||
.HasConversion<string>();
|
||||
if (databaseFacade.IsNpgsql())
|
||||
{
|
||||
builder.Entity<PaymentData>()
|
||||
.Property(o => o.Blob2)
|
||||
.HasColumnType("JSONB");
|
||||
builder.Entity<PaymentData>()
|
||||
.Property(o => o.Amount)
|
||||
.HasColumnType("NUMERIC");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text;
|
||||
using BTCPayServer.Client.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
@ -21,14 +24,14 @@ namespace BTCPayServer.Data
|
||||
[MaxLength(20)]
|
||||
[Required]
|
||||
public string PaymentMethodId { get; set; }
|
||||
public byte[] Blob { get; set; }
|
||||
public byte[] Proof { get; set; }
|
||||
public string Blob { get; set; }
|
||||
public string Proof { get; set; }
|
||||
#nullable enable
|
||||
public string? Destination { get; set; }
|
||||
#nullable restore
|
||||
public StoreData StoreData { get; set; }
|
||||
|
||||
internal static void OnModelCreating(ModelBuilder builder)
|
||||
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
||||
{
|
||||
builder.Entity<PayoutData>()
|
||||
.HasOne(o => o.PullPaymentData)
|
||||
@ -43,6 +46,33 @@ namespace BTCPayServer.Data
|
||||
.HasIndex(o => o.State);
|
||||
builder.Entity<PayoutData>()
|
||||
.HasIndex(x => new { DestinationId = x.Destination, x.State });
|
||||
|
||||
if (databaseFacade.IsNpgsql())
|
||||
{
|
||||
builder.Entity<PayoutData>()
|
||||
.Property(o => o.Blob)
|
||||
.HasColumnType("JSONB");
|
||||
builder.Entity<PayoutData>()
|
||||
.Property(o => o.Proof)
|
||||
.HasColumnType("JSONB");
|
||||
}
|
||||
else if (databaseFacade.IsMySql())
|
||||
{
|
||||
builder.Entity<PayoutData>()
|
||||
.Property(o => o.Blob)
|
||||
.HasConversion(new ValueConverter<string, byte[]>
|
||||
(
|
||||
convertToProviderExpression: (str) => Encoding.UTF8.GetBytes(str),
|
||||
convertFromProviderExpression: (bytes) => Encoding.UTF8.GetString(bytes)
|
||||
));
|
||||
builder.Entity<PayoutData>()
|
||||
.Property(o => o.Proof)
|
||||
.HasConversion(new ValueConverter<string, byte[]>
|
||||
(
|
||||
convertToProviderExpression: (str) => Encoding.UTF8.GetBytes(str),
|
||||
convertFromProviderExpression: (bytes) => Encoding.UTF8.GetString(bytes)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// utility methods
|
||||
|
@ -3,8 +3,11 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BTCPayServer.Client.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
@ -24,16 +27,33 @@ namespace BTCPayServer.Data
|
||||
public DateTimeOffset? EndDate { get; set; }
|
||||
public bool Archived { get; set; }
|
||||
public List<PayoutData> Payouts { get; set; }
|
||||
public byte[] Blob { get; set; }
|
||||
public string Blob { get; set; }
|
||||
|
||||
|
||||
internal static void OnModelCreating(ModelBuilder builder)
|
||||
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
||||
{
|
||||
builder.Entity<PullPaymentData>()
|
||||
.HasIndex(o => o.StoreId);
|
||||
builder.Entity<PullPaymentData>()
|
||||
.HasOne(o => o.StoreData)
|
||||
.HasOne(o => o.StoreData)
|
||||
.WithMany(o => o.PullPayments).OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
if (databaseFacade.IsNpgsql())
|
||||
{
|
||||
builder.Entity<PullPaymentData>()
|
||||
.Property(o => o.Blob)
|
||||
.HasColumnType("JSONB");
|
||||
}
|
||||
else if (databaseFacade.IsMySql())
|
||||
{
|
||||
builder.Entity<PullPaymentData>()
|
||||
.Property(o => o.Blob)
|
||||
.HasConversion(new ValueConverter<string, byte[]>
|
||||
(
|
||||
convertToProviderExpression: (str) => Encoding.UTF8.GetBytes(str),
|
||||
convertFromProviderExpression: (bytes) => Encoding.UTF8.GetString(bytes)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
public (DateTimeOffset Start, DateTimeOffset? End)? GetPeriod(DateTimeOffset now)
|
||||
|
@ -5,6 +5,7 @@ using BTCPayServer.Client.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
@ -44,7 +45,6 @@ namespace BTCPayServer.Data
|
||||
public IEnumerable<LightningAddressData> LightningAddresses { get; set; }
|
||||
public IEnumerable<PayoutProcessorData> PayoutProcessors { get; set; }
|
||||
public IEnumerable<PayoutData> Payouts { get; set; }
|
||||
public IEnumerable<CustodianAccountData> CustodianAccounts { get; set; }
|
||||
public IEnumerable<StoreSettingData> Settings { get; set; }
|
||||
public IEnumerable<FormData> Forms { get; set; }
|
||||
public IEnumerable<StoreRole> StoreRoles { get; set; }
|
||||
|
@ -0,0 +1,39 @@
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BTCPayServer.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20240104155620_AddApprovalToApplicationUser")]
|
||||
public partial class AddApprovalToApplicationUser : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "Approved",
|
||||
table: "AspNetUsers",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "RequiresApproval",
|
||||
table: "AspNetUsers",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Approved",
|
||||
table: "AspNetUsers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "RequiresApproval",
|
||||
table: "AspNetUsers");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BTCPayServer.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20240220000000_FixWalletObjectsWithEmptyWalletId")]
|
||||
public partial class FixWalletObjectsWithEmptyWalletId : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder.IsNpgsql())
|
||||
{
|
||||
migrationBuilder.Sql("DELETE FROM \"WalletObjects\" WHERE \"WalletId\"='';");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BTCPayServer.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20240229000000_PayoutAndPullPaymentToJsonBlob")]
|
||||
public partial class PayoutAndPullPaymentToJsonBlob : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder.IsNpgsql())
|
||||
{
|
||||
migrationBuilder.Sql("ALTER TABLE \"Payouts\" ALTER COLUMN \"Blob\" TYPE JSONB USING regexp_replace(convert_from(\"Blob\",'UTF8'), '\\\\u0000', '', 'g')::JSONB");
|
||||
migrationBuilder.Sql("ALTER TABLE \"Payouts\" ALTER COLUMN \"Proof\" TYPE JSONB USING regexp_replace(convert_from(\"Proof\",'UTF8'), '\\\\u0000', '', 'g')::JSONB");
|
||||
migrationBuilder.Sql("ALTER TABLE \"PullPayments\" ALTER COLUMN \"Blob\" TYPE JSONB USING regexp_replace(convert_from(\"Blob\",'UTF8'), '\\\\u0000', '', 'g')::JSONB");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BTCPayServer.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20240229092905_AddManagerAndEmployeeToStoreRoles")]
|
||||
public partial class AddManagerAndEmployeeToStoreRoles : Migration
|
||||
{
|
||||
object GetPermissionsData(MigrationBuilder migrationBuilder, string[] permissions)
|
||||
{
|
||||
return migrationBuilder.IsNpgsql()
|
||||
? permissions
|
||||
: JsonConvert.SerializeObject(permissions);
|
||||
}
|
||||
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
var permissionsType = migrationBuilder.IsNpgsql() ? "TEXT[]" : "TEXT";
|
||||
migrationBuilder.InsertData(
|
||||
"StoreRoles",
|
||||
columns: new[] { "Id", "Role", "Permissions" },
|
||||
columnTypes: new[] { "TEXT", "TEXT", permissionsType },
|
||||
values: new object[,]
|
||||
{
|
||||
{
|
||||
"Manager", "Manager", GetPermissionsData(migrationBuilder, new[]
|
||||
{
|
||||
"btcpay.store.canviewstoresettings",
|
||||
"btcpay.store.canmodifyinvoices",
|
||||
"btcpay.store.webhooks.canmodifywebhooks",
|
||||
"btcpay.store.canmodifypaymentrequests",
|
||||
"btcpay.store.canmanagepullpayments",
|
||||
"btcpay.store.canmanagepayouts"
|
||||
})
|
||||
},
|
||||
{
|
||||
"Employee", "Employee", GetPermissionsData(migrationBuilder, new[]
|
||||
{
|
||||
"btcpay.store.canmodifyinvoices",
|
||||
"btcpay.store.canmodifypaymentrequests",
|
||||
"btcpay.store.cancreatenonapprovedpullpayments",
|
||||
"btcpay.store.canviewpayouts",
|
||||
"btcpay.store.canviewpullpayments"
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
"StoreRoles",
|
||||
keyColumns: new[] { "Id" },
|
||||
keyColumnTypes: new[] { "TEXT" },
|
||||
keyValues: new[] { "Guest" },
|
||||
columns: new[] { "Permissions" },
|
||||
columnTypes: new[] { permissionsType },
|
||||
values: new object[]
|
||||
{
|
||||
GetPermissionsData(migrationBuilder, new[]
|
||||
{
|
||||
"btcpay.store.canmodifyinvoices",
|
||||
"btcpay.store.canviewpaymentrequests",
|
||||
"btcpay.store.canviewpullpayments",
|
||||
"btcpay.store.canviewpayouts"
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DeleteData("StoreRoles", "Id", "Manager");
|
||||
migrationBuilder.DeleteData("StoreRoles", "Id", "Employee");
|
||||
|
||||
var permissionsType = migrationBuilder.IsNpgsql() ? "TEXT[]" : "TEXT";
|
||||
migrationBuilder.UpdateData(
|
||||
"StoreRoles",
|
||||
keyColumns: new[] { "Id" },
|
||||
keyColumnTypes: new[] { "TEXT" },
|
||||
keyValues: new[] { "Guest" },
|
||||
columns: new[] { "Permissions" },
|
||||
columnTypes: new[] { permissionsType },
|
||||
values: new object[]
|
||||
{
|
||||
GetPermissionsData(migrationBuilder, new[]
|
||||
{
|
||||
"btcpay.store.canviewstoresettings",
|
||||
"btcpay.store.canmodifyinvoices",
|
||||
"btcpay.store.canviewcustodianaccounts",
|
||||
"btcpay.store.candeposittocustodianaccount"
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BTCPayServer.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20240304003640_addinvoicecolumns")]
|
||||
public partial class addinvoicecolumns : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<decimal>(
|
||||
name: "Amount",
|
||||
table: "Invoices",
|
||||
type: migrationBuilder.IsNpgsql() ? "NUMERIC" : "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Currency",
|
||||
table: "Invoices",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Amount",
|
||||
table: "Invoices");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Currency",
|
||||
table: "Invoices");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
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("20240317024757_payments_refactor")]
|
||||
public partial class payments_refactor : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<decimal>(
|
||||
name: "Amount",
|
||||
table: "Payments",
|
||||
type: migrationBuilder.IsNpgsql() ? "NUMERIC" : "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTimeOffset>(
|
||||
name: "Created",
|
||||
table: "Payments",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Currency",
|
||||
table: "Payments",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Status",
|
||||
table: "Payments",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
if (migrationBuilder.IsNpgsql())
|
||||
{
|
||||
migrationBuilder.AlterColumn<bool?>(
|
||||
name: "Accounted",
|
||||
table: "Payments",
|
||||
nullable: true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Amount",
|
||||
table: "Payments");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Created",
|
||||
table: "Payments");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Currency",
|
||||
table: "Payments");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Status",
|
||||
table: "Payments");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
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("20240325095923_RemoveCustodian")]
|
||||
public partial class RemoveCustodian : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "CustodianAccount");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "CustodianAccount",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "TEXT", maxLength: 50, nullable: false),
|
||||
StoreId = table.Column<string>(type: "TEXT", maxLength: 50, nullable: false),
|
||||
Blob = table.Column<byte[]>(type: "BLOB", nullable: true),
|
||||
Blob2 = table.Column<string>(type: "TEXT", nullable: true),
|
||||
CustodianCode = table.Column<string>(type: "TEXT", maxLength: 50, nullable: false),
|
||||
Name = table.Column<string>(type: "TEXT", maxLength: 50, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_CustodianAccount", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_CustodianAccount_Stores_StoreId",
|
||||
column: x => x.StoreId,
|
||||
principalTable: "Stores",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_CustodianAccount_StoreId",
|
||||
table: "CustodianAccount",
|
||||
column: "StoreId");
|
||||
}
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ namespace BTCPayServer.Migrations
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.0");
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.1");
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.APIKeyData", b =>
|
||||
{
|
||||
@ -112,6 +112,9 @@ namespace BTCPayServer.Migrations
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Approved")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<byte[]>("Blob")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
@ -158,6 +161,9 @@ namespace BTCPayServer.Migrations
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("RequiresApproval")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("RequiresEmailConfirmation")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@ -183,40 +189,6 @@ namespace BTCPayServer.Migrations
|
||||
b.ToTable("AspNetUsers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.CustodianAccountData", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<byte[]>("Blob")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<string>("Blob2")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustodianCode")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("StoreId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StoreId");
|
||||
|
||||
b.ToTable("CustodianAccount");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.Fido2Credential", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
@ -275,6 +247,9 @@ namespace BTCPayServer.Migrations
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<decimal?>("Amount")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Archived")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@ -287,6 +262,9 @@ namespace BTCPayServer.Migrations
|
||||
b.Property<DateTimeOffset>("Created")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Currency")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomerEmail")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@ -533,15 +511,27 @@ namespace BTCPayServer.Migrations
|
||||
b.Property<bool>("Accounted")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<decimal?>("Amount")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<byte[]>("Blob")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<string>("Blob2")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("Created")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Currency")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("InvoiceDataId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@ -592,8 +582,8 @@ namespace BTCPayServer.Migrations
|
||||
.HasMaxLength(30)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<byte[]>("Blob")
|
||||
.HasColumnType("BLOB");
|
||||
b.Property<string>("Blob")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("Date")
|
||||
.HasColumnType("TEXT");
|
||||
@ -606,8 +596,8 @@ namespace BTCPayServer.Migrations
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<byte[]>("Proof")
|
||||
.HasColumnType("BLOB");
|
||||
b.Property<string>("Proof")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PullPaymentDataId")
|
||||
.HasColumnType("TEXT");
|
||||
@ -697,8 +687,8 @@ namespace BTCPayServer.Migrations
|
||||
b.Property<bool>("Archived")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<byte[]>("Blob")
|
||||
.HasColumnType("BLOB");
|
||||
b.Property<string>("Blob")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("EndDate")
|
||||
.HasColumnType("TEXT");
|
||||
@ -1213,17 +1203,6 @@ namespace BTCPayServer.Migrations
|
||||
b.Navigation("StoreData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.CustodianAccountData", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")
|
||||
.WithMany("CustodianAccounts")
|
||||
.HasForeignKey("StoreId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("StoreData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.Fido2Credential", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.ApplicationUser", "ApplicationUser")
|
||||
@ -1632,8 +1611,6 @@ namespace BTCPayServer.Migrations
|
||||
|
||||
b.Navigation("Apps");
|
||||
|
||||
b.Navigation("CustodianAccounts");
|
||||
|
||||
b.Navigation("Forms");
|
||||
|
||||
b.Navigation("Invoices");
|
||||
|
@ -1,4 +1,4 @@
|
||||
[
|
||||
[
|
||||
{
|
||||
"name":"Afghan Afghani",
|
||||
"code":"AFN",
|
||||
@ -58,7 +58,7 @@
|
||||
{
|
||||
"name":"Argentine Peso",
|
||||
"code":"ARS",
|
||||
"divisibility":2,
|
||||
"divisibility":0,
|
||||
"symbol":null,
|
||||
"crypto":false
|
||||
},
|
||||
@ -289,7 +289,7 @@
|
||||
{
|
||||
"name":"Colombian Peso",
|
||||
"code":"COP",
|
||||
"divisibility":2,
|
||||
"divisibility":0,
|
||||
"symbol":null,
|
||||
"crypto":false
|
||||
},
|
||||
|
@ -77,7 +77,15 @@ namespace BTCPayServer.Services.Rates
|
||||
continue;
|
||||
try
|
||||
{
|
||||
_CurrencyProviders.TryAdd(new RegionInfo(culture.LCID).ISOCurrencySymbol, culture);
|
||||
var symbol = new RegionInfo(culture.LCID).ISOCurrencySymbol;
|
||||
var c = symbol switch
|
||||
{
|
||||
// ARS and COP are officially 2 digits, but due to depreciation,
|
||||
// nobody really use those anymore. (See https://github.com/btcpayserver/btcpayserver/issues/5708)
|
||||
"ARS" or "COP" => ModifyCurrencyDecimalDigit(culture, 0),
|
||||
_ => culture
|
||||
};
|
||||
_CurrencyProviders.TryAdd(symbol, c);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
@ -91,6 +99,15 @@ namespace BTCPayServer.Services.Rates
|
||||
}
|
||||
}
|
||||
|
||||
private CultureInfo ModifyCurrencyDecimalDigit(CultureInfo culture, int decimals)
|
||||
{
|
||||
var modifiedCulture = new CultureInfo(culture.Name);
|
||||
NumberFormatInfo modifiedNumberFormat = (NumberFormatInfo)modifiedCulture.NumberFormat.Clone();
|
||||
modifiedNumberFormat.CurrencyDecimalDigits = decimals;
|
||||
modifiedCulture.NumberFormat = modifiedNumberFormat;
|
||||
return modifiedCulture;
|
||||
}
|
||||
|
||||
private void AddCurrency(Dictionary<string, IFormatProvider> currencyProviders, string code, int divisibility, string symbol)
|
||||
{
|
||||
var culture = new CultureInfo("en-US");
|
||||
|
40
BTCPayServer.Rating/Providers/BitnobRateProvider.cs
Normal file
40
BTCPayServer.Rating/Providers/BitnobRateProvider.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Rating.Providers
|
||||
{
|
||||
public class BitnobRateProvider : IRateProvider
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
public BitnobRateProvider(HttpClient httpClient)
|
||||
{
|
||||
_httpClient = httpClient ?? new HttpClient();
|
||||
}
|
||||
public RateSourceInfo RateSourceInfo => new("bitnob", "Bitnob", "https://api.bitnob.co/api/v1/rates/bitcoin/price");
|
||||
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
using var response = await _httpClient.GetAsync("https://api.bitnob.co/api/v1/rates/bitcoin/price", cancellationToken);
|
||||
JObject jobj = await response.Content.ReadAsAsync<JObject>(cancellationToken);
|
||||
var dataObject = jobj["data"] as JObject;
|
||||
|
||||
if (dataObject == null)
|
||||
{
|
||||
return Array.Empty<PairRate>();
|
||||
}
|
||||
var pairRates = new List<PairRate>();
|
||||
foreach (var property in dataObject.Properties())
|
||||
{
|
||||
string[] parts = property.Name.Split('_');
|
||||
decimal value = property.Value.Value<decimal>();
|
||||
pairRates.Add(new PairRate(new CurrencyPair("BTC", parts[1]), new BidAsk(value)));
|
||||
}
|
||||
return pairRates.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user