Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,19 @@ public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, Jso

if (seconds >= 0)
{
return DateTimeUtils.UnixEpoch.AddSeconds(seconds);
// As of the 2025-10-29.clover release, there is a bug in the Terminal API
// where the last_seen_at field is returned as milliseconds rather than seconds.
// Since we don't know when/how a fix will be put in to change that behavior,
// we make a heuristic guess to determine if a timestamp is in milliseconds or seconds.
// This check will work until the year ~5138.
if (seconds > 5000000000L)
{
return DateTimeUtils.UnixEpoch.AddMilliseconds(seconds);
}
else
{
return DateTimeUtils.UnixEpoch.AddSeconds(seconds);
}
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,19 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist

if (seconds >= 0)
{
return DateTimeUtils.UnixEpoch.AddSeconds(seconds);
// As of the 2025-10-29.clover release, there is a bug in the Terminal API
// where the last_seen_at field is returned as milliseconds rather than seconds.
// Since we don't know when/how a fix will be put in to change that behavior,
// we make a heuristic guess to determine if a timestamp is in milliseconds or seconds.
// This check will work until the year ~5138.
if (seconds > 5000000000L)
{
return DateTimeUtils.UnixEpoch.AddMilliseconds(seconds);
}
else
{
return DateTimeUtils.UnixEpoch.AddSeconds(seconds);
}
}
else
{
Expand Down
140 changes: 139 additions & 1 deletion src/StripeTests/Entities/Terminal/Readers/ReaderTest.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
namespace StripeTests.Terminal
{
using System;
using Newtonsoft.Json;
using Stripe.Terminal;
using Xunit;
#if NET6_0_OR_GREATER
using System.Text.Json;
#endif

public class ReaderTest : BaseStripeTest
{
public ReaderTest(StripeMockFixture stripeMockFixture)
: base(stripeMockFixture)
: base(stripeMockFixture)
{
}

Expand All @@ -21,5 +25,139 @@ public void Deserialize()
Assert.NotNull(reader.Id);
Assert.Equal("terminal.reader", reader.Object);
}

[Fact]
public void DeserializeWithLastSeenAtInMilliseconds()
{
// This test verifies that timestamps in milliseconds are handled correctly
// Addresses GitHub issue #3253 where last_seen_at comes as milliseconds instead of seconds

// Unix timestamp in milliseconds for November 13, 2023 18:26:40 UTC
// 1699900000000 milliseconds = 1699900000 seconds
long timestampMillis = 1699900000000L;

string json = $@"{{
""id"": ""tmr_test123"",
""object"": ""terminal.reader"",
""device_type"": ""simulated_stripe_s700"",
""label"": ""Test Reader"",
""last_seen_at"": {timestampMillis},
""livemode"": false,
""location"": ""tml_test456"",
""metadata"": {{}},
""serial_number"": ""123456789"",
""status"": ""online""
}}";

// This should now work correctly with the updated converter
var reader = JsonConvert.DeserializeObject<Reader>(json);

Assert.NotNull(reader);
Assert.NotNull(reader.LastSeenAt);

// Verify the timestamp was parsed correctly
// Expected: November 13, 2023 18:26:40 UTC (timestamp 1699900000)
var expectedDate = new DateTime(2023, 11, 13, 18, 26, 40, DateTimeKind.Utc);
Assert.Equal(expectedDate, reader.LastSeenAt.Value);
}

[Fact]
public void DeserializeWithLastSeenAtInSeconds()
{
// This test verifies that regular timestamps in seconds still work correctly

// Unix timestamp in seconds for November 13, 2023 18:26:40 UTC
long timestampSeconds = 1699900000L;

string json = $@"{{
""id"": ""tmr_test456"",
""object"": ""terminal.reader"",
""device_type"": ""simulated_stripe_s700"",
""label"": ""Test Reader Seconds"",
""last_seen_at"": {timestampSeconds},
""livemode"": false,
""location"": ""tml_test789"",
""metadata"": {{}},
""serial_number"": ""987654321"",
""status"": ""online""
}}";

var reader = JsonConvert.DeserializeObject<Reader>(json);

Assert.NotNull(reader);
Assert.NotNull(reader.LastSeenAt);

// Verify the timestamp was parsed correctly
// Expected: November 13, 2023 18:26:40 UTC (timestamp 1699900000)
var expectedDate = new DateTime(2023, 11, 13, 18, 26, 40, DateTimeKind.Utc);
Assert.Equal(expectedDate, reader.LastSeenAt.Value);
}

#if NET6_0_OR_GREATER
[Fact]
public void DeserializeWithLastSeenAtInMilliseconds_SystemTextJson()
{
// This test verifies that timestamps in milliseconds are handled correctly in System.Text.Json
// Addresses GitHub issue #3253 where last_seen_at comes as milliseconds instead of seconds

// Unix timestamp in milliseconds for November 13, 2023 18:26:40 UTC
// 1699900000000 milliseconds = 1699900000 seconds
long timestampMillis = 1699900000000L;
string json = $@"{{
""id"": ""tmr_test123"",
""object"": ""terminal.reader"",
""device_type"": ""simulated_stripe_s700"",
""label"": ""Test Reader STJ"",
""last_seen_at"": {timestampMillis},
""livemode"": false,
""location"": ""tml_test456"",
""metadata"": {{}},
""serial_number"": ""123456789"",
""status"": ""online""
}}";

// This should now work correctly with the updated STJ converter
var reader = System.Text.Json.JsonSerializer.Deserialize<Reader>(json);

Assert.NotNull(reader);
Assert.NotNull(reader.LastSeenAt);

// Verify the timestamp was parsed correctly
// Expected: November 13, 2023 18:26:40 UTC (timestamp 1699900000)
var expectedDate = new DateTime(2023, 11, 13, 18, 26, 40, DateTimeKind.Utc);
Assert.Equal(expectedDate, reader.LastSeenAt.Value);
}

[Fact]
public void DeserializeWithLastSeenAtInSeconds_SystemTextJson()
{
// This test verifies that regular timestamps in seconds still work correctly in System.Text.Json

// Unix timestamp in seconds for November 13, 2023 18:26:40 UTC
long timestampSeconds = 1699900000L;
string json = $@"{{
""id"": ""tmr_test456"",
""object"": ""terminal.reader"",
""device_type"": ""simulated_stripe_s700"",
""label"": ""Test Reader Seconds STJ"",
""last_seen_at"": {timestampSeconds},
""livemode"": false,
""location"": ""tml_test789"",
""metadata"": {{}},
""serial_number"": ""987654321"",
""status"": ""online""
}}";

var reader = System.Text.Json.JsonSerializer.Deserialize<Reader>(json);

Assert.NotNull(reader);
Assert.NotNull(reader.LastSeenAt);

// Verify the timestamp was parsed correctly
// Expected: November 13, 2023 18:26:40 UTC (timestamp 1699900000)
var expectedDate = new DateTime(2023, 11, 13, 18, 26, 40, DateTimeKind.Utc);
Assert.Equal(expectedDate, reader.LastSeenAt.Value);
}
#endif
}
}
Loading