mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-01-18 13:26:47 +01:00
Greenfield: Refactor app endpoints (#6051)
* Greenfield: Refactor app endpoints - Do not change unset data - Clean up difference between request (template) and data (items/perks) - Add missing properties (form id and custom tip percentage) - Update docs * Revert ToSettings changes in GreenfieldAppsController
This commit is contained in:
parent
bf66b54c9a
commit
2482b9df74
@ -9,41 +9,41 @@ namespace BTCPayServer.Client;
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<PointOfSaleAppData> CreatePointOfSaleApp(string storeId,
|
||||
CreatePointOfSaleAppRequest request, CancellationToken token = default)
|
||||
PointOfSaleAppRequest request, CancellationToken token = default)
|
||||
{
|
||||
if (request == null) throw new ArgumentNullException(nameof(request));
|
||||
return await SendHttpRequest<PointOfSaleAppData>($"api/v1/stores/{storeId}/apps/pos", request, HttpMethod.Post, token);
|
||||
}
|
||||
|
||||
public virtual async Task<CrowdfundAppData> CreateCrowdfundApp(string storeId,
|
||||
CreateCrowdfundAppRequest request, CancellationToken token = default)
|
||||
CrowdfundAppRequest request, CancellationToken token = default)
|
||||
{
|
||||
if (request == null) throw new ArgumentNullException(nameof(request));
|
||||
return await SendHttpRequest<CrowdfundAppData>($"api/v1/stores/{storeId}/apps/crowdfund", request, HttpMethod.Post, token);
|
||||
}
|
||||
|
||||
public virtual async Task<PointOfSaleAppData> UpdatePointOfSaleApp(string appId,
|
||||
CreatePointOfSaleAppRequest request, CancellationToken token = default)
|
||||
PointOfSaleAppRequest request, CancellationToken token = default)
|
||||
{
|
||||
if (request == null) throw new ArgumentNullException(nameof(request));
|
||||
return await SendHttpRequest<PointOfSaleAppData>($"api/v1/apps/pos/{appId}", request, HttpMethod.Put, token);
|
||||
}
|
||||
|
||||
public virtual async Task<AppDataBase> GetApp(string appId, CancellationToken token = default)
|
||||
public virtual async Task<AppBaseData> GetApp(string appId, CancellationToken token = default)
|
||||
{
|
||||
if (appId == null) throw new ArgumentNullException(nameof(appId));
|
||||
return await SendHttpRequest<AppDataBase>($"api/v1/apps/{appId}", null, HttpMethod.Get, token);
|
||||
return await SendHttpRequest<AppBaseData>($"api/v1/apps/{appId}", null, HttpMethod.Get, token);
|
||||
}
|
||||
|
||||
public virtual async Task<AppDataBase[]> GetAllApps(string storeId, CancellationToken token = default)
|
||||
public virtual async Task<AppBaseData[]> GetAllApps(string storeId, CancellationToken token = default)
|
||||
{
|
||||
if (storeId == null) throw new ArgumentNullException(nameof(storeId));
|
||||
return await SendHttpRequest<AppDataBase[]>($"api/v1/stores/{storeId}/apps", null, HttpMethod.Get, token);
|
||||
return await SendHttpRequest<AppBaseData[]>($"api/v1/stores/{storeId}/apps", null, HttpMethod.Get, token);
|
||||
}
|
||||
|
||||
public virtual async Task<AppDataBase[]> GetAllApps(CancellationToken token = default)
|
||||
public virtual async Task<AppBaseData[]> GetAllApps(CancellationToken token = default)
|
||||
{
|
||||
return await SendHttpRequest<AppDataBase[]>("api/v1/apps", null, HttpMethod.Get, token);
|
||||
return await SendHttpRequest<AppBaseData[]>("api/v1/apps", null, HttpMethod.Get, token);
|
||||
}
|
||||
|
||||
public virtual async Task<PointOfSaleAppData> GetPosApp(string appId, CancellationToken token = default)
|
||||
|
21
BTCPayServer.Client/Models/AppBaseData.cs
Normal file
21
BTCPayServer.Client/Models/AppBaseData.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Client.Models;
|
||||
|
||||
public class AppBaseData
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string AppType { get; set; }
|
||||
public string AppName { get; set; }
|
||||
public string StoreId { get; set; }
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? Archived { get; set; }
|
||||
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
||||
public DateTimeOffset Created { get; set; }
|
||||
}
|
||||
|
||||
public interface IAppRequest
|
||||
{
|
||||
public string AppName { get; set; }
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public enum PosViewType
|
||||
{
|
||||
Static,
|
||||
Cart,
|
||||
Light,
|
||||
Print
|
||||
}
|
||||
|
||||
public class CreateAppRequest
|
||||
{
|
||||
public string AppName { get; set; }
|
||||
public string AppType { get; set; }
|
||||
}
|
||||
|
||||
public class CreatePointOfSaleAppRequest : CreateAppRequest
|
||||
{
|
||||
public string Currency { get; set; } = null;
|
||||
public string Title { get; set; } = null;
|
||||
public string Description { get; set; } = null;
|
||||
public string Template { get; set; } = null;
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public PosViewType DefaultView { get; set; }
|
||||
public bool ShowItems { get; set; } = false;
|
||||
public bool ShowCustomAmount { get; set; } = false;
|
||||
public bool ShowDiscount { get; set; } = false;
|
||||
public bool ShowSearch { get; set; } = true;
|
||||
public bool ShowCategories { get; set; } = true;
|
||||
public bool EnableTips { get; set; } = false;
|
||||
public string CustomAmountPayButtonText { get; set; } = null;
|
||||
public string FixedAmountPayButtonText { get; set; } = null;
|
||||
public string TipText { get; set; } = null;
|
||||
public string NotificationUrl { get; set; } = null;
|
||||
public string RedirectUrl { get; set; } = null;
|
||||
public bool? RedirectAutomatically { get; set; } = null;
|
||||
public bool? Archived { get; set; } = null;
|
||||
public string FormId { get; set; } = null;
|
||||
}
|
||||
|
||||
public enum CrowdfundResetEvery
|
||||
{
|
||||
Never,
|
||||
Hour,
|
||||
Day,
|
||||
Month,
|
||||
Year
|
||||
}
|
||||
|
||||
public class CreateCrowdfundAppRequest : CreateAppRequest
|
||||
{
|
||||
public string Title { get; set; } = null;
|
||||
public bool? Enabled { get; set; } = null;
|
||||
public bool? EnforceTargetAmount { get; set; } = null;
|
||||
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
||||
public DateTimeOffset? StartDate { get; set; } = null;
|
||||
public string TargetCurrency { get; set; } = null;
|
||||
public string Description { get; set; } = null;
|
||||
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
||||
public DateTimeOffset? EndDate { get; set; } = null;
|
||||
public decimal? TargetAmount { get; set; } = null;
|
||||
public string MainImageUrl { get; set; } = null;
|
||||
public string NotificationUrl { get; set; } = null;
|
||||
public string Tagline { get; set; } = null;
|
||||
public string PerksTemplate { get; set; } = null;
|
||||
public bool? SoundsEnabled { get; set; } = null;
|
||||
public string DisqusShortname { get; set; } = null;
|
||||
public bool? AnimationsEnabled { get; set; } = null;
|
||||
public int? ResetEveryAmount { get; set; } = null;
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public CrowdfundResetEvery ResetEvery { get; set; } = CrowdfundResetEvery.Never;
|
||||
public bool? DisplayPerksValue { get; set; } = null;
|
||||
public bool? DisplayPerksRanking { get; set; } = null;
|
||||
public bool? SortPerksByPopularity { get; set; } = null;
|
||||
public bool? Archived { get; set; } = null;
|
||||
public string[] Sounds { get; set; } = null;
|
||||
public string[] AnimationColors { get; set; } = null;
|
||||
}
|
||||
}
|
55
BTCPayServer.Client/Models/CrowdfundAppData.cs
Normal file
55
BTCPayServer.Client/Models/CrowdfundAppData.cs
Normal file
@ -0,0 +1,55 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace BTCPayServer.Client.Models;
|
||||
|
||||
public abstract class CrowdfundBaseData : AppBaseData
|
||||
{
|
||||
public string? Title { get; set; }
|
||||
public bool? Enabled { get; set; }
|
||||
public bool? EnforceTargetAmount { get; set; }
|
||||
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
||||
public DateTimeOffset? StartDate { get; set; }
|
||||
public string? TargetCurrency { get; set; }
|
||||
public string? Description { get; set; }
|
||||
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
||||
public DateTimeOffset? EndDate { get; set; }
|
||||
public decimal? TargetAmount { get; set; }
|
||||
public string? MainImageUrl { get; set; }
|
||||
public string? NotificationUrl { get; set; }
|
||||
public string? Tagline { get; set; }
|
||||
public bool? DisqusEnabled { get; set; }
|
||||
public string? DisqusShortname { get; set; }
|
||||
public bool? SoundsEnabled { get; set; }
|
||||
public bool? AnimationsEnabled { get; set; }
|
||||
public int? ResetEveryAmount { get; set; }
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public CrowdfundResetEvery? ResetEvery { get; set; }
|
||||
public bool? DisplayPerksValue { get; set; }
|
||||
public bool? DisplayPerksRanking { get; set; }
|
||||
public bool? SortPerksByPopularity { get; set; }
|
||||
public string[]? Sounds { get; set; }
|
||||
public string[]? AnimationColors { get; set; }
|
||||
public string? FormId { get; set; }
|
||||
}
|
||||
|
||||
public class CrowdfundAppData : CrowdfundBaseData
|
||||
{
|
||||
public object? Perks { get; set; }
|
||||
}
|
||||
|
||||
public class CrowdfundAppRequest : CrowdfundBaseData, IAppRequest
|
||||
{
|
||||
public string? PerksTemplate { get; set; }
|
||||
}
|
||||
|
||||
public enum CrowdfundResetEvery
|
||||
{
|
||||
Never,
|
||||
Hour,
|
||||
Day,
|
||||
Month,
|
||||
Year
|
||||
}
|
@ -1,67 +1,46 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
namespace BTCPayServer.Client.Models;
|
||||
|
||||
public abstract class PointOfSaleBaseData : AppBaseData
|
||||
{
|
||||
public class AppDataBase
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string AppType { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string StoreId { get; set; }
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? Archived { get; set; }
|
||||
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
||||
public DateTimeOffset Created { get; set; }
|
||||
}
|
||||
|
||||
public class PointOfSaleAppData : AppDataBase
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string DefaultView { get; set; }
|
||||
public bool ShowItems { get; set; }
|
||||
public bool ShowCustomAmount { get; set; }
|
||||
public bool ShowDiscount { get; set; }
|
||||
public bool ShowSearch { get; set; }
|
||||
public bool ShowCategories { get; set; }
|
||||
public bool EnableTips { get; set; }
|
||||
public string Currency { get; set; }
|
||||
public object Items { get; set; }
|
||||
public string FixedAmountPayButtonText { get; set; }
|
||||
public string CustomAmountPayButtonText { get; set; }
|
||||
public string TipText { get; set; }
|
||||
public string NotificationUrl { get; set; }
|
||||
public string RedirectUrl { get; set; }
|
||||
public string Description { get; set; }
|
||||
public bool? RedirectAutomatically { get; set; }
|
||||
}
|
||||
|
||||
public class CrowdfundAppData : AppDataBase
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
public bool EnforceTargetAmount { get; set; }
|
||||
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
||||
public DateTimeOffset? StartDate { get; set; }
|
||||
public string TargetCurrency { get; set; }
|
||||
public string Description { get; set; }
|
||||
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
||||
public DateTimeOffset? EndDate { get; set; }
|
||||
public decimal? TargetAmount { get; set; }
|
||||
public string MainImageUrl { get; set; }
|
||||
public string NotificationUrl { get; set; }
|
||||
public string Tagline { get; set; }
|
||||
public object Perks { get; set; }
|
||||
public bool DisqusEnabled { get; set; }
|
||||
public string DisqusShortname { get; set; }
|
||||
public bool SoundsEnabled { get; set; }
|
||||
public bool AnimationsEnabled { get; set; }
|
||||
public int ResetEveryAmount { get; set; }
|
||||
public string ResetEvery { get; set; }
|
||||
public bool DisplayPerksValue { get; set; }
|
||||
public bool DisplayPerksRanking { get; set; }
|
||||
public bool SortPerksByPopularity { get; set; }
|
||||
public string[] Sounds { get; set; }
|
||||
public string[] AnimationColors { get; set; }
|
||||
}
|
||||
public string? Title { get; set; }
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public PosViewType? DefaultView { get; set; }
|
||||
public bool? ShowItems { get; set; }
|
||||
public bool? ShowCustomAmount { get; set; }
|
||||
public bool? ShowDiscount { get; set; }
|
||||
public bool? ShowSearch { get; set; }
|
||||
public bool? ShowCategories { get; set; }
|
||||
public bool? EnableTips { get; set; }
|
||||
public string? Currency { get; set; }
|
||||
public string? FixedAmountPayButtonText { get; set; }
|
||||
public string? CustomAmountPayButtonText { get; set; }
|
||||
public string? TipText { get; set; }
|
||||
public string? NotificationUrl { get; set; }
|
||||
public string? RedirectUrl { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public bool? RedirectAutomatically { get; set; }
|
||||
public int[]? CustomTipPercentages { get; set; }
|
||||
public string? FormId { get; set; }
|
||||
}
|
||||
|
||||
public class PointOfSaleAppData : PointOfSaleBaseData
|
||||
{
|
||||
public object? Items { get; set; }
|
||||
}
|
||||
|
||||
public class PointOfSaleAppRequest : PointOfSaleBaseData, IAppRequest
|
||||
{
|
||||
public string? Template { get; set; }
|
||||
}
|
||||
|
||||
public enum PosViewType
|
||||
{
|
||||
Static,
|
||||
Cart,
|
||||
Light,
|
||||
Print
|
||||
}
|
||||
|
@ -253,11 +253,11 @@ namespace BTCPayServer.Tests
|
||||
|
||||
// Test validation for creating the app
|
||||
await AssertValidationError(new[] { "AppName" },
|
||||
async () => await client.CreatePointOfSaleApp(user.StoreId, new CreatePointOfSaleAppRequest() { }));
|
||||
async () => await client.CreatePointOfSaleApp(user.StoreId, new PointOfSaleAppRequest()));
|
||||
await AssertValidationError(new[] { "AppName" },
|
||||
async () => await client.CreatePointOfSaleApp(
|
||||
user.StoreId,
|
||||
new CreatePointOfSaleAppRequest()
|
||||
new PointOfSaleAppRequest
|
||||
{
|
||||
AppName = "this is a really long app name this is a really long app name this is a really long app name",
|
||||
}
|
||||
@ -266,7 +266,7 @@ namespace BTCPayServer.Tests
|
||||
await AssertValidationError(new[] { "Currency" },
|
||||
async () => await client.CreatePointOfSaleApp(
|
||||
user.StoreId,
|
||||
new CreatePointOfSaleAppRequest()
|
||||
new PointOfSaleAppRequest
|
||||
{
|
||||
AppName = "good name",
|
||||
Currency = "fake currency"
|
||||
@ -276,7 +276,7 @@ namespace BTCPayServer.Tests
|
||||
await AssertValidationError(new[] { "Template" },
|
||||
async () => await client.CreatePointOfSaleApp(
|
||||
user.StoreId,
|
||||
new CreatePointOfSaleAppRequest()
|
||||
new PointOfSaleAppRequest
|
||||
{
|
||||
AppName = "good name",
|
||||
Template = "lol invalid template"
|
||||
@ -286,7 +286,7 @@ namespace BTCPayServer.Tests
|
||||
await AssertValidationError(new[] { "AppName", "Currency", "Template" },
|
||||
async () => await client.CreatePointOfSaleApp(
|
||||
user.StoreId,
|
||||
new CreatePointOfSaleAppRequest()
|
||||
new PointOfSaleAppRequest
|
||||
{
|
||||
Currency = "fake currency",
|
||||
Template = "lol invalid template"
|
||||
@ -297,14 +297,14 @@ namespace BTCPayServer.Tests
|
||||
// Test creating a POS app successfully
|
||||
var app = await client.CreatePointOfSaleApp(
|
||||
user.StoreId,
|
||||
new CreatePointOfSaleAppRequest()
|
||||
new PointOfSaleAppRequest
|
||||
{
|
||||
AppName = "test app from API",
|
||||
Currency = "JPY",
|
||||
Title = "test app title"
|
||||
}
|
||||
);
|
||||
Assert.Equal("test app from API", app.Name);
|
||||
Assert.Equal("test app from API", app.AppName);
|
||||
Assert.Equal(user.StoreId, app.StoreId);
|
||||
Assert.Equal("PointOfSale", app.AppType);
|
||||
Assert.Equal("test app title", app.Title);
|
||||
@ -313,12 +313,19 @@ namespace BTCPayServer.Tests
|
||||
// Test title falls back to name
|
||||
app = await client.CreatePointOfSaleApp(
|
||||
user.StoreId,
|
||||
new CreatePointOfSaleAppRequest
|
||||
new PointOfSaleAppRequest
|
||||
{
|
||||
AppName = "test app name"
|
||||
AppName = "test app name",
|
||||
Description = "test description",
|
||||
ShowItems = true,
|
||||
ShowCategories = false
|
||||
}
|
||||
);
|
||||
Assert.Equal("test app name", app.Title);
|
||||
Assert.Equal("test description", app.Description);
|
||||
Assert.True(app.ShowItems);
|
||||
Assert.False(app.ShowCategories);
|
||||
Assert.False(app.ShowDiscount);
|
||||
|
||||
// Make sure we return a 404 if we try to get an app that doesn't exist
|
||||
await AssertHttpError(404, async () =>
|
||||
@ -332,30 +339,33 @@ namespace BTCPayServer.Tests
|
||||
|
||||
// Test that we can retrieve the app data
|
||||
var retrievedApp = await client.GetApp(app.Id);
|
||||
Assert.Equal(app.Name, retrievedApp.Name);
|
||||
Assert.Equal(app.AppName, retrievedApp.AppName);
|
||||
Assert.Equal(app.StoreId, retrievedApp.StoreId);
|
||||
Assert.Equal(app.AppType, retrievedApp.AppType);
|
||||
|
||||
// Test that we can update the app data
|
||||
await client.UpdatePointOfSaleApp(
|
||||
var retrievedPosApp = await client.UpdatePointOfSaleApp(
|
||||
app.Id,
|
||||
new CreatePointOfSaleAppRequest()
|
||||
new PointOfSaleAppRequest
|
||||
{
|
||||
AppName = "new app name",
|
||||
Title = "new app title",
|
||||
Archived = true
|
||||
}
|
||||
);
|
||||
Assert.Equal("new app name", retrievedPosApp.AppName);
|
||||
Assert.Equal("new app title", retrievedPosApp.Title);
|
||||
Assert.True(retrievedPosApp.Archived);
|
||||
|
||||
// Test generic GET app endpoint first
|
||||
retrievedApp = await client.GetApp(app.Id);
|
||||
Assert.Equal("new app name", retrievedApp.Name);
|
||||
Assert.Equal("new app name", retrievedApp.AppName);
|
||||
Assert.True(retrievedApp.Archived);
|
||||
|
||||
// Test the POS-specific endpoint also
|
||||
var retrievedPosApp = await client.GetPosApp(app.Id);
|
||||
Assert.Equal("new app name", retrievedPosApp.Name);
|
||||
retrievedPosApp = await client.GetPosApp(app.Id);
|
||||
Assert.Equal("new app name", retrievedPosApp.AppName);
|
||||
Assert.Equal("new app title", retrievedPosApp.Title);
|
||||
Assert.True(retrievedPosApp.Archived);
|
||||
|
||||
// Make sure we return a 404 if we try to delete an app that doesn't exist
|
||||
await AssertHttpError(404, async () =>
|
||||
@ -383,11 +393,11 @@ namespace BTCPayServer.Tests
|
||||
|
||||
// Test validation for creating the app
|
||||
await AssertValidationError(new[] { "AppName" },
|
||||
async () => await client.CreateCrowdfundApp(user.StoreId, new CreateCrowdfundAppRequest() { }));
|
||||
async () => await client.CreateCrowdfundApp(user.StoreId, new CrowdfundAppRequest()));
|
||||
await AssertValidationError(new[] { "AppName" },
|
||||
async () => await client.CreateCrowdfundApp(
|
||||
user.StoreId,
|
||||
new CreateCrowdfundAppRequest()
|
||||
new CrowdfundAppRequest
|
||||
{
|
||||
AppName = "this is a really long app name this is a really long app name this is a really long app name",
|
||||
}
|
||||
@ -396,7 +406,7 @@ namespace BTCPayServer.Tests
|
||||
await AssertValidationError(new[] { "TargetCurrency" },
|
||||
async () => await client.CreateCrowdfundApp(
|
||||
user.StoreId,
|
||||
new CreateCrowdfundAppRequest()
|
||||
new CrowdfundAppRequest
|
||||
{
|
||||
AppName = "good name",
|
||||
TargetCurrency = "fake currency"
|
||||
@ -406,7 +416,7 @@ namespace BTCPayServer.Tests
|
||||
await AssertValidationError(new[] { "PerksTemplate" },
|
||||
async () => await client.CreateCrowdfundApp(
|
||||
user.StoreId,
|
||||
new CreateCrowdfundAppRequest()
|
||||
new CrowdfundAppRequest
|
||||
{
|
||||
AppName = "good name",
|
||||
PerksTemplate = "lol invalid template"
|
||||
@ -416,7 +426,7 @@ namespace BTCPayServer.Tests
|
||||
await AssertValidationError(new[] { "AppName", "TargetCurrency", "PerksTemplate" },
|
||||
async () => await client.CreateCrowdfundApp(
|
||||
user.StoreId,
|
||||
new CreateCrowdfundAppRequest()
|
||||
new CrowdfundAppRequest
|
||||
{
|
||||
TargetCurrency = "fake currency",
|
||||
PerksTemplate = "lol invalid template"
|
||||
@ -426,7 +436,7 @@ namespace BTCPayServer.Tests
|
||||
await AssertValidationError(new[] { "AnimationColors" },
|
||||
async () => await client.CreateCrowdfundApp(
|
||||
user.StoreId,
|
||||
new CreateCrowdfundAppRequest()
|
||||
new CrowdfundAppRequest
|
||||
{
|
||||
AppName = "good name",
|
||||
AnimationColors = new string[] { }
|
||||
@ -436,7 +446,7 @@ namespace BTCPayServer.Tests
|
||||
await AssertValidationError(new[] { "AnimationColors" },
|
||||
async () => await client.CreateCrowdfundApp(
|
||||
user.StoreId,
|
||||
new CreateCrowdfundAppRequest()
|
||||
new CrowdfundAppRequest
|
||||
{
|
||||
AppName = "good name",
|
||||
AnimationColors = new string[] { " ", " " }
|
||||
@ -446,7 +456,7 @@ namespace BTCPayServer.Tests
|
||||
await AssertValidationError(new[] { "Sounds" },
|
||||
async () => await client.CreateCrowdfundApp(
|
||||
user.StoreId,
|
||||
new CreateCrowdfundAppRequest()
|
||||
new CrowdfundAppRequest
|
||||
{
|
||||
AppName = "good name",
|
||||
Sounds = new string[] { " " }
|
||||
@ -456,7 +466,7 @@ namespace BTCPayServer.Tests
|
||||
await AssertValidationError(new[] { "Sounds" },
|
||||
async () => await client.CreateCrowdfundApp(
|
||||
user.StoreId,
|
||||
new CreateCrowdfundAppRequest()
|
||||
new CrowdfundAppRequest
|
||||
{
|
||||
AppName = "good name",
|
||||
Sounds = new string[] { " ", " ", " " }
|
||||
@ -466,7 +476,7 @@ namespace BTCPayServer.Tests
|
||||
await AssertValidationError(new[] { "EndDate" },
|
||||
async () => await client.CreateCrowdfundApp(
|
||||
user.StoreId,
|
||||
new CreateCrowdfundAppRequest()
|
||||
new CrowdfundAppRequest
|
||||
{
|
||||
AppName = "good name",
|
||||
StartDate = DateTime.Parse("1998-01-01"),
|
||||
@ -478,13 +488,13 @@ namespace BTCPayServer.Tests
|
||||
// Test creating a crowdfund app
|
||||
var app = await client.CreateCrowdfundApp(
|
||||
user.StoreId,
|
||||
new CreateCrowdfundAppRequest()
|
||||
new CrowdfundAppRequest
|
||||
{
|
||||
AppName = "test app from API",
|
||||
Title = "test app title"
|
||||
}
|
||||
);
|
||||
Assert.Equal("test app from API", app.Name);
|
||||
Assert.Equal("test app from API", app.AppName);
|
||||
Assert.Equal(user.StoreId, app.StoreId);
|
||||
Assert.Equal("Crowdfund", app.AppType);
|
||||
Assert.False(app.Archived);
|
||||
@ -492,9 +502,10 @@ namespace BTCPayServer.Tests
|
||||
// Test title falls back to name
|
||||
app = await client.CreateCrowdfundApp(
|
||||
user.StoreId,
|
||||
new CreateCrowdfundAppRequest
|
||||
new CrowdfundAppRequest
|
||||
{
|
||||
AppName = "test app name"
|
||||
AppName = "test app name",
|
||||
Description = "test description"
|
||||
}
|
||||
);
|
||||
Assert.Equal("test app name", app.Title);
|
||||
@ -511,15 +522,16 @@ namespace BTCPayServer.Tests
|
||||
|
||||
// Test that we can retrieve the app data
|
||||
var retrievedApp = await client.GetApp(app.Id);
|
||||
Assert.Equal(app.Name, retrievedApp.Name);
|
||||
Assert.Equal(app.AppName, retrievedApp.AppName);
|
||||
Assert.Equal(app.StoreId, retrievedApp.StoreId);
|
||||
Assert.Equal(app.AppType, retrievedApp.AppType);
|
||||
Assert.False(retrievedApp.Archived);
|
||||
|
||||
// Test the crowdfund-specific endpoint also
|
||||
var retrievedCfApp = await client.GetCrowdfundApp(app.Id);
|
||||
Assert.Equal(app.Name, retrievedCfApp.Name);
|
||||
Assert.Equal(app.AppName, retrievedCfApp.AppName);
|
||||
Assert.Equal(app.Title, retrievedCfApp.Title);
|
||||
Assert.Equal("test description", retrievedCfApp.Description);
|
||||
Assert.False(retrievedCfApp.Archived);
|
||||
|
||||
// Make sure we return a 404 if we try to delete an app that doesn't exist
|
||||
@ -548,17 +560,17 @@ namespace BTCPayServer.Tests
|
||||
|
||||
var posApp = await client.CreatePointOfSaleApp(
|
||||
user.StoreId,
|
||||
new CreatePointOfSaleAppRequest()
|
||||
new PointOfSaleAppRequest
|
||||
{
|
||||
AppName = "test app from API",
|
||||
Currency = "JPY"
|
||||
}
|
||||
);
|
||||
var crowdfundApp = await client.CreateCrowdfundApp(user.StoreId, new CreateCrowdfundAppRequest() { AppName = "test app from API" });
|
||||
var crowdfundApp = await client.CreateCrowdfundApp(user.StoreId, new CrowdfundAppRequest { AppName = "test app from API" });
|
||||
|
||||
// Create another store and one app on it so we can get all apps from all stores for the user below
|
||||
var newStore = await client.CreateStore(new CreateStoreRequest() { Name = "A" });
|
||||
var newApp = await client.CreateCrowdfundApp(newStore.Id, new CreateCrowdfundAppRequest() { AppName = "new app" });
|
||||
var newStore = await client.CreateStore(new CreateStoreRequest { Name = "A" });
|
||||
var newApp = await client.CreateCrowdfundApp(newStore.Id, new CrowdfundAppRequest { AppName = "new app" });
|
||||
|
||||
Assert.NotEqual(newApp.Id, user.StoreId);
|
||||
|
||||
@ -567,12 +579,12 @@ namespace BTCPayServer.Tests
|
||||
|
||||
Assert.Equal(2, apps.Length);
|
||||
|
||||
Assert.Equal(posApp.Name, apps[0].Name);
|
||||
Assert.Equal(posApp.AppName, apps[0].AppName);
|
||||
Assert.Equal(posApp.StoreId, apps[0].StoreId);
|
||||
Assert.Equal(posApp.AppType, apps[0].AppType);
|
||||
Assert.False(apps[0].Archived);
|
||||
|
||||
Assert.Equal(crowdfundApp.Name, apps[1].Name);
|
||||
Assert.Equal(crowdfundApp.AppName, apps[1].AppName);
|
||||
Assert.Equal(crowdfundApp.StoreId, apps[1].StoreId);
|
||||
Assert.Equal(crowdfundApp.AppType, apps[1].AppType);
|
||||
Assert.False(apps[1].Archived);
|
||||
@ -582,17 +594,17 @@ namespace BTCPayServer.Tests
|
||||
|
||||
Assert.Equal(3, apps.Length);
|
||||
|
||||
Assert.Equal(posApp.Name, apps[0].Name);
|
||||
Assert.Equal(posApp.AppName, apps[0].AppName);
|
||||
Assert.Equal(posApp.StoreId, apps[0].StoreId);
|
||||
Assert.Equal(posApp.AppType, apps[0].AppType);
|
||||
Assert.False(apps[0].Archived);
|
||||
|
||||
Assert.Equal(crowdfundApp.Name, apps[1].Name);
|
||||
Assert.Equal(crowdfundApp.AppName, apps[1].AppName);
|
||||
Assert.Equal(crowdfundApp.StoreId, apps[1].StoreId);
|
||||
Assert.Equal(crowdfundApp.AppType, apps[1].AppType);
|
||||
Assert.False(apps[1].Archived);
|
||||
|
||||
Assert.Equal(newApp.Name, apps[2].Name);
|
||||
Assert.Equal(newApp.AppName, apps[2].AppName);
|
||||
Assert.Equal(newApp.StoreId, apps[2].StoreId);
|
||||
Assert.Equal(newApp.AppType, apps[2].AppType);
|
||||
Assert.False(apps[2].Archived);
|
||||
|
@ -80,6 +80,7 @@ using MarkPayoutRequest = BTCPayServer.Client.Models.MarkPayoutRequest;
|
||||
using PaymentRequestData = BTCPayServer.Client.Models.PaymentRequestData;
|
||||
using RatesViewModel = BTCPayServer.Models.StoreViewModels.RatesViewModel;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using PosViewType = BTCPayServer.Client.Models.PosViewType;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
{
|
||||
@ -3109,20 +3110,20 @@ namespace BTCPayServer.Tests
|
||||
var client = await acc.CreateClient();
|
||||
var posController = acc.GetController<UIPointOfSaleController>();
|
||||
|
||||
var app = await client.CreatePointOfSaleApp(acc.StoreId, new CreatePointOfSaleAppRequest()
|
||||
var app = await client.CreatePointOfSaleApp(acc.StoreId, new PointOfSaleAppRequest
|
||||
{
|
||||
AppName = "Static",
|
||||
DefaultView = Client.Models.PosViewType.Static,
|
||||
DefaultView = PosViewType.Static,
|
||||
Template = new PointOfSaleSettings().Template
|
||||
});
|
||||
var resp = await posController.ViewPointOfSale(app.Id, choiceKey: "green-tea");
|
||||
var invoiceId = GetInvoiceId(resp);
|
||||
await acc.PayOnChain(invoiceId);
|
||||
|
||||
app = await client.CreatePointOfSaleApp(acc.StoreId, new CreatePointOfSaleAppRequest()
|
||||
app = await client.CreatePointOfSaleApp(acc.StoreId, new PointOfSaleAppRequest
|
||||
{
|
||||
AppName = "Cart",
|
||||
DefaultView = Client.Models.PosViewType.Cart,
|
||||
DefaultView = PosViewType.Cart,
|
||||
Template = new PointOfSaleSettings().Template
|
||||
});
|
||||
resp = await posController.ViewPointOfSale(app.Id, posData: new JObject()
|
||||
|
@ -17,7 +17,8 @@ using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using PosViewType = BTCPayServer.Plugins.PointOfSale.PosViewType;
|
||||
using CrowdfundResetEvery = BTCPayServer.Client.Models.CrowdfundResetEvery;
|
||||
using PosViewType = BTCPayServer.Client.Models.PosViewType;
|
||||
|
||||
namespace BTCPayServer.Controllers.Greenfield
|
||||
{
|
||||
@ -47,19 +48,20 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
|
||||
[HttpPost("~/api/v1/stores/{storeId}/apps/crowdfund")]
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
public async Task<IActionResult> CreateCrowdfundApp(string storeId, CreateCrowdfundAppRequest request)
|
||||
public async Task<IActionResult> CreateCrowdfundApp(string storeId, CrowdfundAppRequest request)
|
||||
{
|
||||
var store = await _storeRepository.FindStore(storeId);
|
||||
if (store == null)
|
||||
return this.CreateAPIError(404, "store-not-found", "The store was not found");
|
||||
|
||||
// This is not obvious but we must have a non-null currency or else request validation may work incorrectly
|
||||
request.TargetCurrency = request.TargetCurrency ?? store.GetStoreBlob().DefaultCurrency;
|
||||
// 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;
|
||||
|
||||
var validationResult = ValidateCrowdfundAppRequest(request);
|
||||
if (validationResult != null)
|
||||
ValidateAppRequest(request);
|
||||
ValidateCrowdfundAppRequest(request);
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return validationResult;
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
|
||||
var appData = new AppData
|
||||
@ -70,7 +72,8 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
Archived = request.Archived ?? false
|
||||
};
|
||||
|
||||
appData.SetSettings(ToCrowdfundSettings(request));
|
||||
var settings = ToCrowdfundSettings(request, new CrowdfundSettings { Title = request.Title ?? request.AppName });
|
||||
appData.SetSettings(settings);
|
||||
|
||||
await _appService.UpdateOrCreateApp(appData);
|
||||
|
||||
@ -79,19 +82,20 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
|
||||
[HttpPost("~/api/v1/stores/{storeId}/apps/pos")]
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
public async Task<IActionResult> CreatePointOfSaleApp(string storeId, CreatePointOfSaleAppRequest request)
|
||||
public async Task<IActionResult> CreatePointOfSaleApp(string storeId, PointOfSaleAppRequest request)
|
||||
{
|
||||
var store = await _storeRepository.FindStore(storeId);
|
||||
if (store == null)
|
||||
return this.CreateAPIError(404, "store-not-found", "The store was not found");
|
||||
|
||||
// This is not obvious but we must have a non-null currency or else request validation may work incorrectly
|
||||
request.Currency = request.Currency ?? store.GetStoreBlob().DefaultCurrency;
|
||||
// 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;
|
||||
|
||||
var validationResult = ValidatePOSAppRequest(request);
|
||||
if (validationResult != null)
|
||||
ValidateAppRequest(request);
|
||||
ValidatePOSAppRequest(request);
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return validationResult;
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
|
||||
var appData = new AppData
|
||||
@ -102,7 +106,8 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
Archived = request.Archived ?? false
|
||||
};
|
||||
|
||||
appData.SetSettings(ToPointOfSaleSettings(request));
|
||||
var settings = ToPointOfSaleSettings(request, new PointOfSaleSettings { Title = request.Title ?? request.AppName });
|
||||
appData.SetSettings(settings);
|
||||
|
||||
await _appService.UpdateOrCreateApp(appData);
|
||||
|
||||
@ -111,7 +116,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
|
||||
[HttpPut("~/api/v1/apps/pos/{appId}")]
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
public async Task<IActionResult> UpdatePointOfSaleApp(string appId, CreatePointOfSaleAppRequest request)
|
||||
public async Task<IActionResult> UpdatePointOfSaleApp(string appId, PointOfSaleAppRequest request)
|
||||
{
|
||||
var app = await _appService.GetApp(appId, PointOfSaleAppType.AppType, includeArchived: true);
|
||||
if (app == null)
|
||||
@ -121,21 +126,28 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
|
||||
var settings = app.GetSettings<PointOfSaleSettings>();
|
||||
|
||||
// This is not obvious but we must have a non-null currency or else request validation may work incorrectly
|
||||
request.Currency = request.Currency ?? settings.Currency;
|
||||
// This is not obvious, but we must have a non-null currency or else request validation may not work correctly
|
||||
request.Currency ??= settings.Currency;
|
||||
|
||||
var validationResult = ValidatePOSAppRequest(request);
|
||||
if (validationResult != null)
|
||||
ValidatePOSAppRequest(request);
|
||||
if (!string.IsNullOrEmpty(request.AppName))
|
||||
{
|
||||
return validationResult;
|
||||
ValidateAppRequest(request);
|
||||
}
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
|
||||
app.Name = request.AppName;
|
||||
if (!string.IsNullOrEmpty(request.AppName))
|
||||
{
|
||||
app.Name = request.AppName;
|
||||
}
|
||||
if (request.Archived != null)
|
||||
{
|
||||
app.Archived = request.Archived.Value;
|
||||
}
|
||||
app.SetSettings(ToPointOfSaleSettings(request));
|
||||
app.SetSettings(ToPointOfSaleSettings(request, settings));
|
||||
|
||||
await _appService.UpdateOrCreateApp(app);
|
||||
|
||||
@ -218,11 +230,12 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
return this.CreateAPIError(404, "app-not-found", "The app with specified ID was not found");
|
||||
}
|
||||
|
||||
private CrowdfundSettings ToCrowdfundSettings(CreateCrowdfundAppRequest request)
|
||||
private CrowdfundSettings ToCrowdfundSettings(CrowdfundAppRequest request, CrowdfundSettings settings)
|
||||
{
|
||||
var parsedSounds = ValidateStringArray(request.Sounds);
|
||||
var parsedColors = ValidateStringArray(request.AnimationColors);
|
||||
|
||||
Enum.TryParse<BTCPayServer.Services.Apps.CrowdfundResetEvery>(request.ResetEvery.ToString(), true, out var resetEvery);
|
||||
|
||||
return new CrowdfundSettings
|
||||
{
|
||||
Title = request.Title?.Trim() ?? request.AppName,
|
||||
@ -244,32 +257,36 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
SoundsEnabled = request.SoundsEnabled ?? parsedSounds != null,
|
||||
AnimationsEnabled = request.AnimationsEnabled ?? parsedColors != null,
|
||||
ResetEveryAmount = request.ResetEveryAmount ?? 1,
|
||||
ResetEvery = (Services.Apps.CrowdfundResetEvery)request.ResetEvery,
|
||||
ResetEvery = resetEvery,
|
||||
DisplayPerksValue = request.DisplayPerksValue ?? false,
|
||||
DisplayPerksRanking = request.DisplayPerksRanking ?? false,
|
||||
SortPerksByPopularity = request.SortPerksByPopularity ?? false,
|
||||
Sounds = parsedSounds ?? new CrowdfundSettings().Sounds,
|
||||
AnimationColors = parsedColors ?? new CrowdfundSettings().AnimationColors
|
||||
AnimationColors = parsedColors ?? new CrowdfundSettings().AnimationColors,
|
||||
FormId = request.FormId
|
||||
};
|
||||
}
|
||||
|
||||
private PointOfSaleSettings ToPointOfSaleSettings(CreatePointOfSaleAppRequest request)
|
||||
private PointOfSaleSettings ToPointOfSaleSettings(PointOfSaleAppRequest request, PointOfSaleSettings settings)
|
||||
{
|
||||
Enum.TryParse<BTCPayServer.Plugins.PointOfSale.PosViewType>(request.DefaultView.ToString(), true, out var defaultView);
|
||||
|
||||
return new PointOfSaleSettings
|
||||
{
|
||||
Title = request.Title ?? request.AppName,
|
||||
DefaultView = (PosViewType)request.DefaultView,
|
||||
ShowItems = request.ShowItems,
|
||||
ShowCustomAmount = request.ShowCustomAmount,
|
||||
ShowDiscount = request.ShowDiscount,
|
||||
ShowSearch = request.ShowSearch,
|
||||
ShowCategories = request.ShowCategories,
|
||||
EnableTips = request.EnableTips,
|
||||
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,
|
||||
Currency = request.Currency,
|
||||
Template = request.Template != null ? AppService.SerializeTemplate(AppService.Parse(request.Template)) : null,
|
||||
ButtonText = request.FixedAmountPayButtonText ?? PointOfSaleSettings.BUTTON_TEXT_DEF,
|
||||
CustomButtonText = request.CustomAmountPayButtonText ?? PointOfSaleSettings.CUSTOM_BUTTON_TEXT_DEF,
|
||||
CustomTipText = request.TipText ?? PointOfSaleSettings.CUSTOM_TIP_TEXT_DEF,
|
||||
CustomTipPercentages = request.CustomTipPercentages,
|
||||
NotificationUrl = request.NotificationUrl,
|
||||
RedirectUrl = request.RedirectUrl,
|
||||
Description = request.Description,
|
||||
@ -278,27 +295,27 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
};
|
||||
}
|
||||
|
||||
private AppDataBase ToModel(AppData appData)
|
||||
private AppBaseData ToModel(AppData appData)
|
||||
{
|
||||
return new AppDataBase
|
||||
return new AppBaseData
|
||||
{
|
||||
Id = appData.Id,
|
||||
Archived = appData.Archived,
|
||||
AppType = appData.AppType,
|
||||
Name = appData.Name,
|
||||
AppName = appData.Name,
|
||||
StoreId = appData.StoreDataId,
|
||||
Created = appData.Created,
|
||||
};
|
||||
}
|
||||
|
||||
private AppDataBase ToModel(Models.AppViewModels.ListAppsViewModel.ListAppViewModel appData)
|
||||
private AppBaseData ToModel(Models.AppViewModels.ListAppsViewModel.ListAppViewModel appData)
|
||||
{
|
||||
return new AppDataBase
|
||||
return new AppBaseData
|
||||
{
|
||||
Id = appData.Id,
|
||||
Archived = appData.Archived,
|
||||
AppType = appData.AppType,
|
||||
Name = appData.AppName,
|
||||
AppName = appData.AppName,
|
||||
StoreId = appData.StoreId,
|
||||
Created = appData.Created,
|
||||
};
|
||||
@ -307,17 +324,18 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
private PointOfSaleAppData ToPointOfSaleModel(AppData appData)
|
||||
{
|
||||
var settings = appData.GetSettings<PointOfSaleSettings>();
|
||||
|
||||
Enum.TryParse<PosViewType>(settings.DefaultView.ToString(), true, out var defaultView);
|
||||
|
||||
return new PointOfSaleAppData
|
||||
{
|
||||
Id = appData.Id,
|
||||
Archived = appData.Archived,
|
||||
AppType = appData.AppType,
|
||||
Name = appData.Name,
|
||||
AppName = appData.Name,
|
||||
StoreId = appData.StoreDataId,
|
||||
Created = appData.Created,
|
||||
Title = settings.Title,
|
||||
DefaultView = settings.DefaultView.ToString(),
|
||||
DefaultView = defaultView,
|
||||
ShowItems = settings.ShowItems,
|
||||
ShowCustomAmount = settings.ShowCustomAmount,
|
||||
ShowDiscount = settings.ShowDiscount,
|
||||
@ -325,28 +343,30 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
ShowCategories = settings.ShowCategories,
|
||||
EnableTips = settings.EnableTips,
|
||||
Currency = settings.Currency,
|
||||
Items = JsonConvert.DeserializeObject(
|
||||
JsonConvert.SerializeObject(
|
||||
AppService.Parse(settings.Template),
|
||||
new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
|
||||
}
|
||||
)
|
||||
),
|
||||
FixedAmountPayButtonText = settings.ButtonText,
|
||||
CustomAmountPayButtonText = settings.CustomButtonText,
|
||||
TipText = settings.CustomTipText,
|
||||
CustomTipPercentages = settings.CustomTipPercentages,
|
||||
FormId = settings.FormId,
|
||||
NotificationUrl = settings.NotificationUrl,
|
||||
RedirectUrl = settings.RedirectUrl,
|
||||
Description = settings.Description,
|
||||
RedirectAutomatically = settings.RedirectAutomatically ?? false,
|
||||
RedirectAutomatically = settings.RedirectAutomatically,
|
||||
Items = JsonConvert.DeserializeObject(
|
||||
JsonConvert.SerializeObject(
|
||||
AppService.Parse(settings.Template),
|
||||
new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver =
|
||||
new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
|
||||
}
|
||||
)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
private IActionResult? ValidatePOSAppRequest(CreatePointOfSaleAppRequest request)
|
||||
private void ValidatePOSAppRequest(PointOfSaleAppRequest request)
|
||||
{
|
||||
var validationResult = ValidateCreateAppRequest(request);
|
||||
if (request.Currency != null && _currencies.GetCurrencyData(request.Currency, false) == null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.Currency), "Invalid currency");
|
||||
@ -364,25 +384,19 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
ModelState.AddModelError(nameof(request.Template), "Invalid template");
|
||||
}
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
validationResult = this.CreateValidationError(ModelState);
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
private CrowdfundAppData ToCrowdfundModel(AppData appData)
|
||||
{
|
||||
var settings = appData.GetSettings<CrowdfundSettings>();
|
||||
Enum.TryParse<CrowdfundResetEvery>(settings.ResetEvery.ToString(), true, out var resetEvery);
|
||||
|
||||
return new CrowdfundAppData
|
||||
{
|
||||
Id = appData.Id,
|
||||
Archived = appData.Archived,
|
||||
AppType = appData.AppType,
|
||||
Name = appData.Name,
|
||||
AppName = appData.Name,
|
||||
StoreId = appData.StoreDataId,
|
||||
Created = appData.Created,
|
||||
Title = settings.Title,
|
||||
@ -396,6 +410,17 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
MainImageUrl = settings.MainImageUrl,
|
||||
NotificationUrl = settings.NotificationUrl,
|
||||
Tagline = settings.Tagline,
|
||||
DisqusEnabled = settings.DisqusEnabled,
|
||||
DisqusShortname = settings.DisqusShortname,
|
||||
SoundsEnabled = settings.SoundsEnabled,
|
||||
AnimationsEnabled = settings.AnimationsEnabled,
|
||||
ResetEveryAmount = settings.ResetEveryAmount,
|
||||
ResetEvery = resetEvery,
|
||||
DisplayPerksValue = settings.DisplayPerksValue,
|
||||
DisplayPerksRanking = settings.DisplayPerksRanking,
|
||||
SortPerksByPopularity = settings.SortPerksByPopularity,
|
||||
Sounds = settings.Sounds,
|
||||
AnimationColors = settings.AnimationColors,
|
||||
Perks = JsonConvert.DeserializeObject(
|
||||
JsonConvert.SerializeObject(
|
||||
AppService.Parse(settings.PerksTemplate),
|
||||
@ -404,18 +429,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
|
||||
}
|
||||
)
|
||||
),
|
||||
DisqusEnabled = settings.DisqusEnabled,
|
||||
DisqusShortname = settings.DisqusShortname,
|
||||
SoundsEnabled = settings.SoundsEnabled,
|
||||
AnimationsEnabled = settings.AnimationsEnabled,
|
||||
ResetEveryAmount = settings.ResetEveryAmount,
|
||||
ResetEvery = settings.ResetEvery.ToString(),
|
||||
DisplayPerksValue = settings.DisplayPerksValue,
|
||||
DisplayPerksRanking = settings.DisplayPerksRanking,
|
||||
SortPerksByPopularity = settings.SortPerksByPopularity,
|
||||
Sounds = settings.Sounds,
|
||||
AnimationColors = settings.AnimationColors
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
@ -435,34 +449,38 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
return arr.Select(s => s.Trim()).ToArray();
|
||||
}
|
||||
|
||||
private IActionResult? ValidateCrowdfundAppRequest(CreateCrowdfundAppRequest request)
|
||||
private void ValidateCrowdfundAppRequest(CrowdfundAppRequest request)
|
||||
{
|
||||
var validationResult = ValidateCreateAppRequest(request);
|
||||
if (request.TargetCurrency != null && _currencies.GetCurrencyData(request.TargetCurrency, false) == null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.TargetCurrency), "Invalid currency");
|
||||
}
|
||||
|
||||
try
|
||||
if (request.PerksTemplate != null)
|
||||
{
|
||||
// Just checking if we can serialize
|
||||
AppService.SerializeTemplate(AppService.Parse(request.PerksTemplate));
|
||||
}
|
||||
catch
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.PerksTemplate), "Invalid template");
|
||||
try
|
||||
{
|
||||
// Just checking if we can serialize
|
||||
AppService.SerializeTemplate(AppService.Parse(request.PerksTemplate));
|
||||
}
|
||||
catch
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.PerksTemplate), "Invalid template");
|
||||
}
|
||||
}
|
||||
|
||||
if (request.ResetEvery != Client.Models.CrowdfundResetEvery.Never && request.StartDate == null)
|
||||
if (request.ResetEvery.HasValue && request.ResetEvery != CrowdfundResetEvery.Never)
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.StartDate), "A start date is needed when the goal resets every X amount of time");
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
if (request.ResetEvery != Client.Models.CrowdfundResetEvery.Never && request.ResetEveryAmount <= 0)
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.ResetEveryAmount), "You must reset the goal at a minimum of 1");
|
||||
}
|
||||
|
||||
|
||||
if (request.Sounds != null && ValidateStringArray(request.Sounds) == null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.Sounds), "Sounds must be a non-empty array of non-empty strings");
|
||||
@ -473,36 +491,22 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
ModelState.AddModelError(nameof(request.AnimationColors), "Animation colors must be a non-empty array of non-empty strings");
|
||||
}
|
||||
|
||||
if (request.StartDate != null && request.EndDate != null && DateTimeOffset.Compare((DateTimeOffset)request.StartDate, (DateTimeOffset)request.EndDate!) > 0)
|
||||
if (request is { StartDate: not null, EndDate: not null } && DateTimeOffset.Compare((DateTimeOffset)request.StartDate, (DateTimeOffset)request.EndDate!) > 0)
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.EndDate), "End date cannot be before start date");
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
validationResult = this.CreateValidationError(ModelState);
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
private IActionResult? ValidateCreateAppRequest(CreateAppRequest request)
|
||||
private void ValidateAppRequest(IAppRequest? request)
|
||||
{
|
||||
if (request is null)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(request.AppName))
|
||||
if (string.IsNullOrEmpty(request?.AppName))
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.AppName), "App name is missing");
|
||||
}
|
||||
else if (request.AppName.Length < 1 || request.AppName.Length > 50)
|
||||
else if (request.AppName.Length is < 1 or > 50)
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.AppName), "Name can only be between 1 and 50 characters");
|
||||
ModelState.AddModelError(nameof(request.AppName), "App name can only be between 1 and 50 characters");
|
||||
}
|
||||
|
||||
return !ModelState.IsValid ? this.CreateValidationError(ModelState) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1088,7 +1088,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
|
||||
public override async Task<PointOfSaleAppData> CreatePointOfSaleApp(
|
||||
string storeId,
|
||||
CreatePointOfSaleAppRequest request, CancellationToken token = default)
|
||||
PointOfSaleAppRequest request, CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<PointOfSaleAppData>(
|
||||
await GetController<GreenfieldAppsController>().CreatePointOfSaleApp(storeId, request));
|
||||
@ -1096,7 +1096,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
|
||||
public override async Task<PointOfSaleAppData> UpdatePointOfSaleApp(
|
||||
string appId,
|
||||
CreatePointOfSaleAppRequest request, CancellationToken token = default)
|
||||
PointOfSaleAppRequest request, CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<PointOfSaleAppData>(
|
||||
await GetController<GreenfieldAppsController>().UpdatePointOfSaleApp(appId, request));
|
||||
@ -1104,27 +1104,27 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
|
||||
public override async Task<CrowdfundAppData> CreateCrowdfundApp(
|
||||
string storeId,
|
||||
CreateCrowdfundAppRequest request, CancellationToken token = default)
|
||||
CrowdfundAppRequest request, CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<CrowdfundAppData>(
|
||||
await GetController<GreenfieldAppsController>().CreateCrowdfundApp(storeId, request));
|
||||
}
|
||||
|
||||
public override async Task<AppDataBase> GetApp(string appId, CancellationToken token = default)
|
||||
public override async Task<AppBaseData> GetApp(string appId, CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<AppDataBase>(
|
||||
return GetFromActionResult<AppBaseData>(
|
||||
await GetController<GreenfieldAppsController>().GetApp(appId));
|
||||
}
|
||||
|
||||
public override async Task<AppDataBase[]> GetAllApps(string storeId, CancellationToken token = default)
|
||||
public override async Task<AppBaseData[]> GetAllApps(string storeId, CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<AppDataBase[]>(
|
||||
return GetFromActionResult<AppBaseData[]>(
|
||||
await GetController<GreenfieldAppsController>().GetAllApps(storeId));
|
||||
}
|
||||
|
||||
public override async Task<AppDataBase[]> GetAllApps(CancellationToken token = default)
|
||||
public override async Task<AppBaseData[]> GetAllApps(CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<AppDataBase[]>(
|
||||
return GetFromActionResult<AppBaseData[]>(
|
||||
await GetController<GreenfieldAppsController>().GetAllApps());
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,6 @@ using BTCPayServer.Plugins.PointOfSale.Controllers;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Apps;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using Ganss.Xss;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
@ -10,7 +10,6 @@ namespace BTCPayServer.Services.Apps
|
||||
public DateTime? StartDate { get; set; }
|
||||
public DateTime? EndDate { get; set; }
|
||||
public string TargetCurrency { get; set; }
|
||||
|
||||
decimal? _TargetAmount;
|
||||
public decimal? TargetAmount
|
||||
{
|
||||
@ -41,16 +40,14 @@ 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 FormId { get; set; }
|
||||
public string[] AnimationColors { get; set; } =
|
||||
{
|
||||
[
|
||||
"#FF6138", "#FFBE53", "#2980B9", "#282741"
|
||||
};
|
||||
];
|
||||
|
||||
public string[] Sounds { get; set; } =
|
||||
{
|
||||
[
|
||||
"https://github.com/ClaudiuHKS/AdvancedQuakeSounds/tree/master/sound/AQS/dominating.wav",
|
||||
"https://github.com/ClaudiuHKS/AdvancedQuakeSounds/tree/master/sound/AQS/doublekill.wav",
|
||||
"https://github.com/ClaudiuHKS/AdvancedQuakeSounds/tree/master/sound/AQS/doublekill2.wav",
|
||||
@ -78,7 +75,7 @@ namespace BTCPayServer.Services.Apps
|
||||
"https://github.com/ClaudiuHKS/AdvancedQuakeSounds/tree/master/sound/AQS/ultrakill.wav",
|
||||
"https://github.com/ClaudiuHKS/AdvancedQuakeSounds/tree/master/sound/AQS/unstoppable.wav",
|
||||
"https://github.com/ClaudiuHKS/AdvancedQuakeSounds/tree/master/sound/AQS/whickedsick.wav"
|
||||
};
|
||||
];
|
||||
}
|
||||
public enum CrowdfundResetEvery
|
||||
{
|
||||
|
@ -1,4 +1,3 @@
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Plugins.PointOfSale.Models;
|
||||
using PosViewType = BTCPayServer.Plugins.PointOfSale.PosViewType;
|
||||
|
||||
@ -89,19 +88,17 @@ namespace BTCPayServer.Services.Apps
|
||||
public bool ShowItems { get; set; }
|
||||
public bool ShowCustomAmount { get; set; }
|
||||
public bool ShowDiscount { get; set; }
|
||||
public bool ShowSearch { get; set; } = true;
|
||||
public bool ShowCategories { get; set; } = true;
|
||||
public bool ShowSearch { get; set; }
|
||||
public bool ShowCategories { get; set; }
|
||||
public bool EnableTips { get; set; }
|
||||
|
||||
public string FormId { get; set; } = null;
|
||||
|
||||
public string FormId { get; set; }
|
||||
public const string BUTTON_TEXT_DEF = "Buy for {0}";
|
||||
public string ButtonText { get; set; } = BUTTON_TEXT_DEF;
|
||||
public const string CUSTOM_BUTTON_TEXT_DEF = "Pay";
|
||||
public string CustomButtonText { get; set; } = CUSTOM_BUTTON_TEXT_DEF;
|
||||
public const string CUSTOM_TIP_TEXT_DEF = "Do you want to leave a tip?";
|
||||
public string CustomTipText { get; set; } = CUSTOM_TIP_TEXT_DEF;
|
||||
public static readonly int[] CUSTOM_TIP_PERCENTAGES_DEF = new int[] { 15, 18, 20 };
|
||||
public static readonly int[] CUSTOM_TIP_PERCENTAGES_DEF = { 15, 18, 20 };
|
||||
public int[] CustomTipPercentages { get; set; } = CUSTOM_TIP_PERCENTAGES_DEF;
|
||||
public string Description { get; set; }
|
||||
public string NotificationUrl { get; set; }
|
||||
|
@ -21,7 +21,7 @@
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreatePointOfSaleAppRequest"
|
||||
"$ref": "#/components/schemas/PointOfSaleAppRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -84,7 +84,7 @@
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreatePointOfSaleAppRequest"
|
||||
"$ref": "#/components/schemas/PointOfSaleAppRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -223,7 +223,7 @@
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateCrowdfundAppRequest"
|
||||
"$ref": "#/components/schemas/CrowdfundAppRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -291,7 +291,7 @@
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/BasicAppData"
|
||||
"$ref": "#/components/schemas/AppBaseData"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -372,7 +372,7 @@
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/BasicAppData"
|
||||
"$ref": "#/components/schemas/AppBaseData"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -405,7 +405,7 @@
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/BasicAppData"
|
||||
"$ref": "#/components/schemas/AppBaseData"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -425,10 +425,46 @@
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"PointOfSaleAppData": {
|
||||
"AppBaseData": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Id of the app",
|
||||
"example": "3ki4jsAkN4u9rv1PUzj1odX4Nx7s"
|
||||
},
|
||||
"appName": {
|
||||
"type": "string",
|
||||
"description": "Name given to the app when it was created",
|
||||
"example": "my test app"
|
||||
},
|
||||
"storeId": {
|
||||
"type": "string",
|
||||
"description": "Id of the store to which the app belongs",
|
||||
"example": "9CiNzKoANXxmk5ayZngSXrHTiVvvgCrwrpFQd4m2K776"
|
||||
},
|
||||
"created": {
|
||||
"type": "integer",
|
||||
"example": 1651554744,
|
||||
"description": "UNIX timestamp for when the app was created"
|
||||
},
|
||||
"appType": {
|
||||
"type": "string",
|
||||
"example": "PointOfSale",
|
||||
"description": "Type of the app which was created"
|
||||
},
|
||||
"archived": {
|
||||
"type": "boolean",
|
||||
"description": "If true, the app does not appear in the apps list by default.",
|
||||
"default": false,
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"PointOfSaleBaseData": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/BasicAppData"
|
||||
"$ref": "#/components/schemas/AppBaseData"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
@ -436,12 +472,14 @@
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "Display title of the app",
|
||||
"example": "My PoS app"
|
||||
"example": "My PoS app",
|
||||
"nullable": true
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "App description",
|
||||
"example": "This is my amazing PoS app"
|
||||
"example": "This is my amazing PoS app",
|
||||
"nullable": true
|
||||
},
|
||||
"defaultView": {
|
||||
"type": "string",
|
||||
@ -458,48 +496,132 @@
|
||||
"Cart",
|
||||
"Light",
|
||||
"Print"
|
||||
]
|
||||
],
|
||||
"nullable": true
|
||||
},
|
||||
"showItems": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Display item selection for keypad",
|
||||
"example": true
|
||||
"example": true,
|
||||
"nullable": true
|
||||
},
|
||||
"showCustomAmount": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the option to enter a custom amount is shown",
|
||||
"example": true
|
||||
"example": true,
|
||||
"nullable": true
|
||||
},
|
||||
"showDiscount": {
|
||||
"default": false,
|
||||
"type": "boolean",
|
||||
"description": "Whether the option to enter a discount is shown",
|
||||
"example": false
|
||||
"example": false,
|
||||
"nullable": true
|
||||
},
|
||||
"showSearch": {
|
||||
"type": "boolean",
|
||||
"description": "Display the search bar",
|
||||
"example": false,
|
||||
"default": true
|
||||
"default": true,
|
||||
"nullable": true
|
||||
},
|
||||
"showCategories": {
|
||||
"type": "boolean",
|
||||
"description": "Display the list of categories",
|
||||
"example": false,
|
||||
"default": true
|
||||
"default": true,
|
||||
"nullable": true
|
||||
},
|
||||
"enableTips": {
|
||||
"default": false,
|
||||
"type": "boolean",
|
||||
"description": "Whether the option to enter a tip is shown",
|
||||
"example": true
|
||||
"example": true,
|
||||
"nullable": true
|
||||
},
|
||||
"currency": {
|
||||
"type": "string",
|
||||
"description": "Currency used for the app",
|
||||
"example": "BTC"
|
||||
"example": "BTC",
|
||||
"nullable": true
|
||||
},
|
||||
"fixedAmountPayButtonText": {
|
||||
"type": "string",
|
||||
"description": "Payment button text template for items with a set price",
|
||||
"example": "Buy for {0}",
|
||||
"nullable": true
|
||||
},
|
||||
"customAmountPayButtonText": {
|
||||
"type": "string",
|
||||
"description": "Payment button text which appears for items which allow user to input a custom amount",
|
||||
"example": "Pay",
|
||||
"nullable": true
|
||||
},
|
||||
"tipText": {
|
||||
"type": "string",
|
||||
"description": "Prompt which appears next to the tip amount field if tipping is enabled",
|
||||
"example": "Do you want to leave a tip?",
|
||||
"nullable": true
|
||||
},
|
||||
"customTipPercentages": {
|
||||
"type": "array",
|
||||
"description": "Array of predefined tip percentage amounts",
|
||||
"items": {
|
||||
"type": "number"
|
||||
},
|
||||
"default": [15,18,20],
|
||||
"nullable": true
|
||||
},
|
||||
"notificationUrl": {
|
||||
"type": "string",
|
||||
"description": "Callback notification url to POST to once when invoice is paid for and once when there are enough blockchain confirmations",
|
||||
"nullable": true
|
||||
},
|
||||
"redirectUrl": {
|
||||
"type": "string",
|
||||
"description": "URL user is redirected to once invoice is paid",
|
||||
"nullable": true
|
||||
},
|
||||
"redirectAutomatically": {
|
||||
"type": "boolean",
|
||||
"description": "Whether user is redirected to specified redirect URL automatically after the invoice is paid",
|
||||
"example": true,
|
||||
"nullable": true
|
||||
},
|
||||
"formId": {
|
||||
"type": "string",
|
||||
"description": "Form ID to request customer data",
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"PointOfSaleAppRequest": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/PointOfSaleBaseData"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"template": {
|
||||
"type": "string",
|
||||
"description": "JSON of item available in the app"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"PointOfSaleAppData": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/PointOfSaleBaseData"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "JSON object of app items",
|
||||
@ -535,34 +657,169 @@
|
||||
"disabled": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"fixedAmountPayButtonText": {
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"CrowdfundBaseData": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/AppBaseData"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "Payment button text template for items with a set price",
|
||||
"example": "Buy for {0}"
|
||||
"description": "Display title of the app",
|
||||
"example": "My crowdfund app",
|
||||
"nullable": true
|
||||
},
|
||||
"customAmountPayButtonText": {
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Payment button text which appears for items which allow user to input a custom amount",
|
||||
"example": "Pay"
|
||||
"description": "App description",
|
||||
"example": "My crowdfund description",
|
||||
"nullable": true
|
||||
},
|
||||
"tipText": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the app is enabled to be viewed by everyone",
|
||||
"example": true,
|
||||
"nullable": true
|
||||
},
|
||||
"enforceTargetAmount": {
|
||||
"type": "boolean",
|
||||
"description": "Whether contributions over the set target amount are allowed",
|
||||
"example": false,
|
||||
"nullable": true
|
||||
},
|
||||
"startDate": {
|
||||
"type": "number",
|
||||
"description": "UNIX timestamp for crowdfund start time (https://www.unixtimestamp.com/)",
|
||||
"allOf": [ {"$ref": "#/components/schemas/UnixTimestamp"}],
|
||||
"example": 768658369,
|
||||
"nullable": true
|
||||
},
|
||||
"endDate": {
|
||||
"type": "number",
|
||||
"description": "UNIX timestamp for crowdfund end time (https://www.unixtimestamp.com/)",
|
||||
"allOf": [ {"$ref": "#/components/schemas/UnixTimestamp"}],
|
||||
"example": 771336769,
|
||||
"nullable": true
|
||||
},
|
||||
"targetCurrency": {
|
||||
"type": "string",
|
||||
"description": "Prompt which appears next to the tip amount field if tipping is enabled",
|
||||
"example": "Do you want to leave a tip?"
|
||||
"description": "Target currency for the crowdfund",
|
||||
"example": "BTC",
|
||||
"nullable": true
|
||||
},
|
||||
"targetAmount": {
|
||||
"type": "number",
|
||||
"description": "Target amount for the crowdfund",
|
||||
"example": 420.69,
|
||||
"nullable": true
|
||||
},
|
||||
"mainImageUrl": {
|
||||
"type": "string",
|
||||
"description": "URL for image used as a cover image for the app",
|
||||
"nullable": true
|
||||
},
|
||||
"notificationUrl": {
|
||||
"type": "string",
|
||||
"description": "Callback notification url to POST to once when invoice is paid for and once when there are enough blockchain confirmations"
|
||||
"description": "Callback notification url to POST to once when invoice is paid for and once when there are enough blockchain confirmations",
|
||||
"nullable": true
|
||||
},
|
||||
"redirectUrl": {
|
||||
"tagline": {
|
||||
"type": "string",
|
||||
"description": "URL user is redirected to once invoice is paid"
|
||||
"description": "Tagline for the app displayed to user",
|
||||
"example": "I can't believe it's not butter",
|
||||
"nullable": true
|
||||
},
|
||||
"redirectAutomatically": {
|
||||
"disqusEnabled": {
|
||||
"type": "boolean",
|
||||
"description": "Whether user is redirected to specified redirect URL automatically after the invoice is paid",
|
||||
"example": true
|
||||
"description": "Whether Disqus is enabled for the app",
|
||||
"nullable": true
|
||||
},
|
||||
"disqusShortname": {
|
||||
"type": "string",
|
||||
"description": "Disqus shortname to used for the app",
|
||||
"nullable": true
|
||||
},
|
||||
"soundsEnabled": {
|
||||
"type": "boolean",
|
||||
"description": "Whether sounds on new contributions are enabled",
|
||||
"example": false,
|
||||
"nullable": true
|
||||
},
|
||||
"animationsEnabled": {
|
||||
"type": "boolean",
|
||||
"description": "Whether background animations on new contributions are enabled",
|
||||
"example": true,
|
||||
"nullable": true
|
||||
},
|
||||
"resetEveryAmount": {
|
||||
"type": "number",
|
||||
"description": "Contribution goal reset frequency amount",
|
||||
"example": 1,
|
||||
"nullable": true
|
||||
},
|
||||
"resetEvery": {
|
||||
"type": "string",
|
||||
"description": "Contribution goal reset frequency",
|
||||
"example": "Day",
|
||||
"nullable": true
|
||||
},
|
||||
"displayPerksValue": {
|
||||
"type": "boolean",
|
||||
"description": "Whether perk values are displayed",
|
||||
"example": false,
|
||||
"nullable": true
|
||||
},
|
||||
"sortPerksByPopularity": {
|
||||
"type": "boolean",
|
||||
"description": "Whether perks are sorted by popularity",
|
||||
"default": true,
|
||||
"nullable": true
|
||||
},
|
||||
"sounds": {
|
||||
"type": "array",
|
||||
"description": "Array of custom sounds which can be used on new contributions",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": ["https://github.com/ClaudiuHKS/AdvancedQuakeSounds/raw/master/sound/AQS/doublekill.wav"],
|
||||
"nullable": true
|
||||
},
|
||||
"animationColors": {
|
||||
"type": "array",
|
||||
"description": "Array of custom HEX colors which can be used for background animations on new contributions",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": ["#FF0000", "#00FF00", "#0000FF"],
|
||||
"nullable": true
|
||||
},
|
||||
"formId": {
|
||||
"type": "string",
|
||||
"description": "Form ID to request customer data",
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"CrowdfundAppRequest": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/CrowdfundBaseData"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"perksTemplate": {
|
||||
"type": "string",
|
||||
"description": "JSON of perks available in the app"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -571,57 +828,11 @@
|
||||
"CrowdfundAppData": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/BasicAppData"
|
||||
"$ref": "#/components/schemas/CrowdfundBaseData"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "Display title of the app",
|
||||
"example": "My crowdfund app"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "App description",
|
||||
"example": "My crowdfund description"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the app is enabled to be viewed by everyone",
|
||||
"example": true
|
||||
},
|
||||
"enforceTargetAmount": {
|
||||
"type": "boolean",
|
||||
"description": "Whether contributions over the set target amount are allowed",
|
||||
"example": false
|
||||
},
|
||||
"startDate": {
|
||||
"type": "number",
|
||||
"description": "UNIX timestamp for crowdfund start time (https://www.unixtimestamp.com/)",
|
||||
"allOf": [ {"$ref": "#/components/schemas/UnixTimestamp"}],
|
||||
"example": 768658369
|
||||
},
|
||||
"endDate": {
|
||||
"type": "number",
|
||||
"description": "UNIX timestamp for crowdfund end time (https://www.unixtimestamp.com/)",
|
||||
"allOf": [ {"$ref": "#/components/schemas/UnixTimestamp"}],
|
||||
"example": 771336769
|
||||
},
|
||||
"targetCurrency": {
|
||||
"type": "string",
|
||||
"description": "Target currency for the crowdfund",
|
||||
"example": "BTC"
|
||||
},
|
||||
"targetAmount": {
|
||||
"type": "number",
|
||||
"description": "Target amount for the crowdfund",
|
||||
"example": 420.69
|
||||
},
|
||||
"mainImageUrl": {
|
||||
"type": "string",
|
||||
"description": "URL for image used as a cover image for the app"
|
||||
},
|
||||
"perks": {
|
||||
"type": "object",
|
||||
"description": "JSON of perks available in the app",
|
||||
@ -672,369 +883,10 @@
|
||||
"disabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"notificationUrl": {
|
||||
"type": "string",
|
||||
"description": "Callback notification url to POST to once when invoice is paid for and once when there are enough blockchain confirmations"
|
||||
},
|
||||
"tagline": {
|
||||
"type": "string",
|
||||
"description": "Tagline for the app displayed to user",
|
||||
"example": "I can't believe it's not butter"
|
||||
},
|
||||
"disqusEnabled": {
|
||||
"type": "boolean",
|
||||
"description": "Whether Disqus is enabled for the app"
|
||||
},
|
||||
"disqusShortname": {
|
||||
"type": "string",
|
||||
"description": "Disqus shortname to used for the app"
|
||||
},
|
||||
"soundsEnabled": {
|
||||
"type": "boolean",
|
||||
"description": "Whether sounds on new contributions are enabled",
|
||||
"example": false
|
||||
},
|
||||
"animationsEnabled": {
|
||||
"type": "boolean",
|
||||
"description": "Whether background animations on new contributions are enabled",
|
||||
"example": true
|
||||
},
|
||||
"resetEveryAmount": {
|
||||
"type": "number",
|
||||
"description": "Contribution goal reset frequency amount",
|
||||
"example": 1
|
||||
},
|
||||
"resetEvery": {
|
||||
"type": "string",
|
||||
"description": "Contribution goal reset frequency",
|
||||
"example": "Day"
|
||||
},
|
||||
"displayPerksValue": {
|
||||
"type": "boolean",
|
||||
"description": "Whether perk values are displayed",
|
||||
"example": false
|
||||
},
|
||||
"sortPerksByPopularity": {
|
||||
"type": "boolean",
|
||||
"description": "Whether perks are sorted by popularity",
|
||||
"default": true
|
||||
},
|
||||
"sounds": {
|
||||
"type": "array",
|
||||
"description": "Array of custom sounds which can be used on new contributions",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": ["https://github.com/ClaudiuHKS/AdvancedQuakeSounds/raw/master/sound/AQS/doublekill.wav"]
|
||||
},
|
||||
"animationColors": {
|
||||
"type": "array",
|
||||
"description": "Array of custom HEX colors which can be used for background animations on new contributions",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": ["#FF0000", "#00FF00", "#0000FF"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"BasicAppData": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Id of the app",
|
||||
"example": "3ki4jsAkN4u9rv1PUzj1odX4Nx7s"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name given to the app when it was created",
|
||||
"example": "my test app"
|
||||
},
|
||||
"storeId": {
|
||||
"type": "string",
|
||||
"description": "Id of the store to which the app belongs",
|
||||
"example": "9CiNzKoANXxmk5ayZngSXrHTiVvvgCrwrpFQd4m2K776"
|
||||
},
|
||||
"created": {
|
||||
"type": "integer",
|
||||
"example": 1651554744,
|
||||
"description": "UNIX timestamp for when the app was created"
|
||||
},
|
||||
"appType": {
|
||||
"type": "string",
|
||||
"example": "PointOfSale",
|
||||
"description": "Type of the app which was created"
|
||||
},
|
||||
"archived": {
|
||||
"type": "boolean",
|
||||
"description": "If true, the app does not appear in the apps list by default.",
|
||||
"default": false,
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"CreatePointOfSaleAppRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"appName": {
|
||||
"type": "string",
|
||||
"description": "The name of the app (shown in admin UI)",
|
||||
"nullable": false
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the app (shown to the user)",
|
||||
"nullable": true
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "The description of the app",
|
||||
"nullable": true
|
||||
},
|
||||
"template": {
|
||||
"type": "string",
|
||||
"description": "Template for items available in the app",
|
||||
"nullable": true
|
||||
},
|
||||
"defaultView": {
|
||||
"type": "string",
|
||||
"description": "Template for items available in the app",
|
||||
"nullable": true,
|
||||
"x-enumNames": [
|
||||
"Static",
|
||||
"Cart",
|
||||
"Light",
|
||||
"Print"
|
||||
],
|
||||
"enum": [
|
||||
"Static",
|
||||
"Cart",
|
||||
"Light",
|
||||
"Print"
|
||||
]
|
||||
},
|
||||
"currency": {
|
||||
"type": "string",
|
||||
"description": "Currency to use for the app. Defaults to the currency used by the store if not specified",
|
||||
"example": "BTC",
|
||||
"nullable": true
|
||||
},
|
||||
"showCustomAmount": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to include a special item in the store which allows user to input a custom payment amount",
|
||||
"default": false,
|
||||
"nullable": true
|
||||
},
|
||||
"showDiscount": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to allow user to input a discount amount. Applies to Cart view only. Not recommended for customer self-checkout",
|
||||
"default": true,
|
||||
"nullable": true
|
||||
},
|
||||
"enableTips": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to allow user to input a tip amount. Applies to Cart and Light views only",
|
||||
"default": true,
|
||||
"nullable": true
|
||||
},
|
||||
"customAmountPayButtonText": {
|
||||
"type": "string",
|
||||
"description": "Payment button text which appears for items which allow user to input a custom amount",
|
||||
"default": "Pay",
|
||||
"nullable": true
|
||||
},
|
||||
"fixedAmountPayButtonText": {
|
||||
"type": "string",
|
||||
"description": "Payment button text which appears for items which have a fixed price",
|
||||
"default": "Buy for {PRICE_HERE}",
|
||||
"nullable": true
|
||||
},
|
||||
"tipText": {
|
||||
"type": "string",
|
||||
"description": "Prompt which appears next to the tip amount field if tipping is enabled",
|
||||
"default": "Do you want to leave a tip?",
|
||||
"nullable": true
|
||||
},
|
||||
"notificationUrl": {
|
||||
"type": "string",
|
||||
"description": "Callback notification url to POST to once when invoice is paid for and once when there are enough blockchain confirmations",
|
||||
"nullable": true
|
||||
},
|
||||
"redirectUrl": {
|
||||
"type": "string",
|
||||
"description": "URL to redirect user to once invoice is paid",
|
||||
"nullable": true
|
||||
},
|
||||
"redirectAutomatically": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to redirect user to redirect URL automatically once invoice is paid. Defaults to what is set in the store settings",
|
||||
"nullable": true
|
||||
},
|
||||
"formId": {
|
||||
"type": "string",
|
||||
"description": "Form ID to request customer data",
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"CreateCrowdfundAppRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"appName": {
|
||||
"type": "string",
|
||||
"description": "The name of the app (shown in admin UI)",
|
||||
"example": "Kukkstarter",
|
||||
"nullable": false
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the app (shown to the user)",
|
||||
"example": "My crowdfund app",
|
||||
"nullable": true
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "The description of the app (shown to the user)",
|
||||
"example": "My app description",
|
||||
"nullable": true
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Determines if the app is enabled to be viewed by everyone",
|
||||
"default": true,
|
||||
"nullable": true
|
||||
},
|
||||
"enforceTargetAmount": {
|
||||
"type": "boolean",
|
||||
"description": "Will not allow contributions over the set target amount",
|
||||
"default": false,
|
||||
"nullable": true
|
||||
},
|
||||
"startDate": {
|
||||
"type": "number",
|
||||
"description": "UNIX timestamp for crowdfund start time (https://www.unixtimestamp.com/)",
|
||||
"allOf": [ {"$ref": "#/components/schemas/UnixTimestamp"}],
|
||||
"example": 768658369,
|
||||
"nullable": true
|
||||
},
|
||||
"endDate": {
|
||||
"type": "number",
|
||||
"description": "UNIX timestamp for crowdfund end time (https://www.unixtimestamp.com/)",
|
||||
"allOf": [ {"$ref": "#/components/schemas/UnixTimestamp"}],
|
||||
"example": 771336769,
|
||||
"nullable": true
|
||||
},
|
||||
"targetCurrency": {
|
||||
"type": "string",
|
||||
"description": "Target currency for the crowdfund. Defaults to the currency used by the store if not specified",
|
||||
"example": "BTC",
|
||||
"nullable": true
|
||||
},
|
||||
"targetAmount": {
|
||||
"type": "number",
|
||||
"description": "Target amount for the crowdfund",
|
||||
"example": 420,
|
||||
"nullable": true
|
||||
},
|
||||
"mainImageUrl": {
|
||||
"type": "string",
|
||||
"description": "URL for image to be used as a cover image for the app",
|
||||
"nullable": true
|
||||
},
|
||||
"perksTemplate": {
|
||||
"type": "string",
|
||||
"description": "YAML template of perks available in the app",
|
||||
"example": "test_perk:\r\n price: 100\r\n title: test perk\r\n price_type: \"fixed\" \r\n disabled: false",
|
||||
"nullable": true
|
||||
},
|
||||
"notificationUrl": {
|
||||
"type": "string",
|
||||
"description": "Callback notification url to POST to once when invoice is paid for and once when there are enough blockchain confirmations",
|
||||
"nullable": true
|
||||
},
|
||||
"tagline": {
|
||||
"type": "string",
|
||||
"description": "Tagline for the app (shown to the user)",
|
||||
"example": "I can't believe it's not butter",
|
||||
"nullable": true
|
||||
},
|
||||
"disqusShortname": {
|
||||
"type": "string",
|
||||
"description": "Disqus shortname to used for the app. Enables Disqus functionality if set.",
|
||||
"nullable": true
|
||||
},
|
||||
"soundsEnabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enables sounds on new contributions if set to true",
|
||||
"default": false,
|
||||
"nullable": true
|
||||
},
|
||||
"animationsEnabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enables background animations on new contributions if set to true",
|
||||
"default": false,
|
||||
"nullable": true
|
||||
},
|
||||
"resetEveryAmount": {
|
||||
"type": "number",
|
||||
"description": "Contribution goal reset frequency amount. Must be used in conjunction with resetEvery and startDate.",
|
||||
"default": 1,
|
||||
"nullable": true
|
||||
},
|
||||
"resetEvery": {
|
||||
"type": "string",
|
||||
"description": "Contribution goal reset frequency. Must be used in conjunction with resetEveryAmount and startDate.",
|
||||
"nullable": true,
|
||||
"default": "Never",
|
||||
"x-enumNames": [
|
||||
"Never",
|
||||
"Hour",
|
||||
"Day",
|
||||
"Month",
|
||||
"Year"
|
||||
],
|
||||
"enum": [
|
||||
"Never",
|
||||
"Hour",
|
||||
"Day",
|
||||
"Month",
|
||||
"Year"
|
||||
]
|
||||
},
|
||||
"displayPerksValue": {
|
||||
"type": "boolean",
|
||||
"description": "Displays values of perks if set to true",
|
||||
"default": false,
|
||||
"nullable": true
|
||||
},
|
||||
"sortPerksByPopularity": {
|
||||
"type": "boolean",
|
||||
"description": "Sorts perks by popularity if set to true",
|
||||
"default": false,
|
||||
"nullable": true
|
||||
},
|
||||
"sounds": {
|
||||
"type": "array",
|
||||
"description": "Array of custom sounds to use on new contributions",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"nullable": true,
|
||||
"example": [ "https://github.com/ClaudiuHKS/AdvancedQuakeSounds/raw/master/sound/AQS/doublekill.wav" ]
|
||||
},
|
||||
"animationColors": {
|
||||
"type": "array",
|
||||
"description": "Array of custom HEX colors to use for background animations on new contributions",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"nullable": true,
|
||||
"example": ["#FF0000", "#00FF00", "#0000FF"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user