POS: Backwards-compatible price parsing (#5163)

* POS: Backwards-compatible price parsing

Fixes #5159 and a regression introduced in bbff9710bf: The price in posData needs to be parsed in a backwards-compatible manner, as the old format of price as an object exists in the invoice metadata.

* Test corner cases

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
This commit is contained in:
d11n 2023-07-11 08:32:01 +02:00 committed by GitHub
parent c777746b69
commit 1600dd4759
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 75 additions and 5 deletions

View file

@ -1145,6 +1145,37 @@ namespace BTCPayServer.Tests
Assert.Equal("000000161", m.OrderId);
}
[Fact]
public void CanParseOldPosAppData()
{
var data = new JObject()
{
["price"] = 1.64m
}.ToString();
Assert.Equal(1.64m, JsonConvert.DeserializeObject<PosAppCartItem>(data).Price);
data = new JObject()
{
["price"] = new JObject()
{
["value"] = 1.65m
}
}.ToString();
Assert.Equal(1.65m, JsonConvert.DeserializeObject<PosAppCartItem>(data).Price);
data = new JObject()
{
["price"] = new JObject()
{
["value"] = null
}
}.ToString();
Assert.Equal(0.0m, JsonConvert.DeserializeObject<PosAppCartItem>(data).Price);
var o = JObject.Parse(JsonConvert.SerializeObject(new PosAppCartItem() { Price = 1.356m }));
Assert.Equal(1.356m, o["price"].Value<decimal>());
}
[Fact]
public void CanParseCurrencyValue()
{

View file

@ -1,5 +1,8 @@
using System;
using System.Globalization;
using BTCPayServer.Plugins.PointOfSale.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Services.Invoices;
@ -33,6 +36,7 @@ public class PosAppCartItem
public string Id { get; set; }
[JsonProperty(PropertyName = "price")]
[JsonConverter(typeof(PosAppCartItemPriceJsonConverter))]
public decimal Price { get; set; }
[JsonProperty(PropertyName = "title")]
@ -48,11 +52,46 @@ public class PosAppCartItem
public string Image { get; set; }
}
public class PosAppCartItemPrice
public class PosAppCartItemPriceJsonConverter : JsonConverter
{
[JsonProperty(PropertyName = "formatted")]
public string Formatted { get; set; }
public override bool CanConvert(Type objectType)
{
return objectType == typeof(decimal) || objectType == typeof(object);
}
[JsonProperty(PropertyName = "type")]
public ViewPointOfSaleViewModel.ItemPriceType Type { get; set; }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
switch (token.Type)
{
case JTokenType.Float:
if (objectType == typeof(decimal))
return token.Value<decimal>();
throw new JsonSerializationException($"Unexpected object type: {objectType}");
case JTokenType.Integer:
case JTokenType.String:
if (objectType == typeof(decimal))
return decimal.Parse(token.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture);
throw new JsonSerializationException($"Unexpected object type: {objectType}");
case JTokenType.Null:
return null;
case JTokenType.Object:
return token.ToObject<JObject>()?["value"]?.Value<decimal?>();
default:
throw new JsonSerializationException($"Unexpected token type: {token.Type}");
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
switch (value)
{
case null:
break;
case decimal x:
writer.WriteValue(x.ToString(CultureInfo.InvariantCulture));
break;
}
}
}