mirror of
https://github.com/simple-login/app.git
synced 2024-09-20 15:05:59 +08:00
support changing plan
This commit is contained in:
parent
51eb550751
commit
9b91f4a4a4
|
@ -14,8 +14,7 @@
|
|||
{% if sub.cancelled %}
|
||||
<p>
|
||||
You are on the <b>{{ sub.plan_name() }}</b> plan. <br>
|
||||
You have canceled your subscription and it will end on {{current_user.next_bill_date()}}
|
||||
({{ sub.next_bill_date | dt }}).
|
||||
You have canceled your subscription and it will end on {{ current_user.next_bill_date() }}
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
|
@ -33,23 +32,47 @@
|
|||
{% else %}
|
||||
<p>
|
||||
You are on the <b>{{ sub.plan_name() }}</b> plan. Thank you very much for supporting
|
||||
SimpleLogin. 🙌
|
||||
SimpleLogin. 🙌 <br>
|
||||
The next billing cycle starts at {{ sub.next_bill_date.strftime("%Y-%m-%d") }}.
|
||||
</p>
|
||||
|
||||
<div class="mt-3">
|
||||
Click here to update billing information on Paddle, our payment partner: <br>
|
||||
<a class="btn btn-success" href="{{ sub.update_url }}"> Update billing information </a>
|
||||
<a class="btn btn-outline-success mt-2" href="{{ sub.update_url }}"> Update billing information </a>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="mt-6">
|
||||
<h4>Change Plan</h4>
|
||||
You can change the plan at any moment. <br>
|
||||
Please note that the new billing cycle starts instantly
|
||||
i.e. you will be charged <b>immediately</b> the annual fee when switching from monthly plan or vice-versa
|
||||
<b>without pro rata computation </b>. <br>
|
||||
|
||||
To change the plan you can also cancel the current one and subscribe a new one <b>by the end</b> of this plan.
|
||||
|
||||
{% if sub.plan == PlanEnum.yearly %}
|
||||
<form method="post">
|
||||
<input type="hidden" name="form-name" value="change-monthly">
|
||||
<button class="btn btn-outline-primary mt-2">Change to Monthly Plan</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<form method="post">
|
||||
<input type="hidden" name="form-name" value="change-yearly">
|
||||
<button class="btn btn-outline-primary mt-2">Change to Yearly Plan</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div>
|
||||
<h4>Cancel subscription</h4>
|
||||
Don't want to protect your inbox anymore? <br>
|
||||
|
||||
<form method="post">
|
||||
<input type="hidden" name="form-name" value="cancel">
|
||||
|
||||
<span class="cancel btn btn-warning">
|
||||
<span class="cancel btn btn-outline-danger mt-2">
|
||||
Cancel subscription <i class="fe fe-alert-triangle text-danger"></i>
|
||||
</span>
|
||||
</form>
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
from flask import render_template, flash, redirect, url_for, request
|
||||
from flask_login import login_required, current_user
|
||||
|
||||
from app.config import PADDLE_MONTHLY_PRODUCT_ID, PADDLE_YEARLY_PRODUCT_ID
|
||||
from app.dashboard.base import dashboard_bp
|
||||
from app.log import LOG
|
||||
from app.models import Subscription
|
||||
from app.models import Subscription, PlanEnum
|
||||
from app.extensions import db
|
||||
from app.paddle_utils import cancel_subscription
|
||||
from app.paddle_utils import cancel_subscription, change_plan
|
||||
|
||||
|
||||
@dashboard_bp.route("/billing", methods=["GET", "POST"])
|
||||
|
@ -29,10 +30,43 @@ def billing():
|
|||
flash("Your subscription has been canceled successfully", "success")
|
||||
else:
|
||||
flash(
|
||||
"Something went wrong, sorry for the inconvenience. Please retry. We are already notified and will be on it asap",
|
||||
"Something went wrong, sorry for the inconvenience. Please retry. "
|
||||
"We are already notified and will be on it asap",
|
||||
"error",
|
||||
)
|
||||
|
||||
return redirect(url_for("dashboard.billing"))
|
||||
elif request.form.get("form-name") == "change-monthly":
|
||||
LOG.debug(f"User {current_user} changes to monthly plan")
|
||||
success = change_plan(sub.subscription_id, PADDLE_MONTHLY_PRODUCT_ID)
|
||||
|
||||
if success:
|
||||
sub.plan = PlanEnum.monthly
|
||||
db.session.commit()
|
||||
flash("Your subscription has been updated", "success")
|
||||
else:
|
||||
flash(
|
||||
"Something went wrong, sorry for the inconvenience. Please retry. "
|
||||
"We are already notified and will be on it asap",
|
||||
"error",
|
||||
)
|
||||
|
||||
return redirect(url_for("dashboard.billing"))
|
||||
elif request.form.get("form-name") == "change-yearly":
|
||||
LOG.debug(f"User {current_user} changes to yearly plan")
|
||||
success = change_plan(sub.subscription_id, PADDLE_YEARLY_PRODUCT_ID)
|
||||
|
||||
if success:
|
||||
sub.plan = PlanEnum.yearly
|
||||
db.session.commit()
|
||||
flash("Your subscription has been updated", "success")
|
||||
else:
|
||||
flash(
|
||||
"Something went wrong, sorry for the inconvenience. Please retry. "
|
||||
"We are already notified and will be on it asap",
|
||||
"error",
|
||||
)
|
||||
|
||||
return redirect(url_for("dashboard.billing"))
|
||||
|
||||
return render_template("dashboard/billing.html", sub=sub)
|
||||
return render_template("dashboard/billing.html", sub=sub, PlanEnum=PlanEnum)
|
||||
|
|
|
@ -76,3 +76,22 @@ def cancel_subscription(subscription_id: int) -> bool:
|
|||
)
|
||||
|
||||
return res["success"]
|
||||
|
||||
|
||||
def change_plan(subscription_id: int, plan_id) -> bool:
|
||||
r = requests.post(
|
||||
"https://vendors.paddle.com/api/2.0/subscription/users/update",
|
||||
data={
|
||||
"vendor_id": PADDLE_VENDOR_ID,
|
||||
"vendor_auth_code": PADDLE_AUTH_CODE,
|
||||
"subscription_id": subscription_id,
|
||||
"plan_id": plan_id,
|
||||
},
|
||||
)
|
||||
res = r.json()
|
||||
if not res["success"]:
|
||||
LOG.error(
|
||||
f"cannot change subscription {subscription_id} to {plan_id}, paddle response: {res}"
|
||||
)
|
||||
|
||||
return res["success"]
|
||||
|
|
33
server.py
33
server.py
|
@ -411,7 +411,40 @@ def setup_paddle_callback(app: Flask):
|
|||
db.session.commit()
|
||||
else:
|
||||
return "No such subscription", 400
|
||||
elif request.form.get("alert_name") == "subscription_updated":
|
||||
subscription_id = request.form.get("subscription_id")
|
||||
|
||||
sub: Subscription = Subscription.get_by(subscription_id=subscription_id)
|
||||
if sub:
|
||||
LOG.debug(
|
||||
"Update subscription %s %s on %s, next bill date %s",
|
||||
subscription_id,
|
||||
sub.user,
|
||||
request.form.get("cancellation_effective_date"),
|
||||
sub.next_bill_date,
|
||||
)
|
||||
if (
|
||||
int(request.form.get("subscription_plan_id"))
|
||||
== PADDLE_MONTHLY_PRODUCT_ID
|
||||
):
|
||||
plan = PlanEnum.monthly
|
||||
else:
|
||||
plan = PlanEnum.yearly
|
||||
|
||||
sub.cancel_url = request.form.get("cancel_url")
|
||||
sub.update_url = request.form.get("update_url")
|
||||
sub.event_time = arrow.now()
|
||||
sub.next_bill_date = arrow.get(
|
||||
request.form.get("next_bill_date"), "YYYY-MM-DD"
|
||||
).date()
|
||||
sub.plan = plan
|
||||
|
||||
# make sure to set the new plan as not-cancelled
|
||||
sub.cancelled = False
|
||||
|
||||
db.session.commit()
|
||||
else:
|
||||
return "No such subscription", 400
|
||||
return "OK"
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue