Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
f09d6618c9 |
@ -36,7 +36,4 @@
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.4" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.1.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BTCPayServer.Client\BTCPayServer.Client.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -1,10 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Contracts
|
||||
{
|
||||
public interface IBTCPayServerClientFactory
|
||||
{
|
||||
Task<BTCPayServerClient> Create(string userId, params string[] storeIds);
|
||||
}
|
||||
}
|
@ -1,5 +1,3 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Contracts
|
||||
{
|
||||
public interface ISyncSummaryProvider
|
||||
@ -7,12 +5,5 @@ namespace BTCPayServer.Abstractions.Contracts
|
||||
bool AllAvailable();
|
||||
|
||||
string Partial { get; }
|
||||
IEnumerable<ISyncStatus> GetStatuses();
|
||||
}
|
||||
|
||||
public interface ISyncStatus
|
||||
{
|
||||
public string CryptoCode { get; set; }
|
||||
public bool Available { get; }
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
<RepositoryType>git</RepositoryType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<Version Condition=" '$(Version)' == '' ">1.4.0</Version>
|
||||
<Version Condition=" '$(Version)' == '' ">1.3.0</Version>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
@ -27,8 +27,8 @@
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NBitcoin" Version="6.0.7" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.2.4" />
|
||||
<PackageReference Include="NBitcoin" Version="5.0.73" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.2.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -1,44 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<IEnumerable<InvoiceData>> GetInvoices(string storeId, string[] orderId = null,
|
||||
InvoiceStatus[] status = null,
|
||||
DateTimeOffset? startDate = null,
|
||||
DateTimeOffset? endDate = null,
|
||||
string textSearch = null,
|
||||
bool includeArchived = false,
|
||||
public virtual async Task<IEnumerable<InvoiceData>> GetInvoices(string storeId, bool includeArchived = false,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
Dictionary<string, object> queryPayload = new Dictionary<string, object>();
|
||||
queryPayload.Add(nameof(includeArchived), includeArchived);
|
||||
|
||||
if (startDate is DateTimeOffset s)
|
||||
queryPayload.Add(nameof(startDate), Utils.DateTimeToUnixTime(s));
|
||||
|
||||
if (endDate is DateTimeOffset e)
|
||||
queryPayload.Add(nameof(endDate), Utils.DateTimeToUnixTime(e));
|
||||
|
||||
if (orderId != null)
|
||||
queryPayload.Add(nameof(orderId), orderId);
|
||||
if (textSearch != null)
|
||||
queryPayload.Add(nameof(textSearch), textSearch);
|
||||
if (status != null)
|
||||
queryPayload.Add(nameof(status), status.Select(s=> s.ToString().ToLower()).ToArray());
|
||||
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/invoices",
|
||||
queryPayload), token);
|
||||
new Dictionary<string, object>() {{nameof(includeArchived), includeArchived}}), token);
|
||||
return await HandleResponse<IEnumerable<InvoiceData>>(response);
|
||||
}
|
||||
|
||||
@ -93,7 +70,7 @@ namespace BTCPayServer.Client
|
||||
{
|
||||
if (request == null)
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
if (request.Status != InvoiceStatus.Settled && request.Status != InvoiceStatus.Invalid)
|
||||
if (request.Status!= InvoiceStatus.Settled && request.Status!= InvoiceStatus.Invalid)
|
||||
throw new ArgumentOutOfRangeException(nameof(request.Status), "Status can only be Invalid or Complete");
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}/status", bodyPayload: request,
|
||||
|
@ -9,7 +9,7 @@ namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<LightningNodeInformationData> GetLightningNodeInfo(string cryptoCode,
|
||||
public async Task<LightningNodeInformationData> GetLightningNodeInfo(string cryptoCode,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
@ -18,7 +18,7 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<LightningNodeInformationData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task ConnectToLightningNode(string cryptoCode, ConnectToNodeRequest request,
|
||||
public async Task ConnectToLightningNode(string cryptoCode, ConnectToNodeRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
@ -29,7 +29,7 @@ namespace BTCPayServer.Client
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public virtual async Task<IEnumerable<LightningChannelData>> GetLightningNodeChannels(string cryptoCode,
|
||||
public async Task<IEnumerable<LightningChannelData>> GetLightningNodeChannels(string cryptoCode,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
@ -38,16 +38,16 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<IEnumerable<LightningChannelData>>(response);
|
||||
}
|
||||
|
||||
public virtual async Task OpenLightningChannel(string cryptoCode, OpenLightningChannelRequest request,
|
||||
public async Task<string> OpenLightningChannel(string cryptoCode, OpenLightningChannelRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/channels", bodyPayload: request,
|
||||
method: HttpMethod.Post), token);
|
||||
await HandleResponse(response);
|
||||
return await HandleResponse<string>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<string> GetLightningDepositAddress(string cryptoCode, CancellationToken token = default)
|
||||
public async Task<string> GetLightningDepositAddress(string cryptoCode, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/address", method: HttpMethod.Post), token);
|
||||
@ -55,7 +55,7 @@ namespace BTCPayServer.Client
|
||||
}
|
||||
|
||||
|
||||
public virtual async Task PayLightningInvoice(string cryptoCode, PayLightningInvoiceRequest request,
|
||||
public async Task PayLightningInvoice(string cryptoCode, PayLightningInvoiceRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
@ -66,7 +66,7 @@ namespace BTCPayServer.Client
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LightningInvoiceData> GetLightningInvoice(string cryptoCode,
|
||||
public async Task<LightningInvoiceData> GetLightningInvoice(string cryptoCode,
|
||||
string invoiceId, CancellationToken token = default)
|
||||
{
|
||||
if (invoiceId == null)
|
||||
@ -77,7 +77,7 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<LightningInvoiceData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LightningInvoiceData> CreateLightningInvoice(string cryptoCode, CreateLightningInvoiceRequest request,
|
||||
public async Task<LightningInvoiceData> CreateLightningInvoice(string cryptoCode, CreateLightningInvoiceRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
|
@ -9,7 +9,7 @@ namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<LightningNodeInformationData> GetLightningNodeInfo(string storeId, string cryptoCode,
|
||||
public async Task<LightningNodeInformationData> GetLightningNodeInfo(string storeId, string cryptoCode,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
@ -18,7 +18,7 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<LightningNodeInformationData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task ConnectToLightningNode(string storeId, string cryptoCode, ConnectToNodeRequest request,
|
||||
public async Task ConnectToLightningNode(string storeId, string cryptoCode, ConnectToNodeRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
@ -29,7 +29,7 @@ namespace BTCPayServer.Client
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public virtual async Task<IEnumerable<LightningChannelData>> GetLightningNodeChannels(string storeId, string cryptoCode,
|
||||
public async Task<IEnumerable<LightningChannelData>> GetLightningNodeChannels(string storeId, string cryptoCode,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
@ -38,16 +38,16 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<IEnumerable<LightningChannelData>>(response);
|
||||
}
|
||||
|
||||
public virtual async Task OpenLightningChannel(string storeId, string cryptoCode, OpenLightningChannelRequest request,
|
||||
public async Task<string> OpenLightningChannel(string storeId, string cryptoCode, OpenLightningChannelRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/channels", bodyPayload: request,
|
||||
method: HttpMethod.Post), token);
|
||||
await HandleResponse(response);
|
||||
return await HandleResponse<string>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<string> GetLightningDepositAddress(string storeId, string cryptoCode,
|
||||
public async Task<string> GetLightningDepositAddress(string storeId, string cryptoCode,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
@ -56,7 +56,7 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<string>(response);
|
||||
}
|
||||
|
||||
public virtual async Task PayLightningInvoice(string storeId, string cryptoCode, PayLightningInvoiceRequest request,
|
||||
public async Task PayLightningInvoice(string storeId, string cryptoCode, PayLightningInvoiceRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
@ -67,7 +67,7 @@ namespace BTCPayServer.Client
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LightningInvoiceData> GetLightningInvoice(string storeId, string cryptoCode,
|
||||
public async Task<LightningInvoiceData> GetLightningInvoice(string storeId, string cryptoCode,
|
||||
string invoiceId, CancellationToken token = default)
|
||||
{
|
||||
if (invoiceId == null)
|
||||
@ -78,7 +78,7 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<LightningInvoiceData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LightningInvoiceData> CreateLightningInvoice(string storeId, string cryptoCode,
|
||||
public async Task<LightningInvoiceData> CreateLightningInvoice(string storeId, string cryptoCode,
|
||||
CreateLightningInvoiceRequest request, CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
|
@ -9,19 +9,12 @@ namespace BTCPayServer.Client
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<IEnumerable<LightningNetworkPaymentMethodData>>
|
||||
GetStoreLightningNetworkPaymentMethods(string storeId, bool? enabled = null,
|
||||
GetStoreLightningNetworkPaymentMethods(string storeId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var query = new Dictionary<string, object>();
|
||||
if (enabled != null)
|
||||
{
|
||||
query.Add(nameof(enabled), enabled);
|
||||
}
|
||||
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LightningNetwork",
|
||||
query), token);
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LightningNetwork"), token);
|
||||
return await HandleResponse<IEnumerable<LightningNetworkPaymentMethodData>>(response);
|
||||
}
|
||||
|
||||
@ -56,9 +49,15 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<LightningNetworkPaymentMethodData>(response);
|
||||
}
|
||||
|
||||
public virtual Task<LightningNetworkPaymentMethodData>
|
||||
public virtual async Task<LightningNetworkPaymentMethodData>
|
||||
UpdateStoreLightningNetworkPaymentMethodToInternalNode(string storeId,
|
||||
string cryptoCode, CancellationToken token = default) => UpdateStoreLightningNetworkPaymentMethod(
|
||||
storeId, cryptoCode, new LightningNetworkPaymentMethodData(cryptoCode, "Internal Node", true), token);
|
||||
string cryptoCode, LightningNetworkPaymentMethodData paymentMethod,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}/internal",
|
||||
method: HttpMethod.Put), token);
|
||||
return await HandleResponse<LightningNetworkPaymentMethodData>(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<PermissionMetadata[]> GetPermissionMetadata(CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest("misc/permissions"), token);
|
||||
return await HandleResponse<PermissionMetadata[]>(response);
|
||||
}
|
||||
public virtual async Task<Language[]> GetAvailableLanguages(CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest("misc/lang"), token);
|
||||
return await HandleResponse<Language[]>(response);
|
||||
}
|
||||
}
|
||||
}
|
@ -9,19 +9,11 @@ namespace BTCPayServer.Client
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<IEnumerable<OnChainPaymentMethodData>> GetStoreOnChainPaymentMethods(string storeId,
|
||||
bool? enabled = null,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var query = new Dictionary<string, object>();
|
||||
if (enabled != null)
|
||||
{
|
||||
query.Add(nameof(enabled), enabled);
|
||||
}
|
||||
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain",
|
||||
query), token);
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain"), token);
|
||||
return await HandleResponse<IEnumerable<OnChainPaymentMethodData>>(response);
|
||||
}
|
||||
|
||||
@ -78,17 +70,5 @@ namespace BTCPayServer.Client
|
||||
method: HttpMethod.Get), token);
|
||||
return await HandleResponse<OnChainPaymentMethodPreviewResultData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<OnChainPaymentMethodDataWithSensitiveData> GenerateOnChainWallet(string storeId,
|
||||
string cryptoCode, GenerateOnChainWalletRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain/{cryptoCode}/generate",
|
||||
bodyPayload: request,
|
||||
method: HttpMethod.Post), token);
|
||||
return await HandleResponse<OnChainPaymentMethodDataWithSensitiveData>(response);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -9,18 +9,18 @@ namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<PullPaymentData> CreatePullPayment(string storeId, CreatePullPaymentRequest request, CancellationToken cancellationToken = default)
|
||||
public async Task<PullPaymentData> CreatePullPayment(string storeId, CreatePullPaymentRequest request, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{HttpUtility.UrlEncode(storeId)}/pull-payments", bodyPayload: request, method: HttpMethod.Post), cancellationToken);
|
||||
return await HandleResponse<PullPaymentData>(response);
|
||||
}
|
||||
public virtual async Task<PullPaymentData> GetPullPayment(string pullPaymentId, CancellationToken cancellationToken = default)
|
||||
public async Task<PullPaymentData> GetPullPayment(string pullPaymentId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}", method: HttpMethod.Get), cancellationToken);
|
||||
return await HandleResponse<PullPaymentData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<PullPaymentData[]> GetPullPayments(string storeId, bool includeArchived = false, CancellationToken cancellationToken = default)
|
||||
public async Task<PullPaymentData[]> GetPullPayments(string storeId, bool includeArchived = false, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Dictionary<string, object> query = new Dictionary<string, object>();
|
||||
query.Add("includeArchived", includeArchived);
|
||||
@ -28,44 +28,34 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<PullPaymentData[]>(response);
|
||||
}
|
||||
|
||||
public virtual async Task ArchivePullPayment(string storeId, string pullPaymentId, CancellationToken cancellationToken = default)
|
||||
public async Task ArchivePullPayment(string storeId, string pullPaymentId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{HttpUtility.UrlEncode(storeId)}/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}", method: HttpMethod.Delete), cancellationToken);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public virtual async Task<PayoutData[]> GetPayouts(string pullPaymentId, bool includeCancelled = false, CancellationToken cancellationToken = default)
|
||||
public async Task<PayoutData[]> GetPayouts(string pullPaymentId, bool includeCancelled = false, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Dictionary<string, object> query = new Dictionary<string, object>();
|
||||
query.Add("includeCancelled", includeCancelled);
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}/payouts", queryPayload: query, method: HttpMethod.Get), cancellationToken);
|
||||
return await HandleResponse<PayoutData[]>(response);
|
||||
}
|
||||
public virtual async Task<PayoutData> CreatePayout(string pullPaymentId, CreatePayoutRequest payoutRequest, CancellationToken cancellationToken = default)
|
||||
public async Task<PayoutData> CreatePayout(string pullPaymentId, CreatePayoutRequest payoutRequest, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}/payouts", bodyPayload: payoutRequest, method: HttpMethod.Post), cancellationToken);
|
||||
return await HandleResponse<PayoutData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task CancelPayout(string storeId, string payoutId, CancellationToken cancellationToken = default)
|
||||
public async Task CancelPayout(string storeId, string payoutId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{HttpUtility.UrlEncode(storeId)}/payouts/{HttpUtility.UrlEncode(payoutId)}", method: HttpMethod.Delete), cancellationToken);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
public virtual async Task<PayoutData> ApprovePayout(string storeId, string payoutId, ApprovePayoutRequest request, CancellationToken cancellationToken = default)
|
||||
public async Task<PayoutData> ApprovePayout(string storeId, string payoutId, ApprovePayoutRequest request, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{HttpUtility.UrlEncode(storeId)}/payouts/{HttpUtility.UrlEncode(payoutId)}", bodyPayload: request, method: HttpMethod.Post), cancellationToken);
|
||||
return await HandleResponse<PayoutData>(response);
|
||||
}
|
||||
|
||||
public async Task MarkPayoutPaid(string storeId, string payoutId,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest(
|
||||
$"api/v1/stores/{HttpUtility.UrlEncode(storeId)}/payouts/{HttpUtility.UrlEncode(payoutId)}/mark-paid",
|
||||
method: HttpMethod.Post), cancellationToken);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<Dictionary<string, GenericPaymentMethodData>> GetStorePaymentMethods(string storeId,
|
||||
bool? enabled = null,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var query = new Dictionary<string, object>();
|
||||
if (enabled != null)
|
||||
{
|
||||
query.Add(nameof(enabled), enabled);
|
||||
}
|
||||
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods",
|
||||
query), token);
|
||||
return await HandleResponse<Dictionary<string, GenericPaymentMethodData>>(response);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
#nullable enable
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -20,16 +19,5 @@ namespace BTCPayServer.Client
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/users", null, request, HttpMethod.Post), token);
|
||||
return await HandleResponse<ApplicationUserData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task DeleteUser(string userId, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/users/{userId}", null, HttpMethod.Delete), token);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public virtual async Task DeleteCurrentUser(CancellationToken token = default)
|
||||
{
|
||||
await DeleteUser("me", token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,56 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<StoreWebhookData> CreateWebhook(string storeId, Client.Models.CreateStoreWebhookRequest create, CancellationToken token = default)
|
||||
public async Task<StoreWebhookData> CreateWebhook(string storeId, Client.Models.CreateStoreWebhookRequest create, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks", bodyPayload: create, method: HttpMethod.Post), token);
|
||||
return await HandleResponse<StoreWebhookData>(response);
|
||||
}
|
||||
public virtual async Task<StoreWebhookData> GetWebhook(string storeId, string webhookId, CancellationToken token = default)
|
||||
public async Task<StoreWebhookData> GetWebhook(string storeId, string webhookId, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks/{webhookId}"), token);
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
|
||||
return null;
|
||||
return await HandleResponse<StoreWebhookData>(response);
|
||||
}
|
||||
public virtual async Task<StoreWebhookData> UpdateWebhook(string storeId, string webhookId, Models.UpdateStoreWebhookRequest update, CancellationToken token = default)
|
||||
public async Task<StoreWebhookData> UpdateWebhook(string storeId, string webhookId, Models.UpdateStoreWebhookRequest update, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks/{webhookId}", bodyPayload: update, method: HttpMethod.Put), token);
|
||||
return await HandleResponse<StoreWebhookData>(response);
|
||||
}
|
||||
public virtual async Task<bool> DeleteWebhook(string storeId, string webhookId, CancellationToken token = default)
|
||||
public async Task<bool> DeleteWebhook(string storeId, string webhookId, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks/{webhookId}", method: HttpMethod.Delete), token);
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
public virtual async Task<StoreWebhookData[]> GetWebhooks(string storeId, CancellationToken token = default)
|
||||
public async Task<StoreWebhookData[]> GetWebhooks(string storeId, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks"), token);
|
||||
return await HandleResponse<StoreWebhookData[]>(response);
|
||||
}
|
||||
public virtual async Task<WebhookDeliveryData[]> GetWebhookDeliveries(string storeId, string webhookId, CancellationToken token = default)
|
||||
public async Task<WebhookDeliveryData[]> GetWebhookDeliveries(string storeId, string webhookId, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks/{webhookId}/deliveries"), token);
|
||||
return await HandleResponse<WebhookDeliveryData[]>(response);
|
||||
}
|
||||
public virtual async Task<WebhookDeliveryData> GetWebhookDelivery(string storeId, string webhookId, string deliveryId, CancellationToken token = default)
|
||||
public async Task<WebhookDeliveryData> GetWebhookDelivery(string storeId, string webhookId, string deliveryId, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks/{webhookId}/deliveries/{deliveryId}"), token);
|
||||
return await HandleResponse<WebhookDeliveryData>(response);
|
||||
}
|
||||
public virtual async Task<string> RedeliverWebhook(string storeId, string webhookId, string deliveryId, CancellationToken token = default)
|
||||
public async Task<string> RedeliverWebhook(string storeId, string webhookId, string deliveryId, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks/{webhookId}/deliveries/{deliveryId}/redeliver", null, HttpMethod.Post), token);
|
||||
return await HandleResponse<string>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<WebhookEvent> GetWebhookDeliveryRequest(string storeId, string webhookId, string deliveryId, CancellationToken token = default)
|
||||
public async Task<WebhookEvent> GetWebhookDeliveryRequest(string storeId, string webhookId, string deliveryId, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/webhooks/{webhookId}/deliveries/{deliveryId}/request"), token);
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
|
||||
|
@ -5,7 +5,6 @@ using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
@ -70,13 +69,6 @@ namespace BTCPayServer.Client
|
||||
return JsonConvert.DeserializeObject<T>(str);
|
||||
}
|
||||
|
||||
public async Task<T> SendHttpRequest<T>(string path,
|
||||
Dictionary<string, object> queryPayload = null,
|
||||
HttpMethod method = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var resp = await _httpClient.SendAsync(CreateHttpRequest(path, queryPayload, method), cancellationToken);
|
||||
return await HandleResponse<T>(resp);
|
||||
}
|
||||
protected virtual HttpRequestMessage CreateHttpRequest(string path,
|
||||
Dictionary<string, object> queryPayload = null,
|
||||
HttpMethod method = null)
|
||||
|
@ -1,31 +0,0 @@
|
||||
using System;
|
||||
using NBitcoin;
|
||||
using NBitcoin.JsonConverters;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Client.JsonConverters
|
||||
{
|
||||
public class MnemonicJsonConverter : JsonConverter<Mnemonic>
|
||||
{
|
||||
public override Mnemonic ReadJson(JsonReader reader, Type objectType, Mnemonic existingValue, bool hasExistingValue,
|
||||
JsonSerializer serializer)
|
||||
{
|
||||
return reader.TokenType switch
|
||||
{
|
||||
JsonToken.String => new Mnemonic((string)reader.Value),
|
||||
JsonToken.Null => null,
|
||||
_ => throw new JsonObjectException(reader.Path, "Mnemonic must be a json string")
|
||||
};
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, Mnemonic value, JsonSerializer serializer)
|
||||
{
|
||||
if (value != null)
|
||||
writer.WriteValue(value.ToString());
|
||||
else
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
public class WordcountJsonConverter : JsonConverter
|
||||
{
|
||||
static WordcountJsonConverter()
|
||||
{
|
||||
_Wordcount = new Dictionary<long, WordCount>()
|
||||
{
|
||||
{18, WordCount.Eighteen},
|
||||
{15, WordCount.Fifteen},
|
||||
{12, WordCount.Twelve},
|
||||
{24, WordCount.TwentyFour},
|
||||
{21, WordCount.TwentyOne}
|
||||
};
|
||||
_WordcountReverse = _Wordcount.ToDictionary(kv => kv.Value, kv => kv.Key);
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return typeof(NBitcoin.WordCount).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo()) ||
|
||||
typeof(NBitcoin.WordCount?).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.TokenType == JsonToken.Null)
|
||||
return default;
|
||||
if (reader.TokenType != JsonToken.Integer)
|
||||
throw new NBitcoin.JsonConverters.JsonObjectException(
|
||||
$"Unexpected json token type, expected Integer, actual {reader.TokenType}", reader);
|
||||
if (!_Wordcount.TryGetValue((long)reader.Value, out var result))
|
||||
throw new NBitcoin.JsonConverters.JsonObjectException(
|
||||
$"Invalid WordCount, possible values {string.Join(", ", _Wordcount.Keys.ToArray())} (default: 12)",
|
||||
reader);
|
||||
return result;
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
if (value is WordCount wc)
|
||||
writer.WriteValue(_WordcountReverse[wc]);
|
||||
}
|
||||
|
||||
readonly static Dictionary<long, WordCount> _Wordcount = new Dictionary<long, WordCount>()
|
||||
{
|
||||
{18, WordCount.Eighteen},
|
||||
{15, WordCount.Fifteen},
|
||||
{12, WordCount.Twelve},
|
||||
{24, WordCount.TwentyFour},
|
||||
{21, WordCount.TwentyOne}
|
||||
};
|
||||
|
||||
readonly static Dictionary<WordCount, long> _WordcountReverse;
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
public class WordlistJsonConverter : JsonConverter
|
||||
{
|
||||
static WordlistJsonConverter()
|
||||
{
|
||||
|
||||
_Wordlists = new Dictionary<string, Wordlist>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{"English", Wordlist.English},
|
||||
{"Japanese", Wordlist.Japanese},
|
||||
{"Spanish", Wordlist.Spanish},
|
||||
{"ChineseSimplified", Wordlist.ChineseSimplified},
|
||||
{"ChineseTraditional", Wordlist.ChineseTraditional},
|
||||
{"French", Wordlist.French},
|
||||
{"PortugueseBrazil", Wordlist.PortugueseBrazil},
|
||||
{"Czech", Wordlist.Czech}
|
||||
};
|
||||
|
||||
_WordlistsReverse = _Wordlists.ToDictionary(kv => kv.Value, kv => kv.Key);
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return typeof(Wordlist).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.TokenType == JsonToken.Null)
|
||||
return null;
|
||||
if (reader.TokenType != JsonToken.String)
|
||||
throw new NBitcoin.JsonConverters.JsonObjectException(
|
||||
$"Unexpected json token type, expected String, actual {reader.TokenType}", reader);
|
||||
if (!_Wordlists.TryGetValue((string)reader.Value, out var result))
|
||||
throw new NBitcoin.JsonConverters.JsonObjectException(
|
||||
$"Invalid wordlist, possible values {string.Join(", ", _Wordlists.Keys.ToArray())} (default: English)",
|
||||
reader);
|
||||
return result;
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
if (value is Wordlist wl)
|
||||
writer.WriteValue(_WordlistsReverse[wl]);
|
||||
}
|
||||
|
||||
readonly static Dictionary<string, Wordlist> _Wordlists;
|
||||
readonly static Dictionary<Wordlist, string> _WordlistsReverse;
|
||||
}
|
@ -7,10 +7,35 @@ using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class CreateInvoiceRequest : InvoiceDataBase
|
||||
public class CreateInvoiceRequest
|
||||
{
|
||||
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||
public decimal? Amount { get; set; }
|
||||
public string[] AdditionalSearchTerms { get; set; }
|
||||
public decimal Amount { get; set; }
|
||||
public string Currency { get; set; }
|
||||
public JObject Metadata { get; set; }
|
||||
public CheckoutOptions Checkout { get; set; } = new CheckoutOptions();
|
||||
|
||||
public class CheckoutOptions
|
||||
{
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public SpeedPolicy? SpeedPolicy { get; set; }
|
||||
|
||||
public string[] PaymentMethods { get; set; }
|
||||
|
||||
[JsonConverter(typeof(TimeSpanJsonConverter.Minutes))]
|
||||
[JsonProperty("expirationMinutes")]
|
||||
public TimeSpan? Expiration { get; set; }
|
||||
[JsonConverter(typeof(TimeSpanJsonConverter.Minutes))]
|
||||
[JsonProperty("monitoringMinutes")]
|
||||
public TimeSpan? Monitoring { get; set; }
|
||||
|
||||
public double? PaymentTolerance { get; set; }
|
||||
[JsonProperty("redirectURL")]
|
||||
public string RedirectURL { get; set; }
|
||||
|
||||
public bool? RedirectAutomatically { get; set; }
|
||||
public string DefaultLanguage { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
using BTCPayServer.Client.JsonConverters;
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace BTCPayServer.Client
|
||||
{
|
||||
public class GenerateOnChainWalletRequest
|
||||
{
|
||||
public int AccountNumber { get; set; } = 0;
|
||||
[JsonConverter(typeof(MnemonicJsonConverter))]
|
||||
public Mnemonic ExistingMnemonic { get; set; }
|
||||
[JsonConverter(typeof(WordlistJsonConverter))]
|
||||
public NBitcoin.Wordlist WordList { get; set; }
|
||||
|
||||
[JsonConverter(typeof(WordcountJsonConverter))]
|
||||
public NBitcoin.WordCount? WordCount { get; set; } = NBitcoin.WordCount.Twelve;
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public NBitcoin.ScriptPubKeyType ScriptPubKeyType { get; set; } = ScriptPubKeyType.Segwit;
|
||||
public string Passphrase { get; set; }
|
||||
public bool ImportKeysToRPC { get; set; }
|
||||
public bool SavePrivateKeys { get; set; }
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class GenericPaymentMethodData
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
public object Data { get; set; }
|
||||
}
|
||||
}
|
@ -1,53 +1,12 @@
|
||||
using System;
|
||||
using BTCPayServer.Client.JsonConverters;
|
||||
using BTCPayServer.JsonConverters;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public enum InvoiceType
|
||||
{
|
||||
Standard,
|
||||
TopUp
|
||||
}
|
||||
public class InvoiceDataBase
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public InvoiceType Type { get; set; }
|
||||
public string Currency { get; set; }
|
||||
public JObject Metadata { get; set; }
|
||||
public CheckoutOptions Checkout { get; set; } = new CheckoutOptions();
|
||||
public class CheckoutOptions
|
||||
{
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public SpeedPolicy? SpeedPolicy { get; set; }
|
||||
|
||||
public string[] PaymentMethods { get; set; }
|
||||
|
||||
[JsonConverter(typeof(TimeSpanJsonConverter.Minutes))]
|
||||
[JsonProperty("expirationMinutes")]
|
||||
public TimeSpan? Expiration { get; set; }
|
||||
[JsonConverter(typeof(TimeSpanJsonConverter.Minutes))]
|
||||
[JsonProperty("monitoringMinutes")]
|
||||
public TimeSpan? Monitoring { get; set; }
|
||||
|
||||
public double? PaymentTolerance { get; set; }
|
||||
[JsonProperty("redirectURL")]
|
||||
public string RedirectURL { get; set; }
|
||||
|
||||
public bool? RedirectAutomatically { get; set; }
|
||||
public string DefaultLanguage { get; set; }
|
||||
}
|
||||
}
|
||||
public class InvoiceData : InvoiceDataBase
|
||||
public class InvoiceData : CreateInvoiceRequest
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string StoreId { get; set; }
|
||||
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||
public decimal Amount { get; set; }
|
||||
public string CheckoutLink { get; set; }
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public InvoiceStatus Status { get; set; }
|
||||
|
@ -1,21 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class Language
|
||||
{
|
||||
public Language(string code, string displayName)
|
||||
{
|
||||
DisplayName = displayName;
|
||||
Code = code;
|
||||
}
|
||||
|
||||
[JsonProperty("code")]
|
||||
public string Code { get; set; }
|
||||
[JsonProperty("currentLanguage")]
|
||||
public string DisplayName { get; set; }
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class LightningNetworkPaymentMethodBaseData
|
||||
{
|
||||
|
||||
public string ConnectionString { get; set; }
|
||||
public LightningNetworkPaymentMethodBaseData()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class LightningNetworkPaymentMethodData: LightningNetworkPaymentMethodBaseData
|
||||
public class LightningNetworkPaymentMethodData
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the payment method is enabled
|
||||
@ -12,6 +12,8 @@ namespace BTCPayServer.Client.Models
|
||||
/// </summary>
|
||||
public string CryptoCode { get; set; }
|
||||
|
||||
public string ConnectionString { get; set; }
|
||||
|
||||
public LightningNetworkPaymentMethodData()
|
||||
{
|
||||
}
|
||||
|
@ -1,24 +0,0 @@
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class OnChainPaymentMethodBaseData
|
||||
{
|
||||
/// <summary>
|
||||
/// The derivation scheme
|
||||
/// </summary>
|
||||
public string DerivationScheme { get; set; }
|
||||
|
||||
public string Label { get; set; }
|
||||
|
||||
[JsonConverter(typeof(NBitcoin.JsonConverters.KeyPathJsonConverter))]
|
||||
public RootedKeyPath AccountKeyPath { get; set; }
|
||||
|
||||
public OnChainPaymentMethodBaseData()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class OnChainPaymentMethodData : OnChainPaymentMethodBaseData
|
||||
public class OnChainPaymentMethodData
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the payment method is enabled
|
||||
@ -14,16 +15,23 @@ namespace BTCPayServer.Client.Models
|
||||
/// </summary>
|
||||
public string CryptoCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The derivation scheme
|
||||
/// </summary>
|
||||
public string DerivationScheme { get; set; }
|
||||
|
||||
public string Label { get; set; }
|
||||
|
||||
[JsonConverter(typeof(NBitcoin.JsonConverters.KeyPathJsonConverter))]
|
||||
public RootedKeyPath AccountKeyPath { get; set; }
|
||||
|
||||
public OnChainPaymentMethodData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public OnChainPaymentMethodData(string cryptoCode, string derivationScheme, bool enabled, string label, RootedKeyPath accountKeyPath)
|
||||
public OnChainPaymentMethodData(string cryptoCode, string derivationScheme, bool enabled)
|
||||
{
|
||||
Enabled = enabled;
|
||||
Label = label;
|
||||
AccountKeyPath = accountKeyPath;
|
||||
CryptoCode = cryptoCode;
|
||||
DerivationScheme = derivationScheme;
|
||||
}
|
||||
|
@ -1,23 +0,0 @@
|
||||
using BTCPayServer.Client.JsonConverters;
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class OnChainPaymentMethodDataWithSensitiveData : OnChainPaymentMethodData
|
||||
{
|
||||
public OnChainPaymentMethodDataWithSensitiveData()
|
||||
{
|
||||
}
|
||||
|
||||
public OnChainPaymentMethodDataWithSensitiveData(string cryptoCode, string derivationScheme, bool enabled,
|
||||
string label, RootedKeyPath accountKeyPath, Mnemonic mnemonic) : base(cryptoCode, derivationScheme, enabled,
|
||||
label, accountKeyPath)
|
||||
{
|
||||
Mnemonic = mnemonic;
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(MnemonicJsonConverter))]
|
||||
public Mnemonic Mnemonic { get; set; }
|
||||
}
|
||||
}
|
@ -9,7 +9,5 @@ namespace BTCPayServer.Client.Models
|
||||
public string Address { get; set; }
|
||||
[JsonConverter(typeof(KeyPathJsonConverter))]
|
||||
public KeyPath KeyPath { get; set; }
|
||||
|
||||
public string PaymentLink { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,5 @@ namespace BTCPayServer.Client.Models
|
||||
[JsonConverter(typeof(KeyPathJsonConverter))]
|
||||
public KeyPath KeyPath { get; set; }
|
||||
public string Address { get; set; }
|
||||
public int Confirmations { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,40 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class PermissionMetadata
|
||||
{
|
||||
static PermissionMetadata()
|
||||
{
|
||||
Dictionary<string, PermissionMetadata> nodes = new Dictionary<string, PermissionMetadata>();
|
||||
foreach (var policy in Client.Policies.AllPolicies)
|
||||
{
|
||||
nodes.Add(policy, new PermissionMetadata() { PermissionName = policy });
|
||||
}
|
||||
foreach (var n in nodes)
|
||||
{
|
||||
foreach (var policy in Client.Policies.AllPolicies)
|
||||
{
|
||||
if (policy.Equals(n.Key, StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
if (Client.Permission.Create(n.Key).Contains(Client.Permission.Create(policy)))
|
||||
n.Value.SubPermissions.Add(policy);
|
||||
}
|
||||
}
|
||||
foreach (var n in nodes)
|
||||
{
|
||||
n.Value.SubPermissions.Sort();
|
||||
}
|
||||
PermissionNodes = nodes.Values.OrderBy(v => v.PermissionName).ToArray();
|
||||
}
|
||||
public readonly static PermissionMetadata[] PermissionNodes;
|
||||
[JsonProperty("name")]
|
||||
public string PermissionName { get; set; }
|
||||
[JsonProperty("included")]
|
||||
public List<string> SubPermissions { get; set; } = new List<string>();
|
||||
}
|
||||
}
|
@ -27,17 +27,12 @@ namespace BTCPayServer.Client.Models
|
||||
/// <summary>
|
||||
/// detailed sync information per chain
|
||||
/// </summary>
|
||||
public IEnumerable<SyncStatus> SyncStatus { get; set; }
|
||||
}
|
||||
|
||||
public class SyncStatus
|
||||
{
|
||||
public string CryptoCode { get; set; }
|
||||
public virtual bool Available { get; set; }
|
||||
public IEnumerable<ServerInfoSyncStatusData> SyncStatus { get; set; }
|
||||
}
|
||||
|
||||
public class ServerInfoSyncStatusData: SyncStatus
|
||||
public class ServerInfoSyncStatusData
|
||||
{
|
||||
public string CryptoCode { get; set; }
|
||||
public int ChainHeight { get; set; }
|
||||
public int? SyncHeight { get; set; }
|
||||
public ServerInfoNodeData NodeInformation { get; set; }
|
||||
|
@ -19,31 +19,7 @@ namespace BTCPayServer.Client.Models
|
||||
}
|
||||
public string DeliveryId { get; set; }
|
||||
public string WebhookId { get; set; }
|
||||
string _OriginalDeliveryId;
|
||||
public string OriginalDeliveryId
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_OriginalDeliveryId is null)
|
||||
{
|
||||
// Due to a typo in old version, we serialized `orignalDeliveryId` rather than `orignalDeliveryId`
|
||||
// We silently fix that here.
|
||||
// Note we can remove this code later on, as old webhook event are unlikely to be useful to anyone,
|
||||
// and having a null orignalDeliveryId is not end of the world
|
||||
if (AdditionalData != null &&
|
||||
AdditionalData.TryGetValue("orignalDeliveryId", out var tok))
|
||||
{
|
||||
_OriginalDeliveryId = tok.Value<string>();
|
||||
AdditionalData.Remove("orignalDeliveryId");
|
||||
}
|
||||
}
|
||||
return _OriginalDeliveryId;
|
||||
}
|
||||
set
|
||||
{
|
||||
_OriginalDeliveryId = value;
|
||||
}
|
||||
}
|
||||
public string OrignalDeliveryId { get; set; }
|
||||
public bool IsRedelivery { get; set; }
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public WebhookEventType Type { get; set; }
|
||||
|
@ -17,7 +17,6 @@ namespace BTCPayServer.Client
|
||||
public const string CanViewStoreSettings = "btcpay.store.canviewstoresettings";
|
||||
public const string CanViewInvoices = "btcpay.store.canviewinvoices";
|
||||
public const string CanCreateInvoice = "btcpay.store.cancreateinvoice";
|
||||
public const string CanModifyInvoices = "btcpay.store.canmodifyinvoices";
|
||||
public const string CanViewPaymentRequests = "btcpay.store.canviewpaymentrequests";
|
||||
public const string CanModifyPaymentRequests = "btcpay.store.canmodifypaymentrequests";
|
||||
public const string CanModifyProfile = "btcpay.user.canmodifyprofile";
|
||||
@ -25,7 +24,6 @@ namespace BTCPayServer.Client
|
||||
public const string CanManageNotificationsForUser = "btcpay.user.canmanagenotificationsforuser";
|
||||
public const string CanViewNotificationsForUser = "btcpay.user.canviewnotificationsforuser";
|
||||
public const string CanCreateUser = "btcpay.server.cancreateuser";
|
||||
public const string CanDeleteUser = "btcpay.user.candeleteuser";
|
||||
public const string CanManagePullPayments = "btcpay.store.canmanagepullpayments";
|
||||
public const string Unrestricted = "unrestricted";
|
||||
public static IEnumerable<string> AllPolicies
|
||||
@ -34,7 +32,6 @@ namespace BTCPayServer.Client
|
||||
{
|
||||
yield return CanViewInvoices;
|
||||
yield return CanCreateInvoice;
|
||||
yield return CanModifyInvoices;
|
||||
yield return CanModifyStoreWebhooks;
|
||||
yield return CanModifyServerSettings;
|
||||
yield return CanModifyStoreSettings;
|
||||
@ -44,7 +41,6 @@ namespace BTCPayServer.Client
|
||||
yield return CanModifyProfile;
|
||||
yield return CanViewProfile;
|
||||
yield return CanCreateUser;
|
||||
yield return CanDeleteUser;
|
||||
yield return CanManageNotificationsForUser;
|
||||
yield return CanViewNotificationsForUser;
|
||||
yield return Unrestricted;
|
||||
@ -166,12 +162,10 @@ namespace BTCPayServer.Client
|
||||
switch (subpolicy)
|
||||
{
|
||||
case Policies.CanViewInvoices when this.Policy == Policies.CanModifyStoreSettings:
|
||||
case Policies.CanViewInvoices when this.Policy == Policies.CanModifyInvoices:
|
||||
case Policies.CanModifyStoreWebhooks when this.Policy == Policies.CanModifyStoreSettings:
|
||||
case Policies.CanViewInvoices when this.Policy == Policies.CanViewStoreSettings:
|
||||
case Policies.CanViewStoreSettings when this.Policy == Policies.CanModifyStoreSettings:
|
||||
case Policies.CanCreateInvoice when this.Policy == Policies.CanModifyStoreSettings:
|
||||
case Policies.CanModifyInvoices when this.Policy == Policies.CanModifyStoreSettings:
|
||||
case Policies.CanViewProfile when this.Policy == Policies.CanModifyProfile:
|
||||
case Policies.CanModifyPaymentRequests when this.Policy == Policies.CanModifyStoreSettings:
|
||||
case Policies.CanViewPaymentRequests when this.Policy == Policies.CanModifyStoreSettings:
|
||||
@ -179,7 +173,6 @@ namespace BTCPayServer.Client
|
||||
case Policies.CanCreateLightningInvoiceInternalNode when this.Policy == Policies.CanUseInternalLightningNode:
|
||||
case Policies.CanCreateLightningInvoiceInStore when this.Policy == Policies.CanUseLightningNodeInStore:
|
||||
case Policies.CanViewNotificationsForUser when this.Policy == Policies.CanManageNotificationsForUser:
|
||||
case Policies.CanUseInternalLightningNode when this.Policy == Policies.CanModifyServerSettings:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
@ -1,29 +0,0 @@
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitAlthash()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("HTML");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Althash",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://explorer.htmlcoin.com/api/tx/{0}" : "https://explorer.htmlcoin.com/api/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
UriScheme = "htmlcoin",
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"HTML_X = HTML_USD",
|
||||
"HTML_USD = hitbtc(HTML_USD)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/althash.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("88'") : new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -11,8 +11,8 @@ namespace BTCPayServer
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "BitCore",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://explorer.bitcore.cc/tx/{0}" : "https://explorer.bitcore.cc/tx/{0}",
|
||||
DisplayName = "Bitcore",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://insight.bitcore.cc/tx/{0}" : "https://insight.bitcore.cc/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
UriScheme = "bitcore",
|
||||
DefaultRateRules = new[]
|
||||
|
@ -1,7 +1,6 @@
|
||||
#if ALTCOINS
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BTCPayServer.Common;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
using NBXplorer.Models;
|
||||
@ -34,15 +33,13 @@ namespace BTCPayServer
|
||||
output.Value is AssetMoney assetMoney && assetMoney.AssetId == AssetId));
|
||||
}
|
||||
|
||||
public override PaymentUrlBuilder GenerateBIP21(string cryptoInfoAddress, Money cryptoInfoDue)
|
||||
public override string GenerateBIP21(string cryptoInfoAddress, Money cryptoInfoDue)
|
||||
{
|
||||
//precision 0: 10 = 0.00000010
|
||||
//precision 2: 10 = 0.00001000
|
||||
//precision 8: 10 = 10
|
||||
var money = cryptoInfoDue is null ? null : new Money(cryptoInfoDue.ToDecimal(MoneyUnit.BTC) / decimal.Parse("1".PadRight(1 + 8 - Divisibility, '0')), MoneyUnit.BTC);
|
||||
var builder = base.GenerateBIP21(cryptoInfoAddress, money);
|
||||
builder.QueryParams.Add("assetid", AssetId.ToString());
|
||||
return builder;
|
||||
var money = new Money(cryptoInfoDue.ToDecimal(MoneyUnit.BTC) / decimal.Parse("1".PadRight(1 + 8 - Divisibility, '0')), MoneyUnit.BTC);
|
||||
return $"{base.GenerateBIP21(cryptoInfoAddress, money)}&assetid={AssetId}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BTCPayServer.Common;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
using NBXplorer.Models;
|
||||
@ -122,15 +121,9 @@ namespace BTCPayServer
|
||||
});
|
||||
}
|
||||
|
||||
public virtual PaymentUrlBuilder GenerateBIP21(string cryptoInfoAddress, Money cryptoInfoDue)
|
||||
public virtual string GenerateBIP21(string cryptoInfoAddress, Money cryptoInfoDue)
|
||||
{
|
||||
var builder = new PaymentUrlBuilder(UriScheme);
|
||||
builder.Host = cryptoInfoAddress;
|
||||
if (cryptoInfoDue != null && cryptoInfoDue != Money.Zero)
|
||||
{
|
||||
builder.QueryParams.Add("amount", cryptoInfoDue.ToString(false, true));
|
||||
}
|
||||
return builder;
|
||||
return $"{UriScheme}:{cryptoInfoAddress}?amount={cryptoInfoDue.ToString(false, true)}";
|
||||
}
|
||||
|
||||
public virtual List<TransactionInformation> FilterValidTransactions(List<TransactionInformation> transactionInformationSet)
|
||||
|
@ -51,7 +51,6 @@ namespace BTCPayServer
|
||||
InitMonacoin();
|
||||
InitDash();
|
||||
InitFeathercoin();
|
||||
InitAlthash();
|
||||
InitGroestlcoin();
|
||||
InitViacoin();
|
||||
InitMonero();
|
||||
@ -132,10 +131,5 @@ namespace BTCPayServer
|
||||
}
|
||||
return network as T;
|
||||
}
|
||||
public bool TryGetNetwork<T>(string cryptoCode, out T network) where T : BTCPayNetworkBase
|
||||
{
|
||||
network = GetNetwork<T>(cryptoCode);
|
||||
return network != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,9 @@
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="NBXplorer.Client" Version="4.0.3" />
|
||||
<PackageReference Include="NBXplorer.Client" Version="3.0.20" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(Altcoins)' != 'true'">
|
||||
<Compile Remove="Altcoins\**\*.cs"></Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
@ -1,6 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using NBitcoin;
|
||||
using NBXplorer.DerivationStrategy;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
@ -13,23 +11,5 @@ namespace BTCPayServer
|
||||
hashSet.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public static ScriptPubKeyType ScriptPubKeyType(this DerivationStrategyBase derivationStrategyBase)
|
||||
{
|
||||
if (IsSegwitCore(derivationStrategyBase))
|
||||
{
|
||||
return NBitcoin.ScriptPubKeyType.Segwit;
|
||||
}
|
||||
|
||||
return (derivationStrategyBase is P2SHDerivationStrategy p2shStrat && IsSegwitCore(p2shStrat.Inner))
|
||||
? NBitcoin.ScriptPubKeyType.SegwitP2SH
|
||||
: NBitcoin.ScriptPubKeyType.Legacy;
|
||||
}
|
||||
|
||||
private static bool IsSegwitCore(DerivationStrategyBase derivationStrategyBase)
|
||||
{
|
||||
return (derivationStrategyBase is P2WSHDerivationStrategy) ||
|
||||
(derivationStrategyBase is DirectDerivationStrategy direct) && direct.Segwit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +0,0 @@
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer.Common
|
||||
{
|
||||
public interface IExplorerClientProvider
|
||||
{
|
||||
ExplorerClient GetExplorerClient(string cryptoCode);
|
||||
ExplorerClient GetExplorerClient(BTCPayNetworkBase network);
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BTCPayServer.Common
|
||||
{
|
||||
public class PaymentUrlBuilder
|
||||
{
|
||||
public PaymentUrlBuilder(string uriScheme)
|
||||
{
|
||||
UriScheme = uriScheme;
|
||||
}
|
||||
public string UriScheme { get; set; }
|
||||
public Dictionary<string, string> QueryParams { get; set; } = new Dictionary<string, string>();
|
||||
public string? Host { get; set; }
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder($"{UriScheme}:{Host}");
|
||||
if (QueryParams.Count != 0)
|
||||
{
|
||||
var parts = QueryParams.Select(q => Uri.EscapeDataString(q.Key) + "=" + System.Web.NBitcoin.HttpUtility.UrlEncode(q.Value))
|
||||
.ToArray();
|
||||
builder.Append($"?{string.Join('&', parts)}");
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
@ -55,7 +55,6 @@ namespace BTCPayServer.Data
|
||||
public DbSet<StoreWebhookData> StoreWebhooks { get; set; }
|
||||
public DbSet<StoreData> Stores { get; set; }
|
||||
public DbSet<U2FDevice> U2FDevices { get; set; }
|
||||
public DbSet<Fido2Credential> Fido2Credentials { get; set; }
|
||||
public DbSet<UserStore> UserStore { get; set; }
|
||||
public DbSet<WalletData> Wallets { get; set; }
|
||||
public DbSet<WalletTransactionData> WalletTransactions { get; set; }
|
||||
@ -100,7 +99,6 @@ namespace BTCPayServer.Data
|
||||
StoreWebhookData.OnModelCreating(builder);
|
||||
//StoreData.OnModelCreating(builder);
|
||||
U2FDevice.OnModelCreating(builder);
|
||||
Fido2Credential.OnModelCreating(builder);
|
||||
Data.UserStore.OnModelCreating(builder);
|
||||
//WalletData.OnModelCreating(builder);
|
||||
WalletTransactionData.OnModelCreating(builder);
|
||||
|
@ -9,7 +9,6 @@ namespace BTCPayServer.Data
|
||||
{
|
||||
public bool RequiresEmailConfirmation { get; set; }
|
||||
public List<StoredFile> StoredFiles { get; set; }
|
||||
[Obsolete("U2F support has been replace with FIDO2")]
|
||||
public List<U2FDevice> U2FDevices { get; set; }
|
||||
public List<APIKeyData> APIKeys { get; set; }
|
||||
public DateTimeOffset? Created { get; set; }
|
||||
@ -17,6 +16,5 @@ namespace BTCPayServer.Data
|
||||
|
||||
public List<NotificationData> Notifications { get; set; }
|
||||
public List<UserStore> UserStores { get; set; }
|
||||
public List<Fido2Credential> Fido2Credentials { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public class Fido2Credential
|
||||
{
|
||||
public string Name { get; set; }
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public string Id { get; set; }
|
||||
|
||||
public string ApplicationUserId { get; set; }
|
||||
|
||||
public byte[] Blob { get; set; }
|
||||
public CredentialType Type { get; set; }
|
||||
public enum CredentialType
|
||||
{
|
||||
FIDO2
|
||||
}
|
||||
public static void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
builder.Entity<Fido2Credential>()
|
||||
.HasOne(o => o.ApplicationUser)
|
||||
.WithMany(i => i.Fido2Credentials)
|
||||
.HasForeignKey(i => i.ApplicationUserId).OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
}
|
||||
|
||||
public ApplicationUser ApplicationUser { get; set; }
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using BTCPayServer.Client.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NBitcoin;
|
||||
|
||||
@ -54,4 +53,13 @@ namespace BTCPayServer.Data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum PayoutState
|
||||
{
|
||||
AwaitingApproval,
|
||||
AwaitingPayment,
|
||||
InProgress,
|
||||
Completed,
|
||||
Cancelled
|
||||
}
|
||||
}
|
||||
|
@ -3,13 +3,11 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using BTCPayServer.Client.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
|
||||
public class PullPaymentData
|
||||
{
|
||||
[Key]
|
||||
@ -88,6 +86,7 @@ namespace BTCPayServer.Data
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class PayoutExtensions
|
||||
{
|
||||
public static IQueryable<PayoutData> GetPayoutInPeriod(this IQueryable<PayoutData> payouts, PullPaymentData pp)
|
||||
|
@ -23,12 +23,11 @@ namespace BTCPayServer.Data
|
||||
|
||||
internal static void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
builder.Entity<U2FDevice>()
|
||||
.HasOne(o => o.ApplicationUser)
|
||||
.WithMany(i => i.U2FDevices)
|
||||
.HasForeignKey(i => i.ApplicationUserId).OnDelete(DeleteBehavior.Cascade);
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,48 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
namespace BTCPayServer.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20210314092253_Fido2Credentials")]
|
||||
public partial class Fido2Credentials : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null;
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Fido2Credentials",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(nullable: false, maxLength: maxLength),
|
||||
Name = table.Column<string>(nullable: true),
|
||||
ApplicationUserId = table.Column<string>(nullable: true, maxLength: maxLength),
|
||||
Blob = table.Column<byte[]>(nullable: true),
|
||||
Type = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Fido2Credentials", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Fido2Credentials_AspNetUsers_ApplicationUserId",
|
||||
column: x => x.ApplicationUserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Fido2Credentials_ApplicationUserId",
|
||||
table: "Fido2Credentials",
|
||||
column: "ApplicationUserId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Fido2Credentials");
|
||||
}
|
||||
}
|
||||
}
|
@ -170,31 +170,6 @@ namespace BTCPayServer.Migrations
|
||||
b.ToTable("AspNetUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.Fido2Credential", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ApplicationUserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<byte[]>("Blob")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApplicationUserId");
|
||||
|
||||
b.ToTable("Fido2Credentials");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.HistoricalAddressInvoiceData", b =>
|
||||
{
|
||||
b.Property<string>("InvoiceDataId")
|
||||
@ -983,14 +958,6 @@ namespace BTCPayServer.Migrations
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.Fido2Credential", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.ApplicationUser", "ApplicationUser")
|
||||
.WithMany("Fido2Credentials")
|
||||
.HasForeignKey("ApplicationUserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.HistoricalAddressInvoiceData", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
|
||||
|
@ -20,7 +20,6 @@ namespace BTCPayServer.Plugins.Test.Services
|
||||
await using var context = _testPluginDbContextFactory.CreateContext();
|
||||
|
||||
await context.TestPluginRecords.AddAsync(new TestPluginData() {Timestamp = DateTimeOffset.UtcNow});
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
|
||||
|
@ -9,8 +9,8 @@
|
||||
|
||||
<div class="row">
|
||||
<h2>Persisted Data</h2>
|
||||
<p>The following is data persisted to the configured database but in an isolated DbContext. Every time you start BTCPay Server with this plugin enabled, a timestamp is logged.</p>
|
||||
<ul class="list-group">
|
||||
<p>The following is data persisted to the configured database but in an isolated DbContext. Every time you start BTCPayw with this plugin enabled, a timestamp is logged.</p>
|
||||
<ul class="list-group">>
|
||||
@foreach (var item in Model.Data)
|
||||
{
|
||||
<li class="list-group-item">@item.Id at @item.Timestamp.ToString("F")</li>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.6.0" />
|
||||
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.7" />
|
||||
<PackageReference Include="NBitcoin" Version="6.0.7" />
|
||||
<PackageReference Include="NBitcoin" Version="5.0.73" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="DigitalRuby.ExchangeSharp" Version="0.6.3" />
|
||||
</ItemGroup>
|
||||
|
@ -1266,13 +1266,6 @@
|
||||
"symbol":null,
|
||||
"crypto":true
|
||||
},
|
||||
{
|
||||
"name":"Althash",
|
||||
"code":"HTML",
|
||||
"divisibility":8,
|
||||
"symbol":null,
|
||||
"crypto":true
|
||||
},
|
||||
{
|
||||
"name":"CHC",
|
||||
"code":"CHC",
|
||||
|
@ -28,6 +28,7 @@ namespace BTCPayServer.Services.Rates
|
||||
}
|
||||
|
||||
static readonly Dictionary<string, IFormatProvider> _CurrencyProviders = new Dictionary<string, IFormatProvider>();
|
||||
|
||||
public string FormatCurrency(string price, string currency)
|
||||
{
|
||||
return FormatCurrency(decimal.Parse(price, CultureInfo.InvariantCulture), currency);
|
||||
@ -109,8 +110,9 @@ namespace BTCPayServer.Services.Rates
|
||||
/// </summary>
|
||||
/// <param name="value">The value</param>
|
||||
/// <param name="currency">Currency code</param>
|
||||
/// <param name="threeLetterSuffix">Add three letter suffix (like USD)</param>
|
||||
/// <returns></returns>
|
||||
public string DisplayFormatCurrency(decimal value, string currency)
|
||||
public string DisplayFormatCurrency(decimal value, string currency, bool threeLetterSuffix = true)
|
||||
{
|
||||
var provider = GetNumberFormatInfo(currency, true);
|
||||
var currencyData = GetCurrencyData(currency, true);
|
||||
|
@ -50,14 +50,13 @@ namespace BTCPayServer.Tests
|
||||
tester.ActivateLightning();
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
var cryptoCode = "BTC";
|
||||
await user.GrantAccessAsync(true);
|
||||
user.RegisterDerivationScheme(cryptoCode);
|
||||
user.GrantAccess(true);
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
user.RegisterDerivationScheme("LTC");
|
||||
user.RegisterLightningNode(cryptoCode, LightningConnectionType.CLightning);
|
||||
var btcNetwork = tester.PayTester.Networks.GetNetwork<BTCPayNetwork>(cryptoCode);
|
||||
var invoice = await user.BitPay.CreateInvoiceAsync(
|
||||
new Invoice
|
||||
user.RegisterLightningNode("BTC", LightningConnectionType.CLightning);
|
||||
var btcNetwork = tester.PayTester.Networks.GetNetwork<BTCPayNetwork>("BTC");
|
||||
var invoice = user.BitPay.CreateInvoice(
|
||||
new Invoice()
|
||||
{
|
||||
Price = 1.5m,
|
||||
Currency = "USD",
|
||||
@ -70,43 +69,36 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(3, invoice.CryptoInfo.Length);
|
||||
|
||||
var controller = user.GetController<StoresController>();
|
||||
var lightningVm = (LightningNodeViewModel)Assert.IsType<ViewResult>(await controller.SetupLightningNode(user.StoreId, cryptoCode)).Model;
|
||||
var lightningVm = (LightningNodeViewModel)Assert.IsType<ViewResult>(controller.AddLightningNode(user.StoreId, "BTC")).Model;
|
||||
Assert.True(lightningVm.Enabled);
|
||||
var response = await controller.SetLightningNodeEnabled(user.StoreId, cryptoCode, false);
|
||||
Assert.IsType<RedirectToActionResult>(response);
|
||||
|
||||
// Get enabled state from overview action
|
||||
StoreViewModel storeModel;
|
||||
response = await controller.UpdateStore();
|
||||
storeModel = (StoreViewModel)Assert.IsType<ViewResult>(response).Model;
|
||||
var lnNode = storeModel.LightningNodes.Find(node => node.CryptoCode == cryptoCode);
|
||||
Assert.NotNull(lnNode);
|
||||
Assert.False(lnNode.Enabled);
|
||||
lightningVm.Enabled = false;
|
||||
controller.AddLightningNode(user.StoreId, lightningVm, "save", "BTC").GetAwaiter().GetResult();
|
||||
lightningVm = (LightningNodeViewModel)Assert.IsType<ViewResult>(controller.AddLightningNode(user.StoreId, "BTC")).Model;
|
||||
Assert.False(lightningVm.Enabled);
|
||||
|
||||
WalletSetupViewModel setupVm;
|
||||
var storeId = user.StoreId;
|
||||
response = await controller.GenerateWallet(storeId, cryptoCode, WalletSetupMethod.GenerateOptions, new WalletSetupRequest());
|
||||
var cryptoCode = "BTC";
|
||||
var response = await controller.GenerateWallet(storeId, cryptoCode, WalletSetupMethod.GenerateOptions, new GenerateWalletRequest());
|
||||
Assert.IsType<ViewResult>(response);
|
||||
|
||||
// Get enabled state from overview action
|
||||
response = await controller.UpdateStore();
|
||||
storeModel = (StoreViewModel)Assert.IsType<ViewResult>(response).Model;
|
||||
var derivationScheme = storeModel.DerivationSchemes.Find(scheme => scheme.Crypto == cryptoCode);
|
||||
Assert.NotNull(derivationScheme);
|
||||
Assert.True(derivationScheme.Enabled);
|
||||
// Get setup view model from modify action
|
||||
response = await controller.ModifyWallet(new WalletSetupViewModel { StoreId = storeId, CryptoCode = cryptoCode });
|
||||
setupVm = (WalletSetupViewModel)Assert.IsType<ViewResult>(response).Model;
|
||||
Assert.True(setupVm.Enabled);
|
||||
|
||||
// Disable wallet
|
||||
response = controller.SetWalletEnabled(storeId, cryptoCode, false).GetAwaiter().GetResult();
|
||||
// Only Enabling/Disabling the payment method must redirect to store page
|
||||
setupVm.Enabled = false;
|
||||
response = controller.UpdateWallet(setupVm).GetAwaiter().GetResult();
|
||||
Assert.IsType<RedirectToActionResult>(response);
|
||||
response = await controller.UpdateStore();
|
||||
storeModel = (StoreViewModel)Assert.IsType<ViewResult>(response).Model;
|
||||
derivationScheme = storeModel.DerivationSchemes.Find(scheme => scheme.Crypto == cryptoCode);
|
||||
Assert.NotNull(derivationScheme);
|
||||
Assert.False(derivationScheme.Enabled);
|
||||
|
||||
var oldScheme = derivationScheme.Value;
|
||||
response = await controller.ModifyWallet(new WalletSetupViewModel { StoreId = storeId, CryptoCode = cryptoCode });
|
||||
setupVm = (WalletSetupViewModel)Assert.IsType<ViewResult>(response).Model;
|
||||
Assert.False(setupVm.Enabled);
|
||||
|
||||
invoice = await user.BitPay.CreateInvoiceAsync(
|
||||
var oldScheme = setupVm.DerivationScheme;
|
||||
|
||||
invoice = user.BitPay.CreateInvoice(
|
||||
new Invoice
|
||||
{
|
||||
Price = 1.5m,
|
||||
@ -121,7 +113,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal("LTC", invoice.CryptoInfo[0].CryptoCode);
|
||||
|
||||
// Removing the derivation scheme, should redirect to store page
|
||||
response = controller.ConfirmDeleteWallet(user.StoreId, cryptoCode).GetAwaiter().GetResult();
|
||||
response = controller.ConfirmDeleteWallet(user.StoreId, "BTC").GetAwaiter().GetResult();
|
||||
Assert.IsType<RedirectToActionResult>(response);
|
||||
|
||||
// Setting it again should show the confirmation page
|
||||
@ -180,8 +172,8 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(expected.ToJson(), onchainBTC.ToJson());
|
||||
|
||||
// Let's check that the root hdkey and account key path are taken into account when making a PSBT
|
||||
invoice = await user.BitPay.CreateInvoiceAsync(
|
||||
new Invoice
|
||||
invoice = user.BitPay.CreateInvoice(
|
||||
new Invoice()
|
||||
{
|
||||
Price = 1.5m,
|
||||
Currency = "USD",
|
||||
@ -192,7 +184,7 @@ namespace BTCPayServer.Tests
|
||||
}, Facade.Merchant);
|
||||
|
||||
tester.ExplorerNode.Generate(1);
|
||||
var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo.First(c => c.CryptoCode == cryptoCode).Address,
|
||||
var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo.First(c => c.CryptoCode == "BTC").Address,
|
||||
tester.ExplorerNode.Network);
|
||||
tester.ExplorerNode.SendToAddress(invoiceAddress, Money.Coins(1m));
|
||||
TestUtils.Eventually(() =>
|
||||
@ -204,9 +196,9 @@ namespace BTCPayServer.Tests
|
||||
var psbt = wallet.CreatePSBT(btcNetwork, onchainBTC,
|
||||
new WalletSendModel()
|
||||
{
|
||||
Outputs = new List<WalletSendModel.TransactionOutput>
|
||||
Outputs = new List<WalletSendModel.TransactionOutput>()
|
||||
{
|
||||
new WalletSendModel.TransactionOutput
|
||||
new WalletSendModel.TransactionOutput()
|
||||
{
|
||||
Amount = 0.5m,
|
||||
DestinationAddress = new Key().PubKey.GetAddress(ScriptPubKeyType.Legacy, btcNetwork.NBitcoinNetwork)
|
||||
@ -312,7 +304,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
var controller = tester.PayTester.GetController<InvoiceController>(null);
|
||||
var checkout =
|
||||
(Models.InvoicingModels.PaymentModel)((JsonResult)controller.GetStatus(invoice.Id)
|
||||
(Models.InvoicingModels.PaymentModel)((JsonResult)controller.GetStatus(invoice.Id, null)
|
||||
.GetAwaiter().GetResult()).Value;
|
||||
Assert.Single(checkout.AvailableCryptos);
|
||||
Assert.Equal("LTC", checkout.CryptoCode);
|
||||
@ -329,7 +321,7 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
invoice = user.BitPay.GetInvoice(invoice.Id);
|
||||
Assert.Equal("paid", invoice.Status);
|
||||
checkout = (Models.InvoicingModels.PaymentModel)((JsonResult)controller.GetStatus(invoice.Id)
|
||||
checkout = (Models.InvoicingModels.PaymentModel)((JsonResult)controller.GetStatus(invoice.Id, null)
|
||||
.GetAwaiter().GetResult()).Value;
|
||||
Assert.Equal("paid", checkout.Status);
|
||||
});
|
||||
@ -954,6 +946,28 @@ normal:
|
||||
|
||||
result = testnetParser.Parse(tpub);
|
||||
Assert.Equal(tpub, result.ToString());
|
||||
testnetParser.HintScriptPubKey = BitcoinAddress
|
||||
.Create("tb1q4s33amqm8l7a07zdxcunqnn3gcsjcfz3xc573l", testnetParser.Network).ScriptPubKey;
|
||||
result = testnetParser.Parse(tpub);
|
||||
Assert.Equal(tpub, result.ToString());
|
||||
|
||||
testnetParser.HintScriptPubKey = BitcoinAddress
|
||||
.Create("2N2humNio3YTApSfY6VztQ9hQwDnhDvaqFQ", testnetParser.Network).ScriptPubKey;
|
||||
result = testnetParser.Parse(tpub);
|
||||
Assert.Equal($"{tpub}-[p2sh]", result.ToString());
|
||||
|
||||
testnetParser.HintScriptPubKey = BitcoinAddress
|
||||
.Create("mwD8bHS65cdgUf6rZUUSoVhi3wNQFu1Nfi", testnetParser.Network).ScriptPubKey;
|
||||
result = testnetParser.Parse(tpub);
|
||||
Assert.Equal($"{tpub}-[legacy]", result.ToString());
|
||||
|
||||
testnetParser.HintScriptPubKey = BitcoinAddress
|
||||
.Create("2N2humNio3YTApSfY6VztQ9hQwDnhDvaqFQ", testnetParser.Network).ScriptPubKey;
|
||||
result = testnetParser.Parse($"{tpub}-[legacy]");
|
||||
Assert.Equal($"{tpub}-[p2sh]", result.ToString());
|
||||
|
||||
result = testnetParser.Parse(tpub);
|
||||
Assert.Equal($"{tpub}-[p2sh]", result.ToString());
|
||||
|
||||
var regtestParser = new DerivationSchemeParser(regtestNetworkProvider.GetNetwork<BTCPayNetwork>("BTC"));
|
||||
var parsed =
|
||||
|
@ -89,12 +89,7 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.Id("AddApiKey")).Click();
|
||||
s.Driver.FindElement(By.CssSelector("button[value='btcpay.store.canmodifystoresettings:change-store-mode']")).Click();
|
||||
//there should be a store already by default in the dropdown
|
||||
var src = s.Driver.PageSource;
|
||||
var getPermissionValueIndex =
|
||||
s.Driver.FindElement(By.CssSelector("input[value='btcpay.store.canmodifystoresettings']"))
|
||||
.GetAttribute("name")
|
||||
.Replace(".Permission", ".SpecificStores[0]");
|
||||
var dropdown = s.Driver.FindElement(By.Name(getPermissionValueIndex));
|
||||
var dropdown = s.Driver.FindElement(By.Name("PermissionValues[4].SpecificStores[0]"));
|
||||
var option = dropdown.FindElement(By.TagName("option"));
|
||||
var storeId = option.GetAttribute("value");
|
||||
option.Click();
|
||||
|
@ -23,7 +23,7 @@
|
||||
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.13" />
|
||||
<PackageReference Include="Selenium.Support" Version="3.141.0" />
|
||||
<PackageReference Include="Selenium.WebDriver" Version="3.141.0" />
|
||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="90.0.4430.2400" />
|
||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="88.0.4324.9600" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
@ -167,7 +167,6 @@ namespace BTCPayServer.Tests
|
||||
l.SetMinimumLevel(LogLevel.Information)
|
||||
.AddFilter("Microsoft", LogLevel.Error)
|
||||
.AddFilter("Hangfire", LogLevel.Error)
|
||||
.AddFilter("Fido2NetLib.DistributedCacheMetadataService", LogLevel.Error)
|
||||
.AddProvider(Logs.LogProvider);
|
||||
});
|
||||
})
|
||||
|
@ -1,13 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using BTCPayServer.Views.Stores;
|
||||
using NBitcoin;
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Support.UI;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
|
91
BTCPayServer.Tests/CoinSwitchTests.cs
Normal file
91
BTCPayServer.Tests/CoinSwitchTests.cs
Normal file
@ -0,0 +1,91 @@
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Controllers;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Models.StoreViewModels;
|
||||
using BTCPayServer.Payments.CoinSwitch;
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
{
|
||||
public class CoinSwitchTests
|
||||
{
|
||||
public CoinSwitchTests(ITestOutputHelper helper)
|
||||
{
|
||||
Logs.Tester = new XUnitLog(helper) { Name = "Tests" };
|
||||
Logs.LogProvider = new XUnitLogProvider(helper);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanSetCoinSwitchPaymentMethod()
|
||||
{
|
||||
using (var tester = ServerTester.Create())
|
||||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
var controller = tester.PayTester.GetController<StoresController>(user.UserId, user.StoreId);
|
||||
|
||||
|
||||
var storeBlob = controller.CurrentStore.GetStoreBlob();
|
||||
Assert.Null(storeBlob.CoinSwitchSettings);
|
||||
|
||||
var updateModel = new UpdateCoinSwitchSettingsViewModel()
|
||||
{
|
||||
MerchantId = "aaa",
|
||||
};
|
||||
|
||||
Assert.Equal("UpdateStore", Assert.IsType<RedirectToActionResult>(
|
||||
await controller.UpdateCoinSwitchSettings(user.StoreId, updateModel, "save")).ActionName);
|
||||
|
||||
var store = await tester.PayTester.StoreRepository.FindStore(user.StoreId);
|
||||
storeBlob = controller.CurrentStore.GetStoreBlob();
|
||||
Assert.NotNull(storeBlob.CoinSwitchSettings);
|
||||
Assert.NotNull(storeBlob.CoinSwitchSettings);
|
||||
Assert.IsType<CoinSwitchSettings>(storeBlob.CoinSwitchSettings);
|
||||
Assert.Equal(storeBlob.CoinSwitchSettings.MerchantId,
|
||||
updateModel.MerchantId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanToggleCoinSwitchPaymentMethod()
|
||||
{
|
||||
using (var tester = ServerTester.Create())
|
||||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
var controller = tester.PayTester.GetController<StoresController>(user.UserId, user.StoreId);
|
||||
|
||||
var updateModel = new UpdateCoinSwitchSettingsViewModel()
|
||||
{
|
||||
MerchantId = "aaa",
|
||||
Enabled = true
|
||||
};
|
||||
Assert.Equal("UpdateStore", Assert.IsType<RedirectToActionResult>(
|
||||
await controller.UpdateCoinSwitchSettings(user.StoreId, updateModel, "save")).ActionName);
|
||||
|
||||
|
||||
var store = await tester.PayTester.StoreRepository.FindStore(user.StoreId);
|
||||
|
||||
Assert.True(store.GetStoreBlob().CoinSwitchSettings.Enabled);
|
||||
|
||||
updateModel.Enabled = false;
|
||||
|
||||
Assert.Equal("UpdateStore", Assert.IsType<RedirectToActionResult>(
|
||||
await controller.UpdateCoinSwitchSettings(user.StoreId, updateModel, "save")).ActionName);
|
||||
|
||||
store = await tester.PayTester.StoreRepository.FindStore(user.StoreId);
|
||||
|
||||
Assert.False(store.GetStoreBlob().CoinSwitchSettings.Enabled);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@ namespace BTCPayServer.Tests
|
||||
Logs.LogProvider = new XUnitLogProvider(helper);
|
||||
}
|
||||
|
||||
[Fact(Timeout = LongRunningTestTimeout)]
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanCreateAndDeleteCrowdfundApp()
|
||||
{
|
||||
@ -63,7 +63,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
|
||||
|
||||
[Fact(Timeout = LongRunningTestTimeout)]
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanContributeOnlyWhenAllowed()
|
||||
{
|
||||
@ -155,7 +155,7 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact(Timeout = LongRunningTestTimeout)]
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanComputeCrowdfundModel()
|
||||
{
|
||||
@ -165,7 +165,7 @@ namespace BTCPayServer.Tests
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
await user.ModifyStore(s => s.NetworkFeeMode = NetworkFeeMode.Never);
|
||||
user.ModifyStore(s => s.NetworkFeeMode = NetworkFeeMode.Never);
|
||||
var apps = user.GetController<AppsController>();
|
||||
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp().Result).Model);
|
||||
vm.Name = "test";
|
||||
|
@ -7,7 +7,6 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Support.Extensions;
|
||||
using OpenQA.Selenium.Support.UI;
|
||||
using Xunit;
|
||||
|
||||
@ -28,12 +27,46 @@ namespace BTCPayServer.Tests
|
||||
|
||||
public static void AssertNoError(this IWebDriver driver)
|
||||
{
|
||||
Assert.NotEmpty(driver.FindElements(By.ClassName("navbar-brand")));
|
||||
if (!driver.PageSource.Contains("alert-danger")) return;
|
||||
foreach (var dangerAlert in driver.FindElements(By.ClassName("alert-danger")))
|
||||
Assert.False(dangerAlert.Displayed, $"No alert should be displayed, but found this on {driver.Url}: {dangerAlert.Text}");
|
||||
try
|
||||
{
|
||||
Assert.NotEmpty(driver.FindElements(By.ClassName("navbar-brand")));
|
||||
if (driver.PageSource.Contains("alert-danger"))
|
||||
{
|
||||
foreach (var dangerAlert in driver.FindElements(By.ClassName("alert-danger")))
|
||||
Assert.False(dangerAlert.Displayed, "No alert should be displayed");
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.AppendLine();
|
||||
foreach (var logKind in new[] { LogType.Browser, LogType.Client, LogType.Driver, LogType.Server })
|
||||
{
|
||||
try
|
||||
{
|
||||
var logs = driver.Manage().Logs.GetLog(logKind);
|
||||
builder.AppendLine($"Selenium [{logKind}]:");
|
||||
foreach (var entry in logs)
|
||||
{
|
||||
builder.AppendLine($"[{entry.Level}]: {entry.Message}");
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
builder.AppendLine("---------");
|
||||
}
|
||||
Logs.Tester.LogInformation(builder.ToString());
|
||||
builder = new StringBuilder();
|
||||
builder.AppendLine("Selenium [Sources]:");
|
||||
builder.AppendLine(driver.PageSource);
|
||||
builder.AppendLine("---------");
|
||||
Logs.Tester.LogInformation(builder.ToString());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public static T AssertViewModel<T>(this IActionResult result)
|
||||
{
|
||||
Assert.NotNull(result);
|
||||
@ -79,13 +112,6 @@ namespace BTCPayServer.Tests
|
||||
wait.Until(d=>((IJavaScriptExecutor)d).ExecuteScript("return document.readyState").Equals("complete"));
|
||||
wait.Until(d=>((IJavaScriptExecutor)d).ExecuteScript("return typeof(jQuery) === 'undefined' || jQuery.active === 0").Equals(true));
|
||||
}
|
||||
|
||||
// Open collapse via JS, because if we click the link it triggers the toggle animation.
|
||||
// This leads to Selenium trying to click the button while it is moving resulting in an error.
|
||||
public static void ToggleCollapse(this IWebDriver driver, string collapseId)
|
||||
{
|
||||
driver.ExecuteJavaScript($"document.getElementById('{collapseId}').classList.add('show')");
|
||||
}
|
||||
|
||||
public static IWebElement WaitForElement(this IWebDriver driver, By selector)
|
||||
{
|
||||
|
@ -5,7 +5,6 @@ using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Controllers;
|
||||
@ -13,7 +12,6 @@ using BTCPayServer.Events;
|
||||
using BTCPayServer.JsonConverters;
|
||||
using BTCPayServer.Lightning;
|
||||
using BTCPayServer.Models.InvoicingModels;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Notifications;
|
||||
using BTCPayServer.Services.Notifications.Blobs;
|
||||
@ -21,6 +19,8 @@ using BTCPayServer.Tests.Logging;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NBitcoin;
|
||||
using NBitcoin.OpenAsset;
|
||||
using NBitcoin.Payment;
|
||||
using NBitpayClient;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
@ -40,23 +40,6 @@ namespace BTCPayServer.Tests
|
||||
Logs.LogProvider = new XUnitLogProvider(helper);
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task LocalClientTests()
|
||||
{
|
||||
using var tester = ServerTester.Create();
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync();
|
||||
await user.MakeAdmin();
|
||||
var factory = tester.PayTester.GetService<IBTCPayServerClientFactory>();
|
||||
Assert.NotNull(factory);
|
||||
var client = await factory.Create(user.UserId);
|
||||
var u = await client.GetCurrentUser();
|
||||
var s = await client.GetStores();
|
||||
}
|
||||
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task ApiKeysControllerTests()
|
||||
@ -86,27 +69,6 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanUseMiscAPIs()
|
||||
{
|
||||
using (var tester = ServerTester.Create())
|
||||
{
|
||||
await tester.StartAsync();
|
||||
var acc = tester.NewAccount();
|
||||
await acc.GrantAccessAsync();
|
||||
var unrestricted = await acc.CreateClient();
|
||||
var langs = await unrestricted.GetAvailableLanguages();
|
||||
Assert.NotEmpty(langs);
|
||||
Assert.NotNull(langs[0].Code);
|
||||
Assert.NotNull(langs[0].DisplayName);
|
||||
|
||||
var perms = await unrestricted.GetPermissionMetadata();
|
||||
Assert.NotEmpty(perms);
|
||||
var p = perms.First(p => p.PermissionName == "unrestricted");
|
||||
Assert.True(p.SubPermissions.Count > 6);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
@ -165,45 +127,6 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanDeleteUsersViaApi()
|
||||
{
|
||||
using var tester = ServerTester.Create(newDb: true);
|
||||
await tester.StartAsync();
|
||||
var unauthClient = new BTCPayServerClient(tester.PayTester.ServerUri);
|
||||
// Should not be authorized to perform this action
|
||||
await AssertHttpError(401,
|
||||
async () => await unauthClient.DeleteUser("lol user id"));
|
||||
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync();
|
||||
await user.MakeAdmin();
|
||||
var adminClient = await user.CreateClient(Policies.Unrestricted);
|
||||
|
||||
//can't delete if the only admin
|
||||
await AssertHttpError(403,
|
||||
async () => await adminClient.DeleteCurrentUser());
|
||||
|
||||
// Should 404 if user doesn't exist
|
||||
await AssertHttpError(404,
|
||||
async () => await adminClient.DeleteUser("lol user id"));
|
||||
|
||||
user = tester.NewAccount();
|
||||
await user.GrantAccessAsync();
|
||||
var badClient = await user.CreateClient(Policies.CanCreateInvoice);
|
||||
|
||||
await AssertHttpError(403,
|
||||
async () => await badClient.DeleteCurrentUser());
|
||||
|
||||
var goodClient = await user.CreateClient(Policies.CanDeleteUser, Policies.CanViewProfile);
|
||||
await goodClient.DeleteCurrentUser();
|
||||
await AssertHttpError(404,
|
||||
async () => await adminClient.DeleteUser(user.UserId));
|
||||
|
||||
tester.Stores.Remove(user.StoreId);
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanCreateUsersViaAPI()
|
||||
@ -548,30 +471,6 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
Revision = payout.Revision
|
||||
}));
|
||||
|
||||
// Create one pull payment with an amount of 9 decimals
|
||||
var test3 = await client.CreatePullPayment(storeId, new Client.Models.CreatePullPaymentRequest()
|
||||
{
|
||||
Name = "Test 2",
|
||||
Amount = 12.303228134m,
|
||||
Currency = "BTC",
|
||||
PaymentMethods = new[] { "BTC" }
|
||||
});
|
||||
destination = (await tester.ExplorerNode.GetNewAddressAsync()).ToString();
|
||||
payout = await unauthenticated.CreatePayout(test3.Id, new CreatePayoutRequest()
|
||||
{
|
||||
Destination = destination,
|
||||
PaymentMethod = "BTC"
|
||||
});
|
||||
payout = await client.ApprovePayout(storeId, payout.Id, new ApprovePayoutRequest());
|
||||
// The payout should round the value of the payment down to the network of the payment method
|
||||
Assert.Equal(12.30322814m, payout.PaymentMethodAmount);
|
||||
Assert.Equal(12.303228134m, payout.Amount);
|
||||
|
||||
await client.MarkPayoutPaid(storeId, payout.Id);
|
||||
payout = (await client.GetPayouts(payout.PullPaymentId)).First(data => data.Id == payout.Id);
|
||||
Assert.Equal(PayoutState.Completed, payout.State);
|
||||
await AssertAPIError("invalid-state", async () => await client.MarkPayoutPaid(storeId, payout.Id));
|
||||
}
|
||||
}
|
||||
|
||||
@ -788,7 +687,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.NotNull(newDelivery);
|
||||
Assert.Equal(404, newDelivery.HttpCode);
|
||||
var req = await clientProfile.GetWebhookDeliveryRequest(user.StoreId, hook.Id, newDeliveryId);
|
||||
Assert.Equal(delivery.Id, req.OriginalDeliveryId);
|
||||
Assert.Equal(delivery.Id, req.OrignalDeliveryId);
|
||||
Assert.True(req.IsRedelivery);
|
||||
Assert.Equal(WebhookDeliveryStatus.HttpError, newDelivery.Status);
|
||||
});
|
||||
@ -1052,19 +951,12 @@ namespace BTCPayServer.Tests
|
||||
});
|
||||
await user.RegisterDerivationSchemeAsync("BTC");
|
||||
var newInvoice = await client.CreateInvoice(user.StoreId,
|
||||
new CreateInvoiceRequest()
|
||||
new CreateInvoiceRequest() { Currency = "USD", Amount = 1, Metadata = JObject.Parse("{\"itemCode\": \"testitem\"}"), Checkout = new CreateInvoiceRequest.CheckoutOptions()
|
||||
{
|
||||
Currency = "USD",
|
||||
Amount = 1,
|
||||
Metadata = JObject.Parse("{\"itemCode\": \"testitem\", \"orderId\": \"testOrder\"}"),
|
||||
Checkout = new CreateInvoiceRequest.CheckoutOptions()
|
||||
{
|
||||
RedirectAutomatically = true
|
||||
},
|
||||
AdditionalSearchTerms = new string[] { "Banana" }
|
||||
});
|
||||
RedirectAutomatically = true
|
||||
}});
|
||||
Assert.True(newInvoice.Checkout.RedirectAutomatically);
|
||||
Assert.Equal(user.StoreId, newInvoice.StoreId);
|
||||
|
||||
//list
|
||||
var invoices = await viewOnly.GetInvoices(user.StoreId);
|
||||
|
||||
@ -1072,76 +964,6 @@ namespace BTCPayServer.Tests
|
||||
Assert.Single(invoices);
|
||||
Assert.Equal(newInvoice.Id, invoices.First().Id);
|
||||
|
||||
invoices = await viewOnly.GetInvoices(user.StoreId, textSearch: "Banana");
|
||||
Assert.NotNull(invoices);
|
||||
Assert.Single(invoices);
|
||||
Assert.Equal(newInvoice.Id, invoices.First().Id);
|
||||
|
||||
invoices = await viewOnly.GetInvoices(user.StoreId, textSearch: "apples");
|
||||
Assert.NotNull(invoices);
|
||||
Assert.Empty(invoices);
|
||||
|
||||
//list Filtered
|
||||
var invoicesFiltered = await viewOnly.GetInvoices(user.StoreId,
|
||||
orderId: null, status: null, startDate: DateTimeOffset.Now.AddHours(-1),
|
||||
endDate: DateTimeOffset.Now.AddHours(1));
|
||||
|
||||
Assert.NotNull(invoicesFiltered);
|
||||
Assert.Single(invoicesFiltered);
|
||||
Assert.Equal(newInvoice.Id, invoicesFiltered.First().Id);
|
||||
|
||||
|
||||
Assert.NotNull(invoicesFiltered);
|
||||
Assert.Single(invoicesFiltered);
|
||||
Assert.Equal(newInvoice.Id, invoicesFiltered.First().Id);
|
||||
|
||||
//list Yesterday
|
||||
var invoicesYesterday = await viewOnly.GetInvoices(user.StoreId,
|
||||
orderId: null, status: null, startDate: DateTimeOffset.Now.AddDays(-2),
|
||||
endDate: DateTimeOffset.Now.AddDays(-1));
|
||||
Assert.NotNull(invoicesYesterday);
|
||||
Assert.Empty(invoicesYesterday);
|
||||
|
||||
// Error, startDate and endDate inverted
|
||||
await AssertValidationError(new[] { "startDate", "endDate" },
|
||||
() => viewOnly.GetInvoices(user.StoreId,
|
||||
orderId: null, status: null, startDate: DateTimeOffset.Now.AddDays(-1),
|
||||
endDate: DateTimeOffset.Now.AddDays(-2)));
|
||||
|
||||
await AssertValidationError(new[] { "startDate" },
|
||||
() => viewOnly.SendHttpRequest<Client.Models.InvoiceData[]>($"api/v1/stores/{user.StoreId}/invoices", new Dictionary<string, object>()
|
||||
{
|
||||
{ "startDate", "blah" }
|
||||
}));
|
||||
|
||||
|
||||
//list Existing OrderId
|
||||
var invoicesExistingOrderId =
|
||||
await viewOnly.GetInvoices(user.StoreId, orderId: new []{newInvoice.Metadata["orderId"].ToString()});
|
||||
Assert.NotNull(invoicesExistingOrderId);
|
||||
Assert.Single(invoicesFiltered);
|
||||
Assert.Equal(newInvoice.Id, invoicesFiltered.First().Id);
|
||||
|
||||
//list NonExisting OrderId
|
||||
var invoicesNonExistingOrderId =
|
||||
await viewOnly.GetInvoices(user.StoreId, orderId: new []{"NonExistingOrderId"});
|
||||
Assert.NotNull(invoicesNonExistingOrderId);
|
||||
Assert.Empty(invoicesNonExistingOrderId);
|
||||
|
||||
//list Existing Status
|
||||
var invoicesExistingStatus =
|
||||
await viewOnly.GetInvoices(user.StoreId, status: new []{newInvoice.Status});
|
||||
Assert.NotNull(invoicesExistingStatus);
|
||||
Assert.Single(invoicesExistingStatus);
|
||||
Assert.Equal(newInvoice.Id, invoicesExistingStatus.First().Id);
|
||||
|
||||
//list NonExisting Status
|
||||
var invoicesNonExistingStatus = await viewOnly.GetInvoices(user.StoreId,
|
||||
status: new []{BTCPayServer.Client.Models.InvoiceStatus.Invalid});
|
||||
Assert.NotNull(invoicesNonExistingStatus);
|
||||
Assert.Empty(invoicesNonExistingStatus);
|
||||
|
||||
|
||||
//get
|
||||
var invoice = await viewOnly.GetInvoice(user.StoreId, newInvoice.Id);
|
||||
Assert.Equal(newInvoice.Metadata, invoice.Metadata);
|
||||
@ -1165,7 +987,7 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
Status = InvoiceStatus.Invalid
|
||||
});
|
||||
|
||||
|
||||
await AssertHttpError(403, async () =>
|
||||
{
|
||||
await viewOnly.UpdateInvoice(user.StoreId, invoice.Id,
|
||||
@ -1180,14 +1002,14 @@ namespace BTCPayServer.Tests
|
||||
Metadata = JObject.Parse("{\"itemCode\": \"updated\", newstuff: [1,2,3,4,5]}")
|
||||
});
|
||||
|
||||
Assert.Equal("updated", invoice.Metadata["itemCode"].Value<string>());
|
||||
Assert.Equal(15, ((JArray)invoice.Metadata["newstuff"]).Values<int>().Sum());
|
||||
Assert.Equal("updated",invoice.Metadata["itemCode"].Value<string>());
|
||||
Assert.Equal(15,((JArray) invoice.Metadata["newstuff"]).Values<int>().Sum());
|
||||
|
||||
//also test the the metadata actually got saved
|
||||
invoice = await client.GetInvoice(user.StoreId, invoice.Id);
|
||||
Assert.Equal("updated", invoice.Metadata["itemCode"].Value<string>());
|
||||
Assert.Equal(15, ((JArray)invoice.Metadata["newstuff"]).Values<int>().Sum());
|
||||
|
||||
Assert.Equal("updated",invoice.Metadata["itemCode"].Value<string>());
|
||||
Assert.Equal(15,((JArray) invoice.Metadata["newstuff"]).Values<int>().Sum());
|
||||
|
||||
//archive
|
||||
await AssertHttpError(403, async () =>
|
||||
{
|
||||
@ -1257,30 +1079,30 @@ namespace BTCPayServer.Tests
|
||||
var langs = tester.PayTester.GetService<LanguageService>();
|
||||
foreach (var match in new[] { "it", "it-IT", "it-LOL" })
|
||||
{
|
||||
Assert.Equal("it-IT", langs.FindLanguage(match).Code);
|
||||
Assert.Equal("it-IT", langs.FindBestMatch(match).Code);
|
||||
}
|
||||
foreach (var match in new[] { "pt-BR" })
|
||||
{
|
||||
Assert.Equal("pt-BR", langs.FindLanguage(match).Code);
|
||||
Assert.Equal("pt-BR", langs.FindBestMatch(match).Code);
|
||||
}
|
||||
foreach (var match in new[] { "en", "en-US" })
|
||||
{
|
||||
Assert.Equal("en", langs.FindLanguage(match).Code);
|
||||
Assert.Equal("en", langs.FindBestMatch(match).Code);
|
||||
}
|
||||
foreach (var match in new[] { "pt", "pt-pt", "pt-PT" })
|
||||
{
|
||||
Assert.Equal("pt-PT", langs.FindLanguage(match).Code);
|
||||
Assert.Equal("pt-PT", langs.FindBestMatch(match).Code);
|
||||
}
|
||||
|
||||
//payment method activation tests
|
||||
var store = await client.GetStore(user.StoreId);
|
||||
Assert.False(store.LazyPaymentMethods);
|
||||
store.LazyPaymentMethods = true;
|
||||
store = await client.UpdateStore(store.Id,
|
||||
store = await client.UpdateStore(store.Id,
|
||||
JObject.FromObject(store).ToObject<UpdateStoreRequest>());
|
||||
Assert.True(store.LazyPaymentMethods);
|
||||
|
||||
invoice = await client.CreateInvoice(user.StoreId, new CreateInvoiceRequest() { Amount = 1, Currency = "USD" });
|
||||
invoice = await client.CreateInvoice(user.StoreId, new CreateInvoiceRequest() {Amount = 1, Currency = "USD"});
|
||||
paymentMethods = await client.GetInvoicePaymentMethods(store.Id, invoice.Id);
|
||||
Assert.Single(paymentMethods);
|
||||
Assert.False(paymentMethods.First().Activated);
|
||||
@ -1380,7 +1202,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.NotEqual(0, info.BlockHeight);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task NotificationAPITests()
|
||||
@ -1419,7 +1241,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.Empty(await viewOnlyClient.GetNotifications(true));
|
||||
Assert.Empty(await viewOnlyClient.GetNotifications(false));
|
||||
}
|
||||
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task OnChainPaymentMethodAPITests()
|
||||
@ -1427,115 +1249,56 @@ namespace BTCPayServer.Tests
|
||||
using var tester = ServerTester.Create();
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
var user2 = tester.NewAccount();
|
||||
await user.GrantAccessAsync(true);
|
||||
await user2.GrantAccessAsync(false);
|
||||
|
||||
var client = await user.CreateClient(Policies.CanModifyStoreSettings);
|
||||
var client2 = await user2.CreateClient(Policies.CanModifyStoreSettings);
|
||||
var viewOnlyClient = await user.CreateClient(Policies.CanViewStoreSettings);
|
||||
|
||||
var store = await client.CreateStore(new CreateStoreRequest() { Name = "test store" });
|
||||
var store = await client.CreateStore(new CreateStoreRequest() {Name = "test store"});
|
||||
|
||||
Assert.Empty(await client.GetStoreOnChainPaymentMethods(store.Id));
|
||||
await AssertHttpError(403, async () =>
|
||||
{
|
||||
await viewOnlyClient.UpdateStoreOnChainPaymentMethod(store.Id, "BTC", new OnChainPaymentMethodData() { });
|
||||
});
|
||||
|
||||
});
|
||||
var xpriv = new Mnemonic("all all all all all all all all all all all all").DeriveExtKey()
|
||||
.Derive(KeyPath.Parse("m/84'/1'/0'"));
|
||||
.Derive(KeyPath.Parse("m/84'/0'/0'"));
|
||||
var xpub = xpriv.Neuter().ToString(Network.RegTest);
|
||||
var firstAddress = xpriv.Derive(KeyPath.Parse("0/0")).Neuter().GetPublicKey().GetAddress(ScriptPubKeyType.Segwit, Network.RegTest).ToString();
|
||||
await AssertHttpError(404, async () =>
|
||||
{
|
||||
await client.PreviewStoreOnChainPaymentMethodAddresses(store.Id, "BTC");
|
||||
});
|
||||
|
||||
|
||||
Assert.Equal(firstAddress, (await viewOnlyClient.PreviewProposedStoreOnChainPaymentMethodAddresses(store.Id, "BTC",
|
||||
new OnChainPaymentMethodData() { Enabled = true, DerivationScheme = xpub })).Addresses.First().Address);
|
||||
|
||||
new OnChainPaymentMethodData() {Enabled = true, DerivationScheme = xpub})).Addresses.First().Address);
|
||||
|
||||
var method = await client.UpdateStoreOnChainPaymentMethod(store.Id, "BTC",
|
||||
new OnChainPaymentMethodData() { Enabled = true, DerivationScheme = xpub });
|
||||
|
||||
Assert.Equal(xpub, method.DerivationScheme);
|
||||
new OnChainPaymentMethodData() { Enabled = true, DerivationScheme = xpub});
|
||||
|
||||
Assert.Equal(xpub,method.DerivationScheme);
|
||||
|
||||
method = await client.UpdateStoreOnChainPaymentMethod(store.Id, "BTC",
|
||||
new OnChainPaymentMethodData() { Enabled = true, DerivationScheme = xpub, Label = "lol", AccountKeyPath = RootedKeyPath.Parse("01020304/1/2/3") });
|
||||
new OnChainPaymentMethodData() { Enabled = true, DerivationScheme = xpub, Label = "lol", AccountKeyPath = RootedKeyPath.Parse("01020304/1/2/3") });
|
||||
|
||||
method = await client.GetStoreOnChainPaymentMethod(store.Id, "BTC");
|
||||
|
||||
Assert.Equal("lol", method.Label);
|
||||
Assert.Equal(RootedKeyPath.Parse("01020304/1/2/3"), method.AccountKeyPath);
|
||||
Assert.Equal(xpub, method.DerivationScheme);
|
||||
|
||||
|
||||
Assert.Equal(xpub,method.DerivationScheme);
|
||||
|
||||
|
||||
Assert.Equal(firstAddress, (await viewOnlyClient.PreviewStoreOnChainPaymentMethodAddresses(store.Id, "BTC")).Addresses.First().Address);
|
||||
await AssertHttpError(403, async () =>
|
||||
{
|
||||
await viewOnlyClient.RemoveStoreOnChainPaymentMethod(store.Id, "BTC");
|
||||
});
|
||||
await client.RemoveStoreOnChainPaymentMethod(store.Id, "BTC");
|
||||
await AssertHttpError(404, async () =>
|
||||
{
|
||||
await client.GetStoreOnChainPaymentMethod(store.Id, "BTC");
|
||||
});
|
||||
|
||||
await AssertHttpError(403, async () =>
|
||||
{
|
||||
await viewOnlyClient.GenerateOnChainWallet(store.Id, "BTC", new GenerateOnChainWalletRequest() { });
|
||||
});
|
||||
|
||||
await AssertValidationError(new []{"SavePrivateKeys", "ImportKeysToRPC"}, async () =>
|
||||
{
|
||||
await client2.GenerateOnChainWallet(user2.StoreId, "BTC", new GenerateOnChainWalletRequest()
|
||||
{
|
||||
SavePrivateKeys = true,
|
||||
ImportKeysToRPC = true
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
var allMnemonic = new Mnemonic("all all all all all all all all all all all all");
|
||||
|
||||
|
||||
await client.RemoveStoreOnChainPaymentMethod(store.Id, "BTC");
|
||||
var generateResponse = await client.GenerateOnChainWallet(store.Id, "BTC",
|
||||
new GenerateOnChainWalletRequest() {ExistingMnemonic = allMnemonic,});
|
||||
|
||||
Assert.Equal(generateResponse.Mnemonic.ToString(), allMnemonic.ToString());
|
||||
Assert.Equal(generateResponse.DerivationScheme, xpub);
|
||||
|
||||
await AssertAPIError("already-configured", async () =>
|
||||
{
|
||||
await client.GenerateOnChainWallet(store.Id, "BTC",
|
||||
new GenerateOnChainWalletRequest() {ExistingMnemonic = allMnemonic,});
|
||||
});
|
||||
|
||||
await client.RemoveStoreOnChainPaymentMethod(store.Id, "BTC");
|
||||
generateResponse = await client.GenerateOnChainWallet(store.Id, "BTC",
|
||||
new GenerateOnChainWalletRequest() {});
|
||||
Assert.NotEqual(generateResponse.Mnemonic.ToString(), allMnemonic.ToString());
|
||||
Assert.Equal(generateResponse.Mnemonic.DeriveExtKey().Derive(KeyPath.Parse("m/84'/1'/0'")).Neuter().ToString(Network.RegTest), generateResponse.DerivationScheme);
|
||||
|
||||
await client.RemoveStoreOnChainPaymentMethod(store.Id, "BTC");
|
||||
generateResponse = await client.GenerateOnChainWallet(store.Id, "BTC",
|
||||
new GenerateOnChainWalletRequest() { ExistingMnemonic = allMnemonic, AccountNumber = 1});
|
||||
|
||||
Assert.Equal(generateResponse.Mnemonic.ToString(), allMnemonic.ToString());
|
||||
|
||||
Assert.Equal(new Mnemonic("all all all all all all all all all all all all").DeriveExtKey()
|
||||
.Derive(KeyPath.Parse("m/84'/1'/1'")).Neuter().ToString(Network.RegTest), generateResponse.DerivationScheme);
|
||||
|
||||
await client.RemoveStoreOnChainPaymentMethod(store.Id, "BTC");
|
||||
generateResponse = await client.GenerateOnChainWallet(store.Id, "BTC",
|
||||
new GenerateOnChainWalletRequest() { WordList = Wordlist.Japanese, WordCount = WordCount.TwentyFour});
|
||||
|
||||
Assert.Equal(24,generateResponse.Mnemonic.Words.Length);
|
||||
Assert.Equal(Wordlist.Japanese,generateResponse.Mnemonic.WordList);
|
||||
|
||||
await client.RemoveStoreOnChainPaymentMethod(store.Id, "BTC");
|
||||
await AssertHttpError(404, async () =>
|
||||
{
|
||||
await client.GetStoreOnChainPaymentMethod(store.Id, "BTC");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
[Fact(Timeout = 60 * 2 * 1000)]
|
||||
[Trait("Lightning", "Lightning")]
|
||||
[Trait("Integration", "Integration")]
|
||||
@ -1564,13 +1327,13 @@ namespace BTCPayServer.Tests
|
||||
await adminClient.GetStoreLightningNetworkPaymentMethod(store.Id, "BTC");
|
||||
});
|
||||
await admin.RegisterLightningNodeAsync("BTC", false);
|
||||
|
||||
|
||||
var method = await adminClient.GetStoreLightningNetworkPaymentMethod(store.Id, "BTC");
|
||||
await AssertHttpError(403, async () =>
|
||||
{
|
||||
await viewOnlyClient.RemoveStoreOnChainPaymentMethod(store.Id, "BTC");
|
||||
});
|
||||
await adminClient.RemoveStoreOnChainPaymentMethod(store.Id, "BTC");
|
||||
await adminClient.RemoveStoreOnChainPaymentMethod(store.Id, "BTC");
|
||||
await AssertHttpError(404, async () =>
|
||||
{
|
||||
await adminClient.GetStoreOnChainPaymentMethod(store.Id, "BTC");
|
||||
@ -1627,22 +1390,22 @@ namespace BTCPayServer.Tests
|
||||
});
|
||||
});
|
||||
|
||||
var settings = (await tester.PayTester.GetService<SettingsRepository>().GetSettingAsync<PoliciesSettings>()) ?? new PoliciesSettings();
|
||||
var settings = (await tester.PayTester.GetService<SettingsRepository>().GetSettingAsync<PoliciesSettings>())?? new PoliciesSettings();
|
||||
settings.AllowLightningInternalNodeForAll = false;
|
||||
await tester.PayTester.GetService<SettingsRepository>().UpdateSetting(settings);
|
||||
var nonAdminUser = tester.NewAccount();
|
||||
await nonAdminUser.GrantAccessAsync(false);
|
||||
var nonAdminUserClient = await nonAdminUser.CreateClient(Policies.CanModifyStoreSettings);
|
||||
|
||||
var nonAdminUserClient= await nonAdminUser.CreateClient(Policies.CanModifyStoreSettings);
|
||||
|
||||
await AssertHttpError(404, async () =>
|
||||
{
|
||||
await nonAdminUserClient.GetStoreLightningNetworkPaymentMethod(nonAdminUser.StoreId, "BTC");
|
||||
await nonAdminUserClient.GetStoreLightningNetworkPaymentMethod(nonAdminUser.StoreId, "BTC");
|
||||
});
|
||||
await Assert.ThrowsAsync<GreenFieldValidationException>(async () =>
|
||||
{
|
||||
await nonAdminUserClient.UpdateStoreLightningNetworkPaymentMethod(nonAdminUser.StoreId, "BTC", method);
|
||||
});
|
||||
|
||||
|
||||
settings = await tester.PayTester.GetService<SettingsRepository>().GetSettingAsync<PoliciesSettings>();
|
||||
settings.AllowLightningInternalNodeForAll = true;
|
||||
await tester.PayTester.GetService<SettingsRepository>().UpdateSetting(settings);
|
||||
@ -1656,33 +1419,33 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
using var tester = ServerTester.Create();
|
||||
await tester.StartAsync();
|
||||
|
||||
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync(true);
|
||||
|
||||
|
||||
var client = await user.CreateClient(Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings);
|
||||
var viewOnlyClient = await user.CreateClient(Policies.CanViewStoreSettings);
|
||||
var walletId = await user.RegisterDerivationSchemeAsync("BTC", ScriptPubKeyType.Segwit, true);
|
||||
|
||||
|
||||
//view only clients can't do jack shit with this API
|
||||
await AssertHttpError(403, async () =>
|
||||
{
|
||||
await viewOnlyClient.ShowOnChainWalletOverview(walletId.StoreId, walletId.CryptoCode);
|
||||
await viewOnlyClient.ShowOnChainWalletOverview(walletId.StoreId, walletId.CryptoCode );
|
||||
});
|
||||
var overview = await client.ShowOnChainWalletOverview(walletId.StoreId, walletId.CryptoCode);
|
||||
var overview = await client.ShowOnChainWalletOverview(walletId.StoreId, walletId.CryptoCode );
|
||||
Assert.Equal(0m, overview.Balance);
|
||||
|
||||
|
||||
var fee = await client.GetOnChainFeeRate(walletId.StoreId, walletId.CryptoCode);
|
||||
Assert.NotNull(fee.FeeRate);
|
||||
|
||||
|
||||
var fee = await client.GetOnChainFeeRate(walletId.StoreId, walletId.CryptoCode );
|
||||
Assert.NotNull( fee.FeeRate);
|
||||
|
||||
await AssertHttpError(403, async () =>
|
||||
{
|
||||
await viewOnlyClient.GetOnChainWalletReceiveAddress(walletId.StoreId, walletId.CryptoCode);
|
||||
await viewOnlyClient.GetOnChainWalletReceiveAddress(walletId.StoreId, walletId.CryptoCode );
|
||||
});
|
||||
var address = await client.GetOnChainWalletReceiveAddress(walletId.StoreId, walletId.CryptoCode);
|
||||
var address2 = await client.GetOnChainWalletReceiveAddress(walletId.StoreId, walletId.CryptoCode);
|
||||
var address3 = await client.GetOnChainWalletReceiveAddress(walletId.StoreId, walletId.CryptoCode, true);
|
||||
var address = await client.GetOnChainWalletReceiveAddress(walletId.StoreId, walletId.CryptoCode );
|
||||
var address2 = await client.GetOnChainWalletReceiveAddress(walletId.StoreId, walletId.CryptoCode );
|
||||
var address3 = await client.GetOnChainWalletReceiveAddress(walletId.StoreId, walletId.CryptoCode, true );
|
||||
Assert.Equal(address.Address, address2.Address);
|
||||
Assert.NotEqual(address.Address, address3.Address);
|
||||
await AssertHttpError(403, async () =>
|
||||
@ -1693,25 +1456,25 @@ namespace BTCPayServer.Tests
|
||||
uint256 txhash = null;
|
||||
await tester.WaitForEvent<NewOnChainTransactionEvent>(async () =>
|
||||
{
|
||||
txhash = await tester.ExplorerNode.SendToAddressAsync(
|
||||
txhash = await tester.ExplorerNode.SendToAddressAsync(
|
||||
BitcoinAddress.Create(address3.Address, tester.ExplorerClient.Network.NBitcoinNetwork),
|
||||
new Money(0.01m, MoneyUnit.BTC));
|
||||
});
|
||||
await tester.ExplorerNode.GenerateAsync(1);
|
||||
|
||||
var address4 = await client.GetOnChainWalletReceiveAddress(walletId.StoreId, walletId.CryptoCode, false);
|
||||
|
||||
var address4 = await client.GetOnChainWalletReceiveAddress(walletId.StoreId, walletId.CryptoCode, false );
|
||||
Assert.NotEqual(address3.Address, address4.Address);
|
||||
await client.UnReserveOnChainWalletReceiveAddress(walletId.StoreId, walletId.CryptoCode);
|
||||
var address5 = await client.GetOnChainWalletReceiveAddress(walletId.StoreId, walletId.CryptoCode, true);
|
||||
var address5 = await client.GetOnChainWalletReceiveAddress(walletId.StoreId, walletId.CryptoCode, true );
|
||||
Assert.Equal(address5.Address, address4.Address);
|
||||
|
||||
|
||||
|
||||
var utxo = Assert.Single(await client.GetOnChainWalletUTXOs(walletId.StoreId, walletId.CryptoCode));
|
||||
Assert.Equal(0.01m, utxo.Amount);
|
||||
Assert.Equal(txhash, utxo.Outpoint.Hash);
|
||||
overview = await client.ShowOnChainWalletOverview(walletId.StoreId, walletId.CryptoCode);
|
||||
Assert.Equal(0.01m, overview.Balance);
|
||||
|
||||
Assert.Equal(0.01m, utxo.Amount);
|
||||
Assert.Equal(txhash, utxo.Outpoint.Hash);
|
||||
overview = await client.ShowOnChainWalletOverview(walletId.StoreId, walletId.CryptoCode );
|
||||
Assert.Equal(0.01m, overview.Balance);
|
||||
|
||||
//the simplest request:
|
||||
var nodeAddress = await tester.ExplorerNode.GetNewAddressAsync();
|
||||
var createTxRequest = new CreateOnChainTransactionRequest()
|
||||
@ -1728,7 +1491,7 @@ namespace BTCPayServer.Tests
|
||||
};
|
||||
await AssertHttpError(403, async () =>
|
||||
{
|
||||
await viewOnlyClient.CreateOnChainTransaction(walletId.StoreId, walletId.CryptoCode, createTxRequest);
|
||||
await viewOnlyClient.CreateOnChainTransaction(walletId.StoreId, walletId.CryptoCode, createTxRequest );
|
||||
});
|
||||
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(async () =>
|
||||
{
|
||||
@ -1742,11 +1505,11 @@ namespace BTCPayServer.Tests
|
||||
createTxRequest);
|
||||
});
|
||||
Transaction tx;
|
||||
|
||||
tx = await client.CreateOnChainTransactionButDoNotBroadcast(walletId.StoreId, walletId.CryptoCode,
|
||||
createTxRequest, tester.ExplorerClient.Network.NBitcoinNetwork);
|
||||
|
||||
|
||||
|
||||
tx = await client.CreateOnChainTransactionButDoNotBroadcast(walletId.StoreId, walletId.CryptoCode,
|
||||
createTxRequest, tester.ExplorerClient.Network.NBitcoinNetwork);
|
||||
|
||||
|
||||
Assert.NotNull(tx);
|
||||
Assert.Contains(tx.Outputs, txout => txout.IsTo(nodeAddress) && txout.Value.ToDecimal(MoneyUnit.BTC) == 0.001m);
|
||||
Assert.True((await tester.ExplorerNode.TestMempoolAcceptAsync(tx)).IsAllowed);
|
||||
@ -1756,17 +1519,17 @@ namespace BTCPayServer.Tests
|
||||
tx = await client.CreateOnChainTransactionButDoNotBroadcast(walletId.StoreId, walletId.CryptoCode,
|
||||
createTxRequest, tester.ExplorerClient.Network.NBitcoinNetwork);
|
||||
Assert.NotNull(tx);
|
||||
Assert.True(Assert.Single(tx.Outputs).IsTo(nodeAddress));
|
||||
Assert.True(Assert.Single(tx.Outputs).IsTo(nodeAddress) );
|
||||
Assert.True((await tester.ExplorerNode.TestMempoolAcceptAsync(tx)).IsAllowed);
|
||||
|
||||
createTxRequest.NoChange = false;
|
||||
//coin selection
|
||||
await AssertValidationError(new[] { nameof(createTxRequest.SelectedInputs) }, async () =>
|
||||
{
|
||||
createTxRequest.SelectedInputs = new List<OutPoint>();
|
||||
tx = await client.CreateOnChainTransactionButDoNotBroadcast(walletId.StoreId, walletId.CryptoCode,
|
||||
createTxRequest, tester.ExplorerClient.Network.NBitcoinNetwork);
|
||||
});
|
||||
await AssertValidationError(new []{nameof(createTxRequest.SelectedInputs)}, async () =>
|
||||
{
|
||||
createTxRequest.SelectedInputs = new List<OutPoint>();
|
||||
tx = await client.CreateOnChainTransactionButDoNotBroadcast(walletId.StoreId, walletId.CryptoCode,
|
||||
createTxRequest, tester.ExplorerClient.Network.NBitcoinNetwork);
|
||||
});
|
||||
createTxRequest.SelectedInputs = new List<OutPoint>()
|
||||
{
|
||||
utxo.Outpoint
|
||||
@ -1774,55 +1537,55 @@ namespace BTCPayServer.Tests
|
||||
tx = await client.CreateOnChainTransactionButDoNotBroadcast(walletId.StoreId, walletId.CryptoCode,
|
||||
createTxRequest, tester.ExplorerClient.Network.NBitcoinNetwork);
|
||||
createTxRequest.SelectedInputs = null;
|
||||
|
||||
|
||||
//destination testing
|
||||
await AssertValidationError(new[] { "Destinations" }, async () =>
|
||||
{
|
||||
createTxRequest.Destinations[0].Amount = utxo.Amount;
|
||||
tx = await client.CreateOnChainTransactionButDoNotBroadcast(walletId.StoreId, walletId.CryptoCode,
|
||||
createTxRequest, tester.ExplorerClient.Network.NBitcoinNetwork);
|
||||
});
|
||||
|
||||
await AssertValidationError(new []{ "Destinations"}, async () =>
|
||||
{
|
||||
createTxRequest.Destinations[0].Amount = utxo.Amount;
|
||||
tx = await client.CreateOnChainTransactionButDoNotBroadcast(walletId.StoreId, walletId.CryptoCode,
|
||||
createTxRequest, tester.ExplorerClient.Network.NBitcoinNetwork);
|
||||
});
|
||||
|
||||
createTxRequest.Destinations[0].SubtractFromAmount = true;
|
||||
tx = await client.CreateOnChainTransactionButDoNotBroadcast(walletId.StoreId, walletId.CryptoCode,
|
||||
createTxRequest, tester.ExplorerClient.Network.NBitcoinNetwork);
|
||||
|
||||
|
||||
await AssertValidationError(new[] { "Destinations[0]" }, async () =>
|
||||
{
|
||||
createTxRequest.Destinations[0].Amount = 0m;
|
||||
tx = await client.CreateOnChainTransactionButDoNotBroadcast(walletId.StoreId, walletId.CryptoCode,
|
||||
createTxRequest, tester.ExplorerClient.Network.NBitcoinNetwork);
|
||||
});
|
||||
|
||||
|
||||
|
||||
await AssertValidationError(new []{ "Destinations[0]"}, async () =>
|
||||
{
|
||||
createTxRequest.Destinations[0].Amount = 0m;
|
||||
tx = await client.CreateOnChainTransactionButDoNotBroadcast(walletId.StoreId, walletId.CryptoCode,
|
||||
createTxRequest, tester.ExplorerClient.Network.NBitcoinNetwork);
|
||||
});
|
||||
|
||||
//dest can be a bip21
|
||||
|
||||
|
||||
//cant use bip with subtractfromamount
|
||||
createTxRequest.Destinations[0].Amount = null;
|
||||
createTxRequest.Destinations[0].Destination = $"bitcoin:{nodeAddress}?amount=0.001";
|
||||
await AssertValidationError(new[] { "Destinations[0]" }, async () =>
|
||||
{
|
||||
tx = await client.CreateOnChainTransactionButDoNotBroadcast(walletId.StoreId, walletId.CryptoCode,
|
||||
createTxRequest, tester.ExplorerClient.Network.NBitcoinNetwork);
|
||||
});
|
||||
await AssertValidationError(new []{ "Destinations[0]"}, async () =>
|
||||
{
|
||||
tx = await client.CreateOnChainTransactionButDoNotBroadcast(walletId.StoreId, walletId.CryptoCode,
|
||||
createTxRequest, tester.ExplorerClient.Network.NBitcoinNetwork);
|
||||
});
|
||||
//if amt specified, it overrides bip21 amount
|
||||
createTxRequest.Destinations[0].Amount = 0.0001m;
|
||||
createTxRequest.Destinations[0].SubtractFromAmount = false;
|
||||
tx = await client.CreateOnChainTransactionButDoNotBroadcast(walletId.StoreId, walletId.CryptoCode,
|
||||
createTxRequest, tester.ExplorerClient.Network.NBitcoinNetwork);
|
||||
Assert.Contains(tx.Outputs, txout => txout.Value.GetValue(tester.NetworkProvider.GetNetwork<BTCPayNetwork>("BTC")) == 0.0001m);
|
||||
|
||||
Assert.Contains(tx.Outputs, txout => txout.Value.GetValue(tester.NetworkProvider.GetNetwork<BTCPayNetwork>("BTC")) ==0.0001m );
|
||||
|
||||
//fee rate test
|
||||
createTxRequest.FeeRate = FeeRate.Zero;
|
||||
await AssertValidationError(new[] { "FeeRate" }, async () =>
|
||||
{
|
||||
tx = await client.CreateOnChainTransactionButDoNotBroadcast(walletId.StoreId, walletId.CryptoCode,
|
||||
createTxRequest, tester.ExplorerClient.Network.NBitcoinNetwork);
|
||||
});
|
||||
await AssertValidationError(new []{ "FeeRate"}, async () =>
|
||||
{
|
||||
tx = await client.CreateOnChainTransactionButDoNotBroadcast(walletId.StoreId, walletId.CryptoCode,
|
||||
createTxRequest, tester.ExplorerClient.Network.NBitcoinNetwork);
|
||||
});
|
||||
|
||||
|
||||
createTxRequest.FeeRate = new FeeRate(5.0m);
|
||||
|
||||
createTxRequest.FeeRate = new FeeRate(5.0m);
|
||||
|
||||
createTxRequest.Destinations[0].Amount = 0.001m;
|
||||
createTxRequest.Destinations[0].Destination = nodeAddress.ToString();
|
||||
createTxRequest.Destinations[0].SubtractFromAmount = false;
|
||||
@ -1832,30 +1595,30 @@ namespace BTCPayServer.Tests
|
||||
createTxRequest, tester.ExplorerClient.Network.NBitcoinNetwork);
|
||||
});
|
||||
createTxRequest.ProceedWithBroadcast = true;
|
||||
var txdata =
|
||||
var txdata=
|
||||
await client.CreateOnChainTransaction(walletId.StoreId, walletId.CryptoCode,
|
||||
createTxRequest);
|
||||
Assert.Equal(TransactionStatus.Unconfirmed, txdata.Status);
|
||||
Assert.Null(txdata.BlockHeight);
|
||||
Assert.Null(txdata.BlockHash);
|
||||
Assert.NotNull(await tester.ExplorerClient.GetTransactionAsync(txdata.TransactionHash));
|
||||
|
||||
|
||||
await AssertHttpError(403, async () =>
|
||||
{
|
||||
await viewOnlyClient.GetOnChainWalletTransaction(walletId.StoreId, walletId.CryptoCode, txdata.TransactionHash.ToString());
|
||||
});
|
||||
await client.GetOnChainWalletTransaction(walletId.StoreId, walletId.CryptoCode, txdata.TransactionHash.ToString());
|
||||
|
||||
|
||||
await AssertHttpError(403, async () =>
|
||||
{
|
||||
await viewOnlyClient.ShowOnChainWalletTransactions(walletId.StoreId, walletId.CryptoCode);
|
||||
});
|
||||
Assert.True(Assert.Single(
|
||||
await client.ShowOnChainWalletTransactions(walletId.StoreId, walletId.CryptoCode,
|
||||
new[] { TransactionStatus.Confirmed })).TransactionHash == utxo.Outpoint.Hash);
|
||||
new[] {TransactionStatus.Confirmed})).TransactionHash == utxo.Outpoint.Hash);
|
||||
Assert.Contains(
|
||||
await client.ShowOnChainWalletTransactions(walletId.StoreId, walletId.CryptoCode,
|
||||
new[] { TransactionStatus.Unconfirmed }), data => data.TransactionHash == txdata.TransactionHash);
|
||||
new[] {TransactionStatus.Unconfirmed}), data => data.TransactionHash == txdata.TransactionHash);
|
||||
Assert.Contains(
|
||||
await client.ShowOnChainWalletTransactions(walletId.StoreId, walletId.CryptoCode), data => data.TransactionHash == txdata.TransactionHash);
|
||||
await tester.WaitForEvent<NewBlockEvent>(async () =>
|
||||
@ -1866,7 +1629,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
Assert.Contains(
|
||||
await client.ShowOnChainWalletTransactions(walletId.StoreId, walletId.CryptoCode,
|
||||
new[] { TransactionStatus.Confirmed }), data => data.TransactionHash == txdata.TransactionHash);
|
||||
new[] {TransactionStatus.Confirmed}), data => data.TransactionHash == txdata.TransactionHash);
|
||||
|
||||
}
|
||||
|
||||
@ -1909,55 +1672,6 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(1.2, jsonConverter.ReadJson(Get(stringJson), typeof(double), null, null));
|
||||
Assert.Equal(1.2, jsonConverter.ReadJson(Get(stringJson), typeof(double?), null, null));
|
||||
}
|
||||
|
||||
|
||||
[Fact(Timeout = 60 * 2 * 1000)]
|
||||
[Trait("Lightning", "Lightning")]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task StorePaymentMethodsAPITests()
|
||||
{
|
||||
using var tester = ServerTester.Create();
|
||||
tester.ActivateLightning();
|
||||
await tester.StartAsync();
|
||||
await tester.EnsureChannelsSetup();
|
||||
var admin = tester.NewAccount();
|
||||
await admin.GrantAccessAsync(true);
|
||||
var adminClient = await admin.CreateClient(Policies.Unrestricted);
|
||||
var store = await adminClient.GetStore(admin.StoreId);
|
||||
|
||||
Assert.Empty(await adminClient.GetStorePaymentMethods(store.Id));
|
||||
|
||||
await adminClient.UpdateStoreLightningNetworkPaymentMethodToInternalNode(admin.StoreId, "BTC");
|
||||
|
||||
void VerifyLightning(Dictionary<string, GenericPaymentMethodData> dictionary)
|
||||
{
|
||||
Assert.True(dictionary.TryGetValue(new PaymentMethodId("BTC", PaymentTypes.LightningLike).ToStringNormalized(), out var item));
|
||||
var lightningNetworkPaymentMethodBaseData =Assert.IsType<JObject>(item.Data).ToObject<LightningNetworkPaymentMethodBaseData>();
|
||||
Assert.Equal("Internal Node", lightningNetworkPaymentMethodBaseData.ConnectionString);
|
||||
}
|
||||
|
||||
var methods = await adminClient.GetStorePaymentMethods(store.Id);
|
||||
Assert.Single(methods);
|
||||
VerifyLightning(methods);
|
||||
|
||||
var randK = new Mnemonic(Wordlist.English, WordCount.Twelve).DeriveExtKey().Neuter().ToString(Network.RegTest);
|
||||
await adminClient.UpdateStoreOnChainPaymentMethod(admin.StoreId, "BTC",
|
||||
new OnChainPaymentMethodData("BTC", randK, true, "testing", null));
|
||||
|
||||
void VerifyOnChain(Dictionary<string, GenericPaymentMethodData> dictionary)
|
||||
{
|
||||
Assert.True(dictionary.TryGetValue(new PaymentMethodId("BTC", PaymentTypes.BTCLike).ToStringNormalized(), out var item));
|
||||
var paymentMethodBaseData =Assert.IsType<JObject>(item.Data).ToObject<OnChainPaymentMethodBaseData>();
|
||||
Assert.Equal(randK, paymentMethodBaseData.DerivationScheme);
|
||||
}
|
||||
|
||||
methods = await adminClient.GetStorePaymentMethods(store.Id);
|
||||
Assert.Equal(2, methods.Count);
|
||||
VerifyLightning(methods);
|
||||
VerifyOnChain(methods);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,52 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
{
|
||||
public class LanguageServiceTests
|
||||
{
|
||||
public const int TestTimeout = TestUtils.TestTimeout;
|
||||
public LanguageServiceTests(ITestOutputHelper helper)
|
||||
{
|
||||
Logs.Tester = new XUnitLog(helper) { Name = "Tests" };
|
||||
Logs.LogProvider = new XUnitLogProvider(helper);
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanAutoDetectLanguage()
|
||||
{
|
||||
using (var tester = ServerTester.Create())
|
||||
{
|
||||
await tester.StartAsync();
|
||||
var languageService = tester.PayTester.GetService<LanguageService>();
|
||||
|
||||
// Most common format. First option does not have a quality score. Others do in descending order.
|
||||
// Result should be nl-NL (because the default weight is 1 for nl)
|
||||
var lang1 = languageService.FindLanguageInAcceptLanguageHeader("nl,fr;q=0.7,en;q=0.5");
|
||||
Assert.NotNull(lang1);
|
||||
Assert.Equal("nl-NL", lang1?.Code);
|
||||
|
||||
// Most common format. First option does not have a quality score. Others do in descending order. This time the first option includes a country.
|
||||
// Result should be nl-NL (because the default weight is 1 for nl-BE and it does not exist in BTCPay Server, but nl-NL does and applies too for language "nl")
|
||||
var lang2 = languageService.FindLanguageInAcceptLanguageHeader("nl-BE,fr;q=0.7,en;q=0.5");
|
||||
Assert.NotNull(lang2);
|
||||
Assert.Equal("nl-NL", lang2?.Code);
|
||||
|
||||
// Unusual format, but still valid. All values have a quality score and not ordered.
|
||||
// Result should be fr-FR (because 0.7 is the highest quality score)
|
||||
var lang3 = languageService.FindLanguageInAcceptLanguageHeader("nl;q=0.1,fr;q=0.7,en;q=0.5");
|
||||
Assert.NotNull(lang3);
|
||||
Assert.Equal("fr-FR", lang3?.Code);
|
||||
|
||||
// Unusual format, but still valid. Some language is given that we don't have and a wildcard for everything else.
|
||||
// Result should be NULL, because "xx" does not exist and * is a wildcard and has no meaning.
|
||||
var lang4 = languageService.FindLanguageInAcceptLanguageHeader("xx,*;q=0.5");
|
||||
Assert.Null(lang4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -86,9 +86,9 @@ namespace BTCPayServer.Tests
|
||||
var signedPSBT = unsignedPSBT.Clone();
|
||||
signedPSBT.SignAll(user.DerivationScheme, user.GenerateWalletResponseV.AccountHDKey, user.GenerateWalletResponseV.AccountKeyPath);
|
||||
vmPSBT.PSBT = signedPSBT.ToBase64();
|
||||
var psbtReady = await walletController.WalletPSBTReady(walletId, new WalletPSBTReadyViewModel
|
||||
var psbtReady = await walletController.WalletPSBTReady(walletId, new WalletPSBTReadyViewModel()
|
||||
{
|
||||
SigningContext = new SigningContextModel
|
||||
SigningContext = new SigningContextModel()
|
||||
{
|
||||
PSBT = AssertRedirectedPSBT(await walletController.WalletPSBT(walletId, vmPSBT, "broadcast"), nameof(walletController.WalletPSBTReady))
|
||||
}
|
||||
@ -96,7 +96,9 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(2 + 1, psbtReady.Destinations.Count); // The fee is a destination
|
||||
Assert.Contains(psbtReady.Destinations, d => d.Destination == sendDestination && !d.Positive);
|
||||
Assert.Contains(psbtReady.Destinations, d => d.Positive);
|
||||
|
||||
var redirect = Assert.IsType<RedirectToActionResult>(await walletController.WalletPSBTReady(walletId, psbtReady, command: "broadcast"));
|
||||
Assert.Equal(nameof(walletController.WalletTransactions), redirect.ActionName);
|
||||
|
||||
vmPSBT.PSBT = unsignedPSBT.ToBase64();
|
||||
var combineVM = await walletController.WalletPSBT(walletId, vmPSBT, "combine").AssertViewModelAsync<WalletPSBTCombineViewModel>();
|
||||
Assert.Equal(vmPSBT.PSBT, combineVM.OtherPSBT);
|
||||
@ -117,14 +119,14 @@ namespace BTCPayServer.Tests
|
||||
Assert.True(signedPSBT2.TryFinalize(out _));
|
||||
Assert.Equal(signedPSBT, signedPSBT2);
|
||||
|
||||
var ready = (await walletController.WalletPSBTReady(walletId, new WalletPSBTReadyViewModel
|
||||
var ready = (await walletController.WalletPSBTReady(walletId, new WalletPSBTReadyViewModel()
|
||||
{
|
||||
SigningContext = new SigningContextModel(signedPSBT)
|
||||
})).AssertViewModel<WalletPSBTReadyViewModel>();
|
||||
Assert.Equal(signedPSBT.ToBase64(), ready.SigningContext.PSBT);
|
||||
psbt = AssertRedirectedPSBT(await walletController.WalletPSBTReady(walletId, ready, command: "analyze-psbt"), nameof(walletController.WalletPSBT));
|
||||
Assert.Equal(signedPSBT.ToBase64(), psbt);
|
||||
var redirect = Assert.IsType<RedirectToActionResult>(await walletController.WalletPSBTReady(walletId, ready, command: "broadcast"));
|
||||
redirect = Assert.IsType<RedirectToActionResult>(await walletController.WalletPSBTReady(walletId, ready, command: "broadcast"));
|
||||
Assert.Equal(nameof(walletController.WalletTransactions), redirect.ActionName);
|
||||
|
||||
//test base64 psbt file
|
||||
|
@ -146,7 +146,7 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
var tx = network.NBitcoinNetwork.CreateTransaction();
|
||||
tx.Inputs.Add(new OutPoint(RandomUtils.GetUInt256(), 0), Script.Empty);
|
||||
tx.Outputs.Add(Money.Coins(1.0m), new Key().GetScriptPubKey(ScriptPubKeyType.Legacy));
|
||||
tx.Outputs.Add(Money.Coins(1.0m), new Key().ScriptPubKey);
|
||||
return tx;
|
||||
}
|
||||
|
||||
@ -184,16 +184,12 @@ namespace BTCPayServer.Tests
|
||||
|
||||
foreach (ScriptPubKeyType senderAddressType in Enum.GetValues(typeof(ScriptPubKeyType)))
|
||||
{
|
||||
if (senderAddressType == ScriptPubKeyType.TaprootBIP86)
|
||||
continue;
|
||||
var senderUser = tester.NewAccount();
|
||||
senderUser.GrantAccess(true);
|
||||
senderUser.RegisterDerivationScheme("BTC", senderAddressType);
|
||||
|
||||
foreach (ScriptPubKeyType receiverAddressType in Enum.GetValues(typeof(ScriptPubKeyType)))
|
||||
{
|
||||
if (receiverAddressType == ScriptPubKeyType.TaprootBIP86)
|
||||
continue;
|
||||
var senderCoin = await senderUser.ReceiveUTXO(Money.Satoshis(100000), network);
|
||||
|
||||
Logs.Tester.LogInformation($"Testing payjoin with sender: {senderAddressType} receiver: {receiverAddressType}");
|
||||
@ -226,57 +222,6 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Selenium", "Selenium")]
|
||||
public async Task CanUsePayjoinForTopUp()
|
||||
{
|
||||
using (var s = SeleniumTester.Create())
|
||||
{
|
||||
await s.StartAsync();
|
||||
s.RegisterNewUser(true);
|
||||
var receiver = s.CreateNewStore();
|
||||
var receiverSeed = s.GenerateWallet("BTC", "", true, true, ScriptPubKeyType.Segwit);
|
||||
var receiverWalletId = new WalletId(receiver.storeId, "BTC");
|
||||
|
||||
var sender = s.CreateNewStore();
|
||||
var senderSeed = s.GenerateWallet("BTC", "", true, true, ScriptPubKeyType.Segwit);
|
||||
var senderWalletId = new WalletId(sender.storeId, "BTC");
|
||||
|
||||
await s.Server.ExplorerNode.GenerateAsync(1);
|
||||
await s.FundStoreWallet(senderWalletId);
|
||||
await s.FundStoreWallet(receiverWalletId);
|
||||
|
||||
var invoiceId = s.CreateInvoice(receiver.storeName, null, "BTC");
|
||||
s.GoToInvoiceCheckout(invoiceId);
|
||||
var bip21 = s.Driver.FindElement(By.ClassName("payment__details__instruction__open-wallet__btn"))
|
||||
.GetAttribute("href");
|
||||
Assert.Contains($"{PayjoinClient.BIP21EndpointKey}=", bip21);
|
||||
s.GoToWallet(senderWalletId, WalletsNavPages.Send);
|
||||
s.Driver.FindElement(By.Id("bip21parse")).Click();
|
||||
s.Driver.SwitchTo().Alert().SendKeys(bip21);
|
||||
s.Driver.SwitchTo().Alert().Accept();
|
||||
s.Driver.FindElement(By.Id("Outputs_0__Amount")).Clear();
|
||||
s.Driver.FindElement(By.Id("Outputs_0__Amount")).SendKeys("0.023");
|
||||
|
||||
s.Driver.FindElement(By.Id("SignTransaction")).Click();
|
||||
|
||||
await s.Server.WaitForEvent<NewOnChainTransactionEvent>(() =>
|
||||
{
|
||||
s.Driver.FindElement(By.CssSelector("button[value=payjoin]")).Click();
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
s.FindAlertMessage(StatusMessageModel.StatusSeverity.Success);
|
||||
var invoiceRepository = s.Server.PayTester.GetService<InvoiceRepository>();
|
||||
await TestUtils.EventuallyAsync(async () =>
|
||||
{
|
||||
var invoice = await invoiceRepository.GetInvoice(invoiceId);
|
||||
Assert.Equal(InvoiceStatusLegacy.Paid, invoice.Status);
|
||||
Assert.Equal(0.023m, invoice.Price);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Selenium", "Selenium")]
|
||||
public async Task CanUsePayjoinViaUI()
|
||||
@ -293,15 +238,19 @@ namespace BTCPayServer.Tests
|
||||
var receiverSeed = s.GenerateWallet("BTC", "", true, true, format);
|
||||
var receiverWalletId = new WalletId(receiver.storeId, "BTC");
|
||||
|
||||
//payjoin is enabled by default.
|
||||
//payjoin is not enabled by default.
|
||||
var invoiceId = s.CreateInvoice(receiver.storeName);
|
||||
s.GoToInvoiceCheckout(invoiceId);
|
||||
var bip21 = s.Driver.FindElement(By.ClassName("payment__details__instruction__open-wallet__btn"))
|
||||
.GetAttribute("href");
|
||||
Assert.Contains($"{PayjoinClient.BIP21EndpointKey}=", bip21);
|
||||
Assert.DoesNotContain($"{PayjoinClient.BIP21EndpointKey}=", bip21);
|
||||
|
||||
s.GoToHome();
|
||||
s.GoToStore(receiver.storeId);
|
||||
//payjoin is not enabled by default.
|
||||
Assert.False(s.Driver.FindElement(By.Id("PayJoinEnabled")).Selected);
|
||||
s.Driver.SetCheckbox(By.Id("PayJoinEnabled"), true);
|
||||
s.Driver.FindElement(By.Id("Save")).Click();
|
||||
Assert.True(s.Driver.FindElement(By.Id("PayJoinEnabled")).Selected);
|
||||
|
||||
var sender = s.CreateNewStore();
|
||||
@ -322,7 +271,10 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.SwitchTo().Alert().Accept();
|
||||
Assert.False(string.IsNullOrEmpty(s.Driver.FindElement(By.Id("PayJoinBIP21"))
|
||||
.GetAttribute("value")));
|
||||
s.Driver.FindElement(By.Id("SignTransaction")).Click();
|
||||
s.Driver.FindElement(By.Id("SendMenu")).Click();
|
||||
var nbxSeedButton = s.Driver.FindElement(By.CssSelector("button[value=nbx-seed]"));
|
||||
new WebDriverWait(s.Driver, SeleniumTester.ImplicitWait).Until(d=> nbxSeedButton.Enabled);
|
||||
nbxSeedButton.Click();
|
||||
await s.Server.WaitForEvent<NewOnChainTransactionEvent>(() =>
|
||||
{
|
||||
s.Driver.FindElement(By.CssSelector("button[value=payjoin]")).Click();
|
||||
@ -357,7 +309,8 @@ namespace BTCPayServer.Tests
|
||||
.GetAttribute("value")));
|
||||
s.Driver.FindElement(By.Id("FeeSatoshiPerByte")).Clear();
|
||||
s.Driver.FindElement(By.Id("FeeSatoshiPerByte")).SendKeys("2");
|
||||
s.Driver.FindElement(By.Id("SignTransaction")).Click();
|
||||
s.Driver.FindElement(By.Id("SendMenu")).Click();
|
||||
s.Driver.FindElement(By.CssSelector("button[value=nbx-seed]")).Click();
|
||||
var txId = await s.Server.WaitForEvent<NewOnChainTransactionEvent>(() =>
|
||||
{
|
||||
s.Driver.FindElement(By.CssSelector("button[value=payjoin]")).Click();
|
||||
@ -367,7 +320,7 @@ namespace BTCPayServer.Tests
|
||||
await TestUtils.EventuallyAsync(async () =>
|
||||
{
|
||||
var invoice = await invoiceRepository.GetInvoice(invoiceId);
|
||||
var payments = invoice.GetPayments(false);
|
||||
var payments = invoice.GetPayments();
|
||||
Assert.Equal(2, payments.Count);
|
||||
var originalPayment = payments[0];
|
||||
var coinjoinPayment = payments[1];
|
||||
@ -570,7 +523,7 @@ namespace BTCPayServer.Tests
|
||||
address = (await nbx.GetUnusedAsync(bob.DerivationScheme, DerivationFeature.Deposit)).Address;
|
||||
tester.ExplorerNode.SendToAddress(address, Money.Coins(1.1m));
|
||||
await notifications.NextEventAsync();
|
||||
await bob.ModifyStore(s => s.PayJoinEnabled = true);
|
||||
bob.ModifyStore(s => s.PayJoinEnabled = true);
|
||||
var invoice = bob.BitPay.CreateInvoice(
|
||||
new Invoice() { Price = 0.1m, Currency = "BTC", FullNotifications = true });
|
||||
var invoiceBIP21 = new BitcoinUrlBuilder(invoice.CryptoInfo.First().PaymentUrls.BIP21,
|
||||
@ -1135,7 +1088,7 @@ retry:
|
||||
{
|
||||
var invoiceEntity = await tester.PayTester.GetService<InvoiceRepository>().GetInvoice(invoice7.Id);
|
||||
Assert.Equal(InvoiceStatusLegacy.Paid, invoiceEntity.Status);
|
||||
Assert.Contains(invoiceEntity.GetPayments(false), p => p.Accounted &&
|
||||
Assert.Contains(invoiceEntity.GetPayments(), p => p.Accounted &&
|
||||
((BitcoinLikePaymentData)p.GetCryptoPaymentData()).PayjoinInformation is null);
|
||||
});
|
||||
////Assert.Contains(receiverWalletPayJoinState.GetRecords(), item => item.InvoiceId == invoice7.Id && item.TxSeen);
|
||||
@ -1164,8 +1117,8 @@ retry:
|
||||
{
|
||||
var invoiceEntity = await tester.PayTester.GetService<InvoiceRepository>().GetInvoice(invoice7.Id);
|
||||
Assert.Equal(InvoiceStatusLegacy.New, invoiceEntity.Status);
|
||||
Assert.True(invoiceEntity.GetPayments(false).All(p => !p.Accounted));
|
||||
ourOutpoint = invoiceEntity.GetAllBitcoinPaymentData(false).First().PayjoinInformation.ContributedOutPoints[0];
|
||||
Assert.True(invoiceEntity.GetPayments().All(p => !p.Accounted));
|
||||
ourOutpoint = invoiceEntity.GetAllBitcoinPaymentData().First().PayjoinInformation.ContributedOutPoints[0];
|
||||
});
|
||||
var payjoinRepository = tester.PayTester.GetService<PayJoinRepository>();
|
||||
// The outpoint should now be available for next pj selection
|
||||
|
@ -63,17 +63,16 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
options.AddArguments($"window-size={windowSize.Width}x{windowSize.Height}");
|
||||
options.AddArgument("shm-size=2g");
|
||||
options.AddArgument("start-maximized");
|
||||
|
||||
var cds = ChromeDriverService.CreateDefaultService(chromeDriverPath);
|
||||
cds.EnableVerboseLogging = true;
|
||||
cds.Port = Utils.FreeTcpPort();
|
||||
cds.HostName = "127.0.0.1";
|
||||
cds.Start();
|
||||
Driver = new ChromeDriver(cds, options,
|
||||
Driver = new ChromeDriver(chromeDriverPath, options,
|
||||
// A bit less than test timeout
|
||||
TimeSpan.FromSeconds(50));
|
||||
Driver.Manage().Window.Maximize();
|
||||
|
||||
if (runInBrowser)
|
||||
{
|
||||
// ensure maximized window size
|
||||
// otherwise TESTS WILL FAIL because of different hierarchy in navigation menu
|
||||
Driver.Manage().Window.Maximize();
|
||||
}
|
||||
|
||||
Logs.Tester.LogInformation($"Selenium: Using {Driver.GetType()}");
|
||||
Logs.Tester.LogInformation($"Selenium: Browsing to {Server.PayTester.ServerUri}");
|
||||
@ -128,7 +127,6 @@ namespace BTCPayServer.Tests
|
||||
|
||||
public Mnemonic GenerateWallet(string cryptoCode = "BTC", string seed = "", bool importkeys = false, bool privkeys = false, ScriptPubKeyType format = ScriptPubKeyType.Segwit)
|
||||
{
|
||||
var isImport = !string.IsNullOrEmpty(seed);
|
||||
Driver.FindElement(By.Id($"Modify{cryptoCode}")).Click();
|
||||
|
||||
// Replace previous wallet case
|
||||
@ -138,7 +136,14 @@ namespace BTCPayServer.Tests
|
||||
Driver.FindElement(By.Id("continue")).Click();
|
||||
}
|
||||
|
||||
if (isImport)
|
||||
if (string.IsNullOrEmpty(seed))
|
||||
{
|
||||
var option = privkeys ? "Hotwallet" : "Watchonly";
|
||||
Logs.Tester.LogInformation($"Generating new seed ({option})");
|
||||
Driver.FindElement(By.Id("GenerateWalletLink")).Click();
|
||||
Driver.FindElement(By.Id($"Generate{option}Link")).Click();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logs.Tester.LogInformation("Progressing with existing seed");
|
||||
Driver.FindElement(By.Id("ImportWalletOptionsLink")).Click();
|
||||
@ -146,40 +151,28 @@ namespace BTCPayServer.Tests
|
||||
Driver.FindElement(By.Id("ExistingMnemonic")).SendKeys(seed);
|
||||
Driver.SetCheckbox(By.Id("SavePrivateKeys"), privkeys);
|
||||
}
|
||||
else
|
||||
{
|
||||
var option = privkeys ? "Hotwallet" : "Watchonly";
|
||||
Logs.Tester.LogInformation($"Generating new seed ({option})");
|
||||
Driver.FindElement(By.Id("GenerateWalletLink")).Click();
|
||||
Driver.FindElement(By.Id($"Generate{option}Link")).Click();
|
||||
}
|
||||
|
||||
Driver.FindElement(By.Id("ScriptPubKeyType")).Click();
|
||||
Driver.FindElement(By.CssSelector($"#ScriptPubKeyType option[value={format}]")).Click();
|
||||
|
||||
Driver.ToggleCollapse("AdvancedSettings");
|
||||
|
||||
// Open advanced settings via JS, because if we click the link it triggers the toggle animation.
|
||||
// This leads to Selenium trying to click the button while it is moving resulting in an error.
|
||||
Driver.ExecuteJavaScript("document.getElementById('AdvancedSettings').classList.add('show')");
|
||||
|
||||
Driver.SetCheckbox(By.Id("ImportKeysToRPC"), importkeys);
|
||||
Driver.FindElement(By.Id("Continue")).Click();
|
||||
|
||||
if (isImport)
|
||||
// Seed backup page
|
||||
FindAlertMessage();
|
||||
if (string.IsNullOrEmpty(seed))
|
||||
{
|
||||
// Confirm addresses
|
||||
Driver.FindElement(By.Id("Confirm")).Click();
|
||||
seed = Driver.FindElements(By.Id("RecoveryPhrase")).First().GetAttribute("data-mnemonic");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Seed backup
|
||||
FindAlertMessage();
|
||||
if (string.IsNullOrEmpty(seed))
|
||||
{
|
||||
seed = Driver.FindElements(By.Id("RecoveryPhrase")).First().GetAttribute("data-mnemonic");
|
||||
}
|
||||
|
||||
// Confirm seed backup
|
||||
Driver.FindElement(By.Id("confirm")).Click();
|
||||
Driver.FindElement(By.Id("submit")).Click();
|
||||
}
|
||||
|
||||
// Confirm seed backup
|
||||
Driver.FindElement(By.Id("confirm")).Click();
|
||||
Driver.FindElement(By.Id("submit")).Click();
|
||||
|
||||
WalletId = new WalletId(StoreId, cryptoCode);
|
||||
return new Mnemonic(seed);
|
||||
}
|
||||
@ -219,20 +212,15 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
Driver.FindElement(By.CssSelector("label[for=\"LightningNodeType-Custom\"]")).Click();
|
||||
Driver.FindElement(By.Id("ConnectionString")).SendKeys(connectionString);
|
||||
|
||||
Driver.FindElement(By.Id("test")).Click();
|
||||
Assert.Contains("Connection to the Lightning node successful.", FindAlertMessage().Text);
|
||||
}
|
||||
|
||||
var enabled = Driver.FindElement(By.Id("Enabled"));
|
||||
if (!enabled.Selected) enabled.Click();
|
||||
|
||||
Driver.FindElement(By.Id("test")).Click();
|
||||
Assert.Contains("Connection to the Lightning node succeeded.", FindAlertMessage().Text);
|
||||
|
||||
Driver.FindElement(By.Id("save")).Click();
|
||||
Assert.Contains($"{cryptoCode} Lightning node updated.", FindAlertMessage().Text);
|
||||
|
||||
var enabled = Driver.FindElement(By.Id($"{cryptoCode}LightningEnabled"));
|
||||
if (enabled.Text == "Enable")
|
||||
{
|
||||
enabled.Click();
|
||||
Assert.Contains($"{cryptoCode} Lightning payments are now enabled for this store.", FindAlertMessage().Text);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClickOnAllSideMenus()
|
||||
@ -331,12 +319,11 @@ namespace BTCPayServer.Tests
|
||||
Driver.Navigate().GoToUrl(new Uri(Server.PayTester.ServerUri, "/login"));
|
||||
}
|
||||
|
||||
public string CreateInvoice(string storeName, decimal? amount = 100, string currency = "USD", string refundEmail = "")
|
||||
public string CreateInvoice(string storeName, decimal amount = 100, string currency = "USD", string refundEmail = "")
|
||||
{
|
||||
GoToInvoices();
|
||||
Driver.FindElement(By.Id("CreateNewInvoice")).Click();
|
||||
if (amount is decimal v)
|
||||
Driver.FindElement(By.Id("Amount")).SendKeys(v.ToString(CultureInfo.InvariantCulture));
|
||||
Driver.FindElement(By.Id("Amount")).SendKeys(amount.ToString(CultureInfo.InvariantCulture));
|
||||
var currencyEl = Driver.FindElement(By.Id("Currency"));
|
||||
currencyEl.Clear();
|
||||
currencyEl.SendKeys(currency);
|
||||
@ -373,22 +360,22 @@ namespace BTCPayServer.Tests
|
||||
Driver.FindElement(By.Id("bip21parse")).Click();
|
||||
Driver.SwitchTo().Alert().SendKeys(bip21);
|
||||
Driver.SwitchTo().Alert().Accept();
|
||||
Driver.FindElement(By.Id("SignTransaction")).Click();
|
||||
Driver.FindElement(By.Id("SignWithSeed")).Click();
|
||||
Driver.FindElement(By.Id("SendMenu")).Click();
|
||||
Driver.FindElement(By.CssSelector("button[value=nbx-seed]")).Click();
|
||||
Driver.FindElement(By.CssSelector("button[value=broadcast]")).Click();
|
||||
}
|
||||
|
||||
private void CheckForJSErrors()
|
||||
{
|
||||
//wait for seleniun update: https://stackoverflow.com/questions/57520296/selenium-webdriver-3-141-0-driver-manage-logs-availablelogtypes-throwing-syste
|
||||
// var errorStrings = new List<string>
|
||||
// {
|
||||
// "SyntaxError",
|
||||
// "EvalError",
|
||||
// "ReferenceError",
|
||||
// "RangeError",
|
||||
// "TypeError",
|
||||
// "URIError"
|
||||
// var errorStrings = new List<string>
|
||||
// {
|
||||
// "SyntaxError",
|
||||
// "EvalError",
|
||||
// "ReferenceError",
|
||||
// "RangeError",
|
||||
// "TypeError",
|
||||
// "URIError"
|
||||
// };
|
||||
//
|
||||
// var jsErrors = Driver.Manage().Logs.GetLog(LogType.Browser).Where(x => errorStrings.Any(e => x.Message.Contains(e)));
|
||||
@ -415,7 +402,7 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
Driver.Navigate().GoToUrl(new Uri(Server.PayTester.ServerUri, relativeUrl));
|
||||
}
|
||||
|
||||
|
||||
public void GoToServer(ServerNavPages navPages = ServerNavPages.Index)
|
||||
{
|
||||
Driver.FindElement(By.Id("ServerSettings")).Click();
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@ -8,7 +7,6 @@ using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Models;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Wallets;
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using BTCPayServer.Views.Manage;
|
||||
@ -78,16 +76,16 @@ namespace BTCPayServer.Tests
|
||||
Assert.Contains("server/services/lndseedbackup/BTC", s.Driver.PageSource);
|
||||
s.Driver.Navigate().GoToUrl(s.Link("/server/services/lndseedbackup/BTC"));
|
||||
s.Driver.FindElement(By.Id("details")).Click();
|
||||
var seedEl = s.Driver.FindElement(By.Id("Seed"));
|
||||
var seedEl = s.Driver.FindElement(By.Id("SeedTextArea"));
|
||||
Assert.True(seedEl.Displayed);
|
||||
Assert.Contains("about over million", seedEl.Text, StringComparison.OrdinalIgnoreCase);
|
||||
var passEl = s.Driver.FindElement(By.Id("WalletPassword"));
|
||||
var passEl = s.Driver.FindElement(By.Id("PasswordInput"));
|
||||
Assert.True(passEl.Displayed);
|
||||
Assert.Contains(passEl.Text, "hellorockstar", StringComparison.OrdinalIgnoreCase);
|
||||
s.Driver.FindElement(By.Id("delete")).Click();
|
||||
s.Driver.FindElement(By.Id("continue")).Click();
|
||||
s.FindAlertMessage();
|
||||
seedEl = s.Driver.FindElement(By.Id("Seed"));
|
||||
seedEl = s.Driver.FindElement(By.Id("SeedTextArea"));
|
||||
Assert.Contains("Seed removed", seedEl.Text, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
@ -214,10 +212,6 @@ namespace BTCPayServer.Tests
|
||||
using (var s = SeleniumTester.Create())
|
||||
{
|
||||
await s.StartAsync();
|
||||
var settings = s.Server.PayTester.GetService<SettingsRepository>();
|
||||
var policies = await settings.GetSettingAsync<PoliciesSettings>() ?? new PoliciesSettings();
|
||||
policies.DisableSSHService = false;
|
||||
await settings.UpdateSetting(policies);
|
||||
s.RegisterNewUser(isAdmin: true);
|
||||
s.Driver.Navigate().GoToUrl(s.Link("/server/services"));
|
||||
Assert.Contains("server/services/ssh", s.Driver.PageSource);
|
||||
@ -246,17 +240,6 @@ namespace BTCPayServer.Tests
|
||||
|
||||
text = s.Driver.FindElement(By.Id("SSHKeyFileContent")).Text;
|
||||
Assert.DoesNotContain("test2", text);
|
||||
|
||||
// Let's try to disable it now
|
||||
s.Driver.FindElement(By.Id("disable")).Click();
|
||||
s.Driver.FindElement(By.Id("continue")).Click();
|
||||
policies = await settings.GetSettingAsync<PoliciesSettings>();
|
||||
Assert.True(policies.DisableSSHService);
|
||||
|
||||
s.Driver.Navigate().GoToUrl(s.Link("/server/services/ssh"));
|
||||
Assert.True(s.Driver.PageSource.Contains("404 - Page not found", StringComparison.OrdinalIgnoreCase));
|
||||
policies.DisableSSHService = false;
|
||||
await settings.UpdateSetting(policies);
|
||||
}
|
||||
}
|
||||
|
||||
@ -366,7 +349,7 @@ namespace BTCPayServer.Tests
|
||||
s.AddLightningNode();
|
||||
s.Driver.AssertNoError();
|
||||
var successAlert = s.FindAlertMessage();
|
||||
Assert.Contains("BTC Lightning node updated.", successAlert.Text);
|
||||
Assert.Contains("BTC Lightning node modified.", successAlert.Text);
|
||||
Assert.False(s.Driver.PageSource.Contains(offchainHint), "Lightning hint should be dismissed at this point");
|
||||
|
||||
var storeUrl = s.Driver.Url;
|
||||
@ -498,9 +481,10 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.Id("Apps")).Click();
|
||||
s.Driver.FindElement(By.Id("CreateNewApp")).Click();
|
||||
s.Driver.FindElement(By.Name("Name")).SendKeys("PoS" + Guid.NewGuid());
|
||||
s.Driver.FindElement(By.Id("SelectedAppType")).SendKeys("Point of Sale");
|
||||
s.Driver.FindElement(By.Id("SelectedAppType")).SendKeys("PointOfSale");
|
||||
s.Driver.FindElement(By.Id("SelectedStore")).SendKeys(storeName);
|
||||
s.Driver.FindElement(By.Id("Create")).Click();
|
||||
s.Driver.FindElement(By.Id("DefaultView")).SendKeys("Cart");
|
||||
s.Driver.FindElement(By.CssSelector(".template-item:nth-of-type(1) .btn-primary")).Click();
|
||||
s.Driver.FindElement(By.Id("BuyButtonText")).SendKeys("Take my money");
|
||||
s.Driver.FindElement(By.Id("SaveItemChanges")).Click();
|
||||
@ -509,7 +493,6 @@ namespace BTCPayServer.Tests
|
||||
var template = s.Driver.FindElement(By.Id("Template")).GetAttribute("value");
|
||||
Assert.Contains("buyButtonText: Take my money", template);
|
||||
|
||||
s.Driver.FindElement(By.Id("DefaultView")).SendKeys("Item list and cart");
|
||||
s.Driver.FindElement(By.Id("SaveSettings")).Click();
|
||||
s.Driver.FindElement(By.Id("ViewApp")).Click();
|
||||
|
||||
@ -574,6 +557,7 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
public async Task CanUseCoinSelection()
|
||||
{
|
||||
@ -610,7 +594,7 @@ namespace BTCPayServer.Tests
|
||||
});
|
||||
await s.Server.ExplorerNode.GenerateAsync(1);
|
||||
s.GoToWallet(walletId);
|
||||
s.Driver.ToggleCollapse("AdvancedSettings");
|
||||
s.Driver.FindElement(By.Id("advancedSettings")).Click();
|
||||
s.Driver.WaitForAndClick(By.Id("toggleInputSelection"));
|
||||
s.Driver.FindElement(By.Id(spentOutpoint.ToString()));
|
||||
Assert.Equal("true", s.Driver.FindElement(By.Name("InputSelection")).GetAttribute("value").ToLowerInvariant());
|
||||
@ -621,7 +605,8 @@ namespace BTCPayServer.Tests
|
||||
|
||||
var bob = new Key().PubKey.Hash.GetAddress(Network.RegTest);
|
||||
SetTransactionOutput(s, 0, bob, 0.3m);
|
||||
s.Driver.FindElement(By.Id("SignTransaction")).Click();
|
||||
s.Driver.FindElement(By.Id("SendMenu")).Click();
|
||||
s.Driver.FindElement(By.Id("spendWithNBxplorer")).Click();
|
||||
s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).Click();
|
||||
var happyElement = s.FindAlertMessage();
|
||||
var happyText = happyElement.Text;
|
||||
@ -641,14 +626,15 @@ namespace BTCPayServer.Tests
|
||||
await s.StartAsync();
|
||||
s.RegisterNewUser(true);
|
||||
var (storeName, storeId) = s.CreateNewStore();
|
||||
s.GoToStore(storeId, StoreNavPages.Webhooks);
|
||||
s.GoToStore(storeId, Views.Stores.StoreNavPages.Webhooks);
|
||||
|
||||
Logs.Tester.LogInformation("Let's create two webhooks");
|
||||
for (var i = 0; i < 2; i++)
|
||||
{
|
||||
s.Driver.FindElement(By.Id("CreateWebhook")).Click();
|
||||
s.Driver.FindElement(By.Name("PayloadUrl")).SendKeys($"http://127.0.0.1/callback{i}");
|
||||
new SelectElement(s.Driver.FindElement(By.Id("Everything"))).SelectByValue("false");
|
||||
new SelectElement(s.Driver.FindElement(By.Name("Everything")))
|
||||
.SelectByValue("false");
|
||||
s.Driver.FindElement(By.Id("InvoiceCreated")).Click();
|
||||
s.Driver.FindElement(By.Id("InvoiceProcessing")).Click();
|
||||
s.Driver.FindElement(By.Name("add")).Click();
|
||||
@ -742,8 +728,9 @@ namespace BTCPayServer.Tests
|
||||
|
||||
Logs.Tester.LogInformation("Let's see if we can delete store with some webhooks inside");
|
||||
s.GoToStore(storeId);
|
||||
|
||||
s.Driver.ToggleCollapse("danger-zone");
|
||||
// Open danger zone via JS, because if we click the link it triggers the toggle animation.
|
||||
// This leads to Selenium trying to click the button while it is moving resulting in an error.
|
||||
s.Driver.ExecuteJavaScript("document.getElementById('danger-zone').classList.add('show')");
|
||||
s.Driver.FindElement(By.Id("delete-store")).Click();
|
||||
s.Driver.FindElement(By.Id("continue")).Click();
|
||||
s.FindAlertMessage();
|
||||
@ -767,7 +754,7 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.Id("Wallets")).Click();
|
||||
s.Driver.FindElement(By.LinkText("Manage")).Click();
|
||||
s.Driver.FindElement(By.Id("WalletSend")).Click();
|
||||
s.Driver.FindElement(By.Id("SignTransaction")).Click();
|
||||
s.Driver.FindElement(By.Id("SendMenu")).Click();
|
||||
|
||||
//you cannot use the Sign with NBX option without saving private keys when generating the wallet.
|
||||
Assert.DoesNotContain("nbx-seed", s.Driver.PageSource);
|
||||
@ -831,11 +818,6 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.LinkText("Manage")).Click();
|
||||
|
||||
s.ClickOnAllSideMenus();
|
||||
|
||||
// Make sure wallet info is correct
|
||||
s.Driver.FindElement(By.Id("WalletSettings")).Click();
|
||||
Assert.Contains(mnemonic.DeriveExtKey().GetPublicKey().GetHDFingerPrint().ToString(), s.Driver.FindElement(By.Id("AccountKeys_0__MasterFingerprint")).GetAttribute("value"));
|
||||
Assert.Contains("m/84'/1'/0'", s.Driver.FindElement(By.Id("AccountKeys_0__AccountKeyPath")).GetAttribute("value"));
|
||||
|
||||
// Make sure we can rescan, because we are admin!
|
||||
s.Driver.FindElement(By.Id("WalletRescan")).Click();
|
||||
@ -843,24 +825,36 @@ namespace BTCPayServer.Tests
|
||||
|
||||
// We setup the fingerprint and the account key path
|
||||
s.Driver.FindElement(By.Id("WalletSettings")).Click();
|
||||
// s.Driver.FindElement(By.Id("AccountKeys_0__MasterFingerprint")).SendKeys("8bafd160");
|
||||
// s.Driver.FindElement(By.Id("AccountKeys_0__AccountKeyPath")).SendKeys("m/49'/0'/0'" + Keys.Enter);
|
||||
|
||||
// Check the tx sent earlier arrived
|
||||
s.Driver.FindElement(By.Id("WalletTransactions")).Click();
|
||||
|
||||
var walletTransactionLink = s.Driver.Url;
|
||||
Assert.Contains(tx.ToString(), s.Driver.PageSource);
|
||||
|
||||
// Send to bob
|
||||
s.Driver.FindElement(By.Id("WalletSend")).Click();
|
||||
var bob = new Key().PubKey.Hash.GetAddress(Network.RegTest);
|
||||
SetTransactionOutput(s, 0, bob, 1);
|
||||
s.Driver.FindElement(By.Id("SignTransaction")).Click();
|
||||
|
||||
// Broadcast
|
||||
Assert.Contains(bob.ToString(), s.Driver.PageSource);
|
||||
Assert.Contains("1.00000000", s.Driver.PageSource);
|
||||
s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).Click();
|
||||
Assert.Equal(walletTransactionLink, s.Driver.Url);
|
||||
|
||||
void SignWith(Mnemonic signingSource)
|
||||
{
|
||||
// Send to bob
|
||||
s.Driver.FindElement(By.Id("WalletSend")).Click();
|
||||
var bob = new Key().PubKey.Hash.GetAddress(Network.RegTest);
|
||||
SetTransactionOutput(s, 0, bob, 1);
|
||||
s.Driver.FindElement(By.Id("SendMenu")).Click();
|
||||
s.Driver.FindElement(By.CssSelector("button[value=seed]")).Click();
|
||||
|
||||
// Input the seed
|
||||
s.Driver.FindElement(By.Id("SeedOrKey")).SendKeys(signingSource + Keys.Enter);
|
||||
|
||||
// Broadcast
|
||||
Assert.Contains(bob.ToString(), s.Driver.PageSource);
|
||||
Assert.Contains("1.00000000", s.Driver.PageSource);
|
||||
s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).Click();
|
||||
Assert.Equal(walletTransactionLink, s.Driver.Url);
|
||||
}
|
||||
|
||||
SignWith(mnemonic);
|
||||
|
||||
s.Driver.FindElement(By.Id("Wallets")).Click();
|
||||
s.Driver.FindElement(By.LinkText("Manage")).Click();
|
||||
@ -868,16 +862,15 @@ namespace BTCPayServer.Tests
|
||||
|
||||
var jack = new Key().PubKey.Hash.GetAddress(Network.RegTest);
|
||||
SetTransactionOutput(s, 0, jack, 0.01m);
|
||||
s.Driver.FindElement(By.Id("SignTransaction")).Click();
|
||||
s.Driver.FindElement(By.Id("SendMenu")).Click();
|
||||
|
||||
s.Driver.FindElement(By.CssSelector("button[value=nbx-seed]")).Click();
|
||||
Assert.Contains(jack.ToString(), s.Driver.PageSource);
|
||||
Assert.Contains("0.01000000", s.Driver.PageSource);
|
||||
s.Driver.FindElement(By.CssSelector("button[value=analyze-psbt]")).Click();
|
||||
Assert.EndsWith("psbt", s.Driver.Url);
|
||||
|
||||
s.Driver.FindElement(By.Id("OtherActionsDropdownToggle")).Click();
|
||||
s.Driver.FindElement(By.CssSelector("#OtherActions")).Click();
|
||||
s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).Click();
|
||||
|
||||
Assert.EndsWith("psbt/ready", s.Driver.Url);
|
||||
s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).Click();
|
||||
Assert.Equal(walletTransactionLink, s.Driver.Url);
|
||||
@ -893,12 +886,13 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.SwitchTo().Alert().SendKeys(bip21);
|
||||
s.Driver.SwitchTo().Alert().Accept();
|
||||
s.FindAlertMessage(StatusMessageModel.StatusSeverity.Info);
|
||||
Assert.Equal(parsedBip21.Amount.ToString(false), s.Driver.FindElement(By.Id("Outputs_0__Amount")).GetAttribute("value"));
|
||||
Assert.Equal(parsedBip21.Address.ToString(), s.Driver.FindElement(By.Id("Outputs_0__DestinationAddress")).GetAttribute("value"));
|
||||
Assert.Equal(parsedBip21.Amount.ToString(false), s.Driver.FindElement(By.Id($"Outputs_0__Amount")).GetAttribute("value"));
|
||||
Assert.Equal(parsedBip21.Address.ToString(), s.Driver.FindElement(By.Id($"Outputs_0__DestinationAddress")).GetAttribute("value"));
|
||||
|
||||
s.GoToWallet(new WalletId(storeId, "BTC"), WalletsNavPages.Settings);
|
||||
var walletUrl = s.Driver.Url;
|
||||
s.Driver.FindElement(By.Id("OtherActionsDropdownToggle")).Click();
|
||||
|
||||
s.Driver.FindElement(By.Id("SettingsMenu")).Click();
|
||||
s.Driver.FindElement(By.CssSelector("button[value=view-seed]")).Click();
|
||||
|
||||
// Seed backup page
|
||||
@ -913,179 +907,116 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
public async Task CanImportWallet()
|
||||
[Fact]
|
||||
[Trait("Selenium", "Selenium")]
|
||||
public async Task CanUsePullPaymentsViaUI()
|
||||
{
|
||||
using (var s = SeleniumTester.Create())
|
||||
{
|
||||
await s.StartAsync();
|
||||
s.RegisterNewUser(true);
|
||||
var (_, storeId) = s.CreateNewStore();
|
||||
var mnemonic = s.GenerateWallet("BTC", "click chunk owner kingdom faint steak safe evidence bicycle repeat bulb wheel");
|
||||
|
||||
// Make sure wallet info is correct
|
||||
s.GoToWallet(new WalletId(storeId, "BTC"), WalletsNavPages.Settings);
|
||||
Assert.Contains(mnemonic.DeriveExtKey().GetPublicKey().GetHDFingerPrint().ToString(), s.Driver.FindElement(By.Id("AccountKeys_0__MasterFingerprint")).GetAttribute("value"));
|
||||
Assert.Contains( "m/84'/1'/0'", s.Driver.FindElement(By.Id("AccountKeys_0__AccountKeyPath")).GetAttribute("value"));
|
||||
}
|
||||
}
|
||||
s.CreateNewStore();
|
||||
s.GenerateWallet("BTC", "", true, true);
|
||||
|
||||
[Fact]
|
||||
[Trait("Selenium", "Selenium")]
|
||||
public async Task CanUsePullPaymentsViaUI()
|
||||
{
|
||||
using var s = SeleniumTester.Create();
|
||||
await s.StartAsync();
|
||||
s.RegisterNewUser(true);
|
||||
s.CreateNewStore();
|
||||
s.GenerateWallet("BTC", "", true, true);
|
||||
await s.Server.ExplorerNode.GenerateAsync(1);
|
||||
await s.FundStoreWallet(denomination: 50.0m);
|
||||
s.GoToWallet(navPages: WalletsNavPages.PullPayments);
|
||||
s.Driver.FindElement(By.Id("NewPullPayment")).Click();
|
||||
s.Driver.FindElement(By.Id("Name")).SendKeys("PP1");
|
||||
s.Driver.FindElement(By.Id("Amount")).Clear();
|
||||
s.Driver.FindElement(By.Id("Amount")).SendKeys("99.0");;
|
||||
s.Driver.FindElement(By.Id("Create")).Click();
|
||||
s.Driver.FindElement(By.LinkText("View")).Click();
|
||||
|
||||
await s.Server.ExplorerNode.GenerateAsync(1);
|
||||
await s.FundStoreWallet(denomination: 50.0m);
|
||||
s.GoToWallet(navPages: WalletsNavPages.PullPayments);
|
||||
s.Driver.FindElement(By.Id("NewPullPayment")).Click();
|
||||
s.Driver.FindElement(By.Id("Name")).SendKeys("PP1");
|
||||
s.Driver.FindElement(By.Id("Amount")).Clear();
|
||||
s.Driver.FindElement(By.Id("Amount")).SendKeys("99.0");;
|
||||
s.Driver.FindElement(By.Id("Create")).Click();
|
||||
s.Driver.FindElement(By.LinkText("View")).Click();
|
||||
s.GoToWallet(navPages: WalletsNavPages.PullPayments);
|
||||
|
||||
s.GoToWallet(navPages: WalletsNavPages.PullPayments);
|
||||
s.Driver.FindElement(By.Id("NewPullPayment")).Click();
|
||||
s.Driver.FindElement(By.Id("Name")).SendKeys("PP2");
|
||||
s.Driver.FindElement(By.Id("Amount")).Clear();
|
||||
s.Driver.FindElement(By.Id("Amount")).SendKeys("100.0");
|
||||
s.Driver.FindElement(By.Id("Create")).Click();
|
||||
|
||||
s.Driver.FindElement(By.Id("NewPullPayment")).Click();
|
||||
s.Driver.FindElement(By.Id("Name")).SendKeys("PP2");
|
||||
s.Driver.FindElement(By.Id("Amount")).Clear();
|
||||
s.Driver.FindElement(By.Id("Amount")).SendKeys("100.0");
|
||||
s.Driver.FindElement(By.Id("Create")).Click();
|
||||
// This should select the first View, ie, the last one PP2
|
||||
s.Driver.FindElement(By.LinkText("View")).Click();
|
||||
var address = await s.Server.ExplorerNode.GetNewAddressAsync();
|
||||
s.Driver.FindElement(By.Id("Destination")).SendKeys(address.ToString());
|
||||
s.Driver.FindElement(By.Id("ClaimedAmount")).Clear();
|
||||
s.Driver.FindElement(By.Id("ClaimedAmount")).SendKeys("15" + Keys.Enter);
|
||||
s.FindAlertMessage();
|
||||
|
||||
// This should select the first View, ie, the last one PP2
|
||||
s.Driver.FindElement(By.LinkText("View")).Click();
|
||||
var address = await s.Server.ExplorerNode.GetNewAddressAsync();
|
||||
s.Driver.FindElement(By.Id("Destination")).SendKeys(address.ToString());
|
||||
s.Driver.FindElement(By.Id("ClaimedAmount")).Clear();
|
||||
s.Driver.FindElement(By.Id("ClaimedAmount")).SendKeys("15" + Keys.Enter);
|
||||
s.FindAlertMessage();
|
||||
// We should not be able to use an address already used
|
||||
s.Driver.FindElement(By.Id("Destination")).SendKeys(address.ToString());
|
||||
s.Driver.FindElement(By.Id("ClaimedAmount")).Clear();
|
||||
s.Driver.FindElement(By.Id("ClaimedAmount")).SendKeys("20" + Keys.Enter);
|
||||
s.FindAlertMessage(StatusMessageModel.StatusSeverity.Error);
|
||||
|
||||
// We should not be able to use an address already used
|
||||
s.Driver.FindElement(By.Id("Destination")).SendKeys(address.ToString());
|
||||
s.Driver.FindElement(By.Id("ClaimedAmount")).Clear();
|
||||
s.Driver.FindElement(By.Id("ClaimedAmount")).SendKeys("20" + Keys.Enter);
|
||||
s.FindAlertMessage(StatusMessageModel.StatusSeverity.Error);
|
||||
address = await s.Server.ExplorerNode.GetNewAddressAsync();
|
||||
s.Driver.FindElement(By.Id("Destination")).Clear();
|
||||
s.Driver.FindElement(By.Id("Destination")).SendKeys(address.ToString());
|
||||
s.Driver.FindElement(By.Id("ClaimedAmount")).Clear();
|
||||
s.Driver.FindElement(By.Id("ClaimedAmount")).SendKeys("20" + Keys.Enter);
|
||||
s.FindAlertMessage();
|
||||
Assert.Contains("Awaiting Approval", s.Driver.PageSource);
|
||||
|
||||
address = await s.Server.ExplorerNode.GetNewAddressAsync();
|
||||
s.Driver.FindElement(By.Id("Destination")).Clear();
|
||||
s.Driver.FindElement(By.Id("Destination")).SendKeys(address.ToString());
|
||||
s.Driver.FindElement(By.Id("ClaimedAmount")).Clear();
|
||||
s.Driver.FindElement(By.Id("ClaimedAmount")).SendKeys("20" + Keys.Enter);
|
||||
s.FindAlertMessage();
|
||||
Assert.Contains("Awaiting Approval", s.Driver.PageSource);
|
||||
var viewPullPaymentUrl = s.Driver.Url;
|
||||
// This one should have nothing
|
||||
s.GoToWallet(navPages: WalletsNavPages.PullPayments);
|
||||
var payouts = s.Driver.FindElements(By.ClassName("pp-payout"));
|
||||
Assert.Equal(2, payouts.Count);
|
||||
payouts[1].Click();
|
||||
Assert.Contains("No payout waiting for approval", s.Driver.PageSource);
|
||||
|
||||
var viewPullPaymentUrl = s.Driver.Url;
|
||||
// This one should have nothing
|
||||
s.GoToWallet(navPages: WalletsNavPages.PullPayments);
|
||||
var payouts = s.Driver.FindElements(By.ClassName("pp-payout"));
|
||||
Assert.Equal(2, payouts.Count);
|
||||
payouts[1].Click();
|
||||
Assert.Empty(s.Driver.FindElements(By.ClassName("payout")));
|
||||
// PP2 should have payouts
|
||||
s.GoToWallet(navPages: WalletsNavPages.PullPayments);
|
||||
payouts = s.Driver.FindElements(By.ClassName("pp-payout"));
|
||||
payouts[0].Click();
|
||||
|
||||
Assert.NotEmpty(s.Driver.FindElements(By.ClassName("payout")));
|
||||
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingApproval}-selectAllCheckbox")).Click();
|
||||
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingApproval}-actions")).Click();
|
||||
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingApproval}-approve-pay")).Click();
|
||||
// PP2 should have payouts
|
||||
s.GoToWallet(navPages: WalletsNavPages.PullPayments);
|
||||
payouts = s.Driver.FindElements(By.ClassName("pp-payout"));
|
||||
payouts[0].Click();
|
||||
Assert.DoesNotContain("No payout waiting for approval", s.Driver.PageSource);
|
||||
s.Driver.FindElement(By.Id("selectAllCheckbox")).Click();
|
||||
s.Driver.FindElement(By.Id("payCommand")).Click();
|
||||
s.Driver.FindElement(By.Id("SendMenu")).Click();
|
||||
s.Driver.FindElement(By.CssSelector("button[value=nbx-seed]")).Click();
|
||||
s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).Click();
|
||||
|
||||
s.Driver.FindElement(By.Id("SignTransaction")).Click();
|
||||
s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).Click();
|
||||
s.FindAlertMessage();
|
||||
s.FindAlertMessage();
|
||||
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
s.Driver.Navigate().Refresh();
|
||||
Assert.Contains("badge transactionLabel", s.Driver.PageSource);
|
||||
});
|
||||
Assert.Equal("payout", s.Driver.FindElement(By.ClassName("transactionLabel")).Text);
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
s.Driver.Navigate().Refresh();
|
||||
Assert.Contains("badge transactionLabel", s.Driver.PageSource);
|
||||
});
|
||||
Assert.Equal("payout", s.Driver.FindElement(By.ClassName("transactionLabel")).Text);
|
||||
|
||||
s.GoToWallet(navPages: WalletsNavPages.Payouts);
|
||||
var x = s.Driver.PageSource;
|
||||
s.Driver.FindElement(By.Id($"{PayoutState.InProgress}-view")).Click();
|
||||
ReadOnlyCollection<IWebElement> txs;
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
s.Driver.Navigate().Refresh();
|
||||
|
||||
s.GoToWallet(navPages: WalletsNavPages.Payouts);
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
s.Driver.Navigate().Refresh();
|
||||
Assert.Contains("No payout waiting for approval", s.Driver.PageSource);
|
||||
});
|
||||
var txs = s.Driver.FindElements(By.ClassName("transaction-link"));
|
||||
Assert.Equal(2, txs.Count);
|
||||
|
||||
s.Driver.Navigate().GoToUrl(viewPullPaymentUrl);
|
||||
txs = s.Driver.FindElements(By.ClassName("transaction-link"));
|
||||
Assert.Equal(2, txs.Count);
|
||||
});
|
||||
Assert.Contains("In Progress", s.Driver.PageSource);
|
||||
|
||||
s.Driver.Navigate().GoToUrl(viewPullPaymentUrl);
|
||||
txs = s.Driver.FindElements(By.ClassName("transaction-link"));
|
||||
Assert.Equal(2, txs.Count);
|
||||
Assert.Contains(PayoutState.InProgress.GetStateString(), s.Driver.PageSource);
|
||||
await s.Server.ExplorerNode.GenerateAsync(1);
|
||||
|
||||
await s.Server.ExplorerNode.GenerateAsync(1);
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
s.Driver.Navigate().Refresh();
|
||||
Assert.Contains("Completed", s.Driver.PageSource);
|
||||
});
|
||||
await s.Server.ExplorerNode.GenerateAsync(10);
|
||||
var pullPaymentId = viewPullPaymentUrl.Split('/').Last();
|
||||
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
s.Driver.Navigate().Refresh();
|
||||
Assert.Contains(PayoutState.Completed.GetStateString(), s.Driver.PageSource);
|
||||
});
|
||||
await s.Server.ExplorerNode.GenerateAsync(10);
|
||||
var pullPaymentId = viewPullPaymentUrl.Split('/').Last();
|
||||
|
||||
await TestUtils.EventuallyAsync(async () =>
|
||||
{
|
||||
using var ctx = s.Server.PayTester.GetService<ApplicationDbContextFactory>().CreateContext();
|
||||
var payoutsData = await ctx.Payouts.Where(p => p.PullPaymentDataId == pullPaymentId).ToListAsync();
|
||||
Assert.True(payoutsData.All(p => p.State == PayoutState.Completed));
|
||||
});
|
||||
s.GoToHome();
|
||||
//offline/external payout test
|
||||
s.Driver.FindElement(By.Id("NotificationsDropdownToggle")).Click();
|
||||
s.Driver.FindElement(By.CssSelector("#notificationsForm button")).Click();
|
||||
|
||||
|
||||
var newStore = s.CreateNewStore();
|
||||
s.GenerateWallet("BTC", "", true, true);
|
||||
var newWalletId = new WalletId(newStore.storeId, "BTC");
|
||||
s.GoToWallet(newWalletId, WalletsNavPages.PullPayments);
|
||||
|
||||
s.Driver.FindElement(By.Id("NewPullPayment")).Click();
|
||||
s.Driver.FindElement(By.Id("Name")).SendKeys("External Test");
|
||||
s.Driver.FindElement(By.Id("Amount")).Clear();
|
||||
s.Driver.FindElement(By.Id("Amount")).SendKeys("0.001");
|
||||
s.Driver.FindElement(By.Id("Currency")).Clear();
|
||||
s.Driver.FindElement(By.Id("Currency")).SendKeys("BTC");
|
||||
s.Driver.FindElement(By.Id("Create")).Click();
|
||||
s.Driver.FindElement(By.LinkText("View")).Click();
|
||||
|
||||
address = await s.Server.ExplorerNode.GetNewAddressAsync();
|
||||
s.Driver.FindElement(By.Id("Destination")).SendKeys(address.ToString());
|
||||
s.Driver.FindElement(By.Id("ClaimedAmount")).SendKeys(Keys.Enter);
|
||||
s.FindAlertMessage();
|
||||
|
||||
Assert.Contains(PayoutState.AwaitingApproval.GetStateString(), s.Driver.PageSource);
|
||||
s.GoToWallet(newWalletId, WalletsNavPages.Payouts);
|
||||
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingApproval}-view")).Click();
|
||||
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingApproval}-selectAllCheckbox")).Click();
|
||||
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingApproval}-actions")).Click();
|
||||
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingApproval}-approve")).Click();
|
||||
s.FindAlertMessage();
|
||||
var tx =await s.Server.ExplorerNode.SendToAddressAsync(address, Money.FromUnit(0.001m, MoneyUnit.BTC));
|
||||
|
||||
s.GoToWallet(newWalletId, WalletsNavPages.Payouts);
|
||||
|
||||
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingPayment}-view")).Click();
|
||||
Assert.Contains(PayoutState.AwaitingPayment.GetStateString(), s.Driver.PageSource);
|
||||
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingPayment}-selectAllCheckbox")).Click();
|
||||
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingPayment}-actions")).Click();
|
||||
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingPayment}-mark-paid")).Click();
|
||||
s.FindAlertMessage();
|
||||
|
||||
s.Driver.FindElement(By.Id("InProgress-view")).Click();
|
||||
Assert.Contains(tx.ToString(), s.Driver.PageSource);
|
||||
await TestUtils.EventuallyAsync(async () =>
|
||||
{
|
||||
using var ctx = s.Server.PayTester.GetService<ApplicationDbContextFactory>().CreateContext();
|
||||
var payoutsData = await ctx.Payouts.Where(p => p.PullPaymentDataId == pullPaymentId).ToListAsync();
|
||||
Assert.True(payoutsData.All(p => p.State == Data.PayoutState.Completed));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void CanBrowseContent(SeleniumTester s)
|
||||
@ -1101,9 +1032,8 @@ namespace BTCPayServer.Tests
|
||||
|
||||
private static void CanSetupEmailCore(SeleniumTester s)
|
||||
{
|
||||
s.Driver.FindElement(By.Id("QuickFillDropdownToggle")).Click();
|
||||
s.Driver.FindElement(By.ClassName("dropdown-toggle")).Click();
|
||||
s.Driver.FindElement(By.ClassName("dropdown-item")).Click();
|
||||
|
||||
s.Driver.FindElement(By.Id("Settings_Login")).SendKeys("test@gmail.com");
|
||||
s.Driver.FindElement(By.CssSelector("button[value=\"Save\"]")).Submit();
|
||||
s.FindAlertMessage();
|
||||
|
@ -10,10 +10,8 @@ using BTCPayServer.Storage.Services.Providers.AzureBlobStorage.Configuration;
|
||||
using BTCPayServer.Storage.Services.Providers.FileSystemStorage.Configuration;
|
||||
using BTCPayServer.Storage.ViewModels;
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Newtonsoft.Json;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
@ -182,27 +180,23 @@ namespace BTCPayServer.Tests
|
||||
private async Task CanUploadRemoveFiles(ServerController controller)
|
||||
{
|
||||
var fileContent = "content";
|
||||
List<IFormFile> fileList = new List<IFormFile>();
|
||||
fileList.Add(TestUtils.GetFormFile("uploadtestfile1.txt", fileContent));
|
||||
|
||||
var uploadFormFileResult = Assert.IsType<RedirectToActionResult>(await controller.CreateFiles(fileList));
|
||||
Assert.True(uploadFormFileResult.RouteValues.ContainsKey("fileIds"));
|
||||
string[] uploadFileList = (string[])uploadFormFileResult.RouteValues["fileIds"];
|
||||
var fileId = uploadFileList[0];
|
||||
var uploadFormFileResult = Assert.IsType<RedirectToActionResult>(await controller.CreateFile(TestUtils.GetFormFile("uploadtestfile.txt", fileContent)));
|
||||
Assert.True(uploadFormFileResult.RouteValues.ContainsKey("fileId"));
|
||||
var fileId = uploadFormFileResult.RouteValues["fileId"].ToString();
|
||||
Assert.Equal("Files", uploadFormFileResult.ActionName);
|
||||
|
||||
//check if file was uploaded and saved in db
|
||||
var viewFilesViewModel =
|
||||
Assert.IsType<ViewFilesViewModel>(Assert.IsType<ViewResult>(await controller.Files(new string[] { fileId })).Model);
|
||||
Assert.IsType<ViewFilesViewModel>(Assert.IsType<ViewResult>(await controller.Files(fileId)).Model);
|
||||
|
||||
Assert.NotEmpty(viewFilesViewModel.Files);
|
||||
Assert.True(viewFilesViewModel.DirectUrlByFiles.ContainsKey(fileId));
|
||||
Assert.NotEmpty(viewFilesViewModel.DirectUrlByFiles[fileId]);
|
||||
Assert.Equal(fileId, viewFilesViewModel.SelectedFileId);
|
||||
Assert.NotEmpty(viewFilesViewModel.DirectFileUrl);
|
||||
|
||||
|
||||
//verify file is available and the same
|
||||
var net = new System.Net.WebClient();
|
||||
var data = await net.DownloadStringTaskAsync(new Uri(viewFilesViewModel.DirectUrlByFiles[fileId]));
|
||||
var data = await net.DownloadStringTaskAsync(new Uri(viewFilesViewModel.DirectFileUrl));
|
||||
Assert.Equal(fileContent, data);
|
||||
|
||||
//create a temporary link to file
|
||||
@ -234,8 +228,10 @@ namespace BTCPayServer.Tests
|
||||
|
||||
//attempt to fetch deleted file
|
||||
viewFilesViewModel =
|
||||
Assert.IsType<ViewFilesViewModel>(Assert.IsType<ViewResult>(await controller.Files(new string[] { fileId })).Model);
|
||||
Assert.Null(viewFilesViewModel.DirectUrlByFiles);
|
||||
Assert.IsType<ViewFilesViewModel>(Assert.IsType<ViewResult>(await controller.Files(fileId)).Model);
|
||||
|
||||
Assert.Null(viewFilesViewModel.DirectFileUrl);
|
||||
Assert.Null(viewFilesViewModel.SelectedFileId);
|
||||
}
|
||||
|
||||
|
||||
|
@ -125,22 +125,28 @@ namespace BTCPayServer.Tests
|
||||
CreateStoreAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public async Task SetNetworkFeeMode(NetworkFeeMode mode)
|
||||
public void SetNetworkFeeMode(NetworkFeeMode mode)
|
||||
{
|
||||
await ModifyStore(store =>
|
||||
ModifyStore((store) =>
|
||||
{
|
||||
store.NetworkFeeMode = mode;
|
||||
});
|
||||
}
|
||||
|
||||
public async Task ModifyStore(Action<StoreViewModel> modify)
|
||||
public void ModifyStore(Action<StoreViewModel> modify)
|
||||
{
|
||||
var storeController = GetController<StoresController>();
|
||||
var response = await storeController.UpdateStore();
|
||||
StoreViewModel store = (StoreViewModel)((ViewResult)response).Model;
|
||||
StoreViewModel store = (StoreViewModel)((ViewResult)storeController.UpdateStore()).Model;
|
||||
modify(store);
|
||||
storeController.UpdateStore(store).GetAwaiter().GetResult();
|
||||
}
|
||||
public Task ModifyStoreAsync(Action<StoreViewModel> modify)
|
||||
{
|
||||
var storeController = GetController<StoresController>();
|
||||
StoreViewModel store = (StoreViewModel)((ViewResult)storeController.UpdateStore()).Model;
|
||||
modify(store);
|
||||
return storeController.UpdateStore(store);
|
||||
}
|
||||
|
||||
public T GetController<T>(bool setImplicitStore = true) where T : Controller
|
||||
{
|
||||
@ -173,24 +179,36 @@ namespace BTCPayServer.Tests
|
||||
if (StoreId is null)
|
||||
await CreateStoreAsync();
|
||||
SupportedNetwork = parent.NetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
|
||||
var store = parent.PayTester.GetController<StoresController>(UserId, StoreId, true);
|
||||
|
||||
var generateRequest = new WalletSetupRequest
|
||||
var store = parent.PayTester.GetController<StoresController>(UserId, StoreId);
|
||||
GenerateWalletResponseV = await parent.ExplorerClient.GenerateWalletAsync(new GenerateWalletRequest()
|
||||
{
|
||||
ScriptPubKeyType = segwit,
|
||||
SavePrivateKeys = importKeysToNBX,
|
||||
ImportKeysToRPC = importsKeysToBitcoinCore
|
||||
};
|
||||
|
||||
await store.GenerateWallet(StoreId, cryptoCode, WalletSetupMethod.HotWallet, generateRequest);
|
||||
Assert.NotNull(store.GenerateWalletResponse);
|
||||
GenerateWalletResponseV = store.GenerateWalletResponse;
|
||||
});
|
||||
await store.UpdateWallet(
|
||||
new WalletSetupViewModel
|
||||
{
|
||||
StoreId = StoreId,
|
||||
Method = importKeysToNBX ? WalletSetupMethod.HotWallet : WalletSetupMethod.WatchOnly,
|
||||
Enabled = true,
|
||||
CryptoCode = cryptoCode,
|
||||
Network = SupportedNetwork,
|
||||
RootFingerprint = GenerateWalletResponseV.AccountKeyPath.MasterFingerprint.ToString(),
|
||||
RootKeyPath = SupportedNetwork.GetRootKeyPath(),
|
||||
Source = "NBXplorer",
|
||||
AccountKey = GenerateWalletResponseV.AccountHDKey.Neuter().ToWif(),
|
||||
DerivationSchemeFormat = "BTCPay",
|
||||
KeyPath = GenerateWalletResponseV.AccountKeyPath.KeyPath.ToString(),
|
||||
DerivationScheme = DerivationScheme.ToString(),
|
||||
Confirmation = true
|
||||
});
|
||||
return new WalletId(StoreId, cryptoCode);
|
||||
}
|
||||
|
||||
public Task EnablePayJoin()
|
||||
{
|
||||
return ModifyStore(s => s.PayJoinEnabled = true);
|
||||
return ModifyStoreAsync(s => s.PayJoinEnabled = true);
|
||||
}
|
||||
|
||||
public GenerateWalletResponse GenerateWalletResponseV { get; set; }
|
||||
@ -255,8 +273,8 @@ namespace BTCPayServer.Tests
|
||||
var connectionString = parent.GetLightningConnectionString(connectionType, isMerchant);
|
||||
var nodeType = connectionString == LightningSupportedPaymentMethod.InternalNode ? LightningNodeType.Internal : LightningNodeType.Custom;
|
||||
|
||||
await storeController.SetupLightningNode(storeId ?? StoreId,
|
||||
new LightningNodeViewModel { ConnectionString = connectionString, LightningNodeType = nodeType, SkipPortTest = true }, "save", cryptoCode);
|
||||
await storeController.AddLightningNode(storeId ?? StoreId,
|
||||
new LightningNodeViewModel { ConnectionString = connectionString, LightningNodeType = nodeType, SkipPortTest = true }, "save", "BTC");
|
||||
if (storeController.ModelState.ErrorCount != 0)
|
||||
Assert.False(true, storeController.ModelState.FirstOrDefault().Value.Errors[0].ErrorMessage);
|
||||
}
|
||||
|
132
BTCPayServer.Tests/U2FTests.cs
Normal file
132
BTCPayServer.Tests/U2FTests.cs
Normal file
@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Controllers;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Models.AccountViewModels;
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using BTCPayServer.U2F;
|
||||
using BTCPayServer.U2F.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using U2F.Core.Models;
|
||||
using U2F.Core.Utils;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
{
|
||||
public class U2FTests
|
||||
{
|
||||
public const int TestTimeout = 60_000;
|
||||
|
||||
public U2FTests(ITestOutputHelper helper)
|
||||
{
|
||||
Logs.Tester = new XUnitLog(helper) { Name = "Tests" };
|
||||
Logs.LogProvider = new XUnitLogProvider(helper);
|
||||
}
|
||||
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task U2ftest()
|
||||
{
|
||||
using (var tester = ServerTester.Create())
|
||||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
|
||||
var accountController = tester.PayTester.GetController<AccountController>();
|
||||
var manageController = user.GetController<ManageController>();
|
||||
var mock = new MockU2FService(tester.PayTester.GetService<ApplicationDbContextFactory>());
|
||||
manageController._u2FService = mock;
|
||||
accountController._u2FService = mock;
|
||||
|
||||
Assert
|
||||
.IsType<RedirectToActionResult>(await accountController.Login(new LoginViewModel()
|
||||
{
|
||||
Email = user.RegisterDetails.Email,
|
||||
Password = user.RegisterDetails.Password
|
||||
}));
|
||||
|
||||
Assert.Empty(Assert.IsType<U2FAuthenticationViewModel>(Assert
|
||||
.IsType<ViewResult>(await manageController.U2FAuthentication()).Model).Devices);
|
||||
|
||||
var addDeviceVM = Assert.IsType<AddU2FDeviceViewModel>(Assert
|
||||
.IsType<ViewResult>(manageController.AddU2FDevice("testdevice")).Model);
|
||||
|
||||
Assert.NotEmpty(addDeviceVM.Challenge);
|
||||
Assert.Equal("testdevice", addDeviceVM.Name);
|
||||
Assert.NotEmpty(addDeviceVM.Version);
|
||||
Assert.Null(addDeviceVM.DeviceResponse);
|
||||
|
||||
var devReg = new DeviceRegistration(Guid.NewGuid().ToByteArray(), Guid.NewGuid().ToByteArray(),
|
||||
Guid.NewGuid().ToByteArray(), 1);
|
||||
|
||||
mock.GetDevReg = () => devReg;
|
||||
mock.StartedAuthentication = () =>
|
||||
new StartedAuthentication("chocolate", addDeviceVM.AppId,
|
||||
devReg.KeyHandle.ByteArrayToBase64String());
|
||||
addDeviceVM.DeviceResponse = new RegisterResponse("ss",
|
||||
Convert.ToBase64String(Encoding.UTF8.GetBytes("{typ:'x', challenge: 'fff'}"))).ToJson();
|
||||
Assert
|
||||
.IsType<RedirectToActionResult>(await manageController.AddU2FDevice(addDeviceVM));
|
||||
|
||||
Assert.Single(Assert.IsType<U2FAuthenticationViewModel>(Assert
|
||||
.IsType<ViewResult>(await manageController.U2FAuthentication()).Model).Devices);
|
||||
|
||||
var secondaryLoginViewModel = Assert.IsType<SecondaryLoginViewModel>(Assert
|
||||
.IsType<ViewResult>(await accountController.Login(new LoginViewModel()
|
||||
{
|
||||
Email = user.RegisterDetails.Email,
|
||||
Password = user.RegisterDetails.Password
|
||||
})).Model);
|
||||
Assert.NotNull(secondaryLoginViewModel.LoginWithU2FViewModel);
|
||||
Assert.Single(secondaryLoginViewModel.LoginWithU2FViewModel.Challenges);
|
||||
Assert.Equal(secondaryLoginViewModel.LoginWithU2FViewModel.Challenge,
|
||||
secondaryLoginViewModel.LoginWithU2FViewModel.Challenges.First().challenge);
|
||||
|
||||
secondaryLoginViewModel.LoginWithU2FViewModel.DeviceResponse = new AuthenticateResponse(
|
||||
Convert.ToBase64String(Encoding.UTF8.GetBytes(
|
||||
"{typ:'x', challenge: '" + secondaryLoginViewModel.LoginWithU2FViewModel.Challenge + "'}")),
|
||||
"dd", devReg.KeyHandle.ByteArrayToBase64String()).ToJson();
|
||||
Assert
|
||||
.IsType<RedirectToActionResult>(
|
||||
await accountController.LoginWithU2F(secondaryLoginViewModel.LoginWithU2FViewModel));
|
||||
}
|
||||
}
|
||||
|
||||
public class MockU2FService : U2FService
|
||||
{
|
||||
public Func<DeviceRegistration> GetDevReg;
|
||||
public Func<StartedAuthentication> StartedAuthentication;
|
||||
|
||||
public MockU2FService(ApplicationDbContextFactory contextFactory) : base(contextFactory)
|
||||
{
|
||||
}
|
||||
|
||||
protected override StartedRegistration StartDeviceRegistrationCore(string appId)
|
||||
{
|
||||
return global::U2F.Core.Crypto.U2F.StartRegistration(appId);
|
||||
}
|
||||
|
||||
protected override DeviceRegistration FinishRegistrationCore(StartedRegistration startedRegistration,
|
||||
RegisterResponse registerResponse)
|
||||
{
|
||||
return GetDevReg();
|
||||
}
|
||||
|
||||
protected override StartedAuthentication StartAuthenticationCore(string appId, U2FDevice registeredDevice)
|
||||
{
|
||||
return StartedAuthentication();
|
||||
}
|
||||
|
||||
protected override void FinishAuthenticationCore(StartedAuthentication authentication,
|
||||
AuthenticateResponse authenticateResponse, DeviceRegistration registration)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -19,7 +19,7 @@ services:
|
||||
TESTS_DB: "Postgres"
|
||||
TESTS_POSTGRES: User ID=postgres;Host=postgres;Port=5432;Database=btcpayserver
|
||||
TESTS_HOSTNAME: tests
|
||||
TESTS_RUN_EXTERNAL_INTEGRATION: ${TESTS_RUN_EXTERNAL_INTEGRATION:-"false"}
|
||||
TESTS_RUN_EXTERNAL_INTEGRATION: ${TESTS_RUN_EXTERNAL_INTEGRATION:-false}
|
||||
TESTS_AzureBlobStorageConnectionString: ${TESTS_AzureBlobStorageConnectionString:-none}
|
||||
TEST_MERCHANTLIGHTNINGD: "type=clightning;server=unix://etc/merchant_lightningd_datadir/lightning-rpc"
|
||||
TEST_CUSTOMERLIGHTNINGD: "type=clightning;server=unix://etc/customer_lightningd_datadir/lightning-rpc"
|
||||
@ -69,7 +69,7 @@ services:
|
||||
- "sshd_datadir:/root/.ssh"
|
||||
|
||||
devlnd:
|
||||
image: btcpayserver/bitcoin:0.21.1
|
||||
image: btcpayserver/bitcoin:0.21.0
|
||||
environment:
|
||||
BITCOIN_NETWORK: regtest
|
||||
BITCOIN_WALLETDIR: "/data/wallets"
|
||||
@ -84,7 +84,7 @@ services:
|
||||
- customer_lnd
|
||||
- merchant_lnd
|
||||
nbxplorer:
|
||||
image: nicolasdorier/nbxplorer:2.1.56
|
||||
image: nicolasdorier/nbxplorer:2.1.49
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "32838:32838"
|
||||
@ -118,7 +118,7 @@ services:
|
||||
|
||||
bitcoind:
|
||||
restart: unless-stopped
|
||||
image: btcpayserver/bitcoin:0.21.1
|
||||
image: btcpayserver/bitcoin:0.21.0
|
||||
environment:
|
||||
BITCOIN_NETWORK: regtest
|
||||
BITCOIN_WALLETDIR: "/data/wallets"
|
||||
@ -146,7 +146,7 @@ services:
|
||||
- "bitcoin_datadir:/data"
|
||||
|
||||
customer_lightningd:
|
||||
image: btcpayserver/lightning:v0.10.1-dev
|
||||
image: btcpayserver/lightning:v0.9.3-1-dev
|
||||
stop_signal: SIGKILL
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
@ -195,7 +195,7 @@ services:
|
||||
- merchant_lightningd
|
||||
|
||||
merchant_lightningd:
|
||||
image: btcpayserver/lightning:v0.10.1-dev
|
||||
image: btcpayserver/lightning:v0.9.3-1-dev
|
||||
stop_signal: SIGKILL
|
||||
environment:
|
||||
EXPOSE_TCP: "true"
|
||||
@ -227,7 +227,7 @@ services:
|
||||
- "5432"
|
||||
|
||||
merchant_lnd:
|
||||
image: btcpayserver/lnd:v0.13.1-beta-withloop
|
||||
image: btcpayserver/lnd:v0.12.1-beta
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
LND_CHAIN: "btc"
|
||||
@ -261,7 +261,7 @@ services:
|
||||
- bitcoind
|
||||
|
||||
customer_lnd:
|
||||
image: btcpayserver/lnd:v0.13.1-beta-withloop
|
||||
image: btcpayserver/lnd:v0.12.1-beta
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
LND_CHAIN: "btc"
|
||||
|
@ -17,7 +17,7 @@ services:
|
||||
TESTS_DB: "Postgres"
|
||||
TESTS_POSTGRES: User ID=postgres;Host=postgres;Port=5432;Database=btcpayserver
|
||||
TESTS_HOSTNAME: tests
|
||||
TESTS_RUN_EXTERNAL_INTEGRATION: ${TESTS_RUN_EXTERNAL_INTEGRATION:-"false"}
|
||||
TESTS_RUN_EXTERNAL_INTEGRATION: ${TESTS_RUN_EXTERNAL_INTEGRATION:-false}
|
||||
TESTS_AzureBlobStorageConnectionString: ${TESTS_AzureBlobStorageConnectionString:-none}
|
||||
TEST_MERCHANTLIGHTNINGD: "type=clightning;server=unix://etc/merchant_lightningd_datadir/lightning-rpc"
|
||||
TEST_CUSTOMERLIGHTNINGD: "type=clightning;server=unix://etc/customer_lightningd_datadir/lightning-rpc"
|
||||
@ -66,7 +66,7 @@ services:
|
||||
- "sshd_datadir:/root/.ssh"
|
||||
|
||||
devlnd:
|
||||
image: btcpayserver/bitcoin:0.21.1
|
||||
image: btcpayserver/bitcoin:0.21.0
|
||||
environment:
|
||||
BITCOIN_NETWORK: regtest
|
||||
BITCOIN_WALLETDIR: "/data/wallets"
|
||||
@ -81,7 +81,7 @@ services:
|
||||
- customer_lnd
|
||||
- merchant_lnd
|
||||
nbxplorer:
|
||||
image: nicolasdorier/nbxplorer:2.1.56
|
||||
image: nicolasdorier/nbxplorer:2.1.49
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "32838:32838"
|
||||
@ -105,7 +105,7 @@ services:
|
||||
|
||||
bitcoind:
|
||||
restart: unless-stopped
|
||||
image: btcpayserver/bitcoin:0.21.1
|
||||
image: btcpayserver/bitcoin:0.21.0
|
||||
environment:
|
||||
BITCOIN_NETWORK: regtest
|
||||
BITCOIN_WALLETDIR: "/data/wallets"
|
||||
@ -133,7 +133,7 @@ services:
|
||||
- "bitcoin_datadir:/data"
|
||||
|
||||
customer_lightningd:
|
||||
image: btcpayserver/lightning:v0.10.1-dev
|
||||
image: btcpayserver/lightning:v0.9.3-1-dev
|
||||
stop_signal: SIGKILL
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
@ -182,7 +182,7 @@ services:
|
||||
- merchant_lightningd
|
||||
|
||||
merchant_lightningd:
|
||||
image: btcpayserver/lightning:v0.10.1-dev
|
||||
image: btcpayserver/lightning:v0.9.3-1-dev
|
||||
stop_signal: SIGKILL
|
||||
environment:
|
||||
EXPOSE_TCP: "true"
|
||||
@ -215,7 +215,7 @@ services:
|
||||
- "5432"
|
||||
|
||||
merchant_lnd:
|
||||
image: btcpayserver/lnd:v0.13.1-beta-withloop
|
||||
image: btcpayserver/lnd:v0.12.1-beta
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
LND_CHAIN: "btc"
|
||||
@ -249,7 +249,7 @@ services:
|
||||
- bitcoind
|
||||
|
||||
customer_lnd:
|
||||
image: btcpayserver/lnd:v0.13.1-beta-withloop
|
||||
image: btcpayserver/lnd:v0.12.1-beta
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
LND_CHAIN: "btc"
|
||||
|
@ -45,14 +45,12 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BIP78.Sender" Version="0.2.0" />
|
||||
<PackageReference Include="BTCPayServer.Hwi" Version="2.0.1" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.All" Version="1.2.10" />
|
||||
<PackageReference Include="BuildBundlerMinifier" Version="3.2.449" />
|
||||
<PackageReference Include="BTCPayServer.Hwi" Version="1.1.3" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.All" Version="1.2.7" />
|
||||
<PackageReference Include="BuildBundlerMinifier" Version="3.2.435" />
|
||||
<PackageReference Include="BundlerMinifier.Core" Version="3.2.435" />
|
||||
<PackageReference Include="BundlerMinifier.TagHelpers" Version="3.2.435" />
|
||||
<PackageReference Include="CsvHelper" Version="15.0.5" />
|
||||
<PackageReference Include="Fido2" Version="2.0.1" />
|
||||
<PackageReference Include="Fido2.AspNet" Version="2.0.1" />
|
||||
<PackageReference Include="HtmlSanitizer" Version="5.0.372" />
|
||||
<PackageReference Include="McMaster.NETCore.Plugins.Mvc" Version="1.3.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.2" />
|
||||
@ -84,12 +82,14 @@
|
||||
<PackageReference Include="TwentyTwenty.Storage.Azure" Version="2.12.1" />
|
||||
<PackageReference Include="TwentyTwenty.Storage.Google" Version="2.12.1" />
|
||||
<PackageReference Include="TwentyTwenty.Storage.Local" Version="2.12.1" />
|
||||
<PackageReference Include="U2F.Core" Version="1.0.4" />
|
||||
<PackageReference Include="YamlDotNet" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.1.1" Condition="'$(RazorCompileOnBuild)' != 'true'" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="wwwroot\main\bootstrap4-creativestart\creative.js" />
|
||||
<None Include="wwwroot\vendor\font-awesome\fonts\fontawesome-webfont.svg" />
|
||||
<None Include="wwwroot\vendor\font-awesome\fonts\fontawesome-webfont.woff2" />
|
||||
<None Include="wwwroot\vendor\font-awesome\less\animated.less" />
|
||||
@ -125,13 +125,20 @@
|
||||
<None Include="wwwroot\vendor\jquery-easing\jquery.easing.min.js" />
|
||||
<None Include="wwwroot\vendor\jquery\jquery.js" />
|
||||
<None Include="wwwroot\vendor\jquery\jquery.min.js" />
|
||||
<None Include="wwwroot\vendor\magnific-popup\jquery.magnific-popup.js" />
|
||||
<None Include="wwwroot\vendor\magnific-popup\jquery.magnific-popup.min.js" />
|
||||
<None Include="wwwroot\vendor\popper\popper.js" />
|
||||
<None Include="wwwroot\vendor\popper\popper.min.js" />
|
||||
<None Include="wwwroot\vendor\scrollreveal\scrollreveal.js" />
|
||||
<None Include="wwwroot\vendor\scrollreveal\scrollreveal.min.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="wwwroot\vendor\bootstrap" />
|
||||
<Folder Include="Views\Stores\Integrations" />
|
||||
<Folder Include="wwwroot\vendor\clipboard.js\" />
|
||||
<Folder Include="wwwroot\vendor\highlightjs\" />
|
||||
<Folder Include="wwwroot\vendor\summernote" />
|
||||
<Folder Include="wwwroot\vendor\u2f" />
|
||||
<Folder Include="wwwroot\vendor\vue-qrcode-reader" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -1,42 +1,38 @@
|
||||
@inject LinkGenerator linkGenerator
|
||||
@inject UserManager<ApplicationUser> UserManager
|
||||
@inject ISettingsRepository SettingsRepository
|
||||
@inject CssThemeManager CssThemeManager
|
||||
@using BTCPayServer.HostedServices
|
||||
@using BTCPayServer.Views.Notifications
|
||||
@using BTCPayServer.Abstractions.Extensions
|
||||
@using BTCPayServer.Abstractions.Contracts
|
||||
@using BTCPayServer.Client
|
||||
@using BTCPayServer.Services
|
||||
@using Microsoft.AspNetCore.Http.Extensions
|
||||
@model BTCPayServer.Components.NotificationsDropdown.NotificationSummaryViewModel
|
||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
|
||||
@if (Model.UnseenCount > 0)
|
||||
{
|
||||
<li class="nav-item dropdown" id="notifications-nav-item">
|
||||
<a class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(NotificationsNavPages))" href="#" id="NotificationsDropdownToggle" role="button" data-bs-toggle="dropdown">
|
||||
<span class="d-inline-block d-lg-none">Notifications</span>
|
||||
<i class="fa fa-bell d-lg-inline-block d-none"></i>
|
||||
<span class="notification-badge badge rounded-pill bg-danger">@Model.UnseenCount</span>
|
||||
<a class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(NotificationsNavPages))" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" id="Notifications">
|
||||
<span class="d-lg-none d-sm-block">Notifications</span><i class="fa fa-bell d-lg-inline-block d-none"></i>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-end text-center notification-dropdown" aria-labelledby="NotificationsDropdownToggle">
|
||||
<span class="alerts-badge badge badge-pill badge-danger">@Model.UnseenCount</span>
|
||||
<div class="dropdown-menu dropdown-menu-right text-center notification-dropdown" aria-labelledby="navbarDropdown">
|
||||
<div class="d-flex align-items-center justify-content-between py-3 px-4 border-bottom border-light">
|
||||
<h5 class="m-0">Notifications</h5>
|
||||
<form id="notificationsForm" asp-controller="Notifications" asp-action="MarkAllAsSeen" asp-route-returnUrl="@Context.Request.GetCurrentPathWithQueryString()" method="post">
|
||||
<form asp-controller="Notifications" asp-action="MarkAllAsSeen" asp-route-returnUrl="@Context.Request.GetCurrentPathWithQueryString()" method="post">
|
||||
<button class="btn btn-link p-0" type="submit">Mark all as seen</button>
|
||||
</form>
|
||||
</div>
|
||||
@foreach (var notif in Model.Last5)
|
||||
{
|
||||
<a asp-action="NotificationPassThrough" asp-controller="Notifications" asp-route-id="@notif.Id" class="notification d-flex align-items-center dropdown-item border-bottom border-light py-3 px-4">
|
||||
<div class="me-3">
|
||||
<div class="mr-3">
|
||||
<vc:icon symbol="note" />
|
||||
</div>
|
||||
|
||||
<div class="notification-item__content">
|
||||
<div class="text-start text-wrap">
|
||||
<div class="text-left text-wrap">
|
||||
@notif.Body
|
||||
</div>
|
||||
<div class="text-start d-flex">
|
||||
<div class="text-left d-flex">
|
||||
<small class="text-muted" data-timeago-unixms="@notif.Created.ToUnixTimeMilliseconds()">@notif.Created.ToTimeAgo()</small>
|
||||
</div>
|
||||
</div>
|
||||
@ -57,11 +53,11 @@ else
|
||||
</li>
|
||||
}
|
||||
@{
|
||||
var disabled = (await SettingsRepository.GetPolicies()).DisableInstantNotifications;
|
||||
var disabled = CssThemeManager.Policies.DisableInstantNotifications;
|
||||
if (!disabled)
|
||||
{
|
||||
var user = await UserManager.GetUserAsync(User);
|
||||
disabled = user?.DisabledNotifications == "all";
|
||||
disabled = user.DisabledNotifications == "all";
|
||||
}
|
||||
}
|
||||
@if (!disabled)
|
||||
|
@ -9,7 +9,7 @@
|
||||
<nav aria-label="..." class="w-100">
|
||||
@if (Model.Total > Model.Count)
|
||||
{
|
||||
<ul class="pagination float-start">
|
||||
<ul class="pagination float-left">
|
||||
<li class="page-item @(Model.Skip == 0 ? "disabled" : null)">
|
||||
<a class="page-link" tabindex="-1" href="@NavigatePages(-1, Model.Count)">«</a>
|
||||
</li>
|
||||
@ -35,7 +35,7 @@
|
||||
|
||||
@if (Model.Total >= pageSizeOptions.Min())
|
||||
{
|
||||
<ul class="pagination float-end">
|
||||
<ul class="pagination float-right">
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link">Page Size:</span>
|
||||
</li>
|
||||
|
@ -69,12 +69,7 @@ namespace BTCPayServer.Configuration
|
||||
BundleJsCss = conf.GetOrDefault<bool>("bundlejscss", true);
|
||||
DockerDeployment = conf.GetOrDefault<bool>("dockerdeployment", true);
|
||||
AllowAdminRegistration = conf.GetOrDefault<bool>("allow-admin-registration", false);
|
||||
|
||||
TorrcFile = conf.GetOrDefault<string>("torrcfile", null);
|
||||
TorServices = conf.GetOrDefault<string>("torservices", null)
|
||||
?.Split(new[] {';', ','}, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (!string.IsNullOrEmpty(TorrcFile) && TorServices != null)
|
||||
throw new ConfigException($"torrcfile or torservices should be provided, but not both");
|
||||
|
||||
var socksEndpointString = conf.GetOrDefault<string>("socksendpoint", null);
|
||||
if (!string.IsNullOrEmpty(socksEndpointString))
|
||||
@ -200,7 +195,6 @@ namespace BTCPayServer.Configuration
|
||||
set;
|
||||
}
|
||||
public string TorrcFile { get; set; }
|
||||
public string[] TorServices { get; set; }
|
||||
public Uri UpdateUrl { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,6 @@ namespace BTCPayServer.Configuration
|
||||
app.Option("--sshauthorizedkeys", "Path to a authorized_keys file that BTCPayServer can modify from the website (default: empty)", CommandOptionType.SingleValue);
|
||||
app.Option("--sshtrustedfingerprints", "SSH Host public key fingerprint or sha256 (default: empty, it will allow untrusted connections)", CommandOptionType.SingleValue);
|
||||
app.Option("--torrcfile", "Path to torrc file containing hidden services directories (default: empty)", CommandOptionType.SingleValue);
|
||||
app.Option("--torservices", "Tor hostnames of available services added to Server Settings (and sets onion header for btcpay). Format: btcpayserver:host.onion:80;btc-p2p:host2.onion:81,BTC-RPC:host3.onion:82,UNKNOWN:host4.onion:83. (default: empty)", CommandOptionType.SingleValue);
|
||||
app.Option("--socksendpoint", "Socks endpoint to connect to onion urls (default: empty)", CommandOptionType.SingleValue);
|
||||
app.Option("--updateurl", $"Url used for once a day new release version check. Check performed only if value is not empty (default: empty)", CommandOptionType.SingleValue);
|
||||
app.Option("--debuglog", "A rolling log file for debug messages.", CommandOptionType.SingleValue);
|
||||
|
@ -1,24 +1,26 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Security.Policy;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Abstractions.Models;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Events;
|
||||
using BTCPayServer.Fido2;
|
||||
using BTCPayServer.Fido2.Models;
|
||||
using BTCPayServer.Logging;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Models.AccountViewModels;
|
||||
using BTCPayServer.Security;
|
||||
using BTCPayServer.Services;
|
||||
using Fido2NetLib;
|
||||
using BTCPayServer.U2F;
|
||||
using BTCPayServer.U2F.Models;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NicolasDorier.RateLimits;
|
||||
using U2F.Core.Exceptions;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
@ -32,7 +34,8 @@ namespace BTCPayServer.Controllers
|
||||
readonly SettingsRepository _SettingsRepository;
|
||||
readonly Configuration.BTCPayServerOptions _Options;
|
||||
private readonly BTCPayServerEnvironment _btcPayServerEnvironment;
|
||||
private readonly Fido2Service _fido2Service;
|
||||
public U2FService _u2FService;
|
||||
private readonly RateLimitService _rateLimitService;
|
||||
private readonly EventAggregator _eventAggregator;
|
||||
readonly ILogger _logger;
|
||||
|
||||
@ -43,8 +46,9 @@ namespace BTCPayServer.Controllers
|
||||
SettingsRepository settingsRepository,
|
||||
Configuration.BTCPayServerOptions options,
|
||||
BTCPayServerEnvironment btcPayServerEnvironment,
|
||||
EventAggregator eventAggregator,
|
||||
Fido2Service fido2Service)
|
||||
U2FService u2FService,
|
||||
RateLimitService rateLimitService,
|
||||
EventAggregator eventAggregator)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
@ -52,7 +56,8 @@ namespace BTCPayServer.Controllers
|
||||
_SettingsRepository = settingsRepository;
|
||||
_Options = options;
|
||||
_btcPayServerEnvironment = btcPayServerEnvironment;
|
||||
_fido2Service = fido2Service;
|
||||
_u2FService = u2FService;
|
||||
_rateLimitService = rateLimitService;
|
||||
_eventAggregator = eventAggregator;
|
||||
_logger = Logs.PayServer;
|
||||
}
|
||||
@ -120,8 +125,7 @@ namespace BTCPayServer.Controllers
|
||||
return View(model);
|
||||
}
|
||||
|
||||
var fido2Devices = await _fido2Service.HasCredentials(user.Id);
|
||||
if (!await _userManager.IsLockedOutAsync(user) && fido2Devices)
|
||||
if (!await _userManager.IsLockedOutAsync(user) && await _u2FService.HasDevices(user.Id))
|
||||
{
|
||||
if (await _userManager.CheckPasswordAsync(user, model.Password))
|
||||
{
|
||||
@ -140,7 +144,7 @@ namespace BTCPayServer.Controllers
|
||||
return View("SecondaryLogin", new SecondaryLoginViewModel()
|
||||
{
|
||||
LoginWith2FaViewModel = twoFModel,
|
||||
LoginWithFido2ViewModel = fido2Devices? await BuildFido2ViewModel(model.RememberMe, user): null,
|
||||
LoginWithU2FViewModel = await BuildU2FViewModel(model.RememberMe, user)
|
||||
});
|
||||
}
|
||||
else
|
||||
@ -156,7 +160,7 @@ namespace BTCPayServer.Controllers
|
||||
var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: true);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
_logger.LogInformation($"User '{user.Id}' logged in.");
|
||||
_logger.LogInformation("User logged in.");
|
||||
return RedirectToLocal(returnUrl);
|
||||
}
|
||||
if (result.RequiresTwoFactor)
|
||||
@ -171,7 +175,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
if (result.IsLockedOut)
|
||||
{
|
||||
_logger.LogWarning($"User '{user.Id}' account locked out.");
|
||||
_logger.LogWarning("User account locked out.");
|
||||
return RedirectToAction(nameof(Lockout));
|
||||
}
|
||||
else
|
||||
@ -185,29 +189,31 @@ namespace BTCPayServer.Controllers
|
||||
return View(model);
|
||||
}
|
||||
|
||||
private async Task<LoginWithFido2ViewModel> BuildFido2ViewModel(bool rememberMe, ApplicationUser user)
|
||||
private async Task<LoginWithU2FViewModel> BuildU2FViewModel(bool rememberMe, ApplicationUser user)
|
||||
{
|
||||
if (_btcPayServerEnvironment.IsSecure)
|
||||
{
|
||||
var r = await _fido2Service.RequestLogin(user.Id);
|
||||
if (r is null)
|
||||
var u2fChallenge = await _u2FService.GenerateDeviceChallenges(user.Id,
|
||||
Request.GetAbsoluteUriNoPathBase().ToString().TrimEnd('/'));
|
||||
|
||||
return new LoginWithU2FViewModel()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return new LoginWithFido2ViewModel()
|
||||
{
|
||||
Data = r,
|
||||
Version = u2fChallenge[0].version,
|
||||
Challenge = u2fChallenge[0].challenge,
|
||||
Challenges = u2fChallenge,
|
||||
AppId = u2fChallenge[0].appId,
|
||||
UserId = user.Id,
|
||||
RememberMe = rememberMe
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> LoginWithFido2(LoginWithFido2ViewModel viewModel, string returnUrl = null)
|
||||
public async Task<IActionResult> LoginWithU2F(LoginWithU2FViewModel viewModel, string returnUrl = null)
|
||||
{
|
||||
if (!CanLoginOrRegister())
|
||||
{
|
||||
@ -225,25 +231,24 @@ namespace BTCPayServer.Controllers
|
||||
var errorMessage = string.Empty;
|
||||
try
|
||||
{
|
||||
if (await _fido2Service.CompleteLogin(viewModel.UserId, JObject.Parse(viewModel.Response).ToObject<AuthenticatorAssertionRawResponse>()))
|
||||
if (await _u2FService.AuthenticateUser(viewModel.UserId, viewModel.DeviceResponse))
|
||||
{
|
||||
await _signInManager.SignInAsync(user, viewModel.RememberMe, "FIDO2");
|
||||
await _signInManager.SignInAsync(user, viewModel.RememberMe, "U2F");
|
||||
_logger.LogInformation("User logged in.");
|
||||
return RedirectToLocal(returnUrl);
|
||||
}
|
||||
|
||||
errorMessage = "Invalid login attempt.";
|
||||
}
|
||||
catch (Fido2VerificationException e)
|
||||
catch (U2fException e)
|
||||
{
|
||||
errorMessage = e.Message;
|
||||
}
|
||||
|
||||
ModelState.AddModelError(string.Empty, errorMessage);
|
||||
viewModel.Response = null;
|
||||
return View("SecondaryLogin", new SecondaryLoginViewModel()
|
||||
{
|
||||
LoginWithFido2ViewModel = viewModel,
|
||||
LoginWithU2FViewModel = viewModel,
|
||||
LoginWith2FaViewModel = !user.TwoFactorEnabled
|
||||
? null
|
||||
: new LoginWith2faViewModel()
|
||||
@ -252,6 +257,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> LoginWith2fa(bool rememberMe, string returnUrl = null)
|
||||
@ -274,7 +280,7 @@ namespace BTCPayServer.Controllers
|
||||
return View("SecondaryLogin", new SecondaryLoginViewModel()
|
||||
{
|
||||
LoginWith2FaViewModel = new LoginWith2faViewModel { RememberMe = rememberMe },
|
||||
LoginWithFido2ViewModel = (await _fido2Service.HasCredentials(user.Id)) ? await BuildFido2ViewModel(rememberMe, user) : null,
|
||||
LoginWithU2FViewModel = (await _u2FService.HasDevices(user.Id)) ? await BuildU2FViewModel(rememberMe, user) : null
|
||||
});
|
||||
}
|
||||
|
||||
@ -320,7 +326,7 @@ namespace BTCPayServer.Controllers
|
||||
return View("SecondaryLogin", new SecondaryLoginViewModel()
|
||||
{
|
||||
LoginWith2FaViewModel = model,
|
||||
LoginWithFido2ViewModel = (await _fido2Service.HasCredentials(user.Id)) ? await BuildFido2ViewModel(rememberMe, user) : null,
|
||||
LoginWithU2FViewModel = (await _u2FService.HasDevices(user.Id)) ? await BuildU2FViewModel(rememberMe, user) : null
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -23,33 +23,33 @@ namespace BTCPayServer.Controllers
|
||||
" price: 1\n" +
|
||||
" title: Green Tea\n" +
|
||||
" description: Lovely, fresh and tender, Meng Ding Gan Lu ('sweet dew') is grown in the lush Meng Ding Mountains of the southwestern province of Sichuan where it has been cultivated for over a thousand years.\n" +
|
||||
" image: ~/img/pos-sample/green-tea.jpg\n\n" +
|
||||
" image: https://cdn.pixabay.com/photo/2015/03/26/11/03/green-tea-692339__480.jpg\n\n" +
|
||||
"black tea:\n" +
|
||||
" price: 1\n" +
|
||||
" title: Black Tea\n" +
|
||||
" description: Tian Jian Tian Jian means 'heavenly tippy tea' in Chinese, and it describes the finest grade of dark tea. Our Tian Jian dark tea is from Hunan province which is famous for making some of the best dark teas available.\n" +
|
||||
" image: ~/img/pos-sample/black-tea.jpg\n\n" +
|
||||
" image: https://cdn.pixabay.com/photo/2016/11/29/13/04/beverage-1869716__480.jpg\n\n" +
|
||||
"rooibos:\n" +
|
||||
" price: 1.2\n" +
|
||||
" title: Rooibos\n" +
|
||||
" description: Rooibos is a dramatic red tea made from a South African herb that contains polyphenols and flavonoids. Often called 'African redbush tea', Rooibos herbal tea delights the senses and delivers potential health benefits with each caffeine-free sip.\n" +
|
||||
" image: ~/img/pos-sample/rooibos.jpg\n\n" +
|
||||
" image: https://cdn.pixabay.com/photo/2017/01/08/08/14/water-1962388__480.jpg\n\n" +
|
||||
"pu erh:\n" +
|
||||
" price: 2\n" +
|
||||
" title: Pu Erh\n" +
|
||||
" description: This loose pur-erh tea is produced in Yunnan Province, China. The process in a relatively high humidity environment has mellowed the elemental character of the tea when compared to young Pu-erh.\n" +
|
||||
" image: ~/img/pos-sample/pu-erh.jpg\n\n" +
|
||||
" image: https://cdn.pixabay.com/photo/2018/07/21/16/56/tea-cup-3552917__480.jpg\n\n" +
|
||||
"herbal tea:\n" +
|
||||
" price: 1.8\n" +
|
||||
" title: Herbal Tea\n" +
|
||||
" description: Chamomile tea is made from the flower heads of the chamomile plant. The medicinal use of chamomile dates back to the ancient Egyptians, Romans and Greeks. Pay us what you want!\n" +
|
||||
" image: ~/img/pos-sample/herbal-tea.jpg\n" +
|
||||
" image: https://cdn.pixabay.com/photo/2015/07/02/20/57/chamomile-829538__480.jpg\n" +
|
||||
" custom: true\n\n" +
|
||||
"fruit tea:\n" +
|
||||
" price: 1.5\n" +
|
||||
" title: Fruit Tea\n" +
|
||||
" description: The Tibetan Himalayas, the land is majestic and beautiful—a spiritual place where, despite the perilous environment, many journey seeking enlightenment. Pay us what you want!\n" +
|
||||
" image: ~/img/pos-sample/fruit-tea.jpg\n" +
|
||||
" image: https://cdn.pixabay.com/photo/2016/09/16/11/24/darts-1673812__480.jpg\n" +
|
||||
" inventory: 5\n" +
|
||||
" custom: true";
|
||||
DefaultView = PosViewType.Static;
|
||||
|
@ -202,5 +202,10 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
return _UserManager.GetUserId(User);
|
||||
}
|
||||
|
||||
private async Task<bool> IsEmailConfigured(string storeId)
|
||||
{
|
||||
return (await (_emailSenderFactory.GetEmailSender(storeId) as EmailSender)?.GetEmailSettings())?.IsComplete() is true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,11 +8,6 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
public static class GreenFieldUtils
|
||||
{
|
||||
public static IActionResult CreateValidationError(this ControllerBase controller, ModelStateDictionary modelState)
|
||||
{
|
||||
return controller.UnprocessableEntity(modelState.ToGreenfieldValidationError());
|
||||
}
|
||||
|
||||
public static List<GreenfieldValidationError> ToGreenfieldValidationError(this ModelStateDictionary modelState)
|
||||
{
|
||||
List<GreenfieldValidationError> errors = new List<GreenfieldValidationError>();
|
||||
foreach (var error in modelState)
|
||||
@ -22,17 +17,11 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
errors.Add(new GreenfieldValidationError(error.Key, errorMessage.ErrorMessage));
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
return controller.UnprocessableEntity(errors.ToArray());
|
||||
}
|
||||
|
||||
public static IActionResult CreateAPIError(this ControllerBase controller, string errorCode, string errorMessage)
|
||||
{
|
||||
return controller.BadRequest(new GreenfieldAPIError(errorCode, errorMessage));
|
||||
}
|
||||
public static IActionResult CreateAPIError(this ControllerBase controller, int httpCode, string errorCode, string errorMessage)
|
||||
{
|
||||
return controller.StatusCode(httpCode, new GreenfieldAPIError(errorCode, errorMessage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,19 +10,13 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
[EnableCors(CorsPolicies.All)]
|
||||
public class HealthController : ControllerBase
|
||||
{
|
||||
private readonly NBXplorerDashboard _dashBoard;
|
||||
|
||||
public HealthController(NBXplorerDashboard dashBoard )
|
||||
{
|
||||
_dashBoard = dashBoard;
|
||||
}
|
||||
[AllowAnonymous]
|
||||
[HttpGet("~/api/v1/health")]
|
||||
public ActionResult GetHealth()
|
||||
public ActionResult GetHealth(NBXplorerDashboard dashBoard)
|
||||
{
|
||||
ApiHealthData model = new ApiHealthData()
|
||||
{
|
||||
Synchronized = _dashBoard.IsFullySynched()
|
||||
Synchronized = dashBoard.IsFullySynched()
|
||||
};
|
||||
return Ok(model);
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Globalization;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
@ -48,46 +47,25 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
[Authorize(Policy = Policies.CanViewInvoices,
|
||||
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpGet("~/api/v1/stores/{storeId}/invoices")]
|
||||
public async Task<IActionResult> GetInvoices(string storeId, [FromQuery] string[] orderId = null, [FromQuery] string[] status = null,
|
||||
[FromQuery]
|
||||
[ModelBinder(typeof(ModelBinders.DateTimeOffsetModelBinder))]
|
||||
DateTimeOffset? startDate = null,
|
||||
[FromQuery]
|
||||
[ModelBinder(typeof(ModelBinders.DateTimeOffsetModelBinder))]
|
||||
DateTimeOffset? endDate = null,
|
||||
string textSearch = null,
|
||||
[FromQuery] bool includeArchived = false)
|
||||
public async Task<IActionResult> GetInvoices(string storeId, bool includeArchived = false)
|
||||
{
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
{
|
||||
return StoreNotFound();
|
||||
}
|
||||
if (startDate is DateTimeOffset s &&
|
||||
endDate is DateTimeOffset e &&
|
||||
s > e)
|
||||
{
|
||||
this.ModelState.AddModelError(nameof(startDate), "startDate should not be above endDate");
|
||||
this.ModelState.AddModelError(nameof(endDate), "endDate should not be below startDate");
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
return this.CreateValidationError(ModelState);
|
||||
var invoices =
|
||||
await _invoiceRepository.GetInvoices(new InvoiceQuery()
|
||||
{
|
||||
StoreId = new[] {store.Id},
|
||||
IncludeArchived = includeArchived,
|
||||
StartDate = startDate,
|
||||
EndDate = endDate,
|
||||
OrderId = orderId,
|
||||
Status = status,
|
||||
TextSearch = textSearch
|
||||
StoreId = new[] { store.Id },
|
||||
IncludeArchived = includeArchived
|
||||
});
|
||||
|
||||
return Ok(invoices.Select(ToModel));
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Authorize(Policy = Policies.CanViewInvoices,
|
||||
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpGet("~/api/v1/stores/{storeId}/invoices/{invoiceId}")]
|
||||
@ -96,19 +74,19 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
{
|
||||
return InvoiceNotFound();
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var invoice = await _invoiceRepository.GetInvoice(invoiceId, true);
|
||||
if (invoice?.StoreId != store.Id)
|
||||
{
|
||||
return InvoiceNotFound();
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(ToModel(invoice));
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanModifyInvoices,
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings,
|
||||
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpDelete("~/api/v1/stores/{storeId}/invoices/{invoiceId}")]
|
||||
public async Task<IActionResult> ArchiveInvoice(string storeId, string invoiceId)
|
||||
@ -116,18 +94,14 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
{
|
||||
return InvoiceNotFound();
|
||||
}
|
||||
var invoice = await _invoiceRepository.GetInvoice(invoiceId, true);
|
||||
if (invoice?.StoreId != store.Id)
|
||||
{
|
||||
return InvoiceNotFound();
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
await _invoiceRepository.ToggleInvoiceArchival(invoiceId, true, storeId);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanModifyInvoices,
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings,
|
||||
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpPut("~/api/v1/stores/{storeId}/invoices/{invoiceId}")]
|
||||
public async Task<IActionResult> UpdateInvoice(string storeId, string invoiceId, UpdateInvoiceRequest request)
|
||||
@ -135,7 +109,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
{
|
||||
return InvoiceNotFound();
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var result = await _invoiceRepository.UpdateInvoiceMetadata(invoiceId, storeId, request.Metadata);
|
||||
@ -144,7 +118,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
return Ok(ToModel(result));
|
||||
}
|
||||
|
||||
return InvoiceNotFound();
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanCreateInvoice,
|
||||
@ -155,7 +129,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
{
|
||||
return StoreNotFound();
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (request.Amount < 0.0m)
|
||||
@ -195,7 +169,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
|
||||
if (request.Checkout.DefaultLanguage != null)
|
||||
{
|
||||
var lang = LanguageService.FindLanguage(request.Checkout.DefaultLanguage);
|
||||
var lang = LanguageService.FindBestMatch(request.Checkout.DefaultLanguage);
|
||||
if (lang == null)
|
||||
{
|
||||
request.AddModelError(invoiceRequest => invoiceRequest.Checkout.DefaultLanguage,
|
||||
@ -223,7 +197,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
}
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanModifyInvoices,
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings,
|
||||
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpPost("~/api/v1/stores/{storeId}/invoices/{invoiceId}/status")]
|
||||
public async Task<IActionResult> MarkInvoiceStatus(string storeId, string invoiceId,
|
||||
@ -232,13 +206,13 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
{
|
||||
return InvoiceNotFound();
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var invoice = await _invoiceRepository.GetInvoice(invoiceId, true);
|
||||
if (invoice.StoreId != store.Id)
|
||||
{
|
||||
return InvoiceNotFound();
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (!await _invoiceRepository.MarkInvoiceStatus(invoice.Id, request.Status))
|
||||
@ -253,7 +227,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
return await GetInvoice(storeId, invoiceId);
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanModifyInvoices,
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings,
|
||||
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpPost("~/api/v1/stores/{storeId}/invoices/{invoiceId}/unarchive")]
|
||||
public async Task<IActionResult> UnarchiveInvoice(string storeId, string invoiceId)
|
||||
@ -261,13 +235,13 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
{
|
||||
return InvoiceNotFound();
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var invoice = await _invoiceRepository.GetInvoice(invoiceId, true);
|
||||
if (invoice.StoreId != store.Id)
|
||||
{
|
||||
return InvoiceNotFound();
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (!invoice.Archived)
|
||||
@ -286,21 +260,21 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
[Authorize(Policy = Policies.CanViewInvoices,
|
||||
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpGet("~/api/v1/stores/{storeId}/invoices/{invoiceId}/payment-methods")]
|
||||
public async Task<IActionResult> GetInvoicePaymentMethods(string storeId, string invoiceId, bool onlyAccountedPayments = true)
|
||||
public async Task<IActionResult> GetInvoicePaymentMethods(string storeId, string invoiceId)
|
||||
{
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
{
|
||||
return InvoiceNotFound();
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var invoice = await _invoiceRepository.GetInvoice(invoiceId, true);
|
||||
if (invoice?.StoreId != store.Id)
|
||||
{
|
||||
return InvoiceNotFound();
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(ToPaymentMethodModels(invoice, onlyAccountedPayments));
|
||||
return Ok(ToPaymentMethodModels(invoice));
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanViewInvoices,
|
||||
@ -311,13 +285,13 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
{
|
||||
return InvoiceNotFound();
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var invoice = await _invoiceRepository.GetInvoice(invoiceId, true);
|
||||
if (invoice?.StoreId != store.Id)
|
||||
{
|
||||
return InvoiceNotFound();
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (PaymentMethodId.TryParse(paymentMethod, out var paymentMethodId))
|
||||
@ -326,27 +300,17 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
_paymentMethodHandlerDictionary, store, invoice, paymentMethodId);
|
||||
return Ok();
|
||||
}
|
||||
ModelState.AddModelError(nameof(paymentMethod), "Invalid payment method");
|
||||
return this.CreateValidationError(ModelState);
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
private IActionResult InvoiceNotFound()
|
||||
{
|
||||
return this.CreateAPIError(404, "invoice-not-found", "The invoice was not found");
|
||||
}
|
||||
private IActionResult StoreNotFound()
|
||||
{
|
||||
return this.CreateAPIError(404, "store-not-found", "The store was not found");
|
||||
}
|
||||
|
||||
private InvoicePaymentMethodDataModel[] ToPaymentMethodModels(InvoiceEntity entity, bool includeAccountedPaymentOnly)
|
||||
|
||||
private InvoicePaymentMethodDataModel[] ToPaymentMethodModels(InvoiceEntity entity)
|
||||
{
|
||||
return entity.GetPaymentMethods().Select(
|
||||
method =>
|
||||
{
|
||||
var accounting = method.Calculate();
|
||||
var details = method.GetPaymentMethodDetails();
|
||||
var payments = method.ParentEntity.GetPayments(includeAccountedPaymentOnly).Where(paymentEntity =>
|
||||
var payments = method.ParentEntity.GetPayments().Where(paymentEntity =>
|
||||
paymentEntity.GetPaymentMethodId() == method.GetId());
|
||||
|
||||
return new InvoicePaymentMethodDataModel()
|
||||
@ -388,12 +352,10 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
{
|
||||
return new InvoiceData()
|
||||
{
|
||||
StoreId = entity.StoreId,
|
||||
ExpirationTime = entity.ExpirationTime,
|
||||
MonitoringExpiration = entity.MonitoringExpiration,
|
||||
CreatedTime = entity.InvoiceTime,
|
||||
Amount = entity.Price,
|
||||
Type = entity.Type,
|
||||
Id = entity.Id,
|
||||
CheckoutLink = _linkGenerator.CheckoutLink(entity.Id, Request.Scheme, Request.Host, Request.PathBase),
|
||||
Status = entity.Status.ToModernStatus(),
|
||||
|
@ -1,6 +1,5 @@
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Configuration;
|
||||
@ -27,10 +26,11 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
|
||||
|
||||
public InternalLightningNodeApiController(
|
||||
BTCPayNetworkProvider btcPayNetworkProvider, ISettingsRepository settingsRepository, LightningClientFactoryService lightningClientFactory,
|
||||
BTCPayNetworkProvider btcPayNetworkProvider, BTCPayServerEnvironment btcPayServerEnvironment,
|
||||
CssThemeManager cssThemeManager, LightningClientFactoryService lightningClientFactory,
|
||||
IOptions<LightningNetworkOptions> lightningNetworkOptions,
|
||||
IAuthorizationService authorizationService) : base(
|
||||
btcPayNetworkProvider, settingsRepository, authorizationService)
|
||||
btcPayNetworkProvider, btcPayServerEnvironment, cssThemeManager, authorizationService)
|
||||
{
|
||||
_btcPayNetworkProvider = btcPayNetworkProvider;
|
||||
_lightningClientFactory = lightningClientFactory;
|
||||
|
@ -1,7 +1,6 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Configuration;
|
||||
@ -32,9 +31,9 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
public StoreLightningNodeApiController(
|
||||
IOptions<LightningNetworkOptions> lightningNetworkOptions,
|
||||
LightningClientFactoryService lightningClientFactory, BTCPayNetworkProvider btcPayNetworkProvider,
|
||||
ISettingsRepository settingsRepository,
|
||||
BTCPayServerEnvironment btcPayServerEnvironment, CssThemeManager cssThemeManager,
|
||||
IAuthorizationService authorizationService) : base(
|
||||
btcPayNetworkProvider, settingsRepository, authorizationService)
|
||||
btcPayNetworkProvider, btcPayServerEnvironment, cssThemeManager, authorizationService)
|
||||
{
|
||||
_lightningNetworkOptions = lightningNetworkOptions;
|
||||
_lightningClientFactory = lightningClientFactory;
|
||||
|
@ -2,7 +2,6 @@ using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.HostedServices;
|
||||
@ -34,15 +33,17 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
public abstract class LightningNodeApiController : Controller
|
||||
{
|
||||
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
|
||||
private readonly ISettingsRepository _settingsRepository;
|
||||
private readonly BTCPayServerEnvironment _btcPayServerEnvironment;
|
||||
private readonly CssThemeManager _cssThemeManager;
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
|
||||
protected LightningNodeApiController(BTCPayNetworkProvider btcPayNetworkProvider,
|
||||
ISettingsRepository settingsRepository,
|
||||
BTCPayServerEnvironment btcPayServerEnvironment, CssThemeManager cssThemeManager,
|
||||
IAuthorizationService authorizationService)
|
||||
{
|
||||
_btcPayNetworkProvider = btcPayNetworkProvider;
|
||||
_settingsRepository = settingsRepository;
|
||||
_btcPayServerEnvironment = btcPayServerEnvironment;
|
||||
_cssThemeManager = cssThemeManager;
|
||||
_authorizationService = authorizationService;
|
||||
}
|
||||
|
||||
@ -301,8 +302,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
|
||||
protected async Task<bool> CanUseInternalLightning(bool doingAdminThings)
|
||||
{
|
||||
|
||||
return (!doingAdminThings && (await _settingsRepository.GetPolicies()).AllowLightningInternalNodeForAll) ||
|
||||
return (!doingAdminThings && _cssThemeManager.AllowLightningInternalNodeForAll) ||
|
||||
(await _authorizationService.AuthorizeAsync(User, null,
|
||||
new PolicyRequirement(Policies.CanUseInternalLightningNode))).Succeeded;
|
||||
}
|
||||
|
@ -1,904 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Security.Claims;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Security.GreenField;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NBitcoin;
|
||||
using NBXplorer.Models;
|
||||
using YamlDotNet.Core.Tokens;
|
||||
using InvoiceData = BTCPayServer.Client.Models.InvoiceData;
|
||||
using Language = BTCPayServer.Client.Models.Language;
|
||||
using NotificationData = BTCPayServer.Client.Models.NotificationData;
|
||||
using PaymentRequestData = BTCPayServer.Client.Models.PaymentRequestData;
|
||||
using PayoutData = BTCPayServer.Client.Models.PayoutData;
|
||||
using PullPaymentData = BTCPayServer.Client.Models.PullPaymentData;
|
||||
using StoreData = BTCPayServer.Client.Models.StoreData;
|
||||
using StoreWebhookData = BTCPayServer.Client.Models.StoreWebhookData;
|
||||
using WebhookDeliveryData = BTCPayServer.Client.Models.WebhookDeliveryData;
|
||||
|
||||
namespace BTCPayServer.Controllers.GreenField
|
||||
{
|
||||
public class BTCPayServerClientFactory : IBTCPayServerClientFactory
|
||||
{
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private readonly IOptionsMonitor<IdentityOptions> _identityOptions;
|
||||
private readonly StoreOnChainPaymentMethodsController _chainPaymentMethodsController;
|
||||
private readonly StoreOnChainWalletsController _storeOnChainWalletsController;
|
||||
private readonly StoreLightningNetworkPaymentMethodsController _storeLightningNetworkPaymentMethodsController;
|
||||
private readonly HealthController _healthController;
|
||||
private readonly GreenFieldPaymentRequestsController _paymentRequestController;
|
||||
private readonly ApiKeysController _apiKeysController;
|
||||
private readonly NotificationsController _notificationsController;
|
||||
private readonly UsersController _usersController;
|
||||
private readonly GreenFieldStoresController _storesController;
|
||||
private readonly InternalLightningNodeApiController _internalLightningNodeApiController;
|
||||
private readonly StoreLightningNodeApiController _storeLightningNodeApiController;
|
||||
private readonly GreenFieldInvoiceController _greenFieldInvoiceController;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly GreenFieldServerInfoController _greenFieldServerInfoController;
|
||||
private readonly StoreWebhooksController _storeWebhooksController;
|
||||
private readonly GreenfieldPullPaymentController _greenfieldPullPaymentController;
|
||||
private readonly HomeController _homeController;
|
||||
private readonly StorePaymentMethodsController _storePaymentMethodsController;
|
||||
|
||||
public BTCPayServerClientFactory(StoreRepository storeRepository,
|
||||
IOptionsMonitor<IdentityOptions> identityOptions,
|
||||
StoreOnChainPaymentMethodsController chainPaymentMethodsController,
|
||||
StoreOnChainWalletsController storeOnChainWalletsController,
|
||||
StoreLightningNetworkPaymentMethodsController storeLightningNetworkPaymentMethodsController,
|
||||
HealthController healthController,
|
||||
GreenFieldPaymentRequestsController paymentRequestController,
|
||||
ApiKeysController apiKeysController,
|
||||
NotificationsController notificationsController,
|
||||
UsersController usersController,
|
||||
GreenFieldStoresController storesController,
|
||||
InternalLightningNodeApiController internalLightningNodeApiController,
|
||||
StoreLightningNodeApiController storeLightningNodeApiController,
|
||||
GreenFieldInvoiceController greenFieldInvoiceController,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
GreenFieldServerInfoController greenFieldServerInfoController,
|
||||
StoreWebhooksController storeWebhooksController,
|
||||
GreenfieldPullPaymentController greenfieldPullPaymentController,
|
||||
HomeController homeController,
|
||||
StorePaymentMethodsController storePaymentMethodsController)
|
||||
{
|
||||
_storeRepository = storeRepository;
|
||||
_identityOptions = identityOptions;
|
||||
_chainPaymentMethodsController = chainPaymentMethodsController;
|
||||
_storeOnChainWalletsController = storeOnChainWalletsController;
|
||||
_storeLightningNetworkPaymentMethodsController = storeLightningNetworkPaymentMethodsController;
|
||||
_healthController = healthController;
|
||||
_paymentRequestController = paymentRequestController;
|
||||
_apiKeysController = apiKeysController;
|
||||
_notificationsController = notificationsController;
|
||||
_usersController = usersController;
|
||||
_storesController = storesController;
|
||||
_internalLightningNodeApiController = internalLightningNodeApiController;
|
||||
_storeLightningNodeApiController = storeLightningNodeApiController;
|
||||
_greenFieldInvoiceController = greenFieldInvoiceController;
|
||||
_userManager = userManager;
|
||||
_greenFieldServerInfoController = greenFieldServerInfoController;
|
||||
_storeWebhooksController = storeWebhooksController;
|
||||
_greenfieldPullPaymentController = greenfieldPullPaymentController;
|
||||
_homeController = homeController;
|
||||
_storePaymentMethodsController = storePaymentMethodsController;
|
||||
}
|
||||
|
||||
public async Task<BTCPayServerClient> Create(string userId, params string[] storeIds)
|
||||
{
|
||||
var context = new DefaultHttpContext();
|
||||
if (!string.IsNullOrEmpty(userId))
|
||||
{
|
||||
var user = await _userManager.FindByIdAsync(userId);
|
||||
List<Claim> claims = new List<Claim>
|
||||
{
|
||||
new Claim(_identityOptions.CurrentValue.ClaimsIdentity.UserIdClaimType, userId),
|
||||
new Claim(GreenFieldConstants.ClaimTypes.Permission,
|
||||
Permission.Create(Policies.Unrestricted).ToString())
|
||||
};
|
||||
claims.AddRange((await _userManager.GetRolesAsync(user)).Select(s =>
|
||||
new Claim(_identityOptions.CurrentValue.ClaimsIdentity.RoleClaimType, s)));
|
||||
context.User =
|
||||
new ClaimsPrincipal(new ClaimsIdentity(claims, GreenFieldConstants.AuthenticationType));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.User =
|
||||
new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>(), $"Local{GreenFieldConstants.AuthenticationType}"));
|
||||
}
|
||||
|
||||
if (storeIds?.Any() is true)
|
||||
{
|
||||
context.SetStoreData(await _storeRepository.FindStore(storeIds.First()));
|
||||
context.SetStoresData(await _storeRepository.GetStoresByUserId(userId, storeIds));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.SetStoresData(await _storeRepository.GetStoresByUserId(userId));
|
||||
}
|
||||
|
||||
return new LocalBTCPayServerClient(
|
||||
_chainPaymentMethodsController,
|
||||
_storeOnChainWalletsController,
|
||||
_healthController,
|
||||
_paymentRequestController,
|
||||
_apiKeysController,
|
||||
_notificationsController,
|
||||
_usersController,
|
||||
_storesController,
|
||||
_storeLightningNodeApiController,
|
||||
_internalLightningNodeApiController,
|
||||
_storeLightningNetworkPaymentMethodsController,
|
||||
_greenFieldInvoiceController,
|
||||
_greenFieldServerInfoController,
|
||||
_storeWebhooksController,
|
||||
_greenfieldPullPaymentController,
|
||||
_homeController,
|
||||
_storePaymentMethodsController,
|
||||
new HttpContextAccessor() {HttpContext = context}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public class LocalBTCPayServerClient : BTCPayServerClient
|
||||
{
|
||||
private readonly StoreOnChainPaymentMethodsController _chainPaymentMethodsController;
|
||||
private readonly StoreOnChainWalletsController _storeOnChainWalletsController;
|
||||
private readonly HealthController _healthController;
|
||||
private readonly GreenFieldPaymentRequestsController _paymentRequestController;
|
||||
private readonly ApiKeysController _apiKeysController;
|
||||
private readonly NotificationsController _notificationsController;
|
||||
private readonly UsersController _usersController;
|
||||
private readonly GreenFieldStoresController _storesController;
|
||||
private readonly StoreLightningNodeApiController _storeLightningNodeApiController;
|
||||
private readonly InternalLightningNodeApiController _lightningNodeApiController;
|
||||
private readonly StoreLightningNetworkPaymentMethodsController _storeLightningNetworkPaymentMethodsController;
|
||||
private readonly GreenFieldInvoiceController _greenFieldInvoiceController;
|
||||
private readonly GreenFieldServerInfoController _greenFieldServerInfoController;
|
||||
private readonly StoreWebhooksController _storeWebhooksController;
|
||||
private readonly GreenfieldPullPaymentController _greenfieldPullPaymentController;
|
||||
private readonly HomeController _homeController;
|
||||
private readonly StorePaymentMethodsController _storePaymentMethodsController;
|
||||
|
||||
public LocalBTCPayServerClient(StoreOnChainPaymentMethodsController chainPaymentMethodsController,
|
||||
StoreOnChainWalletsController storeOnChainWalletsController,
|
||||
HealthController healthController,
|
||||
GreenFieldPaymentRequestsController paymentRequestController,
|
||||
ApiKeysController apiKeysController,
|
||||
NotificationsController notificationsController,
|
||||
UsersController usersController,
|
||||
GreenFieldStoresController storesController,
|
||||
StoreLightningNodeApiController storeLightningNodeApiController,
|
||||
InternalLightningNodeApiController lightningNodeApiController,
|
||||
StoreLightningNetworkPaymentMethodsController storeLightningNetworkPaymentMethodsController,
|
||||
GreenFieldInvoiceController greenFieldInvoiceController,
|
||||
GreenFieldServerInfoController greenFieldServerInfoController,
|
||||
StoreWebhooksController storeWebhooksController,
|
||||
GreenfieldPullPaymentController greenfieldPullPaymentController,
|
||||
HomeController homeController,
|
||||
StorePaymentMethodsController storePaymentMethodsController,
|
||||
IHttpContextAccessor httpContextAccessor) : base(new Uri("https://dummy.local"), "", "")
|
||||
{
|
||||
_chainPaymentMethodsController = chainPaymentMethodsController;
|
||||
_storeOnChainWalletsController = storeOnChainWalletsController;
|
||||
_healthController = healthController;
|
||||
_paymentRequestController = paymentRequestController;
|
||||
_apiKeysController = apiKeysController;
|
||||
_notificationsController = notificationsController;
|
||||
_usersController = usersController;
|
||||
_storesController = storesController;
|
||||
_storeLightningNodeApiController = storeLightningNodeApiController;
|
||||
_lightningNodeApiController = lightningNodeApiController;
|
||||
_storeLightningNetworkPaymentMethodsController = storeLightningNetworkPaymentMethodsController;
|
||||
_greenFieldInvoiceController = greenFieldInvoiceController;
|
||||
_greenFieldServerInfoController = greenFieldServerInfoController;
|
||||
_storeWebhooksController = storeWebhooksController;
|
||||
_greenfieldPullPaymentController = greenfieldPullPaymentController;
|
||||
_homeController = homeController;
|
||||
_storePaymentMethodsController = storePaymentMethodsController;
|
||||
|
||||
var controllers = new[]
|
||||
{
|
||||
chainPaymentMethodsController, storeOnChainWalletsController, healthController,
|
||||
paymentRequestController, apiKeysController, notificationsController, usersController,
|
||||
storeLightningNetworkPaymentMethodsController, greenFieldInvoiceController, storeWebhooksController,
|
||||
greenFieldServerInfoController, greenfieldPullPaymentController, storesController, homeController,
|
||||
lightningNodeApiController, storeLightningNodeApiController as ControllerBase, storePaymentMethodsController
|
||||
};
|
||||
foreach (var controller in controllers)
|
||||
{
|
||||
controller.ControllerContext.HttpContext = httpContextAccessor.HttpContext;
|
||||
}
|
||||
}
|
||||
|
||||
protected override HttpRequestMessage CreateHttpRequest(string path,
|
||||
Dictionary<string, object> queryPayload = null, HttpMethod method = null)
|
||||
{
|
||||
throw new NotSupportedException("This method is not supported by the LocalBTCPayServerClient.");
|
||||
}
|
||||
|
||||
public override async Task<StoreWebhookData> CreateWebhook(string storeId, CreateStoreWebhookRequest create,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<StoreWebhookData>(
|
||||
await _storeWebhooksController.CreateWebhook(storeId, create));
|
||||
}
|
||||
|
||||
public override async Task<StoreWebhookData> GetWebhook(string storeId, string webhookId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<StoreWebhookData>(
|
||||
await _storeWebhooksController.ListWebhooks(storeId, webhookId));
|
||||
}
|
||||
|
||||
public override async Task<StoreWebhookData> UpdateWebhook(string storeId, string webhookId,
|
||||
UpdateStoreWebhookRequest update,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<StoreWebhookData>(
|
||||
await _storeWebhooksController.UpdateWebhook(storeId, webhookId, update));
|
||||
}
|
||||
|
||||
public override async Task<bool> DeleteWebhook(string storeId, string webhookId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
HandleActionResult(await _storeWebhooksController.DeleteWebhook(storeId, webhookId));
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async Task<StoreWebhookData[]> GetWebhooks(string storeId, CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<StoreWebhookData[]>(
|
||||
await _storeWebhooksController.ListWebhooks(storeId, null));
|
||||
}
|
||||
|
||||
public override async Task<WebhookDeliveryData[]> GetWebhookDeliveries(string storeId, string webhookId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<WebhookDeliveryData[]>(
|
||||
await _storeWebhooksController.ListDeliveries(storeId, webhookId, null));
|
||||
}
|
||||
|
||||
public override async Task<WebhookDeliveryData> GetWebhookDelivery(string storeId, string webhookId,
|
||||
string deliveryId, CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<WebhookDeliveryData>(
|
||||
await _storeWebhooksController.ListDeliveries(storeId, webhookId, deliveryId));
|
||||
}
|
||||
|
||||
public override async Task<string> RedeliverWebhook(string storeId, string webhookId, string deliveryId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<string>(
|
||||
await _storeWebhooksController.RedeliverWebhook(storeId, webhookId, deliveryId));
|
||||
}
|
||||
|
||||
public override async Task<WebhookEvent> GetWebhookDeliveryRequest(string storeId, string webhookId,
|
||||
string deliveryId, CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<WebhookEvent>(
|
||||
await _storeWebhooksController.GetDeliveryRequest(storeId, webhookId, deliveryId));
|
||||
}
|
||||
|
||||
public override async Task<PullPaymentData> CreatePullPayment(string storeId, CreatePullPaymentRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return GetFromActionResult<PullPaymentData>(
|
||||
await _greenfieldPullPaymentController.CreatePullPayment(storeId, request));
|
||||
}
|
||||
|
||||
public override async Task<PullPaymentData> GetPullPayment(string pullPaymentId,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return GetFromActionResult<PullPaymentData>(
|
||||
await _greenfieldPullPaymentController.GetPullPayment(pullPaymentId));
|
||||
}
|
||||
|
||||
public override async Task<PullPaymentData[]> GetPullPayments(string storeId, bool includeArchived = false,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return GetFromActionResult<PullPaymentData[]>(
|
||||
await _greenfieldPullPaymentController.GetPullPayments(storeId, includeArchived));
|
||||
}
|
||||
|
||||
public override async Task ArchivePullPayment(string storeId, string pullPaymentId,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
HandleActionResult(await _greenfieldPullPaymentController.ArchivePullPayment(storeId, pullPaymentId));
|
||||
}
|
||||
|
||||
public override async Task<PayoutData[]> GetPayouts(string pullPaymentId, bool includeCancelled = false,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return GetFromActionResult<PayoutData[]>(
|
||||
await _greenfieldPullPaymentController.GetPayouts(pullPaymentId, includeCancelled));
|
||||
}
|
||||
|
||||
public override async Task<PayoutData> CreatePayout(string pullPaymentId, CreatePayoutRequest payoutRequest,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return GetFromActionResult<PayoutData>(
|
||||
await _greenfieldPullPaymentController.CreatePayout(pullPaymentId, payoutRequest));
|
||||
}
|
||||
|
||||
public override async Task CancelPayout(string storeId, string payoutId,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
HandleActionResult(await _greenfieldPullPaymentController.CancelPayout(storeId, payoutId));
|
||||
}
|
||||
|
||||
public override async Task<PayoutData> ApprovePayout(string storeId, string payoutId,
|
||||
ApprovePayoutRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return GetFromActionResult<PayoutData>(
|
||||
await _greenfieldPullPaymentController.ApprovePayout(storeId, payoutId, request, cancellationToken));
|
||||
}
|
||||
|
||||
public override async Task<LightningNodeInformationData> GetLightningNodeInfo(string storeId, string cryptoCode,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<LightningNodeInformationData>(
|
||||
await _storeLightningNodeApiController.GetInfo(cryptoCode));
|
||||
}
|
||||
|
||||
public override async Task ConnectToLightningNode(string storeId, string cryptoCode,
|
||||
ConnectToNodeRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
HandleActionResult(await _storeLightningNodeApiController.ConnectToNode(cryptoCode, request));
|
||||
}
|
||||
|
||||
public override async Task<IEnumerable<LightningChannelData>> GetLightningNodeChannels(string storeId,
|
||||
string cryptoCode, CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<IEnumerable<LightningChannelData>>(
|
||||
await _storeLightningNodeApiController.GetChannels(cryptoCode));
|
||||
}
|
||||
|
||||
public override async Task OpenLightningChannel(string storeId, string cryptoCode,
|
||||
OpenLightningChannelRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
HandleActionResult(await _storeLightningNodeApiController.OpenChannel(cryptoCode, request));
|
||||
}
|
||||
|
||||
public override async Task<string> GetLightningDepositAddress(string storeId, string cryptoCode,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<string>(
|
||||
await _storeLightningNodeApiController.GetDepositAddress(cryptoCode));
|
||||
}
|
||||
|
||||
public override async Task PayLightningInvoice(string storeId, string cryptoCode,
|
||||
PayLightningInvoiceRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
HandleActionResult(await _storeLightningNodeApiController.PayInvoice(cryptoCode, request));
|
||||
}
|
||||
|
||||
public override async Task<LightningInvoiceData> GetLightningInvoice(string storeId, string cryptoCode,
|
||||
string invoiceId, CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<LightningInvoiceData>(
|
||||
await _storeLightningNodeApiController.GetInvoice(cryptoCode, invoiceId));
|
||||
}
|
||||
|
||||
public override async Task<LightningInvoiceData> CreateLightningInvoice(string storeId, string cryptoCode,
|
||||
CreateLightningInvoiceRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<LightningInvoiceData>(
|
||||
await _storeLightningNodeApiController.CreateInvoice(cryptoCode, request));
|
||||
}
|
||||
|
||||
public override async Task<LightningNodeInformationData> GetLightningNodeInfo(string cryptoCode,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<LightningNodeInformationData>(
|
||||
await _lightningNodeApiController.GetInfo(cryptoCode));
|
||||
}
|
||||
|
||||
public override async Task ConnectToLightningNode(string cryptoCode, ConnectToNodeRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
HandleActionResult(await _lightningNodeApiController.ConnectToNode(cryptoCode, request));
|
||||
}
|
||||
|
||||
public override async Task<IEnumerable<LightningChannelData>> GetLightningNodeChannels(string cryptoCode,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<IEnumerable<LightningChannelData>>(
|
||||
await _lightningNodeApiController.GetChannels(cryptoCode));
|
||||
}
|
||||
|
||||
public override async Task OpenLightningChannel(string cryptoCode, OpenLightningChannelRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
HandleActionResult(await _lightningNodeApiController.OpenChannel(cryptoCode, request));
|
||||
}
|
||||
|
||||
public override async Task<string> GetLightningDepositAddress(string cryptoCode,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<string>(
|
||||
await _lightningNodeApiController.GetDepositAddress(cryptoCode));
|
||||
}
|
||||
|
||||
public override async Task PayLightningInvoice(string cryptoCode, PayLightningInvoiceRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
HandleActionResult(await _lightningNodeApiController.PayInvoice(cryptoCode, request));
|
||||
}
|
||||
|
||||
public override async Task<LightningInvoiceData> GetLightningInvoice(string cryptoCode, string invoiceId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<LightningInvoiceData>(
|
||||
await _lightningNodeApiController.GetInvoice(cryptoCode, invoiceId));
|
||||
}
|
||||
|
||||
public override async Task<LightningInvoiceData> CreateLightningInvoice(string cryptoCode,
|
||||
CreateLightningInvoiceRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<LightningInvoiceData>(
|
||||
await _lightningNodeApiController.CreateInvoice(cryptoCode, request));
|
||||
}
|
||||
|
||||
|
||||
private T GetFromActionResult<T>(IActionResult result)
|
||||
{
|
||||
HandleActionResult(result);
|
||||
switch (result)
|
||||
{
|
||||
case JsonResult jsonResult:
|
||||
return (T) jsonResult.Value;
|
||||
case OkObjectResult {Value: T res}:
|
||||
return res;
|
||||
default:
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleActionResult(IActionResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case UnprocessableEntityObjectResult {Value: List<GreenfieldValidationError> validationErrors}:
|
||||
throw new GreenFieldValidationException(validationErrors.ToArray());
|
||||
case BadRequestObjectResult {Value: GreenfieldAPIError error}:
|
||||
throw new GreenFieldAPIException(error);
|
||||
case NotFoundResult _:
|
||||
throw new GreenFieldAPIException(new GreenfieldAPIError("not-found", ""));
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private T GetFromActionResult<T>(ActionResult result)
|
||||
{
|
||||
return GetFromActionResult<T>((IActionResult)result);
|
||||
}
|
||||
|
||||
private T GetFromActionResult<T>(ActionResult<T> result)
|
||||
{
|
||||
return result.Value ?? GetFromActionResult<T>(result.Result);
|
||||
}
|
||||
|
||||
public override Task<IEnumerable<OnChainPaymentMethodData>> GetStoreOnChainPaymentMethods(string storeId,
|
||||
bool? enabled, CancellationToken token)
|
||||
{
|
||||
return Task.FromResult(GetFromActionResult(_chainPaymentMethodsController.GetOnChainPaymentMethods(storeId, enabled)));
|
||||
}
|
||||
|
||||
public override Task<OnChainPaymentMethodData> GetStoreOnChainPaymentMethod(string storeId,
|
||||
string cryptoCode, CancellationToken token = default)
|
||||
{
|
||||
return Task.FromResult(GetFromActionResult(
|
||||
_chainPaymentMethodsController.GetOnChainPaymentMethod(storeId, cryptoCode)));
|
||||
}
|
||||
|
||||
public override async Task RemoveStoreOnChainPaymentMethod(string storeId, string cryptoCode,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
HandleActionResult(await _chainPaymentMethodsController.RemoveOnChainPaymentMethod(storeId, cryptoCode));
|
||||
}
|
||||
|
||||
public override async Task<OnChainPaymentMethodData> UpdateStoreOnChainPaymentMethod(string storeId,
|
||||
string cryptoCode, OnChainPaymentMethodData paymentMethod,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<OnChainPaymentMethodData>(
|
||||
await _chainPaymentMethodsController.UpdateOnChainPaymentMethod(storeId, cryptoCode, paymentMethod));
|
||||
}
|
||||
|
||||
public override Task<OnChainPaymentMethodPreviewResultData> PreviewProposedStoreOnChainPaymentMethodAddresses(
|
||||
string storeId, string cryptoCode,
|
||||
OnChainPaymentMethodData paymentMethod, int offset = 0, int amount = 10, CancellationToken token = default)
|
||||
{
|
||||
return Task.FromResult(GetFromActionResult<OnChainPaymentMethodPreviewResultData>(
|
||||
_chainPaymentMethodsController.GetProposedOnChainPaymentMethodPreview(storeId, cryptoCode,
|
||||
paymentMethod, offset, amount)));
|
||||
}
|
||||
|
||||
public override Task<OnChainPaymentMethodPreviewResultData> PreviewStoreOnChainPaymentMethodAddresses(
|
||||
string storeId, string cryptoCode, int offset = 0, int amount = 10,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return Task.FromResult(GetFromActionResult<OnChainPaymentMethodPreviewResultData>(
|
||||
_chainPaymentMethodsController.GetOnChainPaymentMethodPreview(storeId, cryptoCode, offset,
|
||||
amount)));
|
||||
}
|
||||
|
||||
public override Task<ApiHealthData> GetHealth(CancellationToken token = default)
|
||||
{
|
||||
return Task.FromResult(GetFromActionResult<ApiHealthData>(_healthController.GetHealth()));
|
||||
}
|
||||
|
||||
public override async Task<IEnumerable<PaymentRequestData>> GetPaymentRequests(string storeId,
|
||||
bool includeArchived = false, CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult(await _paymentRequestController.GetPaymentRequests(storeId, includeArchived));
|
||||
}
|
||||
|
||||
public override async Task<PaymentRequestData> GetPaymentRequest(string storeId, string paymentRequestId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult(await _paymentRequestController.GetPaymentRequest(storeId, paymentRequestId));
|
||||
}
|
||||
|
||||
public override async Task ArchivePaymentRequest(string storeId, string paymentRequestId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
HandleActionResult(await _paymentRequestController.ArchivePaymentRequest(storeId, paymentRequestId));
|
||||
}
|
||||
|
||||
public override async Task<PaymentRequestData> CreatePaymentRequest(string storeId,
|
||||
CreatePaymentRequestRequest request, CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<PaymentRequestData>(
|
||||
await _paymentRequestController.CreatePaymentRequest(storeId, request));
|
||||
}
|
||||
|
||||
public override async Task<PaymentRequestData> UpdatePaymentRequest(string storeId, string paymentRequestId,
|
||||
UpdatePaymentRequestRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<PaymentRequestData>(
|
||||
await _paymentRequestController.UpdatePaymentRequest(storeId, paymentRequestId, request));
|
||||
}
|
||||
|
||||
public override async Task<ApiKeyData> GetCurrentAPIKeyInfo(CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult(await _apiKeysController.GetKey());
|
||||
}
|
||||
|
||||
public override async Task<ApiKeyData> CreateAPIKey(CreateApiKeyRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<ApiKeyData>(await _apiKeysController.CreateKey(request));
|
||||
}
|
||||
|
||||
public override async Task RevokeCurrentAPIKeyInfo(CancellationToken token = default)
|
||||
{
|
||||
HandleActionResult(await _apiKeysController.RevokeCurrentKey());
|
||||
}
|
||||
|
||||
public override async Task RevokeAPIKey(string apikey, CancellationToken token = default)
|
||||
{
|
||||
HandleActionResult(await _apiKeysController.RevokeKey(apikey));
|
||||
}
|
||||
|
||||
public override async Task<IEnumerable<NotificationData>> GetNotifications(bool? seen = null,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<IEnumerable<NotificationData>>(
|
||||
await _notificationsController.GetNotifications(seen));
|
||||
}
|
||||
|
||||
public override async Task<NotificationData> GetNotification(string notificationId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<NotificationData>(
|
||||
await _notificationsController.GetNotification(notificationId));
|
||||
}
|
||||
|
||||
public override async Task<NotificationData> UpdateNotification(string notificationId, bool? seen,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<NotificationData>(
|
||||
await _notificationsController.UpdateNotification(notificationId,
|
||||
new UpdateNotification() {Seen = seen}));
|
||||
}
|
||||
|
||||
public override async Task RemoveNotification(string notificationId, CancellationToken token = default)
|
||||
{
|
||||
HandleActionResult(await _notificationsController.DeleteNotification(notificationId));
|
||||
}
|
||||
|
||||
public override async Task<ApplicationUserData> GetCurrentUser(CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult(await _usersController.GetCurrentUser());
|
||||
}
|
||||
|
||||
public override async Task<ApplicationUserData> CreateUser(CreateApplicationUserRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<ApplicationUserData>(await _usersController.CreateUser(request, token));
|
||||
}
|
||||
|
||||
public override async Task<OnChainWalletOverviewData> ShowOnChainWalletOverview(string storeId,
|
||||
string cryptoCode, CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<OnChainWalletOverviewData>(
|
||||
await _storeOnChainWalletsController.ShowOnChainWalletOverview(storeId, cryptoCode));
|
||||
}
|
||||
|
||||
public override async Task<OnChainWalletAddressData> GetOnChainWalletReceiveAddress(string storeId,
|
||||
string cryptoCode, bool forceGenerate = false,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<OnChainWalletAddressData>(
|
||||
await _storeOnChainWalletsController.GetOnChainWalletReceiveAddress(storeId, cryptoCode,
|
||||
forceGenerate));
|
||||
}
|
||||
|
||||
public override async Task UnReserveOnChainWalletReceiveAddress(string storeId, string cryptoCode,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
HandleActionResult(
|
||||
await _storeOnChainWalletsController.UnReserveOnChainWalletReceiveAddress(storeId, cryptoCode));
|
||||
}
|
||||
|
||||
public override async Task<IEnumerable<OnChainWalletTransactionData>> ShowOnChainWalletTransactions(
|
||||
string storeId, string cryptoCode, TransactionStatus[] statusFilter = null,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<IEnumerable<OnChainWalletTransactionData>>(
|
||||
await _storeOnChainWalletsController.ShowOnChainWalletTransactions(storeId, cryptoCode, statusFilter));
|
||||
}
|
||||
|
||||
public override async Task<OnChainWalletTransactionData> GetOnChainWalletTransaction(string storeId,
|
||||
string cryptoCode, string transactionId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<OnChainWalletTransactionData>(
|
||||
await _storeOnChainWalletsController.GetOnChainWalletTransaction(storeId, cryptoCode, transactionId));
|
||||
}
|
||||
|
||||
public override async Task<IEnumerable<OnChainWalletUTXOData>> GetOnChainWalletUTXOs(string storeId,
|
||||
string cryptoCode, CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<IEnumerable<OnChainWalletUTXOData>>(
|
||||
await _storeOnChainWalletsController.GetOnChainWalletUTXOs(storeId, cryptoCode));
|
||||
}
|
||||
|
||||
public override async Task<OnChainWalletTransactionData> CreateOnChainTransaction(string storeId,
|
||||
string cryptoCode, CreateOnChainTransactionRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
if (!request.ProceedWithBroadcast)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(request.ProceedWithBroadcast),
|
||||
"Please use CreateOnChainTransactionButDoNotBroadcast when wanting to only create the transaction");
|
||||
}
|
||||
|
||||
return GetFromActionResult<OnChainWalletTransactionData>(
|
||||
await _storeOnChainWalletsController.CreateOnChainTransaction(storeId, cryptoCode, request));
|
||||
}
|
||||
|
||||
public override async Task<Transaction> CreateOnChainTransactionButDoNotBroadcast(string storeId,
|
||||
string cryptoCode,
|
||||
CreateOnChainTransactionRequest request, Network network, CancellationToken token = default)
|
||||
{
|
||||
if (request.ProceedWithBroadcast)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(request.ProceedWithBroadcast),
|
||||
"Please use CreateOnChainTransaction when wanting to also broadcast the transaction");
|
||||
}
|
||||
|
||||
return Transaction.Parse(
|
||||
GetFromActionResult<string>(
|
||||
await _storeOnChainWalletsController.CreateOnChainTransaction(storeId, cryptoCode, request)),
|
||||
network);
|
||||
}
|
||||
|
||||
public override async Task<IEnumerable<StoreData>> GetStores(CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult(await _storesController.GetStores());
|
||||
}
|
||||
|
||||
public override async Task<StoreData> GetStore(string storeId, CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult(await _storesController.GetStore(storeId));
|
||||
}
|
||||
|
||||
public override async Task RemoveStore(string storeId, CancellationToken token = default)
|
||||
{
|
||||
HandleActionResult(await _storesController.RemoveStore(storeId));
|
||||
}
|
||||
|
||||
public override async Task<StoreData> CreateStore(CreateStoreRequest request, CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<StoreData>(await _storesController.CreateStore(request));
|
||||
}
|
||||
|
||||
public override async Task<StoreData> UpdateStore(string storeId, UpdateStoreRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<StoreData>(await _storesController.UpdateStore(storeId, request));
|
||||
}
|
||||
|
||||
public override Task<IEnumerable<LightningNetworkPaymentMethodData>>
|
||||
GetStoreLightningNetworkPaymentMethods(string storeId, bool? enabled,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return Task.FromResult(GetFromActionResult(
|
||||
_storeLightningNetworkPaymentMethodsController.GetLightningPaymentMethods(storeId, enabled)));
|
||||
}
|
||||
|
||||
public override Task<LightningNetworkPaymentMethodData> GetStoreLightningNetworkPaymentMethod(
|
||||
string storeId, string cryptoCode, CancellationToken token = default)
|
||||
{
|
||||
return Task.FromResult(GetFromActionResult(
|
||||
_storeLightningNetworkPaymentMethodsController.GetLightningNetworkPaymentMethod(storeId, cryptoCode)));
|
||||
}
|
||||
|
||||
public override async Task RemoveStoreLightningNetworkPaymentMethod(string storeId, string cryptoCode,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
HandleActionResult(
|
||||
await _storeLightningNetworkPaymentMethodsController.RemoveLightningNetworkPaymentMethod(storeId,
|
||||
cryptoCode));
|
||||
}
|
||||
|
||||
public override async Task<LightningNetworkPaymentMethodData> UpdateStoreLightningNetworkPaymentMethod(
|
||||
string storeId, string cryptoCode,
|
||||
LightningNetworkPaymentMethodData paymentMethod, CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<LightningNetworkPaymentMethodData>(await
|
||||
_storeLightningNetworkPaymentMethodsController.UpdateLightningNetworkPaymentMethod(storeId, cryptoCode,
|
||||
paymentMethod));
|
||||
}
|
||||
|
||||
public override async Task<IEnumerable<InvoiceData>> GetInvoices(string storeId, string[] orderId = null,
|
||||
InvoiceStatus[] status = null,
|
||||
DateTimeOffset? startDate = null,
|
||||
DateTimeOffset? endDate = null,
|
||||
string textSearch = null,
|
||||
bool includeArchived = false,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<IEnumerable<InvoiceData>>(
|
||||
await _greenFieldInvoiceController.GetInvoices(storeId, orderId,
|
||||
status?.Select(invoiceStatus => invoiceStatus.ToString())?.ToArray(), startDate,
|
||||
endDate, textSearch, includeArchived));
|
||||
}
|
||||
|
||||
public override async Task<InvoiceData> GetInvoice(string storeId, string invoiceId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<InvoiceData>(await _greenFieldInvoiceController.GetInvoice(storeId, invoiceId));
|
||||
}
|
||||
|
||||
public override async Task<InvoicePaymentMethodDataModel[]> GetInvoicePaymentMethods(string storeId,
|
||||
string invoiceId, CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<InvoicePaymentMethodDataModel[]>(
|
||||
await _greenFieldInvoiceController.GetInvoicePaymentMethods(storeId, invoiceId));
|
||||
}
|
||||
|
||||
public override async Task ArchiveInvoice(string storeId, string invoiceId, CancellationToken token = default)
|
||||
{
|
||||
HandleActionResult(await _greenFieldInvoiceController.ArchiveInvoice(storeId, invoiceId));
|
||||
}
|
||||
|
||||
public override async Task<InvoiceData> CreateInvoice(string storeId, CreateInvoiceRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<InvoiceData>(await _greenFieldInvoiceController.CreateInvoice(storeId, request));
|
||||
}
|
||||
|
||||
public override async Task<InvoiceData> UpdateInvoice(string storeId, string invoiceId,
|
||||
UpdateInvoiceRequest request, CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<InvoiceData>(
|
||||
await _greenFieldInvoiceController.UpdateInvoice(storeId, invoiceId, request));
|
||||
}
|
||||
|
||||
public override async Task<InvoiceData> MarkInvoiceStatus(string storeId, string invoiceId,
|
||||
MarkInvoiceStatusRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<InvoiceData>(
|
||||
await _greenFieldInvoiceController.MarkInvoiceStatus(storeId, invoiceId, request));
|
||||
}
|
||||
|
||||
public override async Task<InvoiceData> UnarchiveInvoice(string storeId, string invoiceId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<InvoiceData>(
|
||||
await _greenFieldInvoiceController.UnarchiveInvoice(storeId, invoiceId));
|
||||
}
|
||||
|
||||
public override Task<ServerInfoData> GetServerInfo(CancellationToken token = default)
|
||||
{
|
||||
return Task.FromResult(GetFromActionResult<ServerInfoData>(_greenFieldServerInfoController.ServerInfo()));
|
||||
}
|
||||
|
||||
public override async Task ActivateInvoicePaymentMethod(string storeId, string invoiceId, string paymentMethod,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
HandleActionResult(
|
||||
await _greenFieldInvoiceController.ActivateInvoicePaymentMethod(storeId, invoiceId, paymentMethod));
|
||||
}
|
||||
|
||||
public override async Task<OnChainWalletFeeRateData> GetOnChainFeeRate(string storeId, string cryptoCode,
|
||||
int? blockTarget = null, CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<OnChainWalletFeeRateData>(
|
||||
await _storeOnChainWalletsController.GetOnChainFeeRate(storeId, cryptoCode, blockTarget));
|
||||
}
|
||||
|
||||
public override async Task DeleteCurrentUser(CancellationToken token = default)
|
||||
{
|
||||
HandleActionResult(await _usersController.DeleteCurrentUser());
|
||||
}
|
||||
|
||||
public override async Task DeleteUser(string userId, CancellationToken token = default)
|
||||
{
|
||||
HandleActionResult(await _usersController.DeleteUser(userId));
|
||||
}
|
||||
|
||||
public override Task<Language[]> GetAvailableLanguages(CancellationToken token = default)
|
||||
{
|
||||
return Task.FromResult(_homeController.LanguageService.GetLanguages().Select(language => new Language(language.Code, language.DisplayName)).ToArray());
|
||||
}
|
||||
|
||||
public override Task<PermissionMetadata[]> GetPermissionMetadata(CancellationToken token = default)
|
||||
{
|
||||
return Task.FromResult(GetFromActionResult<PermissionMetadata[]>(_homeController.Permissions()));
|
||||
}
|
||||
|
||||
public override async Task<LightningNetworkPaymentMethodData> UpdateStoreLightningNetworkPaymentMethodToInternalNode(string storeId, string cryptoCode,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
//nothing to change, just local client sugar
|
||||
return await base.UpdateStoreLightningNetworkPaymentMethodToInternalNode(storeId, cryptoCode, token);
|
||||
}
|
||||
|
||||
public override Task<Dictionary<string, GenericPaymentMethodData>> GetStorePaymentMethods(string storeId, bool? enabled = null, CancellationToken token = default)
|
||||
{
|
||||
return Task.FromResult(GetFromActionResult(_storePaymentMethodsController.GetStorePaymentMethods(storeId, enabled)));
|
||||
}
|
||||
|
||||
public override async Task<OnChainPaymentMethodDataWithSensitiveData> GenerateOnChainWallet(string storeId, string cryptoCode, GenerateOnChainWalletRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<OnChainPaymentMethodDataWithSensitiveData>(await _chainPaymentMethodsController.GenerateOnChainWallet(storeId, cryptoCode, new GenerateWalletRequest()
|
||||
{
|
||||
Passphrase = request.Passphrase,
|
||||
AccountNumber = request.AccountNumber,
|
||||
ExistingMnemonic = request.ExistingMnemonic?.ToString(),
|
||||
WordCount = request.WordCount,
|
||||
WordList = request.WordList,
|
||||
SavePrivateKeys = request.SavePrivateKeys,
|
||||
ScriptPubKeyType = request.ScriptPubKeyType,
|
||||
ImportKeysToRPC = request.ImportKeysToRPC
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
@ -33,7 +33,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
|
||||
[Authorize(Policy = Policies.CanViewPaymentRequests, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpGet("~/api/v1/stores/{storeId}/payment-requests")]
|
||||
public async Task<ActionResult<IEnumerable<Client.Models.PaymentRequestData>>> GetPaymentRequests(string storeId, bool includeArchived = false)
|
||||
public async Task<ActionResult<IEnumerable<PaymentRequestData>>> GetPaymentRequests(string storeId, bool includeArchived = false)
|
||||
{
|
||||
var prs = await _paymentRequestRepository.FindPaymentRequests(
|
||||
new PaymentRequestQuery() { StoreId = storeId, IncludeArchived = includeArchived });
|
||||
@ -42,7 +42,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
|
||||
[Authorize(Policy = Policies.CanViewPaymentRequests, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpGet("~/api/v1/stores/{storeId}/payment-requests/{paymentRequestId}")]
|
||||
public async Task<ActionResult<Client.Models.PaymentRequestData>> GetPaymentRequest(string storeId, string paymentRequestId)
|
||||
public async Task<ActionResult<PaymentRequestData>> GetPaymentRequest(string storeId, string paymentRequestId)
|
||||
{
|
||||
var pr = await _paymentRequestRepository.FindPaymentRequests(
|
||||
new PaymentRequestQuery() { StoreId = storeId, Ids = new[] { paymentRequestId } });
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -32,15 +31,13 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
private readonly CurrencyNameTable _currencyNameTable;
|
||||
private readonly BTCPayNetworkJsonSerializerSettings _serializerSettings;
|
||||
private readonly BTCPayNetworkProvider _networkProvider;
|
||||
private readonly IEnumerable<IPayoutHandler> _payoutHandlers;
|
||||
|
||||
public GreenfieldPullPaymentController(PullPaymentHostedService pullPaymentService,
|
||||
LinkGenerator linkGenerator,
|
||||
ApplicationDbContextFactory dbContextFactory,
|
||||
CurrencyNameTable currencyNameTable,
|
||||
Services.BTCPayNetworkJsonSerializerSettings serializerSettings,
|
||||
BTCPayNetworkProvider networkProvider,
|
||||
IEnumerable<IPayoutHandler> payoutHandlers)
|
||||
BTCPayNetworkProvider networkProvider)
|
||||
{
|
||||
_pullPaymentService = pullPaymentService;
|
||||
_linkGenerator = linkGenerator;
|
||||
@ -48,7 +45,6 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
_currencyNameTable = currencyNameTable;
|
||||
_serializerSettings = serializerSettings;
|
||||
_networkProvider = networkProvider;
|
||||
_payoutHandlers = payoutHandlers;
|
||||
}
|
||||
|
||||
[HttpGet("~/api/v1/stores/{storeId}/pull-payments")]
|
||||
@ -132,7 +128,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
StoreId = storeId,
|
||||
PaymentMethodIds = paymentMethods
|
||||
});
|
||||
var pp = await _pullPaymentService.GetPullPayment(ppId, false);
|
||||
var pp = await _pullPaymentService.GetPullPayment(ppId);
|
||||
return this.Ok(CreatePullPaymentData(pp));
|
||||
}
|
||||
|
||||
@ -164,10 +160,10 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
public async Task<IActionResult> GetPullPayment(string pullPaymentId)
|
||||
{
|
||||
if (pullPaymentId is null)
|
||||
return PullPaymentNotFound();
|
||||
var pp = await _pullPaymentService.GetPullPayment(pullPaymentId, false);
|
||||
return NotFound();
|
||||
var pp = await _pullPaymentService.GetPullPayment(pullPaymentId);
|
||||
if (pp is null)
|
||||
return PullPaymentNotFound();
|
||||
return NotFound();
|
||||
return Ok(CreatePullPaymentData(pp));
|
||||
}
|
||||
|
||||
@ -176,33 +172,19 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
public async Task<IActionResult> GetPayouts(string pullPaymentId, bool includeCancelled = false)
|
||||
{
|
||||
if (pullPaymentId is null)
|
||||
return PullPaymentNotFound();
|
||||
var pp = await _pullPaymentService.GetPullPayment(pullPaymentId, true);
|
||||
return NotFound();
|
||||
var pp = await _pullPaymentService.GetPullPayment(pullPaymentId);
|
||||
if (pp is null)
|
||||
return PullPaymentNotFound();
|
||||
var payouts = pp.Payouts .Where(p => p.State != PayoutState.Cancelled || includeCancelled).ToList();
|
||||
return NotFound();
|
||||
using var ctx = _dbContextFactory.CreateContext();
|
||||
var payouts = await ctx.Payouts.Where(p => p.PullPaymentDataId == pullPaymentId)
|
||||
.Where(p => p.State != Data.PayoutState.Cancelled || includeCancelled)
|
||||
.ToListAsync();
|
||||
var cd = _currencyNameTable.GetCurrencyData(pp.GetBlob().Currency, false);
|
||||
return base.Ok(payouts
|
||||
.Select(p => ToModel(p, cd)).ToList());
|
||||
}
|
||||
|
||||
[HttpGet("~/api/v1/pull-payments/{pullPaymentId}/payouts/{payoutId}")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> GetPayout(string pullPaymentId, string payoutId)
|
||||
{
|
||||
if (payoutId is null)
|
||||
return PayoutNotFound();
|
||||
await using var ctx = _dbContextFactory.CreateContext();
|
||||
var pp = await _pullPaymentService.GetPullPayment(pullPaymentId, true);
|
||||
if (pp is null)
|
||||
return PullPaymentNotFound();
|
||||
var payout = pp.Payouts.FirstOrDefault(p => p.Id == payoutId);
|
||||
if(payout is null )
|
||||
return PayoutNotFound();
|
||||
var cd = _currencyNameTable.GetCurrencyData(payout.PullPaymentData.GetBlob().Currency, false);
|
||||
return base.Ok(ToModel(payout, cd));
|
||||
}
|
||||
|
||||
private Client.Models.PayoutData ToModel(Data.PayoutData p, CurrencyData cd)
|
||||
{
|
||||
var blob = p.GetBlob(_serializerSettings);
|
||||
@ -214,9 +196,14 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
Amount = blob.Amount,
|
||||
PaymentMethodAmount = blob.CryptoAmount,
|
||||
Revision = blob.Revision,
|
||||
State = p.State
|
||||
State = p.State == Data.PayoutState.AwaitingPayment ? Client.Models.PayoutState.AwaitingPayment :
|
||||
p.State == Data.PayoutState.AwaitingApproval ? Client.Models.PayoutState.AwaitingApproval :
|
||||
p.State == Data.PayoutState.Cancelled ? Client.Models.PayoutState.Cancelled :
|
||||
p.State == Data.PayoutState.Completed ? Client.Models.PayoutState.Completed :
|
||||
p.State == Data.PayoutState.InProgress ? Client.Models.PayoutState.InProgress :
|
||||
throw new NotSupportedException(),
|
||||
};
|
||||
model.Destination = blob.Destination;
|
||||
model.Destination = blob.Destination.ToString();
|
||||
model.PaymentMethod = p.PaymentMethodId;
|
||||
return model;
|
||||
}
|
||||
@ -227,26 +214,21 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
{
|
||||
if (request is null)
|
||||
return NotFound();
|
||||
if (!PaymentMethodId.TryParse(request?.PaymentMethod, out var paymentMethodId))
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.PaymentMethod), "Invalid payment method");
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
|
||||
var payoutHandler = _payoutHandlers.FirstOrDefault(handler => handler.CanHandle(paymentMethodId));
|
||||
if (payoutHandler is null)
|
||||
|
||||
var network = request?.PaymentMethod is string paymentMethod ?
|
||||
this._networkProvider.GetNetwork<BTCPayNetwork>(paymentMethod) : null;
|
||||
if (network is null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.PaymentMethod), "Invalid payment method");
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
|
||||
await using var ctx = _dbContextFactory.CreateContext();
|
||||
using var ctx = _dbContextFactory.CreateContext();
|
||||
var pp = await ctx.PullPayments.FindAsync(pullPaymentId);
|
||||
if (pp is null)
|
||||
return PullPaymentNotFound();
|
||||
return NotFound();
|
||||
var ppBlob = pp.GetBlob();
|
||||
IClaimDestination destination = await payoutHandler.ParseClaimDestination(paymentMethodId,request.Destination);
|
||||
if (destination is null)
|
||||
if (request.Destination is null || !ClaimDestination.TryParse(request.Destination, network, out var destination))
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.Destination), "The destination must be an address or a BIP21 URI");
|
||||
return this.CreateValidationError(ModelState);
|
||||
@ -263,7 +245,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
Destination = destination,
|
||||
PullPaymentId = pullPaymentId,
|
||||
Value = request.Amount,
|
||||
PaymentMethodId = paymentMethodId
|
||||
PaymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike)
|
||||
});
|
||||
switch (result.Result)
|
||||
{
|
||||
@ -296,7 +278,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
using var ctx = _dbContextFactory.CreateContext();
|
||||
var pp = await ctx.PullPayments.FindAsync(pullPaymentId);
|
||||
if (pp is null || pp.StoreId != storeId)
|
||||
return PullPaymentNotFound();
|
||||
return NotFound();
|
||||
await _pullPaymentService.Cancel(new PullPaymentHostedService.CancelRequest(pullPaymentId));
|
||||
return Ok();
|
||||
}
|
||||
@ -308,7 +290,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
using var ctx = _dbContextFactory.CreateContext();
|
||||
var payout = await ctx.Payouts.GetPayout(payoutId, storeId);
|
||||
if (payout is null)
|
||||
return PayoutNotFound();
|
||||
return NotFound();
|
||||
await _pullPaymentService.Cancel(new PullPaymentHostedService.CancelRequest(new[] { payoutId }));
|
||||
return Ok();
|
||||
}
|
||||
@ -328,7 +310,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
return this.CreateValidationError(ModelState);
|
||||
var payout = await ctx.Payouts.GetPayout(payoutId, storeId, true, true);
|
||||
if (payout is null)
|
||||
return PayoutNotFound();
|
||||
return NotFound();
|
||||
RateResult rateResult = null;
|
||||
try
|
||||
{
|
||||
@ -363,46 +345,10 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
case PullPaymentHostedService.PayoutApproval.Result.OldRevision:
|
||||
return this.CreateAPIError("old-revision", errorMessage);
|
||||
case PullPaymentHostedService.PayoutApproval.Result.NotFound:
|
||||
return PayoutNotFound();
|
||||
return NotFound();
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("~/api/v1/stores/{storeId}/payouts/{payoutId}/mark-paid")]
|
||||
[Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
public async Task<IActionResult> MarkPayoutPaid(string storeId, string payoutId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
return this.CreateValidationError(ModelState);
|
||||
|
||||
var result = await _pullPaymentService.MarkPaid(new PayoutPaidRequest()
|
||||
{
|
||||
//TODO: Allow API to specify the manual proof object
|
||||
Proof = null,
|
||||
PayoutId = payoutId
|
||||
});
|
||||
var errorMessage = PayoutPaidRequest.GetErrorMessage(result);
|
||||
switch (result)
|
||||
{
|
||||
case PayoutPaidRequest.PayoutPaidResult.Ok:
|
||||
return Ok();
|
||||
case PayoutPaidRequest.PayoutPaidResult.InvalidState:
|
||||
return this.CreateAPIError("invalid-state", errorMessage);
|
||||
case PayoutPaidRequest.PayoutPaidResult.NotFound:
|
||||
return PayoutNotFound();
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
private IActionResult PayoutNotFound()
|
||||
{
|
||||
return this.CreateAPIError(404, "payout-not-found", "The payout was not found");
|
||||
}
|
||||
private IActionResult PullPaymentNotFound()
|
||||
{
|
||||
return this.CreateAPIError(404, "pullpayment-not-found", "The pull payment was not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.HostedServices;
|
||||
using BTCPayServer.Security;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NBXplorer.Models;
|
||||
|
||||
namespace BTCPayServer.Controllers.GreenField
|
||||
{
|
||||
@ -17,41 +21,59 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
public class GreenFieldServerInfoController : Controller
|
||||
{
|
||||
private readonly BTCPayServerEnvironment _env;
|
||||
private readonly NBXplorerDashboard _dashBoard;
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly BTCPayNetworkProvider _networkProvider;
|
||||
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
|
||||
private readonly IEnumerable<ISyncSummaryProvider> _summaryProviders;
|
||||
|
||||
public GreenFieldServerInfoController(
|
||||
BTCPayServerEnvironment env,
|
||||
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary,
|
||||
IEnumerable<ISyncSummaryProvider>summaryProviders)
|
||||
NBXplorerDashboard dashBoard,
|
||||
StoreRepository storeRepository,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
BTCPayNetworkProvider networkProvider,
|
||||
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary)
|
||||
{
|
||||
_env = env;
|
||||
_dashBoard = dashBoard;
|
||||
_storeRepository = storeRepository;
|
||||
_userManager = userManager;
|
||||
_networkProvider = networkProvider;
|
||||
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
|
||||
_summaryProviders = summaryProviders;
|
||||
}
|
||||
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpGet("~/api/v1/server/info")]
|
||||
public ActionResult ServerInfo()
|
||||
public async Task<ActionResult> ServerInfo()
|
||||
{
|
||||
var stores = await _storeRepository.GetStoresByUserId(_userManager.GetUserId(User));
|
||||
var supportedPaymentMethods = _paymentMethodHandlerDictionary
|
||||
.SelectMany(handler => handler.GetSupportedPaymentMethods().Select(id => id.ToString()))
|
||||
.Distinct();
|
||||
|
||||
ServerInfoData model = new ServerInfoData2
|
||||
var syncStatus = _dashBoard.GetAll()
|
||||
.Where(summary => summary.Network.ShowSyncSummary)
|
||||
.Select(summary => new ServerInfoSyncStatusData
|
||||
{
|
||||
CryptoCode = summary.Network.CryptoCode,
|
||||
NodeInformation = summary.Status.BitcoinStatus is BitcoinStatus s ? new ServerInfoNodeData()
|
||||
{
|
||||
Headers = s.Headers,
|
||||
Blocks = s.Blocks,
|
||||
VerificationProgress = s.VerificationProgress
|
||||
} : null,
|
||||
ChainHeight = summary.Status.ChainHeight,
|
||||
SyncHeight = summary.Status.SyncHeight
|
||||
});
|
||||
ServerInfoData model = new ServerInfoData
|
||||
{
|
||||
FullySynched = _summaryProviders.All(provider => provider.AllAvailable()),
|
||||
SyncStatus = _summaryProviders.SelectMany(provider => provider.GetStatuses()),
|
||||
FullySynched = _dashBoard.IsFullySynched(),
|
||||
SyncStatus = syncStatus,
|
||||
Onion = _env.OnionUrl,
|
||||
Version = _env.Version,
|
||||
SupportedPaymentMethods = supportedPaymentMethods
|
||||
};
|
||||
return Ok(model);
|
||||
}
|
||||
|
||||
public class ServerInfoData2 : ServerInfoData
|
||||
{
|
||||
public new IEnumerable<ISyncStatus> SyncStatus { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,7 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Configuration;
|
||||
@ -14,6 +11,7 @@ using BTCPayServer.Lightning;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.Lightning;
|
||||
using BTCPayServer.Security;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@ -29,54 +27,45 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
private StoreData Store => HttpContext.GetStoreData();
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
|
||||
private readonly IOptions<LightningNetworkOptions> _lightningNetworkOptions;
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
private readonly ISettingsRepository _settingsRepository;
|
||||
private readonly CssThemeManager _cssThemeManager;
|
||||
|
||||
public StoreLightningNetworkPaymentMethodsController(
|
||||
StoreRepository storeRepository,
|
||||
BTCPayNetworkProvider btcPayNetworkProvider,
|
||||
IOptions<LightningNetworkOptions> lightningNetworkOptions,
|
||||
IAuthorizationService authorizationService,
|
||||
ISettingsRepository settingsRepository)
|
||||
CssThemeManager cssThemeManager)
|
||||
{
|
||||
_storeRepository = storeRepository;
|
||||
_btcPayNetworkProvider = btcPayNetworkProvider;
|
||||
_lightningNetworkOptions = lightningNetworkOptions;
|
||||
_authorizationService = authorizationService;
|
||||
_settingsRepository = settingsRepository;
|
||||
}
|
||||
|
||||
public static IEnumerable<LightningNetworkPaymentMethodData> GetLightningPaymentMethods(StoreData store,
|
||||
BTCPayNetworkProvider networkProvider, bool? enabled)
|
||||
{
|
||||
var blob = store.GetStoreBlob();
|
||||
var excludedPaymentMethods = blob.GetExcludedPaymentMethods();
|
||||
|
||||
return store.GetSupportedPaymentMethods(networkProvider)
|
||||
.Where((method) => method.PaymentId.PaymentType == PaymentTypes.LightningLike)
|
||||
.OfType<LightningSupportedPaymentMethod>()
|
||||
.Select(paymentMethod =>
|
||||
new LightningNetworkPaymentMethodData(
|
||||
paymentMethod.PaymentId.CryptoCode,
|
||||
paymentMethod.GetExternalLightningUrl()?.ToString() ??
|
||||
paymentMethod.GetDisplayableConnectionString(),
|
||||
!excludedPaymentMethods.Match(paymentMethod.PaymentId)
|
||||
)
|
||||
)
|
||||
.Where((result) => enabled is null || enabled == result.Enabled)
|
||||
.ToList();
|
||||
_cssThemeManager = cssThemeManager;
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpGet("~/api/v1/stores/{storeId}/payment-methods/LightningNetwork")]
|
||||
public ActionResult<IEnumerable<LightningNetworkPaymentMethodData>> GetLightningPaymentMethods(
|
||||
string storeId,
|
||||
[FromQuery] bool? enabled)
|
||||
[FromQuery] bool enabledOnly = false)
|
||||
{
|
||||
return Ok(GetLightningPaymentMethods(Store, _btcPayNetworkProvider, enabled));
|
||||
var blob = Store.GetStoreBlob();
|
||||
var excludedPaymentMethods = blob.GetExcludedPaymentMethods();
|
||||
return Ok(Store.GetSupportedPaymentMethods(_btcPayNetworkProvider)
|
||||
.Where((method) => method.PaymentId.PaymentType == PaymentTypes.LightningLike)
|
||||
.OfType<LightningSupportedPaymentMethod>()
|
||||
.Select(paymentMethod =>
|
||||
new LightningNetworkPaymentMethodData(paymentMethod.PaymentId.CryptoCode,
|
||||
paymentMethod.GetExternalLightningUrl().ToString(), !excludedPaymentMethods.Match(paymentMethod.PaymentId)))
|
||||
.Where((result) => !enabledOnly || result.Enabled)
|
||||
.ToList()
|
||||
);
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpGet("~/api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}")]
|
||||
public ActionResult<LightningNetworkPaymentMethodData> GetLightningNetworkPaymentMethod(string storeId, string cryptoCode)
|
||||
public ActionResult<LightningNetworkPaymentMethodData> GetLightningNetworkPaymentMethod(string cryptoCode)
|
||||
{
|
||||
if (!GetNetwork(cryptoCode, out BTCPayNetwork _))
|
||||
{
|
||||
@ -88,14 +77,12 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(method);
|
||||
}
|
||||
|
||||
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpDelete("~/api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}")]
|
||||
public async Task<IActionResult> RemoveLightningNetworkPaymentMethod(
|
||||
string storeId,
|
||||
string cryptoCode,
|
||||
int offset = 0, int amount = 10)
|
||||
{
|
||||
@ -103,7 +90,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
|
||||
var id = new PaymentMethodId(cryptoCode, PaymentTypes.LightningLike);
|
||||
var store = Store;
|
||||
store.SetSupportedPaymentMethod(id, null);
|
||||
@ -113,7 +100,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpPut("~/api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}")]
|
||||
public async Task<IActionResult> UpdateLightningNetworkPaymentMethod(string storeId, string cryptoCode,
|
||||
public async Task<IActionResult> UpdateLightningNetworkPaymentMethod(string cryptoCode,
|
||||
[FromBody] LightningNetworkPaymentMethodData paymentMethodData)
|
||||
{
|
||||
var paymentMethodId = new PaymentMethodId(cryptoCode, PaymentTypes.LightningLike);
|
||||
@ -123,7 +110,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(paymentMethodData.ConnectionString))
|
||||
if (string.IsNullOrEmpty(paymentMethodData?.ConnectionString))
|
||||
{
|
||||
ModelState.AddModelError(nameof(LightningNetworkPaymentMethodData.ConnectionString),
|
||||
"Missing connectionString");
|
||||
@ -132,18 +119,16 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
if (!ModelState.IsValid)
|
||||
return this.CreateValidationError(ModelState);
|
||||
|
||||
LightningSupportedPaymentMethod? paymentMethod = null;
|
||||
if (!string.IsNullOrEmpty(paymentMethodData!.ConnectionString))
|
||||
LightningSupportedPaymentMethod paymentMethod = null;
|
||||
if (!string.IsNullOrEmpty(paymentMethodData.ConnectionString))
|
||||
{
|
||||
if (paymentMethodData.ConnectionString == LightningSupportedPaymentMethod.InternalNode)
|
||||
{
|
||||
if (!await CanUseInternalLightning())
|
||||
{
|
||||
ModelState.AddModelError(nameof(paymentMethodData.ConnectionString),
|
||||
$"You are not authorized to use the internal lightning node");
|
||||
ModelState.AddModelError(nameof(paymentMethodData.ConnectionString), $"You are not authorized to use the internal lightning node");
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
|
||||
paymentMethod = new Payments.Lightning.LightningSupportedPaymentMethod()
|
||||
{
|
||||
CryptoCode = paymentMethodId.CryptoCode
|
||||
@ -158,21 +143,17 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
ModelState.AddModelError(nameof(paymentMethodData.ConnectionString), $"Invalid URL ({error})");
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
|
||||
if (connectionString.ConnectionType == LightningConnectionType.LndGRPC)
|
||||
{
|
||||
ModelState.AddModelError(nameof(paymentMethodData.ConnectionString),
|
||||
$"BTCPay does not support gRPC connections");
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
|
||||
if (!await CanManageServer() && !connectionString.IsSafe())
|
||||
{
|
||||
ModelState.AddModelError(nameof(paymentMethodData.ConnectionString),
|
||||
$"You do not have 'btcpay.server.canmodifyserversettings' rights, so the connection string should not contain 'cookiefilepath', 'macaroondirectorypath', 'macaroonfilepath', and should not point to a local ip or to a dns name ending with '.internal', '.local', '.lan' or '.'.");
|
||||
ModelState.AddModelError(nameof(paymentMethodData.ConnectionString), $"You do not have 'btcpay.server.canmodifyserversettings' rights, so the connection string should not contain 'cookiefilepath', 'macaroondirectorypath', 'macaroonfilepath', and should not point to a local ip or to a dns name ending with '.internal', '.local', '.lan' or '.'.");
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
|
||||
paymentMethod = new Payments.Lightning.LightningSupportedPaymentMethod()
|
||||
{
|
||||
CryptoCode = paymentMethodId.CryptoCode
|
||||
@ -190,8 +171,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
return Ok(GetExistingLightningLikePaymentMethod(cryptoCode, store));
|
||||
}
|
||||
|
||||
private LightningNetworkPaymentMethodData? GetExistingLightningLikePaymentMethod(string cryptoCode,
|
||||
StoreData? store = null)
|
||||
private LightningNetworkPaymentMethodData GetExistingLightningLikePaymentMethod(string cryptoCode, StoreData store = null)
|
||||
{
|
||||
store ??= Store;
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
@ -202,29 +182,28 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
.FirstOrDefault(method => method.PaymentId == id);
|
||||
|
||||
var excluded = storeBlob.IsExcluded(id);
|
||||
return paymentMethod is null
|
||||
return paymentMethod == null
|
||||
? null
|
||||
: new LightningNetworkPaymentMethodData(paymentMethod.PaymentId.CryptoCode,
|
||||
paymentMethod.GetDisplayableConnectionString(), !excluded);
|
||||
}
|
||||
|
||||
private bool GetNetwork(string cryptoCode, [MaybeNullWhen(false)] out BTCPayNetwork network)
|
||||
private bool GetNetwork(string cryptoCode, out BTCPayNetwork network)
|
||||
{
|
||||
network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
|
||||
network = network?.SupportLightning is true ? network : null;
|
||||
return network != null;
|
||||
}
|
||||
|
||||
|
||||
private async Task<bool> CanUseInternalLightning()
|
||||
{
|
||||
return (await _settingsRepository.GetPolicies()).AllowLightningInternalNodeForAll ||
|
||||
(await _authorizationService.AuthorizeAsync(User, null,
|
||||
new PolicyRequirement(Policies.CanUseInternalLightningNode))).Succeeded;
|
||||
return _cssThemeManager.AllowLightningInternalNodeForAll ||
|
||||
(await _authorizationService.AuthorizeAsync(User, null,
|
||||
new PolicyRequirement(Policies.CanUseInternalLightningNode))).Succeeded;
|
||||
}
|
||||
|
||||
private async Task<bool> CanManageServer()
|
||||
{
|
||||
return
|
||||
return
|
||||
(await _authorizationService.AuthorizeAsync(User, null,
|
||||
new PolicyRequirement(Policies.CanModifyServerSettings))).Succeeded;
|
||||
}
|
||||
|
@ -1,103 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Payments;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NBXplorer.Models;
|
||||
|
||||
namespace BTCPayServer.Controllers.GreenField
|
||||
{
|
||||
public partial class StoreOnChainPaymentMethodsController
|
||||
{
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpPost("~/api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/generate")]
|
||||
public async Task<IActionResult> GenerateOnChainWallet(string storeId, string cryptoCode,
|
||||
GenerateWalletRequest request)
|
||||
{
|
||||
var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
|
||||
if (network is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (!_walletProvider.IsAvailable(network))
|
||||
{
|
||||
return this.CreateAPIError("not-available",
|
||||
$"{cryptoCode} services are not currently available");
|
||||
}
|
||||
|
||||
var method = GetExistingBtcLikePaymentMethod(cryptoCode);
|
||||
if (method != null)
|
||||
{
|
||||
return this.CreateAPIError("already-configured",
|
||||
$"{cryptoCode} wallet is already configured for this store");
|
||||
}
|
||||
|
||||
var canUseHotWallet = await CanUseHotWallet();
|
||||
if (request.SavePrivateKeys && !canUseHotWallet.HotWallet)
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.SavePrivateKeys),
|
||||
"This instance forbids non-admins from having a hot wallet for your store.");
|
||||
}
|
||||
|
||||
if (request.ImportKeysToRPC && !canUseHotWallet.RPCImport)
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.ImportKeysToRPC),
|
||||
"This instance forbids non-admins from having importing the wallet addresses/keys to the underlying node.");
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
|
||||
var client = _explorerClientProvider.GetExplorerClient(network);
|
||||
GenerateWalletResponse response;
|
||||
try
|
||||
{
|
||||
response = await client.GenerateWalletAsync(request);
|
||||
if (response == null)
|
||||
{
|
||||
return this.CreateAPIError("not-available",
|
||||
$"{cryptoCode} services are not currently available");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return this.CreateAPIError("not-available",
|
||||
$"{cryptoCode} error: {e.Message}");
|
||||
}
|
||||
|
||||
var derivationSchemeSettings = new DerivationSchemeSettings(response.DerivationScheme, network);
|
||||
|
||||
derivationSchemeSettings.Source =
|
||||
string.IsNullOrEmpty(request.ExistingMnemonic) ? "NBXplorerGenerated" : "ImportedSeed";
|
||||
derivationSchemeSettings.IsHotWallet = request.SavePrivateKeys;
|
||||
|
||||
var accountSettings = derivationSchemeSettings.GetSigningAccountKeySettings();
|
||||
accountSettings.AccountKeyPath = response.AccountKeyPath.KeyPath;
|
||||
accountSettings.RootFingerprint = response.AccountKeyPath.MasterFingerprint;
|
||||
derivationSchemeSettings.AccountOriginal = response.DerivationScheme.ToString();
|
||||
|
||||
var store = Store;
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
store.SetSupportedPaymentMethod(new PaymentMethodId(cryptoCode, PaymentTypes.BTCLike),
|
||||
derivationSchemeSettings);
|
||||
store.SetStoreBlob(storeBlob);
|
||||
await _storeRepository.UpdateStore(store);
|
||||
var rawResult = GetExistingBtcLikePaymentMethod(cryptoCode, store);
|
||||
var result = new OnChainPaymentMethodDataWithSensitiveData(rawResult.CryptoCode, rawResult.DerivationScheme,
|
||||
rawResult.Enabled, rawResult.Label, rawResult.AccountKeyPath, response.GetMnemonic());
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
private async Task<(bool HotWallet, bool RPCImport)> CanUseHotWallet()
|
||||
{
|
||||
return await _authorizationService.CanUseHotWallet(await _settingsRepository.GetPolicies(), User);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.HostedServices;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using BTCPayServer.Services.Wallets;
|
||||
@ -14,68 +13,50 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NBitcoin;
|
||||
using NBXplorer.DerivationStrategy;
|
||||
using NBXplorer.Models;
|
||||
using StoreData = BTCPayServer.Data.StoreData;
|
||||
|
||||
namespace BTCPayServer.Controllers.GreenField
|
||||
{
|
||||
[ApiController]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
public partial class StoreOnChainPaymentMethodsController : ControllerBase
|
||||
public class StoreOnChainPaymentMethodsController : ControllerBase
|
||||
{
|
||||
private StoreData Store => HttpContext.GetStoreData();
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
|
||||
private readonly BTCPayWalletProvider _walletProvider;
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
private readonly ISettingsRepository _settingsRepository;
|
||||
private readonly ExplorerClientProvider _explorerClientProvider;
|
||||
|
||||
public StoreOnChainPaymentMethodsController(
|
||||
StoreRepository storeRepository,
|
||||
BTCPayNetworkProvider btcPayNetworkProvider,
|
||||
BTCPayWalletProvider walletProvider,
|
||||
IAuthorizationService authorizationService,
|
||||
ExplorerClientProvider explorerClientProvider, ISettingsRepository settingsRepository)
|
||||
BTCPayWalletProvider walletProvider)
|
||||
{
|
||||
_storeRepository = storeRepository;
|
||||
_btcPayNetworkProvider = btcPayNetworkProvider;
|
||||
_walletProvider = walletProvider;
|
||||
_authorizationService = authorizationService;
|
||||
_explorerClientProvider = explorerClientProvider;
|
||||
_settingsRepository = settingsRepository;
|
||||
}
|
||||
|
||||
public static IEnumerable<OnChainPaymentMethodData> GetOnChainPaymentMethods(StoreData store,
|
||||
BTCPayNetworkProvider networkProvider, bool? enabled)
|
||||
{
|
||||
var blob = store.GetStoreBlob();
|
||||
var excludedPaymentMethods = blob.GetExcludedPaymentMethods();
|
||||
|
||||
return store.GetSupportedPaymentMethods(networkProvider)
|
||||
.Where((method) => method.PaymentId.PaymentType == PaymentTypes.BTCLike)
|
||||
.OfType<DerivationSchemeSettings>()
|
||||
.Select(strategy =>
|
||||
new OnChainPaymentMethodData(strategy.PaymentId.CryptoCode,
|
||||
strategy.AccountDerivation.ToString(), !excludedPaymentMethods.Match(strategy.PaymentId), strategy.Label, strategy.GetSigningAccountKeySettings().GetRootedKeyPath()))
|
||||
.Where((result) => enabled is null || enabled == result.Enabled)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpGet("~/api/v1/stores/{storeId}/payment-methods/onchain")]
|
||||
public ActionResult<IEnumerable<OnChainPaymentMethodData>> GetOnChainPaymentMethods(
|
||||
string storeId,
|
||||
[FromQuery] bool? enabled)
|
||||
[FromQuery] bool enabledOnly = false)
|
||||
{
|
||||
return Ok(GetOnChainPaymentMethods(Store, _btcPayNetworkProvider, enabled));
|
||||
var blob = Store.GetStoreBlob();
|
||||
var excludedPaymentMethods = blob.GetExcludedPaymentMethods();
|
||||
return Ok(Store.GetSupportedPaymentMethods(_btcPayNetworkProvider)
|
||||
.Where((method) => method.PaymentId.PaymentType == PaymentTypes.BTCLike)
|
||||
.OfType<DerivationSchemeSettings>()
|
||||
.Select(strategy =>
|
||||
new OnChainPaymentMethodData(strategy.PaymentId.CryptoCode,
|
||||
strategy.AccountDerivation.ToString(), !excludedPaymentMethods.Match(strategy.PaymentId)))
|
||||
.Where((result) => !enabledOnly || result.Enabled)
|
||||
.ToList()
|
||||
);
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpGet("~/api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}")]
|
||||
public ActionResult<OnChainPaymentMethodData> GetOnChainPaymentMethod(
|
||||
string storeId,
|
||||
string cryptoCode)
|
||||
public ActionResult<OnChainPaymentMethodData> GetOnChainPaymentMethod(string cryptoCode)
|
||||
{
|
||||
if (!GetCryptoCodeWallet(cryptoCode, out BTCPayNetwork _, out BTCPayWallet _))
|
||||
{
|
||||
@ -87,14 +68,12 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(method);
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpGet("~/api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/preview")]
|
||||
public IActionResult GetOnChainPaymentMethodPreview(
|
||||
string storeId,
|
||||
string cryptoCode,
|
||||
int offset = 0, int amount = 10)
|
||||
{
|
||||
@ -108,7 +87,6 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var strategy = DerivationSchemeSettings.Parse(paymentMethod.DerivationScheme, network);
|
||||
@ -127,7 +105,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
.ToString()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
catch
|
||||
@ -136,14 +114,13 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
"Invalid Derivation Scheme");
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
[Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
|
||||
|
||||
[Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpPost("~/api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/preview")]
|
||||
public IActionResult GetProposedOnChainPaymentMethodPreview(
|
||||
string storeId,
|
||||
string cryptoCode,
|
||||
public IActionResult GetProposedOnChainPaymentMethodPreview(string cryptoCode,
|
||||
[FromBody] OnChainPaymentMethodData paymentMethodData,
|
||||
int offset = 0, int amount = 10)
|
||||
{
|
||||
@ -151,13 +128,11 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(paymentMethodData?.DerivationScheme))
|
||||
{
|
||||
ModelState.AddModelError(nameof(OnChainPaymentMethodData.DerivationScheme),
|
||||
"Missing derivationScheme");
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
return this.CreateValidationError(ModelState);
|
||||
DerivationSchemeSettings strategy;
|
||||
@ -196,7 +171,6 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpDelete("~/api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}")]
|
||||
public async Task<IActionResult> RemoveOnChainPaymentMethod(
|
||||
string storeId,
|
||||
string cryptoCode,
|
||||
int offset = 0, int amount = 10)
|
||||
{
|
||||
@ -204,7 +178,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
|
||||
var id = new PaymentMethodId(cryptoCode, PaymentTypes.BTCLike);
|
||||
var store = Store;
|
||||
store.SetSupportedPaymentMethod(id, null);
|
||||
@ -214,9 +188,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpPut("~/api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}")]
|
||||
public async Task<IActionResult> UpdateOnChainPaymentMethod(
|
||||
string storeId,
|
||||
string cryptoCode,
|
||||
public async Task<IActionResult> UpdateOnChainPaymentMethod(string cryptoCode,
|
||||
[FromBody] OnChainPaymentMethodData paymentMethodData)
|
||||
{
|
||||
var id = new PaymentMethodId(cryptoCode, PaymentTypes.BTCLike);
|
||||
@ -231,7 +203,6 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
ModelState.AddModelError(nameof(OnChainPaymentMethodData.DerivationScheme),
|
||||
"Missing derivationScheme");
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
return this.CreateValidationError(ModelState);
|
||||
|
||||
@ -254,7 +225,6 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
signing.AccountKeyPath = null;
|
||||
signing.RootFingerprint = null;
|
||||
}
|
||||
|
||||
store.SetSupportedPaymentMethod(id, strategy);
|
||||
storeBlob.SetExcluded(id, !paymentMethodData.Enabled);
|
||||
store.SetStoreBlob(storeBlob);
|
||||
@ -290,8 +260,11 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
return paymentMethod == null
|
||||
? null
|
||||
: new OnChainPaymentMethodData(paymentMethod.PaymentId.CryptoCode,
|
||||
paymentMethod.AccountDerivation.ToString(), !excluded, paymentMethod.Label,
|
||||
paymentMethod.GetSigningAccountKeySettings().GetRootedKeyPath());
|
||||
paymentMethod.AccountDerivation.ToString(), !excluded)
|
||||
{
|
||||
Label = paymentMethod.Label,
|
||||
AccountKeyPath = paymentMethod.GetSigningAccountKeySettings().GetRootedKeyPath()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,16 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.BIP78.Sender;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.HostedServices;
|
||||
using BTCPayServer.Models.WalletViewModels;
|
||||
using BTCPayServer.Payments.PayJoin;
|
||||
using BTCPayServer.Payments.PayJoin.Sender;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Wallets;
|
||||
@ -41,7 +37,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
|
||||
private readonly WalletRepository _walletRepository;
|
||||
private readonly ExplorerClientProvider _explorerClientProvider;
|
||||
private readonly ISettingsRepository _settingsRepository;
|
||||
private readonly CssThemeManager _cssThemeManager;
|
||||
private readonly NBXplorerDashboard _nbXplorerDashboard;
|
||||
private readonly WalletsController _walletsController;
|
||||
private readonly PayjoinClient _payjoinClient;
|
||||
@ -56,7 +52,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
BTCPayNetworkProvider btcPayNetworkProvider,
|
||||
WalletRepository walletRepository,
|
||||
ExplorerClientProvider explorerClientProvider,
|
||||
ISettingsRepository settingsRepository,
|
||||
CssThemeManager cssThemeManager,
|
||||
NBXplorerDashboard nbXplorerDashboard,
|
||||
WalletsController walletsController,
|
||||
PayjoinClient payjoinClient,
|
||||
@ -70,7 +66,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
_btcPayNetworkProvider = btcPayNetworkProvider;
|
||||
_walletRepository = walletRepository;
|
||||
_explorerClientProvider = explorerClientProvider;
|
||||
_settingsRepository = settingsRepository;
|
||||
_cssThemeManager = cssThemeManager;
|
||||
_nbXplorerDashboard = nbXplorerDashboard;
|
||||
_walletsController = walletsController;
|
||||
_payjoinClient = payjoinClient;
|
||||
@ -84,8 +80,8 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
[HttpGet("~/api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet")]
|
||||
public async Task<IActionResult> ShowOnChainWalletOverview(string storeId, string cryptoCode)
|
||||
{
|
||||
if (IsInvalidWalletRequest(cryptoCode, out var network,
|
||||
out var derivationScheme, out var actionResult)) return actionResult;
|
||||
if (IsInvalidWalletRequest(cryptoCode, out BTCPayNetwork network,
|
||||
out DerivationSchemeSettings derivationScheme, out IActionResult actionResult)) return actionResult;
|
||||
|
||||
var wallet = _btcPayWalletProvider.GetWallet(network);
|
||||
var balance = await wallet.GetBalance(derivationScheme.AccountDerivation);
|
||||
@ -103,8 +99,8 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
[HttpGet("~/api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/feerate")]
|
||||
public async Task<IActionResult> GetOnChainFeeRate(string storeId, string cryptoCode, int? blockTarget = null)
|
||||
{
|
||||
if (IsInvalidWalletRequest(cryptoCode, out var network,
|
||||
out var derivationScheme, out var actionResult)) return actionResult;
|
||||
if (IsInvalidWalletRequest(cryptoCode, out BTCPayNetwork network,
|
||||
out DerivationSchemeSettings derivationScheme, out IActionResult actionResult)) return actionResult;
|
||||
|
||||
var feeRateTarget = blockTarget?? Store.GetStoreBlob().RecommendedFeeBlockTarget;
|
||||
return Ok(new OnChainWalletFeeRateData()
|
||||
@ -118,25 +114,17 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
[HttpGet("~/api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/address")]
|
||||
public async Task<IActionResult> GetOnChainWalletReceiveAddress(string storeId, string cryptoCode, bool forceGenerate = false)
|
||||
{
|
||||
if (IsInvalidWalletRequest(cryptoCode, out var network,
|
||||
out var derivationScheme, out var actionResult)) return actionResult;
|
||||
if (IsInvalidWalletRequest(cryptoCode, out BTCPayNetwork network,
|
||||
out DerivationSchemeSettings derivationScheme, out IActionResult actionResult)) return actionResult;
|
||||
|
||||
var kpi = await _walletReceiveService.GetOrGenerate(new WalletId(storeId, cryptoCode), forceGenerate);
|
||||
if (kpi is null)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
var bip21 = network.GenerateBIP21(kpi.Address?.ToString(), null);
|
||||
var allowedPayjoin = derivationScheme.IsHotWallet && Store.GetStoreBlob().PayJoinEnabled;
|
||||
if (allowedPayjoin)
|
||||
{
|
||||
bip21.QueryParams.Add(PayjoinClient.BIP21EndpointKey, Request.GetAbsoluteUri(Url.Action(nameof(PayJoinEndpointController.Submit), "PayJoinEndpoint", new { cryptoCode })));
|
||||
}
|
||||
return Ok(new OnChainWalletAddressData()
|
||||
{
|
||||
Address = kpi.Address?.ToString(),
|
||||
PaymentLink = bip21.ToString(),
|
||||
Address = kpi.Address.ToString(),
|
||||
KeyPath = kpi.KeyPath
|
||||
});
|
||||
}
|
||||
@ -145,8 +133,8 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
[HttpDelete("~/api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/address")]
|
||||
public async Task<IActionResult> UnReserveOnChainWalletReceiveAddress(string storeId, string cryptoCode)
|
||||
{
|
||||
if (IsInvalidWalletRequest(cryptoCode, out var network,
|
||||
out var derivationScheme, out var actionResult)) return actionResult;
|
||||
if (IsInvalidWalletRequest(cryptoCode, out BTCPayNetwork network,
|
||||
out DerivationSchemeSettings derivationScheme, out IActionResult actionResult)) return actionResult;
|
||||
|
||||
var addr = await _walletReceiveService.UnReserveAddress(new WalletId(storeId, cryptoCode));
|
||||
if (addr is null)
|
||||
@ -158,16 +146,11 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpGet("~/api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/transactions")]
|
||||
public async Task<IActionResult> ShowOnChainWalletTransactions(
|
||||
string storeId,
|
||||
string cryptoCode,
|
||||
[FromQuery] TransactionStatus[]? statusFilter = null,
|
||||
[FromQuery] int skip = 0,
|
||||
[FromQuery] int limit = int.MaxValue
|
||||
)
|
||||
public async Task<IActionResult> ShowOnChainWalletTransactions(string storeId, string cryptoCode,
|
||||
[FromQuery]TransactionStatus[] statusFilter = null)
|
||||
{
|
||||
if (IsInvalidWalletRequest(cryptoCode, out var network,
|
||||
out var derivationScheme, out var actionResult)) return actionResult;
|
||||
if (IsInvalidWalletRequest(cryptoCode, out BTCPayNetwork network,
|
||||
out DerivationSchemeSettings derivationScheme, out IActionResult actionResult)) return actionResult;
|
||||
|
||||
var wallet = _btcPayWalletProvider.GetWallet(network);
|
||||
var walletId = new WalletId(storeId, cryptoCode);
|
||||
@ -190,7 +173,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
filteredFlatList.AddRange(txs.ReplacedTransactions.Transactions);
|
||||
}
|
||||
|
||||
var result = filteredFlatList.Skip(skip).Take(limit).Select(information =>
|
||||
var result = filteredFlatList.Select(information =>
|
||||
{
|
||||
walletTransactionsInfoAsync.TryGetValue(information.TransactionId.ToString(), out var transactionInfo);
|
||||
return ToModel(transactionInfo, information, wallet);
|
||||
@ -203,8 +186,8 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
public async Task<IActionResult> GetOnChainWalletTransaction(string storeId, string cryptoCode,
|
||||
string transactionId)
|
||||
{
|
||||
if (IsInvalidWalletRequest(cryptoCode, out var network,
|
||||
out var derivationScheme, out var actionResult)) return actionResult;
|
||||
if (IsInvalidWalletRequest(cryptoCode, out BTCPayNetwork network,
|
||||
out DerivationSchemeSettings derivationScheme, out IActionResult actionResult)) return actionResult;
|
||||
|
||||
var wallet = _btcPayWalletProvider.GetWallet(network);
|
||||
var tx = await wallet.FetchTransaction(derivationScheme.AccountDerivation, uint256.Parse(transactionId));
|
||||
@ -225,8 +208,8 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
[HttpGet("~/api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/utxos")]
|
||||
public async Task<IActionResult> GetOnChainWalletUTXOs(string storeId, string cryptoCode)
|
||||
{
|
||||
if (IsInvalidWalletRequest(cryptoCode, out var network,
|
||||
out var derivationScheme, out var actionResult)) return actionResult;
|
||||
if (IsInvalidWalletRequest(cryptoCode, out BTCPayNetwork network,
|
||||
out DerivationSchemeSettings derivationScheme, out IActionResult actionResult)) return actionResult;
|
||||
|
||||
var wallet = _btcPayWalletProvider.GetWallet(network);
|
||||
|
||||
@ -246,7 +229,6 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
coin.OutPoint.Hash.ToString()),
|
||||
Timestamp = coin.Timestamp,
|
||||
KeyPath = coin.KeyPath,
|
||||
Confirmations = coin.Confirmations,
|
||||
Address = network.NBXplorerNetwork.CreateAddress(derivationScheme.AccountDerivation, coin.KeyPath, coin.ScriptPubKey).ToString()
|
||||
};
|
||||
}).ToList()
|
||||
@ -258,8 +240,8 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
public async Task<IActionResult> CreateOnChainTransaction(string storeId, string cryptoCode,
|
||||
[FromBody] CreateOnChainTransactionRequest request)
|
||||
{
|
||||
if (IsInvalidWalletRequest(cryptoCode, out var network,
|
||||
out var derivationScheme, out var actionResult)) return actionResult;
|
||||
if (IsInvalidWalletRequest(cryptoCode, out BTCPayNetwork network,
|
||||
out DerivationSchemeSettings derivationScheme, out IActionResult actionResult)) return actionResult;
|
||||
if (network.ReadonlyWallet)
|
||||
{
|
||||
return this.CreateAPIError("not-available",
|
||||
@ -304,7 +286,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
subtractFeesOutputsCount.Add(index);
|
||||
}
|
||||
|
||||
BitcoinUrlBuilder? bip21 = null;
|
||||
BitcoinUrlBuilder bip21 = null;
|
||||
var amount = destination.Amount;
|
||||
if (amount.GetValueOrDefault(0) <= 0)
|
||||
{
|
||||
@ -341,7 +323,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
request.AddModelError(transactionRequest => transactionRequest.Destinations[index],
|
||||
"Amount must be specified or destination must be a BIP21 payment link, and greater than 0", this);
|
||||
}
|
||||
if (request.ProceedWithPayjoin && bip21?.UnknowParameters?.ContainsKey(PayjoinClient.BIP21EndpointKey) is true)
|
||||
if (request.ProceedWithPayjoin && bip21?.UnknowParameters?.ContainsKey("pj") is true)
|
||||
{
|
||||
payjoinOutputIndex = index;
|
||||
}
|
||||
@ -445,13 +427,8 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
psbt.PSBT.RebaseKeyPaths(signingKeySettings.AccountKey, rootedKeyPath);
|
||||
var accountKey = signingKey.Derive(rootedKeyPath.KeyPath);
|
||||
|
||||
if (signingContext?.EnforceLowR is bool v)
|
||||
psbt.PSBT.Settings.SigningOptions.EnforceLowR = v;
|
||||
else if (psbt.Suggestions?.ShouldEnforceLowR is bool v2)
|
||||
psbt.PSBT.Settings.SigningOptions.EnforceLowR = v2;
|
||||
|
||||
var changed = psbt.PSBT.PSBTChanged(() => psbt.PSBT.SignAll(derivationScheme.AccountDerivation, accountKey,
|
||||
rootedKeyPath));
|
||||
rootedKeyPath, new SigningOptions() {EnforceLowR = signingContext?.EnforceLowR is bool v ? v : psbt.Suggestions.ShouldEnforceLowR }));
|
||||
|
||||
if (!changed)
|
||||
{
|
||||
@ -463,7 +440,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
var transaction = psbt.PSBT.ExtractTransaction();
|
||||
var transactionHash = transaction.GetHash();
|
||||
BroadcastResult broadcastResult;
|
||||
if (!string.IsNullOrEmpty(signingContext?.PayJoinBIP21))
|
||||
if (!string.IsNullOrEmpty(signingContext.PayJoinBIP21))
|
||||
{
|
||||
signingContext.OriginalPSBT = psbt.PSBT.ToBase64();
|
||||
try
|
||||
@ -473,8 +450,8 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
var payjoinPSBT = await _payjoinClient.RequestPayjoin(
|
||||
new BitcoinUrlBuilder(signingContext.PayJoinBIP21, network.NBitcoinNetwork), new PayjoinWallet(derivationScheme),
|
||||
psbt.PSBT, CancellationToken.None);
|
||||
psbt.PSBT.Settings.SigningOptions = new SigningOptions() { EnforceLowR = !(signingContext?.EnforceLowR is false) };
|
||||
payjoinPSBT = psbt.PSBT.SignAll(derivationScheme.AccountDerivation, accountKey, rootedKeyPath);
|
||||
payjoinPSBT = psbt.PSBT.SignAll(derivationScheme.AccountDerivation, accountKey, rootedKeyPath,
|
||||
new SigningOptions() {EnforceLowR = !(signingContext?.EnforceLowR is false)});
|
||||
payjoinPSBT.Finalize();
|
||||
var payjoinTransaction = payjoinPSBT.ExtractTransaction();
|
||||
var hash = payjoinTransaction.GetHash();
|
||||
@ -510,13 +487,14 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
|
||||
private async Task<(bool HotWallet, bool RPCImport)> CanUseHotWallet()
|
||||
{
|
||||
return await _authorizationService.CanUseHotWallet(await _settingsRepository.GetPolicies(), User);
|
||||
return await _authorizationService.CanUseHotWallet(_cssThemeManager.Policies, User);
|
||||
}
|
||||
|
||||
private bool IsInvalidWalletRequest(string cryptoCode, [MaybeNullWhen(true)] out BTCPayNetwork network,
|
||||
[MaybeNullWhen(true)] out DerivationSchemeSettings derivationScheme, [MaybeNullWhen(false)] out IActionResult actionResult)
|
||||
private bool IsInvalidWalletRequest(string cryptoCode, out BTCPayNetwork network,
|
||||
out DerivationSchemeSettings derivationScheme, out IActionResult actionResult)
|
||||
{
|
||||
derivationScheme = null;
|
||||
actionResult = null;
|
||||
network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
|
||||
if (network is null)
|
||||
{
|
||||
@ -539,7 +517,6 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
return true;
|
||||
}
|
||||
|
||||
actionResult = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -554,15 +531,15 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
return paymentMethod;
|
||||
}
|
||||
|
||||
private OnChainWalletTransactionData ToModel(WalletTransactionInfo? walletTransactionsInfoAsync,
|
||||
private OnChainWalletTransactionData ToModel(WalletTransactionInfo walletTransactionsInfoAsync,
|
||||
TransactionInformation tx,
|
||||
BTCPayWallet wallet)
|
||||
{
|
||||
return new OnChainWalletTransactionData()
|
||||
{
|
||||
TransactionHash = tx.TransactionId,
|
||||
Comment = walletTransactionsInfoAsync?.Comment ?? string.Empty,
|
||||
Labels = walletTransactionsInfoAsync?.Labels ?? new Dictionary<string, LabelData>(),
|
||||
Comment = walletTransactionsInfoAsync?.Comment?? string.Empty,
|
||||
Labels = walletTransactionsInfoAsync?.Labels?? new Dictionary<string, LabelData>(),
|
||||
Amount = tx.BalanceChange.GetValue(wallet.Network),
|
||||
BlockHash = tx.BlockHash,
|
||||
BlockHeight = tx.Height,
|
||||
|
@ -1,46 +0,0 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StoreData = BTCPayServer.Data.StoreData;
|
||||
|
||||
namespace BTCPayServer.Controllers.GreenField
|
||||
{
|
||||
[ApiController]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
public class StorePaymentMethodsController : ControllerBase
|
||||
{
|
||||
private StoreData Store => HttpContext.GetStoreData();
|
||||
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
|
||||
|
||||
public StorePaymentMethodsController(BTCPayNetworkProvider btcPayNetworkProvider)
|
||||
{
|
||||
_btcPayNetworkProvider = btcPayNetworkProvider;
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpGet("~/api/v1/stores/{storeId}/payment-methods")]
|
||||
public ActionResult<Dictionary<string, GenericPaymentMethodData>> GetStorePaymentMethods(
|
||||
string storeId,
|
||||
[FromQuery] bool? enabled)
|
||||
{
|
||||
var storeBlob = Store.GetStoreBlob();
|
||||
var excludedPaymentMethods = storeBlob.GetExcludedPaymentMethods();
|
||||
return Ok(Store.GetSupportedPaymentMethods(_btcPayNetworkProvider)
|
||||
.Where(method =>
|
||||
enabled is null || (enabled is false && excludedPaymentMethods.Match(method.PaymentId)))
|
||||
.ToDictionary(
|
||||
method => method.PaymentId.ToStringNormalized(),
|
||||
method => new GenericPaymentMethodData()
|
||||
{
|
||||
Enabled = enabled.GetValueOrDefault(!excludedPaymentMethods.Match(method.PaymentId)),
|
||||
Data = method.PaymentId.PaymentType.GetGreenfieldData(method)
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
@ -36,7 +36,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
public WebhookNotificationManager WebhookNotificationManager { get; }
|
||||
|
||||
[HttpGet("~/api/v1/stores/{storeId}/webhooks/{webhookId?}")]
|
||||
public async Task<IActionResult> ListWebhooks(string storeId, string webhookId)
|
||||
public async Task<IActionResult> ListWebhooks(string webhookId)
|
||||
{
|
||||
if (webhookId is null)
|
||||
{
|
||||
@ -48,7 +48,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
{
|
||||
var w = await StoreRepository.GetWebhook(CurrentStoreId, webhookId);
|
||||
if (w is null)
|
||||
return WebhookNotFound();
|
||||
return NotFound();
|
||||
return Ok(FromModel(w, false));
|
||||
}
|
||||
}
|
||||
@ -62,7 +62,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
}
|
||||
|
||||
[HttpPost("~/api/v1/stores/{storeId}/webhooks")]
|
||||
public async Task<IActionResult> CreateWebhook(string storeId, Client.Models.CreateStoreWebhookRequest create)
|
||||
public async Task<IActionResult> CreateWebhook(Client.Models.CreateStoreWebhookRequest create)
|
||||
{
|
||||
ValidateWebhookRequest(create);
|
||||
if (!ModelState.IsValid)
|
||||
@ -70,7 +70,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
var webhookId = await StoreRepository.CreateWebhook(CurrentStoreId, ToModel(create));
|
||||
var w = await StoreRepository.GetWebhook(CurrentStoreId, webhookId);
|
||||
if (w is null)
|
||||
return WebhookNotFound();
|
||||
return NotFound();
|
||||
return Ok(FromModel(w, true));
|
||||
}
|
||||
|
||||
@ -88,28 +88,19 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
return this.CreateValidationError(ModelState);
|
||||
var w = await StoreRepository.GetWebhook(CurrentStoreId, webhookId);
|
||||
if (w is null)
|
||||
return WebhookNotFound();
|
||||
return NotFound();
|
||||
await StoreRepository.UpdateWebhook(storeId, webhookId, ToModel(update));
|
||||
return await ListWebhooks(storeId, webhookId);
|
||||
return await ListWebhooks(webhookId);
|
||||
}
|
||||
[HttpDelete("~/api/v1/stores/{storeId}/webhooks/{webhookId}")]
|
||||
public async Task<IActionResult> DeleteWebhook(string storeId, string webhookId)
|
||||
public async Task<IActionResult> DeleteWebhook(string webhookId)
|
||||
{
|
||||
var w = await StoreRepository.GetWebhook(CurrentStoreId, webhookId);
|
||||
if (w is null)
|
||||
return WebhookNotFound();
|
||||
return NotFound();
|
||||
await StoreRepository.DeleteWebhook(CurrentStoreId, webhookId);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
IActionResult WebhookNotFound()
|
||||
{
|
||||
return this.CreateAPIError(404, "webhook-not-found", "The webhook was not found");
|
||||
}
|
||||
IActionResult WebhookDeliveryNotFound()
|
||||
{
|
||||
return this.CreateAPIError(404, "webhookdelivery-not-found", "The webhook delivery was not found");
|
||||
}
|
||||
private WebhookBlob ToModel(StoreWebhookBaseData create)
|
||||
{
|
||||
return new WebhookBlob()
|
||||
@ -130,7 +121,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
|
||||
|
||||
[HttpGet("~/api/v1/stores/{storeId}/webhooks/{webhookId}/deliveries/{deliveryId?}")]
|
||||
public async Task<IActionResult> ListDeliveries(string storeId, string webhookId, string deliveryId, int? count = null)
|
||||
public async Task<IActionResult> ListDeliveries(string webhookId, string deliveryId, int? count = null)
|
||||
{
|
||||
if (deliveryId is null)
|
||||
{
|
||||
@ -142,25 +133,25 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
{
|
||||
var delivery = await StoreRepository.GetWebhookDelivery(CurrentStoreId, webhookId, deliveryId);
|
||||
if (delivery is null)
|
||||
return WebhookDeliveryNotFound();
|
||||
return NotFound();
|
||||
return Ok(FromModel(delivery));
|
||||
}
|
||||
}
|
||||
[HttpPost("~/api/v1/stores/{storeId}/webhooks/{webhookId}/deliveries/{deliveryId}/redeliver")]
|
||||
public async Task<IActionResult> RedeliverWebhook(string storeId, string webhookId, string deliveryId)
|
||||
public async Task<IActionResult> RedeliverWebhook(string webhookId, string deliveryId)
|
||||
{
|
||||
var delivery = await StoreRepository.GetWebhookDelivery(CurrentStoreId, webhookId, deliveryId);
|
||||
if (delivery is null)
|
||||
return WebhookDeliveryNotFound();
|
||||
return NotFound();
|
||||
return this.Ok(new JValue(await WebhookNotificationManager.Redeliver(deliveryId)));
|
||||
}
|
||||
|
||||
[HttpGet("~/api/v1/stores/{storeId}/webhooks/{webhookId}/deliveries/{deliveryId}/request")]
|
||||
public async Task<IActionResult> GetDeliveryRequest(string storeId, string webhookId, string deliveryId)
|
||||
public async Task<IActionResult> GetDeliveryRequest(string webhookId, string deliveryId)
|
||||
{
|
||||
var delivery = await StoreRepository.GetWebhookDelivery(CurrentStoreId, webhookId, deliveryId);
|
||||
if (delivery is null)
|
||||
return WebhookDeliveryNotFound();
|
||||
return NotFound();
|
||||
return File(delivery.GetBlob().Request, "application/json");
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user