stripe-dotnet: Invalid timestamp: must be an integer Unix timestamp in the future.

When specifying a TrialEnd during subscription creation, I get an error:

Stripe.StripeException: Invalid timestamp: must be an integer Unix timestamp in the future.
   at Stripe.Infrastructure.Requestor.ExecuteRequest(HttpRequestMessage requestMessage)
   at Stripe.StripeSubscriptionService.Create(String customerId, String planId, StripeSubscriptionCreateOptions createOptions, StripeRequestOptions requestOptions)
   ...

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 23 (14 by maintainers)

Most upvoted comments

Thanks @xt0rted for the write up!

I think from @afollestad’s original request, if the use case is to request something ends in 10 minutes, it simply wont work the way it is now. I’m sort of surprised this hasn’t came up yet, but most dates are probably set to days out, not minutes. That’s my best guess.

On the Stripe side, they expect epoch time, so it really shouldn’t matter what the user passes. It is relative to UTC so that’s what it should be. I’ll fix this to use .ToUniversalTime() in the function. 👍

@mokumax False alert! This is not exactly a bug in the library, but the way you use it. When you create a subscription you can not pass billing_cycle_anchor: "now". That parameter can only be a timestamp for the future. If you want to start a subscription immediately, you would simply not pass billing_cycle_anchor at all (which is what you ended up doing.

BillingCycleAnchorNow is really only allowed for Updates and not Creation. We should still work on removing those options on creation as they are not supported!

.NET 4.6 added support for Unix time with the DateTimeOffset class as seen here https://referencesource.microsoft.com/#mscorlib/system/datetimeoffset.cs,625. It ensures the value it’s converting is UTC since Unix time is relative to UTC.

The DateTime class is really meant for arbitrary dates & times while DateTimeOffset is meant for points in time (relative to UTC). This is why DateTimeOffset always works with an offset/timezone and is the only one which has Unix time functions added to it. You can see the differences in how the two behave with the following code

var offset = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
(offset == offset.ToUniversalTime()).Dump(); // true
(offset.ToLocalTime() == offset).Dump(); // true
(offset.ToLocalTime() == offset.ToUniversalTime()).Dump(); // true

var date = new DateTime(1970, 1, 1, 0, 0, 0);
(date == date.ToUniversalTime()).Dump(); // false
(date.ToLocalTime() == date).Dump(); // false
(date.ToLocalTime() == date.ToUniversalTime()).Dump(); // false

(offset == date).Dump(); // false

The DateTime comparisons will have different results depending on the DateTimeKind you pass in. By default it’s Unspecified (using DateTime.Now or DateTime.UtcNow will use Local or Utc respectively). The point of that though is to show how there’s too much variance in how it functions while DateTimeOffset is going to give consistent results.

By the looks of it there is a bug in the current EpochTime class which can been seen with the following code

// both give different results
EpochTime.ConvertDateTimeToEpoch(DateTime.Now).Dump();
EpochTime.ConvertDateTimeToEpoch(DateTime.Now.ToUniversalTime()).Dump();

// both give the same result
new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().Dump();
new DateTimeOffset(DateTime.Now.ToUniversalTime()).ToUnixTimeSeconds().Dump();

oh interesting - I’ll do some more research here, specifically finding out how stripe’s other bindings handle this.

What would worry me about the second method, is I would be altering the value the user sent, even if it is to make it the value it should be. 😃

If this is the situation, DateTime.UtcNow.AddMinutes(5) should work.

If stripe.net altered the time, there could be situations where trials or other dates in the system are converted without the users knowledge. Does that make sense?