Jun 28 - Update payment flows
This commit is contained in:
+17
-7
@@ -56,9 +56,12 @@ def subscribe():
|
|||||||
abort(400)
|
abort(400)
|
||||||
|
|
||||||
from control.base import control_session
|
from control.base import control_session
|
||||||
from control.models import Tenant as ControlTenant
|
from control.models import Tenant as ControlTenant, Plan
|
||||||
from app.billing.stripe_client import create_stripe_customer, create_checkout_session
|
from app.billing.stripe_client import create_stripe_customer, create_checkout_session
|
||||||
|
|
||||||
|
# Allow tenant to pick a plan via ?plan=<code> when on Free.
|
||||||
|
requested_plan_code = request.args.get('plan')
|
||||||
|
|
||||||
with control_session() as s:
|
with control_session() as s:
|
||||||
t = s.get(ControlTenant, tenant.id)
|
t = s.get(ControlTenant, tenant.id)
|
||||||
if t is None:
|
if t is None:
|
||||||
@@ -66,13 +69,20 @@ def subscribe():
|
|||||||
return redirect(url_for('tenant_settings.plan'))
|
return redirect(url_for('tenant_settings.plan'))
|
||||||
|
|
||||||
plan = t.plan
|
plan = t.plan
|
||||||
|
|
||||||
|
# If a specific plan was requested (e.g. from the plan picker), use it.
|
||||||
|
if requested_plan_code:
|
||||||
|
chosen = s.query(Plan).filter_by(code=requested_plan_code).first()
|
||||||
|
if chosen and chosen.stripe_price_id:
|
||||||
|
plan = chosen
|
||||||
|
|
||||||
|
# If still on Free (no price ID), show the plan picker instead.
|
||||||
if plan is None or not plan.stripe_price_id:
|
if plan is None or not plan.stripe_price_id:
|
||||||
flash(
|
paid_plans = (s.query(Plan)
|
||||||
'Your current plan does not have a Stripe price configured. '
|
.filter(Plan.stripe_price_id.isnot(None))
|
||||||
'Please contact support to set up billing.',
|
.order_by(Plan.price_cents)
|
||||||
'warning',
|
.all())
|
||||||
)
|
return render_template('billing/plan_picker.html', plans=paid_plans)
|
||||||
return redirect(url_for('tenant_settings.plan'))
|
|
||||||
|
|
||||||
# Get or create Stripe customer. Write back immediately within the
|
# Get or create Stripe customer. Write back immediately within the
|
||||||
# control_session so the customer_id is persisted before the redirect.
|
# control_session so the customer_id is persisted before the redirect.
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Choose a Plan — JQC{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row justify-content-center mt-4">
|
||||||
|
<div class="col-lg-9">
|
||||||
|
<h4 class="mb-1">Choose a Plan</h4>
|
||||||
|
<p class="text-muted mb-4" style="font-size:.9rem;">
|
||||||
|
Select the plan that best fits your team. You can upgrade or downgrade at any time.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="row g-3">
|
||||||
|
{% for plan in plans %}
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card border-0 shadow-sm h-100">
|
||||||
|
<div class="card-header bg-white py-3 text-center">
|
||||||
|
<span class="fw-bold fs-5">{{ plan.name }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-body d-flex flex-column">
|
||||||
|
<table class="table table-sm mb-3" style="font-size:.85rem;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="text-muted">Users</td>
|
||||||
|
<td>{{ plan.max_users if plan.max_users else 'Unlimited' }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-muted">Facilities</td>
|
||||||
|
<td>{{ plan.max_facilities if plan.max_facilities else 'Unlimited' }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-muted">Inspections / mo</td>
|
||||||
|
<td>{{ plan.max_inspections_month if plan.max_inspections_month else 'Unlimited' }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-muted">Mobile API (iPad)</td>
|
||||||
|
<td>{% if plan.allow_mobile_api %}<i class="bi bi-check-circle-fill text-success"></i>{% else %}<i class="bi bi-x-circle-fill text-danger"></i>{% endif %}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-muted">Scheduled Reports</td>
|
||||||
|
<td>{% if plan.allow_scheduled_reports %}<i class="bi bi-check-circle-fill text-success"></i>{% else %}<i class="bi bi-x-circle-fill text-danger"></i>{% endif %}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-muted">Branding</td>
|
||||||
|
<td>{% if plan.allow_branding %}<i class="bi bi-check-circle-fill text-success"></i>{% else %}<i class="bi bi-x-circle-fill text-danger"></i>{% endif %}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-muted">Custom Domain</td>
|
||||||
|
<td>{% if plan.allow_custom_domain %}<i class="bi bi-check-circle-fill text-success"></i>{% else %}<i class="bi bi-x-circle-fill text-danger"></i>{% endif %}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="mt-auto text-center">
|
||||||
|
<a href="{{ url_for('billing.subscribe', plan=plan.code) }}"
|
||||||
|
class="btn btn-primary w-100">
|
||||||
|
<i class="bi bi-lightning-charge me-1"></i> Subscribe — {{ plan.name }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-3 text-center">
|
||||||
|
<a href="{{ url_for('tenant_settings.plan') }}" class="text-muted" style="font-size:.85rem;">
|
||||||
|
<i class="bi bi-arrow-left me-1"></i> Back to Plan & Usage
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user