mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-22 06:21:44 +01:00
Decouple the CreateInvoiceCore from BitpayCreateInvoice, remove some features from greenfield invoice for now
This commit is contained in:
parent
b2ff041ec0
commit
67b04473b5
13 changed files with 135 additions and 126 deletions
|
@ -4,13 +4,38 @@ using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace BTCPayServer.Client.JsonConverters
|
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)
|
public override bool CanConvert(Type objectType)
|
||||||
{
|
{
|
||||||
return objectType == typeof(TimeSpan) || objectType == typeof(TimeSpan?);
|
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)
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -24,11 +49,11 @@ namespace BTCPayServer.Client.JsonConverters
|
||||||
}
|
}
|
||||||
if (reader.TokenType != JsonToken.Integer)
|
if (reader.TokenType != JsonToken.Integer)
|
||||||
throw new JsonObjectException("Invalid timespan, expected integer", reader);
|
throw new JsonObjectException("Invalid timespan, expected integer", reader);
|
||||||
return TimeSpan.FromSeconds((long)reader.Value);
|
return ToTimespan((long)reader.Value);
|
||||||
}
|
}
|
||||||
catch
|
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)
|
if (value is TimeSpan s)
|
||||||
{
|
{
|
||||||
writer.WriteValue((long)s.TotalSeconds);
|
writer.WriteValue(ToLong(s));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using BTCPayServer.Client.JsonConverters;
|
||||||
using BTCPayServer.JsonConverters;
|
using BTCPayServer.JsonConverters;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Converters;
|
using Newtonsoft.Json.Converters;
|
||||||
|
@ -20,12 +21,13 @@ namespace BTCPayServer.Client.Models
|
||||||
public SpeedPolicy? SpeedPolicy { get; set; }
|
public SpeedPolicy? SpeedPolicy { get; set; }
|
||||||
|
|
||||||
public string[] PaymentMethods { 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))]
|
[JsonConverter(typeof(TimeSpanJsonConverter.Minutes))]
|
||||||
public DateTimeOffset? ExpirationTime { get; set; }
|
[JsonProperty("expirationMinutes")]
|
||||||
|
public TimeSpan? Expiration { get; set; }
|
||||||
|
[JsonConverter(typeof(TimeSpanJsonConverter.Minutes))]
|
||||||
|
[JsonProperty("monitoringMinutes")]
|
||||||
|
public TimeSpan? Monitoring { get; set; }
|
||||||
|
|
||||||
[JsonProperty(ItemConverterType = typeof(NumericStringJsonConverter))]
|
[JsonProperty(ItemConverterType = typeof(NumericStringJsonConverter))]
|
||||||
public double? PaymentTolerance { get; set; }
|
public double? PaymentTolerance { get; set; }
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace BTCPayServer.Client.Models
|
||||||
[JsonConverter(typeof(LightMoneyJsonConverter))]
|
[JsonConverter(typeof(LightMoneyJsonConverter))]
|
||||||
public LightMoney Amount { get; set; }
|
public LightMoney Amount { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
[JsonConverter(typeof(JsonConverters.TimeSpanJsonConverter))]
|
[JsonConverter(typeof(JsonConverters.TimeSpanJsonConverter.Seconds))]
|
||||||
public TimeSpan Expiry { get; set; }
|
public TimeSpan Expiry { get; set; }
|
||||||
public bool PrivateRouteHints { get; set; }
|
public bool PrivateRouteHints { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace BTCPayServer.Client.Models
|
||||||
[JsonProperty(ItemConverterType = typeof(NumericStringJsonConverter))]
|
[JsonProperty(ItemConverterType = typeof(NumericStringJsonConverter))]
|
||||||
public decimal Amount { get; set; }
|
public decimal Amount { get; set; }
|
||||||
public string Currency { get; set; }
|
public string Currency { get; set; }
|
||||||
[JsonConverter(typeof(TimeSpanJsonConverter))]
|
[JsonConverter(typeof(TimeSpanJsonConverter.Seconds))]
|
||||||
public TimeSpan? Period { get; set; }
|
public TimeSpan? Period { get; set; }
|
||||||
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
||||||
public DateTimeOffset? ExpiresAt { get; set; }
|
public DateTimeOffset? ExpiresAt { get; set; }
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace BTCPayServer.Client.Models
|
||||||
public string Currency { get; set; }
|
public string Currency { get; set; }
|
||||||
[JsonConverter(typeof(NumericStringJsonConverter))]
|
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||||
public decimal Amount { get; set; }
|
public decimal Amount { get; set; }
|
||||||
[JsonConverter(typeof(TimeSpanJsonConverter))]
|
[JsonConverter(typeof(TimeSpanJsonConverter.Seconds))]
|
||||||
public TimeSpan? Period { get; set; }
|
public TimeSpan? Period { get; set; }
|
||||||
public bool Archived { get; set; }
|
public bool Archived { get; set; }
|
||||||
public string ViewLink { get; set; }
|
public string ViewLink { get; set; }
|
||||||
|
|
|
@ -16,11 +16,11 @@ namespace BTCPayServer.Client.Models
|
||||||
|
|
||||||
public string Website { get; set; }
|
public string Website { get; set; }
|
||||||
|
|
||||||
[JsonConverter(typeof(TimeSpanJsonConverter))]
|
[JsonConverter(typeof(TimeSpanJsonConverter.Seconds))]
|
||||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||||
public TimeSpan InvoiceExpiration { get; set; } = TimeSpan.FromMinutes(15);
|
public TimeSpan InvoiceExpiration { get; set; } = TimeSpan.FromMinutes(15);
|
||||||
|
|
||||||
[JsonConverter(typeof(TimeSpanJsonConverter))]
|
[JsonConverter(typeof(TimeSpanJsonConverter.Seconds))]
|
||||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||||
public TimeSpan MonitoringExpiration { get; set; } = TimeSpan.FromMinutes(60);
|
public TimeSpan MonitoringExpiration { get; set; } = TimeSpan.FromMinutes(60);
|
||||||
|
|
||||||
|
|
|
@ -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,
|
request.AddModelError(invoiceRequest => invoiceRequest.Checkout.Expiration,
|
||||||
"Expiration time must be in the future", this);
|
"Expiration time must be at least 30 seconds", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.Checkout.PaymentTolerance != null &&
|
if (request.Checkout.PaymentTolerance != null &&
|
||||||
|
@ -143,7 +143,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var invoice = await _invoiceController.CreateInvoiceCoreRaw(FromModel(request), store,
|
var invoice = await _invoiceController.CreateInvoiceCoreRaw(request, store,
|
||||||
Request.GetAbsoluteUri(""));
|
Request.GetAbsoluteUri(""));
|
||||||
return Ok(ToModel(invoice));
|
return Ok(ToModel(invoice));
|
||||||
}
|
}
|
||||||
|
@ -263,14 +263,12 @@ namespace BTCPayServer.Controllers.GreenField
|
||||||
Metadata = entity.Metadata.ToJObject(),
|
Metadata = entity.Metadata.ToJObject(),
|
||||||
Checkout = new CreateInvoiceRequest.CheckoutOptions()
|
Checkout = new CreateInvoiceRequest.CheckoutOptions()
|
||||||
{
|
{
|
||||||
ExpirationTime = entity.ExpirationTime,
|
Expiration = entity.ExpirationTime - entity.InvoiceTime,
|
||||||
|
Monitoring = entity.MonitoringExpiration - entity.ExpirationTime,
|
||||||
PaymentTolerance = entity.PaymentTolerance,
|
PaymentTolerance = entity.PaymentTolerance,
|
||||||
PaymentMethods =
|
PaymentMethods =
|
||||||
entity.GetPaymentMethods().Select(method => method.GetId().ToString()).ToArray(),
|
entity.GetPaymentMethods().Select(method => method.GetId().ToString()).ToArray(),
|
||||||
RedirectAutomatically = entity.RedirectAutomatically,
|
SpeedPolicy = entity.SpeedPolicy
|
||||||
RedirectUri = entity.RedirectURL?.ToString(),
|
|
||||||
SpeedPolicy = entity.SpeedPolicy,
|
|
||||||
WebHook = entity.NotificationURL
|
|
||||||
},
|
},
|
||||||
PaymentMethodData = entity.GetPaymentMethods().ToDictionary(method => method.GetId().ToString(),
|
PaymentMethodData = entity.GetPaymentMethods().ToDictionary(method => method.GetId().ToString(),
|
||||||
method =>
|
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<InvoiceMetadata>();
|
|
||||||
}
|
|
||||||
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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,8 +124,8 @@ namespace BTCPayServer.Controllers.GreenField
|
||||||
ShowRecommendedFee = storeBlob.ShowRecommendedFee,
|
ShowRecommendedFee = storeBlob.ShowRecommendedFee,
|
||||||
RecommendedFeeBlockTarget = storeBlob.RecommendedFeeBlockTarget,
|
RecommendedFeeBlockTarget = storeBlob.RecommendedFeeBlockTarget,
|
||||||
DefaultLang = storeBlob.DefaultLang,
|
DefaultLang = storeBlob.DefaultLang,
|
||||||
MonitoringExpiration = TimeSpan.FromMinutes(storeBlob.MonitoringExpiration),
|
MonitoringExpiration = storeBlob.MonitoringExpiration,
|
||||||
InvoiceExpiration = TimeSpan.FromMinutes(storeBlob.InvoiceExpiration),
|
InvoiceExpiration = storeBlob.InvoiceExpiration,
|
||||||
LightningAmountInSatoshi = storeBlob.LightningAmountInSatoshi,
|
LightningAmountInSatoshi = storeBlob.LightningAmountInSatoshi,
|
||||||
CustomLogo = storeBlob.CustomLogo,
|
CustomLogo = storeBlob.CustomLogo,
|
||||||
CustomCSS = storeBlob.CustomCSS,
|
CustomCSS = storeBlob.CustomCSS,
|
||||||
|
@ -160,8 +160,8 @@ namespace BTCPayServer.Controllers.GreenField
|
||||||
blob.ShowRecommendedFee = restModel.ShowRecommendedFee;
|
blob.ShowRecommendedFee = restModel.ShowRecommendedFee;
|
||||||
blob.RecommendedFeeBlockTarget = restModel.RecommendedFeeBlockTarget;
|
blob.RecommendedFeeBlockTarget = restModel.RecommendedFeeBlockTarget;
|
||||||
blob.DefaultLang = restModel.DefaultLang;
|
blob.DefaultLang = restModel.DefaultLang;
|
||||||
blob.MonitoringExpiration = (int)restModel.MonitoringExpiration.TotalMinutes;
|
blob.MonitoringExpiration = restModel.MonitoringExpiration;
|
||||||
blob.InvoiceExpiration = (int)restModel.InvoiceExpiration.TotalMinutes;
|
blob.InvoiceExpiration = restModel.InvoiceExpiration;
|
||||||
blob.LightningAmountInSatoshi = restModel.LightningAmountInSatoshi;
|
blob.LightningAmountInSatoshi = restModel.LightningAmountInSatoshi;
|
||||||
blob.CustomLogo = restModel.CustomLogo;
|
blob.CustomLogo = restModel.CustomLogo;
|
||||||
blob.CustomCSS = restModel.CustomCSS;
|
blob.CustomCSS = restModel.CustomCSS;
|
||||||
|
|
|
@ -21,6 +21,7 @@ using BTCPayServer.Services.Stores;
|
||||||
using BTCPayServer.Validation;
|
using BTCPayServer.Validation;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
using NBitpayClient;
|
using NBitpayClient;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using BitpayCreateInvoiceRequest = BTCPayServer.Models.BitpayCreateInvoiceRequest;
|
using BitpayCreateInvoiceRequest = BTCPayServer.Models.BitpayCreateInvoiceRequest;
|
||||||
|
@ -78,41 +79,31 @@ namespace BTCPayServer.Controllers
|
||||||
{
|
{
|
||||||
var entity = await CreateInvoiceCoreRaw(invoice, store, serverUrl, additionalTags, cancellationToken);
|
var entity = await CreateInvoiceCoreRaw(invoice, store, serverUrl, additionalTags, cancellationToken);
|
||||||
var resp = entity.EntityToDTO();
|
var resp = entity.EntityToDTO();
|
||||||
return new DataWrapper<InvoiceResponse>(resp) {Facade = "pos/invoice"};
|
return new DataWrapper<InvoiceResponse>(resp) { Facade = "pos/invoice" };
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<InvoiceEntity> CreateInvoiceCoreRaw(BitpayCreateInvoiceRequest invoice, StoreData store, string serverUrl, List<string> additionalTags = null, CancellationToken cancellationToken = default)
|
internal async Task<InvoiceEntity> CreateInvoiceCoreRaw(BitpayCreateInvoiceRequest invoice, StoreData store, string serverUrl, List<string> 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();
|
var storeBlob = store.GetStoreBlob();
|
||||||
EmailAddressAttribute emailValidator = new EmailAddressAttribute();
|
var entity = _InvoiceRepository.CreateNewInvoice();
|
||||||
entity.ExpirationTime = invoice.ExpirationTime is DateTimeOffset v ? v : entity.InvoiceTime.AddMinutes(storeBlob.InvoiceExpiration);
|
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)
|
if (entity.ExpirationTime - TimeSpan.FromSeconds(30.0) < entity.InvoiceTime)
|
||||||
{
|
{
|
||||||
throw new BitpayHttpException(400, "The expirationTime is set too soon");
|
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.OrderId = invoice.OrderId;
|
||||||
|
entity.Metadata.PosData = invoice.PosData;
|
||||||
entity.ServerUrl = serverUrl;
|
entity.ServerUrl = serverUrl;
|
||||||
entity.FullNotifications = invoice.FullNotifications || invoice.ExtendedNotifications;
|
entity.FullNotifications = invoice.FullNotifications || invoice.ExtendedNotifications;
|
||||||
entity.ExtendedNotifications = invoice.ExtendedNotifications;
|
entity.ExtendedNotifications = invoice.ExtendedNotifications;
|
||||||
entity.NotificationURLTemplate = invoice.NotificationURL;
|
entity.NotificationURLTemplate = invoice.NotificationURL;
|
||||||
entity.NotificationEmail = invoice.NotificationEmail;
|
entity.NotificationEmail = invoice.NotificationEmail;
|
||||||
entity.PaymentTolerance = storeBlob.PaymentTolerance;
|
|
||||||
if (additionalTags != null)
|
if (additionalTags != null)
|
||||||
entity.InternalTags.AddRange(additionalTags);
|
entity.InternalTags.AddRange(additionalTags);
|
||||||
FillBuyerInfo(invoice, entity);
|
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;
|
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.Price = Math.Max(0.0m, invoice.Price);
|
||||||
invoice.TaxIncluded = Math.Max(0.0m, taxIncluded);
|
invoice.TaxIncluded = Math.Max(0.0m, taxIncluded);
|
||||||
invoice.TaxIncluded = Math.Min(taxIncluded, invoice.Price);
|
invoice.TaxIncluded = Math.Min(taxIncluded, invoice.Price);
|
||||||
|
|
||||||
entity.Metadata.ItemCode = invoice.ItemCode;
|
entity.Metadata.ItemCode = invoice.ItemCode;
|
||||||
entity.Metadata.ItemDesc = invoice.ItemDesc;
|
entity.Metadata.ItemDesc = invoice.ItemDesc;
|
||||||
entity.Metadata.Physical = invoice.Physical;
|
entity.Metadata.Physical = invoice.Physical;
|
||||||
|
@ -135,29 +125,12 @@ namespace BTCPayServer.Controllers
|
||||||
entity.Currency = invoice.Currency;
|
entity.Currency = invoice.Currency;
|
||||||
entity.Price = invoice.Price;
|
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.RedirectURLTemplate = invoice.RedirectURL ?? store.StoreWebsite;
|
||||||
|
|
||||||
entity.RedirectAutomatically =
|
entity.RedirectAutomatically =
|
||||||
invoice.RedirectAutomatically.GetValueOrDefault(storeBlob.RedirectAutomatically);
|
invoice.RedirectAutomatically.GetValueOrDefault(storeBlob.RedirectAutomatically);
|
||||||
|
|
||||||
entity.Status = InvoiceStatus.New;
|
|
||||||
entity.SpeedPolicy = ParseSpeedPolicy(invoice.TransactionSpeed, store.SpeedPolicy);
|
entity.SpeedPolicy = ParseSpeedPolicy(invoice.TransactionSpeed, store.SpeedPolicy);
|
||||||
|
|
||||||
HashSet<CurrencyPair> currencyPairsToFetch = new HashSet<CurrencyPair>();
|
IPaymentFilter excludeFilter = null;
|
||||||
var rules = storeBlob.GetRateRules(_NetworkProvider);
|
|
||||||
var excludeFilter = storeBlob.GetExcludedPaymentMethods(); // Here we can compose filters from other origin with PaymentFilter.Any()
|
|
||||||
|
|
||||||
if (invoice.PaymentCurrencies?.Any() is true)
|
if (invoice.PaymentCurrencies?.Any() is true)
|
||||||
{
|
{
|
||||||
invoice.SupportedTransactionCurrencies ??=
|
invoice.SupportedTransactionCurrencies ??=
|
||||||
|
@ -173,17 +146,67 @@ namespace BTCPayServer.Controllers
|
||||||
var supportedTransactionCurrencies = invoice.SupportedTransactionCurrencies
|
var supportedTransactionCurrencies = invoice.SupportedTransactionCurrencies
|
||||||
.Where(c => c.Value.Enabled)
|
.Where(c => c.Value.Enabled)
|
||||||
.Select(c => PaymentMethodId.TryParse(c.Key, out var p) ? p : null)
|
.Select(c => PaymentMethodId.TryParse(c.Key, out var p) ? p : null)
|
||||||
|
.Where(c => c != null)
|
||||||
.ToHashSet();
|
.ToHashSet();
|
||||||
excludeFilter = PaymentFilter.Or(excludeFilter,
|
excludeFilter = PaymentFilter.Where(p => !supportedTransactionCurrencies.Contains(p));
|
||||||
PaymentFilter.Where(p => !supportedTransactionCurrencies.Contains(p)));
|
|
||||||
}
|
}
|
||||||
|
entity.PaymentTolerance = storeBlob.PaymentTolerance;
|
||||||
|
return await CreateInvoiceCoreRaw(entity, store, excludeFilter, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task<InvoiceEntity> CreateInvoiceCoreRaw(CreateInvoiceRequest invoice, StoreData store, string serverUrl, List<string> 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<InvoiceEntity> 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<CurrencyPair> currencyPairsToFetch = new HashSet<CurrencyPair>();
|
||||||
|
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)
|
foreach (var network in store.GetSupportedPaymentMethods(_NetworkProvider)
|
||||||
.Where(s => !excludeFilter.Match(s.PaymentId))
|
.Where(s => !excludeFilter.Match(s.PaymentId))
|
||||||
.Select(c => _NetworkProvider.GetNetwork<BTCPayNetworkBase>(c.PaymentId.CryptoCode))
|
.Select(c => _NetworkProvider.GetNetwork<BTCPayNetworkBase>(c.PaymentId.CryptoCode))
|
||||||
.Where(c => c != null))
|
.Where(c => c != null))
|
||||||
{
|
{
|
||||||
currencyPairsToFetch.Add(new CurrencyPair(network.CryptoCode, invoice.Currency));
|
currencyPairsToFetch.Add(new CurrencyPair(network.CryptoCode, entity.Currency));
|
||||||
//TODO: abstract
|
//TODO: abstract
|
||||||
if (storeBlob.LightningMaxValue != null)
|
if (storeBlob.LightningMaxValue != null)
|
||||||
currencyPairsToFetch.Add(new CurrencyPair(network.CryptoCode, storeBlob.LightningMaxValue.Currency));
|
currencyPairsToFetch.Add(new CurrencyPair(network.CryptoCode, storeBlob.LightningMaxValue.Currency));
|
||||||
|
@ -195,7 +218,12 @@ namespace BTCPayServer.Controllers
|
||||||
var fetchingByCurrencyPair = _RateProvider.FetchRates(currencyPairsToFetch, rateRules, cancellationToken);
|
var fetchingByCurrencyPair = _RateProvider.FetchRates(currencyPairsToFetch, rateRules, cancellationToken);
|
||||||
var fetchingAll = WhenAllFetched(logs, fetchingByCurrencyPair);
|
var fetchingAll = WhenAllFetched(logs, fetchingByCurrencyPair);
|
||||||
|
|
||||||
var supportedPaymentMethods = store.GetSupportedPaymentMethods(_NetworkProvider)
|
List<ISupportedPaymentMethod> supported = new List<ISupportedPaymentMethod>();
|
||||||
|
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))
|
.Where(s => !excludeFilter.Match(s.PaymentId) && _paymentMethodHandlerDictionary.Support(s.PaymentId))
|
||||||
.Select(c =>
|
.Select(c =>
|
||||||
(Handler: _paymentMethodHandlerDictionary[c.PaymentId],
|
(Handler: _paymentMethodHandlerDictionary[c.PaymentId],
|
||||||
|
@ -205,10 +233,7 @@ namespace BTCPayServer.Controllers
|
||||||
.Select(o =>
|
.Select(o =>
|
||||||
(SupportedPaymentMethod: o.SupportedPaymentMethod,
|
(SupportedPaymentMethod: o.SupportedPaymentMethod,
|
||||||
PaymentMethod: CreatePaymentMethodAsync(fetchingByCurrencyPair, o.Handler, o.SupportedPaymentMethod, o.Network, entity, store, logs)))
|
PaymentMethod: CreatePaymentMethodAsync(fetchingByCurrencyPair, o.Handler, o.SupportedPaymentMethod, o.Network, entity, store, logs)))
|
||||||
.ToList();
|
.ToList())
|
||||||
List<ISupportedPaymentMethod> supported = new List<ISupportedPaymentMethod>();
|
|
||||||
var paymentMethods = new PaymentMethodDictionary();
|
|
||||||
foreach (var o in supportedPaymentMethods)
|
|
||||||
{
|
{
|
||||||
var paymentMethod = await o.PaymentMethod;
|
var paymentMethod = await o.PaymentMethod;
|
||||||
if (paymentMethod == null)
|
if (paymentMethod == null)
|
||||||
|
@ -231,7 +256,6 @@ namespace BTCPayServer.Controllers
|
||||||
|
|
||||||
entity.SetSupportedPaymentMethods(supported);
|
entity.SetSupportedPaymentMethods(supported);
|
||||||
entity.SetPaymentMethods(paymentMethods);
|
entity.SetPaymentMethods(paymentMethods);
|
||||||
entity.Metadata.PosData = invoice.PosData;
|
|
||||||
foreach (var app in await getAppsTaggingStore)
|
foreach (var app in await getAppsTaggingStore)
|
||||||
{
|
{
|
||||||
entity.InternalTags.Add(AppService.GetAppInternalTag(app.Id));
|
entity.InternalTags.Add(AppService.GetAppInternalTag(app.Id));
|
||||||
|
|
|
@ -481,8 +481,8 @@ namespace BTCPayServer.Controllers
|
||||||
vm.SpeedPolicy = store.SpeedPolicy;
|
vm.SpeedPolicy = store.SpeedPolicy;
|
||||||
vm.CanDelete = _Repo.CanDeleteStores();
|
vm.CanDelete = _Repo.CanDeleteStores();
|
||||||
AddPaymentMethods(store, storeBlob, vm);
|
AddPaymentMethods(store, storeBlob, vm);
|
||||||
vm.MonitoringExpiration = storeBlob.MonitoringExpiration;
|
vm.MonitoringExpiration = (int)storeBlob.MonitoringExpiration.TotalMinutes;
|
||||||
vm.InvoiceExpiration = storeBlob.InvoiceExpiration;
|
vm.InvoiceExpiration = (int)storeBlob.InvoiceExpiration.TotalMinutes;
|
||||||
vm.LightningDescriptionTemplate = storeBlob.LightningDescriptionTemplate;
|
vm.LightningDescriptionTemplate = storeBlob.LightningDescriptionTemplate;
|
||||||
vm.PaymentTolerance = storeBlob.PaymentTolerance;
|
vm.PaymentTolerance = storeBlob.PaymentTolerance;
|
||||||
vm.PayJoinEnabled = storeBlob.PayJoinEnabled;
|
vm.PayJoinEnabled = storeBlob.PayJoinEnabled;
|
||||||
|
@ -579,8 +579,8 @@ namespace BTCPayServer.Controllers
|
||||||
var blob = CurrentStore.GetStoreBlob();
|
var blob = CurrentStore.GetStoreBlob();
|
||||||
blob.AnyoneCanInvoice = model.AnyoneCanCreateInvoice;
|
blob.AnyoneCanInvoice = model.AnyoneCanCreateInvoice;
|
||||||
blob.NetworkFeeMode = model.NetworkFeeMode;
|
blob.NetworkFeeMode = model.NetworkFeeMode;
|
||||||
blob.MonitoringExpiration = model.MonitoringExpiration;
|
blob.MonitoringExpiration = TimeSpan.FromMinutes(model.MonitoringExpiration);
|
||||||
blob.InvoiceExpiration = model.InvoiceExpiration;
|
blob.InvoiceExpiration = TimeSpan.FromMinutes(model.InvoiceExpiration);
|
||||||
blob.LightningDescriptionTemplate = model.LightningDescriptionTemplate ?? string.Empty;
|
blob.LightningDescriptionTemplate = model.LightningDescriptionTemplate ?? string.Empty;
|
||||||
blob.PaymentTolerance = model.PaymentTolerance;
|
blob.PaymentTolerance = model.PaymentTolerance;
|
||||||
var payjoinChanged = blob.PayJoinEnabled != model.PayJoinEnabled;
|
var payjoinChanged = blob.PayJoinEnabled != model.PayJoinEnabled;
|
||||||
|
|
|
@ -195,7 +195,7 @@ namespace BTCPayServer.Data
|
||||||
[JsonConverter(typeof(NumericStringJsonConverter))]
|
[JsonConverter(typeof(NumericStringJsonConverter))]
|
||||||
public decimal MinimumClaim { get; set; }
|
public decimal MinimumClaim { get; set; }
|
||||||
public PullPaymentView View { get; set; } = new PullPaymentView();
|
public PullPaymentView View { get; set; } = new PullPaymentView();
|
||||||
[JsonConverter(typeof(TimeSpanJsonConverter))]
|
[JsonConverter(typeof(TimeSpanJsonConverter.Seconds))]
|
||||||
public TimeSpan? Period { get; set; }
|
public TimeSpan? Period { get; set; }
|
||||||
|
|
||||||
[JsonProperty(ItemConverterType = typeof(PaymentMethodIdJsonConverter))]
|
[JsonProperty(ItemConverterType = typeof(PaymentMethodIdJsonConverter))]
|
||||||
|
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using BTCPayServer.Client.JsonConverters;
|
||||||
using BTCPayServer.Client.Models;
|
using BTCPayServer.Client.Models;
|
||||||
using BTCPayServer.JsonConverters;
|
using BTCPayServer.JsonConverters;
|
||||||
using BTCPayServer.Payments;
|
using BTCPayServer.Payments;
|
||||||
|
@ -19,8 +20,8 @@ namespace BTCPayServer.Data
|
||||||
{
|
{
|
||||||
public StoreBlob()
|
public StoreBlob()
|
||||||
{
|
{
|
||||||
InvoiceExpiration = 15;
|
InvoiceExpiration = TimeSpan.FromMinutes(15);
|
||||||
MonitoringExpiration = 1440;
|
MonitoringExpiration = TimeSpan.FromDays(1);
|
||||||
PaymentTolerance = 0;
|
PaymentTolerance = 0;
|
||||||
ShowRecommendedFee = true;
|
ShowRecommendedFee = true;
|
||||||
RecommendedFeeBlockTarget = 1;
|
RecommendedFeeBlockTarget = 1;
|
||||||
|
@ -66,17 +67,19 @@ namespace BTCPayServer.Data
|
||||||
}
|
}
|
||||||
|
|
||||||
public string DefaultLang { get; set; }
|
public string DefaultLang { get; set; }
|
||||||
[DefaultValue(60)]
|
[DefaultValue(typeof(TimeSpan), "1.00:00:00")]
|
||||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
||||||
public int MonitoringExpiration
|
[JsonConverter(typeof(TimeSpanJsonConverter.Minutes))]
|
||||||
|
public TimeSpan MonitoringExpiration
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
|
||||||
[DefaultValue(15)]
|
[DefaultValue(typeof(TimeSpan), "00:15:00")]
|
||||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
||||||
public int InvoiceExpiration
|
[JsonConverter(typeof(TimeSpanJsonConverter.Minutes))]
|
||||||
|
public TimeSpan InvoiceExpiration
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
|
|
|
@ -82,6 +82,5 @@ namespace BTCPayServer.Models
|
||||||
//Bitpay compatibility: create invoice in btcpay uses this instead of supportedTransactionCurrencies
|
//Bitpay compatibility: create invoice in btcpay uses this instead of supportedTransactionCurrencies
|
||||||
[JsonProperty(PropertyName = "paymentCurrencies", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
[JsonProperty(PropertyName = "paymentCurrencies", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||||
public IEnumerable<string> PaymentCurrencies { get; set; }
|
public IEnumerable<string> PaymentCurrencies { get; set; }
|
||||||
public JObject Metadata { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue