Skip to content
This repository has been archived by the owner on May 26, 2023. It is now read-only.

New trial behavior #77

Merged
merged 45 commits into from
Sep 5, 2021
Merged

New trial behavior #77

merged 45 commits into from
Sep 5, 2021

Conversation

bpuig
Copy link
Owner

@bpuig bpuig commented Jun 15, 2021

Discarded #75, now this is a bit simpler.

New behavior

Trial modes

There are two available trial modes: inside or outside. This defines how the trial will be counted when renewal time
is due. Also renewal only affects subscription period, it can be renewed for as many periods as you want.

USAGE WILL NOT BE CLEARED when user has had trial time. This is what gives sense to both methods. If you need to clear, you can do it manually upon renewal.

When a new subscription to a plan is made:

If plan has trial

If plan has trial, subscriber does not have subscription but only a trial. Subscription period starts and ends at null and this is considered subscription is not made. Because in a real case scenario, when a subscriber has a trial it does not have a subscription yet, so the invoice period is made and charged after the trial has ended.

Renewal when trial is "inside"

If trial mode is inside; when trial ends and is renewed invoice period will have substracted the days of trial that have been used.

Example: 7 day trial in a 30 day subscription period.

  • User uses 3 days, likes the app them and renews the subscription.
    • Result: The next subscription renewal will be in 27 days.
  • User uses all 7 day trial. Forgets about the app and comes back a week later.
    • Result: The next subscription renewal will be in 23 days.

In summary: this is NOT a free trial. User always ends up paying the full price for full period.

Renewal when trial is "outside"

If trial mode is outside; when trial ends and is renewed, invoice period will start at the moment it's renewed.

Example: 7 day trial in a 30 day subscription period.

  • User uses 3 days, likes the app them and renews the subscription.
    • Result: The next subscription renewal will be in 30 days. User got 3 days for free.
  • User uses all 7 day trial. Forgets about the app and comes back a week later.
    • Result: The next subscription
      renewal will be in 30 days. User got 7 days for free.

In summary: this is IS a free trial. User does not pay for the trial period, but for the next subscription period.

If plan does not have trial

If plan does not have trial, subscriber has subscription. Because when a plan does not have trial, a new subscription activates a new invoicing period.

TODO:

  • Canceling trial
  • Sorting if trial should be considered part of the period or not
  • Renew multiple period
  • Don't penalize early buyers
  • Ending period information
  • Resettable period issue Ressetable period issue #85

@bpuig bpuig added enhancement New feature or request major Major change labels Jun 15, 2021
@bpuig bpuig added this to the v5.0.0 milestone Jun 15, 2021
@bpuig bpuig linked an issue Jun 15, 2021 that may be closed by this pull request
@bpuig bpuig added the work in progress This work is in progress. label Jun 15, 2021
@bpuig
Copy link
Owner Author

bpuig commented Jun 15, 2021

Now this makes sense. Care to give a thought @boryn ?

@bpuig
Copy link
Owner Author

bpuig commented Jun 15, 2021

This should also fix #74 because you can only have one thing, trial or subscription, they will not collide.

@boryn
Copy link
Contributor

boryn commented Jun 15, 2021

Could you prepare a beta v5 release? Would be much easier to give it a try

@bpuig
Copy link
Owner Author

bpuig commented Jun 16, 2021

Could you prepare a beta v5 release? Would be much easier to give it a try

I'm afraid that would be not possible, it would mean I'd have to merge this PR, you can use the branch this PR is on: https://github.com/bpuig/laravel-subby/tree/new_trial_behaviour

@boryn
Copy link
Contributor

boryn commented Jun 16, 2021

OK

@boryn
Copy link
Contributor

boryn commented Jun 17, 2021

Thank you for this implementation, seems to be much more logical. I share my first thoughts:

"Outside" mode

  • I strongly believe the user should have the right to use their free trial up to the initially set trial_ends_at ("7 days") and when they pay in the 3rd day of trial, the starts_at should start at trial_ends_at and not now(). We should not "punish" them when they quickly decide to pay for our app. (Just when they decide to cancel, we by default allow them to use the app up to the end of the period).
  • In this mode shouldn't the usage be cleared? They used what they got for free during the trial, and with renew() should start a new period with reset, fresh features. We need to consider as well 30 or even 90 days trials. And they may renew (actually start) subscription with a different plan.

"Inside" mode

  • I have made such a simulation:
    • Trial with 7 days created on '2021-05-17 05:03:09'
    • So the trial_ends_at is '2021-05-24 05:03:09'
    • Today (so after a month from creating the trial), I decided to renew. So, I used 7 days trial in May, and upon paying (renewal) should still have 30-7=23 days in my subscription.
    • So expected behaviour would be to get starts_at set to now(), let's say '2021-06-17 05:21:20' and ends_at 23 days from now(): '2021-07-10 05:21:20'
    • But I got: starts_at set to '2021-05-24 05:04:14' and ends_at set to '2021-06-24 05:04:14'.
  • In my understanding, the user just had a gap in usage of the app between '2021-05-24 05:03:09' and '2021-06-17 05:21:20'. His trial was over, could not use the features but when finally decides to renew, we should just discard this gap of no using the app and give them the remaining 23 days.
  • Here if they pay in the 3rd day of trial should not matter (as this is not a free trial). But if they pay after the usage gap, they should get the remaining days.

Shouldn't trial_mode field be copied from plan to plan_subscriptions as well? The plan definition can be changed in the meantime and we should use the definition from upon trial creation.

Free plan

  • With the plan of price 0, I think the subscription should be valid "forever". Now, upon creating a new subscription, I got it just valid until 17 of July. Maybe with price 0 we should just set starts_at and leave ends_at as null which would mean active indefinitely? With the free plan nobody would care to renew it every month...

Universal information about end of trial/subscription

  • It's usual to display to the user information when their subscription ends (either with trial or without). We can grab the information if it is trial or not (isOnTrial()) but I consider getting the end of the period very cumbersome with conditional logic (if on trial, get trial_ends_at, if not on trial get ends_at). That's why I'd recommend something more generic inside the PlanSubscription.php like:
public function getPeriodEndsAtAttribute()
    {
        if ($this->isOnTrial()) {
            return $this->trial_ends_at;
        }

        return $this->ends_at;
    }

Base automatically changed from v5 to main June 17, 2021 14:55
@bpuig
Copy link
Owner Author

bpuig commented Jun 17, 2021

Sorry for all that commits, I don't know what happened.

@boryn
Copy link
Contributor

boryn commented Jun 18, 2021

I strongly believe the user should have the right to use their free trial up to the initially set trial_ends_at ("7 days") and when they pay in the 3rd day of trial, the starts_at should start at trial_ends_at and not now(). We should not "punish" them when they quickly decide to pay for our app. (Just when they decide to cancel, we by default allow them to use the app up to the end of the period).

Done, now you get the remaining days! But trial still ends because I don't want to have trial and subscription at same time. Either one or the other.

So how does it work now? Upon renewal, trial_ends_at is set to starts_at at now()? And ends_at is prolonged by "30 days" from the previous value of trial_ends_at?

In this mode shouldn't the usage be cleared? They used what they got for free during the trial, and with renew() should start a new period with reset, fresh features. We need to consider as well 30 or even 90 days trials. And they may renew (actually start) subscription with a different plan.

I don't think so, because if you get 7 days trial, consume everything and renew, you'd get 1 month of usage for free because you used all the available features and then got renewed with a clear usage. Trial means you just get free time, not features.

I don't understand "Trial means you just get free time, not features". Companies often give 7-day trial for the "Pro" plan and allow users both to have time and to test the features for free. I rather see this scenario. And later when you subscribe, you get fresh set of features. Maybe it should be on an option then?

With the plan of price 0, I think the subscription should be valid "forever". Now, upon creating a new subscription, I got it just valid until 17 of July. Maybe with price 0 we should just set starts_at and leave ends_at as null which would mean active indefinitely? With the free plan nobody would care to renew it every month...

You should get a subscription that renews every given invoice period. For the amount of 0€, so free.

But should I (developer) care about refreshing it every month? Normally renewal comes "from outside" (payment). When we set a free 0€ subscription, it is not natural that we additionaly should care about prolonging it every month. It just should be infinite.

@boryn
Copy link
Contributor

boryn commented Jun 18, 2021

I think I found another issue(s).

Do you have an idea how to solve it? I think we need a check whether a date is inside a subscription period and if $usage->valid_until is inside the active subscription, we should probably use start of the subscription period? BUT we do not have this, it should be manually calculated: ends_at minus invoice period.

Buuuut... then feature usage is not decoupled from subscriptions... And sub period can be a year, and feature a month...

Maybe there should be a loop for incrementing $usage->valid_until until we land on "current" period before doing the real feature record? I mean something like this:

valid_until = valid_until + resettable period
if now() "inside" (valid_until + resettable period) then break the loop and record feature usage

(of course when valid_until is already in current period, there is no loop necessary)

@boryn
Copy link
Contributor

boryn commented Jun 18, 2021

Wait... You already solved it at e335d97 ! Funny thing that we came to the same conclusion of doing it in a loop :)

@bpuig
Copy link
Owner Author

bpuig commented Jun 19, 2021

So how does it work now? Upon renewal, trial_ends_at is set to starts_at at now()? And ends_at is prolonged by "30 days" from the previous value of trial_ends_at?

Trial ends at now and then:

  • Inside: We get trial length, substract remaining days and them from a full period.
    • 7 day trial on 30 day period = 30 day
      • Used 7 days and come back a month later: subscription will do 30 - 7 used days.
      • Used 4 days and renew: Subscription will renew 30 - 4 used days.
  • Outside: We add remaining trial days to the end of subscription.
    • 7 day trial on 30 day period = 37 day
      - Used 7 days and come back a month later: new subscription will be 30 days.
      - Used 4 days and renew: Subscription will renew 30 + 3 unused days.

I don't understand "Trial means you just get free time, not features". Companies often give 7-day trial for the "Pro" plan and allow users both to have time and to test the features for free. I rather see this scenario. And later when you subscribe, you get fresh set of features. Maybe it should be on an option then?

Then you are giving a month set of features for free, do you really want that? I can look into making it an option, makes sense, not economically, but makes sense... 😆

But should I (developer) care about refreshing it every month? Normally renewal comes "from outside" (payment). When we set a free 0€ subscription, it is not natural that we additionaly should care about prolonging it every month. It just should be infinite.

Subscriptions should auto-renew until canceled. I know there is the payment thing that should be sorted sometime, but right now the package is a "bubble" that should not care what happens in the outside world (payment platform).

@bpuig bpuig mentioned this pull request Jun 19, 2021
5 tasks
@bpuig bpuig linked an issue Jun 19, 2021 that may be closed by this pull request
# Conflicts:
#	src/SubbyServiceProvider.php
#	src/Traits/HasSubscriptions.php
…into new_trial_behaviour

� Conflicts:
�	src/SubbyServiceProvider.php
@boryn
Copy link
Contributor

boryn commented Jun 20, 2021

I don't understand "Trial means you just get free time, not features". Companies often give 7-day trial for the "Pro" plan and allow users both to have time and to test the features for free. I rather see this scenario. And later when you subscribe, you get fresh set of features. Maybe it should be on an option then?

Then you are giving a month set of features for free, do you really want that? I can look into making it an option, makes sense, not economically, but makes sense...

Sometimes you need to give a user something totally for free (even the Pro "sub") so that they get to know your solution and only later ask them to pay. If the trial is longer, eg. for 30 days and they later prolong, they need to have fresh usage limits. And even if trial is 7-14 days, when they decided to prolong, they get a new subscription and in my opinion, they need to get fresh usage limits as well. I consider a free trial to be a real free trial. So I think, it would be good implementing such an option.

But should I (developer) care about refreshing it every month? Normally renewal comes "from outside" (payment). When we set a free 0€ subscription, it is not natural that we additionaly should care about prolonging it every month. It just should be infinite.

Subscriptions should auto-renew until canceled. I know there is the payment thing that should be sorted sometime, but right now the package is a "bubble" that should not care what happens in the outside world (payment platform).

Hmm... So I was not aware of the auto-renewal of free subscriptions. Where does it happen in the code?

@bpuig
Copy link
Owner Author

bpuig commented Jun 20, 2021

If the trial is longer, eg. for 30 days and they later prolong, they need to have fresh usage limits.

I'll try to implement the behavior somehow 👍

Hmm... So I was not aware of the auto-renewal of free subscriptions. Where does it happen in the code?

I meant YOU need to auto-renew the subscriptions. The package was (is) thought that you handle renewals when subscription ends, and you should keep track of renewals.

@boryn
Copy link
Contributor

boryn commented Jun 21, 2021

Hmm... So I was not aware of the auto-renewal of free subscriptions. Where does it happen in the code?

I meant YOU need to auto-renew the subscriptions. The package was (is) thought that you handle renewals when subscription ends, and you should keep track of renewals.

That's natural that subscriptions with the price need to have the outside event to trigger the renewal. But the free subscriptions don't even demand the outside world, they should just exist and be available. And that's why I talk about some special handling of the free subscriptions, inside the package "bubble" itself.

AFAIR this manual renewing of free subscriptions is not mentioned in the docs? Or I didn't notice that. Common sense tells me that if the subscription is free, it's just can be used forever and no external steps need to be taken. It would be a big nuisance to set up an extra scheduler job just to keep free subs "alive".

IMHO it would be reasonable to fill the starts_at and leave the ends_at with null, we'd need a new method isFree() (true if price equals 0 (and maybe if ends_at is null)) and in such a case the isActive() would return true if || $this->isFree(). hasEnded() should as well return false if isFree().

@bpuig
Copy link
Owner Author

bpuig commented Jul 28, 2021

I think this is almost good to go. I have been away for some time so I'm not fully fresh about what we were talking.

This branch accepts renewal by periods. Also renewal does not clear usage just manipulates periods, since that is done by the usage model automatically.

If you want to take a look at it, I'll live it open some more time in case I find anything more.

Exceptions for free subscriptions will not be made, they will be treated as a regular subscription with 0 price. This will keep the package more coherent and with the minimum exceptions and variants possible as long as it is usable.

😄

@boryn
Copy link
Contributor

boryn commented Aug 4, 2021

Thank you! I hope to be able to test it the next week and will give some feedback

@bpuig bpuig merged commit 47b7fe3 into main Sep 5, 2021
@bpuig bpuig deleted the new_trial_behaviour branch September 5, 2021 09:58
@bpuig bpuig removed the work in progress This work is in progress. label Oct 3, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request major Major change
Projects
None yet
2 participants