mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 05:54:26 +01:00
Branding updates for 2.0 (#5947)
* Remove deprecated CSS options Closes #5945. * Greenfield: Add brandColor to store APIs Closes #5946. * Migrate file IDs to URLs Closes #5953. * Greenfield: Add CSS and logo URL to store settings API Closes #5945. * Add migration test * Store and Server branding can reference file's via fileid:ID * Add PaymentSoundUrl to Store API --------- Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
This commit is contained in:
@@ -35,13 +35,11 @@ namespace BTCPayServer.Client.Models
|
|||||||
public string CustomAmountPayButtonText { get; set; } = null;
|
public string CustomAmountPayButtonText { get; set; } = null;
|
||||||
public string FixedAmountPayButtonText { get; set; } = null;
|
public string FixedAmountPayButtonText { get; set; } = null;
|
||||||
public string TipText { get; set; } = null;
|
public string TipText { get; set; } = null;
|
||||||
public string CustomCSSLink { get; set; } = null;
|
|
||||||
public string NotificationUrl { get; set; } = null;
|
public string NotificationUrl { get; set; } = null;
|
||||||
public string RedirectUrl { get; set; } = null;
|
public string RedirectUrl { get; set; } = null;
|
||||||
public bool? RedirectAutomatically { get; set; } = null;
|
public bool? RedirectAutomatically { get; set; } = null;
|
||||||
public bool? Archived { get; set; } = null;
|
public bool? Archived { get; set; } = null;
|
||||||
public string FormId { get; set; } = null;
|
public string FormId { get; set; } = null;
|
||||||
public string EmbeddedCSS { get; set; } = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum CrowdfundResetEvery
|
public enum CrowdfundResetEvery
|
||||||
@@ -65,9 +63,7 @@ namespace BTCPayServer.Client.Models
|
|||||||
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
||||||
public DateTimeOffset? EndDate { get; set; } = null;
|
public DateTimeOffset? EndDate { get; set; } = null;
|
||||||
public decimal? TargetAmount { get; set; } = null;
|
public decimal? TargetAmount { get; set; } = null;
|
||||||
public string CustomCSSLink { get; set; } = null;
|
|
||||||
public string MainImageUrl { get; set; } = null;
|
public string MainImageUrl { get; set; } = null;
|
||||||
public string EmbeddedCSS { get; set; } = null;
|
|
||||||
public string NotificationUrl { get; set; } = null;
|
public string NotificationUrl { get; set; } = null;
|
||||||
public string Tagline { get; set; } = null;
|
public string Tagline { get; set; } = null;
|
||||||
public string PerksTemplate { get; set; } = null;
|
public string PerksTemplate { get; set; } = null;
|
||||||
|
|||||||
@@ -17,9 +17,6 @@ namespace BTCPayServer.Client.Models
|
|||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public string Email { get; set; }
|
public string Email { get; set; }
|
||||||
|
|
||||||
public string EmbeddedCSS { get; set; }
|
|
||||||
public string CustomCSSLink { get; set; }
|
|
||||||
public bool AllowCustomPaymentAmounts { get; set; }
|
public bool AllowCustomPaymentAmounts { get; set; }
|
||||||
|
|
||||||
[JsonExtensionData]
|
[JsonExtensionData]
|
||||||
|
|||||||
@@ -30,11 +30,9 @@ namespace BTCPayServer.Client.Models
|
|||||||
public string FixedAmountPayButtonText { get; set; }
|
public string FixedAmountPayButtonText { get; set; }
|
||||||
public string CustomAmountPayButtonText { get; set; }
|
public string CustomAmountPayButtonText { get; set; }
|
||||||
public string TipText { get; set; }
|
public string TipText { get; set; }
|
||||||
public string CustomCSSLink { get; set; }
|
|
||||||
public string NotificationUrl { get; set; }
|
public string NotificationUrl { get; set; }
|
||||||
public string RedirectUrl { get; set; }
|
public string RedirectUrl { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public string EmbeddedCSS { get; set; }
|
|
||||||
public bool? RedirectAutomatically { get; set; }
|
public bool? RedirectAutomatically { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,9 +48,7 @@ namespace BTCPayServer.Client.Models
|
|||||||
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
||||||
public DateTimeOffset? EndDate { get; set; }
|
public DateTimeOffset? EndDate { get; set; }
|
||||||
public decimal? TargetAmount { get; set; }
|
public decimal? TargetAmount { get; set; }
|
||||||
public string CustomCSSLink { get; set; }
|
|
||||||
public string MainImageUrl { get; set; }
|
public string MainImageUrl { get; set; }
|
||||||
public string EmbeddedCSS { get; set; }
|
|
||||||
public string NotificationUrl { get; set; }
|
public string NotificationUrl { get; set; }
|
||||||
public string Tagline { get; set; }
|
public string Tagline { get; set; }
|
||||||
public object Perks { get; set; }
|
public object Perks { get; set; }
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ namespace BTCPayServer.Client.Models
|
|||||||
|
|
||||||
public string Website { get; set; }
|
public string Website { get; set; }
|
||||||
|
|
||||||
|
public string BrandColor { get; set; }
|
||||||
|
public string LogoUrl { get; set; }
|
||||||
|
public string CssUrl { get; set; }
|
||||||
|
public string PaymentSoundUrl { get; set; }
|
||||||
|
|
||||||
public string SupportUrl { get; set; }
|
public string SupportUrl { get; set; }
|
||||||
|
|
||||||
[JsonConverter(typeof(TimeSpanJsonConverter.Seconds))]
|
[JsonConverter(typeof(TimeSpanJsonConverter.Seconds))]
|
||||||
|
|||||||
61
BTCPayServer.Data/Migrations/20240508015052_fileid.cs
Normal file
61
BTCPayServer.Data/Migrations/20240508015052_fileid.cs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
using BTCPayServer.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace BTCPayServer.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
|
[Migration("20240508015052_fileid")]
|
||||||
|
public partial class fileid : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql("""
|
||||||
|
UPDATE "Settings"
|
||||||
|
SET "Value" = jsonb_set(
|
||||||
|
"Value",
|
||||||
|
'{LogoUrl}',
|
||||||
|
to_jsonb('fileid:' || ("Value"->>'LogoFileId'))) - 'LogoFileId'
|
||||||
|
WHERE "Id" = 'BTCPayServer.Services.ThemeSettings'
|
||||||
|
AND "Value"->>'LogoFileId' IS NOT NULL;
|
||||||
|
|
||||||
|
UPDATE "Settings"
|
||||||
|
SET "Value" = jsonb_set(
|
||||||
|
"Value",
|
||||||
|
'{CustomThemeCssUrl}',
|
||||||
|
to_jsonb('fileid:' || ("Value"->>'CustomThemeFileId'))) - 'CustomThemeFileId'
|
||||||
|
WHERE "Id" = 'BTCPayServer.Services.ThemeSettings'
|
||||||
|
AND "Value"->>'CustomThemeFileId' IS NOT NULL;
|
||||||
|
|
||||||
|
UPDATE "Stores"
|
||||||
|
SET "StoreBlob" = jsonb_set(
|
||||||
|
"StoreBlob",
|
||||||
|
'{logoUrl}',
|
||||||
|
to_jsonb('fileid:' || ("StoreBlob"->>'logoFileId'))) - 'logoFileId'
|
||||||
|
WHERE "StoreBlob"->>'logoFileId' IS NOT NULL;
|
||||||
|
|
||||||
|
UPDATE "Stores"
|
||||||
|
SET "StoreBlob" = jsonb_set(
|
||||||
|
"StoreBlob",
|
||||||
|
'{cssUrl}',
|
||||||
|
to_jsonb('fileid:' || ("StoreBlob"->>'cssFileId'))) - 'cssFileId'
|
||||||
|
WHERE "StoreBlob"->>'cssFileId' IS NOT NULL;
|
||||||
|
|
||||||
|
UPDATE "Stores"
|
||||||
|
SET "StoreBlob" = jsonb_set(
|
||||||
|
"StoreBlob",
|
||||||
|
'{paymentSoundUrl}',
|
||||||
|
to_jsonb('fileid:' || ("StoreBlob"->>'soundFileId'))) - 'soundFileId'
|
||||||
|
WHERE "StoreBlob"->>'soundFileId' IS NOT NULL;
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -64,6 +64,11 @@ namespace BTCPayServer.Tests
|
|||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
public Uri ServerUriWithIP
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
public string MySQL
|
public string MySQL
|
||||||
{
|
{
|
||||||
@@ -164,6 +169,7 @@ namespace BTCPayServer.Tests
|
|||||||
await File.WriteAllTextAsync(confPath, config.ToString());
|
await File.WriteAllTextAsync(confPath, config.ToString());
|
||||||
|
|
||||||
ServerUri = new Uri("http://" + HostName + ":" + Port + "/");
|
ServerUri = new Uri("http://" + HostName + ":" + Port + "/");
|
||||||
|
ServerUriWithIP = new Uri("http://127.0.0.1:" + Port + "/");
|
||||||
HttpClient = new HttpClient();
|
HttpClient = new HttpClient();
|
||||||
HttpClient.BaseAddress = ServerUri;
|
HttpClient.BaseAddress = ServerUri;
|
||||||
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development");
|
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development");
|
||||||
|
|||||||
@@ -1366,12 +1366,24 @@ namespace BTCPayServer.Tests
|
|||||||
var newStore = await client.CreateStore(new CreateStoreRequest { Name = "A" });
|
var newStore = await client.CreateStore(new CreateStoreRequest { Name = "A" });
|
||||||
Assert.Equal("A", newStore.Name);
|
Assert.Equal("A", newStore.Name);
|
||||||
|
|
||||||
|
// validate
|
||||||
|
await AssertValidationError(["CssUrl", "LogoUrl", "BrandColor"], async () =>
|
||||||
|
await client.UpdateStore(newStore.Id, new UpdateStoreRequest
|
||||||
|
{
|
||||||
|
CssUrl = "style.css",
|
||||||
|
LogoUrl = "logo.svg",
|
||||||
|
BrandColor = "invalid"
|
||||||
|
}));
|
||||||
|
|
||||||
//update store
|
//update store
|
||||||
Assert.Empty(newStore.PaymentMethodCriteria);
|
Assert.Empty(newStore.PaymentMethodCriteria);
|
||||||
await client.GenerateOnChainWallet(newStore.Id, "BTC", new GenerateOnChainWalletRequest());
|
await client.GenerateOnChainWallet(newStore.Id, "BTC", new GenerateOnChainWalletRequest());
|
||||||
var updatedStore = await client.UpdateStore(newStore.Id, new UpdateStoreRequest
|
var updatedStore = await client.UpdateStore(newStore.Id, new UpdateStoreRequest
|
||||||
{
|
{
|
||||||
Name = "B",
|
Name = "B",
|
||||||
|
CssUrl = "https://example.org/style.css",
|
||||||
|
LogoUrl = "https://example.org/logo.svg",
|
||||||
|
BrandColor = "#003366",
|
||||||
PaymentMethodCriteria = new List<PaymentMethodCriteriaData>
|
PaymentMethodCriteria = new List<PaymentMethodCriteriaData>
|
||||||
{
|
{
|
||||||
new()
|
new()
|
||||||
@@ -1384,6 +1396,9 @@ namespace BTCPayServer.Tests
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
Assert.Equal("B", updatedStore.Name);
|
Assert.Equal("B", updatedStore.Name);
|
||||||
|
Assert.Equal("https://example.org/style.css", updatedStore.CssUrl);
|
||||||
|
Assert.Equal("https://example.org/logo.svg", updatedStore.LogoUrl);
|
||||||
|
Assert.Equal("#003366", updatedStore.BrandColor);
|
||||||
var s = (await client.GetStore(newStore.Id));
|
var s = (await client.GetStore(newStore.Id));
|
||||||
Assert.Equal("B", s.Name);
|
Assert.Equal("B", s.Name);
|
||||||
var pmc = Assert.Single(s.PaymentMethodCriteria);
|
var pmc = Assert.Single(s.PaymentMethodCriteria);
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ namespace BTCPayServer.Tests
|
|||||||
public async Task ModifyPayment(Action<GeneralSettingsViewModel> modify)
|
public async Task ModifyPayment(Action<GeneralSettingsViewModel> modify)
|
||||||
{
|
{
|
||||||
var storeController = GetController<UIStoresController>();
|
var storeController = GetController<UIStoresController>();
|
||||||
var response = storeController.GeneralSettings();
|
var response = await storeController.GeneralSettings();
|
||||||
GeneralSettingsViewModel settings = (GeneralSettingsViewModel)((ViewResult)response).Model;
|
GeneralSettingsViewModel settings = (GeneralSettingsViewModel)((ViewResult)response).Model;
|
||||||
modify(settings);
|
modify(settings);
|
||||||
await storeController.GeneralSettings(settings);
|
await storeController.GeneralSettings(settings);
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.IsType<ViewResult>(
|
Assert.IsType<ViewResult>(
|
||||||
await controller.EditAzureBlobStorageStorageProvider(azureBlobStorageConfiguration));
|
await controller.EditAzureBlobStorageStorageProvider(azureBlobStorageConfiguration));
|
||||||
|
|
||||||
|
|
||||||
var shouldBeRedirectingToAzureStorageConfigPage =
|
var shouldBeRedirectingToAzureStorageConfigPage =
|
||||||
Assert.IsType<RedirectToActionResult>(await controller.Storage());
|
Assert.IsType<RedirectToActionResult>(await controller.Storage());
|
||||||
Assert.Equal(nameof(StorageProvider), shouldBeRedirectingToAzureStorageConfigPage.ActionName);
|
Assert.Equal(nameof(StorageProvider), shouldBeRedirectingToAzureStorageConfigPage.ActionName);
|
||||||
@@ -72,9 +71,8 @@ namespace BTCPayServer.Tests
|
|||||||
await controller.StorageProvider(StorageProvider.AzureBlobStorage.ToString()))
|
await controller.StorageProvider(StorageProvider.AzureBlobStorage.ToString()))
|
||||||
.Model).ConnectionString);
|
.Model).ConnectionString);
|
||||||
|
|
||||||
|
var fileId = await UnitTest1.CanUploadFile(controller);
|
||||||
|
await UnitTest1.CanRemoveFile(controller, fileId);
|
||||||
await UnitTest1.CanUploadRemoveFiles(controller);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(Skip = "Fail on CI")]
|
[Fact(Skip = "Fail on CI")]
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Dapper;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -53,7 +54,6 @@ using BTCPayServer.Services.Rates;
|
|||||||
using BTCPayServer.Storage.Models;
|
using BTCPayServer.Storage.Models;
|
||||||
using BTCPayServer.Storage.Services.Providers.FileSystemStorage.Configuration;
|
using BTCPayServer.Storage.Services.Providers.FileSystemStorage.Configuration;
|
||||||
using BTCPayServer.Storage.ViewModels;
|
using BTCPayServer.Storage.ViewModels;
|
||||||
using ExchangeSharp;
|
|
||||||
using Fido2NetLib;
|
using Fido2NetLib;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
@@ -79,6 +79,7 @@ using CreatePaymentRequestRequest = BTCPayServer.Client.Models.CreatePaymentRequ
|
|||||||
using MarkPayoutRequest = BTCPayServer.Client.Models.MarkPayoutRequest;
|
using MarkPayoutRequest = BTCPayServer.Client.Models.MarkPayoutRequest;
|
||||||
using PaymentRequestData = BTCPayServer.Client.Models.PaymentRequestData;
|
using PaymentRequestData = BTCPayServer.Client.Models.PaymentRequestData;
|
||||||
using RatesViewModel = BTCPayServer.Models.StoreViewModels.RatesViewModel;
|
using RatesViewModel = BTCPayServer.Models.StoreViewModels.RatesViewModel;
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
|
||||||
namespace BTCPayServer.Tests
|
namespace BTCPayServer.Tests
|
||||||
{
|
{
|
||||||
@@ -297,7 +298,7 @@ namespace BTCPayServer.Tests
|
|||||||
|
|
||||||
// Set tolerance to 50%
|
// Set tolerance to 50%
|
||||||
var stores = user.GetController<UIStoresController>();
|
var stores = user.GetController<UIStoresController>();
|
||||||
var response = stores.GeneralSettings();
|
var response = await stores.GeneralSettings();
|
||||||
var vm = Assert.IsType<GeneralSettingsViewModel>(Assert.IsType<ViewResult>(response).Model);
|
var vm = Assert.IsType<GeneralSettingsViewModel>(Assert.IsType<ViewResult>(response).Model);
|
||||||
Assert.Equal(0.0, vm.PaymentTolerance);
|
Assert.Equal(0.0, vm.PaymentTolerance);
|
||||||
vm.PaymentTolerance = 50.0;
|
vm.PaymentTolerance = 50.0;
|
||||||
@@ -439,7 +440,7 @@ namespace BTCPayServer.Tests
|
|||||||
var user = tester.NewAccount();
|
var user = tester.NewAccount();
|
||||||
await user.GrantAccessAsync(true);
|
await user.GrantAccessAsync(true);
|
||||||
var storeController = user.GetController<UIStoresController>();
|
var storeController = user.GetController<UIStoresController>();
|
||||||
var storeResponse = storeController.GeneralSettings();
|
var storeResponse = await storeController.GeneralSettings();
|
||||||
Assert.IsType<ViewResult>(storeResponse);
|
Assert.IsType<ViewResult>(storeResponse);
|
||||||
Assert.IsType<ViewResult>(storeController.SetupLightningNode(user.StoreId, "BTC"));
|
Assert.IsType<ViewResult>(storeController.SetupLightningNode(user.StoreId, "BTC"));
|
||||||
|
|
||||||
@@ -838,7 +839,7 @@ namespace BTCPayServer.Tests
|
|||||||
|
|
||||||
var time = invoice.InvoiceTime;
|
var time = invoice.InvoiceTime;
|
||||||
AssertSearchInvoice(acc, true, invoice.Id, $"startdate:{time.ToString("yyyy-MM-dd HH:mm:ss")}");
|
AssertSearchInvoice(acc, true, invoice.Id, $"startdate:{time.ToString("yyyy-MM-dd HH:mm:ss")}");
|
||||||
AssertSearchInvoice(acc, true, invoice.Id, $"enddate:{time.ToStringLowerInvariant()}");
|
AssertSearchInvoice(acc, true, invoice.Id, $"enddate:{time.ToString().ToLowerInvariant()}");
|
||||||
AssertSearchInvoice(acc, false, invoice.Id,
|
AssertSearchInvoice(acc, false, invoice.Id,
|
||||||
$"startdate:{time.AddSeconds(1).ToString("yyyy-MM-dd HH:mm:ss")}");
|
$"startdate:{time.AddSeconds(1).ToString("yyyy-MM-dd HH:mm:ss")}");
|
||||||
AssertSearchInvoice(acc, false, invoice.Id,
|
AssertSearchInvoice(acc, false, invoice.Id,
|
||||||
@@ -1499,8 +1500,7 @@ namespace BTCPayServer.Tests
|
|||||||
var btcMethod = PaymentTypes.CHAIN.GetPaymentMethodId("BTC").ToString();
|
var btcMethod = PaymentTypes.CHAIN.GetPaymentMethodId("BTC").ToString();
|
||||||
|
|
||||||
// We allow BTC and LN, but not BTC under 5 USD, so only LN should be in the invoice
|
// We allow BTC and LN, but not BTC under 5 USD, so only LN should be in the invoice
|
||||||
var vm = Assert.IsType<CheckoutAppearanceViewModel>(Assert
|
var vm = await user.GetController<UIStoresController>().CheckoutAppearance().AssertViewModelAsync<CheckoutAppearanceViewModel>();
|
||||||
.IsType<ViewResult>(user.GetController<UIStoresController>().CheckoutAppearance()).Model);
|
|
||||||
Assert.Equal(2, vm.PaymentMethodCriteria.Count);
|
Assert.Equal(2, vm.PaymentMethodCriteria.Count);
|
||||||
var criteria = Assert.Single(vm.PaymentMethodCriteria.Where(m => m.PaymentMethod == btcMethod.ToString()));
|
var criteria = Assert.Single(vm.PaymentMethodCriteria.Where(m => m.PaymentMethod == btcMethod.ToString()));
|
||||||
Assert.Equal(PaymentTypes.CHAIN.GetPaymentMethodId("BTC").ToString(), criteria.PaymentMethod);
|
Assert.Equal(PaymentTypes.CHAIN.GetPaymentMethodId("BTC").ToString(), criteria.PaymentMethod);
|
||||||
@@ -1527,8 +1527,7 @@ namespace BTCPayServer.Tests
|
|||||||
// Let's replicate https://github.com/btcpayserver/btcpayserver/issues/2963
|
// Let's replicate https://github.com/btcpayserver/btcpayserver/issues/2963
|
||||||
// We allow BTC for more than 5 USD, and LN for less than 150. The default is LN, so the default
|
// We allow BTC for more than 5 USD, and LN for less than 150. The default is LN, so the default
|
||||||
// payment method should be LN.
|
// payment method should be LN.
|
||||||
vm = Assert.IsType<CheckoutAppearanceViewModel>(Assert
|
vm = await user.GetController<UIStoresController>().CheckoutAppearance().AssertViewModelAsync<CheckoutAppearanceViewModel>();
|
||||||
.IsType<ViewResult>(user.GetController<UIStoresController>().CheckoutAppearance()).Model);
|
|
||||||
vm.DefaultPaymentMethod = lnMethod;
|
vm.DefaultPaymentMethod = lnMethod;
|
||||||
criteria = vm.PaymentMethodCriteria.First();
|
criteria = vm.PaymentMethodCriteria.First();
|
||||||
criteria.Value = "150 USD";
|
criteria.Value = "150 USD";
|
||||||
@@ -1640,7 +1639,7 @@ namespace BTCPayServer.Tests
|
|||||||
user.GrantAccess(true);
|
user.GrantAccess(true);
|
||||||
user.RegisterLightningNode(cryptoCode);
|
user.RegisterLightningNode(cryptoCode);
|
||||||
user.SetLNUrl(cryptoCode, false);
|
user.SetLNUrl(cryptoCode, false);
|
||||||
var vm = user.GetController<UIStoresController>().CheckoutAppearance().AssertViewModel<CheckoutAppearanceViewModel>();
|
var vm = await user.GetController<UIStoresController>().CheckoutAppearance().AssertViewModelAsync<CheckoutAppearanceViewModel>();
|
||||||
var criteria = Assert.Single(vm.PaymentMethodCriteria);
|
var criteria = Assert.Single(vm.PaymentMethodCriteria);
|
||||||
Assert.Equal(PaymentTypes.LN.GetPaymentMethodId(cryptoCode).ToString(), criteria.PaymentMethod);
|
Assert.Equal(PaymentTypes.LN.GetPaymentMethodId(cryptoCode).ToString(), criteria.PaymentMethod);
|
||||||
criteria.Value = "2 USD";
|
criteria.Value = "2 USD";
|
||||||
@@ -1660,7 +1659,7 @@ namespace BTCPayServer.Tests
|
|||||||
// Activating LNUrl, we should still have only 1 payment criteria that can be set.
|
// Activating LNUrl, we should still have only 1 payment criteria that can be set.
|
||||||
user.RegisterLightningNode(cryptoCode);
|
user.RegisterLightningNode(cryptoCode);
|
||||||
user.SetLNUrl(cryptoCode, true);
|
user.SetLNUrl(cryptoCode, true);
|
||||||
vm = user.GetController<UIStoresController>().CheckoutAppearance().AssertViewModel<CheckoutAppearanceViewModel>();
|
vm = await user.GetController<UIStoresController>().CheckoutAppearance().AssertViewModelAsync<CheckoutAppearanceViewModel>();
|
||||||
criteria = Assert.Single(vm.PaymentMethodCriteria);
|
criteria = Assert.Single(vm.PaymentMethodCriteria);
|
||||||
Assert.Equal(PaymentTypes.LN.GetPaymentMethodId(cryptoCode).ToString(), criteria.PaymentMethod);
|
Assert.Equal(PaymentTypes.LN.GetPaymentMethodId(cryptoCode).ToString(), criteria.PaymentMethod);
|
||||||
Assert.IsType<RedirectToActionResult>(user.GetController<UIStoresController>().CheckoutAppearance(vm).Result);
|
Assert.IsType<RedirectToActionResult>(user.GetController<UIStoresController>().CheckoutAppearance(vm).Result);
|
||||||
@@ -2507,6 +2506,90 @@ namespace BTCPayServer.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact(Timeout = LongRunningTestTimeout)]
|
||||||
|
[Trait("Integration", "Integration")]
|
||||||
|
public async Task CanMigrateFileIds()
|
||||||
|
{
|
||||||
|
using var tester = CreateServerTester(newDb: true);
|
||||||
|
tester.DeleteStore = false;
|
||||||
|
await tester.StartAsync();
|
||||||
|
|
||||||
|
var user = tester.NewAccount();
|
||||||
|
await user.GrantAccessAsync();
|
||||||
|
|
||||||
|
using (var ctx = tester.PayTester.GetService<ApplicationDbContextFactory>().CreateContext())
|
||||||
|
{
|
||||||
|
var storeConfig = """
|
||||||
|
{
|
||||||
|
"spread": 0.0,
|
||||||
|
"cssFileId": "2a51c49a-9d54-4013-80a2-3f6e69d08523",
|
||||||
|
"logoFileId": "8f890691-87f9-4c65-80e5-3b7ffaa3551f",
|
||||||
|
"soundFileId": "62bc4757-b92b-4a3b-a8ab-0e9b693d6a29",
|
||||||
|
"networkFeeMode": "MultiplePaymentsOnly",
|
||||||
|
"defaultCurrency": "USD",
|
||||||
|
"showStoreHeader": true,
|
||||||
|
"celebratePayment": true,
|
||||||
|
"paymentTolerance": 0.0,
|
||||||
|
"invoiceExpiration": 15,
|
||||||
|
"preferredExchange": "kraken",
|
||||||
|
"showRecommendedFee": true,
|
||||||
|
"monitoringExpiration": 1440,
|
||||||
|
"showPayInWalletButton": true,
|
||||||
|
"displayExpirationTimer": 5,
|
||||||
|
"excludedPaymentMethods": null,
|
||||||
|
"recommendedFeeBlockTarget": 1
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
var serverConfig = """
|
||||||
|
{
|
||||||
|
"CssUri": null,
|
||||||
|
"FirstRun": false,
|
||||||
|
"LogoFileId": "ce71d90a-dd90-40a3-b1f0-96d00c9abb52",
|
||||||
|
"CustomTheme": true,
|
||||||
|
"CustomThemeCssUri": null,
|
||||||
|
"CustomThemeFileId": "9b00f4ed-914b-437b-abd2-9a90c1b22c34",
|
||||||
|
"CustomThemeExtension": 0
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
await ctx.Database.GetDbConnection().ExecuteAsync("""
|
||||||
|
UPDATE "Stores" SET "StoreBlob"=@storeConfig::JSONB WHERE "Id"=@storeId;
|
||||||
|
""", new { storeId = user.StoreId, storeConfig });
|
||||||
|
await ctx.Database.GetDbConnection().ExecuteAsync("""
|
||||||
|
UPDATE "Settings" SET "Value"=@serverConfig::JSONB WHERE "Id"='BTCPayServer.Services.ThemeSettings';
|
||||||
|
""", new { serverConfig });
|
||||||
|
await ctx.Database.GetDbConnection().ExecuteAsync("""
|
||||||
|
INSERT INTO "Files" VALUES (@id, @fileName, @id || '-' || @fileName, NOW(), @userId);
|
||||||
|
""",
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new { id = "2a51c49a-9d54-4013-80a2-3f6e69d08523", fileName = "store.css", userId = user.UserId },
|
||||||
|
new { id = "8f890691-87f9-4c65-80e5-3b7ffaa3551f", fileName = "store.png", userId = user.UserId },
|
||||||
|
new { id = "ce71d90a-dd90-40a3-b1f0-96d00c9abb52", fileName = "admin.png", userId = user.UserId },
|
||||||
|
new { id = "9b00f4ed-914b-437b-abd2-9a90c1b22c34", fileName = "admin.css", userId = user.UserId },
|
||||||
|
new { id = "62bc4757-b92b-4a3b-a8ab-0e9b693d6a29", fileName = "store.mp3", userId = user.UserId },
|
||||||
|
});
|
||||||
|
await ctx.Database.GetDbConnection().ExecuteAsync("""
|
||||||
|
DELETE FROM "__EFMigrationsHistory" WHERE "MigrationId"='20240508015052_fileid'
|
||||||
|
""");
|
||||||
|
await ctx.Database.MigrateAsync();
|
||||||
|
((MemoryCache)tester.PayTester.GetService<IMemoryCache>()).Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
var controller = tester.PayTester.GetController<UIStoresController>(user.UserId, user.StoreId);
|
||||||
|
var vm = await controller.GeneralSettings().AssertViewModelAsync<GeneralSettingsViewModel>();
|
||||||
|
Assert.Equal(tester.PayTester.ServerUriWithIP + "LocalStorage/8f890691-87f9-4c65-80e5-3b7ffaa3551f-store.png", vm.LogoUrl);
|
||||||
|
Assert.Equal(tester.PayTester.ServerUriWithIP + "LocalStorage/2a51c49a-9d54-4013-80a2-3f6e69d08523-store.css", vm.CssUrl);
|
||||||
|
|
||||||
|
var vm2 = await controller.CheckoutAppearance().AssertViewModelAsync<CheckoutAppearanceViewModel>();
|
||||||
|
Assert.Equal(tester.PayTester.ServerUriWithIP + "LocalStorage/62bc4757-b92b-4a3b-a8ab-0e9b693d6a29-store.mp3", vm2.PaymentSoundUrl);
|
||||||
|
|
||||||
|
var serverController = tester.PayTester.GetController<UIServerController>();
|
||||||
|
var branding = await serverController.Branding().AssertViewModelAsync<BrandingViewModel>();
|
||||||
|
|
||||||
|
Assert.Equal(tester.PayTester.ServerUriWithIP + "LocalStorage/ce71d90a-dd90-40a3-b1f0-96d00c9abb52-admin.png", branding.LogoUrl);
|
||||||
|
Assert.Equal(tester.PayTester.ServerUriWithIP + "LocalStorage/9b00f4ed-914b-437b-abd2-9a90c1b22c34-admin.css", branding.CustomThemeCssUrl);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact(Timeout = LongRunningTestTimeout)]
|
[Fact(Timeout = LongRunningTestTimeout)]
|
||||||
[Trait("Integration", "Integration")]
|
[Trait("Integration", "Integration")]
|
||||||
public async Task CanDoLightningInternalNodeMigration()
|
public async Task CanDoLightningInternalNodeMigration()
|
||||||
@@ -2943,14 +3026,14 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Equal(StorageProvider.FileSystem,
|
Assert.Equal(StorageProvider.FileSystem,
|
||||||
shouldBeRedirectingToLocalStorageConfigPage.RouteValues["provider"]);
|
shouldBeRedirectingToLocalStorageConfigPage.RouteValues["provider"]);
|
||||||
|
|
||||||
await CanUploadRemoveFiles(controller);
|
var fileId = await CanUploadFile(controller);
|
||||||
|
await CanRemoveFile(controller, fileId);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static async Task CanUploadRemoveFiles(UIServerController controller)
|
internal static async Task<string> CanUploadFile(UIServerController controller)
|
||||||
{
|
{
|
||||||
var fileContent = "content";
|
var fileContent = "content";
|
||||||
List<IFormFile> fileList = new List<IFormFile>();
|
var fileList = new List<IFormFile> { TestUtils.GetFormFile("uploadtestfile1.txt", fileContent) };
|
||||||
fileList.Add(TestUtils.GetFormFile("uploadtestfile1.txt", fileContent));
|
|
||||||
|
|
||||||
var uploadFormFileResult = Assert.IsType<RedirectToActionResult>(await controller.CreateFiles(fileList));
|
var uploadFormFileResult = Assert.IsType<RedirectToActionResult>(await controller.CreateFiles(fileList));
|
||||||
Assert.True(uploadFormFileResult.RouteValues.ContainsKey("fileIds"));
|
Assert.True(uploadFormFileResult.RouteValues.ContainsKey("fileIds"));
|
||||||
@@ -2966,7 +3049,6 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.True(viewFilesViewModel.DirectUrlByFiles.ContainsKey(fileId));
|
Assert.True(viewFilesViewModel.DirectUrlByFiles.ContainsKey(fileId));
|
||||||
Assert.NotEmpty(viewFilesViewModel.DirectUrlByFiles[fileId]);
|
Assert.NotEmpty(viewFilesViewModel.DirectUrlByFiles[fileId]);
|
||||||
|
|
||||||
|
|
||||||
//verify file is available and the same
|
//verify file is available and the same
|
||||||
using var net = new HttpClient();
|
using var net = new HttpClient();
|
||||||
var data = await net.GetStringAsync(new Uri(viewFilesViewModel.DirectUrlByFiles[fileId]));
|
var data = await net.GetStringAsync(new Uri(viewFilesViewModel.DirectUrlByFiles[fileId]));
|
||||||
@@ -2991,17 +3073,20 @@ namespace BTCPayServer.Tests
|
|||||||
data = await net.GetStringAsync(new Uri(url));
|
data = await net.GetStringAsync(new Uri(url));
|
||||||
Assert.Equal(fileContent, data);
|
Assert.Equal(fileContent, data);
|
||||||
|
|
||||||
|
return fileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static async Task CanRemoveFile(UIServerController controller, string fileId)
|
||||||
|
{
|
||||||
//delete file
|
//delete file
|
||||||
Assert.IsType<RedirectToActionResult>(await controller.DeleteFile(fileId));
|
Assert.IsType<RedirectToActionResult>(await controller.DeleteFile(fileId));
|
||||||
statusMessageModel = controller.TempData.GetStatusMessageModel();
|
var statusMessageModel = controller.TempData.GetStatusMessageModel();
|
||||||
Assert.NotNull(statusMessageModel);
|
Assert.NotNull(statusMessageModel);
|
||||||
|
|
||||||
Assert.Equal(StatusMessageModel.StatusSeverity.Success, statusMessageModel.Severity);
|
Assert.Equal(StatusMessageModel.StatusSeverity.Success, statusMessageModel.Severity);
|
||||||
|
|
||||||
//attempt to fetch deleted file
|
//attempt to fetch deleted file
|
||||||
viewFilesViewModel =
|
var viewFilesViewModel =
|
||||||
Assert.IsType<ViewFilesViewModel>(Assert.IsType<ViewResult>(await controller.Files(new string[] { fileId })).Model);
|
Assert.IsType<ViewFilesViewModel>(Assert.IsType<ViewResult>(await controller.Files([fileId])).Model);
|
||||||
Assert.Null(viewFilesViewModel.DirectUrlByFiles);
|
Assert.Null(viewFilesViewModel.DirectUrlByFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
@using BTCPayServer.Services
|
@using BTCPayServer.Services
|
||||||
@using BTCPayServer.Abstractions.Contracts
|
|
||||||
@inject ThemeSettings Theme
|
@inject ThemeSettings Theme
|
||||||
@inject IFileService FileService
|
@inject UriResolver UriResolver
|
||||||
@model BTCPayServer.Components.MainLogo.MainLogoViewModel
|
@model BTCPayServer.Components.MainLogo.MainLogoViewModel
|
||||||
|
|
||||||
@if (!string.IsNullOrEmpty(Theme.LogoFileId))
|
@if (Theme.LogoUrl is not null)
|
||||||
{
|
{
|
||||||
var logoSrc = await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Theme.LogoFileId);
|
var logoUrl = await UriResolver.Resolve(this.Context.Request.GetAbsoluteRootUri(), Theme.LogoUrl);
|
||||||
<img src="@logoSrc" alt="BTCPay Server" class="main-logo main-logo-custom @Model.CssClass" />
|
<img src="@logoUrl" alt="BTCPay Server" class="main-logo main-logo-custom @Model.CssClass" />
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
@using BTCPayServer.Abstractions.Contracts
|
|
||||||
@using BTCPayServer.Client
|
|
||||||
@using BTCPayServer.Components.MainLogo
|
@using BTCPayServer.Components.MainLogo
|
||||||
@using BTCPayServer.Services
|
@using BTCPayServer.Services
|
||||||
@using BTCPayServer.Views.Server
|
@using BTCPayServer.Views.Server
|
||||||
@using BTCPayServer.Views.Stores
|
@using BTCPayServer.Views.Stores
|
||||||
@inject BTCPayServerEnvironment Env
|
@inject BTCPayServerEnvironment Env
|
||||||
@inject IFileService FileService
|
|
||||||
@model BTCPayServer.Components.StoreSelector.StoreSelectorViewModel
|
@model BTCPayServer.Components.StoreSelector.StoreSelectorViewModel
|
||||||
@functions {
|
@functions {
|
||||||
@* ReSharper disable once CSharpWarnings::CS1998 *@
|
@* ReSharper disable once CSharpWarnings::CS1998 *@
|
||||||
@@ -39,9 +36,9 @@ else
|
|||||||
<div id="StoreSelector">
|
<div id="StoreSelector">
|
||||||
<div id="StoreSelectorDropdown" class="dropdown only-for-js">
|
<div id="StoreSelectorDropdown" class="dropdown only-for-js">
|
||||||
<button id="StoreSelectorToggle" class="btn btn-secondary dropdown-toggle rounded-pill px-3 @(Model.CurrentStoreId == null ? "empty-state" : "")" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
<button id="StoreSelectorToggle" class="btn btn-secondary dropdown-toggle rounded-pill px-3 @(Model.CurrentStoreId == null ? "empty-state" : "")" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
@if (!string.IsNullOrEmpty(Model.CurrentStoreLogoFileId))
|
@if (!string.IsNullOrEmpty(Model.CurrentStoreLogoUrl))
|
||||||
{
|
{
|
||||||
<img class="logo" src="@(await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.CurrentStoreLogoFileId))" alt="@Model.CurrentDisplayName" />
|
<img class="logo" src="@Model.CurrentStoreLogoUrl" alt="@Model.CurrentDisplayName" />
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Abstractions.Contracts;
|
||||||
|
using BTCPayServer.Abstractions.Extensions;
|
||||||
using BTCPayServer.Client;
|
using BTCPayServer.Client;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
using BTCPayServer.Payments.Bitcoin;
|
using BTCPayServer.Payments.Bitcoin;
|
||||||
|
using BTCPayServer.Services;
|
||||||
using BTCPayServer.Services.Invoices;
|
using BTCPayServer.Services.Invoices;
|
||||||
using BTCPayServer.Services.Stores;
|
using BTCPayServer.Services.Stores;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
@@ -14,13 +17,16 @@ namespace BTCPayServer.Components.StoreSelector
|
|||||||
public class StoreSelector : ViewComponent
|
public class StoreSelector : ViewComponent
|
||||||
{
|
{
|
||||||
private readonly StoreRepository _storeRepo;
|
private readonly StoreRepository _storeRepo;
|
||||||
|
private readonly UriResolver _uriResolver;
|
||||||
private readonly UserManager<ApplicationUser> _userManager;
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
|
||||||
public StoreSelector(
|
public StoreSelector(
|
||||||
StoreRepository storeRepo,
|
StoreRepository storeRepo,
|
||||||
|
UriResolver uriResolver,
|
||||||
UserManager<ApplicationUser> userManager)
|
UserManager<ApplicationUser> userManager)
|
||||||
{
|
{
|
||||||
_storeRepo = storeRepo;
|
_storeRepo = storeRepo;
|
||||||
|
_uriResolver = uriResolver;
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +56,7 @@ namespace BTCPayServer.Components.StoreSelector
|
|||||||
Options = options,
|
Options = options,
|
||||||
CurrentStoreId = currentStore?.Id,
|
CurrentStoreId = currentStore?.Id,
|
||||||
CurrentDisplayName = currentStore?.StoreName,
|
CurrentDisplayName = currentStore?.StoreName,
|
||||||
CurrentStoreLogoFileId = blob?.LogoFileId,
|
CurrentStoreLogoUrl = await _uriResolver.Resolve(Request.GetAbsoluteRootUri(), blob?.LogoUrl),
|
||||||
ArchivedCount = archivedCount
|
ArchivedCount = archivedCount
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace BTCPayServer.Components.StoreSelector
|
|||||||
{
|
{
|
||||||
public List<StoreSelectorOption> Options { get; set; }
|
public List<StoreSelectorOption> Options { get; set; }
|
||||||
public string CurrentStoreId { get; set; }
|
public string CurrentStoreId { get; set; }
|
||||||
public string CurrentStoreLogoFileId { get; set; }
|
public string CurrentStoreLogoUrl { get; set; }
|
||||||
public string CurrentDisplayName { get; set; }
|
public string CurrentDisplayName { get; set; }
|
||||||
public int ArchivedCount { get; set; }
|
public int ArchivedCount { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -233,9 +233,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
Description = request.Description?.Trim(),
|
Description = request.Description?.Trim(),
|
||||||
EndDate = request.EndDate?.UtcDateTime,
|
EndDate = request.EndDate?.UtcDateTime,
|
||||||
TargetAmount = request.TargetAmount,
|
TargetAmount = request.TargetAmount,
|
||||||
CustomCSSLink = request.CustomCSSLink?.Trim(),
|
|
||||||
MainImageUrl = request.MainImageUrl?.Trim(),
|
MainImageUrl = request.MainImageUrl?.Trim(),
|
||||||
EmbeddedCSS = request.EmbeddedCSS?.Trim(),
|
|
||||||
NotificationUrl = request.NotificationUrl?.Trim(),
|
NotificationUrl = request.NotificationUrl?.Trim(),
|
||||||
Tagline = request.Tagline?.Trim(),
|
Tagline = request.Tagline?.Trim(),
|
||||||
PerksTemplate = request.PerksTemplate is not null ? AppService.SerializeTemplate(AppService.Parse(request.PerksTemplate.Trim())) : null,
|
PerksTemplate = request.PerksTemplate is not null ? AppService.SerializeTemplate(AppService.Parse(request.PerksTemplate.Trim())) : null,
|
||||||
@@ -272,11 +270,9 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
ButtonText = request.FixedAmountPayButtonText ?? PointOfSaleSettings.BUTTON_TEXT_DEF,
|
ButtonText = request.FixedAmountPayButtonText ?? PointOfSaleSettings.BUTTON_TEXT_DEF,
|
||||||
CustomButtonText = request.CustomAmountPayButtonText ?? PointOfSaleSettings.CUSTOM_BUTTON_TEXT_DEF,
|
CustomButtonText = request.CustomAmountPayButtonText ?? PointOfSaleSettings.CUSTOM_BUTTON_TEXT_DEF,
|
||||||
CustomTipText = request.TipText ?? PointOfSaleSettings.CUSTOM_TIP_TEXT_DEF,
|
CustomTipText = request.TipText ?? PointOfSaleSettings.CUSTOM_TIP_TEXT_DEF,
|
||||||
CustomCSSLink = request.CustomCSSLink,
|
|
||||||
NotificationUrl = request.NotificationUrl,
|
NotificationUrl = request.NotificationUrl,
|
||||||
RedirectUrl = request.RedirectUrl,
|
RedirectUrl = request.RedirectUrl,
|
||||||
Description = request.Description,
|
Description = request.Description,
|
||||||
EmbeddedCSS = request.EmbeddedCSS,
|
|
||||||
RedirectAutomatically = request.RedirectAutomatically,
|
RedirectAutomatically = request.RedirectAutomatically,
|
||||||
FormId = request.FormId
|
FormId = request.FormId
|
||||||
};
|
};
|
||||||
@@ -341,11 +337,9 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
FixedAmountPayButtonText = settings.ButtonText,
|
FixedAmountPayButtonText = settings.ButtonText,
|
||||||
CustomAmountPayButtonText = settings.CustomButtonText,
|
CustomAmountPayButtonText = settings.CustomButtonText,
|
||||||
TipText = settings.CustomTipText,
|
TipText = settings.CustomTipText,
|
||||||
CustomCSSLink = settings.CustomCSSLink,
|
|
||||||
NotificationUrl = settings.NotificationUrl,
|
NotificationUrl = settings.NotificationUrl,
|
||||||
RedirectUrl = settings.RedirectUrl,
|
RedirectUrl = settings.RedirectUrl,
|
||||||
Description = settings.Description,
|
Description = settings.Description,
|
||||||
EmbeddedCSS = settings.EmbeddedCSS,
|
|
||||||
RedirectAutomatically = settings.RedirectAutomatically ?? false,
|
RedirectAutomatically = settings.RedirectAutomatically ?? false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -399,9 +393,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
Description = settings.Description,
|
Description = settings.Description,
|
||||||
EndDate = settings.EndDate,
|
EndDate = settings.EndDate,
|
||||||
TargetAmount = settings.TargetAmount,
|
TargetAmount = settings.TargetAmount,
|
||||||
CustomCSSLink = settings.CustomCSSLink,
|
|
||||||
MainImageUrl = settings.MainImageUrl,
|
MainImageUrl = settings.MainImageUrl,
|
||||||
EmbeddedCSS = settings.EmbeddedCSS,
|
|
||||||
NotificationUrl = settings.NotificationUrl,
|
NotificationUrl = settings.NotificationUrl,
|
||||||
Tagline = settings.Tagline,
|
Tagline = settings.Tagline,
|
||||||
Perks = JsonConvert.DeserializeObject(
|
Perks = JsonConvert.DeserializeObject(
|
||||||
|
|||||||
@@ -234,9 +234,6 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
if (string.IsNullOrEmpty(data.Title))
|
if (string.IsNullOrEmpty(data.Title))
|
||||||
ModelState.AddModelError(nameof(data.Title), "Title is required");
|
ModelState.AddModelError(nameof(data.Title), "Title is required");
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(data.CustomCSSLink) && data.CustomCSSLink.Length > 500)
|
|
||||||
ModelState.AddModelError(nameof(data.CustomCSSLink), "CustomCSSLink is 500 chars max");
|
|
||||||
|
|
||||||
return !ModelState.IsValid ? this.CreateValidationError(ModelState) : null;
|
return !ModelState.IsValid ? this.CreateValidationError(ModelState) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,8 +254,6 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
ExpiryDate = blob.ExpiryDate,
|
ExpiryDate = blob.ExpiryDate,
|
||||||
Email = blob.Email,
|
Email = blob.Email,
|
||||||
AllowCustomPaymentAmounts = blob.AllowCustomPaymentAmounts,
|
AllowCustomPaymentAmounts = blob.AllowCustomPaymentAmounts,
|
||||||
EmbeddedCSS = blob.EmbeddedCSS,
|
|
||||||
CustomCSSLink = blob.CustomCSSLink,
|
|
||||||
FormResponse = blob.FormResponse,
|
FormResponse = blob.FormResponse,
|
||||||
FormId = blob.FormId
|
FormId = blob.FormId
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -116,6 +116,10 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
Name = data.StoreName,
|
Name = data.StoreName,
|
||||||
Website = data.StoreWebsite,
|
Website = data.StoreWebsite,
|
||||||
Archived = data.Archived,
|
Archived = data.Archived,
|
||||||
|
BrandColor = storeBlob.BrandColor,
|
||||||
|
CssUrl = storeBlob.CssUrl?.ToString(),
|
||||||
|
LogoUrl = storeBlob.LogoUrl?.ToString(),
|
||||||
|
PaymentSoundUrl = storeBlob.PaymentSoundUrl?.ToString(),
|
||||||
SupportUrl = storeBlob.StoreSupportUrl,
|
SupportUrl = storeBlob.StoreSupportUrl,
|
||||||
SpeedPolicy = data.SpeedPolicy,
|
SpeedPolicy = data.SpeedPolicy,
|
||||||
DefaultPaymentMethod = data.GetDefaultPaymentId()?.ToString(),
|
DefaultPaymentMethod = data.GetDefaultPaymentId()?.ToString(),
|
||||||
@@ -192,6 +196,10 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
blob.LightningDescriptionTemplate = restModel.LightningDescriptionTemplate;
|
blob.LightningDescriptionTemplate = restModel.LightningDescriptionTemplate;
|
||||||
blob.PaymentTolerance = restModel.PaymentTolerance;
|
blob.PaymentTolerance = restModel.PaymentTolerance;
|
||||||
blob.PayJoinEnabled = restModel.PayJoinEnabled;
|
blob.PayJoinEnabled = restModel.PayJoinEnabled;
|
||||||
|
blob.BrandColor = restModel.BrandColor;
|
||||||
|
blob.LogoUrl = restModel.LogoUrl is null ? null : UnresolvedUri.Create(restModel.LogoUrl);
|
||||||
|
blob.CssUrl = restModel.CssUrl is null ? null : UnresolvedUri.Create(restModel.CssUrl);
|
||||||
|
blob.PaymentSoundUrl = restModel.PaymentSoundUrl is null ? null : UnresolvedUri.Create(restModel.PaymentSoundUrl);
|
||||||
if (restModel.AutoDetectLanguage.HasValue)
|
if (restModel.AutoDetectLanguage.HasValue)
|
||||||
blob.AutoDetectLanguage = restModel.AutoDetectLanguage.Value;
|
blob.AutoDetectLanguage = restModel.AutoDetectLanguage.Value;
|
||||||
if (restModel.ShowPayInWalletButton.HasValue)
|
if (restModel.ShowPayInWalletButton.HasValue)
|
||||||
@@ -237,6 +245,18 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
{
|
{
|
||||||
ModelState.AddModelError(nameof(request.Website), "Website is not a valid url");
|
ModelState.AddModelError(nameof(request.Website), "Website is not a valid url");
|
||||||
}
|
}
|
||||||
|
if (!string.IsNullOrEmpty(request.LogoUrl) && !Uri.TryCreate(request.LogoUrl, UriKind.Absolute, out _))
|
||||||
|
{
|
||||||
|
ModelState.AddModelError(nameof(request.LogoUrl), "Logo is not a valid url");
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(request.CssUrl) && !Uri.TryCreate(request.CssUrl, UriKind.Absolute, out _))
|
||||||
|
{
|
||||||
|
ModelState.AddModelError(nameof(request.CssUrl), "CSS is not a valid url");
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(request.BrandColor) && !ColorPalette.IsValid(request.BrandColor))
|
||||||
|
{
|
||||||
|
ModelState.AddModelError(nameof(request.BrandColor), "Brand color is not a valid HEX Color (e.g. #F7931A)");
|
||||||
|
}
|
||||||
if (request.InvoiceExpiration < TimeSpan.FromMinutes(1) && request.InvoiceExpiration > TimeSpan.FromMinutes(60 * 24 * 24))
|
if (request.InvoiceExpiration < TimeSpan.FromMinutes(1) && request.InvoiceExpiration > TimeSpan.FromMinutes(60 * 24 * 24))
|
||||||
ModelState.AddModelError(nameof(request.InvoiceExpiration), "InvoiceExpiration can only be between 1 and 34560 mins");
|
ModelState.AddModelError(nameof(request.InvoiceExpiration), "InvoiceExpiration can only be between 1 and 34560 mins");
|
||||||
if (request.DisplayExpirationTimer < TimeSpan.FromMinutes(1) && request.DisplayExpirationTimer > TimeSpan.FromMinutes(60 * 24 * 24))
|
if (request.DisplayExpirationTimer < TimeSpan.FromMinutes(1) && request.DisplayExpirationTimer > TimeSpan.FromMinutes(60 * 24 * 24))
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ namespace BTCPayServer.Controllers
|
|||||||
Currency = i.Currency,
|
Currency = i.Currency,
|
||||||
Timestamp = i.InvoiceTime,
|
Timestamp = i.InvoiceTime,
|
||||||
StoreName = store.StoreName,
|
StoreName = store.StoreName,
|
||||||
StoreBranding = new StoreBrandingViewModel(storeBlob),
|
StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, storeBlob),
|
||||||
ReceiptOptions = receipt
|
ReceiptOptions = receipt
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -889,7 +889,7 @@ namespace BTCPayServer.Controllers
|
|||||||
DefaultLang = lang ?? invoice.DefaultLanguage ?? storeBlob.DefaultLang ?? "en",
|
DefaultLang = lang ?? invoice.DefaultLanguage ?? storeBlob.DefaultLang ?? "en",
|
||||||
ShowPayInWalletButton = storeBlob.ShowPayInWalletButton,
|
ShowPayInWalletButton = storeBlob.ShowPayInWalletButton,
|
||||||
ShowStoreHeader = storeBlob.ShowStoreHeader,
|
ShowStoreHeader = storeBlob.ShowStoreHeader,
|
||||||
StoreBranding = new StoreBrandingViewModel(storeBlob),
|
StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, storeBlob),
|
||||||
HtmlTitle = storeBlob.HtmlTitle ?? "BTCPay Invoice",
|
HtmlTitle = storeBlob.HtmlTitle ?? "BTCPay Invoice",
|
||||||
CelebratePayment = storeBlob.CelebratePayment,
|
CelebratePayment = storeBlob.CelebratePayment,
|
||||||
OnChainWithLnInvoiceFallback = storeBlob.OnChainWithLnInvoiceFallback,
|
OnChainWithLnInvoiceFallback = storeBlob.OnChainWithLnInvoiceFallback,
|
||||||
@@ -978,9 +978,9 @@ namespace BTCPayServer.Controllers
|
|||||||
|
|
||||||
if (storeBlob.PlaySoundOnPayment)
|
if (storeBlob.PlaySoundOnPayment)
|
||||||
{
|
{
|
||||||
model.PaymentSoundUrl = string.IsNullOrEmpty(storeBlob.SoundFileId)
|
model.PaymentSoundUrl = storeBlob.PaymentSoundUrl is null
|
||||||
? string.Concat(Request.GetAbsoluteRootUri().ToString(), "checkout/payment.mp3")
|
? string.Concat(Request.GetAbsoluteRootUri().ToString(), "checkout/payment.mp3")
|
||||||
: await _fileService.GetFileUrl(Request.GetAbsoluteRootUri(), storeBlob.SoundFileId);
|
: await _uriResolver.Resolve(Request.GetAbsoluteRootUri(), storeBlob.PaymentSoundUrl);
|
||||||
model.ErrorSoundUrl = string.Concat(Request.GetAbsoluteRootUri().ToString(), "checkout/error.mp3");
|
model.ErrorSoundUrl = string.Concat(Request.GetAbsoluteRootUri().ToString(), "checkout/error.mp3");
|
||||||
model.NfcReadSoundUrl = string.Concat(Request.GetAbsoluteRootUri().ToString(), "checkout/nfcread.mp3");
|
model.NfcReadSoundUrl = string.Concat(Request.GetAbsoluteRootUri().ToString(), "checkout/nfcread.mp3");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ namespace BTCPayServer.Controllers
|
|||||||
private readonly PaymentMethodViewProvider _viewProvider;
|
private readonly PaymentMethodViewProvider _viewProvider;
|
||||||
private readonly AppService _appService;
|
private readonly AppService _appService;
|
||||||
private readonly IFileService _fileService;
|
private readonly IFileService _fileService;
|
||||||
|
private readonly UriResolver _uriResolver;
|
||||||
|
|
||||||
public WebhookSender WebhookNotificationManager { get; }
|
public WebhookSender WebhookNotificationManager { get; }
|
||||||
|
|
||||||
@@ -91,6 +92,7 @@ namespace BTCPayServer.Controllers
|
|||||||
LinkGenerator linkGenerator,
|
LinkGenerator linkGenerator,
|
||||||
AppService appService,
|
AppService appService,
|
||||||
IFileService fileService,
|
IFileService fileService,
|
||||||
|
UriResolver uriResolver,
|
||||||
IAuthorizationService authorizationService,
|
IAuthorizationService authorizationService,
|
||||||
TransactionLinkProviders transactionLinkProviders,
|
TransactionLinkProviders transactionLinkProviders,
|
||||||
Dictionary<PaymentMethodId, IPaymentModelExtension> paymentModelExtensions,
|
Dictionary<PaymentMethodId, IPaymentModelExtension> paymentModelExtensions,
|
||||||
@@ -120,6 +122,7 @@ namespace BTCPayServer.Controllers
|
|||||||
_paymentModelExtensions = paymentModelExtensions;
|
_paymentModelExtensions = paymentModelExtensions;
|
||||||
_viewProvider = viewProvider;
|
_viewProvider = viewProvider;
|
||||||
_fileService = fileService;
|
_fileService = fileService;
|
||||||
|
_uriResolver = uriResolver;
|
||||||
_appService = appService;
|
_appService = appService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ namespace BTCPayServer.Controllers
|
|||||||
private readonly DisplayFormatter _displayFormatter;
|
private readonly DisplayFormatter _displayFormatter;
|
||||||
private readonly InvoiceRepository _InvoiceRepository;
|
private readonly InvoiceRepository _InvoiceRepository;
|
||||||
private readonly StoreRepository _storeRepository;
|
private readonly StoreRepository _storeRepository;
|
||||||
|
private readonly UriResolver _uriResolver;
|
||||||
private readonly BTCPayNetworkProvider _networkProvider;
|
private readonly BTCPayNetworkProvider _networkProvider;
|
||||||
|
|
||||||
private FormComponentProviders FormProviders { get; }
|
private FormComponentProviders FormProviders { get; }
|
||||||
@@ -55,6 +56,7 @@ namespace BTCPayServer.Controllers
|
|||||||
CurrencyNameTable currencies,
|
CurrencyNameTable currencies,
|
||||||
DisplayFormatter displayFormatter,
|
DisplayFormatter displayFormatter,
|
||||||
StoreRepository storeRepository,
|
StoreRepository storeRepository,
|
||||||
|
UriResolver uriResolver,
|
||||||
InvoiceRepository invoiceRepository,
|
InvoiceRepository invoiceRepository,
|
||||||
FormComponentProviders formProviders,
|
FormComponentProviders formProviders,
|
||||||
FormDataService formDataService,
|
FormDataService formDataService,
|
||||||
@@ -68,6 +70,7 @@ namespace BTCPayServer.Controllers
|
|||||||
_Currencies = currencies;
|
_Currencies = currencies;
|
||||||
_displayFormatter = displayFormatter;
|
_displayFormatter = displayFormatter;
|
||||||
_storeRepository = storeRepository;
|
_storeRepository = storeRepository;
|
||||||
|
_uriResolver = uriResolver;
|
||||||
_InvoiceRepository = invoiceRepository;
|
_InvoiceRepository = invoiceRepository;
|
||||||
FormProviders = formProviders;
|
FormProviders = formProviders;
|
||||||
FormDataService = formDataService;
|
FormDataService = formDataService;
|
||||||
@@ -191,8 +194,6 @@ namespace BTCPayServer.Controllers
|
|||||||
blob.Amount = viewModel.Amount;
|
blob.Amount = viewModel.Amount;
|
||||||
blob.ExpiryDate = viewModel.ExpiryDate?.ToUniversalTime();
|
blob.ExpiryDate = viewModel.ExpiryDate?.ToUniversalTime();
|
||||||
blob.Currency = viewModel.Currency ?? store.GetStoreBlob().DefaultCurrency;
|
blob.Currency = viewModel.Currency ?? store.GetStoreBlob().DefaultCurrency;
|
||||||
blob.EmbeddedCSS = viewModel.EmbeddedCSS;
|
|
||||||
blob.CustomCSSLink = viewModel.CustomCSSLink;
|
|
||||||
blob.AllowCustomPaymentAmounts = viewModel.AllowCustomPaymentAmounts;
|
blob.AllowCustomPaymentAmounts = viewModel.AllowCustomPaymentAmounts;
|
||||||
blob.FormId = viewModel.FormId;
|
blob.FormId = viewModel.FormId;
|
||||||
|
|
||||||
@@ -229,11 +230,7 @@ namespace BTCPayServer.Controllers
|
|||||||
vm.HubPath = PaymentRequestHub.GetHubPath(Request);
|
vm.HubPath = PaymentRequestHub.GetHubPath(Request);
|
||||||
vm.StoreName = store.StoreName;
|
vm.StoreName = store.StoreName;
|
||||||
vm.StoreWebsite = store.StoreWebsite;
|
vm.StoreWebsite = store.StoreWebsite;
|
||||||
vm.StoreBranding = new StoreBrandingViewModel(storeBlob)
|
vm.StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, storeBlob);
|
||||||
{
|
|
||||||
EmbeddedCSS = vm.EmbeddedCSS,
|
|
||||||
CustomCSSLink = vm.CustomCSSLink
|
|
||||||
};
|
|
||||||
|
|
||||||
return View(vm);
|
return View(vm);
|
||||||
}
|
}
|
||||||
@@ -290,11 +287,8 @@ namespace BTCPayServer.Controllers
|
|||||||
viewModel.Form = form;
|
viewModel.Form = form;
|
||||||
|
|
||||||
var storeBlob = result.StoreData.GetStoreBlob();
|
var storeBlob = result.StoreData.GetStoreBlob();
|
||||||
viewModel.StoreBranding = new StoreBrandingViewModel(storeBlob)
|
viewModel.StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, storeBlob);
|
||||||
{
|
|
||||||
EmbeddedCSS = prBlob.EmbeddedCSS,
|
|
||||||
CustomCSSLink = prBlob.CustomCSSLink
|
|
||||||
};
|
|
||||||
return View("Views/UIForms/View", viewModel);
|
return View("Views/UIForms/View", viewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using BTCPayServer.Lightning;
|
|||||||
using BTCPayServer.Models;
|
using BTCPayServer.Models;
|
||||||
using BTCPayServer.Payments;
|
using BTCPayServer.Payments;
|
||||||
using BTCPayServer.Payments.Lightning;
|
using BTCPayServer.Payments.Lightning;
|
||||||
|
using BTCPayServer.Services;
|
||||||
using BTCPayServer.Services.Invoices;
|
using BTCPayServer.Services.Invoices;
|
||||||
using BTCPayServer.Services.Stores;
|
using BTCPayServer.Services.Stores;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
@@ -21,16 +22,19 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
private readonly BTCPayNetworkProvider _BtcPayNetworkProvider;
|
private readonly BTCPayNetworkProvider _BtcPayNetworkProvider;
|
||||||
private readonly Dictionary<PaymentMethodId, IPaymentModelExtension> _paymentModelExtensions;
|
private readonly Dictionary<PaymentMethodId, IPaymentModelExtension> _paymentModelExtensions;
|
||||||
|
private readonly UriResolver _uriResolver;
|
||||||
private readonly PaymentMethodHandlerDictionary _handlers;
|
private readonly PaymentMethodHandlerDictionary _handlers;
|
||||||
private readonly StoreRepository _StoreRepository;
|
private readonly StoreRepository _StoreRepository;
|
||||||
|
|
||||||
public UIPublicLightningNodeInfoController(BTCPayNetworkProvider btcPayNetworkProvider,
|
public UIPublicLightningNodeInfoController(BTCPayNetworkProvider btcPayNetworkProvider,
|
||||||
Dictionary<PaymentMethodId, IPaymentModelExtension> paymentModelExtensions,
|
Dictionary<PaymentMethodId, IPaymentModelExtension> paymentModelExtensions,
|
||||||
|
UriResolver uriResolver,
|
||||||
PaymentMethodHandlerDictionary handlers,
|
PaymentMethodHandlerDictionary handlers,
|
||||||
StoreRepository storeRepository)
|
StoreRepository storeRepository)
|
||||||
{
|
{
|
||||||
_BtcPayNetworkProvider = btcPayNetworkProvider;
|
_BtcPayNetworkProvider = btcPayNetworkProvider;
|
||||||
_paymentModelExtensions = paymentModelExtensions;
|
_paymentModelExtensions = paymentModelExtensions;
|
||||||
|
_uriResolver = uriResolver;
|
||||||
_handlers = handlers;
|
_handlers = handlers;
|
||||||
_StoreRepository = storeRepository;
|
_StoreRepository = storeRepository;
|
||||||
}
|
}
|
||||||
@@ -49,7 +53,7 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
CryptoCode = cryptoCode,
|
CryptoCode = cryptoCode,
|
||||||
StoreName = store.StoreName,
|
StoreName = store.StoreName,
|
||||||
StoreBranding = new StoreBrandingViewModel(storeBlob)
|
StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, storeBlob)
|
||||||
};
|
};
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ namespace BTCPayServer.Controllers
|
|||||||
private readonly ApplicationDbContextFactory _dbContextFactory;
|
private readonly ApplicationDbContextFactory _dbContextFactory;
|
||||||
private readonly CurrencyNameTable _currencyNameTable;
|
private readonly CurrencyNameTable _currencyNameTable;
|
||||||
private readonly DisplayFormatter _displayFormatter;
|
private readonly DisplayFormatter _displayFormatter;
|
||||||
|
private readonly UriResolver _uriResolver;
|
||||||
private readonly PullPaymentHostedService _pullPaymentHostedService;
|
private readonly PullPaymentHostedService _pullPaymentHostedService;
|
||||||
private readonly BTCPayNetworkProvider _networkProvider;
|
private readonly BTCPayNetworkProvider _networkProvider;
|
||||||
private readonly BTCPayNetworkJsonSerializerSettings _serializerSettings;
|
private readonly BTCPayNetworkJsonSerializerSettings _serializerSettings;
|
||||||
@@ -51,6 +52,7 @@ namespace BTCPayServer.Controllers
|
|||||||
public UIPullPaymentController(ApplicationDbContextFactory dbContextFactory,
|
public UIPullPaymentController(ApplicationDbContextFactory dbContextFactory,
|
||||||
CurrencyNameTable currencyNameTable,
|
CurrencyNameTable currencyNameTable,
|
||||||
DisplayFormatter displayFormatter,
|
DisplayFormatter displayFormatter,
|
||||||
|
UriResolver uriResolver,
|
||||||
PullPaymentHostedService pullPaymentHostedService,
|
PullPaymentHostedService pullPaymentHostedService,
|
||||||
BTCPayNetworkProvider networkProvider,
|
BTCPayNetworkProvider networkProvider,
|
||||||
BTCPayNetworkJsonSerializerSettings serializerSettings,
|
BTCPayNetworkJsonSerializerSettings serializerSettings,
|
||||||
@@ -62,6 +64,7 @@ namespace BTCPayServer.Controllers
|
|||||||
_dbContextFactory = dbContextFactory;
|
_dbContextFactory = dbContextFactory;
|
||||||
_currencyNameTable = currencyNameTable;
|
_currencyNameTable = currencyNameTable;
|
||||||
_displayFormatter = displayFormatter;
|
_displayFormatter = displayFormatter;
|
||||||
|
_uriResolver = uriResolver;
|
||||||
_pullPaymentHostedService = pullPaymentHostedService;
|
_pullPaymentHostedService = pullPaymentHostedService;
|
||||||
_serializerSettings = serializerSettings;
|
_serializerSettings = serializerSettings;
|
||||||
_payoutHandlers = payoutHandlers;
|
_payoutHandlers = payoutHandlers;
|
||||||
@@ -121,11 +124,7 @@ namespace BTCPayServer.Controllers
|
|||||||
}).ToList()
|
}).ToList()
|
||||||
};
|
};
|
||||||
vm.IsPending &= vm.AmountDue > 0.0m;
|
vm.IsPending &= vm.AmountDue > 0.0m;
|
||||||
vm.StoreBranding = new StoreBrandingViewModel(storeBlob)
|
vm.StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, storeBlob);
|
||||||
{
|
|
||||||
EmbeddedCSS = blob.View.EmbeddedCSS,
|
|
||||||
CustomCSSLink = blob.View.CustomCSSLink
|
|
||||||
};
|
|
||||||
|
|
||||||
if (_pullPaymentHostedService.SupportsLNURL(blob))
|
if (_pullPaymentHostedService.SupportsLNURL(blob))
|
||||||
{
|
{
|
||||||
@@ -185,13 +184,11 @@ namespace BTCPayServer.Controllers
|
|||||||
var blob = pp.GetBlob();
|
var blob = pp.GetBlob();
|
||||||
blob.Description = viewModel.Description ?? string.Empty;
|
blob.Description = viewModel.Description ?? string.Empty;
|
||||||
blob.Name = viewModel.Name ?? string.Empty;
|
blob.Name = viewModel.Name ?? string.Empty;
|
||||||
blob.View = new PullPaymentBlob.PullPaymentView()
|
blob.View = new PullPaymentBlob.PullPaymentView
|
||||||
{
|
{
|
||||||
Title = viewModel.Name ?? string.Empty,
|
Title = viewModel.Name ?? string.Empty,
|
||||||
Description = viewModel.Description ?? string.Empty,
|
Description = viewModel.Description ?? string.Empty,
|
||||||
CustomCSSLink = viewModel.CustomCSSLink,
|
Email = null
|
||||||
Email = null,
|
|
||||||
EmbeddedCSS = viewModel.EmbeddedCSS,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pp.SetBlob(blob);
|
pp.SetBlob(blob);
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ namespace BTCPayServer.Controllers
|
|||||||
private readonly IFileService _fileService;
|
private readonly IFileService _fileService;
|
||||||
private readonly IEnumerable<IStorageProviderService> _StorageProviderServices;
|
private readonly IEnumerable<IStorageProviderService> _StorageProviderServices;
|
||||||
private readonly LinkGenerator _linkGenerator;
|
private readonly LinkGenerator _linkGenerator;
|
||||||
|
private readonly UriResolver _uriResolver;
|
||||||
private readonly EmailSenderFactory _emailSenderFactory;
|
private readonly EmailSenderFactory _emailSenderFactory;
|
||||||
private readonly TransactionLinkProviders _transactionLinkProviders;
|
private readonly TransactionLinkProviders _transactionLinkProviders;
|
||||||
|
|
||||||
@@ -88,6 +89,7 @@ namespace BTCPayServer.Controllers
|
|||||||
IOptions<ExternalServicesOptions> externalServiceOptions,
|
IOptions<ExternalServicesOptions> externalServiceOptions,
|
||||||
Logs logs,
|
Logs logs,
|
||||||
LinkGenerator linkGenerator,
|
LinkGenerator linkGenerator,
|
||||||
|
UriResolver uriResolver,
|
||||||
EmailSenderFactory emailSenderFactory,
|
EmailSenderFactory emailSenderFactory,
|
||||||
IHostApplicationLifetime applicationLifetime,
|
IHostApplicationLifetime applicationLifetime,
|
||||||
IHtmlHelper html,
|
IHtmlHelper html,
|
||||||
@@ -113,6 +115,7 @@ namespace BTCPayServer.Controllers
|
|||||||
_externalServiceOptions = externalServiceOptions;
|
_externalServiceOptions = externalServiceOptions;
|
||||||
Logs = logs;
|
Logs = logs;
|
||||||
_linkGenerator = linkGenerator;
|
_linkGenerator = linkGenerator;
|
||||||
|
_uriResolver = uriResolver;
|
||||||
_emailSenderFactory = emailSenderFactory;
|
_emailSenderFactory = emailSenderFactory;
|
||||||
ApplicationLifetime = applicationLifetime;
|
ApplicationLifetime = applicationLifetime;
|
||||||
Html = html;
|
Html = html;
|
||||||
@@ -1025,9 +1028,8 @@ namespace BTCPayServer.Controllers
|
|||||||
ContactUrl = server.ContactUrl,
|
ContactUrl = server.ContactUrl,
|
||||||
CustomTheme = theme.CustomTheme,
|
CustomTheme = theme.CustomTheme,
|
||||||
CustomThemeExtension = theme.CustomThemeExtension,
|
CustomThemeExtension = theme.CustomThemeExtension,
|
||||||
CustomThemeCssUri = theme.CustomThemeCssUri,
|
CustomThemeCssUrl = await _uriResolver.Resolve(Request.GetAbsoluteRootUri(), theme.CustomThemeCssUrl),
|
||||||
CustomThemeFileId = theme.CustomThemeFileId,
|
LogoUrl = await _uriResolver.Resolve(Request.GetAbsoluteRootUri(), theme.LogoUrl)
|
||||||
LogoFileId = theme.LogoFileId
|
|
||||||
};
|
};
|
||||||
return View(vm);
|
return View(vm);
|
||||||
}
|
}
|
||||||
@@ -1046,8 +1048,8 @@ namespace BTCPayServer.Controllers
|
|||||||
if (userId is null)
|
if (userId is null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
|
||||||
vm.LogoFileId = theme.LogoFileId;
|
vm.LogoUrl = await _uriResolver.Resolve(this.Request.GetAbsoluteRootUri(), theme.LogoUrl);
|
||||||
vm.CustomThemeFileId = theme.CustomThemeFileId;
|
vm.CustomThemeCssUrl = await _uriResolver.Resolve(this.Request.GetAbsoluteRootUri(), theme.CustomThemeCssUrl);
|
||||||
|
|
||||||
if (server.ServerName != vm.ServerName)
|
if (server.ServerName != vm.ServerName)
|
||||||
{
|
{
|
||||||
@@ -1072,17 +1074,12 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
if (vm.CustomThemeFile.ContentType.Equals("text/css", StringComparison.InvariantCulture))
|
if (vm.CustomThemeFile.ContentType.Equals("text/css", StringComparison.InvariantCulture))
|
||||||
{
|
{
|
||||||
// delete existing file
|
|
||||||
if (!string.IsNullOrEmpty(theme.CustomThemeFileId))
|
|
||||||
{
|
|
||||||
await _fileService.RemoveFile(theme.CustomThemeFileId, userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add new file
|
// add new file
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var storedFile = await _fileService.AddFile(vm.CustomThemeFile, userId);
|
var storedFile = await _fileService.AddFile(vm.CustomThemeFile, userId);
|
||||||
vm.CustomThemeFileId = theme.CustomThemeFileId = storedFile.Id;
|
theme.CustomThemeCssUrl = new UnresolvedUri.FileIdUri(storedFile.Id);
|
||||||
|
vm.CustomThemeCssUrl = await _uriResolver.Resolve(Request.GetAbsoluteRootUri(), theme.CustomThemeCssUrl);
|
||||||
settingsChanged = true;
|
settingsChanged = true;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -1095,10 +1092,12 @@ namespace BTCPayServer.Controllers
|
|||||||
ModelState.AddModelError(nameof(vm.CustomThemeFile), "The uploaded theme file needs to be a CSS file");
|
ModelState.AddModelError(nameof(vm.CustomThemeFile), "The uploaded theme file needs to be a CSS file");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (RemoveCustomThemeFile && !string.IsNullOrEmpty(theme.CustomThemeFileId))
|
else if (RemoveCustomThemeFile && theme.CustomThemeCssUrl is not null)
|
||||||
{
|
{
|
||||||
await _fileService.RemoveFile(theme.CustomThemeFileId, userId);
|
vm.CustomThemeCssUrl = null;
|
||||||
vm.CustomThemeFileId = theme.CustomThemeFileId = null;
|
theme.CustomThemeCssUrl = null;
|
||||||
|
theme.CustomTheme = false;
|
||||||
|
theme.CustomThemeExtension = ThemeExtension.Custom;
|
||||||
settingsChanged = true;
|
settingsChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1122,16 +1121,12 @@ namespace BTCPayServer.Controllers
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
vm.LogoFile = formFile;
|
vm.LogoFile = formFile;
|
||||||
// delete existing file
|
|
||||||
if (!string.IsNullOrEmpty(theme.LogoFileId))
|
|
||||||
{
|
|
||||||
await _fileService.RemoveFile(theme.LogoFileId, userId);
|
|
||||||
}
|
|
||||||
// add new file
|
// add new file
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var storedFile = await _fileService.AddFile(vm.LogoFile, userId);
|
var storedFile = await _fileService.AddFile(vm.LogoFile, userId);
|
||||||
vm.LogoFileId = theme.LogoFileId = storedFile.Id;
|
theme.LogoUrl = new UnresolvedUri.FileIdUri(storedFile.Id);
|
||||||
|
vm.LogoUrl = await _uriResolver.Resolve(Request.GetAbsoluteRootUri(), theme.LogoUrl);
|
||||||
settingsChanged = true;
|
settingsChanged = true;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -1141,29 +1136,19 @@ namespace BTCPayServer.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (RemoveLogoFile && !string.IsNullOrEmpty(theme.LogoFileId))
|
else if (RemoveLogoFile && theme.LogoUrl is not null)
|
||||||
{
|
{
|
||||||
await _fileService.RemoveFile(theme.LogoFileId, userId);
|
vm.LogoUrl = null;
|
||||||
vm.LogoFileId = theme.LogoFileId = null;
|
theme.LogoUrl = null;
|
||||||
settingsChanged = true;
|
settingsChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vm.CustomTheme && !string.IsNullOrEmpty(vm.CustomThemeCssUri) && !Uri.IsWellFormedUriString(vm.CustomThemeCssUri, UriKind.RelativeOrAbsolute))
|
if (vm.CustomTheme && theme.CustomThemeExtension != vm.CustomThemeExtension)
|
||||||
{
|
|
||||||
ModelState.AddModelError(nameof(theme.CustomThemeCssUri), "Please provide a non-empty theme URI");
|
|
||||||
}
|
|
||||||
else if (theme.CustomThemeCssUri != vm.CustomThemeCssUri)
|
|
||||||
{
|
|
||||||
theme.CustomThemeCssUri = vm.CustomThemeCssUri;
|
|
||||||
settingsChanged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (theme.CustomThemeExtension != vm.CustomThemeExtension)
|
|
||||||
{
|
{
|
||||||
// Require a custom theme to be defined in that case
|
// Require a custom theme to be defined in that case
|
||||||
if (string.IsNullOrEmpty(vm.CustomThemeCssUri) && string.IsNullOrEmpty(theme.CustomThemeFileId))
|
if (string.IsNullOrEmpty(vm.CustomThemeCssUrl) && theme.CustomThemeCssUrl is null)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError(nameof(vm.CustomThemeFile), "Please provide a custom theme");
|
ModelState.AddModelError(nameof(vm.CustomThemeCssUrl), "Please provide a custom theme");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1172,7 +1157,7 @@ namespace BTCPayServer.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theme.CustomTheme != vm.CustomTheme)
|
if (theme.CustomTheme != vm.CustomTheme && !RemoveCustomThemeFile)
|
||||||
{
|
{
|
||||||
theme.CustomTheme = vm.CustomTheme;
|
theme.CustomTheme = vm.CustomTheme;
|
||||||
settingsChanged = true;
|
settingsChanged = true;
|
||||||
@@ -1182,6 +1167,7 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
await _SettingsRepository.UpdateSetting(theme);
|
await _SettingsRepository.UpdateSetting(theme);
|
||||||
TempData[WellKnownTempData.SuccessMessage] = "Settings updated successfully";
|
TempData[WellKnownTempData.SuccessMessage] = "Settings updated successfully";
|
||||||
|
return RedirectToAction(nameof(Branding));
|
||||||
}
|
}
|
||||||
|
|
||||||
return View(vm);
|
return View(vm);
|
||||||
|
|||||||
@@ -95,8 +95,6 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
Name = "",
|
Name = "",
|
||||||
Currency = CurrentStore.GetStoreBlob().DefaultCurrency,
|
Currency = CurrentStore.GetStoreBlob().DefaultCurrency,
|
||||||
CustomCSSLink = "",
|
|
||||||
EmbeddedCSS = "",
|
|
||||||
PayoutMethodsItem =
|
PayoutMethodsItem =
|
||||||
paymentMethods.Select(id => new SelectListItem(id.ToString(), id.ToString(), true))
|
paymentMethods.Select(id => new SelectListItem(id.ToString(), id.ToString(), true))
|
||||||
});
|
});
|
||||||
@@ -145,7 +143,7 @@ namespace BTCPayServer.Controllers
|
|||||||
return View(model);
|
return View(model);
|
||||||
model.AutoApproveClaims = model.AutoApproveClaims && (await
|
model.AutoApproveClaims = model.AutoApproveClaims && (await
|
||||||
_authorizationService.AuthorizeAsync(User, storeId, Policies.CanCreatePullPayments)).Succeeded;
|
_authorizationService.AuthorizeAsync(User, storeId, Policies.CanCreatePullPayments)).Succeeded;
|
||||||
await _pullPaymentService.CreatePullPayment(new HostedServices.CreatePullPayment()
|
await _pullPaymentService.CreatePullPayment(new CreatePullPayment
|
||||||
{
|
{
|
||||||
Name = model.Name,
|
Name = model.Name,
|
||||||
Description = model.Description,
|
Description = model.Description,
|
||||||
@@ -153,17 +151,15 @@ namespace BTCPayServer.Controllers
|
|||||||
Currency = model.Currency,
|
Currency = model.Currency,
|
||||||
StoreId = storeId,
|
StoreId = storeId,
|
||||||
PayoutMethodIds = selectedPaymentMethodIds,
|
PayoutMethodIds = selectedPaymentMethodIds,
|
||||||
EmbeddedCSS = model.EmbeddedCSS,
|
|
||||||
CustomCSSLink = model.CustomCSSLink,
|
|
||||||
BOLT11Expiration = TimeSpan.FromDays(model.BOLT11Expiration),
|
BOLT11Expiration = TimeSpan.FromDays(model.BOLT11Expiration),
|
||||||
AutoApproveClaims = model.AutoApproveClaims
|
AutoApproveClaims = model.AutoApproveClaims
|
||||||
});
|
});
|
||||||
this.TempData.SetStatusMessageModel(new StatusMessageModel()
|
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||||
{
|
{
|
||||||
Message = "Pull payment request created",
|
Message = "Pull payment request created",
|
||||||
Severity = StatusMessageModel.StatusSeverity.Success
|
Severity = StatusMessageModel.StatusSeverity.Success
|
||||||
});
|
});
|
||||||
return RedirectToAction(nameof(PullPayments), new { storeId = storeId });
|
return RedirectToAction(nameof(PullPayments), new { storeId });
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(Policy = Policies.CanViewPullPayments, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
[Authorize(Policy = Policies.CanViewPullPayments, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BTCPayServer.Abstractions.Constants;
|
using BTCPayServer.Abstractions.Constants;
|
||||||
|
using BTCPayServer.Abstractions.Extensions;
|
||||||
using BTCPayServer.Abstractions.Models;
|
using BTCPayServer.Abstractions.Models;
|
||||||
using BTCPayServer.Client;
|
using BTCPayServer.Client;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
@@ -19,20 +20,19 @@ namespace BTCPayServer.Controllers;
|
|||||||
public partial class UIStoresController
|
public partial class UIStoresController
|
||||||
{
|
{
|
||||||
[HttpGet("{storeId}/settings")]
|
[HttpGet("{storeId}/settings")]
|
||||||
public IActionResult GeneralSettings()
|
public async Task<IActionResult> GeneralSettings()
|
||||||
{
|
{
|
||||||
var store = HttpContext.GetStoreData();
|
var store = HttpContext.GetStoreData();
|
||||||
if (store == null)
|
if (store == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
|
||||||
var storeBlob = store.GetStoreBlob();
|
var storeBlob = store.GetStoreBlob();
|
||||||
var vm = new GeneralSettingsViewModel
|
var vm = new GeneralSettingsViewModel
|
||||||
{
|
{
|
||||||
Id = store.Id,
|
Id = store.Id,
|
||||||
StoreName = store.StoreName,
|
StoreName = store.StoreName,
|
||||||
StoreWebsite = store.StoreWebsite,
|
StoreWebsite = store.StoreWebsite,
|
||||||
LogoFileId = storeBlob.LogoFileId,
|
LogoUrl = await _uriResolver.Resolve(Request.GetAbsoluteRootUri(), storeBlob.LogoUrl),
|
||||||
CssFileId = storeBlob.CssFileId,
|
CssUrl = await _uriResolver.Resolve(Request.GetAbsoluteRootUri(), storeBlob.CssUrl),
|
||||||
BrandColor = storeBlob.BrandColor,
|
BrandColor = storeBlob.BrandColor,
|
||||||
NetworkFeeMode = storeBlob.NetworkFeeMode,
|
NetworkFeeMode = storeBlob.NetworkFeeMode,
|
||||||
AnyoneCanCreateInvoice = storeBlob.AnyoneCanInvoice,
|
AnyoneCanCreateInvoice = storeBlob.AnyoneCanInvoice,
|
||||||
@@ -104,16 +104,11 @@ public partial class UIStoresController
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
model.LogoFile = formFile;
|
model.LogoFile = formFile;
|
||||||
// delete existing file
|
|
||||||
if (!string.IsNullOrEmpty(blob.LogoFileId))
|
|
||||||
{
|
|
||||||
await _fileService.RemoveFile(blob.LogoFileId, userId);
|
|
||||||
}
|
|
||||||
// add new image
|
// add new image
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var storedFile = await _fileService.AddFile(model.LogoFile, userId);
|
var storedFile = await _fileService.AddFile(model.LogoFile, userId);
|
||||||
blob.LogoFileId = storedFile.Id;
|
blob.LogoUrl = new UnresolvedUri.FileIdUri(storedFile.Id);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -122,10 +117,9 @@ public partial class UIStoresController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (RemoveLogoFile && !string.IsNullOrEmpty(blob.LogoFileId))
|
else if (RemoveLogoFile && blob.LogoUrl is not null)
|
||||||
{
|
{
|
||||||
await _fileService.RemoveFile(blob.LogoFileId, userId);
|
blob.LogoUrl = null;
|
||||||
blob.LogoFileId = null;
|
|
||||||
needUpdate = true;
|
needUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,16 +139,11 @@ public partial class UIStoresController
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// delete existing file
|
|
||||||
if (!string.IsNullOrEmpty(blob.CssFileId))
|
|
||||||
{
|
|
||||||
await _fileService.RemoveFile(blob.CssFileId, userId);
|
|
||||||
}
|
|
||||||
// add new file
|
// add new file
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var storedFile = await _fileService.AddFile(model.CssFile, userId);
|
var storedFile = await _fileService.AddFile(model.CssFile, userId);
|
||||||
blob.CssFileId = storedFile.Id;
|
blob.CssUrl = new UnresolvedUri.FileIdUri(storedFile.Id);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -162,10 +151,9 @@ public partial class UIStoresController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (RemoveCssFile && !string.IsNullOrEmpty(blob.CssFileId))
|
else if (RemoveCssFile && blob.CssUrl is not null)
|
||||||
{
|
{
|
||||||
await _fileService.RemoveFile(blob.CssFileId, userId);
|
blob.CssUrl = null;
|
||||||
blob.CssFileId = null;
|
|
||||||
needUpdate = true;
|
needUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,7 +209,7 @@ public partial class UIStoresController
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{storeId}/checkout")]
|
[HttpGet("{storeId}/checkout")]
|
||||||
public IActionResult CheckoutAppearance()
|
public async Task<IActionResult> CheckoutAppearance()
|
||||||
{
|
{
|
||||||
var storeBlob = CurrentStore.GetStoreBlob();
|
var storeBlob = CurrentStore.GetStoreBlob();
|
||||||
var vm = new CheckoutAppearanceViewModel();
|
var vm = new CheckoutAppearanceViewModel();
|
||||||
@@ -253,7 +241,9 @@ public partial class UIStoresController
|
|||||||
vm.LightningAmountInSatoshi = storeBlob.LightningAmountInSatoshi;
|
vm.LightningAmountInSatoshi = storeBlob.LightningAmountInSatoshi;
|
||||||
vm.LazyPaymentMethods = storeBlob.LazyPaymentMethods;
|
vm.LazyPaymentMethods = storeBlob.LazyPaymentMethods;
|
||||||
vm.RedirectAutomatically = storeBlob.RedirectAutomatically;
|
vm.RedirectAutomatically = storeBlob.RedirectAutomatically;
|
||||||
vm.SoundFileId = storeBlob.SoundFileId;
|
vm.PaymentSoundUrl = storeBlob.PaymentSoundUrl is null
|
||||||
|
? string.Concat(Request.GetAbsoluteRootUri().ToString(), "checkout/payment.mp3")
|
||||||
|
: await _uriResolver.Resolve(Request.GetAbsoluteRootUri(), storeBlob.PaymentSoundUrl);
|
||||||
vm.HtmlTitle = storeBlob.HtmlTitle;
|
vm.HtmlTitle = storeBlob.HtmlTitle;
|
||||||
vm.SupportUrl = storeBlob.StoreSupportUrl;
|
vm.SupportUrl = storeBlob.StoreSupportUrl;
|
||||||
vm.DisplayExpirationTimer = (int)storeBlob.DisplayExpirationTimer.TotalMinutes;
|
vm.DisplayExpirationTimer = (int)storeBlob.DisplayExpirationTimer.TotalMinutes;
|
||||||
@@ -316,17 +306,11 @@ public partial class UIStoresController
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
model.SoundFile = formFile;
|
model.SoundFile = formFile;
|
||||||
// delete existing file
|
|
||||||
if (!string.IsNullOrEmpty(blob.SoundFileId))
|
|
||||||
{
|
|
||||||
await _fileService.RemoveFile(blob.SoundFileId, userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add new file
|
// add new file
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var storedFile = await _fileService.AddFile(model.SoundFile, userId);
|
var storedFile = await _fileService.AddFile(model.SoundFile, userId);
|
||||||
blob.SoundFileId = storedFile.Id;
|
blob.PaymentSoundUrl = new UnresolvedUri.FileIdUri(storedFile.Id);
|
||||||
needUpdate = true;
|
needUpdate = true;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -336,10 +320,9 @@ public partial class UIStoresController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (RemoveSoundFile && !string.IsNullOrEmpty(blob.SoundFileId))
|
else if (RemoveSoundFile && blob.PaymentSoundUrl is not null)
|
||||||
{
|
{
|
||||||
await _fileService.RemoveFile(blob.SoundFileId, userId);
|
blob.PaymentSoundUrl = null;
|
||||||
blob.SoundFileId = null;
|
|
||||||
needUpdate = true;
|
needUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ public partial class UIStoresController : Controller
|
|||||||
IHtmlHelper html,
|
IHtmlHelper html,
|
||||||
EmailSenderFactory emailSenderFactory,
|
EmailSenderFactory emailSenderFactory,
|
||||||
WalletFileParsers onChainWalletParsers,
|
WalletFileParsers onChainWalletParsers,
|
||||||
|
UriResolver uriResolver,
|
||||||
SettingsRepository settingsRepository,
|
SettingsRepository settingsRepository,
|
||||||
EventAggregator eventAggregator)
|
EventAggregator eventAggregator)
|
||||||
{
|
{
|
||||||
@@ -78,6 +79,7 @@ public partial class UIStoresController : Controller
|
|||||||
_externalServiceOptions = externalServiceOptions;
|
_externalServiceOptions = externalServiceOptions;
|
||||||
_emailSenderFactory = emailSenderFactory;
|
_emailSenderFactory = emailSenderFactory;
|
||||||
_onChainWalletParsers = onChainWalletParsers;
|
_onChainWalletParsers = onChainWalletParsers;
|
||||||
|
_uriResolver = uriResolver;
|
||||||
_settingsRepository = settingsRepository;
|
_settingsRepository = settingsRepository;
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
_html = html;
|
_html = html;
|
||||||
@@ -106,6 +108,7 @@ public partial class UIStoresController : Controller
|
|||||||
private readonly IOptions<ExternalServicesOptions> _externalServiceOptions;
|
private readonly IOptions<ExternalServicesOptions> _externalServiceOptions;
|
||||||
private readonly EmailSenderFactory _emailSenderFactory;
|
private readonly EmailSenderFactory _emailSenderFactory;
|
||||||
private readonly WalletFileParsers _onChainWalletParsers;
|
private readonly WalletFileParsers _onChainWalletParsers;
|
||||||
|
private readonly UriResolver _uriResolver;
|
||||||
private readonly EventAggregator _eventAggregator;
|
private readonly EventAggregator _eventAggregator;
|
||||||
private readonly IHtmlHelper _html;
|
private readonly IHtmlHelper _html;
|
||||||
private readonly WebhookSender _webhookNotificationManager;
|
private readonly WebhookSender _webhookNotificationManager;
|
||||||
|
|||||||
@@ -35,9 +35,7 @@ namespace BTCPayServer.Data
|
|||||||
{
|
{
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public string EmbeddedCSS { get; set; }
|
|
||||||
public string Email { get; set; }
|
public string Email { get; set; }
|
||||||
public string CustomCSSLink { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ using BTCPayServer.JsonConverters;
|
|||||||
using BTCPayServer.Payments;
|
using BTCPayServer.Payments;
|
||||||
using BTCPayServer.Rating;
|
using BTCPayServer.Rating;
|
||||||
using BTCPayServer.Services.Mails;
|
using BTCPayServer.Services.Mails;
|
||||||
using BTCPayServer.Services.Rates;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
@@ -217,8 +215,10 @@ namespace BTCPayServer.Data
|
|||||||
|
|
||||||
public List<UIStoresController.StoreEmailRule> EmailRules { get; set; }
|
public List<UIStoresController.StoreEmailRule> EmailRules { get; set; }
|
||||||
public string BrandColor { get; set; }
|
public string BrandColor { get; set; }
|
||||||
public string LogoFileId { get; set; }
|
[JsonConverter(typeof(UnresolvedUriJsonConverter))]
|
||||||
public string CssFileId { get; set; }
|
public UnresolvedUri LogoUrl { get; set; }
|
||||||
|
[JsonConverter(typeof(UnresolvedUriJsonConverter))]
|
||||||
|
public UnresolvedUri CssUrl { get; set; }
|
||||||
|
|
||||||
[DefaultValue(true)]
|
[DefaultValue(true)]
|
||||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
||||||
@@ -236,7 +236,8 @@ namespace BTCPayServer.Data
|
|||||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
||||||
public bool PlaySoundOnPayment { get; set; }
|
public bool PlaySoundOnPayment { get; set; }
|
||||||
|
|
||||||
public string SoundFileId { get; set; }
|
[JsonConverter(typeof(UnresolvedUriJsonConverter))]
|
||||||
|
public UnresolvedUri PaymentSoundUrl { get; set; }
|
||||||
|
|
||||||
public IPaymentFilter GetExcludedPaymentMethods()
|
public IPaymentFilter GetExcludedPaymentMethods()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using BTCPayServer.Data;
|
|||||||
using BTCPayServer.Filters;
|
using BTCPayServer.Filters;
|
||||||
using BTCPayServer.Forms.Models;
|
using BTCPayServer.Forms.Models;
|
||||||
using BTCPayServer.Models;
|
using BTCPayServer.Models;
|
||||||
|
using BTCPayServer.Services;
|
||||||
using BTCPayServer.Services.Stores;
|
using BTCPayServer.Services.Stores;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
@@ -26,15 +27,18 @@ namespace BTCPayServer.Forms;
|
|||||||
public class UIFormsController : Controller
|
public class UIFormsController : Controller
|
||||||
{
|
{
|
||||||
private readonly FormDataService _formDataService;
|
private readonly FormDataService _formDataService;
|
||||||
|
private readonly UriResolver _uriResolver;
|
||||||
private readonly IAuthorizationService _authorizationService;
|
private readonly IAuthorizationService _authorizationService;
|
||||||
private readonly StoreRepository _storeRepository;
|
private readonly StoreRepository _storeRepository;
|
||||||
private FormComponentProviders FormProviders { get; }
|
private FormComponentProviders FormProviders { get; }
|
||||||
|
|
||||||
public UIFormsController(FormComponentProviders formProviders, FormDataService formDataService,
|
public UIFormsController(FormComponentProviders formProviders, FormDataService formDataService,
|
||||||
|
UriResolver uriResolver,
|
||||||
StoreRepository storeRepository, IAuthorizationService authorizationService)
|
StoreRepository storeRepository, IAuthorizationService authorizationService)
|
||||||
{
|
{
|
||||||
FormProviders = formProviders;
|
FormProviders = formProviders;
|
||||||
_formDataService = formDataService;
|
_formDataService = formDataService;
|
||||||
|
_uriResolver = uriResolver;
|
||||||
_authorizationService = authorizationService;
|
_authorizationService = authorizationService;
|
||||||
_storeRepository = storeRepository;
|
_storeRepository = storeRepository;
|
||||||
}
|
}
|
||||||
@@ -169,7 +173,7 @@ public class UIFormsController : Controller
|
|||||||
FormName = formData.Name,
|
FormName = formData.Name,
|
||||||
Form = form,
|
Form = form,
|
||||||
StoreName = store?.StoreName,
|
StoreName = store?.StoreName,
|
||||||
StoreBranding = new StoreBrandingViewModel(storeBlob)
|
StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, storeBlob)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,8 +38,6 @@ namespace BTCPayServer.HostedServices
|
|||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public decimal Amount { get; set; }
|
public decimal Amount { get; set; }
|
||||||
public string Currency { get; set; }
|
public string Currency { get; set; }
|
||||||
public string CustomCSSLink { get; set; }
|
|
||||||
public string EmbeddedCSS { get; set; }
|
|
||||||
public PayoutMethodId[] PayoutMethodIds { get; set; }
|
public PayoutMethodId[] PayoutMethodIds { get; set; }
|
||||||
public bool AutoApproveClaims { get; set; }
|
public bool AutoApproveClaims { get; set; }
|
||||||
public TimeSpan? BOLT11Expiration { get; set; }
|
public TimeSpan? BOLT11Expiration { get; set; }
|
||||||
@@ -131,13 +129,11 @@ namespace BTCPayServer.HostedServices
|
|||||||
Limit = create.Amount,
|
Limit = create.Amount,
|
||||||
SupportedPaymentMethods = create.PayoutMethodIds,
|
SupportedPaymentMethods = create.PayoutMethodIds,
|
||||||
AutoApproveClaims = create.AutoApproveClaims,
|
AutoApproveClaims = create.AutoApproveClaims,
|
||||||
View = new PullPaymentBlob.PullPaymentView()
|
View = new PullPaymentBlob.PullPaymentView
|
||||||
{
|
{
|
||||||
Title = create.Name ?? string.Empty,
|
Title = create.Name ?? string.Empty,
|
||||||
Description = create.Description ?? string.Empty,
|
Description = create.Description ?? string.Empty,
|
||||||
CustomCSSLink = create.CustomCSSLink,
|
Email = null
|
||||||
Email = null,
|
|
||||||
EmbeddedCSS = create.EmbeddedCSS,
|
|
||||||
},
|
},
|
||||||
BOLT11Expiration = create.BOLT11Expiration ?? TimeSpan.FromDays(30.0)
|
BOLT11Expiration = create.BOLT11Expiration ?? TimeSpan.FromDays(30.0)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ namespace BTCPayServer.Hosting
|
|||||||
services.TryAddSingleton<EventAggregator>();
|
services.TryAddSingleton<EventAggregator>();
|
||||||
services.TryAddSingleton<PaymentRequestService>();
|
services.TryAddSingleton<PaymentRequestService>();
|
||||||
services.TryAddSingleton<UserService>();
|
services.TryAddSingleton<UserService>();
|
||||||
|
services.TryAddSingleton<UriResolver>();
|
||||||
services.TryAddSingleton<WalletHistogramService>();
|
services.TryAddSingleton<WalletHistogramService>();
|
||||||
services.AddSingleton<ApplicationDbContextFactory>();
|
services.AddSingleton<ApplicationDbContextFactory>();
|
||||||
services.AddOptions<BTCPayServerOptions>().Configure(
|
services.AddOptions<BTCPayServerOptions>().Configure(
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AngleSharp.Common;
|
|
||||||
using BTCPayServer.Abstractions.Contracts;
|
using BTCPayServer.Abstractions.Contracts;
|
||||||
using BTCPayServer.Client.Models;
|
|
||||||
using BTCPayServer.Configuration;
|
using BTCPayServer.Configuration;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
using BTCPayServer.Fido2;
|
using BTCPayServer.Fido2;
|
||||||
@@ -26,17 +24,14 @@ using BTCPayServer.Services.Stores;
|
|||||||
using BTCPayServer.Storage.Models;
|
using BTCPayServer.Storage.Models;
|
||||||
using BTCPayServer.Storage.Services.Providers.FileSystemStorage.Configuration;
|
using BTCPayServer.Storage.Services.Providers.FileSystemStorage.Configuration;
|
||||||
using Fido2NetLib.Objects;
|
using Fido2NetLib.Objects;
|
||||||
using Microsoft.AspNetCore.Identity;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using NBitcoin;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using PeterO.Cbor;
|
using PeterO.Cbor;
|
||||||
using YamlDotNet.RepresentationModel;
|
using YamlDotNet.RepresentationModel;
|
||||||
using LightningAddressData = BTCPayServer.Data.LightningAddressData;
|
using LightningAddressData = BTCPayServer.Data.LightningAddressData;
|
||||||
using Serializer = NBXplorer.Serializer;
|
|
||||||
|
|
||||||
namespace BTCPayServer.Hosting
|
namespace BTCPayServer.Hosting
|
||||||
{
|
{
|
||||||
@@ -53,6 +48,7 @@ namespace BTCPayServer.Hosting
|
|||||||
private readonly LightningAddressService _lightningAddressService;
|
private readonly LightningAddressService _lightningAddressService;
|
||||||
private readonly ILogger<MigrationStartupTask> _logger;
|
private readonly ILogger<MigrationStartupTask> _logger;
|
||||||
private readonly LightningClientFactoryService _lightningClientFactoryService;
|
private readonly LightningClientFactoryService _lightningClientFactoryService;
|
||||||
|
private readonly IFileService _fileService;
|
||||||
|
|
||||||
public IOptions<LightningNetworkOptions> LightningOptions { get; }
|
public IOptions<LightningNetworkOptions> LightningOptions { get; }
|
||||||
|
|
||||||
@@ -67,6 +63,7 @@ namespace BTCPayServer.Hosting
|
|||||||
BTCPayNetworkJsonSerializerSettings btcPayNetworkJsonSerializerSettings,
|
BTCPayNetworkJsonSerializerSettings btcPayNetworkJsonSerializerSettings,
|
||||||
LightningAddressService lightningAddressService,
|
LightningAddressService lightningAddressService,
|
||||||
ILogger<MigrationStartupTask> logger,
|
ILogger<MigrationStartupTask> logger,
|
||||||
|
IFileService fileService,
|
||||||
LightningClientFactoryService lightningClientFactoryService)
|
LightningClientFactoryService lightningClientFactoryService)
|
||||||
{
|
{
|
||||||
_handlers = handlers;
|
_handlers = handlers;
|
||||||
@@ -78,6 +75,7 @@ namespace BTCPayServer.Hosting
|
|||||||
_btcPayNetworkJsonSerializerSettings = btcPayNetworkJsonSerializerSettings;
|
_btcPayNetworkJsonSerializerSettings = btcPayNetworkJsonSerializerSettings;
|
||||||
_lightningAddressService = lightningAddressService;
|
_lightningAddressService = lightningAddressService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_fileService = fileService;
|
||||||
_lightningClientFactoryService = lightningClientFactoryService;
|
_lightningClientFactoryService = lightningClientFactoryService;
|
||||||
LightningOptions = lightningOptions;
|
LightningOptions = lightningOptions;
|
||||||
}
|
}
|
||||||
|
|||||||
29
BTCPayServer/JsonConverters/UnresolvedUriJsonConverter.cs
Normal file
29
BTCPayServer/JsonConverters/UnresolvedUriJsonConverter.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using BTCPayServer.Payouts;
|
||||||
|
using NBitcoin.JsonConverters;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace BTCPayServer.JsonConverters
|
||||||
|
{
|
||||||
|
public class UnresolvedUriJsonConverter : JsonConverter<UnresolvedUri>
|
||||||
|
{
|
||||||
|
public override UnresolvedUri ReadJson(JsonReader reader, Type objectType, UnresolvedUri existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonToken.Null)
|
||||||
|
return null;
|
||||||
|
if (reader.TokenType != JsonToken.String)
|
||||||
|
throw new JsonObjectException("A UnresolvedUri should be a string", reader);
|
||||||
|
var str = (string)reader.Value;
|
||||||
|
if (str.Length == 0)
|
||||||
|
return null;
|
||||||
|
return UnresolvedUri.Create(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteJson(JsonWriter writer, UnresolvedUri value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
if (value != null)
|
||||||
|
writer.WriteValue(value.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,8 +48,6 @@ namespace BTCPayServer.Models.PaymentRequestViewModels
|
|||||||
Description = blob.Description;
|
Description = blob.Description;
|
||||||
ExpiryDate = blob.ExpiryDate?.UtcDateTime;
|
ExpiryDate = blob.ExpiryDate?.UtcDateTime;
|
||||||
Email = blob.Email;
|
Email = blob.Email;
|
||||||
CustomCSSLink = blob.CustomCSSLink;
|
|
||||||
EmbeddedCSS = blob.EmbeddedCSS;
|
|
||||||
AllowCustomPaymentAmounts = blob.AllowCustomPaymentAmounts;
|
AllowCustomPaymentAmounts = blob.AllowCustomPaymentAmounts;
|
||||||
FormResponse = blob.FormResponse is null
|
FormResponse = blob.FormResponse is null
|
||||||
? null
|
? null
|
||||||
@@ -86,13 +84,6 @@ namespace BTCPayServer.Models.PaymentRequestViewModels
|
|||||||
[MailboxAddress]
|
[MailboxAddress]
|
||||||
public string Email { get; set; }
|
public string Email { get; set; }
|
||||||
|
|
||||||
[MaxLength(500)]
|
|
||||||
[Display(Name = "Custom CSS URL")]
|
|
||||||
public string CustomCSSLink { get; set; }
|
|
||||||
|
|
||||||
[Display(Name = "Custom CSS Code")]
|
|
||||||
public string EmbeddedCSS { get; set; }
|
|
||||||
|
|
||||||
[Display(Name = "Allow payee to create invoices with custom amounts")]
|
[Display(Name = "Allow payee to create invoices with custom amounts")]
|
||||||
public bool AllowCustomPaymentAmounts { get; set; }
|
public bool AllowCustomPaymentAmounts { get; set; }
|
||||||
|
|
||||||
@@ -115,8 +106,6 @@ namespace BTCPayServer.Models.PaymentRequestViewModels
|
|||||||
Description = blob.Description;
|
Description = blob.Description;
|
||||||
ExpiryDate = blob.ExpiryDate?.UtcDateTime;
|
ExpiryDate = blob.ExpiryDate?.UtcDateTime;
|
||||||
Email = blob.Email;
|
Email = blob.Email;
|
||||||
EmbeddedCSS = blob.EmbeddedCSS;
|
|
||||||
CustomCSSLink = blob.CustomCSSLink;
|
|
||||||
AllowCustomPaymentAmounts = blob.AllowCustomPaymentAmounts;
|
AllowCustomPaymentAmounts = blob.AllowCustomPaymentAmounts;
|
||||||
switch (data.Status)
|
switch (data.Status)
|
||||||
{
|
{
|
||||||
@@ -154,8 +143,6 @@ namespace BTCPayServer.Models.PaymentRequestViewModels
|
|||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public string StoreName { get; set; }
|
public string StoreName { get; set; }
|
||||||
public string StoreWebsite { get; set; }
|
public string StoreWebsite { get; set; }
|
||||||
public string EmbeddedCSS { get; set; }
|
|
||||||
public string CustomCSSLink { get; set; }
|
|
||||||
|
|
||||||
#nullable enable
|
#nullable enable
|
||||||
public class InvoiceList : List<PaymentRequestInvoice>
|
public class InvoiceList : List<PaymentRequestInvoice>
|
||||||
|
|||||||
@@ -21,20 +21,15 @@ public class BrandingViewModel
|
|||||||
[Display(Name = "Custom Theme Extension Type")]
|
[Display(Name = "Custom Theme Extension Type")]
|
||||||
public ThemeExtension CustomThemeExtension { get; set; }
|
public ThemeExtension CustomThemeExtension { get; set; }
|
||||||
|
|
||||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
|
||||||
[MaxLength(500)]
|
|
||||||
[Display(Name = "Custom Theme CSS URL")]
|
|
||||||
public string CustomThemeCssUri { get; set; }
|
|
||||||
|
|
||||||
[Display(Name = "Custom Theme File")]
|
[Display(Name = "Custom Theme File")]
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public IFormFile CustomThemeFile { get; set; }
|
public IFormFile CustomThemeFile { get; set; }
|
||||||
|
|
||||||
public string CustomThemeFileId { get; set; }
|
public string CustomThemeCssUrl { get; set; }
|
||||||
|
|
||||||
[Display(Name = "Logo")]
|
[Display(Name = "Logo")]
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public IFormFile LogoFile { get; set; }
|
public IFormFile LogoFile { get; set; }
|
||||||
|
|
||||||
public string LogoFileId { get; set; }
|
public string LogoUrl { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Abstractions.Extensions;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
|
using BTCPayServer.Services;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
namespace BTCPayServer.Models;
|
namespace BTCPayServer.Models;
|
||||||
|
|
||||||
public class StoreBrandingViewModel
|
public class StoreBrandingViewModel
|
||||||
{
|
{
|
||||||
public string BrandColor { get; set; }
|
public string BrandColor { get; set; }
|
||||||
public string LogoFileId { get; set; }
|
public string LogoUrl { get; set; }
|
||||||
public string CssFileId { get; set; }
|
public string CssUrl { get; set; }
|
||||||
public string CustomCSSLink { get; set; }
|
|
||||||
public string EmbeddedCSS { get; set; }
|
|
||||||
|
|
||||||
public StoreBrandingViewModel()
|
public StoreBrandingViewModel()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
public static async Task<StoreBrandingViewModel> CreateAsync(HttpRequest request, UriResolver uriResolver, StoreBlob storeBlob)
|
||||||
public StoreBrandingViewModel(StoreBlob storeBlob)
|
{
|
||||||
|
if (storeBlob == null)
|
||||||
|
return new StoreBrandingViewModel();
|
||||||
|
var result = new StoreBrandingViewModel(storeBlob);
|
||||||
|
result.LogoUrl = await uriResolver.Resolve(request.GetAbsoluteRootUri(), storeBlob.LogoUrl);
|
||||||
|
result.CssUrl = await uriResolver.Resolve(request.GetAbsoluteRootUri(), storeBlob.CssUrl);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
private StoreBrandingViewModel(StoreBlob storeBlob)
|
||||||
{
|
{
|
||||||
if (storeBlob == null) return;
|
|
||||||
BrandColor = storeBlob.BrandColor;
|
BrandColor = storeBlob.BrandColor;
|
||||||
LogoFileId = storeBlob.LogoFileId;
|
|
||||||
CssFileId = storeBlob.CssFileId;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace BTCPayServer.Models.StoreViewModels
|
|||||||
|
|
||||||
[Display(Name = "Custom sound file for successful payment")]
|
[Display(Name = "Custom sound file for successful payment")]
|
||||||
public IFormFile SoundFile { get; set; }
|
public IFormFile SoundFile { get; set; }
|
||||||
public string SoundFileId { get; set; }
|
public string PaymentSoundUrl { get; set; }
|
||||||
|
|
||||||
[Display(Name = "Custom HTML title to display on Checkout page")]
|
[Display(Name = "Custom HTML title to display on Checkout page")]
|
||||||
public string HtmlTitle { get; set; }
|
public string HtmlTitle { get; set; }
|
||||||
|
|||||||
@@ -27,11 +27,11 @@ namespace BTCPayServer.Models.StoreViewModels
|
|||||||
|
|
||||||
[Display(Name = "Logo")]
|
[Display(Name = "Logo")]
|
||||||
public IFormFile LogoFile { get; set; }
|
public IFormFile LogoFile { get; set; }
|
||||||
public string LogoFileId { get; set; }
|
public string LogoUrl { get; set; }
|
||||||
|
|
||||||
[Display(Name = "Custom CSS")]
|
[Display(Name = "Custom CSS")]
|
||||||
public IFormFile CssFile { get; set; }
|
public IFormFile CssFile { get; set; }
|
||||||
public string CssFileId { get; set; }
|
public string CssUrl { get; set; }
|
||||||
|
|
||||||
public bool Archived { get; set; }
|
public bool Archived { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -55,11 +55,6 @@ namespace BTCPayServer.Models.WalletViewModels
|
|||||||
[Required]
|
[Required]
|
||||||
[ReadOnly(true)]
|
[ReadOnly(true)]
|
||||||
public string Currency { get; set; }
|
public string Currency { get; set; }
|
||||||
[MaxLength(500)]
|
|
||||||
[Display(Name = "Custom CSS URL")]
|
|
||||||
public string CustomCSSLink { get; set; }
|
|
||||||
[Display(Name = "Custom CSS Code")]
|
|
||||||
public string EmbeddedCSS { get; set; }
|
|
||||||
|
|
||||||
[Display(Name = "Payout Methods")]
|
[Display(Name = "Payout Methods")]
|
||||||
public IEnumerable<string> PayoutMethods { get; set; }
|
public IEnumerable<string> PayoutMethods { get; set; }
|
||||||
@@ -91,8 +86,6 @@ namespace BTCPayServer.Models.WalletViewModels
|
|||||||
var blob = data.GetBlob();
|
var blob = data.GetBlob();
|
||||||
Name = blob.Name;
|
Name = blob.Name;
|
||||||
Description = blob.Description;
|
Description = blob.Description;
|
||||||
CustomCSSLink = blob.View.CustomCSSLink;
|
|
||||||
EmbeddedCSS = blob.View.EmbeddedCSS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MaxLength(30)]
|
[MaxLength(30)]
|
||||||
@@ -100,11 +93,5 @@ namespace BTCPayServer.Models.WalletViewModels
|
|||||||
|
|
||||||
[Display(Name = "Memo")]
|
[Display(Name = "Memo")]
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
|
|
||||||
[Display(Name = "Custom CSS URL")]
|
|
||||||
public string CustomCSSLink { get; set; }
|
|
||||||
|
|
||||||
[Display(Name = "Custom CSS Code")]
|
|
||||||
public string EmbeddedCSS { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ using BTCPayServer.Forms.Models;
|
|||||||
using BTCPayServer.Models;
|
using BTCPayServer.Models;
|
||||||
using BTCPayServer.Plugins.Crowdfund.Models;
|
using BTCPayServer.Plugins.Crowdfund.Models;
|
||||||
using BTCPayServer.Plugins.PointOfSale.Models;
|
using BTCPayServer.Plugins.PointOfSale.Models;
|
||||||
|
using BTCPayServer.Services;
|
||||||
using BTCPayServer.Services.Apps;
|
using BTCPayServer.Services.Apps;
|
||||||
using BTCPayServer.Services.Invoices;
|
using BTCPayServer.Services.Invoices;
|
||||||
using BTCPayServer.Services.Rates;
|
using BTCPayServer.Services.Rates;
|
||||||
@@ -41,6 +42,7 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers
|
|||||||
AppService appService,
|
AppService appService,
|
||||||
CurrencyNameTable currencies,
|
CurrencyNameTable currencies,
|
||||||
EventAggregator eventAggregator,
|
EventAggregator eventAggregator,
|
||||||
|
UriResolver uriResolver,
|
||||||
StoreRepository storeRepository,
|
StoreRepository storeRepository,
|
||||||
UIInvoiceController invoiceController,
|
UIInvoiceController invoiceController,
|
||||||
UserManager<ApplicationUser> userManager,
|
UserManager<ApplicationUser> userManager,
|
||||||
@@ -53,11 +55,13 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers
|
|||||||
_app = app;
|
_app = app;
|
||||||
_storeRepository = storeRepository;
|
_storeRepository = storeRepository;
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
|
_uriResolver = uriResolver;
|
||||||
_invoiceController = invoiceController;
|
_invoiceController = invoiceController;
|
||||||
FormDataService = formDataService;
|
FormDataService = formDataService;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly EventAggregator _eventAggregator;
|
private readonly EventAggregator _eventAggregator;
|
||||||
|
private readonly UriResolver _uriResolver;
|
||||||
private readonly CurrencyNameTable _currencies;
|
private readonly CurrencyNameTable _currencies;
|
||||||
private readonly StoreRepository _storeRepository;
|
private readonly StoreRepository _storeRepository;
|
||||||
private readonly AppService _appService;
|
private readonly AppService _appService;
|
||||||
@@ -315,7 +319,7 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers
|
|||||||
var vm = new FormViewModel
|
var vm = new FormViewModel
|
||||||
{
|
{
|
||||||
StoreName = store.StoreName,
|
StoreName = store.StoreName,
|
||||||
StoreBranding = new StoreBrandingViewModel(storeBlob),
|
StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, storeBlob),
|
||||||
FormName = formData.Name,
|
FormName = formData.Name,
|
||||||
Form = form,
|
Form = form,
|
||||||
AspController = controller,
|
AspController = controller,
|
||||||
@@ -403,10 +407,8 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers
|
|||||||
TargetCurrency = settings.TargetCurrency,
|
TargetCurrency = settings.TargetCurrency,
|
||||||
Description = settings.Description,
|
Description = settings.Description,
|
||||||
MainImageUrl = settings.MainImageUrl,
|
MainImageUrl = settings.MainImageUrl,
|
||||||
EmbeddedCSS = settings.EmbeddedCSS,
|
|
||||||
EndDate = settings.EndDate,
|
EndDate = settings.EndDate,
|
||||||
TargetAmount = settings.TargetAmount,
|
TargetAmount = settings.TargetAmount,
|
||||||
CustomCSSLink = settings.CustomCSSLink,
|
|
||||||
NotificationUrl = settings.NotificationUrl,
|
NotificationUrl = settings.NotificationUrl,
|
||||||
Tagline = settings.Tagline,
|
Tagline = settings.Tagline,
|
||||||
PerksTemplate = settings.PerksTemplate,
|
PerksTemplate = settings.PerksTemplate,
|
||||||
@@ -518,9 +520,7 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers
|
|||||||
Description = vm.Description,
|
Description = vm.Description,
|
||||||
EndDate = vm.EndDate?.ToUniversalTime(),
|
EndDate = vm.EndDate?.ToUniversalTime(),
|
||||||
TargetAmount = vm.TargetAmount,
|
TargetAmount = vm.TargetAmount,
|
||||||
CustomCSSLink = vm.CustomCSSLink,
|
|
||||||
MainImageUrl = vm.MainImageUrl,
|
MainImageUrl = vm.MainImageUrl,
|
||||||
EmbeddedCSS = vm.EmbeddedCSS,
|
|
||||||
NotificationUrl = vm.NotificationUrl,
|
NotificationUrl = vm.NotificationUrl,
|
||||||
Tagline = vm.Tagline,
|
Tagline = vm.Tagline,
|
||||||
PerksTemplate = vm.PerksTemplate,
|
PerksTemplate = vm.PerksTemplate,
|
||||||
@@ -580,6 +580,7 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var info = (ViewCrowdfundViewModel)await _app.GetInfo(app);
|
var info = (ViewCrowdfundViewModel)await _app.GetInfo(app);
|
||||||
|
info.StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, app.StoreData.GetStoreBlob());
|
||||||
info.HubPath = AppHub.GetHubPath(Request);
|
info.HubPath = AppHub.GetHubPath(Request);
|
||||||
info.SimpleDisplay = Request.Query.ContainsKey("simple");
|
info.SimpleDisplay = Request.Query.ContainsKey("simple");
|
||||||
return info;
|
return info;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ using BTCPayServer.Services.Apps;
|
|||||||
using BTCPayServer.Services.Invoices;
|
using BTCPayServer.Services.Invoices;
|
||||||
using BTCPayServer.Services.Rates;
|
using BTCPayServer.Services.Rates;
|
||||||
using Ganss.Xss;
|
using Ganss.Xss;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
@@ -180,11 +181,6 @@ namespace BTCPayServer.Plugins.Crowdfund
|
|||||||
|
|
||||||
var store = appData.StoreData;
|
var store = appData.StoreData;
|
||||||
var storeBlob = store.GetStoreBlob();
|
var storeBlob = store.GetStoreBlob();
|
||||||
var storeBranding = new StoreBrandingViewModel(storeBlob)
|
|
||||||
{
|
|
||||||
CustomCSSLink = settings.CustomCSSLink,
|
|
||||||
EmbeddedCSS = settings.EmbeddedCSS
|
|
||||||
};
|
|
||||||
var formUrl = settings.FormId != null
|
var formUrl = settings.FormId != null
|
||||||
? _linkGenerator.GetPathByAction(nameof(UICrowdfundController.CrowdfundForm), "UICrowdfund",
|
? _linkGenerator.GetPathByAction(nameof(UICrowdfundController.CrowdfundForm), "UICrowdfund",
|
||||||
new { appId = appData.Id }, _options.Value.RootPath)
|
new { appId = appData.Id }, _options.Value.RootPath)
|
||||||
@@ -196,7 +192,6 @@ namespace BTCPayServer.Plugins.Crowdfund
|
|||||||
Description = settings.Description,
|
Description = settings.Description,
|
||||||
MainImageUrl = settings.MainImageUrl,
|
MainImageUrl = settings.MainImageUrl,
|
||||||
StoreName = store.StoreName,
|
StoreName = store.StoreName,
|
||||||
StoreBranding = storeBranding,
|
|
||||||
StoreId = appData.StoreDataId,
|
StoreId = appData.StoreDataId,
|
||||||
AppId = appData.Id,
|
AppId = appData.Id,
|
||||||
StartDate = settings.StartDate?.ToUniversalTime(),
|
StartDate = settings.StartDate?.ToUniversalTime(),
|
||||||
|
|||||||
@@ -86,13 +86,6 @@ namespace BTCPayServer.Plugins.Crowdfund.Models
|
|||||||
[Display(Name = "Contribution Perks Template")]
|
[Display(Name = "Contribution Perks Template")]
|
||||||
public string PerksTemplate { get; set; }
|
public string PerksTemplate { get; set; }
|
||||||
|
|
||||||
[MaxLength(500)]
|
|
||||||
[Display(Name = "Custom CSS URL")]
|
|
||||||
public string CustomCSSLink { get; set; }
|
|
||||||
|
|
||||||
[Display(Name = "Custom CSS Code")]
|
|
||||||
public string EmbeddedCSS { get; set; }
|
|
||||||
|
|
||||||
[Display(Name = "Count all invoices created on the store as part of the goal")]
|
[Display(Name = "Count all invoices created on the store as part of the goal")]
|
||||||
public bool UseAllStoreInvoices { get; set; }
|
public bool UseAllStoreInvoices { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
|||||||
AppService appService,
|
AppService appService,
|
||||||
CurrencyNameTable currencies,
|
CurrencyNameTable currencies,
|
||||||
StoreRepository storeRepository,
|
StoreRepository storeRepository,
|
||||||
|
UriResolver uriResolver,
|
||||||
InvoiceRepository invoiceRepository,
|
InvoiceRepository invoiceRepository,
|
||||||
UIInvoiceController invoiceController,
|
UIInvoiceController invoiceController,
|
||||||
FormDataService formDataService,
|
FormDataService formDataService,
|
||||||
@@ -55,6 +56,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
|||||||
_currencies = currencies;
|
_currencies = currencies;
|
||||||
_appService = appService;
|
_appService = appService;
|
||||||
_storeRepository = storeRepository;
|
_storeRepository = storeRepository;
|
||||||
|
_uriResolver = uriResolver;
|
||||||
_invoiceRepository = invoiceRepository;
|
_invoiceRepository = invoiceRepository;
|
||||||
_invoiceController = invoiceController;
|
_invoiceController = invoiceController;
|
||||||
_displayFormatter = displayFormatter;
|
_displayFormatter = displayFormatter;
|
||||||
@@ -64,6 +66,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
|||||||
private readonly CurrencyNameTable _currencies;
|
private readonly CurrencyNameTable _currencies;
|
||||||
private readonly InvoiceRepository _invoiceRepository;
|
private readonly InvoiceRepository _invoiceRepository;
|
||||||
private readonly StoreRepository _storeRepository;
|
private readonly StoreRepository _storeRepository;
|
||||||
|
private readonly UriResolver _uriResolver;
|
||||||
private readonly AppService _appService;
|
private readonly AppService _appService;
|
||||||
private readonly UIInvoiceController _invoiceController;
|
private readonly UIInvoiceController _invoiceController;
|
||||||
private readonly DisplayFormatter _displayFormatter;
|
private readonly DisplayFormatter _displayFormatter;
|
||||||
@@ -86,13 +89,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
|||||||
viewType ??= settings.EnableShoppingCart ? PosViewType.Cart : settings.DefaultView;
|
viewType ??= settings.EnableShoppingCart ? PosViewType.Cart : settings.DefaultView;
|
||||||
var store = await _appService.GetStore(app);
|
var store = await _appService.GetStore(app);
|
||||||
var storeBlob = store.GetStoreBlob();
|
var storeBlob = store.GetStoreBlob();
|
||||||
|
var storeBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, storeBlob);
|
||||||
var storeBranding = new StoreBrandingViewModel(storeBlob)
|
|
||||||
{
|
|
||||||
EmbeddedCSS = settings.EmbeddedCSS,
|
|
||||||
CustomCSSLink = settings.CustomCSSLink
|
|
||||||
};
|
|
||||||
// Check if the currency is COP or ARS (exclude decimal places)
|
|
||||||
|
|
||||||
return View($"PointOfSale/Public/{viewType}", new ViewPointOfSaleViewModel
|
return View($"PointOfSale/Public/{viewType}", new ViewPointOfSaleViewModel
|
||||||
{
|
{
|
||||||
@@ -466,7 +463,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
|||||||
var vm = new FormViewModel
|
var vm = new FormViewModel
|
||||||
{
|
{
|
||||||
StoreName = store.StoreName,
|
StoreName = store.StoreName,
|
||||||
StoreBranding = new StoreBrandingViewModel(storeBlob),
|
StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, storeBlob),
|
||||||
FormName = formData.Name,
|
FormName = formData.Name,
|
||||||
Form = form,
|
Form = form,
|
||||||
AspController = controller,
|
AspController = controller,
|
||||||
@@ -529,7 +526,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
|||||||
viewModel.FormName = formData.Name;
|
viewModel.FormName = formData.Name;
|
||||||
viewModel.Form = form;
|
viewModel.Form = form;
|
||||||
viewModel.FormParameters = formParameters;
|
viewModel.FormParameters = formParameters;
|
||||||
viewModel.StoreBranding = new StoreBrandingViewModel(storeBlob);
|
viewModel.StoreBranding = await StoreBrandingViewModel.CreateAsync(Request, _uriResolver, storeBlob);
|
||||||
return View("Views/UIForms/View", viewModel);
|
return View("Views/UIForms/View", viewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -598,8 +595,6 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
|||||||
CustomButtonText = settings.CustomButtonText ?? PointOfSaleSettings.CUSTOM_BUTTON_TEXT_DEF,
|
CustomButtonText = settings.CustomButtonText ?? PointOfSaleSettings.CUSTOM_BUTTON_TEXT_DEF,
|
||||||
CustomTipText = settings.CustomTipText ?? PointOfSaleSettings.CUSTOM_TIP_TEXT_DEF,
|
CustomTipText = settings.CustomTipText ?? PointOfSaleSettings.CUSTOM_TIP_TEXT_DEF,
|
||||||
CustomTipPercentages = settings.CustomTipPercentages != null ? string.Join(",", settings.CustomTipPercentages) : string.Join(",", PointOfSaleSettings.CUSTOM_TIP_PERCENTAGES_DEF),
|
CustomTipPercentages = settings.CustomTipPercentages != null ? string.Join(",", settings.CustomTipPercentages) : string.Join(",", PointOfSaleSettings.CUSTOM_TIP_PERCENTAGES_DEF),
|
||||||
CustomCSSLink = settings.CustomCSSLink,
|
|
||||||
EmbeddedCSS = settings.EmbeddedCSS,
|
|
||||||
Description = settings.Description,
|
Description = settings.Description,
|
||||||
NotificationUrl = settings.NotificationUrl,
|
NotificationUrl = settings.NotificationUrl,
|
||||||
RedirectUrl = settings.RedirectUrl,
|
RedirectUrl = settings.RedirectUrl,
|
||||||
@@ -689,11 +684,9 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
|||||||
CustomButtonText = vm.CustomButtonText,
|
CustomButtonText = vm.CustomButtonText,
|
||||||
CustomTipText = vm.CustomTipText,
|
CustomTipText = vm.CustomTipText,
|
||||||
CustomTipPercentages = ListSplit(vm.CustomTipPercentages),
|
CustomTipPercentages = ListSplit(vm.CustomTipPercentages),
|
||||||
CustomCSSLink = vm.CustomCSSLink,
|
|
||||||
NotificationUrl = vm.NotificationUrl,
|
NotificationUrl = vm.NotificationUrl,
|
||||||
RedirectUrl = vm.RedirectUrl,
|
RedirectUrl = vm.RedirectUrl,
|
||||||
Description = vm.Description,
|
Description = vm.Description,
|
||||||
EmbeddedCSS = vm.EmbeddedCSS,
|
|
||||||
RedirectAutomatically = string.IsNullOrEmpty(vm.RedirectAutomatically) ? null : bool.Parse(vm.RedirectAutomatically),
|
RedirectAutomatically = string.IsNullOrEmpty(vm.RedirectAutomatically) ? null : bool.Parse(vm.RedirectAutomatically),
|
||||||
FormId = vm.FormId
|
FormId = vm.FormId
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -67,10 +67,6 @@ namespace BTCPayServer.Plugins.PointOfSale.Models
|
|||||||
[Display(Name = "Tip percentage amounts (comma separated)")]
|
[Display(Name = "Tip percentage amounts (comma separated)")]
|
||||||
public string CustomTipPercentages { get; set; }
|
public string CustomTipPercentages { get; set; }
|
||||||
|
|
||||||
[MaxLength(500)]
|
|
||||||
[Display(Name = "Custom CSS URL")]
|
|
||||||
public string CustomCSSLink { get; set; }
|
|
||||||
|
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
|
|
||||||
[Display(Name = "Redirect invoice to redirect url automatically after paid")]
|
[Display(Name = "Redirect invoice to redirect url automatically after paid")]
|
||||||
@@ -99,8 +95,6 @@ namespace BTCPayServer.Plugins.PointOfSale.Models
|
|||||||
}
|
}
|
||||||
}, nameof(SelectListItem.Value), nameof(SelectListItem.Text), RedirectAutomatically);
|
}, nameof(SelectListItem.Value), nameof(SelectListItem.Text), RedirectAutomatically);
|
||||||
|
|
||||||
[Display(Name = "Custom CSS Code")]
|
|
||||||
public string EmbeddedCSS { get; set; }
|
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
|
|
||||||
[Display(Name = "Request customer data on checkout")]
|
[Display(Name = "Request customer data on checkout")]
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ using BTCPayServer.Services.Rates;
|
|||||||
using BTCPayServer.Services.Stores;
|
using BTCPayServer.Services.Stores;
|
||||||
using Dapper;
|
using Dapper;
|
||||||
using Ganss.Xss;
|
using Ganss.Xss;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
using NBitcoin.DataEncoders;
|
using NBitcoin.DataEncoders;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
using BTCPayServer.Services.Invoices;
|
using BTCPayServer.Services.Invoices;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
namespace BTCPayServer.Services.Apps
|
namespace BTCPayServer.Services.Apps
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,11 +28,9 @@ namespace BTCPayServer.Services.Apps
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool EnforceTargetAmount { get; set; }
|
public bool EnforceTargetAmount { get; set; }
|
||||||
public string CustomCSSLink { get; set; }
|
|
||||||
public string MainImageUrl { get; set; }
|
public string MainImageUrl { get; set; }
|
||||||
public string NotificationUrl { get; set; }
|
public string NotificationUrl { get; set; }
|
||||||
public string Tagline { get; set; }
|
public string Tagline { get; set; }
|
||||||
public string EmbeddedCSS { get; set; }
|
|
||||||
public string PerksTemplate { get; set; }
|
public string PerksTemplate { get; set; }
|
||||||
public bool DisqusEnabled { get; set; }
|
public bool DisqusEnabled { get; set; }
|
||||||
public bool SoundsEnabled { get; set; }
|
public bool SoundsEnabled { get; set; }
|
||||||
|
|||||||
@@ -103,11 +103,6 @@ namespace BTCPayServer.Services.Apps
|
|||||||
public string CustomTipText { get; set; } = CUSTOM_TIP_TEXT_DEF;
|
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 = new int[] { 15, 18, 20 };
|
||||||
public int[] CustomTipPercentages { get; set; } = CUSTOM_TIP_PERCENTAGES_DEF;
|
public int[] CustomTipPercentages { get; set; } = CUSTOM_TIP_PERCENTAGES_DEF;
|
||||||
|
|
||||||
public string CustomCSSLink { get; set; }
|
|
||||||
|
|
||||||
public string EmbeddedCSS { get; set; }
|
|
||||||
|
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public string NotificationUrl { get; set; }
|
public string NotificationUrl { get; set; }
|
||||||
public string RedirectUrl { get; set; }
|
public string RedirectUrl { get; set; }
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
using System;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using BTCPayServer.JsonConverters;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace BTCPayServer.Services;
|
namespace BTCPayServer.Services;
|
||||||
@@ -21,14 +23,11 @@ public class ThemeSettings
|
|||||||
[Display(Name = "Custom Theme Extension Type")]
|
[Display(Name = "Custom Theme Extension Type")]
|
||||||
public ThemeExtension CustomThemeExtension { get; set; }
|
public ThemeExtension CustomThemeExtension { get; set; }
|
||||||
|
|
||||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
[JsonConverter(typeof(UnresolvedUriJsonConverter))]
|
||||||
[MaxLength(500)]
|
public UnresolvedUri CustomThemeCssUrl { get; set; }
|
||||||
[Display(Name = "Custom Theme CSS URL")]
|
|
||||||
public string CustomThemeCssUri { get; set; }
|
|
||||||
|
|
||||||
public string CustomThemeFileId { get; set; }
|
[JsonConverter(typeof(UnresolvedUriJsonConverter))]
|
||||||
|
public UnresolvedUri LogoUrl { get; set; }
|
||||||
public string LogoFileId { get; set; }
|
|
||||||
|
|
||||||
public bool FirstRun { get; set; } = true;
|
public bool FirstRun { get; set; } = true;
|
||||||
|
|
||||||
@@ -37,9 +36,4 @@ public class ThemeSettings
|
|||||||
// no logs
|
// no logs
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string CssUri
|
|
||||||
{
|
|
||||||
get => CustomTheme ? CustomThemeCssUri : "/main/themes/default.css";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
37
BTCPayServer/Services/UriResolver.cs
Normal file
37
BTCPayServer/Services/UriResolver.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#nullable enable
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Security.AccessControl;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Abstractions.Contracts;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Services
|
||||||
|
{
|
||||||
|
public class UriResolver
|
||||||
|
{
|
||||||
|
private readonly IFileService _fileService;
|
||||||
|
|
||||||
|
public UriResolver(IFileService fileService)
|
||||||
|
{
|
||||||
|
_fileService = fileService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If <paramref name="url"/> is an absolute URL, returns it as is.
|
||||||
|
/// If <paramref name="url"/> starts with "fileid:ID", returns the URL of the file with the ID.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="baseUri"><see cref="BTCPayServer.Abstractions.Extensions.HttpRequestExtensions.GetAbsoluteRootUri"/></param>
|
||||||
|
/// <param name="uri"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<string?> Resolve(Uri baseUri, UnresolvedUri? uri)
|
||||||
|
{
|
||||||
|
return uri switch
|
||||||
|
{
|
||||||
|
null => null,
|
||||||
|
UnresolvedUri.FileIdUri fileId => await _fileService.GetFileUrl(baseUri, fileId.FileId),
|
||||||
|
UnresolvedUri.Raw raw => raw.Uri,
|
||||||
|
_ => throw new NotSupportedException(uri.GetType().Name)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
BTCPayServer/UnresolvedUri.cs
Normal file
25
BTCPayServer/UnresolvedUri.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace BTCPayServer
|
||||||
|
{
|
||||||
|
public record UnresolvedUri
|
||||||
|
{
|
||||||
|
public static UnresolvedUri Create(string str)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(str);
|
||||||
|
if (str.StartsWith("fileid:", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return new FileIdUri(str.Substring("fileid:".Length));
|
||||||
|
}
|
||||||
|
return new Raw(str);
|
||||||
|
}
|
||||||
|
public record FileIdUri(string FileId) : UnresolvedUri
|
||||||
|
{
|
||||||
|
public override string ToString() => $"fileid:{FileId}";
|
||||||
|
}
|
||||||
|
public record Raw(string Uri) : UnresolvedUri
|
||||||
|
{
|
||||||
|
public override string ToString() => Uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -311,36 +311,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@* We are deprecating the custom CSS options in favor of the store branding approach.
|
|
||||||
Display this section only if these values are set. *@
|
|
||||||
@if (!string.IsNullOrWhiteSpace(Model.CustomCSSLink) || !string.IsNullOrWhiteSpace(Model.EmbeddedCSS))
|
|
||||||
{
|
|
||||||
<div class="accordion-item">
|
|
||||||
<h2 class="accordion-header" id="additional-custom-css-header">
|
|
||||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-custom-css" aria-expanded="false" aria-controls="additional-custom-css">
|
|
||||||
Custom CSS
|
|
||||||
<vc:icon symbol="caret-down" />
|
|
||||||
</button>
|
|
||||||
</h2>
|
|
||||||
<div id="additional-custom-css" class="accordion-collapse collapse" aria-labelledby="additional-custom-css-header">
|
|
||||||
<div class="accordion-body">
|
|
||||||
<div class="form-group">
|
|
||||||
<label asp-for="CustomCSSLink" class="form-label"></label>
|
|
||||||
<a href="https://docs.btcpayserver.org/Development/Theme/" target="_blank" rel="noreferrer noopener" title="More information...">
|
|
||||||
<vc:icon symbol="info" />
|
|
||||||
</a>
|
|
||||||
<input asp-for="CustomCSSLink" class="form-control" />
|
|
||||||
<span asp-validation-for="CustomCSSLink" class="text-danger"></span>
|
|
||||||
</div>
|
|
||||||
<div class="form-group mb-4">
|
|
||||||
<label asp-for="EmbeddedCSS" class="form-label"></label>
|
|
||||||
<textarea asp-for="EmbeddedCSS" rows="10" cols="40" class="form-control"></textarea>
|
|
||||||
<span asp-validation-for="EmbeddedCSS" class="text-danger"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,14 +1,4 @@
|
|||||||
@model StoreBrandingViewModel
|
@model StoreBrandingViewModel
|
||||||
@using BTCPayServer.Abstractions.Contracts
|
|
||||||
@inject IFileService FileService
|
|
||||||
@{
|
|
||||||
var logoUrl = !string.IsNullOrEmpty(Model.LogoFileId)
|
|
||||||
? await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.LogoFileId)
|
|
||||||
: null;
|
|
||||||
var cssUrl = !string.IsNullOrEmpty(Model.CssFileId)
|
|
||||||
? await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.CssFileId)
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
@if (!string.IsNullOrEmpty(Model.BrandColor))
|
@if (!string.IsNullOrEmpty(Model.BrandColor))
|
||||||
{
|
{
|
||||||
var brand = Model.BrandColor;
|
var brand = Model.BrandColor;
|
||||||
@@ -39,23 +29,12 @@
|
|||||||
</style>
|
</style>
|
||||||
<meta name="theme-color" content="@brand">
|
<meta name="theme-color" content="@brand">
|
||||||
}
|
}
|
||||||
@if (!string.IsNullOrEmpty(cssUrl))
|
@if (!string.IsNullOrEmpty(Model.CssUrl))
|
||||||
{
|
{
|
||||||
<link href="@cssUrl" asp-append-version="true" rel="stylesheet" />
|
<link href="@Model.CssUrl!" asp-append-version="true" rel="stylesheet" />
|
||||||
}
|
}
|
||||||
@* Deprecated, but added for backwards-compatibility *@
|
@if (!string.IsNullOrEmpty(Model.LogoUrl))
|
||||||
@if (!string.IsNullOrEmpty(Model.CustomCSSLink))
|
|
||||||
{
|
{
|
||||||
<link href="@Model.CustomCSSLink" asp-append-version="true" rel="stylesheet" />
|
<link rel="icon" href="@Model.LogoUrl">
|
||||||
}
|
<link rel="apple-touch-icon" href="@Model.LogoUrl">
|
||||||
@if (!string.IsNullOrEmpty(Model.EmbeddedCSS))
|
|
||||||
{
|
|
||||||
<style>
|
|
||||||
@Safe.Raw(Model.EmbeddedCSS)
|
|
||||||
</style>
|
|
||||||
}
|
|
||||||
@if (!string.IsNullOrEmpty(logoUrl))
|
|
||||||
{
|
|
||||||
<link rel="icon" href="@logoUrl">
|
|
||||||
<link rel="apple-touch-icon" href="@logoUrl">
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
@using BTCPayServer.Services
|
@using BTCPayServer.Services
|
||||||
@using BTCPayServer.Abstractions.Contracts
|
|
||||||
@inject ThemeSettings Theme
|
@inject ThemeSettings Theme
|
||||||
@inject IFileService FileService
|
@inject UriResolver UriResolver
|
||||||
|
|
||||||
<script>if (window.localStorage.getItem('btcpay-hide-sensitive-info') === 'true') { document.documentElement.setAttribute('data-hide-sensitive-info', 'true')}</script>
|
<script>if (window.localStorage.getItem('btcpay-hide-sensitive-info') === 'true') { document.documentElement.setAttribute('data-hide-sensitive-info', 'true')}</script>
|
||||||
@if (Theme.CustomTheme && !string.IsNullOrEmpty(Theme.CssUri))
|
@if (Theme.CustomTheme && Theme.CustomThemeCssUrl is not null)
|
||||||
{ // legacy customization with CSS URI - keep it for backwards-compatibility
|
|
||||||
<link href="@Context.Request.GetRelativePathOrAbsolute(Theme.CssUri)" rel="stylesheet" asp-append-version="true" />
|
|
||||||
}
|
|
||||||
else if (Theme.CustomTheme && !string.IsNullOrEmpty(Theme.CustomThemeFileId))
|
|
||||||
{ // new customization uses theme file id provided by upload
|
{ // new customization uses theme file id provided by upload
|
||||||
@if (Theme.CustomThemeExtension != ThemeExtension.Custom)
|
@if (Theme.CustomThemeExtension != ThemeExtension.Custom)
|
||||||
{ // needs to be added for light and dark, because dark extends light
|
{ // needs to be added for light and dark, because dark extends light
|
||||||
@@ -18,7 +13,8 @@ else if (Theme.CustomTheme && !string.IsNullOrEmpty(Theme.CustomThemeFileId))
|
|||||||
{
|
{
|
||||||
<link href="~/main/themes/default-dark.css" rel="stylesheet" asp-append-version="true" />
|
<link href="~/main/themes/default-dark.css" rel="stylesheet" asp-append-version="true" />
|
||||||
}
|
}
|
||||||
<link href="@(await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Theme.CustomThemeFileId))" rel="stylesheet" asp-append-version="true" />
|
var themeUrl = await UriResolver.Resolve(this.Context.Request.GetAbsoluteRootUri(), Theme.CustomThemeCssUrl);
|
||||||
|
<link href="@themeUrl" rel="stylesheet" asp-append-version="true" />
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -289,36 +289,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@* We are deprecating the custom CSS options in favor of the store branding approach.
|
|
||||||
Display this section only if these values are set. *@
|
|
||||||
@if (!string.IsNullOrWhiteSpace(Model.CustomCSSLink) || !string.IsNullOrWhiteSpace(Model.EmbeddedCSS))
|
|
||||||
{
|
|
||||||
<div class="accordion-item">
|
|
||||||
<h2 class="accordion-header" id="additional-custom-css-header">
|
|
||||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-custom-css" aria-expanded="false" aria-controls="additional-custom-css">
|
|
||||||
Custom CSS
|
|
||||||
<vc:icon symbol="caret-down" />
|
|
||||||
</button>
|
|
||||||
</h2>
|
|
||||||
<div id="additional-custom-css" class="accordion-collapse collapse" aria-labelledby="additional-custom-css-header">
|
|
||||||
<div class="accordion-body">
|
|
||||||
<div class="form-group">
|
|
||||||
<label asp-for="CustomCSSLink" class="form-label"></label>
|
|
||||||
<a href="https://docs.btcpayserver.org/Development/Theme/" target="_blank" rel="noreferrer noopener" title="More information...">
|
|
||||||
<vc:icon symbol="info" />
|
|
||||||
</a>
|
|
||||||
<input asp-for="CustomCSSLink" class="form-control" />
|
|
||||||
<span asp-validation-for="CustomCSSLink" class="text-danger"></span>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label asp-for="EmbeddedCSS" class="form-label"></label>
|
|
||||||
<textarea asp-for="EmbeddedCSS" rows="10" cols="40" class="form-control"></textarea>
|
|
||||||
<span asp-validation-for="EmbeddedCSS" class="text-danger"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,15 +1,9 @@
|
|||||||
@inject IFileService FileService
|
|
||||||
@using BTCPayServer.Abstractions.Contracts
|
|
||||||
@model (string Title, StoreBrandingViewModel StoreBranding)
|
@model (string Title, StoreBrandingViewModel StoreBranding)
|
||||||
@{
|
|
||||||
var logoUrl = !string.IsNullOrEmpty(Model.StoreBranding.LogoFileId)
|
|
||||||
? await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.StoreBranding.LogoFileId)
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
<header class="store-header" v-pre>
|
<header class="store-header" v-pre>
|
||||||
@if (!string.IsNullOrEmpty(logoUrl))
|
@if (!string.IsNullOrEmpty(Model.StoreBranding.LogoUrl))
|
||||||
{
|
{
|
||||||
<img src="@logoUrl" alt="@Model.Title" class="store-logo"/>
|
<img src="@Model.StoreBranding.LogoUrl" alt="@Model.Title" class="store-logo"/>
|
||||||
}
|
}
|
||||||
<h1 class="store-name">@Model.Title</h1>
|
<h1 class="store-name">@Model.Title</h1>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<body class="min-vh-100">
|
<body class="min-vh-100">
|
||||||
<div id="FormView" class="public-page-wrap">
|
<div id="FormView" class="public-page-wrap">
|
||||||
<partial name="_StatusMessage" model="@(new ViewDataDictionary(ViewData) { { "Margin", "mb-4" } })" />
|
<partial name="_StatusMessage" model="@(new ViewDataDictionary(ViewData) { { "Margin", "mb-4" } })" />
|
||||||
@if (!string.IsNullOrEmpty(Model.StoreName) || !string.IsNullOrEmpty(Model.StoreBranding.LogoFileId))
|
@if (!string.IsNullOrEmpty(Model.StoreName) || !string.IsNullOrEmpty(Model.StoreBranding.LogoUrl))
|
||||||
{
|
{
|
||||||
<partial name="_StoreHeader" model="(Model.StoreName, Model.StoreBranding)" />
|
<partial name="_StoreHeader" model="(Model.StoreName, Model.StoreBranding)" />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,46 +123,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@* We are deprecating the custom CSS options in favor of the store branding approach.
|
|
||||||
Display this section only if these values are set. *@
|
|
||||||
@if (!string.IsNullOrWhiteSpace(Model.CustomCSSLink) || !string.IsNullOrWhiteSpace(Model.EmbeddedCSS))
|
|
||||||
{
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xl-8 col-xxl-constrain">
|
|
||||||
<h4 class="mt-5 mb-2">Additional Options</h4>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="accordion" id="additional">
|
|
||||||
<div class="accordion-item">
|
|
||||||
<h2 class="accordion-header" id="additional-custom-css-header">
|
|
||||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-custom-css" aria-expanded="false" aria-controls="additional-custom-css">
|
|
||||||
Custom CSS
|
|
||||||
<vc:icon symbol="caret-down" />
|
|
||||||
</button>
|
|
||||||
</h2>
|
|
||||||
<div id="additional-custom-css" class="accordion-collapse collapse" aria-labelledby="additional-custom-css-header">
|
|
||||||
<div class="accordion-body">
|
|
||||||
<div class="form-group">
|
|
||||||
<label asp-for="CustomCSSLink" class="form-label"></label>
|
|
||||||
<a href="https://docs.btcpayserver.org/Development/Theme/" target="_blank" rel="noreferrer noopener" title="More information...">
|
|
||||||
<vc:icon symbol="info" />
|
|
||||||
</a>
|
|
||||||
<input asp-for="CustomCSSLink" class="form-control" />
|
|
||||||
<span asp-validation-for="CustomCSSLink" class="text-danger"></span>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label asp-for="EmbeddedCSS" class="form-label"></label>
|
|
||||||
<textarea asp-for="EmbeddedCSS" rows="10" cols="40" class="form-control"></textarea>
|
|
||||||
<span asp-validation-for="EmbeddedCSS" class="text-danger"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
@if (!string.IsNullOrEmpty(Model.Id))
|
@if (!string.IsNullOrEmpty(Model.Id))
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
@using BTCPayServer.Views.Stores
|
@using BTCPayServer.Views.Stores
|
||||||
@using BTCPayServer.Abstractions.Extensions
|
|
||||||
@model BTCPayServer.Models.WalletViewModels.UpdatePullPaymentModel
|
@model BTCPayServer.Models.WalletViewModels.UpdatePullPaymentModel
|
||||||
|
|
||||||
@{
|
@{
|
||||||
@@ -53,44 +52,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@* We are deprecating the custom CSS options in favor of the store branding approach.
|
|
||||||
Display this section only if these values are set. *@
|
|
||||||
@if (!string.IsNullOrWhiteSpace(Model.CustomCSSLink) || !string.IsNullOrWhiteSpace(Model.EmbeddedCSS))
|
|
||||||
{
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xl-8 col-xxl-constrain">
|
|
||||||
<h4 class="mt-5 mb-2">Additional Options</h4>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="accordion" id="additional">
|
|
||||||
<div class="accordion-item">
|
|
||||||
<h2 class="accordion-header" id="additional-custom-css-header">
|
|
||||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-custom-css" aria-expanded="false" aria-controls="additional-custom-css">
|
|
||||||
Custom CSS
|
|
||||||
<vc:icon symbol="caret-down" />
|
|
||||||
</button>
|
|
||||||
</h2>
|
|
||||||
<div id="additional-custom-css" class="accordion-collapse collapse" aria-labelledby="additional-custom-css-header">
|
|
||||||
<div class="accordion-body">
|
|
||||||
<div class="form-group">
|
|
||||||
<label asp-for="CustomCSSLink" class="form-label"></label>
|
|
||||||
<a href="https://docs.btcpayserver.org/Development/Theme/" target="_blank" rel="noreferrer noopener">
|
|
||||||
<vc:icon symbol="info" />
|
|
||||||
</a>
|
|
||||||
<input asp-for="CustomCSSLink" class="form-control" />
|
|
||||||
<span asp-validation-for="CustomCSSLink" class="text-danger"></span>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label asp-for="EmbeddedCSS" class="form-label"></label>
|
|
||||||
<textarea asp-for="EmbeddedCSS" rows="10" cols="40" class="form-control"></textarea>
|
|
||||||
<span asp-validation-for="EmbeddedCSS" class="text-danger"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="d-flex align-items-center justify-content-between gap-2">
|
<div class="d-flex align-items-center justify-content-between gap-2">
|
||||||
<label asp-for="LogoFile" class="form-label"></label>
|
<label asp-for="LogoFile" class="form-label"></label>
|
||||||
@if (!string.IsNullOrEmpty(Model.LogoFileId))
|
@if (!string.IsNullOrEmpty(Model.LogoUrl))
|
||||||
{
|
{
|
||||||
<button type="submit" class="btn btn-link p-0 text-danger" name="RemoveLogoFile" value="true">
|
<button type="submit" class="btn btn-link p-0 text-danger" name="RemoveLogoFile" value="true">
|
||||||
<span class="fa fa-times"></span> Remove
|
<span class="fa fa-times"></span> Remove
|
||||||
@@ -47,9 +47,9 @@
|
|||||||
{
|
{
|
||||||
<div class="d-flex align-items-center gap-3">
|
<div class="d-flex align-items-center gap-3">
|
||||||
<input asp-for="LogoFile" type="file" class="form-control flex-grow">
|
<input asp-for="LogoFile" type="file" class="form-control flex-grow">
|
||||||
@if (!string.IsNullOrEmpty(Model.LogoFileId))
|
@if (!string.IsNullOrEmpty(Model.LogoUrl))
|
||||||
{
|
{
|
||||||
<img src="@(await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.LogoFileId))" alt="Logo" style="height:2.1rem;max-width:10.5rem;"/>
|
<img src="@Model.LogoUrl" alt="Logo" style="height:2.1rem;max-width:10.5rem;"/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<span asp-validation-for="LogoFile" class="text-danger"></span>
|
<span asp-validation-for="LogoFile" class="text-danger"></span>
|
||||||
@@ -75,16 +75,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="collapse @(Model.CustomTheme ? "show" : "")" id="CustomThemeSettings">
|
<div class="collapse @(Model.CustomTheme ? "show" : "")" id="CustomThemeSettings">
|
||||||
@if (!string.IsNullOrEmpty(Model.CustomThemeCssUri))
|
|
||||||
{
|
|
||||||
<div class="form-group mb-0 pt-2">
|
|
||||||
<label asp-for="CustomThemeCssUri" class="form-label" data-required></label>
|
|
||||||
<input asp-for="CustomThemeCssUri" class="form-control"/>
|
|
||||||
<span asp-validation-for="CustomThemeCssUri" class="text-danger"></span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<div class="form-group pt-2">
|
<div class="form-group pt-2">
|
||||||
<label asp-for="CustomThemeExtension" class="form-label" data-required></label>
|
<label asp-for="CustomThemeExtension" class="form-label" data-required></label>
|
||||||
<select asp-for="CustomThemeExtension" asp-items="@themeExtension" class="form-select w-auto"></select>
|
<select asp-for="CustomThemeExtension" asp-items="@themeExtension" class="form-select w-auto"></select>
|
||||||
@@ -92,7 +82,7 @@
|
|||||||
<div class="form-group mb-0">
|
<div class="form-group mb-0">
|
||||||
<div class="d-flex align-items-center justify-content-between gap-2">
|
<div class="d-flex align-items-center justify-content-between gap-2">
|
||||||
<label asp-for="CustomThemeFile" class="form-label" data-required></label>
|
<label asp-for="CustomThemeFile" class="form-label" data-required></label>
|
||||||
@if (!string.IsNullOrEmpty(Model.CustomThemeFileId))
|
@if (!string.IsNullOrEmpty(Model.CustomThemeCssUrl))
|
||||||
{
|
{
|
||||||
<button type="submit" class="btn btn-link p-0 text-danger" name="RemoveCustomThemeFile" value="true">
|
<button type="submit" class="btn btn-link p-0 text-danger" name="RemoveCustomThemeFile" value="true">
|
||||||
<span class="fa fa-times"></span> Remove
|
<span class="fa fa-times"></span> Remove
|
||||||
@@ -103,9 +93,9 @@
|
|||||||
{
|
{
|
||||||
<div class="d-flex align-items-center gap-3">
|
<div class="d-flex align-items-center gap-3">
|
||||||
<input asp-for="CustomThemeFile" type="file" class="form-control flex-grow">
|
<input asp-for="CustomThemeFile" type="file" class="form-control flex-grow">
|
||||||
@if (!string.IsNullOrEmpty(Model.CustomThemeFileId))
|
@if (!string.IsNullOrEmpty(Model.CustomThemeCssUrl))
|
||||||
{
|
{
|
||||||
<a href="@(await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.CustomThemeFileId))" target="_blank" rel="noreferrer noopener" class="text-nowrap">Custom CSS</a>
|
<a href="@Model.CustomThemeCssUrl" target="_blank" rel="noreferrer noopener" class="text-nowrap">Custom CSS</a>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<span asp-validation-for="CustomThemeFile" class="text-danger"></span>
|
<span asp-validation-for="CustomThemeFile" class="text-danger"></span>
|
||||||
@@ -116,7 +106,6 @@
|
|||||||
<p class="form-text text-muted">In order to upload a theme file, a <a asp-controller="UIServer" asp-action="Files">file storage</a> must be configured.</p>
|
<p class="form-text text-muted">In order to upload a theme file, a <a asp-controller="UIServer" asp-action="Files">file storage</a> must be configured.</p>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary mt-2" name="command" value="Save">Save</button>
|
<button type="submit" class="btn btn-primary mt-2" name="command" value="Save">Save</button>
|
||||||
|
|||||||
@@ -95,36 +95,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@* We are deprecating the custom CSS options in favor of the store branding approach.
|
|
||||||
Display this section only if these values are set. *@
|
|
||||||
@if (!string.IsNullOrWhiteSpace(Model.CustomCSSLink) || !string.IsNullOrWhiteSpace(Model.EmbeddedCSS))
|
|
||||||
{
|
|
||||||
<div class="accordion-item">
|
|
||||||
<h2 class="accordion-header" id="additional-custom-css-header">
|
|
||||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-custom-css" aria-expanded="false" aria-controls="additional-custom-css">
|
|
||||||
Custom CSS
|
|
||||||
<vc:icon symbol="caret-down" />
|
|
||||||
</button>
|
|
||||||
</h2>
|
|
||||||
<div id="additional-custom-css" class="accordion-collapse collapse" aria-labelledby="additional-custom-css-header">
|
|
||||||
<div class="accordion-body">
|
|
||||||
<div class="form-group">
|
|
||||||
<label asp-for="CustomCSSLink" class="form-label"></label>
|
|
||||||
<a href="https://docs.btcpayserver.org/Development/Theme/" target="_blank" rel="noreferrer noopener" title="More information...">
|
|
||||||
<vc:icon symbol="info" />
|
|
||||||
</a>
|
|
||||||
<input asp-for="CustomCSSLink" class="form-control" />
|
|
||||||
<span asp-validation-for="CustomCSSLink" class="text-danger"></span>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label asp-for="EmbeddedCSS" class="form-label"></label>
|
|
||||||
<textarea asp-for="EmbeddedCSS" rows="10" cols="40" class="form-control"></textarea>
|
|
||||||
<span asp-validation-for="EmbeddedCSS" class="text-danger"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -143,7 +143,7 @@
|
|||||||
<div class="form-group mb-0 py-3">
|
<div class="form-group mb-0 py-3">
|
||||||
<div class="d-flex align-items-center justify-content-between gap-2">
|
<div class="d-flex align-items-center justify-content-between gap-2">
|
||||||
<label asp-for="SoundFile" class="form-label"></label>
|
<label asp-for="SoundFile" class="form-label"></label>
|
||||||
@if (!string.IsNullOrEmpty(Model.SoundFileId))
|
@if (!string.IsNullOrEmpty(Model.PaymentSoundUrl))
|
||||||
{
|
{
|
||||||
<button type="submit" class="btn btn-link p-0 text-danger" name="RemoveSoundFile" value="true" permission="@Policies.CanModifyStoreSettings">
|
<button type="submit" class="btn btn-link p-0 text-danger" name="RemoveSoundFile" value="true" permission="@Policies.CanModifyStoreSettings">
|
||||||
<span class="fa fa-times"></span> Remove
|
<span class="fa fa-times"></span> Remove
|
||||||
@@ -154,12 +154,7 @@
|
|||||||
{
|
{
|
||||||
<div class="d-flex align-items-center gap-3">
|
<div class="d-flex align-items-center gap-3">
|
||||||
<input asp-for="SoundFile" type="file" class="form-control flex-grow">
|
<input asp-for="SoundFile" type="file" class="form-control flex-grow">
|
||||||
@{
|
<audio controls src="@Model.PaymentSoundUrl" style="height:2.1rem;max-width:10.5rem;"></audio>
|
||||||
var soundUrl = string.IsNullOrEmpty(Model.SoundFileId)
|
|
||||||
? string.Concat(Context.Request.GetAbsoluteRootUri().ToString(), "checkout/payment.mp3")
|
|
||||||
: await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.SoundFileId);
|
|
||||||
}
|
|
||||||
<audio controls src="@soundUrl" style="height:2.1rem;max-width:10.5rem;"></audio>
|
|
||||||
</div>
|
</div>
|
||||||
<span asp-validation-for="SoundFile" class="text-danger"></span>
|
<span asp-validation-for="SoundFile" class="text-danger"></span>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="d-flex align-items-center justify-content-between gap-2">
|
<div class="d-flex align-items-center justify-content-between gap-2">
|
||||||
<label asp-for="LogoFile" class="form-label"></label>
|
<label asp-for="LogoFile" class="form-label"></label>
|
||||||
@if (!string.IsNullOrEmpty(Model.LogoFileId))
|
@if (!string.IsNullOrEmpty(Model.LogoUrl))
|
||||||
{
|
{
|
||||||
<button type="submit" class="btn btn-link p-0 text-danger" name="RemoveLogoFile" value="true">
|
<button type="submit" class="btn btn-link p-0 text-danger" name="RemoveLogoFile" value="true">
|
||||||
<span class="fa fa-times"></span> Remove
|
<span class="fa fa-times"></span> Remove
|
||||||
@@ -57,9 +57,9 @@
|
|||||||
{
|
{
|
||||||
<div class="d-flex align-items-center gap-3">
|
<div class="d-flex align-items-center gap-3">
|
||||||
<input asp-for="LogoFile" type="file" class="form-control flex-grow">
|
<input asp-for="LogoFile" type="file" class="form-control flex-grow">
|
||||||
@if (!string.IsNullOrEmpty(Model.LogoFileId))
|
@if (!string.IsNullOrEmpty(Model.LogoUrl))
|
||||||
{
|
{
|
||||||
<img src="@(await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.LogoFileId))" alt="@Model.StoreName Logo" style="height:2.1rem;max-width:10.5rem;"/>
|
<img src="@Model.LogoUrl" alt="@Model.StoreName Logo" style="height:2.1rem;max-width:10.5rem;"/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<span asp-validation-for="LogoFile" class="text-danger"></span>
|
<span asp-validation-for="LogoFile" class="text-danger"></span>
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="d-flex align-items-center justify-content-between gap-2">
|
<div class="d-flex align-items-center justify-content-between gap-2">
|
||||||
<label asp-for="CssFile" class="form-label"></label>
|
<label asp-for="CssFile" class="form-label"></label>
|
||||||
@if (!string.IsNullOrEmpty(Model.CssFileId))
|
@if (!string.IsNullOrEmpty(Model.CssUrl))
|
||||||
{
|
{
|
||||||
<button type="submit" class="btn btn-link p-0 text-danger" name="RemoveCssFile" value="true">
|
<button type="submit" class="btn btn-link p-0 text-danger" name="RemoveCssFile" value="true">
|
||||||
<span class="fa fa-times"></span> Remove
|
<span class="fa fa-times"></span> Remove
|
||||||
@@ -84,9 +84,9 @@
|
|||||||
{
|
{
|
||||||
<div class="d-flex align-items-center gap-3">
|
<div class="d-flex align-items-center gap-3">
|
||||||
<input asp-for="CssFile" type="file" class="form-control flex-grow">
|
<input asp-for="CssFile" type="file" class="form-control flex-grow">
|
||||||
@if (!string.IsNullOrEmpty(Model.CssFileId))
|
@if (!string.IsNullOrEmpty(Model.CssUrl))
|
||||||
{
|
{
|
||||||
<a href="@(await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.CssFileId))" target="_blank" rel="noreferrer noopener" class="text-nowrap">Custom CSS</a>
|
<a href="@Model.CssUrl" target="_blank" rel="noreferrer noopener" class="text-nowrap">Custom CSS</a>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<span asp-validation-for="LogoFile" class="text-danger"></span>
|
<span asp-validation-for="LogoFile" class="text-danger"></span>
|
||||||
|
|||||||
@@ -551,11 +551,6 @@
|
|||||||
"description": "Prompt which appears next to the tip amount field if tipping is enabled",
|
"description": "Prompt which appears next to the tip amount field if tipping is enabled",
|
||||||
"example": "Do you want to leave a tip?"
|
"example": "Do you want to leave a tip?"
|
||||||
},
|
},
|
||||||
"customCSSLink": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Link to a custom CSS stylesheet to be used in the app",
|
|
||||||
"example": "https://bootswatch.com/4/slate/bootstrap.min.css"
|
|
||||||
},
|
|
||||||
"notificationUrl": {
|
"notificationUrl": {
|
||||||
"type": "string",
|
"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"
|
||||||
@@ -564,10 +559,6 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "URL user is redirected to once invoice is paid"
|
"description": "URL user is redirected to once invoice is paid"
|
||||||
},
|
},
|
||||||
"embeddedCSS": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Custom CSS embedded into the app"
|
|
||||||
},
|
|
||||||
"redirectAutomatically": {
|
"redirectAutomatically": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Whether user is redirected to specified redirect URL automatically after the invoice is paid",
|
"description": "Whether user is redirected to specified redirect URL automatically after the invoice is paid",
|
||||||
@@ -627,18 +618,10 @@
|
|||||||
"description": "Target amount for the crowdfund",
|
"description": "Target amount for the crowdfund",
|
||||||
"example": 420.69
|
"example": 420.69
|
||||||
},
|
},
|
||||||
"customCSSLink": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Link to a custom CSS stylesheet to be used in the app"
|
|
||||||
},
|
|
||||||
"mainImageUrl": {
|
"mainImageUrl": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "URL for image used as a cover image for the app"
|
"description": "URL for image used as a cover image for the app"
|
||||||
},
|
},
|
||||||
"embeddedCSS": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Custom CSS embedded into the app"
|
|
||||||
},
|
|
||||||
"perks": {
|
"perks": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "JSON of perks available in the app",
|
"description": "JSON of perks available in the app",
|
||||||
@@ -875,16 +858,6 @@
|
|||||||
"default": "Do you want to leave a tip?",
|
"default": "Do you want to leave a tip?",
|
||||||
"nullable": true
|
"nullable": true
|
||||||
},
|
},
|
||||||
"customCSSLink": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Link to a custom CSS stylesheet to be used in the app",
|
|
||||||
"nullable": true
|
|
||||||
},
|
|
||||||
"embeddedCSS": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Custom CSS to embed into the app",
|
|
||||||
"nullable": true
|
|
||||||
},
|
|
||||||
"notificationUrl": {
|
"notificationUrl": {
|
||||||
"type": "string",
|
"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",
|
||||||
@@ -966,21 +939,11 @@
|
|||||||
"example": 420,
|
"example": 420,
|
||||||
"nullable": true
|
"nullable": true
|
||||||
},
|
},
|
||||||
"customCSSLink": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Link to a custom CSS stylesheet to be used in the app",
|
|
||||||
"nullable": true
|
|
||||||
},
|
|
||||||
"mainImageUrl": {
|
"mainImageUrl": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "URL for image to be used as a cover image for the app",
|
"description": "URL for image to be used as a cover image for the app",
|
||||||
"nullable": true
|
"nullable": true
|
||||||
},
|
},
|
||||||
"embeddedCSS": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Custom CSS to embed into the app",
|
|
||||||
"nullable": true
|
|
||||||
},
|
|
||||||
"perksTemplate": {
|
"perksTemplate": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "YAML template of perks available in the app",
|
"description": "YAML template of perks available in the app",
|
||||||
|
|||||||
@@ -454,19 +454,6 @@
|
|||||||
"nullable": true,
|
"nullable": true,
|
||||||
"allOf": [ { "$ref": "#/components/schemas/UnixTimestamp" } ]
|
"allOf": [ { "$ref": "#/components/schemas/UnixTimestamp" } ]
|
||||||
},
|
},
|
||||||
"embeddedCSS": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Custom CSS styling for the payment request",
|
|
||||||
"nullable": true,
|
|
||||||
"format": "css",
|
|
||||||
"maximum": 500
|
|
||||||
},
|
|
||||||
"customCSSLink": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Custom CSS link for styling the payment request",
|
|
||||||
"nullable": true,
|
|
||||||
"format": "uri"
|
|
||||||
},
|
|
||||||
"allowCustomPaymentAmounts": {
|
"allowCustomPaymentAmounts": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Whether to allow users to create invoices that partially pay the payment request ",
|
"description": "Whether to allow users to create invoices that partially pay the payment request ",
|
||||||
|
|||||||
@@ -411,6 +411,30 @@
|
|||||||
"description": "The support URI of the store, can contain the placeholders `{OrderId}` and `{InvoiceId}`. Can be any valid URI, such as a website, email, and nostr.",
|
"description": "The support URI of the store, can contain the placeholders `{OrderId}` and `{InvoiceId}`. Can be any valid URI, such as a website, email, and nostr.",
|
||||||
"format": "uri"
|
"format": "uri"
|
||||||
},
|
},
|
||||||
|
"logoUrl": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true,
|
||||||
|
"description": "Absolute URL to a logo file or a reference to an uploaded file id with `fileid:ID`",
|
||||||
|
"format": "uri"
|
||||||
|
},
|
||||||
|
"cssUrl": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true,
|
||||||
|
"description": "Absolute URL to CSS file to customize the public/customer-facing pages of the store. (Invoice, Payment Request, Pull Payment, etc.) or a reference to an uploaded file id with `fileid:ID`",
|
||||||
|
"format": "uri"
|
||||||
|
},
|
||||||
|
"paymentSoundUrl": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true,
|
||||||
|
"description": "Absolute URL to a sound file or a reference to an uploaded file id with `fileid:ID`",
|
||||||
|
"format": "uri"
|
||||||
|
},
|
||||||
|
"brandColor": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The brand color of the store in HEX format",
|
||||||
|
"nullable": true,
|
||||||
|
"example": "#F7931A"
|
||||||
|
},
|
||||||
"defaultCurrency": {
|
"defaultCurrency": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The default currency of the store",
|
"description": "The default currency of the store",
|
||||||
|
|||||||
Reference in New Issue
Block a user