Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(trial): Introduce Subscriptions::FreeTrialBillingService #1821

Merged
merged 1 commit into from
Mar 29, 2024

Conversation

julienbourdeau
Copy link
Contributor

@julienbourdeau julienbourdeau commented Mar 27, 2024

Context

Following #1820, we need a job find all subscriptions which has a trial which just ended.

Description

This introduce a new service to find all subscription with a trial period ending today. The job will run every hour to follow customer timezones.

We introduce a new timestamp in subscriptions to know if the trial has ended. This ensure we only run the job once. Without this flag, we would retrieve the same subscription 24 times and dispatch the webhook 24 times. The invoice can't be used to know if the job was run because we only change plans with pay_in_advance = true.

This adds the fourth definition of the at_time_zone method. I'm hoping to refactor this later.

Note that the job doesn't run yet. The following PR will add it to the scheduler and generate invoice and send the webhooks.

Migration

The data is migrated for all subscriptions with a trial that has already ended. This is done to ensure a minimum of consistency in the data.

The logic for initial_started_at is implemented to avoid the N+1 queries triggered by the original method.

I didn't reuse the SQL query from the service because find_by_sql returns an array, not a collection so you can't load the data in batch (like with find_each). We'd have to load ALL subscription in the array 🙈

Alternatively, we can probably write an SQL query that update all data at once, without using the model. It would be fun but I'm not sure it's worth the effort.

@julienbourdeau julienbourdeau self-assigned this Mar 27, 2024
@rsempe rsempe force-pushed the feat/free_trial_billing_service branch 3 times, most recently from 6419bc6 to 52d8da1 Compare March 28, 2024 09:53
@rsempe rsempe force-pushed the feat/free_trial_billing_service branch 2 times, most recently from 1fba3a7 to 4a75da3 Compare March 28, 2024 09:57
@julienbourdeau julienbourdeau force-pushed the feat/free_trial_billing_service branch from 6b1bca9 to d94da19 Compare March 28, 2024 13:03
Copy link
Collaborator

@vincent-pochet vincent-pochet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice one! 🚀

Just one comment regarding the timezone in the migration

@julienbourdeau julienbourdeau force-pushed the feat/free_trial_billing_service branch 2 times, most recently from 037401c to fe4242a Compare March 29, 2024 09:14
@julienbourdeau julienbourdeau force-pushed the feat/free_trial_billing_service branch from fe4242a to 51d3e68 Compare March 29, 2024 09:45
@julienbourdeau julienbourdeau merged commit 93022ed into main Mar 29, 2024
7 checks passed
@julienbourdeau julienbourdeau deleted the feat/free_trial_billing_service branch March 29, 2024 09:55
julienbourdeau added a commit that referenced this pull request Apr 15, 2024
…1847)

## Context

When a subscription has a trial period, we want to invoice the customer
at the end of the trial, not at the beginning.

Following #1820 and #1821

## Description

* Disable billing when creating a new subscription if there is a trial
* Disable billing when activating a pending subscription if there is a
trial
* Run the `Subscriptions::FreeTrialBillingService` introduced in #1821
every hour
* Migrate data for existing subscription with trial already ended
* Ensure we don't bill customers if an invoice was created when the
subscription started (for sub created before this feature)

### Free trial reminder

Keep in mind that if you're trial ends on day X, this day is *NOT FREE*.
If the 10 days free trial start on the 10th at 14:00, it will end on the
20th at 14:00. You get the 10th to the 19th for free. Day 20 is your
first pay day even if you're still in trial for a few hours.

This was not changed in this PR or other related PRs.

## Note

* Introduction of `invoice.skip_charge`
* Refactor of charge fees for first invoice, see:
#1836
* Handle grace_period
* Handle minimum_commitment

---------

Co-authored-by: Romain Sempé <romain@getlago.com>
drejc pushed a commit to fliqa-io/lago-api that referenced this pull request May 15, 2024
drejc pushed a commit to fliqa-io/lago-api that referenced this pull request May 15, 2024
…etlago#1847)

## Context

When a subscription has a trial period, we want to invoice the customer
at the end of the trial, not at the beginning.

Following getlago#1820 and getlago#1821

## Description

* Disable billing when creating a new subscription if there is a trial
* Disable billing when activating a pending subscription if there is a
trial
* Run the `Subscriptions::FreeTrialBillingService` introduced in getlago#1821
every hour
* Migrate data for existing subscription with trial already ended
* Ensure we don't bill customers if an invoice was created when the
subscription started (for sub created before this feature)

### Free trial reminder

Keep in mind that if you're trial ends on day X, this day is *NOT FREE*.
If the 10 days free trial start on the 10th at 14:00, it will end on the
20th at 14:00. You get the 10th to the 19th for free. Day 20 is your
first pay day even if you're still in trial for a few hours.

This was not changed in this PR or other related PRs.

## Note

* Introduction of `invoice.skip_charge`
* Refactor of charge fees for first invoice, see:
getlago#1836
* Handle grace_period
* Handle minimum_commitment

---------

Co-authored-by: Romain Sempé <romain@getlago.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants