Warning if not using 'simple using'

This commit is contained in:
nicolas.dorier 2022-01-14 17:50:29 +09:00
parent c6a7e90c1a
commit 50d4b55f73
No known key found for this signature in database
GPG Key ID: 6618763EF09186FE
40 changed files with 5355 additions and 5732 deletions

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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,

View File

@ -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.
}

View File

@ -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());
}
}
}

View File

@ -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);
});
}
}
}

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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
{

View File

@ -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)

View File

@ -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 { }

View File

@ -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)
{

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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)
{

View File

@ -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 _);
}
}
}

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)
{
}
}
}

View File

@ -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)

View File

@ -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();

View File

@ -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);
}

View File

@ -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,

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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

View File

@ -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)