mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2024-11-19 01:43:50 +01:00
Warning if not using 'simple using'
This commit is contained in:
parent
c6a7e90c1a
commit
50d4b55f73
@ -122,6 +122,7 @@ csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_between_square_brackets = false
|
||||
csharp_style_prefer_null_check_over_type_check = true:warning
|
||||
csharp_prefer_simple_using_statement = true:warning
|
||||
|
||||
# C++ Files
|
||||
|
||||
|
@ -21,12 +21,10 @@ namespace BTCPayServer
|
||||
public static string Unzip(byte[] bytes)
|
||||
{
|
||||
MemoryStream ms = new MemoryStream(bytes);
|
||||
using (GZipStream gzip = new GZipStream(ms, CompressionMode.Decompress))
|
||||
{
|
||||
StreamReader reader = new StreamReader(gzip, Encoding.UTF8);
|
||||
var unzipped = reader.ReadToEnd();
|
||||
return unzipped;
|
||||
}
|
||||
using GZipStream gzip = new GZipStream(ms, CompressionMode.Decompress);
|
||||
StreamReader reader = new StreamReader(gzip, Encoding.UTF8);
|
||||
var unzipped = reader.ReadToEnd();
|
||||
return unzipped;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,166 +35,164 @@ namespace BTCPayServer.Tests
|
||||
//as a user through your profile
|
||||
//as an external application requesting an api key from a user
|
||||
|
||||
using (var s = CreateSeleniumTester())
|
||||
using var s = CreateSeleniumTester();
|
||||
await s.StartAsync();
|
||||
var tester = s.Server;
|
||||
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync();
|
||||
await user.MakeAdmin(false);
|
||||
s.GoToLogin();
|
||||
s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password);
|
||||
s.GoToProfile(ManageNavPages.APIKeys);
|
||||
s.Driver.FindElement(By.Id("AddApiKey")).Click();
|
||||
|
||||
//not an admin, so this permission should not show
|
||||
Assert.DoesNotContain("btcpay.server.canmodifyserversettings", s.Driver.PageSource);
|
||||
await user.MakeAdmin();
|
||||
s.Logout();
|
||||
s.GoToLogin();
|
||||
s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password);
|
||||
s.GoToProfile(ManageNavPages.APIKeys);
|
||||
s.Driver.FindElement(By.Id("AddApiKey")).Click();
|
||||
Assert.Contains("btcpay.server.canmodifyserversettings", s.Driver.PageSource);
|
||||
|
||||
//server management should show now
|
||||
s.Driver.SetCheckbox(By.Id("btcpay.server.canmodifyserversettings"), true);
|
||||
s.Driver.SetCheckbox(By.Id("btcpay.store.canmodifystoresettings"), true);
|
||||
s.Driver.SetCheckbox(By.Id("btcpay.user.canviewprofile"), true);
|
||||
s.Driver.FindElement(By.Id("Generate")).Click();
|
||||
var superApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
|
||||
|
||||
//this api key has access to everything
|
||||
await TestApiAgainstAccessToken(superApiKey, tester, user, Policies.CanModifyServerSettings, Policies.CanModifyStoreSettings, Policies.CanViewProfile);
|
||||
|
||||
|
||||
s.Driver.FindElement(By.Id("AddApiKey")).Click();
|
||||
s.Driver.SetCheckbox(By.Id("btcpay.server.canmodifyserversettings"), true);
|
||||
s.Driver.FindElement(By.Id("Generate")).Click();
|
||||
var serverOnlyApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
|
||||
await TestApiAgainstAccessToken(serverOnlyApiKey, tester, user,
|
||||
Policies.CanModifyServerSettings);
|
||||
|
||||
|
||||
s.Driver.FindElement(By.Id("AddApiKey")).Click();
|
||||
s.Driver.SetCheckbox(By.Id("btcpay.store.canmodifystoresettings"), true);
|
||||
s.Driver.FindElement(By.Id("Generate")).Click();
|
||||
var allStoreOnlyApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
|
||||
await TestApiAgainstAccessToken(allStoreOnlyApiKey, tester, user,
|
||||
Policies.CanModifyStoreSettings);
|
||||
|
||||
s.Driver.FindElement(By.Id("AddApiKey")).Click();
|
||||
s.Driver.FindElement(By.CssSelector("button[value='btcpay.store.canmodifystoresettings:change-store-mode']")).Click();
|
||||
//there should be a store already by default in the dropdown
|
||||
var src = s.Driver.PageSource;
|
||||
var getPermissionValueIndex =
|
||||
s.Driver.FindElement(By.CssSelector("input[value='btcpay.store.canmodifystoresettings']"))
|
||||
.GetAttribute("name")
|
||||
.Replace(".Permission", ".SpecificStores[0]");
|
||||
var dropdown = s.Driver.FindElement(By.Name(getPermissionValueIndex));
|
||||
var option = dropdown.FindElement(By.TagName("option"));
|
||||
var storeId = option.GetAttribute("value");
|
||||
option.Click();
|
||||
s.Driver.FindElement(By.Id("Generate")).Click();
|
||||
var selectiveStoreApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
|
||||
await TestApiAgainstAccessToken(selectiveStoreApiKey, tester, user,
|
||||
Permission.Create(Policies.CanModifyStoreSettings, storeId).ToString());
|
||||
|
||||
s.Driver.FindElement(By.Id("AddApiKey")).Click();
|
||||
s.Driver.FindElement(By.Id("Generate")).Click();
|
||||
var noPermissionsApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
|
||||
await TestApiAgainstAccessToken(noPermissionsApiKey, tester, user);
|
||||
|
||||
await Assert.ThrowsAnyAsync<HttpRequestException>(async () =>
|
||||
{
|
||||
await s.StartAsync();
|
||||
var tester = s.Server;
|
||||
await TestApiAgainstAccessToken<bool>("incorrect key", $"{TestApiPath}/me/id",
|
||||
tester.PayTester.HttpClient);
|
||||
});
|
||||
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync();
|
||||
await user.MakeAdmin(false);
|
||||
s.GoToLogin();
|
||||
s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password);
|
||||
s.GoToProfile(ManageNavPages.APIKeys);
|
||||
s.Driver.FindElement(By.Id("AddApiKey")).Click();
|
||||
//let's test the authorized screen now
|
||||
//options for authorize are:
|
||||
//applicationName
|
||||
//redirect
|
||||
//permissions
|
||||
//strict
|
||||
//selectiveStores
|
||||
//redirect
|
||||
//appidentifier
|
||||
var appidentifier = "testapp";
|
||||
var callbackUrl = s.ServerUri + "postredirect-callback-test";
|
||||
var authUrl = BTCPayServerClient.GenerateAuthorizeUri(s.ServerUri,
|
||||
new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, applicationDetails: (appidentifier, new Uri(callbackUrl))).ToString();
|
||||
s.Driver.Navigate().GoToUrl(authUrl);
|
||||
Assert.Contains(appidentifier, s.Driver.PageSource);
|
||||
Assert.Equal("hidden", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("type").ToLowerInvariant());
|
||||
Assert.Equal("true", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("value").ToLowerInvariant());
|
||||
Assert.Equal("hidden", s.Driver.FindElement(By.Id("btcpay.server.canmodifyserversettings")).GetAttribute("type").ToLowerInvariant());
|
||||
Assert.Equal("true", s.Driver.FindElement(By.Id("btcpay.server.canmodifyserversettings")).GetAttribute("value").ToLowerInvariant());
|
||||
Assert.DoesNotContain("change-store-mode", s.Driver.PageSource);
|
||||
s.Driver.FindElement(By.Id("consent-yes")).Click();
|
||||
Assert.Equal(callbackUrl, s.Driver.Url);
|
||||
|
||||
//not an admin, so this permission should not show
|
||||
Assert.DoesNotContain("btcpay.server.canmodifyserversettings", s.Driver.PageSource);
|
||||
await user.MakeAdmin();
|
||||
s.Logout();
|
||||
s.GoToLogin();
|
||||
s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password);
|
||||
s.GoToProfile(ManageNavPages.APIKeys);
|
||||
s.Driver.FindElement(By.Id("AddApiKey")).Click();
|
||||
Assert.Contains("btcpay.server.canmodifyserversettings", s.Driver.PageSource);
|
||||
var apiKeyRepo = s.Server.PayTester.GetService<APIKeyRepository>();
|
||||
var accessToken = GetAccessTokenFromCallbackResult(s.Driver);
|
||||
|
||||
//server management should show now
|
||||
s.Driver.SetCheckbox(By.Id("btcpay.server.canmodifyserversettings"), true);
|
||||
s.Driver.SetCheckbox(By.Id("btcpay.store.canmodifystoresettings"), true);
|
||||
s.Driver.SetCheckbox(By.Id("btcpay.user.canviewprofile"), true);
|
||||
s.Driver.FindElement(By.Id("Generate")).Click();
|
||||
var superApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
|
||||
await TestApiAgainstAccessToken(accessToken, tester, user,
|
||||
(await apiKeyRepo.GetKey(accessToken)).GetBlob().Permissions);
|
||||
|
||||
//this api key has access to everything
|
||||
await TestApiAgainstAccessToken(superApiKey, tester, user, Policies.CanModifyServerSettings, Policies.CanModifyStoreSettings, Policies.CanViewProfile);
|
||||
authUrl = BTCPayServerClient.GenerateAuthorizeUri(s.ServerUri,
|
||||
new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, false, true, applicationDetails: (null, new Uri(callbackUrl))).ToString();
|
||||
|
||||
s.Driver.Navigate().GoToUrl(authUrl);
|
||||
Assert.DoesNotContain("kukksappname", s.Driver.PageSource);
|
||||
|
||||
s.Driver.FindElement(By.Id("AddApiKey")).Click();
|
||||
s.Driver.SetCheckbox(By.Id("btcpay.server.canmodifyserversettings"), true);
|
||||
s.Driver.FindElement(By.Id("Generate")).Click();
|
||||
var serverOnlyApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
|
||||
await TestApiAgainstAccessToken(serverOnlyApiKey, tester, user,
|
||||
Policies.CanModifyServerSettings);
|
||||
Assert.Equal("checkbox", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("type").ToLowerInvariant());
|
||||
Assert.Equal("true", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("value").ToLowerInvariant());
|
||||
Assert.Equal("checkbox", s.Driver.FindElement(By.Id("btcpay.server.canmodifyserversettings")).GetAttribute("type").ToLowerInvariant());
|
||||
Assert.Equal("true", s.Driver.FindElement(By.Id("btcpay.server.canmodifyserversettings")).GetAttribute("value").ToLowerInvariant());
|
||||
|
||||
s.Driver.SetCheckbox(By.Id("btcpay.server.canmodifyserversettings"), false);
|
||||
Assert.Contains("change-store-mode", s.Driver.PageSource);
|
||||
s.Driver.FindElement(By.Id("consent-yes")).Click();
|
||||
Assert.Equal(callbackUrl, s.Driver.Url);
|
||||
|
||||
s.Driver.FindElement(By.Id("AddApiKey")).Click();
|
||||
s.Driver.SetCheckbox(By.Id("btcpay.store.canmodifystoresettings"), true);
|
||||
s.Driver.FindElement(By.Id("Generate")).Click();
|
||||
var allStoreOnlyApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
|
||||
await TestApiAgainstAccessToken(allStoreOnlyApiKey, tester, user,
|
||||
Policies.CanModifyStoreSettings);
|
||||
accessToken = GetAccessTokenFromCallbackResult(s.Driver);
|
||||
await TestApiAgainstAccessToken(accessToken, tester, user,
|
||||
(await apiKeyRepo.GetKey(accessToken)).GetBlob().Permissions);
|
||||
|
||||
s.Driver.FindElement(By.Id("AddApiKey")).Click();
|
||||
s.Driver.FindElement(By.CssSelector("button[value='btcpay.store.canmodifystoresettings:change-store-mode']")).Click();
|
||||
//there should be a store already by default in the dropdown
|
||||
var src = s.Driver.PageSource;
|
||||
var getPermissionValueIndex =
|
||||
s.Driver.FindElement(By.CssSelector("input[value='btcpay.store.canmodifystoresettings']"))
|
||||
.GetAttribute("name")
|
||||
.Replace(".Permission", ".SpecificStores[0]");
|
||||
var dropdown = s.Driver.FindElement(By.Name(getPermissionValueIndex));
|
||||
var option = dropdown.FindElement(By.TagName("option"));
|
||||
var storeId = option.GetAttribute("value");
|
||||
option.Click();
|
||||
s.Driver.FindElement(By.Id("Generate")).Click();
|
||||
var selectiveStoreApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
|
||||
await TestApiAgainstAccessToken(selectiveStoreApiKey, tester, user,
|
||||
Permission.Create(Policies.CanModifyStoreSettings, storeId).ToString());
|
||||
//let's test the app identifier system
|
||||
authUrl = BTCPayServerClient.GenerateAuthorizeUri(s.ServerUri,
|
||||
new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, false, true, (appidentifier, new Uri(callbackUrl))).ToString();
|
||||
|
||||
s.Driver.FindElement(By.Id("AddApiKey")).Click();
|
||||
s.Driver.FindElement(By.Id("Generate")).Click();
|
||||
var noPermissionsApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
|
||||
await TestApiAgainstAccessToken(noPermissionsApiKey, tester, user);
|
||||
//if it's the same, go to the confirm page
|
||||
s.Driver.Navigate().GoToUrl(authUrl);
|
||||
s.Driver.FindElement(By.Id("continue")).Click();
|
||||
Assert.Equal(callbackUrl, s.Driver.Url);
|
||||
|
||||
await Assert.ThrowsAnyAsync<HttpRequestException>(async () =>
|
||||
{
|
||||
await TestApiAgainstAccessToken<bool>("incorrect key", $"{TestApiPath}/me/id",
|
||||
tester.PayTester.HttpClient);
|
||||
});
|
||||
//same app but different redirect = nono
|
||||
authUrl = BTCPayServerClient.GenerateAuthorizeUri(s.ServerUri,
|
||||
new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, false, true, (appidentifier, new Uri("https://international.local/callback"))).ToString();
|
||||
|
||||
//let's test the authorized screen now
|
||||
//options for authorize are:
|
||||
//applicationName
|
||||
//redirect
|
||||
//permissions
|
||||
//strict
|
||||
//selectiveStores
|
||||
//redirect
|
||||
//appidentifier
|
||||
var appidentifier = "testapp";
|
||||
var callbackUrl = s.ServerUri + "postredirect-callback-test";
|
||||
var authUrl = BTCPayServerClient.GenerateAuthorizeUri(s.ServerUri,
|
||||
new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, applicationDetails: (appidentifier, new Uri(callbackUrl))).ToString();
|
||||
s.Driver.Navigate().GoToUrl(authUrl);
|
||||
Assert.Contains(appidentifier, s.Driver.PageSource);
|
||||
Assert.Equal("hidden", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("type").ToLowerInvariant());
|
||||
Assert.Equal("true", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("value").ToLowerInvariant());
|
||||
Assert.Equal("hidden", s.Driver.FindElement(By.Id("btcpay.server.canmodifyserversettings")).GetAttribute("type").ToLowerInvariant());
|
||||
Assert.Equal("true", s.Driver.FindElement(By.Id("btcpay.server.canmodifyserversettings")).GetAttribute("value").ToLowerInvariant());
|
||||
Assert.DoesNotContain("change-store-mode", s.Driver.PageSource);
|
||||
s.Driver.FindElement(By.Id("consent-yes")).Click();
|
||||
Assert.Equal(callbackUrl, s.Driver.Url);
|
||||
s.Driver.Navigate().GoToUrl(authUrl);
|
||||
Assert.False(s.Driver.Url.StartsWith("https://international.com/callback"));
|
||||
|
||||
var apiKeyRepo = s.Server.PayTester.GetService<APIKeyRepository>();
|
||||
var accessToken = GetAccessTokenFromCallbackResult(s.Driver);
|
||||
|
||||
await TestApiAgainstAccessToken(accessToken, tester, user,
|
||||
(await apiKeyRepo.GetKey(accessToken)).GetBlob().Permissions);
|
||||
|
||||
authUrl = BTCPayServerClient.GenerateAuthorizeUri(s.ServerUri,
|
||||
new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, false, true, applicationDetails: (null, new Uri(callbackUrl))).ToString();
|
||||
|
||||
s.Driver.Navigate().GoToUrl(authUrl);
|
||||
Assert.DoesNotContain("kukksappname", s.Driver.PageSource);
|
||||
|
||||
Assert.Equal("checkbox", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("type").ToLowerInvariant());
|
||||
Assert.Equal("true", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("value").ToLowerInvariant());
|
||||
Assert.Equal("checkbox", s.Driver.FindElement(By.Id("btcpay.server.canmodifyserversettings")).GetAttribute("type").ToLowerInvariant());
|
||||
Assert.Equal("true", s.Driver.FindElement(By.Id("btcpay.server.canmodifyserversettings")).GetAttribute("value").ToLowerInvariant());
|
||||
|
||||
s.Driver.SetCheckbox(By.Id("btcpay.server.canmodifyserversettings"), false);
|
||||
Assert.Contains("change-store-mode", s.Driver.PageSource);
|
||||
s.Driver.FindElement(By.Id("consent-yes")).Click();
|
||||
Assert.Equal(callbackUrl, s.Driver.Url);
|
||||
|
||||
accessToken = GetAccessTokenFromCallbackResult(s.Driver);
|
||||
await TestApiAgainstAccessToken(accessToken, tester, user,
|
||||
(await apiKeyRepo.GetKey(accessToken)).GetBlob().Permissions);
|
||||
|
||||
//let's test the app identifier system
|
||||
authUrl = BTCPayServerClient.GenerateAuthorizeUri(s.ServerUri,
|
||||
new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, false, true, (appidentifier, new Uri(callbackUrl))).ToString();
|
||||
|
||||
//if it's the same, go to the confirm page
|
||||
s.Driver.Navigate().GoToUrl(authUrl);
|
||||
s.Driver.FindElement(By.Id("continue")).Click();
|
||||
Assert.Equal(callbackUrl, s.Driver.Url);
|
||||
|
||||
//same app but different redirect = nono
|
||||
authUrl = BTCPayServerClient.GenerateAuthorizeUri(s.ServerUri,
|
||||
new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, false, true, (appidentifier, new Uri("https://international.local/callback"))).ToString();
|
||||
|
||||
s.Driver.Navigate().GoToUrl(authUrl);
|
||||
Assert.False(s.Driver.Url.StartsWith("https://international.com/callback"));
|
||||
|
||||
// Make sure we can check all permissions when not an admin
|
||||
await user.MakeAdmin(false);
|
||||
s.Logout();
|
||||
s.GoToLogin();
|
||||
s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password);
|
||||
s.GoToProfile(ManageNavPages.APIKeys);
|
||||
s.Driver.FindElement(By.Id("AddApiKey")).Click();
|
||||
int checkedPermissionCount = 0;
|
||||
foreach (var checkbox in s.Driver.FindElements(By.ClassName("form-check-input")))
|
||||
{
|
||||
checkedPermissionCount++;
|
||||
checkbox.Click();
|
||||
}
|
||||
s.Driver.FindElement(By.Id("Generate")).Click();
|
||||
var allAPIKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
|
||||
var apikeydata = await TestApiAgainstAccessToken<ApiKeyData>(allAPIKey, $"api/v1/api-keys/current", tester.PayTester.HttpClient);
|
||||
Assert.Equal(checkedPermissionCount, apikeydata.Permissions.Length);
|
||||
// Make sure we can check all permissions when not an admin
|
||||
await user.MakeAdmin(false);
|
||||
s.Logout();
|
||||
s.GoToLogin();
|
||||
s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password);
|
||||
s.GoToProfile(ManageNavPages.APIKeys);
|
||||
s.Driver.FindElement(By.Id("AddApiKey")).Click();
|
||||
int checkedPermissionCount = 0;
|
||||
foreach (var checkbox in s.Driver.FindElements(By.ClassName("form-check-input")))
|
||||
{
|
||||
checkedPermissionCount++;
|
||||
checkbox.Click();
|
||||
}
|
||||
s.Driver.FindElement(By.Id("Generate")).Click();
|
||||
var allAPIKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
|
||||
var apikeydata = await TestApiAgainstAccessToken<ApiKeyData>(allAPIKey, $"api/v1/api-keys/current", tester.PayTester.HttpClient);
|
||||
Assert.Equal(checkedPermissionCount, apikeydata.Permissions.Length);
|
||||
}
|
||||
|
||||
async Task TestApiAgainstAccessToken(string accessToken, ServerTester tester, TestAccount testAccount,
|
||||
|
@ -243,11 +243,9 @@ namespace BTCPayServer.Tests
|
||||
private async Task WaitSiteIsOperational()
|
||||
{
|
||||
_ = HttpClient.GetAsync("/").ConfigureAwait(false);
|
||||
using (var cts = new CancellationTokenSource(20_000))
|
||||
{
|
||||
var synching = WaitIsFullySynched(cts.Token);
|
||||
await Task.WhenAll(synching).ConfigureAwait(false);
|
||||
}
|
||||
using var cts = new CancellationTokenSource(20_000);
|
||||
var synching = WaitIsFullySynched(cts.Token);
|
||||
await Task.WhenAll(synching).ConfigureAwait(false);
|
||||
// Opportunistic call to wake up view compilation in debug mode, we don't need to await.
|
||||
}
|
||||
|
||||
|
@ -23,227 +23,215 @@ namespace BTCPayServer.Tests
|
||||
public async Task CanHandleRefundEmailForm()
|
||||
{
|
||||
|
||||
using (var s = CreateSeleniumTester())
|
||||
using var s = CreateSeleniumTester();
|
||||
await s.StartAsync();
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser();
|
||||
s.CreateNewStore();
|
||||
s.AddDerivationScheme("BTC");
|
||||
s.GoToStore(StoreNavPages.CheckoutAppearance);
|
||||
s.Driver.FindElement(By.Id("RequiresRefundEmail")).Click();
|
||||
s.Driver.FindElement(By.Name("command")).Click();
|
||||
|
||||
var emailAlreadyThereInvoiceId = s.CreateInvoice(100, "USD", "a@g.com");
|
||||
s.GoToInvoiceCheckout(emailAlreadyThereInvoiceId);
|
||||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
s.GoToHome();
|
||||
s.CreateInvoice();
|
||||
s.Driver.FindElement(By.ClassName("invoice-details-link")).Click();
|
||||
s.Driver.AssertNoError();
|
||||
s.Driver.Navigate().Back();
|
||||
s.Driver.FindElement(By.ClassName("invoice-checkout-link")).Click();
|
||||
Assert.NotEmpty(s.Driver.FindElements(By.Id("checkoutCtrl")));
|
||||
|
||||
Assert.True(s.Driver.FindElement(By.Id("emailAddressFormInput")).Displayed);
|
||||
s.Driver.FindElement(By.Id("emailAddressFormInput")).SendKeys("xxx");
|
||||
s.Driver.FindElement(By.Id("emailAddressForm")).FindElement(By.CssSelector("button.action-button"))
|
||||
.Click();
|
||||
var formInput = s.Driver.FindElement(By.Id("emailAddressFormInput"));
|
||||
|
||||
Assert.True(formInput.Displayed);
|
||||
Assert.Contains("form-input-invalid", formInput.GetAttribute("class"));
|
||||
formInput = s.Driver.FindElement(By.Id("emailAddressFormInput"));
|
||||
formInput.SendKeys("@g.com");
|
||||
var actionButton = s.Driver.FindElement(By.Id("emailAddressForm")).FindElement(By.CssSelector("button.action-button"));
|
||||
actionButton.Click();
|
||||
try // Sometimes the click only take the focus, without actually really clicking on it...
|
||||
{
|
||||
await s.StartAsync();
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser();
|
||||
s.CreateNewStore();
|
||||
s.AddDerivationScheme("BTC");
|
||||
s.GoToStore(StoreNavPages.CheckoutAppearance);
|
||||
s.Driver.FindElement(By.Id("RequiresRefundEmail")).Click();
|
||||
s.Driver.FindElement(By.Name("command")).Click();
|
||||
|
||||
var emailAlreadyThereInvoiceId = s.CreateInvoice(100, "USD", "a@g.com");
|
||||
s.GoToInvoiceCheckout(emailAlreadyThereInvoiceId);
|
||||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
s.GoToHome();
|
||||
s.CreateInvoice();
|
||||
s.Driver.FindElement(By.ClassName("invoice-details-link")).Click();
|
||||
s.Driver.AssertNoError();
|
||||
s.Driver.Navigate().Back();
|
||||
s.Driver.FindElement(By.ClassName("invoice-checkout-link")).Click();
|
||||
Assert.NotEmpty(s.Driver.FindElements(By.Id("checkoutCtrl")));
|
||||
|
||||
Assert.True(s.Driver.FindElement(By.Id("emailAddressFormInput")).Displayed);
|
||||
s.Driver.FindElement(By.Id("emailAddressFormInput")).SendKeys("xxx");
|
||||
s.Driver.FindElement(By.Id("emailAddressForm")).FindElement(By.CssSelector("button.action-button"))
|
||||
.Click();
|
||||
var formInput = s.Driver.FindElement(By.Id("emailAddressFormInput"));
|
||||
|
||||
Assert.True(formInput.Displayed);
|
||||
Assert.Contains("form-input-invalid", formInput.GetAttribute("class"));
|
||||
formInput = s.Driver.FindElement(By.Id("emailAddressFormInput"));
|
||||
formInput.SendKeys("@g.com");
|
||||
var actionButton = s.Driver.FindElement(By.Id("emailAddressForm")).FindElement(By.CssSelector("button.action-button"));
|
||||
actionButton.Click();
|
||||
try // Sometimes the click only take the focus, without actually really clicking on it...
|
||||
{
|
||||
actionButton.Click();
|
||||
}
|
||||
catch { }
|
||||
|
||||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
s.Driver.Navigate().Refresh();
|
||||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
}
|
||||
catch { }
|
||||
|
||||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
s.Driver.Navigate().Refresh();
|
||||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
public async Task CanHandleRefundEmailForm2()
|
||||
{
|
||||
|
||||
using (var s = CreateSeleniumTester())
|
||||
using var s = CreateSeleniumTester();
|
||||
// Prepare user account and store
|
||||
await s.StartAsync();
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser();
|
||||
s.CreateNewStore();
|
||||
s.AddDerivationScheme("BTC");
|
||||
|
||||
// Now create an invoice that requires a refund email
|
||||
var invoice = s.CreateInvoice(100, "USD", "", null, true);
|
||||
s.GoToInvoiceCheckout(invoice);
|
||||
|
||||
var emailInput = s.Driver.FindElement(By.Id("emailAddressFormInput"));
|
||||
Assert.True(emailInput.Displayed);
|
||||
|
||||
emailInput.SendKeys("a@g.com");
|
||||
|
||||
var actionButton = s.Driver.FindElement(By.Id("emailAddressForm")).FindElement(By.CssSelector("button.action-button"));
|
||||
actionButton.Click();
|
||||
try // Sometimes the click only take the focus, without actually really clicking on it...
|
||||
{
|
||||
// Prepare user account and store
|
||||
await s.StartAsync();
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser();
|
||||
s.CreateNewStore();
|
||||
s.AddDerivationScheme("BTC");
|
||||
|
||||
// Now create an invoice that requires a refund email
|
||||
var invoice = s.CreateInvoice(100, "USD", "", null, true);
|
||||
s.GoToInvoiceCheckout(invoice);
|
||||
|
||||
var emailInput = s.Driver.FindElement(By.Id("emailAddressFormInput"));
|
||||
Assert.True(emailInput.Displayed);
|
||||
|
||||
emailInput.SendKeys("a@g.com");
|
||||
|
||||
var actionButton = s.Driver.FindElement(By.Id("emailAddressForm")).FindElement(By.CssSelector("button.action-button"));
|
||||
actionButton.Click();
|
||||
try // Sometimes the click only take the focus, without actually really clicking on it...
|
||||
{
|
||||
actionButton.Click();
|
||||
}
|
||||
catch { }
|
||||
|
||||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
s.Driver.Navigate().Refresh();
|
||||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
|
||||
s.GoToHome();
|
||||
|
||||
// Now create an invoice that doesn't require a refund email
|
||||
s.CreateInvoice(100, "USD", "", null, false);
|
||||
s.Driver.FindElement(By.ClassName("invoice-details-link")).Click();
|
||||
s.Driver.AssertNoError();
|
||||
s.Driver.Navigate().Back();
|
||||
s.Driver.FindElement(By.ClassName("invoice-checkout-link")).Click();
|
||||
Assert.NotEmpty(s.Driver.FindElements(By.Id("checkoutCtrl")));
|
||||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
s.Driver.Navigate().Refresh();
|
||||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
|
||||
s.GoToHome();
|
||||
|
||||
// Now create an invoice that requires refund email but already has one set, email input shouldn't show up
|
||||
s.CreateInvoice(100, "USD", "a@g.com", null, true);
|
||||
s.Driver.FindElement(By.ClassName("invoice-details-link")).Click();
|
||||
s.Driver.AssertNoError();
|
||||
s.Driver.Navigate().Back();
|
||||
s.Driver.FindElement(By.ClassName("invoice-checkout-link")).Click();
|
||||
Assert.NotEmpty(s.Driver.FindElements(By.Id("checkoutCtrl")));
|
||||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
s.Driver.Navigate().Refresh();
|
||||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
}
|
||||
catch { }
|
||||
|
||||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
s.Driver.Navigate().Refresh();
|
||||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
|
||||
s.GoToHome();
|
||||
|
||||
// Now create an invoice that doesn't require a refund email
|
||||
s.CreateInvoice(100, "USD", "", null, false);
|
||||
s.Driver.FindElement(By.ClassName("invoice-details-link")).Click();
|
||||
s.Driver.AssertNoError();
|
||||
s.Driver.Navigate().Back();
|
||||
s.Driver.FindElement(By.ClassName("invoice-checkout-link")).Click();
|
||||
Assert.NotEmpty(s.Driver.FindElements(By.Id("checkoutCtrl")));
|
||||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
s.Driver.Navigate().Refresh();
|
||||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
|
||||
s.GoToHome();
|
||||
|
||||
// Now create an invoice that requires refund email but already has one set, email input shouldn't show up
|
||||
s.CreateInvoice(100, "USD", "a@g.com", null, true);
|
||||
s.Driver.FindElement(By.ClassName("invoice-details-link")).Click();
|
||||
s.Driver.AssertNoError();
|
||||
s.Driver.Navigate().Back();
|
||||
s.Driver.FindElement(By.ClassName("invoice-checkout-link")).Click();
|
||||
Assert.NotEmpty(s.Driver.FindElements(By.Id("checkoutCtrl")));
|
||||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
s.Driver.Navigate().Refresh();
|
||||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
public async Task CanUseLanguageDropdown()
|
||||
{
|
||||
using (var s = CreateSeleniumTester())
|
||||
{
|
||||
await s.StartAsync();
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser();
|
||||
s.CreateNewStore();
|
||||
s.AddDerivationScheme("BTC");
|
||||
using var s = CreateSeleniumTester();
|
||||
await s.StartAsync();
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser();
|
||||
s.CreateNewStore();
|
||||
s.AddDerivationScheme("BTC");
|
||||
|
||||
var invoiceId = s.CreateInvoice();
|
||||
s.GoToInvoiceCheckout(invoiceId);
|
||||
Assert.True(s.Driver.FindElement(By.Id("DefaultLang")).FindElements(By.TagName("option")).Count > 1);
|
||||
var payWithTextEnglish = s.Driver.FindElement(By.Id("pay-with-text")).Text;
|
||||
var invoiceId = s.CreateInvoice();
|
||||
s.GoToInvoiceCheckout(invoiceId);
|
||||
Assert.True(s.Driver.FindElement(By.Id("DefaultLang")).FindElements(By.TagName("option")).Count > 1);
|
||||
var payWithTextEnglish = s.Driver.FindElement(By.Id("pay-with-text")).Text;
|
||||
|
||||
var prettyDropdown = s.Driver.FindElement(By.Id("prettydropdown-DefaultLang"));
|
||||
prettyDropdown.Click();
|
||||
await Task.Delay(200);
|
||||
prettyDropdown.FindElement(By.CssSelector("[data-value=\"da-DK\"]")).Click();
|
||||
await Task.Delay(1000);
|
||||
Assert.NotEqual(payWithTextEnglish, s.Driver.FindElement(By.Id("pay-with-text")).Text);
|
||||
s.Driver.Navigate().GoToUrl(s.Driver.Url + "?lang=da-DK");
|
||||
var prettyDropdown = s.Driver.FindElement(By.Id("prettydropdown-DefaultLang"));
|
||||
prettyDropdown.Click();
|
||||
await Task.Delay(200);
|
||||
prettyDropdown.FindElement(By.CssSelector("[data-value=\"da-DK\"]")).Click();
|
||||
await Task.Delay(1000);
|
||||
Assert.NotEqual(payWithTextEnglish, s.Driver.FindElement(By.Id("pay-with-text")).Text);
|
||||
s.Driver.Navigate().GoToUrl(s.Driver.Url + "?lang=da-DK");
|
||||
|
||||
Assert.NotEqual(payWithTextEnglish, s.Driver.FindElement(By.Id("pay-with-text")).Text);
|
||||
Assert.NotEqual(payWithTextEnglish, s.Driver.FindElement(By.Id("pay-with-text")).Text);
|
||||
|
||||
s.Driver.Quit();
|
||||
}
|
||||
s.Driver.Quit();
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
[Trait("Lightning", "Lightning")]
|
||||
public async Task CanSetDefaultPaymentMethod()
|
||||
{
|
||||
using (var s = CreateSeleniumTester())
|
||||
{
|
||||
s.Server.ActivateLightning();
|
||||
await s.StartAsync();
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser(true);
|
||||
s.CreateNewStore();
|
||||
s.AddLightningNode();
|
||||
s.AddDerivationScheme("BTC");
|
||||
using var s = CreateSeleniumTester();
|
||||
s.Server.ActivateLightning();
|
||||
await s.StartAsync();
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser(true);
|
||||
s.CreateNewStore();
|
||||
s.AddLightningNode();
|
||||
s.AddDerivationScheme("BTC");
|
||||
|
||||
var invoiceId = s.CreateInvoice(defaultPaymentMethod: "BTC_LightningLike");
|
||||
s.GoToInvoiceCheckout(invoiceId);
|
||||
var invoiceId = s.CreateInvoice(defaultPaymentMethod: "BTC_LightningLike");
|
||||
s.GoToInvoiceCheckout(invoiceId);
|
||||
|
||||
Assert.Equal("Bitcoin (Lightning) (BTC)", s.Driver.FindElement(By.ClassName("payment__currencies")).Text);
|
||||
s.Driver.Quit();
|
||||
}
|
||||
Assert.Equal("Bitcoin (Lightning) (BTC)", s.Driver.FindElement(By.ClassName("payment__currencies")).Text);
|
||||
s.Driver.Quit();
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
[Trait("Lightning", "Lightning")]
|
||||
public async Task CanUseLightningSatsFeature()
|
||||
{
|
||||
using (var s = CreateSeleniumTester())
|
||||
{
|
||||
s.Server.ActivateLightning();
|
||||
await s.StartAsync();
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser(true);
|
||||
s.CreateNewStore();
|
||||
s.AddLightningNode();
|
||||
s.GoToStore();
|
||||
s.Driver.FindElement(By.Id("Modify-LightningBTC")).Click();
|
||||
s.Driver.SetCheckbox(By.Id("LightningAmountInSatoshi"), true);
|
||||
s.Driver.FindElement(By.Id("save")).Click();
|
||||
Assert.Contains("BTC Lightning settings successfully updated", s.FindAlertMessage().Text);
|
||||
using var s = CreateSeleniumTester();
|
||||
s.Server.ActivateLightning();
|
||||
await s.StartAsync();
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser(true);
|
||||
s.CreateNewStore();
|
||||
s.AddLightningNode();
|
||||
s.GoToStore();
|
||||
s.Driver.FindElement(By.Id("Modify-LightningBTC")).Click();
|
||||
s.Driver.SetCheckbox(By.Id("LightningAmountInSatoshi"), true);
|
||||
s.Driver.FindElement(By.Id("save")).Click();
|
||||
Assert.Contains("BTC Lightning settings successfully updated", s.FindAlertMessage().Text);
|
||||
|
||||
var invoiceId = s.CreateInvoice(10, "USD", "a@g.com");
|
||||
s.GoToInvoiceCheckout(invoiceId);
|
||||
Assert.Contains("Sats", s.Driver.FindElement(By.ClassName("payment__currencies_noborder")).Text);
|
||||
}
|
||||
var invoiceId = s.CreateInvoice(10, "USD", "a@g.com");
|
||||
s.GoToInvoiceCheckout(invoiceId);
|
||||
Assert.Contains("Sats", s.Driver.FindElement(By.ClassName("payment__currencies_noborder")).Text);
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
public async Task CanUseJSModal()
|
||||
{
|
||||
using (var s = CreateSeleniumTester())
|
||||
using var s = CreateSeleniumTester();
|
||||
await s.StartAsync();
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser();
|
||||
s.CreateNewStore();
|
||||
s.GoToStore();
|
||||
s.AddDerivationScheme();
|
||||
var invoiceId = s.CreateInvoice(0.001m, "BTC", "a@x.com");
|
||||
var invoice = await s.Server.PayTester.InvoiceRepository.GetInvoice(invoiceId);
|
||||
s.Driver.Navigate()
|
||||
.GoToUrl(new Uri(s.ServerUri, $"tests/index.html?invoice={invoiceId}"));
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
await s.StartAsync();
|
||||
s.GoToRegister();
|
||||
s.RegisterNewUser();
|
||||
s.CreateNewStore();
|
||||
s.GoToStore();
|
||||
s.AddDerivationScheme();
|
||||
var invoiceId = s.CreateInvoice(0.001m, "BTC", "a@x.com");
|
||||
var invoice = await s.Server.PayTester.InvoiceRepository.GetInvoice(invoiceId);
|
||||
s.Driver.Navigate()
|
||||
.GoToUrl(new Uri(s.ServerUri, $"tests/index.html?invoice={invoiceId}"));
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
Assert.True(s.Driver.FindElement(By.Name("btcpay")).Displayed);
|
||||
});
|
||||
await s.Server.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(invoice
|
||||
.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike))
|
||||
.GetPaymentMethodDetails().GetPaymentDestination(), Network.RegTest),
|
||||
new Money(0.001m, MoneyUnit.BTC));
|
||||
Assert.True(s.Driver.FindElement(By.Name("btcpay")).Displayed);
|
||||
});
|
||||
await s.Server.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(invoice
|
||||
.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike))
|
||||
.GetPaymentMethodDetails().GetPaymentDestination(), Network.RegTest),
|
||||
new Money(0.001m, MoneyUnit.BTC));
|
||||
|
||||
IWebElement closebutton = null;
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
var frameElement = s.Driver.FindElement(By.Name("btcpay"));
|
||||
var iframe = s.Driver.SwitchTo().Frame(frameElement);
|
||||
closebutton = iframe.FindElement(By.ClassName("close-action"));
|
||||
Assert.True(closebutton.Displayed);
|
||||
});
|
||||
closebutton.Click();
|
||||
s.Driver.AssertElementNotFound(By.Name("btcpay"));
|
||||
Assert.Equal(s.Driver.Url,
|
||||
new Uri(s.ServerUri, $"tests/index.html?invoice={invoiceId}").ToString());
|
||||
}
|
||||
IWebElement closebutton = null;
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
var frameElement = s.Driver.FindElement(By.Name("btcpay"));
|
||||
var iframe = s.Driver.SwitchTo().Frame(frameElement);
|
||||
closebutton = iframe.FindElement(By.ClassName("close-action"));
|
||||
Assert.True(closebutton.Displayed);
|
||||
});
|
||||
closebutton.Click();
|
||||
s.Driver.AssertElementNotFound(By.Name("btcpay"));
|
||||
Assert.Equal(s.Driver.Url,
|
||||
new Uri(s.ServerUri, $"tests/index.html?invoice={invoiceId}").ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,256 +26,250 @@ namespace BTCPayServer.Tests
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanCreateAndDeleteCrowdfundApp()
|
||||
{
|
||||
using (var tester = CreateServerTester())
|
||||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync();
|
||||
var user2 = tester.NewAccount();
|
||||
await user2.GrantAccessAsync();
|
||||
var apps = user.GetController<UIAppsController>();
|
||||
var apps2 = user2.GetController<UIAppsController>();
|
||||
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp(user.StoreId)).Model);
|
||||
Assert.NotNull(vm.SelectedAppType);
|
||||
Assert.Null(vm.AppName);
|
||||
vm.AppName = "test";
|
||||
vm.SelectedAppType = AppType.Crowdfund.ToString();
|
||||
var redirectToAction = Assert.IsType<RedirectToActionResult>(apps.CreateApp(user.StoreId, vm).Result);
|
||||
Assert.Equal(nameof(apps.UpdateCrowdfund), redirectToAction.ActionName);
|
||||
var appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model);
|
||||
var app = appList.Apps[0];
|
||||
apps.HttpContext.SetAppData(new AppData { Id = app.Id, StoreDataId = app.StoreId, Name = app.AppName });
|
||||
var appList2 =
|
||||
Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps2.ListApps(user2.StoreId).Result).Model);
|
||||
Assert.Single(appList.Apps);
|
||||
Assert.Empty(appList2.Apps);
|
||||
Assert.Equal("test", appList.Apps[0].AppName);
|
||||
Assert.Equal(apps.CreatedAppId, appList.Apps[0].Id);
|
||||
Assert.True(appList.Apps[0].IsOwner);
|
||||
Assert.Equal(user.StoreId, appList.Apps[0].StoreId);
|
||||
Assert.IsType<NotFoundResult>(apps2.DeleteApp(appList.Apps[0].Id));
|
||||
Assert.IsType<ViewResult>(apps.DeleteApp(appList.Apps[0].Id));
|
||||
redirectToAction = Assert.IsType<RedirectToActionResult>(apps.DeleteAppPost(appList.Apps[0].Id).Result);
|
||||
Assert.Equal(nameof(apps.ListApps), redirectToAction.ActionName);
|
||||
appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model);
|
||||
Assert.Empty(appList.Apps);
|
||||
}
|
||||
using var tester = CreateServerTester();
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync();
|
||||
var user2 = tester.NewAccount();
|
||||
await user2.GrantAccessAsync();
|
||||
var apps = user.GetController<UIAppsController>();
|
||||
var apps2 = user2.GetController<UIAppsController>();
|
||||
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp(user.StoreId)).Model);
|
||||
Assert.NotNull(vm.SelectedAppType);
|
||||
Assert.Null(vm.AppName);
|
||||
vm.AppName = "test";
|
||||
vm.SelectedAppType = AppType.Crowdfund.ToString();
|
||||
var redirectToAction = Assert.IsType<RedirectToActionResult>(apps.CreateApp(user.StoreId, vm).Result);
|
||||
Assert.Equal(nameof(apps.UpdateCrowdfund), redirectToAction.ActionName);
|
||||
var appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model);
|
||||
var app = appList.Apps[0];
|
||||
apps.HttpContext.SetAppData(new AppData { Id = app.Id, StoreDataId = app.StoreId, Name = app.AppName });
|
||||
var appList2 =
|
||||
Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps2.ListApps(user2.StoreId).Result).Model);
|
||||
Assert.Single(appList.Apps);
|
||||
Assert.Empty(appList2.Apps);
|
||||
Assert.Equal("test", appList.Apps[0].AppName);
|
||||
Assert.Equal(apps.CreatedAppId, appList.Apps[0].Id);
|
||||
Assert.True(appList.Apps[0].IsOwner);
|
||||
Assert.Equal(user.StoreId, appList.Apps[0].StoreId);
|
||||
Assert.IsType<NotFoundResult>(apps2.DeleteApp(appList.Apps[0].Id));
|
||||
Assert.IsType<ViewResult>(apps.DeleteApp(appList.Apps[0].Id));
|
||||
redirectToAction = Assert.IsType<RedirectToActionResult>(apps.DeleteAppPost(appList.Apps[0].Id).Result);
|
||||
Assert.Equal(nameof(apps.ListApps), redirectToAction.ActionName);
|
||||
appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model);
|
||||
Assert.Empty(appList.Apps);
|
||||
}
|
||||
|
||||
[Fact(Timeout = LongRunningTestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanContributeOnlyWhenAllowed()
|
||||
{
|
||||
using (var tester = CreateServerTester())
|
||||
using var tester = CreateServerTester();
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
var apps = user.GetController<UIAppsController>();
|
||||
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp(user.StoreId)).Model);
|
||||
vm.AppName = "test";
|
||||
vm.SelectedAppType = AppType.Crowdfund.ToString();
|
||||
Assert.IsType<RedirectToActionResult>(apps.CreateApp(user.StoreId, vm).Result);
|
||||
var appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model);
|
||||
var app = appList.Apps[0];
|
||||
apps.HttpContext.SetAppData(new AppData { Id = app.Id, StoreDataId = app.StoreId, Name = app.AppName });
|
||||
|
||||
//Scenario 1: Not Enabled - Not Allowed
|
||||
var crowdfundViewModel = Assert.IsType<UpdateCrowdfundViewModel>(Assert
|
||||
.IsType<ViewResult>(apps.UpdateCrowdfund(app.Id)).Model);
|
||||
crowdfundViewModel.TargetCurrency = "BTC";
|
||||
crowdfundViewModel.Enabled = false;
|
||||
crowdfundViewModel.EndDate = null;
|
||||
|
||||
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result);
|
||||
|
||||
var anonAppPubsController = tester.PayTester.GetController<UIAppsPublicController>();
|
||||
var publicApps = user.GetController<UIAppsPublicController>();
|
||||
|
||||
Assert.IsType<NotFoundObjectResult>(await anonAppPubsController.ContributeToCrowdfund(app.Id, new ContributeToCrowdfund()
|
||||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
var apps = user.GetController<UIAppsController>();
|
||||
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp(user.StoreId)).Model);
|
||||
vm.AppName = "test";
|
||||
vm.SelectedAppType = AppType.Crowdfund.ToString();
|
||||
Assert.IsType<RedirectToActionResult>(apps.CreateApp(user.StoreId, vm).Result);
|
||||
var appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model);
|
||||
var app = appList.Apps[0];
|
||||
apps.HttpContext.SetAppData(new AppData { Id = app.Id, StoreDataId = app.StoreId, Name = app.AppName });
|
||||
Amount = new decimal(0.01)
|
||||
}, default));
|
||||
|
||||
//Scenario 1: Not Enabled - Not Allowed
|
||||
var crowdfundViewModel = Assert.IsType<UpdateCrowdfundViewModel>(Assert
|
||||
.IsType<ViewResult>(apps.UpdateCrowdfund(app.Id)).Model);
|
||||
crowdfundViewModel.TargetCurrency = "BTC";
|
||||
crowdfundViewModel.Enabled = false;
|
||||
crowdfundViewModel.EndDate = null;
|
||||
Assert.IsType<NotFoundResult>(await anonAppPubsController.ViewCrowdfund(app.Id, string.Empty));
|
||||
|
||||
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result);
|
||||
//Scenario 2: Not Enabled But Admin - Allowed
|
||||
Assert.IsType<OkObjectResult>(await publicApps.ContributeToCrowdfund(app.Id, new ContributeToCrowdfund()
|
||||
{
|
||||
RedirectToCheckout = false,
|
||||
Amount = new decimal(0.01)
|
||||
}, default));
|
||||
Assert.IsType<ViewResult>(await publicApps.ViewCrowdfund(app.Id, string.Empty));
|
||||
Assert.IsType<NotFoundResult>(await anonAppPubsController.ViewCrowdfund(app.Id, string.Empty));
|
||||
|
||||
var anonAppPubsController = tester.PayTester.GetController<UIAppsPublicController>();
|
||||
var publicApps = user.GetController<UIAppsPublicController>();
|
||||
//Scenario 3: Enabled But Start Date > Now - Not Allowed
|
||||
crowdfundViewModel.StartDate = DateTime.Today.AddDays(2);
|
||||
crowdfundViewModel.Enabled = true;
|
||||
|
||||
Assert.IsType<NotFoundObjectResult>(await anonAppPubsController.ContributeToCrowdfund(app.Id, new ContributeToCrowdfund()
|
||||
{
|
||||
Amount = new decimal(0.01)
|
||||
}, default));
|
||||
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result);
|
||||
Assert.IsType<NotFoundObjectResult>(await anonAppPubsController.ContributeToCrowdfund(app.Id, new ContributeToCrowdfund()
|
||||
{
|
||||
Amount = new decimal(0.01)
|
||||
}, default));
|
||||
|
||||
Assert.IsType<NotFoundResult>(await anonAppPubsController.ViewCrowdfund(app.Id, string.Empty));
|
||||
//Scenario 4: Enabled But End Date < Now - Not Allowed
|
||||
crowdfundViewModel.StartDate = DateTime.Today.AddDays(-2);
|
||||
crowdfundViewModel.EndDate = DateTime.Today.AddDays(-1);
|
||||
crowdfundViewModel.Enabled = true;
|
||||
|
||||
//Scenario 2: Not Enabled But Admin - Allowed
|
||||
Assert.IsType<OkObjectResult>(await publicApps.ContributeToCrowdfund(app.Id, new ContributeToCrowdfund()
|
||||
{
|
||||
RedirectToCheckout = false,
|
||||
Amount = new decimal(0.01)
|
||||
}, default));
|
||||
Assert.IsType<ViewResult>(await publicApps.ViewCrowdfund(app.Id, string.Empty));
|
||||
Assert.IsType<NotFoundResult>(await anonAppPubsController.ViewCrowdfund(app.Id, string.Empty));
|
||||
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result);
|
||||
Assert.IsType<NotFoundObjectResult>(await anonAppPubsController.ContributeToCrowdfund(app.Id, new ContributeToCrowdfund()
|
||||
{
|
||||
Amount = new decimal(0.01)
|
||||
}, default));
|
||||
|
||||
//Scenario 3: Enabled But Start Date > Now - Not Allowed
|
||||
crowdfundViewModel.StartDate = DateTime.Today.AddDays(2);
|
||||
crowdfundViewModel.Enabled = true;
|
||||
//Scenario 5: Enabled and within correct timeframe, however target is enforced and Amount is Over - Not Allowed
|
||||
crowdfundViewModel.StartDate = DateTime.Today.AddDays(-2);
|
||||
crowdfundViewModel.EndDate = DateTime.Today.AddDays(2);
|
||||
crowdfundViewModel.Enabled = true;
|
||||
crowdfundViewModel.TargetAmount = 1;
|
||||
crowdfundViewModel.TargetCurrency = "BTC";
|
||||
crowdfundViewModel.EnforceTargetAmount = true;
|
||||
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result);
|
||||
Assert.IsType<NotFoundObjectResult>(await anonAppPubsController.ContributeToCrowdfund(app.Id, new ContributeToCrowdfund()
|
||||
{
|
||||
Amount = new decimal(1.01)
|
||||
}, default));
|
||||
|
||||
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result);
|
||||
Assert.IsType<NotFoundObjectResult>(await anonAppPubsController.ContributeToCrowdfund(app.Id, new ContributeToCrowdfund()
|
||||
{
|
||||
Amount = new decimal(0.01)
|
||||
}, default));
|
||||
|
||||
//Scenario 4: Enabled But End Date < Now - Not Allowed
|
||||
crowdfundViewModel.StartDate = DateTime.Today.AddDays(-2);
|
||||
crowdfundViewModel.EndDate = DateTime.Today.AddDays(-1);
|
||||
crowdfundViewModel.Enabled = true;
|
||||
|
||||
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result);
|
||||
Assert.IsType<NotFoundObjectResult>(await anonAppPubsController.ContributeToCrowdfund(app.Id, new ContributeToCrowdfund()
|
||||
{
|
||||
Amount = new decimal(0.01)
|
||||
}, default));
|
||||
|
||||
//Scenario 5: Enabled and within correct timeframe, however target is enforced and Amount is Over - Not Allowed
|
||||
crowdfundViewModel.StartDate = DateTime.Today.AddDays(-2);
|
||||
crowdfundViewModel.EndDate = DateTime.Today.AddDays(2);
|
||||
crowdfundViewModel.Enabled = true;
|
||||
crowdfundViewModel.TargetAmount = 1;
|
||||
crowdfundViewModel.TargetCurrency = "BTC";
|
||||
crowdfundViewModel.EnforceTargetAmount = true;
|
||||
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result);
|
||||
Assert.IsType<NotFoundObjectResult>(await anonAppPubsController.ContributeToCrowdfund(app.Id, new ContributeToCrowdfund()
|
||||
{
|
||||
Amount = new decimal(1.01)
|
||||
}, default));
|
||||
|
||||
//Scenario 6: Allowed
|
||||
Assert.IsType<OkObjectResult>(await anonAppPubsController.ContributeToCrowdfund(app.Id, new ContributeToCrowdfund()
|
||||
{
|
||||
Amount = new decimal(0.05)
|
||||
}, default));
|
||||
}
|
||||
//Scenario 6: Allowed
|
||||
Assert.IsType<OkObjectResult>(await anonAppPubsController.ContributeToCrowdfund(app.Id, new ContributeToCrowdfund()
|
||||
{
|
||||
Amount = new decimal(0.05)
|
||||
}, default));
|
||||
}
|
||||
|
||||
[Fact(Timeout = LongRunningTestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanComputeCrowdfundModel()
|
||||
{
|
||||
using (var tester = CreateServerTester())
|
||||
using var tester = CreateServerTester();
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
await user.SetNetworkFeeMode(NetworkFeeMode.Never);
|
||||
var apps = user.GetController<UIAppsController>();
|
||||
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp(user.StoreId)).Model);
|
||||
vm.AppName = "test";
|
||||
vm.SelectedAppType = AppType.Crowdfund.ToString();
|
||||
Assert.IsType<RedirectToActionResult>(apps.CreateApp(user.StoreId, vm).Result);
|
||||
var appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model);
|
||||
var app = appList.Apps[0];
|
||||
apps.HttpContext.SetAppData(new AppData { Id = app.Id, StoreDataId = app.StoreId, Name = app.AppName });
|
||||
|
||||
TestLogs.LogInformation("We create an invoice with a hardcap");
|
||||
var crowdfundViewModel = Assert.IsType<UpdateCrowdfundViewModel>(Assert
|
||||
.IsType<ViewResult>(apps.UpdateCrowdfund(app.Id)).Model);
|
||||
crowdfundViewModel.Enabled = true;
|
||||
crowdfundViewModel.EndDate = null;
|
||||
crowdfundViewModel.TargetAmount = 100;
|
||||
crowdfundViewModel.TargetCurrency = "BTC";
|
||||
crowdfundViewModel.UseAllStoreInvoices = true;
|
||||
crowdfundViewModel.EnforceTargetAmount = true;
|
||||
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result);
|
||||
|
||||
var anonAppPubsController = tester.PayTester.GetController<UIAppsPublicController>();
|
||||
var publicApps = user.GetController<UIAppsPublicController>();
|
||||
|
||||
var model = Assert.IsType<ViewCrowdfundViewModel>(Assert
|
||||
.IsType<ViewResult>(publicApps.ViewCrowdfund(app.Id, String.Empty).Result).Model);
|
||||
|
||||
Assert.Equal(crowdfundViewModel.TargetAmount, model.TargetAmount);
|
||||
Assert.Equal(crowdfundViewModel.EndDate, model.EndDate);
|
||||
Assert.Equal(crowdfundViewModel.StartDate, model.StartDate);
|
||||
Assert.Equal(crowdfundViewModel.TargetCurrency, model.TargetCurrency);
|
||||
Assert.Equal(0m, model.Info.CurrentAmount);
|
||||
Assert.Equal(0m, model.Info.CurrentPendingAmount);
|
||||
Assert.Equal(0m, model.Info.ProgressPercentage);
|
||||
|
||||
TestLogs.LogInformation("Unpaid invoices should show as pending contribution because it is hardcap");
|
||||
TestLogs.LogInformation("Because UseAllStoreInvoices is true, we can manually create an invoice and it should show as contribution");
|
||||
var invoice = await user.BitPay.CreateInvoiceAsync(new Invoice
|
||||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
await user.SetNetworkFeeMode(NetworkFeeMode.Never);
|
||||
var apps = user.GetController<UIAppsController>();
|
||||
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp(user.StoreId)).Model);
|
||||
vm.AppName = "test";
|
||||
vm.SelectedAppType = AppType.Crowdfund.ToString();
|
||||
Assert.IsType<RedirectToActionResult>(apps.CreateApp(user.StoreId, vm).Result);
|
||||
var appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model);
|
||||
var app = appList.Apps[0];
|
||||
apps.HttpContext.SetAppData(new AppData { Id = app.Id, StoreDataId = app.StoreId, Name = app.AppName });
|
||||
Buyer = new Buyer() { email = "test@fwf.com" },
|
||||
Price = 1m,
|
||||
Currency = "BTC",
|
||||
PosData = "posData",
|
||||
ItemDesc = "Some description",
|
||||
TransactionSpeed = "high",
|
||||
FullNotifications = true
|
||||
}, Facade.Merchant);
|
||||
|
||||
TestLogs.LogInformation("We create an invoice with a hardcap");
|
||||
var crowdfundViewModel = Assert.IsType<UpdateCrowdfundViewModel>(Assert
|
||||
.IsType<ViewResult>(apps.UpdateCrowdfund(app.Id)).Model);
|
||||
crowdfundViewModel.Enabled = true;
|
||||
crowdfundViewModel.EndDate = null;
|
||||
crowdfundViewModel.TargetAmount = 100;
|
||||
crowdfundViewModel.TargetCurrency = "BTC";
|
||||
crowdfundViewModel.UseAllStoreInvoices = true;
|
||||
crowdfundViewModel.EnforceTargetAmount = true;
|
||||
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result);
|
||||
model = Assert.IsType<ViewCrowdfundViewModel>(Assert
|
||||
.IsType<ViewResult>(publicApps.ViewCrowdfund(app.Id, string.Empty).Result).Model);
|
||||
|
||||
var anonAppPubsController = tester.PayTester.GetController<UIAppsPublicController>();
|
||||
var publicApps = user.GetController<UIAppsPublicController>();
|
||||
|
||||
var model = Assert.IsType<ViewCrowdfundViewModel>(Assert
|
||||
.IsType<ViewResult>(publicApps.ViewCrowdfund(app.Id, String.Empty).Result).Model);
|
||||
|
||||
Assert.Equal(crowdfundViewModel.TargetAmount, model.TargetAmount);
|
||||
Assert.Equal(crowdfundViewModel.EndDate, model.EndDate);
|
||||
Assert.Equal(crowdfundViewModel.StartDate, model.StartDate);
|
||||
Assert.Equal(crowdfundViewModel.TargetCurrency, model.TargetCurrency);
|
||||
Assert.Equal(0m, model.Info.CurrentAmount);
|
||||
Assert.Equal(0m, model.Info.CurrentPendingAmount);
|
||||
Assert.Equal(0m, model.Info.ProgressPercentage);
|
||||
|
||||
TestLogs.LogInformation("Unpaid invoices should show as pending contribution because it is hardcap");
|
||||
TestLogs.LogInformation("Because UseAllStoreInvoices is true, we can manually create an invoice and it should show as contribution");
|
||||
var invoice = await user.BitPay.CreateInvoiceAsync(new Invoice
|
||||
{
|
||||
Buyer = new Buyer() { email = "test@fwf.com" },
|
||||
Price = 1m,
|
||||
Currency = "BTC",
|
||||
PosData = "posData",
|
||||
ItemDesc = "Some description",
|
||||
TransactionSpeed = "high",
|
||||
FullNotifications = true
|
||||
}, Facade.Merchant);
|
||||
Assert.Equal(0m, model.Info.CurrentAmount);
|
||||
Assert.Equal(1m, model.Info.CurrentPendingAmount);
|
||||
Assert.Equal(0m, model.Info.ProgressPercentage);
|
||||
Assert.Equal(1m, model.Info.PendingProgressPercentage);
|
||||
|
||||
TestLogs.LogInformation("Let's check current amount change once payment is confirmed");
|
||||
var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, tester.ExplorerNode.Network);
|
||||
tester.ExplorerNode.SendToAddress(invoiceAddress, invoice.BtcDue);
|
||||
tester.ExplorerNode.Generate(1); // By default invoice confirmed at 1 block
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
model = Assert.IsType<ViewCrowdfundViewModel>(Assert
|
||||
.IsType<ViewResult>(publicApps.ViewCrowdfund(app.Id, string.Empty).Result).Model);
|
||||
|
||||
Assert.Equal(0m, model.Info.CurrentAmount);
|
||||
Assert.Equal(1m, model.Info.CurrentPendingAmount);
|
||||
Assert.Equal(0m, model.Info.ProgressPercentage);
|
||||
Assert.Equal(1m, model.Info.PendingProgressPercentage);
|
||||
|
||||
TestLogs.LogInformation("Let's check current amount change once payment is confirmed");
|
||||
var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, tester.ExplorerNode.Network);
|
||||
tester.ExplorerNode.SendToAddress(invoiceAddress, invoice.BtcDue);
|
||||
tester.ExplorerNode.Generate(1); // By default invoice confirmed at 1 block
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
model = Assert.IsType<ViewCrowdfundViewModel>(Assert
|
||||
.IsType<ViewResult>(publicApps.ViewCrowdfund(app.Id, String.Empty).Result).Model);
|
||||
Assert.Equal(1m, model.Info.CurrentAmount);
|
||||
Assert.Equal(0m, model.Info.CurrentPendingAmount);
|
||||
});
|
||||
|
||||
TestLogs.LogInformation("Because UseAllStoreInvoices is true, let's make sure the invoice is tagged");
|
||||
var invoiceEntity = tester.PayTester.InvoiceRepository.GetInvoice(invoice.Id).GetAwaiter().GetResult();
|
||||
Assert.True(invoiceEntity.Version >= InvoiceEntity.InternalTagSupport_Version);
|
||||
Assert.Contains(AppService.GetAppInternalTag(app.Id), invoiceEntity.InternalTags);
|
||||
|
||||
crowdfundViewModel.UseAllStoreInvoices = false;
|
||||
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result);
|
||||
|
||||
TestLogs.LogInformation("Because UseAllStoreInvoices is false, let's make sure the invoice is not tagged");
|
||||
invoice = await user.BitPay.CreateInvoiceAsync(new Invoice
|
||||
{
|
||||
Buyer = new Buyer { email = "test@fwf.com" },
|
||||
Price = 1m,
|
||||
Currency = "BTC",
|
||||
PosData = "posData",
|
||||
ItemDesc = "Some description",
|
||||
TransactionSpeed = "high",
|
||||
FullNotifications = true
|
||||
}, Facade.Merchant);
|
||||
invoiceEntity = tester.PayTester.InvoiceRepository.GetInvoice(invoice.Id).GetAwaiter().GetResult();
|
||||
Assert.DoesNotContain(AppService.GetAppInternalTag(app.Id), invoiceEntity.InternalTags);
|
||||
|
||||
TestLogs.LogInformation("After turning setting a softcap, let's check that only actual payments are counted");
|
||||
crowdfundViewModel.EnforceTargetAmount = false;
|
||||
crowdfundViewModel.UseAllStoreInvoices = true;
|
||||
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result);
|
||||
invoice = await user.BitPay.CreateInvoiceAsync(new Invoice
|
||||
{
|
||||
Buyer = new Buyer { email = "test@fwf.com" },
|
||||
Price = 1m,
|
||||
Currency = "BTC",
|
||||
PosData = "posData",
|
||||
ItemDesc = "Some description",
|
||||
TransactionSpeed = "high",
|
||||
FullNotifications = true
|
||||
}, Facade.Merchant);
|
||||
.IsType<ViewResult>(publicApps.ViewCrowdfund(app.Id, String.Empty).Result).Model);
|
||||
Assert.Equal(1m, model.Info.CurrentAmount);
|
||||
Assert.Equal(0m, model.Info.CurrentPendingAmount);
|
||||
invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, tester.ExplorerNode.Network);
|
||||
await tester.ExplorerNode.SendToAddressAsync(invoiceAddress, Money.Coins(0.5m));
|
||||
await tester.ExplorerNode.SendToAddressAsync(invoiceAddress, Money.Coins(0.2m));
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
model = Assert.IsType<ViewCrowdfundViewModel>(Assert
|
||||
.IsType<ViewResult>(publicApps.ViewCrowdfund(app.Id, string.Empty).Result).Model);
|
||||
Assert.Equal(0.7m, model.Info.CurrentPendingAmount);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
TestLogs.LogInformation("Because UseAllStoreInvoices is true, let's make sure the invoice is tagged");
|
||||
var invoiceEntity = tester.PayTester.InvoiceRepository.GetInvoice(invoice.Id).GetAwaiter().GetResult();
|
||||
Assert.True(invoiceEntity.Version >= InvoiceEntity.InternalTagSupport_Version);
|
||||
Assert.Contains(AppService.GetAppInternalTag(app.Id), invoiceEntity.InternalTags);
|
||||
|
||||
crowdfundViewModel.UseAllStoreInvoices = false;
|
||||
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result);
|
||||
|
||||
TestLogs.LogInformation("Because UseAllStoreInvoices is false, let's make sure the invoice is not tagged");
|
||||
invoice = await user.BitPay.CreateInvoiceAsync(new Invoice
|
||||
{
|
||||
Buyer = new Buyer { email = "test@fwf.com" },
|
||||
Price = 1m,
|
||||
Currency = "BTC",
|
||||
PosData = "posData",
|
||||
ItemDesc = "Some description",
|
||||
TransactionSpeed = "high",
|
||||
FullNotifications = true
|
||||
}, Facade.Merchant);
|
||||
invoiceEntity = tester.PayTester.InvoiceRepository.GetInvoice(invoice.Id).GetAwaiter().GetResult();
|
||||
Assert.DoesNotContain(AppService.GetAppInternalTag(app.Id), invoiceEntity.InternalTags);
|
||||
|
||||
TestLogs.LogInformation("After turning setting a softcap, let's check that only actual payments are counted");
|
||||
crowdfundViewModel.EnforceTargetAmount = false;
|
||||
crowdfundViewModel.UseAllStoreInvoices = true;
|
||||
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result);
|
||||
invoice = await user.BitPay.CreateInvoiceAsync(new Invoice
|
||||
{
|
||||
Buyer = new Buyer { email = "test@fwf.com" },
|
||||
Price = 1m,
|
||||
Currency = "BTC",
|
||||
PosData = "posData",
|
||||
ItemDesc = "Some description",
|
||||
TransactionSpeed = "high",
|
||||
FullNotifications = true
|
||||
}, Facade.Merchant);
|
||||
Assert.Equal(0m, model.Info.CurrentPendingAmount);
|
||||
invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, tester.ExplorerNode.Network);
|
||||
await tester.ExplorerNode.SendToAddressAsync(invoiceAddress, Money.Coins(0.5m));
|
||||
await tester.ExplorerNode.SendToAddressAsync(invoiceAddress, Money.Coins(0.2m));
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
model = Assert.IsType<ViewCrowdfundViewModel>(Assert
|
||||
.IsType<ViewResult>(publicApps.ViewCrowdfund(app.Id, string.Empty).Result).Model);
|
||||
Assert.Equal(0.7m, model.Info.CurrentPendingAmount);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,22 +42,20 @@ namespace BTCPayServer.Tests
|
||||
|
||||
public async Task<JObject> GetNextRequest()
|
||||
{
|
||||
using (CancellationTokenSource cancellation = new CancellationTokenSource(2000000))
|
||||
using CancellationTokenSource cancellation = new CancellationTokenSource(2000000);
|
||||
try
|
||||
{
|
||||
try
|
||||
JObject req = null;
|
||||
while (!await _Requests.Reader.WaitToReadAsync(cancellation.Token) ||
|
||||
!_Requests.Reader.TryRead(out req))
|
||||
{
|
||||
JObject req = null;
|
||||
while (!await _Requests.Reader.WaitToReadAsync(cancellation.Token) ||
|
||||
!_Requests.Reader.TryRead(out req))
|
||||
{
|
||||
|
||||
}
|
||||
return req;
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
throw new Xunit.Sdk.XunitException("Callback to the webserver was expected, check if the callback url is accessible from internet");
|
||||
}
|
||||
return req;
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
throw new Xunit.Sdk.XunitException("Callback to the webserver was expected, check if the callback url is accessible from internet");
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -18,34 +18,32 @@ namespace BTCPayServer.Tests
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanAutoDetectLanguage()
|
||||
{
|
||||
using (var tester = CreateServerTester())
|
||||
{
|
||||
await tester.StartAsync();
|
||||
var languageService = tester.PayTester.GetService<LanguageService>();
|
||||
using var tester = CreateServerTester();
|
||||
await tester.StartAsync();
|
||||
var languageService = tester.PayTester.GetService<LanguageService>();
|
||||
|
||||
// Most common format. First option does not have a quality score. Others do in descending order.
|
||||
// Result should be nl-NL (because the default weight is 1 for nl)
|
||||
var lang1 = languageService.FindLanguageInAcceptLanguageHeader("nl,fr;q=0.7,en;q=0.5");
|
||||
Assert.NotNull(lang1);
|
||||
Assert.Equal("nl-NL", lang1?.Code);
|
||||
// Most common format. First option does not have a quality score. Others do in descending order.
|
||||
// Result should be nl-NL (because the default weight is 1 for nl)
|
||||
var lang1 = languageService.FindLanguageInAcceptLanguageHeader("nl,fr;q=0.7,en;q=0.5");
|
||||
Assert.NotNull(lang1);
|
||||
Assert.Equal("nl-NL", lang1?.Code);
|
||||
|
||||
// Most common format. First option does not have a quality score. Others do in descending order. This time the first option includes a country.
|
||||
// Result should be nl-NL (because the default weight is 1 for nl-BE and it does not exist in BTCPay Server, but nl-NL does and applies too for language "nl")
|
||||
var lang2 = languageService.FindLanguageInAcceptLanguageHeader("nl-BE,fr;q=0.7,en;q=0.5");
|
||||
Assert.NotNull(lang2);
|
||||
Assert.Equal("nl-NL", lang2?.Code);
|
||||
// Most common format. First option does not have a quality score. Others do in descending order. This time the first option includes a country.
|
||||
// Result should be nl-NL (because the default weight is 1 for nl-BE and it does not exist in BTCPay Server, but nl-NL does and applies too for language "nl")
|
||||
var lang2 = languageService.FindLanguageInAcceptLanguageHeader("nl-BE,fr;q=0.7,en;q=0.5");
|
||||
Assert.NotNull(lang2);
|
||||
Assert.Equal("nl-NL", lang2?.Code);
|
||||
|
||||
// Unusual format, but still valid. All values have a quality score and not ordered.
|
||||
// Result should be fr-FR (because 0.7 is the highest quality score)
|
||||
var lang3 = languageService.FindLanguageInAcceptLanguageHeader("nl;q=0.1,fr;q=0.7,en;q=0.5");
|
||||
Assert.NotNull(lang3);
|
||||
Assert.Equal("fr-FR", lang3?.Code);
|
||||
// Unusual format, but still valid. All values have a quality score and not ordered.
|
||||
// Result should be fr-FR (because 0.7 is the highest quality score)
|
||||
var lang3 = languageService.FindLanguageInAcceptLanguageHeader("nl;q=0.1,fr;q=0.7,en;q=0.5");
|
||||
Assert.NotNull(lang3);
|
||||
Assert.Equal("fr-FR", lang3?.Code);
|
||||
|
||||
// Unusual format, but still valid. Some language is given that we don't have and a wildcard for everything else.
|
||||
// Result should be NULL, because "xx" does not exist and * is a wildcard and has no meaning.
|
||||
var lang4 = languageService.FindLanguageInAcceptLanguageHeader("xx,*;q=0.5");
|
||||
Assert.Null(lang4);
|
||||
}
|
||||
// Unusual format, but still valid. Some language is given that we don't have and a wildcard for everything else.
|
||||
// Result should be NULL, because "xx" does not exist and * is a wildcard and has no meaning.
|
||||
var lang4 = languageService.FindLanguageInAcceptLanguageHeader("xx,*;q=0.5");
|
||||
Assert.Null(lang4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,23 +22,22 @@ namespace BTCPayServer.Tests
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanUsePoSApp1()
|
||||
{
|
||||
using (var tester = CreateServerTester())
|
||||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
var apps = user.GetController<UIAppsController>();
|
||||
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp(user.StoreId)).Model);
|
||||
vm.AppName = "test";
|
||||
vm.SelectedAppType = AppType.PointOfSale.ToString();
|
||||
Assert.IsType<RedirectToActionResult>(apps.CreateApp(user.StoreId, vm).Result);
|
||||
var appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model);
|
||||
var app = appList.Apps[0];
|
||||
apps.HttpContext.SetAppData(new AppData { Id = app.Id, StoreDataId = app.StoreId, Name = app.AppName });
|
||||
var vmpos = Assert.IsType<UpdatePointOfSaleViewModel>(Assert
|
||||
.IsType<ViewResult>(apps.UpdatePointOfSale(app.Id)).Model);
|
||||
vmpos.Template = @"
|
||||
using var tester = CreateServerTester();
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
var apps = user.GetController<UIAppsController>();
|
||||
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp(user.StoreId)).Model);
|
||||
vm.AppName = "test";
|
||||
vm.SelectedAppType = AppType.PointOfSale.ToString();
|
||||
Assert.IsType<RedirectToActionResult>(apps.CreateApp(user.StoreId, vm).Result);
|
||||
var appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model);
|
||||
var app = appList.Apps[0];
|
||||
apps.HttpContext.SetAppData(new AppData { Id = app.Id, StoreDataId = app.StoreId, Name = app.AppName });
|
||||
var vmpos = Assert.IsType<UpdatePointOfSaleViewModel>(Assert
|
||||
.IsType<ViewResult>(apps.UpdatePointOfSale(app.Id)).Model);
|
||||
vmpos.Template = @"
|
||||
apple:
|
||||
price: 5.0
|
||||
title: good apple
|
||||
@ -49,25 +48,24 @@ donation:
|
||||
price: 1.02
|
||||
custom: true
|
||||
";
|
||||
Assert.IsType<RedirectToActionResult>(apps.UpdatePointOfSale(app.Id, vmpos).Result);
|
||||
vmpos = Assert.IsType<UpdatePointOfSaleViewModel>(Assert
|
||||
.IsType<ViewResult>(apps.UpdatePointOfSale(app.Id)).Model);
|
||||
var publicApps = user.GetController<UIAppsPublicController>();
|
||||
var vmview =
|
||||
Assert.IsType<ViewPointOfSaleViewModel>(Assert
|
||||
.IsType<ViewResult>(publicApps.ViewPointOfSale(app.Id, PosViewType.Cart).Result).Model);
|
||||
Assert.IsType<RedirectToActionResult>(apps.UpdatePointOfSale(app.Id, vmpos).Result);
|
||||
vmpos = Assert.IsType<UpdatePointOfSaleViewModel>(Assert
|
||||
.IsType<ViewResult>(apps.UpdatePointOfSale(app.Id)).Model);
|
||||
var publicApps = user.GetController<UIAppsPublicController>();
|
||||
var vmview =
|
||||
Assert.IsType<ViewPointOfSaleViewModel>(Assert
|
||||
.IsType<ViewResult>(publicApps.ViewPointOfSale(app.Id, PosViewType.Cart).Result).Model);
|
||||
|
||||
// apple shouldn't be available since we it's set to "disabled: true" above
|
||||
Assert.Equal(2, vmview.Items.Length);
|
||||
Assert.Equal("orange", vmview.Items[0].Title);
|
||||
Assert.Equal("donation", vmview.Items[1].Title);
|
||||
// orange is available
|
||||
Assert.IsType<RedirectToActionResult>(publicApps
|
||||
.ViewPointOfSale(app.Id, PosViewType.Cart, 0, null, null, null, null, "orange").Result);
|
||||
// apple is not found
|
||||
Assert.IsType<NotFoundResult>(publicApps
|
||||
.ViewPointOfSale(app.Id, PosViewType.Cart, 0, null, null, null, null, "apple").Result);
|
||||
}
|
||||
// apple shouldn't be available since we it's set to "disabled: true" above
|
||||
Assert.Equal(2, vmview.Items.Length);
|
||||
Assert.Equal("orange", vmview.Items[0].Title);
|
||||
Assert.Equal("donation", vmview.Items[1].Title);
|
||||
// orange is available
|
||||
Assert.IsType<RedirectToActionResult>(publicApps
|
||||
.ViewPointOfSale(app.Id, PosViewType.Cart, 0, null, null, null, null, "orange").Result);
|
||||
// apple is not found
|
||||
Assert.IsType<NotFoundResult>(publicApps
|
||||
.ViewPointOfSale(app.Id, PosViewType.Cart, 0, null, null, null, null, "apple").Result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,36 +23,35 @@ namespace BTCPayServer.Tests
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanPlayWithPSBT()
|
||||
{
|
||||
using (var tester = CreateServerTester())
|
||||
using var tester = CreateServerTester();
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
var invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
var invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||
{
|
||||
Price = 10,
|
||||
Currency = "USD",
|
||||
PosData = "posData",
|
||||
OrderId = "orderId",
|
||||
ItemDesc = "Some \", description",
|
||||
FullNotifications = true
|
||||
}, Facade.Merchant);
|
||||
var cashCow = tester.ExplorerNode;
|
||||
var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, cashCow.Network);
|
||||
cashCow.SendToAddress(invoiceAddress, Money.Coins(1.5m));
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
invoice = user.BitPay.GetInvoice(invoice.Id);
|
||||
Assert.Equal("paid", invoice.Status);
|
||||
});
|
||||
Price = 10,
|
||||
Currency = "USD",
|
||||
PosData = "posData",
|
||||
OrderId = "orderId",
|
||||
ItemDesc = "Some \", description",
|
||||
FullNotifications = true
|
||||
}, Facade.Merchant);
|
||||
var cashCow = tester.ExplorerNode;
|
||||
var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, cashCow.Network);
|
||||
cashCow.SendToAddress(invoiceAddress, Money.Coins(1.5m));
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
invoice = user.BitPay.GetInvoice(invoice.Id);
|
||||
Assert.Equal("paid", invoice.Status);
|
||||
});
|
||||
|
||||
var walletController = user.GetController<UIWalletsController>();
|
||||
var walletId = new WalletId(user.StoreId, "BTC");
|
||||
var sendDestination = new Key().PubKey.Hash.GetAddress(user.SupportedNetwork.NBitcoinNetwork).ToString();
|
||||
var sendModel = new WalletSendModel()
|
||||
{
|
||||
Outputs = new List<WalletSendModel.TransactionOutput>()
|
||||
var walletController = user.GetController<UIWalletsController>();
|
||||
var walletId = new WalletId(user.StoreId, "BTC");
|
||||
var sendDestination = new Key().PubKey.Hash.GetAddress(user.SupportedNetwork.NBitcoinNetwork).ToString();
|
||||
var sendModel = new WalletSendModel()
|
||||
{
|
||||
Outputs = new List<WalletSendModel.TransactionOutput>()
|
||||
{
|
||||
new WalletSendModel.TransactionOutput()
|
||||
{
|
||||
@ -60,81 +59,80 @@ namespace BTCPayServer.Tests
|
||||
Amount = 0.1m,
|
||||
}
|
||||
},
|
||||
FeeSatoshiPerByte = 1,
|
||||
CurrentBalance = 1.5m
|
||||
};
|
||||
FeeSatoshiPerByte = 1,
|
||||
CurrentBalance = 1.5m
|
||||
};
|
||||
|
||||
string redirectedPSBT = AssertRedirectedPSBT(await walletController.WalletSend(walletId, sendModel, command: "analyze-psbt"), nameof(walletController.WalletPSBT));
|
||||
var vmPSBT = await walletController.WalletPSBT(walletId, new WalletPSBTViewModel() { PSBT = redirectedPSBT }).AssertViewModelAsync<WalletPSBTViewModel>();
|
||||
var unsignedPSBT = PSBT.Parse(vmPSBT.PSBT, user.SupportedNetwork.NBitcoinNetwork);
|
||||
Assert.NotNull(vmPSBT.Decoded);
|
||||
string redirectedPSBT = AssertRedirectedPSBT(await walletController.WalletSend(walletId, sendModel, command: "analyze-psbt"), nameof(walletController.WalletPSBT));
|
||||
var vmPSBT = await walletController.WalletPSBT(walletId, new WalletPSBTViewModel() { PSBT = redirectedPSBT }).AssertViewModelAsync<WalletPSBTViewModel>();
|
||||
var unsignedPSBT = PSBT.Parse(vmPSBT.PSBT, user.SupportedNetwork.NBitcoinNetwork);
|
||||
Assert.NotNull(vmPSBT.Decoded);
|
||||
|
||||
var filePSBT = (FileContentResult)(await walletController.WalletPSBT(walletId, vmPSBT, "save-psbt"));
|
||||
PSBT.Load(filePSBT.FileContents, user.SupportedNetwork.NBitcoinNetwork);
|
||||
var filePSBT = (FileContentResult)(await walletController.WalletPSBT(walletId, vmPSBT, "save-psbt"));
|
||||
PSBT.Load(filePSBT.FileContents, user.SupportedNetwork.NBitcoinNetwork);
|
||||
|
||||
var vmPSBT2 = await walletController.WalletPSBT(walletId, new WalletPSBTViewModel
|
||||
var vmPSBT2 = await walletController.WalletPSBT(walletId, new WalletPSBTViewModel
|
||||
{
|
||||
SigningContext = new SigningContextModel
|
||||
{
|
||||
SigningContext = new SigningContextModel
|
||||
{
|
||||
PSBT = AssertRedirectedPSBT(await walletController.WalletPSBT(walletId, vmPSBT, "broadcast"), nameof(walletController.WalletPSBTReady))
|
||||
}
|
||||
}).AssertViewModelAsync<WalletPSBTViewModel>();
|
||||
Assert.NotEmpty(vmPSBT2.Inputs.Where(i => i.Error != null));
|
||||
Assert.Equal(vmPSBT.PSBT, vmPSBT2.SigningContext.PSBT);
|
||||
PSBT = AssertRedirectedPSBT(await walletController.WalletPSBT(walletId, vmPSBT, "broadcast"), nameof(walletController.WalletPSBTReady))
|
||||
}
|
||||
}).AssertViewModelAsync<WalletPSBTViewModel>();
|
||||
Assert.NotEmpty(vmPSBT2.Inputs.Where(i => i.Error != null));
|
||||
Assert.Equal(vmPSBT.PSBT, vmPSBT2.SigningContext.PSBT);
|
||||
|
||||
var signedPSBT = unsignedPSBT.Clone();
|
||||
signedPSBT.SignAll(user.DerivationScheme, user.GenerateWalletResponseV.AccountHDKey, user.GenerateWalletResponseV.AccountKeyPath);
|
||||
vmPSBT.PSBT = signedPSBT.ToBase64();
|
||||
var psbtReady = await walletController.WalletPSBT(walletId, new WalletPSBTViewModel
|
||||
var signedPSBT = unsignedPSBT.Clone();
|
||||
signedPSBT.SignAll(user.DerivationScheme, user.GenerateWalletResponseV.AccountHDKey, user.GenerateWalletResponseV.AccountKeyPath);
|
||||
vmPSBT.PSBT = signedPSBT.ToBase64();
|
||||
var psbtReady = await walletController.WalletPSBT(walletId, new WalletPSBTViewModel
|
||||
{
|
||||
SigningContext = new SigningContextModel
|
||||
{
|
||||
SigningContext = new SigningContextModel
|
||||
{
|
||||
PSBT = AssertRedirectedPSBT(await walletController.WalletPSBT(walletId, vmPSBT, "broadcast"), nameof(walletController.WalletPSBTReady))
|
||||
}
|
||||
}).AssertViewModelAsync<WalletPSBTViewModel>();
|
||||
Assert.Equal(2 + 1, psbtReady.Destinations.Count); // The fee is a destination
|
||||
Assert.Contains(psbtReady.Destinations, d => d.Destination == sendDestination && !d.Positive);
|
||||
Assert.Contains(psbtReady.Destinations, d => d.Positive);
|
||||
PSBT = AssertRedirectedPSBT(await walletController.WalletPSBT(walletId, vmPSBT, "broadcast"), nameof(walletController.WalletPSBTReady))
|
||||
}
|
||||
}).AssertViewModelAsync<WalletPSBTViewModel>();
|
||||
Assert.Equal(2 + 1, psbtReady.Destinations.Count); // The fee is a destination
|
||||
Assert.Contains(psbtReady.Destinations, d => d.Destination == sendDestination && !d.Positive);
|
||||
Assert.Contains(psbtReady.Destinations, d => d.Positive);
|
||||
|
||||
vmPSBT.PSBT = unsignedPSBT.ToBase64();
|
||||
var combineVM = await walletController.WalletPSBT(walletId, vmPSBT, "combine").AssertViewModelAsync<WalletPSBTCombineViewModel>();
|
||||
Assert.Equal(vmPSBT.PSBT, combineVM.OtherPSBT);
|
||||
combineVM.PSBT = signedPSBT.ToBase64();
|
||||
var psbt = AssertRedirectedPSBT(await walletController.WalletPSBTCombine(walletId, combineVM), nameof(walletController.WalletPSBT));
|
||||
vmPSBT.PSBT = unsignedPSBT.ToBase64();
|
||||
var combineVM = await walletController.WalletPSBT(walletId, vmPSBT, "combine").AssertViewModelAsync<WalletPSBTCombineViewModel>();
|
||||
Assert.Equal(vmPSBT.PSBT, combineVM.OtherPSBT);
|
||||
combineVM.PSBT = signedPSBT.ToBase64();
|
||||
var psbt = AssertRedirectedPSBT(await walletController.WalletPSBTCombine(walletId, combineVM), nameof(walletController.WalletPSBT));
|
||||
|
||||
var signedPSBT2 = PSBT.Parse(psbt, user.SupportedNetwork.NBitcoinNetwork);
|
||||
Assert.True(signedPSBT.TryFinalize(out _));
|
||||
Assert.True(signedPSBT2.TryFinalize(out _));
|
||||
Assert.Equal(signedPSBT, signedPSBT2);
|
||||
var signedPSBT2 = PSBT.Parse(psbt, user.SupportedNetwork.NBitcoinNetwork);
|
||||
Assert.True(signedPSBT.TryFinalize(out _));
|
||||
Assert.True(signedPSBT2.TryFinalize(out _));
|
||||
Assert.Equal(signedPSBT, signedPSBT2);
|
||||
|
||||
// Can use uploaded file?
|
||||
combineVM.PSBT = null;
|
||||
combineVM.UploadedPSBTFile = TestUtils.GetFormFile("signedPSBT", signedPSBT.ToBytes());
|
||||
psbt = AssertRedirectedPSBT(await walletController.WalletPSBTCombine(walletId, combineVM), nameof(walletController.WalletPSBT));
|
||||
signedPSBT2 = PSBT.Parse(psbt, user.SupportedNetwork.NBitcoinNetwork);
|
||||
Assert.True(signedPSBT.TryFinalize(out _));
|
||||
Assert.True(signedPSBT2.TryFinalize(out _));
|
||||
Assert.Equal(signedPSBT, signedPSBT2);
|
||||
// Can use uploaded file?
|
||||
combineVM.PSBT = null;
|
||||
combineVM.UploadedPSBTFile = TestUtils.GetFormFile("signedPSBT", signedPSBT.ToBytes());
|
||||
psbt = AssertRedirectedPSBT(await walletController.WalletPSBTCombine(walletId, combineVM), nameof(walletController.WalletPSBT));
|
||||
signedPSBT2 = PSBT.Parse(psbt, user.SupportedNetwork.NBitcoinNetwork);
|
||||
Assert.True(signedPSBT.TryFinalize(out _));
|
||||
Assert.True(signedPSBT2.TryFinalize(out _));
|
||||
Assert.Equal(signedPSBT, signedPSBT2);
|
||||
|
||||
var ready = (await walletController.WalletPSBT(walletId, new WalletPSBTViewModel
|
||||
{
|
||||
SigningContext = new SigningContextModel(signedPSBT)
|
||||
})).AssertViewModel<WalletPSBTViewModel>();
|
||||
Assert.Equal(signedPSBT.ToBase64(), ready.SigningContext.PSBT);
|
||||
psbt = AssertRedirectedPSBT(await walletController.WalletPSBTReady(walletId, ready, command: "analyze-psbt"), nameof(walletController.WalletPSBT));
|
||||
Assert.Equal(signedPSBT.ToBase64(), psbt);
|
||||
var redirect = Assert.IsType<RedirectToActionResult>(await walletController.WalletPSBTReady(walletId, ready, command: "broadcast"));
|
||||
Assert.Equal(nameof(walletController.WalletTransactions), redirect.ActionName);
|
||||
var ready = (await walletController.WalletPSBT(walletId, new WalletPSBTViewModel
|
||||
{
|
||||
SigningContext = new SigningContextModel(signedPSBT)
|
||||
})).AssertViewModel<WalletPSBTViewModel>();
|
||||
Assert.Equal(signedPSBT.ToBase64(), ready.SigningContext.PSBT);
|
||||
psbt = AssertRedirectedPSBT(await walletController.WalletPSBTReady(walletId, ready, command: "analyze-psbt"), nameof(walletController.WalletPSBT));
|
||||
Assert.Equal(signedPSBT.ToBase64(), psbt);
|
||||
var redirect = Assert.IsType<RedirectToActionResult>(await walletController.WalletPSBTReady(walletId, ready, command: "broadcast"));
|
||||
Assert.Equal(nameof(walletController.WalletTransactions), redirect.ActionName);
|
||||
|
||||
//test base64 psbt file
|
||||
Assert.False(string.IsNullOrEmpty(Assert.IsType<WalletPSBTViewModel>(
|
||||
Assert.IsType<ViewResult>(
|
||||
await walletController.WalletPSBT(walletId,
|
||||
new WalletPSBTViewModel
|
||||
{
|
||||
UploadedPSBTFile = TestUtils.GetFormFile("base64", signedPSBT.ToBase64())
|
||||
})).Model).PSBT));
|
||||
}
|
||||
//test base64 psbt file
|
||||
Assert.False(string.IsNullOrEmpty(Assert.IsType<WalletPSBTViewModel>(
|
||||
Assert.IsType<ViewResult>(
|
||||
await walletController.WalletPSBT(walletId,
|
||||
new WalletPSBTViewModel
|
||||
{
|
||||
UploadedPSBTFile = TestUtils.GetFormFile("base64", signedPSBT.ToBase64())
|
||||
})).Model).PSBT));
|
||||
}
|
||||
|
||||
private static string AssertRedirectedPSBT(IActionResult view, string actionName)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -24,222 +24,216 @@ namespace BTCPayServer.Tests
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanCreateViewUpdateAndDeletePaymentRequest()
|
||||
{
|
||||
using (var tester = CreateServerTester())
|
||||
using var tester = CreateServerTester();
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
|
||||
var user2 = tester.NewAccount();
|
||||
|
||||
await user2.GrantAccessAsync();
|
||||
|
||||
var paymentRequestController = user.GetController<UIPaymentRequestController>();
|
||||
var guestpaymentRequestController = user2.GetController<UIPaymentRequestController>();
|
||||
|
||||
var request = new UpdatePaymentRequestViewModel
|
||||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
Title = "original juice",
|
||||
Currency = "BTC",
|
||||
Amount = 1,
|
||||
StoreId = user.StoreId,
|
||||
Description = "description"
|
||||
};
|
||||
var id = Assert
|
||||
.IsType<RedirectToActionResult>(await paymentRequestController.EditPaymentRequest(null, request))
|
||||
.RouteValues.Values.Last().ToString();
|
||||
|
||||
var user2 = tester.NewAccount();
|
||||
paymentRequestController.HttpContext.SetPaymentRequestData(new PaymentRequestData { Id = id, StoreDataId = request.StoreId });
|
||||
|
||||
await user2.GrantAccessAsync();
|
||||
// Permission guard for guests editing
|
||||
Assert
|
||||
.IsType<NotFoundResult>(guestpaymentRequestController.EditPaymentRequest(user.StoreId, id));
|
||||
|
||||
var paymentRequestController = user.GetController<UIPaymentRequestController>();
|
||||
var guestpaymentRequestController = user2.GetController<UIPaymentRequestController>();
|
||||
|
||||
var request = new UpdatePaymentRequestViewModel
|
||||
{
|
||||
Title = "original juice",
|
||||
Currency = "BTC",
|
||||
Amount = 1,
|
||||
StoreId = user.StoreId,
|
||||
Description = "description"
|
||||
};
|
||||
var id = Assert
|
||||
.IsType<RedirectToActionResult>(await paymentRequestController.EditPaymentRequest(null, request))
|
||||
.RouteValues.Values.Last().ToString();
|
||||
|
||||
paymentRequestController.HttpContext.SetPaymentRequestData(new PaymentRequestData { Id = id, StoreDataId = request.StoreId });
|
||||
|
||||
// Permission guard for guests editing
|
||||
Assert
|
||||
.IsType<NotFoundResult>(guestpaymentRequestController.EditPaymentRequest(user.StoreId, id));
|
||||
|
||||
request.Title = "update";
|
||||
Assert.IsType<RedirectToActionResult>(await paymentRequestController.EditPaymentRequest(id, request));
|
||||
|
||||
Assert.Equal(request.Title,
|
||||
Assert.IsType<ViewPaymentRequestViewModel>(Assert
|
||||
.IsType<ViewResult>(await paymentRequestController.ViewPaymentRequest(id)).Model).Title);
|
||||
|
||||
Assert.False(string.IsNullOrEmpty(id));
|
||||
request.Title = "update";
|
||||
Assert.IsType<RedirectToActionResult>(await paymentRequestController.EditPaymentRequest(id, request));
|
||||
|
||||
Assert.Equal(request.Title,
|
||||
Assert.IsType<ViewPaymentRequestViewModel>(Assert
|
||||
.IsType<ViewResult>(await paymentRequestController.ViewPaymentRequest(id)).Model);
|
||||
.IsType<ViewResult>(await paymentRequestController.ViewPaymentRequest(id)).Model).Title);
|
||||
|
||||
// Archive
|
||||
Assert
|
||||
.IsType<RedirectToActionResult>(await paymentRequestController.TogglePaymentRequestArchival(id));
|
||||
Assert.True(Assert
|
||||
.IsType<ViewPaymentRequestViewModel>(Assert
|
||||
.IsType<ViewResult>(await paymentRequestController.ViewPaymentRequest(id)).Model).Archived);
|
||||
Assert.False(string.IsNullOrEmpty(id));
|
||||
|
||||
Assert.Empty(Assert
|
||||
.IsType<ListPaymentRequestsViewModel>(Assert
|
||||
.IsType<ViewResult>(await paymentRequestController.GetPaymentRequests(user.StoreId)).Model).Items);
|
||||
Assert.IsType<ViewPaymentRequestViewModel>(Assert
|
||||
.IsType<ViewResult>(await paymentRequestController.ViewPaymentRequest(id)).Model);
|
||||
|
||||
// Unarchive
|
||||
Assert
|
||||
.IsType<RedirectToActionResult>(await paymentRequestController.TogglePaymentRequestArchival(id));
|
||||
// Archive
|
||||
Assert
|
||||
.IsType<RedirectToActionResult>(await paymentRequestController.TogglePaymentRequestArchival(id));
|
||||
Assert.True(Assert
|
||||
.IsType<ViewPaymentRequestViewModel>(Assert
|
||||
.IsType<ViewResult>(await paymentRequestController.ViewPaymentRequest(id)).Model).Archived);
|
||||
|
||||
Assert.False(Assert
|
||||
.IsType<ViewPaymentRequestViewModel>(Assert
|
||||
.IsType<ViewResult>(await paymentRequestController.ViewPaymentRequest(id)).Model).Archived);
|
||||
Assert.Empty(Assert
|
||||
.IsType<ListPaymentRequestsViewModel>(Assert
|
||||
.IsType<ViewResult>(await paymentRequestController.GetPaymentRequests(user.StoreId)).Model).Items);
|
||||
|
||||
Assert.Single(Assert
|
||||
.IsType<ListPaymentRequestsViewModel>(Assert
|
||||
.IsType<ViewResult>(await paymentRequestController.GetPaymentRequests(user.StoreId)).Model).Items);
|
||||
}
|
||||
// Unarchive
|
||||
Assert
|
||||
.IsType<RedirectToActionResult>(await paymentRequestController.TogglePaymentRequestArchival(id));
|
||||
|
||||
Assert.False(Assert
|
||||
.IsType<ViewPaymentRequestViewModel>(Assert
|
||||
.IsType<ViewResult>(await paymentRequestController.ViewPaymentRequest(id)).Model).Archived);
|
||||
|
||||
Assert.Single(Assert
|
||||
.IsType<ListPaymentRequestsViewModel>(Assert
|
||||
.IsType<ViewResult>(await paymentRequestController.GetPaymentRequests(user.StoreId)).Model).Items);
|
||||
}
|
||||
|
||||
[Fact(Timeout = 60 * 2 * 1000)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanPayPaymentRequestWhenPossible()
|
||||
{
|
||||
using (var tester = CreateServerTester())
|
||||
using var tester = CreateServerTester();
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
|
||||
var paymentRequestController = user.GetController<UIPaymentRequestController>();
|
||||
|
||||
Assert.IsType<NotFoundResult>(
|
||||
await paymentRequestController.PayPaymentRequest(Guid.NewGuid().ToString()));
|
||||
|
||||
|
||||
var request = new UpdatePaymentRequestViewModel()
|
||||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
Title = "original juice",
|
||||
Currency = "BTC",
|
||||
Amount = 1,
|
||||
StoreId = user.StoreId,
|
||||
Description = "description"
|
||||
};
|
||||
var response = Assert
|
||||
.IsType<RedirectToActionResult>(paymentRequestController.EditPaymentRequest(null, request).Result)
|
||||
.RouteValues.Last();
|
||||
|
||||
var paymentRequestController = user.GetController<UIPaymentRequestController>();
|
||||
var invoiceId = Assert
|
||||
.IsType<OkObjectResult>(
|
||||
await paymentRequestController.PayPaymentRequest(response.Value.ToString(), false)).Value
|
||||
.ToString();
|
||||
|
||||
Assert.IsType<NotFoundResult>(
|
||||
await paymentRequestController.PayPaymentRequest(Guid.NewGuid().ToString()));
|
||||
var actionResult = Assert
|
||||
.IsType<RedirectToActionResult>(
|
||||
await paymentRequestController.PayPaymentRequest(response.Value.ToString()));
|
||||
|
||||
Assert.Equal("Checkout", actionResult.ActionName);
|
||||
Assert.Equal("UIInvoice", actionResult.ControllerName);
|
||||
Assert.Contains(actionResult.RouteValues,
|
||||
pair => pair.Key == "Id" && pair.Value.ToString() == invoiceId);
|
||||
|
||||
var request = new UpdatePaymentRequestViewModel()
|
||||
{
|
||||
Title = "original juice",
|
||||
Currency = "BTC",
|
||||
Amount = 1,
|
||||
StoreId = user.StoreId,
|
||||
Description = "description"
|
||||
};
|
||||
var response = Assert
|
||||
.IsType<RedirectToActionResult>(paymentRequestController.EditPaymentRequest(null, request).Result)
|
||||
.RouteValues.Last();
|
||||
var invoice = user.BitPay.GetInvoice(invoiceId, Facade.Merchant);
|
||||
Assert.Equal(1, invoice.Price);
|
||||
|
||||
var invoiceId = Assert
|
||||
.IsType<OkObjectResult>(
|
||||
await paymentRequestController.PayPaymentRequest(response.Value.ToString(), false)).Value
|
||||
.ToString();
|
||||
request = new UpdatePaymentRequestViewModel()
|
||||
{
|
||||
Title = "original juice with expiry",
|
||||
Currency = "BTC",
|
||||
Amount = 1,
|
||||
ExpiryDate = DateTime.Today.Subtract(TimeSpan.FromDays(2)),
|
||||
StoreId = user.StoreId,
|
||||
Description = "description"
|
||||
};
|
||||
|
||||
var actionResult = Assert
|
||||
.IsType<RedirectToActionResult>(
|
||||
await paymentRequestController.PayPaymentRequest(response.Value.ToString()));
|
||||
response = Assert
|
||||
.IsType<RedirectToActionResult>(paymentRequestController.EditPaymentRequest(null, request).Result)
|
||||
.RouteValues.Last();
|
||||
|
||||
Assert.Equal("Checkout", actionResult.ActionName);
|
||||
Assert.Equal("UIInvoice", actionResult.ControllerName);
|
||||
Assert.Contains(actionResult.RouteValues,
|
||||
pair => pair.Key == "Id" && pair.Value.ToString() == invoiceId);
|
||||
|
||||
var invoice = user.BitPay.GetInvoice(invoiceId, Facade.Merchant);
|
||||
Assert.Equal(1, invoice.Price);
|
||||
|
||||
request = new UpdatePaymentRequestViewModel()
|
||||
{
|
||||
Title = "original juice with expiry",
|
||||
Currency = "BTC",
|
||||
Amount = 1,
|
||||
ExpiryDate = DateTime.Today.Subtract(TimeSpan.FromDays(2)),
|
||||
StoreId = user.StoreId,
|
||||
Description = "description"
|
||||
};
|
||||
|
||||
response = Assert
|
||||
.IsType<RedirectToActionResult>(paymentRequestController.EditPaymentRequest(null, request).Result)
|
||||
.RouteValues.Last();
|
||||
|
||||
Assert
|
||||
.IsType<BadRequestObjectResult>(
|
||||
await paymentRequestController.PayPaymentRequest(response.Value.ToString(), false));
|
||||
}
|
||||
Assert
|
||||
.IsType<BadRequestObjectResult>(
|
||||
await paymentRequestController.PayPaymentRequest(response.Value.ToString(), false));
|
||||
}
|
||||
|
||||
[Fact(Timeout = 60 * 2 * 1000)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanCancelPaymentWhenPossible()
|
||||
{
|
||||
using (var tester = CreateServerTester())
|
||||
using var tester = CreateServerTester();
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
|
||||
var paymentRequestController = user.GetController<UIPaymentRequestController>();
|
||||
|
||||
Assert.IsType<NotFoundResult>(await
|
||||
paymentRequestController.CancelUnpaidPendingInvoice(Guid.NewGuid().ToString(), false));
|
||||
|
||||
var request = new UpdatePaymentRequestViewModel()
|
||||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
Title = "original juice",
|
||||
Currency = "BTC",
|
||||
Amount = 1,
|
||||
StoreId = user.StoreId,
|
||||
Description = "description"
|
||||
};
|
||||
var response = Assert
|
||||
.IsType<RedirectToActionResult>(paymentRequestController.EditPaymentRequest(null, request).Result)
|
||||
.RouteValues.Last();
|
||||
var invoiceId = response.Value.ToString();
|
||||
await paymentRequestController.PayPaymentRequest(invoiceId, false);
|
||||
Assert.IsType<BadRequestObjectResult>(await
|
||||
paymentRequestController.CancelUnpaidPendingInvoice(invoiceId, false));
|
||||
|
||||
var paymentRequestController = user.GetController<UIPaymentRequestController>();
|
||||
request.AllowCustomPaymentAmounts = true;
|
||||
|
||||
Assert.IsType<NotFoundResult>(await
|
||||
paymentRequestController.CancelUnpaidPendingInvoice(Guid.NewGuid().ToString(), false));
|
||||
response = Assert
|
||||
.IsType<RedirectToActionResult>(paymentRequestController.EditPaymentRequest(null, request).Result)
|
||||
.RouteValues.Last();
|
||||
|
||||
var request = new UpdatePaymentRequestViewModel()
|
||||
{
|
||||
Title = "original juice",
|
||||
Currency = "BTC",
|
||||
Amount = 1,
|
||||
StoreId = user.StoreId,
|
||||
Description = "description"
|
||||
};
|
||||
var response = Assert
|
||||
.IsType<RedirectToActionResult>(paymentRequestController.EditPaymentRequest(null, request).Result)
|
||||
.RouteValues.Last();
|
||||
var invoiceId = response.Value.ToString();
|
||||
await paymentRequestController.PayPaymentRequest(invoiceId, false);
|
||||
Assert.IsType<BadRequestObjectResult>(await
|
||||
paymentRequestController.CancelUnpaidPendingInvoice(invoiceId, false));
|
||||
var paymentRequestId = response.Value.ToString();
|
||||
|
||||
request.AllowCustomPaymentAmounts = true;
|
||||
invoiceId = Assert
|
||||
.IsType<OkObjectResult>(await paymentRequestController.PayPaymentRequest(paymentRequestId, false))
|
||||
.Value
|
||||
.ToString();
|
||||
|
||||
response = Assert
|
||||
.IsType<RedirectToActionResult>(paymentRequestController.EditPaymentRequest(null, request).Result)
|
||||
.RouteValues.Last();
|
||||
var actionResult = Assert
|
||||
.IsType<RedirectToActionResult>(
|
||||
await paymentRequestController.PayPaymentRequest(response.Value.ToString()));
|
||||
|
||||
var paymentRequestId = response.Value.ToString();
|
||||
Assert.Equal("Checkout", actionResult.ActionName);
|
||||
Assert.Equal("UIInvoice", actionResult.ControllerName);
|
||||
Assert.Contains(actionResult.RouteValues,
|
||||
pair => pair.Key == "Id" && pair.Value.ToString() == invoiceId);
|
||||
|
||||
invoiceId = Assert
|
||||
.IsType<OkObjectResult>(await paymentRequestController.PayPaymentRequest(paymentRequestId, false))
|
||||
.Value
|
||||
.ToString();
|
||||
var invoice = user.BitPay.GetInvoice(invoiceId, Facade.Merchant);
|
||||
Assert.Equal(InvoiceState.ToString(InvoiceStatusLegacy.New), invoice.Status);
|
||||
Assert.IsType<OkObjectResult>(await
|
||||
paymentRequestController.CancelUnpaidPendingInvoice(paymentRequestId, false));
|
||||
|
||||
var actionResult = Assert
|
||||
.IsType<RedirectToActionResult>(
|
||||
await paymentRequestController.PayPaymentRequest(response.Value.ToString()));
|
||||
invoice = user.BitPay.GetInvoice(invoiceId, Facade.Merchant);
|
||||
Assert.Equal(InvoiceState.ToString(InvoiceStatusLegacy.Invalid), invoice.Status);
|
||||
|
||||
Assert.Equal("Checkout", actionResult.ActionName);
|
||||
Assert.Equal("UIInvoice", actionResult.ControllerName);
|
||||
Assert.Contains(actionResult.RouteValues,
|
||||
pair => pair.Key == "Id" && pair.Value.ToString() == invoiceId);
|
||||
Assert.IsType<BadRequestObjectResult>(await
|
||||
paymentRequestController.CancelUnpaidPendingInvoice(paymentRequestId, false));
|
||||
|
||||
var invoice = user.BitPay.GetInvoice(invoiceId, Facade.Merchant);
|
||||
Assert.Equal(InvoiceState.ToString(InvoiceStatusLegacy.New), invoice.Status);
|
||||
Assert.IsType<OkObjectResult>(await
|
||||
paymentRequestController.CancelUnpaidPendingInvoice(paymentRequestId, false));
|
||||
invoiceId = Assert
|
||||
.IsType<OkObjectResult>(await paymentRequestController.PayPaymentRequest(paymentRequestId, false))
|
||||
.Value
|
||||
.ToString();
|
||||
|
||||
invoice = user.BitPay.GetInvoice(invoiceId, Facade.Merchant);
|
||||
Assert.Equal(InvoiceState.ToString(InvoiceStatusLegacy.Invalid), invoice.Status);
|
||||
await user.BitPay.GetInvoiceAsync(invoiceId, Facade.Merchant);
|
||||
|
||||
Assert.IsType<BadRequestObjectResult>(await
|
||||
paymentRequestController.CancelUnpaidPendingInvoice(paymentRequestId, false));
|
||||
|
||||
invoiceId = Assert
|
||||
.IsType<OkObjectResult>(await paymentRequestController.PayPaymentRequest(paymentRequestId, false))
|
||||
.Value
|
||||
.ToString();
|
||||
|
||||
await user.BitPay.GetInvoiceAsync(invoiceId, Facade.Merchant);
|
||||
|
||||
//a hack to generate invoices for the payment request is to manually create an invoice with an order id that matches:
|
||||
user.BitPay.CreateInvoice(new Invoice(1, "USD")
|
||||
{
|
||||
OrderId = PaymentRequestRepository.GetOrderIdForPaymentRequest(paymentRequestId)
|
||||
});
|
||||
//shouldn't crash
|
||||
await paymentRequestController.ViewPaymentRequest(paymentRequestId);
|
||||
await paymentRequestController.CancelUnpaidPendingInvoice(paymentRequestId);
|
||||
}
|
||||
//a hack to generate invoices for the payment request is to manually create an invoice with an order id that matches:
|
||||
user.BitPay.CreateInvoice(new Invoice(1, "USD")
|
||||
{
|
||||
OrderId = PaymentRequestRepository.GetOrderIdForPaymentRequest(paymentRequestId)
|
||||
});
|
||||
//shouldn't crash
|
||||
await paymentRequestController.ViewPaymentRequest(paymentRequestId);
|
||||
await paymentRequestController.CancelUnpaidPendingInvoice(paymentRequestId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -37,40 +37,38 @@ namespace BTCPayServer.Tests
|
||||
[FactWithSecret("AzureBlobStorageConnectionString")]
|
||||
public async Task CanUseAzureBlobStorage()
|
||||
{
|
||||
using (var tester = CreateServerTester())
|
||||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
var controller = tester.PayTester.GetController<UIServerController>(user.UserId, user.StoreId);
|
||||
var azureBlobStorageConfiguration = Assert.IsType<AzureBlobStorageConfiguration>(Assert
|
||||
.IsType<ViewResult>(await controller.StorageProvider(StorageProvider.AzureBlobStorage.ToString()))
|
||||
.Model);
|
||||
using var tester = CreateServerTester();
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
var controller = tester.PayTester.GetController<UIServerController>(user.UserId, user.StoreId);
|
||||
var azureBlobStorageConfiguration = Assert.IsType<AzureBlobStorageConfiguration>(Assert
|
||||
.IsType<ViewResult>(await controller.StorageProvider(StorageProvider.AzureBlobStorage.ToString()))
|
||||
.Model);
|
||||
|
||||
azureBlobStorageConfiguration.ConnectionString = FactWithSecretAttribute.GetFromSecrets("AzureBlobStorageConnectionString");
|
||||
azureBlobStorageConfiguration.ContainerName = "testscontainer";
|
||||
Assert.IsType<ViewResult>(
|
||||
await controller.EditAzureBlobStorageStorageProvider(azureBlobStorageConfiguration));
|
||||
azureBlobStorageConfiguration.ConnectionString = FactWithSecretAttribute.GetFromSecrets("AzureBlobStorageConnectionString");
|
||||
azureBlobStorageConfiguration.ContainerName = "testscontainer";
|
||||
Assert.IsType<ViewResult>(
|
||||
await controller.EditAzureBlobStorageStorageProvider(azureBlobStorageConfiguration));
|
||||
|
||||
|
||||
var shouldBeRedirectingToAzureStorageConfigPage =
|
||||
Assert.IsType<RedirectToActionResult>(await controller.Storage());
|
||||
Assert.Equal(nameof(StorageProvider), shouldBeRedirectingToAzureStorageConfigPage.ActionName);
|
||||
Assert.Equal(StorageProvider.AzureBlobStorage,
|
||||
shouldBeRedirectingToAzureStorageConfigPage.RouteValues["provider"]);
|
||||
var shouldBeRedirectingToAzureStorageConfigPage =
|
||||
Assert.IsType<RedirectToActionResult>(await controller.Storage());
|
||||
Assert.Equal(nameof(StorageProvider), shouldBeRedirectingToAzureStorageConfigPage.ActionName);
|
||||
Assert.Equal(StorageProvider.AzureBlobStorage,
|
||||
shouldBeRedirectingToAzureStorageConfigPage.RouteValues["provider"]);
|
||||
|
||||
//seems like azure config worked, let's see if the conn string was actually saved
|
||||
//seems like azure config worked, let's see if the conn string was actually saved
|
||||
|
||||
Assert.Equal(azureBlobStorageConfiguration.ConnectionString, Assert
|
||||
.IsType<AzureBlobStorageConfiguration>(Assert
|
||||
.IsType<ViewResult>(
|
||||
await controller.StorageProvider(StorageProvider.AzureBlobStorage.ToString()))
|
||||
.Model).ConnectionString);
|
||||
Assert.Equal(azureBlobStorageConfiguration.ConnectionString, Assert
|
||||
.IsType<AzureBlobStorageConfiguration>(Assert
|
||||
.IsType<ViewResult>(
|
||||
await controller.StorageProvider(StorageProvider.AzureBlobStorage.ToString()))
|
||||
.Model).ConnectionString);
|
||||
|
||||
|
||||
|
||||
await UnitTest1.CanUploadRemoveFiles(controller);
|
||||
}
|
||||
await UnitTest1.CanUploadRemoveFiles(controller);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -338,24 +336,22 @@ namespace BTCPayServer.Tests
|
||||
[Fact]
|
||||
public async Task CanUseExchangeSpecificRate()
|
||||
{
|
||||
using (var tester = CreateServerTester())
|
||||
{
|
||||
tester.PayTester.MockRates = false;
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
List<decimal> rates = new List<decimal>();
|
||||
rates.Add(await CreateInvoice(tester, user, "coingecko"));
|
||||
var bitflyer = await CreateInvoice(tester, user, "bitflyer", "JPY");
|
||||
var bitflyer2 = await CreateInvoice(tester, user, "bitflyer", "JPY");
|
||||
Assert.Equal(bitflyer, bitflyer2); // Should be equal because cache
|
||||
rates.Add(bitflyer);
|
||||
using var tester = CreateServerTester();
|
||||
tester.PayTester.MockRates = false;
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
List<decimal> rates = new List<decimal>();
|
||||
rates.Add(await CreateInvoice(tester, user, "coingecko"));
|
||||
var bitflyer = await CreateInvoice(tester, user, "bitflyer", "JPY");
|
||||
var bitflyer2 = await CreateInvoice(tester, user, "bitflyer", "JPY");
|
||||
Assert.Equal(bitflyer, bitflyer2); // Should be equal because cache
|
||||
rates.Add(bitflyer);
|
||||
|
||||
foreach (var rate in rates)
|
||||
{
|
||||
Assert.Single(rates.Where(r => r == rate));
|
||||
}
|
||||
foreach (var rate in rates)
|
||||
{
|
||||
Assert.Single(rates.Where(r => r == rate));
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -16,23 +16,21 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
lock (_portLock)
|
||||
{
|
||||
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
|
||||
using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
while (true)
|
||||
{
|
||||
while (true)
|
||||
try
|
||||
{
|
||||
try
|
||||
var port = _nextPort++;
|
||||
socket.Bind(new IPEndPoint(IPAddress.Loopback, port));
|
||||
return port;
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
// Retry unless exhausted
|
||||
if (_nextPort == 65536)
|
||||
{
|
||||
var port = _nextPort++;
|
||||
socket.Bind(new IPEndPoint(IPAddress.Loopback, port));
|
||||
return port;
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
// Retry unless exhausted
|
||||
if (_nextPort == 65536)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -871,11 +871,9 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var sshClient = await _Options.SSHSettings.ConnectAsync())
|
||||
{
|
||||
var result = await sshClient.RunBash("cat ~/.ssh/authorized_keys", TimeSpan.FromSeconds(10));
|
||||
vm.SSHKeyFileContent = result.Output;
|
||||
}
|
||||
using var sshClient = await _Options.SSHSettings.ConnectAsync();
|
||||
var result = await sshClient.RunBash("cat ~/.ssh/authorized_keys", TimeSpan.FromSeconds(10));
|
||||
vm.SSHKeyFileContent = result.Output;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
@ -1106,17 +1104,13 @@ namespace BTCPayServer.Controllers
|
||||
return NotFound();
|
||||
try
|
||||
{
|
||||
using (var fileStream = new FileStream(
|
||||
using var fileStream = new FileStream(
|
||||
fi.FullName,
|
||||
FileMode.Open,
|
||||
FileAccess.Read,
|
||||
FileShare.ReadWrite))
|
||||
{
|
||||
using (var reader = new StreamReader(fileStream))
|
||||
{
|
||||
vm.Log = await reader.ReadToEndAsync();
|
||||
}
|
||||
}
|
||||
FileShare.ReadWrite);
|
||||
using var reader = new StreamReader(fileStream);
|
||||
vm.Log = await reader.ReadToEndAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@ -810,10 +810,8 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
private async Task<string> ReadAllText(IFormFile file)
|
||||
{
|
||||
using (var stream = new StreamReader(file.OpenReadStream()))
|
||||
{
|
||||
return await stream.ReadToEndAsync();
|
||||
}
|
||||
using var stream = new StreamReader(file.OpenReadStream());
|
||||
return await stream.ReadToEndAsync();
|
||||
}
|
||||
|
||||
private string WalletWarning(bool isHotWallet, string info)
|
||||
|
@ -125,11 +125,9 @@ namespace BTCPayServer
|
||||
{
|
||||
if (webSocket.State == WebSocketState.Open)
|
||||
{
|
||||
using (CancellationTokenSource cts = new CancellationTokenSource())
|
||||
{
|
||||
cts.CancelAfter(5000);
|
||||
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", cts.Token);
|
||||
}
|
||||
using CancellationTokenSource cts = new CancellationTokenSource();
|
||||
cts.CancelAfter(5000);
|
||||
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", cts.Token);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
@ -40,12 +40,10 @@ retry:
|
||||
Logs.Configuration.LogInformation($"SSH settings detected, testing connection to {_options.SSHSettings.Username}@{_options.SSHSettings.Server} on port {_options.SSHSettings.Port} ...");
|
||||
try
|
||||
{
|
||||
using (var connection = await _options.SSHSettings.ConnectAsync(_cancellationTokenSource.Token))
|
||||
{
|
||||
await connection.DisconnectAsync(_cancellationTokenSource.Token);
|
||||
Logs.Configuration.LogInformation($"SSH connection succeeded");
|
||||
canUseSSH = true;
|
||||
}
|
||||
using var connection = await _options.SSHSettings.ConnectAsync(_cancellationTokenSource.Token);
|
||||
await connection.DisconnectAsync(_cancellationTokenSource.Token);
|
||||
Logs.Configuration.LogInformation($"SSH connection succeeded");
|
||||
canUseSSH = true;
|
||||
}
|
||||
catch (Renci.SshNet.Common.SshAuthenticationException ex)
|
||||
{
|
||||
|
@ -59,13 +59,11 @@ namespace BTCPayServer.HostedServices
|
||||
}
|
||||
}
|
||||
}
|
||||
using (var delayCancel = CancellationTokenSource.CreateLinkedTokenSource(Cancellation))
|
||||
{
|
||||
var delay = Task.Delay(Period, delayCancel.Token);
|
||||
var changed = SettingsRepository.WaitSettingsChanged<DynamicDnsSettings>(Cancellation);
|
||||
await Task.WhenAny(delay, changed);
|
||||
delayCancel.Cancel();
|
||||
}
|
||||
using var delayCancel = CancellationTokenSource.CreateLinkedTokenSource(Cancellation);
|
||||
var delay = Task.Delay(Period, delayCancel.Token);
|
||||
var changed = SettingsRepository.WaitSettingsChanged<DynamicDnsSettings>(Cancellation);
|
||||
await Task.WhenAny(delay, changed);
|
||||
delayCancel.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -463,164 +463,154 @@ retry:
|
||||
private async Task ConvertConvertWalletKeyPathRoots()
|
||||
{
|
||||
bool save = false;
|
||||
using (var ctx = _DBContextFactory.CreateContext())
|
||||
using var ctx = _DBContextFactory.CreateContext();
|
||||
foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync())
|
||||
{
|
||||
foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync())
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
var blob = store.GetStoreBlob();
|
||||
var blob = store.GetStoreBlob();
|
||||
|
||||
if (blob.AdditionalData.TryGetValue("walletKeyPathRoots", out var walletKeyPathRootsJToken))
|
||||
if (blob.AdditionalData.TryGetValue("walletKeyPathRoots", out var walletKeyPathRootsJToken))
|
||||
{
|
||||
var walletKeyPathRoots = walletKeyPathRootsJToken.ToObject<Dictionary<string, string>>();
|
||||
|
||||
if (!(walletKeyPathRoots?.Any() is true))
|
||||
continue;
|
||||
foreach (var scheme in store.GetSupportedPaymentMethods(_NetworkProvider)
|
||||
.OfType<DerivationSchemeSettings>())
|
||||
{
|
||||
var walletKeyPathRoots = walletKeyPathRootsJToken.ToObject<Dictionary<string, string>>();
|
||||
|
||||
if (!(walletKeyPathRoots?.Any() is true))
|
||||
continue;
|
||||
foreach (var scheme in store.GetSupportedPaymentMethods(_NetworkProvider)
|
||||
.OfType<DerivationSchemeSettings>())
|
||||
if (walletKeyPathRoots.TryGetValue(scheme.PaymentId.ToString().ToLowerInvariant(),
|
||||
out var root))
|
||||
{
|
||||
if (walletKeyPathRoots.TryGetValue(scheme.PaymentId.ToString().ToLowerInvariant(),
|
||||
out var root))
|
||||
{
|
||||
scheme.AccountKeyPath = new NBitcoin.KeyPath(root);
|
||||
store.SetSupportedPaymentMethod(scheme);
|
||||
save = true;
|
||||
}
|
||||
scheme.AccountKeyPath = new NBitcoin.KeyPath(root);
|
||||
store.SetSupportedPaymentMethod(scheme);
|
||||
save = true;
|
||||
}
|
||||
|
||||
blob.AdditionalData.Remove("walletKeyPathRoots");
|
||||
store.SetStoreBlob(blob);
|
||||
}
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
blob.AdditionalData.Remove("walletKeyPathRoots");
|
||||
store.SetStoreBlob(blob);
|
||||
}
|
||||
if (save)
|
||||
await ctx.SaveChangesAsync();
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
if (save)
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async Task ConvertCrowdfundOldSettings()
|
||||
{
|
||||
using (var ctx = _DBContextFactory.CreateContext())
|
||||
using var ctx = _DBContextFactory.CreateContext();
|
||||
foreach (var app in await ctx.Apps.Where(a => a.AppType == "Crowdfund").ToArrayAsync())
|
||||
{
|
||||
foreach (var app in await ctx.Apps.Where(a => a.AppType == "Crowdfund").ToArrayAsync())
|
||||
{
|
||||
var settings = app.GetSettings<Services.Apps.CrowdfundSettings>();
|
||||
var settings = app.GetSettings<Services.Apps.CrowdfundSettings>();
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
if (settings.UseAllStoreInvoices)
|
||||
if (settings.UseAllStoreInvoices)
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
{
|
||||
app.TagAllInvoices = true;
|
||||
}
|
||||
{
|
||||
app.TagAllInvoices = true;
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async Task MigratePaymentMethodCriteria()
|
||||
{
|
||||
using (var ctx = _DBContextFactory.CreateContext())
|
||||
using var ctx = _DBContextFactory.CreateContext();
|
||||
foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync())
|
||||
{
|
||||
foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync())
|
||||
var blob = store.GetStoreBlob();
|
||||
|
||||
CurrencyValue onChainMinValue = null;
|
||||
CurrencyValue lightningMaxValue = null;
|
||||
if (blob.AdditionalData.TryGetValue("onChainMinValue", out var onChainMinValueJToken))
|
||||
{
|
||||
var blob = store.GetStoreBlob();
|
||||
|
||||
CurrencyValue onChainMinValue = null;
|
||||
CurrencyValue lightningMaxValue = null;
|
||||
if (blob.AdditionalData.TryGetValue("onChainMinValue", out var onChainMinValueJToken))
|
||||
{
|
||||
CurrencyValue.TryParse(onChainMinValueJToken.Value<string>(), out onChainMinValue);
|
||||
blob.AdditionalData.Remove("onChainMinValue");
|
||||
}
|
||||
if (blob.AdditionalData.TryGetValue("lightningMaxValue", out var lightningMaxValueJToken))
|
||||
{
|
||||
CurrencyValue.TryParse(lightningMaxValueJToken.Value<string>(), out lightningMaxValue);
|
||||
blob.AdditionalData.Remove("lightningMaxValue");
|
||||
}
|
||||
blob.PaymentMethodCriteria = store.GetEnabledPaymentIds(_NetworkProvider).Select(paymentMethodId =>
|
||||
{
|
||||
var matchedFromBlob =
|
||||
blob.PaymentMethodCriteria?.SingleOrDefault(criteria => criteria.PaymentMethod == paymentMethodId && criteria.Value != null);
|
||||
return matchedFromBlob switch
|
||||
{
|
||||
null when paymentMethodId.PaymentType == LightningPaymentType.Instance &&
|
||||
lightningMaxValue != null => new PaymentMethodCriteria()
|
||||
{
|
||||
Above = false,
|
||||
PaymentMethod = paymentMethodId,
|
||||
Value = lightningMaxValue
|
||||
},
|
||||
null when paymentMethodId.PaymentType == BitcoinPaymentType.Instance &&
|
||||
onChainMinValue != null => new PaymentMethodCriteria()
|
||||
{
|
||||
Above = true,
|
||||
PaymentMethod = paymentMethodId,
|
||||
Value = onChainMinValue
|
||||
},
|
||||
_ => new PaymentMethodCriteria()
|
||||
{
|
||||
PaymentMethod = paymentMethodId,
|
||||
Above = matchedFromBlob?.Above ?? true,
|
||||
Value = matchedFromBlob?.Value
|
||||
}
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
store.SetStoreBlob(blob);
|
||||
CurrencyValue.TryParse(onChainMinValueJToken.Value<string>(), out onChainMinValue);
|
||||
blob.AdditionalData.Remove("onChainMinValue");
|
||||
}
|
||||
if (blob.AdditionalData.TryGetValue("lightningMaxValue", out var lightningMaxValueJToken))
|
||||
{
|
||||
CurrencyValue.TryParse(lightningMaxValueJToken.Value<string>(), out lightningMaxValue);
|
||||
blob.AdditionalData.Remove("lightningMaxValue");
|
||||
}
|
||||
blob.PaymentMethodCriteria = store.GetEnabledPaymentIds(_NetworkProvider).Select(paymentMethodId =>
|
||||
{
|
||||
var matchedFromBlob =
|
||||
blob.PaymentMethodCriteria?.SingleOrDefault(criteria => criteria.PaymentMethod == paymentMethodId && criteria.Value != null);
|
||||
return matchedFromBlob switch
|
||||
{
|
||||
null when paymentMethodId.PaymentType == LightningPaymentType.Instance &&
|
||||
lightningMaxValue != null => new PaymentMethodCriteria()
|
||||
{
|
||||
Above = false,
|
||||
PaymentMethod = paymentMethodId,
|
||||
Value = lightningMaxValue
|
||||
},
|
||||
null when paymentMethodId.PaymentType == BitcoinPaymentType.Instance &&
|
||||
onChainMinValue != null => new PaymentMethodCriteria()
|
||||
{
|
||||
Above = true,
|
||||
PaymentMethod = paymentMethodId,
|
||||
Value = onChainMinValue
|
||||
},
|
||||
_ => new PaymentMethodCriteria()
|
||||
{
|
||||
PaymentMethod = paymentMethodId,
|
||||
Above = matchedFromBlob?.Above ?? true,
|
||||
Value = matchedFromBlob?.Value
|
||||
}
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
await ctx.SaveChangesAsync();
|
||||
store.SetStoreBlob(blob);
|
||||
}
|
||||
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async Task ConvertNetworkFeeProperty()
|
||||
{
|
||||
using (var ctx = _DBContextFactory.CreateContext())
|
||||
using var ctx = _DBContextFactory.CreateContext();
|
||||
foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync())
|
||||
{
|
||||
foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync())
|
||||
var blob = store.GetStoreBlob();
|
||||
if (blob.AdditionalData.TryGetValue("networkFeeDisabled", out var networkFeeModeJToken))
|
||||
{
|
||||
var blob = store.GetStoreBlob();
|
||||
if (blob.AdditionalData.TryGetValue("networkFeeDisabled", out var networkFeeModeJToken))
|
||||
var networkFeeMode = networkFeeModeJToken.ToObject<bool?>();
|
||||
if (networkFeeMode != null)
|
||||
{
|
||||
var networkFeeMode = networkFeeModeJToken.ToObject<bool?>();
|
||||
if (networkFeeMode != null)
|
||||
{
|
||||
blob.NetworkFeeMode = networkFeeMode.Value ? NetworkFeeMode.Never : NetworkFeeMode.Always;
|
||||
}
|
||||
|
||||
blob.AdditionalData.Remove("networkFeeDisabled");
|
||||
store.SetStoreBlob(blob);
|
||||
blob.NetworkFeeMode = networkFeeMode.Value ? NetworkFeeMode.Never : NetworkFeeMode.Always;
|
||||
}
|
||||
|
||||
blob.AdditionalData.Remove("networkFeeDisabled");
|
||||
store.SetStoreBlob(blob);
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async Task ConvertMultiplierToSpread()
|
||||
{
|
||||
using (var ctx = _DBContextFactory.CreateContext())
|
||||
using var ctx = _DBContextFactory.CreateContext();
|
||||
foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync())
|
||||
{
|
||||
foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync())
|
||||
var blob = store.GetStoreBlob();
|
||||
decimal multiplier = 1.0m;
|
||||
if (blob.AdditionalData.TryGetValue("rateRules", out var rateRulesJToken))
|
||||
{
|
||||
var blob = store.GetStoreBlob();
|
||||
decimal multiplier = 1.0m;
|
||||
if (blob.AdditionalData.TryGetValue("rateRules", out var rateRulesJToken))
|
||||
var rateRules = new Serializer(null).ToObject<List<RateRule_Obsolete>>(rateRulesJToken.ToString());
|
||||
if (rateRules != null && rateRules.Count != 0)
|
||||
{
|
||||
var rateRules = new Serializer(null).ToObject<List<RateRule_Obsolete>>(rateRulesJToken.ToString());
|
||||
if (rateRules != null && rateRules.Count != 0)
|
||||
foreach (var rule in rateRules)
|
||||
{
|
||||
foreach (var rule in rateRules)
|
||||
{
|
||||
multiplier = rule.Apply(null, multiplier);
|
||||
}
|
||||
multiplier = rule.Apply(null, multiplier);
|
||||
}
|
||||
|
||||
blob.AdditionalData.Remove("rateRules");
|
||||
blob.Spread = Math.Min(1.0m, Math.Max(0m, -(multiplier - 1.0m)));
|
||||
store.SetStoreBlob(blob);
|
||||
}
|
||||
|
||||
blob.AdditionalData.Remove("rateRules");
|
||||
blob.Spread = Math.Min(1.0m, Math.Max(0m, -(multiplier - 1.0m)));
|
||||
store.SetStoreBlob(blob);
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public class RateRule_Obsolete
|
||||
@ -646,22 +636,20 @@ retry:
|
||||
|
||||
private async Task DeprecatedLightningConnectionStringCheck()
|
||||
{
|
||||
using (var ctx = _DBContextFactory.CreateContext())
|
||||
using var ctx = _DBContextFactory.CreateContext();
|
||||
foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync())
|
||||
{
|
||||
foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync())
|
||||
foreach (var method in store.GetSupportedPaymentMethods(_NetworkProvider).OfType<Payments.Lightning.LightningSupportedPaymentMethod>())
|
||||
{
|
||||
foreach (var method in store.GetSupportedPaymentMethods(_NetworkProvider).OfType<Payments.Lightning.LightningSupportedPaymentMethod>())
|
||||
var lightning = method.GetExternalLightningUrl();
|
||||
if (lightning?.IsLegacy is true)
|
||||
{
|
||||
var lightning = method.GetExternalLightningUrl();
|
||||
if (lightning?.IsLegacy is true)
|
||||
{
|
||||
method.SetLightningUrl(lightning);
|
||||
store.SetSupportedPaymentMethod(method);
|
||||
}
|
||||
method.SetLightningUrl(lightning);
|
||||
store.SetSupportedPaymentMethod(method);
|
||||
}
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,17 +20,15 @@ namespace BTCPayServer.Hosting
|
||||
{
|
||||
_BundlesByName = new Lazy<Dictionary<string, Bundle>>(() =>
|
||||
{
|
||||
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("BTCPayServer.bundleconfig.json"))
|
||||
using (var reader = new StreamReader(stream, Encoding.UTF8))
|
||||
{
|
||||
var content = reader.ReadToEnd();
|
||||
return JArray.Parse(content).OfType<JObject>()
|
||||
.Select(jobj => new Bundle()
|
||||
{
|
||||
Name = jobj.Property("name", StringComparison.OrdinalIgnoreCase)?.Value.Value<string>() ?? jobj.Property("outputFileName", StringComparison.OrdinalIgnoreCase).Value.Value<string>(),
|
||||
OutputFileUrl = Path.Combine(hosting.ContentRootPath, jobj.Property("outputFileName", StringComparison.OrdinalIgnoreCase).Value.Value<string>())
|
||||
}).ToDictionary(o => o.Name, o => o);
|
||||
}
|
||||
using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("BTCPayServer.bundleconfig.json");
|
||||
using var reader = new StreamReader(stream, Encoding.UTF8);
|
||||
var content = reader.ReadToEnd();
|
||||
return JArray.Parse(content).OfType<JObject>()
|
||||
.Select(jobj => new Bundle()
|
||||
{
|
||||
Name = jobj.Property("name", StringComparison.OrdinalIgnoreCase)?.Value.Value<string>() ?? jobj.Property("outputFileName", StringComparison.OrdinalIgnoreCase).Value.Value<string>(),
|
||||
OutputFileUrl = Path.Combine(hosting.ContentRootPath, jobj.Property("outputFileName", StringComparison.OrdinalIgnoreCase).Value.Value<string>())
|
||||
}).ToDictionary(o => o.Name, o => o);
|
||||
}, true);
|
||||
}
|
||||
else
|
||||
|
@ -43,10 +43,8 @@ namespace BTCPayServer.Models
|
||||
}
|
||||
context.HttpContext.Response.Headers.Add("Content-Type", new Microsoft.Extensions.Primitives.StringValues("application/json"));
|
||||
var str = JsonConvert.SerializeObject(jobj);
|
||||
await using (var writer = new StreamWriter(context.HttpContext.Response.Body, new UTF8Encoding(false), 1024 * 10, true))
|
||||
{
|
||||
await writer.WriteAsync(str);
|
||||
}
|
||||
await using var writer = new StreamWriter(context.HttpContext.Response.Body, new UTF8Encoding(false), 1024 * 10, true);
|
||||
await writer.WriteAsync(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -130,42 +130,40 @@ namespace BTCPayServer.Payments.Lightning
|
||||
|
||||
try
|
||||
{
|
||||
using (var cts = new CancellationTokenSource(LIGHTNING_TIMEOUT))
|
||||
using var cts = new CancellationTokenSource(LIGHTNING_TIMEOUT);
|
||||
var client = CreateLightningClient(supportedPaymentMethod, network);
|
||||
LightningNodeInformation info;
|
||||
try
|
||||
{
|
||||
var client = CreateLightningClient(supportedPaymentMethod, network);
|
||||
LightningNodeInformation info;
|
||||
try
|
||||
{
|
||||
info = await client.GetInfo(cts.Token);
|
||||
}
|
||||
catch (OperationCanceledException) when (cts.IsCancellationRequested)
|
||||
{
|
||||
throw new PaymentMethodUnavailableException("The lightning node did not reply in a timely manner");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new PaymentMethodUnavailableException($"Error while connecting to the API: {ex.Message}" +
|
||||
(!string.IsNullOrEmpty(ex.InnerException?.Message) ? $" ({ex.InnerException.Message})" : ""));
|
||||
}
|
||||
|
||||
var nodeInfo = preferOnion != null && info.NodeInfoList.Any(i => i.IsTor == preferOnion)
|
||||
? info.NodeInfoList.Where(i => i.IsTor == preferOnion.Value).ToArray()
|
||||
: info.NodeInfoList.Select(i => i).ToArray();
|
||||
|
||||
// Maybe the user does not have an easily accessible ln node. Node info should be optional. The UI also supports this.
|
||||
// if (!nodeInfo.Any())
|
||||
// {
|
||||
// throw new PaymentMethodUnavailableException("No lightning node public address has been configured");
|
||||
// }
|
||||
|
||||
var blocksGap = summary.Status.ChainHeight - info.BlockHeight;
|
||||
if (blocksGap > 10)
|
||||
{
|
||||
throw new PaymentMethodUnavailableException($"The lightning node is not synched ({blocksGap} blocks left)");
|
||||
}
|
||||
|
||||
return nodeInfo;
|
||||
info = await client.GetInfo(cts.Token);
|
||||
}
|
||||
catch (OperationCanceledException) when (cts.IsCancellationRequested)
|
||||
{
|
||||
throw new PaymentMethodUnavailableException("The lightning node did not reply in a timely manner");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new PaymentMethodUnavailableException($"Error while connecting to the API: {ex.Message}" +
|
||||
(!string.IsNullOrEmpty(ex.InnerException?.Message) ? $" ({ex.InnerException.Message})" : ""));
|
||||
}
|
||||
|
||||
var nodeInfo = preferOnion != null && info.NodeInfoList.Any(i => i.IsTor == preferOnion)
|
||||
? info.NodeInfoList.Where(i => i.IsTor == preferOnion.Value).ToArray()
|
||||
: info.NodeInfoList.Select(i => i).ToArray();
|
||||
|
||||
// Maybe the user does not have an easily accessible ln node. Node info should be optional. The UI also supports this.
|
||||
// if (!nodeInfo.Any())
|
||||
// {
|
||||
// throw new PaymentMethodUnavailableException("No lightning node public address has been configured");
|
||||
// }
|
||||
|
||||
var blocksGap = summary.Status.ChainHeight - info.BlockHeight;
|
||||
if (blocksGap > 10)
|
||||
{
|
||||
throw new PaymentMethodUnavailableException($"The lightning node is not synched ({blocksGap} blocks left)");
|
||||
}
|
||||
|
||||
return nodeInfo;
|
||||
}
|
||||
catch (Exception e) when (!throws)
|
||||
{
|
||||
@ -197,9 +195,7 @@ namespace BTCPayServer.Payments.Lightning
|
||||
if (!Utils.TryParseEndpoint(nodeInfo.Host, nodeInfo.Port, out var endpoint))
|
||||
throw new PaymentMethodUnavailableException($"Could not parse the endpoint {nodeInfo.Host}");
|
||||
|
||||
using (var tcp = await _socketFactory.ConnectAsync(endpoint, cancellation))
|
||||
{
|
||||
}
|
||||
using var tcp = await _socketFactory.ConnectAsync(endpoint, cancellation);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -458,38 +458,36 @@ namespace BTCPayServer.Payments.Lightning
|
||||
try
|
||||
{
|
||||
var lightningClient = _lightningClientFactory.Create(ConnectionString, _network);
|
||||
using (var session = await lightningClient.Listen(cancellation))
|
||||
using var session = await lightningClient.Listen(cancellation);
|
||||
// Just in case the payment arrived after our last poll but before we listened.
|
||||
await PollAllListenedInvoices(cancellation);
|
||||
if (_ErrorAlreadyLogged)
|
||||
{
|
||||
// Just in case the payment arrived after our last poll but before we listened.
|
||||
await PollAllListenedInvoices(cancellation);
|
||||
if (_ErrorAlreadyLogged)
|
||||
Logs.PayServer.LogInformation($"{_network.CryptoCode} (Lightning): Could reconnect successfully to {ConnectionString.BaseUri}");
|
||||
}
|
||||
_ErrorAlreadyLogged = false;
|
||||
while (!_ListenedInvoices.IsEmpty)
|
||||
{
|
||||
var notification = await session.WaitInvoice(cancellation);
|
||||
if (!_ListenedInvoices.TryGetValue(notification.Id, out var listenedInvoice))
|
||||
continue;
|
||||
if (notification.Id == listenedInvoice.PaymentMethodDetails.InvoiceId &&
|
||||
(notification.BOLT11 == listenedInvoice.PaymentMethodDetails.BOLT11 ||
|
||||
BOLT11PaymentRequest.Parse(notification.BOLT11, _network.NBitcoinNetwork).PaymentHash ==
|
||||
listenedInvoice.PaymentMethodDetails.GetPaymentHash(_network.NBitcoinNetwork)))
|
||||
{
|
||||
Logs.PayServer.LogInformation($"{_network.CryptoCode} (Lightning): Could reconnect successfully to {ConnectionString.BaseUri}");
|
||||
}
|
||||
_ErrorAlreadyLogged = false;
|
||||
while (!_ListenedInvoices.IsEmpty)
|
||||
{
|
||||
var notification = await session.WaitInvoice(cancellation);
|
||||
if (!_ListenedInvoices.TryGetValue(notification.Id, out var listenedInvoice))
|
||||
continue;
|
||||
if (notification.Id == listenedInvoice.PaymentMethodDetails.InvoiceId &&
|
||||
(notification.BOLT11 == listenedInvoice.PaymentMethodDetails.BOLT11 ||
|
||||
BOLT11PaymentRequest.Parse(notification.BOLT11, _network.NBitcoinNetwork).PaymentHash ==
|
||||
listenedInvoice.PaymentMethodDetails.GetPaymentHash(_network.NBitcoinNetwork)))
|
||||
if (notification.Status == LightningInvoiceStatus.Paid &&
|
||||
notification.PaidAt.HasValue && notification.Amount != null)
|
||||
{
|
||||
if (notification.Status == LightningInvoiceStatus.Paid &&
|
||||
notification.PaidAt.HasValue && notification.Amount != null)
|
||||
if (await AddPayment(notification, listenedInvoice.InvoiceId, listenedInvoice.PaymentMethod.GetId().PaymentType))
|
||||
{
|
||||
if (await AddPayment(notification, listenedInvoice.InvoiceId, listenedInvoice.PaymentMethod.GetId().PaymentType))
|
||||
{
|
||||
Logs.PayServer.LogInformation($"{_network.CryptoCode} (Lightning): Payment detected via notification ({listenedInvoice.InvoiceId})");
|
||||
}
|
||||
_ListenedInvoices.TryRemove(notification.Id, out var _);
|
||||
}
|
||||
else if (notification.Status == LightningInvoiceStatus.Expired)
|
||||
{
|
||||
_ListenedInvoices.TryRemove(notification.Id, out var _);
|
||||
Logs.PayServer.LogInformation($"{_network.CryptoCode} (Lightning): Payment detected via notification ({listenedInvoice.InvoiceId})");
|
||||
}
|
||||
_ListenedInvoices.TryRemove(notification.Id, out var _);
|
||||
}
|
||||
else if (notification.Status == LightningInvoiceStatus.Expired)
|
||||
{
|
||||
_ListenedInvoices.TryRemove(notification.Id, out var _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,21 +29,17 @@ namespace BTCPayServer.Security.Bitpay
|
||||
{
|
||||
if (sin == null)
|
||||
return Array.Empty<BitTokenEntity>();
|
||||
using (var ctx = _Factory.CreateContext())
|
||||
{
|
||||
return (await ctx.PairedSINData.Where(p => p.SIN == sin)
|
||||
.ToArrayAsync())
|
||||
.Select(p => CreateTokenEntity(p))
|
||||
.ToArray();
|
||||
}
|
||||
using var ctx = _Factory.CreateContext();
|
||||
return (await ctx.PairedSINData.Where(p => p.SIN == sin)
|
||||
.ToArrayAsync())
|
||||
.Select(p => CreateTokenEntity(p))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public async Task<String> GetStoreIdFromAPIKey(string apiKey)
|
||||
{
|
||||
using (var ctx = _Factory.CreateContext())
|
||||
{
|
||||
return await ctx.ApiKeys.Where(o => o.Id == apiKey).Select(o => o.StoreId).FirstOrDefaultAsync();
|
||||
}
|
||||
using var ctx = _Factory.CreateContext();
|
||||
return await ctx.ApiKeys.Where(o => o.Id == apiKey).Select(o => o.StoreId).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task GenerateLegacyAPIKey(string storeId)
|
||||
@ -57,16 +53,14 @@ namespace BTCPayServer.Security.Bitpay
|
||||
generated[i] = chars[(int)(RandomUtils.GetUInt32() % generated.Length)];
|
||||
}
|
||||
|
||||
using (var ctx = _Factory.CreateContext())
|
||||
using var ctx = _Factory.CreateContext();
|
||||
var existing = await ctx.ApiKeys.Where(o => o.StoreId == storeId && o.Type == APIKeyType.Legacy).ToListAsync();
|
||||
if (existing.Any())
|
||||
{
|
||||
var existing = await ctx.ApiKeys.Where(o => o.StoreId == storeId && o.Type == APIKeyType.Legacy).ToListAsync();
|
||||
if (existing.Any())
|
||||
{
|
||||
ctx.ApiKeys.RemoveRange(existing);
|
||||
}
|
||||
ctx.ApiKeys.Add(new APIKeyData() { Id = new string(generated), StoreId = storeId });
|
||||
await ctx.SaveChangesAsync().ConfigureAwait(false);
|
||||
ctx.ApiKeys.RemoveRange(existing);
|
||||
}
|
||||
ctx.ApiKeys.Add(new APIKeyData() { Id = new string(generated), StoreId = storeId });
|
||||
await ctx.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task RevokeLegacyAPIKeys(string storeId)
|
||||
@ -77,19 +71,15 @@ namespace BTCPayServer.Security.Bitpay
|
||||
return;
|
||||
}
|
||||
|
||||
using (var ctx = _Factory.CreateContext())
|
||||
{
|
||||
ctx.ApiKeys.RemoveRange(keys.Select(s => new APIKeyData() { Id = s }));
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
using var ctx = _Factory.CreateContext();
|
||||
ctx.ApiKeys.RemoveRange(keys.Select(s => new APIKeyData() { Id = s }));
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<string[]> GetLegacyAPIKeys(string storeId)
|
||||
{
|
||||
using (var ctx = _Factory.CreateContext())
|
||||
{
|
||||
return await ctx.ApiKeys.Where(o => o.StoreId == storeId && o.Type == APIKeyType.Legacy).Select(c => c.Id).ToArrayAsync();
|
||||
}
|
||||
using var ctx = _Factory.CreateContext();
|
||||
return await ctx.ApiKeys.Where(o => o.StoreId == storeId && o.Type == APIKeyType.Legacy).Select(c => c.Id).ToArrayAsync();
|
||||
}
|
||||
|
||||
private BitTokenEntity CreateTokenEntity(PairedSINData data)
|
||||
@ -131,41 +121,35 @@ namespace BTCPayServer.Security.Bitpay
|
||||
|
||||
public async Task<PairingCodeEntity> UpdatePairingCode(PairingCodeEntity pairingCodeEntity)
|
||||
{
|
||||
using (var ctx = _Factory.CreateContext())
|
||||
{
|
||||
var pairingCode = await ctx.PairingCodes.FindAsync(pairingCodeEntity.Id);
|
||||
pairingCode.Label = pairingCodeEntity.Label;
|
||||
await ctx.SaveChangesAsync();
|
||||
return CreatePairingCodeEntity(pairingCode);
|
||||
}
|
||||
using var ctx = _Factory.CreateContext();
|
||||
var pairingCode = await ctx.PairingCodes.FindAsync(pairingCodeEntity.Id);
|
||||
pairingCode.Label = pairingCodeEntity.Label;
|
||||
await ctx.SaveChangesAsync();
|
||||
return CreatePairingCodeEntity(pairingCode);
|
||||
}
|
||||
|
||||
public async Task<PairingResult> PairWithStoreAsync(string pairingCodeId, string storeId)
|
||||
{
|
||||
using (var ctx = _Factory.CreateContext())
|
||||
{
|
||||
var pairingCode = await ctx.PairingCodes.FindAsync(pairingCodeId);
|
||||
if (pairingCode == null || pairingCode.Expiration < DateTimeOffset.UtcNow)
|
||||
return PairingResult.Expired;
|
||||
pairingCode.StoreDataId = storeId;
|
||||
var result = await ActivateIfComplete(ctx, pairingCode);
|
||||
await ctx.SaveChangesAsync();
|
||||
return result;
|
||||
}
|
||||
using var ctx = _Factory.CreateContext();
|
||||
var pairingCode = await ctx.PairingCodes.FindAsync(pairingCodeId);
|
||||
if (pairingCode == null || pairingCode.Expiration < DateTimeOffset.UtcNow)
|
||||
return PairingResult.Expired;
|
||||
pairingCode.StoreDataId = storeId;
|
||||
var result = await ActivateIfComplete(ctx, pairingCode);
|
||||
await ctx.SaveChangesAsync();
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<PairingResult> PairWithSINAsync(string pairingCodeId, string sin)
|
||||
{
|
||||
using (var ctx = _Factory.CreateContext())
|
||||
{
|
||||
var pairingCode = await ctx.PairingCodes.FindAsync(pairingCodeId);
|
||||
if (pairingCode == null || pairingCode.Expiration < DateTimeOffset.UtcNow)
|
||||
return PairingResult.Expired;
|
||||
pairingCode.SIN = sin;
|
||||
var result = await ActivateIfComplete(ctx, pairingCode);
|
||||
await ctx.SaveChangesAsync();
|
||||
return result;
|
||||
}
|
||||
using var ctx = _Factory.CreateContext();
|
||||
var pairingCode = await ctx.PairingCodes.FindAsync(pairingCodeId);
|
||||
if (pairingCode == null || pairingCode.Expiration < DateTimeOffset.UtcNow)
|
||||
return PairingResult.Expired;
|
||||
pairingCode.SIN = sin;
|
||||
var result = await ActivateIfComplete(ctx, pairingCode);
|
||||
await ctx.SaveChangesAsync();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -195,20 +179,16 @@ namespace BTCPayServer.Security.Bitpay
|
||||
|
||||
public async Task<BitTokenEntity[]> GetTokensByStoreIdAsync(string storeId)
|
||||
{
|
||||
using (var ctx = _Factory.CreateContext())
|
||||
{
|
||||
return (await ctx.PairedSINData.Where(p => p.StoreDataId == storeId).ToListAsync())
|
||||
.Select(c => CreateTokenEntity(c))
|
||||
.ToArray();
|
||||
}
|
||||
using var ctx = _Factory.CreateContext();
|
||||
return (await ctx.PairedSINData.Where(p => p.StoreDataId == storeId).ToListAsync())
|
||||
.Select(c => CreateTokenEntity(c))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public async Task<PairingCodeEntity> GetPairingAsync(string pairingCode)
|
||||
{
|
||||
using (var ctx = _Factory.CreateContext())
|
||||
{
|
||||
return CreatePairingCodeEntity(await ctx.PairingCodes.FindAsync(pairingCode));
|
||||
}
|
||||
using var ctx = _Factory.CreateContext();
|
||||
return CreatePairingCodeEntity(await ctx.PairingCodes.FindAsync(pairingCode));
|
||||
}
|
||||
|
||||
private PairingCodeEntity CreatePairingCodeEntity(PairingCodeData data)
|
||||
@ -229,26 +209,22 @@ namespace BTCPayServer.Security.Bitpay
|
||||
|
||||
public async Task<bool> DeleteToken(string tokenId)
|
||||
{
|
||||
using (var ctx = _Factory.CreateContext())
|
||||
{
|
||||
var token = await ctx.PairedSINData.FindAsync(tokenId);
|
||||
if (token == null)
|
||||
return false;
|
||||
ctx.PairedSINData.Remove(token);
|
||||
await ctx.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
using var ctx = _Factory.CreateContext();
|
||||
var token = await ctx.PairedSINData.FindAsync(tokenId);
|
||||
if (token == null)
|
||||
return false;
|
||||
ctx.PairedSINData.Remove(token);
|
||||
await ctx.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<BitTokenEntity> GetToken(string tokenId)
|
||||
{
|
||||
using (var ctx = _Factory.CreateContext())
|
||||
{
|
||||
var token = await ctx.PairedSINData.FindAsync(tokenId);
|
||||
if (token == null)
|
||||
return null;
|
||||
return CreateTokenEntity(token);
|
||||
}
|
||||
using var ctx = _Factory.CreateContext();
|
||||
var token = await ctx.PairedSINData.FindAsync(tokenId);
|
||||
if (token == null)
|
||||
return null;
|
||||
return CreateTokenEntity(token);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,19 +28,17 @@ namespace BTCPayServer.Security.Greenfield
|
||||
|
||||
public async Task<List<APIKeyData>> GetKeys(APIKeyQuery query)
|
||||
{
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
using var context = _applicationDbContextFactory.CreateContext();
|
||||
var queryable = context.ApiKeys.AsQueryable();
|
||||
if (query != null)
|
||||
{
|
||||
var queryable = context.ApiKeys.AsQueryable();
|
||||
if (query != null)
|
||||
if (query.UserId != null && query.UserId.Any())
|
||||
{
|
||||
if (query.UserId != null && query.UserId.Any())
|
||||
{
|
||||
queryable = queryable.Where(data => query.UserId.Contains(data.UserId));
|
||||
}
|
||||
queryable = queryable.Where(data => query.UserId.Contains(data.UserId));
|
||||
}
|
||||
|
||||
return await queryable.ToListAsync();
|
||||
}
|
||||
|
||||
return await queryable.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task CreateKey(APIKeyData key)
|
||||
@ -50,11 +48,9 @@ namespace BTCPayServer.Security.Greenfield
|
||||
throw new InvalidOperationException("cannot save a bitpay legacy api key with this repository");
|
||||
}
|
||||
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
await context.ApiKeys.AddAsync(key);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
using var context = _applicationDbContextFactory.CreateContext();
|
||||
await context.ApiKeys.AddAsync(key);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<bool> Remove(string id, string getUserId)
|
||||
|
@ -209,53 +209,47 @@ namespace BTCPayServer.Services.Apps
|
||||
|
||||
public async Task<StoreData[]> GetOwnedStores(string userId)
|
||||
{
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
{
|
||||
return await ctx.UserStore
|
||||
.Where(us => us.ApplicationUserId == userId && us.Role == StoreRoles.Owner)
|
||||
.Select(u => u.StoreData)
|
||||
.ToArrayAsync();
|
||||
}
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
return await ctx.UserStore
|
||||
.Where(us => us.ApplicationUserId == userId && us.Role == StoreRoles.Owner)
|
||||
.Select(u => u.StoreData)
|
||||
.ToArrayAsync();
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteApp(AppData appData)
|
||||
{
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
{
|
||||
ctx.Apps.Add(appData);
|
||||
ctx.Entry(appData).State = EntityState.Deleted;
|
||||
return await ctx.SaveChangesAsync() == 1;
|
||||
}
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
ctx.Apps.Add(appData);
|
||||
ctx.Entry(appData).State = EntityState.Deleted;
|
||||
return await ctx.SaveChangesAsync() == 1;
|
||||
}
|
||||
|
||||
public async Task<ListAppsViewModel.ListAppViewModel[]> GetAllApps(string userId, bool allowNoUser = false, string storeId = null)
|
||||
{
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
var listApps = await ctx.UserStore
|
||||
.Where(us =>
|
||||
(allowNoUser && string.IsNullOrEmpty(userId) || us.ApplicationUserId == userId) &&
|
||||
(storeId == null || us.StoreDataId == storeId))
|
||||
.Join(ctx.Apps, us => us.StoreDataId, app => app.StoreDataId,
|
||||
(us, app) =>
|
||||
new ListAppsViewModel.ListAppViewModel()
|
||||
{
|
||||
IsOwner = us.Role == StoreRoles.Owner,
|
||||
StoreId = us.StoreDataId,
|
||||
StoreName = us.StoreData.StoreName,
|
||||
AppName = app.Name,
|
||||
AppType = app.AppType,
|
||||
Id = app.Id
|
||||
})
|
||||
.ToArrayAsync();
|
||||
|
||||
foreach (ListAppsViewModel.ListAppViewModel app in listApps)
|
||||
{
|
||||
var listApps = await ctx.UserStore
|
||||
.Where(us =>
|
||||
(allowNoUser && string.IsNullOrEmpty(userId) || us.ApplicationUserId == userId) &&
|
||||
(storeId == null || us.StoreDataId == storeId))
|
||||
.Join(ctx.Apps, us => us.StoreDataId, app => app.StoreDataId,
|
||||
(us, app) =>
|
||||
new ListAppsViewModel.ListAppViewModel()
|
||||
{
|
||||
IsOwner = us.Role == StoreRoles.Owner,
|
||||
StoreId = us.StoreDataId,
|
||||
StoreName = us.StoreData.StoreName,
|
||||
AppName = app.Name,
|
||||
AppType = app.AppType,
|
||||
Id = app.Id
|
||||
})
|
||||
.ToArrayAsync();
|
||||
|
||||
foreach (ListAppsViewModel.ListAppViewModel app in listApps)
|
||||
{
|
||||
app.ViewStyle = await GetAppViewStyleAsync(app.Id, app.AppType);
|
||||
}
|
||||
|
||||
return listApps;
|
||||
app.ViewStyle = await GetAppViewStyleAsync(app.Id, app.AppType);
|
||||
}
|
||||
|
||||
return listApps;
|
||||
}
|
||||
|
||||
public async Task<string> GetAppViewStyleAsync(string appId, string appType)
|
||||
@ -284,33 +278,29 @@ namespace BTCPayServer.Services.Apps
|
||||
|
||||
public async Task<List<AppData>> GetApps(string[] appIds, bool includeStore = false)
|
||||
{
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
{
|
||||
var query = ctx.Apps
|
||||
.Where(us => appIds.Contains(us.Id));
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
var query = ctx.Apps
|
||||
.Where(us => appIds.Contains(us.Id));
|
||||
|
||||
if (includeStore)
|
||||
{
|
||||
query = query.Include(data => data.StoreData);
|
||||
}
|
||||
return await query.ToListAsync();
|
||||
if (includeStore)
|
||||
{
|
||||
query = query.Include(data => data.StoreData);
|
||||
}
|
||||
return await query.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<AppData> GetApp(string appId, AppType? appType, bool includeStore = false)
|
||||
{
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
{
|
||||
var query = ctx.Apps
|
||||
.Where(us => us.Id == appId &&
|
||||
(appType == null || us.AppType == appType.ToString()));
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
var query = ctx.Apps
|
||||
.Where(us => us.Id == appId &&
|
||||
(appType == null || us.AppType == appType.ToString()));
|
||||
|
||||
if (includeStore)
|
||||
{
|
||||
query = query.Include(data => data.StoreData);
|
||||
}
|
||||
return await query.FirstOrDefaultAsync();
|
||||
if (includeStore)
|
||||
{
|
||||
query = query.Include(data => data.StoreData);
|
||||
}
|
||||
return await query.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public Task<StoreData> GetStore(AppData app)
|
||||
@ -525,39 +515,35 @@ namespace BTCPayServer.Services.Apps
|
||||
{
|
||||
if (userId == null || appId == null)
|
||||
return null;
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
{
|
||||
var app = await ctx.UserStore
|
||||
.Where(us => us.ApplicationUserId == userId && us.Role == StoreRoles.Owner)
|
||||
.SelectMany(us => us.StoreData.Apps.Where(a => a.Id == appId))
|
||||
.FirstOrDefaultAsync();
|
||||
if (app == null)
|
||||
return null;
|
||||
if (type != null && type.Value.ToString() != app.AppType)
|
||||
return null;
|
||||
return app;
|
||||
}
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
var app = await ctx.UserStore
|
||||
.Where(us => us.ApplicationUserId == userId && us.Role == StoreRoles.Owner)
|
||||
.SelectMany(us => us.StoreData.Apps.Where(a => a.Id == appId))
|
||||
.FirstOrDefaultAsync();
|
||||
if (app == null)
|
||||
return null;
|
||||
if (type != null && type.Value.ToString() != app.AppType)
|
||||
return null;
|
||||
return app;
|
||||
}
|
||||
|
||||
public async Task UpdateOrCreateApp(AppData app)
|
||||
{
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
if (string.IsNullOrEmpty(app.Id))
|
||||
{
|
||||
if (string.IsNullOrEmpty(app.Id))
|
||||
{
|
||||
app.Id = Encoders.Base58.EncodeData(RandomUtils.GetBytes(20));
|
||||
app.Created = DateTimeOffset.UtcNow;
|
||||
await ctx.Apps.AddAsync(app);
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.Apps.Update(app);
|
||||
ctx.Entry(app).Property(data => data.Created).IsModified = false;
|
||||
ctx.Entry(app).Property(data => data.Id).IsModified = false;
|
||||
ctx.Entry(app).Property(data => data.AppType).IsModified = false;
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
app.Id = Encoders.Base58.EncodeData(RandomUtils.GetBytes(20));
|
||||
app.Created = DateTimeOffset.UtcNow;
|
||||
await ctx.Apps.AddAsync(app);
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.Apps.Update(app);
|
||||
ctx.Entry(app).Property(data => data.Created).IsModified = false;
|
||||
ctx.Entry(app).Property(data => data.Id).IsModified = false;
|
||||
ctx.Entry(app).Property(data => data.AppType).IsModified = false;
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private static bool TryParseJson(string json, out JObject result)
|
||||
|
@ -28,14 +28,12 @@ namespace BTCPayServer.Services
|
||||
|
||||
public async Task UpdateInvoiceExpiry(string invoiceId, DateTimeOffset dateTimeOffset)
|
||||
{
|
||||
using (var ctx = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
var invoiceData = await ctx.Invoices.FindAsync(invoiceId).ConfigureAwait(false);
|
||||
if (invoiceData == null)
|
||||
return;
|
||||
// TODO change the expiry time. But how?
|
||||
await ctx.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
using var ctx = _applicationDbContextFactory.CreateContext();
|
||||
var invoiceData = await ctx.Invoices.FindAsync(invoiceId).ConfigureAwait(false);
|
||||
if (invoiceData == null)
|
||||
return;
|
||||
// TODO change the expiry time. But how?
|
||||
await ctx.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
Task IHostedService.StartAsync(CancellationToken cancellationToken)
|
||||
|
@ -44,21 +44,19 @@ namespace BTCPayServer.Services
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(transaction);
|
||||
ArgumentNullException.ThrowIfNull(network);
|
||||
using (var db = _dbContextFactory.CreateContext())
|
||||
using var db = _dbContextFactory.CreateContext();
|
||||
db.PlannedTransactions.Add(new PlannedTransaction()
|
||||
{
|
||||
Id = $"{network.CryptoCode}-{transaction.GetHash()}",
|
||||
BroadcastAt = broadcastTime,
|
||||
Blob = transaction.ToBytes()
|
||||
});
|
||||
try
|
||||
{
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateException)
|
||||
{
|
||||
db.PlannedTransactions.Add(new PlannedTransaction()
|
||||
{
|
||||
Id = $"{network.CryptoCode}-{transaction.GetHash()}",
|
||||
BroadcastAt = broadcastTime,
|
||||
Blob = transaction.ToBytes()
|
||||
});
|
||||
try
|
||||
{
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,38 +67,32 @@ namespace BTCPayServer.Services.Invoices
|
||||
public async Task<bool> RemovePendingInvoice(string invoiceId)
|
||||
{
|
||||
Logs.PayServer.LogInformation($"Remove pending invoice {invoiceId}");
|
||||
using (var ctx = _applicationDbContextFactory.CreateContext())
|
||||
using var ctx = _applicationDbContextFactory.CreateContext();
|
||||
ctx.PendingInvoices.Remove(new PendingInvoiceData() { Id = invoiceId });
|
||||
try
|
||||
{
|
||||
ctx.PendingInvoices.Remove(new PendingInvoiceData() { Id = invoiceId });
|
||||
try
|
||||
{
|
||||
await ctx.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
catch (DbUpdateException) { return false; }
|
||||
await ctx.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
catch (DbUpdateException) { return false; }
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<InvoiceEntity>> GetInvoicesFromAddresses(string[] addresses)
|
||||
{
|
||||
using (var db = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
return (await db.AddressInvoices
|
||||
.Include(a => a.InvoiceData.Payments)
|
||||
using var db = _applicationDbContextFactory.CreateContext();
|
||||
return (await db.AddressInvoices
|
||||
.Include(a => a.InvoiceData.Payments)
|
||||
#pragma warning disable CS0618
|
||||
.Where(a => addresses.Contains(a.Address))
|
||||
#pragma warning restore CS0618
|
||||
.Select(a => a.InvoiceData)
|
||||
.ToListAsync()).Select(ToEntity);
|
||||
}
|
||||
.ToListAsync()).Select(ToEntity);
|
||||
}
|
||||
|
||||
public async Task<string[]> GetPendingInvoices()
|
||||
{
|
||||
using (var ctx = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
return await ctx.PendingInvoices.AsQueryable().Select(data => data.Id).ToArrayAsync();
|
||||
}
|
||||
using var ctx = _applicationDbContextFactory.CreateContext();
|
||||
return await ctx.PendingInvoices.AsQueryable().Select(data => data.Id).ToArrayAsync();
|
||||
}
|
||||
|
||||
public async Task<List<Data.WebhookDeliveryData>> GetWebhookDeliveries(string invoiceId)
|
||||
@ -115,40 +109,34 @@ namespace BTCPayServer.Services.Invoices
|
||||
public async Task<AppData[]> GetAppsTaggingStore(string storeId)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(storeId);
|
||||
using (var ctx = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
return await ctx.Apps.Where(a => a.StoreDataId == storeId && a.TagAllInvoices).ToArrayAsync();
|
||||
}
|
||||
using var ctx = _applicationDbContextFactory.CreateContext();
|
||||
return await ctx.Apps.Where(a => a.StoreDataId == storeId && a.TagAllInvoices).ToArrayAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateInvoice(string invoiceId, UpdateCustomerModel data)
|
||||
{
|
||||
using (var ctx = _applicationDbContextFactory.CreateContext())
|
||||
using var ctx = _applicationDbContextFactory.CreateContext();
|
||||
var invoiceData = await ctx.Invoices.FindAsync(invoiceId).ConfigureAwait(false);
|
||||
if (invoiceData == null)
|
||||
return;
|
||||
if (invoiceData.CustomerEmail == null && data.Email != null)
|
||||
{
|
||||
var invoiceData = await ctx.Invoices.FindAsync(invoiceId).ConfigureAwait(false);
|
||||
if (invoiceData == null)
|
||||
return;
|
||||
if (invoiceData.CustomerEmail == null && data.Email != null)
|
||||
{
|
||||
invoiceData.CustomerEmail = data.Email;
|
||||
AddToTextSearch(ctx, invoiceData, invoiceData.CustomerEmail);
|
||||
}
|
||||
await ctx.SaveChangesAsync().ConfigureAwait(false);
|
||||
invoiceData.CustomerEmail = data.Email;
|
||||
AddToTextSearch(ctx, invoiceData, invoiceData.CustomerEmail);
|
||||
}
|
||||
await ctx.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task ExtendInvoiceMonitor(string invoiceId)
|
||||
{
|
||||
using (var ctx = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
var invoiceData = await ctx.Invoices.FindAsync(invoiceId);
|
||||
using var ctx = _applicationDbContextFactory.CreateContext();
|
||||
var invoiceData = await ctx.Invoices.FindAsync(invoiceId);
|
||||
|
||||
var invoice = invoiceData.GetBlob(_btcPayNetworkProvider);
|
||||
invoice.MonitoringExpiration = invoice.MonitoringExpiration.AddHours(1);
|
||||
invoiceData.Blob = ToBytes(invoice, null);
|
||||
var invoice = invoiceData.GetBlob(_btcPayNetworkProvider);
|
||||
invoice.MonitoringExpiration = invoice.MonitoringExpiration.AddHours(1);
|
||||
invoiceData.Blob = ToBytes(invoice, null);
|
||||
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<InvoiceEntity> CreateInvoiceAsync(string storeId, InvoiceEntity invoice, string[] additionalSearchTerms = null)
|
||||
@ -316,50 +304,45 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public async Task UpdateInvoicePaymentMethod(string invoiceId, PaymentMethod paymentMethod)
|
||||
{
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
using var context = _applicationDbContextFactory.CreateContext();
|
||||
var invoice = await context.Invoices.FindAsync(invoiceId);
|
||||
if (invoice == null)
|
||||
return;
|
||||
var network = paymentMethod.Network;
|
||||
var invoiceEntity = invoice.GetBlob(_btcPayNetworkProvider);
|
||||
var newDetails = paymentMethod.GetPaymentMethodDetails();
|
||||
var existing = invoiceEntity.GetPaymentMethod(paymentMethod.GetId());
|
||||
if (existing.GetPaymentMethodDetails().GetPaymentDestination() != newDetails.GetPaymentDestination() && newDetails.Activated)
|
||||
{
|
||||
var invoice = await context.Invoices.FindAsync(invoiceId);
|
||||
if (invoice == null)
|
||||
return;
|
||||
var network = paymentMethod.Network;
|
||||
var invoiceEntity = invoice.GetBlob(_btcPayNetworkProvider);
|
||||
var newDetails = paymentMethod.GetPaymentMethodDetails();
|
||||
var existing = invoiceEntity.GetPaymentMethod(paymentMethod.GetId());
|
||||
if (existing.GetPaymentMethodDetails().GetPaymentDestination() != newDetails.GetPaymentDestination() && newDetails.Activated)
|
||||
await context.AddressInvoices.AddAsync(new AddressInvoiceData()
|
||||
{
|
||||
await context.AddressInvoices.AddAsync(new AddressInvoiceData()
|
||||
{
|
||||
InvoiceDataId = invoiceId,
|
||||
CreatedTime = DateTimeOffset.UtcNow
|
||||
}
|
||||
.Set(GetDestination(paymentMethod), paymentMethod.GetId()));
|
||||
await context.HistoricalAddressInvoices.AddAsync(new HistoricalAddressInvoiceData()
|
||||
{
|
||||
InvoiceDataId = invoiceId,
|
||||
Assigned = DateTimeOffset.UtcNow
|
||||
}.SetAddress(paymentMethod.GetPaymentMethodDetails().GetPaymentDestination(), network.CryptoCode));
|
||||
InvoiceDataId = invoiceId,
|
||||
CreatedTime = DateTimeOffset.UtcNow
|
||||
}
|
||||
invoiceEntity.SetPaymentMethod(paymentMethod);
|
||||
invoice.Blob = ToBytes(invoiceEntity, network);
|
||||
AddToTextSearch(context, invoice, paymentMethod.GetPaymentMethodDetails().GetPaymentDestination());
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
.Set(GetDestination(paymentMethod), paymentMethod.GetId()));
|
||||
await context.HistoricalAddressInvoices.AddAsync(new HistoricalAddressInvoiceData()
|
||||
{
|
||||
InvoiceDataId = invoiceId,
|
||||
Assigned = DateTimeOffset.UtcNow
|
||||
}.SetAddress(paymentMethod.GetPaymentMethodDetails().GetPaymentDestination(), network.CryptoCode));
|
||||
}
|
||||
invoiceEntity.SetPaymentMethod(paymentMethod);
|
||||
invoice.Blob = ToBytes(invoiceEntity, network);
|
||||
AddToTextSearch(context, invoice, paymentMethod.GetPaymentMethodDetails().GetPaymentDestination());
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task AddPendingInvoiceIfNotPresent(string invoiceId)
|
||||
{
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
using var context = _applicationDbContextFactory.CreateContext();
|
||||
if (!context.PendingInvoices.Any(a => a.Id == invoiceId))
|
||||
{
|
||||
if (!context.PendingInvoices.Any(a => a.Id == invoiceId))
|
||||
context.PendingInvoices.Add(new PendingInvoiceData() { Id = invoiceId });
|
||||
try
|
||||
{
|
||||
context.PendingInvoices.Add(new PendingInvoiceData() { Id = invoiceId });
|
||||
try
|
||||
{
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateException) { } // Already exists
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateException) { } // Already exists
|
||||
}
|
||||
}
|
||||
|
||||
@ -419,80 +402,70 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public async Task UpdateInvoiceStatus(string invoiceId, InvoiceState invoiceState)
|
||||
{
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
var invoiceData = await context.FindAsync<Data.InvoiceData>(invoiceId).ConfigureAwait(false);
|
||||
if (invoiceData == null)
|
||||
return;
|
||||
invoiceData.Status = InvoiceState.ToString(invoiceState.Status);
|
||||
invoiceData.ExceptionStatus = InvoiceState.ToString(invoiceState.ExceptionStatus);
|
||||
await context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
using var context = _applicationDbContextFactory.CreateContext();
|
||||
var invoiceData = await context.FindAsync<Data.InvoiceData>(invoiceId).ConfigureAwait(false);
|
||||
if (invoiceData == null)
|
||||
return;
|
||||
invoiceData.Status = InvoiceState.ToString(invoiceState.Status);
|
||||
invoiceData.ExceptionStatus = InvoiceState.ToString(invoiceState.ExceptionStatus);
|
||||
await context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
internal async Task UpdateInvoicePrice(string invoiceId, InvoiceEntity invoice)
|
||||
{
|
||||
if (invoice.Type != InvoiceType.TopUp)
|
||||
throw new ArgumentException("The invoice type should be TopUp to be able to update invoice price", nameof(invoice));
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
var invoiceData = await context.FindAsync<Data.InvoiceData>(invoiceId).ConfigureAwait(false);
|
||||
if (invoiceData == null)
|
||||
return;
|
||||
var blob = invoiceData.GetBlob(_btcPayNetworkProvider);
|
||||
blob.Price = invoice.Price;
|
||||
AddToTextSearch(context, invoiceData, new[] { invoice.Price.ToString(CultureInfo.InvariantCulture) });
|
||||
invoiceData.Blob = ToBytes(blob, null);
|
||||
await context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
using var context = _applicationDbContextFactory.CreateContext();
|
||||
var invoiceData = await context.FindAsync<Data.InvoiceData>(invoiceId).ConfigureAwait(false);
|
||||
if (invoiceData == null)
|
||||
return;
|
||||
var blob = invoiceData.GetBlob(_btcPayNetworkProvider);
|
||||
blob.Price = invoice.Price;
|
||||
AddToTextSearch(context, invoiceData, new[] { invoice.Price.ToString(CultureInfo.InvariantCulture) });
|
||||
invoiceData.Blob = ToBytes(blob, null);
|
||||
await context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task MassArchive(string[] invoiceIds, bool archive = true)
|
||||
{
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
using var context = _applicationDbContextFactory.CreateContext();
|
||||
var items = context.Invoices.Where(a => invoiceIds.Contains(a.Id));
|
||||
if (items == null)
|
||||
{
|
||||
var items = context.Invoices.Where(a => invoiceIds.Contains(a.Id));
|
||||
if (items == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (InvoiceData invoice in items)
|
||||
{
|
||||
invoice.Archived = archive;
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (InvoiceData invoice in items)
|
||||
{
|
||||
invoice.Archived = archive;
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task ToggleInvoiceArchival(string invoiceId, bool archived, string storeId = null)
|
||||
{
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
var invoiceData = await context.FindAsync<InvoiceData>(invoiceId).ConfigureAwait(false);
|
||||
if (invoiceData == null || invoiceData.Archived == archived ||
|
||||
(storeId != null &&
|
||||
!invoiceData.StoreDataId.Equals(storeId, StringComparison.InvariantCultureIgnoreCase)))
|
||||
return;
|
||||
invoiceData.Archived = archived;
|
||||
await context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
using var context = _applicationDbContextFactory.CreateContext();
|
||||
var invoiceData = await context.FindAsync<InvoiceData>(invoiceId).ConfigureAwait(false);
|
||||
if (invoiceData == null || invoiceData.Archived == archived ||
|
||||
(storeId != null &&
|
||||
!invoiceData.StoreDataId.Equals(storeId, StringComparison.InvariantCultureIgnoreCase)))
|
||||
return;
|
||||
invoiceData.Archived = archived;
|
||||
await context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
public async Task<InvoiceEntity> UpdateInvoiceMetadata(string invoiceId, string storeId, JObject metadata)
|
||||
{
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
var invoiceData = await GetInvoiceRaw(invoiceId, context);
|
||||
if (invoiceData == null || (storeId != null &&
|
||||
!invoiceData.StoreDataId.Equals(storeId,
|
||||
StringComparison.InvariantCultureIgnoreCase)))
|
||||
return null;
|
||||
var blob = invoiceData.GetBlob(_btcPayNetworkProvider);
|
||||
blob.Metadata = InvoiceMetadata.FromJObject(metadata);
|
||||
invoiceData.Blob = ToBytes(blob);
|
||||
await context.SaveChangesAsync().ConfigureAwait(false);
|
||||
return ToEntity(invoiceData);
|
||||
}
|
||||
using var context = _applicationDbContextFactory.CreateContext();
|
||||
var invoiceData = await GetInvoiceRaw(invoiceId, context);
|
||||
if (invoiceData == null || (storeId != null &&
|
||||
!invoiceData.StoreDataId.Equals(storeId,
|
||||
StringComparison.InvariantCultureIgnoreCase)))
|
||||
return null;
|
||||
var blob = invoiceData.GetBlob(_btcPayNetworkProvider);
|
||||
blob.Metadata = InvoiceMetadata.FromJObject(metadata);
|
||||
invoiceData.Blob = ToBytes(blob);
|
||||
await context.SaveChangesAsync().ConfigureAwait(false);
|
||||
return ToEntity(invoiceData);
|
||||
}
|
||||
public async Task<bool> MarkInvoiceStatus(string invoiceId, InvoiceStatus status)
|
||||
{
|
||||
@ -541,25 +514,21 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public async Task<InvoiceEntity> GetInvoice(string id, bool includeAddressData = false)
|
||||
{
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
var res = await GetInvoiceRaw(id, context, includeAddressData);
|
||||
return res == null ? null : ToEntity(res);
|
||||
}
|
||||
using var context = _applicationDbContextFactory.CreateContext();
|
||||
var res = await GetInvoiceRaw(id, context, includeAddressData);
|
||||
return res == null ? null : ToEntity(res);
|
||||
}
|
||||
public async Task<InvoiceEntity[]> GetInvoices(string[] invoiceIds)
|
||||
{
|
||||
var invoiceIdSet = invoiceIds.ToHashSet();
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
IQueryable<Data.InvoiceData> query =
|
||||
context
|
||||
.Invoices
|
||||
.Include(o => o.Payments)
|
||||
.Where(o => invoiceIdSet.Contains(o.Id));
|
||||
using var context = _applicationDbContextFactory.CreateContext();
|
||||
IQueryable<Data.InvoiceData> query =
|
||||
context
|
||||
.Invoices
|
||||
.Include(o => o.Payments)
|
||||
.Where(o => invoiceIdSet.Contains(o.Id));
|
||||
|
||||
return (await query.ToListAsync()).Select(o => ToEntity(o)).ToArray();
|
||||
}
|
||||
return (await query.ToListAsync()).Select(o => ToEntity(o)).ToArray();
|
||||
}
|
||||
|
||||
private async Task<InvoiceData> GetInvoiceRaw(string id, ApplicationDbContext dbContext, bool includeAddressData = false)
|
||||
@ -710,26 +679,22 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public async Task<int> GetInvoicesTotal(InvoiceQuery queryObject)
|
||||
{
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
var query = GetInvoiceQuery(context, queryObject);
|
||||
return await query.CountAsync();
|
||||
}
|
||||
using var context = _applicationDbContextFactory.CreateContext();
|
||||
var query = GetInvoiceQuery(context, queryObject);
|
||||
return await query.CountAsync();
|
||||
}
|
||||
|
||||
public async Task<InvoiceEntity[]> GetInvoices(InvoiceQuery queryObject)
|
||||
{
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
var query = GetInvoiceQuery(context, queryObject);
|
||||
query = query.Include(o => o.Payments);
|
||||
if (queryObject.IncludeAddresses)
|
||||
query = query.Include(o => o.HistoricalAddressInvoices).Include(o => o.AddressInvoices);
|
||||
if (queryObject.IncludeEvents)
|
||||
query = query.Include(o => o.Events);
|
||||
var data = await query.ToArrayAsync().ConfigureAwait(false);
|
||||
return data.Select(ToEntity).ToArray();
|
||||
}
|
||||
using var context = _applicationDbContextFactory.CreateContext();
|
||||
var query = GetInvoiceQuery(context, queryObject);
|
||||
query = query.Include(o => o.Payments);
|
||||
if (queryObject.IncludeAddresses)
|
||||
query = query.Include(o => o.HistoricalAddressInvoices).Include(o => o.AddressInvoices);
|
||||
if (queryObject.IncludeEvents)
|
||||
query = query.Include(o => o.Events);
|
||||
var data = await query.ToArrayAsync().ConfigureAwait(false);
|
||||
return data.Select(ToEntity).ToArray();
|
||||
}
|
||||
|
||||
private string NormalizeExceptionStatus(string status)
|
||||
|
@ -37,11 +37,9 @@ namespace BTCPayServer.Services
|
||||
var result = new List<Language>();
|
||||
foreach (var file in files)
|
||||
{
|
||||
using (var stream = new StreamReader(file))
|
||||
{
|
||||
var json = stream.ReadToEnd();
|
||||
result.Add(JObject.Parse(json).ToObject<Language>());
|
||||
}
|
||||
using var stream = new StreamReader(file);
|
||||
var json = stream.ReadToEnd();
|
||||
result.Add(JObject.Parse(json).ToObject<Language>());
|
||||
}
|
||||
|
||||
_languages = result.ToArray();
|
||||
|
@ -30,12 +30,10 @@ namespace BTCPayServer.Services.Mails
|
||||
Logs.Configuration.LogWarning("Should have sent email, but email settings are not configured");
|
||||
return;
|
||||
}
|
||||
using (var smtp = await emailSettings.CreateSmtpClient())
|
||||
{
|
||||
var mail = emailSettings.CreateMailMessage(new MailboxAddress(email, email), subject, message, true);
|
||||
await smtp.SendAsync(mail, cancellationToken);
|
||||
await smtp.DisconnectAsync(true, cancellationToken);
|
||||
}
|
||||
using var smtp = await emailSettings.CreateSmtpClient();
|
||||
var mail = emailSettings.CreateMailMessage(new MailboxAddress(email, email), subject, message, true);
|
||||
await smtp.SendAsync(mail, cancellationToken);
|
||||
await smtp.DisconnectAsync(true, cancellationToken);
|
||||
}, TimeSpan.Zero);
|
||||
}
|
||||
|
||||
|
@ -22,21 +22,19 @@ namespace BTCPayServer.Services.PaymentRequests
|
||||
|
||||
public async Task<PaymentRequestData> CreateOrUpdatePaymentRequest(PaymentRequestData entity)
|
||||
{
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
using var context = _ContextFactory.CreateContext();
|
||||
if (string.IsNullOrEmpty(entity.Id))
|
||||
{
|
||||
if (string.IsNullOrEmpty(entity.Id))
|
||||
{
|
||||
entity.Id = Guid.NewGuid().ToString();
|
||||
await context.PaymentRequests.AddAsync(entity);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.PaymentRequests.Update(entity);
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
return entity;
|
||||
entity.Id = Guid.NewGuid().ToString();
|
||||
await context.PaymentRequests.AddAsync(entity);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.PaymentRequests.Update(entity);
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
return entity;
|
||||
}
|
||||
|
||||
public async Task<PaymentRequestData> FindPaymentRequest(string id, string userId, CancellationToken cancellationToken = default)
|
||||
@ -46,15 +44,13 @@ namespace BTCPayServer.Services.PaymentRequests
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
{
|
||||
var result = await context.PaymentRequests.Include(x => x.StoreData)
|
||||
.Where(data =>
|
||||
string.IsNullOrEmpty(userId) ||
|
||||
(data.StoreData != null && data.StoreData.UserStores.Any(u => u.ApplicationUserId == userId)))
|
||||
.SingleOrDefaultAsync(x => x.Id == id, cancellationToken);
|
||||
return result;
|
||||
}
|
||||
using var context = _ContextFactory.CreateContext();
|
||||
var result = await context.PaymentRequests.Include(x => x.StoreData)
|
||||
.Where(data =>
|
||||
string.IsNullOrEmpty(userId) ||
|
||||
(data.StoreData != null && data.StoreData.UserStores.Any(u => u.ApplicationUserId == userId)))
|
||||
.SingleOrDefaultAsync(x => x.Id == id, cancellationToken);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<bool> IsPaymentRequestAdmin(string paymentRequestId, string userId)
|
||||
@ -63,76 +59,70 @@ namespace BTCPayServer.Services.PaymentRequests
|
||||
{
|
||||
return false;
|
||||
}
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
{
|
||||
return await context.PaymentRequests.Include(x => x.StoreData)
|
||||
.AnyAsync(data =>
|
||||
data.Id == paymentRequestId &&
|
||||
(data.StoreData != null && data.StoreData.UserStores.Any(u => u.ApplicationUserId == userId)));
|
||||
}
|
||||
using var context = _ContextFactory.CreateContext();
|
||||
return await context.PaymentRequests.Include(x => x.StoreData)
|
||||
.AnyAsync(data =>
|
||||
data.Id == paymentRequestId &&
|
||||
(data.StoreData != null && data.StoreData.UserStores.Any(u => u.ApplicationUserId == userId)));
|
||||
}
|
||||
|
||||
public async Task UpdatePaymentRequestStatus(string paymentRequestId, Client.Models.PaymentRequestData.PaymentRequestStatus status, CancellationToken cancellationToken = default)
|
||||
{
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
{
|
||||
var invoiceData = await context.FindAsync<PaymentRequestData>(paymentRequestId);
|
||||
if (invoiceData == null)
|
||||
return;
|
||||
invoiceData.Status = status;
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
using var context = _ContextFactory.CreateContext();
|
||||
var invoiceData = await context.FindAsync<PaymentRequestData>(paymentRequestId);
|
||||
if (invoiceData == null)
|
||||
return;
|
||||
invoiceData.Status = status;
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<(int Total, PaymentRequestData[] Items)> FindPaymentRequests(PaymentRequestQuery query, CancellationToken cancellationToken = default)
|
||||
{
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
using var context = _ContextFactory.CreateContext();
|
||||
var queryable = context.PaymentRequests.Include(data => data.StoreData).AsQueryable();
|
||||
|
||||
if (!query.IncludeArchived)
|
||||
{
|
||||
var queryable = context.PaymentRequests.Include(data => data.StoreData).AsQueryable();
|
||||
|
||||
if (!query.IncludeArchived)
|
||||
{
|
||||
queryable = queryable.Where(data => !data.Archived);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(query.StoreId))
|
||||
{
|
||||
queryable = queryable.Where(data =>
|
||||
data.StoreDataId == query.StoreId);
|
||||
}
|
||||
|
||||
if (query.Status != null && query.Status.Any())
|
||||
{
|
||||
queryable = queryable.Where(data =>
|
||||
query.Status.Contains(data.Status));
|
||||
}
|
||||
|
||||
if (query.Ids != null && query.Ids.Any())
|
||||
{
|
||||
queryable = queryable.Where(data =>
|
||||
query.Ids.Contains(data.Id));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(query.UserId))
|
||||
{
|
||||
queryable = queryable.Where(i =>
|
||||
i.StoreData != null && i.StoreData.UserStores.Any(u => u.ApplicationUserId == query.UserId));
|
||||
}
|
||||
|
||||
var total = await queryable.CountAsync(cancellationToken);
|
||||
|
||||
queryable = queryable.OrderByDescending(u => u.Created);
|
||||
|
||||
if (query.Skip.HasValue)
|
||||
{
|
||||
queryable = queryable.Skip(query.Skip.Value);
|
||||
}
|
||||
|
||||
if (query.Count.HasValue)
|
||||
{
|
||||
queryable = queryable.Take(query.Count.Value);
|
||||
}
|
||||
return (total, await queryable.ToArrayAsync(cancellationToken));
|
||||
queryable = queryable.Where(data => !data.Archived);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(query.StoreId))
|
||||
{
|
||||
queryable = queryable.Where(data =>
|
||||
data.StoreDataId == query.StoreId);
|
||||
}
|
||||
|
||||
if (query.Status != null && query.Status.Any())
|
||||
{
|
||||
queryable = queryable.Where(data =>
|
||||
query.Status.Contains(data.Status));
|
||||
}
|
||||
|
||||
if (query.Ids != null && query.Ids.Any())
|
||||
{
|
||||
queryable = queryable.Where(data =>
|
||||
query.Ids.Contains(data.Id));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(query.UserId))
|
||||
{
|
||||
queryable = queryable.Where(i =>
|
||||
i.StoreData != null && i.StoreData.UserStores.Any(u => u.ApplicationUserId == query.UserId));
|
||||
}
|
||||
|
||||
var total = await queryable.CountAsync(cancellationToken);
|
||||
|
||||
queryable = queryable.OrderByDescending(u => u.Created);
|
||||
|
||||
if (query.Skip.HasValue)
|
||||
{
|
||||
queryable = queryable.Skip(query.Skip.Value);
|
||||
}
|
||||
|
||||
if (query.Count.HasValue)
|
||||
{
|
||||
queryable = queryable.Take(query.Count.Value);
|
||||
}
|
||||
return (total, await queryable.ToArrayAsync(cancellationToken));
|
||||
}
|
||||
|
||||
public async Task<InvoiceEntity[]> GetInvoicesForPaymentRequest(string paymentRequestId,
|
||||
|
@ -26,11 +26,9 @@ namespace BTCPayServer.Services.Stores
|
||||
{
|
||||
if (storeId == null)
|
||||
return null;
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
{
|
||||
var result = await ctx.FindAsync<StoreData>(storeId).ConfigureAwait(false);
|
||||
return result;
|
||||
}
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
var result = await ctx.FindAsync<StoreData>(storeId).ConfigureAwait(false);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<StoreData> FindStore(string storeId, string userId)
|
||||
@ -62,34 +60,30 @@ namespace BTCPayServer.Services.Stores
|
||||
public async Task<StoreUser[]> GetStoreUsers(string storeId)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(storeId);
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
{
|
||||
return await ctx
|
||||
.UserStore
|
||||
.Where(u => u.StoreDataId == storeId)
|
||||
.Select(u => new StoreUser()
|
||||
{
|
||||
Id = u.ApplicationUserId,
|
||||
Email = u.ApplicationUser.Email,
|
||||
Role = u.Role
|
||||
}).ToArrayAsync();
|
||||
}
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
return await ctx
|
||||
.UserStore
|
||||
.Where(u => u.StoreDataId == storeId)
|
||||
.Select(u => new StoreUser()
|
||||
{
|
||||
Id = u.ApplicationUserId,
|
||||
Email = u.ApplicationUser.Email,
|
||||
Role = u.Role
|
||||
}).ToArrayAsync();
|
||||
}
|
||||
|
||||
public async Task<StoreData[]> GetStoresByUserId(string userId, IEnumerable<string> storeIds = null)
|
||||
{
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
{
|
||||
return (await ctx.UserStore
|
||||
.Where(u => u.ApplicationUserId == userId && (storeIds == null || storeIds.Contains(u.StoreDataId)))
|
||||
.Select(u => new { u.StoreData, u.Role })
|
||||
.ToArrayAsync())
|
||||
.Select(u =>
|
||||
{
|
||||
u.StoreData.Role = u.Role;
|
||||
return u.StoreData;
|
||||
}).ToArray();
|
||||
}
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
return (await ctx.UserStore
|
||||
.Where(u => u.ApplicationUserId == userId && (storeIds == null || storeIds.Contains(u.StoreDataId)))
|
||||
.Select(u => new { u.StoreData, u.Role })
|
||||
.ToArrayAsync())
|
||||
.Select(u =>
|
||||
{
|
||||
u.StoreData.Role = u.Role;
|
||||
return u.StoreData;
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
public async Task<StoreData> GetStoreByInvoiceId(string invoiceId)
|
||||
@ -102,34 +96,30 @@ namespace BTCPayServer.Services.Stores
|
||||
|
||||
public async Task<bool> AddStoreUser(string storeId, string userId, string role)
|
||||
{
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
var userStore = new UserStore() { StoreDataId = storeId, ApplicationUserId = userId, Role = role };
|
||||
ctx.UserStore.Add(userStore);
|
||||
try
|
||||
{
|
||||
var userStore = new UserStore() { StoreDataId = storeId, ApplicationUserId = userId, Role = role };
|
||||
ctx.UserStore.Add(userStore);
|
||||
try
|
||||
{
|
||||
await ctx.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
catch (Microsoft.EntityFrameworkCore.DbUpdateException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
catch (Microsoft.EntityFrameworkCore.DbUpdateException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CleanUnreachableStores()
|
||||
{
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
if (!ctx.Database.SupportDropForeignKey())
|
||||
return;
|
||||
foreach (var store in await ctx.Stores.Where(s => !s.UserStores.Where(u => u.Role == StoreRoles.Owner).Any()).ToArrayAsync())
|
||||
{
|
||||
if (!ctx.Database.SupportDropForeignKey())
|
||||
return;
|
||||
foreach (var store in await ctx.Stores.Where(s => !s.UserStores.Where(u => u.Role == StoreRoles.Owner).Any()).ToArrayAsync())
|
||||
{
|
||||
ctx.Stores.Remove(store);
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
ctx.Stores.Remove(store);
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task RemoveStoreUser(string storeId, string userId)
|
||||
@ -147,18 +137,16 @@ namespace BTCPayServer.Services.Stores
|
||||
|
||||
private async Task DeleteStoreIfOrphan(string storeId)
|
||||
{
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
if (ctx.Database.SupportDropForeignKey())
|
||||
{
|
||||
if (ctx.Database.SupportDropForeignKey())
|
||||
if (!await ctx.UserStore.Where(u => u.StoreDataId == storeId && u.Role == StoreRoles.Owner).AnyAsync())
|
||||
{
|
||||
if (!await ctx.UserStore.Where(u => u.StoreDataId == storeId && u.Role == StoreRoles.Owner).AnyAsync())
|
||||
var store = await ctx.Stores.FindAsync(storeId);
|
||||
if (store != null)
|
||||
{
|
||||
var store = await ctx.Stores.FindAsync(storeId);
|
||||
if (store != null)
|
||||
{
|
||||
ctx.Stores.Remove(store);
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
ctx.Stores.Remove(store);
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -182,22 +170,20 @@ namespace BTCPayServer.Services.Stores
|
||||
if (string.IsNullOrEmpty(storeData.StoreName))
|
||||
throw new ArgumentException("name should not be empty", nameof(storeData.StoreName));
|
||||
ArgumentNullException.ThrowIfNull(ownerId);
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
storeData.Id = Encoders.Base58.EncodeData(RandomUtils.GetBytes(32));
|
||||
var userStore = new UserStore
|
||||
{
|
||||
storeData.Id = Encoders.Base58.EncodeData(RandomUtils.GetBytes(32));
|
||||
var userStore = new UserStore
|
||||
{
|
||||
StoreDataId = storeData.Id,
|
||||
ApplicationUserId = ownerId,
|
||||
Role = StoreRoles.Owner,
|
||||
};
|
||||
StoreDataId = storeData.Id,
|
||||
ApplicationUserId = ownerId,
|
||||
Role = StoreRoles.Owner,
|
||||
};
|
||||
|
||||
SetNewStoreHints(ref storeData);
|
||||
SetNewStoreHints(ref storeData);
|
||||
|
||||
ctx.Add(storeData);
|
||||
ctx.Add(userStore);
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
ctx.Add(storeData);
|
||||
ctx.Add(userStore);
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<StoreData> CreateStore(string ownerId, string name)
|
||||
@ -343,41 +329,35 @@ namespace BTCPayServer.Services.Stores
|
||||
|
||||
public async Task UpdateStore(StoreData store)
|
||||
{
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
{
|
||||
var existing = await ctx.FindAsync<StoreData>(store.Id);
|
||||
ctx.Entry(existing).CurrentValues.SetValues(store);
|
||||
await ctx.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
var existing = await ctx.FindAsync<StoreData>(store.Id);
|
||||
ctx.Entry(existing).CurrentValues.SetValues(store);
|
||||
await ctx.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteStore(string storeId)
|
||||
{
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
{
|
||||
if (!ctx.Database.SupportDropForeignKey())
|
||||
return false;
|
||||
var store = await ctx.Stores.FindAsync(storeId);
|
||||
if (store == null)
|
||||
return false;
|
||||
var webhooks = await ctx.StoreWebhooks
|
||||
.Where(o => o.StoreId == storeId)
|
||||
.Select(o => o.Webhook)
|
||||
.ToArrayAsync();
|
||||
foreach (var w in webhooks)
|
||||
ctx.Webhooks.Remove(w);
|
||||
ctx.Stores.Remove(store);
|
||||
await ctx.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
if (!ctx.Database.SupportDropForeignKey())
|
||||
return false;
|
||||
var store = await ctx.Stores.FindAsync(storeId);
|
||||
if (store == null)
|
||||
return false;
|
||||
var webhooks = await ctx.StoreWebhooks
|
||||
.Where(o => o.StoreId == storeId)
|
||||
.Select(o => o.Webhook)
|
||||
.ToArrayAsync();
|
||||
foreach (var w in webhooks)
|
||||
ctx.Webhooks.Remove(w);
|
||||
ctx.Stores.Remove(store);
|
||||
await ctx.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CanDeleteStores()
|
||||
{
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
{
|
||||
return ctx.Database.SupportDropForeignKey();
|
||||
}
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
return ctx.Database.SupportDropForeignKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,77 +19,69 @@ namespace BTCPayServer.Services
|
||||
public async Task SetWalletInfo(WalletId walletId, WalletBlobInfo blob)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(walletId);
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
var walletData = new WalletData() { Id = walletId.ToString() };
|
||||
walletData.SetBlobInfo(blob);
|
||||
var entity = await ctx.Wallets.AddAsync(walletData);
|
||||
entity.State = EntityState.Modified;
|
||||
try
|
||||
{
|
||||
var walletData = new WalletData() { Id = walletId.ToString() };
|
||||
walletData.SetBlobInfo(blob);
|
||||
var entity = await ctx.Wallets.AddAsync(walletData);
|
||||
entity.State = EntityState.Modified;
|
||||
try
|
||||
{
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateException) // Does not exists
|
||||
{
|
||||
entity.State = EntityState.Added;
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateException) // Does not exists
|
||||
{
|
||||
entity.State = EntityState.Added;
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, WalletTransactionInfo>> GetWalletTransactionsInfo(WalletId walletId, string[] transactionIds = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(walletId);
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
{
|
||||
return (await ctx.WalletTransactions
|
||||
.Where(w => w.WalletDataId == walletId.ToString())
|
||||
.Where(data => transactionIds == null || transactionIds.Contains(data.TransactionId))
|
||||
.Select(w => w)
|
||||
.ToArrayAsync())
|
||||
.ToDictionary(w => w.TransactionId, w => w.GetBlobInfo());
|
||||
}
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
return (await ctx.WalletTransactions
|
||||
.Where(w => w.WalletDataId == walletId.ToString())
|
||||
.Where(data => transactionIds == null || transactionIds.Contains(data.TransactionId))
|
||||
.Select(w => w)
|
||||
.ToArrayAsync())
|
||||
.ToDictionary(w => w.TransactionId, w => w.GetBlobInfo());
|
||||
}
|
||||
|
||||
public async Task<WalletBlobInfo> GetWalletInfo(WalletId walletId)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(walletId);
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
{
|
||||
var data = await ctx.Wallets
|
||||
.Where(w => w.Id == walletId.ToString())
|
||||
.Select(w => w)
|
||||
.FirstOrDefaultAsync();
|
||||
return data?.GetBlobInfo() ?? new WalletBlobInfo();
|
||||
}
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
var data = await ctx.Wallets
|
||||
.Where(w => w.Id == walletId.ToString())
|
||||
.Select(w => w)
|
||||
.FirstOrDefaultAsync();
|
||||
return data?.GetBlobInfo() ?? new WalletBlobInfo();
|
||||
}
|
||||
|
||||
public async Task SetWalletTransactionInfo(WalletId walletId, string transactionId, WalletTransactionInfo walletTransactionInfo)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(walletId);
|
||||
ArgumentNullException.ThrowIfNull(transactionId);
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
var walletData = new WalletTransactionData() { WalletDataId = walletId.ToString(), TransactionId = transactionId };
|
||||
walletData.SetBlobInfo(walletTransactionInfo);
|
||||
var entity = await ctx.WalletTransactions.AddAsync(walletData);
|
||||
entity.State = EntityState.Modified;
|
||||
try
|
||||
{
|
||||
var walletData = new WalletTransactionData() { WalletDataId = walletId.ToString(), TransactionId = transactionId };
|
||||
walletData.SetBlobInfo(walletTransactionInfo);
|
||||
var entity = await ctx.WalletTransactions.AddAsync(walletData);
|
||||
entity.State = EntityState.Modified;
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateException) // Does not exists
|
||||
{
|
||||
entity.State = EntityState.Added;
|
||||
try
|
||||
{
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateException) // Does not exists
|
||||
catch (DbUpdateException) // the Wallet does not exists in the DB
|
||||
{
|
||||
entity.State = EntityState.Added;
|
||||
try
|
||||
{
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateException) // the Wallet does not exists in the DB
|
||||
{
|
||||
await SetWalletInfo(walletId, new WalletBlobInfo());
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
await SetWalletInfo(walletId, new WalletBlobInfo());
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,35 +29,29 @@ namespace BTCPayServer.Storage.Services
|
||||
filesQuery = new FilesQuery();
|
||||
}
|
||||
|
||||
using (var context = _ApplicationDbContextFactory.CreateContext())
|
||||
{
|
||||
return await context.Files
|
||||
.Include(file => file.ApplicationUser)
|
||||
.Where(file =>
|
||||
(!filesQuery.Id.Any() || filesQuery.Id.Contains(file.Id)) &&
|
||||
(!filesQuery.UserIds.Any() || filesQuery.UserIds.Contains(file.ApplicationUserId)))
|
||||
.OrderByDescending(file => file.Timestamp)
|
||||
.ToListAsync();
|
||||
}
|
||||
using var context = _ApplicationDbContextFactory.CreateContext();
|
||||
return await context.Files
|
||||
.Include(file => file.ApplicationUser)
|
||||
.Where(file =>
|
||||
(!filesQuery.Id.Any() || filesQuery.Id.Contains(file.Id)) &&
|
||||
(!filesQuery.UserIds.Any() || filesQuery.UserIds.Contains(file.ApplicationUserId)))
|
||||
.OrderByDescending(file => file.Timestamp)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task RemoveFile(StoredFile file)
|
||||
{
|
||||
using (var context = _ApplicationDbContextFactory.CreateContext())
|
||||
{
|
||||
context.Attach(file);
|
||||
context.Files.Remove(file);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
using var context = _ApplicationDbContextFactory.CreateContext();
|
||||
context.Attach(file);
|
||||
context.Files.Remove(file);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task AddFile(StoredFile storedFile)
|
||||
{
|
||||
using (var context = _ApplicationDbContextFactory.CreateContext())
|
||||
{
|
||||
await context.AddAsync(storedFile);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
using var context = _ApplicationDbContextFactory.CreateContext();
|
||||
await context.AddAsync(storedFile);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public class FilesQuery
|
||||
|
@ -94,13 +94,9 @@ namespace BTCPayServer
|
||||
public async Task Send(string evt, CancellationToken cancellation = default)
|
||||
{
|
||||
var bytes = UTF8.GetBytes(evt);
|
||||
using (var cts = new CancellationTokenSource(5000))
|
||||
{
|
||||
using (var cts2 = CancellationTokenSource.CreateLinkedTokenSource(cancellation))
|
||||
{
|
||||
await Socket.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true, cts2.Token);
|
||||
}
|
||||
}
|
||||
using var cts = new CancellationTokenSource(5000);
|
||||
using var cts2 = CancellationTokenSource.CreateLinkedTokenSource(cancellation);
|
||||
await Socket.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true, cts2.Token);
|
||||
}
|
||||
|
||||
public async Task DisposeAsync(CancellationToken cancellation)
|
||||
|
Loading…
Reference in New Issue
Block a user