mirror of
https://github.com/simple-login/app.git
synced 2026-06-06 19:37:35 +00:00
Handle trial period
- user can upgrade to lifetime if in trial or free - free or trial user can buy a subscription - user has all features when in trial
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
<h1> Billing </h1>
|
||||
|
||||
<p>
|
||||
You are on the <b>{{ current_user.plan_name() }}</b> plan. Thank you very much for supporting SimpleLogin. 🙌
|
||||
You are on the <b>{{ current_user.get_subscription().plan_name() }}</b> plan. Thank you very much for supporting SimpleLogin. 🙌
|
||||
</p>
|
||||
|
||||
{% if sub.cancelled %}
|
||||
|
||||
@@ -20,7 +20,10 @@ def custom_alias():
|
||||
if not current_user.can_create_new_alias():
|
||||
# notify admin
|
||||
LOG.error("user %s tries to create custom alias", current_user)
|
||||
flash("ony premium user can choose custom alias", "warning")
|
||||
flash(
|
||||
"You have reached free plan limit, please upgrade to create new aliases",
|
||||
"warning",
|
||||
)
|
||||
return redirect(url_for("dashboard.index"))
|
||||
|
||||
user_custom_domains = [cd.domain for cd in current_user.verified_custom_domains()]
|
||||
|
||||
@@ -24,7 +24,7 @@ class CouponForm(FlaskForm):
|
||||
@login_required
|
||||
def lifetime_licence():
|
||||
# sanity check: make sure this page is only for free user
|
||||
if current_user.is_premium():
|
||||
if current_user.lifetime_or_active_subscription():
|
||||
flash("You are already a premium user", "warning")
|
||||
return redirect(url_for("dashboard.index"))
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ from app.dashboard.base import dashboard_bp
|
||||
@dashboard_bp.route("/pricing", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def pricing():
|
||||
# sanity check: make sure this page is only for free user
|
||||
if current_user.is_premium():
|
||||
# sanity check: make sure this page is only for free or trial user
|
||||
if not current_user.should_upgrade():
|
||||
flash("You are already a premium user", "warning")
|
||||
return redirect(url_for("dashboard.index"))
|
||||
|
||||
|
||||
+32
-19
@@ -124,7 +124,7 @@ class User(db.Model, ModelMixin, UserMixin):
|
||||
|
||||
# user can use all premium features until this date
|
||||
trial_end = db.Column(
|
||||
ArrowType, default=lambda: arrow.now().shift(days=7), nullable=True
|
||||
ArrowType, default=lambda: arrow.now().shift(days=7, hours=1), nullable=True
|
||||
)
|
||||
|
||||
profile_picture = db.relationship(File)
|
||||
@@ -146,11 +146,8 @@ class User(db.Model, ModelMixin, UserMixin):
|
||||
|
||||
return user
|
||||
|
||||
def should_upgrade(self):
|
||||
return not self.is_premium()
|
||||
|
||||
def is_premium(self):
|
||||
"""user is premium if they have a active subscription"""
|
||||
def lifetime_or_active_subscription(self) -> bool:
|
||||
"""True if user has lifetime licence or active subscription"""
|
||||
if self.lifetime:
|
||||
return True
|
||||
|
||||
@@ -160,7 +157,29 @@ class User(db.Model, ModelMixin, UserMixin):
|
||||
|
||||
return False
|
||||
|
||||
def can_create_new_alias(self):
|
||||
def in_trial(self):
|
||||
"""return True if user does not have lifetime licence or an active subscription AND is in trial period"""
|
||||
if self.lifetime_or_active_subscription():
|
||||
return False
|
||||
|
||||
return self.trial_end and arrow.now() < self.trial_end
|
||||
|
||||
def should_upgrade(self):
|
||||
return not self.lifetime_or_active_subscription()
|
||||
|
||||
def is_premium(self) -> bool:
|
||||
"""
|
||||
user is premium if they:
|
||||
- have a lifetime deal or
|
||||
- in trial period or
|
||||
- active subscription
|
||||
"""
|
||||
if self.lifetime_or_active_subscription():
|
||||
return True
|
||||
|
||||
return self.trial_end and arrow.now() < self.trial_end
|
||||
|
||||
def can_create_new_alias(self) -> bool:
|
||||
if self.is_premium():
|
||||
return True
|
||||
|
||||
@@ -202,7 +221,6 @@ class User(db.Model, ModelMixin, UserMixin):
|
||||
|
||||
def suggested_names(self) -> (str, [str]):
|
||||
"""return suggested name and other name choices """
|
||||
|
||||
other_name = convert_to_id(self.name)
|
||||
|
||||
return self.name, [other_name, "Anonymous", "whoami"]
|
||||
@@ -211,17 +229,6 @@ class User(db.Model, ModelMixin, UserMixin):
|
||||
names = self.name.split(" ")
|
||||
return "".join([n[0].upper() for n in names if n])
|
||||
|
||||
def plan_name(self) -> str:
|
||||
if self.is_premium():
|
||||
sub = self.get_subscription()
|
||||
|
||||
if sub.plan == PlanEnum.monthly:
|
||||
return "Monthly ($2.99/month)"
|
||||
else:
|
||||
return "Yearly ($29.99/year)"
|
||||
else:
|
||||
return "Free Plan"
|
||||
|
||||
def get_subscription(self):
|
||||
"""return *active* subscription
|
||||
TODO: support user unsubscribe and re-subscribe
|
||||
@@ -643,6 +650,12 @@ class Subscription(db.Model, ModelMixin):
|
||||
|
||||
user = db.relationship(User)
|
||||
|
||||
def plan_name(self):
|
||||
if self.plan == PlanEnum.monthly:
|
||||
return "Monthly ($2.99/month)"
|
||||
else:
|
||||
return "Yearly ($29.99/year)"
|
||||
|
||||
|
||||
class DeletedAlias(db.Model, ModelMixin):
|
||||
"""Store all deleted alias to make sure they are NOT reused"""
|
||||
|
||||
@@ -381,7 +381,7 @@ def setup_paddle_callback(app: Flask):
|
||||
|
||||
elif request.form.get("alert_name") == "subscription_cancelled":
|
||||
subscription_id = request.form.get("subscription_id")
|
||||
LOG.debug("Cancel subscription %s", subscription_id)
|
||||
LOG.error("Cancel subscription %s", subscription_id)
|
||||
|
||||
sub: Subscription = Subscription.get_by(subscription_id=subscription_id)
|
||||
if sub:
|
||||
|
||||
@@ -25,15 +25,16 @@
|
||||
{{ current_user.name }}
|
||||
</span>
|
||||
|
||||
{% if current_user.is_premium() %}
|
||||
{% if current_user.in_trial() %}
|
||||
<small class="text-success d-block mt-1">Trial ends in {{ current_user.trial_end|dt }}</small>
|
||||
{% elif current_user.lifetime_or_active_subscription() %}
|
||||
<small class="text-success d-block mt-1">Premium</small>
|
||||
{% endif %}
|
||||
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<div class="dropdown-menu dropdown-menu-right dropdown-menu-arrow">
|
||||
|
||||
|
||||
<a class="dropdown-item" href="{{ url_for('auth.logout') }}">
|
||||
<i class="dropdown-icon fe fe-log-out"></i> Sign out
|
||||
</a>
|
||||
|
||||
Reference in New Issue
Block a user