Jun 28 - Update payment flows
This commit is contained in:
+17
-7
@@ -56,9 +56,12 @@ def subscribe():
|
||||
abort(400)
|
||||
|
||||
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
|
||||
|
||||
# Allow tenant to pick a plan via ?plan=<code> when on Free.
|
||||
requested_plan_code = request.args.get('plan')
|
||||
|
||||
with control_session() as s:
|
||||
t = s.get(ControlTenant, tenant.id)
|
||||
if t is None:
|
||||
@@ -66,13 +69,20 @@ def subscribe():
|
||||
return redirect(url_for('tenant_settings.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:
|
||||
flash(
|
||||
'Your current plan does not have a Stripe price configured. '
|
||||
'Please contact support to set up billing.',
|
||||
'warning',
|
||||
)
|
||||
return redirect(url_for('tenant_settings.plan'))
|
||||
paid_plans = (s.query(Plan)
|
||||
.filter(Plan.stripe_price_id.isnot(None))
|
||||
.order_by(Plan.price_cents)
|
||||
.all())
|
||||
return render_template('billing/plan_picker.html', plans=paid_plans)
|
||||
|
||||
# Get or create Stripe customer. Write back immediately within the
|
||||
# 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