Compare commits
66 Commits
v1.9.0-rc2
...
fix-array-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8290a1606f | ||
|
|
eddd458744 | ||
|
|
439ea20a89 | ||
|
|
cec223c8e7 | ||
|
|
25cb188d00 | ||
|
|
31007a8d96 | ||
|
|
a4fa8db69b | ||
|
|
6193835ea1 | ||
|
|
0c78e9e4ac | ||
|
|
58c409e7fa | ||
|
|
9577eed524 | ||
|
|
76f32cd064 | ||
|
|
92d9c17095 | ||
|
|
b0396df33f | ||
|
|
c17572c76f | ||
|
|
5c91e072a6 | ||
|
|
4991d0f965 | ||
|
|
45b74e1ce5 | ||
|
|
ccb4b9a9ba | ||
|
|
dd635071d6 | ||
|
|
fe1448dfae | ||
|
|
56855bc54d | ||
|
|
b51fa8df5a | ||
|
|
3aa979cb11 | ||
|
|
c95f75bc6c | ||
|
|
b13a636f89 | ||
|
|
8de55cef31 | ||
|
|
cb781f42e3 | ||
|
|
b59292dc24 | ||
|
|
03b793d7e2 | ||
|
|
bee18d1cfb | ||
|
|
d8698181f4 | ||
|
|
47f5d97eaf | ||
|
|
cb3c5e56fd | ||
|
|
39b76c08de | ||
|
|
c3c8cc21ff | ||
|
|
6dba1b6d8b | ||
|
|
381fe70a79 | ||
|
|
feb927c2e4 | ||
|
|
e77bd4c188 | ||
|
|
43436fc49e | ||
|
|
d738f797ec | ||
|
|
b5de97f785 | ||
|
|
b0c1b0895d | ||
|
|
8e60932f81 | ||
|
|
7d14cd74f2 | ||
|
|
717f1610f5 | ||
|
|
f1abe6497f | ||
|
|
046129a57d | ||
|
|
90d300a490 | ||
|
|
a2d506c0db | ||
|
|
58748a24da | ||
|
|
639e8a4a1d | ||
|
|
48ebaf5c5a | ||
|
|
1aaccb1e6b | ||
|
|
f05a7f9f14 | ||
|
|
98ddb348b0 | ||
|
|
a4aa85ebab | ||
|
|
516efe56f4 | ||
|
|
a4d72d5bbc | ||
|
|
24b8ec16f1 | ||
|
|
ac25fef555 | ||
|
|
8302f082a2 | ||
|
|
7546ef7a8e | ||
|
|
f598c70a4f | ||
|
|
422da21de5 |
@@ -1,21 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Pack Test Plugin" type="DotNetProject" factoryName=".NET Project" singleton="false">
|
|
||||||
<option name="EXE_PATH" value="$PROJECT_DIR$/BTCPayServer.PluginPacker/bin/Debug/netcoreapp3.1/BTCPayServer.PluginPacker.dll" />
|
|
||||||
<option name="PROGRAM_PARAMETERS" value="../../../../BTCPayServer.Plugins.Test\bin\Debug\netcoreapp3.1 BTCPayServer.Plugins.Test "../../../../Packed Plugins"" />
|
|
||||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/BTCPayServer.PluginPacker/bin/Debug/netcoreapp3.1" />
|
|
||||||
<option name="PASS_PARENT_ENVS" value="1" />
|
|
||||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
|
||||||
<option name="USE_MONO" value="0" />
|
|
||||||
<option name="RUNTIME_ARGUMENTS" value="" />
|
|
||||||
<option name="PROJECT_PATH" value="$PROJECT_DIR$/BTCPayServer.PluginPacker/BTCPayServer.PluginPacker.csproj" />
|
|
||||||
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
|
||||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
|
||||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
|
||||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
|
||||||
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" />
|
|
||||||
<method v="2">
|
|
||||||
<option name="Build" default="false" projectName="BTCPayServer.Plugins.Test" projectPath="C:\Git\btcpayserver\Plugins\BTCPayServer.Plugins.Test\BTCPayServer.Plugins.Test.csproj" />
|
|
||||||
<option name="Build" />
|
|
||||||
</method>
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -10,7 +10,7 @@ public class CustodianApiException : Exception
|
|||||||
HttpStatus = httpStatus;
|
HttpStatus = httpStatus;
|
||||||
Code = code;
|
Code = code;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CustodianApiException(int httpStatus, string code, string message) : this(httpStatus, code, message, null)
|
public CustodianApiException(int httpStatus, string code, string message) : this(httpStatus, code, message, null)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,10 +52,10 @@ public class Field
|
|||||||
public string HelpText;
|
public string HelpText;
|
||||||
|
|
||||||
[JsonExtensionData] public IDictionary<string, JToken> AdditionalData { get; set; }
|
[JsonExtensionData] public IDictionary<string, JToken> AdditionalData { get; set; }
|
||||||
public List<Field> Fields { get; set; } = new ();
|
public List<Field> Fields { get; set; } = new();
|
||||||
|
|
||||||
// The field is considered "valid" if there are no validation errors
|
// The field is considered "valid" if there are no validation errors
|
||||||
public List<string> ValidationErrors = new ();
|
public List<string> ValidationErrors = new();
|
||||||
|
|
||||||
public virtual bool IsValid()
|
public virtual bool IsValid()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ public class SVGUse : UrlResolutionTagHelper2
|
|||||||
{
|
{
|
||||||
_fileVersionProvider = fileVersionProvider;
|
_fileVersionProvider = fileVersionProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||||
{
|
{
|
||||||
var attr = output.Attributes["href"].Value.ToString();
|
var attr = output.Attributes["href"].Value.ToString();
|
||||||
var symbolIndex = attr!.IndexOf("#", StringComparison.InvariantCulture);
|
var symbolIndex = attr!.IndexOf("#", StringComparison.InvariantCulture);
|
||||||
var start = attr.IndexOf("~", StringComparison.InvariantCulture) + 1;
|
var start = attr.IndexOf("~", StringComparison.InvariantCulture) + 1;
|
||||||
var length = (symbolIndex != -1 ? symbolIndex : attr.Length) - start;
|
var length = (symbolIndex != -1 ? symbolIndex : attr.Length) - start;
|
||||||
var filePath = attr.Substring(start, length);
|
var filePath = attr.Substring(start, length);
|
||||||
if (!string.IsNullOrEmpty(filePath))
|
if (!string.IsNullOrEmpty(filePath))
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ namespace BTCPayServer.Client
|
|||||||
method: HttpMethod.Get), token);
|
method: HttpMethod.Get), token);
|
||||||
return await HandleResponse<AppDataBase>(response);
|
return await HandleResponse<AppDataBase>(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual async Task<AppDataBase[]> GetAllApps(string storeId, CancellationToken token = default)
|
public virtual async Task<AppDataBase[]> GetAllApps(string storeId, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
if (storeId == null)
|
if (storeId == null)
|
||||||
|
|||||||
@@ -80,13 +80,13 @@ namespace BTCPayServer.Client
|
|||||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/trades/quote", queryPayload), token);
|
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/trades/quote", queryPayload), token);
|
||||||
return await HandleResponse<TradeQuoteResponseData>(response);
|
return await HandleResponse<TradeQuoteResponseData>(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual async Task<WithdrawalResponseData> CreateCustodianAccountWithdrawal(string storeId, string accountId, WithdrawRequestData request, CancellationToken token = default)
|
public virtual async Task<WithdrawalResponseData> CreateCustodianAccountWithdrawal(string storeId, string accountId, WithdrawRequestData request, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/withdrawals", bodyPayload: request, method: HttpMethod.Post), token);
|
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/withdrawals", bodyPayload: request, method: HttpMethod.Post), token);
|
||||||
return await HandleResponse<WithdrawalResponseData>(response);
|
return await HandleResponse<WithdrawalResponseData>(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual async Task<WithdrawalSimulationResponseData> SimulateCustodianAccountWithdrawal(string storeId, string accountId, WithdrawRequestData request, CancellationToken token = default)
|
public virtual async Task<WithdrawalSimulationResponseData> SimulateCustodianAccountWithdrawal(string storeId, string accountId, WithdrawRequestData request, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/withdrawals/simulation", bodyPayload: request, method: HttpMethod.Post), token);
|
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/withdrawals/simulation", bodyPayload: request, method: HttpMethod.Post), token);
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ namespace BTCPayServer.Client
|
|||||||
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/invoices", queryPayload), token);
|
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/invoices", queryPayload), token);
|
||||||
return await HandleResponse<LightningInvoiceData[]>(response);
|
return await HandleResponse<LightningInvoiceData[]>(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual async Task<LightningPaymentData[]> GetLightningPayments(string cryptoCode,
|
public virtual async Task<LightningPaymentData[]> GetLightningPayments(string cryptoCode,
|
||||||
bool? includePending = null, long? offsetIndex = null, CancellationToken token = default)
|
bool? includePending = null, long? offsetIndex = null, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ namespace BTCPayServer.Client
|
|||||||
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/invoices", queryPayload), token);
|
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/invoices", queryPayload), token);
|
||||||
return await HandleResponse<LightningInvoiceData[]>(response);
|
return await HandleResponse<LightningInvoiceData[]>(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual async Task<LightningPaymentData[]> GetLightningPayments(string storeId, string cryptoCode,
|
public virtual async Task<LightningPaymentData[]> GetLightningPayments(string storeId, string cryptoCode,
|
||||||
bool? includePending = null, long? offsetIndex = null, CancellationToken token = default)
|
bool? includePending = null, long? offsetIndex = null, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace BTCPayServer.Client
|
|||||||
CancellationToken token = default)
|
CancellationToken token = default)
|
||||||
{
|
{
|
||||||
using var response = await _httpClient.SendAsync(
|
using var response = await _httpClient.SendAsync(
|
||||||
CreateHttpRequest($"api/v1/stores/{storeId}/rates",
|
CreateHttpRequest($"api/v1/stores/{storeId}/rates",
|
||||||
queryPayload: new Dictionary<string, object>() { { "currencyPair", currencyPair } },
|
queryPayload: new Dictionary<string, object>() { { "currencyPair", currencyPair } },
|
||||||
method: HttpMethod.Get),
|
method: HttpMethod.Get),
|
||||||
token);
|
token);
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ namespace BTCPayServer.Client.Models
|
|||||||
public bool? RequiresRefundEmail { get; set; } = null;
|
public bool? RequiresRefundEmail { get; set; } = null;
|
||||||
public string DefaultLanguage { get; set; }
|
public string DefaultLanguage { get; set; }
|
||||||
public CheckoutType? CheckoutType { get; set; }
|
public CheckoutType? CheckoutType { get; set; }
|
||||||
|
public bool? LazyPaymentMethods { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public class InvoiceData : InvoiceDataBase
|
public class InvoiceData : InvoiceDataBase
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ namespace BTCPayServer.Client.Models
|
|||||||
public class LNURLPayPaymentMethodBaseData
|
public class LNURLPayPaymentMethodBaseData
|
||||||
{
|
{
|
||||||
public bool UseBech32Scheme { get; set; }
|
public bool UseBech32Scheme { get; set; }
|
||||||
public bool EnableForStandardInvoices { get; set; }
|
|
||||||
public bool LUD12Enabled { get; set; }
|
public bool LUD12Enabled { get; set; }
|
||||||
|
|
||||||
public LNURLPayPaymentMethodBaseData()
|
public LNURLPayPaymentMethodBaseData()
|
||||||
|
|||||||
@@ -16,12 +16,11 @@ namespace BTCPayServer.Client.Models
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public LNURLPayPaymentMethodData(string cryptoCode, bool enabled, bool useBech32Scheme, bool enableForStandardInvoices)
|
public LNURLPayPaymentMethodData(string cryptoCode, bool enabled, bool useBech32Scheme)
|
||||||
{
|
{
|
||||||
Enabled = enabled;
|
Enabled = enabled;
|
||||||
CryptoCode = cryptoCode;
|
CryptoCode = cryptoCode;
|
||||||
UseBech32Scheme = useBech32Scheme;
|
UseBech32Scheme = useBech32Scheme;
|
||||||
EnableForStandardInvoices = enableForStandardInvoices;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,5 +6,5 @@ public class LightningAddressData
|
|||||||
public string CurrencyCode { get; set; }
|
public string CurrencyCode { get; set; }
|
||||||
public decimal? Min { get; set; }
|
public decimal? Min { get; set; }
|
||||||
public decimal? Max { get; set; }
|
public decimal? Max { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace BTCPayServer.Client.Models
|
|||||||
|
|
||||||
[JsonProperty("BOLT11")]
|
[JsonProperty("BOLT11")]
|
||||||
public string BOLT11 { get; set; }
|
public string BOLT11 { get; set; }
|
||||||
|
|
||||||
public string PaymentHash { get; set; }
|
public string PaymentHash { get; set; }
|
||||||
|
|
||||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||||
@@ -24,7 +24,7 @@ namespace BTCPayServer.Client.Models
|
|||||||
|
|
||||||
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
||||||
public DateTimeOffset? PaidAt { get; set; }
|
public DateTimeOffset? PaidAt { get; set; }
|
||||||
|
|
||||||
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
||||||
public DateTimeOffset ExpiresAt { get; set; }
|
public DateTimeOffset ExpiresAt { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ namespace BTCPayServer.Client.Models
|
|||||||
{
|
{
|
||||||
|
|
||||||
public string ConnectionString { get; set; }
|
public string ConnectionString { get; set; }
|
||||||
public bool DisableBOLT11PaymentOption { get; set; }
|
|
||||||
public LightningNetworkPaymentMethodBaseData()
|
public LightningNetworkPaymentMethodBaseData()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@@ -16,13 +16,12 @@ namespace BTCPayServer.Client.Models
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public LightningNetworkPaymentMethodData(string cryptoCode, string connectionString, bool enabled, string paymentMethod, bool disableBOLT11PaymentOption)
|
public LightningNetworkPaymentMethodData(string cryptoCode, string connectionString, bool enabled, string paymentMethod)
|
||||||
{
|
{
|
||||||
Enabled = enabled;
|
Enabled = enabled;
|
||||||
CryptoCode = cryptoCode;
|
CryptoCode = cryptoCode;
|
||||||
ConnectionString = connectionString;
|
ConnectionString = connectionString;
|
||||||
PaymentMethod = paymentMethod;
|
PaymentMethod = paymentMethod;
|
||||||
DisableBOLT11PaymentOption = disableBOLT11PaymentOption;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string PaymentMethod { get; set; }
|
public string PaymentMethod { get; set; }
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace BTCPayServer.Client.Models;
|
namespace BTCPayServer.Client.Models;
|
||||||
|
|
||||||
@@ -14,9 +14,9 @@ public class WithdrawRequestData
|
|||||||
|
|
||||||
public WithdrawRequestData()
|
public WithdrawRequestData()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public WithdrawRequestData(string paymentMethod, TradeQuantity qty)
|
public WithdrawRequestData(string paymentMethod, TradeQuantity qty)
|
||||||
{
|
{
|
||||||
PaymentMethod = paymentMethod;
|
PaymentMethod = paymentMethod;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace BTCPayServer.Client.Models;
|
|||||||
|
|
||||||
public class WithdrawalResponseData : WithdrawalBaseResponseData
|
public class WithdrawalResponseData : WithdrawalBaseResponseData
|
||||||
{
|
{
|
||||||
|
|
||||||
[JsonConverter(typeof(StringEnumConverter))]
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
public WithdrawalStatus Status { get; }
|
public WithdrawalStatus Status { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -124,14 +124,14 @@ namespace BTCPayServer.Data
|
|||||||
#pragma warning disable CS0612 // Type or member is obsolete
|
#pragma warning disable CS0612 // Type or member is obsolete
|
||||||
WalletTransactionData.OnModelCreating(builder);
|
WalletTransactionData.OnModelCreating(builder);
|
||||||
#pragma warning restore CS0612 // Type or member is obsolete
|
#pragma warning restore CS0612 // Type or member is obsolete
|
||||||
WebhookDeliveryData.OnModelCreating(builder, Database);
|
WebhookDeliveryData.OnModelCreating(builder, Database);
|
||||||
LightningAddressData.OnModelCreating(builder, Database);
|
LightningAddressData.OnModelCreating(builder, Database);
|
||||||
PayoutProcessorData.OnModelCreating(builder, Database);
|
PayoutProcessorData.OnModelCreating(builder, Database);
|
||||||
WebhookData.OnModelCreating(builder, Database);
|
WebhookData.OnModelCreating(builder, Database);
|
||||||
FormData.OnModelCreating(builder, Database);
|
FormData.OnModelCreating(builder, Database);
|
||||||
|
|
||||||
|
|
||||||
if (Database.IsSqlite() && !_designTime)
|
if (Database.IsSqlite() && !_designTime)
|
||||||
{
|
{
|
||||||
// SQLite does not have proper support for DateTimeOffset via Entity Framework Core, see the limitations
|
// SQLite does not have proper support for DateTimeOffset via Entity Framework Core, see the limitations
|
||||||
// here: https://docs.microsoft.com/en-us/ef/core/providers/sqlite/limitations#query-limitations
|
// here: https://docs.microsoft.com/en-us/ef/core/providers/sqlite/limitations#query-limitations
|
||||||
|
|||||||
@@ -13,14 +13,14 @@ public class FormData
|
|||||||
public StoreData Store { get; set; }
|
public StoreData Store { get; set; }
|
||||||
public string Config { get; set; }
|
public string Config { get; set; }
|
||||||
public bool Public { get; set; }
|
public bool Public { get; set; }
|
||||||
|
|
||||||
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
||||||
{
|
{
|
||||||
builder.Entity<FormData>()
|
builder.Entity<FormData>()
|
||||||
.HasOne(o => o.Store)
|
.HasOne(o => o.Store)
|
||||||
.WithMany(o => o.Forms).OnDelete(DeleteBehavior.Cascade);
|
.WithMany(o => o.Forms).OnDelete(DeleteBehavior.Cascade);
|
||||||
builder.Entity<FormData>().HasIndex(o => o.StoreId);
|
builder.Entity<FormData>().HasIndex(o => o.StoreId);
|
||||||
|
|
||||||
if (databaseFacade.IsNpgsql())
|
if (databaseFacade.IsNpgsql())
|
||||||
{
|
{
|
||||||
builder.Entity<FormData>()
|
builder.Entity<FormData>()
|
||||||
|
|||||||
@@ -39,6 +39,6 @@ public class LightningAddressDataBlob
|
|||||||
public string CurrencyCode { get; set; }
|
public string CurrencyCode { get; set; }
|
||||||
public decimal? Min { get; set; }
|
public decimal? Min { get; set; }
|
||||||
public decimal? Max { get; set; }
|
public decimal? Max { get; set; }
|
||||||
|
|
||||||
public JObject InvoiceMetadata { get; set; }
|
public JObject InvoiceMetadata { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ namespace BTCPayServer.Data
|
|||||||
public string Blob2 { get; set; }
|
public string Blob2 { get; set; }
|
||||||
|
|
||||||
|
|
||||||
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
||||||
{
|
{
|
||||||
builder.Entity<NotificationData>()
|
builder.Entity<NotificationData>()
|
||||||
.HasOne(o => o.ApplicationUser)
|
.HasOne(o => o.ApplicationUser)
|
||||||
.WithMany(n => n.Notifications)
|
.WithMany(n => n.Notifications)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -20,7 +20,7 @@ namespace BTCPayServer.Services.Rates
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public RateSourceInfo RateSourceInfo => new RateSourceInfo("NULL","NULL", "https://NULL.NULL");
|
public RateSourceInfo RateSourceInfo => new RateSourceInfo("NULL", "NULL", "https://NULL.NULL");
|
||||||
|
|
||||||
public Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
public Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ namespace BTCPayServer.Services.Rates
|
|||||||
AvailableRateProviders.Add(new(rsi.Id, rsi.DisplayName, rsi.Url, RateSource.Coingecko));
|
AvailableRateProviders.Add(new(rsi.Id, rsi.DisplayName, rsi.Url, RateSource.Coingecko));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AvailableRateProviders.Sort((a,b) => StringComparer.Ordinal.Compare(a.DisplayName, b.DisplayName));
|
AvailableRateProviders.Sort((a, b) => StringComparer.Ordinal.Compare(a.DisplayName, b.DisplayName));
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<AvailableRateProvider> AvailableRateProviders { get; } = new List<AvailableRateProvider>();
|
public List<AvailableRateProvider> AvailableRateProviders { get; } = new List<AvailableRateProvider>();
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ namespace BTCPayServer.Tests
|
|||||||
user.RegisterDerivationScheme(cryptoCode);
|
user.RegisterDerivationScheme(cryptoCode);
|
||||||
user.RegisterDerivationScheme("LTC");
|
user.RegisterDerivationScheme("LTC");
|
||||||
user.RegisterLightningNode(cryptoCode, LightningConnectionType.CLightning);
|
user.RegisterLightningNode(cryptoCode, LightningConnectionType.CLightning);
|
||||||
|
user.SetLNUrl("BTC", false);
|
||||||
var btcNetwork = tester.PayTester.Networks.GetNetwork<BTCPayNetwork>(cryptoCode);
|
var btcNetwork = tester.PayTester.Networks.GetNetwork<BTCPayNetwork>(cryptoCode);
|
||||||
var invoice = await user.BitPay.CreateInvoiceAsync(
|
var invoice = await user.BitPay.CreateInvoiceAsync(
|
||||||
new Invoice
|
new Invoice
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.14" />
|
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.14" />
|
||||||
<PackageReference Include="Selenium.Support" Version="4.1.1" />
|
<PackageReference Include="Selenium.Support" Version="4.1.1" />
|
||||||
<PackageReference Include="Selenium.WebDriver" Version="4.1.1" />
|
<PackageReference Include="Selenium.WebDriver" Version="4.1.1" />
|
||||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="110.0.5481.7700" />
|
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="112.0.5615.4900" />
|
||||||
<PackageReference Include="xunit" Version="2.4.2" />
|
<PackageReference Include="xunit" Version="2.4.2" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ namespace BTCPayServer.Tests
|
|||||||
|
|
||||||
var invoiceId = s.CreateInvoice(defaultPaymentMethod: "BTC_LightningLike");
|
var invoiceId = s.CreateInvoice(defaultPaymentMethod: "BTC_LightningLike");
|
||||||
s.GoToInvoiceCheckout(invoiceId);
|
s.GoToInvoiceCheckout(invoiceId);
|
||||||
Assert.Equal("Bitcoin (Lightning) (BTC)", s.Driver.FindElement(By.ClassName("payment__currencies")).Text);
|
Assert.Equal("Bitcoin (Lightning)", s.Driver.FindElement(By.ClassName("payment__currencies")).Text);
|
||||||
s.Driver.Quit();
|
s.Driver.Quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +187,7 @@ namespace BTCPayServer.Tests
|
|||||||
|
|
||||||
var invoiceId = s.CreateInvoice(10, "USD", "a@g.com");
|
var invoiceId = s.CreateInvoice(10, "USD", "a@g.com");
|
||||||
s.GoToInvoiceCheckout(invoiceId);
|
s.GoToInvoiceCheckout(invoiceId);
|
||||||
Assert.Contains("sats", s.Driver.FindElement(By.ClassName("payment__currencies_noborder")).Text);
|
Assert.Contains("sats", s.Driver.FindElement(By.ClassName("buyerTotalLine")).Text);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(Timeout = TestTimeout)]
|
[Fact(Timeout = TestTimeout)]
|
||||||
|
|||||||
@@ -44,14 +44,6 @@ namespace BTCPayServer.Tests
|
|||||||
s.Driver.FindElement(By.Id("StoreWebsite")).SendKeys(storeUrl);
|
s.Driver.FindElement(By.Id("StoreWebsite")).SendKeys(storeUrl);
|
||||||
s.Driver.FindElement(By.Id("Save")).Click();
|
s.Driver.FindElement(By.Id("Save")).Click();
|
||||||
Assert.Contains("Store successfully updated", s.FindAlertMessage().Text);
|
Assert.Contains("Store successfully updated", s.FindAlertMessage().Text);
|
||||||
|
|
||||||
// Enable LNURL, which we will need for (non-)presence checks throughout this test
|
|
||||||
s.GoToHome();
|
|
||||||
s.GoToLightningSettings();
|
|
||||||
s.Driver.SetCheckbox(By.Id("LNURLEnabled"), true);
|
|
||||||
s.Driver.SetCheckbox(By.Id("LNURLStandardInvoiceEnabled"), true);
|
|
||||||
s.Driver.FindElement(By.Id("save")).Click();
|
|
||||||
Assert.Contains("BTC Lightning settings successfully updated", s.FindAlertMessage().Text);
|
|
||||||
|
|
||||||
s.GoToStore(StoreNavPages.CheckoutAppearance);
|
s.GoToStore(StoreNavPages.CheckoutAppearance);
|
||||||
s.Driver.WaitForAndClick(By.Id("Presets"));
|
s.Driver.WaitForAndClick(By.Id("Presets"));
|
||||||
@@ -79,7 +71,7 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Equal(address, copyAddress);
|
Assert.Equal(address, copyAddress);
|
||||||
Assert.Equal($"bitcoin:{address.ToUpperInvariant()}", qrValue);
|
Assert.Equal($"bitcoin:{address.ToUpperInvariant()}", qrValue);
|
||||||
s.Driver.ElementDoesNotExist(By.Id("Lightning_BTC"));
|
s.Driver.ElementDoesNotExist(By.Id("Lightning_BTC"));
|
||||||
|
|
||||||
// Details should show exchange rate
|
// Details should show exchange rate
|
||||||
s.Driver.ToggleCollapse("PaymentDetails");
|
s.Driver.ToggleCollapse("PaymentDetails");
|
||||||
s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-TotalPrice"));
|
s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-TotalPrice"));
|
||||||
@@ -87,7 +79,7 @@ namespace BTCPayServer.Tests
|
|||||||
s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-AmountDue"));
|
s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-AmountDue"));
|
||||||
Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
|
Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
|
||||||
Assert.Contains("sat/byte", s.Driver.FindElement(By.Id("PaymentDetails-RecommendedFee")).Text);
|
Assert.Contains("sat/byte", s.Driver.FindElement(By.Id("PaymentDetails-RecommendedFee")).Text);
|
||||||
|
|
||||||
// Switch to LNURL
|
// Switch to LNURL
|
||||||
s.Driver.FindElement(By.CssSelector(".payment-method:nth-child(2)")).Click();
|
s.Driver.FindElement(By.CssSelector(".payment-method:nth-child(2)")).Click();
|
||||||
TestUtils.Eventually(() =>
|
TestUtils.Eventually(() =>
|
||||||
@@ -125,7 +117,7 @@ namespace BTCPayServer.Tests
|
|||||||
s.GoToInvoiceCheckout(invoiceId);
|
s.GoToInvoiceCheckout(invoiceId);
|
||||||
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
|
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
|
||||||
Assert.Contains("sats", s.Driver.FindElement(By.Id("AmountDue")).Text);
|
Assert.Contains("sats", s.Driver.FindElement(By.Id("AmountDue")).Text);
|
||||||
|
|
||||||
// Details should not show exchange rate
|
// Details should not show exchange rate
|
||||||
s.Driver.ToggleCollapse("PaymentDetails");
|
s.Driver.ToggleCollapse("PaymentDetails");
|
||||||
s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-ExchangeRate"));
|
s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-ExchangeRate"));
|
||||||
@@ -188,8 +180,10 @@ namespace BTCPayServer.Tests
|
|||||||
});
|
});
|
||||||
|
|
||||||
s.Driver.Navigate().Refresh();
|
s.Driver.Navigate().Refresh();
|
||||||
|
|
||||||
// Pay full amount
|
// Pay full amount
|
||||||
s.PayInvoice();
|
s.PayInvoice();
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
TestUtils.Eventually(() =>
|
TestUtils.Eventually(() =>
|
||||||
{
|
{
|
||||||
@@ -197,8 +191,9 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.True(processingSection.Displayed);
|
Assert.True(processingSection.Displayed);
|
||||||
Assert.Contains("Payment Received", processingSection.Text);
|
Assert.Contains("Payment Received", processingSection.Text);
|
||||||
Assert.Contains("Your payment has been received and is now processing", processingSection.Text);
|
Assert.Contains("Your payment has been received and is now processing", processingSection.Text);
|
||||||
Assert.True(s.Driver.ElementDoesNotExist(By.Id("confetti")));
|
|
||||||
});
|
});
|
||||||
|
s.Driver.FindElement(By.Id("confetti"));
|
||||||
|
|
||||||
// Mine
|
// Mine
|
||||||
s.MineBlockOnInvoiceCheckout();
|
s.MineBlockOnInvoiceCheckout();
|
||||||
TestUtils.Eventually(() =>
|
TestUtils.Eventually(() =>
|
||||||
@@ -206,7 +201,7 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Contains("Mined 1 block",
|
Assert.Contains("Mined 1 block",
|
||||||
s.Driver.WaitForElement(By.Id("CheatSuccessMessage")).Text);
|
s.Driver.WaitForElement(By.Id("CheatSuccessMessage")).Text);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Settled
|
// Settled
|
||||||
TestUtils.Eventually(() =>
|
TestUtils.Eventually(() =>
|
||||||
{
|
{
|
||||||
@@ -244,7 +239,7 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.StartsWith("lnbcrt", copyAddressLightning);
|
Assert.StartsWith("lnbcrt", copyAddressLightning);
|
||||||
Assert.StartsWith($"bitcoin:{address.ToUpperInvariant()}?amount=", qrValue);
|
Assert.StartsWith($"bitcoin:{address.ToUpperInvariant()}?amount=", qrValue);
|
||||||
Assert.Contains("&lightning=LNBCRT", qrValue);
|
Assert.Contains("&lightning=LNBCRT", qrValue);
|
||||||
|
|
||||||
// Check details
|
// Check details
|
||||||
s.Driver.ToggleCollapse("PaymentDetails");
|
s.Driver.ToggleCollapse("PaymentDetails");
|
||||||
Assert.Contains("1 BTC = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
|
Assert.Contains("1 BTC = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
|
||||||
@@ -252,7 +247,7 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text);
|
Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text);
|
||||||
Assert.Contains("BTC", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text);
|
Assert.Contains("BTC", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text);
|
||||||
Assert.Contains("BTC", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text);
|
Assert.Contains("BTC", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text);
|
||||||
|
|
||||||
// Switch to amount displayed in sats
|
// Switch to amount displayed in sats
|
||||||
s.GoToHome();
|
s.GoToHome();
|
||||||
s.GoToStore(StoreNavPages.CheckoutAppearance);
|
s.GoToStore(StoreNavPages.CheckoutAppearance);
|
||||||
@@ -262,7 +257,7 @@ namespace BTCPayServer.Tests
|
|||||||
s.GoToInvoiceCheckout(invoiceId);
|
s.GoToInvoiceCheckout(invoiceId);
|
||||||
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
|
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
|
||||||
Assert.Contains("sats", s.Driver.FindElement(By.Id("AmountDue")).Text);
|
Assert.Contains("sats", s.Driver.FindElement(By.Id("AmountDue")).Text);
|
||||||
|
|
||||||
// Check details
|
// Check details
|
||||||
s.Driver.ToggleCollapse("PaymentDetails");
|
s.Driver.ToggleCollapse("PaymentDetails");
|
||||||
Assert.Contains("1 sat = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
|
Assert.Contains("1 sat = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
|
||||||
@@ -270,7 +265,7 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text);
|
Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text);
|
||||||
Assert.Contains("sats", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text);
|
Assert.Contains("sats", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text);
|
||||||
Assert.Contains("sats", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text);
|
Assert.Contains("sats", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text);
|
||||||
|
|
||||||
// BIP21 with LN as default payment method
|
// BIP21 with LN as default payment method
|
||||||
s.GoToHome();
|
s.GoToHome();
|
||||||
invoiceId = s.CreateInvoice(defaultPaymentMethod: "BTC_LightningLike");
|
invoiceId = s.CreateInvoice(defaultPaymentMethod: "BTC_LightningLike");
|
||||||
@@ -280,7 +275,7 @@ namespace BTCPayServer.Tests
|
|||||||
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
||||||
Assert.StartsWith("bitcoin:", payUrl);
|
Assert.StartsWith("bitcoin:", payUrl);
|
||||||
Assert.Contains("&lightning=lnbcrt", payUrl);
|
Assert.Contains("&lightning=lnbcrt", payUrl);
|
||||||
|
|
||||||
// Check details
|
// Check details
|
||||||
s.Driver.ToggleCollapse("PaymentDetails");
|
s.Driver.ToggleCollapse("PaymentDetails");
|
||||||
Assert.Contains("1 sat = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
|
Assert.Contains("1 sat = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
|
||||||
@@ -293,8 +288,7 @@ namespace BTCPayServer.Tests
|
|||||||
s.GoToHome();
|
s.GoToHome();
|
||||||
s.GoToLightningSettings();
|
s.GoToLightningSettings();
|
||||||
Assert.True(s.Driver.FindElement(By.Id("LNURLEnabled")).Selected);
|
Assert.True(s.Driver.FindElement(By.Id("LNURLEnabled")).Selected);
|
||||||
Assert.True(s.Driver.FindElement(By.Id("LNURLStandardInvoiceEnabled")).Selected);
|
|
||||||
|
|
||||||
// BIP21 with top-up invoice
|
// BIP21 with top-up invoice
|
||||||
invoiceId = s.CreateInvoice(amount: null);
|
invoiceId = s.CreateInvoice(amount: null);
|
||||||
s.GoToInvoiceCheckout(invoiceId);
|
s.GoToInvoiceCheckout(invoiceId);
|
||||||
@@ -312,7 +306,7 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Equal(address, copyAddressOnchain);
|
Assert.Equal(address, copyAddressOnchain);
|
||||||
Assert.StartsWith("lnurl", copyAddressLightning);
|
Assert.StartsWith("lnurl", copyAddressLightning);
|
||||||
Assert.StartsWith($"bitcoin:{address.ToUpperInvariant()}?lightning=LNURL", qrValue);
|
Assert.StartsWith($"bitcoin:{address.ToUpperInvariant()}?lightning=LNURL", qrValue);
|
||||||
|
|
||||||
// Check details
|
// Check details
|
||||||
s.Driver.ToggleCollapse("PaymentDetails");
|
s.Driver.ToggleCollapse("PaymentDetails");
|
||||||
Assert.Contains("1 sat = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
|
Assert.Contains("1 sat = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text);
|
||||||
@@ -331,7 +325,7 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Contains("This invoice will expire in", paymentInfo.Text);
|
Assert.Contains("This invoice will expire in", paymentInfo.Text);
|
||||||
Assert.Contains("00:0", paymentInfo.Text);
|
Assert.Contains("00:0", paymentInfo.Text);
|
||||||
Assert.DoesNotContain("Please send", paymentInfo.Text);
|
Assert.DoesNotContain("Please send", paymentInfo.Text);
|
||||||
|
|
||||||
// Configure countdown timer
|
// Configure countdown timer
|
||||||
s.GoToHome();
|
s.GoToHome();
|
||||||
invoiceId = s.CreateInvoice();
|
invoiceId = s.CreateInvoice();
|
||||||
@@ -343,13 +337,13 @@ namespace BTCPayServer.Tests
|
|||||||
displayExpirationTimer.SendKeys("10");
|
displayExpirationTimer.SendKeys("10");
|
||||||
s.Driver.FindElement(By.Id("Save")).Click();
|
s.Driver.FindElement(By.Id("Save")).Click();
|
||||||
Assert.Contains("Store successfully updated", s.FindAlertMessage().Text);
|
Assert.Contains("Store successfully updated", s.FindAlertMessage().Text);
|
||||||
|
|
||||||
s.GoToInvoiceCheckout(invoiceId);
|
s.GoToInvoiceCheckout(invoiceId);
|
||||||
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
|
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
|
||||||
paymentInfo = s.Driver.FindElement(By.Id("PaymentInfo"));
|
paymentInfo = s.Driver.FindElement(By.Id("PaymentInfo"));
|
||||||
Assert.False(paymentInfo.Displayed);
|
Assert.False(paymentInfo.Displayed);
|
||||||
Assert.DoesNotContain("This invoice will expire in", paymentInfo.Text);
|
Assert.DoesNotContain("This invoice will expire in", paymentInfo.Text);
|
||||||
|
|
||||||
expirySeconds = s.Driver.FindElement(By.Id("ExpirySeconds"));
|
expirySeconds = s.Driver.FindElement(By.Id("ExpirySeconds"));
|
||||||
expirySeconds.Clear();
|
expirySeconds.Clear();
|
||||||
expirySeconds.SendKeys("599");
|
expirySeconds.SendKeys("599");
|
||||||
@@ -359,7 +353,7 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.True(paymentInfo.Displayed);
|
Assert.True(paymentInfo.Displayed);
|
||||||
Assert.Contains("This invoice will expire in", paymentInfo.Text);
|
Assert.Contains("This invoice will expire in", paymentInfo.Text);
|
||||||
Assert.Contains("09:5", paymentInfo.Text);
|
Assert.Contains("09:5", paymentInfo.Text);
|
||||||
|
|
||||||
// Disable LNURL again
|
// Disable LNURL again
|
||||||
s.GoToHome();
|
s.GoToHome();
|
||||||
s.GoToLightningSettings();
|
s.GoToLightningSettings();
|
||||||
@@ -378,7 +372,7 @@ namespace BTCPayServer.Tests
|
|||||||
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
||||||
Assert.StartsWith("bitcoin:", payUrl);
|
Assert.StartsWith("bitcoin:", payUrl);
|
||||||
Assert.Contains("&lightning=lnbcrt", payUrl);
|
Assert.Contains("&lightning=lnbcrt", payUrl);
|
||||||
|
|
||||||
// Language Switch
|
// Language Switch
|
||||||
var languageSelect = new SelectElement(s.Driver.FindElement(By.Id("DefaultLang")));
|
var languageSelect = new SelectElement(s.Driver.FindElement(By.Id("DefaultLang")));
|
||||||
Assert.Equal("English", languageSelect.SelectedOption.Text);
|
Assert.Equal("English", languageSelect.SelectedOption.Text);
|
||||||
@@ -387,9 +381,9 @@ namespace BTCPayServer.Tests
|
|||||||
languageSelect.SelectByText("Deutsch");
|
languageSelect.SelectByText("Deutsch");
|
||||||
Assert.Equal("Details anzeigen", s.Driver.FindElement(By.Id("DetailsToggle")).Text);
|
Assert.Equal("Details anzeigen", s.Driver.FindElement(By.Id("DetailsToggle")).Text);
|
||||||
Assert.Contains("lang=de", s.Driver.Url);
|
Assert.Contains("lang=de", s.Driver.Url);
|
||||||
|
|
||||||
s.Driver.Navigate().Refresh();
|
s.Driver.Navigate().Refresh();
|
||||||
languageSelect = new SelectElement(s.Driver.FindElement(By.Id("DefaultLang")));
|
languageSelect = new SelectElement(s.Driver.WaitForElement(By.Id("DefaultLang")));
|
||||||
Assert.Equal("Deutsch", languageSelect.SelectedOption.Text);
|
Assert.Equal("Deutsch", languageSelect.SelectedOption.Text);
|
||||||
Assert.Equal("Details anzeigen", s.Driver.FindElement(By.Id("DetailsToggle")).Text);
|
Assert.Equal("Details anzeigen", s.Driver.FindElement(By.Id("DetailsToggle")).Text);
|
||||||
languageSelect.SelectByText("English");
|
languageSelect.SelectByText("English");
|
||||||
|
|||||||
@@ -626,7 +626,7 @@ namespace BTCPayServer.Tests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void RoundupCurrenciesCorrectly()
|
public void RoundupCurrenciesCorrectly()
|
||||||
{
|
{
|
||||||
DisplayFormatter displayFormatter = new (CurrencyNameTable.Instance);
|
DisplayFormatter displayFormatter = new(CurrencyNameTable.Instance);
|
||||||
foreach (var test in new[]
|
foreach (var test in new[]
|
||||||
{
|
{
|
||||||
(0.0005m, "0.0005 USD", "USD"), (0.001m, "0.001 USD", "USD"), (0.01m, "0.01 USD", "USD"),
|
(0.0005m, "0.0005 USD", "USD"), (0.001m, "0.001 USD", "USD"), (0.01m, "0.01 USD", "USD"),
|
||||||
@@ -766,7 +766,7 @@ namespace BTCPayServer.Tests
|
|||||||
var root = new Mnemonic(
|
var root = new Mnemonic(
|
||||||
"usage fever hen zero slide mammal silent heavy donate budget pulse say brain thank sausage brand craft about save attract muffin advance illegal cabbage")
|
"usage fever hen zero slide mammal silent heavy donate budget pulse say brain thank sausage brand craft about save attract muffin advance illegal cabbage")
|
||||||
.DeriveExtKey();
|
.DeriveExtKey();
|
||||||
|
|
||||||
// xpub
|
// xpub
|
||||||
var tpub = "tpubD6NzVbkrYhZ4YHNiuTdTmHRmbcPRLfqgyneZFCL1mkzkUBjXriQShxTh9HL34FK2mhieasJVk9EzJrUfkFqRNQBjiXgx3n5BhPkxKBoFmaS";
|
var tpub = "tpubD6NzVbkrYhZ4YHNiuTdTmHRmbcPRLfqgyneZFCL1mkzkUBjXriQShxTh9HL34FK2mhieasJVk9EzJrUfkFqRNQBjiXgx3n5BhPkxKBoFmaS";
|
||||||
Assert.True(DerivationSchemeSettings.TryParseFromWalletFile(tpub, testnet, out var settings, out var error));
|
Assert.True(DerivationSchemeSettings.TryParseFromWalletFile(tpub, testnet, out var settings, out var error));
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ public class FormTests : UnitTestBase
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
form = new Form()
|
form = new Form()
|
||||||
{
|
{
|
||||||
Fields = new List<Field>
|
Fields = new List<Field>
|
||||||
@@ -143,7 +143,7 @@ public class FormTests : UnitTestBase
|
|||||||
{"invoice_item3", new StringValues("updated")},
|
{"invoice_item3", new StringValues("updated")},
|
||||||
{"invoice_test", new StringValues("updated")}
|
{"invoice_test", new StringValues("updated")}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
foreach (var f in form.GetAllFields())
|
foreach (var f in form.GetAllFields())
|
||||||
{
|
{
|
||||||
var field = f.Field;
|
var field = f.Field;
|
||||||
@@ -185,7 +185,7 @@ public class FormTests : UnitTestBase
|
|||||||
form.SetValues(obj);
|
form.SetValues(obj);
|
||||||
obj = service.GetValues(form);
|
obj = service.GetValues(form);
|
||||||
Assert.Null(obj["test"].Value<string>());
|
Assert.Null(obj["test"].Value<string>());
|
||||||
form.SetValues(new JObject{ ["test"] = "hello" });
|
form.SetValues(new JObject { ["test"] = "hello" });
|
||||||
obj = service.GetValues(form);
|
obj = service.GetValues(form);
|
||||||
Assert.Equal("hello", obj["test"].Value<string>());
|
Assert.Equal("hello", obj["test"].Value<string>());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,12 +15,15 @@ using BTCPayServer.Lightning;
|
|||||||
using BTCPayServer.Models.InvoicingModels;
|
using BTCPayServer.Models.InvoicingModels;
|
||||||
using BTCPayServer.Payments;
|
using BTCPayServer.Payments;
|
||||||
using BTCPayServer.Payments.Lightning;
|
using BTCPayServer.Payments.Lightning;
|
||||||
|
using BTCPayServer.PayoutProcessors;
|
||||||
|
using BTCPayServer.PayoutProcessors.OnChain;
|
||||||
using BTCPayServer.Services;
|
using BTCPayServer.Services;
|
||||||
using BTCPayServer.Services.Custodian.Client.MockCustodian;
|
using BTCPayServer.Services.Custodian.Client.MockCustodian;
|
||||||
using BTCPayServer.Services.Notifications;
|
using BTCPayServer.Services.Notifications;
|
||||||
using BTCPayServer.Services.Notifications.Blobs;
|
using BTCPayServer.Services.Notifications.Blobs;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
using NBitpayClient;
|
using NBitpayClient;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@@ -302,7 +305,8 @@ namespace BTCPayServer.Tests
|
|||||||
{
|
{
|
||||||
await client.GetApp("some random ID lol");
|
await client.GetApp("some random ID lol");
|
||||||
});
|
});
|
||||||
await AssertHttpError(404, async () => {
|
await AssertHttpError(404, async () =>
|
||||||
|
{
|
||||||
await client.GetPosApp("some random ID lol");
|
await client.GetPosApp("some random ID lol");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -451,7 +455,7 @@ namespace BTCPayServer.Tests
|
|||||||
// Test creating a crowdfund app
|
// Test creating a crowdfund app
|
||||||
var app = await client.CreateCrowdfundApp(
|
var app = await client.CreateCrowdfundApp(
|
||||||
user.StoreId,
|
user.StoreId,
|
||||||
new CreateCrowdfundAppRequest()
|
new CreateCrowdfundAppRequest()
|
||||||
{
|
{
|
||||||
AppName = "test app from API",
|
AppName = "test app from API",
|
||||||
Title = "test app title"
|
Title = "test app title"
|
||||||
@@ -462,10 +466,12 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Equal("Crowdfund", app.AppType);
|
Assert.Equal("Crowdfund", app.AppType);
|
||||||
|
|
||||||
// Make sure we return a 404 if we try to get an app that doesn't exist
|
// Make sure we return a 404 if we try to get an app that doesn't exist
|
||||||
await AssertHttpError(404, async () => {
|
await AssertHttpError(404, async () =>
|
||||||
|
{
|
||||||
await client.GetApp("some random ID lol");
|
await client.GetApp("some random ID lol");
|
||||||
});
|
});
|
||||||
await AssertHttpError(404, async () => {
|
await AssertHttpError(404, async () =>
|
||||||
|
{
|
||||||
await client.GetCrowdfundApp("some random ID lol");
|
await client.GetCrowdfundApp("some random ID lol");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -488,7 +494,8 @@ namespace BTCPayServer.Tests
|
|||||||
|
|
||||||
// Test deleting the newly created app
|
// Test deleting the newly created app
|
||||||
await client.DeleteApp(retrievedApp.Id);
|
await client.DeleteApp(retrievedApp.Id);
|
||||||
await AssertHttpError(404, async () => {
|
await AssertHttpError(404, async () =>
|
||||||
|
{
|
||||||
await client.GetApp(retrievedApp.Id);
|
await client.GetApp(retrievedApp.Id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -512,8 +519,8 @@ namespace BTCPayServer.Tests
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
var crowdfundApp = await client.CreateCrowdfundApp(user.StoreId, new CreateCrowdfundAppRequest() { AppName = "test app from API" });
|
var crowdfundApp = await client.CreateCrowdfundApp(user.StoreId, new CreateCrowdfundAppRequest() { AppName = "test app from API" });
|
||||||
|
|
||||||
// Create another store and one app on it so we can get all apps from all stores for the user below
|
// Create another store and one app on it so we can get all apps from all stores for the user below
|
||||||
var newStore = await client.CreateStore(new CreateStoreRequest() { Name = "A" });
|
var newStore = await client.CreateStore(new CreateStoreRequest() { Name = "A" });
|
||||||
var newApp = await client.CreateCrowdfundApp(newStore.Id, new CreateCrowdfundAppRequest() { AppName = "new app" });
|
var newApp = await client.CreateCrowdfundApp(newStore.Id, new CreateCrowdfundAppRequest() { AppName = "new app" });
|
||||||
|
|
||||||
@@ -544,7 +551,7 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Equal(crowdfundApp.Name, apps[1].Name);
|
Assert.Equal(crowdfundApp.Name, apps[1].Name);
|
||||||
Assert.Equal(crowdfundApp.StoreId, apps[1].StoreId);
|
Assert.Equal(crowdfundApp.StoreId, apps[1].StoreId);
|
||||||
Assert.Equal(crowdfundApp.AppType, apps[1].AppType);
|
Assert.Equal(crowdfundApp.AppType, apps[1].AppType);
|
||||||
|
|
||||||
Assert.Equal(newApp.Name, apps[2].Name);
|
Assert.Equal(newApp.Name, apps[2].Name);
|
||||||
Assert.Equal(newApp.StoreId, apps[2].StoreId);
|
Assert.Equal(newApp.StoreId, apps[2].StoreId);
|
||||||
Assert.Equal(newApp.AppType, apps[2].AppType);
|
Assert.Equal(newApp.AppType, apps[2].AppType);
|
||||||
@@ -1066,7 +1073,7 @@ namespace BTCPayServer.Tests
|
|||||||
var lnrURLs = await unauthenticated.GetPullPaymentLNURL(test4.Id);
|
var lnrURLs = await unauthenticated.GetPullPaymentLNURL(test4.Id);
|
||||||
Assert.IsType<string>(lnrURLs.LNURLBech32);
|
Assert.IsType<string>(lnrURLs.LNURLBech32);
|
||||||
Assert.IsType<string>(lnrURLs.LNURLUri);
|
Assert.IsType<string>(lnrURLs.LNURLUri);
|
||||||
|
|
||||||
//permission test around auto approved pps and payouts
|
//permission test around auto approved pps and payouts
|
||||||
var nonApproved = await acc.CreateClient(Policies.CanCreateNonApprovedPullPayments);
|
var nonApproved = await acc.CreateClient(Policies.CanCreateNonApprovedPullPayments);
|
||||||
var approved = await acc.CreateClient(Policies.CanCreatePullPayments);
|
var approved = await acc.CreateClient(Policies.CanCreatePullPayments);
|
||||||
@@ -1091,7 +1098,7 @@ namespace BTCPayServer.Tests
|
|||||||
Destination = new Key().GetAddress(ScriptPubKeyType.TaprootBIP86, Network.RegTest).ToString()
|
Destination = new Key().GetAddress(ScriptPubKeyType.TaprootBIP86, Network.RegTest).ToString()
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
var pullPayment = await approved.CreatePullPayment(acc.StoreId, new CreatePullPaymentRequest()
|
var pullPayment = await approved.CreatePullPayment(acc.StoreId, new CreatePullPaymentRequest()
|
||||||
{
|
{
|
||||||
Amount = 100,
|
Amount = 100,
|
||||||
@@ -1100,7 +1107,7 @@ namespace BTCPayServer.Tests
|
|||||||
PaymentMethods = new[] { "BTC" },
|
PaymentMethods = new[] { "BTC" },
|
||||||
AutoApproveClaims = true
|
AutoApproveClaims = true
|
||||||
});
|
});
|
||||||
|
|
||||||
var p = await approved.CreatePayout(acc.StoreId, new CreatePayoutThroughStoreRequest()
|
var p = await approved.CreatePayout(acc.StoreId, new CreatePayoutThroughStoreRequest()
|
||||||
{
|
{
|
||||||
Amount = 100,
|
Amount = 100,
|
||||||
@@ -1256,7 +1263,10 @@ namespace BTCPayServer.Tests
|
|||||||
//update store
|
//update store
|
||||||
Assert.Empty(newStore.PaymentMethodCriteria);
|
Assert.Empty(newStore.PaymentMethodCriteria);
|
||||||
await client.GenerateOnChainWallet(newStore.Id, "BTC", new GenerateOnChainWalletRequest());
|
await client.GenerateOnChainWallet(newStore.Id, "BTC", new GenerateOnChainWalletRequest());
|
||||||
var updatedStore = await client.UpdateStore(newStore.Id, new UpdateStoreRequest() { Name = "B", PaymentMethodCriteria = new List<PaymentMethodCriteriaData>()
|
var updatedStore = await client.UpdateStore(newStore.Id, new UpdateStoreRequest()
|
||||||
|
{
|
||||||
|
Name = "B",
|
||||||
|
PaymentMethodCriteria = new List<PaymentMethodCriteriaData>()
|
||||||
{
|
{
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
@@ -1265,7 +1275,8 @@ namespace BTCPayServer.Tests
|
|||||||
PaymentMethod = "BTC",
|
PaymentMethod = "BTC",
|
||||||
CurrencyCode = "USD"
|
CurrencyCode = "USD"
|
||||||
}
|
}
|
||||||
}});
|
}
|
||||||
|
});
|
||||||
Assert.Equal("B", updatedStore.Name);
|
Assert.Equal("B", updatedStore.Name);
|
||||||
var s = (await client.GetStore(newStore.Id));
|
var s = (await client.GetStore(newStore.Id));
|
||||||
Assert.Equal("B", s.Name);
|
Assert.Equal("B", s.Name);
|
||||||
@@ -1275,9 +1286,9 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.True(pmc.Above);
|
Assert.True(pmc.Above);
|
||||||
Assert.Equal("BTC", pmc.PaymentMethod);
|
Assert.Equal("BTC", pmc.PaymentMethod);
|
||||||
Assert.Equal("USD", pmc.CurrencyCode);
|
Assert.Equal("USD", pmc.CurrencyCode);
|
||||||
updatedStore = await client.UpdateStore(newStore.Id, new UpdateStoreRequest() { Name = "B"});
|
updatedStore = await client.UpdateStore(newStore.Id, new UpdateStoreRequest() { Name = "B" });
|
||||||
Assert.Empty(newStore.PaymentMethodCriteria);
|
Assert.Empty(newStore.PaymentMethodCriteria);
|
||||||
|
|
||||||
//list stores
|
//list stores
|
||||||
var stores = await client.GetStores();
|
var stores = await client.GetStores();
|
||||||
var storeIds = stores.Select(data => data.Id);
|
var storeIds = stores.Select(data => data.Id);
|
||||||
@@ -1466,6 +1477,7 @@ namespace BTCPayServer.Tests
|
|||||||
var newDeliveryId = await clientProfile.RedeliverWebhook(user.StoreId, hook.Id, delivery.Id);
|
var newDeliveryId = await clientProfile.RedeliverWebhook(user.StoreId, hook.Id, delivery.Id);
|
||||||
req = await fakeServer.GetNextRequest();
|
req = await fakeServer.GetNextRequest();
|
||||||
req.Response.StatusCode = 404;
|
req.Response.StatusCode = 404;
|
||||||
|
Assert.StartsWith("BTCPayServer", Assert.Single(req.Request.Headers.UserAgent));
|
||||||
await TestUtils.EventuallyAsync(async () =>
|
await TestUtils.EventuallyAsync(async () =>
|
||||||
{
|
{
|
||||||
// Releasing semaphore several times may help making this test less flaky
|
// Releasing semaphore several times may help making this test less flaky
|
||||||
@@ -2331,10 +2343,10 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.NotNull(merchantInvoice.Id);
|
Assert.NotNull(merchantInvoice.Id);
|
||||||
Assert.NotNull(merchantInvoice.PaymentHash);
|
Assert.NotNull(merchantInvoice.PaymentHash);
|
||||||
Assert.Equal(merchantInvoice.Id, merchantInvoice.PaymentHash);
|
Assert.Equal(merchantInvoice.Id, merchantInvoice.PaymentHash);
|
||||||
|
|
||||||
// The default client is using charge, so we should not be able to query channels
|
// The default client is using charge, so we should not be able to query channels
|
||||||
var chargeClient = await user.CreateClient(Policies.CanUseInternalLightningNode);
|
var chargeClient = await user.CreateClient(Policies.CanUseInternalLightningNode);
|
||||||
|
|
||||||
var info = await chargeClient.GetLightningNodeInfo("BTC");
|
var info = await chargeClient.GetLightningNodeInfo("BTC");
|
||||||
Assert.Single(info.NodeURIs);
|
Assert.Single(info.NodeURIs);
|
||||||
Assert.NotEqual(0, info.BlockHeight);
|
Assert.NotEqual(0, info.BlockHeight);
|
||||||
@@ -2403,7 +2415,7 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.NotNull(payResponse.FeeAmount);
|
Assert.NotNull(payResponse.FeeAmount);
|
||||||
Assert.NotNull(payResponse.TotalAmount);
|
Assert.NotNull(payResponse.TotalAmount);
|
||||||
Assert.NotNull(payResponse.PaymentHash);
|
Assert.NotNull(payResponse.PaymentHash);
|
||||||
|
|
||||||
// check the get invoice response
|
// check the get invoice response
|
||||||
var merchInvoice = await merchantClient.GetLightningInvoice(merchant.StoreId, "BTC", merchantInvoice.Id);
|
var merchInvoice = await merchantClient.GetLightningInvoice(merchant.StoreId, "BTC", merchantInvoice.Id);
|
||||||
Assert.NotNull(merchInvoice);
|
Assert.NotNull(merchInvoice);
|
||||||
@@ -2442,7 +2454,7 @@ namespace BTCPayServer.Tests
|
|||||||
|
|
||||||
// Amount received might be bigger because of internal implementation shit from lightning
|
// Amount received might be bigger because of internal implementation shit from lightning
|
||||||
Assert.True(LightMoney.Satoshis(1000) <= invoice.AmountReceived);
|
Assert.True(LightMoney.Satoshis(1000) <= invoice.AmountReceived);
|
||||||
|
|
||||||
// check payments list for store node
|
// check payments list for store node
|
||||||
var payments = await client.GetLightningPayments(user.StoreId, "BTC");
|
var payments = await client.GetLightningPayments(user.StoreId, "BTC");
|
||||||
Assert.NotEmpty(payments);
|
Assert.NotEmpty(payments);
|
||||||
@@ -2488,7 +2500,7 @@ namespace BTCPayServer.Tests
|
|||||||
var user = tester.NewAccount();
|
var user = tester.NewAccount();
|
||||||
await user.GrantAccessAsync(true);
|
await user.GrantAccessAsync(true);
|
||||||
user.RegisterLightningNode("BTC", LightningConnectionType.CLightning);
|
user.RegisterLightningNode("BTC", LightningConnectionType.CLightning);
|
||||||
|
|
||||||
var client = await user.CreateClient(Policies.Unrestricted);
|
var client = await user.CreateClient(Policies.Unrestricted);
|
||||||
var invoice = await client.CreateInvoice(user.StoreId,
|
var invoice = await client.CreateInvoice(user.StoreId,
|
||||||
new CreateInvoiceRequest
|
new CreateInvoiceRequest
|
||||||
@@ -2503,12 +2515,12 @@ namespace BTCPayServer.Tests
|
|||||||
});
|
});
|
||||||
var pm = Assert.Single(await client.GetInvoicePaymentMethods(user.StoreId, invoice.Id));
|
var pm = Assert.Single(await client.GetInvoicePaymentMethods(user.StoreId, invoice.Id));
|
||||||
Assert.False(pm.AdditionalData.HasValues);
|
Assert.False(pm.AdditionalData.HasValues);
|
||||||
|
|
||||||
var resp = await tester.CustomerLightningD.Pay(pm.Destination);
|
var resp = await tester.CustomerLightningD.Pay(pm.Destination);
|
||||||
Assert.Equal(PayResult.Ok, resp.Result);
|
Assert.Equal(PayResult.Ok, resp.Result);
|
||||||
Assert.NotNull(resp.Details.PaymentHash);
|
Assert.NotNull(resp.Details.PaymentHash);
|
||||||
Assert.NotNull(resp.Details.Preimage);
|
Assert.NotNull(resp.Details.Preimage);
|
||||||
|
|
||||||
pm = Assert.Single(await client.GetInvoicePaymentMethods(user.StoreId, invoice.Id));
|
pm = Assert.Single(await client.GetInvoicePaymentMethods(user.StoreId, invoice.Id));
|
||||||
Assert.True(pm.AdditionalData.HasValues);
|
Assert.True(pm.AdditionalData.HasValues);
|
||||||
Assert.Equal(resp.Details.PaymentHash.ToString(), pm.AdditionalData.GetValue("paymentHash"));
|
Assert.Equal(resp.Details.PaymentHash.ToString(), pm.AdditionalData.GetValue("paymentHash"));
|
||||||
@@ -3193,7 +3205,7 @@ namespace BTCPayServer.Tests
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(Timeout =TestTimeout)]
|
[Fact(Timeout = TestTimeout)]
|
||||||
[Trait("Integration", "Integration")]
|
[Trait("Integration", "Integration")]
|
||||||
public async Task StoreLightningAddressesAPITests()
|
public async Task StoreLightningAddressesAPITests()
|
||||||
{
|
{
|
||||||
@@ -3205,14 +3217,14 @@ namespace BTCPayServer.Tests
|
|||||||
var store = await adminClient.GetStore(admin.StoreId);
|
var store = await adminClient.GetStore(admin.StoreId);
|
||||||
|
|
||||||
Assert.Empty(await adminClient.GetStorePaymentMethods(store.Id));
|
Assert.Empty(await adminClient.GetStorePaymentMethods(store.Id));
|
||||||
var store2 = (await adminClient.CreateStore(new CreateStoreRequest() {Name = "test2"})).Id;
|
var store2 = (await adminClient.CreateStore(new CreateStoreRequest() { Name = "test2" })).Id;
|
||||||
var address1 = Guid.NewGuid().ToString("n").Substring(0, 8);
|
var address1 = Guid.NewGuid().ToString("n").Substring(0, 8);
|
||||||
var address2 = Guid.NewGuid().ToString("n").Substring(0, 8);
|
var address2 = Guid.NewGuid().ToString("n").Substring(0, 8);
|
||||||
|
|
||||||
Assert.Empty(await adminClient.GetStoreLightningAddresses(store.Id));
|
Assert.Empty(await adminClient.GetStoreLightningAddresses(store.Id));
|
||||||
Assert.Empty(await adminClient.GetStoreLightningAddresses(store2));
|
Assert.Empty(await adminClient.GetStoreLightningAddresses(store2));
|
||||||
await adminClient.AddOrUpdateStoreLightningAddress(store.Id, address1, new LightningAddressData());
|
await adminClient.AddOrUpdateStoreLightningAddress(store.Id, address1, new LightningAddressData());
|
||||||
|
|
||||||
await adminClient.AddOrUpdateStoreLightningAddress(store.Id, address1, new LightningAddressData()
|
await adminClient.AddOrUpdateStoreLightningAddress(store.Id, address1, new LightningAddressData()
|
||||||
{
|
{
|
||||||
Max = 1
|
Max = 1
|
||||||
@@ -3221,8 +3233,8 @@ namespace BTCPayServer.Tests
|
|||||||
{
|
{
|
||||||
await adminClient.AddOrUpdateStoreLightningAddress(store2, address1, new LightningAddressData());
|
await adminClient.AddOrUpdateStoreLightningAddress(store2, address1, new LightningAddressData());
|
||||||
});
|
});
|
||||||
Assert.Equal(1,Assert.Single(await adminClient.GetStoreLightningAddresses(store.Id)).Max);
|
Assert.Equal(1, Assert.Single(await adminClient.GetStoreLightningAddresses(store.Id)).Max);
|
||||||
Assert.Empty(await adminClient.GetStoreLightningAddresses(store2));
|
Assert.Empty(await adminClient.GetStoreLightningAddresses(store2));
|
||||||
|
|
||||||
await adminClient.AddOrUpdateStoreLightningAddress(store2, address2, new LightningAddressData());
|
await adminClient.AddOrUpdateStoreLightningAddress(store2, address2, new LightningAddressData());
|
||||||
|
|
||||||
@@ -3233,8 +3245,8 @@ namespace BTCPayServer.Tests
|
|||||||
await adminClient.RemoveStoreLightningAddress(store2, address1);
|
await adminClient.RemoveStoreLightningAddress(store2, address1);
|
||||||
});
|
});
|
||||||
await adminClient.RemoveStoreLightningAddress(store2, address2);
|
await adminClient.RemoveStoreLightningAddress(store2, address2);
|
||||||
|
|
||||||
Assert.Empty(await adminClient.GetStoreLightningAddresses(store2));
|
Assert.Empty(await adminClient.GetStoreLightningAddresses(store2));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(Timeout = 60 * 2 * 1000)]
|
[Fact(Timeout = 60 * 2 * 1000)]
|
||||||
@@ -3400,8 +3412,8 @@ namespace BTCPayServer.Tests
|
|||||||
});
|
});
|
||||||
Assert.Empty(await adminClient.GetStoreLightningAutomatedPayoutProcessors(admin.StoreId, "BTC_LightningNetwork"));
|
Assert.Empty(await adminClient.GetStoreLightningAutomatedPayoutProcessors(admin.StoreId, "BTC_LightningNetwork"));
|
||||||
await adminClient.UpdateStoreLightningAutomatedPayoutProcessors(admin.StoreId, "BTC_LightningNetwork",
|
await adminClient.UpdateStoreLightningAutomatedPayoutProcessors(admin.StoreId, "BTC_LightningNetwork",
|
||||||
new LightningAutomatedPayoutSettings() { IntervalSeconds = TimeSpan.FromSeconds(2) });
|
new LightningAutomatedPayoutSettings() { IntervalSeconds = TimeSpan.FromSeconds(600) });
|
||||||
Assert.Equal(2, Assert.Single(await adminClient.GetStoreLightningAutomatedPayoutProcessors(admin.StoreId, "BTC_LightningNetwork")).IntervalSeconds.TotalSeconds);
|
Assert.Equal(600, Assert.Single(await adminClient.GetStoreLightningAutomatedPayoutProcessors(admin.StoreId, "BTC_LightningNetwork")).IntervalSeconds.TotalSeconds);
|
||||||
await TestUtils.EventuallyAsync(async () =>
|
await TestUtils.EventuallyAsync(async () =>
|
||||||
{
|
{
|
||||||
var payoutC =
|
var payoutC =
|
||||||
@@ -3486,8 +3498,8 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Empty(await adminClient.GetPayoutProcessors(admin.StoreId));
|
Assert.Empty(await adminClient.GetPayoutProcessors(admin.StoreId));
|
||||||
|
|
||||||
await adminClient.UpdateStoreOnChainAutomatedPayoutProcessors(admin.StoreId, "BTC",
|
await adminClient.UpdateStoreOnChainAutomatedPayoutProcessors(admin.StoreId, "BTC",
|
||||||
new OnChainAutomatedPayoutSettings() { IntervalSeconds = TimeSpan.FromSeconds(100000) });
|
new OnChainAutomatedPayoutSettings() { IntervalSeconds = TimeSpan.FromSeconds(3600) });
|
||||||
Assert.Equal(100000, Assert.Single(await adminClient.GetStoreOnChainAutomatedPayoutProcessors(admin.StoreId, "BTC")).IntervalSeconds.TotalSeconds);
|
Assert.Equal(3600, Assert.Single(await adminClient.GetStoreOnChainAutomatedPayoutProcessors(admin.StoreId, "BTC")).IntervalSeconds.TotalSeconds);
|
||||||
|
|
||||||
var tpGen = Assert.Single(await adminClient.GetPayoutProcessors(admin.StoreId));
|
var tpGen = Assert.Single(await adminClient.GetPayoutProcessors(admin.StoreId));
|
||||||
Assert.Equal("BTC", Assert.Single(tpGen.PaymentMethods));
|
Assert.Equal("BTC", Assert.Single(tpGen.PaymentMethods));
|
||||||
@@ -3500,8 +3512,10 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Empty(await adminClient.GetStoreOnChainAutomatedPayoutProcessors(admin.StoreId, "BTC"));
|
Assert.Empty(await adminClient.GetStoreOnChainAutomatedPayoutProcessors(admin.StoreId, "BTC"));
|
||||||
Assert.Empty(await adminClient.GetPayoutProcessors(admin.StoreId));
|
Assert.Empty(await adminClient.GetPayoutProcessors(admin.StoreId));
|
||||||
|
|
||||||
|
// Send just enough money to cover the smallest of the payouts.
|
||||||
|
var fee = (await tester.PayTester.GetService<IFeeProviderFactory>().CreateFeeProvider(tester.DefaultNetwork).GetFeeRateAsync(100)).GetFee(150);
|
||||||
await tester.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create((await adminClient.GetOnChainWalletReceiveAddress(admin.StoreId, "BTC", true)).Address,
|
await tester.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create((await adminClient.GetOnChainWalletReceiveAddress(admin.StoreId, "BTC", true)).Address,
|
||||||
tester.ExplorerClient.Network.NBitcoinNetwork), Money.Coins(0.000012m));
|
tester.ExplorerClient.Network.NBitcoinNetwork), Money.Coins(0.00001m) + fee);
|
||||||
await tester.ExplorerNode.GenerateAsync(1);
|
await tester.ExplorerNode.GenerateAsync(1);
|
||||||
await TestUtils.EventuallyAsync(async () =>
|
await TestUtils.EventuallyAsync(async () =>
|
||||||
{
|
{
|
||||||
@@ -3511,8 +3525,9 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Equal(3, payouts.Length);
|
Assert.Equal(3, payouts.Length);
|
||||||
});
|
});
|
||||||
await adminClient.UpdateStoreOnChainAutomatedPayoutProcessors(admin.StoreId, "BTC",
|
await adminClient.UpdateStoreOnChainAutomatedPayoutProcessors(admin.StoreId, "BTC",
|
||||||
new OnChainAutomatedPayoutSettings() { IntervalSeconds = TimeSpan.FromSeconds(5) });
|
new OnChainAutomatedPayoutSettings() { IntervalSeconds = TimeSpan.FromSeconds(600), FeeBlockTarget = 1000 });
|
||||||
Assert.Equal(5, Assert.Single(await adminClient.GetStoreOnChainAutomatedPayoutProcessors(admin.StoreId, "BTC")).IntervalSeconds.TotalSeconds);
|
Assert.Equal(600, Assert.Single(await adminClient.GetStoreOnChainAutomatedPayoutProcessors(admin.StoreId, "BTC")).IntervalSeconds.TotalSeconds);
|
||||||
|
|
||||||
await TestUtils.EventuallyAsync(async () =>
|
await TestUtils.EventuallyAsync(async () =>
|
||||||
{
|
{
|
||||||
Assert.Equal(2, (await adminClient.ShowOnChainWalletTransactions(admin.StoreId, "BTC")).Count());
|
Assert.Equal(2, (await adminClient.ShowOnChainWalletTransactions(admin.StoreId, "BTC")).Count());
|
||||||
@@ -3520,8 +3535,10 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Single(payouts.Where(data => data.State == PayoutState.InProgress));
|
Assert.Single(payouts.Where(data => data.State == PayoutState.InProgress));
|
||||||
});
|
});
|
||||||
|
|
||||||
await tester.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create((await adminClient.GetOnChainWalletReceiveAddress(admin.StoreId, "BTC", true)).Address,
|
var txid = await tester.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create((await adminClient.GetOnChainWalletReceiveAddress(admin.StoreId, "BTC", true)).Address,
|
||||||
tester.ExplorerClient.Network.NBitcoinNetwork), Money.Coins(0.01m));
|
tester.ExplorerClient.Network.NBitcoinNetwork), Money.Coins(0.01m) + fee);
|
||||||
|
await tester.WaitForEvent<NewOnChainTransactionEvent>(null, correctEvent: ev => ev.NewTransactionEvent.TransactionData.TransactionHash == txid);
|
||||||
|
await tester.PayTester.GetService<PayoutProcessorService>().Restart(new PayoutProcessorService.PayoutProcessorQuery(admin.StoreId, "BTC"));
|
||||||
await TestUtils.EventuallyAsync(async () =>
|
await TestUtils.EventuallyAsync(async () =>
|
||||||
{
|
{
|
||||||
Assert.Equal(4, (await adminClient.ShowOnChainWalletTransactions(admin.StoreId, "BTC")).Count());
|
Assert.Equal(4, (await adminClient.ShowOnChainWalletTransactions(admin.StoreId, "BTC")).Count());
|
||||||
@@ -3706,7 +3723,7 @@ namespace BTCPayServer.Tests
|
|||||||
|
|
||||||
Assert.Equal(0.9m,
|
Assert.Equal(0.9m,
|
||||||
Assert.Single(await clientBasic.GetStoreRates(user.StoreId, new[] { "BTC_XYZ" })).Rate);
|
Assert.Single(await clientBasic.GetStoreRates(user.StoreId, new[] { "BTC_XYZ" })).Rate);
|
||||||
|
|
||||||
config = await clientBasic.GetStoreRateConfiguration(user.StoreId);
|
config = await clientBasic.GetStoreRateConfiguration(user.StoreId);
|
||||||
Assert.NotNull(config);
|
Assert.NotNull(config);
|
||||||
Assert.NotNull(config.EffectiveScript);
|
Assert.NotNull(config.EffectiveScript);
|
||||||
@@ -3940,7 +3957,7 @@ clientBasic.PreviewUpdateStoreRateConfiguration(user.StoreId, new StoreRateConfi
|
|||||||
var withdrawalClient = await admin.CreateClient(Policies.CanWithdrawFromCustodianAccounts);
|
var withdrawalClient = await admin.CreateClient(Policies.CanWithdrawFromCustodianAccounts);
|
||||||
var depositClient = await admin.CreateClient(Policies.CanDepositToCustodianAccounts);
|
var depositClient = await admin.CreateClient(Policies.CanDepositToCustodianAccounts);
|
||||||
var tradeClient = await admin.CreateClient(Policies.CanTradeCustodianAccount);
|
var tradeClient = await admin.CreateClient(Policies.CanTradeCustodianAccount);
|
||||||
|
|
||||||
var store = await adminClient.GetStore(admin.StoreId);
|
var store = await adminClient.GetStore(admin.StoreId);
|
||||||
var storeId = store.Id;
|
var storeId = store.Id;
|
||||||
|
|
||||||
@@ -3981,19 +3998,19 @@ clientBasic.PreviewUpdateStoreRateConfiguration(user.StoreId, new StoreRateConfi
|
|||||||
|
|
||||||
// Test: GetDepositAddress, unauth
|
// Test: GetDepositAddress, unauth
|
||||||
await AssertHttpError(401, async () => await unauthClient.GetCustodianAccountDepositAddress(storeId, accountId, MockCustodian.DepositPaymentMethod));
|
await AssertHttpError(401, async () => await unauthClient.GetCustodianAccountDepositAddress(storeId, accountId, MockCustodian.DepositPaymentMethod));
|
||||||
|
|
||||||
// Test: GetDepositAddress, auth, but wrong permission
|
// Test: GetDepositAddress, auth, but wrong permission
|
||||||
await AssertHttpError(403, async () => await managerClient.GetCustodianAccountDepositAddress(storeId, accountId, MockCustodian.DepositPaymentMethod));
|
await AssertHttpError(403, async () => await managerClient.GetCustodianAccountDepositAddress(storeId, accountId, MockCustodian.DepositPaymentMethod));
|
||||||
|
|
||||||
// Test: GetDepositAddress, wrong payment method
|
// Test: GetDepositAddress, wrong payment method
|
||||||
await AssertApiError( 400, "unsupported-payment-method", async () => await depositClient.GetCustodianAccountDepositAddress(storeId, accountId, "WRONG-PaymentMethod"));
|
await AssertApiError(400, "unsupported-payment-method", async () => await depositClient.GetCustodianAccountDepositAddress(storeId, accountId, "WRONG-PaymentMethod"));
|
||||||
|
|
||||||
// Test: GetDepositAddress, wrong store ID
|
// Test: GetDepositAddress, wrong store ID
|
||||||
await AssertHttpError(403, async () => await depositClient.GetCustodianAccountDepositAddress("WRONG-STORE", accountId, MockCustodian.DepositPaymentMethod));
|
await AssertHttpError(403, async () => await depositClient.GetCustodianAccountDepositAddress("WRONG-STORE", accountId, MockCustodian.DepositPaymentMethod));
|
||||||
|
|
||||||
// Test: GetDepositAddress, wrong account ID
|
// Test: GetDepositAddress, wrong account ID
|
||||||
await AssertHttpError(404, async () => await depositClient.GetCustodianAccountDepositAddress(storeId, "WRONG-ACCOUNT-ID", MockCustodian.DepositPaymentMethod));
|
await AssertHttpError(404, async () => await depositClient.GetCustodianAccountDepositAddress(storeId, "WRONG-ACCOUNT-ID", MockCustodian.DepositPaymentMethod));
|
||||||
|
|
||||||
// Test: GetDepositAddress, correct payment method
|
// Test: GetDepositAddress, correct payment method
|
||||||
var depositAddress = await depositClient.GetCustodianAccountDepositAddress(storeId, accountId, MockCustodian.DepositPaymentMethod);
|
var depositAddress = await depositClient.GetCustodianAccountDepositAddress(storeId, accountId, MockCustodian.DepositPaymentMethod);
|
||||||
Assert.NotNull(depositAddress);
|
Assert.NotNull(depositAddress);
|
||||||
@@ -4001,7 +4018,7 @@ clientBasic.PreviewUpdateStoreRateConfiguration(user.StoreId, new StoreRateConfi
|
|||||||
|
|
||||||
|
|
||||||
// Test: Trade, unauth
|
// Test: Trade, unauth
|
||||||
var tradeRequest = new TradeRequestData { FromAsset = MockCustodian.TradeFromAsset, ToAsset = MockCustodian.TradeToAsset, Qty = new TradeQuantity(MockCustodian.TradeQtyBought, TradeQuantity.ValueType.Exact)};
|
var tradeRequest = new TradeRequestData { FromAsset = MockCustodian.TradeFromAsset, ToAsset = MockCustodian.TradeToAsset, Qty = new TradeQuantity(MockCustodian.TradeQtyBought, TradeQuantity.ValueType.Exact) };
|
||||||
await AssertHttpError(401, async () => await unauthClient.MarketTradeCustodianAccountAsset(storeId, accountId, tradeRequest));
|
await AssertHttpError(401, async () => await unauthClient.MarketTradeCustodianAccountAsset(storeId, accountId, tradeRequest));
|
||||||
|
|
||||||
// Test: Trade, auth, but wrong permission
|
// Test: Trade, auth, but wrong permission
|
||||||
@@ -4049,11 +4066,11 @@ clientBasic.PreviewUpdateStoreRateConfiguration(user.StoreId, new StoreRateConfi
|
|||||||
|
|
||||||
|
|
||||||
// Test: GetTradeQuote, unauth
|
// Test: GetTradeQuote, unauth
|
||||||
await AssertHttpError(401, async () => await unauthClient.GetCustodianAccountTradeQuote(storeId, accountId, MockCustodian.TradeFromAsset, MockCustodian.TradeToAsset));
|
await AssertHttpError(401, async () => await unauthClient.GetCustodianAccountTradeQuote(storeId, accountId, MockCustodian.TradeFromAsset, MockCustodian.TradeToAsset));
|
||||||
|
|
||||||
// Test: GetTradeQuote, auth, but wrong permission
|
// Test: GetTradeQuote, auth, but wrong permission
|
||||||
await AssertHttpError(403, async () => await managerClient.GetCustodianAccountTradeQuote(storeId, accountId, MockCustodian.TradeFromAsset, MockCustodian.TradeToAsset));
|
await AssertHttpError(403, async () => await managerClient.GetCustodianAccountTradeQuote(storeId, accountId, MockCustodian.TradeFromAsset, MockCustodian.TradeToAsset));
|
||||||
|
|
||||||
// Test: GetTradeQuote, auth, correct permission
|
// Test: GetTradeQuote, auth, correct permission
|
||||||
var tradeQuote = await tradeClient.GetCustodianAccountTradeQuote(storeId, accountId, MockCustodian.TradeFromAsset, MockCustodian.TradeToAsset);
|
var tradeQuote = await tradeClient.GetCustodianAccountTradeQuote(storeId, accountId, MockCustodian.TradeFromAsset, MockCustodian.TradeToAsset);
|
||||||
Assert.NotNull(tradeQuote);
|
Assert.NotNull(tradeQuote);
|
||||||
@@ -4063,28 +4080,28 @@ clientBasic.PreviewUpdateStoreRateConfiguration(user.StoreId, new StoreRateConfi
|
|||||||
Assert.Equal(MockCustodian.BtcPriceInEuro, tradeQuote.Ask);
|
Assert.Equal(MockCustodian.BtcPriceInEuro, tradeQuote.Ask);
|
||||||
|
|
||||||
// Test: GetTradeQuote, SATS
|
// Test: GetTradeQuote, SATS
|
||||||
await AssertApiError(400, "use-asset-synonym", async () => await tradeClient.GetCustodianAccountTradeQuote(storeId, accountId, MockCustodian.TradeFromAsset, "SATS"));
|
await AssertApiError(400, "use-asset-synonym", async () => await tradeClient.GetCustodianAccountTradeQuote(storeId, accountId, MockCustodian.TradeFromAsset, "SATS"));
|
||||||
|
|
||||||
// Test: GetTradeQuote, wrong asset
|
// Test: GetTradeQuote, wrong asset
|
||||||
await AssertHttpError(404, async () => await tradeClient.GetCustodianAccountTradeQuote(storeId, accountId, "WRONG-ASSET", MockCustodian.TradeToAsset));
|
await AssertHttpError(404, async () => await tradeClient.GetCustodianAccountTradeQuote(storeId, accountId, "WRONG-ASSET", MockCustodian.TradeToAsset));
|
||||||
await AssertHttpError(404, async () => await tradeClient.GetCustodianAccountTradeQuote(storeId, accountId, MockCustodian.TradeFromAsset , "WRONG-ASSET"));
|
await AssertHttpError(404, async () => await tradeClient.GetCustodianAccountTradeQuote(storeId, accountId, MockCustodian.TradeFromAsset, "WRONG-ASSET"));
|
||||||
|
|
||||||
// Test: wrong account ID
|
// Test: wrong account ID
|
||||||
await AssertHttpError(404, async () => await tradeClient.GetCustodianAccountTradeQuote(storeId, "WRONG-ACCOUNT-ID", MockCustodian.TradeFromAsset, MockCustodian.TradeToAsset));
|
await AssertHttpError(404, async () => await tradeClient.GetCustodianAccountTradeQuote(storeId, "WRONG-ACCOUNT-ID", MockCustodian.TradeFromAsset, MockCustodian.TradeToAsset));
|
||||||
|
|
||||||
// Test: wrong store ID
|
// Test: wrong store ID
|
||||||
await AssertHttpError(403, async () => await tradeClient.GetCustodianAccountTradeQuote("WRONG-STORE-ID", accountId, MockCustodian.TradeFromAsset, MockCustodian.TradeToAsset));
|
await AssertHttpError(403, async () => await tradeClient.GetCustodianAccountTradeQuote("WRONG-STORE-ID", accountId, MockCustodian.TradeFromAsset, MockCustodian.TradeToAsset));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Test: GetTradeInfo, unauth
|
// Test: GetTradeInfo, unauth
|
||||||
await AssertHttpError(401, async () => await unauthClient.GetCustodianAccountTradeInfo(storeId, accountId, MockCustodian.TradeId));
|
await AssertHttpError(401, async () => await unauthClient.GetCustodianAccountTradeInfo(storeId, accountId, MockCustodian.TradeId));
|
||||||
|
|
||||||
// Test: GetTradeInfo, auth, but wrong permission
|
// Test: GetTradeInfo, auth, but wrong permission
|
||||||
await AssertHttpError(403, async () => await managerClient.GetCustodianAccountTradeInfo(storeId, accountId, MockCustodian.TradeId));
|
await AssertHttpError(403, async () => await managerClient.GetCustodianAccountTradeInfo(storeId, accountId, MockCustodian.TradeId));
|
||||||
|
|
||||||
// Test: GetTradeInfo, auth, correct permission
|
// Test: GetTradeInfo, auth, correct permission
|
||||||
var tradeResult = await tradeClient.GetCustodianAccountTradeInfo(storeId, accountId, MockCustodian.TradeId);
|
var tradeResult = await tradeClient.GetCustodianAccountTradeInfo(storeId, accountId, MockCustodian.TradeId);
|
||||||
Assert.NotNull(tradeResult);
|
Assert.NotNull(tradeResult);
|
||||||
@@ -4107,36 +4124,36 @@ clientBasic.PreviewUpdateStoreRateConfiguration(user.StoreId, new StoreRateConfi
|
|||||||
|
|
||||||
// Test: GetTradeInfo, wrong trade ID
|
// Test: GetTradeInfo, wrong trade ID
|
||||||
await AssertHttpError(404, async () => await tradeClient.GetCustodianAccountTradeInfo(storeId, accountId, "WRONG-TRADE-ID"));
|
await AssertHttpError(404, async () => await tradeClient.GetCustodianAccountTradeInfo(storeId, accountId, "WRONG-TRADE-ID"));
|
||||||
|
|
||||||
// Test: wrong account ID
|
// Test: wrong account ID
|
||||||
await AssertHttpError(404, async () => await tradeClient.GetCustodianAccountTradeInfo(storeId, "WRONG-ACCOUNT-ID", MockCustodian.TradeId));
|
await AssertHttpError(404, async () => await tradeClient.GetCustodianAccountTradeInfo(storeId, "WRONG-ACCOUNT-ID", MockCustodian.TradeId));
|
||||||
|
|
||||||
// Test: wrong store ID
|
// Test: wrong store ID
|
||||||
await AssertHttpError(403, async () => await tradeClient.GetCustodianAccountTradeInfo("WRONG-STORE-ID", accountId, MockCustodian.TradeId));
|
await AssertHttpError(403, async () => await tradeClient.GetCustodianAccountTradeInfo("WRONG-STORE-ID", accountId, MockCustodian.TradeId));
|
||||||
|
|
||||||
var qty = new TradeQuantity(MockCustodian.WithdrawalAmount, TradeQuantity.ValueType.Exact);
|
var qty = new TradeQuantity(MockCustodian.WithdrawalAmount, TradeQuantity.ValueType.Exact);
|
||||||
// Test: SimulateWithdrawal, unauth
|
// Test: SimulateWithdrawal, unauth
|
||||||
var simulateWithdrawalRequest = new WithdrawRequestData(MockCustodian.WithdrawalPaymentMethod, qty);
|
var simulateWithdrawalRequest = new WithdrawRequestData(MockCustodian.WithdrawalPaymentMethod, qty);
|
||||||
await AssertHttpError(401, async () => await unauthClient.SimulateCustodianAccountWithdrawal(storeId, accountId, simulateWithdrawalRequest));
|
await AssertHttpError(401, async () => await unauthClient.SimulateCustodianAccountWithdrawal(storeId, accountId, simulateWithdrawalRequest));
|
||||||
|
|
||||||
// Test: SimulateWithdrawal, auth, but wrong permission
|
// Test: SimulateWithdrawal, auth, but wrong permission
|
||||||
await AssertHttpError(403, async () => await managerClient.SimulateCustodianAccountWithdrawal(storeId, accountId, simulateWithdrawalRequest));
|
await AssertHttpError(403, async () => await managerClient.SimulateCustodianAccountWithdrawal(storeId, accountId, simulateWithdrawalRequest));
|
||||||
|
|
||||||
// Test: SimulateWithdrawal, correct payment method, correct amount
|
// Test: SimulateWithdrawal, correct payment method, correct amount
|
||||||
var simulateWithdrawResponse = await withdrawalClient.SimulateCustodianAccountWithdrawal(storeId, accountId, simulateWithdrawalRequest);
|
var simulateWithdrawResponse = await withdrawalClient.SimulateCustodianAccountWithdrawal(storeId, accountId, simulateWithdrawalRequest);
|
||||||
AssertMockWithdrawal(simulateWithdrawResponse, custodianAccountData);
|
AssertMockWithdrawal(simulateWithdrawResponse, custodianAccountData);
|
||||||
|
|
||||||
// Test: SimulateWithdrawal, wrong payment method
|
// Test: SimulateWithdrawal, wrong payment method
|
||||||
var wrongPaymentMethodSimulateWithdrawalRequest = new WithdrawRequestData("WRONG-PAYMENT-METHOD", qty);
|
var wrongPaymentMethodSimulateWithdrawalRequest = new WithdrawRequestData("WRONG-PAYMENT-METHOD", qty);
|
||||||
await AssertApiError( 400, "unsupported-payment-method", async () => await withdrawalClient.SimulateCustodianAccountWithdrawal(storeId, accountId, wrongPaymentMethodSimulateWithdrawalRequest));
|
await AssertApiError(400, "unsupported-payment-method", async () => await withdrawalClient.SimulateCustodianAccountWithdrawal(storeId, accountId, wrongPaymentMethodSimulateWithdrawalRequest));
|
||||||
|
|
||||||
// Test: SimulateWithdrawal, wrong account ID
|
// Test: SimulateWithdrawal, wrong account ID
|
||||||
await AssertHttpError(404, async () => await withdrawalClient.SimulateCustodianAccountWithdrawal(storeId, "WRONG-ACCOUNT-ID", simulateWithdrawalRequest));
|
await AssertHttpError(404, async () => await withdrawalClient.SimulateCustodianAccountWithdrawal(storeId, "WRONG-ACCOUNT-ID", simulateWithdrawalRequest));
|
||||||
|
|
||||||
// Test: SimulateWithdrawal, wrong store ID
|
// Test: SimulateWithdrawal, wrong store ID
|
||||||
// TODO it is wierd that 403 is considered normal, but it is like this for all calls where the store is wrong... I'd have preferred a 404 error, because the store cannot be found.
|
// TODO it is wierd that 403 is considered normal, but it is like this for all calls where the store is wrong... I'd have preferred a 404 error, because the store cannot be found.
|
||||||
await AssertHttpError(403, async () => await withdrawalClient.SimulateCustodianAccountWithdrawal( "WRONG-STORE-ID",accountId, simulateWithdrawalRequest));
|
await AssertHttpError(403, async () => await withdrawalClient.SimulateCustodianAccountWithdrawal("WRONG-STORE-ID", accountId, simulateWithdrawalRequest));
|
||||||
|
|
||||||
// Test: SimulateWithdrawal, correct payment method, wrong amount
|
// Test: SimulateWithdrawal, correct payment method, wrong amount
|
||||||
var wrongAmountSimulateWithdrawalRequest = new WithdrawRequestData(MockCustodian.WithdrawalPaymentMethod, TradeQuantity.Parse("0.666"));
|
var wrongAmountSimulateWithdrawalRequest = new WithdrawRequestData(MockCustodian.WithdrawalPaymentMethod, TradeQuantity.Parse("0.666"));
|
||||||
await AssertHttpError(400, async () => await withdrawalClient.SimulateCustodianAccountWithdrawal(storeId, accountId, wrongAmountSimulateWithdrawalRequest));
|
await AssertHttpError(400, async () => await withdrawalClient.SimulateCustodianAccountWithdrawal(storeId, accountId, wrongAmountSimulateWithdrawalRequest));
|
||||||
@@ -4145,53 +4162,53 @@ clientBasic.PreviewUpdateStoreRateConfiguration(user.StoreId, new StoreRateConfi
|
|||||||
var createWithdrawalRequest = new WithdrawRequestData(MockCustodian.WithdrawalPaymentMethod, qty);
|
var createWithdrawalRequest = new WithdrawRequestData(MockCustodian.WithdrawalPaymentMethod, qty);
|
||||||
var createWithdrawalRequestPercentage = new WithdrawRequestData(MockCustodian.WithdrawalPaymentMethod, qty);
|
var createWithdrawalRequestPercentage = new WithdrawRequestData(MockCustodian.WithdrawalPaymentMethod, qty);
|
||||||
await AssertHttpError(401, async () => await unauthClient.CreateCustodianAccountWithdrawal(storeId, accountId, createWithdrawalRequest));
|
await AssertHttpError(401, async () => await unauthClient.CreateCustodianAccountWithdrawal(storeId, accountId, createWithdrawalRequest));
|
||||||
|
|
||||||
// Test: CreateWithdrawal, auth, but wrong permission
|
// Test: CreateWithdrawal, auth, but wrong permission
|
||||||
await AssertHttpError(403, async () => await managerClient.CreateCustodianAccountWithdrawal(storeId, accountId, createWithdrawalRequest));
|
await AssertHttpError(403, async () => await managerClient.CreateCustodianAccountWithdrawal(storeId, accountId, createWithdrawalRequest));
|
||||||
|
|
||||||
// Test: CreateWithdrawal, correct payment method, correct amount
|
// Test: CreateWithdrawal, correct payment method, correct amount
|
||||||
var withdrawResponse = await withdrawalClient.CreateCustodianAccountWithdrawal(storeId, accountId, createWithdrawalRequest);
|
var withdrawResponse = await withdrawalClient.CreateCustodianAccountWithdrawal(storeId, accountId, createWithdrawalRequest);
|
||||||
AssertMockWithdrawal(withdrawResponse, custodianAccountData);
|
AssertMockWithdrawal(withdrawResponse, custodianAccountData);
|
||||||
|
|
||||||
// Test: CreateWithdrawal, correct payment method, correct amount, but as a percentage
|
// Test: CreateWithdrawal, correct payment method, correct amount, but as a percentage
|
||||||
var withdrawWithPercentageResponse = await withdrawalClient.CreateCustodianAccountWithdrawal(storeId, accountId, createWithdrawalRequestPercentage);
|
var withdrawWithPercentageResponse = await withdrawalClient.CreateCustodianAccountWithdrawal(storeId, accountId, createWithdrawalRequestPercentage);
|
||||||
AssertMockWithdrawal(withdrawWithPercentageResponse, custodianAccountData);
|
AssertMockWithdrawal(withdrawWithPercentageResponse, custodianAccountData);
|
||||||
|
|
||||||
// Test: CreateWithdrawal, wrong payment method
|
// Test: CreateWithdrawal, wrong payment method
|
||||||
var wrongPaymentMethodCreateWithdrawalRequest = new WithdrawRequestData("WRONG-PAYMENT-METHOD", qty);
|
var wrongPaymentMethodCreateWithdrawalRequest = new WithdrawRequestData("WRONG-PAYMENT-METHOD", qty);
|
||||||
await AssertApiError( 400, "unsupported-payment-method", async () => await withdrawalClient.CreateCustodianAccountWithdrawal(storeId, accountId, wrongPaymentMethodCreateWithdrawalRequest));
|
await AssertApiError(400, "unsupported-payment-method", async () => await withdrawalClient.CreateCustodianAccountWithdrawal(storeId, accountId, wrongPaymentMethodCreateWithdrawalRequest));
|
||||||
|
|
||||||
// Test: CreateWithdrawal, wrong account ID
|
// Test: CreateWithdrawal, wrong account ID
|
||||||
await AssertHttpError(404, async () => await withdrawalClient.CreateCustodianAccountWithdrawal(storeId, "WRONG-ACCOUNT-ID", createWithdrawalRequest));
|
await AssertHttpError(404, async () => await withdrawalClient.CreateCustodianAccountWithdrawal(storeId, "WRONG-ACCOUNT-ID", createWithdrawalRequest));
|
||||||
|
|
||||||
// Test: CreateWithdrawal, wrong store ID
|
// Test: CreateWithdrawal, wrong store ID
|
||||||
// TODO it is wierd that 403 is considered normal, but it is like this for all calls where the store is wrong... I'd have preferred a 404 error, because the store cannot be found.
|
// TODO it is wierd that 403 is considered normal, but it is like this for all calls where the store is wrong... I'd have preferred a 404 error, because the store cannot be found.
|
||||||
await AssertHttpError(403, async () => await withdrawalClient.CreateCustodianAccountWithdrawal( "WRONG-STORE-ID",accountId, createWithdrawalRequest));
|
await AssertHttpError(403, async () => await withdrawalClient.CreateCustodianAccountWithdrawal("WRONG-STORE-ID", accountId, createWithdrawalRequest));
|
||||||
|
|
||||||
// Test: CreateWithdrawal, correct payment method, wrong amount
|
// Test: CreateWithdrawal, correct payment method, wrong amount
|
||||||
var wrongAmountCreateWithdrawalRequest = new WithdrawRequestData(MockCustodian.WithdrawalPaymentMethod, TradeQuantity.Parse("0.666"));
|
var wrongAmountCreateWithdrawalRequest = new WithdrawRequestData(MockCustodian.WithdrawalPaymentMethod, TradeQuantity.Parse("0.666"));
|
||||||
await AssertHttpError(400, async () => await withdrawalClient.CreateCustodianAccountWithdrawal(storeId, accountId, wrongAmountCreateWithdrawalRequest));
|
await AssertHttpError(400, async () => await withdrawalClient.CreateCustodianAccountWithdrawal(storeId, accountId, wrongAmountCreateWithdrawalRequest));
|
||||||
|
|
||||||
// Test: GetWithdrawalInfo, unauth
|
// Test: GetWithdrawalInfo, unauth
|
||||||
await AssertHttpError(401, async () => await unauthClient.GetCustodianAccountWithdrawalInfo(storeId, accountId, MockCustodian.WithdrawalPaymentMethod, MockCustodian.WithdrawalId));
|
await AssertHttpError(401, async () => await unauthClient.GetCustodianAccountWithdrawalInfo(storeId, accountId, MockCustodian.WithdrawalPaymentMethod, MockCustodian.WithdrawalId));
|
||||||
|
|
||||||
// Test: GetWithdrawalInfo, auth, but wrong permission
|
// Test: GetWithdrawalInfo, auth, but wrong permission
|
||||||
await AssertHttpError(403, async () => await managerClient.GetCustodianAccountWithdrawalInfo(storeId, accountId, MockCustodian.WithdrawalPaymentMethod, MockCustodian.WithdrawalId));
|
await AssertHttpError(403, async () => await managerClient.GetCustodianAccountWithdrawalInfo(storeId, accountId, MockCustodian.WithdrawalPaymentMethod, MockCustodian.WithdrawalId));
|
||||||
|
|
||||||
// Test: GetWithdrawalInfo, auth, correct permission
|
// Test: GetWithdrawalInfo, auth, correct permission
|
||||||
var withdrawalInfo = await withdrawalClient.GetCustodianAccountWithdrawalInfo(storeId, accountId, MockCustodian.WithdrawalPaymentMethod, MockCustodian.WithdrawalId);
|
var withdrawalInfo = await withdrawalClient.GetCustodianAccountWithdrawalInfo(storeId, accountId, MockCustodian.WithdrawalPaymentMethod, MockCustodian.WithdrawalId);
|
||||||
AssertMockWithdrawal(withdrawalInfo, custodianAccountData);
|
AssertMockWithdrawal(withdrawalInfo, custodianAccountData);
|
||||||
|
|
||||||
// Test: GetWithdrawalInfo, wrong withdrawal ID
|
// Test: GetWithdrawalInfo, wrong withdrawal ID
|
||||||
await AssertHttpError(404, async () => await withdrawalClient.GetCustodianAccountWithdrawalInfo(storeId, accountId, MockCustodian.WithdrawalPaymentMethod, "WRONG-WITHDRAWAL-ID"));
|
await AssertHttpError(404, async () => await withdrawalClient.GetCustodianAccountWithdrawalInfo(storeId, accountId, MockCustodian.WithdrawalPaymentMethod, "WRONG-WITHDRAWAL-ID"));
|
||||||
|
|
||||||
// Test: wrong account ID
|
// Test: wrong account ID
|
||||||
await AssertHttpError(404, async () => await withdrawalClient.GetCustodianAccountWithdrawalInfo(storeId, "WRONG-ACCOUNT-ID", MockCustodian.WithdrawalPaymentMethod, MockCustodian.WithdrawalId));
|
await AssertHttpError(404, async () => await withdrawalClient.GetCustodianAccountWithdrawalInfo(storeId, "WRONG-ACCOUNT-ID", MockCustodian.WithdrawalPaymentMethod, MockCustodian.WithdrawalId));
|
||||||
|
|
||||||
// Test: wrong store ID
|
// Test: wrong store ID
|
||||||
// TODO shouldn't this be 404? I cannot change this without bigger impact, as it would affect all API endpoints that are store centered
|
// TODO shouldn't this be 404? I cannot change this without bigger impact, as it would affect all API endpoints that are store centered
|
||||||
await AssertHttpError(403, async () => await withdrawalClient.GetCustodianAccountWithdrawalInfo("WRONG-STORE-ID", accountId, MockCustodian.WithdrawalPaymentMethod, MockCustodian.WithdrawalId));
|
await AssertHttpError(403, async () => await withdrawalClient.GetCustodianAccountWithdrawalInfo("WRONG-STORE-ID", accountId, MockCustodian.WithdrawalPaymentMethod, MockCustodian.WithdrawalId));
|
||||||
|
|
||||||
// TODO assert API error codes, not just status codes by using AssertCustodianApiError()
|
// TODO assert API error codes, not just status codes by using AssertCustodianApiError()
|
||||||
// TODO also test withdrawals for the various "Status" (Queued, Complete, Failed)
|
// TODO also test withdrawals for the various "Status" (Queued, Complete, Failed)
|
||||||
// TODO create a mock custodian with only ICustodian
|
// TODO create a mock custodian with only ICustodian
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ public class MockCustodian : ICustodian, ICanDeposit, ICanTrade, ICanWithdraw
|
|||||||
var r = new WithdrawResult(WithdrawalPaymentMethod, WithdrawalAsset, ledgerEntries, WithdrawalId, WithdrawalStatus, createdTime, WithdrawalTargetAddress, WithdrawalTransactionId);
|
var r = new WithdrawResult(WithdrawalPaymentMethod, WithdrawalAsset, ledgerEntries, WithdrawalId, WithdrawalStatus, createdTime, WithdrawalTargetAddress, WithdrawalTransactionId);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SimulateWithdrawalResult CreateWithdrawSimulationResult()
|
private SimulateWithdrawalResult CreateWithdrawSimulationResult()
|
||||||
{
|
{
|
||||||
var ledgerEntries = new List<LedgerEntryData>();
|
var ledgerEntries = new List<LedgerEntryData>();
|
||||||
@@ -153,7 +153,7 @@ public class MockCustodian : ICustodian, ICanDeposit, ICanTrade, ICanWithdraw
|
|||||||
{
|
{
|
||||||
if (paymentMethod == WithdrawalPaymentMethod)
|
if (paymentMethod == WithdrawalPaymentMethod)
|
||||||
{
|
{
|
||||||
if (amount.ToString(CultureInfo.InvariantCulture).Equals(""+WithdrawalAmount, StringComparison.InvariantCulture) || WithdrawalAmountPercentage.Equals(amount))
|
if (amount.ToString(CultureInfo.InvariantCulture).Equals("" + WithdrawalAmount, StringComparison.InvariantCulture) || WithdrawalAmountPercentage.Equals(amount))
|
||||||
{
|
{
|
||||||
return Task.FromResult(CreateWithdrawResult());
|
return Task.FromResult(CreateWithdrawResult());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -64,7 +65,6 @@ namespace BTCPayServer.Tests
|
|||||||
var containerIp = File.ReadAllText("/etc/hosts").Split('\n', StringSplitOptions.RemoveEmptyEntries).Last()
|
var containerIp = File.ReadAllText("/etc/hosts").Split('\n', StringSplitOptions.RemoveEmptyEntries).Last()
|
||||||
.Split('\t', StringSplitOptions.RemoveEmptyEntries)[0].Trim();
|
.Split('\t', StringSplitOptions.RemoveEmptyEntries)[0].Trim();
|
||||||
TestLogs.LogInformation($"Selenium: Container's IP {containerIp}");
|
TestLogs.LogInformation($"Selenium: Container's IP {containerIp}");
|
||||||
ServerUri = new Uri(Server.PayTester.ServerUri.AbsoluteUri.Replace($"http://{Server.PayTester.HostName}", $"http://{containerIp}", StringComparison.OrdinalIgnoreCase), UriKind.Absolute);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -76,8 +76,8 @@ namespace BTCPayServer.Tests
|
|||||||
Driver = new ChromeDriver(cds, options,
|
Driver = new ChromeDriver(cds, options,
|
||||||
// A bit less than test timeout
|
// A bit less than test timeout
|
||||||
TimeSpan.FromSeconds(50));
|
TimeSpan.FromSeconds(50));
|
||||||
ServerUri = Server.PayTester.ServerUri;
|
|
||||||
}
|
}
|
||||||
|
ServerUri = Server.PayTester.ServerUri;
|
||||||
Driver.Manage().Window.Maximize();
|
Driver.Manage().Window.Maximize();
|
||||||
|
|
||||||
TestLogs.LogInformation($"Selenium: Using {Driver.GetType()}");
|
TestLogs.LogInformation($"Selenium: Using {Driver.GetType()}");
|
||||||
@@ -87,7 +87,7 @@ namespace BTCPayServer.Tests
|
|||||||
Driver.AssertNoError();
|
Driver.AssertNoError();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PayInvoice(bool mine = false, decimal? amount= null)
|
public void PayInvoice(bool mine = false, decimal? amount = null)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (amount is not null)
|
if (amount is not null)
|
||||||
|
|||||||
@@ -11,8 +11,10 @@ using System.Threading.Tasks;
|
|||||||
using BTCPayServer.Abstractions.Models;
|
using BTCPayServer.Abstractions.Models;
|
||||||
using BTCPayServer.Client;
|
using BTCPayServer.Client;
|
||||||
using BTCPayServer.Client.Models;
|
using BTCPayServer.Client.Models;
|
||||||
|
using BTCPayServer.Controllers;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
using BTCPayServer.Lightning;
|
using BTCPayServer.Lightning;
|
||||||
|
using BTCPayServer.Models.InvoicingModels;
|
||||||
using BTCPayServer.Payments;
|
using BTCPayServer.Payments;
|
||||||
using BTCPayServer.Services;
|
using BTCPayServer.Services;
|
||||||
using BTCPayServer.Services.Invoices;
|
using BTCPayServer.Services.Invoices;
|
||||||
@@ -116,8 +118,11 @@ namespace BTCPayServer.Tests
|
|||||||
|
|
||||||
s.Driver.FindElement(By.Name("buyerEmail")).SendKeys("aa@aa.com");
|
s.Driver.FindElement(By.Name("buyerEmail")).SendKeys("aa@aa.com");
|
||||||
s.Driver.FindElement(By.CssSelector("input[type='submit']")).Click();
|
s.Driver.FindElement(By.CssSelector("input[type='submit']")).Click();
|
||||||
|
invoiceId = s.Driver.Url.Split('/').Last();
|
||||||
s.Driver.Navigate().GoToUrl(editUrl);
|
s.Driver.Navigate().GoToUrl(editUrl);
|
||||||
Assert.Contains("aa@aa.com", s.Driver.PageSource);
|
Assert.Contains("aa@aa.com", s.Driver.PageSource);
|
||||||
|
var invoice = await s.Server.PayTester.GetService<InvoiceRepository>().GetInvoice(invoiceId);
|
||||||
|
Assert.Equal("aa@aa.com", invoice.Metadata.BuyerEmail);
|
||||||
|
|
||||||
//Custom Forms
|
//Custom Forms
|
||||||
s.GoToStore(StoreNavPages.Forms);
|
s.GoToStore(StoreNavPages.Forms);
|
||||||
@@ -148,7 +153,7 @@ namespace BTCPayServer.Tests
|
|||||||
s.Driver.FindElement(By.LinkText("Remove")).Click();
|
s.Driver.FindElement(By.LinkText("Remove")).Click();
|
||||||
s.Driver.WaitForElement(By.Id("ConfirmInput")).SendKeys("DELETE");
|
s.Driver.WaitForElement(By.Id("ConfirmInput")).SendKeys("DELETE");
|
||||||
s.Driver.FindElement(By.Id("ConfirmContinue")).Click();
|
s.Driver.FindElement(By.Id("ConfirmContinue")).Click();
|
||||||
|
|
||||||
Assert.DoesNotContain("Custom Form 1", s.Driver.PageSource);
|
Assert.DoesNotContain("Custom Form 1", s.Driver.PageSource);
|
||||||
s.Driver.FindElement(By.Id("CreateForm")).Click();
|
s.Driver.FindElement(By.Id("CreateForm")).Click();
|
||||||
s.Driver.FindElement(By.Name("Name")).SendKeys("Custom Form 2");
|
s.Driver.FindElement(By.Name("Name")).SendKeys("Custom Form 2");
|
||||||
@@ -163,23 +168,23 @@ namespace BTCPayServer.Tests
|
|||||||
formurl = s.Driver.Url;
|
formurl = s.Driver.Url;
|
||||||
result = await s.Server.PayTester.HttpClient.GetAsync(formurl);
|
result = await s.Server.PayTester.HttpClient.GetAsync(formurl);
|
||||||
Assert.NotEqual(HttpStatusCode.NotFound, result.StatusCode);
|
Assert.NotEqual(HttpStatusCode.NotFound, result.StatusCode);
|
||||||
|
|
||||||
s.GoToHome();
|
s.GoToHome();
|
||||||
s.GoToStore(StoreNavPages.Forms);
|
s.GoToStore(StoreNavPages.Forms);
|
||||||
Assert.Contains("Custom Form 2", s.Driver.PageSource);
|
Assert.Contains("Custom Form 2", s.Driver.PageSource);
|
||||||
|
|
||||||
s.Driver.FindElement(By.LinkText("Custom Form 2")).Click();
|
s.Driver.FindElement(By.LinkText("Custom Form 2")).Click();
|
||||||
|
|
||||||
s.Driver.FindElement(By.Name("Name")).Clear();
|
s.Driver.FindElement(By.Name("Name")).Clear();
|
||||||
s.Driver.FindElement(By.Name("Name")).SendKeys("Custom Form 3");
|
s.Driver.FindElement(By.Name("Name")).SendKeys("Custom Form 3");
|
||||||
s.Driver.FindElement(By.Id("SaveButton")).Click();
|
s.Driver.FindElement(By.Id("SaveButton")).Click();
|
||||||
s.GoToStore(StoreNavPages.Forms);
|
s.GoToStore(StoreNavPages.Forms);
|
||||||
Assert.Contains("Custom Form 3", s.Driver.PageSource);
|
Assert.Contains("Custom Form 3", s.Driver.PageSource);
|
||||||
|
|
||||||
s.Driver.FindElement(By.Id("StoreNav-PaymentRequests")).Click();
|
s.Driver.FindElement(By.Id("StoreNav-PaymentRequests")).Click();
|
||||||
s.Driver.FindElement(By.Id("CreatePaymentRequest")).Click();
|
s.Driver.FindElement(By.Id("CreatePaymentRequest")).Click();
|
||||||
Assert.Equal(4, new SelectElement(s.Driver.FindElement(By.Id("FormId"))).Options.Count);
|
Assert.Equal(4, new SelectElement(s.Driver.FindElement(By.Id("FormId"))).Options.Count);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(Timeout = TestTimeout)]
|
[Fact(Timeout = TestTimeout)]
|
||||||
@@ -1377,9 +1382,9 @@ namespace BTCPayServer.Tests
|
|||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
s.Driver.WaitForElement(By.CssSelector("div.label-manager input")).SendKeys("label2" + Keys.Enter);
|
s.Driver.WaitForElement(By.CssSelector("div.label-manager input")).SendKeys("label2" + Keys.Enter);
|
||||||
});
|
});
|
||||||
|
|
||||||
TestUtils.Eventually(() =>
|
TestUtils.Eventually(() =>
|
||||||
{
|
{
|
||||||
s.Driver.Navigate().Refresh();
|
s.Driver.Navigate().Refresh();
|
||||||
Assert.NotNull(s.Driver.FindElement(By.CssSelector("[data-value='test-label']")));
|
Assert.NotNull(s.Driver.FindElement(By.CssSelector("[data-value='test-label']")));
|
||||||
});
|
});
|
||||||
@@ -1401,10 +1406,10 @@ namespace BTCPayServer.Tests
|
|||||||
s.Driver.WaitForElement(By.CssSelector("[data-value='test-label']")).Click();
|
s.Driver.WaitForElement(By.CssSelector("[data-value='test-label']")).Click();
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
s.Driver.ExecuteJavaScript("document.querySelector('[data-value=\"test-label\"]').nextSibling.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Delete', keyCode: 46}));");
|
s.Driver.ExecuteJavaScript("document.querySelector('[data-value=\"test-label\"]').nextSibling.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Delete', keyCode: 46}));");
|
||||||
|
|
||||||
});
|
});
|
||||||
TestUtils.Eventually(() =>
|
TestUtils.Eventually(() =>
|
||||||
{
|
{
|
||||||
s.Driver.Navigate().Refresh();
|
s.Driver.Navigate().Refresh();
|
||||||
Assert.DoesNotContain("test-label", s.Driver.PageSource);
|
Assert.DoesNotContain("test-label", s.Driver.PageSource);
|
||||||
});
|
});
|
||||||
@@ -2070,7 +2075,8 @@ namespace BTCPayServer.Tests
|
|||||||
var res = await s.Server.CustomerLightningD.Pay(lnurlResponse.Pr);
|
var res = await s.Server.CustomerLightningD.Pay(lnurlResponse.Pr);
|
||||||
Assert.Equal(PayResult.Error, res.Result);
|
Assert.Equal(PayResult.Error, res.Result);
|
||||||
|
|
||||||
await s.Server.CustomerLightningD.Pay(lnurlResponse2.Pr);
|
res = await s.Server.CustomerLightningD.Pay(lnurlResponse2.Pr);
|
||||||
|
Assert.Equal(PayResult.Ok, res.Result);
|
||||||
await TestUtils.EventuallyAsync(async () =>
|
await TestUtils.EventuallyAsync(async () =>
|
||||||
{
|
{
|
||||||
var inv = await s.Server.PayTester.InvoiceRepository.GetInvoice(i);
|
var inv = await s.Server.PayTester.InvoiceRepository.GetInvoice(i);
|
||||||
@@ -2084,22 +2090,23 @@ namespace BTCPayServer.Tests
|
|||||||
});
|
});
|
||||||
// Standard invoice test
|
// Standard invoice test
|
||||||
s.GoToStore(storeId);
|
s.GoToStore(storeId);
|
||||||
s.GoToLightningSettings();
|
|
||||||
s.Driver.SetCheckbox(By.Id("LNURLStandardInvoiceEnabled"), true);
|
|
||||||
SudoForceSaveLightningSettingsRightNowAndFast(s, cryptoCode);
|
|
||||||
i = s.CreateInvoice(storeId, 0.0000001m, cryptoCode);
|
i = s.CreateInvoice(storeId, 0.0000001m, cryptoCode);
|
||||||
s.GoToInvoiceCheckout(i);
|
s.GoToInvoiceCheckout(i);
|
||||||
s.Driver.FindElement(By.ClassName("payment__currencies")).Click();
|
s.Driver.FindElement(By.ClassName("payment__currencies_noborder")).Click();
|
||||||
// BOLT11 is also available for standard invoices
|
// BOLT11 is also displayed for standard invoice (not LNURL, even if it is available)
|
||||||
Assert.Equal(2, s.Driver.FindElements(By.CssSelector(".vex.vex-theme-btcpay .vex-content .vexmenu li.vexmenuitem")).Count);
|
|
||||||
s.Driver.FindElement(By.CssSelector(".vex.vex-theme-btcpay .vex-content .vexmenu li.vexmenuitem")).Click();
|
|
||||||
s.Driver.FindElement(By.Id("copy-tab")).Click();
|
s.Driver.FindElement(By.Id("copy-tab")).Click();
|
||||||
lnurl = s.Driver.FindElement(By.CssSelector("input.checkoutTextbox")).GetAttribute("value");
|
var bolt11 = s.Driver.FindElement(By.CssSelector("input.checkoutTextbox")).GetAttribute("value");
|
||||||
parsed = LNURL.LNURL.Parse(lnurl, out tag);
|
var bolt11Parsed = Lightning.BOLT11PaymentRequest.Parse(bolt11, s.Server.ExplorerNode.Network);
|
||||||
fetchedReuqest = Assert.IsType<LNURLPayRequest>(await LNURL.LNURL.FetchInformation(parsed, new HttpClient()));
|
var invoiceId = s.Driver.Url.Split('/').Last();
|
||||||
|
using (var resp = await s.Server.PayTester.HttpClient.GetAsync("BTC/lnurl/pay/i/" + invoiceId))
|
||||||
|
{
|
||||||
|
resp.EnsureSuccessStatusCode();
|
||||||
|
fetchedReuqest = JsonConvert.DeserializeObject<LNURLPayRequest>(await resp.Content.ReadAsStringAsync());
|
||||||
|
}
|
||||||
Assert.Equal(0.0000001m, fetchedReuqest.MaxSendable.ToDecimal(LightMoneyUnit.BTC));
|
Assert.Equal(0.0000001m, fetchedReuqest.MaxSendable.ToDecimal(LightMoneyUnit.BTC));
|
||||||
Assert.Equal(0.0000001m, fetchedReuqest.MinSendable.ToDecimal(LightMoneyUnit.BTC));
|
Assert.Equal(0.0000001m, fetchedReuqest.MinSendable.ToDecimal(LightMoneyUnit.BTC));
|
||||||
|
|
||||||
|
|
||||||
await Assert.ThrowsAsync<LNUrlException>(async () =>
|
await Assert.ThrowsAsync<LNUrlException>(async () =>
|
||||||
{
|
{
|
||||||
await fetchedReuqest.SendRequest(new LightMoney(0.0000002m, LightMoneyUnit.BTC),
|
await fetchedReuqest.SendRequest(new LightMoney(0.0000002m, LightMoneyUnit.BTC),
|
||||||
@@ -2121,13 +2128,6 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Equal(new LightMoney(0.0000001m, LightMoneyUnit.BTC),
|
Assert.Equal(new LightMoney(0.0000001m, LightMoneyUnit.BTC),
|
||||||
lnurlResponse2.GetPaymentRequest(network).MinimumAmount);
|
lnurlResponse2.GetPaymentRequest(network).MinimumAmount);
|
||||||
s.GoToHome();
|
s.GoToHome();
|
||||||
s.GoToLightningSettings();
|
|
||||||
// LNURL is enabled and settings are expanded
|
|
||||||
Assert.True(s.Driver.FindElement(By.Id("LNURLEnabled")).Selected);
|
|
||||||
Assert.Contains("show", s.Driver.FindElement(By.Id("LNURLSettings")).GetAttribute("class"));
|
|
||||||
s.Driver.SetCheckbox(By.Id("LNURLStandardInvoiceEnabled"), false);
|
|
||||||
s.Driver.FindElement(By.Id("save")).Click();
|
|
||||||
Assert.Contains($"{cryptoCode} Lightning settings successfully updated", s.FindAlertMessage().Text);
|
|
||||||
|
|
||||||
i = s.CreateInvoice(storeId, 0.000001m, cryptoCode);
|
i = s.CreateInvoice(storeId, 0.000001m, cryptoCode);
|
||||||
s.GoToInvoiceCheckout(i);
|
s.GoToInvoiceCheckout(i);
|
||||||
@@ -2141,23 +2141,12 @@ namespace BTCPayServer.Tests
|
|||||||
s.GoToHome();
|
s.GoToHome();
|
||||||
s.GoToLightningSettings();
|
s.GoToLightningSettings();
|
||||||
s.Driver.SetCheckbox(By.Id("LNURLBech32Mode"), false);
|
s.Driver.SetCheckbox(By.Id("LNURLBech32Mode"), false);
|
||||||
s.Driver.SetCheckbox(By.Id("LNURLStandardInvoiceEnabled"), false);
|
|
||||||
s.Driver.SetCheckbox(By.Id("DisableBolt11PaymentMethod"), true);
|
|
||||||
s.Driver.FindElement(By.Id("save")).Click();
|
s.Driver.FindElement(By.Id("save")).Click();
|
||||||
Assert.Contains($"{cryptoCode} Lightning settings successfully updated", s.FindAlertMessage().Text);
|
Assert.Contains($"{cryptoCode} Lightning settings successfully updated", s.FindAlertMessage().Text);
|
||||||
|
|
||||||
// Ensure the toggles are set correctly
|
// Ensure the toggles are set correctly
|
||||||
s.GoToLightningSettings();
|
s.GoToLightningSettings();
|
||||||
|
|
||||||
//TODO: DisableBolt11PaymentMethod is actually disabled because LNURLStandardInvoiceEnabled is disabled
|
|
||||||
// checkboxes is not good choice here, in next release we should have multi choice instead
|
|
||||||
Assert.False(s.Driver.FindElement(By.Id("LNURLBech32Mode")).Selected);
|
Assert.False(s.Driver.FindElement(By.Id("LNURLBech32Mode")).Selected);
|
||||||
Assert.False(s.Driver.FindElement(By.Id("LNURLStandardInvoiceEnabled")).Selected);
|
|
||||||
|
|
||||||
//even though we set DisableBolt11PaymentMethod to true, logic when saving it turns it back off as otherwise no lightning option is available at all!
|
|
||||||
Assert.False(s.Driver.FindElement(By.Id("DisableBolt11PaymentMethod")).Selected);
|
|
||||||
// Invoice creation should fail, because it is a standard invoice with amount, but DisableBolt11PaymentMethod = true and LNURLStandardInvoiceEnabled = false
|
|
||||||
s.CreateInvoice(storeId, 0.0000001m, cryptoCode, "", null, expectedSeverity: StatusMessageModel.StatusSeverity.Success);
|
|
||||||
|
|
||||||
i = s.CreateInvoice(storeId, null, cryptoCode);
|
i = s.CreateInvoice(storeId, null, cryptoCode);
|
||||||
s.GoToInvoiceCheckout(i);
|
s.GoToInvoiceCheckout(i);
|
||||||
@@ -2173,15 +2162,13 @@ namespace BTCPayServer.Tests
|
|||||||
s.AddLightningNode(LightningConnectionType.LndREST, false);
|
s.AddLightningNode(LightningConnectionType.LndREST, false);
|
||||||
s.GoToLightningSettings();
|
s.GoToLightningSettings();
|
||||||
s.Driver.SetCheckbox(By.Id("LNURLEnabled"), true);
|
s.Driver.SetCheckbox(By.Id("LNURLEnabled"), true);
|
||||||
s.Driver.SetCheckbox(By.Id("DisableBolt11PaymentMethod"), true);
|
|
||||||
s.Driver.SetCheckbox(By.Id("LNURLStandardInvoiceEnabled"), true);
|
|
||||||
s.Driver.FindElement(By.Id("save")).Click();
|
s.Driver.FindElement(By.Id("save")).Click();
|
||||||
Assert.Contains($"{cryptoCode} Lightning settings successfully updated", s.FindAlertMessage().Text);
|
Assert.Contains($"{cryptoCode} Lightning settings successfully updated", s.FindAlertMessage().Text);
|
||||||
var invForPP = s.CreateInvoice(0.0000001m, cryptoCode);
|
var invForPP = s.CreateInvoice(null, cryptoCode);
|
||||||
s.GoToInvoiceCheckout(invForPP);
|
s.GoToInvoiceCheckout(invForPP);
|
||||||
s.Driver.FindElement(By.Id("copy-tab")).Click();
|
s.Driver.FindElement(By.Id("copy-tab")).Click();
|
||||||
lnurl = s.Driver.FindElement(By.CssSelector("input.checkoutTextbox")).GetAttribute("value");
|
lnurl = s.Driver.FindElement(By.CssSelector("input.checkoutTextbox")).GetAttribute("value");
|
||||||
parsed = LNURL.LNURL.Parse(lnurl, out tag);
|
LNURL.LNURL.Parse(lnurl, out tag);
|
||||||
|
|
||||||
// Check that pull payment has lightning option
|
// Check that pull payment has lightning option
|
||||||
s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
|
s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
|
||||||
@@ -2305,7 +2292,6 @@ namespace BTCPayServer.Tests
|
|||||||
var invoices = await repo.GetInvoices(new InvoiceQuery() { StoreId = new[] { s.StoreId } });
|
var invoices = await repo.GetInvoices(new InvoiceQuery() { StoreId = new[] { s.StoreId } });
|
||||||
Assert.Equal(2, invoices.Length);
|
Assert.Equal(2, invoices.Length);
|
||||||
var emailSuffix = $"@{s.Server.PayTester.HostName}:{s.Server.PayTester.Port}";
|
var emailSuffix = $"@{s.Server.PayTester.HostName}:{s.Server.PayTester.Port}";
|
||||||
|
|
||||||
foreach (var i in invoices)
|
foreach (var i in invoices)
|
||||||
{
|
{
|
||||||
var lightningPaymentMethod = i.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.LNURLPay));
|
var lightningPaymentMethod = i.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.LNURLPay));
|
||||||
@@ -2322,6 +2308,8 @@ namespace BTCPayServer.Tests
|
|||||||
}
|
}
|
||||||
|
|
||||||
var lnUsername = lnaddress1.Split('@')[0];
|
var lnUsername = lnaddress1.Split('@')[0];
|
||||||
|
|
||||||
|
|
||||||
LNURLPayRequest req;
|
LNURLPayRequest req;
|
||||||
using (var resp = await s.Server.PayTester.HttpClient.GetAsync($"/.well-known/lnurlp/{lnUsername}"))
|
using (var resp = await s.Server.PayTester.HttpClient.GetAsync($"/.well-known/lnurlp/{lnUsername}"))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ namespace BTCPayServer.Tests
|
|||||||
|
|
||||||
public async Task<PayResponse> SendLightningPaymentAsync(Invoice invoice)
|
public async Task<PayResponse> SendLightningPaymentAsync(Invoice invoice)
|
||||||
{
|
{
|
||||||
var bolt11 = invoice.CryptoInfo.Where(o => o.PaymentUrls.BOLT11 != null).First().PaymentUrls.BOLT11;
|
var bolt11 = invoice.CryptoInfo.Where(o => o.PaymentUrls?.BOLT11 != null).First().PaymentUrls.BOLT11;
|
||||||
bolt11 = bolt11.Replace("lightning:", "", StringComparison.OrdinalIgnoreCase);
|
bolt11 = bolt11.Replace("lightning:", "", StringComparison.OrdinalIgnoreCase);
|
||||||
return await CustomerLightningD.Pay(bolt11);
|
return await CustomerLightningD.Pay(bolt11);
|
||||||
}
|
}
|
||||||
@@ -194,7 +194,8 @@ namespace BTCPayServer.Tests
|
|||||||
tcs.TrySetResult(evt);
|
tcs.TrySetResult(evt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await action.Invoke();
|
if (action != null)
|
||||||
|
await action.Invoke();
|
||||||
var result = await tcs.Task;
|
var result = await tcs.Task;
|
||||||
sub.Dispose();
|
sub.Dispose();
|
||||||
return result;
|
return result;
|
||||||
@@ -247,6 +248,8 @@ namespace BTCPayServer.Tests
|
|||||||
|
|
||||||
public List<string> Stores { get; internal set; } = new List<string>();
|
public List<string> Stores { get; internal set; } = new List<string>();
|
||||||
public bool DeleteStore { get; set; } = true;
|
public bool DeleteStore { get; set; } = true;
|
||||||
|
public BTCPayNetworkBase DefaultNetwork => NetworkProvider.DefaultNetwork;
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
foreach (var r in this.Resources)
|
foreach (var r in this.Resources)
|
||||||
|
|||||||
@@ -214,6 +214,13 @@ namespace BTCPayServer.Tests
|
|||||||
get => GenerateWalletResponseV.DerivationScheme;
|
get => GenerateWalletResponseV.DerivationScheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetLNUrl(string cryptoCode, bool activated)
|
||||||
|
{
|
||||||
|
var lnSettingsVm = GetController<UIStoresController>().LightningSettings(StoreId, cryptoCode).AssertViewModel<LightningSettingsViewModel>();
|
||||||
|
lnSettingsVm.LNURLEnabled = activated;
|
||||||
|
Assert.IsType<RedirectToActionResult>(GetController<UIStoresController>().LightningSettings(lnSettingsVm).Result);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task RegisterAsync(bool isAdmin = false)
|
private async Task RegisterAsync(bool isAdmin = false)
|
||||||
{
|
{
|
||||||
var account = parent.PayTester.GetController<UIAccountController>();
|
var account = parent.PayTester.GetController<UIAccountController>();
|
||||||
|
|||||||
@@ -112,7 +112,14 @@ namespace BTCPayServer.Tests
|
|||||||
}
|
}
|
||||||
catch (XunitException) when (!cts.Token.IsCancellationRequested)
|
catch (XunitException) when (!cts.Token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
await Task.Delay(500, cts.Token);
|
bool timeout =false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Delay(500, cts.Token);
|
||||||
|
}
|
||||||
|
catch { timeout = true; }
|
||||||
|
if (timeout)
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1609,7 +1609,7 @@ namespace BTCPayServer.Tests
|
|||||||
// Check correct casing: Addresses in payment URI need to be …
|
// Check correct casing: Addresses in payment URI need to be …
|
||||||
// - lowercase in link version
|
// - lowercase in link version
|
||||||
// - uppercase in QR version
|
// - uppercase in QR version
|
||||||
|
|
||||||
// Standard for all uppercase characters in QR codes is still not implemented in all wallets
|
// Standard for all uppercase characters in QR codes is still not implemented in all wallets
|
||||||
// But we're proceeding with BECH32 being uppercase
|
// But we're proceeding with BECH32 being uppercase
|
||||||
Assert.Equal($"bitcoin:{paymentMethodUnified.BtcAddress}", paymentMethodUnified.InvoiceBitcoinUrl.Split('?')[0]);
|
Assert.Equal($"bitcoin:{paymentMethodUnified.BtcAddress}", paymentMethodUnified.InvoiceBitcoinUrl.Split('?')[0]);
|
||||||
@@ -1635,6 +1635,7 @@ namespace BTCPayServer.Tests
|
|||||||
var cryptoCode = "BTC";
|
var cryptoCode = "BTC";
|
||||||
user.GrantAccess(true);
|
user.GrantAccess(true);
|
||||||
user.RegisterLightningNode(cryptoCode, LightningConnectionType.Charge);
|
user.RegisterLightningNode(cryptoCode, LightningConnectionType.Charge);
|
||||||
|
user.SetLNUrl(cryptoCode, false);
|
||||||
var vm = user.GetController<UIStoresController>().CheckoutAppearance().AssertViewModel<CheckoutAppearanceViewModel>();
|
var vm = user.GetController<UIStoresController>().CheckoutAppearance().AssertViewModel<CheckoutAppearanceViewModel>();
|
||||||
var criteria = Assert.Single(vm.PaymentMethodCriteria);
|
var criteria = Assert.Single(vm.PaymentMethodCriteria);
|
||||||
Assert.Equal(new PaymentMethodId(cryptoCode, LightningPaymentType.Instance).ToString(), criteria.PaymentMethod);
|
Assert.Equal(new PaymentMethodId(cryptoCode, LightningPaymentType.Instance).ToString(), criteria.PaymentMethod);
|
||||||
@@ -1649,16 +1650,12 @@ namespace BTCPayServer.Tests
|
|||||||
Price = 1.5m,
|
Price = 1.5m,
|
||||||
Currency = "USD"
|
Currency = "USD"
|
||||||
}, Facade.Merchant);
|
}, Facade.Merchant);
|
||||||
|
|
||||||
Assert.Single(invoice.CryptoInfo);
|
Assert.Single(invoice.CryptoInfo);
|
||||||
Assert.Equal(PaymentTypes.LightningLike.ToString(), invoice.CryptoInfo[0].PaymentType);
|
Assert.Equal(PaymentTypes.LightningLike.ToString(), invoice.CryptoInfo[0].PaymentType);
|
||||||
|
|
||||||
// Activating LNUrl, we should still have only 1 payment criteria that can be set.
|
// Activating LNUrl, we should still have only 1 payment criteria that can be set.
|
||||||
user.RegisterLightningNode(cryptoCode, LightningConnectionType.Charge);
|
user.RegisterLightningNode(cryptoCode, LightningConnectionType.Charge);
|
||||||
var lnSettingsVm = user.GetController<UIStoresController>().LightningSettings(user.StoreId, cryptoCode).AssertViewModel<LightningSettingsViewModel>();
|
user.SetLNUrl(cryptoCode, true);
|
||||||
lnSettingsVm.LNURLEnabled = true;
|
|
||||||
lnSettingsVm.LNURLStandardInvoiceEnabled = true;
|
|
||||||
Assert.IsType<RedirectToActionResult>(user.GetController<UIStoresController>().LightningSettings(lnSettingsVm).Result);
|
|
||||||
vm = user.GetController<UIStoresController>().CheckoutAppearance().AssertViewModel<CheckoutAppearanceViewModel>();
|
vm = user.GetController<UIStoresController>().CheckoutAppearance().AssertViewModel<CheckoutAppearanceViewModel>();
|
||||||
criteria = Assert.Single(vm.PaymentMethodCriteria);
|
criteria = Assert.Single(vm.PaymentMethodCriteria);
|
||||||
Assert.Equal(new PaymentMethodId(cryptoCode, LightningPaymentType.Instance).ToString(), criteria.PaymentMethod);
|
Assert.Equal(new PaymentMethodId(cryptoCode, LightningPaymentType.Instance).ToString(), criteria.PaymentMethod);
|
||||||
@@ -2445,6 +2442,31 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.False(fn.Seen);
|
Assert.False(fn.Seen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact(Timeout = LongRunningTestTimeout)]
|
||||||
|
[Trait("Integration", "Integration")]
|
||||||
|
public async Task CanFixMappedDomainAppType()
|
||||||
|
{
|
||||||
|
using var tester = CreateServerTester(newDb: true);
|
||||||
|
await tester.StartAsync();
|
||||||
|
var f = tester.PayTester.GetService<ApplicationDbContextFactory>();
|
||||||
|
using (var ctx = f.CreateContext())
|
||||||
|
{
|
||||||
|
var setting = new SettingData() { Id = "BTCPayServer.Services.PoliciesSettings" };
|
||||||
|
setting.Value = JObject.Parse("{\"RootAppId\": null, \"RootAppType\": 1, \"Experimental\": false, \"PluginSource\": null, \"LockSubscription\": false, \"DisableSSHService\": false, \"PluginPreReleases\": false, \"BlockExplorerLinks\": [],\"DomainToAppMapping\": [{\"AppId\": \"87kj5yKay8mB4UUZcJhZH5TqDKMD3CznjwLjiu1oYZXe\", \"Domain\": \"donate.nicolas-dorier.com\", \"AppType\": 0}], \"CheckForNewVersions\": false, \"AllowHotWalletForAll\": false, \"RequiresConfirmedEmail\": false, \"DiscourageSearchEngines\": false, \"DisableInstantNotifications\": false, \"DisableNonAdminCreateUserApi\": false, \"AllowHotWalletRPCImportForAll\": false, \"AllowLightningInternalNodeForAll\": false, \"DisableStoresToUseServerEmailSettings\": false}").ToString();
|
||||||
|
ctx.Settings.Add(setting);
|
||||||
|
await ctx.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
await RestartMigration(tester);
|
||||||
|
using (var ctx = f.CreateContext())
|
||||||
|
{
|
||||||
|
var setting = await ctx.Settings.FirstOrDefaultAsync(c => c.Id == "BTCPayServer.Services.PoliciesSettings");
|
||||||
|
var o = JObject.Parse(setting.Value);
|
||||||
|
Assert.Equal("Crowdfund", o["RootAppType"].Value<string>());
|
||||||
|
o = (JObject)((JArray)o["DomainToAppMapping"])[0];
|
||||||
|
Assert.Equal("PointOfSale", o["AppType"].Value<string>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact(Timeout = LongRunningTestTimeout)]
|
[Fact(Timeout = LongRunningTestTimeout)]
|
||||||
[Trait("Integration", "Integration")]
|
[Trait("Integration", "Integration")]
|
||||||
public async Task CanDoLightningInternalNodeMigration()
|
public async Task CanDoLightningInternalNodeMigration()
|
||||||
|
|||||||
@@ -61,34 +61,34 @@ namespace BTCPayServer.Tests
|
|||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
// /// <summary>
|
// /// <summary>
|
||||||
// /// This will take the translations from v1 or v2
|
// /// This will take the translations from v1 or v2
|
||||||
// /// and upload them to transifex if not found
|
// /// and upload them to transifex if not found
|
||||||
// /// </summary>
|
// /// </summary>
|
||||||
// [FactWithSecret("TransifexAPIToken")]
|
// [FactWithSecret("TransifexAPIToken")]
|
||||||
// [Trait("Utilities", "Utilities")]
|
// [Trait("Utilities", "Utilities")]
|
||||||
//#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
//#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||||
// public async Task UpdateTransifex()
|
// public async Task UpdateTransifex()
|
||||||
// {
|
// {
|
||||||
// // DO NOT RUN IT, THIS WILL ERASE THE CURRENT TRANSIFEX TRANSLATIONS
|
// // DO NOT RUN IT, THIS WILL ERASE THE CURRENT TRANSIFEX TRANSLATIONS
|
||||||
|
|
||||||
// var client = GetTransifexClient();
|
// var client = GetTransifexClient();
|
||||||
// var translations = JsonTranslation.GetTranslations(TranslationFolder.CheckoutV2);
|
// var translations = JsonTranslation.GetTranslations(TranslationFolder.CheckoutV2);
|
||||||
// var enTranslations = translations["en"];
|
// var enTranslations = translations["en"];
|
||||||
// translations.Remove("en");
|
// translations.Remove("en");
|
||||||
|
|
||||||
// foreach (var t in translations)
|
// foreach (var t in translations)
|
||||||
// {
|
// {
|
||||||
// foreach (var w in t.Value.Words.ToArray())
|
// foreach (var w in t.Value.Words.ToArray())
|
||||||
// {
|
// {
|
||||||
// if (t.Value.Words[w.Key] == null)
|
// if (t.Value.Words[w.Key] == null)
|
||||||
// t.Value.Words[w.Key] = enTranslations.Words[w.Key];
|
// t.Value.Words[w.Key] = enTranslations.Words[w.Key];
|
||||||
// }
|
// }
|
||||||
// t.Value.Words.Remove("code");
|
// t.Value.Words.Remove("code");
|
||||||
// t.Value.Words.Remove("NOTICE_WARN");
|
// t.Value.Words.Remove("NOTICE_WARN");
|
||||||
// }
|
// }
|
||||||
// await client.UpdateTranslations(translations);
|
// await client.UpdateTranslations(translations);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
//#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
//#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||||
|
|
||||||
@@ -147,7 +147,7 @@ namespace BTCPayServer.Tests
|
|||||||
public async Task AutoTranslateChatGPT()
|
public async Task AutoTranslateChatGPT()
|
||||||
{
|
{
|
||||||
var file = TranslationFolder.CheckoutV2;
|
var file = TranslationFolder.CheckoutV2;
|
||||||
|
|
||||||
using var driver = new ChromeDriver(new ChromeOptions()
|
using var driver = new ChromeDriver(new ChromeOptions()
|
||||||
{
|
{
|
||||||
DebuggerAddress = "127.0.0.1:9222"
|
DebuggerAddress = "127.0.0.1:9222"
|
||||||
@@ -185,6 +185,19 @@ namespace BTCPayServer.Tests
|
|||||||
var english = englishTranslations.Words[translation.Key];
|
var english = englishTranslations.Words[translation.Key];
|
||||||
if (translation.Value != null)
|
if (translation.Value != null)
|
||||||
continue; // Already translated
|
continue; // Already translated
|
||||||
|
|
||||||
|
//TODO: A better way to avoid rate limits is to use this format:
|
||||||
|
//I am translating a checkout crypto payment page, and I want you to translate it from English (en-US) to French (fr-FR).
|
||||||
|
//##
|
||||||
|
//English: This invoice will expire in
|
||||||
|
//French:
|
||||||
|
//##
|
||||||
|
//English: Scan the QR code, or tap to copy the address.
|
||||||
|
//French:
|
||||||
|
//##
|
||||||
|
//English: Your payment has been received and is now processing.
|
||||||
|
//French:
|
||||||
|
|
||||||
if (!askedPrompt)
|
if (!askedPrompt)
|
||||||
{
|
{
|
||||||
driver.FindElement(By.XPath("//a[contains(text(), \"New chat\")]")).Click();
|
driver.FindElement(By.XPath("//a[contains(text(), \"New chat\")]")).Click();
|
||||||
@@ -534,7 +547,7 @@ retry:
|
|||||||
|
|
||||||
|
|
||||||
public string FullPath { get; set; }
|
public string FullPath { get; set; }
|
||||||
public string TransifexProject { get; set; }
|
public string TransifexProject { get; set; }
|
||||||
public string TransifexResource { get; private set; }
|
public string TransifexResource { get; private set; }
|
||||||
|
|
||||||
public void Save()
|
public void Save()
|
||||||
@@ -564,7 +577,7 @@ retry:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Translate(Dictionary<string,string> sourceTranslations)
|
public void Translate(Dictionary<string, string> sourceTranslations)
|
||||||
{
|
{
|
||||||
foreach (var o in sourceTranslations)
|
foreach (var o in sourceTranslations)
|
||||||
if (o.Value != null)
|
if (o.Value != null)
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ services:
|
|||||||
- selenium
|
- selenium
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
- "tests:127.0.0.1"
|
- "tests:127.0.0.1"
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
custom:
|
||||||
|
ipv4_address: 172.23.0.18
|
||||||
volumes:
|
volumes:
|
||||||
- "sshd_datadir:/root/.ssh"
|
- "sshd_datadir:/root/.ssh"
|
||||||
- "customer_lightningd_datadir:/etc/customer_lightningd_datadir"
|
- "customer_lightningd_datadir:/etc/customer_lightningd_datadir"
|
||||||
@@ -89,6 +93,8 @@ services:
|
|||||||
image: selenium/standalone-chrome:101.0
|
image: selenium/standalone-chrome:101.0
|
||||||
expose:
|
expose:
|
||||||
- "4444"
|
- "4444"
|
||||||
|
extra_hosts:
|
||||||
|
- "tests:172.18.0.18"
|
||||||
nbxplorer:
|
nbxplorer:
|
||||||
image: nicolasdorier/nbxplorer:2.3.58
|
image: nicolasdorier/nbxplorer:2.3.58
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@@ -237,7 +243,7 @@ services:
|
|||||||
- "5432"
|
- "5432"
|
||||||
|
|
||||||
merchant_lnd:
|
merchant_lnd:
|
||||||
image: btcpayserver/lnd:v0.15.4-beta-1
|
image: btcpayserver/lnd:v0.16.2-beta
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
LND_CHAIN: "btc"
|
LND_CHAIN: "btc"
|
||||||
@@ -272,7 +278,7 @@ services:
|
|||||||
- bitcoind
|
- bitcoind
|
||||||
|
|
||||||
customer_lnd:
|
customer_lnd:
|
||||||
image: btcpayserver/lnd:v0.15.4-beta-1
|
image: btcpayserver/lnd:v0.16.2-beta
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
LND_CHAIN: "btc"
|
LND_CHAIN: "btc"
|
||||||
@@ -404,3 +410,12 @@ volumes:
|
|||||||
torrcdir:
|
torrcdir:
|
||||||
tor_servicesdir:
|
tor_servicesdir:
|
||||||
monero_data:
|
monero_data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
driver: bridge
|
||||||
|
custom:
|
||||||
|
driver: bridge
|
||||||
|
ipam:
|
||||||
|
config:
|
||||||
|
- subnet: 172.23.0.0/16
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ services:
|
|||||||
- selenium
|
- selenium
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
- "tests:127.0.0.1"
|
- "tests:127.0.0.1"
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
custom:
|
||||||
|
ipv4_address: 172.23.0.18
|
||||||
volumes:
|
volumes:
|
||||||
- "sshd_datadir:/root/.ssh"
|
- "sshd_datadir:/root/.ssh"
|
||||||
- "customer_lightningd_datadir:/etc/customer_lightningd_datadir"
|
- "customer_lightningd_datadir:/etc/customer_lightningd_datadir"
|
||||||
@@ -84,6 +88,8 @@ services:
|
|||||||
- merchant_lnd
|
- merchant_lnd
|
||||||
selenium:
|
selenium:
|
||||||
image: selenium/standalone-chrome:101.0
|
image: selenium/standalone-chrome:101.0
|
||||||
|
extra_hosts:
|
||||||
|
- "tests:172.18.0.18"
|
||||||
expose:
|
expose:
|
||||||
- "4444"
|
- "4444"
|
||||||
nbxplorer:
|
nbxplorer:
|
||||||
@@ -110,7 +116,6 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- bitcoind
|
- bitcoind
|
||||||
|
|
||||||
|
|
||||||
bitcoind:
|
bitcoind:
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
image: btcpayserver/bitcoin:24.0
|
image: btcpayserver/bitcoin:24.0
|
||||||
@@ -225,7 +230,7 @@ services:
|
|||||||
- "5432"
|
- "5432"
|
||||||
|
|
||||||
merchant_lnd:
|
merchant_lnd:
|
||||||
image: btcpayserver/lnd:v0.15.4-beta-1
|
image: btcpayserver/lnd:v0.16.2-beta
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
LND_CHAIN: "btc"
|
LND_CHAIN: "btc"
|
||||||
@@ -262,7 +267,7 @@ services:
|
|||||||
- bitcoind
|
- bitcoind
|
||||||
|
|
||||||
customer_lnd:
|
customer_lnd:
|
||||||
image: btcpayserver/lnd:v0.15.4-beta-1
|
image: btcpayserver/lnd:v0.16.2-beta
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
LND_CHAIN: "btc"
|
LND_CHAIN: "btc"
|
||||||
@@ -323,3 +328,12 @@ volumes:
|
|||||||
tor_datadir:
|
tor_datadir:
|
||||||
torrcdir:
|
torrcdir:
|
||||||
tor_servicesdir:
|
tor_servicesdir:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
driver: bridge
|
||||||
|
custom:
|
||||||
|
driver: bridge
|
||||||
|
ipam:
|
||||||
|
config:
|
||||||
|
- subnet: 172.23.0.0/16
|
||||||
|
|||||||
@@ -47,7 +47,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BIP78.Sender" Version="0.2.2" />
|
<PackageReference Include="BIP78.Sender" Version="0.2.2" />
|
||||||
<PackageReference Include="BTCPayServer.Hwi" Version="2.0.2" />
|
<PackageReference Include="BTCPayServer.Hwi" Version="2.0.2" />
|
||||||
<PackageReference Include="BTCPayServer.Lightning.All" Version="1.4.22" />
|
<PackageReference Include="BTCPayServer.Lightning.All" Version="1.4.23" />
|
||||||
<PackageReference Include="CsvHelper" Version="15.0.5" />
|
<PackageReference Include="CsvHelper" Version="15.0.5" />
|
||||||
<PackageReference Include="Dapper" Version="2.0.123" />
|
<PackageReference Include="Dapper" Version="2.0.123" />
|
||||||
<PackageReference Include="Fido2" Version="2.0.2" />
|
<PackageReference Include="Fido2" Version="2.0.2" />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
namespace BTCPayServer
|
namespace BTCPayServer
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace BTCPayServer
|
|||||||
return Labels[num % Labels.Length];
|
return Labels[num % Labels.Length];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// https://gist.github.com/zihotki/09fc41d52981fb6f93a81ebf20b35cd5
|
/// https://gist.github.com/zihotki/09fc41d52981fb6f93a81ebf20b35cd5
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates color with corrected brightness.
|
/// Creates color with corrected brightness.
|
||||||
@@ -92,7 +92,7 @@ namespace BTCPayServer
|
|||||||
|
|
||||||
return Color.FromArgb(color.A, (int)red, (int)green, (int)blue);
|
return Color.FromArgb(color.A, (int)red, (int)green, (int)blue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string AdjustBrightness(string html, float correctionFactor)
|
public string AdjustBrightness(string html, float correctionFactor)
|
||||||
{
|
{
|
||||||
var color = AdjustBrightness(ColorTranslator.FromHtml(html), correctionFactor);
|
var color = AdjustBrightness(ColorTranslator.FromHtml(html), correctionFactor);
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public class AppSales : ViewComponent
|
|||||||
};
|
};
|
||||||
if (vm.InitialRendering)
|
if (vm.InitialRendering)
|
||||||
return View(vm);
|
return View(vm);
|
||||||
|
|
||||||
var app = HttpContext.GetAppData();
|
var app = HttpContext.GetAppData();
|
||||||
var stats = await _appService.GetSalesStats(app);
|
var stats = await _appService.GetSalesStats(app);
|
||||||
vm.SalesCount = stats.SalesCount;
|
vm.SalesCount = stats.SalesCount;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ namespace BTCPayServer.Components.LabelManager
|
|||||||
{
|
{
|
||||||
ExcludeTypes = excludeTypes,
|
ExcludeTypes = excludeTypes,
|
||||||
WalletObjectId = walletObjectId,
|
WalletObjectId = walletObjectId,
|
||||||
SelectedLabels = selectedLabels?? Array.Empty<string>(),
|
SelectedLabels = selectedLabels ?? Array.Empty<string>(),
|
||||||
DisplayInline = displayInline,
|
DisplayInline = displayInline,
|
||||||
RichLabelInfo = richLabelInfo,
|
RichLabelInfo = richLabelInfo,
|
||||||
AutoUpdate = autoUpdate,
|
AutoUpdate = autoUpdate,
|
||||||
@@ -25,7 +25,7 @@ namespace BTCPayServer.Components.LabelManager
|
|||||||
|
|
||||||
public class RichLabelInfo
|
public class RichLabelInfo
|
||||||
{
|
{
|
||||||
public string Link { get; set; }
|
public string Link { get; set; }
|
||||||
public string Tooltip { get; set; }
|
public string Tooltip { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,7 +145,10 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" permission="@Policies.CanModifyStoreSettings">
|
<li class="nav-item" permission="@Policies.CanModifyStoreSettings">
|
||||||
<a asp-area="" asp-controller="UIStorePullPayments" asp-action="Payouts" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.IsActivePage(StoreNavPages.Payouts)" id="StoreNav-Payouts">
|
<a asp-area=""
|
||||||
|
asp-controller="UIStorePullPayments" asp-action="Payouts"
|
||||||
|
asp-route-pullPaymentId=""
|
||||||
|
asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.IsActivePage(StoreNavPages.Payouts)" id="StoreNav-Payouts">
|
||||||
<vc:icon symbol="payouts"/>
|
<vc:icon symbol="payouts"/>
|
||||||
<span>Payouts</span>
|
<span>Payouts</span>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ namespace BTCPayServer.Components.QRCode
|
|||||||
{
|
{
|
||||||
public class QRCode : ViewComponent
|
public class QRCode : ViewComponent
|
||||||
{
|
{
|
||||||
private static QRCodeGenerator _qrGenerator = new ();
|
private static QRCodeGenerator _qrGenerator = new();
|
||||||
|
|
||||||
public IViewComponentResult Invoke(string data)
|
public IViewComponentResult Invoke(string data)
|
||||||
{
|
{
|
||||||
var qrCodeData = _qrGenerator.CreateQrCode(data, QRCodeGenerator.ECCLevel.Q);
|
var qrCodeData = _qrGenerator.CreateQrCode(data, QRCodeGenerator.ECCLevel.Q);
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public class StoreRecentTransactions : ViewComponent
|
|||||||
var network = derivationSettings.Network;
|
var network = derivationSettings.Network;
|
||||||
var wallet = _walletProvider.GetWallet(network);
|
var wallet = _walletProvider.GetWallet(network);
|
||||||
var allTransactions = await wallet.FetchTransactionHistory(derivationSettings.AccountDerivation, 0, 5, TimeSpan.FromDays(31.0));
|
var allTransactions = await wallet.FetchTransactionHistory(derivationSettings.AccountDerivation, 0, 5, TimeSpan.FromDays(31.0));
|
||||||
var walletTransactionsInfo = await _walletRepository.GetWalletTransactionsInfo( vm.WalletId , allTransactions.Select(t => t.TransactionId.ToString()).ToArray());
|
var walletTransactionsInfo = await _walletRepository.GetWalletTransactionsInfo(vm.WalletId, allTransactions.Select(t => t.TransactionId.ToString()).ToArray());
|
||||||
|
|
||||||
transactions = allTransactions
|
transactions = allTransactions
|
||||||
.Select(tx =>
|
.Select(tx =>
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewComponents;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
|
|
||||||
namespace BTCPayServer.Components.TruncateCenter;
|
namespace BTCPayServer.Components.TruncateCenter;
|
||||||
|
|
||||||
@@ -15,6 +17,8 @@ public class TruncateCenter : ViewComponent
|
|||||||
{
|
{
|
||||||
public IViewComponentResult Invoke(string text, string link = null, string classes = null, int padding = 7, bool copy = true)
|
public IViewComponentResult Invoke(string text, string link = null, string classes = null, int padding = 7, bool copy = true)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrEmpty(text))
|
||||||
|
return new HtmlContentViewComponentResult(new StringHtmlContent(string.Empty));
|
||||||
var vm = new TruncateCenterViewModel
|
var vm = new TruncateCenterViewModel
|
||||||
{
|
{
|
||||||
Classes = classes,
|
Classes = classes,
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ namespace BTCPayServer.Configuration
|
|||||||
foreach (var n in new BTCPayNetworkProvider(networkType).GetAll().OfType<BTCPayNetwork>())
|
foreach (var n in new BTCPayNetworkProvider(networkType).GetAll().OfType<BTCPayNetwork>())
|
||||||
{
|
{
|
||||||
builder.AppendLine(CultureInfo.InvariantCulture, $"#{n.CryptoCode}.explorer.url={n.NBXplorerNetwork.DefaultSettings.DefaultUrl}");
|
builder.AppendLine(CultureInfo.InvariantCulture, $"#{n.CryptoCode}.explorer.url={n.NBXplorerNetwork.DefaultSettings.DefaultUrl}");
|
||||||
builder.AppendLine(CultureInfo.InvariantCulture, $"#{n.CryptoCode}.explorer.cookiefile={ n.NBXplorerNetwork.DefaultSettings.DefaultCookieFile}");
|
builder.AppendLine(CultureInfo.InvariantCulture, $"#{n.CryptoCode}.explorer.cookiefile={n.NBXplorerNetwork.DefaultSettings.DefaultCookieFile}");
|
||||||
builder.AppendLine(CultureInfo.InvariantCulture, $"#{n.CryptoCode}.blockexplorerlink=https://mempool.space/tx/{{0}}");
|
builder.AppendLine(CultureInfo.InvariantCulture, $"#{n.CryptoCode}.blockexplorerlink=https://mempool.space/tx/{{0}}");
|
||||||
if (n.SupportLightning)
|
if (n.SupportLightning)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
{
|
{
|
||||||
return AppNotFound();
|
return AppNotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(ToPointOfSaleModel(app));
|
return Ok(ToPointOfSaleModel(app));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,7 +202,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
{
|
{
|
||||||
return AppNotFound();
|
return AppNotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(ToCrowdfundModel(app));
|
return Ok(ToCrowdfundModel(app));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,7 +267,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
return new PointOfSaleSettings()
|
return new PointOfSaleSettings()
|
||||||
{
|
{
|
||||||
Title = request.Title,
|
Title = request.Title,
|
||||||
DefaultView = (PosViewType) request.DefaultView,
|
DefaultView = (PosViewType)request.DefaultView,
|
||||||
ShowCustomAmount = request.ShowCustomAmount,
|
ShowCustomAmount = request.ShowCustomAmount,
|
||||||
ShowDiscount = request.ShowDiscount,
|
ShowDiscount = request.ShowDiscount,
|
||||||
EnableTips = request.EnableTips,
|
EnableTips = request.EnableTips,
|
||||||
@@ -331,10 +331,10 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
Currency = settings.Currency,
|
Currency = settings.Currency,
|
||||||
Items = JsonConvert.DeserializeObject(
|
Items = JsonConvert.DeserializeObject(
|
||||||
JsonConvert.SerializeObject(
|
JsonConvert.SerializeObject(
|
||||||
_appService.Parse(settings.Template, settings.Currency),
|
_appService.Parse(settings.Template, settings.Currency),
|
||||||
new JsonSerializerSettings
|
new JsonSerializerSettings
|
||||||
{
|
{
|
||||||
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
|
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@@ -406,10 +406,10 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
Tagline = settings.Tagline,
|
Tagline = settings.Tagline,
|
||||||
Perks = JsonConvert.DeserializeObject(
|
Perks = JsonConvert.DeserializeObject(
|
||||||
JsonConvert.SerializeObject(
|
JsonConvert.SerializeObject(
|
||||||
_appService.Parse(settings.PerksTemplate, settings.TargetCurrency),
|
_appService.Parse(settings.PerksTemplate, settings.TargetCurrency),
|
||||||
new JsonSerializerSettings
|
new JsonSerializerSettings
|
||||||
{
|
{
|
||||||
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
|
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -362,10 +362,10 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
{
|
{
|
||||||
return UnsupportedAsset(asset, ex.Message);
|
return UnsupportedAsset(asset, ex.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
var simulateWithdrawResult =
|
var simulateWithdrawResult =
|
||||||
await withdrawableCustodian.SimulateWithdrawalAsync(request.PaymentMethod, qty, custodianAccount.GetBlob(), cancellationToken);
|
await withdrawableCustodian.SimulateWithdrawalAsync(request.PaymentMethod, qty, custodianAccount.GetBlob(), cancellationToken);
|
||||||
var result = new WithdrawalSimulationResponseData(simulateWithdrawResult.PaymentMethod, simulateWithdrawResult.Asset,
|
var result = new WithdrawalSimulationResponseData(simulateWithdrawResult.PaymentMethod, simulateWithdrawResult.Asset,
|
||||||
accountId, custodian.Code, simulateWithdrawResult.LedgerEntries, simulateWithdrawResult.MinQty, simulateWithdrawResult.MaxQty);
|
accountId, custodian.Code, simulateWithdrawResult.LedgerEntries, simulateWithdrawResult.MinQty, simulateWithdrawResult.MaxQty);
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -547,7 +547,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
Amount = accounting.TotalDue.ToDecimal(MoneyUnit.BTC),
|
Amount = accounting.TotalDue.ToDecimal(MoneyUnit.BTC),
|
||||||
NetworkFee = accounting.NetworkFee.ToDecimal(MoneyUnit.BTC),
|
NetworkFee = accounting.NetworkFee.ToDecimal(MoneyUnit.BTC),
|
||||||
PaymentLink =
|
PaymentLink =
|
||||||
method.GetId().PaymentType.GetPaymentLink(method.Network, details, accounting.Due,
|
method.GetId().PaymentType.GetPaymentLink(method.Network, entity, details, accounting.Due,
|
||||||
Request.GetAbsoluteRoot()),
|
Request.GetAbsoluteRoot()),
|
||||||
Payments = payments.Select(paymentEntity => ToPaymentModel(entity, paymentEntity)).ToList(),
|
Payments = payments.Select(paymentEntity => ToPaymentModel(entity, paymentEntity)).ToList(),
|
||||||
AdditionalData = details.GetAdditionalData()
|
AdditionalData = details.GetAdditionalData()
|
||||||
|
|||||||
@@ -124,7 +124,8 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var invoice = await _invoiceController.CreatePaymentRequestInvoice(pr, amount, this.StoreData, Request, cancellationToken);
|
var prData = await _paymentRequestRepository.FindPaymentRequest(pr.Id, null);
|
||||||
|
var invoice = await _invoiceController.CreatePaymentRequestInvoice(prData, amount, pr.AmountDue, this.StoreData, Request, cancellationToken);
|
||||||
return Ok(GreenfieldInvoiceController.ToModel(invoice, _linkGenerator, Request));
|
return Ok(GreenfieldInvoiceController.ToModel(invoice, _linkGenerator, Request));
|
||||||
}
|
}
|
||||||
catch (BitpayHttpException e)
|
catch (BitpayHttpException e)
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ using BTCPayServer.Security;
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Cors;
|
using Microsoft.AspNetCore.Cors;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using StoreData = BTCPayServer.Data.StoreData;
|
|
||||||
using PayoutProcessorData = BTCPayServer.Client.Models.PayoutProcessorData;
|
using PayoutProcessorData = BTCPayServer.Client.Models.PayoutProcessorData;
|
||||||
|
using StoreData = BTCPayServer.Data.StoreData;
|
||||||
|
|
||||||
namespace BTCPayServer.Controllers.Greenfield
|
namespace BTCPayServer.Controllers.Greenfield
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -264,7 +264,8 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
pullPaymentId = pullPaymentId
|
pullPaymentId = pullPaymentId
|
||||||
}, Request.Scheme, Request.Host.ToString())!);
|
}, Request.Scheme, Request.Host.ToString())!);
|
||||||
|
|
||||||
return base.Ok(new PullPaymentLNURL() {
|
return base.Ok(new PullPaymentLNURL()
|
||||||
|
{
|
||||||
LNURLBech32 = LNURL.LNURL.EncodeUri(lnurlEndpoint, "withdrawRequest", true).ToString(),
|
LNURLBech32 = LNURL.LNURL.EncodeUri(lnurlEndpoint, "withdrawRequest", true).ToString(),
|
||||||
LNURLUri = LNURL.LNURL.EncodeUri(lnurlEndpoint, "withdrawRequest", false).ToString()
|
LNURLUri = LNURL.LNURL.EncodeUri(lnurlEndpoint, "withdrawRequest", false).ToString()
|
||||||
});
|
});
|
||||||
@@ -359,7 +360,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
return this.CreateAPIPermissionError(Policies.CanCreatePullPayments);
|
return this.CreateAPIPermissionError(Policies.CanCreatePullPayments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request is null || !PaymentMethodId.TryParse(request?.PaymentMethod, out var paymentMethodId))
|
if (request is null || !PaymentMethodId.TryParse(request?.PaymentMethod, out var paymentMethodId))
|
||||||
{
|
{
|
||||||
ModelState.AddModelError(nameof(request.PaymentMethod), "Invalid payment method");
|
ModelState.AddModelError(nameof(request.PaymentMethod), "Invalid payment method");
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BTCPayServer.Abstractions.Constants;
|
using BTCPayServer.Abstractions.Constants;
|
||||||
|
using BTCPayServer.Abstractions.Extensions;
|
||||||
using BTCPayServer.Client;
|
using BTCPayServer.Client;
|
||||||
using BTCPayServer.Client.Models;
|
using BTCPayServer.Client.Models;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
@@ -69,6 +70,9 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
public async Task<IActionResult> UpdateStoreLightningAutomatedPayoutProcessor(
|
public async Task<IActionResult> UpdateStoreLightningAutomatedPayoutProcessor(
|
||||||
string storeId, string paymentMethod, LightningAutomatedPayoutSettings request)
|
string storeId, string paymentMethod, LightningAutomatedPayoutSettings request)
|
||||||
{
|
{
|
||||||
|
AutomatedPayoutConstants.ValidateInterval(ModelState, request.IntervalSeconds, nameof(request.IntervalSeconds));
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
return this.CreateValidationError(ModelState);
|
||||||
paymentMethod = PaymentMethodId.Parse(paymentMethod).ToString();
|
paymentMethod = PaymentMethodId.Parse(paymentMethod).ToString();
|
||||||
var activeProcessor =
|
var activeProcessor =
|
||||||
(await _payoutProcessorService.GetProcessors(
|
(await _payoutProcessorService.GetProcessors(
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BTCPayServer.Abstractions.Constants;
|
using BTCPayServer.Abstractions.Constants;
|
||||||
|
using BTCPayServer.Abstractions.Extensions;
|
||||||
using BTCPayServer.Client;
|
using BTCPayServer.Client;
|
||||||
using BTCPayServer.Client.Models;
|
using BTCPayServer.Client.Models;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
@@ -75,6 +77,11 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
public async Task<IActionResult> UpdateStoreOnchainAutomatedPayoutProcessor(
|
public async Task<IActionResult> UpdateStoreOnchainAutomatedPayoutProcessor(
|
||||||
string storeId, string paymentMethod, OnChainAutomatedPayoutSettings request)
|
string storeId, string paymentMethod, OnChainAutomatedPayoutSettings request)
|
||||||
{
|
{
|
||||||
|
AutomatedPayoutConstants.ValidateInterval(ModelState, request.IntervalSeconds, nameof(request.IntervalSeconds));
|
||||||
|
if (request.FeeBlockTarget is int t && (t < 1 || t > 1000))
|
||||||
|
ModelState.AddModelError(nameof(request.FeeBlockTarget), "The feeBlockTarget should be between 1 and 1000");
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
return this.CreateValidationError(ModelState);
|
||||||
paymentMethod = PaymentMethodId.Parse(paymentMethod).ToString();
|
paymentMethod = PaymentMethodId.Parse(paymentMethod).ToString();
|
||||||
var activeProcessor =
|
var activeProcessor =
|
||||||
(await _payoutProcessorService.GetProcessors(
|
(await _payoutProcessorService.GetProcessors(
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
new LNURLPayPaymentMethodData(
|
new LNURLPayPaymentMethodData(
|
||||||
paymentMethod.PaymentId.CryptoCode,
|
paymentMethod.PaymentId.CryptoCode,
|
||||||
!excludedPaymentMethods.Match(paymentMethod.PaymentId),
|
!excludedPaymentMethods.Match(paymentMethod.PaymentId),
|
||||||
paymentMethod.UseBech32Scheme, paymentMethod.EnableForStandardInvoices
|
paymentMethod.UseBech32Scheme
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.Where((result) => enabled is null || enabled == result.Enabled)
|
.Where((result) => enabled is null || enabled == result.Enabled)
|
||||||
@@ -124,8 +124,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
LNURLPaySupportedPaymentMethod? paymentMethod = new LNURLPaySupportedPaymentMethod()
|
LNURLPaySupportedPaymentMethod? paymentMethod = new LNURLPaySupportedPaymentMethod()
|
||||||
{
|
{
|
||||||
CryptoCode = cryptoCode,
|
CryptoCode = cryptoCode,
|
||||||
UseBech32Scheme = paymentMethodData.UseBech32Scheme,
|
UseBech32Scheme = paymentMethodData.UseBech32Scheme
|
||||||
EnableForStandardInvoices = paymentMethodData.EnableForStandardInvoices
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var store = Store;
|
var store = Store;
|
||||||
@@ -154,7 +153,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
: new LNURLPayPaymentMethodData(
|
: new LNURLPayPaymentMethodData(
|
||||||
paymentMethod.PaymentId.CryptoCode,
|
paymentMethod.PaymentId.CryptoCode,
|
||||||
!excluded,
|
!excluded,
|
||||||
paymentMethod.UseBech32Scheme, paymentMethod.EnableForStandardInvoices
|
paymentMethod.UseBech32Scheme
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
private void AssertCryptoCodeWallet(string cryptoCode, out BTCPayNetwork network)
|
private void AssertCryptoCodeWallet(string cryptoCode, out BTCPayNetwork network)
|
||||||
|
|||||||
@@ -33,7 +33,10 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
return new LightningAddressData();
|
return new LightningAddressData();
|
||||||
return new LightningAddressData()
|
return new LightningAddressData()
|
||||||
{
|
{
|
||||||
Username = data.Username, Max = blob.Max, Min = blob.Min, CurrencyCode = blob.CurrencyCode
|
Username = data.Username,
|
||||||
|
Max = blob.Max,
|
||||||
|
Min = blob.Min,
|
||||||
|
CurrencyCode = blob.CurrencyCode
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +44,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
[HttpGet("~/api/v1/stores/{storeId}/lightning-addresses")]
|
[HttpGet("~/api/v1/stores/{storeId}/lightning-addresses")]
|
||||||
public async Task<IActionResult> GetStoreLightningAddresses(string storeId)
|
public async Task<IActionResult> GetStoreLightningAddresses(string storeId)
|
||||||
{
|
{
|
||||||
return Ok((await _lightningAddressService.Get(new LightningAddressQuery() {StoreIds = new[] {storeId}}))
|
return Ok((await _lightningAddressService.Get(new LightningAddressQuery() { StoreIds = new[] { storeId } }))
|
||||||
.Select(ToModel).ToArray());
|
.Select(ToModel).ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +67,8 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
{
|
{
|
||||||
var res = await _lightningAddressService.Get(new LightningAddressQuery()
|
var res = await _lightningAddressService.Get(new LightningAddressQuery()
|
||||||
{
|
{
|
||||||
Usernames = new[] {username}, StoreIds = new[] {storeId},
|
Usernames = new[] { username },
|
||||||
|
StoreIds = new[] { storeId },
|
||||||
});
|
});
|
||||||
return res?.Any() is true ? Ok(ToModel(res.First())) : this.CreateAPIError(404, "lightning-address-not-found", "The lightning address was not present.");
|
return res?.Any() is true ? Ok(ToModel(res.First())) : this.CreateAPIError(404, "lightning-address-not-found", "The lightning address was not present.");
|
||||||
}
|
}
|
||||||
@@ -79,17 +83,17 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
ModelState.AddModelError(nameof(data.Min), "Minimum must be greater than 0 if provided.");
|
ModelState.AddModelError(nameof(data.Min), "Minimum must be greater than 0 if provided.");
|
||||||
return this.CreateValidationError(ModelState);
|
return this.CreateValidationError(ModelState);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await _lightningAddressService.Set(new Data.LightningAddressData()
|
if (await _lightningAddressService.Set(new Data.LightningAddressData()
|
||||||
{
|
{
|
||||||
StoreDataId = storeId,
|
StoreDataId = storeId,
|
||||||
Username = username
|
Username = username
|
||||||
}.SetBlob(new LightningAddressDataBlob()
|
}.SetBlob(new LightningAddressDataBlob()
|
||||||
{
|
{
|
||||||
Max = data.Max,
|
Max = data.Max,
|
||||||
Min = data.Min,
|
Min = data.Min,
|
||||||
CurrencyCode = data.CurrencyCode
|
CurrencyCode = data.CurrencyCode
|
||||||
})))
|
})))
|
||||||
{
|
{
|
||||||
return await GetStoreLightningAddress(storeId, username);
|
return await GetStoreLightningAddress(storeId, username);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,8 +66,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
paymentMethod.GetExternalLightningUrl()?.ToString() ??
|
paymentMethod.GetExternalLightningUrl()?.ToString() ??
|
||||||
paymentMethod.GetDisplayableConnectionString(),
|
paymentMethod.GetDisplayableConnectionString(),
|
||||||
!excludedPaymentMethods.Match(paymentMethod.PaymentId),
|
!excludedPaymentMethods.Match(paymentMethod.PaymentId),
|
||||||
paymentMethod.PaymentId.ToStringNormalized(),
|
paymentMethod.PaymentId.ToStringNormalized()
|
||||||
paymentMethod.DisableBOLT11PaymentOption
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.Where((result) => enabled is null || enabled == result.Enabled)
|
.Where((result) => enabled is null || enabled == result.Enabled)
|
||||||
@@ -207,7 +206,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
? null
|
? null
|
||||||
: new LightningNetworkPaymentMethodData(paymentMethod.PaymentId.CryptoCode,
|
: new LightningNetworkPaymentMethodData(paymentMethod.PaymentId.CryptoCode,
|
||||||
paymentMethod.GetDisplayableConnectionString(), !excluded,
|
paymentMethod.GetDisplayableConnectionString(), !excluded,
|
||||||
paymentMethod.PaymentId.ToStringNormalized(), paymentMethod.DisableBOLT11PaymentOption);
|
paymentMethod.PaymentId.ToStringNormalized());
|
||||||
}
|
}
|
||||||
|
|
||||||
private BTCPayNetwork AssertSupportLightning(string cryptoCode)
|
private BTCPayNetwork AssertSupportLightning(string cryptoCode)
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ namespace BTCPayServer.Controllers.GreenField
|
|||||||
{
|
{
|
||||||
parsedCurrencyPairs = blob.DefaultCurrencyPairs.ToHashSet();
|
parsedCurrencyPairs = blob.DefaultCurrencyPairs.ToHashSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
ValidateAndSanitizeConfiguration(configuration, blob);
|
ValidateAndSanitizeConfiguration(configuration, blob);
|
||||||
if (!ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
return this.CreateValidationError(ModelState);
|
return this.CreateValidationError(ModelState);
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ namespace BTCPayServer.Controllers.GreenField
|
|||||||
_rateProviderFactory = rateProviderFactory;
|
_rateProviderFactory = rateProviderFactory;
|
||||||
_btcPayNetworkProvider = btcPayNetworkProvider;
|
_btcPayNetworkProvider = btcPayNetworkProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("")]
|
[HttpGet("")]
|
||||||
[Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
[Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||||
public async Task<IActionResult> GetStoreRates([FromQuery] string[]? currencyPair)
|
public async Task<IActionResult> GetStoreRates([FromQuery] string[]? currencyPair)
|
||||||
@@ -59,7 +59,7 @@ namespace BTCPayServer.Controllers.GreenField
|
|||||||
{
|
{
|
||||||
parsedCurrencyPairs = blob.DefaultCurrencyPairs.ToHashSet();
|
parsedCurrencyPairs = blob.DefaultCurrencyPairs.ToHashSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var rules = blob.GetRateRules(_btcPayNetworkProvider);
|
var rules = blob.GetRateRules(_btcPayNetworkProvider);
|
||||||
|
|
||||||
|
|||||||
@@ -149,13 +149,13 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
LightningDescriptionTemplate = storeBlob.LightningDescriptionTemplate,
|
LightningDescriptionTemplate = storeBlob.LightningDescriptionTemplate,
|
||||||
PaymentTolerance = storeBlob.PaymentTolerance,
|
PaymentTolerance = storeBlob.PaymentTolerance,
|
||||||
PayJoinEnabled = storeBlob.PayJoinEnabled,
|
PayJoinEnabled = storeBlob.PayJoinEnabled,
|
||||||
PaymentMethodCriteria = storeBlob.PaymentMethodCriteria?.Where(criteria => criteria.Value is not null)?.Select(criteria => new PaymentMethodCriteriaData()
|
PaymentMethodCriteria = storeBlob.PaymentMethodCriteria?.Where(criteria => criteria.Value is not null)?.Select(criteria => new PaymentMethodCriteriaData()
|
||||||
{
|
{
|
||||||
Above = criteria.Above,
|
Above = criteria.Above,
|
||||||
Amount = criteria.Value.Value,
|
Amount = criteria.Value.Value,
|
||||||
CurrencyCode = criteria.Value.Currency,
|
CurrencyCode = criteria.Value.Currency,
|
||||||
PaymentMethod = criteria.PaymentMethod.ToStringNormalized()
|
PaymentMethod = criteria.PaymentMethod.ToStringNormalized()
|
||||||
})?.ToList()?? new List<PaymentMethodCriteriaData>()
|
})?.ToList() ?? new List<PaymentMethodCriteriaData>()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,7 +249,8 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
if (string.IsNullOrEmpty(pmc.CurrencyCode))
|
if (string.IsNullOrEmpty(pmc.CurrencyCode))
|
||||||
{
|
{
|
||||||
request.AddModelError(data => data.PaymentMethodCriteria[index].CurrencyCode, "CurrencyCode is required", this);
|
request.AddModelError(data => data.PaymentMethodCriteria[index].CurrencyCode, "CurrencyCode is required", this);
|
||||||
}else if (CurrencyNameTable.Instance.GetCurrencyData(pmc.CurrencyCode, false) is null)
|
}
|
||||||
|
else if (CurrencyNameTable.Instance.GetCurrencyData(pmc.CurrencyCode, false) is null)
|
||||||
{
|
{
|
||||||
request.AddModelError(data => data.PaymentMethodCriteria[index].CurrencyCode, "CurrencyCode is invalid", this);
|
request.AddModelError(data => data.PaymentMethodCriteria[index].CurrencyCode, "CurrencyCode is invalid", this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,11 +30,11 @@ using LightningAddressData = BTCPayServer.Client.Models.LightningAddressData;
|
|||||||
using NotificationData = BTCPayServer.Client.Models.NotificationData;
|
using NotificationData = BTCPayServer.Client.Models.NotificationData;
|
||||||
using PaymentRequestData = BTCPayServer.Client.Models.PaymentRequestData;
|
using PaymentRequestData = BTCPayServer.Client.Models.PaymentRequestData;
|
||||||
using PayoutData = BTCPayServer.Client.Models.PayoutData;
|
using PayoutData = BTCPayServer.Client.Models.PayoutData;
|
||||||
|
using PayoutProcessorData = BTCPayServer.Client.Models.PayoutProcessorData;
|
||||||
using PullPaymentData = BTCPayServer.Client.Models.PullPaymentData;
|
using PullPaymentData = BTCPayServer.Client.Models.PullPaymentData;
|
||||||
using StoreData = BTCPayServer.Client.Models.StoreData;
|
using StoreData = BTCPayServer.Client.Models.StoreData;
|
||||||
using StoreWebhookData = BTCPayServer.Client.Models.StoreWebhookData;
|
using StoreWebhookData = BTCPayServer.Client.Models.StoreWebhookData;
|
||||||
using WebhookDeliveryData = BTCPayServer.Client.Models.WebhookDeliveryData;
|
using WebhookDeliveryData = BTCPayServer.Client.Models.WebhookDeliveryData;
|
||||||
using PayoutProcessorData = BTCPayServer.Client.Models.PayoutProcessorData;
|
|
||||||
|
|
||||||
namespace BTCPayServer.Controllers.Greenfield
|
namespace BTCPayServer.Controllers.Greenfield
|
||||||
{
|
{
|
||||||
@@ -223,14 +223,14 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
return GetFromActionResult<MarketTradeResponseData>(
|
return GetFromActionResult<MarketTradeResponseData>(
|
||||||
await GetController<GreenfieldCustodianAccountController>().MarketTradeCustodianAccountAsset(storeId, accountId, request, cancellationToken));
|
await GetController<GreenfieldCustodianAccountController>().MarketTradeCustodianAccountAsset(storeId, accountId, request, cancellationToken));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<WithdrawalSimulationResponseData> SimulateCustodianAccountWithdrawal(string storeId, string accountId,
|
public override async Task<WithdrawalSimulationResponseData> SimulateCustodianAccountWithdrawal(string storeId, string accountId,
|
||||||
WithdrawRequestData request, CancellationToken cancellationToken = default)
|
WithdrawRequestData request, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return GetFromActionResult<WithdrawalSimulationResponseData>(
|
return GetFromActionResult<WithdrawalSimulationResponseData>(
|
||||||
await GetController<GreenfieldCustodianAccountController>().SimulateWithdrawal(storeId, accountId, request, cancellationToken));
|
await GetController<GreenfieldCustodianAccountController>().SimulateWithdrawal(storeId, accountId, request, cancellationToken));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<WithdrawalResponseData> CreateCustodianAccountWithdrawal(string storeId, string accountId,
|
public override async Task<WithdrawalResponseData> CreateCustodianAccountWithdrawal(string storeId, string accountId,
|
||||||
WithdrawRequestData request, CancellationToken cancellationToken = default)
|
WithdrawRequestData request, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
@@ -1238,7 +1238,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
return Task.FromResult(GetFromActionResult<StoreRateConfiguration>(GetController<GreenfieldStoreRateConfigurationController>().GetStoreRateConfiguration()));
|
return Task.FromResult(GetFromActionResult<StoreRateConfiguration>(GetController<GreenfieldStoreRateConfigurationController>().GetStoreRateConfiguration()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<List<StoreRateResult>> GetStoreRates (string storeId,
|
public override async Task<List<StoreRateResult>> GetStoreRates(string storeId,
|
||||||
string[] currencyPair, CancellationToken token = default)
|
string[] currencyPair, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
return GetFromActionResult<List<StoreRateResult>>(await GetController<GreenfieldStoreRatesController>().GetStoreRates(currencyPair));
|
return GetFromActionResult<List<StoreRateResult>>(await GetController<GreenfieldStoreRatesController>().GetStoreRates(currencyPair));
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ public class LightningAddressService
|
|||||||
{
|
{
|
||||||
data.Username = NormalizeUsername(data.Username);
|
data.Username = NormalizeUsername(data.Username);
|
||||||
await using var context = _applicationDbContextFactory.CreateContext();
|
await using var context = _applicationDbContextFactory.CreateContext();
|
||||||
var result = (await GetCore(context, new LightningAddressQuery() { Usernames = new[] { data.Username} }))
|
var result = (await GetCore(context, new LightningAddressQuery() { Usernames = new[] { data.Username } }))
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
if (result is not null)
|
if (result is not null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ namespace BTCPayServer.Controllers
|
|||||||
[HttpGet("/cheat/permissions")]
|
[HttpGet("/cheat/permissions")]
|
||||||
[HttpGet("/cheat/permissions/stores/{storeId}")]
|
[HttpGet("/cheat/permissions/stores/{storeId}")]
|
||||||
[CheatModeRoute]
|
[CheatModeRoute]
|
||||||
public async Task<IActionResult> CheatPermissions([FromServices]IAuthorizationService authorizationService, string storeId = null)
|
public async Task<IActionResult> CheatPermissions([FromServices] IAuthorizationService authorizationService, string storeId = null)
|
||||||
{
|
{
|
||||||
var vm = new CheatPermissionsViewModel();
|
var vm = new CheatPermissionsViewModel();
|
||||||
vm.StoreId = storeId;
|
vm.StoreId = storeId;
|
||||||
@@ -790,7 +790,7 @@ namespace BTCPayServer.Controllers
|
|||||||
if (matchedDomainMapping is not null)
|
if (matchedDomainMapping is not null)
|
||||||
return RedirectToAction(nameof(UIHomeController.Home), "UIHome");
|
return RedirectToAction(nameof(UIHomeController.Home), "UIHome");
|
||||||
}
|
}
|
||||||
|
|
||||||
return RedirectToAction(nameof(UIHomeController.Index), "UIHome");
|
return RedirectToAction(nameof(UIHomeController.Index), "UIHome");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace BTCPayServer.Controllers
|
|||||||
var app = await _appService.GetApp(appId, null);
|
var app = await _appService.GetApp(appId, null);
|
||||||
if (app is null)
|
if (app is null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
|
||||||
var res = await _appService.ViewLink(app);
|
var res = await _appService.ViewLink(app);
|
||||||
if (res is null)
|
if (res is null)
|
||||||
{
|
{
|
||||||
@@ -150,11 +150,11 @@ namespace BTCPayServer.Controllers
|
|||||||
var defaultCurrency = await GetStoreDefaultCurrentIfEmpty(appData.StoreDataId, null);
|
var defaultCurrency = await GetStoreDefaultCurrentIfEmpty(appData.StoreDataId, null);
|
||||||
await _appService.SetDefaultSettings(appData, defaultCurrency);
|
await _appService.SetDefaultSettings(appData, defaultCurrency);
|
||||||
await _appService.UpdateOrCreateApp(appData);
|
await _appService.UpdateOrCreateApp(appData);
|
||||||
|
|
||||||
TempData[WellKnownTempData.SuccessMessage] = "App successfully created";
|
TempData[WellKnownTempData.SuccessMessage] = "App successfully created";
|
||||||
CreatedAppId = appData.Id;
|
CreatedAppId = appData.Id;
|
||||||
|
|
||||||
|
|
||||||
var url = await type.ConfigureLink(appData);
|
var url = await type.ConfigureLink(appData);
|
||||||
return Redirect(url);
|
return Redirect(url);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ namespace BTCPayServer.Controllers
|
|||||||
var metaData = PosDataParser.ParsePosData(invoice.Metadata.ToJObject());
|
var metaData = PosDataParser.ParsePosData(invoice.Metadata.ToJObject());
|
||||||
var additionalData = metaData
|
var additionalData = metaData
|
||||||
.Where(dict => !InvoiceAdditionalDataExclude.Contains(dict.Key))
|
.Where(dict => !InvoiceAdditionalDataExclude.Contains(dict.Key))
|
||||||
.ToDictionary(dict=> dict.Key, dict=> dict.Value);
|
.ToDictionary(dict => dict.Key, dict => dict.Value);
|
||||||
var model = new InvoiceDetailsModel
|
var model = new InvoiceDetailsModel
|
||||||
{
|
{
|
||||||
StoreId = store.Id,
|
StoreId = store.Id,
|
||||||
@@ -184,8 +184,14 @@ namespace BTCPayServer.Controllers
|
|||||||
var receipt = InvoiceDataBase.ReceiptOptions.Merge(store.GetStoreBlob().ReceiptOptions, i.ReceiptOptions);
|
var receipt = InvoiceDataBase.ReceiptOptions.Merge(store.GetStoreBlob().ReceiptOptions, i.ReceiptOptions);
|
||||||
|
|
||||||
if (receipt.Enabled is not true)
|
if (receipt.Enabled is not true)
|
||||||
|
{
|
||||||
|
if (i.RedirectURL is not null)
|
||||||
|
{
|
||||||
|
return Redirect(i.RedirectURL.ToString());
|
||||||
|
}
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
|
||||||
|
}
|
||||||
var storeBlob = store.GetStoreBlob();
|
var storeBlob = store.GetStoreBlob();
|
||||||
var vm = new InvoiceReceiptViewModel
|
var vm = new InvoiceReceiptViewModel
|
||||||
{
|
{
|
||||||
@@ -201,12 +207,12 @@ namespace BTCPayServer.Controllers
|
|||||||
CssFileId = storeBlob.CssFileId,
|
CssFileId = storeBlob.CssFileId,
|
||||||
ReceiptOptions = receipt
|
ReceiptOptions = receipt
|
||||||
};
|
};
|
||||||
|
|
||||||
if (i.Status.ToModernStatus() != InvoiceStatus.Settled)
|
if (i.Status.ToModernStatus() != InvoiceStatus.Settled)
|
||||||
{
|
{
|
||||||
return View(vm);
|
return View(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
JToken? receiptData = null;
|
JToken? receiptData = null;
|
||||||
i.Metadata?.AdditionalData?.TryGetValue("receiptData", out receiptData);
|
i.Metadata?.AdditionalData?.TryGetValue("receiptData", out receiptData);
|
||||||
|
|
||||||
@@ -238,7 +244,7 @@ namespace BTCPayServer.Controllers
|
|||||||
Link = link,
|
Link = link,
|
||||||
Id = txId,
|
Id = txId,
|
||||||
Destination = paymentData.GetDestination(),
|
Destination = paymentData.GetDestination(),
|
||||||
PaymentProof = GetPaymentProof(paymentData),
|
PaymentProof = paymentData.GetPaymentProof(),
|
||||||
PaymentType = paymentData.GetPaymentType()
|
PaymentType = paymentData.GetPaymentType()
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
@@ -252,16 +258,6 @@ namespace BTCPayServer.Controllers
|
|||||||
return View(vm);
|
return View(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string? GetPaymentProof(CryptoPaymentData paymentData)
|
|
||||||
{
|
|
||||||
return paymentData switch
|
|
||||||
{
|
|
||||||
BitcoinLikePaymentData b => b.Outpoint.ToString(),
|
|
||||||
LightningLikePaymentData l => l.Preimage?.ToString(),
|
|
||||||
_ => null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private string? GetTransactionLink(PaymentMethodId paymentMethodId, string txId)
|
private string? GetTransactionLink(PaymentMethodId paymentMethodId, string txId)
|
||||||
{
|
{
|
||||||
var network = _NetworkProvider.GetNetwork(paymentMethodId.CryptoCode);
|
var network = _NetworkProvider.GetNetwork(paymentMethodId.CryptoCode);
|
||||||
@@ -676,48 +672,55 @@ namespace BTCPayServer.Controllers
|
|||||||
var btcId = PaymentMethodId.Parse("BTC");
|
var btcId = PaymentMethodId.Parse("BTC");
|
||||||
var lnId = PaymentMethodId.Parse("BTC_LightningLike");
|
var lnId = PaymentMethodId.Parse("BTC_LightningLike");
|
||||||
var lnurlId = PaymentMethodId.Parse("BTC_LNURLPAY");
|
var lnurlId = PaymentMethodId.Parse("BTC_LNURLPAY");
|
||||||
|
|
||||||
|
|
||||||
|
var displayedPaymentMethods = invoice.GetPaymentMethods().Select(p => p.GetId()).ToList();
|
||||||
|
|
||||||
|
// Exclude Lightning if OnChainWithLnInvoiceFallback is active and we have both payment methods
|
||||||
|
if (storeBlob is { OnChainWithLnInvoiceFallback: true } &&
|
||||||
|
displayedPaymentMethods.Contains(btcId))
|
||||||
|
{
|
||||||
|
displayedPaymentMethods.Remove(lnId);
|
||||||
|
displayedPaymentMethods.Remove(lnurlId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOLT11 doesn't really support payment without amount
|
||||||
|
if (invoice.IsUnsetTopUp())
|
||||||
|
displayedPaymentMethods.Remove(lnId);
|
||||||
|
|
||||||
|
// Exclude lnurl if bolt11 is available
|
||||||
|
if (displayedPaymentMethods.Contains(lnId) && displayedPaymentMethods.Contains(lnurlId))
|
||||||
|
displayedPaymentMethods.Remove(lnurlId);
|
||||||
|
|
||||||
|
if (paymentMethodId is not null && !displayedPaymentMethods.Contains(paymentMethodId))
|
||||||
|
paymentMethodId = null;
|
||||||
if (paymentMethodId is null)
|
if (paymentMethodId is null)
|
||||||
{
|
{
|
||||||
var enabledPaymentIds = store.GetEnabledPaymentIds(_NetworkProvider).ToArray();
|
|
||||||
|
|
||||||
// Exclude Lightning if OnChainWithLnInvoiceFallback is active and we have both payment methods
|
|
||||||
if (storeBlob is { CheckoutType: CheckoutType.V2, OnChainWithLnInvoiceFallback: true })
|
|
||||||
{
|
|
||||||
if (enabledPaymentIds.Contains(btcId) && enabledPaymentIds.Contains(lnId))
|
|
||||||
{
|
|
||||||
enabledPaymentIds = enabledPaymentIds.Where(pmId => pmId != lnId).ToArray();
|
|
||||||
}
|
|
||||||
if (enabledPaymentIds.Contains(btcId) && enabledPaymentIds.Contains(lnurlId))
|
|
||||||
{
|
|
||||||
enabledPaymentIds = enabledPaymentIds.Where(pmId => pmId != lnurlId).ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PaymentMethodId? invoicePaymentId = invoice.GetDefaultPaymentMethod();
|
PaymentMethodId? invoicePaymentId = invoice.GetDefaultPaymentMethod();
|
||||||
PaymentMethodId? storePaymentId = store.GetDefaultPaymentId();
|
PaymentMethodId? storePaymentId = store.GetDefaultPaymentId();
|
||||||
if (invoicePaymentId is not null)
|
if (invoicePaymentId is not null)
|
||||||
{
|
{
|
||||||
if (enabledPaymentIds.Contains(invoicePaymentId))
|
if (displayedPaymentMethods.Contains(invoicePaymentId))
|
||||||
paymentMethodId = invoicePaymentId;
|
paymentMethodId = invoicePaymentId;
|
||||||
}
|
}
|
||||||
if (paymentMethodId is null && storePaymentId is not null)
|
if (paymentMethodId is null && storePaymentId is not null)
|
||||||
{
|
{
|
||||||
if (enabledPaymentIds.Contains(storePaymentId))
|
if (displayedPaymentMethods.Contains(storePaymentId))
|
||||||
paymentMethodId = storePaymentId;
|
paymentMethodId = storePaymentId;
|
||||||
}
|
}
|
||||||
if (paymentMethodId is null && invoicePaymentId is not null)
|
if (paymentMethodId is null && invoicePaymentId is not null)
|
||||||
{
|
{
|
||||||
paymentMethodId = invoicePaymentId.FindNearest(enabledPaymentIds);
|
paymentMethodId = invoicePaymentId.FindNearest(displayedPaymentMethods);
|
||||||
}
|
}
|
||||||
if (paymentMethodId is null && storePaymentId is not null)
|
if (paymentMethodId is null && storePaymentId is not null)
|
||||||
{
|
{
|
||||||
paymentMethodId = storePaymentId.FindNearest(enabledPaymentIds);
|
paymentMethodId = storePaymentId.FindNearest(displayedPaymentMethods);
|
||||||
}
|
}
|
||||||
if (paymentMethodId is null)
|
if (paymentMethodId is null)
|
||||||
{
|
{
|
||||||
paymentMethodId = enabledPaymentIds.FirstOrDefault(e => e.CryptoCode == _NetworkProvider.DefaultNetwork.CryptoCode && e.PaymentType == PaymentTypes.BTCLike) ??
|
paymentMethodId = displayedPaymentMethods.FirstOrDefault(e => e.CryptoCode == _NetworkProvider.DefaultNetwork.CryptoCode && e.PaymentType == PaymentTypes.BTCLike) ??
|
||||||
enabledPaymentIds.FirstOrDefault(e => e.CryptoCode == _NetworkProvider.DefaultNetwork.CryptoCode && e.PaymentType != PaymentTypes.LNURLPay) ??
|
displayedPaymentMethods.FirstOrDefault(e => e.CryptoCode == _NetworkProvider.DefaultNetwork.CryptoCode && e.PaymentType != PaymentTypes.LNURLPay) ??
|
||||||
enabledPaymentIds.FirstOrDefault();
|
displayedPaymentMethods.FirstOrDefault();
|
||||||
}
|
}
|
||||||
isDefaultPaymentId = true;
|
isDefaultPaymentId = true;
|
||||||
}
|
}
|
||||||
@@ -731,30 +734,37 @@ namespace BTCPayServer.Controllers
|
|||||||
return null;
|
return null;
|
||||||
var paymentMethodTemp = invoice
|
var paymentMethodTemp = invoice
|
||||||
.GetPaymentMethods()
|
.GetPaymentMethods()
|
||||||
.FirstOrDefault(pm =>
|
.Where(p => displayedPaymentMethods.Contains(p.GetId()))
|
||||||
{
|
.FirstOrDefault();
|
||||||
var pmId = pm.GetId();
|
|
||||||
return paymentMethodId.CryptoCode == pmId.CryptoCode &&
|
|
||||||
((invoice.IsUnsetTopUp() && !storeBlob.OnChainWithLnInvoiceFallback) || pmId != lnurlId);
|
|
||||||
});
|
|
||||||
if (paymentMethodTemp == null)
|
|
||||||
paymentMethodTemp = invoice.GetPaymentMethods().FirstOrDefault();
|
|
||||||
if (paymentMethodTemp is null)
|
if (paymentMethodTemp is null)
|
||||||
return null;
|
return null;
|
||||||
network = paymentMethodTemp.Network;
|
network = paymentMethodTemp.Network;
|
||||||
paymentMethodId = paymentMethodTemp.GetId();
|
paymentMethodId = paymentMethodTemp.GetId();
|
||||||
}
|
}
|
||||||
|
|
||||||
var paymentMethod = invoice.GetPaymentMethod(paymentMethodId);
|
|
||||||
var paymentMethodDetails = paymentMethod.GetPaymentMethodDetails();
|
// We activate the default payment method, and also those which aren't displayed (as they can't be set as default)
|
||||||
if (!paymentMethodDetails.Activated)
|
bool activated = false;
|
||||||
|
foreach (var pm in invoice.GetPaymentMethods())
|
||||||
{
|
{
|
||||||
if (await _invoiceActivator.ActivateInvoicePaymentMethod(paymentMethod.GetId(), invoice, store))
|
var pmi = pm.GetId();
|
||||||
|
if (pmi != paymentMethodId || !displayedPaymentMethods.Contains(pmi))
|
||||||
|
continue;
|
||||||
|
var pmd = pm.GetPaymentMethodDetails();
|
||||||
|
if (!pmd.Activated)
|
||||||
{
|
{
|
||||||
return await GetInvoiceModel(invoiceId, paymentMethodId, lang);
|
if (await _invoiceActivator.ActivateInvoicePaymentMethod(pmi, invoice, store))
|
||||||
|
{
|
||||||
|
activated = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (activated)
|
||||||
|
return await GetInvoiceModel(invoiceId, paymentMethodId, lang);
|
||||||
|
|
||||||
|
|
||||||
|
var paymentMethod = invoice.GetPaymentMethod(paymentMethodId);
|
||||||
|
var paymentMethodDetails = paymentMethod.GetPaymentMethodDetails();
|
||||||
var dto = invoice.EntityToDTO();
|
var dto = invoice.EntityToDTO();
|
||||||
var accounting = paymentMethod.Calculate();
|
var accounting = paymentMethod.Calculate();
|
||||||
var paymentMethodHandler = _paymentMethodHandlerDictionary[paymentMethodId];
|
var paymentMethodHandler = _paymentMethodHandlerDictionary[paymentMethodId];
|
||||||
@@ -764,7 +774,7 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
case "auto":
|
case "auto":
|
||||||
case null when storeBlob.AutoDetectLanguage:
|
case null when storeBlob.AutoDetectLanguage:
|
||||||
lang = _languageService.AutoDetectLanguageUsingHeader(HttpContext.Request.Headers, null).Code;
|
lang = _languageService.AutoDetectLanguageUsingHeader(HttpContext.Request.Headers, null)?.Code;
|
||||||
break;
|
break;
|
||||||
case { } langs when !string.IsNullOrEmpty(langs):
|
case { } langs when !string.IsNullOrEmpty(langs):
|
||||||
{
|
{
|
||||||
@@ -783,11 +793,13 @@ namespace BTCPayServer.Controllers
|
|||||||
Request.Host,
|
Request.Host,
|
||||||
Request.PathBase) : null;
|
Request.PathBase) : null;
|
||||||
|
|
||||||
|
var isAltcoinsBuild = false;
|
||||||
|
#if ALTCOINS
|
||||||
|
isAltcoinsBuild = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
var model = new PaymentModel
|
var model = new PaymentModel
|
||||||
{
|
{
|
||||||
#if ALTCOINS
|
|
||||||
AltcoinsBuild = true,
|
|
||||||
#endif
|
|
||||||
Activated = paymentMethodDetails.Activated,
|
Activated = paymentMethodDetails.Activated,
|
||||||
CryptoCode = network.CryptoCode,
|
CryptoCode = network.CryptoCode,
|
||||||
RootPath = Request.PathBase.Value.WithTrailingSlash(),
|
RootPath = Request.PathBase.Value.WithTrailingSlash(),
|
||||||
@@ -852,11 +864,15 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
var availableCryptoPaymentMethodId = kv.GetId();
|
var availableCryptoPaymentMethodId = kv.GetId();
|
||||||
var availableCryptoHandler = _paymentMethodHandlerDictionary[availableCryptoPaymentMethodId];
|
var availableCryptoHandler = _paymentMethodHandlerDictionary[availableCryptoPaymentMethodId];
|
||||||
|
var pmName = availableCryptoHandler.GetPaymentMethodName(availableCryptoPaymentMethodId);
|
||||||
return new PaymentModel.AvailableCrypto
|
return new PaymentModel.AvailableCrypto
|
||||||
{
|
{
|
||||||
|
Displayed = displayedPaymentMethods.Contains(kv.GetId()),
|
||||||
PaymentMethodId = kv.GetId().ToString(),
|
PaymentMethodId = kv.GetId().ToString(),
|
||||||
CryptoCode = kv.Network?.CryptoCode ?? kv.GetId().CryptoCode,
|
CryptoCode = kv.Network?.CryptoCode ?? kv.GetId().CryptoCode,
|
||||||
PaymentMethodName = availableCryptoHandler.GetPaymentMethodName(availableCryptoPaymentMethodId),
|
PaymentMethodName = isAltcoinsBuild
|
||||||
|
? pmName
|
||||||
|
: pmName.Replace("Bitcoin (", "").Replace(")", "").Replace("Lightning ", ""),
|
||||||
IsLightning =
|
IsLightning =
|
||||||
kv.GetId().PaymentType == PaymentTypes.LightningLike,
|
kv.GetId().PaymentType == PaymentTypes.LightningLike,
|
||||||
CryptoImage = Request.GetRelativePathOrAbsolute(availableCryptoHandler.GetCryptoImage(availableCryptoPaymentMethodId)),
|
CryptoImage = Request.GetRelativePathOrAbsolute(availableCryptoHandler.GetCryptoImage(availableCryptoPaymentMethodId)),
|
||||||
@@ -872,17 +888,6 @@ namespace BTCPayServer.Controllers
|
|||||||
.ToList()
|
.ToList()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Exclude Lightning if OnChainWithLnInvoiceFallback is active and we have both payment methods
|
|
||||||
if (storeBlob is { CheckoutType: CheckoutType.V2, OnChainWithLnInvoiceFallback: true })
|
|
||||||
{
|
|
||||||
var onchainPM = model.AvailableCryptos.Find(c => c.PaymentMethodId == btcId.ToString());
|
|
||||||
var lightningPM = model.AvailableCryptos.Find(c => c.PaymentMethodId == lnId.ToString());
|
|
||||||
if (onchainPM != null && lightningPM != null)
|
|
||||||
{
|
|
||||||
model.AvailableCryptos.Remove(lightningPM);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
paymentMethodHandler.PreparePaymentModel(model, dto, storeBlob, paymentMethod);
|
paymentMethodHandler.PreparePaymentModel(model, dto, storeBlob, paymentMethod);
|
||||||
model.UISettings = paymentMethodHandler.GetCheckoutUISettings();
|
model.UISettings = paymentMethodHandler.GetCheckoutUISettings();
|
||||||
model.PaymentMethodId = paymentMethodId.ToString();
|
model.PaymentMethodId = paymentMethodId.ToString();
|
||||||
@@ -897,22 +902,22 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
var currency = invoiceEntity.Currency;
|
var currency = invoiceEntity.Currency;
|
||||||
var crypto = cryptoCode.ToUpperInvariant(); // uppercase to make comparison easier, might be "sats"
|
var crypto = cryptoCode.ToUpperInvariant(); // uppercase to make comparison easier, might be "sats"
|
||||||
|
|
||||||
// if invoice source currency is the same as currently display currency, no need for "order amount from invoice"
|
// if invoice source currency is the same as currently display currency, no need for "order amount from invoice"
|
||||||
if (crypto == currency || (crypto == "SATS" && currency == "BTC") || (crypto == "BTC" && currency == "SATS"))
|
if (crypto == currency || (crypto == "SATS" && currency == "BTC") || (crypto == "BTC" && currency == "SATS"))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return _displayFormatter.Currency(invoiceEntity.Price, currency, format);
|
return _displayFormatter.Currency(invoiceEntity.Price, currency, format);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string? ExchangeRate(string cryptoCode, PaymentMethod paymentMethod, DisplayFormatter.CurrencyFormat format = DisplayFormatter.CurrencyFormat.Code)
|
private string? ExchangeRate(string cryptoCode, PaymentMethod paymentMethod, DisplayFormatter.CurrencyFormat format = DisplayFormatter.CurrencyFormat.Code)
|
||||||
{
|
{
|
||||||
var currency = paymentMethod.ParentEntity.Currency;
|
var currency = paymentMethod.ParentEntity.Currency;
|
||||||
var crypto = cryptoCode.ToUpperInvariant(); // uppercase to make comparison easier, might be "sats"
|
var crypto = cryptoCode.ToUpperInvariant(); // uppercase to make comparison easier, might be "sats"
|
||||||
|
|
||||||
if (crypto == currency || (crypto == "SATS" && currency == "BTC") || (crypto == "BTC" && currency == "SATS"))
|
if (crypto == currency || (crypto == "SATS" && currency == "BTC") || (crypto == "BTC" && currency == "SATS"))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return _displayFormatter.Currency(paymentMethod.Rate, currency, format);
|
return _displayFormatter.Currency(paymentMethod.Rate, currency, format);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1296,10 +1301,20 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
case JTokenType.Array:
|
case JTokenType.Array:
|
||||||
var items = item.Value.AsEnumerable().ToList();
|
var items = item.Value.AsEnumerable().ToList();
|
||||||
|
var arrayResult = new List<object>();
|
||||||
for (var i = 0; i < items.Count; i++)
|
for (var i = 0; i < items.Count; i++)
|
||||||
{
|
{
|
||||||
result.TryAdd($"{item.Key}[{i}]", ParsePosData(items[i]));
|
if (items[i] is JObject)
|
||||||
|
{
|
||||||
|
arrayResult.Add( ParsePosData(items[i]));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
arrayResult.Add( items[i].ToString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result.TryAdd(item.Key, arrayResult);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case JTokenType.Object:
|
case JTokenType.Object:
|
||||||
|
|||||||
@@ -187,33 +187,36 @@ namespace BTCPayServer.Controllers
|
|||||||
return await CreateInvoiceCoreRaw(entity, store, excludeFilter, null, cancellationToken, entityManipulator);
|
return await CreateInvoiceCoreRaw(entity, store, excludeFilter, null, cancellationToken, entityManipulator);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<InvoiceEntity> CreatePaymentRequestInvoice(ViewPaymentRequestViewModel pr, decimal? amount, StoreData storeData, HttpRequest request, CancellationToken cancellationToken)
|
internal async Task<InvoiceEntity> CreatePaymentRequestInvoice(Data.PaymentRequestData prData, decimal? amount, decimal amountDue, StoreData storeData, HttpRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (pr.AllowCustomPaymentAmounts && amount != null)
|
var id = prData.Id;
|
||||||
amount = Math.Min(pr.AmountDue, amount.Value);
|
var prBlob = prData.GetBlob();
|
||||||
|
if (prBlob.AllowCustomPaymentAmounts && amount != null)
|
||||||
|
amount = Math.Min(amountDue, amount.Value);
|
||||||
else
|
else
|
||||||
amount = pr.AmountDue;
|
amount = amountDue;
|
||||||
var redirectUrl = _linkGenerator.PaymentRequestLink(pr.Id, request.Scheme, request.Host, request.PathBase);
|
var redirectUrl = _linkGenerator.PaymentRequestLink(id, request.Scheme, request.Host, request.PathBase);
|
||||||
|
|
||||||
var invoiceMetadata =
|
JObject invoiceMetadata = prData.GetBlob()?.FormResponse ?? new JObject();
|
||||||
new InvoiceMetadata
|
invoiceMetadata.Merge(new InvoiceMetadata
|
||||||
{
|
{
|
||||||
OrderId = PaymentRequestRepository.GetOrderIdForPaymentRequest(pr.Id),
|
OrderId = PaymentRequestRepository.GetOrderIdForPaymentRequest(id),
|
||||||
PaymentRequestId = pr.Id,
|
PaymentRequestId = id,
|
||||||
BuyerEmail = pr.Email
|
BuyerEmail = invoiceMetadata.TryGetValue("buyerEmail", out var formEmail) && formEmail.Type == JTokenType.String ? formEmail.Value<string>():
|
||||||
};
|
string.IsNullOrEmpty(prBlob.Email) ? null : prBlob.Email
|
||||||
|
}.ToJObject(), new JsonMergeSettings() { MergeNullValueHandling = MergeNullValueHandling.Ignore });
|
||||||
|
|
||||||
var invoiceRequest =
|
var invoiceRequest =
|
||||||
new CreateInvoiceRequest
|
new CreateInvoiceRequest
|
||||||
{
|
{
|
||||||
Metadata = invoiceMetadata.ToJObject(),
|
Metadata = invoiceMetadata,
|
||||||
Currency = pr.Currency,
|
Currency = prBlob.Currency,
|
||||||
Amount = amount,
|
Amount = amount,
|
||||||
Checkout = { RedirectURL = redirectUrl },
|
Checkout = { RedirectURL = redirectUrl },
|
||||||
Receipt = new InvoiceDataBase.ReceiptOptions { Enabled = false }
|
Receipt = new InvoiceDataBase.ReceiptOptions { Enabled = false }
|
||||||
};
|
};
|
||||||
|
|
||||||
var additionalTags = new List<string> { PaymentRequestRepository.GetInternalTag(pr.Id) };
|
var additionalTags = new List<string> { PaymentRequestRepository.GetInternalTag(id) };
|
||||||
return await CreateInvoiceCoreRaw(invoiceRequest, storeData, request.GetAbsoluteRoot(), additionalTags, cancellationToken);
|
return await CreateInvoiceCoreRaw(invoiceRequest, storeData, request.GetAbsoluteRoot(), additionalTags, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,6 +249,7 @@ namespace BTCPayServer.Controllers
|
|||||||
entity.RedirectAutomatically = invoice.Checkout.RedirectAutomatically ?? storeBlob.RedirectAutomatically;
|
entity.RedirectAutomatically = invoice.Checkout.RedirectAutomatically ?? storeBlob.RedirectAutomatically;
|
||||||
entity.CheckoutType = invoice.Checkout.CheckoutType;
|
entity.CheckoutType = invoice.Checkout.CheckoutType;
|
||||||
entity.RequiresRefundEmail = invoice.Checkout.RequiresRefundEmail;
|
entity.RequiresRefundEmail = invoice.Checkout.RequiresRefundEmail;
|
||||||
|
entity.LazyPaymentMethods = invoice.Checkout.LazyPaymentMethods ?? storeBlob.LazyPaymentMethods;
|
||||||
IPaymentFilter? excludeFilter = null;
|
IPaymentFilter? excludeFilter = null;
|
||||||
if (invoice.Checkout.PaymentMethods != null)
|
if (invoice.Checkout.PaymentMethods != null)
|
||||||
{
|
{
|
||||||
@@ -458,7 +462,7 @@ namespace BTCPayServer.Controllers
|
|||||||
var storeBlob = store.GetStoreBlob();
|
var storeBlob = store.GetStoreBlob();
|
||||||
|
|
||||||
// Checkout v2 does not show a payment method switch for Bitcoin-only + BIP21, so exclude that case
|
// Checkout v2 does not show a payment method switch for Bitcoin-only + BIP21, so exclude that case
|
||||||
var preparePayment = storeBlob.LazyPaymentMethods && !storeBlob.OnChainWithLnInvoiceFallback
|
var preparePayment = entity.LazyPaymentMethods && !storeBlob.OnChainWithLnInvoiceFallback
|
||||||
? null
|
? null
|
||||||
: handler.PreparePayment(supportedPaymentMethod, store, network);
|
: handler.PreparePayment(supportedPaymentMethod, store, network);
|
||||||
var rate = await fetchingByCurrencyPair[new CurrencyPair(network.CryptoCode, entity.Currency)];
|
var rate = await fetchingByCurrencyPair[new CurrencyPair(network.CryptoCode, entity.Currency)];
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ using BTCPayServer.Data.Payouts.LightningLike;
|
|||||||
using BTCPayServer.Events;
|
using BTCPayServer.Events;
|
||||||
using BTCPayServer.HostedServices;
|
using BTCPayServer.HostedServices;
|
||||||
using BTCPayServer.Lightning;
|
using BTCPayServer.Lightning;
|
||||||
|
using BTCPayServer.Logging;
|
||||||
using BTCPayServer.Payments;
|
using BTCPayServer.Payments;
|
||||||
using BTCPayServer.Payments.Lightning;
|
using BTCPayServer.Payments.Lightning;
|
||||||
using BTCPayServer.Plugins.Crowdfund;
|
using BTCPayServer.Plugins.Crowdfund;
|
||||||
@@ -58,6 +59,7 @@ namespace BTCPayServer
|
|||||||
private readonly PullPaymentHostedService _pullPaymentHostedService;
|
private readonly PullPaymentHostedService _pullPaymentHostedService;
|
||||||
private readonly BTCPayNetworkJsonSerializerSettings _btcPayNetworkJsonSerializerSettings;
|
private readonly BTCPayNetworkJsonSerializerSettings _btcPayNetworkJsonSerializerSettings;
|
||||||
private readonly IPluginHookService _pluginHookService;
|
private readonly IPluginHookService _pluginHookService;
|
||||||
|
private readonly InvoiceActivator _invoiceActivator;
|
||||||
|
|
||||||
public UILNURLController(InvoiceRepository invoiceRepository,
|
public UILNURLController(InvoiceRepository invoiceRepository,
|
||||||
EventAggregator eventAggregator,
|
EventAggregator eventAggregator,
|
||||||
@@ -71,7 +73,8 @@ namespace BTCPayServer
|
|||||||
LightningLikePayoutHandler lightningLikePayoutHandler,
|
LightningLikePayoutHandler lightningLikePayoutHandler,
|
||||||
PullPaymentHostedService pullPaymentHostedService,
|
PullPaymentHostedService pullPaymentHostedService,
|
||||||
BTCPayNetworkJsonSerializerSettings btcPayNetworkJsonSerializerSettings,
|
BTCPayNetworkJsonSerializerSettings btcPayNetworkJsonSerializerSettings,
|
||||||
IPluginHookService pluginHookService)
|
IPluginHookService pluginHookService,
|
||||||
|
InvoiceActivator invoiceActivator)
|
||||||
{
|
{
|
||||||
_invoiceRepository = invoiceRepository;
|
_invoiceRepository = invoiceRepository;
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
@@ -86,6 +89,7 @@ namespace BTCPayServer
|
|||||||
_pullPaymentHostedService = pullPaymentHostedService;
|
_pullPaymentHostedService = pullPaymentHostedService;
|
||||||
_btcPayNetworkJsonSerializerSettings = btcPayNetworkJsonSerializerSettings;
|
_btcPayNetworkJsonSerializerSettings = btcPayNetworkJsonSerializerSettings;
|
||||||
_pluginHookService = pluginHookService;
|
_pluginHookService = pluginHookService;
|
||||||
|
_invoiceActivator = invoiceActivator;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("withdraw/pp/{pullPaymentId}")]
|
[HttpGet("withdraw/pp/{pullPaymentId}")]
|
||||||
@@ -192,7 +196,7 @@ namespace BTCPayServer
|
|||||||
case PayResult.Error:
|
case PayResult.Error:
|
||||||
default:
|
default:
|
||||||
await _pullPaymentHostedService.Cancel(
|
await _pullPaymentHostedService.Cancel(
|
||||||
new PullPaymentHostedService.CancelRequest(new []
|
new PullPaymentHostedService.CancelRequest(new[]
|
||||||
{ claimResponse.PayoutData.Id }, null));
|
{ claimResponse.PayoutData.Id }, null));
|
||||||
|
|
||||||
return BadRequest(new LNUrlStatusResponse
|
return BadRequest(new LNUrlStatusResponse
|
||||||
@@ -305,7 +309,7 @@ namespace BTCPayServer
|
|||||||
};
|
};
|
||||||
|
|
||||||
var invoiceMetadata = new InvoiceMetadata();
|
var invoiceMetadata = new InvoiceMetadata();
|
||||||
invoiceMetadata.OrderId =AppService.GetAppOrderId(app);
|
invoiceMetadata.OrderId = AppService.GetAppOrderId(app);
|
||||||
if (item != null)
|
if (item != null)
|
||||||
{
|
{
|
||||||
invoiceMetadata.ItemCode = item.Id;
|
invoiceMetadata.ItemCode = item.Id;
|
||||||
@@ -355,8 +359,8 @@ namespace BTCPayServer
|
|||||||
public string InvoiceMetadata { get; set; }
|
public string InvoiceMetadata { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConcurrentDictionary<string, LightningAddressItem> Items { get; } = new ();
|
public ConcurrentDictionary<string, LightningAddressItem> Items { get; } = new();
|
||||||
public ConcurrentDictionary<string, string[]> StoreToItemMap { get; } = new ();
|
public ConcurrentDictionary<string, string[]> StoreToItemMap { get; } = new();
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
@@ -435,12 +439,16 @@ namespace BTCPayServer
|
|||||||
List<string> additionalTags = null,
|
List<string> additionalTags = null,
|
||||||
bool allowOverpay = true)
|
bool allowOverpay = true)
|
||||||
{
|
{
|
||||||
if (GetLNUrlPaymentMethodId(cryptoCode, store, out _) is null)
|
var pmi = GetLNUrlPaymentMethodId(cryptoCode, store, out _);
|
||||||
|
if (pmi is null)
|
||||||
return NotFound("LNUrl or LN is disabled");
|
return NotFound("LNUrl or LN is disabled");
|
||||||
|
|
||||||
InvoiceEntity i;
|
InvoiceEntity i;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
createInvoice.Checkout ??= new InvoiceDataBase.CheckoutOptions();
|
||||||
|
createInvoice.Checkout.LazyPaymentMethods = false;
|
||||||
|
createInvoice.Checkout.PaymentMethods = new[] { pmi.ToStringNormalized() };
|
||||||
i = await _invoiceController.CreateInvoiceCoreRaw(createInvoice, store, Request.GetAbsoluteRoot(), additionalTags);
|
i = await _invoiceController.CreateInvoiceCoreRaw(createInvoice, store, Request.GetAbsoluteRoot(), additionalTags);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -466,13 +474,15 @@ namespace BTCPayServer
|
|||||||
lnurlRequest ??= new LNURLPayRequest();
|
lnurlRequest ??= new LNURLPayRequest();
|
||||||
lnUrlMetadata ??= new Dictionary<string, string>();
|
lnUrlMetadata ??= new Dictionary<string, string>();
|
||||||
|
|
||||||
if (lnUrlMetadata?.TryGetValue("text/identifier", out var lnAddress) is true && lnAddress is string)
|
var pm = i.GetPaymentMethod(pmi);
|
||||||
|
if (pm is null)
|
||||||
|
return null;
|
||||||
|
var paymentMethodDetails = (LNURLPayPaymentMethodDetails)pm.GetPaymentMethodDetails();
|
||||||
|
bool updatePaymentMethodDetails = false;
|
||||||
|
if (lnUrlMetadata?.TryGetValue("text/identifier", out var lnAddress) is true && lnAddress is not null)
|
||||||
{
|
{
|
||||||
var pm = i.GetPaymentMethod(pmi);
|
|
||||||
var paymentMethodDetails = (LNURLPayPaymentMethodDetails)pm.GetPaymentMethodDetails();
|
|
||||||
paymentMethodDetails.ConsumedLightningAddress = lnAddress;
|
paymentMethodDetails.ConsumedLightningAddress = lnAddress;
|
||||||
pm.SetPaymentMethodDetails(paymentMethodDetails);
|
updatePaymentMethodDetails = true;
|
||||||
await _invoiceRepository.UpdateInvoicePaymentMethod(i.Id, pm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!lnUrlMetadata.ContainsKey("text/plain"))
|
if (!lnUrlMetadata.ContainsKey("text/plain"))
|
||||||
@@ -493,7 +503,7 @@ namespace BTCPayServer
|
|||||||
lnurlRequest.Metadata = JsonConvert.SerializeObject(lnUrlMetadata.Select(kv => new[] { kv.Key, kv.Value }));
|
lnurlRequest.Metadata = JsonConvert.SerializeObject(lnUrlMetadata.Select(kv => new[] { kv.Key, kv.Value }));
|
||||||
if (i.Type != InvoiceType.TopUp)
|
if (i.Type != InvoiceType.TopUp)
|
||||||
{
|
{
|
||||||
lnurlRequest.MinSendable = new LightMoney(i.GetPaymentMethod(pmi).Calculate().Due.ToDecimal(MoneyUnit.Satoshi), LightMoneyUnit.Satoshi);
|
lnurlRequest.MinSendable = new LightMoney(pm.Calculate().Due.ToDecimal(MoneyUnit.Satoshi), LightMoneyUnit.Satoshi);
|
||||||
if (!allowOverpay)
|
if (!allowOverpay)
|
||||||
lnurlRequest.MaxSendable = lnurlRequest.MinSendable;
|
lnurlRequest.MaxSendable = lnurlRequest.MinSendable;
|
||||||
}
|
}
|
||||||
@@ -506,15 +516,16 @@ namespace BTCPayServer
|
|||||||
lnurlRequest.MaxSendable = LightMoney.FromUnit(6.12m, LightMoneyUnit.BTC);
|
lnurlRequest.MaxSendable = LightMoney.FromUnit(6.12m, LightMoneyUnit.BTC);
|
||||||
|
|
||||||
lnurlRequest = await _pluginHookService.ApplyFilter("modify-lnurlp-request", lnurlRequest) as LNURLPayRequest;
|
lnurlRequest = await _pluginHookService.ApplyFilter("modify-lnurlp-request", lnurlRequest) as LNURLPayRequest;
|
||||||
|
if (paymentMethodDetails.PayRequest is null)
|
||||||
i.Metadata ??= new InvoiceMetadata();
|
|
||||||
var metadata = i.Metadata.ToJObject();
|
|
||||||
if (metadata.Property("payRequest") is null)
|
|
||||||
{
|
{
|
||||||
metadata.Add("payRequest", JToken.FromObject(lnurlRequest));
|
paymentMethodDetails.PayRequest = lnurlRequest;
|
||||||
await _invoiceRepository.UpdateInvoiceMetadata(i.Id, i.StoreId, metadata);
|
updatePaymentMethodDetails = true;
|
||||||
|
}
|
||||||
|
if (updatePaymentMethodDetails)
|
||||||
|
{
|
||||||
|
pm.SetPaymentMethodDetails(paymentMethodDetails);
|
||||||
|
await _invoiceRepository.UpdateInvoicePaymentMethod(i.Id, pm);
|
||||||
}
|
}
|
||||||
|
|
||||||
return lnurlRequest;
|
return lnurlRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,17 +578,22 @@ namespace BTCPayServer
|
|||||||
|
|
||||||
var lightningPaymentMethod = i.GetPaymentMethod(pmi);
|
var lightningPaymentMethod = i.GetPaymentMethod(pmi);
|
||||||
var paymentMethodDetails =
|
var paymentMethodDetails =
|
||||||
lightningPaymentMethod.GetPaymentMethodDetails() as LNURLPayPaymentMethodDetails;
|
lightningPaymentMethod?.GetPaymentMethodDetails() as LNURLPayPaymentMethodDetails;
|
||||||
if (paymentMethodDetails.LightningSupportedPaymentMethod is null)
|
if (paymentMethodDetails is not null && !paymentMethodDetails.Activated)
|
||||||
|
{
|
||||||
|
if (!await _invoiceActivator.ActivateInvoicePaymentMethod(pmi, i, store))
|
||||||
|
return NotFound();
|
||||||
|
i = await _invoiceRepository.GetInvoice(invoiceId, true);
|
||||||
|
lightningPaymentMethod = i.GetPaymentMethod(pmi);
|
||||||
|
paymentMethodDetails = lightningPaymentMethod.GetPaymentMethodDetails() as LNURLPayPaymentMethodDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paymentMethodDetails?.LightningSupportedPaymentMethod is null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
|
||||||
LNURLPayRequest lnurlPayRequest;
|
LNURLPayRequest lnurlPayRequest = paymentMethodDetails.PayRequest;
|
||||||
var blob = store.GetStoreBlob();
|
var blob = store.GetStoreBlob();
|
||||||
if (i.Metadata.AdditionalData.TryGetValue("payRequest", out var t) && t is JObject jo)
|
if (paymentMethodDetails.PayRequest is null)
|
||||||
{
|
|
||||||
lnurlPayRequest = jo.ToObject<LNURLPayRequest>();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
lnurlPayRequest = await CreateLNUrlRequestFromInvoice(cryptoCode, i, store, blob, allowOverpay: false);
|
lnurlPayRequest = await CreateLNUrlRequestFromInvoice(cryptoCode, i, store, blob, allowOverpay: false);
|
||||||
if (lnurlPayRequest is null)
|
if (lnurlPayRequest is null)
|
||||||
@@ -641,8 +657,7 @@ namespace BTCPayServer
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var expiry = i.ExpirationTime.ToUniversalTime() - DateTimeOffset.UtcNow;
|
var expiry = i.ExpirationTime.ToUniversalTime() - DateTimeOffset.UtcNow;
|
||||||
var metadata = JsonConvert.SerializeObject(lnurlPayRequest.Metadata);
|
var description = (await _pluginHookService.ApplyFilter("modify-lnurlp-description", lnurlPayRequest.Metadata)) as string;
|
||||||
var description = (await _pluginHookService.ApplyFilter("modify-lnurlp-description", metadata)) as string;
|
|
||||||
if (description is null)
|
if (description is null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
|
||||||
@@ -678,15 +693,13 @@ namespace BTCPayServer
|
|||||||
paymentMethodDetails.InvoiceId = invoice.Id;
|
paymentMethodDetails.InvoiceId = invoice.Id;
|
||||||
paymentMethodDetails.GeneratedBoltAmount = amt;
|
paymentMethodDetails.GeneratedBoltAmount = amt;
|
||||||
updatePaymentMethod = true;
|
updatePaymentMethod = true;
|
||||||
|
|
||||||
_eventAggregator.Publish(new InvoiceNewPaymentDetailsEvent(invoiceId,
|
|
||||||
paymentMethodDetails, pmi));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updatePaymentMethod)
|
if (updatePaymentMethod)
|
||||||
{
|
{
|
||||||
lightningPaymentMethod.SetPaymentMethodDetails(paymentMethodDetails);
|
lightningPaymentMethod.SetPaymentMethodDetails(paymentMethodDetails);
|
||||||
await _invoiceRepository.UpdateInvoicePaymentMethod(invoiceId, lightningPaymentMethod);
|
await _invoiceRepository.UpdateInvoicePaymentMethod(invoiceId, lightningPaymentMethod);
|
||||||
|
_eventAggregator.Publish(new InvoiceNewPaymentDetailsEvent(invoiceId, paymentMethodDetails, pmi));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(new LNURLPayRequest.LNURLPayRequestCallbackResponse
|
return Ok(new LNURLPayRequest.LNURLPayRequestCallbackResponse
|
||||||
@@ -704,7 +717,7 @@ namespace BTCPayServer
|
|||||||
Reason = "Invoice not in a valid payable state"
|
Reason = "Invoice not in a valid payable state"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||||
[HttpGet("~/stores/{storeId}/plugins/lightning-address")]
|
[HttpGet("~/stores/{storeId}/plugins/lightning-address")]
|
||||||
@@ -759,13 +772,13 @@ namespace BTCPayServer
|
|||||||
}
|
}
|
||||||
|
|
||||||
JObject metadata = null;
|
JObject metadata = null;
|
||||||
if (!string.IsNullOrEmpty(vm.Add.InvoiceMetadata) )
|
if (!string.IsNullOrEmpty(vm.Add.InvoiceMetadata))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
metadata = JObject.Parse(vm.Add.InvoiceMetadata);
|
metadata = JObject.Parse(vm.Add.InvoiceMetadata);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
vm.AddModelError(addressVm => addressVm.Add.InvoiceMetadata, "Metadata must be a valid json object", this);
|
vm.AddModelError(addressVm => addressVm.Add.InvoiceMetadata, "Metadata must be a valid json object", this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,14 +198,14 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
var storeBlob = store.GetStoreBlob();
|
var storeBlob = store.GetStoreBlob();
|
||||||
vm.StoreName = store.StoreName;
|
vm.StoreName = store.StoreName;
|
||||||
vm.BrandColor = storeBlob.BrandColor;
|
vm.BrandColor = storeBlob.BrandColor;
|
||||||
vm.LogoFileId = storeBlob.LogoFileId;
|
vm.LogoFileId = storeBlob.LogoFileId;
|
||||||
vm.CssFileId = storeBlob.CssFileId;
|
vm.CssFileId = storeBlob.CssFileId;
|
||||||
vm.HubPath = PaymentRequestHub.GetHubPath(Request);
|
vm.HubPath = PaymentRequestHub.GetHubPath(Request);
|
||||||
|
|
||||||
return View(vm);
|
return View(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,26 +224,34 @@ namespace BTCPayServer.Controllers
|
|||||||
var prBlob = result.GetBlob();
|
var prBlob = result.GetBlob();
|
||||||
if (prBlob.FormResponse is not null)
|
if (prBlob.FormResponse is not null)
|
||||||
{
|
{
|
||||||
return RedirectToAction("PayPaymentRequest", new {payReqId});
|
return RedirectToAction("PayPaymentRequest", new { payReqId });
|
||||||
}
|
}
|
||||||
var prFormId = prBlob.FormId;
|
var prFormId = prBlob.FormId;
|
||||||
var formData = await FormDataService.GetForm(prFormId);
|
var formData = await FormDataService.GetForm(prFormId);
|
||||||
if (formData is null)
|
if (formData is null)
|
||||||
{
|
{
|
||||||
|
|
||||||
return RedirectToAction("PayPaymentRequest", new {payReqId});
|
return RedirectToAction("PayPaymentRequest", new { payReqId });
|
||||||
}
|
}
|
||||||
|
|
||||||
var form = Form.Parse(formData.Config);
|
var form = Form.Parse(formData.Config);
|
||||||
|
if (!string.IsNullOrEmpty(prBlob.Email))
|
||||||
|
{
|
||||||
|
var emailField = form.GetFieldByFullName("buyerEmail");
|
||||||
|
if (emailField is not null)
|
||||||
|
{
|
||||||
|
emailField.Value = prBlob.Email;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (Request.Method == "POST" && Request.HasFormContentType)
|
if (Request.Method == "POST" && Request.HasFormContentType)
|
||||||
{
|
{
|
||||||
form.ApplyValuesFromForm(Request.Form);
|
form.ApplyValuesFromForm(Request.Form);
|
||||||
if (FormDataService.Validate(form, ModelState))
|
if (FormDataService.Validate(form, ModelState))
|
||||||
{
|
{
|
||||||
prBlob.FormResponse = FormDataService.GetValues(form);
|
prBlob.FormResponse = FormDataService.GetValues(form);
|
||||||
result.SetBlob(prBlob);
|
result.SetBlob(prBlob);
|
||||||
await _PaymentRequestRepository.CreateOrUpdatePaymentRequest(result);
|
await _PaymentRequestRepository.CreateOrUpdatePaymentRequest(result);
|
||||||
return RedirectToAction("PayPaymentRequest", new {payReqId});
|
return RedirectToAction("PayPaymentRequest", new { payReqId });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
viewModel.FormName = formData.Name;
|
viewModel.FormName = formData.Name;
|
||||||
@@ -277,13 +285,12 @@ namespace BTCPayServer.Controllers
|
|||||||
|
|
||||||
return BadRequest("Payment Request cannot be paid as it has been archived");
|
return BadRequest("Payment Request cannot be paid as it has been archived");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result.FormSubmitted && !string.IsNullOrEmpty(result.FormId))
|
if (!result.FormSubmitted && !string.IsNullOrEmpty(result.FormId))
|
||||||
{
|
{
|
||||||
var formData = await FormDataService.GetForm(result.FormId);
|
var formData = await FormDataService.GetForm(result.FormId);
|
||||||
if (formData is not null)
|
if (formData is not null)
|
||||||
{
|
{
|
||||||
return RedirectToAction("ViewPaymentRequestForm", new {payReqId});
|
return RedirectToAction("ViewPaymentRequestForm", new { payReqId });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,7 +329,8 @@ namespace BTCPayServer.Controllers
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var store = await _storeRepository.FindStore(result.StoreId);
|
var store = await _storeRepository.FindStore(result.StoreId);
|
||||||
var newInvoice = await _InvoiceController.CreatePaymentRequestInvoice(result, amount, store, Request, cancellationToken);
|
var prData = await _PaymentRequestRepository.FindPaymentRequest(result.Id, null);
|
||||||
|
var newInvoice = await _InvoiceController.CreatePaymentRequestInvoice(prData, amount, result.AmountDue, store, Request, cancellationToken);
|
||||||
if (redirectToInvoice)
|
if (redirectToInvoice)
|
||||||
{
|
{
|
||||||
return RedirectToAction("Checkout", "UIInvoice", new { invoiceId = newInvoice.Id });
|
return RedirectToAction("Checkout", "UIInvoice", new { invoiceId = newInvoice.Id });
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ namespace BTCPayServer.Controllers
|
|||||||
var store = await _storeRepository.FindStore(pp.StoreId);
|
var store = await _storeRepository.FindStore(pp.StoreId);
|
||||||
if (store is null)
|
if (store is null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
|
||||||
var storeBlob = store.GetStoreBlob();
|
var storeBlob = store.GetStoreBlob();
|
||||||
var payouts = (await ctx.Payouts.GetPayoutInPeriod(pp)
|
var payouts = (await ctx.Payouts.GetPayoutInPeriod(pp)
|
||||||
.OrderByDescending(o => o.Date)
|
.OrderByDescending(o => o.Date)
|
||||||
|
|||||||
@@ -424,7 +424,7 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
var types = _AppService.GetAvailableAppTypes();
|
var types = _AppService.GetAvailableAppTypes();
|
||||||
var apps = (await _AppService.GetAllApps(null, true))
|
var apps = (await _AppService.GetAllApps(null, true))
|
||||||
.Select(a =>
|
.Select(a =>
|
||||||
new SelectListItem($"{types[a.AppType]} - {a.AppName} - {a.StoreName}", a.Id)).ToList();
|
new SelectListItem($"{types[a.AppType]} - {a.AppName} - {a.StoreName}", a.Id)).ToList();
|
||||||
apps.Insert(0, new SelectListItem("(None)", null));
|
apps.Insert(0, new SelectListItem("(None)", null));
|
||||||
return apps;
|
return apps;
|
||||||
|
|||||||
@@ -455,9 +455,9 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
IncludeArchived = false,
|
IncludeArchived = false,
|
||||||
IncludeStoreData = true,
|
IncludeStoreData = true,
|
||||||
Stores = new[] {storeId},
|
Stores = new[] { storeId },
|
||||||
PayoutIds = payoutIds,
|
PayoutIds = payoutIds,
|
||||||
PaymentMethods = new[] {paymentMethodId.ToString()}
|
PaymentMethods = new[] { paymentMethodId.ToString() }
|
||||||
}, ctx, cancellationToken);
|
}, ctx, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -181,7 +181,6 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
CryptoCode = vm.CryptoCode,
|
CryptoCode = vm.CryptoCode,
|
||||||
UseBech32Scheme = true,
|
UseBech32Scheme = true,
|
||||||
EnableForStandardInvoices = false,
|
|
||||||
LUD12Enabled = false
|
LUD12Enabled = false
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -245,26 +244,12 @@ namespace BTCPayServer.Controllers
|
|||||||
};
|
};
|
||||||
SetExistingValues(store, vm);
|
SetExistingValues(store, vm);
|
||||||
|
|
||||||
if (lightning != null)
|
|
||||||
{
|
|
||||||
vm.DisableBolt11PaymentMethod = lightning.DisableBOLT11PaymentOption;
|
|
||||||
}
|
|
||||||
|
|
||||||
var lnurl = GetExistingLNURLSupportedPaymentMethod(vm.CryptoCode, store);
|
var lnurl = GetExistingLNURLSupportedPaymentMethod(vm.CryptoCode, store);
|
||||||
if (lnurl != null)
|
if (lnurl != null)
|
||||||
{
|
{
|
||||||
vm.LNURLEnabled = !store.GetStoreBlob().GetExcludedPaymentMethods().Match(lnurl.PaymentId);
|
vm.LNURLEnabled = !store.GetStoreBlob().GetExcludedPaymentMethods().Match(lnurl.PaymentId);
|
||||||
vm.LNURLBech32Mode = lnurl.UseBech32Scheme;
|
vm.LNURLBech32Mode = lnurl.UseBech32Scheme;
|
||||||
vm.LNURLStandardInvoiceEnabled = lnurl.EnableForStandardInvoices;
|
|
||||||
vm.LUD12Enabled = lnurl.LUD12Enabled;
|
vm.LUD12Enabled = lnurl.LUD12Enabled;
|
||||||
vm.DisableBolt11PaymentMethod =
|
|
||||||
vm.LNURLEnabled && vm.LNURLStandardInvoiceEnabled && vm.DisableBolt11PaymentMethod;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//disable by default for now
|
|
||||||
//vm.LNURLEnabled = !lnSet;
|
|
||||||
vm.DisableBolt11PaymentMethod = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return View(vm);
|
return View(vm);
|
||||||
@@ -290,22 +275,11 @@ namespace BTCPayServer.Controllers
|
|||||||
blob.LightningAmountInSatoshi = vm.LightningAmountInSatoshi;
|
blob.LightningAmountInSatoshi = vm.LightningAmountInSatoshi;
|
||||||
blob.LightningPrivateRouteHints = vm.LightningPrivateRouteHints;
|
blob.LightningPrivateRouteHints = vm.LightningPrivateRouteHints;
|
||||||
blob.OnChainWithLnInvoiceFallback = vm.OnChainWithLnInvoiceFallback;
|
blob.OnChainWithLnInvoiceFallback = vm.OnChainWithLnInvoiceFallback;
|
||||||
var disableBolt11PaymentMethod =
|
|
||||||
vm.LNURLEnabled && vm.LNURLStandardInvoiceEnabled && vm.DisableBolt11PaymentMethod;
|
|
||||||
var lnurlId = new PaymentMethodId(vm.CryptoCode, PaymentTypes.LNURLPay);
|
var lnurlId = new PaymentMethodId(vm.CryptoCode, PaymentTypes.LNURLPay);
|
||||||
blob.SetExcluded(lnurlId, !vm.LNURLEnabled);
|
blob.SetExcluded(lnurlId, !vm.LNURLEnabled);
|
||||||
var lightning = GetExistingLightningSupportedPaymentMethod(vm.CryptoCode, store);
|
|
||||||
// Going to mark "lightning" as non-null here assuming that if we are POSTing here it's because we have a Lightning Node set-up
|
|
||||||
if (lightning!.DisableBOLT11PaymentOption != disableBolt11PaymentMethod)
|
|
||||||
{
|
|
||||||
needUpdate = true;
|
|
||||||
lightning.DisableBOLT11PaymentOption = disableBolt11PaymentMethod;
|
|
||||||
store.SetSupportedPaymentMethod(lightning);
|
|
||||||
}
|
|
||||||
|
|
||||||
var lnurl = GetExistingLNURLSupportedPaymentMethod(vm.CryptoCode, store);
|
var lnurl = GetExistingLNURLSupportedPaymentMethod(vm.CryptoCode, store);
|
||||||
if (lnurl is null || (
|
if (lnurl is null || (
|
||||||
lnurl.EnableForStandardInvoices != vm.LNURLStandardInvoiceEnabled ||
|
|
||||||
lnurl.UseBech32Scheme != vm.LNURLBech32Mode ||
|
lnurl.UseBech32Scheme != vm.LNURLBech32Mode ||
|
||||||
lnurl.LUD12Enabled != vm.LUD12Enabled))
|
lnurl.LUD12Enabled != vm.LUD12Enabled))
|
||||||
{
|
{
|
||||||
@@ -315,7 +289,6 @@ namespace BTCPayServer.Controllers
|
|||||||
store.SetSupportedPaymentMethod(new LNURLPaySupportedPaymentMethod
|
store.SetSupportedPaymentMethod(new LNURLPaySupportedPaymentMethod
|
||||||
{
|
{
|
||||||
CryptoCode = vm.CryptoCode,
|
CryptoCode = vm.CryptoCode,
|
||||||
EnableForStandardInvoices = vm.LNURLStandardInvoiceEnabled,
|
|
||||||
UseBech32Scheme = vm.LNURLBech32Mode,
|
UseBech32Scheme = vm.LNURLBech32Mode,
|
||||||
LUD12Enabled = vm.LUD12Enabled
|
LUD12Enabled = vm.LUD12Enabled
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -730,7 +730,7 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
await _fileService.RemoveFile(blob.CssFileId, userId);
|
await _fileService.RemoveFile(blob.CssFileId, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add new CSS file
|
// add new CSS file
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ namespace BTCPayServer.Controllers
|
|||||||
ModelState.Remove(nameof(vm.PSBT));
|
ModelState.Remove(nameof(vm.PSBT));
|
||||||
ModelState.Remove(nameof(vm.FileName));
|
ModelState.Remove(nameof(vm.FileName));
|
||||||
ModelState.Remove(nameof(vm.UploadedPSBTFile));
|
ModelState.Remove(nameof(vm.UploadedPSBTFile));
|
||||||
await FetchTransactionDetails(walletId,derivationSchemeSettings, vm, network);
|
await FetchTransactionDetails(walletId, derivationSchemeSettings, vm, network);
|
||||||
return View("WalletPSBTDecoded", vm);
|
return View("WalletPSBTDecoded", vm);
|
||||||
|
|
||||||
case "save-psbt":
|
case "save-psbt":
|
||||||
@@ -386,15 +386,15 @@ namespace BTCPayServer.Controllers
|
|||||||
inputVm.BalanceChange = ValueToString(balanceChange2, network);
|
inputVm.BalanceChange = ValueToString(balanceChange2, network);
|
||||||
inputVm.Positive = balanceChange2 >= Money.Zero;
|
inputVm.Positive = balanceChange2 >= Money.Zero;
|
||||||
inputVm.Index = (int)input.Index;
|
inputVm.Index = (int)input.Index;
|
||||||
|
|
||||||
var walletObjectIds = new List<ObjectTypeId>();
|
var walletObjectIds = new List<ObjectTypeId>();
|
||||||
walletObjectIds.Add(new ObjectTypeId(WalletObjectData.Types.Utxo, input.PrevOut.ToString()));
|
walletObjectIds.Add(new ObjectTypeId(WalletObjectData.Types.Utxo, input.PrevOut.ToString()));
|
||||||
walletObjectIds.Add(new ObjectTypeId(WalletObjectData.Types.Tx, input.PrevOut.Hash.ToString()));
|
walletObjectIds.Add(new ObjectTypeId(WalletObjectData.Types.Tx, input.PrevOut.Hash.ToString()));
|
||||||
var address = txOut?.ScriptPubKey.GetDestinationAddress(network.NBitcoinNetwork)?.ToString();
|
var address = txOut?.ScriptPubKey.GetDestinationAddress(network.NBitcoinNetwork)?.ToString();
|
||||||
if(address != null)
|
if (address != null)
|
||||||
walletObjectIds.Add(new ObjectTypeId(WalletObjectData.Types.Address, address));
|
walletObjectIds.Add(new ObjectTypeId(WalletObjectData.Types.Address, address));
|
||||||
inputToObjects.Add(input.Index, walletObjectIds.ToArray());
|
inputToObjects.Add(input.Index, walletObjectIds.ToArray());
|
||||||
|
|
||||||
}
|
}
|
||||||
vm.Destinations = new List<WalletPSBTReadyViewModel.DestinationViewModel>();
|
vm.Destinations = new List<WalletPSBTReadyViewModel.DestinationViewModel>();
|
||||||
foreach (var output in psbtObject.Outputs)
|
foreach (var output in psbtObject.Outputs)
|
||||||
@@ -409,9 +409,9 @@ namespace BTCPayServer.Controllers
|
|||||||
dest.Positive = balanceChange2 >= Money.Zero;
|
dest.Positive = balanceChange2 >= Money.Zero;
|
||||||
dest.Destination = output.ScriptPubKey.GetDestinationAddress(network.NBitcoinNetwork)?.ToString() ?? output.ScriptPubKey.ToString();
|
dest.Destination = output.ScriptPubKey.GetDestinationAddress(network.NBitcoinNetwork)?.ToString() ?? output.ScriptPubKey.ToString();
|
||||||
var address = output.ScriptPubKey.GetDestinationAddress(network.NBitcoinNetwork)?.ToString();
|
var address = output.ScriptPubKey.GetDestinationAddress(network.NBitcoinNetwork)?.ToString();
|
||||||
if(address != null)
|
if (address != null)
|
||||||
outputToObjects.Add(dest.Destination, new ObjectTypeId(WalletObjectData.Types.Address, address));
|
outputToObjects.Add(dest.Destination, new ObjectTypeId(WalletObjectData.Types.Address, address));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (psbtObject.TryGetFee(out var fee))
|
if (psbtObject.TryGetFee(out var fee))
|
||||||
@@ -442,13 +442,14 @@ namespace BTCPayServer.Controllers
|
|||||||
.DistinctBy(id => $"{id.Type}:{id.Id}").ToArray();
|
.DistinctBy(id => $"{id.Type}:{id.Id}").ToArray();
|
||||||
|
|
||||||
var labelInfo = await WalletRepository.GetWalletTransactionsInfo(walletId, combinedTypeIds);
|
var labelInfo = await WalletRepository.GetWalletTransactionsInfo(walletId, combinedTypeIds);
|
||||||
foreach (KeyValuePair<uint,ObjectTypeId[]> inputToObject in inputToObjects)
|
foreach (KeyValuePair<uint, ObjectTypeId[]> inputToObject in inputToObjects)
|
||||||
{
|
{
|
||||||
var keys = inputToObject.Value.Select(id => id.Id).ToArray();
|
var keys = inputToObject.Value.Select(id => id.Id).ToArray();
|
||||||
WalletTransactionInfo ix = null;
|
WalletTransactionInfo ix = null;
|
||||||
foreach (var key in keys)
|
foreach (var key in keys)
|
||||||
{
|
{
|
||||||
if (!labelInfo.TryGetValue(key, out var i)) continue;
|
if (!labelInfo.TryGetValue(key, out var i))
|
||||||
|
continue;
|
||||||
if (ix is null)
|
if (ix is null)
|
||||||
{
|
{
|
||||||
ix = i;
|
ix = i;
|
||||||
@@ -458,20 +459,22 @@ namespace BTCPayServer.Controllers
|
|||||||
ix.Merge(i);
|
ix.Merge(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ix is null) continue;
|
if (ix is null)
|
||||||
|
continue;
|
||||||
|
|
||||||
var labels = _labelService.CreateTransactionTagModels(ix, Request);
|
var labels = _labelService.CreateTransactionTagModels(ix, Request);
|
||||||
var input = vm.Inputs.First(model => model.Index == inputToObject.Key);
|
var input = vm.Inputs.First(model => model.Index == inputToObject.Key);
|
||||||
input.Labels = labels;
|
input.Labels = labels;
|
||||||
}
|
}
|
||||||
foreach (var outputToObject in outputToObjects)
|
foreach (var outputToObject in outputToObjects)
|
||||||
{
|
{
|
||||||
if (!labelInfo.TryGetValue(outputToObject.Value.Id, out var ix)) continue;
|
if (!labelInfo.TryGetValue(outputToObject.Value.Id, out var ix))
|
||||||
|
continue;
|
||||||
var labels = _labelService.CreateTransactionTagModels(ix, Request);
|
var labels = _labelService.CreateTransactionTagModels(ix, Request);
|
||||||
var destination = vm.Destinations.First(model => model.Destination == outputToObject.Key);
|
var destination = vm.Destinations.First(model => model.Destination == outputToObject.Key);
|
||||||
destination.Labels = labels;
|
destination.Labels = labels;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{walletId}/psbt/ready")]
|
[HttpPost("{walletId}/psbt/ready")]
|
||||||
@@ -491,7 +494,7 @@ namespace BTCPayServer.Controllers
|
|||||||
if (derivationSchemeSettings == null)
|
if (derivationSchemeSettings == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
|
||||||
await FetchTransactionDetails(walletId,derivationSchemeSettings, vm, network);
|
await FetchTransactionDetails(walletId, derivationSchemeSettings, vm, network);
|
||||||
|
|
||||||
switch (command)
|
switch (command)
|
||||||
{
|
{
|
||||||
@@ -622,7 +625,7 @@ namespace BTCPayServer.Controllers
|
|||||||
BackUrl = vm.BackUrl
|
BackUrl = vm.BackUrl
|
||||||
});
|
});
|
||||||
case "decode":
|
case "decode":
|
||||||
await FetchTransactionDetails(walletId,derivationSchemeSettings, vm, network);
|
await FetchTransactionDetails(walletId, derivationSchemeSettings, vm, network);
|
||||||
return View("WalletPSBTDecoded", vm);
|
return View("WalletPSBTDecoded", vm);
|
||||||
default:
|
default:
|
||||||
vm.Errors.Add("Unknown command");
|
vm.Errors.Add("Unknown command");
|
||||||
|
|||||||
@@ -340,7 +340,7 @@ namespace BTCPayServer.Controllers
|
|||||||
CryptoImage = GetImage(paymentMethod.PaymentId, network),
|
CryptoImage = GetImage(paymentMethod.PaymentId, network),
|
||||||
PaymentLink = bip21.ToString(),
|
PaymentLink = bip21.ToString(),
|
||||||
ReturnUrl = returnUrl ?? HttpContext.Request.GetTypedHeaders().Referer?.AbsolutePath,
|
ReturnUrl = returnUrl ?? HttpContext.Request.GetTypedHeaders().Referer?.AbsolutePath,
|
||||||
SelectedLabels = labels?? Array.Empty<string>()
|
SelectedLabels = labels ?? Array.Empty<string>()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -733,14 +733,14 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
var labels = transactionOutput.Labels.Where(s => !string.IsNullOrWhiteSpace(s)).ToArray();
|
var labels = transactionOutput.Labels.Where(s => !string.IsNullOrWhiteSpace(s)).ToArray();
|
||||||
var walletObjectAddress = new WalletObjectId(walletId, WalletObjectData.Types.Address, transactionOutput.DestinationAddress.ToLowerInvariant());
|
var walletObjectAddress = new WalletObjectId(walletId, WalletObjectData.Types.Address, transactionOutput.DestinationAddress.ToLowerInvariant());
|
||||||
var obj = await WalletRepository.GetWalletObject(walletObjectAddress);
|
var obj = await WalletRepository.GetWalletObject(walletObjectAddress);
|
||||||
if (obj is null)
|
if (obj is null)
|
||||||
{
|
{
|
||||||
await WalletRepository.EnsureWalletObject(walletObjectAddress);
|
await WalletRepository.EnsureWalletObject(walletObjectAddress);
|
||||||
}
|
}
|
||||||
await WalletRepository.AddWalletObjectLabels(walletObjectAddress, labels);
|
await WalletRepository.AddWalletObjectLabels(walletObjectAddress, labels);
|
||||||
}
|
}
|
||||||
|
|
||||||
var derivationScheme = GetDerivationSchemeSettings(walletId);
|
var derivationScheme = GetDerivationSchemeSettings(walletId);
|
||||||
if (derivationScheme is null)
|
if (derivationScheme is null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
@@ -787,10 +787,10 @@ namespace BTCPayServer.Controllers
|
|||||||
switch (response.Result)
|
switch (response.Result)
|
||||||
{
|
{
|
||||||
case ClaimRequest.ClaimResult.Duplicate:
|
case ClaimRequest.ClaimResult.Duplicate:
|
||||||
errorMessage += $"{claimRequest.Value} to {claimRequest.Destination.ToString() } - address reuse<br/>";
|
errorMessage += $"{claimRequest.Value} to {claimRequest.Destination.ToString()} - address reuse<br/>";
|
||||||
break;
|
break;
|
||||||
case ClaimRequest.ClaimResult.AmountTooLow:
|
case ClaimRequest.ClaimResult.AmountTooLow:
|
||||||
errorMessage += $"{claimRequest.Value} to {claimRequest.Destination.ToString() } - amount too low<br/>";
|
errorMessage += $"{claimRequest.Value} to {claimRequest.Destination.ToString()} - amount too low<br/>";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -919,8 +919,8 @@ namespace BTCPayServer.Controllers
|
|||||||
ModelState.Clear();
|
ModelState.Clear();
|
||||||
if (address is not null)
|
if (address is not null)
|
||||||
{
|
{
|
||||||
var addressLabels = await WalletRepository.GetWalletLabels(new WalletObjectId(walletId, WalletObjectData.Types.Address, address.ToString()));
|
var addressLabels = await WalletRepository.GetWalletLabels(new WalletObjectId(walletId, WalletObjectData.Types.Address, address.ToString()));
|
||||||
vm.Outputs.Last().Labels = addressLabels.Select(tuple => tuple.Label).ToArray();
|
vm.Outputs.Last().Labels = addressLabels.Select(tuple => tuple.Label).ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1350,14 +1350,14 @@ namespace BTCPayServer.Controllers
|
|||||||
Response.Headers.Add("X-Content-Type-Options", "nosniff");
|
Response.Headers.Add("X-Content-Type-Options", "nosniff");
|
||||||
return Content(res, mimeType);
|
return Content(res, mimeType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UpdateLabelsRequest
|
public class UpdateLabelsRequest
|
||||||
{
|
{
|
||||||
public string? Id { get; set; }
|
public string? Id { get; set; }
|
||||||
public string? Type { get; set; }
|
public string? Type { get; set; }
|
||||||
public string[]? Labels { get; set; }
|
public string[]? Labels { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{walletId}/update-labels")]
|
[HttpPost("{walletId}/update-labels")]
|
||||||
[IgnoreAntiforgeryToken]
|
[IgnoreAntiforgeryToken]
|
||||||
public async Task<IActionResult> UpdateLabels(
|
public async Task<IActionResult> UpdateLabels(
|
||||||
@@ -1366,23 +1366,23 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(request.Type) || string.IsNullOrEmpty(request.Id) || request.Labels is null)
|
if (string.IsNullOrEmpty(request.Type) || string.IsNullOrEmpty(request.Id) || request.Labels is null)
|
||||||
return BadRequest();
|
return BadRequest();
|
||||||
|
|
||||||
var objid = new WalletObjectId(walletId, request.Type, request.Id);
|
var objid = new WalletObjectId(walletId, request.Type, request.Id);
|
||||||
var obj = await WalletRepository.GetWalletObject(objid);
|
var obj = await WalletRepository.GetWalletObject(objid);
|
||||||
if (obj is null)
|
if (obj is null)
|
||||||
{
|
{
|
||||||
await WalletRepository.EnsureWalletObject(objid);
|
await WalletRepository.EnsureWalletObject(objid);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var currentLabels = obj.GetNeighbours().Where(data => data.Type == WalletObjectData.Types.Label).ToArray();
|
var currentLabels = obj.GetNeighbours().Where(data => data.Type == WalletObjectData.Types.Label).ToArray();
|
||||||
var toRemove = currentLabels.Where(data => !request.Labels.Contains(data.Id)).Select(data => data.Id).ToArray();
|
var toRemove = currentLabels.Where(data => !request.Labels.Contains(data.Id)).Select(data => data.Id).ToArray();
|
||||||
await WalletRepository.RemoveWalletObjectLabels(objid, toRemove);
|
await WalletRepository.RemoveWalletObjectLabels(objid, toRemove);
|
||||||
}
|
}
|
||||||
await WalletRepository.AddWalletObjectLabels(objid, request.Labels);
|
await WalletRepository.AddWalletObjectLabels(objid, request.Labels);
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{walletId}/labels")]
|
[HttpGet("{walletId}/labels")]
|
||||||
[IgnoreAntiforgeryToken]
|
[IgnoreAntiforgeryToken]
|
||||||
public async Task<IActionResult> GetLabels(
|
public async Task<IActionResult> GetLabels(
|
||||||
@@ -1399,11 +1399,11 @@ namespace BTCPayServer.Controllers
|
|||||||
: await WalletRepository.GetWalletLabels(walletObjectId);
|
: await WalletRepository.GetWalletLabels(walletObjectId);
|
||||||
return Ok(labels
|
return Ok(labels
|
||||||
.Where(l => !excludeTypes || !WalletObjectData.Types.AllTypes.Contains(l.Label))
|
.Where(l => !excludeTypes || !WalletObjectData.Types.AllTypes.Contains(l.Label))
|
||||||
.Select(tuple => new
|
.Select(tuple => new
|
||||||
{
|
{
|
||||||
label = tuple.Label,
|
label = tuple.Label,
|
||||||
color = tuple.Color,
|
color = tuple.Color,
|
||||||
textColor = ColorPalette.Default.TextColor(tuple.Color)
|
textColor = ColorPalette.Default.TextColor(tuple.Color)
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace BTCPayServer.Data
|
|||||||
{
|
{
|
||||||
public static class IHasBlobExtensions
|
public static class IHasBlobExtensions
|
||||||
{
|
{
|
||||||
static readonly JsonSerializerSettings DefaultSerializer;
|
static readonly JsonSerializerSettings DefaultSerializer;
|
||||||
static IHasBlobExtensions()
|
static IHasBlobExtensions()
|
||||||
{
|
{
|
||||||
DefaultSerializer = new JsonSerializerSettings()
|
DefaultSerializer = new JsonSerializerSettings()
|
||||||
@@ -30,7 +30,7 @@ namespace BTCPayServer.Data
|
|||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
[Obsolete("Use Blob2 instead")]
|
[Obsolete("Use Blob2 instead")]
|
||||||
public byte[] Blob { get { return data.Blob; } set { data.Blob = value; } }
|
public byte[] Blob { get { return data.Blob; } set { data.Blob = value; } }
|
||||||
public string Blob2 { get { return data.Blob2; } set { data.Blob2 = value; } }
|
public string Blob2 { get { return data.Blob2; } set { data.Blob2 = value; } }
|
||||||
}
|
}
|
||||||
class HasBlobWrapper : IHasBlob
|
class HasBlobWrapper : IHasBlob
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace BTCPayServer.Data
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var entity = invoiceData.HasTypedBlob<InvoiceEntity>().GetBlob();
|
var entity = invoiceData.HasTypedBlob<InvoiceEntity>().GetBlob();
|
||||||
entity.Networks = networks;
|
entity.Networks = networks;
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ public class BitcoinLikePayoutHandler : IPayoutHandler
|
|||||||
var explorerClient = _explorerClientProvider.GetExplorerClient(network);
|
var explorerClient = _explorerClientProvider.GetExplorerClient(network);
|
||||||
if (claimRequest.Destination is IBitcoinLikeClaimDestination bitcoinLikeClaimDestination)
|
if (claimRequest.Destination is IBitcoinLikeClaimDestination bitcoinLikeClaimDestination)
|
||||||
{
|
{
|
||||||
|
|
||||||
await explorerClient.TrackAsync(TrackedSource.Create(bitcoinLikeClaimDestination.Address));
|
await explorerClient.TrackAsync(TrackedSource.Create(bitcoinLikeClaimDestination.Address));
|
||||||
await WalletRepository.AddWalletTransactionAttachment(
|
await WalletRepository.AddWalletTransactionAttachment(
|
||||||
new WalletId(claimRequest.StoreId, claimRequest.PaymentMethodId.CryptoCode),
|
new WalletId(claimRequest.StoreId, claimRequest.PaymentMethodId.CryptoCode),
|
||||||
@@ -210,11 +210,13 @@ public class BitcoinLikePayoutHandler : IPayoutHandler
|
|||||||
await using (var context = _dbContextFactory.CreateContext())
|
await using (var context = _dbContextFactory.CreateContext())
|
||||||
{
|
{
|
||||||
var payouts = (await PullPaymentHostedService.GetPayouts(new PullPaymentHostedService.PayoutQuery()
|
var payouts = (await PullPaymentHostedService.GetPayouts(new PullPaymentHostedService.PayoutQuery()
|
||||||
{
|
{
|
||||||
States = new[] {PayoutState.AwaitingPayment}, Stores = new[] {storeId}, PayoutIds = payoutIds
|
States = new[] { PayoutState.AwaitingPayment },
|
||||||
}, context)).Where(data =>
|
Stores = new[] { storeId },
|
||||||
PaymentMethodId.TryParse(data.PaymentMethodId, out var paymentMethodId) &&
|
PayoutIds = payoutIds
|
||||||
CanHandle(paymentMethodId))
|
}, context)).Where(data =>
|
||||||
|
PaymentMethodId.TryParse(data.PaymentMethodId, out var paymentMethodId) &&
|
||||||
|
CanHandle(paymentMethodId))
|
||||||
.Select(data => (data, ParseProof(data) as PayoutTransactionOnChainBlob)).Where(tuple => tuple.Item2 != null && tuple.Item2.TransactionId != null && tuple.Item2.Accounted == false);
|
.Select(data => (data, ParseProof(data) as PayoutTransactionOnChainBlob)).Where(tuple => tuple.Item2 != null && tuple.Item2.TransactionId != null && tuple.Item2.Accounted == false);
|
||||||
foreach (var valueTuple in payouts)
|
foreach (var valueTuple in payouts)
|
||||||
{
|
{
|
||||||
@@ -230,14 +232,16 @@ public class BitcoinLikePayoutHandler : IPayoutHandler
|
|||||||
Severity = StatusMessageModel.StatusSeverity.Success
|
Severity = StatusMessageModel.StatusSeverity.Success
|
||||||
};
|
};
|
||||||
case "reject-payment":
|
case "reject-payment":
|
||||||
await using (var context = _dbContextFactory.CreateContext())
|
await using (var context = _dbContextFactory.CreateContext())
|
||||||
{
|
{
|
||||||
var payouts = (await PullPaymentHostedService.GetPayouts(new PullPaymentHostedService.PayoutQuery()
|
var payouts = (await PullPaymentHostedService.GetPayouts(new PullPaymentHostedService.PayoutQuery()
|
||||||
{
|
{
|
||||||
States = new[] {PayoutState.AwaitingPayment}, Stores = new[] {storeId}, PayoutIds = payoutIds
|
States = new[] { PayoutState.AwaitingPayment },
|
||||||
}, context)).Where(data =>
|
Stores = new[] { storeId },
|
||||||
PaymentMethodId.TryParse(data.PaymentMethodId, out var paymentMethodId) &&
|
PayoutIds = payoutIds
|
||||||
CanHandle(paymentMethodId))
|
}, context)).Where(data =>
|
||||||
|
PaymentMethodId.TryParse(data.PaymentMethodId, out var paymentMethodId) &&
|
||||||
|
CanHandle(paymentMethodId))
|
||||||
.Select(data => (data, ParseProof(data) as PayoutTransactionOnChainBlob)).Where(tuple => tuple.Item2 != null && tuple.Item2.TransactionId != null && tuple.Item2.Accounted == true);
|
.Select(data => (data, ParseProof(data) as PayoutTransactionOnChainBlob)).Where(tuple => tuple.Item2 != null && tuple.Item2.TransactionId != null && tuple.Item2.Accounted == true);
|
||||||
foreach (var valueTuple in payouts)
|
foreach (var valueTuple in payouts)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -225,11 +225,11 @@ namespace BTCPayServer.Data
|
|||||||
[DefaultValue(true)]
|
[DefaultValue(true)]
|
||||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
||||||
public bool ShowPayInWalletButton { get; set; } = true;
|
public bool ShowPayInWalletButton { get; set; } = true;
|
||||||
|
|
||||||
[DefaultValue(true)]
|
[DefaultValue(true)]
|
||||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
||||||
public bool ShowStoreHeader { get; set; } = true;
|
public bool ShowStoreHeader { get; set; } = true;
|
||||||
|
|
||||||
[DefaultValue(true)]
|
[DefaultValue(true)]
|
||||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
||||||
public bool CelebratePayment { get; set; } = true;
|
public bool CelebratePayment { get; set; } = true;
|
||||||
|
|||||||
@@ -186,12 +186,12 @@ namespace BTCPayServer.Data
|
|||||||
{
|
{
|
||||||
return IsPaymentTypeEnabled(storeData, networks, cryptoCode, LNURLPayPaymentType.Instance);
|
return IsPaymentTypeEnabled(storeData, networks, cryptoCode, LNURLPayPaymentType.Instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsPaymentTypeEnabled(this StoreData storeData, BTCPayNetworkProvider networks, string cryptoCode, PaymentType paymentType)
|
private static bool IsPaymentTypeEnabled(this StoreData storeData, BTCPayNetworkProvider networks, string cryptoCode, PaymentType paymentType)
|
||||||
{
|
{
|
||||||
var paymentMethods = storeData.GetSupportedPaymentMethods(networks);
|
var paymentMethods = storeData.GetSupportedPaymentMethods(networks);
|
||||||
var excludeFilters = storeData.GetStoreBlob().GetExcludedPaymentMethods();
|
var excludeFilters = storeData.GetStoreBlob().GetExcludedPaymentMethods();
|
||||||
return paymentMethods.Any(method =>
|
return paymentMethods.Any(method =>
|
||||||
method.PaymentId.CryptoCode == cryptoCode &&
|
method.PaymentId.CryptoCode == cryptoCode &&
|
||||||
method.PaymentId.PaymentType == paymentType &&
|
method.PaymentId.PaymentType == paymentType &&
|
||||||
!excludeFilters.Match(method.PaymentId));
|
!excludeFilters.Match(method.PaymentId));
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ namespace BTCPayServer
|
|||||||
{
|
{
|
||||||
return AccountDerivation is null ? null : DBUtils.nbxv1_get_wallet_id(Network.CryptoCode, AccountDerivation.ToString());
|
return AccountDerivation is null ? null : DBUtils.nbxv1_get_wallet_id(Network.CryptoCode, AccountDerivation.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryParseXpub(string xpub, DerivationSchemeParser derivationSchemeParser, ref DerivationSchemeSettings derivationSchemeSettings, ref string error, bool electrum = true)
|
private static bool TryParseXpub(string xpub, DerivationSchemeParser derivationSchemeParser, ref DerivationSchemeSettings derivationSchemeSettings, ref string error, bool electrum = true)
|
||||||
{
|
{
|
||||||
if (!electrum)
|
if (!electrum)
|
||||||
@@ -87,9 +87,12 @@ namespace BTCPayServer
|
|||||||
var match = derivationRegex.Match(xpub.Trim());
|
var match = derivationRegex.Match(xpub.Trim());
|
||||||
if (match.Success)
|
if (match.Success)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(match.Groups[1].Value)) rootFingerprint = HDFingerprint.Parse(match.Groups[1].Value);
|
if (!string.IsNullOrEmpty(match.Groups[1].Value))
|
||||||
if (!string.IsNullOrEmpty(match.Groups[2].Value)) accountKeyPath = KeyPath.Parse(match.Groups[2].Value);
|
rootFingerprint = HDFingerprint.Parse(match.Groups[1].Value);
|
||||||
if (!string.IsNullOrEmpty(match.Groups[3].Value)) xpub = match.Groups[3].Value;
|
if (!string.IsNullOrEmpty(match.Groups[2].Value))
|
||||||
|
accountKeyPath = KeyPath.Parse(match.Groups[2].Value);
|
||||||
|
if (!string.IsNullOrEmpty(match.Groups[3].Value))
|
||||||
|
xpub = match.Groups[3].Value;
|
||||||
}
|
}
|
||||||
derivationSchemeSettings.AccountOriginal = xpub.Trim();
|
derivationSchemeSettings.AccountOriginal = xpub.Trim();
|
||||||
derivationSchemeSettings.AccountDerivation = electrum ? derivationSchemeParser.ParseElectrum(derivationSchemeSettings.AccountOriginal) : derivationSchemeParser.Parse(derivationSchemeSettings.AccountOriginal);
|
derivationSchemeSettings.AccountDerivation = electrum ? derivationSchemeParser.ParseElectrum(derivationSchemeSettings.AccountOriginal) : derivationSchemeParser.Parse(derivationSchemeSettings.AccountOriginal);
|
||||||
|
|||||||
@@ -41,13 +41,13 @@ namespace BTCPayServer
|
|||||||
{
|
{
|
||||||
pattern = pattern.Replace(" ", "");
|
pattern = pattern.Replace(" ", "");
|
||||||
int[] res = new int[pattern.Length / 2];
|
int[] res = new int[pattern.Length / 2];
|
||||||
for (int i = 0; i < pattern.Length; i+=2)
|
for (int i = 0; i < pattern.Length; i += 2)
|
||||||
{
|
{
|
||||||
var b = pattern[i..(i + 2)];
|
var b = pattern[i..(i + 2)];
|
||||||
if (b == "XX")
|
if (b == "XX")
|
||||||
res[i/2] = -1;
|
res[i / 2] = -1;
|
||||||
else
|
else
|
||||||
res[i/2] = byte.Parse(b, System.Globalization.NumberStyles.HexNumber);
|
res[i / 2] = byte.Parse(b, System.Globalization.NumberStyles.HexNumber);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,12 +13,12 @@ namespace BTCPayServer.Filters
|
|||||||
public DomainMappingConstraintAttribute()
|
public DomainMappingConstraintAttribute()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public DomainMappingConstraintAttribute(string appType)
|
public DomainMappingConstraintAttribute(string appType)
|
||||||
{
|
{
|
||||||
AppType = appType;
|
AppType = appType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Order => 100;
|
public int Order => 100;
|
||||||
private string AppType { get; }
|
private string AppType { get; }
|
||||||
|
|
||||||
@@ -33,21 +33,22 @@ namespace BTCPayServer.Filters
|
|||||||
{
|
{
|
||||||
var appId = (string)context.RouteContext.RouteData.Values["appId"];
|
var appId = (string)context.RouteContext.RouteData.Values["appId"];
|
||||||
var matchedDomainMapping = mapping.FirstOrDefault(item => item.AppId == appId);
|
var matchedDomainMapping = mapping.FirstOrDefault(item => item.AppId == appId);
|
||||||
|
|
||||||
// App is accessed via path, redirect to canonical domain
|
// App is accessed via path, redirect to canonical domain
|
||||||
var req = context.RouteContext.HttpContext.Request;
|
var req = context.RouteContext.HttpContext.Request;
|
||||||
if (matchedDomainMapping != null && req.Method != "POST" && !req.HasFormContentType)
|
if (matchedDomainMapping != null && req.Method != "POST" && !req.HasFormContentType)
|
||||||
{
|
{
|
||||||
var uri = new UriBuilder(req.Scheme, matchedDomainMapping.Domain);
|
var uri = new UriBuilder(req.Scheme, matchedDomainMapping.Domain);
|
||||||
if (req.Host.Port.HasValue) uri.Port = req.Host.Port.Value;
|
if (req.Host.Port.HasValue)
|
||||||
|
uri.Port = req.Host.Port.Value;
|
||||||
context.RouteContext.HttpContext.Response.Redirect(uri.ToString());
|
context.RouteContext.HttpContext.Response.Redirect(uri.ToString());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasDomainMapping)
|
if (hasDomainMapping)
|
||||||
{
|
{
|
||||||
var matchedDomainMapping = mapping.FirstOrDefault(item =>
|
var matchedDomainMapping = mapping.FirstOrDefault(item =>
|
||||||
item.Domain.Equals(context.RouteContext.HttpContext.Request.Host.Host,
|
item.Domain.Equals(context.RouteContext.HttpContext.Request.Host.Host,
|
||||||
StringComparison.InvariantCultureIgnoreCase));
|
StringComparison.InvariantCultureIgnoreCase));
|
||||||
if (matchedDomainMapping != null)
|
if (matchedDomainMapping != null)
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ public static class FormDataExtensions
|
|||||||
serviceCollection.AddSingleton<IFormComponentProvider, HtmlSelectFormProvider>();
|
serviceCollection.AddSingleton<IFormComponentProvider, HtmlSelectFormProvider>();
|
||||||
serviceCollection.AddSingleton<IFormComponentProvider, FieldValueMirror>();
|
serviceCollection.AddSingleton<IFormComponentProvider, FieldValueMirror>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JObject Deserialize(this FormData form)
|
public static JObject Deserialize(this FormData form)
|
||||||
{
|
{
|
||||||
return JsonConvert.DeserializeObject<JObject>(form.Config);
|
return JsonConvert.DeserializeObject<JObject>(form.Config);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public class FormDataService
|
|||||||
private readonly FormComponentProviders _formProviders;
|
private readonly FormComponentProviders _formProviders;
|
||||||
|
|
||||||
public FormDataService(
|
public FormDataService(
|
||||||
ApplicationDbContextFactory applicationDbContextFactory,
|
ApplicationDbContextFactory applicationDbContextFactory,
|
||||||
FormComponentProviders formProviders)
|
FormComponentProviders formProviders)
|
||||||
{
|
{
|
||||||
_applicationDbContextFactory = applicationDbContextFactory;
|
_applicationDbContextFactory = applicationDbContextFactory;
|
||||||
@@ -47,16 +47,16 @@ public class FormDataService
|
|||||||
Field.Create("State", "buyerState", null, false, null),
|
Field.Create("State", "buyerState", null, false, null),
|
||||||
new SelectField()
|
new SelectField()
|
||||||
{
|
{
|
||||||
Name = "buyerCountry",
|
Name = "buyerCountry",
|
||||||
Label = "Country",
|
Label = "Country",
|
||||||
Required = true,
|
Required = true,
|
||||||
Type = "select",
|
Type = "select",
|
||||||
Options = "Afghanistan, Albania, Algeria, Andorra, Angola, Antigua and Barbuda, Argentina, Armenia, Australia, Austria, Azerbaijan, The Bahamas, Bahrain, Bangladesh, Barbados, Belarus, Belgium, Belize, Benin, Bhutan, Bolivia, Bosnia and Herzegovina, Botswana, Brazil, Brunei, Bulgaria, Burkina Faso, Burundi, Cabo Verde, Cambodia, Cameroon, Canada, Central African Republic (CAR), Chad, Chile, China, Colombia, Comoros, Democratic Republic of the Congo, Republic of the Congo, Costa Rica, Cote d'Ivoire, Croatia, Cuba, Cyprus, Czech Republic, Denmark, Djibouti, Dominica, Dominican Republic, Ecuador, Egypt, El Salvador, Equatorial Guinea, Eritrea, Estonia, Eswatini (formerly Swaziland), Ethiopia, Fiji, Finland, France, Gabon, The Gambia, Georgia, Germany, Ghana, Greece, Grenada, Guatemala, Guinea, Guinea-Bissau, Guyana, Haiti, Honduras, Hungary, Iceland, India, Indonesia, Iran, Iraq, Ireland, Israel, Italy, Jamaica, Japan, Jordan, Kazakhstan, Kenya, Kiribati, Kosovo, Kuwait, Kyrgyzstan, Laos, Latvia, Lebanon, Lesotho, Liberia, Libya, Liechtenstein, Lithuania, Luxembourg, Madagascar, Malawi, Malaysia, Maldives, Mali, Malta, Marshall Islands, Mauritania, Mauritius, Mexico, Micronesia, Moldova, Monaco, Mongolia, Montenegro, Morocco, Mozambique, Myanmar (formerly Burma), Namibia, Nauru, Nepal, Netherlands, New Zealand, Nicaragua, Niger, Nigeria, North Korea, North Macedonia (formerly Macedonia), Norway, Oman, Pakistan, Palau, Palestine, Panama, Papua New Guinea, Paraguay, Peru, Philippines, Poland, Portugal, Qatar, Romania, Russia, Rwanda, Saint Kitts and Nevis, Saint Lucia, Saint Vincent and the Grenadines, Samoa, San Marino, Sao Tome and Principe, Saudi Arabia, Senegal, Serbia, Seychelles, Sierra Leone, Singapore, Slovakia, Slovenia, Solomon Islands, Somalia, South Africa, South Korea, South Sudan, Spain, Sri Lanka, Sudan, Suriname, Sweden, Switzerland, Syria, Taiwan, Tajikistan, Tanzania, Thailand, Timor-Leste (formerly East Timor), Togo, Tonga, Trinidad and Tobago, Tunisia, Turkey, Turkmenistan, Tuvalu, Uganda, Ukraine, United Arab Emirates (UAE), United Kingdom (UK), United States of America (USA), Uruguay, Uzbekistan, Vanuatu, Vatican City (Holy See), Venezuela, Vietnam, Yemen, Zambia, Zimbabwe.".Split(',').Select(s => new SelectListItem(s,s)).ToList()
|
Options = "Afghanistan, Albania, Algeria, Andorra, Angola, Antigua and Barbuda, Argentina, Armenia, Australia, Austria, Azerbaijan, The Bahamas, Bahrain, Bangladesh, Barbados, Belarus, Belgium, Belize, Benin, Bhutan, Bolivia, Bosnia and Herzegovina, Botswana, Brazil, Brunei, Bulgaria, Burkina Faso, Burundi, Cabo Verde, Cambodia, Cameroon, Canada, Central African Republic (CAR), Chad, Chile, China, Colombia, Comoros, Democratic Republic of the Congo, Republic of the Congo, Costa Rica, Cote d'Ivoire, Croatia, Cuba, Cyprus, Czech Republic, Denmark, Djibouti, Dominica, Dominican Republic, Ecuador, Egypt, El Salvador, Equatorial Guinea, Eritrea, Estonia, Eswatini (formerly Swaziland), Ethiopia, Fiji, Finland, France, Gabon, The Gambia, Georgia, Germany, Ghana, Greece, Grenada, Guatemala, Guinea, Guinea-Bissau, Guyana, Haiti, Honduras, Hungary, Iceland, India, Indonesia, Iran, Iraq, Ireland, Israel, Italy, Jamaica, Japan, Jordan, Kazakhstan, Kenya, Kiribati, Kosovo, Kuwait, Kyrgyzstan, Laos, Latvia, Lebanon, Lesotho, Liberia, Libya, Liechtenstein, Lithuania, Luxembourg, Madagascar, Malawi, Malaysia, Maldives, Mali, Malta, Marshall Islands, Mauritania, Mauritius, Mexico, Micronesia, Moldova, Monaco, Mongolia, Montenegro, Morocco, Mozambique, Myanmar (formerly Burma), Namibia, Nauru, Nepal, Netherlands, New Zealand, Nicaragua, Niger, Nigeria, North Korea, North Macedonia (formerly Macedonia), Norway, Oman, Pakistan, Palau, Palestine, Panama, Papua New Guinea, Paraguay, Peru, Philippines, Poland, Portugal, Qatar, Romania, Russia, Rwanda, Saint Kitts and Nevis, Saint Lucia, Saint Vincent and the Grenadines, Samoa, San Marino, Sao Tome and Principe, Saudi Arabia, Senegal, Serbia, Seychelles, Sierra Leone, Singapore, Slovakia, Slovenia, Solomon Islands, Somalia, South Africa, South Korea, South Sudan, Spain, Sri Lanka, Sudan, Suriname, Sweden, Switzerland, Syria, Taiwan, Tajikistan, Tanzania, Thailand, Timor-Leste (formerly East Timor), Togo, Tonga, Trinidad and Tobago, Tunisia, Turkey, Turkmenistan, Tuvalu, Uganda, Ukraine, United Arab Emirates (UAE), United Kingdom (UK), United States of America (USA), Uruguay, Uzbekistan, Vanuatu, Vatican City (Holy See), Venezuela, Vietnam, Yemen, Zambia, Zimbabwe.".Split(',').Select(s => new SelectListItem(s,s)).ToList()
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly Dictionary<string, (string selectText, string name, Form form)> _hardcodedOptions = new()
|
private static readonly Dictionary<string, (string selectText, string name, Form form)> _hardcodedOptions = new()
|
||||||
{
|
{
|
||||||
{"", ("Do not request any information", null, null)!},
|
{"", ("Do not request any information", null, null)!},
|
||||||
@@ -64,13 +64,13 @@ public class FormDataService
|
|||||||
{"Address", ("Request shipping address", "Provide your address", StaticFormAddress)},
|
{"Address", ("Request shipping address", "Provide your address", StaticFormAddress)},
|
||||||
};
|
};
|
||||||
|
|
||||||
public async Task<SelectList> GetSelect(string storeId ,string selectedFormId)
|
public async Task<SelectList> GetSelect(string storeId, string selectedFormId)
|
||||||
{
|
{
|
||||||
var forms = await GetForms(storeId);
|
var forms = await GetForms(storeId);
|
||||||
return new SelectList(_hardcodedOptions.Select(pair => new SelectListItem(pair.Value.selectText, pair.Key, selectedFormId == pair.Key)).Concat(forms.Select(data => new SelectListItem(data.Name, data.Id, data.Id == selectedFormId))),
|
return new SelectList(_hardcodedOptions.Select(pair => new SelectListItem(pair.Value.selectText, pair.Key, selectedFormId == pair.Key)).Concat(forms.Select(data => new SelectListItem(data.Name, data.Id, data.Id == selectedFormId))),
|
||||||
nameof(SelectListItem.Value), nameof(SelectListItem.Text));
|
nameof(SelectListItem.Value), nameof(SelectListItem.Text));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<FormData>> GetForms(string storeId)
|
public async Task<List<FormData>> GetForms(string storeId)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(storeId);
|
ArgumentNullException.ThrowIfNull(storeId);
|
||||||
@@ -129,7 +129,7 @@ public class FormDataService
|
|||||||
{
|
{
|
||||||
return _formProviders.Validate(form, modelState);
|
return _formProviders.Validate(form, modelState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsFormSchemaValid(string schema, [MaybeNullWhen(false)] out Form form, [MaybeNullWhen(false)] out string error)
|
public bool IsFormSchemaValid(string schema, [MaybeNullWhen(false)] out Form form, [MaybeNullWhen(false)] out string error)
|
||||||
{
|
{
|
||||||
error = null;
|
error = null;
|
||||||
@@ -144,7 +144,7 @@ public class FormDataService
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
error = $"Form config was invalid: {ex.Message}";
|
error = $"Form config was invalid: {ex.Message}";
|
||||||
}
|
}
|
||||||
return error is null && form is not null;
|
return error is null && form is not null;
|
||||||
}
|
}
|
||||||
@@ -177,7 +177,7 @@ public class FormDataService
|
|||||||
public JObject GetValues(Form form)
|
public JObject GetValues(Form form)
|
||||||
{
|
{
|
||||||
var r = new JObject();
|
var r = new JObject();
|
||||||
|
|
||||||
foreach (var f in form.GetAllFields())
|
foreach (var f in form.GetAllFields())
|
||||||
{
|
{
|
||||||
var node = r;
|
var node = r;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ public class FieldValueMirror : IFormComponentProvider
|
|||||||
{
|
{
|
||||||
if (form.GetFieldByFullName(field.Value) is null)
|
if (form.GetFieldByFullName(field.Value) is null)
|
||||||
{
|
{
|
||||||
field.ValidationErrors = new List<string>() {$"{field.Name} requires {field.Value} to be present"};
|
field.ValidationErrors = new List<string>() { $"{field.Name} requires {field.Value} to be present" };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public class HtmlSelectFormProvider : FormComponentProviderBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SelectField: Field
|
public class SelectField : Field
|
||||||
{
|
{
|
||||||
public List<SelectListItem> Options { get; set; }
|
public List<SelectListItem> Options { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ public class ModifyForm
|
|||||||
|
|
||||||
[DisplayName("Form configuration (JSON)")]
|
[DisplayName("Form configuration (JSON)")]
|
||||||
public string FormConfig { get; set; }
|
public string FormConfig { get; set; }
|
||||||
|
|
||||||
[DisplayName("Allow form for public use")]
|
[DisplayName("Allow form for public use")]
|
||||||
public bool Public { get; set; }
|
public bool Public { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public class UIFormsController : Controller
|
|||||||
[HttpGet("~/stores/{storeId}/forms/new")]
|
[HttpGet("~/stores/{storeId}/forms/new")]
|
||||||
public IActionResult Create(string storeId)
|
public IActionResult Create(string storeId)
|
||||||
{
|
{
|
||||||
var vm = new ModifyForm {FormConfig = new Form().ToString()};
|
var vm = new ModifyForm { FormConfig = new Form().ToString() };
|
||||||
return View("Modify", vm);
|
return View("Modify", vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,10 +57,11 @@ public class UIFormsController : Controller
|
|||||||
public async Task<IActionResult> Modify(string storeId, string id)
|
public async Task<IActionResult> Modify(string storeId, string id)
|
||||||
{
|
{
|
||||||
var form = await _formDataService.GetForm(storeId, id);
|
var form = await _formDataService.GetForm(storeId, id);
|
||||||
if (form is null) return NotFound();
|
if (form is null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
var config = Form.Parse(form.Config);
|
var config = Form.Parse(form.Config);
|
||||||
return View(new ModifyForm {Name = form.Name, FormConfig = config.ToString(), Public = form.Public});
|
return View(new ModifyForm { Name = form.Name, FormConfig = config.ToString(), Public = form.Public });
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("~/stores/{storeId}/forms/modify/{id?}")]
|
[HttpPost("~/stores/{storeId}/forms/modify/{id?}")]
|
||||||
@@ -76,7 +77,7 @@ public class UIFormsController : Controller
|
|||||||
|
|
||||||
if (!_formDataService.IsFormSchemaValid(modifyForm.FormConfig, out var form, out var error))
|
if (!_formDataService.IsFormSchemaValid(modifyForm.FormConfig, out var form, out var error))
|
||||||
{
|
{
|
||||||
|
|
||||||
ModelState.AddModelError(nameof(modifyForm.FormConfig),
|
ModelState.AddModelError(nameof(modifyForm.FormConfig),
|
||||||
$"Form config was invalid: {error})");
|
$"Form config was invalid: {error})");
|
||||||
}
|
}
|
||||||
@@ -84,7 +85,7 @@ public class UIFormsController : Controller
|
|||||||
{
|
{
|
||||||
modifyForm.FormConfig = form.ToString();
|
modifyForm.FormConfig = form.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
{
|
{
|
||||||
@@ -95,7 +96,11 @@ public class UIFormsController : Controller
|
|||||||
{
|
{
|
||||||
var formData = new FormData
|
var formData = new FormData
|
||||||
{
|
{
|
||||||
Id = id, StoreId = storeId, Name = modifyForm.Name, Config = modifyForm.FormConfig,Public = modifyForm.Public
|
Id = id,
|
||||||
|
StoreId = storeId,
|
||||||
|
Name = modifyForm.Name,
|
||||||
|
Config = modifyForm.FormConfig,
|
||||||
|
Public = modifyForm.Public
|
||||||
};
|
};
|
||||||
var isNew = id is null;
|
var isNew = id is null;
|
||||||
await _formDataService.AddOrUpdateForm(formData);
|
await _formDataService.AddOrUpdateForm(formData);
|
||||||
@@ -106,7 +111,7 @@ public class UIFormsController : Controller
|
|||||||
});
|
});
|
||||||
if (isNew)
|
if (isNew)
|
||||||
{
|
{
|
||||||
return RedirectToAction("Modify", new {storeId, id = formData.Id});
|
return RedirectToAction("Modify", new { storeId, id = formData.Id });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -123,9 +128,10 @@ public class UIFormsController : Controller
|
|||||||
await _formDataService.RemoveForm(id, storeId);
|
await _formDataService.RemoveForm(id, storeId);
|
||||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||||
{
|
{
|
||||||
Severity = StatusMessageModel.StatusSeverity.Success, Message = "Form removed"
|
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||||
|
Message = "Form removed"
|
||||||
});
|
});
|
||||||
return RedirectToAction("FormsList", new {storeId});
|
return RedirectToAction("FormsList", new { storeId });
|
||||||
}
|
}
|
||||||
|
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
@@ -154,7 +160,7 @@ public class UIFormsController : Controller
|
|||||||
form.ApplyValuesFromForm(Request.Query);
|
form.ApplyValuesFromForm(Request.Query);
|
||||||
var store = formData.Store ?? await _storeRepository.FindStore(formData.StoreId);
|
var store = formData.Store ?? await _storeRepository.FindStore(formData.StoreId);
|
||||||
var storeBlob = store?.GetStoreBlob();
|
var storeBlob = store?.GetStoreBlob();
|
||||||
|
|
||||||
return View("View", new FormViewModel
|
return View("View", new FormViewModel
|
||||||
{
|
{
|
||||||
FormName = formData.Name,
|
FormName = formData.Name,
|
||||||
@@ -187,7 +193,7 @@ public class UIFormsController : Controller
|
|||||||
|
|
||||||
if (!Request.HasFormContentType)
|
if (!Request.HasFormContentType)
|
||||||
return await GetFormView(formData);
|
return await GetFormView(formData);
|
||||||
|
|
||||||
var form = Form.Parse(formData.Config);
|
var form = Form.Parse(formData.Config);
|
||||||
form.ApplyValuesFromForm(Request.Form);
|
form.ApplyValuesFromForm(Request.Form);
|
||||||
|
|
||||||
@@ -202,6 +208,6 @@ public class UIFormsController : Controller
|
|||||||
var request = _formDataService.GenerateInvoiceParametersFromForm(form);
|
var request = _formDataService.GenerateInvoiceParametersFromForm(form);
|
||||||
var inv = await invoiceController.CreateInvoiceCoreRaw(request, store, Request.GetAbsoluteRoot());
|
var inv = await invoiceController.CreateInvoiceCoreRaw(request, store, Request.GetAbsoluteRoot());
|
||||||
|
|
||||||
return RedirectToAction("Checkout", "UIInvoice", new {invoiceId = inv.Id});
|
return RedirectToAction("Checkout", "UIInvoice", new { invoiceId = inv.Id });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ namespace BTCPayServer.HostedServices
|
|||||||
foreach (var t in _Tasks)
|
foreach (var t in _Tasks)
|
||||||
t.ContinueWith(t =>
|
t.ContinueWith(t =>
|
||||||
{
|
{
|
||||||
if (t.IsFaulted)
|
if (t.IsFaulted && !CancellationToken.IsCancellationRequested)
|
||||||
Logs.PayServer.LogWarning(t.Exception, $"Unhanded exception in {this.GetType().Name}");
|
Logs.PayServer.LogWarning(t.Exception, $"Unhanded exception in {this.GetType().Name}");
|
||||||
}, TaskScheduler.Default);
|
}, TaskScheduler.Default);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
@@ -40,7 +40,7 @@ namespace BTCPayServer.HostedServices
|
|||||||
|
|
||||||
internal abstract Task[] InitializeTasks();
|
internal abstract Task[] InitializeTasks();
|
||||||
|
|
||||||
protected CancellationToken Cancellation
|
protected CancellationToken CancellationToken
|
||||||
{
|
{
|
||||||
get { return _Cts.Token; }
|
get { return _Cts.Token; }
|
||||||
}
|
}
|
||||||
@@ -69,8 +69,6 @@ namespace BTCPayServer.HostedServices
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public CancellationToken CancellationToken => _Cts.Token;
|
|
||||||
|
|
||||||
public virtual async Task StopAsync(CancellationToken cancellationToken)
|
public virtual async Task StopAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (_Cts != null)
|
if (_Cts != null)
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ namespace BTCPayServer.HostedServices
|
|||||||
readonly InvoiceRepository _InvoiceRepository;
|
readonly InvoiceRepository _InvoiceRepository;
|
||||||
private readonly EmailSenderFactory _EmailSenderFactory;
|
private readonly EmailSenderFactory _EmailSenderFactory;
|
||||||
private readonly StoreRepository _StoreRepository;
|
private readonly StoreRepository _StoreRepository;
|
||||||
|
public const string NamedClient = "bitpay-ipn";
|
||||||
public BitpayIPNSender(
|
public BitpayIPNSender(
|
||||||
IHttpClientFactory httpClientFactory,
|
IHttpClientFactory httpClientFactory,
|
||||||
IBackgroundJobClient jobClient,
|
IBackgroundJobClient jobClient,
|
||||||
@@ -52,7 +52,7 @@ namespace BTCPayServer.HostedServices
|
|||||||
StoreRepository storeRepository,
|
StoreRepository storeRepository,
|
||||||
EmailSenderFactory emailSenderFactory)
|
EmailSenderFactory emailSenderFactory)
|
||||||
{
|
{
|
||||||
_Client = httpClientFactory.CreateClient();
|
_Client = httpClientFactory.CreateClient(NamedClient);
|
||||||
_JobClient = jobClient;
|
_JobClient = jobClient;
|
||||||
_EventAggregator = eventAggregator;
|
_EventAggregator = eventAggregator;
|
||||||
_InvoiceRepository = invoiceRepository;
|
_InvoiceRepository = invoiceRepository;
|
||||||
@@ -232,7 +232,6 @@ namespace BTCPayServer.HostedServices
|
|||||||
|
|
||||||
request.RequestUri = new Uri(notification.NotificationURL, UriKind.Absolute);
|
request.RequestUri = new Uri(notification.NotificationURL, UriKind.Absolute);
|
||||||
request.Content = new StringContent(notificationString, UTF8, "application/json");
|
request.Content = new StringContent(notificationString, UTF8, "application/json");
|
||||||
|
|
||||||
using CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
using CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||||
cts.CancelAfter(TimeSpan.FromMinutes(1.0));
|
cts.CancelAfter(TimeSpan.FromMinutes(1.0));
|
||||||
var response = await _Client.SendAsync(request, cts.Token);
|
var response = await _Client.SendAsync(request, cts.Token);
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ namespace BTCPayServer.HostedServices
|
|||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
await _transactionBroadcaster.ProcessAll(Cancellation);
|
await _transactionBroadcaster.ProcessAll(CancellationToken);
|
||||||
await Task.Delay(PollInternal, Cancellation);
|
await Task.Delay(PollInternal, CancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ namespace BTCPayServer.HostedServices
|
|||||||
readonly TimeSpan Period = TimeSpan.FromMinutes(60);
|
readonly TimeSpan Period = TimeSpan.FromMinutes(60);
|
||||||
async Task UpdateRecord()
|
async Task UpdateRecord()
|
||||||
{
|
{
|
||||||
using (var timeout = CancellationTokenSource.CreateLinkedTokenSource(Cancellation))
|
using (var timeout = CancellationTokenSource.CreateLinkedTokenSource(CancellationToken))
|
||||||
{
|
{
|
||||||
var settings = await SettingsRepository.GetSettingAsync<DynamicDnsSettings>() ?? new DynamicDnsSettings();
|
var settings = await SettingsRepository.GetSettingAsync<DynamicDnsSettings>() ?? new DynamicDnsSettings();
|
||||||
foreach (var service in settings.Services)
|
foreach (var service in settings.Services)
|
||||||
@@ -59,9 +59,9 @@ namespace BTCPayServer.HostedServices
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
using var delayCancel = CancellationTokenSource.CreateLinkedTokenSource(Cancellation);
|
using var delayCancel = CancellationTokenSource.CreateLinkedTokenSource(CancellationToken);
|
||||||
var delay = Task.Delay(Period, delayCancel.Token);
|
var delay = Task.Delay(Period, delayCancel.Token);
|
||||||
var changed = SettingsRepository.WaitSettingsChanged<DynamicDnsSettings>(Cancellation);
|
var changed = SettingsRepository.WaitSettingsChanged<DynamicDnsSettings>(CancellationToken);
|
||||||
await Task.WhenAny(delay, changed);
|
await Task.WhenAny(delay, changed);
|
||||||
delayCancel.Cancel();
|
delayCancel.Cancel();
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user