Open ignaciocunado opened 2 months ago
I know this is a pretty specific use case, but we wanted to see if maybe anybody else had come across this scenario and if there was a possible fix for this.
I will submit a PR if I find a possible and suitable implementation.
Hi @ignaciocunado ,
Thanks for sharing this. The use case makes sense. People change their minds all the time, especially subscription customers ;).
Problem: If we use the current functionality, a new cycle will start when upgrading their subscription count. This will cause the scheduled order item containing the
next_plan
to be removed.
Were you able to confirm the behavior you're describing? The next_plan
is actually stored on the Subscription model, not on the scheduled OrderItem. If possible, could you provide a failing test?
/** @test */
public function swapPlanThenCount()
{
$user = $this->getUserWithZeroBalance();
$subscription = $this->getSubscriptionForUser($user);
// Swap to new plan
$subscription = $subscription->swapNextCycle('weekly-20-1')->fresh();
$this->assertEquals('monthly-10-1', $subscription->plan);
$this->assertEquals('weekly-20-1', $subscription->next_plan);
$cycle_should_have_started_at = now()->subWeeks(2);
$cycle_should_end_at = $cycle_should_have_started_at->copy()->addMonth();
$new_order_item = $subscription->scheduledOrderItem;
$this->assertCarbon($cycle_should_end_at, $new_order_item->process_at, 1); // based on previous plan's cycle
$this->assertEquals(2200, $new_order_item->total);
$this->assertEquals(200, $new_order_item->tax);
$this->assertEquals('Twice as expensive monthly subscription', $new_order_item->description);
Event::assertNotDispatched(SubscriptionPlanSwapped::class);
// Increase subscription count
$subscription = $subscription->incrementQuantity(1, false)->fresh();
$this->assertEquals(2, $subscription->quantity);
$new_order_item = $subscription->scheduledOrderItem;
$this->assertNotNull($subscription->next_plan);
$this->assertEquals($subscription->plan, 'monthly-10-1'); // Plan has not been updated
$this->assertEquals(4400, $new_order_item->total); // Plan not updated on the scheduled order item (twice as before as quantity is doubled). First assertion that fails
$this->assertEquals(400, $new_order_item->tax); // Same here
$this->assertEquals('Twice as expensive monthly subscription', $new_order_item->description);
}
Time: 00:01.175, Memory: 52.50 MB
There was 1 failure:
1) Laravel\Cashier\Tests\SwapSubscriptionPlanTest::swapPlanThenCount
Failed asserting that 2200 matches expected 4400.
/Users/ignacunado/Herd/laravel-cashier-mollie/tests/SwapSubscriptionPlanTest.php:332
FAILURES!
Tests: 232, Assertions: 1778, Failures: 1.
Script ./vendor/bin/phpunit tests handling the test event returned with error code 1
This test shows that when updating the plan at the end of the cycle and then using incrementQuantity
, a new orderItem is scheduled and processed with the same plan and the new quantity. Even though the next_plan
attribute on the subscription model remains set, there are no scheduled order items for this new plan, so I guess it will never be changed?
So I guess that there are (at least) two options here with the current functionality.
restartCycleWithModifications
is called which schedules a new order item at now()
is called and the subscription object has the next_plan
attribute set (meaning the plan should change for the next cycle), then we change the plan on this order item (immediately). This has flaws as a user could terminate a yearly subscription before the end of the cycle and get their money back (set next plan to a weekly subscription, change quantity and then cancel at the end of the week).restartCycleWithModifications
is called which schedules a new order item at now()
is called and the subscription object has the next_plan
attribute set (meaning the plan should change for the next cycle), then we change the plan for the next order item. This is also kind of bad. Say a user has a yearly subscription ending in February, and have set it to downgrade to a monthly one which will take effect in February. In January the user decides to increment their subscription quantity by 1. Then they would need to wait a whole other year to swap to a monthly subscription as the cycle restarts.Again, pretty specific scenarios but they can happen.
Whilst implementing functionality for users to be able to change their subscription plan/quantity, we came across the following scenario:
swapNextCycle
, their subscription attributenext_plan
is set for a monthly subscription and a new order item is scheduled for the end of their cycle.Problem: If we use the current functionality, a new cycle will start when upgrading their subscription count. This will cause the scheduled order item containing the
next_plan
to be removed.When should the plan downgrade happen?