btcpayserver/BTCPayServer.Tests/TestAccount.cs

694 lines
29 KiB
C#
Raw Normal View History

2020-06-29 04:44:35 +02:00
using System;
2020-11-13 08:28:15 +01:00
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using System.IO;
2020-06-28 10:55:27 +02:00
using System.Linq;
2020-03-29 17:28:22 +02:00
using System.Net.Http;
2017-09-13 08:47:34 +02:00
using System.Text;
2020-11-13 08:28:15 +01:00
using System.Threading;
2017-09-13 08:47:34 +02:00
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Extensions;
2021-12-31 08:59:02 +01:00
using BTCPayServer.BIP78.Sender;
2020-03-16 08:36:55 +01:00
using BTCPayServer.Client;
using BTCPayServer.Client.Models;
2020-06-28 10:55:27 +02:00
using BTCPayServer.Controllers;
using BTCPayServer.Data;
2020-04-07 12:32:30 +02:00
using BTCPayServer.Events;
2020-06-28 10:55:27 +02:00
using BTCPayServer.Lightning;
using BTCPayServer.Lightning.CLightning;
using BTCPayServer.Models.AccountViewModels;
using BTCPayServer.Models.StoreViewModels;
2021-12-31 08:59:02 +01:00
using BTCPayServer.Payments.Lightning;
using BTCPayServer.Payments.PayJoin.Sender;
2020-03-29 17:28:22 +02:00
using BTCPayServer.Services;
using BTCPayServer.Services.Stores;
using BTCPayServer.Services.Wallets;
2020-06-28 10:55:27 +02:00
using BTCPayServer.Tests.Logging;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
2020-12-11 05:41:39 +01:00
using Microsoft.CodeAnalysis.Operations;
using Microsoft.EntityFrameworkCore;
2020-06-28 10:55:27 +02:00
using NBitcoin;
2020-03-29 17:28:22 +02:00
using NBitcoin.Payment;
2020-06-28 10:55:27 +02:00
using NBitpayClient;
using NBXplorer.DerivationStrategy;
using NBXplorer.Models;
2020-11-13 08:28:15 +01:00
using Newtonsoft.Json;
2020-03-29 17:28:22 +02:00
using Newtonsoft.Json.Linq;
using Npgsql;
2020-06-28 10:55:27 +02:00
using Xunit;
2020-11-13 08:28:15 +01:00
using Xunit.Sdk;
2017-09-13 08:47:34 +02:00
namespace BTCPayServer.Tests
{
public class TestAccount
{
readonly ServerTester parent;
public string LNAddress;
2020-03-29 17:28:22 +02:00
public TestAccount(ServerTester parent)
{
this.parent = parent;
BitPay = new Bitpay(new Key(), parent.PayTester.ServerUri);
}
2017-09-13 08:47:34 +02:00
2020-01-06 13:57:32 +01:00
public void GrantAccess(bool isAdmin = false)
{
2020-01-06 13:57:32 +01:00
GrantAccessAsync(isAdmin).GetAwaiter().GetResult();
}
2020-03-19 11:11:15 +01:00
public async Task MakeAdmin(bool isAdmin = true)
2019-10-12 13:35:30 +02:00
{
var userManager = parent.PayTester.GetService<UserManager<ApplicationUser>>();
var u = await userManager.FindByIdAsync(UserId);
2020-03-19 11:11:15 +01:00
if (isAdmin)
await userManager.AddToRoleAsync(u, Roles.ServerAdmin);
else
await userManager.RemoveFromRoleAsync(u, Roles.ServerAdmin);
IsAdmin = true;
2019-10-12 13:35:30 +02:00
}
2020-03-25 16:57:54 +01:00
public Task<BTCPayServerClient> CreateClient()
2020-03-20 17:14:47 +01:00
{
2020-03-29 17:28:22 +02:00
return Task.FromResult(new BTCPayServerClient(parent.PayTester.ServerUri, RegisterDetails.Email,
RegisterDetails.Password));
2020-03-20 17:14:47 +01:00
}
2020-03-16 08:36:55 +01:00
public async Task<BTCPayServerClient> CreateClient(params string[] permissions)
{
2022-01-07 04:32:00 +01:00
var manageController = parent.PayTester.GetController<UIManageController>(UserId, StoreId, IsAdmin);
2024-01-18 01:47:39 +01:00
Assert.IsType<RedirectToActionResult>(await manageController.AddApiKey(
2022-01-07 04:32:00 +01:00
new UIManageController.AddApiKeyViewModel()
2020-03-16 08:36:55 +01:00
{
2020-04-22 15:12:38 +02:00
PermissionValues = permissions.Select(s =>
2020-03-23 14:17:17 +01:00
{
2020-04-22 15:12:38 +02:00
Permission.TryParse(s, out var p);
return p;
}).GroupBy(permission => permission.Policy).Select(p =>
{
var stores = p.Where(permission => !string.IsNullOrEmpty(permission.Scope))
.Select(permission => permission.Scope).ToList();
2022-01-07 04:32:00 +01:00
return new UIManageController.AddApiKeyViewModel.PermissionValueItem()
2020-04-22 15:12:38 +02:00
{
Permission = p.Key,
Forbidden = false,
2022-01-07 04:32:00 +01:00
StoreMode = stores.Any() ? UIManageController.AddApiKeyViewModel.ApiKeyStoreMode.Specific : UIManageController.AddApiKeyViewModel.ApiKeyStoreMode.AllStores,
2020-04-22 15:12:38 +02:00
SpecificStores = stores,
Value = true
};
2020-03-23 14:17:17 +01:00
}).ToList()
2020-03-16 08:36:55 +01:00
}));
var statusMessage = manageController.TempData.GetStatusMessageModel();
Assert.NotNull(statusMessage);
2020-04-10 09:06:09 +02:00
var str = "<code class='alert-link'>";
var apiKey = statusMessage.Html.Substring(statusMessage.Html.IndexOf(str) + str.Length);
2020-03-16 08:36:55 +01:00
apiKey = apiKey.Substring(0, apiKey.IndexOf("</code>"));
return new BTCPayServerClient(parent.PayTester.ServerUri, apiKey);
}
2020-01-06 13:57:32 +01:00
public void Register(bool isAdmin = false)
{
2020-01-06 13:57:32 +01:00
RegisterAsync(isAdmin).GetAwaiter().GetResult();
}
2020-03-29 17:28:22 +02:00
2020-01-06 13:57:32 +01:00
public async Task GrantAccessAsync(bool isAdmin = false)
{
2020-01-06 13:57:32 +01:00
await RegisterAsync(isAdmin);
await CreateStoreAsync();
2022-01-07 04:32:00 +01:00
var store = GetController<UIStoresController>();
var pairingCode = BitPay.RequestClientAuthorization("test", Facade.Merchant);
Assert.IsType<ViewResult>(await store.RequestPairing(pairingCode.ToString()));
await store.Pair(pairingCode.ToString(), StoreId);
}
2020-03-27 06:17:31 +01:00
public BTCPayServerClient CreateClientFromAPIKey(string apiKey)
{
return new BTCPayServerClient(parent.PayTester.ServerUri, apiKey);
}
public void CreateStore()
{
CreateStoreAsync().GetAwaiter().GetResult();
}
public async Task SetNetworkFeeMode(NetworkFeeMode mode)
2019-01-06 07:04:30 +01:00
{
await ModifyPayment(payment =>
2019-01-06 07:04:30 +01:00
{
payment.NetworkFeeMode = mode;
2019-01-06 07:04:30 +01:00
});
}
2020-03-29 17:28:22 +02:00
public async Task ModifyPayment(Action<GeneralSettingsViewModel> modify)
{
2022-01-07 04:32:00 +01:00
var storeController = GetController<UIStoresController>();
var response = storeController.GeneralSettings();
GeneralSettingsViewModel settings = (GeneralSettingsViewModel)((ViewResult)response).Model;
modify(settings);
await storeController.GeneralSettings(settings);
}
2021-12-31 08:59:02 +01:00
public async Task ModifyWalletSettings(Action<WalletSettingsViewModel> modify)
{
2022-01-07 04:32:00 +01:00
var storeController = GetController<UIStoresController>();
var response = await storeController.WalletSettings(StoreId, "BTC");
WalletSettingsViewModel walletSettings = (WalletSettingsViewModel)((ViewResult)response).Model;
modify(walletSettings);
storeController.UpdateWalletSettings(walletSettings).GetAwaiter().GetResult();
}
2021-12-31 08:59:02 +01:00
public async Task ModifyOnchainPaymentSettings(Action<WalletSettingsViewModel> modify)
{
2022-01-07 04:32:00 +01:00
var storeController = GetController<UIStoresController>();
var response = await storeController.WalletSettings(StoreId, "BTC");
WalletSettingsViewModel walletSettings = (WalletSettingsViewModel)((ViewResult)response).Model;
modify(walletSettings);
storeController.UpdatePaymentSettings(walletSettings).GetAwaiter().GetResult();
}
public T GetController<T>(bool setImplicitStore = true) where T : Controller
2018-04-03 09:53:55 +02:00
{
2019-10-12 13:35:30 +02:00
var controller = parent.PayTester.GetController<T>(UserId, setImplicitStore ? StoreId : null, IsAdmin);
return controller;
2018-04-03 09:53:55 +02:00
}
public async Task CreateStoreAsync()
{
if (UserId is null)
{
await RegisterAsync();
}
2022-01-07 04:32:00 +01:00
var store = GetController<UIUserStoresController>();
await store.CreateStore(new CreateStoreViewModel { Name = "Test Store", PreferredExchange = "coingecko" });
StoreId = store.CreatedStoreId;
2018-07-19 12:31:17 +02:00
parent.Stores.Add(StoreId);
}
public BTCPayNetwork SupportedNetwork { get; set; }
2020-04-07 12:32:30 +02:00
public WalletId RegisterDerivationScheme(string crytoCode, ScriptPubKeyType segwit = ScriptPubKeyType.Legacy, bool importKeysToNBX = false)
{
return RegisterDerivationSchemeAsync(crytoCode, segwit, importKeysToNBX).GetAwaiter().GetResult();
}
2020-03-29 17:28:22 +02:00
2020-04-07 12:32:30 +02:00
public async Task<WalletId> RegisterDerivationSchemeAsync(string cryptoCode, ScriptPubKeyType segwit = ScriptPubKeyType.Legacy,
2021-03-24 13:33:49 +01:00
bool importKeysToNBX = false, bool importsKeysToBitcoinCore = false)
{
if (StoreId is null)
await CreateStoreAsync();
SupportedNetwork = parent.NetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
2022-01-07 04:32:00 +01:00
var store = parent.PayTester.GetController<UIStoresController>(UserId, StoreId, true);
2021-06-17 09:02:47 +02:00
var generateRequest = new WalletSetupRequest
{
2020-04-07 12:32:30 +02:00
ScriptPubKeyType = segwit,
SavePrivateKeys = importKeysToNBX,
2021-03-24 13:33:49 +01:00
ImportKeysToRPC = importsKeysToBitcoinCore
2021-06-17 09:02:47 +02:00
};
await store.GenerateWallet(StoreId, cryptoCode, WalletSetupMethod.HotWallet, generateRequest);
2021-06-17 11:27:17 +02:00
Assert.NotNull(store.GenerateWalletResponse);
GenerateWalletResponseV = store.GenerateWalletResponse;
return new WalletId(StoreId, cryptoCode);
}
public GenerateWalletResponse GenerateWalletResponseV { get; set; }
public DerivationStrategyBase DerivationScheme
{
get => GenerateWalletResponseV.DerivationScheme;
}
2017-11-06 09:31:02 +01:00
2023-04-25 01:51:38 +02:00
public void SetLNUrl(string cryptoCode, bool activated)
{
var lnSettingsVm = GetController<UIStoresController>().LightningSettings(StoreId, cryptoCode).AssertViewModel<LightningSettingsViewModel>();
lnSettingsVm.LNURLEnabled = activated;
Assert.IsType<RedirectToActionResult>(GetController<UIStoresController>().LightningSettings(lnSettingsVm).Result);
}
2020-01-06 13:57:32 +01:00
private async Task RegisterAsync(bool isAdmin = false)
{
2022-01-07 04:32:00 +01:00
var account = parent.PayTester.GetController<UIAccountController>();
RegisterDetails = new RegisterViewModel()
{
Email = Utils.GenerateEmail(),
ConfirmPassword = Password,
Password = Password,
2020-01-06 13:57:32 +01:00
IsAdmin = isAdmin
};
await account.Register(RegisterDetails);
//this addresses an obscure issue where LockSubscription is unintentionally set to "true",
//resulting in a large number of tests failing.
2021-12-31 08:59:02 +01:00
if (account.RegisteredUserId == null)
{
var settings = parent.PayTester.GetService<SettingsRepository>();
var policies = await settings.GetSettingAsync<PoliciesSettings>() ?? new PoliciesSettings();
policies.LockSubscription = false;
await account.Register(RegisterDetails);
}
TestLogs.LogInformation($"UserId: {account.RegisteredUserId} Password: {Password}");
UserId = account.RegisteredUserId;
Email = RegisterDetails.Email;
IsAdmin = account.RegisteredAdmin;
}
public string Password { get; set; } = "Kitten0@";
2017-09-13 08:47:34 +02:00
2020-03-29 17:28:22 +02:00
public RegisterViewModel RegisterDetails { get; set; }
public Bitpay BitPay
{
2020-03-29 17:28:22 +02:00
get;
set;
}
2020-03-29 17:28:22 +02:00
public string UserId
{
2020-03-29 17:28:22 +02:00
get;
set;
}
public string Email
{
get;
set;
}
2017-09-13 08:47:34 +02:00
public string StoreId
{
2020-03-29 17:28:22 +02:00
get;
set;
}
2020-03-29 17:28:22 +02:00
2019-10-12 13:35:30 +02:00
public bool IsAdmin { get; internal set; }
2018-07-01 09:10:17 +02:00
public void RegisterLightningNode(string cryptoCode, string connectionType = null, bool isMerchant = true)
{
RegisterLightningNodeAsync(cryptoCode, connectionType, isMerchant).GetAwaiter().GetResult();
}
public Task RegisterLightningNodeAsync(string cryptoCode, bool isMerchant = true, string storeId = null)
{
return RegisterLightningNodeAsync(cryptoCode, null, isMerchant, storeId);
}
public async Task RegisterLightningNodeAsync(string cryptoCode, string connectionType, bool isMerchant = true, string storeId = null)
{
2022-01-07 04:32:00 +01:00
var storeController = GetController<UIStoresController>();
var connectionString = parent.GetLightningConnectionString(connectionType, isMerchant);
var nodeType = connectionString == LightningSupportedPaymentMethod.InternalNode ? LightningNodeType.Internal : LightningNodeType.Custom;
var vm = new LightningNodeViewModel { ConnectionString = connectionString, LightningNodeType = nodeType, SkipPortTest = true };
await storeController.SetupLightningNode(storeId ?? StoreId,
vm, "save", cryptoCode);
if (storeController.ModelState.ErrorCount != 0)
Assert.Fail(storeController.ModelState.FirstOrDefault().Value.Errors[0].ErrorMessage);
}
public async Task RegisterInternalLightningNodeAsync(string cryptoCode, string storeId = null)
{
2022-01-07 04:32:00 +01:00
var storeController = GetController<UIStoresController>();
var vm = new LightningNodeViewModel { ConnectionString = "", LightningNodeType = LightningNodeType.Internal, SkipPortTest = true };
2021-04-19 16:21:50 +02:00
await storeController.SetupLightningNode(storeId ?? StoreId,
vm, "save", cryptoCode);
if (storeController.ModelState.ErrorCount != 0)
Assert.Fail(storeController.ModelState.FirstOrDefault().Value.Errors[0].ErrorMessage);
}
2020-03-29 17:28:22 +02:00
public async Task<Coin> ReceiveUTXO(Money value, BTCPayNetwork network = null)
2020-03-29 17:28:22 +02:00
{
network ??= SupportedNetwork;
2020-03-29 17:28:22 +02:00
var cashCow = parent.ExplorerNode;
var btcPayWallet = parent.PayTester.GetService<BTCPayWalletProvider>().GetWallet(network);
var address = (await btcPayWallet.ReserveAddressAsync(this.DerivationScheme)).Address;
2020-04-07 12:32:30 +02:00
await parent.WaitForEvent<NewOnChainTransactionEvent>(async () =>
{
await cashCow.SendToAddressAsync(address, value);
});
2020-04-08 10:10:42 +02:00
int i = 0;
2020-06-28 10:55:27 +02:00
while (i < 30)
2020-04-08 10:10:42 +02:00
{
var result = (await btcPayWallet.GetUnspentCoins(DerivationScheme))
.FirstOrDefault(c => c.ScriptPubKey == address.ScriptPubKey)?.Coin;
if (result != null)
{
return result;
}
await Task.Delay(1000);
i++;
}
Assert.False(true);
2020-04-08 14:31:58 +02:00
return null;
2020-03-29 17:28:22 +02:00
}
public async Task<BitcoinAddress> GetNewAddress(BTCPayNetwork network)
{
var btcPayWallet = parent.PayTester.GetService<BTCPayWalletProvider>().GetWallet(network);
var address = (await btcPayWallet.ReserveAddressAsync(this.DerivationScheme)).Address;
return address;
}
public async Task<PSBT> Sign(PSBT psbt)
{
2024-01-18 01:47:39 +01:00
parent.PayTester.GetService<BTCPayWalletProvider>()
2020-03-29 17:28:22 +02:00
.GetWallet(psbt.Network.NetworkSet.CryptoCode);
var explorerClient = parent.PayTester.GetService<ExplorerClientProvider>()
.GetExplorerClient(psbt.Network.NetworkSet.CryptoCode);
psbt = (await explorerClient.UpdatePSBTAsync(new UpdatePSBTRequest()
{
2020-06-28 10:55:27 +02:00
DerivationScheme = DerivationScheme,
PSBT = psbt
2020-03-29 17:28:22 +02:00
})).PSBT;
return psbt.SignAll(this.DerivationScheme, GenerateWalletResponseV.AccountHDKey,
GenerateWalletResponseV.AccountKeyPath);
}
2021-11-22 09:16:08 +01:00
Logging.ILog TestLogs => this.parent.TestLogs;
2020-06-28 10:55:27 +02:00
public async Task<PSBT> SubmitPayjoin(Invoice invoice, PSBT psbt, string expectedError = null, bool senderError = false)
2020-03-29 17:28:22 +02:00
{
2020-06-17 14:43:56 +02:00
var endpoint = GetPayjoinBitcoinUrl(invoice, psbt.Network);
2020-04-07 12:32:30 +02:00
if (endpoint == null)
{
throw new InvalidOperationException("No payjoin endpoint for the invoice");
2020-04-07 12:32:30 +02:00
}
2020-03-29 17:28:22 +02:00
var pjClient = parent.PayTester.GetService<PayjoinClient>();
var storeRepository = parent.PayTester.GetService<StoreRepository>();
var store = await storeRepository.FindStore(StoreId);
var settings = store.GetSupportedPaymentMethods(parent.NetworkProvider).OfType<DerivationSchemeSettings>()
.First();
2021-11-22 09:16:08 +01:00
TestLogs.LogInformation($"Proposing {psbt.GetGlobalTransaction().GetHash()}");
2020-04-08 08:20:19 +02:00
if (expectedError is null && !senderError)
2020-03-29 17:28:22 +02:00
{
var proposed = await pjClient.RequestPayjoin(endpoint, new PayjoinWallet(settings), psbt, default);
2021-11-22 09:16:08 +01:00
TestLogs.LogInformation($"Proposed payjoin is {proposed.GetGlobalTransaction().GetHash()}");
2020-03-29 17:28:22 +02:00
Assert.NotNull(proposed);
return proposed;
}
else
{
2020-04-08 08:20:19 +02:00
if (senderError)
{
await Assert.ThrowsAsync<PayjoinSenderException>(async () => await pjClient.RequestPayjoin(endpoint, new PayjoinWallet(settings), psbt, default));
2020-04-08 08:20:19 +02:00
}
else
{
var ex = await Assert.ThrowsAsync<PayjoinReceiverException>(async () => await pjClient.RequestPayjoin(endpoint, new PayjoinWallet(settings), psbt, default));
var split = expectedError.Split('|');
Assert.Equal(split[0], ex.ErrorCode);
if (split.Length > 1)
Assert.Contains(split[1], ex.ReceiverMessage);
2020-04-08 08:20:19 +02:00
}
2020-03-29 17:28:22 +02:00
return null;
}
}
public async Task<Transaction> SubmitPayjoin(Invoice invoice, Transaction transaction, BTCPayNetwork network,
string expectedError = null)
{
var response =
await SubmitPayjoinCore(transaction.ToHex(), invoice, network.NBitcoinNetwork, expectedError);
if (response == null)
return null;
var signed = Transaction.Parse(await response.Content.ReadAsStringAsync(), network.NBitcoinNetwork);
return signed;
}
async Task<HttpResponseMessage> SubmitPayjoinCore(string content, Invoice invoice, Network network,
string expectedError)
{
2020-06-17 14:43:56 +02:00
var bip21 = GetPayjoinBitcoinUrl(invoice, network);
bip21.TryGetPayjoinEndpoint(out var endpoint);
2020-03-29 17:28:22 +02:00
var response = await parent.PayTester.HttpClient.PostAsync(endpoint,
new StringContent(content, Encoding.UTF8, "text/plain"));
if (expectedError != null)
{
var split = expectedError.Split('|');
2020-03-29 17:28:22 +02:00
Assert.False(response.IsSuccessStatusCode);
var error = JObject.Parse(await response.Content.ReadAsStringAsync());
if (split.Length > 0)
Assert.Equal(split[0], error["errorCode"].Value<string>());
if (split.Length > 1)
Assert.Contains(split[1], error["message"].Value<string>());
2020-03-29 17:28:22 +02:00
return null;
}
else
{
if (!response.IsSuccessStatusCode)
{
var error = JObject.Parse(await response.Content.ReadAsStringAsync());
Assert.Fail($"Error: {error["errorCode"].Value<string>()}: {error["message"].Value<string>()}");
2020-03-29 17:28:22 +02:00
}
}
return response;
}
2020-06-17 14:43:56 +02:00
public static BitcoinUrlBuilder GetPayjoinBitcoinUrl(Invoice invoice, Network network)
2020-03-29 17:28:22 +02:00
{
var parsedBip21 = new BitcoinUrlBuilder(
invoice.CryptoInfo.First(c => c.CryptoCode == network.NetworkSet.CryptoCode).PaymentUrls.BIP21,
network);
2024-01-18 01:47:39 +01:00
if (!parsedBip21.TryGetPayjoinEndpoint(out _))
2020-06-17 14:43:56 +02:00
return null;
return parsedBip21;
2020-03-29 17:28:22 +02:00
}
2020-11-13 08:28:15 +01:00
class WebhookListener : IDisposable
{
private Client.Models.StoreWebhookData _wh;
private FakeServer _server;
private readonly List<StoreWebhookEvent> _webhookEvents;
2020-11-13 08:28:15 +01:00
private CancellationTokenSource _cts;
public WebhookListener(Client.Models.StoreWebhookData wh, FakeServer server, List<StoreWebhookEvent> webhookEvents)
2020-11-13 08:28:15 +01:00
{
_wh = wh;
_server = server;
_webhookEvents = webhookEvents;
_cts = new CancellationTokenSource();
_ = Listen(_cts.Token);
}
async Task Listen(CancellationToken cancellation)
{
while (!cancellation.IsCancellationRequested)
{
var req = await _server.GetNextRequest(cancellation);
var bytes = await req.Request.Body.ReadBytesAsync((int)req.Request.Headers.ContentLength);
var callback = Encoding.UTF8.GetString(bytes);
2023-05-26 16:49:32 +02:00
lock (_webhookEvents)
{
_webhookEvents.Add(JsonConvert.DeserializeObject<DummyStoreWebhookEvent>(callback));
2023-05-26 16:49:32 +02:00
}
2020-11-13 08:28:15 +01:00
req.Response.StatusCode = 200;
_server.Done();
}
}
public void Dispose()
{
_cts.Cancel();
_server.Dispose();
}
}
public class DummyStoreWebhookEvent : StoreWebhookEvent
{
}
public List<StoreWebhookEvent> WebhookEvents { get; set; } = new List<StoreWebhookEvent>();
public async Task<TEvent> AssertHasWebhookEvent<TEvent>(string eventType, Action<TEvent> assert) where TEvent : class
2020-11-13 08:28:15 +01:00
{
2020-12-11 05:41:39 +01:00
int retry = 0;
2021-12-31 08:59:02 +01:00
retry:
2023-05-26 16:49:32 +02:00
lock (WebhookEvents)
2020-11-13 08:28:15 +01:00
{
2023-05-26 16:49:32 +02:00
foreach (var evt in WebhookEvents)
2020-11-13 08:28:15 +01:00
{
2023-05-26 16:49:32 +02:00
if (evt.Type == eventType)
2020-11-13 08:28:15 +01:00
{
2023-05-26 16:49:32 +02:00
var typedEvt = evt.ReadAs<TEvent>();
try
{
assert(typedEvt);
return typedEvt;
}
catch (XunitException)
{
}
2020-11-13 08:28:15 +01:00
}
}
}
2020-12-11 05:41:39 +01:00
if (retry < 3)
{
await Task.Delay(1000);
2020-12-11 05:41:39 +01:00
retry++;
goto retry;
}
Assert.Fail("No webhook event match the assertion");
2020-11-13 08:28:15 +01:00
return null;
}
public async Task SetupWebhook()
{
var server = new FakeServer();
2020-11-13 08:28:15 +01:00
await server.Start();
var client = await CreateClient(Policies.CanModifyStoreWebhooks);
var wh = await client.CreateWebhook(StoreId, new CreateStoreWebhookRequest()
{
AutomaticRedelivery = false,
Url = server.ServerUri.AbsoluteUri
});
parent.Resources.Add(new WebhookListener(wh, server, WebhookEvents));
}
public async Task PayInvoice(string invoiceId)
{
var inv = await BitPay.GetInvoiceAsync(invoiceId);
var net = parent.ExplorerNode.Network;
await parent.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(inv.BitcoinAddress, net), inv.BtcDue);
2020-11-13 08:28:15 +01:00
await TestUtils.EventuallyAsync(async () =>
{
var localInvoice = await BitPay.GetInvoiceAsync(invoiceId, Facade.Merchant);
Assert.Equal("paid", localInvoice.Status);
});
}
public async Task AddGuest(string userId)
{
var repo = parent.PayTester.GetService<StoreRepository>();
await repo.AddOrUpdateStoreUser(StoreId, userId, StoreRoleId.Guest);
}
public async Task AddOwner(string userId)
{
var repo = parent.PayTester.GetService<StoreRepository>();
await repo.AddOrUpdateStoreUser(StoreId, userId, StoreRoleId.Owner);
}
public async Task AddManager(string userId)
{
var repo = parent.PayTester.GetService<StoreRepository>();
await repo.AddOrUpdateStoreUser(StoreId, userId, StoreRoleId.Manager);
}
public async Task AddEmployee(string userId)
{
var repo = parent.PayTester.GetService<StoreRepository>();
await repo.AddOrUpdateStoreUser(StoreId, userId, StoreRoleId.Employee);
}
public async Task<uint256> PayOnChain(string invoiceId)
{
var cryptoCode = "BTC";
var client = await CreateClient();
var methods = await client.GetInvoicePaymentMethods(StoreId, invoiceId);
var method = methods.First(m => m.PaymentMethod == cryptoCode);
var address = method.Destination;
var tx = await client.CreateOnChainTransaction(StoreId, cryptoCode, new CreateOnChainTransactionRequest()
{
Destinations = new List<CreateOnChainTransactionRequest.CreateOnChainTransactionRequestDestination>()
{
new ()
{
Destination = address,
Amount = method.Due
}
},
FeeRate = new FeeRate(1.0m)
});
await WaitInvoicePaid(invoiceId);
return tx.TransactionHash;
}
public async Task PayOnBOLT11(string invoiceId)
{
var cryptoCode = "BTC";
var client = await CreateClient();
var methods = await client.GetInvoicePaymentMethods(StoreId, invoiceId);
var method = methods.First(m => m.PaymentMethod == $"{cryptoCode}-LightningNetwork");
var bolt11 = method.Destination;
TestLogs.LogInformation("PAYING");
await parent.CustomerLightningD.Pay(bolt11);
TestLogs.LogInformation("PAID");
await WaitInvoicePaid(invoiceId);
}
public async Task PayOnLNUrl(string invoiceId)
{
var cryptoCode = "BTC";
var network = SupportedNetwork.NBitcoinNetwork;
var client = await CreateClient();
var methods = await client.GetInvoicePaymentMethods(StoreId, invoiceId);
var method = methods.First(m => m.PaymentMethod == $"{cryptoCode}-LNURLPAY");
var lnurL = LNURL.LNURL.Parse(method.PaymentLink, out var tag);
var http = new HttpClient();
var payreq = (LNURL.LNURLPayRequest)await LNURL.LNURL.FetchInformation(lnurL, tag, http);
var resp = await payreq.SendRequest(payreq.MinSendable, network, http);
var bolt11 = resp.Pr;
await parent.CustomerLightningD.Pay(bolt11);
await WaitInvoicePaid(invoiceId);
}
public Task WaitInvoicePaid(string invoiceId)
{
return TestUtils.EventuallyAsync(async () =>
{
var client = await CreateClient();
var invoice = await client.GetInvoice(StoreId, invoiceId);
if (invoice.Status == InvoiceStatus.Settled)
return;
Assert.Equal(InvoiceStatus.Processing, invoice.Status);
});
}
public async Task PayOnLNAddress(string lnAddrUser = null)
{
lnAddrUser ??= LNAddress;
var network = SupportedNetwork.NBitcoinNetwork;
var payReqStr = await (await parent.PayTester.HttpClient.GetAsync($".well-known/lnurlp/{lnAddrUser}")).Content.ReadAsStringAsync();
var payreq = JsonConvert.DeserializeObject<LNURL.LNURLPayRequest>(payReqStr);
var resp = await payreq.SendRequest(payreq.MinSendable, network, parent.PayTester.HttpClient);
var bolt11 = resp.Pr;
await parent.CustomerLightningD.Pay(bolt11);
}
public async Task<string> CreateLNAddress()
{
var lnAddrUser = Guid.NewGuid().ToString();
var ctx = parent.PayTester.GetService<ApplicationDbContextFactory>().CreateContext();
ctx.LightningAddresses.Add(new()
{
StoreDataId = StoreId,
Username = lnAddrUser
});
await ctx.SaveChangesAsync();
LNAddress = lnAddrUser;
return lnAddrUser;
}
public async Task ImportOldInvoices(string storeId = null)
{
storeId ??= StoreId;
var oldInvoices = File.ReadAllLines(TestUtils.GetTestDataFullPath("OldInvoices.csv"));
var oldPayments = File.ReadAllLines(TestUtils.GetTestDataFullPath("OldPayments.csv"));
var dbContext = this.parent.PayTester.GetService<ApplicationDbContextFactory>().CreateContext();
var db = (NpgsqlConnection)dbContext.Database.GetDbConnection();
await db.OpenAsync();
using (var writer = db.BeginTextImport("COPY \"Invoices\" (\"Id\",\"Blob\",\"Created\",\"CustomerEmail\",\"ExceptionStatus\",\"ItemCode\",\"OrderId\",\"Status\",\"StoreDataId\",\"Archived\",\"Blob2\") FROM STDIN DELIMITER ',' CSV HEADER"))
{
foreach (var invoice in oldInvoices)
{
var localInvoice = invoice.Replace("3sgUCCtUBg6S8LJkrbdfAWbsJMqByFLfvSqjG6xKBWEd", storeId);
await writer.WriteLineAsync(localInvoice);
}
await writer.FlushAsync();
}
using (var writer = db.BeginTextImport("COPY \"Payments\" (\"Id\",\"Blob\",\"InvoiceDataId\",\"Accounted\",\"Blob2\",\"Type\") FROM STDIN DELIMITER ',' CSV HEADER"))
{
foreach (var invoice in oldPayments)
{
var localPayment = invoice.Replace("3sgUCCtUBg6S8LJkrbdfAWbsJMqByFLfvSqjG6xKBWEd", storeId);
await writer.WriteLineAsync(localPayment);
}
await writer.FlushAsync();
}
}
2018-07-01 09:10:17 +02:00
}
2017-09-13 08:47:34 +02:00
}