Jun 29 - Update: Fail2ban, welcome email, dunning sequence, escalation

This commit is contained in:
2026-06-29 13:47:57 -04:00
parent c133b5d419
commit b00bbba2da
14 changed files with 414 additions and 1 deletions
+22
View File
@@ -15,23 +15,45 @@ from app import mail
logger = logging.getLogger(__name__)
_SUBJECTS = {
'welcome': 'Welcome to JQC — your workspace is ready',
'payment_failed': 'Action Required: Payment failed for your JQC subscription',
'payment_reminder': 'Reminder: Payment still needed for your JQC subscription',
'payment_final': 'Final Notice: Your JQC account will be cancelled soon',
'trial_ending': 'Your JQC free trial ends in 3 days',
'subscription_cancelled': 'Your JQC subscription has been cancelled',
}
_HTML_TEMPLATES = {
'welcome': 'billing/email/welcome.html',
'payment_failed': 'billing/email/payment_failed.html',
'payment_reminder': 'billing/email/payment_reminder.html',
'payment_final': 'billing/email/payment_final.html',
'trial_ending': 'billing/email/trial_ending.html',
'subscription_cancelled': 'billing/email/subscription_cancelled.html',
}
_PLAIN_BODIES = {
'welcome': (
"Welcome to Janitorial QC!\n\n"
"Your workspace is ready at:\n {login_url}\n\n"
"Trial: {trial_days} days (expires {trial_ends_at})\n\n— The JQC Team"
),
'payment_failed': (
"We were unable to process your most recent payment for your JQC subscription.\n\n"
"Please update your payment method to avoid interruption to your service:\n"
" {portal_url}\n\n— The JQC Team"
),
'payment_reminder': (
"Friendly reminder: your JQC subscription payment is still outstanding.\n\n"
"Update your payment method to restore full access:\n"
" {portal_url}\n\n— The JQC Team"
),
'payment_final': (
"This is a final notice: your JQC subscription will be cancelled within 24 hours\n"
"if payment is not received.\n\n"
"Update your payment method now to keep your workspace:\n"
" {portal_url}\n\n— The JQC Team"
),
'trial_ending': (
"Your JQC free trial will end on {trial_ends_at}.\n\n"
"Subscribe now to keep your workspace active:\n"
+6
View File
@@ -210,6 +210,9 @@ def _on_payment_succeeded(obj):
if t.subscription_status == 'past_due':
t.subscription_status = 'active'
t.past_due_since = None
t.dunning_stage = 0
t.dunning_sent_at = None
if period_end:
t.current_period_end = _ts_to_dt(period_end)
@@ -236,6 +239,9 @@ def _on_payment_failed(obj):
return
t.subscription_status = 'past_due'
if t.past_due_since is None:
from control.time_utils import now_eastern
t.past_due_since = now_eastern()
billing_email = t.billing_email
s.add(TenantAudit(