Fix: Crash caused by very old point of sales invoices (#5283) (#5291)

This commit is contained in:
Nicolas Dorier 2023-09-05 15:32:49 +09:00 committed by GitHub
parent 79b2f1652b
commit 3b05de7f30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 87 additions and 39 deletions

View File

@ -23,6 +23,7 @@ using BTCPayServer.Payments.Bitcoin;
using BTCPayServer.Payments.Lightning;
using BTCPayServer.Rating;
using BTCPayServer.Services;
using BTCPayServer.Services.Apps;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Labels;
using BTCPayServer.Services.Rates;
@ -346,6 +347,65 @@ namespace BTCPayServer.Tests
Assert.True(Torrc.TryParse(input, out torrc));
Assert.Equal(expected, torrc.ToString());
}
[Fact]
public void CanParseCartItems()
{
Assert.True(AppService.TryParsePosCartItems(new JObject()
{
{"cart", new JArray()
{
new JObject()
{
{ "id", "ddd"},
{"price", 4},
{"count", 1}
}
}}
}, out var items));
Assert.Equal("ddd", items[0].Id);
Assert.Equal(1, items[0].Count);
Assert.Equal(4, items[0].Price);
// Using legacy parsing
Assert.True(AppService.TryParsePosCartItems(new JObject()
{
{"cart", new JArray()
{
new JObject()
{
{ "id", "ddd"},
{"price", new JObject()
{
{ "value", 8.49m }
}
},
{"count", 1}
}
}}
}, out items));
Assert.Equal("ddd", items[0].Id);
Assert.Equal(1, items[0].Count);
Assert.Equal(8.49m, items[0].Price);
Assert.False(AppService.TryParsePosCartItems(new JObject()
{
{"cart", new JArray()
{
new JObject()
{
{ "id", "ddd"},
{"price", new JObject()
{
{ "value", "nocrahs" }
}
},
{"count", 1}
}
}}
}, out items));
}
[Fact]
public void CanCalculateDust()
{

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
@ -402,52 +403,41 @@ namespace BTCPayServer.Services.Apps
}
}
#nullable enable
public static bool TryParsePosCartItems(JObject? posData, [MaybeNullWhen(false)] out Dictionary<string, int> cartItems)
{
cartItems = null;
if (posData is null)
return false;
if (!posData.TryGetValue("cart", out var cartObject))
return false;
if (cartObject is null)
return false;
cartItems = new();
foreach (var o in cartObject.OfType<JObject>())
{
var id = o.GetValue("id", StringComparison.InvariantCulture)?.ToString();
if (id != null)
{
var countStr = o.GetValue("count", StringComparison.InvariantCulture)?.ToString() ?? string.Empty;
if (int.TryParse(countStr, out var count))
{
cartItems.TryAdd(id, count);
}
}
}
return true;
}
public static bool TryParsePosCartItems(JObject? posData, [MaybeNullWhen(false)] out List<PosCartItem> cartItems)
{
cartItems = null;
if (posData is null)
return false;
if (!posData.TryGetValue("cart", out var cartObject))
if (!posData.TryGetValue("cart", out var cartObject) || cartObject is null)
return false;
cartItems = new List<PosCartItem>();
foreach (var o in cartObject.OfType<JObject>())
try
{
var id = o.GetValue("id", StringComparison.InvariantCulture)?.ToString();
if (id == null) continue;
var countStr = o.GetValue("count", StringComparison.InvariantCulture)?.ToString() ?? string.Empty;
var price = o.GetValue("price")?.Value<decimal>() ?? 0m;
if (int.TryParse(countStr, out var count))
cartItems = new List<PosCartItem>();
foreach (var o in cartObject.OfType<JObject>())
{
cartItems.Add(new PosCartItem { Id = id, Count = count, Price = price });
var id = o.GetValue("id", StringComparison.InvariantCulture)?.ToString();
if (id == null)
continue;
var countStr = o.GetValue("count", StringComparison.InvariantCulture)?.ToString() ?? string.Empty;
var price = o.GetValue("price") switch
{
JValue v => v.Value<decimal>(),
// Don't crash on legacy format
JObject v2 => v2["value"]?.Value<decimal>() ?? 0m,
_ => 0m
};
if (int.TryParse(countStr, out var count))
{
cartItems.Add(new PosCartItem { Id = id, Count = count, Price = price });
}
}
return true;
}
catch (FormatException)
{
return false;
}
return true;
}
public async Task SetDefaultSettings(AppData appData, string defaultCurrency)

View File

@ -12,7 +12,6 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Newtonsoft.Json;
using Org.BouncyCastle.Crypto.Signers;
namespace BTCPayServer.Services.Reporting;
@ -78,10 +77,9 @@ public class ProductsReportProvider : ReportProvider
}
else
{
var posData = i.Metadata?.PosData?.ToObject<PosAppData>();
if (posData?.Cart is { } cart)
if (AppService.TryParsePosCartItems(i.Metadata?.PosData, out var items))
{
foreach (var item in cart)
foreach (var item in items)
{
var copy = values.ToList();
copy.Add(item.Id);