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