using System; using System.Globalization; using System.Linq; using System.Text; using System.Text.Encodings.Web; using System.Text.RegularExpressions; using System.Threading.Tasks; using BTCPayServer.Abstractions.Constants; using BTCPayServer.Abstractions.Extensions; using BTCPayServer.Models.AppViewModels; using BTCPayServer.Services.Apps; using Microsoft.AspNetCore.Mvc; namespace BTCPayServer.Controllers { public partial class UIAppsController { [HttpGet("{appId}/settings/pos")] public async Task UpdatePointOfSale(string appId) { var app = GetCurrentApp(); if (app == null) return NotFound(); var settings = app.GetSettings(); settings.DefaultView = settings.EnableShoppingCart ? PosViewType.Cart : settings.DefaultView; settings.EnableShoppingCart = false; var vm = new UpdatePointOfSaleViewModel { Id = appId, StoreId = app.StoreDataId, StoreName = app.StoreData?.StoreName, StoreDefaultCurrency = await GetStoreDefaultCurrentIfEmpty(app.StoreDataId, settings.Currency), AppName = app.Name, Title = settings.Title, DefaultView = settings.DefaultView, ShowCustomAmount = settings.ShowCustomAmount, ShowDiscount = settings.ShowDiscount, EnableTips = settings.EnableTips, Currency = settings.Currency, Template = settings.Template, ButtonText = settings.ButtonText ?? PointOfSaleSettings.BUTTON_TEXT_DEF, CustomButtonText = settings.CustomButtonText ?? PointOfSaleSettings.CUSTOM_BUTTON_TEXT_DEF, CustomTipText = settings.CustomTipText ?? PointOfSaleSettings.CUSTOM_TIP_TEXT_DEF, CustomTipPercentages = settings.CustomTipPercentages != null ? string.Join(",", settings.CustomTipPercentages) : string.Join(",", PointOfSaleSettings.CUSTOM_TIP_PERCENTAGES_DEF), CustomCSSLink = settings.CustomCSSLink, EmbeddedCSS = settings.EmbeddedCSS, Description = settings.Description, NotificationUrl = settings.NotificationUrl, RedirectUrl = settings.RedirectUrl, SearchTerm = app.TagAllInvoices ? $"storeid:{app.StoreDataId}" : $"orderid:{AppService.GetAppOrderId(app)}", RedirectAutomatically = settings.RedirectAutomatically.HasValue ? settings.RedirectAutomatically.Value ? "true" : "false" : "", RequiresRefundEmail = settings.RequiresRefundEmail }; if (HttpContext?.Request != null) { var appUrl = HttpContext.Request.GetAbsoluteUri($"/apps/{appId}/pos"); var encoder = HtmlEncoder.Default; if (settings.ShowCustomAmount) { StringBuilder builder = new StringBuilder(); builder.AppendLine(CultureInfo.InvariantCulture, $"
"); builder.AppendLine($" "); builder.AppendLine($" "); builder.AppendLine($" "); builder.AppendLine($" "); builder.AppendLine($" "); builder.AppendLine($" "); builder.AppendLine($"
"); vm.Example1 = builder.ToString(); } try { var items = _appService.Parse(settings.Template, settings.Currency); var builder = new StringBuilder(); builder.AppendLine(CultureInfo.InvariantCulture, $"
"); builder.AppendLine($" "); builder.AppendLine($" "); builder.AppendLine($" "); builder.AppendLine($" "); builder.AppendLine(CultureInfo.InvariantCulture, $" "); builder.AppendLine($"
"); vm.Example2 = builder.ToString(); } catch { } vm.InvoiceUrl = appUrl + "invoices/SkdsDghkdP3D3qkj7bLq3"; } vm.ExampleCallback = "{\n \"id\":\"SkdsDghkdP3D3qkj7bLq3\",\n \"url\":\"https://btcpay.example.com/invoice?id=SkdsDghkdP3D3qkj7bLq3\",\n \"status\":\"paid\",\n \"price\":10,\n \"currency\":\"EUR\",\n \"invoiceTime\":1520373130312,\n \"expirationTime\":1520374030312,\n \"currentTime\":1520373179327,\n \"exceptionStatus\":false,\n \"buyerFields\":{\n \"buyerEmail\":\"customer@example.com\",\n \"buyerNotify\":false\n },\n \"paymentSubtotals\": {\n \"BTC\":114700\n },\n \"paymentTotals\": {\n \"BTC\":118400\n },\n \"transactionCurrency\": \"BTC\",\n \"amountPaid\": \"1025900\",\n \"exchangeRates\": {\n \"BTC\": {\n \"EUR\": 8721.690715789999,\n \"USD\": 10817.99\n }\n }\n}"; return View(vm); } [HttpPost("{appId}/settings/pos")] public async Task UpdatePointOfSale(string appId, UpdatePointOfSaleViewModel vm) { var app = GetCurrentApp(); if (app == null) return NotFound(); if (!ModelState.IsValid) return View(vm); vm.Currency = await GetStoreDefaultCurrentIfEmpty(app.StoreDataId, vm.Currency); if (_currencies.GetCurrencyData(vm.Currency, false) == null) ModelState.AddModelError(nameof(vm.Currency), "Invalid currency"); try { vm.Template = _appService.SerializeTemplate(_appService.Parse(vm.Template, vm.Currency)); } catch { ModelState.AddModelError(nameof(vm.Template), "Invalid template"); } if (!ModelState.IsValid) { return View(vm); } app.Name = vm.AppName; app.SetSettings(new PointOfSaleSettings { Title = vm.Title, DefaultView = vm.DefaultView, ShowCustomAmount = vm.ShowCustomAmount, ShowDiscount = vm.ShowDiscount, EnableTips = vm.EnableTips, Currency = vm.Currency, Template = vm.Template, ButtonText = vm.ButtonText, CustomButtonText = vm.CustomButtonText, CustomTipText = vm.CustomTipText, CustomTipPercentages = ListSplit(vm.CustomTipPercentages), CustomCSSLink = vm.CustomCSSLink, NotificationUrl = vm.NotificationUrl, RedirectUrl = vm.RedirectUrl, Description = vm.Description, EmbeddedCSS = vm.EmbeddedCSS, RedirectAutomatically = string.IsNullOrEmpty(vm.RedirectAutomatically) ? (bool?)null : bool.Parse(vm.RedirectAutomatically), RequiresRefundEmail = vm.RequiresRefundEmail, }); await _appService.UpdateOrCreateApp(app); TempData[WellKnownTempData.SuccessMessage] = "App updated"; return RedirectToAction(nameof(UpdatePointOfSale), new { appId }); } private int[] ListSplit(string list, string separator = ",") { if (string.IsNullOrEmpty(list)) { return Array.Empty(); } // Remove all characters except numeric and comma Regex charsToDestroy = new Regex(@"[^\d|\" + separator + "]"); list = charsToDestroy.Replace(list, ""); return list.Split(separator, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray(); } } }