mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-03-04 09:58:13 +01:00
Add crowdfund app create endpoint (#4068)
* Add crowdfund app create endpoint * replace DateTimeJsonConverter with NBitcoin.JsonConverters.DateTimeToUnixTimeConverter * Use DateTimeOffset instead of DateTime * Use array instead of CSV * update "startDate" and "endDate" docs definition * update docs
This commit is contained in:
parent
3942463ac9
commit
52af129c8b
7 changed files with 576 additions and 6 deletions
|
@ -19,6 +19,17 @@ namespace BTCPayServer.Client
|
|||
method: HttpMethod.Post), token);
|
||||
return await HandleResponse<PointOfSaleAppData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<CrowdfundAppData> CreateCrowdfundApp(string storeId,
|
||||
CreateCrowdfundAppRequest request, CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/apps/crowdfund", bodyPayload: request,
|
||||
method: HttpMethod.Post), token);
|
||||
return await HandleResponse<CrowdfundAppData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<PointOfSaleAppData> UpdatePointOfSaleApp(string appId,
|
||||
CreatePointOfSaleAppRequest request, CancellationToken token = default)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
|
@ -40,4 +41,44 @@ namespace BTCPayServer.Client.Models
|
|||
public string EmbeddedCSS { get; set; } = null;
|
||||
public CheckoutType? CheckoutType { 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 CustomCSSLink { get; set; } = null;
|
||||
public string MainImageUrl { get; set; } = null;
|
||||
public string EmbeddedCSS { 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 string[] Sounds { get; set; } = null;
|
||||
public string[] AnimationColors { get; set; } = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,4 +17,9 @@ namespace BTCPayServer.Client.Models
|
|||
{
|
||||
// We can add POS specific things here later
|
||||
}
|
||||
|
||||
public class CrowdfundAppData : AppDataBase
|
||||
{
|
||||
// We can add Crowdfund specific things here later
|
||||
}
|
||||
}
|
||||
|
|
|
@ -288,6 +288,117 @@ namespace BTCPayServer.Tests
|
|||
await client.GetApp(retrievedApp.Id);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanCreateCrowdfundApp()
|
||||
{
|
||||
using var tester = CreateServerTester();
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
await user.RegisterDerivationSchemeAsync("BTC");
|
||||
var client = await user.CreateClient();
|
||||
|
||||
// Test validation for creating the app
|
||||
await AssertValidationError(new[] { "AppName" },
|
||||
async () => await client.CreateCrowdfundApp(user.StoreId, new CreateCrowdfundAppRequest() {}));
|
||||
await AssertValidationError(new[] { "AppName" },
|
||||
async () => await client.CreateCrowdfundApp(
|
||||
user.StoreId,
|
||||
new CreateCrowdfundAppRequest()
|
||||
{
|
||||
AppName = "this is a really long app name this is a really long app name this is a really long app name",
|
||||
}
|
||||
)
|
||||
);
|
||||
await AssertValidationError(new[] { "TargetCurrency" },
|
||||
async () => await client.CreateCrowdfundApp(
|
||||
user.StoreId,
|
||||
new CreateCrowdfundAppRequest()
|
||||
{
|
||||
AppName = "good name",
|
||||
TargetCurrency = "fake currency"
|
||||
}
|
||||
)
|
||||
);
|
||||
await AssertValidationError(new[] { "PerksTemplate" },
|
||||
async () => await client.CreateCrowdfundApp(
|
||||
user.StoreId,
|
||||
new CreateCrowdfundAppRequest()
|
||||
{
|
||||
AppName = "good name",
|
||||
PerksTemplate = "lol invalid template"
|
||||
}
|
||||
)
|
||||
);
|
||||
await AssertValidationError(new[] { "AppName", "TargetCurrency", "PerksTemplate" },
|
||||
async () => await client.CreateCrowdfundApp(
|
||||
user.StoreId,
|
||||
new CreateCrowdfundAppRequest()
|
||||
{
|
||||
TargetCurrency = "fake currency",
|
||||
PerksTemplate = "lol invalid template"
|
||||
}
|
||||
)
|
||||
);
|
||||
await AssertValidationError(new[] { "AnimationColors" },
|
||||
async () => await client.CreateCrowdfundApp(
|
||||
user.StoreId,
|
||||
new CreateCrowdfundAppRequest()
|
||||
{
|
||||
AppName = "good name",
|
||||
AnimationColors = new string[] {}
|
||||
}
|
||||
)
|
||||
);
|
||||
await AssertValidationError(new[] { "AnimationColors" },
|
||||
async () => await client.CreateCrowdfundApp(
|
||||
user.StoreId,
|
||||
new CreateCrowdfundAppRequest()
|
||||
{
|
||||
AppName = "good name",
|
||||
AnimationColors = new string[] { " ", " " }
|
||||
}
|
||||
)
|
||||
);
|
||||
await AssertValidationError(new[] { "Sounds" },
|
||||
async () => await client.CreateCrowdfundApp(
|
||||
user.StoreId,
|
||||
new CreateCrowdfundAppRequest()
|
||||
{
|
||||
AppName = "good name",
|
||||
Sounds = new string[] { " " }
|
||||
}
|
||||
)
|
||||
);
|
||||
await AssertValidationError(new[] { "Sounds" },
|
||||
async () => await client.CreateCrowdfundApp(
|
||||
user.StoreId,
|
||||
new CreateCrowdfundAppRequest()
|
||||
{
|
||||
AppName = "good name",
|
||||
Sounds = new string[] { " ", " ", " " }
|
||||
}
|
||||
)
|
||||
);
|
||||
await AssertValidationError(new[] { "EndDate" },
|
||||
async () => await client.CreateCrowdfundApp(
|
||||
user.StoreId,
|
||||
new CreateCrowdfundAppRequest()
|
||||
{
|
||||
AppName = "good name",
|
||||
StartDate = DateTime.Parse("1998-01-01"),
|
||||
EndDate = DateTime.Parse("1997-12-31")
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Test creating a crowdfund app
|
||||
var app = await client.CreateCrowdfundApp(user.StoreId, new CreateCrowdfundAppRequest() { AppName = "test app from API" });
|
||||
Assert.Equal("test app from API", app.Name);
|
||||
Assert.Equal(user.StoreId, app.StoreId);
|
||||
Assert.Equal("Crowdfund", app.AppType);
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#nullable enable
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Client;
|
||||
|
@ -38,6 +39,37 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||
_currencies = currencies;
|
||||
}
|
||||
|
||||
[HttpPost("~/api/v1/stores/{storeId}/apps/crowdfund")]
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
public async Task<IActionResult> CreateCrowdfundApp(string storeId, CreateCrowdfundAppRequest 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;
|
||||
|
||||
var validationResult = ValidateCrowdfundAppRequest(request);
|
||||
if (validationResult != null)
|
||||
{
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
var appData = new AppData
|
||||
{
|
||||
StoreDataId = storeId,
|
||||
Name = request.AppName,
|
||||
AppType = AppType.Crowdfund.ToString()
|
||||
};
|
||||
|
||||
appData.SetSettings(ToCrowdfundSettings(request));
|
||||
|
||||
await _appService.UpdateOrCreateApp(appData);
|
||||
|
||||
return Ok(ToCrowdfundModel(appData));
|
||||
}
|
||||
|
||||
[HttpPost("~/api/v1/stores/{storeId}/apps/pos")]
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
public async Task<IActionResult> CreatePointOfSaleApp(string storeId, CreatePointOfSaleAppRequest request)
|
||||
|
@ -66,7 +98,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||
|
||||
await _appService.UpdateOrCreateApp(appData);
|
||||
|
||||
return Ok(ToModel(appData));
|
||||
return Ok(ToPointOfSaleModel(appData));
|
||||
}
|
||||
|
||||
[HttpPut("~/api/v1/apps/pos/{appId}")]
|
||||
|
@ -95,7 +127,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||
|
||||
await _appService.UpdateOrCreateApp(app);
|
||||
|
||||
return Ok(ToModel(app));
|
||||
return Ok(ToPointOfSaleModel(app));
|
||||
}
|
||||
|
||||
private RequiresRefundEmail? BoolToRequiresRefundEmail(bool? requiresRefundEmail)
|
||||
|
@ -115,7 +147,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
public async Task<IActionResult> GetApp(string appId)
|
||||
{
|
||||
var app = await _appService.GetApp(appId, AppType.PointOfSale);
|
||||
var app = await _appService.GetApp(appId, null);
|
||||
if (app == null)
|
||||
{
|
||||
return AppNotFound();
|
||||
|
@ -143,6 +175,43 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||
return this.CreateAPIError(404, "app-not-found", "The app with specified ID was not found");
|
||||
}
|
||||
|
||||
private CrowdfundSettings ToCrowdfundSettings(CreateCrowdfundAppRequest request)
|
||||
{
|
||||
var parsedSounds = ValidateStringArray(request.Sounds);
|
||||
var parsedColors = ValidateStringArray(request.AnimationColors);
|
||||
|
||||
return new CrowdfundSettings
|
||||
{
|
||||
Title = request.Title?.Trim(),
|
||||
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,
|
||||
CustomCSSLink = request.CustomCSSLink?.Trim(),
|
||||
MainImageUrl = request.MainImageUrl?.Trim(),
|
||||
EmbeddedCSS = request.EmbeddedCSS?.Trim(),
|
||||
NotificationUrl = request.NotificationUrl?.Trim(),
|
||||
Tagline = request.Tagline?.Trim(),
|
||||
PerksTemplate = request.PerksTemplate != null ? _appService.SerializeTemplate(_appService.Parse(request.PerksTemplate?.Trim(), request.TargetCurrency)) : null,
|
||||
// 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,
|
||||
ResetEvery = (Services.Apps.CrowdfundResetEvery)request.ResetEvery,
|
||||
DisplayPerksValue = request.DisplayPerksValue ?? false,
|
||||
DisplayPerksRanking = request.DisplayPerksRanking ?? false,
|
||||
SortPerksByPopularity = request.SortPerksByPopularity ?? false,
|
||||
Sounds = parsedSounds ?? new CrowdfundSettings().Sounds,
|
||||
AnimationColors = parsedColors ?? new CrowdfundSettings().AnimationColors
|
||||
};
|
||||
}
|
||||
|
||||
private PointOfSaleSettings ToPointOfSaleSettings(CreatePointOfSaleAppRequest request)
|
||||
{
|
||||
return new PointOfSaleSettings()
|
||||
|
@ -169,10 +238,20 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||
};
|
||||
}
|
||||
|
||||
private PointOfSaleAppData ToModel(AppData appData)
|
||||
private AppDataBase ToModel(AppData appData)
|
||||
{
|
||||
var settings = appData.GetSettings<PointOfSaleSettings>();
|
||||
return new AppDataBase
|
||||
{
|
||||
Id = appData.Id,
|
||||
AppType = appData.AppType,
|
||||
Name = appData.Name,
|
||||
StoreId = appData.StoreDataId,
|
||||
Created = appData.Created,
|
||||
};
|
||||
}
|
||||
|
||||
private PointOfSaleAppData ToPointOfSaleModel(AppData appData)
|
||||
{
|
||||
return new PointOfSaleAppData
|
||||
{
|
||||
Id = appData.Id,
|
||||
|
@ -211,6 +290,84 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||
return validationResult;
|
||||
}
|
||||
|
||||
private CrowdfundAppData ToCrowdfundModel(AppData appData)
|
||||
{
|
||||
return new CrowdfundAppData
|
||||
{
|
||||
Id = appData.Id,
|
||||
AppType = appData.AppType,
|
||||
Name = appData.Name,
|
||||
StoreId = appData.StoreDataId,
|
||||
Created = appData.Created
|
||||
};
|
||||
}
|
||||
|
||||
private string[]? ValidateStringArray(string[]? arr)
|
||||
{
|
||||
if (arr == null || !arr.Any())
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
private IActionResult? ValidateCrowdfundAppRequest(CreateCrowdfundAppRequest request)
|
||||
{
|
||||
var validationResult = ValidateCreateAppRequest(request);
|
||||
if (request.TargetCurrency != null && _currencies.GetCurrencyData(request.TargetCurrency, false) == null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.TargetCurrency), "Invalid currency");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_appService.SerializeTemplate(_appService.Parse(request.PerksTemplate, request.TargetCurrency));
|
||||
}
|
||||
catch
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.PerksTemplate), "Invalid template");
|
||||
}
|
||||
|
||||
if (request.ResetEvery != Client.Models.CrowdfundResetEvery.Never && request.StartDate == null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.StartDate), "A start date is needed when the goal resets every X amount of time");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
if (request.StartDate != null && request.EndDate != 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)
|
||||
{
|
||||
if (request is null)
|
||||
|
|
|
@ -1163,6 +1163,14 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||
await GetController<GreenfieldAppsController>().UpdatePointOfSaleApp(appId, request));
|
||||
}
|
||||
|
||||
public override async Task<CrowdfundAppData> CreateCrowdfundApp(
|
||||
string storeId,
|
||||
CreateCrowdfundAppRequest request, CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<CrowdfundAppData>(
|
||||
await GetController<GreenfieldAppsController>().CreateCrowdfundApp(storeId, request));
|
||||
}
|
||||
|
||||
public override async Task<AppDataBase> GetApp(string appId, CancellationToken token = default)
|
||||
{
|
||||
return GetFromActionResult<AppDataBase>(
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
"post": {
|
||||
"operationId": "Apps_CreatePointOfSaleApp",
|
||||
"summary": "Create a new Point of Sale app",
|
||||
"description": "Point of Sale apps allows accepting payments for items in a virtual store",
|
||||
"description": "Point of Sale app allows accepting payments for items in a virtual store",
|
||||
"requestBody": {
|
||||
"x-name": "request",
|
||||
"content": {
|
||||
|
@ -126,6 +126,69 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/api/v1/stores/{storeId}/apps/crowdfund": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "storeId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "The store ID",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"post": {
|
||||
"operationId": "Apps_CreateCrowdfundApp",
|
||||
"summary": "Create a new Crowdfund app",
|
||||
"requestBody": {
|
||||
"x-name": "request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateCrowdfundAppRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true,
|
||||
"x-position": 1
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Created app details",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CrowdfundAppData"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Unable to validate the request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ValidationProblemDetails"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Apps",
|
||||
"Crowdfund"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"API_Key": [
|
||||
"btcpay.store.canmodifystoresettings"
|
||||
],
|
||||
"Basic": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v1/apps/{appId}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
|
@ -217,6 +280,15 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"CrowdfundAppData": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/BasicAppData"
|
||||
},
|
||||
{
|
||||
}
|
||||
]
|
||||
},
|
||||
"BasicAppData": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -368,6 +440,171 @@
|
|||
"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
|
||||
},
|
||||
"customCSSLink": {
|
||||
"type": "string",
|
||||
"description": "Link to a custom CSS stylesheet to be used in the app",
|
||||
"nullable": true
|
||||
},
|
||||
"mainImageUrl": {
|
||||
"type": "string",
|
||||
"description": "URL for image to be used as a cover image for the app",
|
||||
"nullable": true
|
||||
},
|
||||
"embeddedCSS": {
|
||||
"type": "string",
|
||||
"description": "Custom CSS to embed into 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": "Enables background animations on new contributions 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
|
||||
},
|
||||
"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…
Add table
Reference in a new issue