Compare commits
1 Commits
bugfix/vau
...
fqpjn3
Author | SHA1 | Date | |
---|---|---|---|
f09d6618c9 |
@ -240,7 +240,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.True(valid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task EnsureSwaggerPermissionsDocumented()
|
||||
@ -252,7 +252,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
var description =
|
||||
"BTCPay Server supports authenticating and authorizing users through an API Key that is generated by them. Send the API Key as a header value to Authorization with the format: `token {token}`. For a smoother experience, you can generate a url that redirects users to an API key creation screen.\n\n The following permissions are available to the context of the user creating the API Key:\n\n#OTHERPERMISSIONS#\n\nThe following permissions are available if the user is an administrator:\n\n#SERVERPERMISSIONS#\n\nThe following permissions applies to all stores of the user, you can limit to a specific store with the following format: `btcpay.store.cancreateinvoice:6HSHAEU4iYWtjxtyRs9KyPjM9GAQp8kw2T9VWbGG1FnZ`:\n\n#STOREPERMISSIONS#\n\nNote that API Keys only limits permission of a user and can never expand it. If an API Key has the permission `btcpay.server.canmodifyserversettings` but that the user account creating this API Key is not administrator, the API Key will not be able to modify the server settings.\n";
|
||||
|
||||
|
||||
var storePolicies =
|
||||
ManageController.AddApiKeyViewModel.PermissionValueItem.PermissionDescriptions.Where(pair =>
|
||||
Policies.IsStorePolicy(pair.Key) && !pair.Key.EndsWith(":", StringComparison.InvariantCulture));
|
||||
@ -270,7 +270,7 @@ namespace BTCPayServer.Tests
|
||||
.Replace("#STOREPERMISSIONS#",
|
||||
string.Join("\n", storePolicies.Select(pair => $"* `{pair.Key}`: {pair.Value.Title}")));
|
||||
Logs.Tester.LogInformation(description);
|
||||
|
||||
|
||||
var sresp = Assert
|
||||
.IsType<JsonResult>(await tester.PayTester.GetController<HomeController>(acc.UserId, acc.StoreId)
|
||||
.Swagger()).Value.ToJson();
|
||||
@ -668,7 +668,7 @@ namespace BTCPayServer.Tests
|
||||
AssertPayoutLabel(info);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[Fact]
|
||||
[Trait("Fast", "Fast")]
|
||||
@ -905,7 +905,7 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
await tester.ExplorerNode.SendToAddressAsync(
|
||||
BitcoinAddress.Create(invoice.BitcoinAddress, Network.RegTest), Money.Coins(0.00005m));
|
||||
}, e => e.InvoiceId == invoice.Id && e.PaymentMethodId.PaymentType == LightningPaymentType.Instance );
|
||||
}, e => e.InvoiceId == invoice.Id && e.PaymentMethodId.PaymentType == LightningPaymentType.Instance);
|
||||
await tester.ExplorerNode.GenerateAsync(1);
|
||||
await Task.Delay(100); // wait a bit for payment to process before fetching new invoice
|
||||
var newInvoice = await user.BitPay.GetInvoiceAsync(invoice.Id);
|
||||
@ -2321,6 +2321,8 @@ namespace BTCPayServer.Tests
|
||||
{("[1,2,3]", new Dictionary<string, object>() {{string.Empty, "[1,2,3]"}})},
|
||||
{("{ \"key\": \"value\"}", new Dictionary<string, object>() {{"key", "value"}})},
|
||||
{("{ \"key\": true}", new Dictionary<string, object>() {{"key", "True"}})},
|
||||
// Duplicate keys should not crash things
|
||||
{("{ \"key\": true, \"key\": true}", new Dictionary<string, object>() {{"key", "True"}})},
|
||||
{
|
||||
("{ invalidjson file here}",
|
||||
new Dictionary<string, object>() {{String.Empty, "{ invalidjson file here}"}})
|
||||
@ -2330,6 +2332,17 @@ namespace BTCPayServer.Tests
|
||||
testCases.ForEach(tuple =>
|
||||
{
|
||||
Assert.Equal(tuple.expectedOutput, InvoiceController.PosDataParser.ParsePosData(tuple.input));
|
||||
JObject jobj = null;
|
||||
try
|
||||
{
|
||||
jobj = JObject.Parse(tuple.input);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
// If the posData is directly a jobject, it should parse the same as if it was a string with json encoded
|
||||
if (jobj != null)
|
||||
Assert.Equal(tuple.expectedOutput, InvoiceController.PosDataParser.ParsePosData(jobj));
|
||||
});
|
||||
}
|
||||
|
||||
@ -3129,7 +3142,7 @@ namespace BTCPayServer.Tests
|
||||
("\\test.com", false),
|
||||
("te\\st.com", false)
|
||||
};
|
||||
foreach(var t in tests)
|
||||
foreach (var t in tests)
|
||||
{
|
||||
Assert.Equal(t.Item2, t.Item1.IsValidFileName());
|
||||
}
|
||||
@ -3439,7 +3452,7 @@ namespace BTCPayServer.Tests
|
||||
var acc = tester.NewAccount();
|
||||
await acc.GrantAccessAsync(true);
|
||||
await acc.CreateStoreAsync();
|
||||
|
||||
|
||||
// Test if legacy DerivationStrategy column is converted to DerivationStrategies
|
||||
var store = await tester.PayTester.StoreRepository.FindStore(acc.StoreId);
|
||||
var xpub = "tpubDDmH1briYfZcTDMEc7uMEA5hinzjUTzR9yMC1drxTMeiWyw1VyCqTuzBke6df2sqbfw9QG6wbgTLF5yLjcXsZNaXvJMZLwNEwyvmiFWcLav";
|
||||
@ -3454,7 +3467,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(derivation, v.AccountOriginal.ToString());
|
||||
Assert.Equal(xpub, v.SigningKey.ToString());
|
||||
Assert.Equal(xpub, v.GetSigningAccountKeySettings().AccountKey.ToString());
|
||||
|
||||
|
||||
await acc.RegisterLightningNodeAsync("BTC", LightningConnectionType.CLightning, true);
|
||||
store = await tester.PayTester.StoreRepository.FindStore(acc.StoreId);
|
||||
var lnMethod = store.GetSupportedPaymentMethods(tester.NetworkProvider).OfType<LightningSupportedPaymentMethod>().First();
|
||||
@ -3601,43 +3614,43 @@ namespace BTCPayServer.Tests
|
||||
|
||||
var settings = tester.PayTester.GetService<SettingsRepository>();
|
||||
var emailSenderFactory = tester.PayTester.GetService<EmailSenderFactory>();
|
||||
|
||||
|
||||
Assert.Null(await Assert.IsType<ServerEmailSender>(emailSenderFactory.GetEmailSender()).GetEmailSettings());
|
||||
Assert.Null(await Assert.IsType<StoreEmailSender>(emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings());
|
||||
|
||||
|
||||
|
||||
await settings.UpdateSetting(new PoliciesSettings() { DisableStoresToUseServerEmailSettings = false });
|
||||
await settings.UpdateSetting(new EmailSettings()
|
||||
{
|
||||
From = "admin@admin.com",
|
||||
Login = "admin@admin.com",
|
||||
Password = "admin@admin.com",
|
||||
Port = 1234,
|
||||
Server = "admin.com",
|
||||
EnableSSL = true
|
||||
From = "admin@admin.com",
|
||||
Login = "admin@admin.com",
|
||||
Password = "admin@admin.com",
|
||||
Port = 1234,
|
||||
Server = "admin.com",
|
||||
EnableSSL = true
|
||||
});
|
||||
Assert.Equal("admin@admin.com",(await Assert.IsType<ServerEmailSender>(emailSenderFactory.GetEmailSender()).GetEmailSettings()).Login);
|
||||
Assert.Equal("admin@admin.com",(await Assert.IsType<StoreEmailSender>(emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings()).Login);
|
||||
Assert.Equal("admin@admin.com", (await Assert.IsType<ServerEmailSender>(emailSenderFactory.GetEmailSender()).GetEmailSettings()).Login);
|
||||
Assert.Equal("admin@admin.com", (await Assert.IsType<StoreEmailSender>(emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings()).Login);
|
||||
|
||||
await settings.UpdateSetting(new PoliciesSettings() { DisableStoresToUseServerEmailSettings = true });
|
||||
Assert.Equal("admin@admin.com",(await Assert.IsType<ServerEmailSender>(emailSenderFactory.GetEmailSender()).GetEmailSettings()).Login);
|
||||
Assert.Equal("admin@admin.com", (await Assert.IsType<ServerEmailSender>(emailSenderFactory.GetEmailSender()).GetEmailSettings()).Login);
|
||||
Assert.Null(await Assert.IsType<StoreEmailSender>(emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings());
|
||||
|
||||
Assert.IsType<RedirectToActionResult>(await acc.GetController<StoresController>().Emails(acc.StoreId, new EmailsViewModel(new EmailSettings()
|
||||
{
|
||||
From = "store@store.com",
|
||||
From = "store@store.com",
|
||||
Login = "store@store.com",
|
||||
Password = "store@store.com",
|
||||
Port = 1234,
|
||||
Server = "store.com",
|
||||
EnableSSL = true
|
||||
}), ""));
|
||||
|
||||
Assert.Equal("store@store.com",(await Assert.IsType<StoreEmailSender>(emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings()).Login);
|
||||
|
||||
Assert.Equal("store@store.com", (await Assert.IsType<StoreEmailSender>(emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings()).Login);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ namespace BTCPayServer.Controllers
|
||||
ExpirationDate = invoice.ExpirationTime,
|
||||
MonitoringDate = invoice.MonitoringExpiration,
|
||||
Fiat = _CurrencyNameTable.DisplayFormatCurrency(invoice.Price, invoice.Currency),
|
||||
TaxIncluded = _CurrencyNameTable.DisplayFormatCurrency(invoice.Metadata.TaxIncluded ?? 0.0m, invoice.Currency),
|
||||
TaxIncluded = _CurrencyNameTable.DisplayFormatCurrency(invoice.Metadata.TaxIncluded.AsDecimal() ?? 0.0m, invoice.Currency),
|
||||
NotificationUrl = invoice.NotificationURL?.AbsoluteUri,
|
||||
RedirectUrl = invoice.RedirectURL?.AbsoluteUri,
|
||||
TypedMetadata = invoice.Metadata,
|
||||
@ -539,7 +539,7 @@ namespace BTCPayServer.Controllers
|
||||
Activated = paymentMethodDetails.Activated,
|
||||
CryptoCode = network.CryptoCode,
|
||||
RootPath = this.Request.PathBase.Value.WithTrailingSlash(),
|
||||
OrderId = invoice.Metadata.OrderId,
|
||||
OrderId = invoice.Metadata.OrderId.AsString(),
|
||||
InvoiceId = invoice.Id,
|
||||
DefaultLang = lang ?? invoice.DefaultLanguage ?? storeBlob.DefaultLang ?? "en",
|
||||
CustomCSSLink = storeBlob.CustomCSS,
|
||||
@ -556,7 +556,7 @@ namespace BTCPayServer.Controllers
|
||||
ExpirationSeconds = Math.Max(0, (int)(invoice.ExpirationTime - DateTimeOffset.UtcNow).TotalSeconds),
|
||||
MaxTimeSeconds = (int)(invoice.ExpirationTime - invoice.InvoiceTime).TotalSeconds,
|
||||
MaxTimeMinutes = (int)(invoice.ExpirationTime - invoice.InvoiceTime).TotalMinutes,
|
||||
ItemDesc = invoice.Metadata.ItemDesc,
|
||||
ItemDesc = invoice.Metadata.ItemDesc.AsString(),
|
||||
Rate = ExchangeRate(paymentMethod),
|
||||
MerchantRefLink = invoice.RedirectURL?.AbsoluteUri ?? "/",
|
||||
RedirectAutomatically = invoice.RedirectAutomatically,
|
||||
@ -725,7 +725,7 @@ namespace BTCPayServer.Controllers
|
||||
ShowCheckout = invoice.Status == InvoiceStatusLegacy.New,
|
||||
Date = invoice.InvoiceTime,
|
||||
InvoiceId = invoice.Id,
|
||||
OrderId = invoice.Metadata.OrderId ?? string.Empty,
|
||||
OrderId = invoice.Metadata.OrderId.AsString() ?? string.Empty,
|
||||
RedirectUrl = invoice.RedirectURL?.AbsoluteUri ?? string.Empty,
|
||||
AmountCurrency = _CurrencyNameTable.DisplayFormatCurrency(invoice.Price, invoice.Currency),
|
||||
CanMarkInvalid = state.CanMarkInvalid(),
|
||||
@ -900,42 +900,55 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
public class PosDataParser
|
||||
{
|
||||
public static Dictionary<string, object> ParsePosData(string posData)
|
||||
public static Dictionary<string, object> ParsePosData(JToken posData)
|
||||
{
|
||||
var result = new Dictionary<string, object>();
|
||||
if (string.IsNullOrEmpty(posData))
|
||||
if (posData is null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
try
|
||||
JObject posDataJObj = posData as JObject;
|
||||
if (posDataJObj is null)
|
||||
{
|
||||
var jObject = JObject.Parse(posData);
|
||||
foreach (var item in jObject)
|
||||
if (posData.AsString() is string posDataStr)
|
||||
{
|
||||
|
||||
switch (item.Value.Type)
|
||||
if (string.IsNullOrEmpty(posDataStr))
|
||||
return result;
|
||||
try
|
||||
{
|
||||
case JTokenType.Array:
|
||||
var items = item.Value.AsEnumerable().ToList();
|
||||
for (var i = 0; i < items.Count; i++)
|
||||
{
|
||||
result.Add($"{item.Key}[{i}]", ParsePosData(items[i].ToString()));
|
||||
}
|
||||
break;
|
||||
case JTokenType.Object:
|
||||
result.Add(item.Key, ParsePosData(item.Value.ToString()));
|
||||
break;
|
||||
default:
|
||||
result.Add(item.Key, item.Value.ToString());
|
||||
break;
|
||||
posDataJObj = JObject.Parse(posDataStr);
|
||||
}
|
||||
catch
|
||||
{
|
||||
result.TryAdd(string.Empty, posDataStr);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
catch
|
||||
if (posDataJObj is null)
|
||||
return result;
|
||||
|
||||
|
||||
foreach (var item in posDataJObj)
|
||||
{
|
||||
result.Add(string.Empty, posData);
|
||||
|
||||
switch (item.Value.Type)
|
||||
{
|
||||
case JTokenType.Array:
|
||||
var items = item.Value.AsEnumerable().ToList();
|
||||
for (var i = 0; i < items.Count; i++)
|
||||
{
|
||||
result.TryAdd($"{item.Key}[{i}]", ParsePosData(items[i]));
|
||||
}
|
||||
break;
|
||||
case JTokenType.Object:
|
||||
result.TryAdd(item.Key, ParsePosData(item.Value));
|
||||
break;
|
||||
default:
|
||||
result.TryAdd(item.Key, item.Value.ToString());
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -196,9 +196,9 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
if (entity.Metadata.BuyerEmail != null)
|
||||
{
|
||||
if (!EmailValidator.IsEmail(entity.Metadata.BuyerEmail))
|
||||
if (!EmailValidator.IsEmail(entity.Metadata.BuyerEmail.AsString()))
|
||||
throw new BitpayHttpException(400, "Invalid email");
|
||||
entity.RefundMail = entity.Metadata.BuyerEmail;
|
||||
entity.RefundMail = entity.Metadata.BuyerEmail.AsString();
|
||||
}
|
||||
entity.Status = InvoiceStatusLegacy.New;
|
||||
HashSet<CurrencyPair> currencyPairsToFetch = new HashSet<CurrencyPair>();
|
||||
|
@ -29,7 +29,10 @@ namespace BTCPayServer.Controllers
|
||||
var usersQuery = _UserManager.Users;
|
||||
if (!string.IsNullOrWhiteSpace(model.SearchTerm))
|
||||
{
|
||||
#pragma warning disable CA1307 // Specify StringComparison
|
||||
// This is Entity query, can't pass string comparison without crashing
|
||||
usersQuery = usersQuery.Where(u => u.Email.Contains(model.SearchTerm));
|
||||
#pragma warning restore CA1307 // Specify StringComparison
|
||||
}
|
||||
|
||||
if (sortOrder != null)
|
||||
|
@ -34,6 +34,7 @@ using NBitpayClient;
|
||||
using NBXplorer.DerivationStrategy;
|
||||
using NBXplorer.Models;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
@ -427,6 +428,42 @@ namespace BTCPayServer
|
||||
{
|
||||
return ctx.Items.TryGet("BTCPAY.STOREDATA") as StoreData;
|
||||
}
|
||||
|
||||
|
||||
// https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Linq/JToken.cs#L1235
|
||||
private static readonly JTokenType[] StringTypes = new[] { JTokenType.Date, JTokenType.Integer, JTokenType.Float, JTokenType.String, JTokenType.Comment, JTokenType.Raw, JTokenType.Boolean, JTokenType.Bytes, JTokenType.Guid, JTokenType.TimeSpan, JTokenType.Uri };
|
||||
public static string AsString(this JToken tok)
|
||||
{
|
||||
if (tok is JValue v && StringTypes.Contains(v.Type))
|
||||
{
|
||||
if (v.Value is null)
|
||||
return null;
|
||||
|
||||
if (v.Value is byte[] bytes)
|
||||
return Convert.ToBase64String(bytes);
|
||||
if (v.Value is BigInteger bigint)
|
||||
return bigint.ToString(CultureInfo.InvariantCulture);
|
||||
return Convert.ToString(v.Value, CultureInfo.InvariantCulture);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Linq/JToken.cs#L1108
|
||||
private static readonly JTokenType[] NumberTypes = new[] { JTokenType.Integer, JTokenType.Float, JTokenType.String, JTokenType.Comment, JTokenType.Raw, JTokenType.Boolean };
|
||||
public static decimal? AsDecimal(this JToken tok)
|
||||
{
|
||||
if (tok is JValue v && NumberTypes.Contains(v.Type))
|
||||
{
|
||||
if (v.Value is null)
|
||||
return null;
|
||||
if (v.Value is BigInteger bigint)
|
||||
return (decimal)bigint;
|
||||
return Convert.ToDecimal(v.Value, CultureInfo.InvariantCulture);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void SetStoreData(this HttpContext ctx, StoreData storeData)
|
||||
{
|
||||
ctx.Items["BTCPAY.STOREDATA"] = storeData;
|
||||
|
@ -35,7 +35,7 @@ namespace BTCPayServer
|
||||
{
|
||||
var negative = sats < 0;
|
||||
var amt = sats.ToString(CultureInfo.InvariantCulture)
|
||||
.Replace("-", "")
|
||||
.Replace("-", "", StringComparison.OrdinalIgnoreCase)
|
||||
.PadLeft(divisibility, '0');
|
||||
amt = amt.Length == divisibility ? $"0.{amt}" : amt.Insert(amt.Length - divisibility, ".");
|
||||
return decimal.Parse($"{(negative? "-": string.Empty)}{amt}", CultureInfo.InvariantCulture);
|
||||
|
@ -104,8 +104,8 @@ namespace BTCPayServer.HostedServices
|
||||
return;
|
||||
}
|
||||
|
||||
if ((!string.IsNullOrEmpty(invoiceEvent.Invoice.Metadata.ItemCode) ||
|
||||
AppService.TryParsePosCartItems(invoiceEvent.Invoice.Metadata.PosData, out cartItems)))
|
||||
if ((!string.IsNullOrEmpty(invoiceEvent.Invoice.Metadata.ItemCode.AsString()) ||
|
||||
AppService.TryParsePosCartItems(invoiceEvent.Invoice.Metadata.PosData.AsString(), out cartItems)))
|
||||
{
|
||||
var appIds = AppService.GetAppInternalTags(invoiceEvent.Invoice);
|
||||
|
||||
@ -115,9 +115,9 @@ namespace BTCPayServer.HostedServices
|
||||
}
|
||||
|
||||
var items = cartItems ?? new Dictionary<string, int>();
|
||||
if (!string.IsNullOrEmpty(invoiceEvent.Invoice.Metadata.ItemCode))
|
||||
if (!string.IsNullOrEmpty(invoiceEvent.Invoice.Metadata.ItemCode.AsString()))
|
||||
{
|
||||
items.TryAdd(invoiceEvent.Invoice.Metadata.ItemCode, 1);
|
||||
items.TryAdd(invoiceEvent.Invoice.Metadata.ItemCode.AsString(), 1);
|
||||
}
|
||||
|
||||
_eventAggregator.Publish(new UpdateAppInventory()
|
||||
|
@ -89,9 +89,9 @@ namespace BTCPayServer.HostedServices
|
||||
textSearch.Add(invoice.Id);
|
||||
textSearch.Add(invoice.InvoiceTime.ToString(CultureInfo.InvariantCulture));
|
||||
textSearch.Add(invoice.Price.ToString(CultureInfo.InvariantCulture));
|
||||
textSearch.Add(invoice.Metadata.OrderId);
|
||||
textSearch.Add(invoice.Metadata.OrderId.AsString());
|
||||
textSearch.Add(invoice.StoreId);
|
||||
textSearch.Add(invoice.Metadata.BuyerEmail);
|
||||
textSearch.Add(invoice.Metadata.BuyerEmail.AsString());
|
||||
//
|
||||
textSearch.Add(invoice.RefundMail);
|
||||
// TODO: Are there more things to cache? PaymentData?
|
||||
|
@ -84,8 +84,8 @@ namespace BTCPayServer.Payments.Lightning
|
||||
|
||||
string description = storeBlob.LightningDescriptionTemplate;
|
||||
description = description.Replace("{StoreName}", store.StoreName ?? "", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("{ItemDescription}", invoice.Metadata.ItemDesc ?? "", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("{OrderId}", invoice.Metadata.OrderId ?? "", StringComparison.OrdinalIgnoreCase);
|
||||
.Replace("{ItemDescription}", invoice.Metadata.ItemDesc.AsString() ?? "", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("{OrderId}", invoice.Metadata.OrderId.AsString() ?? "", StringComparison.OrdinalIgnoreCase);
|
||||
using (var cts = new CancellationTokenSource(LIGHTNING_TIMEOUT))
|
||||
{
|
||||
try
|
||||
|
@ -28,7 +28,7 @@ namespace BTCPayServer.Plugins
|
||||
|
||||
public static bool IsExceptionByPlugin(Exception exception)
|
||||
{
|
||||
return _pluginAssemblies.Any(assembly => assembly.FullName.Contains(exception.Source));
|
||||
return _pluginAssemblies.Any(assembly => assembly.FullName.Contains(exception.Source, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
public static IMvcBuilder AddPlugins(this IMvcBuilder mvcBuilder, IServiceCollection serviceCollection,
|
||||
IConfiguration config, ILoggerFactory loggerFactory)
|
||||
|
@ -98,8 +98,8 @@ namespace BTCPayServer.Services.Apps
|
||||
var currentPayments = GetContributionsByPaymentMethodId(settings.TargetCurrency, completeInvoices, !settings.EnforceTargetAmount);
|
||||
|
||||
var perkCount = paidInvoices
|
||||
.Where(entity => !string.IsNullOrEmpty(entity.Metadata.ItemCode))
|
||||
.GroupBy(entity => entity.Metadata.ItemCode)
|
||||
.Where(entity => !string.IsNullOrEmpty(entity.Metadata.ItemCode.AsString()))
|
||||
.GroupBy(entity => entity.Metadata.ItemCode.AsString())
|
||||
.ToDictionary(entities => entities.Key, entities => entities.Count());
|
||||
|
||||
var perks = Parse(settings.PerksTemplate, settings.TargetCurrency);
|
||||
|
@ -87,7 +87,7 @@ namespace BTCPayServer.Services.Invoices.Export
|
||||
// while looking just at export you could sum Paid and assume merchant "received payments"
|
||||
NetworkFee = payment.NetworkFee.ToString(CultureInfo.InvariantCulture),
|
||||
InvoiceDue = Math.Round(invoiceDue, currency.NumberDecimalDigits),
|
||||
OrderId = invoice.Metadata.OrderId ?? string.Empty,
|
||||
OrderId = invoice.Metadata.OrderId.AsString() ?? string.Empty,
|
||||
StoreId = invoice.StoreId,
|
||||
InvoiceId = invoice.Id,
|
||||
InvoiceCreatedDate = invoice.InvoiceTime.UtcDateTime,
|
||||
@ -98,11 +98,11 @@ namespace BTCPayServer.Services.Invoices.Export
|
||||
InvoiceStatus = invoice.StatusString,
|
||||
InvoiceExceptionStatus = invoice.ExceptionStatusString,
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
InvoiceItemCode = invoice.Metadata.ItemCode,
|
||||
InvoiceItemDesc = invoice.Metadata.ItemDesc,
|
||||
InvoiceItemCode = invoice.Metadata.ItemCode.AsString(),
|
||||
InvoiceItemDesc = invoice.Metadata.ItemDesc.AsString(),
|
||||
InvoicePrice = invoice.Price,
|
||||
InvoiceCurrency = invoice.Currency,
|
||||
BuyerEmail = invoice.Metadata.BuyerEmail
|
||||
BuyerEmail = invoice.Metadata.BuyerEmail.AsString()
|
||||
};
|
||||
|
||||
exportList.Add(target);
|
||||
|
@ -30,67 +30,39 @@ namespace BTCPayServer.Services.Invoices
|
||||
seria.ContractResolver = new CamelCasePropertyNamesContractResolver();
|
||||
MetadataSerializer = seria;
|
||||
}
|
||||
public string OrderId { get; set; }
|
||||
public JToken OrderId { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerName")]
|
||||
public string BuyerName { get; set; }
|
||||
public JToken BuyerName { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerEmail")]
|
||||
public string BuyerEmail { get; set; }
|
||||
public JToken BuyerEmail { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerCountry")]
|
||||
public string BuyerCountry { get; set; }
|
||||
public JToken BuyerCountry { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerZip")]
|
||||
public string BuyerZip { get; set; }
|
||||
public JToken BuyerZip { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerState")]
|
||||
public string BuyerState { get; set; }
|
||||
public JToken BuyerState { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerCity")]
|
||||
public string BuyerCity { get; set; }
|
||||
public JToken BuyerCity { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerAddress2")]
|
||||
public string BuyerAddress2 { get; set; }
|
||||
public JToken BuyerAddress2 { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerAddress1")]
|
||||
public string BuyerAddress1 { get; set; }
|
||||
public JToken BuyerAddress1 { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "buyerPhone")]
|
||||
public string BuyerPhone { get; set; }
|
||||
public JToken BuyerPhone { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "itemDesc")]
|
||||
public string ItemDesc { get; set; }
|
||||
public JToken ItemDesc { get; set; }
|
||||
[JsonProperty(PropertyName = "itemCode")]
|
||||
public string ItemCode { get; set; }
|
||||
public JToken ItemCode { get; set; }
|
||||
[JsonProperty(PropertyName = "physical")]
|
||||
public bool? Physical { get; set; }
|
||||
public JToken Physical { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "taxIncluded", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
public decimal? TaxIncluded { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string PosData
|
||||
{
|
||||
get
|
||||
{
|
||||
return PosRawData?.ToString();
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
PosRawData = JValue.CreateNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
PosRawData = JToken.Parse(value);
|
||||
}
|
||||
catch (Exception )
|
||||
{
|
||||
PosRawData = JToken.FromObject(value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public JToken TaxIncluded { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "posData")]
|
||||
public JToken PosRawData { get; set; }
|
||||
public JToken PosData { get; set; }
|
||||
[JsonExtensionData]
|
||||
public IDictionary<string, JToken> AdditionalData { get; set; }
|
||||
|
||||
@ -283,9 +255,11 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
private Uri FillPlaceholdersUri(string v)
|
||||
{
|
||||
var uriStr = (v ?? string.Empty).Replace("{OrderId}", System.Web.HttpUtility.UrlEncode(Metadata.OrderId) ?? "", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("{InvoiceId}", System.Web.HttpUtility.UrlEncode(Id) ?? "", StringComparison.OrdinalIgnoreCase);
|
||||
if (Uri.TryCreate(uriStr, UriKind.Absolute, out var uri) && (uri.Scheme == "http" || uri.Scheme == "https"))
|
||||
v ??= string.Empty;
|
||||
string orderId = Metadata.OrderId.AsString();
|
||||
v = v.Replace("{OrderId}", orderId is null ? string.Empty : System.Web.HttpUtility.UrlEncode(orderId), StringComparison.OrdinalIgnoreCase);
|
||||
v = v.Replace("{InvoiceId}", System.Web.HttpUtility.UrlEncode(Id), StringComparison.OrdinalIgnoreCase);
|
||||
if (Uri.TryCreate(v, UriKind.Absolute, out var uri) && (uri.Scheme == "http" || uri.Scheme == "https"))
|
||||
return uri;
|
||||
return null;
|
||||
}
|
||||
@ -330,8 +304,8 @@ namespace BTCPayServer.Services.Invoices
|
||||
{
|
||||
Id = Id,
|
||||
StoreId = StoreId,
|
||||
OrderId = Metadata.OrderId,
|
||||
PosData = Metadata.PosData,
|
||||
OrderId = Metadata.OrderId.AsString(),
|
||||
PosData = Metadata.PosData.AsString(),
|
||||
CurrentTime = DateTimeOffset.UtcNow,
|
||||
InvoiceTime = InvoiceTime,
|
||||
ExpirationTime = ExpirationTime,
|
||||
@ -449,9 +423,9 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
//dto.AmountPaid dto.MinerFees & dto.TransactionCurrency are not supported by btcpayserver as we have multi currency payment support per invoice
|
||||
|
||||
dto.ItemCode = Metadata.ItemCode;
|
||||
dto.ItemDesc = Metadata.ItemDesc;
|
||||
dto.TaxIncluded = Metadata.TaxIncluded ?? 0m;
|
||||
dto.ItemCode = Metadata.ItemCode.AsString();
|
||||
dto.ItemDesc = Metadata.ItemDesc.AsString();
|
||||
dto.TaxIncluded = Metadata.TaxIncluded.AsDecimal() ?? 0m;
|
||||
dto.Price = Price;
|
||||
dto.Currency = Currency;
|
||||
dto.Buyer = new JObject();
|
||||
@ -463,7 +437,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
dto.Buyer.Add(new JProperty("postalCode", Metadata.BuyerZip));
|
||||
dto.Buyer.Add(new JProperty("country", Metadata.BuyerCountry));
|
||||
dto.Buyer.Add(new JProperty("phone", Metadata.BuyerPhone));
|
||||
dto.Buyer.Add(new JProperty("email", string.IsNullOrWhiteSpace(Metadata.BuyerEmail) ? RefundMail : Metadata.BuyerEmail));
|
||||
dto.Buyer.Add(new JProperty("email", string.IsNullOrWhiteSpace(Metadata.BuyerEmail.AsString()) ? RefundMail : Metadata.BuyerEmail));
|
||||
|
||||
dto.Token = Encoders.Base58.EncodeData(RandomUtils.GetBytes(16)); //No idea what it is useful for
|
||||
dto.Guid = Guid.NewGuid().ToString();
|
||||
|
@ -166,11 +166,11 @@ namespace BTCPayServer.Services.Invoices
|
||||
Id = invoice.Id,
|
||||
Created = invoice.InvoiceTime,
|
||||
Blob = ToBytes(invoice, null),
|
||||
OrderId = invoice.Metadata.OrderId,
|
||||
OrderId = invoice.Metadata.OrderId.AsString(),
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
Status = invoice.StatusString,
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
ItemCode = invoice.Metadata.ItemCode,
|
||||
ItemCode = invoice.Metadata.ItemCode.AsString(),
|
||||
CustomerEmail = invoice.RefundMail,
|
||||
Archived = false
|
||||
};
|
||||
@ -207,9 +207,9 @@ namespace BTCPayServer.Services.Invoices
|
||||
textSearch.Add(invoice.Id);
|
||||
textSearch.Add(invoice.InvoiceTime.ToString(CultureInfo.InvariantCulture));
|
||||
textSearch.Add(invoice.Price.ToString(CultureInfo.InvariantCulture));
|
||||
textSearch.Add(invoice.Metadata.OrderId);
|
||||
textSearch.Add(invoice.Metadata.OrderId.AsString());
|
||||
textSearch.Add(invoice.StoreId);
|
||||
textSearch.Add(invoice.Metadata.BuyerEmail);
|
||||
textSearch.Add(invoice.Metadata.BuyerEmail.AsString());
|
||||
AddToTextSearch(context, invoiceData, textSearch.ToArray());
|
||||
|
||||
await context.SaveChangesAsync().ConfigureAwait(false);
|
||||
@ -597,7 +597,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
{
|
||||
entity.Events = invoice.Events.OrderBy(c => c.Timestamp).ToList();
|
||||
}
|
||||
if (!string.IsNullOrEmpty(entity.RefundMail) && string.IsNullOrEmpty(entity.Metadata.BuyerEmail))
|
||||
if (!string.IsNullOrEmpty(entity.RefundMail) && string.IsNullOrEmpty(entity.Metadata.BuyerEmail.AsString()))
|
||||
{
|
||||
entity.Metadata.BuyerEmail = entity.RefundMail;
|
||||
}
|
||||
|
@ -150,14 +150,14 @@
|
||||
{
|
||||
<h3 class="mb-3">Product Information</h3>
|
||||
<table class="table table-sm table-responsive-md removetopborder">
|
||||
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemCode))
|
||||
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemCode.AsString()))
|
||||
{
|
||||
<tr>
|
||||
<th>Item code</th>
|
||||
<td>@Model.TypedMetadata.ItemCode</td>
|
||||
</tr>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemDesc))
|
||||
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemDesc.AsString()))
|
||||
{
|
||||
<tr>
|
||||
<th>Item Description</th>
|
||||
@ -183,14 +183,14 @@
|
||||
<div class="col-md-6 mb-4">
|
||||
<h3 class="mb-3">Product information</h3>
|
||||
<table class="table table-sm table-responsive-md removetopborder">
|
||||
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemCode))
|
||||
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemCode.AsString()))
|
||||
{
|
||||
<tr>
|
||||
<th>Item code</th>
|
||||
<td>@Model.TypedMetadata.ItemCode</td>
|
||||
</tr>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemDesc))
|
||||
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemDesc.AsString()))
|
||||
{
|
||||
<tr>
|
||||
<th>Item Description</th>
|
||||
|
Reference in New Issue
Block a user