2022-02-26 21:22:29 -08:00
|
|
|
#nullable enable
|
|
|
|
using System;
|
2022-11-17 21:20:07 -08:00
|
|
|
using System.Linq;
|
2022-02-26 21:22:29 -08:00
|
|
|
using System.Threading.Tasks;
|
|
|
|
using BTCPayServer.Abstractions.Constants;
|
2023-01-06 14:18:07 +01:00
|
|
|
using BTCPayServer.Abstractions.Extensions;
|
2022-02-26 21:22:29 -08:00
|
|
|
using BTCPayServer.Client;
|
|
|
|
using BTCPayServer.Client.Models;
|
|
|
|
using BTCPayServer.Data;
|
2023-03-17 03:56:32 +01:00
|
|
|
using BTCPayServer.Plugins.Crowdfund;
|
|
|
|
using BTCPayServer.Plugins.PointOfSale;
|
2022-02-26 21:22:29 -08:00
|
|
|
using BTCPayServer.Services.Apps;
|
2022-07-17 22:23:22 -07:00
|
|
|
using BTCPayServer.Services.Rates;
|
2022-02-26 21:22:29 -08:00
|
|
|
using BTCPayServer.Services.Stores;
|
|
|
|
using Microsoft.AspNetCore.Authorization;
|
|
|
|
using Microsoft.AspNetCore.Cors;
|
|
|
|
using Microsoft.AspNetCore.Identity;
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
2023-02-07 00:01:53 -08:00
|
|
|
using Newtonsoft.Json;
|
2024-06-26 10:42:22 +02:00
|
|
|
using CrowdfundResetEvery = BTCPayServer.Client.Models.CrowdfundResetEvery;
|
|
|
|
using PosViewType = BTCPayServer.Client.Models.PosViewType;
|
2022-02-26 21:22:29 -08:00
|
|
|
|
|
|
|
namespace BTCPayServer.Controllers.Greenfield
|
|
|
|
{
|
|
|
|
[ApiController]
|
|
|
|
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
|
|
|
[EnableCors(CorsPolicies.All)]
|
|
|
|
public class GreenfieldAppsController : ControllerBase
|
|
|
|
{
|
|
|
|
private readonly AppService _appService;
|
|
|
|
private readonly StoreRepository _storeRepository;
|
2022-07-17 22:23:22 -07:00
|
|
|
private readonly CurrencyNameTable _currencies;
|
2023-01-29 16:42:24 -08:00
|
|
|
private readonly UserManager<ApplicationUser> _userManager;
|
2022-02-26 21:22:29 -08:00
|
|
|
|
|
|
|
public GreenfieldAppsController(
|
|
|
|
AppService appService,
|
|
|
|
StoreRepository storeRepository,
|
2022-07-17 22:23:22 -07:00
|
|
|
BTCPayNetworkProvider btcPayNetworkProvider,
|
2023-01-29 16:42:24 -08:00
|
|
|
CurrencyNameTable currencies,
|
|
|
|
UserManager<ApplicationUser> userManager
|
2022-02-26 21:22:29 -08:00
|
|
|
)
|
|
|
|
{
|
|
|
|
_appService = appService;
|
|
|
|
_storeRepository = storeRepository;
|
2022-07-17 22:23:22 -07:00
|
|
|
_currencies = currencies;
|
2023-01-29 16:42:24 -08:00
|
|
|
_userManager = userManager;
|
2022-02-26 21:22:29 -08:00
|
|
|
}
|
|
|
|
|
2022-11-17 21:20:07 -08:00
|
|
|
[HttpPost("~/api/v1/stores/{storeId}/apps/crowdfund")]
|
|
|
|
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
2024-06-26 10:42:22 +02:00
|
|
|
public async Task<IActionResult> CreateCrowdfundApp(string storeId, CrowdfundAppRequest request)
|
2022-11-17 21:20:07 -08:00
|
|
|
{
|
|
|
|
var store = await _storeRepository.FindStore(storeId);
|
|
|
|
if (store == null)
|
|
|
|
return this.CreateAPIError(404, "store-not-found", "The store was not found");
|
|
|
|
|
2024-06-26 10:42:22 +02:00
|
|
|
// This is not obvious, but we must have a non-null currency or else request validation may not work correctly
|
|
|
|
request.TargetCurrency ??= store.GetStoreBlob().DefaultCurrency;
|
2022-11-17 21:20:07 -08:00
|
|
|
|
2024-06-26 10:42:22 +02:00
|
|
|
ValidateAppRequest(request);
|
|
|
|
ValidateCrowdfundAppRequest(request);
|
|
|
|
if (!ModelState.IsValid)
|
2022-11-17 21:20:07 -08:00
|
|
|
{
|
2024-06-26 10:42:22 +02:00
|
|
|
return this.CreateValidationError(ModelState);
|
2022-11-17 21:20:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
var appData = new AppData
|
|
|
|
{
|
|
|
|
StoreDataId = storeId,
|
|
|
|
Name = request.AppName,
|
2023-09-11 02:59:17 +02:00
|
|
|
AppType = CrowdfundAppType.AppType,
|
|
|
|
Archived = request.Archived ?? false
|
2022-11-17 21:20:07 -08:00
|
|
|
};
|
|
|
|
|
2024-06-26 10:42:22 +02:00
|
|
|
var settings = ToCrowdfundSettings(request, new CrowdfundSettings { Title = request.Title ?? request.AppName });
|
|
|
|
appData.SetSettings(settings);
|
2022-11-17 21:20:07 -08:00
|
|
|
|
|
|
|
await _appService.UpdateOrCreateApp(appData);
|
|
|
|
|
|
|
|
return Ok(ToCrowdfundModel(appData));
|
|
|
|
}
|
|
|
|
|
2022-05-01 22:28:27 -07:00
|
|
|
[HttpPost("~/api/v1/stores/{storeId}/apps/pos")]
|
|
|
|
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
2024-06-26 10:42:22 +02:00
|
|
|
public async Task<IActionResult> CreatePointOfSaleApp(string storeId, PointOfSaleAppRequest request)
|
2022-02-26 21:22:29 -08:00
|
|
|
{
|
2022-07-17 22:23:22 -07:00
|
|
|
var store = await _storeRepository.FindStore(storeId);
|
|
|
|
if (store == null)
|
|
|
|
return this.CreateAPIError(404, "store-not-found", "The store was not found");
|
|
|
|
|
2024-06-26 10:42:22 +02:00
|
|
|
// This is not obvious, but we must have a non-null currency or else request validation may not work correctly
|
|
|
|
request.Currency ??= store.GetStoreBlob().DefaultCurrency;
|
2022-07-17 22:23:22 -07:00
|
|
|
|
2024-06-26 10:42:22 +02:00
|
|
|
ValidateAppRequest(request);
|
|
|
|
ValidatePOSAppRequest(request);
|
|
|
|
if (!ModelState.IsValid)
|
2022-02-26 21:22:29 -08:00
|
|
|
{
|
2024-06-26 10:42:22 +02:00
|
|
|
return this.CreateValidationError(ModelState);
|
2022-02-26 21:22:29 -08:00
|
|
|
}
|
2023-01-06 14:18:07 +01:00
|
|
|
|
2022-02-26 21:22:29 -08:00
|
|
|
var appData = new AppData
|
|
|
|
{
|
2022-05-01 22:28:27 -07:00
|
|
|
StoreDataId = storeId,
|
2022-02-26 21:22:29 -08:00
|
|
|
Name = request.AppName,
|
2023-09-11 02:59:17 +02:00
|
|
|
AppType = PointOfSaleAppType.AppType,
|
|
|
|
Archived = request.Archived ?? false
|
2022-02-26 21:22:29 -08:00
|
|
|
};
|
|
|
|
|
2024-06-26 10:42:22 +02:00
|
|
|
var settings = ToPointOfSaleSettings(request, new PointOfSaleSettings { Title = request.Title ?? request.AppName });
|
|
|
|
appData.SetSettings(settings);
|
2022-02-26 21:22:29 -08:00
|
|
|
|
|
|
|
await _appService.UpdateOrCreateApp(appData);
|
|
|
|
|
2022-11-17 21:20:07 -08:00
|
|
|
return Ok(ToPointOfSaleModel(appData));
|
2022-02-26 21:22:29 -08:00
|
|
|
}
|
|
|
|
|
2022-07-17 22:23:22 -07:00
|
|
|
[HttpPut("~/api/v1/apps/pos/{appId}")]
|
|
|
|
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
2024-06-26 10:42:22 +02:00
|
|
|
public async Task<IActionResult> UpdatePointOfSaleApp(string appId, PointOfSaleAppRequest request)
|
2022-07-17 22:23:22 -07:00
|
|
|
{
|
2023-09-11 02:59:17 +02:00
|
|
|
var app = await _appService.GetApp(appId, PointOfSaleAppType.AppType, includeArchived: true);
|
2022-07-17 22:23:22 -07:00
|
|
|
if (app == null)
|
|
|
|
{
|
|
|
|
return AppNotFound();
|
|
|
|
}
|
|
|
|
|
|
|
|
var settings = app.GetSettings<PointOfSaleSettings>();
|
2023-01-06 14:18:07 +01:00
|
|
|
|
2024-06-26 10:42:22 +02:00
|
|
|
// This is not obvious, but we must have a non-null currency or else request validation may not work correctly
|
|
|
|
request.Currency ??= settings.Currency;
|
2022-07-17 22:23:22 -07:00
|
|
|
|
2024-06-26 10:42:22 +02:00
|
|
|
ValidatePOSAppRequest(request);
|
|
|
|
if (!string.IsNullOrEmpty(request.AppName))
|
|
|
|
{
|
|
|
|
ValidateAppRequest(request);
|
|
|
|
}
|
|
|
|
if (!ModelState.IsValid)
|
2022-07-17 22:23:22 -07:00
|
|
|
{
|
2024-06-26 10:42:22 +02:00
|
|
|
return this.CreateValidationError(ModelState);
|
2022-07-17 22:23:22 -07:00
|
|
|
}
|
|
|
|
|
2024-06-26 10:42:22 +02:00
|
|
|
if (!string.IsNullOrEmpty(request.AppName))
|
|
|
|
{
|
|
|
|
app.Name = request.AppName;
|
|
|
|
}
|
2023-09-11 02:59:17 +02:00
|
|
|
if (request.Archived != null)
|
|
|
|
{
|
|
|
|
app.Archived = request.Archived.Value;
|
|
|
|
}
|
2024-06-26 10:42:22 +02:00
|
|
|
app.SetSettings(ToPointOfSaleSettings(request, settings));
|
2023-01-06 14:18:07 +01:00
|
|
|
|
2022-07-17 22:23:22 -07:00
|
|
|
await _appService.UpdateOrCreateApp(app);
|
|
|
|
|
2022-11-17 21:20:07 -08:00
|
|
|
return Ok(ToPointOfSaleModel(app));
|
2022-07-17 22:23:22 -07:00
|
|
|
}
|
|
|
|
|
2023-01-29 16:42:24 -08:00
|
|
|
[HttpGet("~/api/v1/apps")]
|
|
|
|
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
|
|
|
public async Task<IActionResult> GetAllApps()
|
|
|
|
{
|
2023-09-11 02:59:17 +02:00
|
|
|
var apps = await _appService.GetAllApps(_userManager.GetUserId(User), includeArchived: true);
|
2023-01-29 16:42:24 -08:00
|
|
|
|
|
|
|
return Ok(apps.Select(ToModel).ToArray());
|
|
|
|
}
|
|
|
|
|
|
|
|
[HttpGet("~/api/v1/stores/{storeId}/apps")]
|
|
|
|
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
|
|
|
public async Task<IActionResult> GetAllApps(string storeId)
|
|
|
|
{
|
2023-09-11 02:59:17 +02:00
|
|
|
var apps = await _appService.GetAllApps(_userManager.GetUserId(User), false, storeId, true);
|
2023-01-29 16:42:24 -08:00
|
|
|
|
|
|
|
return Ok(apps.Select(ToModel).ToArray());
|
|
|
|
}
|
|
|
|
|
2022-06-26 18:14:16 -07:00
|
|
|
[HttpGet("~/api/v1/apps/{appId}")]
|
|
|
|
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
|
|
|
public async Task<IActionResult> GetApp(string appId)
|
|
|
|
{
|
2023-09-11 02:59:17 +02:00
|
|
|
var app = await _appService.GetApp(appId, null, includeArchived: true);
|
2022-06-26 18:14:16 -07:00
|
|
|
if (app == null)
|
|
|
|
{
|
|
|
|
return AppNotFound();
|
|
|
|
}
|
2023-01-06 14:18:07 +01:00
|
|
|
|
2022-06-26 18:14:16 -07:00
|
|
|
return Ok(ToModel(app));
|
|
|
|
}
|
|
|
|
|
2023-02-07 00:01:53 -08:00
|
|
|
[HttpGet("~/api/v1/apps/pos/{appId}")]
|
|
|
|
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
|
|
|
public async Task<IActionResult> GetPosApp(string appId)
|
|
|
|
{
|
2023-09-11 02:59:17 +02:00
|
|
|
var app = await _appService.GetApp(appId, PointOfSaleAppType.AppType, includeArchived: true);
|
2023-02-07 00:01:53 -08:00
|
|
|
if (app == null)
|
|
|
|
{
|
|
|
|
return AppNotFound();
|
|
|
|
}
|
2023-04-10 11:07:03 +09:00
|
|
|
|
2023-02-07 00:01:53 -08:00
|
|
|
return Ok(ToPointOfSaleModel(app));
|
|
|
|
}
|
|
|
|
|
|
|
|
[HttpGet("~/api/v1/apps/crowdfund/{appId}")]
|
|
|
|
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
|
|
|
public async Task<IActionResult> GetCrowdfundApp(string appId)
|
|
|
|
{
|
2023-09-11 02:59:17 +02:00
|
|
|
var app = await _appService.GetApp(appId, CrowdfundAppType.AppType, includeArchived: true);
|
2023-02-07 00:01:53 -08:00
|
|
|
if (app == null)
|
|
|
|
{
|
|
|
|
return AppNotFound();
|
|
|
|
}
|
2023-04-10 11:07:03 +09:00
|
|
|
|
2023-02-07 00:01:53 -08:00
|
|
|
return Ok(ToCrowdfundModel(app));
|
|
|
|
}
|
|
|
|
|
2022-06-26 18:14:16 -07:00
|
|
|
[HttpDelete("~/api/v1/apps/{appId}")]
|
2024-07-10 15:50:32 +02:00
|
|
|
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
2022-06-26 18:14:16 -07:00
|
|
|
public async Task<IActionResult> DeleteApp(string appId)
|
|
|
|
{
|
2023-09-11 02:59:17 +02:00
|
|
|
var app = await _appService.GetApp(appId, null, includeArchived: true);
|
2022-06-26 18:14:16 -07:00
|
|
|
if (app == null)
|
|
|
|
{
|
|
|
|
return AppNotFound();
|
|
|
|
}
|
2023-01-06 14:18:07 +01:00
|
|
|
|
2022-06-26 18:14:16 -07:00
|
|
|
await _appService.DeleteApp(app);
|
|
|
|
|
|
|
|
return Ok();
|
|
|
|
}
|
|
|
|
|
|
|
|
private IActionResult AppNotFound()
|
|
|
|
{
|
|
|
|
return this.CreateAPIError(404, "app-not-found", "The app with specified ID was not found");
|
|
|
|
}
|
|
|
|
|
2024-06-26 10:42:22 +02:00
|
|
|
private CrowdfundSettings ToCrowdfundSettings(CrowdfundAppRequest request, CrowdfundSettings settings)
|
2022-11-17 21:20:07 -08:00
|
|
|
{
|
|
|
|
var parsedSounds = ValidateStringArray(request.Sounds);
|
|
|
|
var parsedColors = ValidateStringArray(request.AnimationColors);
|
2024-06-26 10:42:22 +02:00
|
|
|
Enum.TryParse<BTCPayServer.Services.Apps.CrowdfundResetEvery>(request.ResetEvery.ToString(), true, out var resetEvery);
|
|
|
|
|
2022-11-17 21:20:07 -08:00
|
|
|
return new CrowdfundSettings
|
|
|
|
{
|
2024-03-11 11:04:41 +01:00
|
|
|
Title = request.Title?.Trim() ?? request.AppName,
|
2022-11-17 21:20:07 -08:00
|
|
|
Enabled = request.Enabled ?? true,
|
|
|
|
EnforceTargetAmount = request.EnforceTargetAmount ?? false,
|
|
|
|
StartDate = request.StartDate?.UtcDateTime,
|
|
|
|
TargetCurrency = request.TargetCurrency?.Trim(),
|
|
|
|
Description = request.Description?.Trim(),
|
|
|
|
EndDate = request.EndDate?.UtcDateTime,
|
|
|
|
TargetAmount = request.TargetAmount,
|
|
|
|
MainImageUrl = request.MainImageUrl?.Trim(),
|
|
|
|
NotificationUrl = request.NotificationUrl?.Trim(),
|
|
|
|
Tagline = request.Tagline?.Trim(),
|
2023-05-23 02:18:57 +02:00
|
|
|
PerksTemplate = request.PerksTemplate is not null ? AppService.SerializeTemplate(AppService.Parse(request.PerksTemplate.Trim())) : null,
|
2022-11-17 21:20:07 -08:00
|
|
|
// If Disqus shortname is not null or empty we assume that Disqus should be enabled
|
|
|
|
DisqusEnabled = !string.IsNullOrEmpty(request.DisqusShortname?.Trim()),
|
|
|
|
DisqusShortname = request.DisqusShortname?.Trim(),
|
|
|
|
// If explicit parameter is not passed for enabling sounds/animations, turn them on if custom sounds/colors are passed
|
|
|
|
SoundsEnabled = request.SoundsEnabled ?? parsedSounds != null,
|
|
|
|
AnimationsEnabled = request.AnimationsEnabled ?? parsedColors != null,
|
|
|
|
ResetEveryAmount = request.ResetEveryAmount ?? 1,
|
2024-06-26 10:42:22 +02:00
|
|
|
ResetEvery = resetEvery,
|
2022-11-17 21:20:07 -08:00
|
|
|
DisplayPerksValue = request.DisplayPerksValue ?? false,
|
|
|
|
DisplayPerksRanking = request.DisplayPerksRanking ?? false,
|
|
|
|
SortPerksByPopularity = request.SortPerksByPopularity ?? false,
|
|
|
|
Sounds = parsedSounds ?? new CrowdfundSettings().Sounds,
|
2024-06-26 10:42:22 +02:00
|
|
|
AnimationColors = parsedColors ?? new CrowdfundSettings().AnimationColors,
|
|
|
|
FormId = request.FormId
|
2022-11-17 21:20:07 -08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-06-26 10:42:22 +02:00
|
|
|
private PointOfSaleSettings ToPointOfSaleSettings(PointOfSaleAppRequest request, PointOfSaleSettings settings)
|
2022-07-17 22:23:22 -07:00
|
|
|
{
|
2024-06-26 10:42:22 +02:00
|
|
|
Enum.TryParse<BTCPayServer.Plugins.PointOfSale.PosViewType>(request.DefaultView.ToString(), true, out var defaultView);
|
|
|
|
|
2023-11-13 13:59:14 +01:00
|
|
|
return new PointOfSaleSettings
|
2022-07-17 22:23:22 -07:00
|
|
|
{
|
2024-03-11 11:04:41 +01:00
|
|
|
Title = request.Title ?? request.AppName,
|
2024-06-26 10:42:22 +02:00
|
|
|
DefaultView = defaultView,
|
|
|
|
ShowItems = request.ShowItems ?? false,
|
|
|
|
ShowCustomAmount = request.ShowCustomAmount ?? false,
|
|
|
|
ShowDiscount = request.ShowDiscount ?? false,
|
|
|
|
ShowSearch = request.ShowSearch ?? false,
|
|
|
|
ShowCategories = request.ShowCategories ?? false,
|
|
|
|
EnableTips = request.EnableTips ?? false,
|
2022-07-17 22:23:22 -07:00
|
|
|
Currency = request.Currency,
|
2023-05-23 02:18:57 +02:00
|
|
|
Template = request.Template != null ? AppService.SerializeTemplate(AppService.Parse(request.Template)) : null,
|
2022-07-17 22:23:22 -07:00
|
|
|
ButtonText = request.FixedAmountPayButtonText ?? PointOfSaleSettings.BUTTON_TEXT_DEF,
|
|
|
|
CustomButtonText = request.CustomAmountPayButtonText ?? PointOfSaleSettings.CUSTOM_BUTTON_TEXT_DEF,
|
|
|
|
CustomTipText = request.TipText ?? PointOfSaleSettings.CUSTOM_TIP_TEXT_DEF,
|
2024-06-26 10:42:22 +02:00
|
|
|
CustomTipPercentages = request.CustomTipPercentages,
|
2022-07-17 22:23:22 -07:00
|
|
|
NotificationUrl = request.NotificationUrl,
|
|
|
|
RedirectUrl = request.RedirectUrl,
|
|
|
|
Description = request.Description,
|
|
|
|
RedirectAutomatically = request.RedirectAutomatically,
|
2023-11-30 10:05:35 +01:00
|
|
|
FormId = request.FormId
|
2022-07-17 22:23:22 -07:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-06-26 10:42:22 +02:00
|
|
|
private AppBaseData ToModel(AppData appData)
|
2022-02-26 21:22:29 -08:00
|
|
|
{
|
2024-06-26 10:42:22 +02:00
|
|
|
return new AppBaseData
|
2022-11-17 21:20:07 -08:00
|
|
|
{
|
|
|
|
Id = appData.Id,
|
2023-09-11 02:59:17 +02:00
|
|
|
Archived = appData.Archived,
|
2022-11-17 21:20:07 -08:00
|
|
|
AppType = appData.AppType,
|
2024-06-26 10:42:22 +02:00
|
|
|
AppName = appData.Name,
|
2022-11-17 21:20:07 -08:00
|
|
|
StoreId = appData.StoreDataId,
|
|
|
|
Created = appData.Created,
|
2023-01-29 16:42:24 -08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-06-26 10:42:22 +02:00
|
|
|
private AppBaseData ToModel(Models.AppViewModels.ListAppsViewModel.ListAppViewModel appData)
|
2023-01-29 16:42:24 -08:00
|
|
|
{
|
2024-06-26 10:42:22 +02:00
|
|
|
return new AppBaseData
|
2023-01-29 16:42:24 -08:00
|
|
|
{
|
|
|
|
Id = appData.Id,
|
2023-09-11 02:59:17 +02:00
|
|
|
Archived = appData.Archived,
|
2023-01-29 16:42:24 -08:00
|
|
|
AppType = appData.AppType,
|
2024-06-26 10:42:22 +02:00
|
|
|
AppName = appData.AppName,
|
2023-01-29 16:42:24 -08:00
|
|
|
StoreId = appData.StoreId,
|
|
|
|
Created = appData.Created,
|
2022-11-17 21:20:07 -08:00
|
|
|
};
|
|
|
|
}
|
2022-07-17 22:23:22 -07:00
|
|
|
|
2022-11-17 21:20:07 -08:00
|
|
|
private PointOfSaleAppData ToPointOfSaleModel(AppData appData)
|
|
|
|
{
|
2023-02-07 00:01:53 -08:00
|
|
|
var settings = appData.GetSettings<PointOfSaleSettings>();
|
2024-06-26 10:42:22 +02:00
|
|
|
Enum.TryParse<PosViewType>(settings.DefaultView.ToString(), true, out var defaultView);
|
|
|
|
|
2022-05-01 22:28:27 -07:00
|
|
|
return new PointOfSaleAppData
|
2022-02-26 21:22:29 -08:00
|
|
|
{
|
2022-05-01 22:28:27 -07:00
|
|
|
Id = appData.Id,
|
2023-09-11 02:59:17 +02:00
|
|
|
Archived = appData.Archived,
|
2022-05-01 22:28:27 -07:00
|
|
|
AppType = appData.AppType,
|
2024-06-26 10:42:22 +02:00
|
|
|
AppName = appData.Name,
|
2022-05-01 22:28:27 -07:00
|
|
|
StoreId = appData.StoreDataId,
|
2022-07-17 22:23:22 -07:00
|
|
|
Created = appData.Created,
|
2023-02-07 00:01:53 -08:00
|
|
|
Title = settings.Title,
|
2024-06-26 10:42:22 +02:00
|
|
|
DefaultView = defaultView,
|
2024-03-14 11:11:54 +01:00
|
|
|
ShowItems = settings.ShowItems,
|
2023-02-07 00:01:53 -08:00
|
|
|
ShowCustomAmount = settings.ShowCustomAmount,
|
|
|
|
ShowDiscount = settings.ShowDiscount,
|
2023-11-13 13:59:14 +01:00
|
|
|
ShowSearch = settings.ShowSearch,
|
|
|
|
ShowCategories = settings.ShowCategories,
|
2023-02-07 00:01:53 -08:00
|
|
|
EnableTips = settings.EnableTips,
|
|
|
|
Currency = settings.Currency,
|
|
|
|
FixedAmountPayButtonText = settings.ButtonText,
|
|
|
|
CustomAmountPayButtonText = settings.CustomButtonText,
|
|
|
|
TipText = settings.CustomTipText,
|
2024-06-26 10:42:22 +02:00
|
|
|
CustomTipPercentages = settings.CustomTipPercentages,
|
|
|
|
FormId = settings.FormId,
|
2023-02-07 00:01:53 -08:00
|
|
|
NotificationUrl = settings.NotificationUrl,
|
|
|
|
RedirectUrl = settings.RedirectUrl,
|
|
|
|
Description = settings.Description,
|
2024-06-26 10:42:22 +02:00
|
|
|
RedirectAutomatically = settings.RedirectAutomatically,
|
|
|
|
Items = JsonConvert.DeserializeObject(
|
|
|
|
JsonConvert.SerializeObject(
|
|
|
|
AppService.Parse(settings.Template),
|
|
|
|
new JsonSerializerSettings
|
|
|
|
{
|
|
|
|
ContractResolver =
|
|
|
|
new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
|
|
|
|
}
|
|
|
|
)
|
|
|
|
)
|
2022-05-01 22:28:27 -07:00
|
|
|
};
|
|
|
|
}
|
2022-02-26 21:22:29 -08:00
|
|
|
|
2024-06-26 10:42:22 +02:00
|
|
|
private void ValidatePOSAppRequest(PointOfSaleAppRequest request)
|
2022-07-17 22:23:22 -07:00
|
|
|
{
|
|
|
|
if (request.Currency != null && _currencies.GetCurrencyData(request.Currency, false) == null)
|
|
|
|
{
|
|
|
|
ModelState.AddModelError(nameof(request.Currency), "Invalid currency");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (request.Template != null)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2023-05-23 02:18:57 +02:00
|
|
|
// Just checking if we can serialize
|
|
|
|
AppService.SerializeTemplate(AppService.Parse(request.Template));
|
2022-07-17 22:23:22 -07:00
|
|
|
}
|
|
|
|
catch
|
|
|
|
{
|
|
|
|
ModelState.AddModelError(nameof(request.Template), "Invalid template");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-17 21:20:07 -08:00
|
|
|
private CrowdfundAppData ToCrowdfundModel(AppData appData)
|
|
|
|
{
|
2023-02-07 00:01:53 -08:00
|
|
|
var settings = appData.GetSettings<CrowdfundSettings>();
|
2024-06-26 10:42:22 +02:00
|
|
|
Enum.TryParse<CrowdfundResetEvery>(settings.ResetEvery.ToString(), true, out var resetEvery);
|
2023-02-07 00:01:53 -08:00
|
|
|
|
2022-11-17 21:20:07 -08:00
|
|
|
return new CrowdfundAppData
|
|
|
|
{
|
|
|
|
Id = appData.Id,
|
2023-09-11 02:59:17 +02:00
|
|
|
Archived = appData.Archived,
|
2022-11-17 21:20:07 -08:00
|
|
|
AppType = appData.AppType,
|
2024-06-26 10:42:22 +02:00
|
|
|
AppName = appData.Name,
|
2022-11-17 21:20:07 -08:00
|
|
|
StoreId = appData.StoreDataId,
|
2023-02-07 00:01:53 -08:00
|
|
|
Created = appData.Created,
|
|
|
|
Title = settings.Title,
|
|
|
|
Enabled = settings.Enabled,
|
|
|
|
EnforceTargetAmount = settings.EnforceTargetAmount,
|
|
|
|
StartDate = settings.StartDate,
|
|
|
|
TargetCurrency = settings.TargetCurrency,
|
|
|
|
Description = settings.Description,
|
|
|
|
EndDate = settings.EndDate,
|
|
|
|
TargetAmount = settings.TargetAmount,
|
|
|
|
MainImageUrl = settings.MainImageUrl,
|
|
|
|
NotificationUrl = settings.NotificationUrl,
|
|
|
|
Tagline = settings.Tagline,
|
|
|
|
DisqusEnabled = settings.DisqusEnabled,
|
|
|
|
DisqusShortname = settings.DisqusShortname,
|
|
|
|
SoundsEnabled = settings.SoundsEnabled,
|
|
|
|
AnimationsEnabled = settings.AnimationsEnabled,
|
|
|
|
ResetEveryAmount = settings.ResetEveryAmount,
|
2024-06-26 10:42:22 +02:00
|
|
|
ResetEvery = resetEvery,
|
2023-02-07 00:01:53 -08:00
|
|
|
DisplayPerksValue = settings.DisplayPerksValue,
|
|
|
|
DisplayPerksRanking = settings.DisplayPerksRanking,
|
|
|
|
SortPerksByPopularity = settings.SortPerksByPopularity,
|
|
|
|
Sounds = settings.Sounds,
|
2024-06-26 10:42:22 +02:00
|
|
|
AnimationColors = settings.AnimationColors,
|
|
|
|
Perks = JsonConvert.DeserializeObject(
|
|
|
|
JsonConvert.SerializeObject(
|
|
|
|
AppService.Parse(settings.PerksTemplate),
|
|
|
|
new JsonSerializerSettings
|
|
|
|
{
|
|
|
|
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
|
|
|
|
}
|
|
|
|
)
|
|
|
|
)
|
2022-11-17 21:20:07 -08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
private string[]? ValidateStringArray(string[]? arr)
|
|
|
|
{
|
2023-01-06 14:18:07 +01:00
|
|
|
if (arr == null || !arr.Any())
|
2022-11-17 21:20:07 -08:00
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure it's not just an array of empty strings
|
|
|
|
if (arr.All(s => string.IsNullOrEmpty(s.Trim())))
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return arr.Select(s => s.Trim()).ToArray();
|
|
|
|
}
|
|
|
|
|
2024-06-26 10:42:22 +02:00
|
|
|
private void ValidateCrowdfundAppRequest(CrowdfundAppRequest request)
|
2022-11-17 21:20:07 -08:00
|
|
|
{
|
|
|
|
if (request.TargetCurrency != null && _currencies.GetCurrencyData(request.TargetCurrency, false) == null)
|
|
|
|
{
|
|
|
|
ModelState.AddModelError(nameof(request.TargetCurrency), "Invalid currency");
|
|
|
|
}
|
|
|
|
|
2024-06-26 10:42:22 +02:00
|
|
|
if (request.PerksTemplate != null)
|
2022-11-17 21:20:07 -08:00
|
|
|
{
|
2024-06-26 10:42:22 +02:00
|
|
|
try
|
|
|
|
{
|
|
|
|
// Just checking if we can serialize
|
|
|
|
AppService.SerializeTemplate(AppService.Parse(request.PerksTemplate));
|
|
|
|
}
|
|
|
|
catch
|
|
|
|
{
|
|
|
|
ModelState.AddModelError(nameof(request.PerksTemplate), "Invalid template");
|
|
|
|
}
|
2022-11-17 21:20:07 -08:00
|
|
|
}
|
|
|
|
|
2024-06-26 10:42:22 +02:00
|
|
|
if (request.ResetEvery.HasValue && request.ResetEvery != CrowdfundResetEvery.Never)
|
2022-11-17 21:20:07 -08:00
|
|
|
{
|
2024-06-26 10:42:22 +02:00
|
|
|
if (request.StartDate == null)
|
|
|
|
{
|
|
|
|
ModelState.AddModelError(nameof(request.StartDate), "A start date is needed when the goal resets every X amount of time");
|
|
|
|
}
|
|
|
|
if (request.ResetEveryAmount <= 0)
|
|
|
|
{
|
|
|
|
ModelState.AddModelError(nameof(request.ResetEveryAmount), "You must reset the goal at a minimum of 1");
|
|
|
|
}
|
2022-11-17 21:20:07 -08:00
|
|
|
}
|
2024-06-26 10:42:22 +02:00
|
|
|
|
2022-11-17 21:20:07 -08:00
|
|
|
if (request.Sounds != null && ValidateStringArray(request.Sounds) == null)
|
|
|
|
{
|
|
|
|
ModelState.AddModelError(nameof(request.Sounds), "Sounds must be a non-empty array of non-empty strings");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (request.AnimationColors != null && ValidateStringArray(request.AnimationColors) == null)
|
|
|
|
{
|
|
|
|
ModelState.AddModelError(nameof(request.AnimationColors), "Animation colors must be a non-empty array of non-empty strings");
|
|
|
|
}
|
|
|
|
|
2024-06-26 10:42:22 +02:00
|
|
|
if (request is { StartDate: not null, EndDate: not null } && DateTimeOffset.Compare((DateTimeOffset)request.StartDate, (DateTimeOffset)request.EndDate!) > 0)
|
2022-11-17 21:20:07 -08:00
|
|
|
{
|
|
|
|
ModelState.AddModelError(nameof(request.EndDate), "End date cannot be before start date");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-26 10:42:22 +02:00
|
|
|
private void ValidateAppRequest(IAppRequest? request)
|
2022-05-01 22:28:27 -07:00
|
|
|
{
|
2024-06-26 10:42:22 +02:00
|
|
|
if (string.IsNullOrEmpty(request?.AppName))
|
2022-02-26 21:22:29 -08:00
|
|
|
{
|
|
|
|
ModelState.AddModelError(nameof(request.AppName), "App name is missing");
|
|
|
|
}
|
2024-06-26 10:42:22 +02:00
|
|
|
else if (request.AppName.Length is < 1 or > 50)
|
2022-02-26 21:22:29 -08:00
|
|
|
{
|
2024-06-26 10:42:22 +02:00
|
|
|
ModelState.AddModelError(nameof(request.AppName), "App name can only be between 1 and 50 characters");
|
2022-02-26 21:22:29 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|