From 04037b3d2d9a9cd62b5bc9995ec4a7c93c0af839 Mon Sep 17 00:00:00 2001 From: Nisaba <34550856+Nisaba@users.noreply.github.com> Date: Wed, 21 Feb 2024 13:41:21 +0000 Subject: [PATCH] Crowdfund : Add Buyer information / Additional information(forms) like POS (#5659) * Crowfund : Add Buyer information / Additional information(forms) like POS * PR 5659 - changes * Cleanups * fix perk * Crowdfund form tests * Add Selenium test for Crowfund * Selenium update * update Selenium * selenium update * update selenium * Test fixes and view improvements * Cleanups * do not use hacky form element for form detection --------- Co-authored-by: nisaba Co-authored-by: Dennis Reimann Co-authored-by: Kukks --- .gitignore | 1 + BTCPayServer.Tests/CrowdfundTests.cs | 113 +++ BTCPayServer.Tests/SeleniumTests.cs | 99 ++- .../Controllers/UICrowdfundController.cs | 143 +++- .../Plugins/Crowdfund/CrowdfundPlugin.cs | 6 +- .../Models/UpdateCrowdfundViewModel.cs | 4 + .../Models/ViewCrowdfundViewModel.cs | 2 +- BTCPayServer/Services/Apps/AppHub.cs | 2 +- .../Services/Apps/CrowdfundSettings.cs | 3 + .../Crowdfund/Public/ViewCrowdfund.cshtml | 690 +++++++++--------- .../Shared/Crowdfund/UpdateCrowdfund.cshtml | 10 + .../Views/Shared/TemplateEditor.cshtml | 2 +- BTCPayServer/wwwroot/crowdfund/app.js | 488 +++++++------ 13 files changed, 944 insertions(+), 619 deletions(-) diff --git a/.gitignore b/.gitignore index 8fcf0213b..4f3499bf3 100644 --- a/.gitignore +++ b/.gitignore @@ -300,3 +300,4 @@ Plugins/packed BTCPayServer/wwwroot/swagger/v1/openapi.json BTCPayServer/appsettings.dev.json BTCPayServer.Tests/monero_wallet +/BTCPayServer.Tests/NewBlocks.bat diff --git a/BTCPayServer.Tests/CrowdfundTests.cs b/BTCPayServer.Tests/CrowdfundTests.cs index 87f66cb98..cfedd8b7c 100644 --- a/BTCPayServer.Tests/CrowdfundTests.cs +++ b/BTCPayServer.Tests/CrowdfundTests.cs @@ -1,15 +1,19 @@ using System; using System.Threading.Tasks; +using BTCPayServer.Abstractions.Form; using BTCPayServer.Client; using BTCPayServer.Client.Models; using BTCPayServer.Controllers; using BTCPayServer.Data; +using BTCPayServer.Forms; +using BTCPayServer.Forms.Models; using BTCPayServer.Models.AppViewModels; using BTCPayServer.Plugins.Crowdfund; using BTCPayServer.Plugins.Crowdfund.Controllers; using BTCPayServer.Plugins.Crowdfund.Models; using BTCPayServer.Services.Apps; using BTCPayServer.Services.Invoices; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NBitcoin; using NBitpayClient; @@ -303,5 +307,114 @@ namespace BTCPayServer.Tests Assert.Equal(0.7m, model.Info.CurrentPendingAmount); }); } + + [Fact(Timeout = LongRunningTestTimeout)] + [Trait("Integration", "Integration")] + public async Task CrowdfundWithFormNoPerk() + { + using var tester = CreateServerTester(); + await tester.StartAsync(); + var user = tester.NewAccount(); + await user.GrantAccessAsync(); + user.RegisterDerivationScheme("BTC"); + await user.SetNetworkFeeMode(NetworkFeeMode.Never); + + var frmService = tester.PayTester.GetService(); + var appService = tester.PayTester.GetService(); + var crowdfund = user.GetController(); + var apps = user.GetController(); + var appData = new AppData { StoreDataId = user.StoreId, Name = "test", AppType = CrowdfundAppType.AppType }; + await appService.UpdateOrCreateApp(appData); + var appList = Assert.IsType(Assert.IsType(apps.ListApps(user.StoreId).Result).Model); + var app = appList.Apps[0]; + apps.HttpContext.SetAppData(appData); + crowdfund.HttpContext.SetAppData(appData); + + var form = new Form + { + Fields = + [ + Field.Create("Enter your email", "item1", "test@toto.com", true, null, "email"), + Field.Create("Name", "item2", 2.ToString(), true, null), + Field.Create("Item3", "invoice_item3", 3.ToString(), true, null) + ] + }; + var frmData = new FormData + { + StoreId = user.StoreId, + Name = "frmTest", + Config = form.ToString() + }; + await frmService.AddOrUpdateForm(frmData); + + var lstForms = await frmService.GetForms(user.StoreId); + Assert.NotEmpty(lstForms); + + var crowdfundViewModel = await crowdfund.UpdateCrowdfund(app.Id).AssertViewModelAsync(); + crowdfundViewModel.FormId = lstForms[0].Id; + crowdfundViewModel.TargetCurrency = "BTC"; + crowdfundViewModel.Enabled = true; + Assert.IsType(crowdfund.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result); + + var vm2 = await crowdfund.CrowdfundForm(app.Id, (decimal?)0.01).AssertViewModelAsync(); + var res = await crowdfund.CrowdfundFormSubmit(app.Id, (decimal)0.01, "", vm2); + Assert.IsNotType(res); + Assert.IsNotType(res); + } + + [Fact(Timeout = LongRunningTestTimeout)] + [Trait("Integration", "Integration")] + public async Task CrowdfundWithFormAndPerk() + { + using var tester = CreateServerTester(); + await tester.StartAsync(); + var user = tester.NewAccount(); + await user.GrantAccessAsync(); + user.RegisterDerivationScheme("BTC"); + await user.SetNetworkFeeMode(NetworkFeeMode.Never); + + var frmService = tester.PayTester.GetService(); + var appService = tester.PayTester.GetService(); + var crowdfund = user.GetController(); + var apps = user.GetController(); + var appData = new AppData { StoreDataId = user.StoreId, Name = "test", AppType = CrowdfundAppType.AppType }; + await appService.UpdateOrCreateApp(appData); + var appList = Assert.IsType(Assert.IsType(apps.ListApps(user.StoreId).Result).Model); + var app = appList.Apps[0]; + apps.HttpContext.SetAppData(appData); + crowdfund.HttpContext.SetAppData(appData); + + var form = new Form + { + Fields = + [ + Field.Create("Enter your email", "item1", "test@toto.com", true, null, "email"), + Field.Create("Name", "item2", 2.ToString(), true, null), + Field.Create("Item3", "invoice_item3", 3.ToString(), true, null) + ] + }; + var frmData = new FormData + { + StoreId = user.StoreId, + Name = "frmTest", + Config = form.ToString() + }; + await frmService.AddOrUpdateForm(frmData); + + var lstForms = await frmService.GetForms(user.StoreId); + Assert.NotEmpty(lstForms); + + var crowdfundViewModel = await crowdfund.UpdateCrowdfund(app.Id).AssertViewModelAsync(); + crowdfundViewModel.FormId = lstForms[0].Id; + crowdfundViewModel.TargetCurrency = "BTC"; + crowdfundViewModel.Enabled = true; + crowdfundViewModel.PerksTemplate = "[{\"id\": \"xxx\",\"title\": \"Perk 1\",\"priceType\": \"Fixed\",\"price\": \"0.001\",\"image\": \"\",\"description\": \"\",\"categories\": [],\"disabled\": false}]"; + Assert.IsType(crowdfund.UpdateCrowdfund(app.Id, crowdfundViewModel, "save").Result); + + var vm2 = await crowdfund.CrowdfundForm(app.Id, (decimal?)0.01, "xxx").AssertViewModelAsync(); + var res = await crowdfund.CrowdfundFormSubmit(app.Id, (decimal)0.01, "xxx", vm2); + Assert.IsNotType(res); + Assert.IsNotType(res); + } } } diff --git a/BTCPayServer.Tests/SeleniumTests.cs b/BTCPayServer.Tests/SeleniumTests.cs index 8175aa20a..64f355181 100644 --- a/BTCPayServer.Tests/SeleniumTests.cs +++ b/BTCPayServer.Tests/SeleniumTests.cs @@ -97,7 +97,7 @@ namespace BTCPayServer.Tests s.Driver.FindElement(By.Id("ViewApp")).Click(); s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last()); - + s.Driver.FindElement(By.CssSelector("button[type='submit']")).Click(); Assert.Contains("Enter your email", s.Driver.PageSource); @@ -108,7 +108,7 @@ namespace BTCPayServer.Tests var invoiceId = s.Driver.Url[(s.Driver.Url.LastIndexOf("/", StringComparison.Ordinal) + 1)..]; s.Driver.Close(); s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First()); - + s.GoToInvoice(invoiceId); Assert.Contains("aa@aa.com", s.Driver.PageSource); @@ -123,10 +123,10 @@ namespace BTCPayServer.Tests s.Driver.FindElement(By.XPath("//a[starts-with(@id, 'Edit-')]")).Click(); var editUrl = s.Driver.Url; - + s.Driver.FindElement(By.Id("ViewPaymentRequest")).Click(); s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last()); - + s.Driver.FindElement(By.CssSelector("[data-test='form-button']")).Click(); Assert.Contains("Enter your email", s.Driver.PageSource); @@ -135,7 +135,7 @@ namespace BTCPayServer.Tests invoiceId = s.Driver.Url.Split('/').Last(); s.Driver.Close(); s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First()); - + s.Driver.Navigate().GoToUrl(editUrl); Assert.Contains("aa@aa.com", s.Driver.PageSource); var invoice = await s.Server.PayTester.GetService().GetInvoice(invoiceId); @@ -147,13 +147,13 @@ namespace BTCPayServer.Tests s.Driver.FindElement(By.Id("CreateForm")).Click(); s.Driver.FindElement(By.Name("Name")).SendKeys("Custom Form 1"); s.Driver.FindElement(By.Id("ApplyEmailTemplate")).Click(); - + s.Driver.FindElement(By.Id("CodeTabButton")).Click(); s.Driver.WaitForElement(By.Id("CodeTabPane")); - + var config = s.Driver.FindElement(By.Name("FormConfig")).GetAttribute("value"); Assert.Contains("buyerEmail", config); - + s.Driver.FindElement(By.Name("FormConfig")).Clear(); s.Driver.FindElement(By.Name("FormConfig")) .SendKeys(config.Replace("Enter your email", "CustomFormInputTest")); @@ -179,10 +179,10 @@ namespace BTCPayServer.Tests s.Driver.FindElement(By.Id("CreateForm")).Click(); s.Driver.FindElement(By.Name("Name")).SendKeys("Custom Form 2"); s.Driver.FindElement(By.Id("ApplyEmailTemplate")).Click(); - + s.Driver.FindElement(By.Id("CodeTabButton")).Click(); s.Driver.WaitForElement(By.Id("CodeTabPane")); - + s.Driver.SetCheckbox(By.Name("Public"), true); s.Driver.FindElement(By.Name("FormConfig")).Clear(); @@ -1292,34 +1292,33 @@ namespace BTCPayServer.Tests s.Driver.ExecuteJavaScript("document.getElementById('EndDate').value = ''"); s.Driver.FindElement(By.Id("SaveSettings")).Click(); Assert.Contains("App updated", s.FindAlertMessage().Text); - var appId = s.Driver.Url.Split('/')[4]; + var editUrl = s.Driver.Url; + var appId = editUrl.Split('/')[4]; - // CHeck public page + // Check public page s.Driver.FindElement(By.Id("ViewApp")).Click(); var windows = s.Driver.WindowHandles; Assert.Equal(2, windows.Count); s.Driver.SwitchTo().Window(windows[1]); var cfUrl = s.Driver.Url; - Assert.Equal("Currently active!", - s.Driver.FindElement(By.CssSelector("[data-test='time-state']")).Text); + Assert.Equal("Currently active!", s.Driver.FindElement(By.CssSelector("[data-test='time-state']")).Text); // Contribute s.Driver.FindElement(By.Id("crowdfund-body-header-cta")).Click(); - s.Driver.WaitUntilAvailable(By.Name("btcpay")); - - var frameElement = s.Driver.FindElement(By.Name("btcpay")); - Assert.True(frameElement.Displayed); - var iframe = s.Driver.SwitchTo().Frame(frameElement); - iframe.WaitUntilAvailable(By.Id("Checkout-v2")); - - IWebElement closebutton = null; TestUtils.Eventually(() => { - closebutton = iframe.FindElement(By.Id("close")); - Assert.True(closebutton.Displayed); + s.Driver.WaitUntilAvailable(By.Name("btcpay")); + + var frameElement = s.Driver.FindElement(By.Name("btcpay")); + Assert.True(frameElement.Displayed); + var iframe = s.Driver.SwitchTo().Frame(frameElement); + iframe.WaitUntilAvailable(By.Id("Checkout-v2")); + + var closeButton = iframe.FindElement(By.Id("close")); + Assert.True(closeButton.Displayed); + closeButton.Click(); }); - closebutton.Click(); s.Driver.AssertElementNotFound(By.Name("btcpay")); // Back to admin view @@ -1343,6 +1342,56 @@ namespace BTCPayServer.Tests s.Driver.FindElement(By.Id($"App-{appId}")).Click(); s.Driver.FindElement(By.Id("btn-archive-toggle")).Click(); Assert.Contains("The app has been unarchived and will appear in the apps list by default again.", s.FindAlertMessage().Text); + + // Crowdfund with form + s.GoToUrl(editUrl); + new SelectElement(s.Driver.FindElement(By.Id("FormId"))).SelectByValue("Email"); + s.Driver.FindElement(By.Id("SaveSettings")).Click(); + Assert.Contains("App updated", s.FindAlertMessage().Text); + + s.Driver.FindElement(By.Id("ViewApp")).Click(); + s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last()); + s.Driver.FindElement(By.Id("crowdfund-body-header-cta")).Click(); + + Assert.Contains("Enter your email", s.Driver.PageSource); + s.Driver.FindElement(By.Name("buyerEmail")).SendKeys("test-without-perk@crowdfund.com"); + s.Driver.FindElement(By.CssSelector("input[type='submit']")).Click(); + + s.PayInvoice(true, 10); + var invoiceId = s.Driver.Url[(s.Driver.Url.LastIndexOf("/", StringComparison.Ordinal) + 1)..]; + s.Driver.Close(); + s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First()); + + s.GoToInvoice(invoiceId); + Assert.Contains("test-without-perk@crowdfund.com", s.Driver.PageSource); + + // Crowdfund with perk + s.GoToUrl(editUrl); + s.Driver.ScrollTo(By.Id("btAddItem")); + s.Driver.FindElement(By.Id("btAddItem")).Click(); + s.Driver.FindElement(By.Id("EditorTitle")).SendKeys("Perk 1"); + s.Driver.FindElement(By.Id("EditorId")).SendKeys("Perk-1"); + s.Driver.FindElement(By.Id("EditorAmount")).SendKeys("20"); + s.Driver.FindElement(By.Id("ApplyItemChanges")).Click(); + s.Driver.FindElement(By.Id("SaveSettings")).Click(); + Assert.Contains("App updated", s.FindAlertMessage().Text); + + s.Driver.FindElement(By.Id("ViewApp")).Click(); + s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last()); + s.Driver.WaitForElement(By.Id("Perk-1")).Click(); + s.Driver.WaitForElement(By.CssSelector("#Perk-1 button[type=\"submit\"]")).Submit(); + + Assert.Contains("Enter your email", s.Driver.PageSource); + s.Driver.FindElement(By.Name("buyerEmail")).SendKeys("test-with-perk@crowdfund.com"); + s.Driver.FindElement(By.CssSelector("input[type='submit']")).Click(); + + s.PayInvoice(true, 20); + invoiceId = s.Driver.Url[(s.Driver.Url.LastIndexOf("/", StringComparison.Ordinal) + 1)..]; + s.Driver.Close(); + s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First()); + + s.GoToInvoice(invoiceId); + Assert.Contains("test-with-perk@crowdfund.com", s.Driver.PageSource); } [Fact(Timeout = TestTimeout)] diff --git a/BTCPayServer/Plugins/Crowdfund/Controllers/UICrowdfundController.cs b/BTCPayServer/Plugins/Crowdfund/Controllers/UICrowdfundController.cs index 0889979f2..8002cf4b0 100644 --- a/BTCPayServer/Plugins/Crowdfund/Controllers/UICrowdfundController.cs +++ b/BTCPayServer/Plugins/Crowdfund/Controllers/UICrowdfundController.cs @@ -5,11 +5,15 @@ using System.Threading; using System.Threading.Tasks; using BTCPayServer.Abstractions.Constants; using BTCPayServer.Abstractions.Extensions; +using BTCPayServer.Abstractions.Form; using BTCPayServer.Client; using BTCPayServer.Client.Models; using BTCPayServer.Controllers; using BTCPayServer.Data; using BTCPayServer.Filters; +using BTCPayServer.Forms; +using BTCPayServer.Forms.Models; +using BTCPayServer.Models; using BTCPayServer.Plugins.Crowdfund.Models; using BTCPayServer.Plugins.PointOfSale.Models; using BTCPayServer.Services.Apps; @@ -20,7 +24,10 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; +using NBitcoin; +using NBitcoin.DataEncoders; using NBitpayClient; +using Newtonsoft.Json.Linq; using NicolasDorier.RateLimits; using CrowdfundResetEvery = BTCPayServer.Services.Apps.CrowdfundResetEvery; @@ -37,6 +44,7 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers StoreRepository storeRepository, UIInvoiceController invoiceController, UserManager userManager, + FormDataService formDataService, CrowdfundAppType app) { _currencies = currencies; @@ -46,6 +54,7 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers _storeRepository = storeRepository; _eventAggregator = eventAggregator; _invoiceController = invoiceController; + FormDataService = formDataService; } private readonly EventAggregator _eventAggregator; @@ -55,6 +64,7 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers private readonly UIInvoiceController _invoiceController; private readonly UserManager _userManager; private readonly CrowdfundAppType _app; + public FormDataService FormDataService { get; } [HttpGet("/")] [HttpGet("/apps/{appId}/crowdfund")] @@ -95,7 +105,7 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers [EnableCors(CorsPolicies.All)] [DomainMappingConstraint(CrowdfundAppType.AppType)] [RateLimitsFilter(ZoneLimits.PublicInvoices, Scope = RateLimitsScope.RemoteAddress)] - public async Task ContributeToCrowdfund(string appId, ContributeToCrowdfund request, CancellationToken cancellationToken) + public async Task ContributeToCrowdfund(string appId, ContributeToCrowdfund request, string formResponse = null, CancellationToken cancellationToken = default) { var app = await _appService.GetApp(appId, CrowdfundAppType.AppType, true); @@ -121,6 +131,24 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers return NotFound("Crowdfund is not currently active"); } + JObject formResponseJObject = null; + + if (settings.FormId is not null) + { + var formData = await FormDataService.GetForm(settings.FormId); + if (formData is not null) + { + formResponseJObject = TryParseJObject(formResponse) ?? new JObject(); + var form = Form.Parse(formData.Config); + FormDataService.SetValues(form, formResponseJObject); + if (!FormDataService.Validate(form, ModelState)) + { + // someone tried to bypass validation + return RedirectToAction(nameof(ViewCrowdfund), new { appId }); + } + } + } + var store = await _appService.GetStore(app); var title = settings.Title; decimal? price = request.Amount; @@ -203,6 +231,11 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers entity.FullNotifications = true; entity.ExtendedNotifications = true; entity.Metadata.OrderUrl = appUrl; + if (formResponseJObject is null) + return; + var meta = entity.Metadata.ToJObject(); + meta.Merge(formResponseJObject); + entity.Metadata = InvoiceMetadata.FromJObject(meta); }); if (request.RedirectToCheckout) @@ -219,6 +252,108 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers } } + private JObject TryParseJObject(string posData) + { + try + { + return JObject.Parse(posData); + } + catch + { + } + return null; + } + + [HttpGet("/apps/{appId}/crowdfund/form")] + [IgnoreAntiforgeryToken] + [XFrameOptions(XFrameOptionsAttribute.XFrameOptions.Unset)] + public async Task CrowdfundForm(string appId, decimal? amount=0, string choiceKey="") + { + var app = await _appService.GetApp(appId, CrowdfundAppType.AppType); + if (app == null) + return NotFound(); + + var settings = app.GetSettings(); + var formData = await FormDataService.GetForm(settings.FormId); + if (formData is null) + { + return RedirectToAction(nameof(ViewCrowdfund), new { appId }); + } + + var prefix = Encoders.Base58.EncodeData(RandomUtils.GetBytes(16)) + "_"; + var formParameters = new MultiValueDictionary(); + var controller = nameof(UICrowdfundController).TrimEnd("Controller", StringComparison.InvariantCulture); + var store = await _appService.GetStore(app); + var storeBlob = store.GetStoreBlob(); + var form = Form.Parse(formData.Config); + form.ApplyValuesFromForm(Request.Query); + var vm = new FormViewModel + { + StoreName = store.StoreName, + StoreBranding = new StoreBrandingViewModel(storeBlob), + FormName = formData.Name, + Form = form, + AspController = controller, + AspAction = nameof(CrowdfundFormSubmit), + RouteParameters = new Dictionary { { "appId", appId }, { "amount", amount.ToString() }, { "choiceKey", choiceKey } }, + FormParameters = formParameters, + FormParameterPrefix = prefix + }; + + return View("Views/UIForms/View", vm); + } + + [HttpPost("/apps/{appId}/crowdfund/form/submit")] + [IgnoreAntiforgeryToken] + [XFrameOptions(XFrameOptionsAttribute.XFrameOptions.Unset)] + public async Task CrowdfundFormSubmit(string appId, decimal amount, string choiceKey, FormViewModel viewModel) + { + var app = await _appService.GetApp(appId, CrowdfundAppType.AppType); + if (app == null) + return NotFound(); + + var settings = app.GetSettings(); + var formData = await FormDataService.GetForm(settings.FormId); + if (formData is null) + { + return RedirectToAction(nameof(ViewCrowdfund)); + } + var form = Form.Parse(formData.Config); + var formFieldNames = form.GetAllFields().Select(tuple => tuple.FullName).Distinct().ToArray(); + + // For unit testing + if (Request.Headers.Count == 1) + { + Request.Headers.Add("Content-Type", "application/x-www-form-urlencoded"); + } + var formParameters = Request.Form + .Where(pair => pair.Key.StartsWith(viewModel.FormParameterPrefix)) + .ToDictionary(pair => pair.Key.Replace(viewModel.FormParameterPrefix, string.Empty), pair => pair.Value) + .ToMultiValueDictionary(p => p.Key, p => p.Value.ToString()); + + form.ApplyValuesFromForm(Request.Form.Where(pair => formFieldNames.Contains(pair.Key))); + + if (FormDataService.Validate(form, ModelState)) + { + var appInfo = await GetAppInfo(appId); + var req = new ContributeToCrowdfund() + { + RedirectToCheckout = true, + Amount = amount == 0 ? null : amount, + ChoiceKey = choiceKey, + ViewCrowdfundViewModel = appInfo + }; + + return ContributeToCrowdfund(appId, req, formResponse: FormDataService.GetValues(form).ToString()).Result; + } + + viewModel.FormName = formData.Name; + viewModel.Form = form; + + viewModel.FormParameters = formParameters; + return View("Views/UIForms/View", viewModel); + } + [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)] [HttpGet("{appId}/settings/crowdfund")] public async Task UpdateCrowdfund(string appId) @@ -264,7 +399,8 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers DisplayPerksValue = settings.DisplayPerksValue, SortPerksByPopularity = settings.SortPerksByPopularity, Sounds = string.Join(Environment.NewLine, settings.Sounds), - AnimationColors = string.Join(Environment.NewLine, settings.AnimationColors) + AnimationColors = string.Join(Environment.NewLine, settings.AnimationColors), + FormId = settings.FormId }; return View("Crowdfund/UpdateCrowdfund", vm); } @@ -373,7 +509,8 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers DisplayPerksRanking = vm.DisplayPerksRanking, SortPerksByPopularity = vm.SortPerksByPopularity, Sounds = parsedSounds, - AnimationColors = parsedAnimationColors + AnimationColors = parsedAnimationColors, + FormId = vm.FormId }; app.TagAllInvoices = vm.UseAllStoreInvoices; diff --git a/BTCPayServer/Plugins/Crowdfund/CrowdfundPlugin.cs b/BTCPayServer/Plugins/Crowdfund/CrowdfundPlugin.cs index ec8b57b24..ec5f3afdd 100644 --- a/BTCPayServer/Plugins/Crowdfund/CrowdfundPlugin.cs +++ b/BTCPayServer/Plugins/Crowdfund/CrowdfundPlugin.cs @@ -11,7 +11,6 @@ using BTCPayServer.Data; using BTCPayServer.Models; using BTCPayServer.Plugins.Crowdfund.Controllers; using BTCPayServer.Plugins.Crowdfund.Models; -using BTCPayServer.Plugins.PointOfSale; using BTCPayServer.Services; using BTCPayServer.Services.Apps; using BTCPayServer.Services.Invoices; @@ -183,6 +182,10 @@ namespace BTCPayServer.Plugins.Crowdfund CustomCSSLink = settings.CustomCSSLink, EmbeddedCSS = settings.EmbeddedCSS }; + var formUrl = settings.FormId != null + ? _linkGenerator.GetPathByAction(nameof(UICrowdfundController.CrowdfundForm), "UICrowdfund", + new { appId = appData.Id }, _options.Value.RootPath) + : null; return new ViewCrowdfundViewModel { Title = settings.Title, @@ -210,6 +213,7 @@ namespace BTCPayServer.Plugins.Crowdfund PerkCount = perkCount, PerkValue = perkValue, NeverReset = settings.ResetEvery == CrowdfundResetEvery.Never, + FormUrl = formUrl, Sounds = settings.Sounds, AnimationColors = settings.AnimationColors, CurrencyData = _currencyNameTable.GetCurrencyData(settings.TargetCurrency, true), diff --git a/BTCPayServer/Plugins/Crowdfund/Models/UpdateCrowdfundViewModel.cs b/BTCPayServer/Plugins/Crowdfund/Models/UpdateCrowdfundViewModel.cs index 66d4d80e8..db2a2d7a7 100644 --- a/BTCPayServer/Plugins/Crowdfund/Models/UpdateCrowdfundViewModel.cs +++ b/BTCPayServer/Plugins/Crowdfund/Models/UpdateCrowdfundViewModel.cs @@ -117,6 +117,10 @@ namespace BTCPayServer.Plugins.Crowdfund.Models // NOTE: Improve validation if needed public bool ModelWithMinimumData => Description != null && Title != null && TargetCurrency != null; + + [Display(Name = "Request contributor data on checkout")] + public string FormId { get; set; } + public bool Archived { get; set; } } } diff --git a/BTCPayServer/Plugins/Crowdfund/Models/ViewCrowdfundViewModel.cs b/BTCPayServer/Plugins/Crowdfund/Models/ViewCrowdfundViewModel.cs index 58bf15b74..d63b4f457 100644 --- a/BTCPayServer/Plugins/Crowdfund/Models/ViewCrowdfundViewModel.cs +++ b/BTCPayServer/Plugins/Crowdfund/Models/ViewCrowdfundViewModel.cs @@ -36,7 +36,7 @@ namespace BTCPayServer.Plugins.Crowdfund.Models public string[] Sounds { get; set; } public int ResetEveryAmount { get; set; } public bool NeverReset { get; set; } - + public string FormUrl { get; set; } public Dictionary PerkCount { get; set; } public CurrencyData CurrencyData { get; set; } diff --git a/BTCPayServer/Services/Apps/AppHub.cs b/BTCPayServer/Services/Apps/AppHub.cs index 28d65fadd..aa5b4b409 100644 --- a/BTCPayServer/Services/Apps/AppHub.cs +++ b/BTCPayServer/Services/Apps/AppHub.cs @@ -46,7 +46,7 @@ namespace BTCPayServer.Services.Apps { var result = - await _crowdfundController.ContributeToCrowdfund(Context.Items["app"].ToString(), model, Context.ConnectionAborted); + await _crowdfundController.ContributeToCrowdfund(Context.Items["app"].ToString(), model, cancellationToken: Context.ConnectionAborted); switch (result) { case OkObjectResult okObjectResult: diff --git a/BTCPayServer/Services/Apps/CrowdfundSettings.cs b/BTCPayServer/Services/Apps/CrowdfundSettings.cs index 2b303c0ba..e70bfe5c7 100644 --- a/BTCPayServer/Services/Apps/CrowdfundSettings.cs +++ b/BTCPayServer/Services/Apps/CrowdfundSettings.cs @@ -45,6 +45,9 @@ namespace BTCPayServer.Services.Apps public bool DisplayPerksRanking { get; set; } public bool DisplayPerksValue { get; set; } public bool SortPerksByPopularity { get; set; } + public string FormId { get; set; } = null; + + public string[] AnimationColors { get; set; } = { "#FF6138", "#FFBE53", "#2980B9", "#282741" diff --git a/BTCPayServer/Views/Shared/Crowdfund/Public/ViewCrowdfund.cshtml b/BTCPayServer/Views/Shared/Crowdfund/Public/ViewCrowdfund.cshtml index 2fa2eaf81..d6ecc9b55 100644 --- a/BTCPayServer/Views/Shared/Crowdfund/Public/ViewCrowdfund.cshtml +++ b/BTCPayServer/Views/Shared/Crowdfund/Public/ViewCrowdfund.cshtml @@ -3,22 +3,22 @@ @inject BTCPayServer.Services.BTCPayServerEnvironment Env @inject BTCPayServer.Security.ContentSecurityPolicies Csp @{ - ViewData["Title"] = Model.Title; + ViewData["Title"] = Model.Title; ViewData["StoreBranding"] = Model.StoreBranding; - Layout = null; - Csp.UnsafeEval(); - if (!string.IsNullOrEmpty(Model.DisqusShortname)) - { - Csp.Add("script-src", $"https://{Model.DisqusShortname}.disqus.com"); - Csp.Add("script-src", "https://c.disquscdn.com"); - } + Layout = null; + Csp.UnsafeEval(); + if (!string.IsNullOrEmpty(Model.DisqusShortname)) + { + Csp.Add("script-src", $"https://{Model.DisqusShortname}.disqus.com"); + Csp.Add("script-src", "https://c.disquscdn.com"); + } } - - - + + +