btcpayserver/BTCPayServer/Controllers/AppsPublicController.cs

288 lines
13 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
2019-01-08 15:10:05 +01:00
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Data;
2018-12-10 08:39:21 +01:00
using BTCPayServer.Filters;
2019-04-09 04:10:27 +02:00
using BTCPayServer.ModelBinders;
2018-12-28 17:38:20 +01:00
using BTCPayServer.Models;
using BTCPayServer.Models.AppViewModels;
using BTCPayServer.Payments;
using BTCPayServer.Rating;
using BTCPayServer.Security;
using BTCPayServer.Services.Apps;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Rates;
2019-01-07 14:25:35 +01:00
using Ganss.XSS;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Http.Extensions;
2018-12-28 17:38:20 +01:00
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using NBitpayClient;
using YamlDotNet.RepresentationModel;
using static BTCPayServer.Controllers.AppsController;
namespace BTCPayServer.Controllers
{
public class AppsPublicController : Controller
{
2019-02-19 05:04:58 +01:00
public AppsPublicController(AppService AppService,
InvoiceController invoiceController,
UserManager<ApplicationUser> userManager)
{
2019-02-19 05:04:58 +01:00
_AppService = AppService;
_InvoiceController = invoiceController;
2018-12-28 17:38:20 +01:00
_UserManager = userManager;
}
2019-02-19 05:04:58 +01:00
private AppService _AppService;
private InvoiceController _InvoiceController;
2018-12-28 17:38:20 +01:00
private readonly UserManager<ApplicationUser> _UserManager;
[HttpGet]
[Route("/apps/{appId}/pos")]
[XFrameOptionsAttribute(XFrameOptionsAttribute.XFrameOptions.AllowAll)]
public async Task<IActionResult> ViewPointOfSale(string appId)
{
2019-02-19 05:04:58 +01:00
var app = await _AppService.GetApp(appId, AppType.PointOfSale);
if (app == null)
return NotFound();
var settings = app.GetSettings<PointOfSaleSettings>();
2019-02-19 05:04:58 +01:00
var numberFormatInfo = _AppService.Currencies.GetNumberFormatInfo(settings.Currency) ?? _AppService.Currencies.GetNumberFormatInfo("USD");
double step = Math.Pow(10, -(numberFormatInfo.CurrencyDecimalDigits));
return View(new ViewPointOfSaleViewModel()
{
Title = settings.Title,
Step = step.ToString(CultureInfo.InvariantCulture),
EnableShoppingCart = settings.EnableShoppingCart,
ShowCustomAmount = settings.ShowCustomAmount,
ShowDiscount = settings.ShowDiscount,
EnableTips = settings.EnableTips,
CurrencyCode = settings.Currency,
CurrencySymbol = numberFormatInfo.CurrencySymbol,
CurrencyInfo = new ViewPointOfSaleViewModel.CurrencyInfoData()
{
CurrencySymbol = string.IsNullOrEmpty(numberFormatInfo.CurrencySymbol) ? settings.Currency : numberFormatInfo.CurrencySymbol,
Divisibility = numberFormatInfo.CurrencyDecimalDigits,
DecimalSeparator = numberFormatInfo.CurrencyDecimalSeparator,
ThousandSeparator = numberFormatInfo.NumberGroupSeparator,
Prefixed = new[] { 0, 2 }.Contains(numberFormatInfo.CurrencyPositivePattern),
SymbolSpace = new[] { 2, 3 }.Contains(numberFormatInfo.CurrencyPositivePattern)
},
2019-02-19 05:04:58 +01:00
Items = _AppService.Parse(settings.Template, settings.Currency),
ButtonText = settings.ButtonText,
CustomButtonText = settings.CustomButtonText,
CustomTipText = settings.CustomTipText,
2018-12-13 14:36:19 +01:00
CustomTipPercentages = settings.CustomTipPercentages,
2018-12-17 19:11:11 +01:00
CustomCSSLink = settings.CustomCSSLink,
AppId = appId
});
}
2018-12-11 16:36:25 +01:00
[HttpGet]
[Route("/apps/{appId}/crowdfund")]
[XFrameOptionsAttribute(XFrameOptionsAttribute.XFrameOptions.AllowAll)]
2018-12-22 15:02:16 +01:00
public async Task<IActionResult> ViewCrowdfund(string appId, string statusMessage)
2018-12-11 16:36:25 +01:00
{
2019-02-19 05:04:58 +01:00
var app = await _AppService.GetApp(appId, AppType.Crowdfund, true);
2019-01-05 09:18:15 +01:00
if (app == null)
return NotFound();
var settings = app.GetSettings<CrowdfundSettings>();
2019-01-07 14:25:35 +01:00
2019-02-19 05:04:58 +01:00
var isAdmin = await _AppService.GetAppDataIfOwner(GetUserId(), appId, AppType.Crowdfund) != null;
2019-01-07 14:25:35 +01:00
var hasEnoughSettingsToLoad = !string.IsNullOrEmpty(settings.TargetCurrency );
if (!hasEnoughSettingsToLoad)
{
if(!isAdmin)
return NotFound();
return NotFound("A Target Currency must be set for this app in order to be loadable.");
}
2019-03-09 08:08:31 +01:00
var appInfo = (ViewCrowdfundViewModel)(await _AppService.GetAppInfo(appId));
appInfo.HubPath = AppHub.GetHubPath(this.Request);
if (settings.Enabled) return View(appInfo);
2019-01-05 09:18:15 +01:00
if(!isAdmin)
return NotFound();
2019-03-09 08:08:31 +01:00
return View(appInfo);
}
2018-12-22 15:02:16 +01:00
[HttpPost]
2018-12-22 15:02:16 +01:00
[Route("/apps/{appId}/crowdfund")]
[XFrameOptionsAttribute(XFrameOptionsAttribute.XFrameOptions.AllowAll)]
[IgnoreAntiforgeryToken]
[EnableCors(CorsPolicies.All)]
public async Task<IActionResult> ContributeToCrowdfund(string appId, ContributeToCrowdfund request, CancellationToken cancellationToken)
{
2019-02-19 05:04:58 +01:00
var app = await _AppService.GetApp(appId, AppType.Crowdfund, true);
2019-01-02 11:29:47 +01:00
if (app == null)
return NotFound();
var settings = app.GetSettings<CrowdfundSettings>();
2019-02-19 05:04:58 +01:00
var isAdmin = await _AppService.GetAppDataIfOwner(GetUserId(), appId, AppType.Crowdfund) != null;
if (!settings.Enabled && !isAdmin) {
return NotFound("Crowdfund is not currently active");
2019-01-02 11:29:47 +01:00
}
var info = (ViewCrowdfundViewModel)await _AppService.GetAppInfo(appId);
2019-03-09 08:08:31 +01:00
info.HubPath = AppHub.GetHubPath(this.Request);
if (!isAdmin &&
((settings.StartDate.HasValue && DateTime.Now < settings.StartDate) ||
(settings.EndDate.HasValue && DateTime.Now > settings.EndDate) ||
(settings.EnforceTargetAmount &&
(info.Info.PendingProgressPercentage.GetValueOrDefault(0) +
info.Info.ProgressPercentage.GetValueOrDefault(0)) >= 100)))
2019-01-02 11:29:47 +01:00
{
2019-01-05 09:18:15 +01:00
return NotFound("Crowdfund is not currently active");
2019-01-02 11:29:47 +01:00
}
2019-02-19 05:04:58 +01:00
var store = await _AppService.GetStore(app);
var title = settings.Title;
2019-01-02 11:29:47 +01:00
var price = request.Amount;
ViewPointOfSaleViewModel.Item choice = null;
2018-12-29 11:52:07 +01:00
if (!string.IsNullOrEmpty(request.ChoiceKey))
{
2019-02-19 05:04:58 +01:00
var choices = _AppService.Parse(settings.PerksTemplate, settings.TargetCurrency);
choice = choices.FirstOrDefault(c => c.Id == request.ChoiceKey);
2018-12-29 11:52:07 +01:00
if (choice == null)
2019-01-05 09:38:27 +01:00
return NotFound("Incorrect option provided");
2018-12-29 11:52:07 +01:00
title = choice.Title;
price = choice.Price.Value;
if (request.Amount > price)
price = request.Amount;
}
2019-01-04 16:42:35 +01:00
2019-01-05 19:47:39 +01:00
if (!isAdmin && (settings.EnforceTargetAmount && info.TargetAmount.HasValue && price >
(info.TargetAmount - (info.Info.CurrentAmount + info.Info.CurrentPendingAmount))))
2019-01-04 16:42:35 +01:00
{
2019-01-05 09:18:15 +01:00
return NotFound("Contribution Amount is more than is currently allowed.");
2019-01-04 16:42:35 +01:00
}
2018-12-22 15:02:16 +01:00
store.AdditionalClaims.Add(new Claim(Policies.CanCreateInvoice.Key, store.Id));
try
{
var invoice = await _InvoiceController.CreateInvoiceCore(new CreateInvoiceRequest()
{
OrderId = AppService.GetCrowdfundOrderId(appId),
Currency = settings.TargetCurrency,
ItemCode = request.ChoiceKey ?? string.Empty,
ItemDesc = title,
BuyerEmail = request.Email,
Price = price,
NotificationURL = settings.NotificationUrl,
NotificationEmail = settings.NotificationEmail,
FullNotifications = true,
ExtendedNotifications = true,
RedirectURL = request.RedirectUrl ?? Request.GetDisplayUrl()
}, store, HttpContext.Request.GetAbsoluteRoot(), new List<string> { AppService.GetAppInternalTag(appId) }, cancellationToken: cancellationToken);
if (request.RedirectToCheckout)
{
return RedirectToAction(nameof(InvoiceController.Checkout), "Invoice",
new {invoiceId = invoice.Data.Id});
}
else
{
return Ok(invoice.Data.Id);
}
}
catch (BitpayHttpException e)
{
return BadRequest(e.Message);
}
2018-12-11 16:36:25 +01:00
}
[HttpPost]
[Route("/apps/{appId}/pos")]
[XFrameOptionsAttribute(XFrameOptionsAttribute.XFrameOptions.AllowAll)]
[IgnoreAntiforgeryToken]
[EnableCors(CorsPolicies.All)]
public async Task<IActionResult> ViewPointOfSale(string appId,
2019-04-09 04:10:27 +02:00
[ModelBinder(typeof(InvariantDecimalModelBinder))] decimal amount,
string email,
string orderId,
string notificationUrl,
string redirectUrl,
string choiceKey,
string posData = null, CancellationToken cancellationToken = default)
{
2019-02-19 05:04:58 +01:00
var app = await _AppService.GetApp(appId, AppType.PointOfSale);
if (string.IsNullOrEmpty(choiceKey) && amount <= 0)
{
return RedirectToAction(nameof(ViewPointOfSale), new { appId = appId });
}
if (app == null)
return NotFound();
var settings = app.GetSettings<PointOfSaleSettings>();
if (string.IsNullOrEmpty(choiceKey) && !settings.ShowCustomAmount && !settings.EnableShoppingCart)
{
return RedirectToAction(nameof(ViewPointOfSale), new { appId = appId });
}
string title = null;
var price = 0.0m;
ViewPointOfSaleViewModel.Item choice = null;
if (!string.IsNullOrEmpty(choiceKey))
{
2019-02-19 05:04:58 +01:00
var choices = _AppService.Parse(settings.Template, settings.Currency);
choice = choices.FirstOrDefault(c => c.Id == choiceKey);
if (choice == null)
return NotFound();
title = choice.Title;
price = choice.Price.Value;
if (amount > price)
price = amount;
}
else
{
if (!settings.ShowCustomAmount && !settings.EnableShoppingCart)
return NotFound();
price = amount;
title = settings.Title;
}
2019-02-19 05:04:58 +01:00
var store = await _AppService.GetStore(app);
store.AdditionalClaims.Add(new Claim(Policies.CanCreateInvoice.Key, store.Id));
var invoice = await _InvoiceController.CreateInvoiceCore(new CreateInvoiceRequest()
2019-04-08 15:46:24 +02:00
{
ItemCode = choice?.Id,
ItemDesc = title,
Currency = settings.Currency,
Price = price,
BuyerEmail = email,
OrderId = orderId,
NotificationURL =
string.IsNullOrEmpty(notificationUrl) ? settings.NotificationUrl : notificationUrl,
NotificationEmail = settings.NotificationEmail,
RedirectURL = redirectUrl ?? Request.GetDisplayUrl(),
FullNotifications = true,
ExtendedNotifications = true,
PosData = string.IsNullOrEmpty(posData) ? null : posData,
RedirectAutomatically = settings.RedirectAutomatically,
2019-04-08 15:46:24 +02:00
}, store, HttpContext.Request.GetAbsoluteRoot(),
new List<string>() {AppService.GetAppInternalTag(appId)},
cancellationToken);
2018-11-23 05:09:30 +01:00
return RedirectToAction(nameof(InvoiceController.Checkout), "Invoice", new { invoiceId = invoice.Data.Id });
}
2018-12-28 17:38:20 +01:00
private string GetUserId()
{
return _UserManager.GetUserId(User);
}
}
}