Compare commits
106 Commits
v1.11.6
...
v1.12.0-rc
Author | SHA1 | Date | |
---|---|---|---|
541cef55b8 | |||
e3863ac076 | |||
0e2379caa6 | |||
a17c486f81 | |||
e4aaff5e34 | |||
97fda9d362 | |||
7a06423bc7 | |||
26374ef476 | |||
6324a1a1e8 | |||
b751e23e93 | |||
72ee65843d | |||
d413dd9257 | |||
433adf4668 | |||
d78267d7ee | |||
0c16492d1c | |||
eda437995f | |||
379286c366 | |||
3f344f2c0c | |||
d050c8e3b2 | |||
b13f140b86 | |||
7066a2a577 | |||
a8ebaa6784 | |||
60ff7e86b8 | |||
44b7ed0e6e | |||
afed3a0899 | |||
28265b30d2 | |||
a97172cea6 | |||
605741182d | |||
2c94a87be4 | |||
6f98d5aa20 | |||
3d08e70101 | |||
bdf56c0a6f | |||
b9b3860e6b | |||
b0554bbf17 | |||
b31f1812d2 | |||
04292d09e1 | |||
1081eab9db | |||
4023b24209 | |||
bac9ab08d1 | |||
75bf8a5086 | |||
3ffae30b95 | |||
2fda9cf539 | |||
c8b9a425b8 | |||
62865d7d88 | |||
3afd24fcd7 | |||
fd582aad75 | |||
f9155772f5 | |||
2e4313bf18 | |||
5ad320ee4b | |||
d46543ae16 | |||
2f23bad3bc | |||
6d288271cd | |||
b4daa76aeb | |||
5bd8067328 | |||
9ccc42f556 | |||
3ee4f43eb5 | |||
d1bf47a5c0 | |||
ccf9cfa332 | |||
773f8a9aea | |||
dd62e166a1 | |||
2fb72d5aa6 | |||
46f0818765 | |||
96569ae4aa | |||
f2b1e5f93e | |||
2326894a2b | |||
c15f02ddbf | |||
7708084331 | |||
696a414e95 | |||
c16dfb2dcb | |||
c979c4774c | |||
e82281d273 | |||
27c22d5e33 | |||
6acc545b66 | |||
609ec0989f | |||
b702621a04 | |||
89041a6744 | |||
c485c109e6 | |||
29a49d5f71 | |||
a5fafc4864 | |||
a921504bcf | |||
027154a4d3 | |||
bf1a1368ff | |||
097ffbf8a3 | |||
ec076d1560 | |||
8dadfa2111 | |||
c8ee6ead0b | |||
018e4c501d | |||
99a0b70cfa | |||
314a1352ec | |||
901e6be21e | |||
d58dde950e | |||
8ac18b74df | |||
2846c38ff5 | |||
d44efce225 | |||
d3dca7e808 | |||
41e3828eea | |||
9e76b4d28e | |||
ef03497350 | |||
e5a2aeb145 | |||
229a4ea56c | |||
f20e6d3768 | |||
1d210eb6e3 | |||
d8422a979f | |||
0cf6d39f02 | |||
076c20a3b7 | |||
0cfb0ba890 |
.gitignore
BTCPayServer.Abstractions
BTCPayServer.Client
BTCPayServer.Client.csprojBTCPayServerClient.PullPayments.cs
Models
CreateAppRequest.csInvoiceExceptionStatus.csLNURLPayPaymentMethodBaseData.csLNURLPayPaymentMethodData.csPointOfSaleAppData.csRegisterBoltcardRequest.csStoreBaseData.csStoreWebhookData.csWebhookEvent.csWebhookEventType.csWebhookInvoiceEvent.cs
Permissions.csBTCPayServer.Common
Altcoins
BTCPayNetworkProvider.Althash.csBTCPayNetworkProvider.Argoneum.csBTCPayNetworkProvider.BGold.csBTCPayNetworkProvider.BPlus.csBTCPayNetworkProvider.Bitcore.csBTCPayNetworkProvider.Dash.csBTCPayNetworkProvider.Dogecoin.csBTCPayNetworkProvider.Feathercoin.csBTCPayNetworkProvider.Groestlcoin.csBTCPayNetworkProvider.Litecoin.csBTCPayNetworkProvider.Monacoin.csBTCPayNetworkProvider.Polis.csBTCPayNetworkProvider.Ufo.csBTCPayNetworkProvider.Viacoin.cs
BTCPayNetwork.csBTCPayNetworkProvider.Bitcoin.csBTCPayNetworkProvider.csBTCPayServer.Common.csprojLiquid
BTCPayNetworkProvider.Liquid.csBTCPayNetworkProvider.LiquidAssets.csElementsLikeBtcPayNetwork.csLiquidExtensions.cs
Monero
Zcash
Configuration
SelectedChains.csBTCPayServer.Data
BTCPayServer.PluginPacker
BTCPayServer.Rating
BTCPayServer.Tests
AltcoinTests
BTCPayServer.Tests.csprojBTCPayServerTester.csChargeTester.csDockerfileExtensions.csFastTests.csFormTests.csGreenfieldAPITests.csSeleniumTester.csSeleniumTests.csServerTester.csTestAccount.csTestUtils.csThirdPartyTests.csUnitTest1.csUnitTestBase.csdocker-bitcoin-multisig-setup.shdocker-compose.altcoins.ymldocker-compose.ymlBTCPayServer
APDUVaultTransport.csBTCPayServer.csprojProgram.csVaultClient.csVaultHWITransport.cs_ViewImports.cshtml
Blazor
ColorPalette.csComponents
InvoiceStatus
MainLogo
MainNav
Pager
QRCode
StoreLightningBalance
StoreNumbers
StoreRecentInvoices
StoreRecentTransactions
StoreSelector
StoreWalletBalance
ThemeSwitch
TruncateCenter
WalletNav
Configuration
Controllers
GreenField
GreenfieldAppsController.csGreenfieldInvoiceController.csGreenfieldLightningNodeApiController.Internal.csGreenfieldLightningNodeApiController.Store.csGreenfieldPaymentRequestsController.csGreenfieldPullPaymentController.csGreenfieldReportsController.csGreenfieldStoreLNURLPayPaymentMethodsController.csGreenfieldStoreLightningNetworkPaymentMethodsController.csGreenfieldStoreOnChainWalletsController.csGreenfieldStoreWebhooksController.csGreenfieldStoresController.csGreenfieldUsersController.csLocalBTCPayServerClient.cs
UIAccountController.csUIAppsController.csUIBoltcardController.csUICustodianAccountsController.csUIHomeController.csUIInvoiceController.Testing.csUIInvoiceController.UI.csUIInvoiceController.csUILNURLController.csUIManageController.APIKeys.csUIPaymentRequestController.csUIPublicLightningNodeInfoController.csUIPullPaymentController.Boltcard.csUIPullPaymentController.csUIReportsController.CheatMode.csUIReportsController.csUIServerController.csUIStorePullPaymentsController.PullPayments.csUIStoresController.Email.csUIStoresController.LightningLike.csUIStoresController.csUIVaultController.csUIWalletsController.csData
AddressInvoiceDataExtensions.csBoltcardDataExtensions.cs
DerivationSchemeSettings.csExtensions.csPayouts
StoreBlob.csWebhookDataExtensions.csExtensions
Filters
Forms
FieldValueMirror.csFormDataService.csHtmlFieldsetFormProvider.csIFormComponentProvider.cs
Models
UIFormsController.csHostedServices
GithubVersionFetcher.csPluginUpdateFetcher.csPullPaymentHostedService.csStoreEmailRuleProcessorSender.csTransactionLabelMarkerHostedService.csWebhookSender.cs
Webhooks
Hosting
BTCPayServerServices.csBlockExplorerLinkStartupTask.csMigrationStartupTask.csStartup.csToPostgresMigrationStartupTask.cs
HwiWebSocketTransport.csModels
InvoicingModels
CreateInvoiceModel.csInvoiceDetailsModel.csInvoiceReceiptViewModel.csInvoicesModel.csPaymentModel.cs
PaymentRequestViewModels
SetupBoltcardViewModel.csStoreBrandingViewModel.csStoreViewModels
ViewPullPaymentModel.csWalletViewModels
PaymentRequest
Payments
Lightning
LightningExtensions.csLightningLikePaymentHandler.csLightningListener.csLightningSupportedPaymentMethod.cs
PaymentTypes.Bitcoin.csPaymentTypes.Lightning.csPaymentTypes.csPayoutProcessors
Plugins
Altcoins
AltcoinsPlugin.BGold.csAltcoinsPlugin.Dash.csAltcoinsPlugin.Dogecoin.csAltcoinsPlugin.Groestlcoin.csAltcoinsPlugin.Litecoin.csAltcoinsPlugin.Monacoin.csAltcoinsPlugin.cs
Liquid
Monero
AltcoinsPlugin.Monero.csMoneroLikeSpecificBtcPayNetwork.cs
RPC
JsonRpcClient.cs
Models
CreateAccountRequest.csCreateAccountResponse.csCreateAddressRequest.csCreateAddressResponse.csGetAccountsRequest.csGetAccountsResponse.csGetFeeEstimateRequest.csGetFeeEstimateResponse.csGetHeightResponse.csGetTransferByTransactionIdRequest.csGetTransferByTransactionIdResponse.csGetTransfersRequest.csGetTransfersResponse.csInfo.csMakeUriRequest.csMakeUriResponse.csParseStringConverter.csPeer.csSubaddrIndex.csSubaddressAccount.csSyncInfoResponse.cs
Utils
Zcash
AltcoinsPlugin.Zcash.cs
RPC
JsonRpcClient.cs
Models
CreateAccountRequest.csCreateAccountResponse.csCreateAddressRequest.csCreateAddressResponse.csGetAccountsRequest.csGetAccountsResponse.csGetFeeEstimateRequest.csGetFeeEstimateResponse.csGetHeightResponse.csGetTransferByTransactionIdRequest.csGetTransferByTransactionIdResponse.csGetTransfersRequest.csGetTransfersResponse.csInfo.csMakeUriRequest.csMakeUriResponse.csParseStringConverter.csPeer.csSubaddrIndex.csSubaddressAccount.csSyncInfoResponse.cs
Utils
ZcashLikeSpecificBtcPayNetwork.csBitcoin
Crowdfund
PluginManager.csPluginService.csPluginServiceCollection.csPointOfSale
Properties
Security
Bitpay
GreenField
Services
Altcoins
Apps
Cheater.csDefaultTransactionLinkProvider.csDisplayFormatter.csFees
FallbackFeeProvider.csFeeProviderFactory.csMempoolSpaceFeeProvider.csNBxplorerFeeProvider.csStaticFeeProvider.cs
Invoices
LightningClientFactoryService.csMails
Notifications
PaymentRequests
Reporting
FormattedAmount.csOnChainWalletReportProvider.csPaymentsReportProvider.csPayoutsReportProvider.csProductsReportProvider.csQueryContext.csReportProvider.csViewDefinition.cs
Stores
TransactionLinkProvider.csTransactionLinkProviders.csUserService.csWalletRepository.csViews
Shared
Bitcoin
BitcoinLikeMethodCheckout-v2.cshtmlBitcoinLikeMethodCheckout.cshtmlViewBitcoinLikePaymentData.cshtml
Crowdfund
LayoutFoot.cshtmlLayoutHead.cshtmlLayoutHeadStoreBranding.cshtmlLayoutHeadTheme.cshtmlLightning
LightningLikeMethodCheckout-v2.cshtmlLightningLikeMethodCheckout.cshtmlViewLightningLikePaymentData.cshtml
ListRoles.cshtmlLocalhostBrowserSupport.cshtmlMonero
NFC
PointOfSale
Public
UpdatePointOfSale.cshtmlShopify
ShowQR.cshtmlTemplateEditor.cshtmlVaultElements.cshtmlZcash
_Footer.cshtml_Layout.cshtml_LayoutSignedOut.cshtml_LayoutWizard.cshtml_StatusMessage.cshtml_StoreFooterLogo.cshtml_StoreHeader.cshtmlUIAccount
UIApps
UICustodianAccounts
UIForms
UIHome
UIInvoice
Checkout-Body.cshtmlCheckout-Cheating.cshtmlCheckout.cshtmlCheckoutV2.cshtmlCreateInvoice.cshtmlInvoice.cshtmlInvoiceReceipt.cshtmlInvoiceReceiptPrint.cshtmlListInvoices.cshtmlListInvoicesPaymentsPartial.cshtml
UILNURL
UIManage
UIMoneroLikeStore
UINotifications
UIPaymentRequest
UIPublicLightningNodeInfo
UIPullPayment
UIReports
UIServer
UIStorePullPayments
UIStores
ImportWallet
Index.cshtmlLightning.cshtmlLightningSettings.cshtmlListTokens.cshtmlModifyWebhook.cshtmlRates.cshtmlStoreEmails.cshtmlStoreUsers.cshtmlTestWebhook.cshtmlWalletSettings.cshtmlWebhooks.cshtml_Nav.cshtmlUIWallets
wwwroot
checkout-v2
checkout/js
crowdfund
img
js
copy-to-clipboard.jsdatatable.jsform-editor.jsstore-reports.jstemplate-editor.jstheme-switch.jsvaultbridge.jsvaultbridge.ui.jsvue-utils.js
main
payment-request
pos
swagger/v1
swagger.template.apps.jsonswagger.template.custodians.jsonswagger.template.invoices.jsonswagger.template.jsonswagger.template.lightning.common.jsonswagger.template.payment-requests.jsonswagger.template.pull-payments.jsonswagger.template.stores-payment-methods.lnurl.jsonswagger.template.stores-users.jsonswagger.template.stores-wallet.on-chain.jsonswagger.template.stores.jsonswagger.template.users.jsonswagger.template.webhooks.json
vendor
Build
Changelog.mdamd64.Dockerfilearm32v7.Dockerfilearm64v8.Dockerfile
3
.gitignore
vendored
3
.gitignore
vendored
@ -298,4 +298,5 @@ Packed Plugins
|
||||
Plugins/packed
|
||||
|
||||
BTCPayServer/wwwroot/swagger/v1/openapi.json
|
||||
BTCPayServer/appsettings.dev.json
|
||||
BTCPayServer/appsettings.dev.json
|
||||
BTCPayServer.Tests/monero_wallet
|
||||
|
@ -31,11 +31,11 @@
|
||||
<None Include="icon.png" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="HtmlSanitizer" Version="5.0.372" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.9" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.9" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.7" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.1" />
|
||||
<PackageReference Include="HtmlSanitizer" Version="8.0.723" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.0-beta.2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BTCPayServer.Client\BTCPayServer.Client.csproj" />
|
||||
|
@ -84,6 +84,7 @@ namespace BTCPayServer.Abstractions.Contracts
|
||||
.UseNpgsql(_options.Value.ConnectionString, o =>
|
||||
{
|
||||
o.EnableRetryOnFailure(10);
|
||||
o.SetPostgresVersion(12, 0);
|
||||
if (!string.IsNullOrEmpty(_schemaPrefix))
|
||||
{
|
||||
o.MigrationsHistoryTable(_schemaPrefix);
|
||||
|
@ -20,6 +20,15 @@ namespace BTCPayServer.Abstractions.Extensions
|
||||
Relative
|
||||
}
|
||||
|
||||
public static void SetBlazorAllowed(this ViewDataDictionary viewData, bool allowed)
|
||||
{
|
||||
viewData["BlazorAllowed"] = allowed;
|
||||
}
|
||||
public static bool IsBlazorAllowed(this ViewDataDictionary viewData)
|
||||
{
|
||||
return viewData["BlazorAllowed"] is not false;
|
||||
}
|
||||
|
||||
public static void SetActivePage<T>(this ViewDataDictionary viewData, T activePage, string title = null, string activeId = null)
|
||||
where T : IConvertible
|
||||
{
|
||||
|
@ -5,7 +5,6 @@ using System.Reflection;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Npgsql.Internal.TypeHandlers.GeometricHandlers;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Form;
|
||||
|
||||
@ -105,31 +104,7 @@ public class Form
|
||||
}
|
||||
}
|
||||
|
||||
public void SetValues(JObject values)
|
||||
{
|
||||
var fields = GetAllFields().ToDictionary(k => k.FullName, k => k.Field);
|
||||
SetValues(fields, new List<string>(), values);
|
||||
}
|
||||
|
||||
private void SetValues(Dictionary<string, Field> fields, List<string> path, JObject values)
|
||||
{
|
||||
foreach (var prop in values.Properties())
|
||||
{
|
||||
List<string> propPath = new List<string>(path.Count + 1);
|
||||
propPath.AddRange(path);
|
||||
propPath.Add(prop.Name);
|
||||
if (prop.Value.Type == JTokenType.Object)
|
||||
{
|
||||
SetValues(fields, propPath, (JObject)prop.Value);
|
||||
}
|
||||
else if (prop.Value.Type == JTokenType.String)
|
||||
{
|
||||
var fullName = string.Join('_', propPath.Where(s => !string.IsNullOrEmpty(s)));
|
||||
if (fields.TryGetValue(fullName, out var f) && !f.Constant)
|
||||
f.Value = prop.Value.Value<string>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
using System.Web;
|
||||
using Ganss.XSS;
|
||||
using Ganss.Xss;
|
||||
using Microsoft.AspNetCore.Html;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
|
@ -2,12 +2,13 @@ using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace BTCPayServer.Abstractions.TagHelpers;
|
||||
|
||||
[HtmlTargetElement(Attributes = "[permission]")]
|
||||
[HtmlTargetElement(Attributes = "[not-permission]" )]
|
||||
[HtmlTargetElement(Attributes = "[not-permission]")]
|
||||
public class PermissionTagHelper : TagHelper
|
||||
{
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
@ -22,29 +23,72 @@ public class PermissionTagHelper : TagHelper
|
||||
public string Permission { get; set; }
|
||||
public string NotPermission { get; set; }
|
||||
public string PermissionResource { get; set; }
|
||||
|
||||
public bool AndMode { get; set; } = false;
|
||||
|
||||
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Permission) && string.IsNullOrEmpty(NotPermission))
|
||||
var permissions = Permission?.Split(',', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
|
||||
var notPermissions = NotPermission?.Split(',', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
|
||||
|
||||
if (!permissions.Any() && !notPermissions.Any())
|
||||
return;
|
||||
if (_httpContextAccessor.HttpContext is null)
|
||||
return;
|
||||
|
||||
var expectedResult = !string.IsNullOrEmpty(Permission);
|
||||
var key = $"{Permission??NotPermission}_{PermissionResource}";
|
||||
if (!_httpContextAccessor.HttpContext.Items.TryGetValue(key, out var o) ||
|
||||
o is not AuthorizationResult res)
|
||||
bool shouldRender = true; // Assume tag should be rendered unless a check fails
|
||||
|
||||
// Process 'Permission' - User must have these permissions
|
||||
if (permissions.Any())
|
||||
{
|
||||
res = await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User,
|
||||
PermissionResource,
|
||||
Permission);
|
||||
_httpContextAccessor.HttpContext.Items.Add(key, res);
|
||||
bool finalResult = AndMode;
|
||||
foreach (var perm in permissions)
|
||||
{
|
||||
var key = $"{perm}_{PermissionResource}";
|
||||
AuthorizationResult res = await GetOrAddAuthorizationResult(key, perm);
|
||||
|
||||
if (AndMode)
|
||||
finalResult &= res.Succeeded;
|
||||
else
|
||||
finalResult |= res.Succeeded;
|
||||
|
||||
if (!AndMode && finalResult) break;
|
||||
}
|
||||
|
||||
shouldRender = finalResult;
|
||||
}
|
||||
if (expectedResult != res.Succeeded)
|
||||
|
||||
// Process 'NotPermission' - User must not have these permissions
|
||||
if (shouldRender && notPermissions.Any())
|
||||
{
|
||||
foreach (var notPerm in notPermissions)
|
||||
{
|
||||
var key = $"{notPerm}_{PermissionResource}";
|
||||
AuthorizationResult res = await GetOrAddAuthorizationResult(key, notPerm);
|
||||
|
||||
if (res.Succeeded) // If the user has a 'NotPermission', they should not see the tag
|
||||
{
|
||||
shouldRender = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!shouldRender)
|
||||
{
|
||||
output.SuppressOutput();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<AuthorizationResult> GetOrAddAuthorizationResult(string key, string permission)
|
||||
{
|
||||
if (!_httpContextAccessor.HttpContext.Items.TryGetValue(key, out var cachedResult))
|
||||
{
|
||||
var res = await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User,
|
||||
PermissionResource, permission);
|
||||
_httpContextAccessor.HttpContext.Items[key] = res;
|
||||
return res;
|
||||
}
|
||||
|
||||
return cachedResult as AuthorizationResult;
|
||||
}
|
||||
}
|
||||
|
@ -30,8 +30,8 @@
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.3.21" />
|
||||
<PackageReference Include="NBitcoin" Version="7.0.24" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.5.1" />
|
||||
<PackageReference Include="NBitcoin" Version="7.0.32" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -20,6 +20,12 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<PullPaymentData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<RegisterBoltcardResponse> RegisterBoltcard(string pullPaymentId, RegisterBoltcardRequest request, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}/boltcards", bodyPayload: request, method: HttpMethod.Post), cancellationToken);
|
||||
return await HandleResponse<RegisterBoltcardResponse>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<PullPaymentData[]> GetPullPayments(string storeId, bool includeArchived = false, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Dictionary<string, object> query = new Dictionary<string, object>();
|
||||
|
@ -28,6 +28,8 @@ namespace BTCPayServer.Client.Models
|
||||
public PosViewType DefaultView { get; set; }
|
||||
public bool ShowCustomAmount { get; set; } = false;
|
||||
public bool ShowDiscount { get; set; } = true;
|
||||
public bool ShowSearch { get; set; } = true;
|
||||
public bool ShowCategories { get; set; } = true;
|
||||
public bool EnableTips { get; set; } = true;
|
||||
public string CustomAmountPayButtonText { get; set; } = null;
|
||||
public string FixedAmountPayButtonText { get; set; } = null;
|
||||
@ -40,7 +42,6 @@ namespace BTCPayServer.Client.Models
|
||||
public bool? Archived { get; set; } = null;
|
||||
public string FormId { get; set; } = null;
|
||||
public string EmbeddedCSS { get; set; } = null;
|
||||
public CheckoutType? CheckoutType { get; set; } = null;
|
||||
}
|
||||
|
||||
public enum CrowdfundResetEvery
|
||||
|
@ -1,12 +1,12 @@
|
||||
namespace BTCPayServer.Client.Models
|
||||
namespace BTCPayServer.Client.Models;
|
||||
public enum InvoiceExceptionStatus
|
||||
{
|
||||
public enum InvoiceExceptionStatus
|
||||
{
|
||||
None,
|
||||
PaidLate,
|
||||
PaidPartial,
|
||||
Marked,
|
||||
Invalid,
|
||||
PaidOver
|
||||
}
|
||||
None,
|
||||
PaidLate,
|
||||
PaidPartial,
|
||||
Marked,
|
||||
Invalid,
|
||||
PaidOver
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,8 +1,12 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class LNURLPayPaymentMethodBaseData
|
||||
{
|
||||
public bool UseBech32Scheme { get; set; }
|
||||
|
||||
[JsonProperty("lud12Enabled")]
|
||||
public bool LUD12Enabled { get; set; }
|
||||
|
||||
public LNURLPayPaymentMethodBaseData()
|
||||
|
@ -16,11 +16,12 @@ namespace BTCPayServer.Client.Models
|
||||
{
|
||||
}
|
||||
|
||||
public LNURLPayPaymentMethodData(string cryptoCode, bool enabled, bool useBech32Scheme)
|
||||
public LNURLPayPaymentMethodData(string cryptoCode, bool enabled, bool useBech32Scheme, bool lud12Enabled)
|
||||
{
|
||||
Enabled = enabled;
|
||||
CryptoCode = cryptoCode;
|
||||
UseBech32Scheme = useBech32Scheme;
|
||||
LUD12Enabled = lud12Enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ namespace BTCPayServer.Client.Models
|
||||
public string DefaultView { get; set; }
|
||||
public bool ShowCustomAmount { get; set; }
|
||||
public bool ShowDiscount { get; set; }
|
||||
public bool ShowSearch { get; set; }
|
||||
public bool ShowCategories { get; set; }
|
||||
public bool EnableTips { get; set; }
|
||||
public string Currency { get; set; }
|
||||
public object Items { get; set; }
|
||||
|
39
BTCPayServer.Client/Models/RegisterBoltcardRequest.cs
Normal file
39
BTCPayServer.Client/Models/RegisterBoltcardRequest.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using NBitcoin.JsonConverters;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public enum OnExistingBehavior
|
||||
{
|
||||
KeepVersion,
|
||||
UpdateVersion
|
||||
}
|
||||
public class RegisterBoltcardRequest
|
||||
{
|
||||
[JsonConverter(typeof(HexJsonConverter))]
|
||||
[JsonProperty("UID")]
|
||||
public byte[] UID { get; set; }
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public OnExistingBehavior? OnExisting { get; set; }
|
||||
}
|
||||
public class RegisterBoltcardResponse
|
||||
{
|
||||
[JsonProperty("LNURLW")]
|
||||
public string LNURLW { get; set; }
|
||||
public int Version { get; set; }
|
||||
[JsonProperty("K0")]
|
||||
public string K0 { get; set; }
|
||||
[JsonProperty("K1")]
|
||||
public string K1 { get; set; }
|
||||
[JsonProperty("K2")]
|
||||
public string K2 { get; set; }
|
||||
[JsonProperty("K3")]
|
||||
public string K3 { get; set; }
|
||||
[JsonProperty("K4")]
|
||||
public string K4 { get; set; }
|
||||
}
|
||||
}
|
@ -37,8 +37,11 @@ namespace BTCPayServer.Client.Models
|
||||
public bool AnyoneCanCreateInvoice { get; set; }
|
||||
public string DefaultCurrency { get; set; }
|
||||
public bool RequiresRefundEmail { get; set; }
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public CheckoutType CheckoutType { get; set; }
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public CheckoutType? CheckoutType { get; set; }
|
||||
|
||||
public bool LightningAmountInSatoshi { get; set; }
|
||||
public bool LightningPrivateRouteHints { get; set; }
|
||||
public bool OnChainWithLnInvoiceFallback { get; set; }
|
||||
@ -73,6 +76,17 @@ namespace BTCPayServer.Client.Models
|
||||
|
||||
public bool PayJoinEnabled { get; set; }
|
||||
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? AutoDetectLanguage { get; set; }
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? ShowPayInWalletButton { get; set; }
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? ShowStoreHeader { get; set; }
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? CelebratePayment { get; set; }
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? PlaySoundOnPayment { get; set; }
|
||||
|
||||
public InvoiceData.ReceiptOptions Receipt { get; set; }
|
||||
|
||||
|
||||
|
@ -11,8 +11,7 @@ namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public bool Everything { get; set; } = true;
|
||||
|
||||
[JsonProperty(ItemConverterType = typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
|
||||
public WebhookEventType[] SpecificEvents { get; set; } = Array.Empty<WebhookEventType>();
|
||||
public string[] SpecificEvents { get; set; } = Array.Empty<string>();
|
||||
}
|
||||
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
@ -9,7 +9,7 @@ namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class WebhookEvent
|
||||
{
|
||||
public readonly static JsonSerializerSettings DefaultSerializerSettings;
|
||||
public static readonly JsonSerializerSettings DefaultSerializerSettings;
|
||||
static WebhookEvent()
|
||||
{
|
||||
DefaultSerializerSettings = new JsonSerializerSettings();
|
||||
@ -45,8 +45,7 @@ namespace BTCPayServer.Client.Models
|
||||
}
|
||||
}
|
||||
public bool IsRedelivery { get; set; }
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public WebhookEventType Type { get; set; }
|
||||
public string Type { get; set; }
|
||||
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
||||
public DateTimeOffset Timestamp { get; set; }
|
||||
[JsonExtensionData]
|
||||
|
@ -1,13 +1,20 @@
|
||||
namespace BTCPayServer.Client.Models
|
||||
namespace BTCPayServer.Client.Models;
|
||||
|
||||
public static class WebhookEventType
|
||||
{
|
||||
public enum WebhookEventType
|
||||
{
|
||||
InvoiceCreated,
|
||||
InvoiceReceivedPayment,
|
||||
InvoiceProcessing,
|
||||
InvoiceExpired,
|
||||
InvoiceSettled,
|
||||
InvoiceInvalid,
|
||||
InvoicePaymentSettled,
|
||||
}
|
||||
public const string InvoiceCreated = nameof(InvoiceCreated);
|
||||
public const string InvoiceReceivedPayment = nameof(InvoiceReceivedPayment);
|
||||
public const string InvoiceProcessing = nameof(InvoiceProcessing);
|
||||
public const string InvoiceExpired = nameof(InvoiceExpired);
|
||||
public const string InvoiceSettled = nameof(InvoiceSettled);
|
||||
public const string InvoiceInvalid = nameof(InvoiceInvalid);
|
||||
public const string InvoicePaymentSettled = nameof(InvoicePaymentSettled);
|
||||
public const string PayoutCreated = nameof(PayoutCreated);
|
||||
public const string PayoutApproved = nameof(PayoutApproved);
|
||||
public const string PayoutUpdated = nameof(PayoutUpdated);
|
||||
public const string PaymentRequestUpdated = nameof(PaymentRequestUpdated);
|
||||
public const string PaymentRequestCreated = nameof(PaymentRequestCreated);
|
||||
public const string PaymentRequestArchived = nameof(PaymentRequestArchived);
|
||||
public const string PaymentRequestStatusChanged = nameof(PaymentRequestStatusChanged);
|
||||
|
||||
}
|
||||
|
@ -1,44 +1,74 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class WebhookInvoiceEvent : WebhookEvent
|
||||
public class WebhookPayoutEvent : StoreWebhookEvent
|
||||
{
|
||||
public WebhookPayoutEvent(string evtType, string storeId)
|
||||
{
|
||||
if (!evtType.StartsWith("payout", StringComparison.InvariantCultureIgnoreCase))
|
||||
throw new ArgumentException("Invalid event type", nameof(evtType));
|
||||
Type = evtType;
|
||||
StoreId = storeId;
|
||||
}
|
||||
|
||||
[JsonProperty(Order = 2)] public string PayoutId { get; set; }
|
||||
[JsonProperty(Order = 3)] public string PullPaymentId { get; set; }
|
||||
[JsonProperty(Order = 4)] [JsonConverter(typeof(StringEnumConverter))]public PayoutState PayoutState { get; set; }
|
||||
}
|
||||
public class WebhookPaymentRequestEvent : StoreWebhookEvent
|
||||
{
|
||||
public WebhookPaymentRequestEvent(string evtType, string storeId)
|
||||
{
|
||||
if (!evtType.StartsWith("paymentrequest", StringComparison.InvariantCultureIgnoreCase))
|
||||
throw new ArgumentException("Invalid event type", nameof(evtType));
|
||||
Type = evtType;
|
||||
StoreId = storeId;
|
||||
}
|
||||
|
||||
[JsonProperty(Order = 2)] public string PaymentRequestId { get; set; }
|
||||
[JsonProperty(Order = 3)] [JsonConverter(typeof(StringEnumConverter))]public PaymentRequestData.PaymentRequestStatus Status { get; set; }
|
||||
}
|
||||
|
||||
public abstract class StoreWebhookEvent : WebhookEvent
|
||||
{
|
||||
[JsonProperty(Order = 1)] public string StoreId { get; set; }
|
||||
}
|
||||
|
||||
public class WebhookInvoiceEvent : StoreWebhookEvent
|
||||
{
|
||||
public WebhookInvoiceEvent()
|
||||
{
|
||||
}
|
||||
|
||||
public WebhookInvoiceEvent(WebhookEventType evtType)
|
||||
{
|
||||
this.Type = evtType;
|
||||
public WebhookInvoiceEvent(string evtType, string storeId)
|
||||
{
|
||||
if (!evtType.StartsWith("invoice", StringComparison.InvariantCultureIgnoreCase))
|
||||
throw new ArgumentException("Invalid event type", nameof(evtType));
|
||||
Type = evtType;
|
||||
StoreId = storeId;
|
||||
}
|
||||
|
||||
[JsonProperty(Order = 1)] public string StoreId { get; set; }
|
||||
[JsonProperty(Order = 2)] public string InvoiceId { get; set; }
|
||||
[JsonProperty(Order = 3)] public JObject Metadata { get; set; }
|
||||
}
|
||||
|
||||
public class WebhookInvoiceSettledEvent : WebhookInvoiceEvent
|
||||
{
|
||||
public WebhookInvoiceSettledEvent()
|
||||
{
|
||||
}
|
||||
|
||||
public WebhookInvoiceSettledEvent(WebhookEventType evtType) : base(evtType)
|
||||
public WebhookInvoiceSettledEvent(string storeId) : base(WebhookEventType.InvoiceSettled, storeId)
|
||||
{
|
||||
}
|
||||
|
||||
public bool ManuallyMarked { get; set; }
|
||||
public bool OverPaid { get; set; }
|
||||
}
|
||||
|
||||
public class WebhookInvoiceInvalidEvent : WebhookInvoiceEvent
|
||||
{
|
||||
public WebhookInvoiceInvalidEvent()
|
||||
{
|
||||
}
|
||||
|
||||
public WebhookInvoiceInvalidEvent(WebhookEventType evtType) : base(evtType)
|
||||
public WebhookInvoiceInvalidEvent(string storeId) : base(WebhookEventType.InvoiceInvalid, storeId)
|
||||
{
|
||||
}
|
||||
|
||||
@ -47,11 +77,7 @@ namespace BTCPayServer.Client.Models
|
||||
|
||||
public class WebhookInvoiceProcessingEvent : WebhookInvoiceEvent
|
||||
{
|
||||
public WebhookInvoiceProcessingEvent()
|
||||
{
|
||||
}
|
||||
|
||||
public WebhookInvoiceProcessingEvent(WebhookEventType evtType) : base(evtType)
|
||||
public WebhookInvoiceProcessingEvent(string storeId) : base(WebhookEventType.InvoiceProcessing, storeId)
|
||||
{
|
||||
}
|
||||
|
||||
@ -60,38 +86,25 @@ namespace BTCPayServer.Client.Models
|
||||
|
||||
public class WebhookInvoiceReceivedPaymentEvent : WebhookInvoiceEvent
|
||||
{
|
||||
public WebhookInvoiceReceivedPaymentEvent()
|
||||
{
|
||||
}
|
||||
|
||||
public WebhookInvoiceReceivedPaymentEvent(WebhookEventType evtType) : base(evtType)
|
||||
public WebhookInvoiceReceivedPaymentEvent(string type, string storeId) : base(type, storeId)
|
||||
{
|
||||
}
|
||||
|
||||
public bool AfterExpiration { get; set; }
|
||||
public string PaymentMethod { get; set; }
|
||||
public InvoicePaymentMethodDataModel.Payment Payment { get; set; }
|
||||
public bool OverPaid { get; set; }
|
||||
}
|
||||
|
||||
public class WebhookInvoicePaymentSettledEvent : WebhookInvoiceReceivedPaymentEvent
|
||||
{
|
||||
public WebhookInvoicePaymentSettledEvent()
|
||||
{
|
||||
}
|
||||
|
||||
public WebhookInvoicePaymentSettledEvent(WebhookEventType evtType) : base(evtType)
|
||||
public WebhookInvoicePaymentSettledEvent(string storeId) : base(WebhookEventType.InvoicePaymentSettled, storeId)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class WebhookInvoiceExpiredEvent : WebhookInvoiceEvent
|
||||
{
|
||||
public WebhookInvoiceExpiredEvent()
|
||||
{
|
||||
}
|
||||
|
||||
public WebhookInvoiceExpiredEvent(WebhookEventType evtType) : base(evtType)
|
||||
public WebhookInvoiceExpiredEvent(string storeId) : base(WebhookEventType.InvoiceExpired, storeId)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ namespace BTCPayServer.Client
|
||||
public const string CanModifyStoreWebhooks = "btcpay.store.webhooks.canmodifywebhooks";
|
||||
public const string CanModifyStoreSettingsUnscoped = "btcpay.store.canmodifystoresettings:";
|
||||
public const string CanViewStoreSettings = "btcpay.store.canviewstoresettings";
|
||||
public const string CanViewReports = "btcpay.store.canviewreports";
|
||||
public const string CanViewInvoices = "btcpay.store.canviewinvoices";
|
||||
public const string CanCreateInvoice = "btcpay.store.cancreateinvoice";
|
||||
public const string CanModifyInvoices = "btcpay.store.canmodifyinvoices";
|
||||
@ -34,7 +35,10 @@ namespace BTCPayServer.Client
|
||||
public const string CanDeleteUser = "btcpay.user.candeleteuser";
|
||||
public const string CanManagePullPayments = "btcpay.store.canmanagepullpayments";
|
||||
public const string CanArchivePullPayments = "btcpay.store.canarchivepullpayments";
|
||||
public const string CanManagePayouts = "btcpay.store.canmanagepayouts";
|
||||
public const string CanViewPayouts = "btcpay.store.canviewpayouts";
|
||||
public const string CanCreatePullPayments = "btcpay.store.cancreatepullpayments";
|
||||
public const string CanViewPullPayments = "btcpay.store.canviewpullpayments";
|
||||
public const string CanCreateNonApprovedPullPayments = "btcpay.store.cancreatenonapprovedpullpayments";
|
||||
public const string CanViewCustodianAccounts = "btcpay.store.canviewcustodianaccounts";
|
||||
public const string CanManageCustodianAccounts = "btcpay.store.canmanagecustodianaccounts";
|
||||
@ -53,6 +57,7 @@ namespace BTCPayServer.Client
|
||||
yield return CanModifyServerSettings;
|
||||
yield return CanModifyStoreSettings;
|
||||
yield return CanViewStoreSettings;
|
||||
yield return CanViewReports;
|
||||
yield return CanViewPaymentRequests;
|
||||
yield return CanModifyPaymentRequests;
|
||||
yield return CanModifyProfile;
|
||||
@ -72,6 +77,7 @@ namespace BTCPayServer.Client
|
||||
yield return CanManagePullPayments;
|
||||
yield return CanArchivePullPayments;
|
||||
yield return CanCreatePullPayments;
|
||||
yield return CanViewPullPayments;
|
||||
yield return CanCreateNonApprovedPullPayments;
|
||||
yield return CanViewCustodianAccounts;
|
||||
yield return CanManageCustodianAccounts;
|
||||
@ -79,6 +85,8 @@ namespace BTCPayServer.Client
|
||||
yield return CanWithdrawFromCustodianAccounts;
|
||||
yield return CanTradeCustodianAccount;
|
||||
yield return CanManageUsers;
|
||||
yield return CanManagePayouts;
|
||||
yield return CanViewPayouts;
|
||||
}
|
||||
}
|
||||
public static bool IsValidPolicy(string policy)
|
||||
@ -252,11 +260,13 @@ namespace BTCPayServer.Client
|
||||
Policies.CanViewStoreSettings,
|
||||
Policies.CanModifyStoreWebhooks,
|
||||
Policies.CanModifyPaymentRequests,
|
||||
Policies.CanManagePayouts,
|
||||
Policies.CanUseLightningNodeInStore);
|
||||
|
||||
PolicyHasChild(policyMap,Policies.CanManageUsers, Policies.CanCreateUser);
|
||||
PolicyHasChild(policyMap,Policies.CanManagePullPayments, Policies.CanCreatePullPayments, Policies.CanArchivePullPayments);
|
||||
PolicyHasChild(policyMap,Policies.CanCreatePullPayments, Policies.CanCreateNonApprovedPullPayments);
|
||||
PolicyHasChild(policyMap, Policies.CanCreateNonApprovedPullPayments, Policies.CanViewPullPayments);
|
||||
PolicyHasChild(policyMap,Policies.CanModifyPaymentRequests, Policies.CanViewPaymentRequests);
|
||||
PolicyHasChild(policyMap,Policies.CanModifyProfile, Policies.CanViewProfile);
|
||||
PolicyHasChild(policyMap,Policies.CanUseLightningNodeInStore, Policies.CanViewLightningInvoiceInStore, Policies.CanCreateLightningInvoiceInStore);
|
||||
@ -267,7 +277,8 @@ namespace BTCPayServer.Client
|
||||
PolicyHasChild(policyMap, Policies.CanUseInternalLightningNode, Policies.CanCreateLightningInvoiceInternalNode, Policies.CanViewLightningInvoiceInternalNode);
|
||||
PolicyHasChild(policyMap, Policies.CanManageCustodianAccounts, Policies.CanViewCustodianAccounts);
|
||||
PolicyHasChild(policyMap, Policies.CanModifyInvoices, Policies.CanViewInvoices, Policies.CanCreateInvoice, Policies.CanCreateLightningInvoiceInStore);
|
||||
PolicyHasChild(policyMap, Policies.CanViewStoreSettings, Policies.CanViewInvoices, Policies.CanViewPaymentRequests);
|
||||
PolicyHasChild(policyMap, Policies.CanViewStoreSettings, Policies.CanViewInvoices, Policies.CanViewPaymentRequests, Policies.CanViewReports, Policies.CanViewPullPayments, Policies.CanViewPayouts);
|
||||
PolicyHasChild(policyMap, Policies.CanManagePayouts, Policies.CanViewPayouts);
|
||||
|
||||
var missingPolicies = Policies.AllPolicies.ToHashSet();
|
||||
//recurse through the tree to see which policies are not included in the tree
|
||||
|
@ -1,28 +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 = "Htmlcoin",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://explorer.htmlcoin.com/api/tx/{0}" : "https://explorer.htmlcoin.com/api/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
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("172'") : new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitArgoneum()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("AGM");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Argoneum",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet
|
||||
? "https://chainz.cryptoid.info/agm/tx.dws?{0}"
|
||||
: "https://chainz.cryptoid.info/agm-test/tx.dws?{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"AGM_X = AGM_BTC * BTC_X",
|
||||
"AGM_BTC = argoneum(AGM_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/argoneum.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("421'")
|
||||
: new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitBGold()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("BTG");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "BGold",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://btgexplorer.com/tx/{0}" : "https://testnet.btgexplorer.com/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"BTG_X = BTG_BTC * BTC_X",
|
||||
"BTG_BTC = gate(BTG_BTC)",
|
||||
},
|
||||
CryptoImagePath = "imlegacy/btg.svg",
|
||||
LightningImagePath = "imlegacy/btg-lightning.svg",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("156'") : new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitBPlus()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("XBC");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "BPlus",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://chainz.cryptoid.info/xbc/tx.dws?{0}" : "https://chainz.cryptoid.info/xbc/tx.dws?{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"XBC_X = XBC_BTC * BTC_X",
|
||||
"XBC_BTC = cryptopia(XBC_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/xbc.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("65'") : new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitBitcore()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("BTX");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "BitCore",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://explorer.bitcore.cc/tx/{0}" : "https://explorer.bitcore.cc/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"BTX_X = BTX_BTC * BTC_X",
|
||||
"BTX_BTC = graviex(BTX_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/bitcore.svg",
|
||||
LightningImagePath = "imlegacy/bitcore-lightning.svg",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("160'") : new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitDash()
|
||||
{
|
||||
//not needed: NBitcoin.Altcoins.Dash.Instance.EnsureRegistered();
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("DASH");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Dash",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet
|
||||
? "https://insight.dash.org/insight/tx/{0}"
|
||||
: "https://testnet-insight.dashevo.org/insight/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"DASH_X = DASH_BTC * BTC_X",
|
||||
"DASH_BTC = bitfinex(DSH_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/dash.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
//https://github.com/satoshilabs/slips/blob/master/slip-0044.md
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("5'")
|
||||
: new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitDogecoin()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("DOGE");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Dogecoin",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://dogechain.info/tx/{0}" : "https://dogechain.info/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"DOGE_X = DOGE_BTC * BTC_X",
|
||||
"DOGE_BTC = bittrex(DOGE_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/dogecoin.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("3'") : new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitFeathercoin()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("FTC");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Feathercoin",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://explorer.feathercoin.com/tx/{0}" : "https://explorer.feathercoin.com/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"FTC_X = FTC_BTC * BTC_X",
|
||||
"FTC_BTC = bittrex(FTC_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/feathercoin.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("8'") : new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitGroestlcoin()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("GRS");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Groestlcoin",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet
|
||||
? "https://chainz.cryptoid.info/grs/tx.dws?{0}.htm"
|
||||
: "https://chainz.cryptoid.info/grs-test/tx.dws?{0}.htm",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"GRS_X = GRS_BTC * BTC_X",
|
||||
"GRS_BTC = bittrex(GRS_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/groestlcoin.png",
|
||||
LightningImagePath = "imlegacy/groestlcoin-lightning.svg",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("17'") : new KeyPath("1'"),
|
||||
SupportRBF = true,
|
||||
SupportPayJoin = true,
|
||||
VaultSupported = true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitLitecoin()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("LTC");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Litecoin",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet
|
||||
? "https://live.blockcypher.com/ltc/tx/{0}/"
|
||||
: "http://explorer.litecointools.com/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"LTC_X = LTC_BTC * BTC_X",
|
||||
"LTC_BTC = coingecko(LTC_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/litecoin.svg",
|
||||
LightningImagePath = "imlegacy/litecoin-lightning.svg",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("2'") : new KeyPath("1'"),
|
||||
//https://github.com/pooler/electrum-ltc/blob/0d6989a9d2fb2edbea421c116e49d1015c7c5a91/electrum_ltc/constants.py
|
||||
ElectrumMapping = NetworkType == ChainName.Mainnet
|
||||
? new Dictionary<uint, DerivationType>()
|
||||
{
|
||||
{0x0488b21eU, DerivationType.Legacy },
|
||||
{0x049d7cb2U, DerivationType.SegwitP2SH },
|
||||
{0x04b24746U, DerivationType.Segwit },
|
||||
}
|
||||
: new Dictionary<uint, DerivationType>()
|
||||
{
|
||||
{0x043587cfU, DerivationType.Legacy },
|
||||
{0x044a5262U, DerivationType.SegwitP2SH },
|
||||
{0x045f1cf6U, DerivationType.Segwit }
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitMonacoin()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("MONA");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Monacoin",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://mona.insight.monaco-ex.org/insight/tx/{0}" : "https://testnet-mona.insight.monaco-ex.org/insight/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"MONA_X = MONA_BTC * BTC_X",
|
||||
"MONA_BTC = bittrex(MONA_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/monacoin.png",
|
||||
LightningImagePath = "imlegacy/mona-lightning.svg",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("22'") : new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitPolis()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("POLIS");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Polis",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://blockbook.polispay.org/tx/{0}" : "https://blockbook.polispay.org/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"POLIS_X = POLIS_BTC * BTC_X",
|
||||
"POLIS_BTC = polispay(POLIS_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/polis.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("1997'") : new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitUfo()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("UFO");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Ufo",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://chainz.cryptoid.info/ufo/tx.dws?{0}" : "https://chainz.cryptoid.info/ufo/tx.dws?{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"UFO_X = UFO_BTC * BTC_X",
|
||||
"UFO_BTC = coinexchange(UFO_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/ufo.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("202'") : new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitViacoin()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("VIA");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Viacoin",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://explorer.viacoin.org/tx/{0}" : "https://explorer.viacoin.org/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"VIA_X = VIA_BTC * BTC_X",
|
||||
"VIA_BTC = bittrex(VIA_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/viacoin.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("14'") : new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
#if ALTCOINS
|
||||
using NBitcoin;
|
||||
using NBitcoin.Altcoins;
|
||||
using NBitcoin.Altcoins.Elements;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitLiquid()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("LBTC");
|
||||
Add(new ElementsBTCPayNetwork()
|
||||
{
|
||||
AssetId = NetworkType == ChainName.Mainnet ? ElementsParams<Liquid>.PeggedAssetId : ElementsParams<Liquid.LiquidRegtest>.PeggedAssetId,
|
||||
CryptoCode = "LBTC",
|
||||
NetworkCryptoCode = "LBTC",
|
||||
DisplayName = "Liquid Bitcoin",
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"LBTC_X = LBTC_BTC * BTC_X",
|
||||
"LBTC_BTC = 1",
|
||||
},
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://liquid.network/tx/{0}" : "https://liquid.network/testnet/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
CryptoImagePath = "imlegacy/liquid.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("1776'") : new KeyPath("1'"),
|
||||
SupportRBF = true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
#endif
|
@ -1,83 +0,0 @@
|
||||
#if ALTCOINS
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitLiquidAssets()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("LBTC");
|
||||
Add(new ElementsBTCPayNetwork()
|
||||
{
|
||||
CryptoCode = "USDt",
|
||||
NetworkCryptoCode = "LBTC",
|
||||
ShowSyncSummary = false,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"USDT_UST = 1",
|
||||
"USDT_X = USDT_BTC * BTC_X",
|
||||
"USDT_BTC = bitfinex(UST_BTC)",
|
||||
},
|
||||
AssetId = new uint256("ce091c998b83c78bb71a632313ba3760f1763d9cfcffae02258ffa9865a37bd2"),
|
||||
DisplayName = "Liquid Tether",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://liquid.network/tx/{0}" : "https://liquid.network/testnet/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
CryptoImagePath = "imlegacy/liquid-tether.svg",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("1776'") : new KeyPath("1'"),
|
||||
SupportRBF = true,
|
||||
SupportLightning = false
|
||||
});
|
||||
|
||||
Add(new ElementsBTCPayNetwork()
|
||||
{
|
||||
CryptoCode = "ETB",
|
||||
NetworkCryptoCode = "LBTC",
|
||||
ShowSyncSummary = false,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
|
||||
"ETB_X = ETB_BTC * BTC_X",
|
||||
"ETB_BTC = bitpay(ETB_BTC)"
|
||||
},
|
||||
Divisibility = 2,
|
||||
AssetId = new uint256("aa775044c32a7df391902b3659f46dfe004ccb2644ce2ddc7dba31e889391caf"),
|
||||
DisplayName = "Ethiopian Birr",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://liquid.network/tx/{0}" : "https://liquid.network/testnet/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
CryptoImagePath = "imlegacy/etb.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("1776'") : new KeyPath("1'"),
|
||||
SupportRBF = true,
|
||||
SupportLightning = false
|
||||
});
|
||||
|
||||
Add(new ElementsBTCPayNetwork()
|
||||
{
|
||||
CryptoCode = "LCAD",
|
||||
NetworkCryptoCode = "LBTC",
|
||||
ShowSyncSummary = false,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"LCAD_CAD = 1",
|
||||
"LCAD_X = CAD_BTC * BTC_X",
|
||||
"LCAD_BTC = bylls(CAD_BTC)",
|
||||
"CAD_BTC = LCAD_BTC"
|
||||
},
|
||||
AssetId = new uint256("0e99c1a6da379d1f4151fb9df90449d40d0608f6cb33a5bcbfc8c265f42bab0a"),
|
||||
DisplayName = "Liquid CAD",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://liquid.network/tx/{0}" : "https://liquid.network/testnet/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
CryptoImagePath = "imlegacy/lcad.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("1776'") : new KeyPath("1'"),
|
||||
SupportRBF = true,
|
||||
SupportLightning = false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
#endif
|
@ -1,50 +0,0 @@
|
||||
#if ALTCOINS
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BTCPayServer.Common;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
using NBXplorer.Models;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public class ElementsBTCPayNetwork : BTCPayNetwork
|
||||
{
|
||||
public string NetworkCryptoCode { get; set; }
|
||||
public uint256 AssetId { get; set; }
|
||||
public override bool ReadonlyWallet { get; set; } = true;
|
||||
|
||||
public override IEnumerable<(MatchedOutput matchedOutput, OutPoint outPoint)> GetValidOutputs(
|
||||
NewTransactionEvent evtOutputs)
|
||||
{
|
||||
return evtOutputs.Outputs.Where(output =>
|
||||
output.Value is AssetMoney assetMoney && assetMoney.AssetId == AssetId).Select(output =>
|
||||
{
|
||||
var outpoint = new OutPoint(evtOutputs.TransactionData.TransactionHash, output.Index);
|
||||
return (output, outpoint);
|
||||
});
|
||||
}
|
||||
|
||||
public override List<TransactionInformation> FilterValidTransactions(List<TransactionInformation> transactionInformationSet)
|
||||
{
|
||||
return transactionInformationSet.FindAll(information =>
|
||||
information.Outputs.Any(output =>
|
||||
output.Value is AssetMoney assetMoney && assetMoney.AssetId == AssetId) ||
|
||||
information.Inputs.Any(output =>
|
||||
output.Value is AssetMoney assetMoney && assetMoney.AssetId == AssetId));
|
||||
}
|
||||
|
||||
public override PaymentUrlBuilder GenerateBIP21(string cryptoInfoAddress, decimal? cryptoInfoDue)
|
||||
{
|
||||
//precision 0: 10 = 0.00000010
|
||||
//precision 2: 10 = 0.00001000
|
||||
//precision 8: 10 = 10
|
||||
var money = cryptoInfoDue / (decimal)Math.Pow(10, 8 - Divisibility);
|
||||
var builder = base.GenerateBIP21(cryptoInfoAddress, money);
|
||||
builder.QueryParams.Add("assetid", AssetId.ToString());
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,18 +0,0 @@
|
||||
#if ALTCOINS
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public static class LiquidExtensions
|
||||
{
|
||||
public static IEnumerable<string> GetAllElementsSubChains(this BTCPayNetworkProvider networkProvider, BTCPayNetworkProvider unfiltered)
|
||||
{
|
||||
var elementsBased = networkProvider.GetAll().OfType<ElementsBTCPayNetwork>();
|
||||
var parentChains = elementsBased.Select(network => network.NetworkCryptoCode.ToUpperInvariant()).Distinct();
|
||||
return unfiltered.GetAll().OfType<ElementsBTCPayNetwork>()
|
||||
.Where(network => parentChains.Contains(network.NetworkCryptoCode)).Select(network => network.CryptoCode.ToUpperInvariant());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,28 +0,0 @@
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitMonero()
|
||||
{
|
||||
Add(new MoneroLikeSpecificBtcPayNetwork()
|
||||
{
|
||||
CryptoCode = "XMR",
|
||||
DisplayName = "Monero",
|
||||
Divisibility = 12,
|
||||
BlockExplorerLink =
|
||||
NetworkType == ChainName.Mainnet
|
||||
? "https://www.exploremonero.com/transaction/{0}"
|
||||
: "https://testnet.xmrchain.net/tx/{0}",
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"XMR_X = XMR_BTC * BTC_X",
|
||||
"XMR_BTC = kraken(XMR_BTC)"
|
||||
},
|
||||
CryptoImagePath = "/imlegacy/monero.svg",
|
||||
UriScheme = "monero"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public class MoneroLikeSpecificBtcPayNetwork : BTCPayNetworkBase
|
||||
{
|
||||
public int MaxTrackedConfirmation = 10;
|
||||
public string UriScheme { get; set; }
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
// Change this if you want another zcash coin
|
||||
public void InitZcash()
|
||||
{
|
||||
Add(new ZcashLikeSpecificBtcPayNetwork()
|
||||
{
|
||||
CryptoCode = "ZEC",
|
||||
DisplayName = "Zcash",
|
||||
Divisibility = 8,
|
||||
BlockExplorerLink =
|
||||
NetworkType == ChainName.Mainnet
|
||||
? "https://www.exploreZcash.com/transaction/{0}"
|
||||
: "https://testnet.xmrchain.net/tx/{0}",
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"ZEC_X = ZEC_BTC * BTC_X",
|
||||
"ZEC_BTC = kraken(ZEC_BTC)"
|
||||
},
|
||||
CryptoImagePath = "/imlegacy/zcash.png",
|
||||
UriScheme = "zcash"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public class ZcashLikeSpecificBtcPayNetwork : BTCPayNetworkBase
|
||||
{
|
||||
public int MaxTrackedConfirmation = 10;
|
||||
public string UriScheme { get; set; }
|
||||
}
|
||||
}
|
@ -4,6 +4,8 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BTCPayServer.Common;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
using NBXplorer.Models;
|
||||
@ -62,6 +64,31 @@ namespace BTCPayServer
|
||||
public KeyPath CoinType { get; set; }
|
||||
|
||||
public Dictionary<uint, DerivationType> ElectrumMapping = new Dictionary<uint, DerivationType>();
|
||||
public BTCPayNetwork SetDefaultElectrumMapping(ChainName chainName)
|
||||
{
|
||||
//https://github.com/spesmilo/electrum/blob/11733d6bc271646a00b69ff07657119598874da4/electrum/constants.py
|
||||
ElectrumMapping = chainName == ChainName.Mainnet
|
||||
? new Dictionary<uint, DerivationType>()
|
||||
{
|
||||
{0x0488b21eU, DerivationType.Legacy }, // xpub
|
||||
{0x049d7cb2U, DerivationType.SegwitP2SH }, // ypub
|
||||
{0x04b24746U, DerivationType.Segwit }, //zpub
|
||||
}
|
||||
: new Dictionary<uint, DerivationType>()
|
||||
{
|
||||
{0x043587cfU, DerivationType.Legacy}, // tpub
|
||||
{0x044a5262U, DerivationType.SegwitP2SH}, // upub
|
||||
{0x045f1cf6U, DerivationType.Segwit} // vpub
|
||||
};
|
||||
if (!NBitcoinNetwork.Consensus.SupportSegwit)
|
||||
{
|
||||
ElectrumMapping =
|
||||
ElectrumMapping
|
||||
.Where(kv => kv.Value == DerivationType.Legacy)
|
||||
.ToDictionary(k => k.Key, k => k.Value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual bool WalletSupported { get; set; } = true;
|
||||
public virtual bool ReadonlyWallet { get; set; } = false;
|
||||
@ -107,25 +134,8 @@ namespace BTCPayServer
|
||||
|
||||
public abstract class BTCPayNetworkBase
|
||||
{
|
||||
private string _blockExplorerLink;
|
||||
public bool ShowSyncSummary { get; set; } = true;
|
||||
public string CryptoCode { get; set; }
|
||||
|
||||
public string BlockExplorerLink
|
||||
{
|
||||
get => _blockExplorerLink;
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(BlockExplorerLinkDefault))
|
||||
{
|
||||
BlockExplorerLinkDefault = value;
|
||||
}
|
||||
|
||||
_blockExplorerLink = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string BlockExplorerLinkDefault { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
public int Divisibility { get; set; } = 8;
|
||||
public bool IsBTC
|
||||
@ -153,5 +163,8 @@ namespace BTCPayServer
|
||||
{
|
||||
return NBitcoin.JsonConverters.Serializer.ToString(obj, null);
|
||||
}
|
||||
|
||||
[Obsolete("Use TransactionLinkProviders service instead")]
|
||||
public string BlockExplorerLink { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,44 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitBitcoin()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("BTC");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Bitcoin",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://mempool.space/tx/{0}" :
|
||||
NetworkType == Bitcoin.Instance.Signet.ChainName ? "https://mempool.space/signet/tx/{0}"
|
||||
: "https://mempool.space/testnet/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
CryptoImagePath = "imlegacy/bitcoin.svg",
|
||||
LightningImagePath = "imlegacy/bitcoin-lightning.svg",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("0'") : new KeyPath("1'"),
|
||||
SupportRBF = true,
|
||||
SupportPayJoin = true,
|
||||
VaultSupported = true,
|
||||
//https://github.com/spesmilo/electrum/blob/11733d6bc271646a00b69ff07657119598874da4/electrum/constants.py
|
||||
ElectrumMapping = NetworkType == ChainName.Mainnet
|
||||
? new Dictionary<uint, DerivationType>()
|
||||
{
|
||||
{0x0488b21eU, DerivationType.Legacy }, // xpub
|
||||
{0x049d7cb2U, DerivationType.SegwitP2SH }, // ypub
|
||||
{0x04b24746U, DerivationType.Segwit }, //zpub
|
||||
}
|
||||
: new Dictionary<uint, DerivationType>()
|
||||
{
|
||||
{0x043587cfU, DerivationType.Legacy}, // tpub
|
||||
{0x044a5262U, DerivationType.SegwitP2SH}, // upub
|
||||
{0x045f1cf6U, DerivationType.Segwit} // vpub
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BTCPayServer.Configuration;
|
||||
using BTCPayServer.Logging;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
using StandardConfiguration;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
@ -19,92 +26,39 @@ namespace BTCPayServer
|
||||
}
|
||||
}
|
||||
|
||||
BTCPayNetworkProvider(BTCPayNetworkProvider unfiltered, string[] cryptoCodes)
|
||||
{
|
||||
NetworkType = unfiltered.NetworkType;
|
||||
_NBXplorerNetworkProvider = new NBXplorerNetworkProvider(unfiltered.NetworkType);
|
||||
_Networks = new Dictionary<string, BTCPayNetworkBase>();
|
||||
cryptoCodes = cryptoCodes.Select(c => c.ToUpperInvariant()).ToArray();
|
||||
foreach (var network in unfiltered._Networks)
|
||||
{
|
||||
if (cryptoCodes.Contains(network.Key))
|
||||
{
|
||||
_Networks.Add(network.Key, network.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public ChainName NetworkType { get; private set; }
|
||||
public BTCPayNetworkProvider(ChainName networkType)
|
||||
public BTCPayNetworkProvider(
|
||||
IEnumerable<BTCPayNetworkBase> networks,
|
||||
SelectedChains selectedChains,
|
||||
NBXplorerNetworkProvider nbxplorerNetworkProvider,
|
||||
Logs logs,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
_NBXplorerNetworkProvider = new NBXplorerNetworkProvider(networkType);
|
||||
NetworkType = networkType;
|
||||
InitBitcoin();
|
||||
#if ALTCOINS
|
||||
InitLiquid();
|
||||
InitLiquidAssets();
|
||||
InitLitecoin();
|
||||
InitBitcore();
|
||||
InitDogecoin();
|
||||
InitBGold();
|
||||
InitMonacoin();
|
||||
InitDash();
|
||||
InitFeathercoin();
|
||||
InitAlthash();
|
||||
InitGroestlcoin();
|
||||
InitViacoin();
|
||||
InitMonero();
|
||||
InitZcash();
|
||||
// InitArgoneum();//their rate source is down 9/15/20.
|
||||
// InitMonetaryUnit(); Not supported from Bittrex from 11/23/2022, dead shitcoin
|
||||
#if !ALTCOINS
|
||||
var onlyBTC = networks.Count() == 1 && networks.First().IsBTC;
|
||||
if (!onlyBTC)
|
||||
throw new ConfigException($"This build of BTCPay Server does not support altcoins");
|
||||
#endif
|
||||
|
||||
// Assume that electrum mappings are same as BTC if not specified
|
||||
foreach (var network in _Networks.Values.OfType<BTCPayNetwork>())
|
||||
_NBXplorerNetworkProvider = nbxplorerNetworkProvider;
|
||||
NetworkType = nbxplorerNetworkProvider.NetworkType;
|
||||
foreach (var network in networks)
|
||||
{
|
||||
if (network.ElectrumMapping.Count == 0)
|
||||
{
|
||||
network.ElectrumMapping = GetNetwork<BTCPayNetwork>("BTC").ElectrumMapping;
|
||||
if (!network.NBitcoinNetwork.Consensus.SupportSegwit)
|
||||
{
|
||||
network.ElectrumMapping =
|
||||
network.ElectrumMapping
|
||||
.Where(kv => kv.Value == DerivationType.Legacy)
|
||||
.ToDictionary(k => k.Key, k => k.Value);
|
||||
}
|
||||
}
|
||||
_Networks.Add(network.CryptoCode.ToUpperInvariant(), network);
|
||||
}
|
||||
|
||||
// Disabled because of https://twitter.com/Cryptopia_NZ/status/1085084168852291586
|
||||
//InitBPlus();
|
||||
//InitUfo();
|
||||
#endif
|
||||
}
|
||||
foreach (var chain in selectedChains.ExplicitlySelected)
|
||||
{
|
||||
if (GetNetwork<BTCPayNetworkBase>(chain) == null)
|
||||
throw new ConfigException($"Invalid chains \"{chain}\"");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Keep only the specified crypto
|
||||
/// </summary>
|
||||
/// <param name="cryptoCodes">Crypto to support</param>
|
||||
/// <returns></returns>
|
||||
public BTCPayNetworkProvider Filter(string[] cryptoCodes)
|
||||
{
|
||||
return new BTCPayNetworkProvider(this, cryptoCodes);
|
||||
logs.Configuration.LogInformation(
|
||||
"Supported chains: " + String.Join(',', _Networks.Select(n => n.Key).ToArray()));
|
||||
}
|
||||
|
||||
public BTCPayNetwork BTC => GetNetwork<BTCPayNetwork>("BTC");
|
||||
public BTCPayNetworkBase DefaultNetwork => BTC ?? GetAll().First();
|
||||
|
||||
public void Add(BTCPayNetwork network)
|
||||
{
|
||||
if (network.NBitcoinNetwork == null)
|
||||
return;
|
||||
Add(network as BTCPayNetworkBase);
|
||||
}
|
||||
public void Add(BTCPayNetworkBase network)
|
||||
{
|
||||
_Networks.Add(network.CryptoCode.ToUpperInvariant(), network);
|
||||
}
|
||||
|
||||
public IEnumerable<BTCPayNetworkBase> GetAll()
|
||||
{
|
||||
return _Networks.Values.ToArray();
|
||||
|
@ -4,10 +4,13 @@
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="NBXplorer.Client" Version="4.2.5" />
|
||||
<PackageReference Include="NBXplorer.Client" Version="4.2.6" />
|
||||
<PackageReference Include="NicolasDorier.StandardConfiguration" Version="2.0.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(Altcoins)' != 'true'">
|
||||
<Compile Remove="Altcoins\**\*.cs"></Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Altcoins\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
0
BTCPayServer/Configuration/ConfigException.cs → BTCPayServer.Common/Configuration/ConfigException.cs
0
BTCPayServer/Configuration/ConfigException.cs → BTCPayServer.Common/Configuration/ConfigException.cs
49
BTCPayServer.Common/SelectedChains.cs
Normal file
49
BTCPayServer.Common/SelectedChains.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BTCPayServer.Logging;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json.Bson;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public class SelectedChains
|
||||
{
|
||||
HashSet<string> chains = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
bool all = false;
|
||||
public SelectedChains(IConfiguration configuration, Logs logs)
|
||||
{
|
||||
foreach (var chain in (configuration["chains"] ?? "btc")
|
||||
.Split(',', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(t => t.ToUpperInvariant()))
|
||||
{
|
||||
if (new[] { "ETH", "USDT20", "FAU" }.Contains(chain, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
logs.Configuration.LogWarning($"'{chain}' is not anymore supported, please remove it from 'chains'");
|
||||
continue;
|
||||
}
|
||||
if (chain == "*")
|
||||
{
|
||||
all = true;
|
||||
continue;
|
||||
}
|
||||
chains.Add(chain);
|
||||
}
|
||||
if (chains.Count == 0)
|
||||
chains.Add("BTC");
|
||||
if (all)
|
||||
chains.Clear();
|
||||
}
|
||||
|
||||
public bool Contains(string cryptoCode)
|
||||
{
|
||||
return all || chains.Contains(cryptoCode);
|
||||
}
|
||||
public void Add(string cryptoCode)
|
||||
{
|
||||
chains.Add(cryptoCode);
|
||||
}
|
||||
public IEnumerable<string> ExplicitlySelected => chains;
|
||||
}
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="../Build/Version.csproj" Condition="Exists('../Build/Version.csproj')" />
|
||||
<Import Project="../Build/Common.csproj" />
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.9">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.9" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BTCPayServer.Abstractions\BTCPayServer.Abstractions.csproj" />
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
|
@ -30,9 +30,6 @@ namespace BTCPayServer.Data
|
||||
public List<PendingInvoiceData> PendingInvoices { get; set; }
|
||||
public List<InvoiceSearchData> InvoiceSearchData { get; set; }
|
||||
public List<RefundData> Refunds { get; set; }
|
||||
public string CurrentRefundId { get; set; }
|
||||
[ForeignKey("Id,CurrentRefundId")]
|
||||
public RefundData CurrentRefund { get; set; }
|
||||
|
||||
|
||||
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
||||
@ -42,8 +39,6 @@ namespace BTCPayServer.Data
|
||||
.WithMany(a => a.Invoices).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.Entity<InvoiceData>().HasIndex(o => o.StoreDataId);
|
||||
builder.Entity<InvoiceData>().HasIndex(o => o.OrderId);
|
||||
builder.Entity<InvoiceData>()
|
||||
.HasOne(o => o.CurrentRefund);
|
||||
builder.Entity<InvoiceData>().HasIndex(o => o.Created);
|
||||
|
||||
if (databaseFacade.IsNpgsql())
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
@ -13,7 +14,6 @@ namespace BTCPayServer.Data
|
||||
public PullPaymentData PullPaymentData { get; set; }
|
||||
public InvoiceData InvoiceData { get; set; }
|
||||
|
||||
|
||||
internal static void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
builder.Entity<RefundData>()
|
||||
|
@ -1,14 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public class WalletObjectData
|
||||
public class WalletObjectData : IEqualityComparer<WalletObjectData>
|
||||
{
|
||||
public class Types
|
||||
{
|
||||
@ -88,9 +86,30 @@ namespace BTCPayServer.Data
|
||||
if (databaseFacade.IsNpgsql())
|
||||
{
|
||||
builder.Entity<WalletObjectData>()
|
||||
.Property(o => o.Data)
|
||||
.HasColumnType("JSONB");
|
||||
.Property(o => o.Data)
|
||||
.HasColumnType("JSONB");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(WalletObjectData x, WalletObjectData y)
|
||||
{
|
||||
if (ReferenceEquals(x, y)) return true;
|
||||
if (ReferenceEquals(x, null)) return false;
|
||||
if (ReferenceEquals(y, null)) return false;
|
||||
if (x.GetType() != y.GetType()) return false;
|
||||
return string.Equals(x.WalletId, y.WalletId, StringComparison.InvariantCultureIgnoreCase) &&
|
||||
string.Equals(x.Type, y.Type, StringComparison.InvariantCultureIgnoreCase) &&
|
||||
string.Equals(x.Id, y.Id, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
public int GetHashCode(WalletObjectData obj)
|
||||
{
|
||||
HashCode hashCode = new HashCode();
|
||||
hashCode.Add(obj.WalletId, StringComparer.InvariantCultureIgnoreCase);
|
||||
hashCode.Add(obj.Type, StringComparer.InvariantCultureIgnoreCase);
|
||||
hashCode.Add(obj.Id, StringComparer.InvariantCultureIgnoreCase);
|
||||
return hashCode.ToHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
|
@ -0,0 +1,38 @@
|
||||
using System.Security.Permissions;
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BTCPayServer.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20231020135844_AddBoltcardsTable")]
|
||||
public partial class AddBoltcardsTable : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "boltcards",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<string>(maxLength: 32, nullable: false),
|
||||
counter = table.Column<int>(type: "INT", nullable: false, defaultValue: 0),
|
||||
ppid = table.Column<string>(maxLength: 30, nullable: true),
|
||||
version = table.Column<int>(nullable: false, defaultValue: 0)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_id", x => x.id);
|
||||
table.ForeignKey("FK_boltcards_PullPayments", x => x.ppid, "PullPayments", "Id", onDelete: ReferentialAction.SetNull);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable("boltcards");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BTCPayServer.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20231121031609_removecurrentrefund")]
|
||||
public partial class removecurrentrefund : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder.IsNpgsql())
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Invoices_Refunds_Id_CurrentRefundId",
|
||||
table: "Invoices");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Invoices_Id_CurrentRefundId",
|
||||
table: "Invoices");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CurrentRefundId",
|
||||
table: "Invoices");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -287,9 +287,6 @@ namespace BTCPayServer.Migrations
|
||||
b.Property<DateTimeOffset>("Created")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CurrentRefundId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomerEmail")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@ -316,8 +313,6 @@ namespace BTCPayServer.Migrations
|
||||
|
||||
b.HasIndex("StoreDataId");
|
||||
|
||||
b.HasIndex("Id", "CurrentRefundId");
|
||||
|
||||
b.ToTable("Invoices");
|
||||
});
|
||||
|
||||
@ -1251,12 +1246,6 @@ namespace BTCPayServer.Migrations
|
||||
.HasForeignKey("StoreDataId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("BTCPayServer.Data.RefundData", "CurrentRefund")
|
||||
.WithMany()
|
||||
.HasForeignKey("Id", "CurrentRefundId");
|
||||
|
||||
b.Navigation("CurrentRefund");
|
||||
|
||||
b.Navigation("StoreData");
|
||||
});
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Version>1.0.0.0</Version>
|
||||
<PackAsTool>true</PackAsTool>
|
||||
<ToolCommandName>btcpay-plugin</ToolCommandName>
|
||||
|
@ -4,11 +4,11 @@
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.9" />
|
||||
<PackageReference Include="NBitcoin" Version="7.0.24" />
|
||||
<PackageReference Include="NBitcoin" Version="7.0.32" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="DigitalRuby.ExchangeSharp" Version="1.0.2" />
|
||||
<PackageReference Include="DigitalRuby.ExchangeSharp" Version="1.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -20,13 +20,13 @@ namespace BTCPayServer.Services.Rates
|
||||
}
|
||||
public class CurrencyNameTable
|
||||
{
|
||||
public static CurrencyNameTable Instance = new CurrencyNameTable();
|
||||
public static CurrencyNameTable Instance = new();
|
||||
public CurrencyNameTable()
|
||||
{
|
||||
_Currencies = LoadCurrency().ToDictionary(k => k.Code);
|
||||
_Currencies = LoadCurrency().ToDictionary(k => k.Code, StringComparer.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
static readonly Dictionary<string, IFormatProvider> _CurrencyProviders = new Dictionary<string, IFormatProvider>();
|
||||
static readonly Dictionary<string, IFormatProvider> _CurrencyProviders = new();
|
||||
|
||||
public NumberFormatInfo GetNumberFormatInfo(string currency, bool useFallback)
|
||||
{
|
||||
|
@ -19,7 +19,7 @@ namespace BTCPayServer.Rating
|
||||
public static CurrencyPair Parse(string str)
|
||||
{
|
||||
if (!TryParse(str, out var result))
|
||||
throw new FormatException("Invalid currency pair");
|
||||
throw new FormatException($"Invalid currency pair ({str})");
|
||||
return result;
|
||||
}
|
||||
public static bool TryParse(string str, out CurrencyPair value)
|
||||
|
@ -1,29 +0,0 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Rating;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public class ArgoneumRateProvider : IRateProvider
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
public ArgoneumRateProvider(HttpClient httpClient)
|
||||
{
|
||||
_httpClient = httpClient ?? new HttpClient();
|
||||
}
|
||||
|
||||
public RateSourceInfo RateSourceInfo => new("argoneum", "Argoneum", "https://rates.argoneum.net/rates");
|
||||
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
// Example result: AGM to BTC rate: {"agm":5000000.000000}
|
||||
var response = await _httpClient.GetAsync("https://rates.argoneum.net/rates/btc", cancellationToken);
|
||||
var jobj = await response.Content.ReadAsAsync<JObject>(cancellationToken);
|
||||
var value = jobj["agm"].Value<decimal>();
|
||||
return new[] { new PairRate(new CurrencyPair("BTC", "AGM"), new BidAsk(value)) };
|
||||
}
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Rating;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Services.Rates;
|
||||
|
||||
|
||||
public class ExchangeRateHostRateProvider : IRateProvider
|
||||
{
|
||||
public RateSourceInfo RateSourceInfo => new("exchangeratehost", "Exchangerate.host", "https://api.exchangerate.host/latest?base=BTC");
|
||||
private readonly HttpClient _httpClient;
|
||||
public ExchangeRateHostRateProvider(HttpClient httpClient)
|
||||
{
|
||||
_httpClient = httpClient ?? new HttpClient();
|
||||
}
|
||||
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _httpClient.GetAsync(RateSourceInfo.Url, cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var jobj = await response.Content.ReadAsAsync<JObject>(cancellationToken);
|
||||
if(jobj["success"].Value<bool>() is not true || !jobj["base"].Value<string>().Equals("BTC", StringComparison.InvariantCulture))
|
||||
throw new Exception("exchangerate.host returned a non success response or the base currency was not the requested one (BTC)");
|
||||
var results = (JObject) jobj["rates"] ;
|
||||
//key value is currency code to rate value
|
||||
var list = new List<PairRate>();
|
||||
foreach (var item in results)
|
||||
{
|
||||
string name = item.Key;
|
||||
var value = item.Value.Value<decimal>();
|
||||
list.Add(new PairRate(new CurrencyPair("BTC", name), new BidAsk(value)));
|
||||
}
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public class RipioExchangeProvider : IRateProvider
|
||||
{
|
||||
public RateSourceInfo RateSourceInfo => new("ripio", "Ripio", "https://api.exchange.ripio.com/api/v1/rate/all/");
|
||||
public RateSourceInfo RateSourceInfo => new("ripio", "Ripio", "https://api.ripiotrade.co/v4/public/tickers");
|
||||
private readonly HttpClient _httpClient;
|
||||
public RipioExchangeProvider(HttpClient httpClient)
|
||||
{
|
||||
@ -21,9 +21,9 @@ namespace BTCPayServer.Services.Rates
|
||||
}
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _httpClient.GetAsync("https://api.exchange.ripio.com/api/v1/rate/all/", cancellationToken);
|
||||
var response = await _httpClient.GetAsync("https://api.ripiotrade.co/v4/public/tickers", cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var jarray = (JArray)(await response.Content.ReadAsAsync<JArray>(cancellationToken));
|
||||
var jarray = (JArray)(await response.Content.ReadAsAsync<JObject>(cancellationToken))["data"];
|
||||
return jarray
|
||||
.Children<JObject>()
|
||||
.Select(jobj => ParsePair(jobj))
|
||||
|
@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
||||
using BTCPayServer.Configuration;
|
||||
using BTCPayServer.Controllers;
|
||||
using BTCPayServer.Models.WalletViewModels;
|
||||
using BTCPayServer.Plugins.Altcoins;
|
||||
using BTCPayServer.Services.Wallets;
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@ -52,11 +53,12 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
tester.ActivateLBTC();
|
||||
await tester.StartAsync();
|
||||
|
||||
//https://github.com/ElementsProject/elements/issues/956
|
||||
await tester.LBTCExplorerNode.SendCommandAsync("rescanblockchain");
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
user.RegisterDerivationScheme("LBTC");
|
||||
user.RegisterDerivationScheme("USDT");
|
||||
user.RegisterDerivationScheme("ETB");
|
||||
await user.GrantAccessAsync();
|
||||
|
||||
await tester.LBTCExplorerNode.GenerateAsync(4);
|
||||
//no tether on our regtest, lets create it and set it
|
||||
var tether = tester.NetworkProvider.GetNetwork<ElementsBTCPayNetwork>("USDT");
|
||||
@ -75,6 +77,10 @@ namespace BTCPayServer.Tests
|
||||
.AssetId = etb.AssetId;
|
||||
|
||||
|
||||
user.RegisterDerivationScheme("LBTC");
|
||||
user.RegisterDerivationScheme("USDT");
|
||||
user.RegisterDerivationScheme("ETB");
|
||||
|
||||
//test: register 2 assets on the same elements network and make sure paying an invoice on one does not affect the other in any way
|
||||
var invoice = await user.BitPay.CreateInvoiceAsync(new Invoice(0.1m, "BTC"));
|
||||
Assert.Equal(3, invoice.SupportedTransactionCurrencies.Count);
|
||||
@ -82,7 +88,7 @@ namespace BTCPayServer.Tests
|
||||
//1 lbtc = 1 btc
|
||||
Assert.Equal(1, ci.Rate);
|
||||
var star = await tester.LBTCExplorerNode.SendCommandAsync("sendtoaddress", ci.Address, ci.Due, "", "", false, true,
|
||||
1, "UNSET", lbtc.AssetId);
|
||||
1, "UNSET",false, lbtc.AssetId.ToString());
|
||||
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
@ -95,8 +101,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
ci = invoice.CryptoInfo.Single(info => info.CryptoCode.Equals("USDT"));
|
||||
Assert.Equal(3, invoice.SupportedTransactionCurrencies.Count);
|
||||
star = await tester.LBTCExplorerNode.SendCommandAsync("sendtoaddress", ci.Address, ci.Due, "", "", false, true,
|
||||
1, "UNSET", tether.AssetId);
|
||||
star = tester.LBTCExplorerNode.SendCommand("sendtoaddress", ci.Address, decimal.Parse(ci.Due), "x", "z", false, true, 1, "unset", false, tether.AssetId.ToString());
|
||||
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Import Project="../Build/Common.csproj" />
|
||||
<PropertyGroup>
|
||||
<NoWarn>$(NoWarn),xUnit1031</NoWarn>
|
||||
<IsPackable>false</IsPackable>
|
||||
<UserSecretsId>AB0AC1DD-9D26-485B-9416-56A33F268117</UserSecretsId>
|
||||
<!--https://devblogs.microsoft.com/aspnet/testing-asp-net-core-mvc-web-apps-in-memory/-->
|
||||
@ -19,13 +20,13 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.14" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.15" />
|
||||
<PackageReference Include="Selenium.Support" Version="4.1.1" />
|
||||
<PackageReference Include="Selenium.WebDriver" Version="4.1.1" />
|
||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="116.0.5845.9600" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="119.0.6045.10500" />
|
||||
<PackageReference Include="xunit" Version="2.6.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
@ -232,17 +232,13 @@ namespace BTCPayServer.Tests
|
||||
ndax.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("BTC_CAD"), new BidAsk(6000m)));
|
||||
rateProvider.Providers.Add("ndax", ndax);
|
||||
|
||||
var bittrex = new MockRateProvider();
|
||||
bittrex.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("DOGE_BTC"), new BidAsk(0.004m)));
|
||||
rateProvider.Providers.Add("bittrex", bittrex);
|
||||
|
||||
|
||||
var bitfinex = new MockRateProvider();
|
||||
bitfinex.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("UST_BTC"), new BidAsk(0.000136m)));
|
||||
rateProvider.Providers.Add("bitfinex", bitfinex);
|
||||
|
||||
var bitpay = new MockRateProvider();
|
||||
bitpay.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("ETB_BTC"), new BidAsk(0.1m)));
|
||||
bitpay.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("DOGE_BTC"), new BidAsk(0.004m)));
|
||||
rateProvider.Providers.Add("bitpay", bitpay);
|
||||
var kraken = new MockRateProvider();
|
||||
kraken.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("ETH_BTC"), new BidAsk(0.1m)));
|
||||
|
@ -12,7 +12,8 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
this._Parent = serverTester;
|
||||
var url = serverTester.GetEnvironment(environmentName, defaultValue);
|
||||
Client = (ChargeClient)LightningClientFactory.CreateClient(url, network);
|
||||
|
||||
Client = (ChargeClient)new LightningClientFactory(network).Create(url);
|
||||
P2PHost = _Parent.GetEnvironment(environmentName + "_HOST", defaultHost);
|
||||
}
|
||||
public ChargeClient Client { get; set; }
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0.401-bullseye-slim AS builder
|
||||
FROM mcr.microsoft.com/dotnet/sdk:8.0.100-bookworm-slim AS builder
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends chromium-driver \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
@ -98,7 +98,7 @@ retry:
|
||||
}
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
Assert.False(true, "Elements was found");
|
||||
Assert.Fail("Elements was found");
|
||||
}
|
||||
|
||||
public static void UntilJsIsReady(this WebDriverWait wait)
|
||||
@ -197,10 +197,11 @@ retry:
|
||||
driver.FindElement(selector).Click();
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public static bool ElementDoesNotExist(this IWebDriver driver, By selector)
|
||||
{
|
||||
Assert.Throws<NoSuchElementException>(() =>
|
||||
Assert.Throws<NoSuchElementException>(
|
||||
[DebuggerStepThrough]
|
||||
() =>
|
||||
{
|
||||
driver.FindElement(selector);
|
||||
});
|
||||
|
@ -10,6 +10,7 @@ using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Abstractions.Models;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Configuration;
|
||||
@ -18,9 +19,12 @@ using BTCPayServer.Data;
|
||||
using BTCPayServer.HostedServices;
|
||||
using BTCPayServer.Hosting;
|
||||
using BTCPayServer.JsonConverters;
|
||||
using BTCPayServer.Logging;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.Bitcoin;
|
||||
using BTCPayServer.Payments.Lightning;
|
||||
using BTCPayServer.Plugins;
|
||||
using BTCPayServer.Plugins.Bitcoin;
|
||||
using BTCPayServer.Rating;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Apps;
|
||||
@ -131,7 +135,7 @@ namespace BTCPayServer.Tests
|
||||
var tags = new HashSet<String>(g.Select(o => o.Tag));
|
||||
if (tags.Count != 1)
|
||||
{
|
||||
Assert.False(true, $"All docker images '{g.Key}' in docker-compose.yml and docker-compose.altcoins.yml should have the same tags. (Found {string.Join(',', tags)})");
|
||||
Assert.Fail($"All docker images '{g.Key}' in docker-compose.yml and docker-compose.altcoins.yml should have the same tags. (Found {string.Join(',', tags)})");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -410,7 +414,7 @@ namespace BTCPayServer.Tests
|
||||
public void CanCalculateDust()
|
||||
{
|
||||
var entity = new InvoiceEntity() { Currency = "USD" };
|
||||
entity.Networks = new BTCPayNetworkProvider(ChainName.Regtest);
|
||||
entity.Networks = CreateNetworkProvider(ChainName.Regtest);
|
||||
#pragma warning disable CS0618
|
||||
entity.Payments = new System.Collections.Generic.List<PaymentEntity>();
|
||||
entity.SetPaymentMethod(new PaymentMethod()
|
||||
@ -456,7 +460,7 @@ namespace BTCPayServer.Tests
|
||||
[Fact]
|
||||
public void CanCalculateCryptoDue()
|
||||
{
|
||||
var networkProvider = new BTCPayNetworkProvider(ChainName.Regtest);
|
||||
var networkProvider = CreateNetworkProvider(ChainName.Regtest);
|
||||
var entity = new InvoiceEntity() { Currency = "USD" };
|
||||
entity.Networks = networkProvider;
|
||||
#pragma warning disable CS0618
|
||||
@ -644,7 +648,7 @@ namespace BTCPayServer.Tests
|
||||
[Fact]
|
||||
public void CanAcceptInvoiceWithTolerance()
|
||||
{
|
||||
var networkProvider = new BTCPayNetworkProvider(ChainName.Regtest);
|
||||
var networkProvider = CreateNetworkProvider(ChainName.Regtest);
|
||||
var paymentMethodHandlerDictionary = new PaymentMethodHandlerDictionary(new IPaymentMethodHandler[]
|
||||
{
|
||||
new BitcoinLikePaymentHandler(null, networkProvider, null, null, null, null),
|
||||
@ -764,7 +768,7 @@ namespace BTCPayServer.Tests
|
||||
[Fact]
|
||||
public async Task CanEnumerateTorServices()
|
||||
{
|
||||
var tor = new TorServices(new BTCPayNetworkProvider(ChainName.Regtest),
|
||||
var tor = new TorServices(CreateNetworkProvider(ChainName.Regtest),
|
||||
new OptionsWrapper<BTCPayServerOptions>(new BTCPayServerOptions()
|
||||
{
|
||||
TorrcFile = TestUtils.GetTestDataFullPath("Tor/torrc")
|
||||
@ -776,7 +780,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.Single(tor.Services.Where(t => t.ServiceType == TorServiceType.RPC));
|
||||
Assert.True(tor.Services.Count(t => t.ServiceType == TorServiceType.Other) > 1);
|
||||
|
||||
tor = new TorServices(new BTCPayNetworkProvider(ChainName.Regtest),
|
||||
tor = new TorServices(CreateNetworkProvider(ChainName.Regtest),
|
||||
new OptionsWrapper<BTCPayServerOptions>(new BTCPayServerOptions()
|
||||
{
|
||||
TorrcFile = null,
|
||||
@ -813,7 +817,7 @@ namespace BTCPayServer.Tests
|
||||
[Fact]
|
||||
public void CanParseDerivationSchemes()
|
||||
{
|
||||
var networkProvider = new BTCPayNetworkProvider(ChainName.Regtest);
|
||||
var networkProvider = CreateNetworkProvider(ChainName.Regtest);
|
||||
var parser = new DerivationSchemeParser(networkProvider.BTC);
|
||||
|
||||
// xpub
|
||||
@ -839,7 +843,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(expected, inner.ToString());
|
||||
|
||||
// Output Descriptor
|
||||
networkProvider = new BTCPayNetworkProvider(ChainName.Mainnet);
|
||||
networkProvider = CreateNetworkProvider(ChainName.Mainnet);
|
||||
parser = new DerivationSchemeParser(networkProvider.BTC);
|
||||
var od = "wpkh([8bafd160/49h/0h/0h]xpub661MyMwAqRbcGVBsTGeNZN6QGVHmMHLdSA4FteGsRrEriu4pnVZMZWnruFFFXkMnyoBjyHndD3Qwcfz4MPzBUxjSevweNFQx7SAYZATtcDw/0/*)#9x4vkw48";
|
||||
(strategyBase, rootedKeyPath) = parser.ParseOutputDescriptor(od);
|
||||
@ -881,8 +885,8 @@ namespace BTCPayServer.Tests
|
||||
[Fact]
|
||||
public void ParseDerivationSchemeSettings()
|
||||
{
|
||||
var testnet = new BTCPayNetworkProvider(ChainName.Testnet).GetNetwork<BTCPayNetwork>("BTC");
|
||||
var mainnet = new BTCPayNetworkProvider(ChainName.Mainnet).GetNetwork<BTCPayNetwork>("BTC");
|
||||
var testnet = CreateNetworkProvider(ChainName.Testnet).GetNetwork<BTCPayNetwork>("BTC");
|
||||
var mainnet = CreateNetworkProvider(ChainName.Mainnet).GetNetwork<BTCPayNetwork>("BTC");
|
||||
var root = new Mnemonic(
|
||||
"usage fever hen zero slide mammal silent heavy donate budget pulse say brain thank sausage brand craft about save attract muffin advance illegal cabbage")
|
||||
.DeriveExtKey();
|
||||
@ -954,6 +958,40 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal("49'/0'/0'", specter.AccountKeySettings[0].AccountKeyPath.ToString());
|
||||
Assert.Equal("Specter", specter.Label);
|
||||
Assert.Null(error);
|
||||
|
||||
//BSMS BIP129, Nunchuk
|
||||
|
||||
var bsms = @"BSMS 1.0
|
||||
wsh(sortedmulti(1,[5c9e228d/48'/0'/0'/2']xpub6EgGHjcvovyN3nK921zAGPfuB41cJXkYRdt3tLGmiMyvbgHpss4X1eRZwShbEBb1znz2e2bCkCED87QZpin3sSYKbmCzQ9Sc7LaV98ngdeX/**,[2b0e251e/48'/0'/0'/2']xpub6DrimHB8KUSkPvmJ8Pk8RE769EdDm2VEoZ8MBz76w9QupP8Py4wexs4Pa3aRB1LUEhc9GyY6ypDWEFFRCgqeDQePcyWQfjtmintrehq3JCL/**))
|
||||
/0/*,/1/*
|
||||
bc1qfzu57kgu5jthl934f9xrdzzx8mmemx7gn07tf0grnvz504j6kzusu2v0ku
|
||||
";
|
||||
|
||||
Assert.True(DerivationSchemeSettings.TryParseFromWalletFile(bsms,
|
||||
mainnet, out var nunchuk, out error));
|
||||
|
||||
Assert.Equal(2, nunchuk.AccountKeySettings.Length);
|
||||
//check that the account key settings match those in bsms string
|
||||
Assert.Equal("5c9e228d", nunchuk.AccountKeySettings[0].RootFingerprint.ToString());
|
||||
Assert.Equal("48'/0'/0'/2'", nunchuk.AccountKeySettings[0].AccountKeyPath.ToString());
|
||||
Assert.Equal("2b0e251e", nunchuk.AccountKeySettings[1].RootFingerprint.ToString());
|
||||
Assert.Equal("48'/0'/0'/2'", nunchuk.AccountKeySettings[1].AccountKeyPath.ToString());
|
||||
|
||||
var multsig = Assert.IsType < MultisigDerivationStrategy >
|
||||
(Assert.IsType<P2WSHDerivationStrategy>(nunchuk.AccountDerivation).Inner);
|
||||
|
||||
Assert.True(multsig.LexicographicOrder);
|
||||
Assert.Equal(1, multsig.RequiredSignatures);
|
||||
|
||||
var deposit = new NBXplorer.KeyPathTemplates(null).GetKeyPathTemplate(DerivationFeature.Deposit);
|
||||
var line =nunchuk.AccountDerivation.GetLineFor(deposit).Derive(0);
|
||||
|
||||
Assert.Equal(BitcoinAddress.Create("bc1qfzu57kgu5jthl934f9xrdzzx8mmemx7gn07tf0grnvz504j6kzusu2v0ku", Network.Main).ScriptPubKey,
|
||||
line.ScriptPubKey);
|
||||
|
||||
Assert.Equal("BSMS", nunchuk.Source);
|
||||
Assert.Null(error);
|
||||
|
||||
|
||||
// Failure case
|
||||
Assert.False(DerivationSchemeSettings.TryParseFromWalletFile(
|
||||
@ -964,10 +1002,10 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckRatesProvider()
|
||||
public async Task CheckRatesProvider()
|
||||
{
|
||||
var spy = new SpyRateProvider();
|
||||
RateRules.TryParse("X_X = bittrex(X_X);", out var rateRules);
|
||||
RateRules.TryParse("X_X = bitpay(X_X);", out var rateRules);
|
||||
|
||||
var factory = CreateBTCPayRateFactory();
|
||||
factory.Providers.Clear();
|
||||
@ -975,24 +1013,23 @@ namespace BTCPayServer.Tests
|
||||
factory.Providers.Clear();
|
||||
var fetch = new BackgroundFetcherRateProvider(spy);
|
||||
fetch.DoNotAutoFetchIfExpired = true;
|
||||
factory.Providers.Add("bittrex", fetch);
|
||||
var fetchedRate = fetcher.FetchRate(CurrencyPair.Parse("BTC_USD"), rateRules, default).GetAwaiter()
|
||||
.GetResult();
|
||||
factory.Providers.Add("bitpay", fetch);
|
||||
var fetchedRate = await fetcher.FetchRate(CurrencyPair.Parse("BTC_USD"), rateRules, default);
|
||||
spy.AssertHit();
|
||||
fetchedRate = fetcher.FetchRate(CurrencyPair.Parse("BTC_USD"), rateRules, default).GetAwaiter().GetResult();
|
||||
fetchedRate = await fetcher.FetchRate(CurrencyPair.Parse("BTC_USD"), rateRules, default);
|
||||
spy.AssertNotHit();
|
||||
fetch.UpdateIfNecessary(default).GetAwaiter().GetResult();
|
||||
await fetch.UpdateIfNecessary(default);
|
||||
spy.AssertNotHit();
|
||||
fetch.RefreshRate = TimeSpan.FromSeconds(1.0);
|
||||
Thread.Sleep(1020);
|
||||
fetchedRate = fetcher.FetchRate(CurrencyPair.Parse("BTC_USD"), rateRules, default).GetAwaiter().GetResult();
|
||||
fetchedRate = await fetcher.FetchRate(CurrencyPair.Parse("BTC_USD"), rateRules, default);
|
||||
spy.AssertNotHit();
|
||||
fetch.ValidatyTime = TimeSpan.FromSeconds(1.0);
|
||||
fetch.UpdateIfNecessary(default).GetAwaiter().GetResult();
|
||||
await fetch.UpdateIfNecessary(default);
|
||||
spy.AssertHit();
|
||||
fetch.GetRatesAsync(default).GetAwaiter().GetResult();
|
||||
await fetch.GetRatesAsync(default);
|
||||
Thread.Sleep(1000);
|
||||
Assert.Throws<InvalidOperationException>(() => fetch.GetRatesAsync(default).GetAwaiter().GetResult());
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(() => fetch.GetRatesAsync(default));
|
||||
}
|
||||
|
||||
public static RateProviderFactory CreateBTCPayRateFactory()
|
||||
@ -1246,7 +1283,7 @@ namespace BTCPayServer.Tests
|
||||
[Fact]
|
||||
public void HasCurrencyDataForNetworks()
|
||||
{
|
||||
var btcPayNetworkProvider = new BTCPayNetworkProvider(ChainName.Regtest);
|
||||
var btcPayNetworkProvider = CreateNetworkProvider(ChainName.Regtest);
|
||||
foreach (var network in btcPayNetworkProvider.GetAll())
|
||||
{
|
||||
var cd = CurrencyNameTable.Instance.GetCurrencyData(network.CryptoCode, false);
|
||||
@ -1577,7 +1614,7 @@ namespace BTCPayServer.Tests
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.AppendLine("// Some cool comments");
|
||||
builder.AppendLine("DOGE_X = DOGE_BTC * BTC_X * 1.1");
|
||||
builder.AppendLine("DOGE_BTC = Bittrex(DOGE_BTC)");
|
||||
builder.AppendLine("DOGE_BTC = bitpay(DOGE_BTC)");
|
||||
builder.AppendLine("// Some other cool comments");
|
||||
builder.AppendLine("BTC_usd = kraken(BTC_USD)");
|
||||
builder.AppendLine("BTC_X = Coinbase(BTC_X);");
|
||||
@ -1588,7 +1625,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(
|
||||
"// Some cool comments\n" +
|
||||
"DOGE_X = DOGE_BTC * BTC_X * 1.1;\n" +
|
||||
"DOGE_BTC = bittrex(DOGE_BTC);\n" +
|
||||
"DOGE_BTC = bitpay(DOGE_BTC);\n" +
|
||||
"// Some other cool comments\n" +
|
||||
"BTC_USD = kraken(BTC_USD);\n" +
|
||||
"BTC_X = coinbase(BTC_X);\n" +
|
||||
@ -1596,10 +1633,10 @@ namespace BTCPayServer.Tests
|
||||
rules.ToString());
|
||||
var tests = new[]
|
||||
{
|
||||
(Pair: "DOGE_USD", Expected: "bittrex(DOGE_BTC) * kraken(BTC_USD) * 1.1"),
|
||||
(Pair: "DOGE_USD", Expected: "bitpay(DOGE_BTC) * kraken(BTC_USD) * 1.1"),
|
||||
(Pair: "BTC_USD", Expected: "kraken(BTC_USD)"),
|
||||
(Pair: "BTC_CAD", Expected: "coinbase(BTC_CAD)"),
|
||||
(Pair: "DOGE_CAD", Expected: "bittrex(DOGE_BTC) * coinbase(BTC_CAD) * 1.1"),
|
||||
(Pair: "DOGE_CAD", Expected: "bitpay(DOGE_BTC) * coinbase(BTC_CAD) * 1.1"),
|
||||
(Pair: "LTC_CAD", Expected: "coinaverage(LTC_CAD) * 1.02"),
|
||||
(Pair: "SATS_CAD", Expected: "0.00000001 * coinbase(BTC_CAD)"),
|
||||
(Pair: "Sats_USD", Expected: "0.00000001 * kraken(BTC_USD)")
|
||||
@ -1609,13 +1646,13 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(test.Expected, rules.GetRuleFor(CurrencyPair.Parse(test.Pair)).ToString());
|
||||
}
|
||||
rules.Spread = 0.2m;
|
||||
Assert.Equal("(bittrex(DOGE_BTC) * kraken(BTC_USD) * 1.1) * (0.8, 1.2)", rules.GetRuleFor(CurrencyPair.Parse("DOGE_USD")).ToString());
|
||||
Assert.Equal("(bitpay(DOGE_BTC) * kraken(BTC_USD) * 1.1) * (0.8, 1.2)", rules.GetRuleFor(CurrencyPair.Parse("DOGE_USD")).ToString());
|
||||
////////////////
|
||||
|
||||
// Check errors conditions
|
||||
builder = new StringBuilder();
|
||||
builder.AppendLine("DOGE_X = LTC_CAD * BTC_X * 1.1");
|
||||
builder.AppendLine("DOGE_BTC = Bittrex(DOGE_BTC)");
|
||||
builder.AppendLine("DOGE_BTC = bitpay(DOGE_BTC)");
|
||||
builder.AppendLine("BTC_usd = kraken(BTC_USD)");
|
||||
builder.AppendLine("LTC_CHF = LTC_CHF * 1.01");
|
||||
builder.AppendLine("BTC_X = Coinbase(BTC_X)");
|
||||
@ -1636,7 +1673,7 @@ namespace BTCPayServer.Tests
|
||||
// Check if we can resolve exchange rates
|
||||
builder = new StringBuilder();
|
||||
builder.AppendLine("DOGE_X = DOGE_BTC * BTC_X * 1.1");
|
||||
builder.AppendLine("DOGE_BTC = Bittrex(DOGE_BTC)");
|
||||
builder.AppendLine("DOGE_BTC = bitpay(DOGE_BTC)");
|
||||
builder.AppendLine("BTC_usd = kraken(BTC_USD)");
|
||||
builder.AppendLine("BTC_X = Coinbase(BTC_X)");
|
||||
builder.AppendLine("X_X = CoinAverage(X_X) * 1.02");
|
||||
@ -1644,10 +1681,10 @@ namespace BTCPayServer.Tests
|
||||
|
||||
var tests2 = new[]
|
||||
{
|
||||
(Pair: "DOGE_USD", Expected: "bittrex(DOGE_BTC) * kraken(BTC_USD) * 1.1", ExpectedExchangeRates: "bittrex(DOGE_BTC),kraken(BTC_USD)"),
|
||||
(Pair: "DOGE_USD", Expected: "bitpay(DOGE_BTC) * kraken(BTC_USD) * 1.1", ExpectedExchangeRates: "bitpay(DOGE_BTC),kraken(BTC_USD)"),
|
||||
(Pair: "BTC_USD", Expected: "kraken(BTC_USD)", ExpectedExchangeRates: "kraken(BTC_USD)"),
|
||||
(Pair: "BTC_CAD", Expected: "coinbase(BTC_CAD)", ExpectedExchangeRates: "coinbase(BTC_CAD)"),
|
||||
(Pair: "DOGE_CAD", Expected: "bittrex(DOGE_BTC) * coinbase(BTC_CAD) * 1.1", ExpectedExchangeRates: "bittrex(DOGE_BTC),coinbase(BTC_CAD)"),
|
||||
(Pair: "DOGE_CAD", Expected: "bitpay(DOGE_BTC) * coinbase(BTC_CAD) * 1.1", ExpectedExchangeRates: "bitpay(DOGE_BTC),coinbase(BTC_CAD)"),
|
||||
(Pair: "LTC_CAD", Expected: "coinaverage(LTC_CAD) * 1.02", ExpectedExchangeRates: "coinaverage(LTC_CAD)"),
|
||||
(Pair: "SATS_USD", Expected: "0.00000001 * kraken(BTC_USD)", ExpectedExchangeRates: "kraken(BTC_USD)"),
|
||||
(Pair: "SATS_EUR", Expected: "0.00000001 * coinbase(BTC_EUR)", ExpectedExchangeRates: "coinbase(BTC_EUR)")
|
||||
@ -1659,11 +1696,11 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(test.ExpectedExchangeRates, string.Join(',', rule.ExchangeRates.OfType<object>().ToArray()));
|
||||
}
|
||||
var rule2 = rules.GetRuleFor(CurrencyPair.Parse("DOGE_CAD"));
|
||||
rule2.ExchangeRates.SetRate("bittrex", CurrencyPair.Parse("DOGE_BTC"), new BidAsk(5000m));
|
||||
rule2.ExchangeRates.SetRate("bitpay", CurrencyPair.Parse("DOGE_BTC"), new BidAsk(5000m));
|
||||
rule2.Reevaluate();
|
||||
Assert.True(rule2.HasError);
|
||||
Assert.Equal("5000 * ERR_RATE_UNAVAILABLE(coinbase, BTC_CAD) * 1.1", rule2.ToString(true));
|
||||
Assert.Equal("bittrex(DOGE_BTC) * coinbase(BTC_CAD) * 1.1", rule2.ToString(false));
|
||||
Assert.Equal("bitpay(DOGE_BTC) * coinbase(BTC_CAD) * 1.1", rule2.ToString(false));
|
||||
rule2.ExchangeRates.SetRate("coinbase", CurrencyPair.Parse("BTC_CAD"), new BidAsk(2000.4m));
|
||||
rule2.Reevaluate();
|
||||
Assert.False(rule2.HasError);
|
||||
@ -1825,8 +1862,7 @@ namespace BTCPayServer.Tests
|
||||
new KeyValuePair<string, string>("chains", "usdt")}
|
||||
})
|
||||
});
|
||||
|
||||
var networkProvider = config.ConfigureNetworkProvider(BTCPayLogs);
|
||||
var networkProvider = CreateNetworkProvider(config);
|
||||
Assert.NotNull(networkProvider.GetNetwork("LBTC"));
|
||||
Assert.NotNull(networkProvider.GetNetwork("USDT"));
|
||||
}
|
||||
@ -1834,9 +1870,9 @@ namespace BTCPayServer.Tests
|
||||
[Trait("Altcoins", "Altcoins")]
|
||||
public void CanParseDerivationScheme()
|
||||
{
|
||||
var testnetNetworkProvider = new BTCPayNetworkProvider(ChainName.Testnet);
|
||||
var regtestNetworkProvider = new BTCPayNetworkProvider(ChainName.Regtest);
|
||||
var mainnetNetworkProvider = new BTCPayNetworkProvider(ChainName.Mainnet);
|
||||
var testnetNetworkProvider = CreateNetworkProvider(ChainName.Testnet);
|
||||
var regtestNetworkProvider = CreateNetworkProvider(ChainName.Regtest);
|
||||
var mainnetNetworkProvider = CreateNetworkProvider(ChainName.Mainnet);
|
||||
var testnetParser = new DerivationSchemeParser(testnetNetworkProvider.GetNetwork<BTCPayNetwork>("BTC"));
|
||||
var mainnetParser = new DerivationSchemeParser(mainnetNetworkProvider.GetNetwork<BTCPayNetwork>("BTC"));
|
||||
NBXplorer.DerivationStrategy.DerivationStrategyBase result;
|
||||
@ -2002,7 +2038,7 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
#pragma warning disable CS0618
|
||||
var dummy = new Key().PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.RegTest).ToString();
|
||||
var networkProvider = new BTCPayNetworkProvider(ChainName.Regtest);
|
||||
var networkProvider = CreateNetworkProvider(ChainName.Regtest);
|
||||
var networkBTC = networkProvider.GetNetwork("BTC");
|
||||
var networkLTC = networkProvider.GetNetwork("LTC");
|
||||
InvoiceEntity invoiceEntity = new InvoiceEntity();
|
||||
@ -2119,7 +2155,7 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
["derivationStrategy"] = "tpubDDLQZ1WMdy5YJAJWmRNoTJ3uQkavEPXCXnmD4eAuo9BKbzFUBbJmVHys5M3ku4Qw1C165wGpVWH55gZpHjdsCyntwNzhmCAzGejSL6rzbyf"
|
||||
};
|
||||
var scheme = DerivationSchemeSettings.Parse("tpubDDLQZ1WMdy5YJAJWmRNoTJ3uQkavEPXCXnmD4eAuo9BKbzFUBbJmVHys5M3ku4Qw1C165wGpVWH55gZpHjdsCyntwNzhmCAzGejSL6rzbyf", new BTCPayNetworkProvider(ChainName.Regtest).BTC);
|
||||
var scheme = DerivationSchemeSettings.Parse("tpubDDLQZ1WMdy5YJAJWmRNoTJ3uQkavEPXCXnmD4eAuo9BKbzFUBbJmVHys5M3ku4Qw1C165wGpVWH55gZpHjdsCyntwNzhmCAzGejSL6rzbyf", CreateNetworkProvider(ChainName.Regtest).BTC);
|
||||
|
||||
scheme.Source = "ManualDerivationScheme";
|
||||
scheme.AccountOriginal = "tpubDDLQZ1WMdy5YJAJWmRNoTJ3uQkavEPXCXnmD4eAuo9BKbzFUBbJmVHys5M3ku4Qw1C165wGpVWH55gZpHjdsCyntwNzhmCAzGejSL6rzbyf";
|
||||
@ -2139,7 +2175,7 @@ namespace BTCPayServer.Tests
|
||||
.Select(o =>
|
||||
{
|
||||
var entity = JsonConvert.DeserializeObject<InvoiceEntity>(o.ToString());
|
||||
entity.Networks = new BTCPayNetworkProvider(ChainName.Regtest);
|
||||
entity.Networks = CreateNetworkProvider(ChainName.Regtest);
|
||||
return entity.DerivationStrategies.ToString();
|
||||
})
|
||||
.ToHashSet();
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Form;
|
||||
using BTCPayServer.Forms;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
@ -10,16 +11,25 @@ using Xunit.Abstractions;
|
||||
|
||||
namespace BTCPayServer.Tests;
|
||||
|
||||
[Trait("Fast", "Fast")]
|
||||
[Collection(nameof(NonParallelizableCollectionDefinition))]
|
||||
[Trait("Integration", "Integration")]
|
||||
public class FormTests : UnitTestBase
|
||||
{
|
||||
public FormTests(ITestOutputHelper helper) : base(helper)
|
||||
{
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanParseForm()
|
||||
|
||||
[Fact(Timeout = TestUtils.TestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanParseForm()
|
||||
{
|
||||
using var tester = CreateServerTester();
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
var service = tester.PayTester.GetService<FormDataService>();
|
||||
|
||||
var form = new Form()
|
||||
{
|
||||
Fields = new List<Field>
|
||||
@ -40,8 +50,6 @@ public class FormTests : UnitTestBase
|
||||
}
|
||||
}
|
||||
};
|
||||
var providers = new FormComponentProviders(new List<IFormComponentProvider>());
|
||||
var service = new FormDataService(null, providers);
|
||||
Assert.False(service.IsFormSchemaValid(form.ToString(), out _, out _));
|
||||
form = new Form
|
||||
{
|
||||
@ -164,7 +172,7 @@ public class FormTests : UnitTestBase
|
||||
Assert.Equal("original", obj["invoice"]["test"].Value<string>());
|
||||
Assert.Equal("updated", obj["invoice_item3"].Value<string>());
|
||||
Clear(form);
|
||||
form.SetValues(obj);
|
||||
service.SetValues(form, obj);
|
||||
obj = service.GetValues(form);
|
||||
Assert.Equal("original", obj["invoice"]["test"].Value<string>());
|
||||
Assert.Equal("updated", obj["invoice_item3"].Value<string>());
|
||||
@ -182,12 +190,73 @@ public class FormTests : UnitTestBase
|
||||
}
|
||||
}
|
||||
};
|
||||
form.SetValues(obj);
|
||||
|
||||
service.SetValues(form, obj);
|
||||
obj = service.GetValues(form);
|
||||
Assert.Null(obj["test"].Value<string>());
|
||||
form.SetValues(new JObject { ["test"] = "hello" });
|
||||
|
||||
service.SetValues(form, new JObject { ["test"] = "hello" });
|
||||
obj = service.GetValues(form);
|
||||
Assert.Equal("hello", obj["test"].Value<string>());
|
||||
|
||||
var req = service.GenerateInvoiceParametersFromForm(form);
|
||||
Assert.Null(req.Amount);
|
||||
Assert.Null(req.Currency);
|
||||
|
||||
form.Fields.Add(new Field
|
||||
{
|
||||
Name = $"{FormDataService.InvoiceParameterPrefix}amount",
|
||||
Type = "number",
|
||||
Value = "1"
|
||||
});
|
||||
req = service.GenerateInvoiceParametersFromForm(form);
|
||||
Assert.Equal(1, req.Amount);
|
||||
|
||||
form.Fields.Add(new Field
|
||||
{
|
||||
Name = $"{FormDataService.InvoiceParameterPrefix}amount_adjustment",
|
||||
Type = "number",
|
||||
Value = "1"
|
||||
});
|
||||
req = service.GenerateInvoiceParametersFromForm(form);
|
||||
Assert.Equal(2, req.Amount);
|
||||
form.Fields.Add(new Field
|
||||
{
|
||||
Name = $"{FormDataService.InvoiceParameterPrefix}amount_adjustment2",
|
||||
Type = "number",
|
||||
Value = "2"
|
||||
});
|
||||
form.Fields.Add(new Field
|
||||
{
|
||||
Name = $"{FormDataService.InvoiceParameterPrefix}currency",
|
||||
Type = "text",
|
||||
Value = "eur"
|
||||
});
|
||||
req = service.GenerateInvoiceParametersFromForm(form);
|
||||
Assert.Equal("eur", req.Currency);
|
||||
Assert.Equal(4, req.Amount);
|
||||
|
||||
|
||||
form.Fields.Add(new Field
|
||||
{
|
||||
Name = $"{FormDataService.InvoiceParameterPrefix}amount_multiply_adjustment",
|
||||
Type = "number",
|
||||
Value = "2"
|
||||
});
|
||||
|
||||
req = service.GenerateInvoiceParametersFromForm(form);
|
||||
Assert.Equal(8, req.Amount);
|
||||
|
||||
|
||||
form.Fields.Add(new Field
|
||||
{
|
||||
Name = $"{FormDataService.InvoiceParameterPrefix}amount_multiply_adjustment1",
|
||||
Type = "number",
|
||||
Value = "2"
|
||||
});
|
||||
|
||||
req = service.GenerateInvoiceParametersFromForm(form);
|
||||
Assert.Equal(16, req.Amount);
|
||||
}
|
||||
|
||||
private void Clear(Form form)
|
||||
|
@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
@ -1084,7 +1085,39 @@ namespace BTCPayServer.Tests
|
||||
Assert.IsType<string>(lnrURLs.LNURLUri);
|
||||
Assert.Equal(12.303228134m, test4.Amount);
|
||||
Assert.Equal("BTC", test4.Currency);
|
||||
|
||||
|
||||
// Check we can register Boltcard
|
||||
var uid = new byte[7];
|
||||
RandomNumberGenerator.Fill(uid);
|
||||
var card = await client.RegisterBoltcard(test4.Id, new RegisterBoltcardRequest()
|
||||
{
|
||||
UID = uid
|
||||
});
|
||||
Assert.Equal(0, card.Version);
|
||||
var card1keys = new[] { card.K0, card.K1, card.K2, card.K3, card.K4 };
|
||||
Assert.DoesNotContain(null, card1keys);
|
||||
var card2 = await client.RegisterBoltcard(test4.Id, new RegisterBoltcardRequest()
|
||||
{
|
||||
UID = uid
|
||||
});
|
||||
Assert.Equal(1, card2.Version);
|
||||
Assert.StartsWith("lnurlw://", card2.LNURLW);
|
||||
Assert.EndsWith("/boltcard", card2.LNURLW);
|
||||
var card2keys = new[] { card2.K0, card2.K1, card2.K2, card2.K3, card2.K4 };
|
||||
Assert.DoesNotContain(null, card2keys);
|
||||
for (int i = 0; i < card1keys.Length; i++)
|
||||
{
|
||||
if (i == 1)
|
||||
Assert.Contains(card1keys[i], card2keys);
|
||||
else
|
||||
Assert.DoesNotContain(card1keys[i], card2keys);
|
||||
}
|
||||
var card3 = await client.RegisterBoltcard(test4.Id, new RegisterBoltcardRequest()
|
||||
{
|
||||
UID = uid,
|
||||
OnExisting = OnExistingBehavior.KeepVersion
|
||||
});
|
||||
Assert.Equal(card2.Version, card3.Version);
|
||||
// Test with SATS denomination values
|
||||
var testSats = await client.CreatePullPayment(storeId, new Client.Models.CreatePullPaymentRequest()
|
||||
{
|
||||
@ -1284,15 +1317,18 @@ namespace BTCPayServer.Tests
|
||||
var client = await user.CreateClient(Policies.Unrestricted);
|
||||
|
||||
//create store
|
||||
var newStore = await client.CreateStore(new CreateStoreRequest() { Name = "A" });
|
||||
var newStore = await client.CreateStore(new CreateStoreRequest { Name = "A" });
|
||||
Assert.Equal("A", newStore.Name);
|
||||
Assert.Equal(CheckoutType.V2, newStore.CheckoutType);
|
||||
|
||||
//update store
|
||||
Assert.Empty(newStore.PaymentMethodCriteria);
|
||||
await client.GenerateOnChainWallet(newStore.Id, "BTC", new GenerateOnChainWalletRequest());
|
||||
var updatedStore = await client.UpdateStore(newStore.Id, new UpdateStoreRequest()
|
||||
var updatedStore = await client.UpdateStore(newStore.Id, new UpdateStoreRequest
|
||||
{
|
||||
Name = "B",
|
||||
PaymentMethodCriteria = new List<PaymentMethodCriteriaData>()
|
||||
CheckoutType = CheckoutType.V1,
|
||||
PaymentMethodCriteria = new List<PaymentMethodCriteriaData>
|
||||
{
|
||||
new()
|
||||
{
|
||||
@ -1304,6 +1340,7 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
});
|
||||
Assert.Equal("B", updatedStore.Name);
|
||||
Assert.Equal(CheckoutType.V1, updatedStore.CheckoutType);
|
||||
var s = (await client.GetStore(newStore.Id));
|
||||
Assert.Equal("B", s.Name);
|
||||
var pmc = Assert.Single(s.PaymentMethodCriteria);
|
||||
@ -1457,7 +1494,7 @@ namespace BTCPayServer.Tests
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanUseWebhooks()
|
||||
{
|
||||
void AssertHook(FakeServer fakeServer, Client.Models.StoreWebhookData hook)
|
||||
void AssertHook(FakeServer fakeServer, StoreWebhookData hook)
|
||||
{
|
||||
Assert.True(hook.Enabled);
|
||||
Assert.True(hook.AuthorizedEvents.Everything);
|
||||
@ -2095,18 +2132,18 @@ namespace BTCPayServer.Tests
|
||||
//validation errors
|
||||
await AssertValidationError(new[] { nameof(CreateInvoiceRequest.Amount), $"{nameof(CreateInvoiceRequest.Checkout)}.{nameof(CreateInvoiceRequest.Checkout.PaymentTolerance)}", $"{nameof(CreateInvoiceRequest.Checkout)}.{nameof(CreateInvoiceRequest.Checkout.PaymentMethods)}[0]" }, async () =>
|
||||
{
|
||||
await client.CreateInvoice(user.StoreId, new CreateInvoiceRequest() { Amount = -1, Checkout = new CreateInvoiceRequest.CheckoutOptions() { PaymentTolerance = -2, PaymentMethods = new[] { "jasaas_sdsad" } } });
|
||||
await client.CreateInvoice(user.StoreId, new CreateInvoiceRequest { Amount = -1, Checkout = new CreateInvoiceRequest.CheckoutOptions { PaymentTolerance = -2, PaymentMethods = new[] { "jasaas_sdsad" } } });
|
||||
});
|
||||
|
||||
await AssertHttpError(403, async () =>
|
||||
{
|
||||
await viewOnly.CreateInvoice(user.StoreId,
|
||||
new CreateInvoiceRequest() { Currency = "helloinvalid", Amount = 1 });
|
||||
new CreateInvoiceRequest { Currency = "helloinvalid", Amount = 1 });
|
||||
});
|
||||
await user.RegisterDerivationSchemeAsync("BTC");
|
||||
string origOrderId = "testOrder";
|
||||
var newInvoice = await client.CreateInvoice(user.StoreId,
|
||||
new CreateInvoiceRequest()
|
||||
new CreateInvoiceRequest
|
||||
{
|
||||
Currency = "USD",
|
||||
Amount = 1,
|
||||
@ -2193,7 +2230,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
//list NonExisting Status
|
||||
var invoicesNonExistingStatus = await viewOnly.GetInvoices(user.StoreId,
|
||||
status: new[] { BTCPayServer.Client.Models.InvoiceStatus.Invalid });
|
||||
status: new[] { InvoiceStatus.Invalid });
|
||||
Assert.NotNull(invoicesNonExistingStatus);
|
||||
Assert.Empty(invoicesNonExistingStatus);
|
||||
|
||||
@ -2211,7 +2248,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
//update
|
||||
newInvoice = await client.CreateInvoice(user.StoreId,
|
||||
new CreateInvoiceRequest() { Currency = "USD", Amount = 1 });
|
||||
new CreateInvoiceRequest { Currency = "USD", Amount = 1 });
|
||||
Assert.Contains(InvoiceStatus.Settled, newInvoice.AvailableStatusesForManualMarking);
|
||||
Assert.Contains(InvoiceStatus.Invalid, newInvoice.AvailableStatusesForManualMarking);
|
||||
await client.MarkInvoiceStatus(user.StoreId, newInvoice.Id, new MarkInvoiceStatusRequest()
|
||||
@ -2223,7 +2260,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.DoesNotContain(InvoiceStatus.Settled, newInvoice.AvailableStatusesForManualMarking);
|
||||
Assert.Contains(InvoiceStatus.Invalid, newInvoice.AvailableStatusesForManualMarking);
|
||||
newInvoice = await client.CreateInvoice(user.StoreId,
|
||||
new CreateInvoiceRequest() { Currency = "USD", Amount = 1 });
|
||||
new CreateInvoiceRequest { Currency = "USD", Amount = 1 });
|
||||
await client.MarkInvoiceStatus(user.StoreId, newInvoice.Id, new MarkInvoiceStatusRequest()
|
||||
{
|
||||
Status = InvoiceStatus.Invalid
|
||||
@ -2238,13 +2275,13 @@ namespace BTCPayServer.Tests
|
||||
await AssertHttpError(403, async () =>
|
||||
{
|
||||
await viewOnly.UpdateInvoice(user.StoreId, invoice.Id,
|
||||
new UpdateInvoiceRequest()
|
||||
new UpdateInvoiceRequest
|
||||
{
|
||||
Metadata = metadataForUpdate
|
||||
});
|
||||
});
|
||||
invoice = await client.UpdateInvoice(user.StoreId, invoice.Id,
|
||||
new UpdateInvoiceRequest()
|
||||
new UpdateInvoiceRequest
|
||||
{
|
||||
Metadata = metadataForUpdate
|
||||
});
|
||||
@ -2284,13 +2321,12 @@ namespace BTCPayServer.Tests
|
||||
await client.UnarchiveInvoice(user.StoreId, invoice.Id);
|
||||
Assert.NotNull(await client.GetInvoice(user.StoreId, invoice.Id));
|
||||
|
||||
|
||||
foreach (var marked in new[] { InvoiceStatus.Settled, InvoiceStatus.Invalid })
|
||||
{
|
||||
var inv = await client.CreateInvoice(user.StoreId,
|
||||
new CreateInvoiceRequest() { Currency = "USD", Amount = 100 });
|
||||
new CreateInvoiceRequest { Currency = "USD", Amount = 100 });
|
||||
await user.PayInvoice(inv.Id);
|
||||
await client.MarkInvoiceStatus(user.StoreId, inv.Id, new MarkInvoiceStatusRequest()
|
||||
await client.MarkInvoiceStatus(user.StoreId, inv.Id, new MarkInvoiceStatusRequest
|
||||
{
|
||||
Status = marked
|
||||
});
|
||||
@ -2318,13 +2354,12 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
newInvoice = await client.CreateInvoice(user.StoreId,
|
||||
new CreateInvoiceRequest()
|
||||
new CreateInvoiceRequest
|
||||
{
|
||||
Currency = "USD",
|
||||
Amount = 1,
|
||||
Checkout = new CreateInvoiceRequest.CheckoutOptions()
|
||||
Checkout = new CreateInvoiceRequest.CheckoutOptions
|
||||
{
|
||||
DefaultLanguage = "it-it ",
|
||||
RedirectURL = "http://toto.com/lol"
|
||||
@ -4471,7 +4506,7 @@ clientBasic.PreviewUpdateStoreRateConfiguration(user.StoreId, new StoreRateConfi
|
||||
await AssertHttpError(404, async () => await withdrawalClient.SimulateCustodianAccountWithdrawal(storeId, "WRONG-ACCOUNT-ID", simulateWithdrawalRequest));
|
||||
|
||||
// Test: SimulateWithdrawal, wrong store ID
|
||||
// TODO it is wierd that 403 is considered normal, but it is like this for all calls where the store is wrong... I'd have preferred a 404 error, because the store cannot be found.
|
||||
// TODO it is weird that 403 is considered normal, but it is like this for all calls where the store is wrong... I'd have preferred a 404 error, because the store cannot be found.
|
||||
await AssertHttpError(403, async () => await withdrawalClient.SimulateCustodianAccountWithdrawal("WRONG-STORE-ID", accountId, simulateWithdrawalRequest));
|
||||
|
||||
// Test: SimulateWithdrawal, correct payment method, wrong amount
|
||||
@ -4502,7 +4537,7 @@ clientBasic.PreviewUpdateStoreRateConfiguration(user.StoreId, new StoreRateConfi
|
||||
await AssertHttpError(404, async () => await withdrawalClient.CreateCustodianAccountWithdrawal(storeId, "WRONG-ACCOUNT-ID", createWithdrawalRequest));
|
||||
|
||||
// Test: CreateWithdrawal, wrong store ID
|
||||
// TODO it is wierd that 403 is considered normal, but it is like this for all calls where the store is wrong... I'd have preferred a 404 error, because the store cannot be found.
|
||||
// TODO it is weird that 403 is considered normal, but it is like this for all calls where the store is wrong... I'd have preferred a 404 error, because the store cannot be found.
|
||||
await AssertHttpError(403, async () => await withdrawalClient.CreateCustodianAccountWithdrawal("WRONG-STORE-ID", accountId, createWithdrawalRequest));
|
||||
|
||||
// Test: CreateWithdrawal, correct payment method, wrong amount
|
||||
|
@ -295,17 +295,12 @@ namespace BTCPayServer.Tests
|
||||
|
||||
public void AddLightningNode()
|
||||
{
|
||||
AddLightningNode(null, null, true);
|
||||
AddLightningNode(null, true);
|
||||
}
|
||||
|
||||
public void AddLightningNode(LightningConnectionType? connectionType = null, bool test = true)
|
||||
public void AddLightningNode(string connectionType = null, bool test = true)
|
||||
{
|
||||
AddLightningNode(null, connectionType, test);
|
||||
}
|
||||
|
||||
public void AddLightningNode(string cryptoCode = null, LightningConnectionType? connectionType = null, bool test = true)
|
||||
{
|
||||
cryptoCode ??= "BTC";
|
||||
var cryptoCode = "BTC";
|
||||
if (!Driver.PageSource.Contains("Connect to a Lightning node"))
|
||||
{
|
||||
GoToLightningSettings();
|
||||
|
@ -6,6 +6,7 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
@ -17,6 +18,7 @@ using BTCPayServer.Controllers;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Lightning;
|
||||
using BTCPayServer.Models.InvoicingModels;
|
||||
using BTCPayServer.NTag424;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
@ -25,6 +27,8 @@ using BTCPayServer.Views.Manage;
|
||||
using BTCPayServer.Views.Server;
|
||||
using BTCPayServer.Views.Stores;
|
||||
using BTCPayServer.Views.Wallets;
|
||||
using Dapper;
|
||||
using ExchangeSharp;
|
||||
using LNURL;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@ -92,9 +96,8 @@ namespace BTCPayServer.Tests
|
||||
Assert.Contains("App updated", s.FindAlertMessage().Text);
|
||||
|
||||
s.Driver.FindElement(By.Id("ViewApp")).Click();
|
||||
var windows = s.Driver.WindowHandles;
|
||||
Assert.Equal(2, windows.Count);
|
||||
s.Driver.SwitchTo().Window(windows[1]);
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
|
||||
|
||||
s.Driver.FindElement(By.CssSelector("button[type='submit']")).Click();
|
||||
|
||||
Assert.Contains("Enter your email", s.Driver.PageSource);
|
||||
@ -103,12 +106,16 @@ namespace BTCPayServer.Tests
|
||||
|
||||
s.PayInvoice(true);
|
||||
var invoiceId = s.Driver.Url[(s.Driver.Url.LastIndexOf("/", StringComparison.Ordinal) + 1)..];
|
||||
s.Driver.Close();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First());
|
||||
|
||||
s.GoToInvoice(invoiceId);
|
||||
Assert.Contains("aa@aa.com", s.Driver.PageSource);
|
||||
|
||||
// Payment Request
|
||||
s.Driver.FindElement(By.Id("StoreNav-PaymentRequests")).Click();
|
||||
s.Driver.FindElement(By.Id("CreatePaymentRequest")).Click();
|
||||
Thread.Sleep(10000);
|
||||
s.Driver.FindElement(By.Id("Title")).SendKeys("Pay123");
|
||||
s.Driver.FindElement(By.Id("Amount")).SendKeys("700");
|
||||
new SelectElement(s.Driver.FindElement(By.Id("FormId"))).SelectByValue("Email");
|
||||
@ -116,13 +123,19 @@ namespace BTCPayServer.Tests
|
||||
|
||||
s.Driver.FindElement(By.XPath("//a[starts-with(@id, 'Edit-')]")).Click();
|
||||
var editUrl = s.Driver.Url;
|
||||
|
||||
s.Driver.FindElement(By.Id("ViewPaymentRequest")).Click();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
|
||||
|
||||
s.Driver.FindElement(By.CssSelector("[data-test='form-button']")).Click();
|
||||
Assert.Contains("Enter your email", s.Driver.PageSource);
|
||||
|
||||
s.Driver.FindElement(By.Name("buyerEmail")).SendKeys("aa@aa.com");
|
||||
s.Driver.FindElement(By.CssSelector("input[type='submit']")).Click();
|
||||
invoiceId = s.Driver.Url.Split('/').Last();
|
||||
s.Driver.Close();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First());
|
||||
|
||||
s.Driver.Navigate().GoToUrl(editUrl);
|
||||
Assert.Contains("aa@aa.com", s.Driver.PageSource);
|
||||
var invoice = await s.Server.PayTester.GetService<InvoiceRepository>().GetInvoice(invoiceId);
|
||||
@ -196,7 +209,6 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.Id("StoreNav-PaymentRequests")).Click();
|
||||
s.Driver.FindElement(By.Id("CreatePaymentRequest")).Click();
|
||||
Assert.Equal(4, new SelectElement(s.Driver.FindElement(By.Id("FormId"))).Options.Count);
|
||||
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
@ -216,8 +228,7 @@ namespace BTCPayServer.Tests
|
||||
s.GoToInvoices(s.StoreId);
|
||||
}
|
||||
// Let's CPFP from the invoices page
|
||||
s.Driver.SetCheckbox(By.Id("selectAllCheckbox"), true);
|
||||
s.Driver.FindElement(By.Id("ActionsDropdownToggle")).Click();
|
||||
s.Driver.SetCheckbox(By.CssSelector(".mass-action-select-all"), true);
|
||||
s.Driver.FindElement(By.Id("BumpFee")).Click();
|
||||
s.Driver.FindElement(By.Id("BroadcastTransaction")).Click();
|
||||
s.FindAlertMessage();
|
||||
@ -225,16 +236,14 @@ namespace BTCPayServer.Tests
|
||||
|
||||
// CPFP again should fail because all invoices got bumped
|
||||
s.GoToInvoices();
|
||||
s.Driver.SetCheckbox(By.Id("selectAllCheckbox"), true);
|
||||
s.Driver.FindElement(By.Id("ActionsDropdownToggle")).Click();
|
||||
s.Driver.SetCheckbox(By.CssSelector(".mass-action-select-all"), true);
|
||||
s.Driver.FindElement(By.Id("BumpFee")).Click();
|
||||
Assert.Contains($"/stores/{s.StoreId}/invoices", s.Driver.Url);
|
||||
Assert.Contains("any UTXO available", s.FindAlertMessage(StatusMessageModel.StatusSeverity.Error).Text);
|
||||
|
||||
// But we should be able to bump from the wallet's page
|
||||
s.GoToWallet(navPages: WalletsNavPages.Transactions);
|
||||
s.Driver.SetCheckbox(By.Id("selectAllCheckbox"), true);
|
||||
s.Driver.FindElement(By.Id("ActionsDropdownToggle")).Click();
|
||||
s.Driver.SetCheckbox(By.CssSelector(".mass-action-select-all"), true);
|
||||
s.Driver.FindElement(By.Id("BumpFee")).Click();
|
||||
s.Driver.FindElement(By.Id("BroadcastTransaction")).Click();
|
||||
Assert.Contains($"/wallets/{s.WalletId}", s.Driver.Url);
|
||||
@ -457,6 +466,12 @@ namespace BTCPayServer.Tests
|
||||
using var s = CreateSeleniumTester();
|
||||
await s.StartAsync();
|
||||
s.RegisterNewUser(true);
|
||||
s.CreateNewStore();
|
||||
|
||||
// Store Emails without server fallback
|
||||
s.GoToStore(StoreNavPages.Emails);
|
||||
s.Driver.FindElement(By.Id("ConfigureEmailRules")).Click();
|
||||
Assert.Contains("You need to configure email settings before this feature works", s.Driver.PageSource);
|
||||
|
||||
// Server Emails
|
||||
s.Driver.Navigate().GoToUrl(s.Link("/server/emails"));
|
||||
@ -466,12 +481,11 @@ namespace BTCPayServer.Tests
|
||||
s.FindAlertMessage();
|
||||
}
|
||||
CanSetupEmailCore(s);
|
||||
s.CreateNewStore();
|
||||
|
||||
// Store Emails
|
||||
// Store Emails with server fallback
|
||||
s.GoToStore(StoreNavPages.Emails);
|
||||
s.Driver.FindElement(By.Id("ConfigureEmailRules")).Click();
|
||||
Assert.Contains("You need to configure email settings before this feature works", s.Driver.PageSource);
|
||||
Assert.Contains("Emails will be sent with the email settings of the server", s.Driver.PageSource);
|
||||
|
||||
s.GoToStore(StoreNavPages.Emails);
|
||||
CanSetupEmailCore(s);
|
||||
@ -481,10 +495,11 @@ namespace BTCPayServer.Tests
|
||||
Assert.Contains("There are no rules yet.", s.Driver.PageSource);
|
||||
Assert.DoesNotContain("id=\"SaveEmailRules\"", s.Driver.PageSource);
|
||||
Assert.DoesNotContain("You need to configure email settings before this feature works", s.Driver.PageSource);
|
||||
Assert.DoesNotContain("Emails will be sent with the email settings of the server", s.Driver.PageSource);
|
||||
|
||||
s.Driver.FindElement(By.Id("CreateEmailRule")).Click();
|
||||
var select = new SelectElement(s.Driver.FindElement(By.Id("Rules_0__Trigger")));
|
||||
select.SelectByText("InvoiceSettled", true);
|
||||
select.SelectByText("An invoice has been settled", true);
|
||||
s.Driver.FindElement(By.Id("Rules_0__To")).SendKeys("test@gmail.com");
|
||||
s.Driver.FindElement(By.Id("Rules_0__CustomerEmail")).Click();
|
||||
s.Driver.FindElement(By.Id("Rules_0__Subject")).SendKeys("Thanks!");
|
||||
@ -556,24 +571,24 @@ namespace BTCPayServer.Tests
|
||||
s.AddDerivationScheme();
|
||||
s.GoToInvoices();
|
||||
s.CreateInvoice();
|
||||
s.Driver.FindElement(By.Id("markStatusDropdownMenuButton")).Click();
|
||||
s.Driver.FindElements(By.ClassName("changeInvoiceState"))[0].Click();
|
||||
s.Driver.FindElement(By.CssSelector("[data-invoice-state-badge] .dropdown-toggle")).Click();
|
||||
s.Driver.FindElements(By.CssSelector("[data-invoice-state-badge] .dropdown-menu button"))[0].Click();
|
||||
TestUtils.Eventually(() => Assert.Contains("Invalid (marked)", s.Driver.PageSource));
|
||||
s.Driver.Navigate().Refresh();
|
||||
|
||||
s.Driver.FindElement(By.Id("markStatusDropdownMenuButton")).Click();
|
||||
s.Driver.FindElements(By.ClassName("changeInvoiceState"))[0].Click();
|
||||
s.Driver.FindElement(By.CssSelector("[data-invoice-state-badge] .dropdown-toggle")).Click();
|
||||
s.Driver.FindElements(By.CssSelector("[data-invoice-state-badge] .dropdown-menu button"))[0].Click();
|
||||
TestUtils.Eventually(() => Assert.Contains("Settled (marked)", s.Driver.PageSource));
|
||||
|
||||
s.Driver.Navigate().Refresh();
|
||||
|
||||
s.Driver.FindElement(By.Id("markStatusDropdownMenuButton")).Click();
|
||||
s.Driver.FindElements(By.ClassName("changeInvoiceState"))[0].Click();
|
||||
s.Driver.FindElement(By.CssSelector("[data-invoice-state-badge] .dropdown-toggle")).Click();
|
||||
s.Driver.FindElements(By.CssSelector("[data-invoice-state-badge] .dropdown-menu button"))[0].Click();
|
||||
TestUtils.Eventually(() => Assert.Contains("Invalid (marked)", s.Driver.PageSource));
|
||||
s.Driver.Navigate().Refresh();
|
||||
|
||||
s.Driver.FindElement(By.Id("markStatusDropdownMenuButton")).Click();
|
||||
s.Driver.FindElements(By.ClassName("changeInvoiceState"))[0].Click();
|
||||
s.Driver.FindElement(By.CssSelector("[data-invoice-state-badge] .dropdown-toggle")).Click();
|
||||
s.Driver.FindElements(By.CssSelector("[data-invoice-state-badge] .dropdown-menu button"))[0].Click();
|
||||
TestUtils.Eventually(() => Assert.Contains("Settled (marked)", s.Driver.PageSource));
|
||||
}
|
||||
|
||||
@ -730,9 +745,8 @@ namespace BTCPayServer.Tests
|
||||
Assert.Contains(invoiceId, s.Driver.PageSource);
|
||||
|
||||
// archive via list
|
||||
s.Driver.FindElement(By.CssSelector($".selector[value=\"{invoiceId}\"]")).Click();
|
||||
s.Driver.FindElement(By.Id("ActionsDropdownToggle")).Click();
|
||||
s.Driver.FindElement(By.Id("ActionsDropdownArchive")).Click();
|
||||
s.Driver.FindElement(By.CssSelector($".mass-action-select[value=\"{invoiceId}\"]")).Click();
|
||||
s.Driver.FindElement(By.Id("ArchiveSelected")).Click();
|
||||
Assert.Contains("1 invoice archived", s.FindAlertMessage().Text);
|
||||
Assert.DoesNotContain(invoiceId, s.Driver.PageSource);
|
||||
|
||||
@ -740,9 +754,8 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.Id("StatusOptionsToggle")).Click();
|
||||
s.Driver.FindElement(By.Id("StatusOptionsIncludeArchived")).Click();
|
||||
Assert.Contains(invoiceId, s.Driver.PageSource);
|
||||
s.Driver.FindElement(By.CssSelector($".selector[value=\"{invoiceId}\"]")).Click();
|
||||
s.Driver.FindElement(By.Id("ActionsDropdownToggle")).Click();
|
||||
s.Driver.FindElement(By.Id("ActionsDropdownUnarchive")).Click();
|
||||
s.Driver.FindElement(By.CssSelector($".mass-action-select[value=\"{invoiceId}\"]")).Click();
|
||||
s.Driver.FindElement(By.Id("UnarchiveSelected")).Click();
|
||||
Assert.Contains("1 invoice unarchived", s.FindAlertMessage().Text);
|
||||
Assert.Contains(invoiceId, s.Driver.PageSource);
|
||||
|
||||
@ -936,8 +949,9 @@ namespace BTCPayServer.Tests
|
||||
Policies.CanModifyServerSettings
|
||||
});
|
||||
|
||||
s.GoToUrl("/logout");
|
||||
await alice.MakeAdmin();
|
||||
s.Logout();
|
||||
|
||||
s.GoToLogin();
|
||||
s.LogIn(alice.Email, alice.Password);
|
||||
s.GoToUrl($"/cheat/permissions/stores/{alice.StoreId}");
|
||||
@ -989,13 +1003,12 @@ namespace BTCPayServer.Tests
|
||||
Assert.Contains("App successfully created", s.FindAlertMessage().Text);
|
||||
|
||||
s.Driver.FindElement(By.CssSelector("label[for='DefaultView_Cart']")).Click();
|
||||
s.Driver.FindElement(By.CssSelector(".template-item:nth-of-type(1) .btn-primary")).Click();
|
||||
s.Driver.FindElement(By.CssSelector(".template-item:nth-of-type(1)")).Click();
|
||||
s.Driver.FindElement(By.Id("BuyButtonText")).SendKeys("Take my money");
|
||||
s.Driver.FindElement(By.Id("EditorCategories-ts-control")).SendKeys("Drinks");
|
||||
s.Driver.FindElement(By.Id("SaveItemChanges")).Click();
|
||||
s.Driver.FindElement(By.Id("ToggleRawEditor")).Click();
|
||||
s.Driver.FindElement(By.Id("ApplyItemChanges")).Click();
|
||||
|
||||
var template = s.Driver.FindElement(By.Id("Template")).GetAttribute("value");
|
||||
var template = s.Driver.FindElement(By.Id("TemplateConfig")).GetAttribute("value");
|
||||
Assert.Contains("\"buyButtonText\": \"Take my money\"", template);
|
||||
Assert.Matches("\"categories\": \\[\n\\s+\"Drinks\"\n\\s+\\]", template);
|
||||
|
||||
@ -1035,20 +1048,20 @@ namespace BTCPayServer.Tests
|
||||
select.SelectByText("Point of", true);
|
||||
s.Driver.FindElement(By.Id("SaveButton")).Click();
|
||||
s.FindAlertMessage();
|
||||
|
||||
s.Logout();
|
||||
s.GoToLogin();
|
||||
s.LogIn(userId);
|
||||
// Make sure after login, we are not redirected to the PoS
|
||||
s.Logout();
|
||||
s.LogIn(userId);
|
||||
Assert.DoesNotContain("Tea shop", s.Driver.PageSource);
|
||||
var prevUrl = s.Driver.Url;
|
||||
|
||||
// We are only if explicitly going to /
|
||||
s.GoToUrl("/");
|
||||
Assert.Contains("Tea shop", s.Driver.PageSource);
|
||||
s.Driver.Navigate().GoToUrl(new Uri(prevUrl, UriKind.Absolute));
|
||||
// Check redirect to canonical url
|
||||
s.GoToUrl(posBaseUrl);
|
||||
Assert.Equal("/", new Uri(s.Driver.Url, UriKind.Absolute).AbsolutePath);
|
||||
|
||||
// Let's check with domain mapping as well.
|
||||
s.Driver.Navigate().GoToUrl(new Uri(prevUrl, UriKind.Absolute));
|
||||
s.GoToServer(ServerNavPages.Policies);
|
||||
s.Driver.ScrollTo(By.Id("RootAppId"));
|
||||
select = new SelectElement(s.Driver.FindElement(By.Id("RootAppId")));
|
||||
@ -1061,19 +1074,20 @@ namespace BTCPayServer.Tests
|
||||
select.SelectByText("Point of", true);
|
||||
s.Driver.FindElement(By.Id("SaveButton")).Click();
|
||||
Assert.Contains("Policies updated successfully", s.FindAlertMessage().Text);
|
||||
|
||||
// Make sure after login, we are not redirected to the PoS
|
||||
s.Logout();
|
||||
s.LogIn(userId);
|
||||
// Make sure after login, we are not redirected to the PoS
|
||||
Assert.DoesNotContain("Tea shop", s.Driver.PageSource);
|
||||
|
||||
// We are only if explicitly going to /
|
||||
s.GoToUrl("/");
|
||||
Assert.Contains("Tea shop", s.Driver.PageSource);
|
||||
// Check redirect to canonical url
|
||||
s.GoToUrl(posBaseUrl);
|
||||
Assert.Equal("/", new Uri(s.Driver.Url, UriKind.Absolute).AbsolutePath);
|
||||
|
||||
// Archive
|
||||
Assert.True(s.Driver.ElementDoesNotExist(By.Id("Nav-ArchivedApps")));
|
||||
s.Driver.SwitchTo().Window(windows[0]);
|
||||
Assert.True(s.Driver.ElementDoesNotExist(By.Id("Nav-ArchivedApps")));
|
||||
s.Driver.FindElement(By.Id("btn-archive-toggle")).Click();
|
||||
Assert.Contains("The app has been archived and will no longer appear in the apps list by default.", s.FindAlertMessage().Text);
|
||||
|
||||
@ -1107,7 +1121,7 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.Id("Title")).SendKeys("Kukkstarter");
|
||||
s.Driver.FindElement(By.CssSelector("div.note-editable.card-block")).SendKeys("1BTC = 1BTC");
|
||||
s.Driver.FindElement(By.Id("TargetCurrency")).Clear();
|
||||
s.Driver.FindElement(By.Id("TargetCurrency")).SendKeys("JPY");
|
||||
s.Driver.FindElement(By.Id("TargetCurrency")).SendKeys("EUR");
|
||||
s.Driver.FindElement(By.Id("TargetAmount")).SendKeys("700");
|
||||
|
||||
// test wrong dates
|
||||
@ -1122,7 +1136,8 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.Id("SaveSettings")).Click();
|
||||
Assert.Contains("App updated", s.FindAlertMessage().Text);
|
||||
var appId = s.Driver.Url.Split('/')[4];
|
||||
|
||||
|
||||
// CHeck public page
|
||||
s.Driver.FindElement(By.Id("ViewApp")).Click();
|
||||
var windows = s.Driver.WindowHandles;
|
||||
Assert.Equal(2, windows.Count);
|
||||
@ -1132,6 +1147,26 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal("Currently active!",
|
||||
s.Driver.FindElement(By.CssSelector("[data-test='time-state']")).Text);
|
||||
|
||||
// Contribute
|
||||
s.Driver.FindElement(By.Id("crowdfund-body-header-cta")).Click();
|
||||
Thread.Sleep(1000);
|
||||
s.Driver.WaitUntilAvailable(By.Name("btcpay"));
|
||||
|
||||
var frameElement = s.Driver.FindElement(By.Name("btcpay"));
|
||||
Assert.True(frameElement.Displayed);
|
||||
var iframe = s.Driver.SwitchTo().Frame(frameElement);
|
||||
iframe.WaitUntilAvailable(By.Id("Checkout-v2"));
|
||||
|
||||
IWebElement closebutton = null;
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
closebutton = iframe.FindElement(By.Id("close"));
|
||||
Assert.True(closebutton.Displayed);
|
||||
});
|
||||
closebutton.Click();
|
||||
s.Driver.AssertElementNotFound(By.Name("btcpay"));
|
||||
|
||||
// Back to admin view
|
||||
s.Driver.Close();
|
||||
s.Driver.SwitchTo().Window(windows[0]);
|
||||
|
||||
@ -1179,13 +1214,13 @@ namespace BTCPayServer.Tests
|
||||
var editUrl = s.Driver.Url;
|
||||
|
||||
s.Driver.FindElement(By.Id("ViewPaymentRequest")).Click();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
|
||||
var viewUrl = s.Driver.Url;
|
||||
|
||||
Assert.Equal("Amount due", s.Driver.FindElement(By.CssSelector("[data-test='amount-due-title']")).Text);
|
||||
Assert.Equal("Pay Invoice", s.Driver.FindElement(By.CssSelector("[data-test='pay-button']")).Text.Trim());
|
||||
Assert.Equal("Pay Invoice", s.Driver.FindElement(By.Id("PayInvoice")).Text.Trim());
|
||||
s.Driver.Close();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First());
|
||||
|
||||
// expire
|
||||
s.GoToUrl(editUrl);
|
||||
s.Driver.ExecuteJavaScript("document.getElementById('ExpiryDate').value = '2021-01-21T21:00:00.000Z'");
|
||||
s.Driver.FindElement(By.Id("SaveButton")).Click();
|
||||
s.Driver.FindElement(By.XPath("//a[starts-with(@id, 'Edit-')]")).Click();
|
||||
@ -1205,12 +1240,28 @@ namespace BTCPayServer.Tests
|
||||
Assert.True(s.Driver.FindElement(By.Id("Currency")).Enabled);
|
||||
|
||||
s.GoToUrl(viewUrl);
|
||||
s.Driver.AssertElementNotFound(By.CssSelector("[data-test='status']"));
|
||||
Assert.Equal("Pay Invoice", s.Driver.FindElement(By.CssSelector("[data-test='pay-button']")).Text.Trim());
|
||||
Assert.Equal("Pay Invoice", s.Driver.FindElement(By.Id("PayInvoice")).Text.Trim());
|
||||
|
||||
// test invoice creation, click with JS, because the button is inside a sticky header
|
||||
s.Driver.ExecuteJavaScript("document.querySelector('[data-test=\"pay-button\"]').click()");
|
||||
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
|
||||
// test invoice creation
|
||||
s.Driver.FindElement(By.Id("PayInvoice")).Click();
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
s.Driver.WaitUntilAvailable(By.Name("btcpay"));
|
||||
|
||||
var frameElement = s.Driver.FindElement(By.Name("btcpay"));
|
||||
Assert.True(frameElement.Displayed);
|
||||
var iframe = s.Driver.SwitchTo().Frame(frameElement);
|
||||
iframe.WaitUntilAvailable(By.Id("Checkout-v2"));
|
||||
|
||||
IWebElement closebutton = null;
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
closebutton = iframe.FindElement(By.Id("close"));
|
||||
Assert.True(closebutton.Displayed);
|
||||
});
|
||||
closebutton.Click();
|
||||
s.Driver.AssertElementNotFound(By.Name("btcpay"));
|
||||
});
|
||||
|
||||
// amount and currency should not be editable, because invoice exists
|
||||
s.GoToUrl(editUrl);
|
||||
@ -1234,32 +1285,45 @@ namespace BTCPayServer.Tests
|
||||
|
||||
// payment
|
||||
s.GoToUrl(viewUrl);
|
||||
s.Driver.ExecuteJavaScript("document.querySelector('[data-test=\"pay-button\"]').click()");
|
||||
|
||||
// Pay full amount
|
||||
s.PayInvoice();
|
||||
|
||||
// Processing
|
||||
s.Driver.FindElement(By.Id("PayInvoice")).Click();
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
var processingSection = s.Driver.WaitForElement(By.Id("processing"));
|
||||
Assert.True(processingSection.Displayed);
|
||||
Assert.Contains("Payment Received", processingSection.Text);
|
||||
Assert.Contains("Your payment has been received and is now processing", processingSection.Text);
|
||||
});
|
||||
s.Driver.WaitUntilAvailable(By.Name("btcpay"));
|
||||
|
||||
var frameElement = s.Driver.FindElement(By.Name("btcpay"));
|
||||
Assert.True(frameElement.Displayed);
|
||||
var iframe = s.Driver.SwitchTo().Frame(frameElement);
|
||||
iframe.WaitUntilAvailable(By.Id("Checkout-v2"));
|
||||
|
||||
// Pay full amount
|
||||
s.PayInvoice();
|
||||
|
||||
// Processing
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
var processingSection = s.Driver.WaitForElement(By.Id("processing"));
|
||||
Assert.True(processingSection.Displayed);
|
||||
Assert.Contains("Payment Received", processingSection.Text);
|
||||
Assert.Contains("Your payment has been received and is now processing", processingSection.Text);
|
||||
});
|
||||
|
||||
s.GoToUrl(viewUrl);
|
||||
Assert.Equal("Processing", s.Driver.WaitForElement(By.CssSelector("[data-test='status']")).Text);
|
||||
s.Driver.Navigate().Back();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles[0]);
|
||||
Assert.Equal("Processing", s.Driver.WaitForElement(By.CssSelector("[data-test='status']")).Text);
|
||||
s.Driver.SwitchTo().Frame(frameElement);
|
||||
|
||||
// Mine
|
||||
s.MineBlockOnInvoiceCheckout();
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
Assert.Contains("Mined 1 block",
|
||||
s.Driver.WaitForElement(By.Id("CheatSuccessMessage")).Text);
|
||||
// Mine
|
||||
s.MineBlockOnInvoiceCheckout();
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
Assert.Contains("Mined 1 block",
|
||||
s.Driver.WaitForElement(By.Id("CheatSuccessMessage")).Text);
|
||||
});
|
||||
|
||||
s.Driver.FindElement(By.Id("close")).Click();
|
||||
s.Driver.AssertElementNotFound(By.Name("btcpay"));
|
||||
});
|
||||
s.GoToUrl(viewUrl);
|
||||
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles[0]);
|
||||
Assert.Equal("Settled", s.Driver.WaitForElement(By.CssSelector("[data-test='status']")).Text);
|
||||
}
|
||||
|
||||
@ -1356,7 +1420,7 @@ namespace BTCPayServer.Tests
|
||||
TestLogs.LogInformation("Let's try to update one of them");
|
||||
s.Driver.FindElement(By.LinkText("Modify")).Click();
|
||||
|
||||
using FakeServer server = new FakeServer();
|
||||
using var server = new FakeServer();
|
||||
await server.Start();
|
||||
s.Driver.FindElement(By.Name("PayloadUrl")).Clear();
|
||||
s.Driver.FindElement(By.Name("PayloadUrl")).SendKeys(server.ServerUri.AbsoluteUri);
|
||||
@ -1365,13 +1429,6 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.Name("update")).Click();
|
||||
s.FindAlertMessage();
|
||||
s.Driver.FindElement(By.LinkText("Modify")).Click();
|
||||
foreach (var value in Enum.GetValues(typeof(WebhookEventType)))
|
||||
{
|
||||
// Here we make sure we did not forget an event type in the list
|
||||
// However, maybe some event should not appear here because not at the store level.
|
||||
// Fix as needed.
|
||||
Assert.Contains($"value=\"{value}\"", s.Driver.PageSource);
|
||||
}
|
||||
|
||||
// This one should be checked
|
||||
Assert.Contains("value=\"InvoiceProcessing\" checked", s.Driver.PageSource);
|
||||
@ -1398,7 +1455,7 @@ namespace BTCPayServer.Tests
|
||||
server.Done();
|
||||
|
||||
TestLogs.LogInformation("Let's make a failed event");
|
||||
s.CreateInvoice();
|
||||
var invoiceId = s.CreateInvoice();
|
||||
request = await server.GetNextRequest();
|
||||
request.Response.StatusCode = 404;
|
||||
server.Done();
|
||||
@ -1423,7 +1480,7 @@ namespace BTCPayServer.Tests
|
||||
CanBrowseContent(s);
|
||||
|
||||
s.GoToInvoices();
|
||||
s.Driver.FindElement(By.LinkText("Details")).Click();
|
||||
s.Driver.FindElement(By.LinkText(invoiceId)).Click();
|
||||
CanBrowseContent(s);
|
||||
var element = s.Driver.FindElement(By.ClassName("redeliver"));
|
||||
element.Click();
|
||||
@ -1676,13 +1733,14 @@ namespace BTCPayServer.Tests
|
||||
// no previous page in the wizard, hence no back button
|
||||
Assert.True(s.Driver.ElementDoesNotExist(By.Id("GoBack")));
|
||||
s.Driver.FindElement(By.Id("CancelWizard")).Click();
|
||||
Assert.Equal(settingsUri.ToString(), s.Driver.Url);
|
||||
|
||||
// Transactions list contains export and action, ensure functions are present.
|
||||
Assert.Equal(settingsUri.ToString(), s.Driver.Url);
|
||||
|
||||
// Transactions list contains export, ensure functions are present.
|
||||
s.Driver.FindElement(By.Id($"StoreNav-Wallet{cryptoCode}")).Click();
|
||||
s.Driver.FindElement(By.Id("ActionsDropdownToggle")).Click();
|
||||
|
||||
s.Driver.FindElement(By.ClassName("mass-action-select-all")).Click();
|
||||
s.Driver.FindElement(By.Id("BumpFee"));
|
||||
|
||||
|
||||
// JSON export
|
||||
s.Driver.FindElement(By.Id("ExportDropdownToggle")).Click();
|
||||
s.Driver.FindElement(By.Id("ExportJSON")).Click();
|
||||
@ -1693,20 +1751,15 @@ namespace BTCPayServer.Tests
|
||||
Assert.Contains("\"Amount\": \"3.00000000\"", s.Driver.PageSource);
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First());
|
||||
|
||||
// BIP-329 export
|
||||
s.Driver.FindElement(By.Id("ExportDropdownToggle")).Click();
|
||||
s.Driver.FindElement(By.Id("ExportBIP329")).Click();
|
||||
Thread.Sleep(1000);
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
|
||||
Assert.Contains(s.WalletId.ToString(), s.Driver.Url);
|
||||
Assert.EndsWith("export?format=bip329", s.Driver.Url);
|
||||
Assert.Contains("{\"type\":\"tx\",\"ref\":\"", s.Driver.PageSource);
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First());
|
||||
|
||||
// CSV export
|
||||
s.Driver.FindElement(By.Id("ExportDropdownToggle")).Click();
|
||||
s.Driver.FindElement(By.Id("ExportCSV")).Click();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First());
|
||||
|
||||
// BIP-329 export
|
||||
s.Driver.FindElement(By.Id("ExportDropdownToggle")).Click();
|
||||
s.Driver.FindElement(By.Id("ExportBIP329")).Click();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First());
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
@ -1754,7 +1807,12 @@ namespace BTCPayServer.Tests
|
||||
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.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
|
||||
Assert.Contains("PP1", s.Driver.PageSource);
|
||||
s.Driver.Close();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First());
|
||||
|
||||
s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
|
||||
|
||||
@ -1765,9 +1823,9 @@ namespace BTCPayServer.Tests
|
||||
var description = s.Driver.FindElement(By.ClassName("card-block"));
|
||||
description.SendKeys("Description Edit");
|
||||
s.Driver.FindElement(By.Id("SaveButton")).Click();
|
||||
|
||||
s.Driver.FindElement(By.LinkText("PP1 Edited")).Click();
|
||||
|
||||
|
||||
s.Driver.FindElement(By.LinkText("View")).Click();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
|
||||
Assert.Contains("Description Edit", s.Driver.PageSource);
|
||||
Assert.Contains("PP1 Edited", s.Driver.PageSource);
|
||||
}
|
||||
@ -1793,7 +1851,12 @@ namespace BTCPayServer.Tests
|
||||
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.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
|
||||
Assert.Contains("PP1", s.Driver.PageSource);
|
||||
s.Driver.Close();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First());
|
||||
|
||||
s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
|
||||
|
||||
@ -1805,6 +1868,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
// This should select the first View, ie, the last one PP2
|
||||
s.Driver.FindElement(By.LinkText("View")).Click();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
|
||||
var address = await s.Server.ExplorerNode.GetNewAddressAsync();
|
||||
s.Driver.FindElement(By.Id("Destination")).SendKeys(address.ToString());
|
||||
s.Driver.FindElement(By.Id("ClaimedAmount")).Clear();
|
||||
@ -1826,6 +1890,9 @@ namespace BTCPayServer.Tests
|
||||
Assert.Contains("Awaiting Approval", s.Driver.PageSource);
|
||||
|
||||
var viewPullPaymentUrl = s.Driver.Url;
|
||||
s.Driver.Close();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First());
|
||||
|
||||
// This one should have nothing
|
||||
s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
|
||||
var payouts = s.Driver.FindElements(By.ClassName("pp-payout"));
|
||||
@ -1838,8 +1905,7 @@ namespace BTCPayServer.Tests
|
||||
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.ClassName("mass-action-select-all")).Click();
|
||||
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingApproval}-approve-pay")).Click();
|
||||
|
||||
s.Driver.FindElement(By.Id("SignTransaction")).Click();
|
||||
@ -1904,8 +1970,10 @@ namespace BTCPayServer.Tests
|
||||
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();
|
||||
|
||||
s.Driver.FindElement(By.LinkText("View")).Click();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
|
||||
|
||||
address = await s.Server.ExplorerNode.GetNewAddressAsync();
|
||||
s.Driver.FindElement(By.Id("Destination")).SendKeys(address.ToString());
|
||||
s.Driver.FindElement(By.Id("ClaimedAmount")).SendKeys(Keys.Enter);
|
||||
@ -1914,18 +1982,18 @@ namespace BTCPayServer.Tests
|
||||
Assert.Contains(PayoutState.AwaitingApproval.GetStateString(), s.Driver.PageSource);
|
||||
s.GoToStore(s.StoreId, StoreNavPages.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.ClassName("mass-action-select-all")).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.Driver.Close();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First());
|
||||
|
||||
s.GoToStore(s.StoreId, StoreNavPages.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.ClassName("mass-action-select-all")).Click();
|
||||
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingPayment}-mark-paid")).Click();
|
||||
s.FindAlertMessage();
|
||||
|
||||
@ -1958,17 +2026,16 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.Id("Currency")).SendKeys("BTC");
|
||||
s.Driver.FindElement(By.Id("Create")).Click();
|
||||
s.Driver.FindElement(By.LinkText("View")).Click();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
|
||||
|
||||
// Bitcoin-only, SelectedPaymentMethod should not be displayed
|
||||
s.Driver.ElementDoesNotExist(By.Id("SelectedPaymentMethod"));
|
||||
|
||||
var bolt = (await s.Server.CustomerLightningD.CreateInvoice(
|
||||
payoutAmount,
|
||||
$"LN payout test {DateTime.UtcNow.Ticks}",
|
||||
TimeSpan.FromHours(1), CancellationToken.None)).BOLT11;
|
||||
s.Driver.FindElement(By.Id("Destination")).SendKeys(bolt);
|
||||
s.Driver.FindElement(By.Id("SelectedPaymentMethod")).Click();
|
||||
s.Driver.FindElement(By.CssSelector(
|
||||
$"#SelectedPaymentMethod option[value={new PaymentMethodId("BTC", PaymentTypes.LightningLike)}]"))
|
||||
.Click();
|
||||
|
||||
s.Driver.FindElement(By.Id("ClaimedAmount")).SendKeys(Keys.Enter);
|
||||
//we do not allow short-life bolts.
|
||||
s.FindAlertMessage(StatusMessageModel.StatusSeverity.Error);
|
||||
@ -1979,27 +2046,23 @@ namespace BTCPayServer.Tests
|
||||
TimeSpan.FromDays(31), CancellationToken.None)).BOLT11;
|
||||
s.Driver.FindElement(By.Id("Destination")).Clear();
|
||||
s.Driver.FindElement(By.Id("Destination")).SendKeys(bolt);
|
||||
s.Driver.FindElement(By.Id("SelectedPaymentMethod")).Click();
|
||||
s.Driver.FindElement(By.CssSelector(
|
||||
$"#SelectedPaymentMethod option[value={new PaymentMethodId("BTC", PaymentTypes.LightningLike)}]"))
|
||||
.Click();
|
||||
|
||||
s.Driver.FindElement(By.Id("ClaimedAmount")).SendKeys(Keys.Enter);
|
||||
s.FindAlertMessage();
|
||||
|
||||
Assert.Contains(PayoutState.AwaitingApproval.GetStateString(), s.Driver.PageSource);
|
||||
s.Driver.Close();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First());
|
||||
|
||||
s.GoToStore(newStore.storeId, StoreNavPages.Payouts);
|
||||
s.Driver.FindElement(By.Id($"{new PaymentMethodId("BTC", PaymentTypes.LightningLike)}-view")).Click();
|
||||
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.ClassName("mass-action-select-all")).Click();
|
||||
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingApproval}-approve-pay")).Click();
|
||||
Assert.Contains(bolt, s.Driver.PageSource);
|
||||
Assert.Contains($"{payoutAmount.ToString()} BTC", s.Driver.PageSource);
|
||||
Assert.Contains($"{payoutAmount} BTC", s.Driver.PageSource);
|
||||
s.Driver.FindElement(By.CssSelector("#pay-invoices-form")).Submit();
|
||||
|
||||
s.FindAlertMessage(StatusMessageModel.StatusSeverity.Success);
|
||||
s.FindAlertMessage();
|
||||
s.GoToStore(newStore.storeId, StoreNavPages.Payouts);
|
||||
s.Driver.FindElement(By.Id($"{new PaymentMethodId("BTC", PaymentTypes.LightningLike)}-view")).Click();
|
||||
|
||||
@ -2009,8 +2072,7 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingPayment}-view")).Click();
|
||||
Assert.Contains(bolt, 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.ClassName("mass-action-select-all")).Click();
|
||||
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingPayment}-mark-paid")).Click();
|
||||
s.Driver.FindElement(By.Id($"{new PaymentMethodId("BTC", PaymentTypes.LightningLike)}-view")).Click();
|
||||
|
||||
@ -2025,16 +2087,21 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.SetCheckbox(By.Id("AutoApproveClaims"), true);
|
||||
s.Driver.FindElement(By.Id("Amount")).Clear();
|
||||
s.Driver.FindElement(By.Id("Amount")).SendKeys("99.0" + Keys.Enter);
|
||||
s.FindAlertMessage(StatusMessageModel.StatusSeverity.Success);
|
||||
s.FindAlertMessage();
|
||||
|
||||
s.Driver.FindElement(By.LinkText("View")).Click();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
|
||||
|
||||
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(StatusMessageModel.StatusSeverity.Success);
|
||||
s.FindAlertMessage();
|
||||
|
||||
Assert.Contains(PayoutState.AwaitingPayment.GetStateString(), s.Driver.PageSource);
|
||||
s.Driver.Close();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First());
|
||||
|
||||
// LNURL Withdraw support check with BTC denomination
|
||||
s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
|
||||
@ -2045,11 +2112,15 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.Id("Amount")).SendKeys("0.0000001");
|
||||
s.Driver.FindElement(By.Id("Currency")).Clear();
|
||||
s.Driver.FindElement(By.Id("Currency")).SendKeys("BTC" + Keys.Enter);
|
||||
s.FindAlertMessage(StatusMessageModel.StatusSeverity.Success);
|
||||
s.FindAlertMessage();
|
||||
|
||||
s.Driver.FindElement(By.LinkText("View")).Click();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
|
||||
|
||||
s.Driver.FindElement(By.CssSelector("#lnurlwithdraw-button")).Click();
|
||||
s.Driver.WaitForElement(By.Id("qr-code-data-input"));
|
||||
|
||||
// Try to use lnurlw via the QR Code
|
||||
var lnurl = new Uri(LNURL.LNURL.Parse(s.Driver.FindElement(By.Id("qr-code-data-input")).GetAttribute("value"), out _).ToString().Replace("https", "http"));
|
||||
s.Driver.FindElement(By.CssSelector("button[data-bs-dismiss='modal']")).Click();
|
||||
var info = Assert.IsType<LNURLWithdrawRequest>(await LNURL.LNURL.FetchInformation(lnurl, s.Server.PayTester.HttpClient));
|
||||
@ -2060,7 +2131,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(info.CurrentBalance, new LightMoney(0.0000001m, LightMoneyUnit.BTC));
|
||||
|
||||
var bolt2 = (await s.Server.CustomerLightningD.CreateInvoice(
|
||||
new LightMoney(0.0000001m, LightMoneyUnit.BTC),
|
||||
new LightMoney(0.00000005m, LightMoneyUnit.BTC),
|
||||
$"LNurl w payout test {DateTime.UtcNow.Ticks}",
|
||||
TimeSpan.FromHours(1), CancellationToken.None));
|
||||
var response = await info.SendRequest(bolt2.BOLT11, s.Server.PayTester.HttpClient, null,null);
|
||||
@ -2072,6 +2143,54 @@ namespace BTCPayServer.Tests
|
||||
Assert.Contains(PayoutState.Completed.GetStateString(), s.Driver.PageSource);
|
||||
Assert.Equal(LightningInvoiceStatus.Paid, (await s.Server.CustomerLightningD.GetInvoice(bolt2.Id)).Status);
|
||||
});
|
||||
s.Driver.Close();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First());
|
||||
|
||||
// Simulate a boltcard
|
||||
{
|
||||
var db = s.Server.PayTester.GetService<ApplicationDbContextFactory>();
|
||||
var ppid = lnurl.AbsoluteUri.Split("/").Last();
|
||||
var issuerKey = new IssuerKey(SettingsRepositoryExtensions.FixedKey());
|
||||
var uid = RandomNumberGenerator.GetBytes(7);
|
||||
var cardKey = issuerKey.CreateCardKey(uid, 0);
|
||||
var keys = cardKey.DeriveBoltcardKeys(issuerKey);
|
||||
await db.LinkBoltcardToPullPayment(ppid, issuerKey, uid);
|
||||
var piccData = new byte[] { 0xc7 }.Concat(uid).Concat(new byte[] { 1, 0, 0, 0, 0, 0, 0, 0 }).ToArray();
|
||||
var p = keys.EncryptionKey.Encrypt(piccData);
|
||||
var c = keys.AuthenticationKey.GetSunMac(uid, 1);
|
||||
var boltcardUrl = new Uri(s.Server.PayTester.ServerUri.AbsoluteUri + $"boltcard?p={Encoders.Hex.EncodeData(p).ToStringUpperInvariant()}&c={Encoders.Hex.EncodeData(c).ToStringUpperInvariant()}");
|
||||
// p and c should work so long as no bolt11 has been submitted
|
||||
info = (LNURLWithdrawRequest)await LNURL.LNURL.FetchInformation(boltcardUrl, s.Server.PayTester.HttpClient);
|
||||
info = (LNURLWithdrawRequest)await LNURL.LNURL.FetchInformation(boltcardUrl, s.Server.PayTester.HttpClient);
|
||||
var fakeBoltcardUrl = new Uri(Regex.Replace(boltcardUrl.AbsoluteUri, "p=([A-F0-9]{32})", $"p={RandomBytes(16)}"));
|
||||
await Assert.ThrowsAsync<LNUrlException>(() => LNURL.LNURL.FetchInformation(fakeBoltcardUrl, s.Server.PayTester.HttpClient));
|
||||
fakeBoltcardUrl = new Uri(Regex.Replace(boltcardUrl.AbsoluteUri, "c=([A-F0-9]{16})", $"c={RandomBytes(8)}"));
|
||||
await Assert.ThrowsAsync<LNUrlException>(() => LNURL.LNURL.FetchInformation(fakeBoltcardUrl, s.Server.PayTester.HttpClient));
|
||||
|
||||
bolt2 = (await s.Server.CustomerLightningD.CreateInvoice(
|
||||
new LightMoney(0.00000005m, LightMoneyUnit.BTC),
|
||||
$"LNurl w payout test2 {DateTime.UtcNow.Ticks}",
|
||||
TimeSpan.FromHours(1), CancellationToken.None));
|
||||
response = await info.SendRequest(bolt2.BOLT11, s.Server.PayTester.HttpClient, null, null);
|
||||
Assert.Equal("OK", response.Status);
|
||||
// No replay should be possible
|
||||
await Assert.ThrowsAsync<LNUrlException>(() => LNURL.LNURL.FetchInformation(boltcardUrl, s.Server.PayTester.HttpClient));
|
||||
response = await info.SendRequest(bolt2.BOLT11, s.Server.PayTester.HttpClient, null, null);
|
||||
Assert.Equal("ERROR", response.Status);
|
||||
Assert.Contains("Replayed", response.Reason);
|
||||
|
||||
// Check the state of the registration, counter should have increased
|
||||
var reg = await db.GetBoltcardRegistration(issuerKey, uid);
|
||||
Assert.Equal((ppid, 1, 0), (reg.PullPaymentId, reg.Counter, reg.Version));
|
||||
await db.SetBoltcardResetState(issuerKey, uid);
|
||||
// After reset, counter is 0, version unchanged and ppId null
|
||||
reg = await db.GetBoltcardRegistration(issuerKey, uid);
|
||||
Assert.Equal((null, 0, 0), (reg.PullPaymentId, reg.Counter, reg.Version));
|
||||
await db.LinkBoltcardToPullPayment(ppid, issuerKey, uid);
|
||||
// Relink should bump Version
|
||||
reg = await db.GetBoltcardRegistration(issuerKey, uid);
|
||||
Assert.Equal((ppid, 0, 1), (reg.PullPaymentId, reg.Counter, reg.Version));
|
||||
}
|
||||
|
||||
s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
|
||||
s.Driver.FindElement(By.Id("NewPullPayment")).Click();
|
||||
@ -2081,8 +2200,11 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.Id("Amount")).SendKeys("0.0000001");
|
||||
s.Driver.FindElement(By.Id("Currency")).Clear();
|
||||
s.Driver.FindElement(By.Id("Currency")).SendKeys("BTC" + Keys.Enter);
|
||||
s.FindAlertMessage(StatusMessageModel.StatusSeverity.Success);
|
||||
s.FindAlertMessage();
|
||||
|
||||
s.Driver.FindElement(By.LinkText("View")).Click();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
|
||||
|
||||
s.Driver.FindElement(By.CssSelector("#lnurlwithdraw-button")).Click();
|
||||
lnurl = new Uri(LNURL.LNURL.Parse(s.Driver.FindElement(By.Id("qr-code-data-input")).GetAttribute("value"), out _).ToString().Replace("https", "http"));
|
||||
|
||||
@ -2106,6 +2228,8 @@ namespace BTCPayServer.Tests
|
||||
|
||||
Assert.Contains(PayoutState.AwaitingApproval.GetStateString(), s.Driver.PageSource);
|
||||
});
|
||||
s.Driver.Close();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First());
|
||||
|
||||
// LNURL Withdraw support check with SATS denomination
|
||||
s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
|
||||
@ -2116,8 +2240,11 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.Id("Amount")).SendKeys("21021");
|
||||
s.Driver.FindElement(By.Id("Currency")).Clear();
|
||||
s.Driver.FindElement(By.Id("Currency")).SendKeys("SATS" + Keys.Enter);
|
||||
s.FindAlertMessage(StatusMessageModel.StatusSeverity.Success);
|
||||
s.FindAlertMessage();
|
||||
|
||||
s.Driver.FindElement(By.LinkText("View")).Click();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
|
||||
|
||||
s.Driver.FindElement(By.CssSelector("#lnurlwithdraw-button")).Click();
|
||||
lnurl = new Uri(LNURL.LNURL.Parse(s.Driver.FindElement(By.Id("qr-code-data-input")).GetAttribute("value"), out _).ToString().Replace("https", "http"));
|
||||
s.Driver.FindElement(By.CssSelector("button[data-bs-dismiss='modal']")).Click();
|
||||
@ -2142,6 +2269,13 @@ namespace BTCPayServer.Tests
|
||||
Assert.Contains(PayoutState.Completed.GetStateString(), s.Driver.PageSource);
|
||||
Assert.Equal(LightningInvoiceStatus.Paid, (await s.Server.CustomerLightningD.GetInvoice(bolt2.Id)).Status);
|
||||
});
|
||||
s.Driver.Close();
|
||||
}
|
||||
|
||||
private string RandomBytes(int count)
|
||||
{
|
||||
var c = RandomNumberGenerator.GetBytes(count);
|
||||
return Encoders.Hex.EncodeData(c);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -2214,7 +2348,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.Contains("EUR", s.Driver.FindElement(By.Id("Currency")).Text);
|
||||
Assert.Contains("0,00", s.Driver.FindElement(By.Id("Amount")).Text);
|
||||
Assert.Equal("", s.Driver.FindElement(By.Id("Calculation")).Text);
|
||||
Assert.True(s.Driver.FindElement(By.Id("ModeTablist-amount")).Selected);
|
||||
Assert.True(s.Driver.FindElement(By.Id("ModeTablist-amounts")).Selected);
|
||||
Assert.False(s.Driver.FindElement(By.Id("ModeTablist-discount")).Enabled);
|
||||
Assert.False(s.Driver.FindElement(By.Id("ModeTablist-tip")).Enabled);
|
||||
|
||||
@ -2223,13 +2357,17 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.CssSelector(".keypad [data-key='2']")).Click();
|
||||
s.Driver.FindElement(By.CssSelector(".keypad [data-key='3']")).Click();
|
||||
s.Driver.FindElement(By.CssSelector(".keypad [data-key='4']")).Click();
|
||||
s.Driver.FindElement(By.CssSelector(".keypad [data-key='.']")).Click();
|
||||
s.Driver.FindElement(By.CssSelector(".keypad [data-key='0']")).Click();
|
||||
s.Driver.FindElement(By.CssSelector(".keypad [data-key='0']")).Click();
|
||||
Assert.Equal("1.234,00", s.Driver.FindElement(By.Id("Amount")).Text);
|
||||
Assert.Equal("", s.Driver.FindElement(By.Id("Calculation")).Text);
|
||||
s.Driver.FindElement(By.CssSelector(".keypad [data-key='+']")).Click();
|
||||
s.Driver.FindElement(By.CssSelector(".keypad [data-key='5']")).Click();
|
||||
s.Driver.FindElement(By.CssSelector(".keypad [data-key='6']")).Click();
|
||||
Assert.Equal("1.234,56", s.Driver.FindElement(By.Id("Amount")).Text);
|
||||
Assert.True(s.Driver.FindElement(By.Id("ModeTablist-discount")).Enabled);
|
||||
Assert.True(s.Driver.FindElement(By.Id("ModeTablist-tip")).Enabled);
|
||||
Assert.Equal("", s.Driver.FindElement(By.Id("Calculation")).Text);
|
||||
Assert.Equal("1.234,00 € + 0,56 €", s.Driver.FindElement(By.Id("Calculation")).Text);
|
||||
|
||||
// Discount: 10%
|
||||
s.Driver.FindElement(By.CssSelector("label[for='ModeTablist-discount']")).Click();
|
||||
@ -2237,14 +2375,14 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.CssSelector(".keypad [data-key='0']")).Click();
|
||||
Assert.Contains("1.111,10", s.Driver.FindElement(By.Id("Amount")).Text);
|
||||
Assert.Contains("10% discount", s.Driver.FindElement(By.Id("Discount")).Text);
|
||||
Assert.Contains("1.234,56 € - 123,46 € (10%)", s.Driver.FindElement(By.Id("Calculation")).Text);
|
||||
Assert.Contains("1.234,00 € + 0,56 € - 123,46 € (10%)", s.Driver.FindElement(By.Id("Calculation")).Text);
|
||||
|
||||
// Tip: 10%
|
||||
s.Driver.FindElement(By.CssSelector("label[for='ModeTablist-tip']")).Click();
|
||||
s.Driver.WaitForElement(By.Id("Tip-Custom"));
|
||||
s.Driver.FindElement(By.Id("Tip-10")).Click();
|
||||
Assert.Contains("1.222,21", s.Driver.FindElement(By.Id("Amount")).Text);
|
||||
Assert.Contains("1.234,56 € - 123,46 € (10%) + 111,11 € (10%)", s.Driver.FindElement(By.Id("Calculation")).Text);
|
||||
Assert.Contains("1.234,00 € + 0,56 € - 123,46 € (10%) + 111,11 € (10%)", s.Driver.FindElement(By.Id("Calculation")).Text);
|
||||
|
||||
// Pay
|
||||
s.Driver.FindElement(By.Id("pay-button")).Click();
|
||||
@ -2527,20 +2665,22 @@ namespace BTCPayServer.Tests
|
||||
|
||||
s.Driver.FindElement(By.Id("Create")).Click();
|
||||
s.Driver.FindElement(By.LinkText("View")).Click();
|
||||
s.Driver.FindElement(By.Id("Destination")).SendKeys(lnurl);
|
||||
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
|
||||
var pullPaymentId = s.Driver.Url.Split('/').Last();
|
||||
|
||||
s.Driver.FindElement(By.Id("Destination")).SendKeys(lnurl);
|
||||
s.Driver.FindElement(By.Id("ClaimedAmount")).Clear();
|
||||
s.Driver.FindElement(By.Id("ClaimedAmount")).SendKeys("0.0000001" + Keys.Enter);
|
||||
s.FindAlertMessage();
|
||||
|
||||
|
||||
s.Driver.Close();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First());
|
||||
s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
|
||||
var payouts = s.Driver.FindElements(By.ClassName("pp-payout"));
|
||||
payouts[0].Click();
|
||||
s.Driver.FindElement(By.Id("BTC_LightningLike-view")).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.ClassName("mass-action-select-all")).Click();
|
||||
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingApproval}-approve-pay")).Click();
|
||||
|
||||
Assert.Contains(lnurl, s.Driver.PageSource);
|
||||
@ -2628,7 +2768,7 @@ namespace BTCPayServer.Tests
|
||||
callbacks.Add(request.Callback);
|
||||
break;
|
||||
default:
|
||||
Assert.False(true, "Should have matched");
|
||||
Assert.Fail("Should have matched");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2969,13 +3109,18 @@ retry:
|
||||
{
|
||||
s.Driver.FindElement(By.Id("QuickFillDropdownToggle")).Click();
|
||||
s.Driver.FindElement(By.CssSelector("#quick-fill .dropdown-menu .dropdown-item:first-child")).Click();
|
||||
s.Driver.FindElement(By.Id("Settings_Login")).Clear();
|
||||
s.Driver.FindElement(By.Id("Settings_Login")).SendKeys("test@gmail.com");
|
||||
s.Driver.FindElement(By.CssSelector("button[value=\"Save\"]")).Submit();
|
||||
s.FindAlertMessage();
|
||||
s.Driver.FindElement(By.Id("Settings_Password")).Clear();
|
||||
s.Driver.FindElement(By.Id("Settings_Password")).SendKeys("mypassword");
|
||||
|
||||
s.Driver.FindElement(By.Id("Settings_From")).Clear();
|
||||
s.Driver.FindElement(By.Id("Settings_From")).SendKeys("Firstname Lastname <email@example.com>");
|
||||
s.Driver.FindElement(By.Id("Save")).SendKeys(Keys.Enter);
|
||||
Assert.Contains("Configured", s.Driver.PageSource);
|
||||
s.Driver.FindElement(By.Id("Settings_Login")).Clear();
|
||||
s.Driver.FindElement(By.Id("Settings_Login")).SendKeys("test_fix@gmail.com");
|
||||
s.Driver.FindElement(By.Id("Save")).SendKeys(Keys.Enter);
|
||||
Assert.Contains("Configured", s.Driver.PageSource);
|
||||
|
@ -5,11 +5,14 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Hosting;
|
||||
using BTCPayServer.Lightning;
|
||||
using BTCPayServer.Lightning.CLightning;
|
||||
using BTCPayServer.Payments.Lightning;
|
||||
using BTCPayServer.Tests.Lnd;
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using Microsoft.Extensions.Configuration.Memory;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NBitcoin;
|
||||
using NBitcoin.RPC;
|
||||
@ -26,7 +29,7 @@ namespace BTCPayServer.Tests
|
||||
public ILoggerProvider LoggerProvider { get; }
|
||||
|
||||
internal ILog TestLogs;
|
||||
public ServerTester(string scope, bool newDb, ILog testLogs, ILoggerProvider loggerProvider)
|
||||
public ServerTester(string scope, bool newDb, ILog testLogs, ILoggerProvider loggerProvider, BTCPayNetworkProvider networkProvider)
|
||||
{
|
||||
LoggerProvider = loggerProvider;
|
||||
this.TestLogs = testLogs;
|
||||
@ -36,7 +39,7 @@ namespace BTCPayServer.Tests
|
||||
if (!Directory.Exists(_Directory))
|
||||
Directory.CreateDirectory(_Directory);
|
||||
|
||||
NetworkProvider = new BTCPayNetworkProvider(ChainName.Regtest);
|
||||
NetworkProvider = networkProvider;
|
||||
ExplorerNode = new RPCClient(RPCCredentialString.Parse(GetEnvironment("TESTS_BTCRPCCONNECTION", "server=http://127.0.0.1:43782;ceiwHEbqWI83:DwubwWsoo3")), NetworkProvider.GetNetwork<BTCPayNetwork>("BTC").NBitcoinNetwork);
|
||||
ExplorerNode.ScanRPCCapabilities();
|
||||
|
||||
@ -94,17 +97,18 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
ActivateLightning(LightningConnectionType.CLightning);
|
||||
}
|
||||
public void ActivateLightning(LightningConnectionType internalNode)
|
||||
public void ActivateLightning(string internalNode)
|
||||
{
|
||||
var btc = NetworkProvider.GetNetwork<BTCPayNetwork>("BTC").NBitcoinNetwork;
|
||||
CustomerLightningD = LightningClientFactory.CreateClient(GetEnvironment("TEST_CUSTOMERLIGHTNINGD", "type=clightning;server=tcp://127.0.0.1:30992/"), btc);
|
||||
MerchantLightningD = LightningClientFactory.CreateClient(GetEnvironment("TEST_MERCHANTLIGHTNINGD", "type=clightning;server=tcp://127.0.0.1:30993/"), btc);
|
||||
var factory = new LightningClientFactory(btc);
|
||||
CustomerLightningD = factory.Create(GetEnvironment("TEST_CUSTOMERLIGHTNINGD", "type=clightning;server=tcp://127.0.0.1:30992/"));
|
||||
MerchantLightningD = factory.Create(GetEnvironment("TEST_MERCHANTLIGHTNINGD", "type=clightning;server=tcp://127.0.0.1:30993/"));
|
||||
MerchantCharge = new ChargeTester(this, "TEST_MERCHANTCHARGE", "type=charge;server=http://127.0.0.1:54938/;api-token=foiewnccewuify;allowinsecure=true", "merchant_lightningd", btc);
|
||||
MerchantLnd = new LndMockTester(this, "TEST_MERCHANTLND", "http://lnd:lnd@127.0.0.1:35531/", "merchant_lnd", btc);
|
||||
PayTester.UseLightning = true;
|
||||
PayTester.IntegratedLightning = GetLightningConnectionString(internalNode, true);
|
||||
}
|
||||
public string GetLightningConnectionString(LightningConnectionType? connectionType, bool isMerchant)
|
||||
public string GetLightningConnectionString(string connectionType, bool isMerchant)
|
||||
{
|
||||
string connectionString = null;
|
||||
if (connectionType is null)
|
||||
|
@ -278,7 +278,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
public bool IsAdmin { get; internal set; }
|
||||
|
||||
public void RegisterLightningNode(string cryptoCode, LightningConnectionType? connectionType = null, bool isMerchant = true)
|
||||
public void RegisterLightningNode(string cryptoCode, string connectionType = null, bool isMerchant = true)
|
||||
{
|
||||
RegisterLightningNodeAsync(cryptoCode, connectionType, isMerchant).GetAwaiter().GetResult();
|
||||
}
|
||||
@ -286,7 +286,7 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
return RegisterLightningNodeAsync(cryptoCode, null, isMerchant, storeId);
|
||||
}
|
||||
public async Task RegisterLightningNodeAsync(string cryptoCode, LightningConnectionType? connectionType, bool isMerchant = true, string storeId = null)
|
||||
public async Task RegisterLightningNodeAsync(string cryptoCode, string connectionType, bool isMerchant = true, string storeId = null)
|
||||
{
|
||||
var storeController = GetController<UIStoresController>();
|
||||
|
||||
@ -297,7 +297,7 @@ namespace BTCPayServer.Tests
|
||||
await storeController.SetupLightningNode(storeId ?? StoreId,
|
||||
vm, "save", cryptoCode);
|
||||
if (storeController.ModelState.ErrorCount != 0)
|
||||
Assert.False(true, storeController.ModelState.FirstOrDefault().Value.Errors[0].ErrorMessage);
|
||||
Assert.Fail(storeController.ModelState.FirstOrDefault().Value.Errors[0].ErrorMessage);
|
||||
}
|
||||
|
||||
public async Task RegisterInternalLightningNodeAsync(string cryptoCode, string storeId = null)
|
||||
@ -307,7 +307,7 @@ namespace BTCPayServer.Tests
|
||||
await storeController.SetupLightningNode(storeId ?? StoreId,
|
||||
vm, "save", cryptoCode);
|
||||
if (storeController.ModelState.ErrorCount != 0)
|
||||
Assert.False(true, storeController.ModelState.FirstOrDefault().Value.Errors[0].ErrorMessage);
|
||||
Assert.Fail(storeController.ModelState.FirstOrDefault().Value.Errors[0].ErrorMessage);
|
||||
}
|
||||
|
||||
public async Task<Coin> ReceiveUTXO(Money value, BTCPayNetwork network = null)
|
||||
@ -432,8 +432,7 @@ namespace BTCPayServer.Tests
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var error = JObject.Parse(await response.Content.ReadAsStringAsync());
|
||||
Assert.True(false,
|
||||
$"Error: {error["errorCode"].Value<string>()}: {error["message"].Value<string>()}");
|
||||
Assert.Fail($"Error: {error["errorCode"].Value<string>()}: {error["message"].Value<string>()}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -488,7 +487,7 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
|
||||
public List<WebhookInvoiceEvent> WebhookEvents { get; set; } = new List<WebhookInvoiceEvent>();
|
||||
public TEvent AssertHasWebhookEvent<TEvent>(WebhookEventType eventType, Action<TEvent> assert) where TEvent : class
|
||||
public TEvent AssertHasWebhookEvent<TEvent>(string eventType, Action<TEvent> assert) where TEvent : class
|
||||
{
|
||||
int retry = 0;
|
||||
retry:
|
||||
@ -516,12 +515,12 @@ retry:
|
||||
retry++;
|
||||
goto retry;
|
||||
}
|
||||
Assert.True(false, "No webhook event match the assertion");
|
||||
Assert.Fail("No webhook event match the assertion");
|
||||
return null;
|
||||
}
|
||||
public async Task SetupWebhook()
|
||||
{
|
||||
FakeServer server = new FakeServer();
|
||||
var server = new FakeServer();
|
||||
await server.Start();
|
||||
var client = await CreateClient(Policies.CanModifyStoreWebhooks);
|
||||
var wh = await client.CreateWebhook(StoreId, new CreateStoreWebhookRequest()
|
||||
@ -537,7 +536,7 @@ retry:
|
||||
{
|
||||
var inv = await BitPay.GetInvoiceAsync(invoiceId);
|
||||
var net = parent.ExplorerNode.Network;
|
||||
this.parent.ExplorerNode.SendToAddress(BitcoinAddress.Create(inv.BitcoinAddress, net), inv.BtcDue);
|
||||
await parent.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(inv.BitcoinAddress, net), inv.BtcDue);
|
||||
await TestUtils.EventuallyAsync(async () =>
|
||||
{
|
||||
var localInvoice = await BitPay.GetInvoiceAsync(invoiceId, Facade.Merchant);
|
||||
|
@ -6,6 +6,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NBitcoin;
|
||||
using OpenQA.Selenium;
|
||||
using Xunit;
|
||||
using Xunit.Sdk;
|
||||
|
@ -11,11 +11,14 @@ using BTCPayServer.Controllers;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Models.StoreViewModels;
|
||||
using BTCPayServer.Rating;
|
||||
using BTCPayServer.Services.Fees;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using BTCPayServer.Storage.Models;
|
||||
using BTCPayServer.Storage.Services.Providers.AzureBlobStorage.Configuration;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.FileSystemGlobbing;
|
||||
using NBitcoin;
|
||||
using NBitpayClient;
|
||||
@ -73,6 +76,25 @@ namespace BTCPayServer.Tests
|
||||
await UnitTest1.CanUploadRemoveFiles(controller);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanQueryMempoolFeeProvider()
|
||||
{
|
||||
IServiceCollection collection = new ServiceCollection();
|
||||
collection.AddMemoryCache();
|
||||
collection.AddHttpClient();
|
||||
var prov = collection.BuildServiceProvider();
|
||||
foreach (var isTestnet in new[] { true, false })
|
||||
{
|
||||
var mempoolSpaceFeeProvider = new MempoolSpaceFeeProvider(
|
||||
prov.GetService<IMemoryCache>(),
|
||||
"test" + isTestnet,
|
||||
prov.GetService<IHttpClientFactory>(),
|
||||
isTestnet);
|
||||
var rates = await mempoolSpaceFeeProvider.GetFeeRatesAsync();
|
||||
Assert.NotEmpty(rates);
|
||||
await mempoolSpaceFeeProvider.GetFeeRateAsync(20);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
public async Task CanQueryDirectProviders()
|
||||
{
|
||||
@ -268,17 +290,16 @@ retry:
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var details = ex is EqualException ? (ex as EqualException).Actual : ex.Message;
|
||||
var details = ex.Message;
|
||||
TestLogs.LogInformation($"FAILED: {url} ({file}) {details}");
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact()]
|
||||
[Fact]
|
||||
public void CanSolveTheDogesRatesOnKraken()
|
||||
{
|
||||
var provider = new BTCPayNetworkProvider(ChainName.Mainnet);
|
||||
var factory = FastTests.CreateBTCPayRateFactory();
|
||||
var fetcher = new RateFetcher(factory);
|
||||
|
||||
@ -296,9 +317,9 @@ retry:
|
||||
{
|
||||
var factory = FastTests.CreateBTCPayRateFactory();
|
||||
var fetcher = new RateFetcher(factory);
|
||||
var provider = new BTCPayNetworkProvider(ChainName.Mainnet);
|
||||
var provider = CreateNetworkProvider(ChainName.Mainnet);
|
||||
var b = new StoreBlob();
|
||||
string[] temporarilyBroken = { "UGX" };
|
||||
string[] temporarilyBroken = Array.Empty<string>();
|
||||
foreach (var k in StoreBlob.RecommendedExchanges)
|
||||
{
|
||||
b.DefaultCurrency = k.Key;
|
||||
@ -307,14 +328,20 @@ retry:
|
||||
var result = fetcher.FetchRates(pairs, rules, default);
|
||||
foreach ((CurrencyPair key, Task<RateResult> value) in result)
|
||||
{
|
||||
TestLogs.LogInformation($"Testing {key} when default currency is {k.Key}");
|
||||
var rateResult = await value;
|
||||
var hasRate = rateResult.BidAsk != null;
|
||||
|
||||
if (temporarilyBroken.Contains(k.Key))
|
||||
{
|
||||
TestLogs.LogInformation($"Skipping {key} because it is marked as temporarily broken");
|
||||
continue;
|
||||
if (!hasRate)
|
||||
{
|
||||
TestLogs.LogInformation($"Skipping {key} because it is marked as temporarily broken");
|
||||
continue;
|
||||
}
|
||||
TestLogs.LogInformation($"Note: {key} is marked as temporarily broken, but the rate is available");
|
||||
}
|
||||
var rateResult = await value;
|
||||
TestLogs.LogInformation($"Testing {key} when default currency is {k.Key}");
|
||||
Assert.True(rateResult.BidAsk != null, $"Impossible to get the rate {rateResult.EvaluatedRule}");
|
||||
Assert.True(hasRate, $"Impossible to get the rate {rateResult.EvaluatedRule}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -323,7 +350,7 @@ retry:
|
||||
public async Task CanGetRateCryptoCurrenciesByDefault()
|
||||
{
|
||||
using var cts = new CancellationTokenSource(60_000);
|
||||
var provider = new BTCPayNetworkProvider(ChainName.Mainnet);
|
||||
var provider = CreateNetworkProvider(ChainName.Mainnet);
|
||||
var factory = FastTests.CreateBTCPayRateFactory();
|
||||
var fetcher = new RateFetcher(factory);
|
||||
var pairs =
|
||||
@ -422,6 +449,11 @@ retry:
|
||||
version = Regex.Match(actual, "Original file: /npm/vue-sanitize-directive@([0-9]+.[0-9]+.[0-9]+)").Groups[1].Value;
|
||||
expected = (await (await client.GetAsync($"https://cdn.jsdelivr.net/npm/vue-sanitize-directive@{version}/dist/vue-sanitize-directive.umd.min.js")).Content.ReadAsStringAsync()).Trim();
|
||||
EqualJsContent(expected, actual);
|
||||
|
||||
actual = GetFileContent("BTCPayServer", "wwwroot", "vendor", "decimal.js", "decimal.min.js").Trim();
|
||||
version = Regex.Match(actual, "Original file: /npm/decimal\\.js@([0-9]+.[0-9]+.[0-9]+)/decimal\\.js").Groups[1].Value;
|
||||
expected = (await (await client.GetAsync($"https://cdn.jsdelivr.net/npm/decimal.js@{version}/decimal.min.js")).Content.ReadAsStringAsync()).Trim();
|
||||
EqualJsContent(expected, actual);
|
||||
}
|
||||
|
||||
private void EqualJsContent(string expected, string actual)
|
||||
|
@ -15,6 +15,7 @@ using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Abstractions.Models;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Configuration;
|
||||
using BTCPayServer.Controllers;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Events;
|
||||
@ -23,6 +24,7 @@ using BTCPayServer.Fido2.Models;
|
||||
using BTCPayServer.HostedServices;
|
||||
using BTCPayServer.Hosting;
|
||||
using BTCPayServer.Lightning;
|
||||
using BTCPayServer.Lightning.Charge;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Models.AccountViewModels;
|
||||
using BTCPayServer.Models.AppViewModels;
|
||||
@ -45,6 +47,7 @@ using BTCPayServer.Services.Apps;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.Labels;
|
||||
using BTCPayServer.Services.Mails;
|
||||
using BTCPayServer.Services.Notifications;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using BTCPayServer.Storage.Models;
|
||||
using BTCPayServer.Storage.Services.Providers.FileSystemStorage.Configuration;
|
||||
@ -56,6 +59,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NBitcoin;
|
||||
using NBitcoin.DataEncoders;
|
||||
using NBitcoin.Payment;
|
||||
@ -68,6 +72,7 @@ using Newtonsoft.Json.Schema;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
using CreateInvoiceRequest = BTCPayServer.Client.Models.CreateInvoiceRequest;
|
||||
using RatesViewModel = BTCPayServer.Models.StoreViewModels.RatesViewModel;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
@ -175,7 +180,7 @@ namespace BTCPayServer.Tests
|
||||
// If this test fail, run `UpdateSwagger` once.
|
||||
if (description != json["components"]["securitySchemes"]["API_Key"]["description"].Value<string>())
|
||||
{
|
||||
Assert.False(true, "Please run manually the test `UpdateSwagger` once");
|
||||
Assert.Fail("Please run manually the test `UpdateSwagger` once");
|
||||
}
|
||||
}
|
||||
|
||||
@ -269,7 +274,7 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var details = ex is EqualException ? (ex as EqualException).Actual : ex.Message;
|
||||
var details = ex.Message;
|
||||
TestLogs.LogInformation($"FAILED: {url} ({file}) {details}");
|
||||
|
||||
throw;
|
||||
@ -386,11 +391,11 @@ namespace BTCPayServer.Tests
|
||||
var newBolt11 = newInvoice.CryptoInfo.First(o => o.PaymentUrls.BOLT11 != null).PaymentUrls.BOLT11;
|
||||
var oldBolt11 = invoice.CryptoInfo.First(o => o.PaymentUrls.BOLT11 != null).PaymentUrls.BOLT11;
|
||||
Assert.NotEqual(newBolt11, oldBolt11);
|
||||
Assert.Equal(newInvoice.BtcDue.GetValue(),
|
||||
Assert.Equal(newInvoice.BtcDue.ToDecimal(MoneyUnit.BTC),
|
||||
BOLT11PaymentRequest.Parse(newBolt11, Network.RegTest).MinimumAmount.ToDecimal(LightMoneyUnit.BTC));
|
||||
}, 40000);
|
||||
|
||||
TestLogs.LogInformation($"Paying invoice {newInvoice.Id} remaining due amount {newInvoice.BtcDue.GetValue()} via lightning");
|
||||
TestLogs.LogInformation($"Paying invoice {newInvoice.Id} remaining due amount {newInvoice.BtcDue.GetValue((BTCPayNetwork) tester.DefaultNetwork)} via lightning");
|
||||
var evt = await tester.WaitForEvent<InvoiceDataChangedEvent>(async () =>
|
||||
{
|
||||
await tester.SendLightningPaymentAsync(newInvoice);
|
||||
@ -475,7 +480,7 @@ namespace BTCPayServer.Tests
|
||||
await ProcessLightningPayment(LightningConnectionType.LndREST);
|
||||
}
|
||||
|
||||
async Task ProcessLightningPayment(LightningConnectionType type)
|
||||
async Task ProcessLightningPayment(string type)
|
||||
{
|
||||
// For easier debugging and testing
|
||||
// LightningLikePaymentHandler.LIGHTNING_TIMEOUT = int.MaxValue;
|
||||
@ -539,7 +544,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
var pairingCode = (string)token.RouteValues["pairingCode"];
|
||||
|
||||
acc.BitPay.AuthorizeClient(new PairingCode(pairingCode)).GetAwaiter().GetResult();
|
||||
await acc.BitPay.AuthorizeClient(new PairingCode(pairingCode));
|
||||
Assert.True(acc.BitPay.TestAccess(Facade.Merchant));
|
||||
}
|
||||
|
||||
@ -603,7 +608,7 @@ namespace BTCPayServer.Tests
|
||||
completed = true;
|
||||
break;
|
||||
default:
|
||||
Assert.False(true, $"{evtName} was not expected");
|
||||
Assert.Fail($"{evtName} was not expected");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1294,7 +1299,7 @@ namespace BTCPayServer.Tests
|
||||
using var tester = CreateServerTester();
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
|
||||
var rng = new Random();
|
||||
@ -1328,8 +1333,6 @@ namespace BTCPayServer.Tests
|
||||
var btcmethod = (await client.GetInvoicePaymentMethods(user.StoreId, invoice.Id))[0];
|
||||
var paid = btcSent;
|
||||
var invoiceAddress = BitcoinAddress.Create(btcmethod.Destination, cashCow.Network);
|
||||
|
||||
|
||||
var btc = new PaymentMethodId("BTC", PaymentTypes.BTCLike);
|
||||
var networkFee = (await tester.PayTester.InvoiceRepository.GetInvoice(invoice.Id))
|
||||
.GetPaymentMethods()[btc]
|
||||
@ -1341,9 +1344,7 @@ namespace BTCPayServer.Tests
|
||||
networkFee = 0.0m;
|
||||
}
|
||||
|
||||
cashCow.SendToAddress(invoiceAddress, paid);
|
||||
|
||||
|
||||
await cashCow.SendToAddressAsync(invoiceAddress, paid);
|
||||
await TestUtils.EventuallyAsync(async () =>
|
||||
{
|
||||
try
|
||||
@ -1363,7 +1364,7 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
catch (JsonSerializationException)
|
||||
{
|
||||
Assert.False(true, "The bitpay's amount is not set");
|
||||
Assert.Fail("The bitpay's amount is not set");
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1418,7 +1419,7 @@ namespace BTCPayServer.Tests
|
||||
StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
rateVm.ScriptTest = "BTC_USD,BTC_CAD,DOGE_USD,DOGE_CAD";
|
||||
rateVm.Script = "DOGE_X = bittrex(DOGE_BTC) * BTC_X;\n" +
|
||||
rateVm.Script = "DOGE_X = bitpay(DOGE_BTC) * BTC_X;\n" +
|
||||
"X_CAD = ndax(X_CAD);\n" +
|
||||
"X_X = coingecko(X_X);";
|
||||
rateVm.Spread = 50;
|
||||
@ -1947,11 +1948,11 @@ namespace BTCPayServer.Tests
|
||||
using var tester = CreateServerTester();
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
await user.SetupWebhook();
|
||||
var invoice = user.BitPay.CreateInvoice(
|
||||
new Invoice()
|
||||
var invoice = await user.BitPay.CreateInvoiceAsync(
|
||||
new Invoice
|
||||
{
|
||||
Price = 5000.0m,
|
||||
TaxIncluded = 1000.0m,
|
||||
@ -1983,7 +1984,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.Single(textSearchResult);
|
||||
});
|
||||
|
||||
invoice = user.BitPay.GetInvoice(invoice.Id, Facade.Merchant);
|
||||
invoice = await user.BitPay.GetInvoiceAsync(invoice.Id, Facade.Merchant);
|
||||
Assert.Equal(1000.0m, invoice.TaxIncluded);
|
||||
Assert.Equal(5000.0m, invoice.Price);
|
||||
Assert.Equal(Money.Coins(0), invoice.BtcPaid);
|
||||
@ -1998,11 +1999,8 @@ namespace BTCPayServer.Tests
|
||||
Assert.Empty(user.BitPay.GetInvoices(invoice.InvoiceTime.UtcDateTime - TimeSpan.FromDays(5),
|
||||
invoice.InvoiceTime.DateTime - TimeSpan.FromDays(1)));
|
||||
|
||||
|
||||
var firstPayment = Money.Coins(0.04m);
|
||||
|
||||
var txFee = Money.Zero;
|
||||
|
||||
var cashCow = tester.ExplorerNode;
|
||||
|
||||
var invoiceAddress = BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network);
|
||||
@ -2030,7 +2028,7 @@ namespace BTCPayServer.Tests
|
||||
secondPayment = localInvoice.BtcDue;
|
||||
});
|
||||
|
||||
cashCow.SendToAddress(invoiceAddress, secondPayment);
|
||||
await cashCow.SendToAddressAsync(invoiceAddress, secondPayment);
|
||||
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
@ -2044,7 +2042,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.False((bool)((JValue)localInvoice.ExceptionStatus).Value);
|
||||
});
|
||||
|
||||
cashCow.Generate(1); //The user has medium speed settings, so 1 conf is enough to be confirmed
|
||||
await cashCow.GenerateAsync(1); //The user has medium speed settings, so 1 conf is enough to be confirmed
|
||||
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
@ -2052,7 +2050,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal("confirmed", localInvoice.Status);
|
||||
});
|
||||
|
||||
cashCow.Generate(5); //Now should be complete
|
||||
await cashCow.GenerateAsync(5); //Now should be complete
|
||||
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
@ -2061,7 +2059,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.NotEqual(0.0m, localInvoice.Rate);
|
||||
});
|
||||
|
||||
invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||
invoice = await user.BitPay.CreateInvoiceAsync(new Invoice
|
||||
{
|
||||
Price = 5000.0m,
|
||||
Currency = "USD",
|
||||
@ -2074,7 +2072,7 @@ namespace BTCPayServer.Tests
|
||||
}, Facade.Merchant);
|
||||
invoiceAddress = BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network);
|
||||
|
||||
var txId = cashCow.SendToAddress(invoiceAddress, invoice.BtcDue + Money.Coins(1));
|
||||
var txId = await cashCow.SendToAddressAsync(invoiceAddress, invoice.BtcDue + Money.Coins(1));
|
||||
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
@ -2091,7 +2089,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.Single(textSearchResult);
|
||||
});
|
||||
|
||||
cashCow.Generate(1);
|
||||
await cashCow.GenerateAsync(2);
|
||||
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
@ -2106,6 +2104,7 @@ namespace BTCPayServer.Tests
|
||||
c =>
|
||||
{
|
||||
Assert.False(c.ManuallyMarked);
|
||||
Assert.True(c.OverPaid);
|
||||
});
|
||||
user.AssertHasWebhookEvent<WebhookInvoiceProcessingEvent>(WebhookEventType.InvoiceProcessing,
|
||||
c =>
|
||||
@ -2256,13 +2255,17 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
|
||||
|
||||
class MockVersionFetcher : IVersionFetcher
|
||||
class MockVersionFetcher : GithubVersionFetcher
|
||||
{
|
||||
public const string MOCK_NEW_VERSION = "9.9.9.9";
|
||||
public Task<string> Fetch(CancellationToken cancellation)
|
||||
public override Task<string> Fetch(CancellationToken cancellation)
|
||||
{
|
||||
return Task.FromResult(MOCK_NEW_VERSION);
|
||||
}
|
||||
|
||||
public MockVersionFetcher(IHttpClientFactory httpClientFactory, BTCPayServerOptions options, ILogger<GithubVersionFetcher> logger, SettingsRepository settingsRepository, BTCPayServerEnvironment environment, NotificationSender notificationSender) : base(httpClientFactory, options, logger, settingsRepository, environment, notificationSender)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Fact(Timeout = LongRunningTestTimeout)]
|
||||
@ -2281,8 +2284,13 @@ namespace BTCPayServer.Tests
|
||||
var mockEnv = tester.PayTester.GetService<BTCPayServerEnvironment>();
|
||||
var mockSender = tester.PayTester.GetService<Services.Notifications.NotificationSender>();
|
||||
|
||||
var svc = new NewVersionCheckerHostedService(settings, mockEnv, mockSender, new MockVersionFetcher(), BTCPayLogs);
|
||||
await svc.ProcessVersionCheck();
|
||||
var svc = new MockVersionFetcher(tester.PayTester.GetService<IHttpClientFactory>(),
|
||||
tester.PayTester.GetService<BTCPayServerOptions>(),
|
||||
tester.PayTester.GetService<ILogger<GithubVersionFetcher>>(),
|
||||
settings,
|
||||
mockEnv,
|
||||
mockSender);
|
||||
await svc.Do(CancellationToken.None);
|
||||
|
||||
// since last version present in database was null, it should've been updated with version mock returned
|
||||
var lastVersion = await settings.GetSettingAsync<NewVersionCheckerDataHolder>();
|
||||
@ -2390,9 +2398,14 @@ namespace BTCPayServer.Tests
|
||||
Assert.NotNull(lnMethod.GetExternalLightningUrl());
|
||||
|
||||
var url = lnMethod.GetExternalLightningUrl();
|
||||
Assert.Equal(LightningConnectionType.Charge, url.ConnectionType);
|
||||
Assert.Equal("pass", url.Password);
|
||||
Assert.Equal("usr", url.Username);
|
||||
var kv = LightningConnectionStringHelper.ExtractValues(url, out var connType);
|
||||
Assert.Equal(LightningConnectionType.Charge,connType);
|
||||
var client = Assert.IsType<ChargeClient>(tester.PayTester.GetService<LightningClientFactoryService>()
|
||||
.Create(url, tester.NetworkProvider.GetNetwork<BTCPayNetwork>("BTC")));
|
||||
var auth = Assert.IsType<ChargeAuthentication.UserPasswordAuthentication>(client.ChargeAuthentication);
|
||||
|
||||
Assert.Equal("pass", auth.NetworkCredential.Password);
|
||||
Assert.Equal("usr", auth.NetworkCredential.UserName);
|
||||
|
||||
// Test if lightning connection strings get migrated to internal
|
||||
store.DerivationStrategies = new JObject()
|
||||
@ -2881,15 +2894,15 @@ namespace BTCPayServer.Tests
|
||||
Assert.Single(paymentTypes["On-Chain"]);
|
||||
|
||||
// 2 on-chain transactions: It received from the cashcow, then paid its own invoice
|
||||
report = await GetReport(acc, new() { ViewName = "On-Chain Wallets" });
|
||||
report = await GetReport(acc, new() { ViewName = "Wallets" });
|
||||
var txIdIndex = report.GetIndex("TransactionId");
|
||||
var balanceIndex = report.GetIndex("BalanceChange");
|
||||
Assert.Equal(2, report.Data.Count);
|
||||
Assert.Equal(64, report.Data[0][txIdIndex].Value<string>().Length);
|
||||
Assert.Contains(report.Data, d => d[balanceIndex].Value<decimal>() == 1.0m);
|
||||
Assert.Contains(report.Data, d => d[balanceIndex]["v"].Value<decimal>() == 1.0m);
|
||||
|
||||
// Items sold
|
||||
report = await GetReport(acc, new() { ViewName = "Products sold" });
|
||||
report = await GetReport(acc, new() { ViewName = "Sales" });
|
||||
var itemIndex = report.GetIndex("Product");
|
||||
var countIndex = report.GetIndex("Quantity");
|
||||
var itemsCount = report.Data.GroupBy(d => d[itemIndex].Value<string>())
|
||||
|
@ -3,8 +3,18 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Models;
|
||||
using BTCPayServer.Hosting;
|
||||
using BTCPayServer.Logging;
|
||||
using BTCPayServer.Plugins.Bitcoin;
|
||||
using BTCPayServer.Plugins;
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NBXplorer;
|
||||
using Xunit.Abstractions;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Configuration.Memory;
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
{
|
||||
@ -15,7 +25,53 @@ namespace BTCPayServer.Tests
|
||||
TestLogs = new XUnitLog(helper) { Name = "Tests" };
|
||||
TestLogProvider = new XUnitLogProvider(helper);
|
||||
BTCPayLogs = new BTCPayServer.Logging.Logs();
|
||||
BTCPayLogs.Configure(new BTCPayServer.Logging.FuncLoggerFactory((n) => new XUnitLog(helper) { Name = n }));
|
||||
LoggerFactory = new BTCPayServer.Logging.FuncLoggerFactory((n) => new XUnitLog(helper) { Name = n });
|
||||
BTCPayLogs.Configure(LoggerFactory);
|
||||
}
|
||||
|
||||
public BTCPayNetworkProvider CreateNetworkProvider(ChainName chainName)
|
||||
{
|
||||
var conf = new ConfigurationRoot(new List<IConfigurationProvider>()
|
||||
{
|
||||
new MemoryConfigurationProvider(new MemoryConfigurationSource()
|
||||
{
|
||||
InitialData = new[] {
|
||||
new KeyValuePair<string, string>("chains", "*"),
|
||||
new KeyValuePair<string, string>("network", chainName.ToString())
|
||||
}
|
||||
})
|
||||
});
|
||||
return CreateNetworkProvider(conf);
|
||||
}
|
||||
public BTCPayNetworkProvider CreateNetworkProvider(IConfiguration conf = null)
|
||||
{
|
||||
conf ??= new ConfigurationRoot(new List<IConfigurationProvider>()
|
||||
{
|
||||
new MemoryConfigurationProvider(new MemoryConfigurationSource()
|
||||
{
|
||||
InitialData = new[] {
|
||||
new KeyValuePair<string, string>("chains", "*"),
|
||||
new KeyValuePair<string, string>("network", "regtest")
|
||||
}
|
||||
})
|
||||
});
|
||||
var bootstrap = Startup.CreateBootstrap(conf);
|
||||
var services = new PluginServiceCollection(new ServiceCollection(), bootstrap);
|
||||
var plugins = new List<BaseBTCPayServerPlugin>() { new BitcoinPlugin() };
|
||||
#if ALTCOINS
|
||||
plugins.Add(new BTCPayServer.Plugins.Altcoins.AltcoinsPlugin());
|
||||
#endif
|
||||
foreach (var p in plugins)
|
||||
{
|
||||
p.Execute(services);
|
||||
}
|
||||
services.AddSingleton(services.BootstrapServices.GetRequiredService<SelectedChains>());
|
||||
services.AddSingleton(services.BootstrapServices.GetRequiredService<NBXplorerNetworkProvider>());
|
||||
services.AddSingleton(services.BootstrapServices.GetRequiredService<Logs>());
|
||||
services.AddSingleton(services.BootstrapServices.GetRequiredService<IConfiguration>());
|
||||
services.AddSingleton<BTCPayNetworkProvider>();
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
return serviceProvider.GetService<BTCPayNetworkProvider>();
|
||||
}
|
||||
public ILog TestLogs
|
||||
{
|
||||
@ -26,14 +82,15 @@ namespace BTCPayServer.Tests
|
||||
get;
|
||||
}
|
||||
public BTCPayServer.Logging.Logs BTCPayLogs { get; }
|
||||
public FuncLoggerFactory LoggerFactory { get; }
|
||||
|
||||
public ServerTester CreateServerTester([CallerMemberNameAttribute] string scope = null, bool newDb = false)
|
||||
{
|
||||
return new ServerTester(scope, newDb, TestLogs, TestLogProvider);
|
||||
return new ServerTester(scope, newDb, TestLogs, TestLogProvider, CreateNetworkProvider());
|
||||
}
|
||||
public SeleniumTester CreateSeleniumTester([CallerMemberNameAttribute] string scope = null, bool newDb = false)
|
||||
{
|
||||
return new SeleniumTester() { Server = new ServerTester(scope, newDb, TestLogs, TestLogProvider) };
|
||||
return new SeleniumTester() { Server = new ServerTester(scope, newDb, TestLogs, TestLogProvider, CreateNetworkProvider()) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ declare -A xpubs
|
||||
|
||||
printf "\n👛 Create descriptor wallets\n\n"
|
||||
for ((n=1;n<=3;n++)); do
|
||||
# Create descriptor wallets, surpress error output in case wallet already exists
|
||||
# Create descriptor wallets, suppress error output in case wallet already exists
|
||||
./docker-bitcoin-cli.sh -named createwallet wallet_name="${prefix}_part_${n}" descriptors=true > /dev/null 2>&1
|
||||
|
||||
# Collect xpubs
|
||||
@ -38,7 +38,7 @@ multisig_int_desc="{\"desc\": $internal_desc_sum, \"active\": true, \"internal\"
|
||||
|
||||
multisig_desc="[$multisig_ext_desc, $multisig_int_desc]"
|
||||
|
||||
# Create multisig wallet, surpress error output in case wallet already exists
|
||||
# Create multisig wallet, suppress error output in case wallet already exists
|
||||
|
||||
printf "\n🔐 Create multisig wallet\n"
|
||||
printf "\nExternal descriptor: $external_desc\n"
|
||||
|
@ -24,7 +24,7 @@ services:
|
||||
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"
|
||||
TEST_MERCHANTLND: "http://lnd:lnd@merchant_lnd:8080/"
|
||||
TEST_MERCHANTLND: "http://merchant_lnd:8080/"
|
||||
TESTS_INCONTAINER: "true"
|
||||
TESTS_SSHCONNECTION: "root@sshd:22"
|
||||
TESTS_SSHPASSWORD: ""
|
||||
@ -73,7 +73,7 @@ services:
|
||||
- "sshd_datadir:/root/.ssh"
|
||||
|
||||
devlnd:
|
||||
image: btcpayserver/bitcoin:25.0
|
||||
image: btcpayserver/bitcoin:26.0
|
||||
environment:
|
||||
BITCOIN_NETWORK: regtest
|
||||
BITCOIN_WALLETDIR: "/data/wallets"
|
||||
@ -99,7 +99,7 @@ services:
|
||||
custom:
|
||||
|
||||
nbxplorer:
|
||||
image: nicolasdorier/nbxplorer:2.3.63
|
||||
image: nicolasdorier/nbxplorer:2.4.0
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "32838:32838"
|
||||
@ -135,7 +135,7 @@ services:
|
||||
|
||||
bitcoind:
|
||||
restart: unless-stopped
|
||||
image: btcpayserver/bitcoin:25.0
|
||||
image: btcpayserver/bitcoin:26.0
|
||||
environment:
|
||||
BITCOIN_NETWORK: regtest
|
||||
BITCOIN_WALLETDIR: "/data/wallets"
|
||||
@ -224,7 +224,7 @@ services:
|
||||
- "5432"
|
||||
|
||||
merchant_lnd:
|
||||
image: btcpayserver/lnd:v0.16.4-beta-1
|
||||
image: btcpayserver/lnd:v0.17.2-beta
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
LND_CHAIN: "btc"
|
||||
@ -259,7 +259,7 @@ services:
|
||||
- bitcoind
|
||||
|
||||
customer_lnd:
|
||||
image: btcpayserver/lnd:v0.16.4-beta-1
|
||||
image: btcpayserver/lnd:v0.17.2-beta
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
LND_CHAIN: "btc"
|
||||
@ -307,7 +307,7 @@ services:
|
||||
- "torrcdir:/usr/local/etc/tor"
|
||||
- "tor_servicesdir:/var/lib/tor/hidden_services"
|
||||
monerod:
|
||||
image: btcpayserver/monero:0.17.0.0-amd64
|
||||
image: btcpayserver/monero:0.18.2.2-5
|
||||
restart: unless-stopped
|
||||
container_name: xmr_monerod
|
||||
entrypoint: sleep 999999
|
||||
@ -317,7 +317,7 @@ services:
|
||||
ports:
|
||||
- "18081:18081"
|
||||
monero_wallet:
|
||||
image: btcpayserver/monero:0.17.0.0-amd64
|
||||
image: btcpayserver/monero:0.18.2.2-5
|
||||
restart: unless-stopped
|
||||
container_name: xmr_wallet_rpc
|
||||
entrypoint: monero-wallet-rpc --testnet --rpc-bind-ip=0.0.0.0 --disable-rpc-login --confirm-external-bind --rpc-bind-port=18082 --non-interactive --trusted-daemon --daemon-address=monerod:18081 --wallet-file=/wallet/wallet.keys --password-file=/wallet/password --tx-notify="/bin/sh ./scripts/notifier.sh -k -X GET https://host.docker.internal:14142/monerolikedaemoncallback/tx?cryptoCode=xmr&hash=%s"
|
||||
@ -349,7 +349,7 @@ services:
|
||||
elementsd-liquid:
|
||||
restart: always
|
||||
container_name: btcpayserver_elementsd_liquid
|
||||
image: btcpayserver/elements:0.21.0.1
|
||||
image: btcpayserver/elements:0.21.0.2-4
|
||||
environment:
|
||||
ELEMENTS_CHAIN: elementsregtest
|
||||
ELEMENTS_EXTRA_ARGS: |
|
||||
@ -364,11 +364,9 @@ services:
|
||||
whitelist=0.0.0.0/0
|
||||
rpcallowip=0.0.0.0/0
|
||||
validatepegin=0
|
||||
initialfreecoins=210000000000000
|
||||
initialfreecoins=2100000000000000
|
||||
con_dyna_deploy_signal=1
|
||||
con_dyna_deploy_start=0
|
||||
con_nminerconfirmationwindow=1
|
||||
con_nrulechangeactivationthreshold=1
|
||||
con_dyna_deploy_start=10
|
||||
expose:
|
||||
- "19332"
|
||||
- "19444"
|
||||
|
@ -22,7 +22,7 @@ services:
|
||||
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"
|
||||
TEST_MERCHANTLND: "http://lnd:lnd@merchant_lnd:8080/"
|
||||
TEST_MERCHANTLND: "http://merchant_lnd:8080/"
|
||||
TESTS_INCONTAINER: "true"
|
||||
TESTS_SSHCONNECTION: "root@sshd:22"
|
||||
TESTS_SSHPASSWORD: ""
|
||||
@ -70,7 +70,7 @@ services:
|
||||
- "sshd_datadir:/root/.ssh"
|
||||
|
||||
devlnd:
|
||||
image: btcpayserver/bitcoin:25.0
|
||||
image: btcpayserver/bitcoin:26.0
|
||||
environment:
|
||||
BITCOIN_NETWORK: regtest
|
||||
BITCOIN_WALLETDIR: "/data/wallets"
|
||||
@ -96,7 +96,7 @@ services:
|
||||
custom:
|
||||
|
||||
nbxplorer:
|
||||
image: nicolasdorier/nbxplorer:2.3.63
|
||||
image: nicolasdorier/nbxplorer:2.4.0
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "32838:32838"
|
||||
@ -121,7 +121,7 @@ services:
|
||||
|
||||
bitcoind:
|
||||
restart: unless-stopped
|
||||
image: btcpayserver/bitcoin:25.0
|
||||
image: btcpayserver/bitcoin:26.0
|
||||
environment:
|
||||
BITCOIN_NETWORK: regtest
|
||||
BITCOIN_WALLETDIR: "/data/wallets"
|
||||
@ -211,7 +211,7 @@ services:
|
||||
- "5432"
|
||||
|
||||
merchant_lnd:
|
||||
image: btcpayserver/lnd:v0.16.4-beta-1
|
||||
image: btcpayserver/lnd:v0.17.2-beta
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
LND_CHAIN: "btc"
|
||||
@ -248,7 +248,7 @@ services:
|
||||
- bitcoind
|
||||
|
||||
customer_lnd:
|
||||
image: btcpayserver/lnd:v0.16.4-beta-1
|
||||
image: btcpayserver/lnd:v0.17.2-beta
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
LND_CHAIN: "btc"
|
||||
|
42
BTCPayServer/APDUVaultTransport.cs
Normal file
42
BTCPayServer/APDUVaultTransport.cs
Normal file
@ -0,0 +1,42 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using BTCPayServer.NTag424;
|
||||
using NBitcoin.DataEncoders;
|
||||
using System;
|
||||
using SocketIOClient;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public class APDUVaultTransport : IAPDUTransport
|
||||
{
|
||||
private readonly VaultClient _vaultClient;
|
||||
|
||||
public APDUVaultTransport(VaultClient vaultClient)
|
||||
{
|
||||
_vaultClient = vaultClient;
|
||||
}
|
||||
|
||||
|
||||
public async Task WaitForCard(CancellationToken cancellationToken)
|
||||
{
|
||||
await _vaultClient.SendVaultRequest("/wait-for-card", null, cancellationToken);
|
||||
}
|
||||
public async Task WaitForRemoved(CancellationToken cancellationToken)
|
||||
{
|
||||
await _vaultClient.SendVaultRequest("/wait-for-disconnected", null, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<NtagResponse> SendAPDU(byte[] apdu, CancellationToken cancellationToken)
|
||||
{
|
||||
var resp = await _vaultClient.SendVaultRequest("/",
|
||||
new JObject()
|
||||
{
|
||||
["apdu"] = Encoders.Hex.EncodeData(apdu)
|
||||
}, cancellationToken);
|
||||
var data = Encoders.Hex.DecodeData(resp["data"].Value<string>());
|
||||
return new NtagResponse(data, resp["status"].Value<ushort>());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Import Project="../Build/Version.csproj" Condition="Exists('../Build/Version.csproj')" />
|
||||
<Import Project="../Build/Common.csproj" />
|
||||
|
||||
@ -38,6 +38,7 @@
|
||||
|
||||
<ItemGroup Condition="'$(Altcoins)' != 'true'">
|
||||
<Content Remove="Services\Altcoins\**\*" />
|
||||
<Compile Remove="Plugins\Altcoins\**\*" />
|
||||
<Content Remove="Views\UIMoneroLikeStore\**\*" />
|
||||
<Content Remove="Views\UIZcashLikeStore\**\*" />
|
||||
<Content Remove="Views\Shared\Monero\**\*" />
|
||||
@ -45,29 +46,29 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BTCPayServer.NTag424" Version="1.0.18" />
|
||||
<PackageReference Include="YamlDotNet" Version="8.0.0" />
|
||||
<PackageReference Include="BIP78.Sender" Version="0.2.2" />
|
||||
<PackageReference Include="BTCPayServer.Hwi" Version="2.0.2" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.All" Version="1.4.31" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.All" Version="1.5.2" />
|
||||
<PackageReference Include="CsvHelper" Version="15.0.5" />
|
||||
<PackageReference Include="Dapper" Version="2.0.123" />
|
||||
<PackageReference Include="Dapper" Version="2.1.24" />
|
||||
<PackageReference Include="Fido2" Version="2.0.2" />
|
||||
<PackageReference Include="Fido2.AspNet" Version="2.0.2" />
|
||||
<PackageReference Include="HtmlSanitizer" Version="5.0.372" />
|
||||
<PackageReference Include="LNURL" Version="0.0.34" />
|
||||
<PackageReference Include="MailKit" Version="3.3.0" />
|
||||
<PackageReference Include="BTCPayServer.NETCore.Plugins.Mvc" Version="1.4.4" />
|
||||
<PackageReference Include="QRCoder" Version="1.4.3" />
|
||||
<PackageReference Include="System.IO.Pipelines" Version="6.0.3" />
|
||||
<PackageReference Include="System.IO.Pipelines" Version="8.0.0" />
|
||||
<PackageReference Include="NBitpayClient" Version="1.0.0.39" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="NicolasDorier.CommandLine" Version="2.0.0" />
|
||||
<PackageReference Include="NicolasDorier.CommandLine.Configuration" Version="2.0.0" />
|
||||
<PackageReference Include="NicolasDorier.RateLimits" Version="1.2.3" />
|
||||
<PackageReference Include="Serilog" Version="2.9.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="3.2.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
|
||||
<PackageReference Include="SSH.NET" Version="2020.0.2" />
|
||||
<PackageReference Include="Serilog" Version="3.1.1" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.1-dev-00968" />
|
||||
<PackageReference Include="SSH.NET" Version="2023.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -76,8 +77,8 @@
|
||||
<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="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.9" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.9" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -137,6 +138,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Watch Include="Views\**\*.*"></Watch>
|
||||
<Watch Remove="Views\Shared\LocalhostBrowserSupport.cshtml" />
|
||||
<Watch Remove="Views\UIAccount\CheatPermissions.cshtml" />
|
||||
<Watch Remove="Views\UIReports\StoreReports.cshtml" />
|
||||
<Content Update="Views\UIApps\_ViewImports.cshtml">
|
||||
|
@ -1,4 +1,3 @@
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace BTCPayServer.Blazor
|
||||
|
@ -1,4 +1,3 @@
|
||||
@using System.Security.Claims
|
||||
@using BTCPayServer.Abstractions.Contracts;
|
||||
@using BTCPayServer.Configuration;
|
||||
@using BTCPayServer.Data;
|
||||
@ -86,7 +85,8 @@
|
||||
}
|
||||
|
||||
public void Dispose() => _EventAggregatorListener?.Dispose();
|
||||
string SeenCount(int? count)
|
||||
|
||||
static string SeenCount(int? count)
|
||||
{
|
||||
if (count is not int c)
|
||||
return "0";
|
||||
@ -94,12 +94,14 @@
|
||||
return $"{NotificationManager.MaxUnseen - 1}+";
|
||||
return c.ToString();
|
||||
}
|
||||
|
||||
void UpdateState((List<NotificationViewModel> Items, int? Count) res)
|
||||
{
|
||||
UnseenCount = SeenCount(res.Count);
|
||||
Last5 = res.Items;
|
||||
}
|
||||
protected async override Task OnParametersSetAsync()
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (await GetUserId() is string userId)
|
||||
{
|
||||
@ -117,15 +119,16 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
async Task<string>
|
||||
GetUserId()
|
||||
|
||||
async Task<string> GetUserId()
|
||||
{
|
||||
var state = await _AuthenticationStateProvider.GetAuthenticationStateAsync();
|
||||
if (!state.User.Identity.IsAuthenticated)
|
||||
return null;
|
||||
return _UserManager.GetUserId(state.User);
|
||||
}
|
||||
public async Task MarkAllAsSeen()
|
||||
|
||||
private async Task MarkAllAsSeen()
|
||||
{
|
||||
if (await GetUserId() is string userId)
|
||||
{
|
||||
@ -133,6 +136,7 @@
|
||||
UnseenCount = "0";
|
||||
}
|
||||
}
|
||||
|
||||
private static string NotificationIcon(string type)
|
||||
{
|
||||
return type switch
|
||||
|
@ -13,14 +13,20 @@ namespace BTCPayServer
|
||||
{
|
||||
return Regex.Match(color, Pattern).Success;
|
||||
}
|
||||
public string TextColor(string bgColor)
|
||||
|
||||
public Color TextColor(Color bg)
|
||||
{
|
||||
int nThreshold = 105;
|
||||
var bg = ColorTranslator.FromHtml(bgColor);
|
||||
int bgDelta = Convert.ToInt32((bg.R * 0.299) + (bg.G * 0.587) + (bg.B * 0.114));
|
||||
Color color = (255 - bgDelta < nThreshold) ? Color.Black : Color.White;
|
||||
int bgDelta = Convert.ToInt32(bg.R * 0.299 + bg.G * 0.587 + bg.B * 0.114);
|
||||
return 255 - bgDelta < nThreshold ? Color.Black : Color.White;
|
||||
}
|
||||
|
||||
public string TextColor(string bg)
|
||||
{
|
||||
var color = TextColor(FromHtml(bg));
|
||||
return ColorTranslator.ToHtml(color).ToLowerInvariant();
|
||||
}
|
||||
|
||||
// Borrowed from https://github.com/ManageIQ/guides/blob/master/labels.md
|
||||
public static readonly ColorPalette Default = new ColorPalette(new string[] {
|
||||
"#fbca04",
|
||||
@ -31,6 +37,7 @@ namespace BTCPayServer
|
||||
"#cdcdcd",
|
||||
"#cc317c",
|
||||
});
|
||||
|
||||
private ColorPalette(string[] labels)
|
||||
{
|
||||
Labels = labels;
|
||||
@ -98,5 +105,10 @@ namespace BTCPayServer
|
||||
var color = AdjustBrightness(ColorTranslator.FromHtml(html), correctionFactor);
|
||||
return ColorTranslator.ToHtml(color);
|
||||
}
|
||||
|
||||
public Color FromHtml(string html)
|
||||
{
|
||||
return ColorTranslator.FromHtml(html);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
67
BTCPayServer/Components/InvoiceStatus/Default.cshtml
Normal file
67
BTCPayServer/Components/InvoiceStatus/Default.cshtml
Normal file
@ -0,0 +1,67 @@
|
||||
@using BTCPayServer.Services.Invoices
|
||||
@using BTCPayServer.Abstractions.Extensions
|
||||
@model BTCPayServer.Components.InvoiceStatus.InvoiceStatusViewModel
|
||||
@inject PaymentMethodHandlerDictionary PaymentMethodHandlerDictionary
|
||||
|
||||
@{
|
||||
var state = Model.State.ToString();
|
||||
var badgeClass = Model.State.Status.ToModernStatus().ToString().ToLower();
|
||||
var canMark = !string.IsNullOrEmpty(Model.InvoiceId) && (Model.State.CanMarkComplete() || Model.State.CanMarkInvalid());
|
||||
}
|
||||
<div class="d-inline-flex align-items-center gap-2">
|
||||
@if (Model.IsArchived)
|
||||
{
|
||||
<span class="badge bg-warning">archived</span>
|
||||
}
|
||||
<div class="badge badge-@badgeClass" data-invoice-state-badge="@Model.InvoiceId">
|
||||
@if (canMark)
|
||||
{
|
||||
<span class="dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
@state
|
||||
</span>
|
||||
<div class="dropdown-menu">
|
||||
@if (Model.State.CanMarkInvalid())
|
||||
{
|
||||
<button type="button" class="dropdown-item lh-base" data-invoice-id="@Model.InvoiceId" data-new-state="invalid">
|
||||
Mark as invalid
|
||||
</button>
|
||||
}
|
||||
@if (Model.State.CanMarkComplete())
|
||||
{
|
||||
<button type="button" class="dropdown-item lh-base" data-invoice-id="@Model.InvoiceId" data-new-state="settled">
|
||||
Mark as settled
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@state
|
||||
}
|
||||
</div>
|
||||
@if (Model.Payments != null)
|
||||
{
|
||||
foreach (var paymentMethodId in Model.Payments.Select(payment => payment.GetPaymentMethodId()).Distinct())
|
||||
{
|
||||
var image = PaymentMethodHandlerDictionary[paymentMethodId]?.GetCryptoImage(paymentMethodId);
|
||||
var badge = paymentMethodId.PaymentType.GetBadge();
|
||||
if (!string.IsNullOrEmpty(image) || !string.IsNullOrEmpty(badge))
|
||||
{
|
||||
<span class="d-inline-flex align-items-center gap-1">
|
||||
@if (!string.IsNullOrEmpty(image))
|
||||
{
|
||||
<img src="@Context.Request.GetRelativePathOrAbsolute(image)" alt="@paymentMethodId.PaymentType.ToString()" style="height:1.5em" />
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(badge))
|
||||
{
|
||||
@badge
|
||||
}
|
||||
</span>
|
||||
}
|
||||
}
|
||||
}
|
||||
@if (Model.HasRefund)
|
||||
{
|
||||
<span class="badge bg-warning">Refund</span>
|
||||
}
|
||||
</div>
|
22
BTCPayServer/Components/InvoiceStatus/InvoiceStatus.cs
Normal file
22
BTCPayServer/Components/InvoiceStatus/InvoiceStatus.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System.Collections.Generic;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BTCPayServer.Components.InvoiceStatus
|
||||
{
|
||||
public class InvoiceStatus : ViewComponent
|
||||
{
|
||||
public IViewComponentResult Invoke(InvoiceState state, List<PaymentEntity> payments, string invoiceId, bool isArchived = false, bool hasRefund = false)
|
||||
{
|
||||
var vm = new InvoiceStatusViewModel
|
||||
{
|
||||
State = state,
|
||||
Payments = payments,
|
||||
InvoiceId = invoiceId,
|
||||
IsArchived = isArchived,
|
||||
HasRefund = hasRefund
|
||||
};
|
||||
return View(vm);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
|
||||
namespace BTCPayServer.Components.InvoiceStatus
|
||||
{
|
||||
public class InvoiceStatusViewModel
|
||||
{
|
||||
public InvoiceState State { get; set; }
|
||||
public List<PaymentEntity> Payments { get; set; }
|
||||
public string InvoiceId { get; set; }
|
||||
public bool IsArchived { get; set; }
|
||||
public bool HasRefund { get; set; }
|
||||
}
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
@using BTCPayServer.Services
|
||||
@using BTCPayServer.Abstractions.Contracts
|
||||
@using BTCPayServer.Abstractions.Extensions
|
||||
@using BTCPayServer.Abstractions.TagHelpers
|
||||
@inject ThemeSettings Theme
|
||||
@inject IFileService FileService
|
||||
@model BTCPayServer.Components.MainLogo.MainLogoViewModel
|
||||
|
@ -4,10 +4,10 @@
|
||||
@using BTCPayServer.Views.Manage
|
||||
@using BTCPayServer.Views.PaymentRequest
|
||||
@using BTCPayServer.Views.Wallets
|
||||
@using BTCPayServer.Abstractions.Extensions
|
||||
@using BTCPayServer.Client
|
||||
@using BTCPayServer.Components.ThemeSwitch
|
||||
@using BTCPayServer.Components.UIExtensionPoint
|
||||
@using BTCPayServer.Plugins
|
||||
@using BTCPayServer.Services
|
||||
@using BTCPayServer.Views.Apps
|
||||
@using BTCPayServer.Views.CustodianAccounts
|
||||
@ -16,6 +16,7 @@
|
||||
@inject SignInManager<ApplicationUser> SignInManager
|
||||
@inject PoliciesSettings PoliciesSettings
|
||||
@inject ThemeSettings Theme
|
||||
@inject PluginService PluginService
|
||||
|
||||
@model BTCPayServer.Components.MainNav.MainNavViewModel
|
||||
|
||||
@ -134,25 +135,25 @@
|
||||
<span>Invoices</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" permission="@Policies.CanViewInvoices">
|
||||
<li class="nav-item" permission="@Policies.CanViewReports">
|
||||
<a asp-area="" asp-controller="UIReports" asp-action="StoreReports" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.IsActivePage(StoreNavPages.Reporting)" id="SectionNav-Reporting">
|
||||
<vc:icon symbol="invoice" />
|
||||
<span>Reporting</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" permission="@Policies.CanModifyStoreSettings">
|
||||
<li class="nav-item" permission="@Policies.CanViewPaymentRequests">
|
||||
<a asp-area="" asp-controller="UIPaymentRequest" asp-action="GetPaymentRequests" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.IsActiveCategory(typeof(PaymentRequestsNavPages))" id="StoreNav-PaymentRequests">
|
||||
<vc:icon symbol="payment-requests"/>
|
||||
<span>Requests</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" permission="@Policies.CanViewStoreSettings">
|
||||
<li class="nav-item" permission="@Policies.CanViewPullPayments">
|
||||
<a asp-area="" asp-controller="UIStorePullPayments" asp-action="PullPayments" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.IsActivePage(StoreNavPages.PullPayments)" id="StoreNav-PullPayments">
|
||||
<vc:icon symbol="pull-payments"/>
|
||||
<span>Pull Payments</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" permission="@Policies.CanModifyStoreSettings">
|
||||
<li class="nav-item" permission="@Policies.CanViewPayouts">
|
||||
<a asp-area=""
|
||||
asp-controller="UIStorePullPayments" asp-action="Payouts"
|
||||
asp-route-pullPaymentId=""
|
||||
@ -186,7 +187,14 @@
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item" permission="@Policies.CanModifyServerSettings">
|
||||
<a asp-area="" asp-controller="UIServer" asp-action="ListPlugins" class="nav-link @ViewData.IsActivePage(ServerNavPages.Plugins)" id="Nav-ManagePlugins">
|
||||
<vc:icon symbol="manage-plugins"/>
|
||||
@if (PluginService.GetDisabledPlugins().Any())
|
||||
{
|
||||
<span class="me-2 btcpay-status btcpay-status--disabled"></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<vc:icon symbol="manage-plugins" />
|
||||
}
|
||||
<span>Manage Plugins</span>
|
||||
</a>
|
||||
</li>
|
||||
@ -250,7 +258,7 @@
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item dropup">
|
||||
<a class="nav-link @ViewData.IsActiveCategory(typeof(ManageNavPages))" role="button" data-bs-toggle="dropdown" aria-expanded="false" id="Nav-Account">
|
||||
<a class="nav-link @ViewData.IsActiveCategory(typeof(ManageNavPages))" role="button" data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-expanded="false" id="Nav-Account">
|
||||
<vc:icon symbol="account"/>
|
||||
<span>Account</span>
|
||||
</a>
|
||||
@ -265,7 +273,7 @@
|
||||
@if (!Theme.CustomTheme)
|
||||
{
|
||||
<li class="py-1 px-3">
|
||||
<vc:theme-switch css-class="nav-link pb-0"/>
|
||||
<vc:theme-switch css-class="w-100 pt-2"/>
|
||||
</li>
|
||||
}
|
||||
<li class="py-1 px-3">
|
||||
|
@ -83,7 +83,7 @@
|
||||
|
||||
if (Model.PaginationQuery != null)
|
||||
{
|
||||
// merge both, prefering the `query` properties in case of duplicate keys
|
||||
// merge both, preferring the `query` properties in case of duplicate keys
|
||||
query = query.Concat(Model.PaginationQuery)
|
||||
.GroupBy(e => e.Key)
|
||||
.ToDictionary(g => g.Key, g => g.First().Value);
|
||||
|
@ -11,13 +11,14 @@ namespace BTCPayServer.Components.QRCode
|
||||
{
|
||||
private static QRCodeGenerator _qrGenerator = new();
|
||||
|
||||
public IViewComponentResult Invoke(string data)
|
||||
public IViewComponentResult Invoke(string data, int size=256)
|
||||
{
|
||||
var qrCodeData = _qrGenerator.CreateQrCode(data, QRCodeGenerator.ECCLevel.Q);
|
||||
var qrCode = new PngByteQRCode(qrCodeData);
|
||||
var bytes = qrCode.GetGraphic(5, new byte[] { 0, 0, 0, 255 }, new byte[] { 0xf5, 0xf5, 0xf7, 255 });
|
||||
var b64 = Convert.ToBase64String(bytes);
|
||||
return new HtmlContentViewComponentResult(new HtmlString($"<img style=\"image-rendering:pixelated;image-rendering:-moz-crisp-edges;min-width:256px;min-height:256px\" src=\"data:image/png;base64,{b64}\" class=\"qr-code\" />"));
|
||||
return new HtmlContentViewComponentResult(new HtmlString(
|
||||
$"<img style=\"image-rendering:pixelated;image-rendering:-moz-crisp-edges;min-width:{size}px;min-height:{size}px\" src=\"data:image/png;base64,{b64}\" class=\"qr-code\" />"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ public class StoreLightningBalance : ViewComponent
|
||||
}
|
||||
if (existing.IsInternalNode && _lightningNetworkOptions.Value.InternalLightningByCryptoCode.TryGetValue(cryptoCode, out var internalLightningNode))
|
||||
{
|
||||
return _lightningClientFactory.Create(internalLightningNode, network);
|
||||
return internalLightningNode;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -55,7 +55,8 @@ public class StoreNumbers : ViewComponent
|
||||
.Where(p => p.PullPaymentData.StoreId == vm.Store.Id && !p.PullPaymentData.Archived && p.State == PayoutState.AwaitingApproval)
|
||||
.CountAsync();
|
||||
vm.RefundsIssued = await ctx.Invoices
|
||||
.Where(i => i.StoreData.Id == vm.Store.Id && !i.Archived && i.CurrentRefundId != null && i.Created >= offset)
|
||||
.Where(i => i.StoreData.Id == vm.Store.Id && !i.Archived && i.Created >= offset)
|
||||
.SelectMany(i => i.Refunds)
|
||||
.CountAsync();
|
||||
|
||||
return View(vm);
|
||||
|
@ -52,41 +52,8 @@
|
||||
<a asp-controller="UIInvoice" asp-action="Invoice" asp-route-invoiceId="@invoice.InvoiceId" class="text-break">@invoice.InvoiceId</a>
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
@if (invoice.Details.Archived)
|
||||
{
|
||||
<span class="badge bg-warning">archived</span>
|
||||
}
|
||||
<span class="badge badge-@invoice.Status.Status.ToModernStatus().ToString().ToLower()">
|
||||
@invoice.Status.Status.ToModernStatus().ToString()
|
||||
@if (invoice.Status.ExceptionStatus != InvoiceExceptionStatus.None)
|
||||
{
|
||||
@($"({invoice.Status.ExceptionStatus.ToString()})")
|
||||
}
|
||||
</span>
|
||||
@foreach (var paymentMethodId in invoice.Details.Payments.Select(payment => payment.GetPaymentMethodId()).Distinct())
|
||||
{
|
||||
var image = PaymentMethodHandlerDictionary[paymentMethodId]?.GetCryptoImage(paymentMethodId);
|
||||
var badge = paymentMethodId.PaymentType.GetBadge();
|
||||
if (!string.IsNullOrEmpty(image) || !string.IsNullOrEmpty(badge))
|
||||
{
|
||||
<span class="d-inline-flex align-items-center gap-1">
|
||||
@if (!string.IsNullOrEmpty(image))
|
||||
{
|
||||
<img src="@Context.Request.GetRelativePathOrAbsolute(image)" alt="@paymentMethodId.PaymentType.ToString()" style="height:1.5em" />
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(badge))
|
||||
{
|
||||
@badge
|
||||
}
|
||||
</span>
|
||||
}
|
||||
}
|
||||
@if (invoice.HasRefund)
|
||||
{
|
||||
<span class="badge bg-warning">Refund</span>
|
||||
}
|
||||
</div>
|
||||
<vc:invoice-status state="invoice.Status" payments="invoice.Details.Payments" invoice-id="@invoice.InvoiceId"
|
||||
is-archived="invoice.Details.Archived" has-refund="invoice.HasRefund" />
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span data-sensitive>@DisplayFormatter.Currency(invoice.Amount, invoice.Currency)</span>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user