This is not required for our initial version, but is important to think about. Most solutions have some kind of support for free trials. It starts with a trial_interval in the billing schedule. For example, a monthly schedule where the 1st month is a trial. That ensures that we don't charge the customer for the first cycle.
I've done some research.
1) The trial period is the period before the billing cycles start. No invoice.
2) Intervals often don't match.
So, a monthly prepaid service might have a "14 day" trial. Then, close to the expiry of the 14 days, the customer is notified and given a chance to cancel their subscription. If they don't, we automatically start billing (create the first billing cycle and recurring order, in case of prepayment charge the card).
Original GitHub conversation: https://github.com/bojanz/commerce_recurring/issues/3
Comment | File | Size | Author |
---|---|---|---|
#22 | interdiff_20-22.txt | 2.91 KB | jsacksick |
#22 | commerce_recurring-add-support-for-trials-2919596-22.patch | 42.78 KB | jsacksick |
|
Comments
Comment #2
brunodboOne question here is whether to require a customer to enter their payment details when starting the trial (e.g., Stripe allows for this, while Braintree doesn't). Would it be possible to make this be an option in the trial period configuration?
Comment #3
Hubbs CreditAttribution: Hubbs at Acro Commerce commentedI'm looking forward to this, but in the meantime, you can potentially use the promotions module with this patch applied to create a one time discount of 100%. It applies the discount one during initial checkout but not for any additional recurring billing.
However, with that patch there is a bug I've found where the new setting only works for the user/1 supperadmin. Hopefully, that can be resolved soon, then this can be an OK workaround for the time being.
Comment #4
nikathone@hubbs how do you checkout free order then? I was talking to Matt and we thought that this might be possible in case https://www.drupal.org/project/commerce/issues/2856583 cover a situation to create payment method but not create payment when order is free.
Comment #5
Hubbs CreditAttribution: Hubbs at Acro Commerce commented@nikathone My tests using the discount method I mentioned were going through because the tax was still being calculated, and therefore my orders weren't $0. I incorrectly assumed that it would work on a $0 scenario. Sorry about that. So with that said, it looks like we would also need the patch you've mentioned. :S
Comment #6
mike82 CreditAttribution: mike82 commentedI think it should not be only considered a free trial, but a free trial is only a special case of a different first period. I have a lot of experience with billing systems and the current implementation is not considering a lot of possible cases for subscriptions.
The general approach is to allow an initial period with different first time price and start the regular subscription afterwards. Also both periods should support offsets. A free trial period is therefore only a special case, of this with price 0. If you think of a 14 day trial as you mentioned, it doesn't need to be for free. You could have 14 days for 10$ trial and afterwards a regular subscription of 30$ per month.
Comment #7
bojanz CreditAttribution: bojanz at Centarro commented@mike82
I think we need to special case free trials VS differently-priced-initial-periods because of the "no invoice" rule. A differently priced first period would still be invoiced.
Comment #8
nikathoneThinking about free trial using commerce_license. I came up with 10 steps which I think would allow me to get it working with current code, few patches and custom codes:
Comment #9
jsacksick CreditAttribution: jsacksick at Centarro commentedHere's my first attempt at this:
onSubscriptionTrialCancel()
andonSubscriptionTrialStart()
. I don't think we needonSubscriptionTrialEnd()
since as soon as the trial ends, the subscription is set to active andRecurringOrderManager::ensureOrder()
is calledI'm wondering if I should update generateFirstBillingPeriod() to take into account the trial period...
We're probably still missing a few things here and there...
@bojanz: thoughts?
Comment #11
jsacksick CreditAttribution: jsacksick at Centarro commentedAttempt to fix the functional tests.
Comment #12
jsacksick CreditAttribution: jsacksick at Centarro commentedSo I made some changes following a discussion I had with @bojanz today and after #2927164: Add code for activating pending subscriptions got committed.
Unfortunately, I'm having troubles creating the interdiff, so I can't provide it atm.
Comment #13
bojanz CreditAttribution: bojanz at Centarro commentedGreat work! This seems ready to go, but I'll do another more in-depth review later today.
Comment #14
bojanz CreditAttribution: bojanz at Centarro commentedWe said that we'd also populate "starts" when populating "trial_starts" and "trial_ends".
Aside from being a good idea, it also allows us to significantly simplify this query (even removing the need to build it in a helper method), since we'd always check just starts and the state.
It is odd that we store allow_trials, we usually don't do that.
I would expect trial_period to be NULL if trials are not allowed. Then ->allowsTrials() would just check if trial_period is empty.
IDs
"or not" is not needed nor usually said.
There's usually a newline after the one-line method description.
Looks accidental.
Comment #15
porchlight CreditAttribution: porchlight as a volunteer and at Northern Commerce commentedPiggybacking on bojanz comment -- without the subscription "start" date filled out, the SubscriptionListBuilder crashes when calling getStartDate(). So if we are going to set the start value when somebody signs up, then problem solved. But if not, then we should throw in a shorthand if/else like used for the end_date column in the list builder buildRow() function. Awesome progress though! Aside from that small issue everything else seems to be working as expected. Still testing more though...
Comment #16
bojanz CreditAttribution: bojanz at Centarro commentedFinal point: we need to rename trial_period to trial_interval.
Reasons:
1) Programming languages, systems, APIs can't agree what is an interval VS what is a period. In commerce_recurring an interval is "5 days" while a period is "Jan 1st - Jan 6th". This matches how PHP itself names these concepts. So, trial_period is wrong because it doesn't have a start date and an end date.
This mismatch is obvious in this part of the patch:
2) Having trial_interval (number, unit) matches interval (number, unit). Jonathan's argument was that it was a bit confusing cause trials don't actually repeat, but other systems seem to have the same parallel (period / trial period, interval / trial period). For example, Recurly: https://dev.recurly.com/docs/create-plan
Comment #17
arthur.baghdasar CreditAttribution: arthur.baghdasar commentedCannot get this to work.
I've created a free trial for 1 hour in billing schedules form and set the billing to be prepaid, added a product and variation with that.
But when trying to purchase that item, I get free trail 0USD to pay, it doesn't ask me for any payment method to fill.
The order was created, but the actual subscription no.
Maybe I'm missing something.
Comment #18
bojanz CreditAttribution: bojanz at Centarro commentedThis patch can't make it possible to collect a payment method for a free order.
That's a related Commerce issue: #2871483: Add checkout settings for payment method behavior.
Comment #19
jsacksick CreditAttribution: jsacksick at Centarro commented@bojanz: Thanks for your feedbacks.
I'm now setting the Subscription "starts" timestamp on order place.
I'm wondering if we shouldn't modify SubscriptionActivate to re-set the "starts" timestamp to the current date...
Additionally, as pointed out in #17, we're not capturing the payment for free trials since the order total is 0.
I'm setting "trial_interval" to an empty array by default (instead of NULL because the schema defines it as "mapping" which means setting it to NULL wouldn't work.
Comment #20
jsacksick CreditAttribution: jsacksick at Centarro commentedMinor fixes that I forgot in the previous patch.
Comment #21
bojanz CreditAttribution: bojanz at Centarro commentedLooks almost ready!
I don't see why SubscriptionActivate would mess with the started date.
$trial_interval is never defined before this. Assuming that it wants to check for $this->configuration['trial_interval']
getTotalPrice() should be fine enough, I don't think we need getAdjustedTotalPrice?
Looks like this too wasn't updated for per-total adjustments, which means we need to fix it (could be in a followup though, with test coverage).
Comment #22
jsacksick CreditAttribution: jsacksick at Centarro commentedWould it be ok to do this when we prorate?
If we need to change the code to prorate on the total instead, then maybe we should focus on that in a follow-up issue.
Comment #24
bojanz CreditAttribution: bojanz at Centarro commentedI have committed #22 with a few improvements:
1) Moved the trial_starts and trial_ends fields (and their getters/setters) before the starts/ends fields, since conceptually the trial period happens before the first real period. Updated tests and other method orderings to reflect this.
2) Added a comment to generateTrialPeriod:
3) Modified OrderSubscriber to set the trial starts from the $trial_period, and not from $start_date.
That way the plugin has more freedom over what to return.
See you in #2871483: Add checkout settings for payment method behavior and other recurring issues!
Comment #25
arthur.baghdasar CreditAttribution: arthur.baghdasar commentedHi thank you all for your time and work.
I've tested the new version of 8.x-1.x branch and here is what I've got.
I've created a free interval for 1 hour and a product variation with that billing schedule.
Once I've purchased that product, an order has been placed and a new subscription added marked as "Trial".
After waiting for an hour Activate subscription job run the subscription became active, but I do not see client billed for that,
I expected to see new recurring order marked as completed as my billing schedule was configured to be pre paid.
Note:To get to the trial sbscriptions I've update commerce to the latest dev and commented out this part at
modules/contrib/commerce/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php:146
Comment #26
arthur.baghdasar CreditAttribution: arthur.baghdasar commentedAfter half an hour running the cron order appeared suddanly as paid, Thank you for your efforts.
Comment #30
bojanz CreditAttribution: bojanz at Centarro commentedAdding #3029058: Complete the free trial implementation to related issues, which completed the logic.