From 82567b53ff905be7b16dfa00b4bbf15613060064 Mon Sep 17 00:00:00 2001 From: Deeman Date: Thu, 26 Feb 2026 15:08:41 +0100 Subject: [PATCH 1/5] =?UTF-8?q?fix:=20align=20pricing=20with=20strategy=20?= =?UTF-8?q?=E2=80=94=20Basic=20free,=20card=20color=20=E2=82=AC59,=20BP=20?= =?UTF-8?q?PDF=20=E2=82=AC149?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - supplier_basic: monthly_price/yearly_price → 0 (free tier, no Paddle subscription) - boost_card_color: price 19 → 59 (aligns with MARKETING.md) - setup_paddle.py: Basic products commented out, card_color 1900→5900, business_plan 9900→14900 - export.html: business plan PDF price €99 → €149 Co-Authored-By: Claude Sonnet 4.6 --- .../padelnomics/planner/templates/export.html | 2 +- web/src/padelnomics/scripts/setup_paddle.py | 30 +++++++------------ web/src/padelnomics/suppliers/routes.py | 8 ++--- 3 files changed, 16 insertions(+), 24 deletions(-) diff --git a/web/src/padelnomics/planner/templates/export.html b/web/src/padelnomics/planner/templates/export.html index a2446dd..e962fb8 100644 --- a/web/src/padelnomics/planner/templates/export.html +++ b/web/src/padelnomics/planner/templates/export.html @@ -43,7 +43,7 @@

{{ t.export_subtitle }}

-
€99 one-time
+
€149 one-time
{{ t.sup_growth_cta }} +

{{ t.sup_pricing_eur_note }}

@@ -518,9 +543,20 @@
  • {{ t.sup_pro_f6 }}
  • {{ t.sup_pro_cta }} +

    {{ t.sup_pricing_eur_note }}

    + +
    +

    {{ t.sup_roi_line }}

    +
    + + +
    +

    {{ t.sup_credits_only_pre }} {{ t.sup_credits_only_cta }} {{ t.sup_credits_only_post }}

    +
    +

    {{ t.sup_boosts_h3 }}

    {{ t.sup_boosts_sub }}

    @@ -543,7 +579,7 @@
    {{ t.sup_boost_color }} - €19/mo + €59/mo
    @@ -566,7 +602,7 @@ {{ t.sup_cmp_row1 }} - €1,799/yr + €1,799/yr (yearly plan) €10,000+/event €5,000+/yr* €600/yr @@ -612,22 +648,6 @@

    {{ t.sup_cmp_footnote }}

    - -
    -

    {{ t.sup_proof_h2 }}

    -

    {{ calc_requests }}+ {{ t.sup_proof_stat1 }} · {{ total_suppliers }}+ {{ t.sup_proof_stat2 }} · {{ total_countries }} {{ t.sup_proof_stat3 }}

    -
    -
    -
    “{{ t.sup_proof_q1 }}”
    - {{ t.sup_proof_cite1 }} -
    -
    -
    “{{ t.sup_proof_q2 }}”
    - {{ t.sup_proof_cite2 }} -
    -
    -
    -

    {{ t.sup_faq_h2 }}

    @@ -679,7 +699,7 @@

    {{ t.sup_cta_h2 }}

    {{ t.sup_cta_p }}

    - {{ t.sup_hero_cta }} + {{ t.sup_cta_btn }}
    diff --git a/web/src/padelnomics/suppliers/templates/suppliers/partials/signup_step_1.html b/web/src/padelnomics/suppliers/templates/suppliers/partials/signup_step_1.html index 8afd96c..cd48e4d 100644 --- a/web/src/padelnomics/suppliers/templates/suppliers/partials/signup_step_1.html +++ b/web/src/padelnomics/suppliers/templates/suppliers/partials/signup_step_1.html @@ -64,12 +64,22 @@ {% if key == 'supplier_growth' %}{% endif %}

    {{ plan.name }}

    + {% if plan.yearly_price == 0 %} +
    {{ t.sup_basic_free_label }}
    +
    {{ t.sup_step1_free_forever }}
    + {% else %}
    €{{ plan.yearly_monthly_equivalent }} /mo
    {{ t.sup_step1_billed_yearly | tformat(price=plan.yearly_price) }}
    + {% endif %}
    + {% if plan.monthly_price == 0 %} +
    {{ t.sup_basic_free_label }}
    +
    {{ t.sup_step1_free_forever }}
    + {% else %}
    €{{ plan.monthly_price }} /mo
    {{ t.sup_step1_billed_monthly }}
    + {% endif %}
      {% for key in plan.feature_keys %} From a1e2a5aa8d0211736fd33db87376ae5088952eba Mon Sep 17 00:00:00 2001 From: Deeman Date: Thu, 26 Feb 2026 15:17:30 +0100 Subject: [PATCH 3/5] content: update EN+DE copy for pricing overhaul MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EN changes: - sup_meta_desc: remove "from €39/mo", lead with free listing + qualified leads - sup_hero_cta / sup_cta_btn: "See Plans & Pricing" → "Get Started Free" - sup_basic_dir: "Directory listing" → "Free forever" - sup_basic_cta: "Get Listed" → "List Your Company Free" - sup_yearly_note_basic: remove €349 price → "Free forever" - sup_boosts_sub: add card color €59/mo note - sup_faq_a2: update Basic from €39 to free, remove Basic yearly price - sup_faq_q5/a5: rename to include Lead-Back Guarantee; add guarantee mechanic New keys (EN + DE): sup_cta_btn, sup_basic_free_label, sup_pricing_eur_note, sup_guarantee_h2/p/badge, sup_leads_section_h2/sub, sup_leads_unlock_cta, sup_roi_line, sup_credits_only_pre/cta/post, sup_step1_free_forever, sd_guarantee_btn/contact_label/email/phone/both/submit/success/window_error/already_claimed DE: all above translated with native German register (du, compound nouns, no calque constructions) Co-Authored-By: Claude Sonnet 4.6 --- web/src/padelnomics/locales/de.json | 174 ++++++++++++++-------------- web/src/padelnomics/locales/en.json | 41 +++++-- 2 files changed, 120 insertions(+), 95 deletions(-) diff --git a/web/src/padelnomics/locales/de.json b/web/src/padelnomics/locales/de.json index ee73447..920e7b9 100644 --- a/web/src/padelnomics/locales/de.json +++ b/web/src/padelnomics/locales/de.json @@ -888,11 +888,11 @@ "month_nov": "Nov", "month_dec": "Dez", "sup_meta_title": "Für Anbieter – Erreiche Padel-Unternehmer", - "sup_meta_desc": "Werde auf Padelnomics gelistet. Erreiche Unternehmer, die bereits einen Finanzplan für ihr Padel-Projekt erstellt haben. Basic, Growth und Pro ab €39/Monat.", + "sup_meta_desc": "Kostenloser Verzeichniseintrag auf Padelnomics. Qualifizierte Leads von Interessenten mit fertigem Businessplan. Growth- und Pro-Pläne ab €199/Monat.", "sup_hero_h1a": "Kein Kaltakquise mehr.", "sup_hero_h1b": "Triff Käufer, die bereits einen Businessplan haben.", "sup_hero_sub": "Jeder Lead auf Padelnomics hat CAPEX, Umsatz und ROI bereits modelliert – bevor er dich kontaktiert. Keine Zeitverschwender. Kein „ich schau mich nur um.“", - "sup_hero_cta": "Pläne & Preise ansehen", + "sup_hero_cta": "Kostenlos starten", "sup_hero_trust_pre": "Vertrauen von Anbietern in", "sup_hero_trust_post": "Ländern", "sup_stat_plans": "erstellte Businesspläne", @@ -932,7 +932,7 @@ "sup_lead_timeline": "Zeitplan", "sup_lead_contact": "Kontakt", "sup_leads_unlock_pre": "Vollständige Kontaktdaten und Projektspezifikationen mit Credits freischalten.", - "sup_leads_unlock_cta": "Jetzt starten →", + "sup_leads_unlock_cta": "Leads freischalten →", "sup_leads_example": "Dies sind Beispiel-Leads. Echte Leads erscheinen, sobald Unternehmer Angebotsanfragen einreichen.", "sup_why_h2": "Warum Padelnomics-Leads anders sind", "sup_why_sub": "Jeder Lead hat bereits ein Finanzmodell für sein Projekt erstellt.", @@ -948,14 +948,14 @@ "sup_billing_yearly": "Jährlich", "sup_billing_save": "Bis zu 26% sparen", "sup_basic_name": "Basic", - "sup_basic_dir": "Verzeichniseintrag", + "sup_basic_dir": "Dauerhaft kostenlos", "sup_basic_f1": "Verifiziert ✓ Badge", "sup_basic_f2": "Firmenlogo", "sup_basic_f3": "Vollständige Beschreibung & Slogan", "sup_basic_f4": "Website & Kontaktdaten", "sup_basic_f5": "Checkliste der angebotenen Leistungen", "sup_basic_f6": "Kontaktformular auf der Listing-Seite", - "sup_basic_cta": "Jetzt listen", + "sup_basic_cta": "Unternehmen kostenlos eintragen", "sup_growth_name": "Growth", "sup_growth_popular": "Beliebtester Plan", "sup_growth_credits": "30 Credits/Monat inklusive", @@ -975,11 +975,11 @@ "sup_pro_f5": "Bevorzugte Platzierung im Verzeichnis", "sup_pro_f6": "100 Lead-Credits pro Monat", "sup_pro_cta": "Jetzt starten", - "sup_yearly_note_basic": "€349 jährlich", + "sup_yearly_note_basic": "Dauerhaft kostenlos", "sup_yearly_note_growth": "€1.799 jährlich", "sup_yearly_note_pro": "€4.499 jährlich", "sup_boosts_h3": "Boost Add-Ons", - "sup_boosts_sub": "Mit jedem kostenpflichtigen Plan verfügbar. Im Dashboard verwalten.", + "sup_boosts_sub": "Für jeden Plan verfügbar. Im Dashboard verwalten. Kartenfarbe ab €59/Monat.", "sup_boost_logo": "Logo", "sup_boost_highlight": "Hervorhebung", "sup_boost_verified": "Verifiziert-Badge", @@ -1026,13 +1026,13 @@ "sup_faq_a1_post": "und klicke auf „Ist das dein Unternehmen?“ Wir überprüfen deine Identität und geben dir Zugang, um einen Plan auszuwählen und dein Profil zu aktualisieren.", "sup_faq_dir_link": "Verzeichnis", "sup_faq_q2": "Wie viel kostet es?", - "sup_faq_a2": "Wir bieten drei Pläne an: Basic (€39/Monat) für einen verifizierten Verzeichniseintrag mit Kontaktformular; Growth (€199/Monat, 30 Credits) mit vollem Lead-Zugang und Prioritätsplatzierung; und Pro (€499/Monat, 100 Credits) für maximale Sichtbarkeit und Lead-Volumen. Jährliche Abrechnung spart bis zu 26 % – Basic bei €349/Jahr, Growth bei €1.799/Jahr, Pro bei €4.499/Jahr. Optionale Boost-Add-Ons sind zusätzlich erhältlich.", + "sup_faq_a2": "Basic ist kostenlos – verifizierter Verzeichniseintrag mit Kontaktformular, kein Abo nötig. Growth (€199/Monat, 30 Credits) bietet vollen Lead-Zugang und Prioritätsplatzierung. Pro (€499/Monat, 100 Credits) maximiert Sichtbarkeit und Lead-Volumen. Jährliche Abrechnung spart bis zu 25 %: Growth bei €1.799/Jahr, Pro bei €4.499/Jahr. Optionale Boost-Add-Ons sind für jeden Plan verfügbar.", "sup_faq_q3": "Was macht Padelnomics-Leads anders als andere Plattformen?", "sup_faq_a3": "Jeder Lead auf Padelnomics hat unser Finanzplanungstool genutzt, um sein Projekt zu modellieren – CAPEX, Umsatzprognosen, ROI und Schuldendienstdeckung – bevor er sich meldet. Das bedeutet: sie sind ernst, haben ein realistisches Budget und sind bereit, mit Anbietern zu sprechen. Du bekommst keine Kaltanfragen, sondern vorqualifizierte Projektbriefings.", "sup_faq_q4": "Wie sieht der Preisvergleich mit Alternativen aus?", "sup_faq_a4": "Eine Messepräsenz kostet €10.000+ pro Veranstaltung und liefert meist nur Browsing-Kontakte. Google Ads für Padel-Baukeywords kosten €20–80 pro Klick – das sind €5.000+/Jahr, bevor du mit einem einzigen Interessenten sprichst. Ein typischer Kaltverzeichniseintrag kostet ~€600/Jahr ohne einen einzigen Lead. Padelnomics Growth bei €1.799/Jahr beinhaltet 30 Lead-Credits pro Monat mit vollständigen Projektbriefings.", - "sup_faq_q5": "Wie funktionieren Credits?", - "sup_faq_a5": "Mit Credits schaltest du die Kontaktdaten von Leads frei. Jeder Plan beinhaltet monatliche Credits (Growth: 30, Pro: 100). Heiße Leads kosten 35 Credits, warme 20 und coole 8. Du kannst jederzeit zusätzliche Credit-Pakete über dein Dashboard kaufen. Ungenutzte Credits werden auf den nächsten Monat übertragen.", + "sup_faq_q5": "Wie funktionieren Credits – und was ist die Lead-Back-Garantie?", + "sup_faq_a5": "Credits schalten die Kontaktdaten von Leads frei. Growth enthält 30 Credits/Monat, Pro 100. Heiße Leads kosten 35 Credits, warme 20, coole 8. Credit-Pakete sind auch ohne Abo erhältlich. Ungenutzte Credits werden übertragen. Wenn ein Lead nicht antwortet, klickst du nach 3 Werktagen auf „Lead hat nicht geantwortet“ – die Credits werden sofort zurückgebucht, ohne Support-Ticket.", "sup_faq_q6": "Welche Informationen enthalten Leads?", "sup_faq_a6": "Jeder Lead enthält: Anlagentyp (innen/außen), Court-Anzahl, Glas- und Beleuchtungsvorlieben, Land und Stadt, Budgetschätzung, Projektphase, Zeitplan, Finanzierungsstatus, Stakeholder-Typ, benötigte Leistungen und vollständige Kontaktdaten.", "sup_faq_q7": "Wie werden Leads Anbietern zugeordnet?", @@ -1471,7 +1471,6 @@ "sd_flash_valid_email": "Bitte gib eine gültige E-Mail-Adresse ein.", "sd_flash_claim_error": "Dieser Eintrag wurde bereits beansprucht oder existiert nicht.", "sd_flash_listing_saved": "Eintrag erfolgreich gespeichert.", - "bp_indoor": "Indoor", "bp_outdoor": "Outdoor", "bp_own": "Kauf", @@ -1480,49 +1479,41 @@ "bp_payback_not_reached": "Nicht in 60 Monaten erreicht", "bp_months": "{n} Monate", "bp_years": "{n} Jahre", - "bp_exec_paragraph": "Dieser Businessplan modelliert eine {facility_type}-Padel-Anlage mit {courts} Pl\u00e4tzen ({sqm} m\u00b2). Die Gesamtinvestition betr\u00e4gt {total_capex}, finanziert mit {equity} Eigenkapital und {loan} Fremdkapital. Die prognostizierte IRR betr\u00e4gt {irr} bei einer Amortisationszeit von {payback}.", - + "bp_exec_paragraph": "Dieser Businessplan modelliert eine {facility_type}-Padel-Anlage mit {courts} Plätzen ({sqm} m²). Die Gesamtinvestition beträgt {total_capex}, finanziert mit {equity} Eigenkapital und {loan} Fremdkapital. Die prognostizierte IRR beträgt {irr} bei einer Amortisationszeit von {payback}.", "bp_lbl_scenario": "Szenario", - "bp_lbl_generated_by": "Erstellt von Padelnomics \u2014 padelnomics.io", - + "bp_lbl_generated_by": "Erstellt von Padelnomics — padelnomics.io", "bp_lbl_total_investment": "Gesamtinvestition", "bp_lbl_equity_required": "Eigenkapitalbedarf", "bp_lbl_year3_ebitda": "EBITDA Jahr 3", "bp_lbl_irr": "IRR", "bp_lbl_payback_period": "Amortisationszeit", "bp_lbl_year1_revenue": "Umsatz Jahr 1", - "bp_lbl_item": "Position", "bp_lbl_amount": "Betrag", "bp_lbl_notes": "Hinweise", "bp_lbl_total_capex": "Gesamt-CAPEX", - "bp_lbl_capex_stats": "CAPEX je Platz: {per_court} \u2022 CAPEX je m\u00b2: {per_sqm}", - + "bp_lbl_capex_stats": "CAPEX je Platz: {per_court} • CAPEX je m²: {per_sqm}", "bp_lbl_equity": "Eigenkapital", "bp_lbl_loan": "Darlehen", "bp_lbl_interest_rate": "Zinssatz", "bp_lbl_loan_term": "Darlehenslaufzeit", "bp_lbl_monthly_payment": "Monatliche Rate", - "bp_lbl_annual_debt_service": "J\u00e4hrlicher Schuldendienst", + "bp_lbl_annual_debt_service": "Jährlicher Schuldendienst", "bp_lbl_ltv": "Beleihungsauslauf", - "bp_lbl_monthly": "Monatlich", "bp_lbl_total_monthly_opex": "Monatlicher OPEX gesamt", "bp_lbl_annual_opex": "Jahres-OPEX", - "bp_lbl_weighted_hourly_rate": "Gewichteter Stundensatz", "bp_lbl_target_utilization": "Zielauslastung", "bp_lbl_gross_monthly_revenue": "Monatlicher Bruttoumsatz", "bp_lbl_net_monthly_revenue": "Monatlicher Nettoumsatz", "bp_lbl_monthly_ebitda": "Monatliches EBITDA", "bp_lbl_monthly_net_cf": "Monatlicher Netto-Cashflow", - "bp_lbl_year": "Jahr", "bp_lbl_revenue": "Umsatz", "bp_lbl_ebitda": "EBITDA", "bp_lbl_debt_service": "Schuldendienst", "bp_lbl_net_cf": "Netto-CF", - "bp_lbl_moic": "MOIC", "bp_lbl_cash_on_cash": "Cash-on-Cash (J3)", "bp_lbl_payback": "Amortisation", @@ -1530,116 +1521,127 @@ "bp_lbl_ebitda_margin": "EBITDA-Marge", "bp_lbl_dscr_y3": "DSCR (J3)", "bp_lbl_yield_on_cost": "Rendite auf Kosten", - "bp_lbl_month": "Monat", "bp_lbl_opex": "OPEX", "bp_lbl_debt": "Schulden", "bp_lbl_cumulative": "Kumulativ", - - "bp_lbl_disclaimer": "Haftungsausschluss: Dieser Businessplan wurde auf Basis benutzerdefinierter Annahmen mit dem Padelnomics-Finanzmodell erstellt. Alle Prognosen sind Sch\u00e4tzungen und stellen keine Finanzberatung dar. Die tats\u00e4chlichen Ergebnisse k\u00f6nnen je nach Marktbedingungen, Umsetzung und anderen Faktoren erheblich abweichen. Konsultiere Finanzberater, bevor du Investitionsentscheidungen triffst. \u00a9 Padelnomics \u2014 padelnomics.io", - + "bp_lbl_disclaimer": "Haftungsausschluss: Dieser Businessplan wurde auf Basis benutzerdefinierter Annahmen mit dem Padelnomics-Finanzmodell erstellt. Alle Prognosen sind Schätzungen und stellen keine Finanzberatung dar. Die tatsächlichen Ergebnisse können je nach Marktbedingungen, Umsetzung und anderen Faktoren erheblich abweichen. Konsultiere Finanzberater, bevor du Investitionsentscheidungen triffst. © Padelnomics — padelnomics.io", "email_magic_link_heading": "Bei {app_name} anmelden", - "email_magic_link_body": "Hier ist dein Anmeldelink. Er l\u00e4uft in {expiry_minutes} Minuten ab.", - "email_magic_link_btn": "Anmelden \u2192", + "email_magic_link_body": "Hier ist dein Anmeldelink. Er läuft in {expiry_minutes} Minuten ab.", + "email_magic_link_btn": "Anmelden →", "email_magic_link_fallback": "Wenn der Button nicht funktioniert, kopiere diese URL in deinen Browser:", "email_magic_link_ignore": "Wenn du das nicht angefordert hast, kannst du diese E-Mail ignorieren.", - "email_magic_link_subject": "Dein Anmeldelink f\u00fcr {app_name}", - "email_magic_link_preheader": "Dieser Link l\u00e4uft in {expiry_minutes} Minuten ab", - - "email_quote_verify_heading": "Best\u00e4tige deine E-Mail f\u00fcr Angebote", + "email_magic_link_subject": "Dein Anmeldelink für {app_name}", + "email_magic_link_preheader": "Dieser Link läuft in {expiry_minutes} Minuten ab", + "email_quote_verify_heading": "Bestätige deine E-Mail für Angebote", "email_quote_verify_greeting": "Hallo {first_name},", - "email_quote_verify_body": "Danke f\u00fcr deine Angebotsanfrage. Best\u00e4tige deine E-Mail, um deine Anfrage zu aktivieren und dein {app_name}-Konto zu erstellen.", + "email_quote_verify_body": "Danke für deine Angebotsanfrage. Bestätige deine E-Mail, um deine Anfrage zu aktivieren und dein {app_name}-Konto zu erstellen.", "email_quote_verify_project_label": "Dein Projekt:", "email_quote_verify_urgency": "Verifizierte Anfragen werden von unserem Anbieternetzwerk bevorzugt behandelt.", - "email_quote_verify_btn": "Best\u00e4tigen & Aktivieren \u2192", - "email_quote_verify_expires": "Dieser Link l\u00e4uft in 60 Minuten ab.", + "email_quote_verify_btn": "Bestätigen & Aktivieren →", + "email_quote_verify_expires": "Dieser Link läuft in 60 Minuten ab.", "email_quote_verify_fallback": "Wenn der Button nicht funktioniert, kopiere diese URL in deinen Browser:", "email_quote_verify_ignore": "Wenn du das nicht angefordert hast, kannst du diese E-Mail ignorieren.", - "email_quote_verify_subject": "Best\u00e4tige deine E-Mail \u2014 Anbieter sind bereit f\u00fcr Angebote", + "email_quote_verify_subject": "Bestätige deine E-Mail — Anbieter sind bereit für Angebote", "email_quote_verify_preheader": "Ein Klick, um deine Angebotsanfrage zu aktivieren", "email_quote_verify_preheader_courts": "Ein Klick, um dein {court_count}-Court-Projekt zu aktivieren", - "email_welcome_heading": "Willkommen bei {app_name}", "email_welcome_greeting": "Hallo {first_name},", - "email_welcome_body": "Du hast jetzt Zugang zum Finanzplaner, Marktdaten und dem Anbieterverzeichnis \u2014 alles, was du f\u00fcr die Planung deines Padel-Gesch\u00e4fts brauchst.", + "email_welcome_body": "Du hast jetzt Zugang zum Finanzplaner, Marktdaten und dem Anbieterverzeichnis — alles, was du für die Planung deines Padel-Geschäfts brauchst.", "email_welcome_quickstart_heading": "Schnellstart:", - "email_welcome_link_planner": "Finanzplaner \u2014 modelliere deine Investition", - "email_welcome_link_markets": "Marktdaten \u2014 erkunde die Padel-Nachfrage nach Stadt", - "email_welcome_link_quotes": "Angebote einholen \u2014 verbinde dich mit verifizierten Anbietern", - "email_welcome_btn": "Jetzt planen \u2192", - "email_welcome_subject": "Du bist dabei \u2014 so f\u00e4ngst du an", + "email_welcome_link_planner": "Finanzplaner — modelliere deine Investition", + "email_welcome_link_markets": "Marktdaten — erkunde die Padel-Nachfrage nach Stadt", + "email_welcome_link_quotes": "Angebote einholen — verbinde dich mit verifizierten Anbietern", + "email_welcome_btn": "Jetzt planen →", + "email_welcome_subject": "Du bist dabei — so fängst du an", "email_welcome_preheader": "Dein Padel-Planungstoolkit ist bereit", - "email_waitlist_supplier_heading": "Du stehst auf der Anbieter-Warteliste", - "email_waitlist_supplier_body": "Danke f\u00fcr dein Interesse am {plan_name}-Plan. Wir bauen eine Plattform, die dich mit qualifizierten Leads von Padel-Unternehmern verbindet, die aktiv Projekte planen.", - "email_waitlist_supplier_perks_intro": "Als fr\u00fches Wartelisten-Mitglied erh\u00e4ltst du:", - "email_waitlist_supplier_perk_1": "Fr\u00fchen Zugang vor dem \u00f6ffentlichen Launch", + "email_waitlist_supplier_body": "Danke für dein Interesse am {plan_name}-Plan. Wir bauen eine Plattform, die dich mit qualifizierten Leads von Padel-Unternehmern verbindet, die aktiv Projekte planen.", + "email_waitlist_supplier_perks_intro": "Als frühes Wartelisten-Mitglied erhältst du:", + "email_waitlist_supplier_perk_1": "Frühen Zugang vor dem öffentlichen Launch", "email_waitlist_supplier_perk_2": "Exklusive Launch-Preise (gesichert)", - "email_waitlist_supplier_perk_3": "Pers\u00f6nliches Onboarding-Gespr\u00e4ch", + "email_waitlist_supplier_perk_3": "Persönliches Onboarding-Gespräch", "email_waitlist_supplier_meanwhile": "In der Zwischenzeit erkunde unsere kostenlosen Ressourcen:", - "email_waitlist_supplier_link_planner": "Finanzplanungstool \u2014 plane deine Padel-Anlage", - "email_waitlist_supplier_link_directory": "Anbieterverzeichnis \u2014 verifizierte Anbieter durchsuchen", - "email_waitlist_supplier_subject": "Du bist dabei \u2014 {plan_name} fr\u00fcher Zugang kommt", + "email_waitlist_supplier_link_planner": "Finanzplanungstool — plane deine Padel-Anlage", + "email_waitlist_supplier_link_directory": "Anbieterverzeichnis — verifizierte Anbieter durchsuchen", + "email_waitlist_supplier_subject": "Du bist dabei — {plan_name} früher Zugang kommt", "email_waitlist_supplier_preheader": "Exklusive Launch-Preise + bevorzugtes Onboarding", "email_waitlist_general_heading": "Du stehst auf der Warteliste", - "email_waitlist_general_body": "Danke f\u00fcr deine Anmeldung. Wir bauen die Planungsplattform f\u00fcr Padel-Unternehmer \u2014 Finanzmodellierung, Marktdaten und Anbietervernetzung an einem Ort.", - "email_waitlist_general_perks_intro": "Als fr\u00fches Wartelisten-Mitglied erh\u00e4ltst du:", - "email_waitlist_general_perk_1": "Fr\u00fchen Zugang vor dem \u00f6ffentlichen Launch", + "email_waitlist_general_body": "Danke für deine Anmeldung. Wir bauen die Planungsplattform für Padel-Unternehmer — Finanzmodellierung, Marktdaten und Anbietervernetzung an einem Ort.", + "email_waitlist_general_perks_intro": "Als frühes Wartelisten-Mitglied erhältst du:", + "email_waitlist_general_perk_1": "Frühen Zugang vor dem öffentlichen Launch", "email_waitlist_general_perk_2": "Exklusive Launch-Preise", - "email_waitlist_general_perk_3": "Priorit\u00e4ts-Onboarding und Support", + "email_waitlist_general_perk_3": "Prioritäts-Onboarding und Support", "email_waitlist_general_outro": "Wir melden uns bald.", - "email_waitlist_general_subject": "Du stehst auf der Liste \u2014 wir benachrichtigen dich zum Launch", - "email_waitlist_general_preheader": "Fr\u00fcher Zugang + exklusive Launch-Preise", - + "email_waitlist_general_subject": "Du stehst auf der Liste — wir benachrichtigen dich zum Launch", + "email_waitlist_general_preheader": "Früher Zugang + exklusive Launch-Preise", "email_lead_forward_heading": "Neues Projekt-Lead", - "email_lead_forward_urgency": "Dieses Lead wurde gerade freigeschaltet. Anbieter, die innerhalb von 24 Stunden antworten, gewinnen 3x h\u00e4ufiger das Projekt.", + "email_lead_forward_urgency": "Dieses Lead wurde gerade freigeschaltet. Anbieter, die innerhalb von 24 Stunden antworten, gewinnen 3x häufiger das Projekt.", "email_lead_forward_section_brief": "Projektbeschreibung", "email_lead_forward_section_contact": "Kontakt", "email_lead_forward_lbl_facility": "Anlage", - "email_lead_forward_lbl_courts": "Pl\u00e4tze", + "email_lead_forward_lbl_courts": "Plätze", "email_lead_forward_lbl_location": "Standort", "email_lead_forward_lbl_timeline": "Zeitplan", "email_lead_forward_lbl_phase": "Phase", "email_lead_forward_lbl_services": "Leistungen", - "email_lead_forward_lbl_additional": "Zus\u00e4tzlich", + "email_lead_forward_lbl_additional": "Zusätzlich", "email_lead_forward_lbl_name": "Name", "email_lead_forward_lbl_email": "E-Mail", "email_lead_forward_lbl_phone": "Telefon", "email_lead_forward_lbl_company": "Unternehmen", "email_lead_forward_lbl_role": "Rolle", - "email_lead_forward_btn": "Im Lead-Feed ansehen \u2192", + "email_lead_forward_btn": "Im Lead-Feed ansehen →", "email_lead_forward_reply_direct": "oder direkt an {contact_email} antworten", "email_lead_forward_preheader_suffix": "Kontaktdaten enthalten", - - "email_lead_matched_heading": "Ein Anbieter m\u00f6chte dein Projekt besprechen", + "email_lead_matched_heading": "Ein Anbieter möchte dein Projekt besprechen", "email_lead_matched_greeting": "Hallo {first_name},", - "email_lead_matched_body": "Gute Nachrichten \u2014 ein verifizierter Anbieter wurde mit deinem Padel-Projekt abgeglichen. Er hat deine Projektbeschreibung und Kontaktdaten.", - "email_lead_matched_context": "Du hast eine Angebotsanfrage f\u00fcr eine {facility_type}-Anlage mit {court_count} Pl\u00e4tzen in {country} eingereicht.", - "email_lead_matched_next_heading": "Was passiert als N\u00e4chstes", - "email_lead_matched_next_body": "Der Anbieter hat deine Projektbeschreibung und Kontaktdaten erhalten. Die meisten Anbieter melden sich innerhalb von 24\u201348 Stunden per E-Mail oder Telefon.", - "email_lead_matched_tip": "Tipp: Schnelles Reagieren auf Anbieter-Kontaktaufnahmen erh\u00f6ht deine Chance auf wettbewerbsf\u00e4hige Angebote.", - "email_lead_matched_btn": "Zum Dashboard \u2192", - "email_lead_matched_note": "Du erh\u00e4ltst diese Benachrichtigung jedes Mal, wenn ein neuer Anbieter deine Projektdetails freischaltet.", - "email_lead_matched_subject": "{first_name}, ein Anbieter m\u00f6chte dein Projekt besprechen", - "email_lead_matched_preheader": "Der Anbieter wird sich direkt bei dir melden \u2014 das erwartet dich", - + "email_lead_matched_body": "Gute Nachrichten — ein verifizierter Anbieter wurde mit deinem Padel-Projekt abgeglichen. Er hat deine Projektbeschreibung und Kontaktdaten.", + "email_lead_matched_context": "Du hast eine Angebotsanfrage für eine {facility_type}-Anlage mit {court_count} Plätzen in {country} eingereicht.", + "email_lead_matched_next_heading": "Was passiert als Nächstes", + "email_lead_matched_next_body": "Der Anbieter hat deine Projektbeschreibung und Kontaktdaten erhalten. Die meisten Anbieter melden sich innerhalb von 24–48 Stunden per E-Mail oder Telefon.", + "email_lead_matched_tip": "Tipp: Schnelles Reagieren auf Anbieter-Kontaktaufnahmen erhöht deine Chance auf wettbewerbsfähige Angebote.", + "email_lead_matched_btn": "Zum Dashboard →", + "email_lead_matched_note": "Du erhältst diese Benachrichtigung jedes Mal, wenn ein neuer Anbieter deine Projektdetails freischaltet.", + "email_lead_matched_subject": "{first_name}, ein Anbieter möchte dein Projekt besprechen", + "email_lead_matched_preheader": "Der Anbieter wird sich direkt bei dir melden — das erwartet dich", "email_enquiry_heading": "Neue Anfrage von {contact_name}", - "email_enquiry_body": "Du hast eine neue Anfrage \u00fcber deinen {supplier_name}-Verzeichniseintrag.", + "email_enquiry_body": "Du hast eine neue Anfrage über deinen {supplier_name}-Verzeichniseintrag.", "email_enquiry_lbl_from": "Von", "email_enquiry_lbl_message": "Nachricht", - "email_enquiry_respond_fast": "Antworte innerhalb von 24 Stunden f\u00fcr den besten Eindruck.", + "email_enquiry_respond_fast": "Antworte innerhalb von 24 Stunden für den besten Eindruck.", "email_enquiry_reply": "Antworte direkt an {contact_email}.", - "email_enquiry_subject": "Neue Anfrage von {contact_name} \u00fcber deinen Verzeichniseintrag", + "email_enquiry_subject": "Neue Anfrage von {contact_name} über deinen Verzeichniseintrag", "email_enquiry_preheader": "Antworte, um mit diesem potenziellen Kunden in Kontakt zu treten", - "email_business_plan_heading": "Dein Businessplan ist fertig", "email_business_plan_body": "Dein Padel-Businessplan wurde als PDF erstellt und steht zum Download bereit.", - "email_business_plan_includes": "Dein Plan enth\u00e4lt Investitions\u00fcbersicht, Umsatzprognosen und Break-Even-Analyse.", - "email_business_plan_btn": "PDF herunterladen \u2192", - "email_business_plan_quote_cta": "Bereit f\u00fcr den n\u00e4chsten Schritt? Angebote von Anbietern einholen \u2192", + "email_business_plan_includes": "Dein Plan enthält Investitionsübersicht, Umsatzprognosen und Break-Even-Analyse.", + "email_business_plan_btn": "PDF herunterladen →", + "email_business_plan_quote_cta": "Bereit für den nächsten Schritt? Angebote von Anbietern einholen →", "email_business_plan_subject": "Dein Businessplan-PDF steht zum Download bereit", - "email_business_plan_preheader": "Professioneller Padel-Finanzplan \u2014 jetzt herunterladen", - - "email_footer_tagline": "Die Planungsplattform f\u00fcr Padel-Unternehmer", - "email_footer_copyright": "\u00a9 {year} {app_name}. Du erh\u00e4ltst diese E-Mail, weil du ein Konto hast oder eine Anfrage gestellt hast." + "email_business_plan_preheader": "Professioneller Padel-Finanzplan — jetzt herunterladen", + "email_footer_tagline": "Die Planungsplattform für Padel-Unternehmer", + "email_footer_copyright": "© {year} {app_name}. Du erhältst diese E-Mail, weil du ein Konto hast oder eine Anfrage gestellt hast.", + "sup_cta_btn": "Kostenlos starten", + "sup_basic_free_label": "Kostenlos", + "sup_pricing_eur_note": "Alle Preise in EUR", + "sup_guarantee_h2": "Lead-Back-Garantie", + "sup_guarantee_p": "Wenn ein Lead nicht antwortet, Credits per Klick zurückbuchen. Kein Support-Ticket, kein Warten – nach 3 Werktagen einfach im Dashboard auf „Lead hat nicht geantwortet“ klicken.", + "sup_guarantee_badge": "Garantie ohne Risiko", + "sup_leads_section_h2": "So sehen deine Interessenten aus", + "sup_leads_section_sub": "Jeder Lead hat unseren Finanzplaner genutzt. Kontaktdaten werden nach dem Freischalten sichtbar.", + "sup_roi_line": "Ein einziges 4-Court-Projekt = €30.000+ Gewinn. Growth-Plan: €2.388/Jahr. Die Rechnung ist einfach.", + "sup_credits_only_pre": "Noch nicht bereit für ein Abo? Kaufe ein Credit-Paket und schalte Leads einzeln frei. Keine Bindung, keine Monatsgebühr.", + "sup_credits_only_cta": "Credits kaufen →", + "sup_credits_only_post": "", + "sup_step1_free_forever": "Dauerhaft kostenlos", + "sd_guarantee_btn": "Lead hat nicht geantwortet", + "sd_guarantee_contact_label": "Wie hast du versucht, den Lead zu erreichen?", + "sd_guarantee_contact_email": "E-Mail", + "sd_guarantee_contact_phone": "Telefon", + "sd_guarantee_contact_both": "Beides – E-Mail und Telefon", + "sd_guarantee_submit": "Credits zurückbuchen", + "sd_guarantee_success": "Credits wurden deinem Guthaben gutgeschrieben.", + "sd_guarantee_window_error": "Garantiezeitraum abgelaufen (nur 3–30 Tage nach dem Freischalten verfügbar).", + "sd_guarantee_already_claimed": "Du hast für diesen Lead bereits eine Rückerstattung beantragt." } diff --git a/web/src/padelnomics/locales/en.json b/web/src/padelnomics/locales/en.json index 70927f4..a8d4ca6 100644 --- a/web/src/padelnomics/locales/en.json +++ b/web/src/padelnomics/locales/en.json @@ -888,11 +888,11 @@ "month_nov": "Nov", "month_dec": "Dec", "sup_meta_title": "For Suppliers - Reach Padel Entrepreneurs", - "sup_meta_desc": "Get listed on Padelnomics. Reach entrepreneurs who've already built a financial model for their padel project. Basic, Growth and Pro plans from €39/mo.", + "sup_meta_desc": "Free directory listing on Padelnomics. Qualified leads from buyers with business plans. Growth and Pro plans from €199/mo.", "sup_hero_h1a": "Stop Chasing Cold Leads.", "sup_hero_h1b": "Meet Buyers Who Already Have a Business Plan.", "sup_hero_sub": "Every lead on Padelnomics has modeled their CAPEX, projected revenue, and calculated ROI — before they contact you. No tire-kickers. No “just browsing.”", - "sup_hero_cta": "See Plans & Pricing", + "sup_hero_cta": "Get Started Free", "sup_hero_trust_pre": "Trusted by suppliers in", "sup_hero_trust_post": "countries", "sup_stat_plans": "Business plans created", @@ -948,14 +948,14 @@ "sup_billing_yearly": "Yearly", "sup_billing_save": "Save up to 26%", "sup_basic_name": "Basic", - "sup_basic_dir": "Directory listing", + "sup_basic_dir": "Free forever", "sup_basic_f1": "Verified ✓ badge", "sup_basic_f2": "Company logo", "sup_basic_f3": "Full description & tagline", "sup_basic_f4": "Website & contact details", "sup_basic_f5": "Services offered checklist", "sup_basic_f6": "Enquiry form on listing page", - "sup_basic_cta": "Get Listed", + "sup_basic_cta": "List Your Company Free", "sup_growth_name": "Growth", "sup_growth_popular": "Most Popular", "sup_growth_credits": "30 credits/mo included", @@ -975,11 +975,11 @@ "sup_pro_f5": "Priority placement in directory", "sup_pro_f6": "100 lead credits per month", "sup_pro_cta": "Get Started", - "sup_yearly_note_basic": "€349 billed yearly", + "sup_yearly_note_basic": "Free forever", "sup_yearly_note_growth": "€1,799 billed yearly", "sup_yearly_note_pro": "€4,499 billed yearly", "sup_boosts_h3": "Boost Add-Ons", - "sup_boosts_sub": "Available with any paid plan. Manage from your dashboard.", + "sup_boosts_sub": "Available on any plan. Manage from your dashboard. Card color from €59/mo.", "sup_boost_logo": "Logo", "sup_boost_highlight": "Highlight", "sup_boost_verified": "Verified Badge", @@ -1026,13 +1026,13 @@ "sup_faq_a1_post": "and click “Is this your company?” We’ll verify your identity and give you access to choose a plan and upgrade your profile.", "sup_faq_dir_link": "directory", "sup_faq_q2": "How much does it cost?", - "sup_faq_a2": "We offer three plans: Basic (€39/mo) for a verified directory listing with enquiry form; Growth (€199/mo, 30 credits) with full lead access and priority placement; and Pro (€499/mo, 100 credits) for maximum visibility and lead volume. Yearly billing saves up to 26% — Basic at €349/yr, Growth at €1,799/yr, Pro at €4,499/yr. Optional boost add-ons are available on top.", + "sup_faq_a2": "Basic is free — a verified directory listing with an enquiry form, no subscription required. Growth (€199/mo, 30 credits) gives full lead access and priority placement. Pro (€499/mo, 100 credits) adds maximum visibility and lead volume. Yearly billing saves up to 25%: Growth at €1,799/yr, Pro at €4,499/yr. Optional boost add-ons are available on any plan.", "sup_faq_q3": "What makes Padelnomics leads different from other platforms?", "sup_faq_a3": "Every lead on Padelnomics has used our financial planning tool to model their project — CAPEX, revenue projections, ROI, and debt service coverage — before reaching out. This means they’re serious, they have a realistic budget, and they’re ready to talk to suppliers. You’re not getting cold enquiries; you’re getting pre-qualified project briefs.", "sup_faq_q4": "How does pricing compare to alternatives?", "sup_faq_a4": "A trade show booth costs €10,000+ per event and delivers mostly browsing contacts. Google Ads for padel construction keywords run €20–80 per click — that’s €5,000+/yr before you talk to a single prospect. A typical cold directory listing charges ~€600/yr with no leads at all. Padelnomics Growth at €1,799/yr includes 30 lead credits per month with full project briefs.", - "sup_faq_q5": "How do credits work?", - "sup_faq_a5": "Credits are how you unlock lead contact details. Each plan includes monthly credits (Growth: 30, Pro: 100). Hot leads cost 35 credits, warm leads 20, and cool leads 8. You can buy additional credit packs anytime from your dashboard. Unused credits roll over month to month.", + "sup_faq_q5": "How do credits work — and what's the Lead-Back Guarantee?", + "sup_faq_a5": "Credits unlock lead contact details. Growth includes 30 credits/mo; Pro includes 100. Hot leads cost 35 credits, warm leads 20, cool leads 8. You can also buy credit packs without a subscription. Unused credits roll over. If a lead doesn't respond, click \"Lead didn't respond\" on the card after 3 business days and your credits are instantly returned to your balance — no support ticket, no waiting.", "sup_faq_q6": "What information do leads include?", "sup_faq_a6": "Every lead includes: facility type (indoor/outdoor), court count, glass and lighting preferences, country and city, budget estimate, project phase, timeline, financing status, stakeholder type, services needed, and full contact details.", "sup_faq_q7": "How are leads matched to suppliers?", @@ -1047,6 +1047,29 @@ "sup_faq_a10_post": "with your company details and we’ll add you to the directory within 48 hours.", "sup_cta_h2": "Your Next Client Is Already Building a Business Plan", "sup_cta_p": "They’ve modeled the ROI. They know their budget. They’re looking for a supplier like you.", + "sup_cta_btn": "Get Started Free", + "sup_basic_free_label": "Free", + "sup_pricing_eur_note": "All prices in EUR", + "sup_guarantee_h2": "Lead-Back Guarantee", + "sup_guarantee_p": "If a lead doesn’t respond, claim your credits back with one click. No support tickets, no waiting — just click \"Lead didn’t respond\" in your dashboard after 3 business days.", + "sup_guarantee_badge": "No-risk guarantee", + "sup_leads_section_h2": "See What Your Prospects Look Like", + "sup_leads_section_sub": "Every lead has used our financial planner. Contact details are blurred until you unlock.", + "sup_leads_unlock_cta": "Start Getting Leads", + "sup_roi_line": "A single 4-court project = €30,000+ in profit. Growth plan costs €2,388/year. The math is simple.", + "sup_credits_only_pre": "Not ready for a subscription? Buy a credit pack and unlock leads one at a time. No commitment, no monthly fee.", + "sup_credits_only_cta": "Buy Credits →", + "sup_credits_only_post": "", + "sup_step1_free_forever": "Free forever", + "sd_guarantee_btn": "Lead didn’t respond", + "sd_guarantee_contact_label": "How did you try to reach them?", + "sd_guarantee_contact_email": "Email", + "sd_guarantee_contact_phone": "Phone", + "sd_guarantee_contact_both": "Both email and phone", + "sd_guarantee_submit": "Claim credits back", + "sd_guarantee_success": "Credits returned to your balance.", + "sd_guarantee_window_error": "Guarantee window has closed (only available 3–30 days after unlock).", + "sd_guarantee_already_claimed": "You’ve already claimed a refund for this lead.", "scenario_cta_try_numbers": "Try with your own numbers →", "scenario_payback_label": "Payback", "scenario_months_unit": "months", From cc43d936f05dba3815640e502533655ff1c7b849 Mon Sep 17 00:00:00 2001 From: Deeman Date: Thu, 26 Feb 2026 15:22:52 +0100 Subject: [PATCH 4/5] =?UTF-8?q?feat:=20lead-back=20guarantee=20=E2=80=94?= =?UTF-8?q?=20one-click=20credit=20refund=20after=203=20days=20no=20respon?= =?UTF-8?q?se?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backend: - Migration 0020: add guarantee_claimed_at, guarantee_contact_method to lead_forwards - credits.py: refund_lead_guarantee() — validates 3–30 day window, reverses credit spend via ledger entry (event_type='guarantee_refund'), sets status='no_response' - GuaranteeAlreadyClaimed, GuaranteeWindowClosed exceptions - Route: POST /suppliers/leads//guarantee-claim — HTMX endpoint, returns updated lead card partial with success message - _get_lead_feed_data: pull forward_id, forward_created_at, guarantee_claimed_at so dashboard feed can show/hide the guarantee button per-lead UI: - lead_card_unlocked.html: "Lead didn't respond" button rendered client-side via JS (3–30 day window check in browser), shows contact method radio + submit - Success state and already-claimed state handled in partial EN/DE: remove empty sup_credits_only_post key (fails i18n parity test) Co-Authored-By: Claude Sonnet 4.6 --- web/src/padelnomics/credits.py | 91 ++++++++++++++++++- web/src/padelnomics/locales/de.json | 1 - web/src/padelnomics/locales/en.json | 78 ++++++---------- .../versions/0020_lead_guarantee_columns.py | 16 ++++ .../public/templates/suppliers.html | 2 +- web/src/padelnomics/suppliers/routes.py | 60 +++++++++++- .../partials/lead_card_unlocked.html | 46 ++++++++++ 7 files changed, 238 insertions(+), 56 deletions(-) create mode 100644 web/src/padelnomics/migrations/versions/0020_lead_guarantee_columns.py diff --git a/web/src/padelnomics/credits.py b/web/src/padelnomics/credits.py index ac58f37..79cf79d 100644 --- a/web/src/padelnomics/credits.py +++ b/web/src/padelnomics/credits.py @@ -5,7 +5,7 @@ All balance mutations go through this module to keep credit_ledger (source of tr and suppliers.credit_balance (denormalized cache) in sync within a single transaction. """ -from datetime import datetime +from datetime import UTC, datetime from .core import execute, fetch_all, fetch_one, transaction @@ -204,3 +204,92 @@ async def get_ledger(supplier_id: int, limit: int = 50) -> list[dict]: ORDER BY cl.created_at DESC LIMIT ?""", (supplier_id, limit), ) + + +class GuaranteeAlreadyClaimed(Exception): + """Raised when supplier tries to claim guarantee twice for the same lead.""" + + +class GuaranteeWindowClosed(Exception): + """Raised when the 3-30 day guarantee window has passed.""" + + +async def refund_lead_guarantee( + supplier_id: int, + forward_id: int, + contact_method: str, +) -> int: + """Refund credits for a non-responding lead. Returns new balance. + + Preconditions: + - forward must exist, belong to supplier_id, status='sent' + - guarantee_claimed_at must be NULL (not already claimed) + - created_at must be 3–30 days ago (calendar days) + - contact_method must be one of: 'email', 'phone', 'both' + + The refund is a ledger credit entry (event_type='guarantee_refund') referencing + the forward_id. lead_forwards.status is updated to 'no_response'. + We keep the cash — only credits are returned to the supplier balance. + """ + assert contact_method in ("email", "phone", "both"), ( + f"Invalid contact_method: {contact_method!r}" + ) + + forward = await fetch_one( + "SELECT * FROM lead_forwards WHERE id = ? AND supplier_id = ?", + (forward_id, supplier_id), + ) + assert forward is not None, f"Forward {forward_id} not found for supplier {supplier_id}" + + if forward["guarantee_claimed_at"] is not None: + raise GuaranteeAlreadyClaimed( + f"Guarantee already claimed for forward {forward_id}" + ) + + # Check 3–30 day window (calendar days) + created = datetime.fromisoformat(forward["created_at"].replace("Z", "+00:00")) + now = datetime.now(UTC) + age_days = (now - created).days + + if age_days < 3 or age_days > 30: + raise GuaranteeWindowClosed( + f"Guarantee window closed: forward is {age_days} days old (must be 3–30)" + ) + + refund_amount = forward["credit_cost"] + now_iso = datetime.utcnow().isoformat() + + async with transaction() as db: + row = await db.execute_fetchall( + "SELECT credit_balance FROM suppliers WHERE id = ?", (supplier_id,) + ) + current = row[0][0] if row else 0 + new_balance = current + refund_amount + + await db.execute( + """INSERT INTO credit_ledger + (supplier_id, delta, balance_after, event_type, reference_id, note, created_at) + VALUES (?, ?, ?, 'guarantee_refund', ?, ?, ?)""", + ( + supplier_id, + refund_amount, + new_balance, + forward_id, + f"Lead-back guarantee refund for forward #{forward_id}", + now_iso, + ), + ) + await db.execute( + "UPDATE suppliers SET credit_balance = ? WHERE id = ?", + (new_balance, supplier_id), + ) + await db.execute( + """UPDATE lead_forwards + SET status = 'no_response', + guarantee_claimed_at = ?, + guarantee_contact_method = ? + WHERE id = ?""", + (now_iso, contact_method, forward_id), + ) + + return new_balance diff --git a/web/src/padelnomics/locales/de.json b/web/src/padelnomics/locales/de.json index 920e7b9..d292f9a 100644 --- a/web/src/padelnomics/locales/de.json +++ b/web/src/padelnomics/locales/de.json @@ -1633,7 +1633,6 @@ "sup_roi_line": "Ein einziges 4-Court-Projekt = €30.000+ Gewinn. Growth-Plan: €2.388/Jahr. Die Rechnung ist einfach.", "sup_credits_only_pre": "Noch nicht bereit für ein Abo? Kaufe ein Credit-Paket und schalte Leads einzeln frei. Keine Bindung, keine Monatsgebühr.", "sup_credits_only_cta": "Credits kaufen →", - "sup_credits_only_post": "", "sup_step1_free_forever": "Dauerhaft kostenlos", "sd_guarantee_btn": "Lead hat nicht geantwortet", "sd_guarantee_contact_label": "Wie hast du versucht, den Lead zu erreichen?", diff --git a/web/src/padelnomics/locales/en.json b/web/src/padelnomics/locales/en.json index a8d4ca6..5c6af14 100644 --- a/web/src/padelnomics/locales/en.json +++ b/web/src/padelnomics/locales/en.json @@ -932,7 +932,7 @@ "sup_lead_timeline": "Timeline", "sup_lead_contact": "Contact", "sup_leads_unlock_pre": "Unlock full contact details and project specs with credits.", - "sup_leads_unlock_cta": "Get started →", + "sup_leads_unlock_cta": "Start Getting Leads", "sup_leads_example": "These are example leads. Real leads appear as entrepreneurs submit quote requests.", "sup_why_h2": "Why Padelnomics Leads Are Different", "sup_why_sub": "Every lead has already built a financial model for their project.", @@ -1055,11 +1055,9 @@ "sup_guarantee_badge": "No-risk guarantee", "sup_leads_section_h2": "See What Your Prospects Look Like", "sup_leads_section_sub": "Every lead has used our financial planner. Contact details are blurred until you unlock.", - "sup_leads_unlock_cta": "Start Getting Leads", "sup_roi_line": "A single 4-court project = €30,000+ in profit. Growth plan costs €2,388/year. The math is simple.", "sup_credits_only_pre": "Not ready for a subscription? Buy a credit pack and unlock leads one at a time. No commitment, no monthly fee.", "sup_credits_only_cta": "Buy Credits →", - "sup_credits_only_post": "", "sup_step1_free_forever": "Free forever", "sd_guarantee_btn": "Lead didn’t respond", "sd_guarantee_contact_label": "How did you try to reach them?", @@ -1494,7 +1492,6 @@ "sd_flash_valid_email": "Please enter a valid email address.", "sd_flash_claim_error": "This listing has already been claimed or does not exist.", "sd_flash_listing_saved": "Listing saved successfully.", - "bp_indoor": "Indoor", "bp_outdoor": "Outdoor", "bp_own": "Own", @@ -1503,24 +1500,20 @@ "bp_payback_not_reached": "Not reached in 60 months", "bp_months": "{n} months", "bp_years": "{n} years", - "bp_exec_paragraph": "This business plan models a {facility_type} padel facility with {courts} courts ({sqm} m\u00b2). Total investment is {total_capex}, financed with {equity} equity and {loan} debt. The projected IRR is {irr} with a payback period of {payback}.", - + "bp_exec_paragraph": "This business plan models a {facility_type} padel facility with {courts} courts ({sqm} m²). Total investment is {total_capex}, financed with {equity} equity and {loan} debt. The projected IRR is {irr} with a payback period of {payback}.", "bp_lbl_scenario": "Scenario", - "bp_lbl_generated_by": "Generated by Padelnomics \u2014 padelnomics.io", - + "bp_lbl_generated_by": "Generated by Padelnomics — padelnomics.io", "bp_lbl_total_investment": "Total Investment", "bp_lbl_equity_required": "Equity Required", "bp_lbl_year3_ebitda": "Year 3 EBITDA", "bp_lbl_irr": "IRR", "bp_lbl_payback_period": "Payback Period", "bp_lbl_year1_revenue": "Year 1 Revenue", - "bp_lbl_item": "Item", "bp_lbl_amount": "Amount", "bp_lbl_notes": "Notes", "bp_lbl_total_capex": "Total CAPEX", - "bp_lbl_capex_stats": "CAPEX per court: {per_court} \u2022 CAPEX per m\u00b2: {per_sqm}", - + "bp_lbl_capex_stats": "CAPEX per court: {per_court} • CAPEX per m²: {per_sqm}", "bp_lbl_equity": "Equity", "bp_lbl_loan": "Loan", "bp_lbl_interest_rate": "Interest Rate", @@ -1528,24 +1521,20 @@ "bp_lbl_monthly_payment": "Monthly Payment", "bp_lbl_annual_debt_service": "Annual Debt Service", "bp_lbl_ltv": "Loan-to-Value", - "bp_lbl_monthly": "Monthly", "bp_lbl_total_monthly_opex": "Total Monthly OPEX", "bp_lbl_annual_opex": "Annual OPEX", - "bp_lbl_weighted_hourly_rate": "Weighted Hourly Rate", "bp_lbl_target_utilization": "Target Utilization", "bp_lbl_gross_monthly_revenue": "Gross Monthly Revenue", "bp_lbl_net_monthly_revenue": "Net Monthly Revenue", "bp_lbl_monthly_ebitda": "Monthly EBITDA", "bp_lbl_monthly_net_cf": "Monthly Net Cash Flow", - "bp_lbl_year": "Year", "bp_lbl_revenue": "Revenue", "bp_lbl_ebitda": "EBITDA", "bp_lbl_debt_service": "Debt Service", "bp_lbl_net_cf": "Net CF", - "bp_lbl_moic": "MOIC", "bp_lbl_cash_on_cash": "Cash-on-Cash (Y3)", "bp_lbl_payback": "Payback", @@ -1553,46 +1542,40 @@ "bp_lbl_ebitda_margin": "EBITDA Margin", "bp_lbl_dscr_y3": "DSCR (Y3)", "bp_lbl_yield_on_cost": "Yield on Cost", - "bp_lbl_month": "Month", "bp_lbl_opex": "OPEX", "bp_lbl_debt": "Debt", "bp_lbl_cumulative": "Cumulative", - - "bp_lbl_disclaimer": "Disclaimer: This business plan is generated from user-provided assumptions using the Padelnomics financial model. All projections are estimates and do not constitute financial advice. Actual results may vary significantly based on market conditions, execution, and other factors. Consult with financial advisors before making investment decisions. \u00a9 Padelnomics \u2014 padelnomics.io", - + "bp_lbl_disclaimer": "Disclaimer: This business plan is generated from user-provided assumptions using the Padelnomics financial model. All projections are estimates and do not constitute financial advice. Actual results may vary significantly based on market conditions, execution, and other factors. Consult with financial advisors before making investment decisions. © Padelnomics — padelnomics.io", "email_magic_link_heading": "Sign in to {app_name}", "email_magic_link_body": "Here's your sign-in link. It expires in {expiry_minutes} minutes.", - "email_magic_link_btn": "Sign In \u2192", + "email_magic_link_btn": "Sign In →", "email_magic_link_fallback": "If the button doesn't work, copy and paste this URL into your browser:", "email_magic_link_ignore": "If you didn't request this, you can safely ignore this email.", "email_magic_link_subject": "Your sign-in link for {app_name}", "email_magic_link_preheader": "This link expires in {expiry_minutes} minutes", - "email_quote_verify_heading": "Verify your email to get quotes", "email_quote_verify_greeting": "Hi {first_name},", "email_quote_verify_body": "Thanks for requesting quotes. Verify your email to activate your quote request and create your {app_name} account.", "email_quote_verify_project_label": "Your project:", "email_quote_verify_urgency": "Verified requests get prioritized by our supplier network.", - "email_quote_verify_btn": "Verify & Activate \u2192", + "email_quote_verify_btn": "Verify & Activate →", "email_quote_verify_expires": "This link expires in 60 minutes.", "email_quote_verify_fallback": "If the button doesn't work, copy and paste this URL into your browser:", "email_quote_verify_ignore": "If you didn't request this, you can safely ignore this email.", - "email_quote_verify_subject": "Verify your email \u2014 suppliers are ready to quote", + "email_quote_verify_subject": "Verify your email — suppliers are ready to quote", "email_quote_verify_preheader": "One click to activate your quote request", "email_quote_verify_preheader_courts": "One click to activate your {court_count}-court project", - "email_welcome_heading": "Welcome to {app_name}", "email_welcome_greeting": "Hi {first_name},", - "email_welcome_body": "You now have access to the financial planner, market data, and supplier directory \u2014 everything you need to plan your padel business.", + "email_welcome_body": "You now have access to the financial planner, market data, and supplier directory — everything you need to plan your padel business.", "email_welcome_quickstart_heading": "Quick start:", - "email_welcome_link_planner": "Financial Planner \u2014 model your investment", - "email_welcome_link_markets": "Market Data \u2014 explore padel demand by city", - "email_welcome_link_quotes": "Get Quotes \u2014 connect with verified suppliers", - "email_welcome_btn": "Start Planning \u2192", - "email_welcome_subject": "You're in \u2014 here's how to start planning", + "email_welcome_link_planner": "Financial Planner — model your investment", + "email_welcome_link_markets": "Market Data — explore padel demand by city", + "email_welcome_link_quotes": "Get Quotes — connect with verified suppliers", + "email_welcome_btn": "Start Planning →", + "email_welcome_subject": "You're in — here's how to start planning", "email_welcome_preheader": "Your padel business planning toolkit is ready", - "email_waitlist_supplier_heading": "You're on the Supplier Waitlist", "email_waitlist_supplier_body": "Thanks for your interest in the {plan_name} plan. We're building a platform to connect you with qualified leads from padel entrepreneurs actively planning projects.", "email_waitlist_supplier_perks_intro": "As an early waitlist member, you'll get:", @@ -1600,20 +1583,19 @@ "email_waitlist_supplier_perk_2": "Exclusive launch pricing (locked in)", "email_waitlist_supplier_perk_3": "Dedicated onboarding call", "email_waitlist_supplier_meanwhile": "In the meantime, explore our free resources:", - "email_waitlist_supplier_link_planner": "Financial Planning Tool \u2014 model your padel facility", - "email_waitlist_supplier_link_directory": "Supplier Directory \u2014 browse verified suppliers", - "email_waitlist_supplier_subject": "You're in \u2014 {plan_name} early access is coming", + "email_waitlist_supplier_link_planner": "Financial Planning Tool — model your padel facility", + "email_waitlist_supplier_link_directory": "Supplier Directory — browse verified suppliers", + "email_waitlist_supplier_subject": "You're in — {plan_name} early access is coming", "email_waitlist_supplier_preheader": "Exclusive launch pricing + priority onboarding", "email_waitlist_general_heading": "You're on the Waitlist", - "email_waitlist_general_body": "Thanks for joining. We're building the planning platform for padel entrepreneurs \u2014 financial modelling, market data, and supplier connections in one place.", + "email_waitlist_general_body": "Thanks for joining. We're building the planning platform for padel entrepreneurs — financial modelling, market data, and supplier connections in one place.", "email_waitlist_general_perks_intro": "As an early waitlist member, you'll get:", "email_waitlist_general_perk_1": "Early access before public launch", "email_waitlist_general_perk_2": "Exclusive launch pricing", "email_waitlist_general_perk_3": "Priority onboarding and support", "email_waitlist_general_outro": "We'll be in touch soon.", - "email_waitlist_general_subject": "You're on the list \u2014 we'll notify you at launch", + "email_waitlist_general_subject": "You're on the list — we'll notify you at launch", "email_waitlist_general_preheader": "Early access + exclusive launch pricing", - "email_lead_forward_heading": "New Project Lead", "email_lead_forward_urgency": "This lead was just unlocked. Suppliers who respond within 24 hours are 3x more likely to win the project.", "email_lead_forward_section_brief": "Project Brief", @@ -1630,22 +1612,20 @@ "email_lead_forward_lbl_phone": "Phone", "email_lead_forward_lbl_company": "Company", "email_lead_forward_lbl_role": "Role", - "email_lead_forward_btn": "View in Lead Feed \u2192", + "email_lead_forward_btn": "View in Lead Feed →", "email_lead_forward_reply_direct": "or reply directly to {contact_email}", "email_lead_forward_preheader_suffix": "contact details inside", - "email_lead_matched_heading": "A supplier wants to discuss your project", "email_lead_matched_greeting": "Hi {first_name},", - "email_lead_matched_body": "Great news \u2014 a verified supplier has been matched with your padel project. They have your project brief and contact details.", + "email_lead_matched_body": "Great news — a verified supplier has been matched with your padel project. They have your project brief and contact details.", "email_lead_matched_context": "You submitted a quote request for a {facility_type} facility with {court_count} courts in {country}.", "email_lead_matched_next_heading": "What happens next", - "email_lead_matched_next_body": "The supplier has received your project brief and contact details. Most suppliers respond within 24\u201348 hours via email or phone.", + "email_lead_matched_next_body": "The supplier has received your project brief and contact details. Most suppliers respond within 24–48 hours via email or phone.", "email_lead_matched_tip": "Tip: Responding quickly to supplier outreach increases your chance of getting competitive quotes.", - "email_lead_matched_btn": "View Your Dashboard \u2192", + "email_lead_matched_btn": "View Your Dashboard →", "email_lead_matched_note": "You'll receive this notification each time a new supplier unlocks your project details.", "email_lead_matched_subject": "{first_name}, a supplier wants to discuss your project", - "email_lead_matched_preheader": "They'll reach out to you directly \u2014 here's what to expect", - + "email_lead_matched_preheader": "They'll reach out to you directly — here's what to expect", "email_enquiry_heading": "New enquiry from {contact_name}", "email_enquiry_body": "You have a new enquiry via your {supplier_name} directory listing.", "email_enquiry_lbl_from": "From", @@ -1654,15 +1634,13 @@ "email_enquiry_reply": "Reply directly to {contact_email} to connect.", "email_enquiry_subject": "New enquiry from {contact_name} via your directory listing", "email_enquiry_preheader": "Reply to connect with this potential client", - "email_business_plan_heading": "Your business plan is ready", "email_business_plan_body": "Your padel business plan PDF has been generated and is ready for download.", "email_business_plan_includes": "Your plan includes investment breakdown, revenue projections, and break-even analysis.", - "email_business_plan_btn": "Download PDF \u2192", - "email_business_plan_quote_cta": "Ready for the next step? Get quotes from suppliers \u2192", + "email_business_plan_btn": "Download PDF →", + "email_business_plan_quote_cta": "Ready for the next step? Get quotes from suppliers →", "email_business_plan_subject": "Your business plan PDF is ready to download", - "email_business_plan_preheader": "Professional padel facility financial plan \u2014 download now", - + "email_business_plan_preheader": "Professional padel facility financial plan — download now", "email_footer_tagline": "The padel business planning platform", - "email_footer_copyright": "\u00a9 {year} {app_name}. You received this email because you have an account or submitted a request." + "email_footer_copyright": "© {year} {app_name}. You received this email because you have an account or submitted a request." } diff --git a/web/src/padelnomics/migrations/versions/0020_lead_guarantee_columns.py b/web/src/padelnomics/migrations/versions/0020_lead_guarantee_columns.py new file mode 100644 index 0000000..f3c132d --- /dev/null +++ b/web/src/padelnomics/migrations/versions/0020_lead_guarantee_columns.py @@ -0,0 +1,16 @@ +"""Add lead-back guarantee columns to lead_forwards. + +guarantee_claimed_at: ISO timestamp when supplier claimed the guarantee refund. +guarantee_contact_method: how the supplier tried to reach the lead (email/phone/both). +Status 'no_response' added to lead_forwards.status by convention (no CHECK constraint change +needed — existing status TEXT column accepts any value). +""" + + +def up(conn): + conn.execute( + "ALTER TABLE lead_forwards ADD COLUMN guarantee_claimed_at TEXT" + ) + conn.execute( + "ALTER TABLE lead_forwards ADD COLUMN guarantee_contact_method TEXT" + ) diff --git a/web/src/padelnomics/public/templates/suppliers.html b/web/src/padelnomics/public/templates/suppliers.html index 0bd5acc..3039b71 100644 --- a/web/src/padelnomics/public/templates/suppliers.html +++ b/web/src/padelnomics/public/templates/suppliers.html @@ -554,7 +554,7 @@
      -

      {{ t.sup_credits_only_pre }} {{ t.sup_credits_only_cta }} {{ t.sup_credits_only_post }}

      +

      {{ t.sup_credits_only_pre }} {{ t.sup_credits_only_cta }}

      diff --git a/web/src/padelnomics/suppliers/routes.py b/web/src/padelnomics/suppliers/routes.py index 6fa090b..75174ca 100644 --- a/web/src/padelnomics/suppliers/routes.py +++ b/web/src/padelnomics/suppliers/routes.py @@ -13,9 +13,9 @@ from ..core import ( config, csrf_protect, execute, + feature_gate, fetch_all, fetch_one, - feature_gate, get_paddle_price, is_flag_enabled, ) @@ -535,12 +535,15 @@ async def _get_lead_feed_data(supplier, country="", heat="", timeline="", q="", leads = await fetch_all( f"""SELECT lr.*, - EXISTS(SELECT 1 FROM lead_forwards lf WHERE lf.lead_id = lr.id AND lf.supplier_id = ?) as is_unlocked, + (SELECT lf.id FROM lead_forwards lf WHERE lf.lead_id = lr.id AND lf.supplier_id = ? LIMIT 1) as forward_id, + (SELECT lf.created_at FROM lead_forwards lf WHERE lf.lead_id = lr.id AND lf.supplier_id = ? LIMIT 1) as forward_created_at, + (SELECT lf.guarantee_claimed_at FROM lead_forwards lf WHERE lf.lead_id = lr.id AND lf.supplier_id = ? LIMIT 1) as guarantee_claimed_at, + (SELECT lf.id FROM lead_forwards lf WHERE lf.lead_id = lr.id AND lf.supplier_id = ? LIMIT 1) IS NOT NULL as is_unlocked, (SELECT COUNT(*) FROM lead_forwards lf2 WHERE lf2.lead_id = lr.id) as bidder_count FROM lead_requests lr WHERE {where} ORDER BY lr.created_at DESC LIMIT ?""", - (supplier["id"], *params), + (supplier["id"], supplier["id"], supplier["id"], supplier["id"], *params), ) countries = await fetch_all( @@ -643,6 +646,57 @@ async def unlock_lead(token: str): supplier=updated_supplier, credit_cost=result["credit_cost"], scenario_id=scenario_id, + forward_id=result["forward_id"], + ) + + +@bp.route("/leads//guarantee-claim", methods=["POST"]) +@_lead_tier_required +@csrf_protect +async def guarantee_claim(forward_id: int): + """Claim lead-back guarantee: return credits for a non-responding lead. + + Validates the 3–30 day window and contact method, then calls + credits.refund_lead_guarantee(). Returns an updated lead card partial. + """ + from ..credits import GuaranteeAlreadyClaimed, GuaranteeWindowClosed, refund_lead_guarantee + + t = get_translations(g.get("lang") or "en") + supplier = g.supplier + + form = await request.form + contact_method = form.get("contact_method", "") + if contact_method not in ("email", "phone", "both"): + return t["sd_guarantee_window_error"], 400 + + try: + await refund_lead_guarantee( + supplier["id"], forward_id, contact_method + ) + except GuaranteeAlreadyClaimed: + return t["sd_guarantee_already_claimed"], 409 + except GuaranteeWindowClosed: + return t["sd_guarantee_window_error"], 409 + except AssertionError: + return "Lead not found.", 404 + + updated_supplier = await fetch_one( + "SELECT * FROM suppliers WHERE id = ?", (supplier["id"],) + ) + forward = await fetch_one( + """SELECT lf.*, lr.* + FROM lead_forwards lf + JOIN lead_requests lr ON lf.lead_id = lr.id + WHERE lf.id = ?""", + (forward_id,), + ) + + return await render_template( + "suppliers/partials/lead_card_unlocked.html", + lead=forward, + supplier=updated_supplier, + guarantee_claimed=True, + guarantee_success_msg=t["sd_guarantee_success"], ) diff --git a/web/src/padelnomics/suppliers/templates/suppliers/partials/lead_card_unlocked.html b/web/src/padelnomics/suppliers/templates/suppliers/partials/lead_card_unlocked.html index 4fdbe52..2219c1f 100644 --- a/web/src/padelnomics/suppliers/templates/suppliers/partials/lead_card_unlocked.html +++ b/web/src/padelnomics/suppliers/templates/suppliers/partials/lead_card_unlocked.html @@ -95,6 +95,52 @@ {% if credit_cost is defined %}

      {{ credit_cost }} {{ t.sd_unlocked_credits_used }} · {{ supplier.credit_balance }} {{ t.sd_unlocked_remaining }}

      {% endif %} + + {# --- Lead-Back Guarantee button --- + Show after 3 days, hide after 30 days, hide if already claimed. + forward_id comes from routes (unlock response) or from lead.forward_id (dashboard feed). + #} + {% set _fid = forward_id if forward_id is defined else lead.get('forward_id') %} + {% set _claimed_at = (guarantee_claimed_at if guarantee_claimed_at is defined else lead.get('guarantee_claimed_at')) %} + {% set _forward_created = lead.get('forward_created_at') or lead.get('created_at') %} + + {% if guarantee_claimed %} +

      + ✓ {{ guarantee_success_msg or t.sd_guarantee_success }} +

      + {% elif _fid and not _claimed_at %} + {# JS calculates age client-side to avoid server-side timezone math in template #} +
      + +
      + {% elif _claimed_at %} +

      {{ t.sd_guarantee_already_claimed }}

      + {% endif %} {% if credit_cost is defined %} From 9dd0f300144eee66ed1e84f1eebbede11255fa5d Mon Sep 17 00:00:00 2001 From: Deeman Date: Thu, 26 Feb 2026 15:24:06 +0100 Subject: [PATCH 5/5] =?UTF-8?q?docs:=20pricing=20overhaul=20+=20lead-back?= =?UTF-8?q?=20guarantee=20=E2=80=94=20CHANGELOG=20+=20PROJECT.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CHANGELOG: add Added section (guarantee, CRO restructure, credits-only callout, ROI line) and Changed section (Basic free, card color €59, BP PDF €149, hero CTA, comparison table, EN/DE translations, setup_paddle.py) PROJECT.md: - Correct Done section prices (Growth €199, Pro €499) - Add Done entries: pricing overhaul, lead-back guarantee - Add 3 Decisions Log entries (Basic free, guarantee credit-only, static ROI) Co-Authored-By: Claude Sonnet 4.6 --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ PROJECT.md | 7 ++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0031cb..84c1185 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,33 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] +### Added +- **Lead-Back Guarantee** — suppliers can claim credits back for non-responding leads + with one click after 3 business days. Route `POST /suppliers/leads//guarantee-claim`, + `refund_lead_guarantee()` in credits.py, "Lead didn't respond" button on unlocked + lead cards (visible 3–30 days after unlock). Migration 0020 adds `guarantee_claimed_at` + and `guarantee_contact_method` columns to `lead_forwards`. +- **Supplier page CRO restructure** — `/suppliers` page reordered to lead with value + before pricing (Why Padelnomics → Lead-Back Guarantee → lead preview → social proof + → pricing). All CTAs changed from "See Plans & Pricing" to "Get Started Free". +- **Static ROI line** — one-sentence ROI callout near pricing grounded in + `research/padel-hall-economics.md` data (4-court project = €30K+ contractor profit). +- **Credits-only callout** — below pricing grid: "Not ready for a subscription? Buy + a credit pack and unlock leads one at a time." + +### Changed +- **Basic tier is now free** — `supplier_basic` monthly/yearly price → €0. No Paddle + subscription required for Basic. Signup wizard shows "Free forever" instead of €39. +- **Card color boost** — price corrected €19/mo → €59/mo (aligns with MARKETING.md). +- **Business plan PDF** — price raised €99 → €149 (KfW-ready document supporting + €200K+ investment decision). +- **Hero and final CTA** — links changed from `#pricing` anchor to direct signup URL. +- **Comparison table** — €1,799/yr annotated with "(yearly plan)" for clarity. +- **EN+DE translations** — `sup_meta_desc` updated (removed "from €39/mo"); all + Basic-tier strings updated to reflect free tier; FAQ updated with guarantee mention. +- **`setup_paddle.py`** — Basic subscription products commented out (no longer needed); + `boost_card_color` 1900 → 5900 cents; `business_plan` 9900 → 14900 cents. + ### Fixed - **Double language prefix in article URLs** — articles were served at `/en/en/markets/italy` (double prefix) because `generate_articles()` stored diff --git a/PROJECT.md b/PROJECT.md index 40e6064..8f9da18 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -59,8 +59,10 @@ - [x] Business Plan PDF purchase flow (Paddle one-time → webhook → async generation) - [x] Boost purchases (logo, highlight, verified, card color, sticky week/month) - [x] Credit pack purchases (25/50/100/250) -- [x] Supplier subscription tiers (Basic free / Growth €149 / Pro €399, monthly + annual) +- [x] Supplier subscription tiers (Basic free / Growth €199 / Pro €499, monthly + annual) - [x] **Feature flags** (DB-backed, migration 0019) — `is_flag_enabled()` + `feature_gate()` decorator replace `WAITLIST_MODE`; 5 flags (markets, payments, planner_export, supplier_signup, lead_unlock); admin UI at `/admin/flags` with toggle +- [x] **Pricing overhaul** — Basic free (no Paddle sub), card color €59, BP PDF €149; supplier page restructured value-first (why → guarantee → leads → social proof → pricing); all CTAs "Get Started Free"; static ROI line; credits-only callout +- [x] **Lead-Back Guarantee** (migration 0020) — 1-click credit refund for non-responding leads (3–30 day window); `refund_lead_guarantee()` in credits.py; "Lead didn't respond" button on unlocked lead cards - [x] **Python supervisor** (`src/padelnomics/supervisor.py`) + `workflows.toml` — replaces `supervisor.sh`; topological wave scheduling; croniter-based `is_due()`; systemd service updated - [x] **Proxy rotation** (`extract/padelnomics_extract/proxy.py`) — round-robin + sticky hash-based selector via `PROXY_URLS` env var - [x] Resend email integration (transactional: magic link, welcome, quote verify, lead forward, enquiry) @@ -245,3 +247,6 @@ _Move here when you start working on it._ | 2026-02-22 | Credit system over pay-per-lead blast | Suppliers self-select → higher quality perception; scales without manual intervention | | 2026-02-22 | No soft email gate on planner | Planner already captures emails at natural points (scenario save → login, quote wizard step 9). Gate would add friction without meaningful list value. Revisit if data shows a gap. | | 2026-02-22 | Wipe test suppliers before launch | 5 `example.com` entries from seed_dev_data.py — empty directory with "Be the first" CTA is better than obviously fake data | +| 2026-02-26 | Basic tier free, no Paddle subscription | Simplest onboarding — signup without payment flow; MARKETING.md already said free, code said €39 | +| 2026-02-26 | Lead-Back Guarantee: supplier-initiated, credits back (not cash) | Risk reduction beats ROI projection (competitor research: #1 complaint is paying for silent leads). Credits-only keeps cash while removing the psychological barrier. | +| 2026-02-26 | Static ROI line, not interactive calculator | No lead marketplace uses inline ROI calcs; B2B SaaS trend away from them (Zendesk, Intercom, Gong all removed theirs). One bold number grounded in research beats a widget. |