start contrib perks

This commit is contained in:
Kukks
2018-12-29 11:52:07 +01:00
parent 6fced3fab2
commit 6eb36abe2e
9 changed files with 156 additions and 20 deletions

View File

@@ -31,6 +31,7 @@ namespace BTCPayServer.Controllers
public string NotificationUrl { get; set; } public string NotificationUrl { get; set; }
public string Tagline { get; set; } public string Tagline { get; set; }
public string EmbeddedCSS { get; set; } public string EmbeddedCSS { get; set; }
public string PerksTemplate { get; set; }
} }
@@ -57,16 +58,31 @@ namespace BTCPayServer.Controllers
CustomCSSLink = settings.CustomCSSLink, CustomCSSLink = settings.CustomCSSLink,
NotificationUrl = settings.NotificationUrl, NotificationUrl = settings.NotificationUrl,
Tagline = settings.Tagline, Tagline = settings.Tagline,
PerksTemplate = settings.PerksTemplate
}; };
return View(vm); return View(vm);
} }
[HttpPost] [HttpPost]
[Route("{appId}/settings/crowdfund")] [Route("{appId}/settings/crowdfund")]
public async Task<IActionResult> UpdatePointOfSale(string appId, UpdateCrowdfundViewModel vm) public async Task<IActionResult> UpdateCrowdfund(string appId, UpdateCrowdfundViewModel vm)
{ {
if (_AppsHelper.GetCurrencyData(vm.TargetCurrency, false) == null) if (_AppsHelper.GetCurrencyData(vm.TargetCurrency, false) == null)
ModelState.AddModelError(nameof(vm.TargetCurrency), "Invalid currency"); ModelState.AddModelError(nameof(vm.TargetCurrency), "Invalid currency");
try
{
_AppsHelper.Parse(vm.PerksTemplate, vm.TargetCurrency);
}
catch
{
ModelState.AddModelError(nameof(vm.PerksTemplate), "Invalid template");
}
if (!ModelState.IsValid)
{
return View(vm);
}
var app = await GetOwnedApp(appId, AppType.Crowdfund); var app = await GetOwnedApp(appId, AppType.Crowdfund);
if (app == null) if (app == null)
return NotFound(); return NotFound();
@@ -84,7 +100,8 @@ namespace BTCPayServer.Controllers
MainImageUrl = vm.MainImageUrl, MainImageUrl = vm.MainImageUrl,
EmbeddedCSS = vm.EmbeddedCSS, EmbeddedCSS = vm.EmbeddedCSS,
NotificationUrl = vm.NotificationUrl, NotificationUrl = vm.NotificationUrl,
Tagline = vm.Tagline Tagline = vm.Tagline,
PerksTemplate = vm.PerksTemplate
}); });
await UpdateAppSettings(app); await UpdateAppSettings(app);
_EventAggregator.Publish(new CrowdfundAppUpdated() _EventAggregator.Publish(new CrowdfundAppUpdated()

View File

@@ -120,9 +120,17 @@ namespace BTCPayServer.Controllers
} }
StatusMessage = "App successfully created"; StatusMessage = "App successfully created";
CreatedAppId = id; CreatedAppId = id;
if (appType == AppType.PointOfSale)
return RedirectToAction(nameof(UpdatePointOfSale), new { appId = id }); switch (appType)
return RedirectToAction(nameof(ListApps)); {
case AppType.PointOfSale:
return RedirectToAction(nameof(UpdatePointOfSale), new { appId = id });
case AppType.Crowdfund:
return RedirectToAction(nameof(UpdateCrowdfund), new { appId = id });
default:
return RedirectToAction(nameof(ListApps));
}
} }
[HttpGet] [HttpGet]

View File

@@ -113,18 +113,40 @@ namespace BTCPayServer.Controllers
return NotFound(); return NotFound();
var settings = app.GetSettings<CrowdfundSettings>(); var settings = app.GetSettings<CrowdfundSettings>();
var store = await _AppsHelper.GetStore(app); var store = await _AppsHelper.GetStore(app);
string title = null;
var price = 0.0m;
if (!string.IsNullOrEmpty(request.ChoiceKey))
{
var choices = _AppsHelper.Parse(settings.PerksTemplate, settings.TargetCurrency);
var choice = choices.FirstOrDefault(c => c.Id == request.ChoiceKey);
if (choice == null)
return NotFound();
title = choice.Title;
price = choice.Price.Value;
if (request.Amount > price)
price = request.Amount;
}
else
{
price = request.Amount;
title = settings.Title;
}
store.AdditionalClaims.Add(new Claim(Policies.CanCreateInvoice.Key, store.Id)); store.AdditionalClaims.Add(new Claim(Policies.CanCreateInvoice.Key, store.Id));
var invoice = await _InvoiceController.CreateInvoiceCore(new Invoice() var invoice = await _InvoiceController.CreateInvoiceCore(new Invoice()
{ {
OrderId = $"{CrowdfundHubStreamer.CrowdfundInvoiceOrderIdPrefix}{appId}", OrderId = $"{CrowdfundHubStreamer.CrowdfundInvoiceOrderIdPrefix}{appId}",
Currency = settings.TargetCurrency, Currency = settings.TargetCurrency,
ItemCode = request.ChoiceKey ?? string.Empty,
ItemDesc = title,
BuyerEmail = request.Email, BuyerEmail = request.Email,
Price = request.Amount, Price = price,
NotificationURL = settings.NotificationUrl, NotificationURL = settings.NotificationUrl,
FullNotifications = true, FullNotifications = true,
ExtendedNotifications = true, ExtendedNotifications = true,
RedirectURL = HttpContext.Request.GetAbsoluteRoot()+ "/apps/{appId}/crowdfund", RedirectURL = request.RedirectUrl,
}, store, HttpContext.Request.GetAbsoluteRoot()); }, store, HttpContext.Request.GetAbsoluteRoot());
if (request.RedirectToCheckout) if (request.RedirectToCheckout)
@@ -195,7 +217,7 @@ namespace BTCPayServer.Controllers
OrderId = orderId, OrderId = orderId,
NotificationURL = notificationUrl, NotificationURL = notificationUrl,
RedirectURL = redirectUrl, RedirectURL = redirectUrl,
FullNotifications = true FullNotifications = true,
}, store, HttpContext.Request.GetAbsoluteRoot()); }, store, HttpContext.Request.GetAbsoluteRoot());
return RedirectToAction(nameof(InvoiceController.Checkout), "Invoice", new { invoiceId = invoice.Data.Id }); return RedirectToAction(nameof(InvoiceController.Checkout), "Invoice", new { invoiceId = invoice.Data.Id });
} }

View File

@@ -96,8 +96,7 @@ namespace BTCPayServer.Hubs
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1); entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1);
var app = await _AppsHelper.GetApp(appId, AppType.Crowdfund, true); var app = await _AppsHelper.GetApp(appId, AppType.Crowdfund, true);
var result = await GetInfo(app, _InvoiceRepository, _RateFetcher, var result = await GetInfo(app);
_BtcPayNetworkProvider);
entry.SetValue(result); entry.SetValue(result);
return result; return result;
}); });
@@ -191,26 +190,27 @@ namespace BTCPayServer.Hubs
} }
private static async Task<ViewCrowdfundViewModel> GetInfo(AppData appData, InvoiceRepository invoiceRepository, private async Task<ViewCrowdfundViewModel> GetInfo(AppData appData, string statusMessage= null)
RateFetcher rateFetcher, BTCPayNetworkProvider btcPayNetworkProvider, string statusMessage= null)
{ {
var settings = appData.GetSettings<AppsController.CrowdfundSettings>(); var settings = appData.GetSettings<AppsController.CrowdfundSettings>();
var invoices = await GetInvoicesForApp(appData, invoiceRepository); var invoices = await GetInvoicesForApp(appData, _InvoiceRepository);
var rateRules = appData.StoreData.GetStoreBlob().GetRateRules(btcPayNetworkProvider); var rateRules = appData.StoreData.GetStoreBlob().GetRateRules(_BtcPayNetworkProvider);
var currentAmount = await GetCurrentContributionAmount( var currentAmount = await GetCurrentContributionAmount(
invoices.Where(entity => entity.Status == InvoiceStatus.Complete).ToArray(), invoices.Where(entity => entity.Status == InvoiceStatus.Complete).ToArray(),
settings.TargetCurrency, rateFetcher, rateRules); settings.TargetCurrency, _RateFetcher, rateRules);
var currentPendingAmount = await GetCurrentContributionAmount( var currentPendingAmount = await GetCurrentContributionAmount(
invoices.Where(entity => entity.Status != InvoiceStatus.Complete).ToArray(), invoices.Where(entity => entity.Status != InvoiceStatus.Complete).ToArray(),
settings.TargetCurrency, rateFetcher, rateRules); settings.TargetCurrency, _RateFetcher, rateRules);
var active = (settings.StartDate == null || DateTime.Now >= settings.StartDate) && var active = (settings.StartDate == null || DateTime.Now >= settings.StartDate) &&
(settings.EndDate == null || DateTime.Now <= settings.EndDate) && (settings.EndDate == null || DateTime.Now <= settings.EndDate) &&
(!settings.EnforceTargetAmount || settings.TargetAmount > currentAmount); (!settings.EnforceTargetAmount || settings.TargetAmount > currentAmount);
return new ViewCrowdfundViewModel() return new ViewCrowdfundViewModel()
{ {
Title = settings.Title, Title = settings.Title,
@@ -227,6 +227,7 @@ namespace BTCPayServer.Hubs
TargetCurrency = settings.TargetCurrency, TargetCurrency = settings.TargetCurrency,
EnforceTargetAmount = settings.EnforceTargetAmount, EnforceTargetAmount = settings.EnforceTargetAmount,
StatusMessage = statusMessage, StatusMessage = statusMessage,
Perks = _AppsHelper.Parse(settings.PerksTemplate, settings.TargetCurrency),
Info = new ViewCrowdfundViewModel.CrowdfundInfo() Info = new ViewCrowdfundViewModel.CrowdfundInfo()
{ {
TotalContributors = invoices.Length, TotalContributors = invoices.Length,

View File

@@ -36,6 +36,9 @@ namespace BTCPayServer.Models.AppViewModels
[Display(Name = "Do not allow additional contributions after target has been reached")] [Display(Name = "Do not allow additional contributions after target has been reached")]
public bool EnforceTargetAmount { get; set; } public bool EnforceTargetAmount { get; set; }
[Display(Name = "Contribution Perks Template")]
public string PerksTemplate { get; set; }
[MaxLength(500)] [MaxLength(500)]
[Display(Name = "Custom bootstrap CSS file")] [Display(Name = "Custom bootstrap CSS file")]
public string CustomCSSLink { get; set; } public string CustomCSSLink { get; set; }

View File

@@ -22,6 +22,7 @@ namespace BTCPayServer.Models.AppViewModels
public CrowdfundInfo Info { get; set; } public CrowdfundInfo Info { get; set; }
public string Tagline { get; set; } public string Tagline { get; set; }
public ViewPointOfSaleViewModel.Item[] Perks { get; set; }
public class CrowdfundInfo public class CrowdfundInfo
@@ -46,6 +47,8 @@ namespace BTCPayServer.Models.AppViewModels
public ViewCrowdfundViewModel ViewCrowdfundViewModel { get; set; } public ViewCrowdfundViewModel ViewCrowdfundViewModel { get; set; }
[Required] public decimal Amount { get; set; } [Required] public decimal Amount { get; set; }
public string Email { get; set; } public string Email { get; set; }
public bool RedirectToCheckout { get; set; } public string ChoiceKey { get; set; }
public bool RedirectToCheckout { get; set; }
public string RedirectUrl { get; set; }
} }
} }

View File

@@ -3,6 +3,24 @@
ViewData["Title"] = "Update Crowdfund"; ViewData["Title"] = "Update Crowdfund";
} }
<section> <section>
<div class="modal" id="product-modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Contribution Perks Management</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="js-product-save btn btn-primary">Save Changes</button>
</div>
</div>
</div>
</div>
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-lg-12 text-center"> <div class="col-lg-12 text-center">
@@ -61,6 +79,19 @@
<input asp-for="EndDate" class="form-control" /> <input asp-for="EndDate" class="form-control" />
<span asp-validation-for="EndDate" class="text-danger"></span> <span asp-validation-for="EndDate" class="text-danger"></span>
</div> </div>
<div class="form-group">
<label class="control-label">Contribution Perks</label>
<div class="mb-3">
<a class="js-product-add btn btn-secondary" href="#" data-toggle="modal" data-target="#product-modal"><i class="fa fa-plus fa-fw"></i> Add Product</a>
</div>
<div class="js-products bg-light row p-3">
</div>
</div>
<div class="form-group">
<label asp-for="PerksTemplate" class="control-label"></label>*
<textarea asp-for="PerksTemplate" rows="10" cols="40" class="js-product-template form-control"></textarea>
<span asp-validation-for="PerksTemplate" class="text-danger"></span>
</div>
<div class="form-group"> <div class="form-group">
<label asp-for="CustomCSSLink" class="control-label"></label> <label asp-for="CustomCSSLink" class="control-label"></label>
<a href="https://docs.btcpayserver.org/development/theme#bootstrap-themes" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a> <a href="https://docs.btcpayserver.org/development/theme#bootstrap-themes" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
@@ -104,5 +135,56 @@
<link rel="stylesheet" href="~/vendor/highlightjs/default.min.css"> <link rel="stylesheet" href="~/vendor/highlightjs/default.min.css">
<script src="~/vendor/highlightjs/highlight.min.js"></script> <script src="~/vendor/highlightjs/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script> <script>hljs.initHighlightingOnLoad();</script>
<script id="template-product-item" type="text/template">
<div class="col-sm-4 col-md-3 mb-3">
<div class="card">
{image}
<div class="card-body">
<h6 class="card-title">{title}</h6>
<a href="#" class="js-product-edit btn btn-primary" data-toggle="modal" data-target="#product-modal">Edit</a>
<a href="#" class="js-product-remove btn btn-danger"><i class="fa fa-trash"></i></a>
</div>
</div>
</div>
</script>
<script id="template-product-content" type="text/template">
<div class="mb-3">
<input class="js-product-id" type="hidden" name="id" value="{id}">
<input class="js-product-index" type="hidden" name="index" value="{index}">
<div class="form-row">
<div class="col-sm-6">
<label>Title</label>*
<input type="text" class="js-product-title form-control mb-2" value="{title}" autofocus />
</div>
<div class="col-sm-3">
<label>Price</label>*
<input type="text" class="js-product-price form-control mb-2" value="{price}" />
</div>
<div class="col-sm-3">
<label>Custom price</label>
<select class="js-product-custom form-control">
{custom}
</select>
</div>
</div>
<div class="form-row">
<div class="col">
<label>Image</label>
<input type="text" class="js-product-image form-control mb-2" value="{image}" />
</div>
</div>
<div class="form-row">
<div class="col">
<label>Description</label>
<textarea rows="3" cols="40" class="js-product-description form-control mb-2">{description}</textarea>
</div>
</div>
</div>
</script>
<script src="~/products/js/products.js"></script>
<script src="~/products/js/products.jquery.js"></script>
} }

View File

@@ -94,7 +94,7 @@
<h2 class="text-muted" v-if="srvModel.tagline">{{srvModel.tagline}}</h2> <h2 class="text-muted" v-if="srvModel.tagline">{{srvModel.tagline}}</h2>
</div> </div>
<div class="col-sm-12 col-md-3 col-lg-2"> <div class="col-sm-12 col-md-3 col-lg-2">
<button v-if="srvModel.info.active" class="btn btn-lg btn-primary" v-on:click="contributeModalOpen = true">Contribute</button> <button v-if="srvModel.info.active" class="btn btn-lg btn-primary w-100" v-on:click="contributeModalOpen = true">Contribute</button>
</div> </div>

View File

@@ -170,7 +170,7 @@ var resizeCanvas = function() {
window.addEventListener("resize", resizeCanvas); window.addEventListener("resize", resizeCanvas);
// addClickListeners(); // addClickListeners();
handleInactiveUser(); // handleInactiveUser();
})(); })();
function handleInactiveUser() { function handleInactiveUser() {