support changing plan

This commit is contained in:
Son NK 2020-04-12 19:43:07 +02:00
parent 51eb550751
commit 9b91f4a4a4
4 changed files with 118 additions and 9 deletions

View File

@ -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>

View File

@ -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)

View File

@ -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"]

View File

@ -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"