Files
igny8/multi-tenancy/faulty-docs-with issues/PRICING-TO-PAID-SIGNUP-GAP.md
IGNY8 VPS (Salman) c54db6c2d9 reorg
2025-12-08 20:15:09 +00:00

9.7 KiB

CRITICAL GAP: Pricing Page to Paid Plans Signup

Issue Not Covered in Previous Documentation

Discovered: Marketing pricing page analysis
Severity: HIGH - Payment flow is broken


Problem Identified

Current State (Broken)

Pricing Page: frontend/src/marketing/pages/Pricing.tsx:307-316

ALL plan cards (Starter $89, Growth $139, Scale $229) have identical buttons:

<a href="https://app.igny8.com/signup">
  Start free trial
</a>

This means:

  • User clicks "Start free trial" on Growth ($139/month)
  • Goes to https://app.igny8.com/signup
  • Gets FREE TRIAL with free-trial plan (0 payment)
  • NO WAY to actually sign up for paid plans from pricing page

What's Missing

There is NO paid plan signup flow at all.


Required Solution

Pricing page buttons:

// Starter
<a href="https://app.igny8.com/signup?plan=starter">
  Get Started - $89/mo
</a>

// Growth  
<a href="https://app.igny8.com/signup?plan=growth">
  Get Started - $139/mo
</a>

// Scale
<a href="https://app.igny8.com/signup?plan=scale">
  Get Started - $229/mo
</a>

// Free trial stays same
<a href="https://app.igny8.com/signup">
  Start Free Trial
</a>

App signup page logic:

// In SignUpForm.tsx
const searchParams = new URLSearchParams(window.location.search);
const planSlug = searchParams.get('plan');

if (planSlug) {
  // Paid plan signup - show payment form
  navigate('/payment', { state: { planSlug } });
} else {
  // Free trial - current simple form
  // Continue with free trial registration
}

Backend:

# RegisterSerializer checks plan query/body
plan_slug = request.data.get('plan_slug') or request.GET.get('plan')

if plan_slug in ['starter', 'growth', 'scale']:
    # Paid plan - requires payment
    plan = Plan.objects.get(slug=plan_slug)
    account.status = 'pending_payment'
    # Create Subscription with status='pending_payment'
    # Wait for payment confirmation
else:
    # Free trial
    plan = Plan.objects.get(slug='free-trial')
    account.status = 'trial'
    # Immediate access

Option B: Separate Payment Route

Pricing page:

// Paid plans go to /payment
<a href="https://app.igny8.com/payment?plan=starter">
  Get Started - $89/mo
</a>

// Free trial stays /signup
<a href="https://app.igny8.com/signup">
  Start Free Trial
</a>

Create new route:

  • /signup - Free trial only (current implementation)
  • /payment - Paid plans with payment form

Implementation Required

1. Update Pricing Page CTAs

File: frontend/src/marketing/pages/Pricing.tsx:307

Add plan data to tiers:

const tiers = [
  {
    name: "Starter",
    slug: "starter",  // NEW
    price: "$89",
    // ... rest
  },
  // ...
];

Update CTA button logic:

<a
  href={`https://app.igny8.com/signup?plan=${tier.slug}`}
  className={...}
>
  {tier.price === "Free" ? "Start free trial" : `Get ${tier.name} - ${tier.price}/mo`}
</a>

2. Create Payment Flow Page

File: frontend/src/pages/Payment.tsx (NEW)

import { useLocation, useNavigate } from 'react-router-dom';
import { useState, useEffect } from 'react';

export default function Payment() {
  const location = useLocation();
  const navigate = useNavigate();
  const [selectedPlan, setSelectedPlan] = useState(null);
  
  useEffect(() => {
    const params = new URLSearchParams(location.search);
    const planSlug = params.get('plan');
    
    if (!planSlug) {
      // No plan selected, redirect to pricing
      navigate('/pricing');
      return;
    }
    
    // Load plan details from API
    fetch(`/api/v1/auth/plans/?slug=${planSlug}`)
      .then(res => res.json())
      .then(data => setSelectedPlan(data.results[0]));
  }, [location]);
  
  return (
    <div>
      <h1>Complete Your Subscription</h1>
      {selectedPlan && (
        <>
          <h2>{selectedPlan.name} - ${selectedPlan.price}/{selectedPlan.billing_cycle}</h2>
          
          {/* Payment method selection */}
          <div>
            <h3>Select Payment Method</h3>
            <button>Credit Card (Stripe) - Coming Soon</button>
            <button>PayPal - Coming Soon</button>
            <button>Bank Transfer</button>
          </div>
          
          {/* If bank transfer selected, show form */}
          <form onSubmit={handleBankTransferSubmit}>
            <input name="email" placeholder="Your email" required />
            <input name="account_name" placeholder="Account name" required />
            <button>Submit - We'll send payment details</button>
          </form>
        </>
      )}
    </div>
  );
}

3. Update Backend Registration

File: backend/igny8_core/auth/serializers.py:276

Add plan_slug handling:

def create(self, validated_data):
    from django.db import transaction
    from igny8_core.business.billing.models import CreditTransaction
    
    with transaction.atomic():
        # Check for plan_slug in request
        plan_slug = validated_data.get('plan_slug')
        
        if plan_slug in ['starter', 'growth', 'scale']:
            # PAID PLAN - requires payment
            plan = Plan.objects.get(slug=plan_slug, is_active=True)
            account_status = 'pending_payment'
            initial_credits = 0  # No credits until payment
            # Do NOT create CreditTransaction yet
        else:
            # FREE TRIAL - immediate access
            try:
                plan = Plan.objects.get(slug='free-trial', is_active=True)
            except Plan.DoesNotExist:
                plan = Plan.objects.get(slug='free', is_active=True)
            account_status = 'trial'
            initial_credits = plan.get_effective_credits_per_month()
        
        # ... create user and account ...
        
        account = Account.objects.create(
            name=account_name,
            slug=slug,
            owner=user,
            plan=plan,
            credits=initial_credits,
            status=account_status
        )
        
        # Only log credits for trial (paid accounts get credits after payment)
        if account_status == 'trial' and initial_credits > 0:
            CreditTransaction.objects.create(
                account=account,
                transaction_type='subscription',
                amount=initial_credits,
                balance_after=initial_credits,
                description=f'Free trial credits from {plan.name}',
                metadata={'registration': True, 'trial': True}
            )
        
        # ... rest of code ...

Current Pricing Page Button Behavior

All buttons currently do this:

igny8.com/pricing
  ├─ Starter card → "Start free trial" → https://app.igny8.com/signup
  ├─ Growth card → "Start free trial" → https://app.igny8.com/signup  
  └─ Scale card → "Start free trial" → https://app.igny8.com/signup

Result: NO WAY to sign up for paid plans.


Marketing Site (igny8.com)

// Pricing.tsx - Update tier CTAs

{tier.price === "Free" ? (
  <a href="https://app.igny8.com/signup">
    Start Free Trial
  </a>
) : (
  <a href={`https://app.igny8.com/signup?plan=${tier.slug}`}>
    Get {tier.name} - {tier.price}/mo
  </a>
)}

App Site (app.igny8.com)

// SignUpForm.tsx - Check for plan parameter

useEffect(() => {
  const params = new URLSearchParams(window.location.search);
  const planSlug = params.get('plan');
  
  if (planSlug && ['starter', 'growth', 'scale'].includes(planSlug)) {
    // Redirect to payment page
    navigate(`/payment?plan=${planSlug}`);
  }
  // Otherwise continue with free trial signup
}, []);

Payment Page (NEW)

  • Route: /payment?plan=starter
  • Shows: Plan details, payment method selection
  • Options: Bank Transfer (active), Stripe (coming soon), PayPal (coming soon)
  • Flow: Collect info → Create pending account → Send payment instructions

Update to Requirements

Add to FINAL-IMPLEMENTATION-REQUIREMENTS.md

New Section: E. Paid Plans Signup Flow

### CRITICAL ISSUE E: No Paid Plan Signup Path

#### Problem
Marketing pricing page shows paid plans ($89, $139, $229) but all buttons go to free trial signup.
No way for users to actually subscribe to paid plans.

#### Fix
1. Pricing page buttons must differentiate:
   - Free trial: /signup (no params)
   - Paid plans: /signup?plan=starter (with plan slug)

2. Signup page must detect plan parameter:
   - If plan=paid → Redirect to /payment
   - If no plan → Free trial signup

3. Create /payment page:
   - Show selected plan details
   - Payment method selection (bank transfer active, others coming soon)
   - Collect user info + payment details
   - Create account with status='pending_payment'
   - Send payment instructions

4. Backend must differentiate:
   - Free trial: immediate credits and access
   - Paid plans: 0 credits, pending_payment status, wait for confirmation

Files That Need Updates

Frontend

  1. frontend/src/marketing/pages/Pricing.tsx:307 - Add plan slug to CTAs
  2. frontend/src/components/auth/SignUpForm.tsx - Detect plan param, redirect to payment
  3. frontend/src/pages/Payment.tsx - NEW FILE - Payment flow page
  4. frontend/src/App.tsx - Add /payment route

Backend

  1. backend/igny8_core/auth/serializers.py:276 - Handle plan_slug for paid plans
  2. backend/igny8_core/auth/views.py:978 - Expose plan_slug in RegisterSerializer

This Was Missing From All Previous Documentation

Free trial flow - COVERED
Paid plan subscription flow - NOT COVERED

This is a critical gap that needs to be added to the implementation plan.