Subscription prorations with proration_date in the past

1,846 views
Skip to first unread message

Volodymyr Khoroz

unread,
Mar 5, 2016, 11:14:32 AM3/5/16
to Stripe API Discussion
Hello Stripe Team,

I am planning to use you awesome API for billing my customers.
I have created some code snippets using subscriptions and would like to allow upgrade/downgrade as well using a proration feature.

A peculiarity of my billing workflow is that when a customer upgrades from plan A to plan B in the middle of a billing period - they should be billed a full price difference between these two plans.
For example, if on May 1 a user subscribed to plan A priced at $10 and on May 15 they upgrade to a plan B priced at $25 I would like to bill them additional $15.

I found it very convenient to use a prorate=true and set propation_date to May 1 (i.e. a equal to a current period's current_period_start field).
This is an example POST request from logs:
{
    prorate: "True"
    proration_date: "1457191725"
    plan: "silver"
}

However, when I apply this - I get an error "InvalidRequestError: Request req_81kxHrGNZxH8qz: Cannot specify proration date before the current subscription started".

I found a similar error it this topic: https://groups.google.com/a/lists.stripe.com/forum/#!topic/api-discuss/0bC8Il3-rbI but it doesn't contain a solution for my problem.

Could you let me know if you are planning to allow proration_date in the past in the upcoming releases or am I doing anything wrong with it ?

Volodymyr Khoroz

unread,
Mar 5, 2016, 12:03:45 PM3/5/16
to Stripe API Discussion
A small debugging information:

Before an upgrade my subscriptions object was looking like this (I left only fields of interest):

{
  "current_period_end": 1459870125,
  "current_period_start": 1457191725,
  "object": "subscription",
  "plan": {
    "created": 1454018086,
    "plan": "base"
  },
  "start": 1457191974,
  "status": "active",
 }

When I attempted to upgrade with this object (proration_date = current_period_start) - it failed:


{
    "prorate": "True"
    "proration_date": "1457191725"
    "plan": "silver"
}

Later I noticed that in a subscription object start and current_period_start fields differ by a mere 4 minutes (probably due to my intensive testing of upgrades/downgrades).
So, I went further and tried to upgrade with the following request (proration_date = start):

{
    "prorate": "True"
    "proration_date": "1457191974"
    "plan": "silver"
}

Unsurprisingly, it succeeded, however after that a new subscription object contains a different start field value:

{
  "current_period_end": 1459870125,
  "current_period_start": 1457191725,
  "plan": {
    "created": 1454018086,
    "id": "silver",
  },
  "start": 1457196129,
}

Notice that now a delay between current_period_start and start fields has increased to well above 1 hour.

So, I tried to upgrade once again using a new start value:

{
    "prorate": "True"
    "proration_date": "1457196129"
    "plan": "silver"
}

As I expected, this time an invoice was made for only $29.95 instead of a full difference of $30.

So, this looks like a quite confusing limitation which doesn't allow to stagger several consequent upgrades in a scenario when I need to charge a full difference between plans each time.

Any ideas ?

Remi J.

unread,
Mar 11, 2016, 10:41:33 AM3/11/16
to api-d...@lists.stripe.com
Hey Volodymyr,

First off, really sorry about the delays in getting back to you here about his! I've attempted to reproduce this on my end to better understand what's not working for you but I can't reproduce the error myself. Let me go over what I did to make sure we're on the same page here!

First off, I have a customer in test mode that is subscribed to a $10 monthly plan since the 4th of March. When I look at his subscription, here's what I see (keeping only relevant fields:

{
    "id": "sub_XYZ",
    "object": "subscription",
    "current_period_end": 1459798783,
    "current_period_start": 1457120383,
    "customer": "cus_XYZ",
    "plan": {
        "id": "10usdmonth30dtrial",
        "object": "plan",
        "amount": 1000,
        "trial_period_days": 30
    },
    "quantity": 1,
    "start": 1454528383,
    "status": "active",
    "trial_end": 1457120383,
    "trial_start": 1454528383
}

Now, I have a separate plan with an id set to "id" (don't ask me why!) and it's a $100 monthly plan without a trial period. I want to update to that plan and pass my subscription's `current_period_end` to pretend I did the update on that date. To do this, I call the following code in PHP to preview the change:

$upcominginvoice = \Stripe\Invoice::upcoming(array(
    "customer" => $customer->id,
    "subscription" => $subscription->id,
    "subscription_prorate" => true,
    "subscription_plan" => "id",
    "subscription_proration_date" => $subscription->current_period_start
));

In return, I get an invoice object as expected with 3 line items. The first 2 are the proration items where one is the -$10 credit and the second one is the $100 amount due. Those would end up in a $90 owed by the customer as expected. The third one is the line item for the next billing cycle for $100. This is due to the fact that we don't charge the proration immediately as the plans are on the same billing cycle. This is covered in our documentation [1] but you can force the charge after the update by calling the Create Invoice API [2] and then the Pay Invoice API [3] otherwise to charge those $90.

Here's what the upcoming invoice returns:

Stripe\Invoice JSON: {
    "object": "invoice",
    "amount_due": 19000,
    "date": 1459798783,
    "lines": {
        "object": "list",
        "data": [
            {
                "object": "line_item",
                "amount": -1000,
                "currency": "usd",
                "description": "Unused time on 10usdmonth30dtrial after 11 Mar 2016",
                "period": {
                    "start": 1457120383,
                    "end": 1457120383
                },
                "proration": true,
                "quantity": 1,
                "type": "invoiceitem"
            },
            {
                "object": "line_item",
                "amount": 10000,
                "currency": "usd",
                "description": "Remaining time on name after 11 Mar 2016",
                "period": {
                    "start": 1457120383,
                    "end": 1457120383
                },
                "proration": true,
                "quantity": 1,
                "type": "invoiceitem"
            },
            {
                "object": "line_item",
                "amount": 10000,
                "currency": "usd",
                "period": {
                    "start": 1459798783,
                    "end": 1462390783
                },
                "proration": false,
                "quantity": 1,
                "subscription": null,
                "type": "subscription"
            }
        ],
    },
    "period_end": 1459798783,
    "period_start": 1457120383,
    "subscription_proration_date": 1457120383,
    "subtotal": 19000,
    "total": 19000,
}

This looks like the result you are looking for. I then went ahead and updated the subscription as planned which looked like this in PHP:

$subscription->plan = "id";
$subscription->prorate = true;
$subscription->proration_date = $subscription->current_period_start;
$newSubscription = $subscription->save();

This worked and my customer now has 2 pending invoice items, one for $100 and one for -$10 as expected which will be added to their upcoming invoice.

The issue, as I now realized you mentioned, is that after a first update like this one, the `start` for the subscription is set to the time the update happened. This means that on the next update, you can't pass the `current_period_start` as we enforce that `proration_date` is more recent than `start` itself.

I would agree that we should allow for proration_date to be after `current_period_start` but still before `start` in that situation. I've reported the issue internally so we can look into this and the potential side-effects this change could have though I'm not sure this is something we could support in the near future.

In the meantime, I'd recommend that you calculate proration yourself here. This is definitely a bummer but the way you're calculating proration is fairly specific and easier to calculate than most. Ultimately, you just want to charge the customer for the new plan price minus the previous plan price. This would allow you to attempt to change that amount first and if the charge succeeds automatically update the subscription [4] and pass `prorate: false` to avoid proration. This will also allow you to do specific calculations for customers changing late in the billing cycle. Otherwise, someone moving from the $10 plan to the $1000 one on the last day would end up paying $900 for less than 24 hours of usage on the new plan.

I hope this helps!
Remi



--
You received this message because you are subscribed to the Google Groups "Stripe API Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to api-discuss...@lists.stripe.com.
To post to this group, send email to api-d...@lists.stripe.com.
Visit this group at https://groups.google.com/a/lists.stripe.com/group/api-discuss/.

Volodymyr Khoroz

unread,
Mar 11, 2016, 5:19:00 PM3/11/16
to Stripe API Discussion
Thank you Remi,

You understood everything correct and your attempts to reproduce where exactly how I did it.
What I was asking for is described in the following paragraph copied from your response:


I would agree that we should allow for proration_date to be after `current_period_start` but still before `start` in that situation. I've reported the issue internally so we can look into this and the potential side-effects this change could have though I'm not sure this is something we could support in the near future.

I understand that it cannot be changed right away as it needs thorough verification to not break other things.
I would appreciate if you could keep me in the loop about if/when this is going to be a supported use case.

I would like to keep my custom implementation as neat as possible, thus manual price calculation is not something I plan to implement.
For now I will decide between 2 options:
- prevent a customer from upgrading more than once during a month.
- allow a small discount for the second upgrade imposed by current proration limitations.

For a case you've described with upgrading close to the end of period my customers have an option to make a full upgrade i.e. pay $1000 in which case an old subscription will end and a new subscription will be created for a full period with a new billing cycle.  In most cases my customers will use my services few times during a month, that's why neither hourly nor even daily proration duesn't suit my needs.

Best Regards,
Volodymyr Khoroz

Data Miner

unread,
Jul 27, 2016, 4:12:21 PM7/27/16
to Stripe API Discussion
Hi Remi,

Is there an update to this bug?

Here is the summary: If a customer upgrades several times during the month to progressively higher plans. The proration date can't be set to 'current_period_start' because the subscription['start'] date has moved forward to the last updated time of the subscription. 


Thanks
David 

Remi J.

unread,
Jul 27, 2016, 4:18:18 PM7/27/16
to api-d...@lists.stripe.com
Hey David,

We've been pushing a lot of updates to Subscriptions recently and fixed multiple bugs but haven't tackled this one just yet. I'll follow up once it's been fixed or once I have a firmer timeline for this.

All the best,
Remi

--

Andrew V

unread,
Aug 9, 2016, 5:46:36 AM8/9/16
to Stripe API Discussion
Remi,
I can replicate the issue.
Test Code

customer = stripe.Customer.retrieve(customer_id)
subscription = customer.subscriptions.data[0]
print 'A', subscription.id, subscription.plan.id, subscription.current_period_start, subscription.start

new_plan = stripe.Plan.retrieve("monthly_0300")
subscription.plan = new_plan.id
subscription.proration_date = subscription.current_period_start
subscription_json = subscription.save()
print 'B', subscription.id, subscription.plan.id, subscription.current_period_start, subscription.start

new_plan = stripe.Plan.retrieve("monthly_0650")
subscription.plan = new_plan.id
subscription.proration_date = subscription.current_period_start
subscription_json = subscription.save()
print 'C', subscription.id, subscription.plan.id, subscription.current_period_start, subscription.start

new_plan = stripe.Plan.retrieve("monthly_1400")
subscription.plan = new_plan.id
subscription.proration_date = int(subscription.current_period_start)
subscription_json = subscription.save()
print 'D', subscription.id, subscription.plan.id, subscription.current_period_start, subscription.start


OUTPUT 1
A sub_8xlNMHwifh9vQv monthly_0300 1470570815 1470588333
B sub_8xlNMHwifh9vQv monthly_0300 1470570815 1470588333
Cannot specify proration date before the current subscription started

OUTPUT 2
In the above code, if I replace 
subscription.proration_date = subscription.current_period_start
WITH
subscription.proration_date = max(int(subscription.current_period_start),int(subscription.start))

Then I  get this:
A sub_8xlNMHwifh9vQv monthly_0300 1470570815 1470588333
B sub_8xlNMHwifh9vQv monthly_0300 1470570815 1470588333
C sub_8xlNMHwifh9vQv monthly_0650 1470570815 1470590840
D sub_8xlNMHwifh9vQv monthly_1400 1470570815 1470590842

The subscription.start increases every time.

COMMENT
David is correct.
subscription.start  is AFTER  subscription.current_period_start
and that causes the error.

Remi J.

unread,
Aug 9, 2016, 5:48:00 AM8/9/16
to api-d...@lists.stripe.com
Hey Andrew,

Thank you for the extra information. We're still working on a fix but I'll update this thread as soon as we have a fix deployed or once I have a timeline to share!

All the best,
Remi

To unsubscribe from this group and stop receiving emails from it, send an email to api-discuss+unsubscribe@lists.stripe.com.

Stripe User

unread,
Nov 16, 2016, 3:21:39 PM11/16/16
to Stripe API Discussion
Hi Stipe guys,

How much longer do we have to wait for this bug fix? It has been 9 months since it was originally reported. 

We are running into this issue every week and is causing customer confusion and support calls. 

Thanks
David 

Zach

unread,
Nov 17, 2016, 5:49:04 PM11/17/16
to Stripe API Discussion
Hi Stripe,

+ 1 we have the same problem.

Is there a work around we can use for the time being?

Thank you,
Zach

Remi J.

unread,
Nov 17, 2016, 5:51:25 PM11/17/16
to api-d...@lists.stripe.com
Hey everyone,

We're working on a fix for this issue on our end to allow passing `proration_date` with a date before `start`, as long as it's still in the current billing period. We're planning to deploy this in the next few days and I'll follow up here as soon as it's live!

Cheers,
Remi

To unsubscribe from this group and stop receiving emails from it, send an email to api-discuss+unsubscribe@lists.stripe.com.

Zach

unread,
Nov 18, 2016, 1:49:48 PM11/18/16
to Stripe API Discussion
Hi Remi,

Awesome, thank you! 

Zach

Peter

unread,
Nov 29, 2016, 6:40:22 PM11/29/16
to Stripe API Discussion
Hey all,

I actually rolled out the change to enable this last week, but forgot to post an update in this thread. Sorry about that, and happy prorating!

Best,
Peter

Stripe User

unread,
Dec 2, 2016, 8:01:14 PM12/2/16
to Stripe API Discussion

Thanks Peter and Remi for the notification. We have a clarification question for you. 

Can you explain what the change was or what is the correct implementation:

Do we set the proration_date to subscription["current_period_start"] or to subscription["start"]

Thanks in advance

David

Peter

unread,
Dec 2, 2016, 8:10:16 PM12/2/16
to Stripe API Discussion
Specifically, this change will allow you to set the proration date before `subscription.start` (which is reset each time the subscription is updated), so long as the proration date is within `subscription.current_period_start` & `subscription.current_period_end`.

You could set the proration_date to whatever value you'd like within that range - though I think it sounds like you want to set it to `current_period_start`. (Since you could set the proration_date to `start` before this change, I assume that wasn't what you were trying to do.) For context, `start` is "date the most recent update to this subscription started.", and `current_period_start` is "Start of the current period that the subscription has been invoiced for". Which one you'd choose as the proration date depends on what you're trying to do (you could also choose another value entirely - it doesn't have to be just one of those two).

Hopefully that clears things up!

Best,
Peter

Stripe User

unread,
Dec 3, 2016, 9:09:22 AM12/3/16
to Stripe API Discussion

Great. that helps. Basically we want to turn proration off because in our case it does not make sense. If a user upgrades we want to charge them the full amount by setting the proration date to current_period_start. 

Thanks
David 
Reply all
Reply to author
Forward
0 new messages