diff --git a/BTCPayServer.Client/JsonConverters/TimeSpanJsonConverter.cs b/BTCPayServer.Client/JsonConverters/TimeSpanJsonConverter.cs index 621fb783a..b392655d4 100644 --- a/BTCPayServer.Client/JsonConverters/TimeSpanJsonConverter.cs +++ b/BTCPayServer.Client/JsonConverters/TimeSpanJsonConverter.cs @@ -4,13 +4,38 @@ using Newtonsoft.Json; namespace BTCPayServer.Client.JsonConverters { - public class TimeSpanJsonConverter : JsonConverter + public abstract class TimeSpanJsonConverter : JsonConverter { + public class Seconds : TimeSpanJsonConverter + { + protected override long ToLong(TimeSpan value) + { + return (long)value.TotalSeconds; + } + + protected override TimeSpan ToTimespan(long value) + { + return TimeSpan.FromSeconds(value); + } + } + public class Minutes : TimeSpanJsonConverter + { + protected override long ToLong(TimeSpan value) + { + return (long)value.TotalMinutes; + } + protected override TimeSpan ToTimespan(long value) + { + return TimeSpan.FromMinutes(value); + } + } public override bool CanConvert(Type objectType) { return objectType == typeof(TimeSpan) || objectType == typeof(TimeSpan?); } + protected abstract TimeSpan ToTimespan(long value); + protected abstract long ToLong(TimeSpan value); public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { try @@ -24,11 +49,11 @@ namespace BTCPayServer.Client.JsonConverters } if (reader.TokenType != JsonToken.Integer) throw new JsonObjectException("Invalid timespan, expected integer", reader); - return TimeSpan.FromSeconds((long)reader.Value); + return ToTimespan((long)reader.Value); } catch { - throw new JsonObjectException("Invalid locktime", reader); + throw new JsonObjectException("Invalid timespan", reader); } } @@ -36,7 +61,7 @@ namespace BTCPayServer.Client.JsonConverters { if (value is TimeSpan s) { - writer.WriteValue((long)s.TotalSeconds); + writer.WriteValue(ToLong(s)); } } } diff --git a/BTCPayServer.Client/Models/CreateInvoiceRequest.cs b/BTCPayServer.Client/Models/CreateInvoiceRequest.cs index 621993687..fddf81bda 100644 --- a/BTCPayServer.Client/Models/CreateInvoiceRequest.cs +++ b/BTCPayServer.Client/Models/CreateInvoiceRequest.cs @@ -1,4 +1,5 @@ using System; +using BTCPayServer.Client.JsonConverters; using BTCPayServer.JsonConverters; using Newtonsoft.Json; using Newtonsoft.Json.Converters; @@ -20,12 +21,13 @@ namespace BTCPayServer.Client.Models public SpeedPolicy? SpeedPolicy { get; set; } public string[] PaymentMethods { get; set; } - public bool? RedirectAutomatically { get; set; } - public string RedirectUri { get; set; } - public Uri WebHook { get; set; } - [JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))] - public DateTimeOffset? ExpirationTime { get; set; } + [JsonConverter(typeof(TimeSpanJsonConverter.Minutes))] + [JsonProperty("expirationMinutes")] + public TimeSpan? Expiration { get; set; } + [JsonConverter(typeof(TimeSpanJsonConverter.Minutes))] + [JsonProperty("monitoringMinutes")] + public TimeSpan? Monitoring { get; set; } [JsonProperty(ItemConverterType = typeof(NumericStringJsonConverter))] public double? PaymentTolerance { get; set; } diff --git a/BTCPayServer.Client/Models/CreateLightningInvoiceRequest.cs b/BTCPayServer.Client/Models/CreateLightningInvoiceRequest.cs index 4282b422c..ba38843fa 100644 --- a/BTCPayServer.Client/Models/CreateLightningInvoiceRequest.cs +++ b/BTCPayServer.Client/Models/CreateLightningInvoiceRequest.cs @@ -20,7 +20,7 @@ namespace BTCPayServer.Client.Models [JsonConverter(typeof(LightMoneyJsonConverter))] public LightMoney Amount { get; set; } public string Description { get; set; } - [JsonConverter(typeof(JsonConverters.TimeSpanJsonConverter))] + [JsonConverter(typeof(JsonConverters.TimeSpanJsonConverter.Seconds))] public TimeSpan Expiry { get; set; } public bool PrivateRouteHints { get; set; } diff --git a/BTCPayServer.Client/Models/CreatePullPaymentRequest.cs b/BTCPayServer.Client/Models/CreatePullPaymentRequest.cs index a314573ad..6cce582ca 100644 --- a/BTCPayServer.Client/Models/CreatePullPaymentRequest.cs +++ b/BTCPayServer.Client/Models/CreatePullPaymentRequest.cs @@ -11,7 +11,7 @@ namespace BTCPayServer.Client.Models [JsonProperty(ItemConverterType = typeof(NumericStringJsonConverter))] public decimal Amount { get; set; } public string Currency { get; set; } - [JsonConverter(typeof(TimeSpanJsonConverter))] + [JsonConverter(typeof(TimeSpanJsonConverter.Seconds))] public TimeSpan? Period { get; set; } [JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))] public DateTimeOffset? ExpiresAt { get; set; } diff --git a/BTCPayServer.Client/Models/PullPaymentBaseData.cs b/BTCPayServer.Client/Models/PullPaymentBaseData.cs index d95b60e06..371bdee9d 100644 --- a/BTCPayServer.Client/Models/PullPaymentBaseData.cs +++ b/BTCPayServer.Client/Models/PullPaymentBaseData.cs @@ -16,7 +16,7 @@ namespace BTCPayServer.Client.Models public string Currency { get; set; } [JsonConverter(typeof(NumericStringJsonConverter))] public decimal Amount { get; set; } - [JsonConverter(typeof(TimeSpanJsonConverter))] + [JsonConverter(typeof(TimeSpanJsonConverter.Seconds))] public TimeSpan? Period { get; set; } public bool Archived { get; set; } public string ViewLink { get; set; } diff --git a/BTCPayServer.Client/Models/StoreBaseData.cs b/BTCPayServer.Client/Models/StoreBaseData.cs index 33cb6c00f..61e8ed747 100644 --- a/BTCPayServer.Client/Models/StoreBaseData.cs +++ b/BTCPayServer.Client/Models/StoreBaseData.cs @@ -16,11 +16,11 @@ namespace BTCPayServer.Client.Models public string Website { get; set; } - [JsonConverter(typeof(TimeSpanJsonConverter))] + [JsonConverter(typeof(TimeSpanJsonConverter.Seconds))] [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public TimeSpan InvoiceExpiration { get; set; } = TimeSpan.FromMinutes(15); - [JsonConverter(typeof(TimeSpanJsonConverter))] + [JsonConverter(typeof(TimeSpanJsonConverter.Seconds))] [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public TimeSpan MonitoringExpiration { get; set; } = TimeSpan.FromMinutes(60); diff --git a/BTCPayServer/Controllers/GreenField/InvoiceController.cs b/BTCPayServer/Controllers/GreenField/InvoiceController.cs index 05aebc392..9fb31470b 100644 --- a/BTCPayServer/Controllers/GreenField/InvoiceController.cs +++ b/BTCPayServer/Controllers/GreenField/InvoiceController.cs @@ -125,10 +125,10 @@ namespace BTCPayServer.Controllers.GreenField } } - if (request.Checkout.ExpirationTime != null && request.Checkout.ExpirationTime < DateTime.Now) + if (request.Checkout.Expiration != null && request.Checkout.Expiration < TimeSpan.FromSeconds(30.0)) { - request.AddModelError(invoiceRequest => invoiceRequest.Checkout.ExpirationTime, - "Expiration time must be in the future", this); + request.AddModelError(invoiceRequest => invoiceRequest.Checkout.Expiration, + "Expiration time must be at least 30 seconds", this); } if (request.Checkout.PaymentTolerance != null && @@ -143,7 +143,7 @@ namespace BTCPayServer.Controllers.GreenField try { - var invoice = await _invoiceController.CreateInvoiceCoreRaw(FromModel(request), store, + var invoice = await _invoiceController.CreateInvoiceCoreRaw(request, store, Request.GetAbsoluteUri("")); return Ok(ToModel(invoice)); } @@ -263,14 +263,12 @@ namespace BTCPayServer.Controllers.GreenField Metadata = entity.Metadata.ToJObject(), Checkout = new CreateInvoiceRequest.CheckoutOptions() { - ExpirationTime = entity.ExpirationTime, + Expiration = entity.ExpirationTime - entity.InvoiceTime, + Monitoring = entity.MonitoringExpiration - entity.ExpirationTime, PaymentTolerance = entity.PaymentTolerance, PaymentMethods = entity.GetPaymentMethods().Select(method => method.GetId().ToString()).ToArray(), - RedirectAutomatically = entity.RedirectAutomatically, - RedirectUri = entity.RedirectURL?.ToString(), - SpeedPolicy = entity.SpeedPolicy, - WebHook = entity.NotificationURL + SpeedPolicy = entity.SpeedPolicy }, PaymentMethodData = entity.GetPaymentMethods().ToDictionary(method => method.GetId().ToString(), method => @@ -317,47 +315,5 @@ namespace BTCPayServer.Controllers.GreenField }) }; } - - private Models.BitpayCreateInvoiceRequest FromModel(CreateInvoiceRequest entity) - { - InvoiceMetadata invoiceMetadata = null; - if (entity.Metadata != null) - { - invoiceMetadata = entity.Metadata.ToObject(); - } - return new Models.BitpayCreateInvoiceRequest() - { - Buyer = invoiceMetadata == null ? null : new Buyer() - { - Address1 = invoiceMetadata.BuyerAddress1, - Address2 = invoiceMetadata.BuyerAddress2, - City = invoiceMetadata.BuyerCity, - country = invoiceMetadata.BuyerCountry, - email = invoiceMetadata.BuyerEmail, - Name = invoiceMetadata.BuyerName, - phone = invoiceMetadata.BuyerPhone, - State = invoiceMetadata.BuyerState, - zip = invoiceMetadata.BuyerZip, - }, - Currency = entity.Currency, - Price = entity.Amount, - Refundable = true, - ExtendedNotifications = true, - FullNotifications = true, - RedirectURL = entity.Checkout.RedirectUri, - RedirectAutomatically = entity.Checkout.RedirectAutomatically, - ExpirationTime = entity.Checkout.ExpirationTime, - TransactionSpeed = entity.Checkout.SpeedPolicy?.ToString(), - PaymentCurrencies = entity.Checkout.PaymentMethods, - NotificationURL = entity.Checkout.RedirectUri, - PosData = invoiceMetadata?.PosData, - Physical = invoiceMetadata?.Physical ?? false, - ItemCode = invoiceMetadata?.ItemCode, - ItemDesc = invoiceMetadata?.ItemDesc, - TaxIncluded = invoiceMetadata?.TaxIncluded, - OrderId = invoiceMetadata?.OrderId, - Metadata = entity.Metadata - }; - } } } diff --git a/BTCPayServer/Controllers/GreenField/StoresController.cs b/BTCPayServer/Controllers/GreenField/StoresController.cs index 8ae2a6199..25b59fd82 100644 --- a/BTCPayServer/Controllers/GreenField/StoresController.cs +++ b/BTCPayServer/Controllers/GreenField/StoresController.cs @@ -124,8 +124,8 @@ namespace BTCPayServer.Controllers.GreenField ShowRecommendedFee = storeBlob.ShowRecommendedFee, RecommendedFeeBlockTarget = storeBlob.RecommendedFeeBlockTarget, DefaultLang = storeBlob.DefaultLang, - MonitoringExpiration = TimeSpan.FromMinutes(storeBlob.MonitoringExpiration), - InvoiceExpiration = TimeSpan.FromMinutes(storeBlob.InvoiceExpiration), + MonitoringExpiration = storeBlob.MonitoringExpiration, + InvoiceExpiration = storeBlob.InvoiceExpiration, LightningAmountInSatoshi = storeBlob.LightningAmountInSatoshi, CustomLogo = storeBlob.CustomLogo, CustomCSS = storeBlob.CustomCSS, @@ -160,8 +160,8 @@ namespace BTCPayServer.Controllers.GreenField blob.ShowRecommendedFee = restModel.ShowRecommendedFee; blob.RecommendedFeeBlockTarget = restModel.RecommendedFeeBlockTarget; blob.DefaultLang = restModel.DefaultLang; - blob.MonitoringExpiration = (int)restModel.MonitoringExpiration.TotalMinutes; - blob.InvoiceExpiration = (int)restModel.InvoiceExpiration.TotalMinutes; + blob.MonitoringExpiration = restModel.MonitoringExpiration; + blob.InvoiceExpiration = restModel.InvoiceExpiration; blob.LightningAmountInSatoshi = restModel.LightningAmountInSatoshi; blob.CustomLogo = restModel.CustomLogo; blob.CustomCSS = restModel.CustomCSS; diff --git a/BTCPayServer/Controllers/InvoiceController.cs b/BTCPayServer/Controllers/InvoiceController.cs index e9adc862d..a9a2a00a4 100644 --- a/BTCPayServer/Controllers/InvoiceController.cs +++ b/BTCPayServer/Controllers/InvoiceController.cs @@ -21,6 +21,7 @@ using BTCPayServer.Services.Stores; using BTCPayServer.Validation; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; +using Microsoft.CodeAnalysis.CSharp.Syntax; using NBitpayClient; using Newtonsoft.Json; using BitpayCreateInvoiceRequest = BTCPayServer.Models.BitpayCreateInvoiceRequest; @@ -78,41 +79,31 @@ namespace BTCPayServer.Controllers { var entity = await CreateInvoiceCoreRaw(invoice, store, serverUrl, additionalTags, cancellationToken); var resp = entity.EntityToDTO(); - return new DataWrapper(resp) {Facade = "pos/invoice"}; + return new DataWrapper(resp) { Facade = "pos/invoice" }; } internal async Task CreateInvoiceCoreRaw(BitpayCreateInvoiceRequest invoice, StoreData store, string serverUrl, List additionalTags = null, CancellationToken cancellationToken = default) { - invoice.Currency = invoice.Currency?.ToUpperInvariant() ?? "USD"; - InvoiceLogs logs = new InvoiceLogs(); - logs.Write("Creation of invoice starting"); - var entity = _InvoiceRepository.CreateNewInvoice(); - - var getAppsTaggingStore = _InvoiceRepository.GetAppsTaggingStore(store.Id); var storeBlob = store.GetStoreBlob(); - EmailAddressAttribute emailValidator = new EmailAddressAttribute(); - entity.ExpirationTime = invoice.ExpirationTime is DateTimeOffset v ? v : entity.InvoiceTime.AddMinutes(storeBlob.InvoiceExpiration); + var entity = _InvoiceRepository.CreateNewInvoice(); + entity.ExpirationTime = invoice.ExpirationTime is DateTimeOffset v ? v : entity.InvoiceTime + storeBlob.InvoiceExpiration; + entity.MonitoringExpiration = entity.ExpirationTime + storeBlob.MonitoringExpiration; if (entity.ExpirationTime - TimeSpan.FromSeconds(30.0) < entity.InvoiceTime) { throw new BitpayHttpException(400, "The expirationTime is set too soon"); } - entity.MonitoringExpiration = entity.ExpirationTime + TimeSpan.FromMinutes(storeBlob.MonitoringExpiration); + invoice.Currency = invoice.Currency?.ToUpperInvariant() ?? "USD"; + entity.Currency = invoice.Currency; entity.Metadata.OrderId = invoice.OrderId; + entity.Metadata.PosData = invoice.PosData; entity.ServerUrl = serverUrl; entity.FullNotifications = invoice.FullNotifications || invoice.ExtendedNotifications; entity.ExtendedNotifications = invoice.ExtendedNotifications; entity.NotificationURLTemplate = invoice.NotificationURL; entity.NotificationEmail = invoice.NotificationEmail; - entity.PaymentTolerance = storeBlob.PaymentTolerance; if (additionalTags != null) entity.InternalTags.AddRange(additionalTags); FillBuyerInfo(invoice, entity); - if (entity.Metadata.BuyerEmail != null) - { - if (!EmailValidator.IsEmail(entity.Metadata.BuyerEmail)) - throw new BitpayHttpException(400, "Invalid email"); - entity.RefundMail = entity.Metadata.BuyerEmail; - } var taxIncluded = invoice.TaxIncluded.HasValue ? invoice.TaxIncluded.Value : 0m; @@ -127,7 +118,6 @@ namespace BTCPayServer.Controllers invoice.Price = Math.Max(0.0m, invoice.Price); invoice.TaxIncluded = Math.Max(0.0m, taxIncluded); invoice.TaxIncluded = Math.Min(taxIncluded, invoice.Price); - entity.Metadata.ItemCode = invoice.ItemCode; entity.Metadata.ItemDesc = invoice.ItemDesc; entity.Metadata.Physical = invoice.Physical; @@ -135,29 +125,12 @@ namespace BTCPayServer.Controllers entity.Currency = invoice.Currency; entity.Price = invoice.Price; - if (invoice.Metadata != null) - { - var currentMetadata = entity.Metadata.ToJObject(); - foreach (var prop in invoice.Metadata.Properties()) - { - if (!currentMetadata.ContainsKey(prop.Name)) - currentMetadata.Add(prop.Name, prop.Value); - } - entity.Metadata = InvoiceMetadata.FromJObject(currentMetadata); - } - entity.RedirectURLTemplate = invoice.RedirectURL ?? store.StoreWebsite; - entity.RedirectAutomatically = invoice.RedirectAutomatically.GetValueOrDefault(storeBlob.RedirectAutomatically); - - entity.Status = InvoiceStatus.New; entity.SpeedPolicy = ParseSpeedPolicy(invoice.TransactionSpeed, store.SpeedPolicy); - HashSet currencyPairsToFetch = new HashSet(); - var rules = storeBlob.GetRateRules(_NetworkProvider); - var excludeFilter = storeBlob.GetExcludedPaymentMethods(); // Here we can compose filters from other origin with PaymentFilter.Any() - + IPaymentFilter excludeFilter = null; if (invoice.PaymentCurrencies?.Any() is true) { invoice.SupportedTransactionCurrencies ??= @@ -173,17 +146,67 @@ namespace BTCPayServer.Controllers var supportedTransactionCurrencies = invoice.SupportedTransactionCurrencies .Where(c => c.Value.Enabled) .Select(c => PaymentMethodId.TryParse(c.Key, out var p) ? p : null) + .Where(c => c != null) .ToHashSet(); - excludeFilter = PaymentFilter.Or(excludeFilter, - PaymentFilter.Where(p => !supportedTransactionCurrencies.Contains(p))); + excludeFilter = PaymentFilter.Where(p => !supportedTransactionCurrencies.Contains(p)); } + entity.PaymentTolerance = storeBlob.PaymentTolerance; + return await CreateInvoiceCoreRaw(entity, store, excludeFilter, cancellationToken); + } + internal async Task CreateInvoiceCoreRaw(CreateInvoiceRequest invoice, StoreData store, string serverUrl, List additionalTags = null, CancellationToken cancellationToken = default) + { + var storeBlob = store.GetStoreBlob(); + var entity = _InvoiceRepository.CreateNewInvoice(); + entity.ExpirationTime = entity.InvoiceTime + (invoice.Checkout.Expiration ?? storeBlob.InvoiceExpiration); + entity.MonitoringExpiration = entity.ExpirationTime + (invoice.Checkout.Monitoring ?? storeBlob.MonitoringExpiration); + if (invoice.Metadata != null) + entity.Metadata = InvoiceMetadata.FromJObject(invoice.Metadata); + invoice.Checkout ??= new CreateInvoiceRequest.CheckoutOptions(); + entity.Currency = invoice.Currency; + entity.Price = invoice.Amount; + entity.SpeedPolicy = invoice.Checkout.SpeedPolicy ?? store.SpeedPolicy; + IPaymentFilter excludeFilter = null; + if (invoice.Checkout.PaymentMethods != null) + { + var supportedTransactionCurrencies = invoice.Checkout.PaymentMethods + .Select(c => PaymentMethodId.TryParse(c, out var p) ? p : null) + .ToHashSet(); + excludeFilter = PaymentFilter.Where(p => !supportedTransactionCurrencies.Contains(p)); + } + entity.PaymentTolerance = invoice.Checkout.PaymentTolerance ?? storeBlob.PaymentTolerance; + return await CreateInvoiceCoreRaw(entity, store, excludeFilter, cancellationToken); + } + + internal async Task CreateInvoiceCoreRaw(InvoiceEntity entity, StoreData store, IPaymentFilter invoicePaymentMethodFilter, CancellationToken cancellationToken = default) + { + InvoiceLogs logs = new InvoiceLogs(); + logs.Write("Creation of invoice starting"); + + var getAppsTaggingStore = _InvoiceRepository.GetAppsTaggingStore(store.Id); + var storeBlob = store.GetStoreBlob(); + + if (entity.Metadata.BuyerEmail != null) + { + if (!EmailValidator.IsEmail(entity.Metadata.BuyerEmail)) + throw new BitpayHttpException(400, "Invalid email"); + entity.RefundMail = entity.Metadata.BuyerEmail; + } + entity.Status = InvoiceStatus.New; + HashSet currencyPairsToFetch = new HashSet(); + var rules = storeBlob.GetRateRules(_NetworkProvider); + var excludeFilter = storeBlob.GetExcludedPaymentMethods(); // Here we can compose filters from other origin with PaymentFilter.Any() + if (invoicePaymentMethodFilter != null) + { + excludeFilter = PaymentFilter.Or(excludeFilter, + invoicePaymentMethodFilter); + } foreach (var network in store.GetSupportedPaymentMethods(_NetworkProvider) .Where(s => !excludeFilter.Match(s.PaymentId)) .Select(c => _NetworkProvider.GetNetwork(c.PaymentId.CryptoCode)) .Where(c => c != null)) { - currencyPairsToFetch.Add(new CurrencyPair(network.CryptoCode, invoice.Currency)); + currencyPairsToFetch.Add(new CurrencyPair(network.CryptoCode, entity.Currency)); //TODO: abstract if (storeBlob.LightningMaxValue != null) currencyPairsToFetch.Add(new CurrencyPair(network.CryptoCode, storeBlob.LightningMaxValue.Currency)); @@ -195,7 +218,12 @@ namespace BTCPayServer.Controllers var fetchingByCurrencyPair = _RateProvider.FetchRates(currencyPairsToFetch, rateRules, cancellationToken); var fetchingAll = WhenAllFetched(logs, fetchingByCurrencyPair); - var supportedPaymentMethods = store.GetSupportedPaymentMethods(_NetworkProvider) + List supported = new List(); + var paymentMethods = new PaymentMethodDictionary(); + + // This loop ends with .ToList so we are querying all payment methods at once + // instead of sequentially to improve response time + foreach (var o in store.GetSupportedPaymentMethods(_NetworkProvider) .Where(s => !excludeFilter.Match(s.PaymentId) && _paymentMethodHandlerDictionary.Support(s.PaymentId)) .Select(c => (Handler: _paymentMethodHandlerDictionary[c.PaymentId], @@ -205,10 +233,7 @@ namespace BTCPayServer.Controllers .Select(o => (SupportedPaymentMethod: o.SupportedPaymentMethod, PaymentMethod: CreatePaymentMethodAsync(fetchingByCurrencyPair, o.Handler, o.SupportedPaymentMethod, o.Network, entity, store, logs))) - .ToList(); - List supported = new List(); - var paymentMethods = new PaymentMethodDictionary(); - foreach (var o in supportedPaymentMethods) + .ToList()) { var paymentMethod = await o.PaymentMethod; if (paymentMethod == null) @@ -231,7 +256,6 @@ namespace BTCPayServer.Controllers entity.SetSupportedPaymentMethods(supported); entity.SetPaymentMethods(paymentMethods); - entity.Metadata.PosData = invoice.PosData; foreach (var app in await getAppsTaggingStore) { entity.InternalTags.Add(AppService.GetAppInternalTag(app.Id)); diff --git a/BTCPayServer/Controllers/StoresController.cs b/BTCPayServer/Controllers/StoresController.cs index 99384dcde..6392f1564 100644 --- a/BTCPayServer/Controllers/StoresController.cs +++ b/BTCPayServer/Controllers/StoresController.cs @@ -481,8 +481,8 @@ namespace BTCPayServer.Controllers vm.SpeedPolicy = store.SpeedPolicy; vm.CanDelete = _Repo.CanDeleteStores(); AddPaymentMethods(store, storeBlob, vm); - vm.MonitoringExpiration = storeBlob.MonitoringExpiration; - vm.InvoiceExpiration = storeBlob.InvoiceExpiration; + vm.MonitoringExpiration = (int)storeBlob.MonitoringExpiration.TotalMinutes; + vm.InvoiceExpiration = (int)storeBlob.InvoiceExpiration.TotalMinutes; vm.LightningDescriptionTemplate = storeBlob.LightningDescriptionTemplate; vm.PaymentTolerance = storeBlob.PaymentTolerance; vm.PayJoinEnabled = storeBlob.PayJoinEnabled; @@ -579,8 +579,8 @@ namespace BTCPayServer.Controllers var blob = CurrentStore.GetStoreBlob(); blob.AnyoneCanInvoice = model.AnyoneCanCreateInvoice; blob.NetworkFeeMode = model.NetworkFeeMode; - blob.MonitoringExpiration = model.MonitoringExpiration; - blob.InvoiceExpiration = model.InvoiceExpiration; + blob.MonitoringExpiration = TimeSpan.FromMinutes(model.MonitoringExpiration); + blob.InvoiceExpiration = TimeSpan.FromMinutes(model.InvoiceExpiration); blob.LightningDescriptionTemplate = model.LightningDescriptionTemplate ?? string.Empty; blob.PaymentTolerance = model.PaymentTolerance; var payjoinChanged = blob.PayJoinEnabled != model.PayJoinEnabled; diff --git a/BTCPayServer/Data/PullPaymentsExtensions.cs b/BTCPayServer/Data/PullPaymentsExtensions.cs index b29e872b8..eddb1080e 100644 --- a/BTCPayServer/Data/PullPaymentsExtensions.cs +++ b/BTCPayServer/Data/PullPaymentsExtensions.cs @@ -195,7 +195,7 @@ namespace BTCPayServer.Data [JsonConverter(typeof(NumericStringJsonConverter))] public decimal MinimumClaim { get; set; } public PullPaymentView View { get; set; } = new PullPaymentView(); - [JsonConverter(typeof(TimeSpanJsonConverter))] + [JsonConverter(typeof(TimeSpanJsonConverter.Seconds))] public TimeSpan? Period { get; set; } [JsonProperty(ItemConverterType = typeof(PaymentMethodIdJsonConverter))] diff --git a/BTCPayServer/Data/StoreBlob.cs b/BTCPayServer/Data/StoreBlob.cs index 44d4267c5..1c76b6421 100644 --- a/BTCPayServer/Data/StoreBlob.cs +++ b/BTCPayServer/Data/StoreBlob.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; +using BTCPayServer.Client.JsonConverters; using BTCPayServer.Client.Models; using BTCPayServer.JsonConverters; using BTCPayServer.Payments; @@ -19,8 +20,8 @@ namespace BTCPayServer.Data { public StoreBlob() { - InvoiceExpiration = 15; - MonitoringExpiration = 1440; + InvoiceExpiration = TimeSpan.FromMinutes(15); + MonitoringExpiration = TimeSpan.FromDays(1); PaymentTolerance = 0; ShowRecommendedFee = true; RecommendedFeeBlockTarget = 1; @@ -66,17 +67,19 @@ namespace BTCPayServer.Data } public string DefaultLang { get; set; } - [DefaultValue(60)] + [DefaultValue(typeof(TimeSpan), "1.00:00:00")] [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] - public int MonitoringExpiration + [JsonConverter(typeof(TimeSpanJsonConverter.Minutes))] + public TimeSpan MonitoringExpiration { get; set; } - [DefaultValue(15)] + [DefaultValue(typeof(TimeSpan), "00:15:00")] [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] - public int InvoiceExpiration + [JsonConverter(typeof(TimeSpanJsonConverter.Minutes))] + public TimeSpan InvoiceExpiration { get; set; diff --git a/BTCPayServer/Models/BitpayCreateInvoiceRequest.cs b/BTCPayServer/Models/BitpayCreateInvoiceRequest.cs index 3ef626a98..d5112a369 100644 --- a/BTCPayServer/Models/BitpayCreateInvoiceRequest.cs +++ b/BTCPayServer/Models/BitpayCreateInvoiceRequest.cs @@ -82,6 +82,5 @@ namespace BTCPayServer.Models //Bitpay compatibility: create invoice in btcpay uses this instead of supportedTransactionCurrencies [JsonProperty(PropertyName = "paymentCurrencies", DefaultValueHandling = DefaultValueHandling.Ignore)] public IEnumerable PaymentCurrencies { get; set; } - public JObject Metadata { get; set; } } }